From c7ebc5814f5e9a1aa769225528997ec9aa0922f7 Mon Sep 17 00:00:00 2001 From: Leszek Swirski Date: Thu, 28 Feb 2019 14:34:26 +0100 Subject: [PATCH] Reland "[ignition] Skip binding dead labels" This is a reland of 35269f77f8a7fb5360717e2cb470a6ef1637944e Switches on an expression that unconditionally throws would have all their case statements dead, causing a DCHECK error in the SwitchBuilder. This fixes up the DCHECK to allow dead labels. Original change's description: > [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 > Reviewed-by: Ross McIlroy > Cr-Commit-Position: refs/heads/master@{#59942} TBR=rmcilroy@chromium.org Bug: chromium:934166 Change-Id: If6eab4162106717ce64a2dc477000c6a76354cb4 Reviewed-on: https://chromium-review.googlesource.com/c/1494535 Reviewed-by: Leszek Swirski Commit-Queue: Leszek Swirski Cr-Commit-Position: refs/heads/master@{#59948} --- src/interpreter/bytecode-array-builder.cc | 89 ++++++---- src/interpreter/bytecode-array-builder.h | 21 ++- src/interpreter/bytecode-array-writer.cc | 167 +++++++++++------- src/interpreter/bytecode-array-writer.h | 15 +- src/interpreter/bytecode-label.cc | 9 +- src/interpreter/bytecode-label.h | 83 ++++++--- src/interpreter/bytecode-register-optimizer.h | 3 +- src/interpreter/control-flow-builders.cc | 4 +- src/interpreter/control-flow-builders.h | 3 +- .../AsyncGenerators.golden | 68 +++---- .../bytecode_expectations/CompareNil.golden | 4 +- .../DestructuringAssignment.golden | 7 +- .../bytecode_expectations/ForAwaitOf.golden | 28 +-- .../bytecode_expectations/ForOfLoop.golden | 14 +- .../bytecode_expectations/IfConditions.golden | 20 +-- .../StandardForLoop.golden | 14 +- .../bytecode_expectations/TryCatch.golden | 7 +- test/cctest/interpreter/test-interpreter.cc | 17 +- test/mjsunit/regress/regress-crbug-934166.js | 18 ++ test/mjsunit/switch.js | 10 ++ .../bytecode-array-builder-unittest.cc | 151 ++++++---------- .../bytecode-array-writer-unittest.cc | 20 ++- 22 files changed, 398 insertions(+), 374 deletions(-) create mode 100644 test/mjsunit/regress/regress-crbug-934166.js diff --git a/src/interpreter/bytecode-array-builder.cc b/src/interpreter/bytecode-array-builder.cc index 2183068576..ffd9853088 100644 --- a/src/interpreter/bytecode-array-builder.cc +++ b/src/interpreter/bytecode-array-builder.cc @@ -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 \ 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) { diff --git a/src/interpreter/bytecode-array-builder.h b/src/interpreter/bytecode-array-builder.h index d362ffffa4..449d3de3d7 100644 --- a/src/interpreter/bytecode-array-builder.h +++ b/src/interpreter/bytecode-array-builder.h @@ -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 diff --git a/src/interpreter/bytecode-array-writer.cc b/src/interpreter/bytecode-array-writer.cc index 377696c736..05f655b71a 100644 --- a/src/interpreter/bytecode-array-writer.cc +++ b/src/interpreter/bytecode-array-writer.cc @@ -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(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(kMaxUInt32)); - // Label has been bound already so this is a backwards jump. - uint32_t delta = static_cast(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(kMaxUInt32)); + // Label has been bound already so this is a backwards jump. + uint32_t delta = + static_cast(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); } diff --git a/src/interpreter/bytecode-array-writer.h b/src/interpreter/bytecode-array-writer.h index e6db2fce22..d18c62a90f 100644 --- a/src/interpreter/bytecode-array-writer.h +++ b/src/interpreter/bytecode-array-writer.h @@ -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 ToBytecodeArray(Isolate* isolate, int register_count, int parameter_count, Handle 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* bytecodes() { return &bytecodes_; } SourcePositionTableBuilder* source_position_table_builder() { return &source_position_table_builder_; diff --git a/src/interpreter/bytecode-label.cc b/src/interpreter/bytecode-label.cc index da607a2927..df49b03bd4 100644 --- a/src/interpreter/bytecode-label.cc +++ b/src/interpreter/bytecode-label.cc @@ -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 diff --git a/src/interpreter/bytecode-label.h b/src/interpreter/bytecode-label.h index 9622c1513e..4581f4f4e2 100644 --- a/src/interpreter/bytecode-label.h +++ b/src/interpreter/bytecode-label.h @@ -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(-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(-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 labels_; + bool is_bound_; DISALLOW_COPY_AND_ASSIGN(BytecodeLabels); }; diff --git a/src/interpreter/bytecode-register-optimizer.h b/src/interpreter/bytecode-register-optimizer.h index 7ba7d3b602..b120741872 100644 --- a/src/interpreter/bytecode-register-optimizer.h +++ b/src/interpreter/bytecode-register-optimizer.h @@ -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 @@ -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); diff --git a/src/interpreter/control-flow-builders.cc b/src/interpreter/control-flow-builders.cc index 6b1bdc3424..8eb44069f6 100644 --- a/src/interpreter/control-flow-builders.cc +++ b/src/interpreter/control-flow-builders.cc @@ -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); } @@ -79,7 +78,7 @@ void LoopBuilder::BindContinueTarget() { continue_labels_.Bind(builder()); } SwitchBuilder::~SwitchBuilder() { #ifdef DEBUG for (auto site : case_sites_) { - DCHECK(site.is_bound()); + DCHECK(!site.has_referrer_jump() || site.is_bound()); } #endif } @@ -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) { diff --git a/src/interpreter/control-flow-builders.h b/src/interpreter/control-flow-builders.h index 8359f0d1ee..d4f2d11e7c 100644 --- a/src/interpreter/control-flow-builders.h +++ b/src/interpreter/control-flow-builders.h @@ -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_; diff --git a/test/cctest/interpreter/bytecode_expectations/AsyncGenerators.golden b/test/cctest/interpreter/bytecode_expectations/AsyncGenerators.golden index 1aac88b8fb..231a9050b8 100644 --- a/test/cctest/interpreter/bytecode_expectations/AsyncGenerators.golden +++ b/test/cctest/interpreter/bytecode_expectations/AsyncGenerators.golden @@ -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], ] diff --git a/test/cctest/interpreter/bytecode_expectations/CompareNil.golden b/test/cctest/interpreter/bytecode_expectations/CompareNil.golden index 83a267c29f..d9413a1866 100644 --- a/test/cctest/interpreter/bytecode_expectations/CompareNil.golden +++ b/test/cctest/interpreter/bytecode_expectations/CompareNil.golden @@ -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: [ ] diff --git a/test/cctest/interpreter/bytecode_expectations/DestructuringAssignment.golden b/test/cctest/interpreter/bytecode_expectations/DestructuringAssignment.golden index 43b798a513..f4a7c340c4 100644 --- a/test/cctest/interpreter/bytecode_expectations/DestructuringAssignment.golden +++ b/test/cctest/interpreter/bytecode_expectations/DestructuringAssignment.golden @@ -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], ] --- diff --git a/test/cctest/interpreter/bytecode_expectations/ForAwaitOf.golden b/test/cctest/interpreter/bytecode_expectations/ForAwaitOf.golden index 6496470999..6fe59da400 100644 --- a/test/cctest/interpreter/bytecode_expectations/ForAwaitOf.golden +++ b/test/cctest/interpreter/bytecode_expectations/ForAwaitOf.golden @@ -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], ] diff --git a/test/cctest/interpreter/bytecode_expectations/ForOfLoop.golden b/test/cctest/interpreter/bytecode_expectations/ForOfLoop.golden index 6a22b18c33..2672e0688e 100644 --- a/test/cctest/interpreter/bytecode_expectations/ForOfLoop.golden +++ b/test/cctest/interpreter/bytecode_expectations/ForOfLoop.golden @@ -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], ] diff --git a/test/cctest/interpreter/bytecode_expectations/IfConditions.golden b/test/cctest/interpreter/bytecode_expectations/IfConditions.golden index e68211a189..3691bda376 100644 --- a/test/cctest/interpreter/bytecode_expectations/IfConditions.golden +++ b/test/cctest/interpreter/bytecode_expectations/IfConditions.golden @@ -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: [ ] diff --git a/test/cctest/interpreter/bytecode_expectations/StandardForLoop.golden b/test/cctest/interpreter/bytecode_expectations/StandardForLoop.golden index d2e5938f5b..d72bb35fee 100644 --- a/test/cctest/interpreter/bytecode_expectations/StandardForLoop.golden +++ b/test/cctest/interpreter/bytecode_expectations/StandardForLoop.golden @@ -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], ] diff --git a/test/cctest/interpreter/bytecode_expectations/TryCatch.golden b/test/cctest/interpreter/bytecode_expectations/TryCatch.golden index b078ef048a..ec70270dae 100644 --- a/test/cctest/interpreter/bytecode_expectations/TryCatch.golden +++ b/test/cctest/interpreter/bytecode_expectations/TryCatch.golden @@ -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], ] --- diff --git a/test/cctest/interpreter/test-interpreter.cc b/test/cctest/interpreter/test-interpreter.cc index fab903ce4e..0970ca1ef2 100644 --- a/test/cctest/interpreter/test-interpreter.cc +++ b/test/cctest/interpreter/test-interpreter.cc @@ -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); diff --git a/test/mjsunit/regress/regress-crbug-934166.js b/test/mjsunit/regress/regress-crbug-934166.js new file mode 100644 index 0000000000..d6fae7136b --- /dev/null +++ b/test/mjsunit/regress/regress-crbug-934166.js @@ -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(); + } +} diff --git a/test/mjsunit/switch.js b/test/mjsunit/switch.js index 4722e9e5d8..4b27789ad9 100644 --- a/test/mjsunit/switch.js +++ b/test/mjsunit/switch.js @@ -515,3 +515,13 @@ test_switches(true); } assertEquals(1, i); })(); + +assertThrows(function() { + function f(){} + // The f()-- unconditionally throws a ReferenceError. + switch(f()--) { + // This label is dead. + default: + break; + } +}, ReferenceError); diff --git a/test/unittests/interpreter/bytecode-array-builder-unittest.cc b/test/unittests/interpreter/bytecode-array-builder-unittest.cc index 3df844c4bd..e928fd7bc6 100644 --- a/test/unittests/interpreter/bytecode-array-builder-unittest.cc +++ b/test/unittests/interpreter/bytecode-array-builder-unittest.cc @@ -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 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 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 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 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 diff --git a/test/unittests/interpreter/bytecode-array-writer-unittest.cc b/test/unittests/interpreter/bytecode-array-writer-unittest.cc index 7c01228936..35cc3b3c28 100644 --- a/test/unittests/interpreter/bytecode-array-writer-unittest.cc +++ b/test/unittests/interpreter/bytecode-array-writer-unittest.cc @@ -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* 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});