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:
Leszek Swirski 2019-02-28 14:34:26 +01:00 committed by Commit Bot
parent 8ee20f5e61
commit c7ebc5814f
22 changed files with 398 additions and 374 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -70,7 +70,6 @@ void LoopBuilder::JumpToHeader(int loop_depth) {
int level = Min(loop_depth, AbstractCode::kMaxLoopNestingMarker - 1);
// Loop must have closed form, i.e. all loop elements are within the loop,
// the loop header precedes the body and next elements in the loop.
DCHECK(loop_header_.is_bound());
builder()->JumpLoop(&loop_header_, level);
}
@ -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) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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);

View File

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

View File

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