Reland "[ignition] Skip binding dead labels"
This is a reland of 35269f77f8
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 <leszeks@chromium.org>
> Reviewed-by: Ross McIlroy <rmcilroy@chromium.org>
> 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 <leszeks@chromium.org>
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59948}
This commit is contained in:
parent
8ee20f5e61
commit
c7ebc5814f
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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_;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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_;
|
||||
|
@ -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],
|
||||
]
|
||||
|
||||
|
@ -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: [
|
||||
]
|
||||
|
@ -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],
|
||||
]
|
||||
|
||||
---
|
||||
|
@ -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],
|
||||
]
|
||||
|
@ -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],
|
||||
]
|
||||
|
@ -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: [
|
||||
]
|
||||
|
@ -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],
|
||||
]
|
||||
|
||||
|
@ -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],
|
||||
]
|
||||
|
||||
---
|
||||
|
@ -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);
|
||||
|
18
test/mjsunit/regress/regress-crbug-934166.js
Normal file
18
test/mjsunit/regress/regress-crbug-934166.js
Normal 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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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});
|
||||
|
Loading…
Reference in New Issue
Block a user