[ignition] Skip binding dead labels

BytecodeLabels for forward jumps may create a dead basic block if their
corresponding jump was elided (due to it dead code elimination). We can
avoid generating such dead basic blocks by skipping the label bind when
no corresponding jump has been observed. This works because all jumps
except JumpLoop are forward jumps, so we only have to special case one
Bind for loop headers to bind unconditionally.

Since Binds are now conditional on a jump existing, we can no longer rely
on using Bind to get the current offset (e.g. at the beginning of a try
block). Instead, we now expose the current offset in the bytecode array
writer. Conveniently, this means that we can be a bit smarter about basic
blocks around these statements.

As a drive-by, remove the unused Bind(target,label) function.

Bug: chromium:934166
Change-Id: I532aa452fb083560d07b90da99caca0b1d082aa3
Reviewed-on: https://chromium-review.googlesource.com/c/1488763
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Ross McIlroy <rmcilroy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59942}
This commit is contained in:
Leszek Swirski 2019-02-28 12:35:52 +01:00 committed by Commit Bot
parent 3f057d44de
commit 35269f77f8
21 changed files with 387 additions and 373 deletions

View File

@ -146,6 +146,12 @@ void BytecodeArrayBuilder::WriteJump(BytecodeNode* node, BytecodeLabel* label) {
bytecode_array_writer_.WriteJump(node, label);
}
void BytecodeArrayBuilder::WriteJumpLoop(BytecodeNode* node,
BytecodeLoopHeader* loop_header) {
AttachOrEmitDeferredSourceInfo(node);
bytecode_array_writer_.WriteJumpLoop(node, loop_header);
}
void BytecodeArrayBuilder::WriteSwitch(BytecodeNode* node,
BytecodeJumpTable* jump_table) {
AttachOrEmitDeferredSourceInfo(node);
@ -330,7 +336,7 @@ class BytecodeNodeBuilder {
template <typename... Operands> \
void BytecodeArrayBuilder::Output##name(BytecodeLabel* label, \
Operands... operands) { \
DCHECK(Bytecodes::IsJump(Bytecode::k##name)); \
DCHECK(Bytecodes::IsForwardJump(Bytecode::k##name)); \
BytecodeNode node(Create##name##Node(operands...)); \
WriteJump(&node, label); \
LeaveBasicBlock(); \
@ -338,6 +344,12 @@ class BytecodeNodeBuilder {
BYTECODE_LIST(DEFINE_BYTECODE_OUTPUT)
#undef DEFINE_BYTECODE_OUTPUT
void BytecodeArrayBuilder::OutputJumpLoop(BytecodeLoopHeader* loop_header,
int loop_depth) {
BytecodeNode node(CreateJumpLoopNode(0, loop_depth));
WriteJumpLoop(&node, loop_header);
}
void BytecodeArrayBuilder::OutputSwitchOnSmiNoFeedback(
BytecodeJumpTable* jump_table) {
BytecodeNode node(CreateSwitchOnSmiNoFeedbackNode(
@ -1053,6 +1065,10 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::ToNumeric(int feedback_slot) {
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(BytecodeLabel* label) {
// Don't generate code for a label which hasn't had a corresponding forward
// jump generated already. For backwards jumps, use BindLoopHeader.
if (!label->has_referrer_jump()) return *this;
// Flush the register optimizer when binding a label to ensure all
// expected registers are valid when jumping to this label.
if (register_optimizer_) register_optimizer_->Flush();
@ -1061,9 +1077,12 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(BytecodeLabel* label) {
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(const BytecodeLabel& target,
BytecodeLabel* label) {
bytecode_array_writer_.BindLabel(target, label);
BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(
BytecodeLoopHeader* loop_header) {
// Flush the register optimizer when starting a loop to ensure all expected
// registers are valid when jumping to the loop header.
if (register_optimizer_) register_optimizer_->Flush();
bytecode_array_writer_.BindLoopHeader(loop_header);
LeaveBasicBlock();
return *this;
}
@ -1078,6 +1097,33 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(BytecodeJumpTable* jump_table,
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::MarkHandler(
int handler_id, HandlerTable::CatchPrediction catch_prediction) {
// The handler starts a new basic block, and any reasonable try block won't
// let control fall through into it.
DCHECK_IMPLIES(register_optimizer_,
register_optimizer_->EnsureAllRegistersAreFlushed());
bytecode_array_writer_.BindHandlerTarget(handler_table_builder(), handler_id);
handler_table_builder()->SetPrediction(handler_id, catch_prediction);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::MarkTryBegin(int handler_id,
Register context) {
// Flush registers to make sure everything visible to the handler is
// materialized.
if (register_optimizer_) register_optimizer_->Flush();
bytecode_array_writer_.BindTryRegionStart(handler_table_builder(),
handler_id);
handler_table_builder()->SetContextRegister(handler_id, context);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::MarkTryEnd(int handler_id) {
bytecode_array_writer_.BindTryRegionEnd(handler_table_builder(), handler_id);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Jump(BytecodeLabel* label) {
DCHECK(!label->is_bound());
OutputJump(label, 0);
@ -1178,10 +1224,9 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfJSReceiver(
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::JumpLoop(BytecodeLabel* label,
int loop_depth) {
DCHECK(label->is_bound());
OutputJumpLoop(label, 0, loop_depth);
BytecodeArrayBuilder& BytecodeArrayBuilder::JumpLoop(
BytecodeLoopHeader* loop_header, int loop_depth) {
OutputJumpLoop(loop_header, loop_depth);
return *this;
}
@ -1321,7 +1366,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::SwitchOnGeneratorState(
BytecodeNode node(CreateSwitchOnGeneratorStateNode(
generator, jump_table->constant_pool_index(), jump_table->size()));
WriteSwitch(&node, jump_table);
LeaveBasicBlock();
return *this;
}
@ -1331,33 +1375,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::ResumeGenerator(
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::MarkHandler(
int handler_id, HandlerTable::CatchPrediction catch_prediction) {
BytecodeLabel handler;
Bind(&handler);
handler_table_builder()->SetHandlerTarget(handler_id, handler.offset());
handler_table_builder()->SetPrediction(handler_id, catch_prediction);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::MarkTryBegin(int handler_id,
Register context) {
// TODO(leszeks): Do we need to start a new basic block here? Could we simply
// get the current bytecode offset from the array writer instead?
BytecodeLabel try_begin;
Bind(&try_begin);
handler_table_builder()->SetTryRegionStart(handler_id, try_begin.offset());
handler_table_builder()->SetContextRegister(handler_id, context);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::MarkTryEnd(int handler_id) {
BytecodeLabel try_end;
Bind(&try_end);
handler_table_builder()->SetTryRegionEnd(handler_id, try_end.offset());
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CallProperty(Register callable,
RegisterList args,
int feedback_slot) {

View File

@ -27,6 +27,7 @@ class Isolate;
namespace interpreter {
class BytecodeLabel;
class BytecodeLoopHeader;
class BytecodeNode;
class BytecodeRegisterOptimizer;
class BytecodeJumpTable;
@ -397,13 +398,20 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final {
BytecodeArrayBuilder& ToNumber(int feedback_slot);
BytecodeArrayBuilder& ToNumeric(int feedback_slot);
// Exception handling.
BytecodeArrayBuilder& MarkHandler(int handler_id,
HandlerTable::CatchPrediction will_catch);
BytecodeArrayBuilder& MarkTryBegin(int handler_id, Register context);
BytecodeArrayBuilder& MarkTryEnd(int handler_id);
// Flow Control.
BytecodeArrayBuilder& Bind(BytecodeLabel* label);
BytecodeArrayBuilder& Bind(const BytecodeLabel& target, BytecodeLabel* label);
BytecodeArrayBuilder& Bind(BytecodeLoopHeader* label);
BytecodeArrayBuilder& Bind(BytecodeJumpTable* jump_table, int case_value);
BytecodeArrayBuilder& Jump(BytecodeLabel* label);
BytecodeArrayBuilder& JumpLoop(BytecodeLabel* label, int loop_depth);
BytecodeArrayBuilder& JumpLoop(BytecodeLoopHeader* loop_header,
int loop_depth);
BytecodeArrayBuilder& JumpIfTrue(ToBooleanMode mode, BytecodeLabel* label);
BytecodeArrayBuilder& JumpIfFalse(ToBooleanMode mode, BytecodeLabel* label);
@ -458,12 +466,6 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final {
BytecodeArrayBuilder& ResumeGenerator(Register generator,
RegisterList registers);
// Exception handling.
BytecodeArrayBuilder& MarkHandler(int handler_id,
HandlerTable::CatchPrediction will_catch);
BytecodeArrayBuilder& MarkTryBegin(int handler_id, Register context);
BytecodeArrayBuilder& MarkTryEnd(int handler_id);
// Creates a new handler table entry and returns a {hander_id} identifying the
// entry, so that it can be referenced by above exception handling support.
int NewHandlerEntry() { return handler_table_builder()->NewHandlerEntry(); }
@ -568,6 +570,8 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final {
BYTECODE_LIST(DECLARE_BYTECODE_OUTPUT)
#undef DECLARE_OPERAND_TYPE_INFO
V8_INLINE void OutputJumpLoop(BytecodeLoopHeader* loop_header,
int loop_depth);
V8_INLINE void OutputSwitchOnSmiNoFeedback(BytecodeJumpTable* jump_table);
bool RegisterIsValid(Register reg) const;
@ -583,6 +587,7 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final {
// Write bytecode to bytecode array.
void Write(BytecodeNode* node);
void WriteJump(BytecodeNode* node, BytecodeLabel* label);
void WriteJumpLoop(BytecodeNode* node, BytecodeLoopHeader* loop_header);
void WriteSwitch(BytecodeNode* node, BytecodeJumpTable* label);
// Not implemented as the illegal bytecode is used inside internally

View File

@ -11,6 +11,7 @@
#include "src/interpreter/bytecode-register.h"
#include "src/interpreter/bytecode-source-info.h"
#include "src/interpreter/constant-array-builder.h"
#include "src/interpreter/handler-table-builder.h"
#include "src/log.h"
#include "src/objects-inl.h"
@ -74,10 +75,8 @@ void BytecodeArrayWriter::Write(BytecodeNode* node) {
}
void BytecodeArrayWriter::WriteJump(BytecodeNode* node, BytecodeLabel* label) {
DCHECK(Bytecodes::IsJump(node->bytecode()));
DCHECK(Bytecodes::IsForwardJump(node->bytecode()));
// TODO(rmcilroy): For forward jumps we could also mark the label as dead,
// thereby avoiding emitting dead code when we bind the label.
if (exit_seen_in_block_) return; // Don't emit dead code.
UpdateExitSeenInBlock(node->bytecode());
MaybeElideLastBytecode(node->bytecode(), node->source_info().is_valid());
@ -86,12 +85,22 @@ void BytecodeArrayWriter::WriteJump(BytecodeNode* node, BytecodeLabel* label) {
EmitJump(node, label);
}
void BytecodeArrayWriter::WriteJumpLoop(BytecodeNode* node,
BytecodeLoopHeader* loop_header) {
DCHECK_EQ(node->bytecode(), Bytecode::kJumpLoop);
if (exit_seen_in_block_) return; // Don't emit dead code.
UpdateExitSeenInBlock(node->bytecode());
MaybeElideLastBytecode(node->bytecode(), node->source_info().is_valid());
UpdateSourcePositionTable(node);
EmitJumpLoop(node, loop_header);
}
void BytecodeArrayWriter::WriteSwitch(BytecodeNode* node,
BytecodeJumpTable* jump_table) {
DCHECK(Bytecodes::IsSwitch(node->bytecode()));
// TODO(rmcilroy): For jump tables we could also mark the table as dead,
// thereby avoiding emitting dead code when we bind the entries.
if (exit_seen_in_block_) return; // Don't emit dead code.
UpdateExitSeenInBlock(node->bytecode());
MaybeElideLastBytecode(node->bytecode(), node->source_info().is_valid());
@ -101,30 +110,18 @@ void BytecodeArrayWriter::WriteSwitch(BytecodeNode* node,
}
void BytecodeArrayWriter::BindLabel(BytecodeLabel* label) {
DCHECK(label->has_referrer_jump());
size_t current_offset = bytecodes()->size();
if (label->is_forward_target()) {
// An earlier jump instruction refers to this label. Update it's location.
PatchJump(current_offset, label->offset());
// Now treat as if the label will only be back referred to.
}
label->bind_to(current_offset);
InvalidateLastBytecode();
exit_seen_in_block_ = false; // Starting a new basic block.
// Update the jump instruction's location.
PatchJump(current_offset, label->jump_offset());
label->bind();
StartBasicBlock();
}
void BytecodeArrayWriter::BindLabel(const BytecodeLabel& target,
BytecodeLabel* label) {
DCHECK(!label->is_bound());
DCHECK(target.is_bound());
if (label->is_forward_target()) {
// An earlier jump instruction refers to this label. Update it's location.
PatchJump(target.offset(), label->offset());
// Now treat as if the label will only be back referred to.
}
label->bind_to(target.offset());
InvalidateLastBytecode();
// exit_seen_in_block_ was reset when target was bound, so shouldn't be
// changed here.
void BytecodeArrayWriter::BindLoopHeader(BytecodeLoopHeader* loop_header) {
size_t current_offset = bytecodes()->size();
loop_header->bind_to(current_offset);
StartBasicBlock();
}
void BytecodeArrayWriter::BindJumpTableEntry(BytecodeJumpTable* jump_table,
@ -139,8 +136,37 @@ void BytecodeArrayWriter::BindJumpTableEntry(BytecodeJumpTable* jump_table,
Smi::FromInt(static_cast<int>(relative_jump)));
jump_table->mark_bound(case_value);
StartBasicBlock();
}
void BytecodeArrayWriter::BindHandlerTarget(
HandlerTableBuilder* handler_table_builder, int handler_id) {
size_t current_offset = bytecodes()->size();
StartBasicBlock();
handler_table_builder->SetHandlerTarget(handler_id, current_offset);
}
void BytecodeArrayWriter::BindTryRegionStart(
HandlerTableBuilder* handler_table_builder, int handler_id) {
size_t current_offset = bytecodes()->size();
// Try blocks don't have to be in a separate basic block, but we do have to
// invalidate the bytecode to avoid eliding it and changing the offset.
InvalidateLastBytecode();
exit_seen_in_block_ = false; // Starting a new basic block.
handler_table_builder->SetTryRegionStart(handler_id, current_offset);
}
void BytecodeArrayWriter::BindTryRegionEnd(
HandlerTableBuilder* handler_table_builder, int handler_id) {
// Try blocks don't have to be in a separate basic block, but we do have to
// invalidate the bytecode to avoid eliding it and changing the offset.
InvalidateLastBytecode();
size_t current_offset = bytecodes()->size();
handler_table_builder->SetTryRegionEnd(handler_id, current_offset);
}
void BytecodeArrayWriter::StartBasicBlock() {
InvalidateLastBytecode();
exit_seen_in_block_ = false;
}
void BytecodeArrayWriter::UpdateSourcePositionTable(
@ -378,50 +404,57 @@ void BytecodeArrayWriter::PatchJump(size_t jump_target, size_t jump_location) {
unbound_jumps_--;
}
void BytecodeArrayWriter::EmitJump(BytecodeNode* node, BytecodeLabel* label) {
DCHECK(Bytecodes::IsJump(node->bytecode()));
void BytecodeArrayWriter::EmitJumpLoop(BytecodeNode* node,
BytecodeLoopHeader* loop_header) {
DCHECK_EQ(node->bytecode(), Bytecode::kJumpLoop);
DCHECK_EQ(0u, node->operand(0));
size_t current_offset = bytecodes()->size();
if (label->is_bound()) {
CHECK_GE(current_offset, label->offset());
CHECK_LE(current_offset, static_cast<size_t>(kMaxUInt32));
// Label has been bound already so this is a backwards jump.
uint32_t delta = static_cast<uint32_t>(current_offset - label->offset());
OperandScale operand_scale = Bytecodes::ScaleForUnsignedOperand(delta);
if (operand_scale > OperandScale::kSingle) {
// Adjust for scaling byte prefix for wide jump offset.
delta += 1;
}
DCHECK_EQ(Bytecode::kJumpLoop, node->bytecode());
node->update_operand0(delta);
} else {
// The label has not yet been bound so this is a forward reference
// that will be patched when the label is bound. We create a
// reservation in the constant pool so the jump can be patched
// when the label is bound. The reservation means the maximum size
// of the operand for the constant is known and the jump can
// be emitted into the bytecode stream with space for the operand.
unbound_jumps_++;
label->set_referrer(current_offset);
OperandSize reserved_operand_size =
constant_array_builder()->CreateReservedEntry();
DCHECK_NE(Bytecode::kJumpLoop, node->bytecode());
switch (reserved_operand_size) {
case OperandSize::kNone:
UNREACHABLE();
break;
case OperandSize::kByte:
node->update_operand0(k8BitJumpPlaceholder);
break;
case OperandSize::kShort:
node->update_operand0(k16BitJumpPlaceholder);
break;
case OperandSize::kQuad:
node->update_operand0(k32BitJumpPlaceholder);
break;
}
CHECK_GE(current_offset, loop_header->offset());
CHECK_LE(current_offset, static_cast<size_t>(kMaxUInt32));
// Label has been bound already so this is a backwards jump.
uint32_t delta =
static_cast<uint32_t>(current_offset - loop_header->offset());
OperandScale operand_scale = Bytecodes::ScaleForUnsignedOperand(delta);
if (operand_scale > OperandScale::kSingle) {
// Adjust for scaling byte prefix for wide jump offset.
delta += 1;
}
node->update_operand0(delta);
EmitBytecode(node);
}
void BytecodeArrayWriter::EmitJump(BytecodeNode* node, BytecodeLabel* label) {
DCHECK(Bytecodes::IsForwardJump(node->bytecode()));
DCHECK_EQ(0u, node->operand(0));
size_t current_offset = bytecodes()->size();
// The label has not yet been bound so this is a forward reference
// that will be patched when the label is bound. We create a
// reservation in the constant pool so the jump can be patched
// when the label is bound. The reservation means the maximum size
// of the operand for the constant is known and the jump can
// be emitted into the bytecode stream with space for the operand.
unbound_jumps_++;
label->set_referrer(current_offset);
OperandSize reserved_operand_size =
constant_array_builder()->CreateReservedEntry();
DCHECK_NE(Bytecode::kJumpLoop, node->bytecode());
switch (reserved_operand_size) {
case OperandSize::kNone:
UNREACHABLE();
break;
case OperandSize::kByte:
node->update_operand0(k8BitJumpPlaceholder);
break;
case OperandSize::kShort:
node->update_operand0(k16BitJumpPlaceholder);
break;
case OperandSize::kQuad:
node->update_operand0(k32BitJumpPlaceholder);
break;
}
EmitBytecode(node);
}

View File

@ -19,9 +19,11 @@ class SourcePositionTableBuilder;
namespace interpreter {
class BytecodeLabel;
class BytecodeLoopHeader;
class BytecodeNode;
class BytecodeJumpTable;
class ConstantArrayBuilder;
class HandlerTableBuilder;
namespace bytecode_array_writer_unittest {
class BytecodeArrayWriterUnittest;
@ -37,10 +39,18 @@ class V8_EXPORT_PRIVATE BytecodeArrayWriter final {
void Write(BytecodeNode* node);
void WriteJump(BytecodeNode* node, BytecodeLabel* label);
void WriteJumpLoop(BytecodeNode* node, BytecodeLoopHeader* loop_header);
void WriteSwitch(BytecodeNode* node, BytecodeJumpTable* jump_table);
void BindLabel(BytecodeLabel* label);
void BindLabel(const BytecodeLabel& target, BytecodeLabel* label);
void BindLoopHeader(BytecodeLoopHeader* loop_header);
void BindJumpTableEntry(BytecodeJumpTable* jump_table, int case_value);
void BindHandlerTarget(HandlerTableBuilder* handler_table_builder,
int handler_id);
void BindTryRegionStart(HandlerTableBuilder* handler_table_builder,
int handler_id);
void BindTryRegionEnd(HandlerTableBuilder* handler_table_builder,
int handler_id);
Handle<BytecodeArray> ToBytecodeArray(Isolate* isolate, int register_count,
int parameter_count,
Handle<ByteArray> handler_table);
@ -71,6 +81,7 @@ class V8_EXPORT_PRIVATE BytecodeArrayWriter final {
void EmitBytecode(const BytecodeNode* const node);
void EmitJump(BytecodeNode* node, BytecodeLabel* label);
void EmitJumpLoop(BytecodeNode* node, BytecodeLoopHeader* loop_header);
void EmitSwitch(BytecodeNode* node, BytecodeJumpTable* jump_table);
void UpdateSourcePositionTable(const BytecodeNode* const node);
@ -79,6 +90,8 @@ class V8_EXPORT_PRIVATE BytecodeArrayWriter final {
void MaybeElideLastBytecode(Bytecode next_bytecode, bool has_source_info);
void InvalidateLastBytecode();
void StartBasicBlock();
ZoneVector<uint8_t>* bytecodes() { return &bytecodes_; }
SourcePositionTableBuilder* source_position_table_builder() {
return &source_position_table_builder_;

View File

@ -18,18 +18,13 @@ BytecodeLabel* BytecodeLabels::New() {
}
void BytecodeLabels::Bind(BytecodeArrayBuilder* builder) {
DCHECK(!is_bound_);
is_bound_ = true;
for (auto& label : labels_) {
builder->Bind(&label);
}
}
void BytecodeLabels::BindToLabel(BytecodeArrayBuilder* builder,
const BytecodeLabel& target) {
for (auto& label : labels_) {
builder->Bind(target, &label);
}
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -15,42 +15,67 @@ namespace interpreter {
class BytecodeArrayBuilder;
// A label representing a branch target in a bytecode array. When a
// label is bound, it represents a known position in the bytecode
// array. For labels that are forward references there can be at most
// one reference whilst it is unbound.
class V8_EXPORT_PRIVATE BytecodeLabel final {
// A label representing a loop header in a bytecode array. It is bound before
// the jump is seen, so its position is always known by the time the jump is
// reached.
class V8_EXPORT_PRIVATE BytecodeLoopHeader final {
public:
BytecodeLabel() : bound_(false), offset_(kInvalidOffset) {}
BytecodeLoopHeader() : offset_(kInvalidOffset) {}
bool is_bound() const { return bound_; }
size_t offset() const { return offset_; }
size_t offset() const {
DCHECK_NE(offset_, kInvalidOffset);
return offset_;
}
private:
static const size_t kInvalidOffset = static_cast<size_t>(-1);
void bind_to(size_t offset) {
DCHECK(!bound_ && offset != kInvalidOffset);
DCHECK_NE(offset, kInvalidOffset);
DCHECK_EQ(offset_, kInvalidOffset);
offset_ = offset;
}
// The bytecode offset of the loop header.
size_t offset_;
friend class BytecodeArrayWriter;
};
// A label representing a forward branch target in a bytecode array. When a
// label is bound, it represents a known position in the bytecode array. A label
// can only have at most one referrer jump.
class V8_EXPORT_PRIVATE BytecodeLabel final {
public:
BytecodeLabel() : bound_(false), jump_offset_(kInvalidOffset) {}
bool is_bound() const { return bound_; }
size_t jump_offset() const {
DCHECK_NE(jump_offset_, kInvalidOffset);
return jump_offset_;
}
bool has_referrer_jump() const { return jump_offset_ != kInvalidOffset; }
private:
static const size_t kInvalidOffset = static_cast<size_t>(-1);
void bind() {
DCHECK(!bound_);
bound_ = true;
}
void set_referrer(size_t offset) {
DCHECK(!bound_ && offset != kInvalidOffset && offset_ == kInvalidOffset);
offset_ = offset;
DCHECK(!bound_);
DCHECK_NE(offset, kInvalidOffset);
DCHECK_EQ(jump_offset_, kInvalidOffset);
jump_offset_ = offset;
}
bool is_forward_target() const {
return offset() != kInvalidOffset && !is_bound();
}
// There are three states for a label:
// bound_ offset_
// UNSET false kInvalidOffset
// FORWARD_TARGET false Offset of referring jump
// BACKWARD_TARGET true Offset of label in bytecode array when bound
// Set when the label is bound (i.e. the start of the target basic block).
bool bound_;
size_t offset_;
// Set when the jump referrer is set (i.e. the location of the jump).
size_t jump_offset_;
friend class BytecodeArrayWriter;
};
@ -58,26 +83,26 @@ class V8_EXPORT_PRIVATE BytecodeLabel final {
// Class representing a branch target of multiple jumps.
class V8_EXPORT_PRIVATE BytecodeLabels {
public:
explicit BytecodeLabels(Zone* zone) : labels_(zone) {}
explicit BytecodeLabels(Zone* zone) : labels_(zone), is_bound_(false) {}
BytecodeLabel* New();
void Bind(BytecodeArrayBuilder* builder);
void BindToLabel(BytecodeArrayBuilder* builder, const BytecodeLabel& target);
bool is_bound() const {
bool is_bound = !labels_.empty() && labels_.front().is_bound();
DCHECK(!is_bound ||
std::all_of(labels_.begin(), labels_.end(),
[](const BytecodeLabel& l) { return l.is_bound(); }));
return is_bound;
DCHECK_IMPLIES(
is_bound_,
std::all_of(labels_.begin(), labels_.end(), [](const BytecodeLabel& l) {
return !l.has_referrer_jump() || l.is_bound();
}));
return is_bound_;
}
bool empty() const { return labels_.empty(); }
private:
ZoneLinkedList<BytecodeLabel> labels_;
bool is_bound_;
DISALLOW_COPY_AND_ASSIGN(BytecodeLabels);
};

View File

@ -60,6 +60,7 @@ class V8_EXPORT_PRIVATE BytecodeRegisterOptimizer final
// Materialize all live registers and flush equivalence sets.
void Flush();
bool EnsureAllRegistersAreFlushed() const;
// Prepares for |bytecode|.
template <Bytecode bytecode, AccumulatorUse accumulator_use>
@ -132,8 +133,6 @@ class V8_EXPORT_PRIVATE BytecodeRegisterOptimizer final
RegisterInfo* non_set_member);
void PushToRegistersNeedingFlush(RegisterInfo* reg);
bool EnsureAllRegistersAreFlushed() const;
// Methods for finding and creating metadata for each register.
RegisterInfo* GetRegisterInfo(Register reg) {
size_t index = GetRegisterInfoTableIndex(reg);

View File

@ -70,7 +70,6 @@ void LoopBuilder::JumpToHeader(int loop_depth) {
int level = Min(loop_depth, AbstractCode::kMaxLoopNestingMarker - 1);
// Loop must have closed form, i.e. all loop elements are within the loop,
// the loop header precedes the body and next elements in the loop.
DCHECK(loop_header_.is_bound());
builder()->JumpLoop(&loop_header_, level);
}
@ -108,7 +107,6 @@ void TryCatchBuilder::BeginTry(Register context) {
void TryCatchBuilder::EndTry() {
builder()->MarkTryEnd(handler_id_);
builder()->Jump(&exit_);
builder()->Bind(&handler_);
builder()->MarkHandler(handler_id_, catch_prediction_);
if (block_coverage_builder_ != nullptr) {

View File

@ -121,7 +121,7 @@ class V8_EXPORT_PRIVATE LoopBuilder final : public BreakableControlFlowBuilder {
void ContinueIfNull() { EmitJumpIfNull(&continue_labels_); }
private:
BytecodeLabel loop_header_;
BytecodeLoopHeader loop_header_;
// Unbound labels that identify jumps for continue statements in the code and
// jumps from checking the loop condition to the header for do-while loops.
@ -188,7 +188,6 @@ class V8_EXPORT_PRIVATE TryCatchBuilder final : public ControlFlowBuilder {
private:
int handler_id_;
HandlerTable::CatchPrediction catch_prediction_;
BytecodeLabel handler_;
BytecodeLabel exit_;
BlockCoverageBuilder* block_coverage_builder_;

View File

@ -14,7 +14,7 @@ snippet: "
"
frame size: 8
parameter count: 1
bytecode array length: 190
bytecode array length: 180
bytecodes: [
B(SwitchOnGeneratorState), R(0), U8(0), U8(2),
B(Mov), R(closure), R(1),
@ -35,7 +35,7 @@ bytecodes: [
B(LdaSmi), I8(1),
B(Star), R(1),
B(Mov), R(5), R(2),
B(Jump), U8(95),
B(Jump), U8(85),
B(LdaUndefined),
B(Star), R(6),
B(Mov), R(0), R(5),
@ -53,8 +53,7 @@ bytecodes: [
B(LdaSmi), I8(1),
B(Star), R(1),
B(Mov), R(5), R(2),
B(Jump), U8(51),
B(Jump), U8(36),
B(Jump), U8(41),
B(Star), R(5),
B(CreateCatchContext), R(5), U8(4),
B(Star), R(4),
@ -70,10 +69,6 @@ bytecodes: [
B(Star), R(2),
B(LdaSmi), I8(2),
B(Star), R(1),
B(Jump), U8(15),
B(LdaSmi), I8(-1),
B(Star), R(2),
B(Star), R(1),
B(Jump), U8(7),
B(Star), R(2),
B(LdaZero),
@ -111,8 +106,8 @@ constant pool: [
Smi [23],
]
handlers: [
[20, 136, 144],
[23, 100, 102],
[20, 134, 134],
[23, 100, 100],
]
---
@ -122,7 +117,7 @@ snippet: "
"
frame size: 8
parameter count: 1
bytecode array length: 235
bytecode array length: 225
bytecodes: [
B(SwitchOnGeneratorState), R(0), U8(0), U8(3),
B(Mov), R(closure), R(1),
@ -143,7 +138,7 @@ bytecodes: [
B(LdaSmi), I8(1),
B(Star), R(1),
B(Mov), R(5), R(2),
B(Jump), U8(140),
B(Jump), U8(130),
/* 22 S> */ B(LdaSmi), I8(42),
B(Star), R(6),
B(LdaFalse),
@ -160,7 +155,7 @@ bytecodes: [
B(LdaSmi), I8(1),
B(Star), R(1),
B(Mov), R(5), R(2),
B(Jump), U8(95),
B(Jump), U8(85),
B(LdaUndefined),
B(Star), R(6),
B(Mov), R(0), R(5),
@ -178,8 +173,7 @@ bytecodes: [
B(LdaSmi), I8(1),
B(Star), R(1),
B(Mov), R(5), R(2),
B(Jump), U8(51),
B(Jump), U8(36),
B(Jump), U8(41),
B(Star), R(5),
B(CreateCatchContext), R(5), U8(7),
B(Star), R(4),
@ -195,10 +189,6 @@ bytecodes: [
B(Star), R(2),
B(LdaSmi), I8(2),
B(Star), R(1),
B(Jump), U8(15),
B(LdaSmi), I8(-1),
B(Star), R(2),
B(Star), R(1),
B(Jump), U8(7),
B(Star), R(2),
B(LdaZero),
@ -239,8 +229,8 @@ constant pool: [
Smi [23],
]
handlers: [
[20, 181, 189],
[23, 145, 147],
[20, 179, 179],
[23, 145, 145],
]
---
@ -250,7 +240,7 @@ snippet: "
"
frame size: 20
parameter count: 1
bytecode array length: 416
bytecode array length: 406
bytecodes: [
B(SwitchOnGeneratorState), R(0), U8(0), U8(3),
B(Mov), R(closure), R(4),
@ -364,7 +354,7 @@ bytecodes: [
B(LdaSmi), I8(1),
B(Star), R(4),
B(Mov), R(14), R(5),
B(Jump), U8(95),
B(Jump), U8(85),
B(LdaUndefined),
B(Star), R(9),
B(Mov), R(0), R(8),
@ -382,8 +372,7 @@ bytecodes: [
B(LdaSmi), I8(1),
B(Star), R(4),
B(Mov), R(8), R(5),
B(Jump), U8(51),
B(Jump), U8(36),
B(Jump), U8(41),
B(Star), R(8),
B(CreateCatchContext), R(8), U8(16),
B(Star), R(7),
@ -399,10 +388,6 @@ bytecodes: [
B(Star), R(5),
B(LdaSmi), I8(2),
B(Star), R(4),
B(Jump), U8(15),
B(LdaSmi), I8(-1),
B(Star), R(5),
B(Star), R(4),
B(Jump), U8(7),
B(Star), R(5),
B(LdaZero),
@ -447,14 +432,14 @@ constant pool: [
Smi [6],
Smi [9],
SCOPE_INFO_TYPE,
Smi [321],
Smi [311],
Smi [6],
Smi [9],
Smi [23],
]
handlers: [
[20, 362, 370],
[23, 326, 328],
[20, 360, 360],
[23, 326, 326],
[93, 180, 188],
[234, 247, 249],
]
@ -467,7 +452,7 @@ snippet: "
"
frame size: 17
parameter count: 1
bytecode array length: 482
bytecode array length: 472
bytecodes: [
B(SwitchOnGeneratorState), R(0), U8(0), U8(5),
B(Mov), R(closure), R(1),
@ -595,7 +580,7 @@ bytecodes: [
B(LdaSmi), I8(1),
B(Star), R(1),
B(Mov), R(7), R(2),
B(Jump), U8(95),
B(Jump), U8(85),
B(LdaUndefined),
B(Star), R(6),
B(Mov), R(0), R(5),
@ -613,8 +598,7 @@ bytecodes: [
B(LdaSmi), I8(1),
B(Star), R(1),
B(Mov), R(5), R(2),
B(Jump), U8(51),
B(Jump), U8(36),
B(Jump), U8(41),
B(Star), R(5),
B(CreateCatchContext), R(5), U8(17),
B(Star), R(4),
@ -630,10 +614,6 @@ bytecodes: [
B(Star), R(2),
B(LdaSmi), I8(2),
B(Star), R(1),
B(Jump), U8(15),
B(LdaSmi), I8(-1),
B(Star), R(2),
B(Star), R(1),
B(Jump), U8(7),
B(Star), R(2),
B(LdaZero),
@ -679,14 +659,14 @@ constant pool: [
ONE_BYTE_INTERNALIZED_STRING_TYPE ["done"],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["value"],
SCOPE_INFO_TYPE,
Smi [387],
Smi [287],
Smi [377],
Smi [277],
Smi [6],
Smi [9],
Smi [23],
]
handlers: [
[20, 428, 436],
[23, 392, 394],
[20, 426, 426],
[23, 392, 392],
]

View File

@ -211,7 +211,7 @@ snippet: "
"
frame size: 1
parameter count: 1
bytecode array length: 14
bytecode array length: 12
bytecodes: [
/* 30 E> */ B(StackCheck),
/* 42 S> */ B(LdaZero),
@ -221,8 +221,6 @@ bytecodes: [
/* 74 S> */ B(Return),
/* 86 S> */ B(LdaSmi), I8(2),
/* 95 S> */ B(Return),
B(LdaUndefined),
/* 98 S> */ B(Return),
]
constant pool: [
]

View File

@ -115,7 +115,7 @@ snippet: "
"
frame size: 16
parameter count: 1
bytecode array length: 266
bytecode array length: 264
bytecodes: [
/* 30 E> */ B(StackCheck),
/* 48 S> */ B(CreateArrayLiteral), U8(0), U8(0), U8(37),
@ -185,7 +185,6 @@ bytecodes: [
B(Star), R(14),
B(JumpLoop), U8(33), I8(0),
B(Mov), R(13), R(1),
B(Ldar), R(1),
B(LdaSmi), I8(-1),
B(Star), R(10),
B(Star), R(9),
@ -242,8 +241,8 @@ constant pool: [
ONE_BYTE_INTERNALIZED_STRING_TYPE [""],
]
handlers: [
[44, 174, 182],
[228, 241, 243],
[44, 172, 180],
[226, 239, 241],
]
---

View File

@ -16,7 +16,7 @@ snippet: "
"
frame size: 21
parameter count: 1
bytecode array length: 329
bytecode array length: 325
bytecodes: [
B(SwitchOnGeneratorState), R(0), U8(0), U8(2),
B(Mov), R(closure), R(4),
@ -140,7 +140,6 @@ bytecodes: [
B(Mov), R(0), R(5),
B(InvokeIntrinsic), U8(Runtime::k_AsyncFunctionResolve), R(5), U8(3),
/* 57 S> */ B(Return),
B(Jump), U8(30),
B(Star), R(5),
B(CreateCatchContext), R(5), U8(10),
B(Star), R(4),
@ -154,8 +153,6 @@ bytecodes: [
B(Star), R(8),
B(Mov), R(0), R(6),
B(InvokeIntrinsic), U8(Runtime::k_AsyncFunctionReject), R(6), U8(3),
/* 57 S> */ B(Return),
B(LdaUndefined),
/* 57 S> */ B(Return),
]
constant pool: [
@ -172,7 +169,7 @@ constant pool: [
SCOPE_INFO_TYPE,
]
handlers: [
[20, 297, 299],
[20, 297, 297],
[77, 157, 165],
[211, 260, 262],
]
@ -186,7 +183,7 @@ snippet: "
"
frame size: 21
parameter count: 1
bytecode array length: 350
bytecode array length: 346
bytecodes: [
B(SwitchOnGeneratorState), R(0), U8(0), U8(2),
B(Mov), R(closure), R(4),
@ -318,7 +315,6 @@ bytecodes: [
B(Mov), R(0), R(5),
B(InvokeIntrinsic), U8(Runtime::k_AsyncFunctionResolve), R(5), U8(3),
/* 68 S> */ B(Return),
B(Jump), U8(30),
B(Star), R(5),
B(CreateCatchContext), R(5), U8(12),
B(Star), R(4),
@ -332,8 +328,6 @@ bytecodes: [
B(Star), R(8),
B(Mov), R(0), R(6),
B(InvokeIntrinsic), U8(Runtime::k_AsyncFunctionReject), R(6), U8(3),
/* 68 S> */ B(Return),
B(LdaUndefined),
/* 68 S> */ B(Return),
]
constant pool: [
@ -352,7 +346,7 @@ constant pool: [
SCOPE_INFO_TYPE,
]
handlers: [
[20, 318, 320],
[20, 318, 318],
[77, 161, 169],
[215, 264, 266],
]
@ -369,7 +363,7 @@ snippet: "
"
frame size: 21
parameter count: 1
bytecode array length: 345
bytecode array length: 341
bytecodes: [
B(SwitchOnGeneratorState), R(0), U8(0), U8(2),
B(Mov), R(closure), R(4),
@ -500,7 +494,6 @@ bytecodes: [
B(Mov), R(0), R(5),
B(InvokeIntrinsic), U8(Runtime::k_AsyncFunctionResolve), R(5), U8(3),
/* 114 S> */ B(Return),
B(Jump), U8(30),
B(Star), R(5),
B(CreateCatchContext), R(5), U8(10),
B(Star), R(4),
@ -514,8 +507,6 @@ bytecodes: [
B(Star), R(8),
B(Mov), R(0), R(6),
B(InvokeIntrinsic), U8(Runtime::k_AsyncFunctionReject), R(6), U8(3),
/* 114 S> */ B(Return),
B(LdaUndefined),
/* 114 S> */ B(Return),
]
constant pool: [
@ -532,7 +523,7 @@ constant pool: [
SCOPE_INFO_TYPE,
]
handlers: [
[20, 313, 315],
[20, 313, 313],
[77, 173, 181],
[227, 276, 278],
]
@ -547,7 +538,7 @@ snippet: "
"
frame size: 16
parameter count: 1
bytecode array length: 265
bytecode array length: 261
bytecodes: [
B(Mov), R(closure), R(2),
B(Mov), R(this), R(3),
@ -647,7 +638,6 @@ bytecodes: [
B(Mov), R(0), R(3),
B(InvokeIntrinsic), U8(Runtime::k_AsyncFunctionResolve), R(3), U8(3),
/* 96 S> */ B(Return),
B(Jump), U8(30),
B(Star), R(3),
B(CreateCatchContext), R(3), U8(11),
B(Star), R(2),
@ -661,8 +651,6 @@ bytecodes: [
B(Star), R(6),
B(Mov), R(0), R(4),
B(InvokeIntrinsic), U8(Runtime::k_AsyncFunctionReject), R(4), U8(3),
/* 96 S> */ B(Return),
B(LdaUndefined),
/* 96 S> */ B(Return),
]
constant pool: [
@ -680,7 +668,7 @@ constant pool: [
SCOPE_INFO_TYPE,
]
handlers: [
[16, 233, 235],
[16, 233, 233],
[59, 112, 120],
[166, 179, 181],
]

View File

@ -739,7 +739,7 @@ snippet: "
"
frame size: 18
parameter count: 2
bytecode array length: 232
bytecode array length: 228
bytecodes: [
B(Mov), R(closure), R(5),
B(Mov), R(this), R(6),
@ -827,7 +827,6 @@ bytecodes: [
B(Mov), R(0), R(6),
B(InvokeIntrinsic), U8(Runtime::k_AsyncFunctionResolve), R(6), U8(3),
/* 60 S> */ B(Return),
B(Jump), U8(30),
B(Star), R(6),
B(CreateCatchContext), R(6), U8(6),
B(Star), R(5),
@ -841,8 +840,6 @@ bytecodes: [
B(Star), R(9),
B(Mov), R(0), R(7),
B(InvokeIntrinsic), U8(Runtime::k_AsyncFunctionReject), R(7), U8(3),
/* 60 S> */ B(Return),
B(LdaUndefined),
/* 60 S> */ B(Return),
]
constant pool: [
@ -855,7 +852,7 @@ constant pool: [
SCOPE_INFO_TYPE,
]
handlers: [
[16, 200, 202],
[16, 200, 200],
[50, 96, 104],
[150, 163, 165],
]
@ -869,7 +866,7 @@ snippet: "
"
frame size: 17
parameter count: 2
bytecode array length: 268
bytecode array length: 264
bytecodes: [
B(SwitchOnGeneratorState), R(0), U8(0), U8(1),
B(Mov), R(closure), R(4),
@ -970,7 +967,6 @@ bytecodes: [
B(Mov), R(0), R(5),
B(InvokeIntrinsic), U8(Runtime::k_AsyncFunctionResolve), R(5), U8(3),
/* 54 S> */ B(Return),
B(Jump), U8(30),
B(Star), R(5),
B(CreateCatchContext), R(5), U8(7),
B(Star), R(4),
@ -984,8 +980,6 @@ bytecodes: [
B(Star), R(8),
B(Mov), R(0), R(6),
B(InvokeIntrinsic), U8(Runtime::k_AsyncFunctionReject), R(6), U8(3),
/* 54 S> */ B(Return),
B(LdaUndefined),
/* 54 S> */ B(Return),
]
constant pool: [
@ -999,7 +993,7 @@ constant pool: [
SCOPE_INFO_TYPE,
]
handlers: [
[20, 236, 238],
[20, 236, 236],
[54, 132, 140],
[186, 199, 201],
]

View File

@ -147,7 +147,7 @@ snippet: "
"
frame size: 0
parameter count: 2
bytecode array length: 19
bytecode array length: 17
bytecodes: [
/* 10 E> */ B(StackCheck),
/* 18 S> */ B(LdaZero),
@ -157,8 +157,6 @@ bytecodes: [
/* 47 S> */ B(Return),
/* 63 S> */ B(Wide), B(LdaSmi), I16(-200),
/* 75 S> */ B(Return),
B(LdaUndefined),
/* 80 S> */ B(Return),
]
constant pool: [
]
@ -258,7 +256,7 @@ snippet: "
"
frame size: 2
parameter count: 2
bytecode array length: 29
bytecode array length: 27
bytecodes: [
/* 10 E> */ B(StackCheck),
/* 24 S> */ B(LdaZero),
@ -273,8 +271,6 @@ bytecodes: [
/* 1092 S> */ B(Return),
/* 1102 S> */ B(Wide), B(LdaSmi), I16(-200),
/* 1114 S> */ B(Return),
B(LdaUndefined),
/* 1117 S> */ B(Return),
]
constant pool: [
HEAP_NUMBER_TYPE [0.01],
@ -357,7 +353,7 @@ snippet: "
"
frame size: 2
parameter count: 1
bytecode array length: 26
bytecode array length: 24
bytecodes: [
/* 10 E> */ B(StackCheck),
/* 25 S> */ B(LdaZero),
@ -371,8 +367,6 @@ bytecodes: [
/* 1087 S> */ B(Return),
/* 1097 S> */ B(Wide), B(LdaSmi), I16(-200),
/* 1109 S> */ B(Return),
B(LdaUndefined),
/* 1112 S> */ B(Return),
]
constant pool: [
]
@ -461,7 +455,7 @@ snippet: "
"
frame size: 1
parameter count: 1
bytecode array length: 14
bytecode array length: 12
bytecodes: [
/* 10 E> */ B(StackCheck),
/* 25 S> */ B(LdaZero),
@ -471,8 +465,6 @@ bytecodes: [
/* 53 S> */ B(Return),
/* 69 S> */ B(LdaSmi), I8(-20),
/* 80 S> */ B(Return),
B(LdaUndefined),
/* 85 S> */ B(Return),
]
constant pool: [
]
@ -494,7 +486,7 @@ snippet: "
"
frame size: 0
parameter count: 3
bytecode array length: 36
bytecode array length: 34
bytecodes: [
/* 10 E> */ B(StackCheck),
/* 21 S> */ B(Ldar), R(arg1),
@ -515,8 +507,6 @@ bytecodes: [
/* 102 S> */ B(Return),
/* 118 S> */ B(LdaSmi), I8(-1),
/* 128 S> */ B(Return),
B(LdaUndefined),
/* 133 S> */ B(Return),
]
constant pool: [
]

View File

@ -374,7 +374,7 @@ snippet: "
"
frame size: 8
parameter count: 1
bytecode array length: 85
bytecode array length: 81
bytecodes: [
B(Mov), R(closure), R(3),
B(Mov), R(this), R(4),
@ -400,7 +400,6 @@ bytecodes: [
B(Mov), R(0), R(4),
/* 49 E> */ B(InvokeIntrinsic), U8(Runtime::k_AsyncFunctionResolve), R(4), U8(3),
/* 67 S> */ B(Return),
B(Jump), U8(30),
B(Star), R(4),
B(CreateCatchContext), R(4), U8(0),
B(Star), R(3),
@ -414,15 +413,13 @@ bytecodes: [
B(Star), R(7),
B(Mov), R(0), R(5),
B(InvokeIntrinsic), U8(Runtime::k_AsyncFunctionReject), R(5), U8(3),
/* 67 S> */ B(Return),
B(LdaUndefined),
/* 67 S> */ B(Return),
]
constant pool: [
SCOPE_INFO_TYPE,
]
handlers: [
[16, 53, 55],
[16, 53, 53],
]
---
@ -434,7 +431,7 @@ snippet: "
"
frame size: 7
parameter count: 1
bytecode array length: 121
bytecode array length: 117
bytecodes: [
B(SwitchOnGeneratorState), R(0), U8(0), U8(1),
B(Mov), R(closure), R(2),
@ -473,7 +470,6 @@ bytecodes: [
B(Mov), R(0), R(3),
/* 49 E> */ B(InvokeIntrinsic), U8(Runtime::k_AsyncFunctionResolve), R(3), U8(3),
/* 61 S> */ B(Return),
B(Jump), U8(30),
B(Star), R(3),
B(CreateCatchContext), R(3), U8(1),
B(Star), R(2),
@ -487,8 +483,6 @@ bytecodes: [
B(Star), R(6),
B(Mov), R(0), R(4),
B(InvokeIntrinsic), U8(Runtime::k_AsyncFunctionReject), R(4), U8(3),
/* 61 S> */ B(Return),
B(LdaUndefined),
/* 61 S> */ B(Return),
]
constant pool: [
@ -496,6 +490,6 @@ constant pool: [
SCOPE_INFO_TYPE,
]
handlers: [
[20, 89, 91],
[20, 89, 89],
]

View File

@ -11,13 +11,12 @@ snippet: "
"
frame size: 2
parameter count: 1
bytecode array length: 27
bytecode array length: 23
bytecodes: [
/* 30 E> */ B(StackCheck),
B(Mov), R(context), R(0),
/* 40 S> */ B(LdaSmi), I8(1),
/* 49 S> */ B(Return),
B(Jump), U8(18),
B(Star), R(1),
B(CreateCatchContext), R(1), U8(0),
B(Star), R(0),
@ -27,14 +26,12 @@ bytecodes: [
B(PushContext), R(1),
/* 63 S> */ B(LdaSmi), I8(2),
/* 72 S> */ B(Return),
B(LdaUndefined),
/* 75 S> */ B(Return),
]
constant pool: [
SCOPE_INFO_TYPE,
]
handlers: [
[4, 7, 9],
[4, 7, 7],
]
---

View File

@ -1528,17 +1528,18 @@ TEST(InterpreterJumps) {
NewFeedbackMetadata(isolate, &feedback_spec);
Register reg(0), scratch(1);
BytecodeLabel label[3];
BytecodeLoopHeader loop_header;
BytecodeLabel label[2];
builder.LoadLiteral(Smi::zero())
.StoreAccumulatorInRegister(reg)
.Jump(&label[1]);
SetRegister(builder, reg, 1024, scratch).Bind(&label[0]);
IncrementRegister(builder, reg, 1, scratch, GetIndex(slot)).Jump(&label[2]);
SetRegister(builder, reg, 2048, scratch).Bind(&label[1]);
.Jump(&label[0]);
SetRegister(builder, reg, 1024, scratch).Bind(&loop_header);
IncrementRegister(builder, reg, 1, scratch, GetIndex(slot)).Jump(&label[1]);
SetRegister(builder, reg, 2048, scratch).Bind(&label[0]);
IncrementRegister(builder, reg, 2, scratch, GetIndex(slot1))
.JumpLoop(&label[0], 0);
SetRegister(builder, reg, 4096, scratch).Bind(&label[2]);
.JumpLoop(&loop_header, 0);
SetRegister(builder, reg, 4096, scratch).Bind(&label[1]);
IncrementRegister(builder, reg, 4, scratch, GetIndex(slot2))
.LoadAccumulatorWithRegister(reg)
.Return();
@ -1667,6 +1668,8 @@ TEST(InterpreterJumpConstantWith16BitOperand) {
builder.LoadLiteral(Smi::zero());
builder.StoreAccumulatorInRegister(reg);
// Conditional jump to the fake label, to force both basic blocks to be live.
builder.JumpIfTrue(ToBooleanMode::kConvertToBoolean, &fake);
// Consume all 8-bit operands
for (int i = 1; i <= 256; i++) {
builder.LoadLiteral(i + 0.5);

View File

@ -0,0 +1,18 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
{
for(let i = 0; i < 10; ++i){
try{
// Carefully constructed by a fuzzer to use a new register for s(), whose
// write is dead due to the unconditional throw after s()=N, but which is
// read in the ({...g}) call, which therefore must also be marked dead and
// elided.
with(f&&g&&(s()=N)({...g})){}
} catch {}
%OptimizeOsr();
}
}

View File

@ -280,10 +280,12 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
// Short jumps with Imm8 operands
{
BytecodeLabel start, after_jump1, after_jump2, after_jump3, after_jump4,
BytecodeLoopHeader loop_header;
BytecodeLabel after_jump1, after_jump2, after_jump3, after_jump4,
after_jump5, after_jump6, after_jump7, after_jump8, after_jump9,
after_jump10;
builder.Bind(&start)
after_jump10, after_loop;
builder.JumpIfNull(&after_loop)
.Bind(&loop_header)
.Jump(&after_jump1)
.Bind(&after_jump1)
.JumpIfNull(&after_jump2)
@ -304,14 +306,16 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
.Bind(&after_jump9)
.JumpIfFalse(ToBooleanMode::kAlreadyBoolean, &after_jump10)
.Bind(&after_jump10)
.JumpLoop(&start, 0);
.JumpLoop(&loop_header, 0)
.Bind(&after_loop);
}
// Longer jumps with constant operands
BytecodeLabel end[10];
{
// Longer jumps with constant operands
BytecodeLabel after_jump;
builder.Jump(&end[0])
builder.JumpIfNull(&after_jump)
.Jump(&end[0])
.Bind(&after_jump)
.JumpIfTrue(ToBooleanMode::kConvertToBoolean, &end[1])
.JumpIfTrue(ToBooleanMode::kAlreadyBoolean, &end[2])
@ -337,10 +341,9 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
// Emit throw and re-throw in it's own basic block so that the rest of the
// code isn't omitted due to being dead.
BytecodeLabel after_throw;
builder.Throw().Bind(&after_throw);
BytecodeLabel after_rethrow;
builder.ReThrow().Bind(&after_rethrow);
BytecodeLabel after_throw, after_rethrow;
builder.JumpIfNull(&after_throw).Throw().Bind(&after_throw);
builder.JumpIfNull(&after_rethrow).ReThrow().Bind(&after_rethrow);
builder.ForInEnumerate(reg)
.ForInPrepare(triple, 1)
@ -414,10 +417,10 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
builder.Debugger();
// Emit abort bytecode.
{
BytecodeLabel after;
builder.Abort(AbortReason::kOperandIsASmi).Bind(&after);
}
BytecodeLabel after_abort;
builder.JumpIfNull(&after_abort)
.Abort(AbortReason::kOperandIsASmi)
.Bind(&after_abort);
// Insert dummy ops to force longer jumps.
for (int i = 0; i < 256; i++) {
@ -568,10 +571,11 @@ TEST_F(BytecodeArrayBuilderTest, ForwardJumps) {
Register reg(0);
BytecodeLabel far0, far1, far2, far3, far4;
BytecodeLabel near0, near1, near2, near3, near4;
BytecodeLabel after_jump0, after_jump1;
BytecodeLabel after_jump_near0, after_jump_far0;
builder.Jump(&near0)
.Bind(&after_jump0)
builder.JumpIfNull(&after_jump_near0)
.Jump(&near0)
.Bind(&after_jump_near0)
.CompareOperation(Token::Value::EQ, reg, 1)
.JumpIfTrue(ToBooleanMode::kAlreadyBoolean, &near1)
.CompareOperation(Token::Value::EQ, reg, 2)
@ -585,8 +589,9 @@ TEST_F(BytecodeArrayBuilderTest, ForwardJumps) {
.Bind(&near2)
.Bind(&near3)
.Bind(&near4)
.JumpIfNull(&after_jump_far0)
.Jump(&far0)
.Bind(&after_jump1)
.Bind(&after_jump_far0)
.CompareOperation(Token::Value::EQ, reg, 3)
.JumpIfTrue(ToBooleanMode::kAlreadyBoolean, &far1)
.CompareOperation(Token::Value::EQ, reg, 4)
@ -602,9 +607,13 @@ TEST_F(BytecodeArrayBuilderTest, ForwardJumps) {
builder.Return();
Handle<BytecodeArray> array = builder.ToBytecodeArray(isolate());
DCHECK_EQ(array->length(), 44 + kFarJumpDistance - 22 + 1);
DCHECK_EQ(array->length(), 48 + kFarJumpDistance - 22 + 1);
BytecodeArrayIterator iterator(array);
// Ignore JumpIfNull operation.
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 22);
iterator.Advance();
@ -637,6 +646,9 @@ TEST_F(BytecodeArrayBuilderTest, ForwardJumps) {
CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 2);
iterator.Advance();
// Ignore JumpIfNull operation.
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpConstant);
CHECK_EQ(iterator.GetConstantForIndexOperand(0),
Smi::FromInt(kFarJumpDistance));
@ -682,11 +694,22 @@ TEST_F(BytecodeArrayBuilderTest, BackwardJumps) {
Register reg(0);
BytecodeLabel label0;
builder.Bind(&label0).JumpLoop(&label0, 0);
BytecodeLabel end;
builder.JumpIfNull(&end);
BytecodeLabel after_loop;
// Conditional jump to force the code after the JumpLoop to be live.
// Technically this jump is illegal because it's jumping into the middle of
// the subsequent loops, but that's ok for this unit test.
BytecodeLoopHeader loop_header;
builder.JumpIfNull(&after_loop)
.Bind(&loop_header)
.JumpLoop(&loop_header, 0)
.Bind(&after_loop);
for (int i = 0; i < 42; i++) {
BytecodeLabel after_jump;
builder.JumpLoop(&label0, 0).Bind(&after_jump);
BytecodeLabel after_loop;
// Conditional jump to force the code after the JumpLoop to be live.
builder.JumpIfNull(&after_loop).JumpLoop(&loop_header, 0).Bind(&after_loop);
}
// Add padding to force wide backwards jumps.
@ -694,21 +717,28 @@ TEST_F(BytecodeArrayBuilderTest, BackwardJumps) {
builder.Debugger();
}
builder.JumpLoop(&label0, 0);
BytecodeLabel end;
builder.JumpLoop(&loop_header, 0);
builder.Bind(&end);
builder.Return();
Handle<BytecodeArray> array = builder.ToBytecodeArray(isolate());
BytecodeArrayIterator iterator(array);
// Ignore the JumpIfNull to the end
iterator.Advance();
// Ignore the JumpIfNull to after the first JumpLoop
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 0);
iterator.Advance();
for (unsigned i = 0; i < 42; i++) {
// Ignore the JumpIfNull to after the JumpLoop
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
CHECK_EQ(iterator.current_operand_scale(), OperandScale::kSingle);
// offset of 3 (because kJumpLoop takes two immediate operands)
CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), i * 3 + 3);
// offset of 5 (because kJumpLoop takes two immediate operands and
// JumpIfNull takes 1)
CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), i * 5 + 5);
iterator.Advance();
}
// Check padding to force wide backwards jumps.
@ -718,7 +748,7 @@ TEST_F(BytecodeArrayBuilderTest, BackwardJumps) {
}
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
CHECK_EQ(iterator.current_operand_scale(), OperandScale::kDouble);
CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 386);
CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 42 * 5 + 256 + 4);
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kReturn);
iterator.Advance();
@ -821,71 +851,6 @@ TEST_F(BytecodeArrayBuilderTest, WideSwitch) {
CHECK(iterator.done());
}
TEST_F(BytecodeArrayBuilderTest, LabelReuse) {
BytecodeArrayBuilder builder(zone(), 1, 0);
// Labels can only have 1 forward reference, but
// can be referred to mulitple times once bound.
BytecodeLabel label, after_jump0, after_jump1;
builder.Jump(&label)
.Bind(&label)
.JumpLoop(&label, 0)
.Bind(&after_jump0)
.JumpLoop(&label, 0)
.Bind(&after_jump1)
.Return();
Handle<BytecodeArray> array = builder.ToBytecodeArray(isolate());
BytecodeArrayIterator iterator(array);
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 2);
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 0);
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 3);
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kReturn);
iterator.Advance();
CHECK(iterator.done());
}
TEST_F(BytecodeArrayBuilderTest, LabelAddressReuse) {
static const int kRepeats = 3;
BytecodeArrayBuilder builder(zone(), 1, 0);
for (int i = 0; i < kRepeats; i++) {
BytecodeLabel label, after_jump0, after_jump1;
builder.Jump(&label)
.Bind(&label)
.JumpLoop(&label, 0)
.Bind(&after_jump0)
.JumpLoop(&label, 0)
.Bind(&after_jump1);
}
builder.Return();
Handle<BytecodeArray> array = builder.ToBytecodeArray(isolate());
BytecodeArrayIterator iterator(array);
for (int i = 0; i < kRepeats; i++) {
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 2);
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 0);
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 3);
iterator.Advance();
}
CHECK_EQ(iterator.current_bytecode(), Bytecode::kReturn);
iterator.Advance();
CHECK(iterator.done());
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -49,8 +49,8 @@ class BytecodeArrayWriterUnittest : public TestWithIsolateAndZone {
void WriteJump(Bytecode bytecode, BytecodeLabel* label,
BytecodeSourceInfo info = BytecodeSourceInfo());
void WriteJumpLoop(Bytecode bytecode, BytecodeLabel* label, int depth,
BytecodeSourceInfo info = BytecodeSourceInfo());
void WriteJumpLoop(Bytecode bytecode, BytecodeLoopHeader* loop_header,
int depth, BytecodeSourceInfo info = BytecodeSourceInfo());
BytecodeArrayWriter* writer() { return &bytecode_array_writer_; }
ZoneVector<unsigned char>* bytecodes() { return writer()->bytecodes(); }
@ -105,10 +105,11 @@ void BytecodeArrayWriterUnittest::WriteJump(Bytecode bytecode,
}
void BytecodeArrayWriterUnittest::WriteJumpLoop(Bytecode bytecode,
BytecodeLabel* label, int depth,
BytecodeLoopHeader* loop_header,
int depth,
BytecodeSourceInfo info) {
BytecodeNode node(bytecode, 0, depth, info);
writer()->WriteJump(&node, label);
writer()->WriteJumpLoop(&node, loop_header);
}
TEST_F(BytecodeArrayWriterUnittest, SimpleExample) {
@ -195,7 +196,8 @@ TEST_F(BytecodeArrayWriterUnittest, ComplexExample) {
{0, 30, false}, {1, 42, true}, {3, 42, false}, {6, 68, true},
{18, 63, true}, {32, 54, false}, {37, 85, true}, {46, 85, true}};
BytecodeLabel back_jump, jump_for_in, jump_end_1, jump_end_2, jump_end_3;
BytecodeLoopHeader loop_header;
BytecodeLabel jump_for_in, jump_end_1, jump_end_2, jump_end_3;
Write(Bytecode::kStackCheck, {30, false});
Write(Bytecode::kLdaConstant, U8(0), {42, true});
@ -206,7 +208,7 @@ TEST_F(BytecodeArrayWriterUnittest, ComplexExample) {
Write(Bytecode::kForInPrepare, R(3), U8(4));
Write(Bytecode::kLdaZero);
Write(Bytecode::kStar, R(7));
writer()->BindLabel(&back_jump);
writer()->BindLoopHeader(&loop_header);
Write(Bytecode::kForInContinue, R(7), R(6), {63, true});
WriteJump(Bytecode::kJumpIfFalse, &jump_end_3);
Write(Bytecode::kForInNext, R(3), R(7), R(4), U8(1));
@ -219,7 +221,7 @@ TEST_F(BytecodeArrayWriterUnittest, ComplexExample) {
writer()->BindLabel(&jump_for_in);
Write(Bytecode::kForInStep, R(7));
Write(Bytecode::kStar, R(7));
WriteJumpLoop(Bytecode::kJumpLoop, &back_jump, 0);
WriteJumpLoop(Bytecode::kJumpLoop, &loop_header, 0);
writer()->BindLabel(&jump_end_1);
writer()->BindLabel(&jump_end_2);
writer()->BindLabel(&jump_end_3);
@ -328,7 +330,9 @@ TEST_F(BytecodeArrayWriterUnittest, DeadcodeElimination) {
Write(Bytecode::kLdaSmi, 127); // Dead code.
WriteJump(Bytecode::kJumpIfFalse, &after_conditional_jump); // Dead code.
writer()->BindLabel(&after_jump);
writer()->BindLabel(&after_conditional_jump);
// We would bind the after_conditional_jump label here, but the jump to it is
// dead.
CHECK(!after_conditional_jump.has_referrer_jump());
Write(Bytecode::kLdaSmi, 127, {65, true});
WriteJump(Bytecode::kJumpIfFalse, &after_return);
Write(Bytecode::kReturn, {75, true});