Commit f133bc8a authored by Leszek Swirski's avatar Leszek Swirski Committed by Commit Bot

[ignition] Introduce Switch bytecode for generators

Introduce a new SwitchSmiTable bytecode for generators, which does a
table lookup for the accumulator value in a jump table stored in the
constant array pool. This removes the if-else chains at resumable
function/loop headers.

As a drive-by, add a scoped environment saving struct to the bytecode
graph builder.

Bug: v8:6351
Bug: v8:6366
Change-Id: I63be15a8b599d6684c7df19dedb8860562678fb0
Reviewed-on: https://chromium-review.googlesource.com/500271
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#45314}
parent ae421616
......@@ -1649,6 +1649,7 @@ v8_source_set("v8_base") {
"src/interpreter/bytecode-flags.h",
"src/interpreter/bytecode-generator.cc",
"src/interpreter/bytecode-generator.h",
"src/interpreter/bytecode-jump-table.h",
"src/interpreter/bytecode-label.cc",
"src/interpreter/bytecode-label.h",
"src/interpreter/bytecode-node.cc",
......
......@@ -2663,6 +2663,14 @@ class FunctionLiteral final : public Expression {
bool AllowsLazyCompilation();
bool CanSuspend() {
if (suspend_count() > 0) {
DCHECK(IsResumableFunction(kind()));
return true;
}
return false;
}
Handle<String> debug_name() const {
if (raw_name_ != NULL && !raw_name_->IsEmpty()) {
return raw_name_->string();
......
......@@ -188,6 +188,10 @@ void UpdateOutLiveness(Bytecode bytecode, BytecodeLivenessState& out_liveness,
if (Bytecodes::IsForwardJump(bytecode)) {
int target_offset = accessor.GetJumpTargetOffset();
out_liveness.Union(*liveness_map.GetInLiveness(target_offset));
} else if (Bytecodes::IsSwitch(bytecode)) {
for (const auto& entry : accessor.GetJumpTableTargetOffsets()) {
out_liveness.Union(*liveness_map.GetInLiveness(entry.target_offset));
}
}
// Update from next bytecode (unless there isn't one or this is an
......
......@@ -108,6 +108,18 @@ class BytecodeGraphBuilder::Environment : public ZoneObject {
int accumulator_base_;
};
// A helper for creating a temporary sub-environment for simple branches.
struct BytecodeGraphBuilder::SubEnvironment final {
public:
explicit SubEnvironment(BytecodeGraphBuilder* builder)
: builder_(builder), parent_(builder->environment()->Copy()) {}
~SubEnvironment() { builder_->set_environment(parent_); }
private:
BytecodeGraphBuilder* builder_;
BytecodeGraphBuilder::Environment* parent_;
};
// Issues:
// - Scopes - intimately tied to AST. Need to eval what is needed.
......@@ -870,9 +882,10 @@ BytecodeGraphBuilder::Environment* BytecodeGraphBuilder::CheckContextExtensions(
jsgraph()->TheHoleConstant());
NewBranch(check_no_extension);
Environment* true_environment = environment()->Copy();
{
SubEnvironment sub_environment(this);
NewIfFalse();
// If there is an extension, merge into the slow path.
if (slow_environment == nullptr) {
......@@ -883,12 +896,9 @@ BytecodeGraphBuilder::Environment* BytecodeGraphBuilder::CheckContextExtensions(
}
}
{
set_environment(true_environment);
NewIfTrue();
// Do nothing on if there is no extension, eventually falling through to
// the fast path.
}
NewIfTrue();
// Do nothing on if there is no extension, eventually falling through to
// the fast path.
}
// The depth can be zero, in which case no slow-path checks are built, and the
......@@ -2155,6 +2165,26 @@ void BytecodeGraphBuilder::VisitJumpIfNotUndefinedConstant() {
void BytecodeGraphBuilder::VisitJumpLoop() { BuildJump(); }
void BytecodeGraphBuilder::VisitSwitchOnSmiNoFeedback() {
PrepareEagerCheckpoint();
Node* acc = environment()->LookupAccumulator();
for (const auto& entry : bytecode_iterator().GetJumpTableTargetOffsets()) {
// TODO(leszeks): This should be a switch, but under OSR we fail to type the
// input correctly so we have to do a JS strict equal instead.
NewBranch(
NewNode(javascript()->StrictEqual(CompareOperationHint::kSignedSmall),
acc, jsgraph()->SmiConstant(entry.case_value)));
{
SubEnvironment sub_environment(this);
NewIfTrue();
MergeIntoSuccessorEnvironment(entry.target_offset);
}
NewIfFalse();
}
}
void BytecodeGraphBuilder::VisitStackCheck() {
PrepareEagerCheckpoint();
Node* node = NewNode(javascript()->StackCheck());
......@@ -2400,19 +2430,21 @@ void BytecodeGraphBuilder::BuildJump() {
void BytecodeGraphBuilder::BuildJumpIf(Node* condition) {
NewBranch(condition);
Environment* if_false_environment = environment()->Copy();
NewIfTrue();
MergeIntoSuccessorEnvironment(bytecode_iterator().GetJumpTargetOffset());
set_environment(if_false_environment);
{
SubEnvironment sub_environment(this);
NewIfTrue();
MergeIntoSuccessorEnvironment(bytecode_iterator().GetJumpTargetOffset());
}
NewIfFalse();
}
void BytecodeGraphBuilder::BuildJumpIfNot(Node* condition) {
NewBranch(condition);
Environment* if_true_environment = environment()->Copy();
NewIfFalse();
MergeIntoSuccessorEnvironment(bytecode_iterator().GetJumpTargetOffset());
set_environment(if_true_environment);
{
SubEnvironment sub_environment(this);
NewIfFalse();
MergeIntoSuccessorEnvironment(bytecode_iterator().GetJumpTargetOffset());
}
NewIfTrue();
}
......@@ -2432,24 +2464,26 @@ void BytecodeGraphBuilder::BuildJumpIfNotEqual(Node* comperand) {
void BytecodeGraphBuilder::BuildJumpIfFalse() {
NewBranch(environment()->LookupAccumulator());
Environment* if_true_environment = environment()->Copy();
environment()->BindAccumulator(jsgraph()->FalseConstant());
NewIfFalse();
MergeIntoSuccessorEnvironment(bytecode_iterator().GetJumpTargetOffset());
if_true_environment->BindAccumulator(jsgraph()->TrueConstant());
set_environment(if_true_environment);
{
SubEnvironment sub_environment(this);
NewIfFalse();
environment()->BindAccumulator(jsgraph()->FalseConstant());
MergeIntoSuccessorEnvironment(bytecode_iterator().GetJumpTargetOffset());
}
NewIfTrue();
environment()->BindAccumulator(jsgraph()->TrueConstant());
}
void BytecodeGraphBuilder::BuildJumpIfTrue() {
NewBranch(environment()->LookupAccumulator());
Environment* if_false_environment = environment()->Copy();
environment()->BindAccumulator(jsgraph()->TrueConstant());
NewIfTrue();
MergeIntoSuccessorEnvironment(bytecode_iterator().GetJumpTargetOffset());
if_false_environment->BindAccumulator(jsgraph()->FalseConstant());
set_environment(if_false_environment);
{
SubEnvironment sub_environment(this);
NewIfTrue();
environment()->BindAccumulator(jsgraph()->TrueConstant());
MergeIntoSuccessorEnvironment(bytecode_iterator().GetJumpTargetOffset());
}
NewIfFalse();
environment()->BindAccumulator(jsgraph()->FalseConstant());
}
void BytecodeGraphBuilder::BuildJumpIfToBooleanTrue() {
......
......@@ -39,6 +39,7 @@ class BytecodeGraphBuilder {
private:
class Environment;
struct SubEnvironment;
void VisitBytecodes(bool stack_check);
......
......@@ -2835,6 +2835,8 @@ class RepresentationSelector {
case IrOpcode::kIfException:
case IrOpcode::kIfTrue:
case IrOpcode::kIfFalse:
case IrOpcode::kIfValue:
case IrOpcode::kIfDefault:
case IrOpcode::kDeoptimize:
case IrOpcode::kEffectPhi:
case IrOpcode::kTerminate:
......
......@@ -168,11 +168,14 @@ Runtime::FunctionId BytecodeArrayAccessor::GetIntrinsicIdOperand(
static_cast<IntrinsicsHelper::IntrinsicId>(raw_id));
}
Handle<Object> BytecodeArrayAccessor::GetConstantAtIndex(int index) const {
return FixedArray::get(bytecode_array()->constant_pool(), index,
bytecode_array()->GetIsolate());
}
Handle<Object> BytecodeArrayAccessor::GetConstantForIndexOperand(
int operand_index) const {
return FixedArray::get(bytecode_array()->constant_pool(),
GetIndexOperand(operand_index),
bytecode_array()->GetIsolate());
return GetConstantAtIndex(GetIndexOperand(operand_index));
}
int BytecodeArrayAccessor::GetJumpTargetOffset() const {
......@@ -182,16 +185,31 @@ int BytecodeArrayAccessor::GetJumpTargetOffset() const {
if (bytecode == Bytecode::kJumpLoop) {
relative_offset = -relative_offset;
}
return current_offset() + relative_offset + current_prefix_offset();
return GetAbsoluteOffset(relative_offset);
} else if (interpreter::Bytecodes::IsJumpConstant(bytecode)) {
Smi* smi = Smi::cast(*GetConstantForIndexOperand(0));
return current_offset() + smi->value() + current_prefix_offset();
return GetAbsoluteOffset(smi->value());
} else {
UNREACHABLE();
return kMinInt;
}
}
JumpTableTargetOffsets BytecodeArrayAccessor::GetJumpTableTargetOffsets()
const {
DCHECK_EQ(current_bytecode(), Bytecode::kSwitchOnSmiNoFeedback);
uint32_t table_start = GetIndexOperand(0);
uint32_t table_size = GetUnsignedImmediateOperand(1);
int32_t case_value_base = GetImmediateOperand(2);
return JumpTableTargetOffsets(this, table_start, table_size, case_value_base);
}
int BytecodeArrayAccessor::GetAbsoluteOffset(int relative_offset) const {
return current_offset() + relative_offset + current_prefix_offset();
}
bool BytecodeArrayAccessor::OffsetWithinBytecode(int offset) const {
return current_offset() <= offset &&
offset < current_offset() + current_bytecode_size();
......@@ -203,6 +221,68 @@ std::ostream& BytecodeArrayAccessor::PrintTo(std::ostream& os) const {
bytecode_array()->parameter_count());
}
JumpTableTargetOffsets::JumpTableTargetOffsets(
const BytecodeArrayAccessor* accessor, int table_start, int table_size,
int case_value_base)
: accessor_(accessor),
table_start_(table_start),
table_size_(table_size),
case_value_base_(case_value_base) {}
JumpTableTargetOffsets::iterator JumpTableTargetOffsets::begin() const {
return iterator(case_value_base_, table_start_, table_start_ + table_size_,
accessor_);
}
JumpTableTargetOffsets::iterator JumpTableTargetOffsets::end() const {
return iterator(case_value_base_ + table_size_, table_start_ + table_size_,
table_start_ + table_size_, accessor_);
}
JumpTableTargetOffsets::iterator::iterator(
int case_value, int table_offset, int table_end,
const BytecodeArrayAccessor* accessor)
: accessor_(accessor),
index_(case_value),
table_offset_(table_offset),
table_end_(table_end) {
UpdateAndAdvanceToValid();
}
JumpTableTargetOffset JumpTableTargetOffsets::iterator::operator*() {
DCHECK_LT(table_offset_, table_end_);
DCHECK(current_->IsSmi());
return {index_, accessor_->GetAbsoluteOffset(Smi::cast(*current_)->value())};
}
JumpTableTargetOffsets::iterator& JumpTableTargetOffsets::iterator::
operator++() {
DCHECK_LT(table_offset_, table_end_);
++table_offset_;
++index_;
UpdateAndAdvanceToValid();
return *this;
}
bool JumpTableTargetOffsets::iterator::operator!=(
const JumpTableTargetOffsets::iterator& other) {
DCHECK_EQ(accessor_, other.accessor_);
DCHECK_EQ(table_end_, other.table_end_);
DCHECK_EQ(index_ - other.index_, table_offset_ - other.table_offset_);
return index_ != other.index_;
}
void JumpTableTargetOffsets::iterator::UpdateAndAdvanceToValid() {
if (table_offset_ >= table_end_) return;
current_ = accessor_->GetConstantAtIndex(table_offset_);
Isolate* isolate = accessor_->bytecode_array()->GetIsolate();
while (current_->IsTheHole(isolate)) {
++table_offset_;
++index_;
current_ = accessor_->GetConstantAtIndex(table_offset_);
}
}
} // namespace interpreter
} // namespace internal
} // namespace v8
......@@ -16,6 +16,48 @@ namespace v8 {
namespace internal {
namespace interpreter {
class BytecodeArrayAccessor;
struct JumpTableTargetOffset {
int case_value;
int target_offset;
};
class JumpTableTargetOffsets final {
public:
// Minimal iterator implementation for use in ranged-for.
class iterator final {
public:
iterator(int case_value, int table_offset, int table_end,
const BytecodeArrayAccessor* accessor);
JumpTableTargetOffset operator*();
iterator& operator++();
bool operator!=(const iterator& other);
private:
void UpdateAndAdvanceToValid();
const BytecodeArrayAccessor* accessor_;
Handle<Object> current_;
int index_;
int table_offset_;
int table_end_;
};
JumpTableTargetOffsets(const BytecodeArrayAccessor* accessor, int table_start,
int table_size, int case_value_base);
iterator begin() const;
iterator end() const;
private:
const BytecodeArrayAccessor* accessor_;
int table_start_;
int table_size_;
int case_value_base_;
};
class V8_EXPORT_PRIVATE BytecodeArrayAccessor {
public:
BytecodeArrayAccessor(Handle<BytecodeArray> bytecode_array,
......@@ -41,12 +83,21 @@ class V8_EXPORT_PRIVATE BytecodeArrayAccessor {
int GetRegisterOperandRange(int operand_index) const;
Runtime::FunctionId GetRuntimeIdOperand(int operand_index) const;
Runtime::FunctionId GetIntrinsicIdOperand(int operand_index) const;
Handle<Object> GetConstantAtIndex(int offset) const;
Handle<Object> GetConstantForIndexOperand(int operand_index) const;
// Returns the absolute offset of the branch target at the current
// bytecode. It is an error to call this method if the bytecode is
// not for a jump or conditional jump.
// Returns the absolute offset of the branch target at the current bytecode.
// It is an error to call this method if the bytecode is not for a jump or
// conditional jump.
int GetJumpTargetOffset() const;
// Returns an iterator over the absolute offsets of the targets of the current
// switch bytecode's jump table. It is an error to call this method if the
// bytecode is not a switch.
JumpTableTargetOffsets GetJumpTableTargetOffsets() const;
// Returns the absolute offset of the bytecode at the given relative offset
// from the current bytecode.
int GetAbsoluteOffset(int relative_offset) const;
bool OffsetWithinBytecode(int offset) const;
......
......@@ -6,6 +6,7 @@
#include "src/globals.h"
#include "src/interpreter/bytecode-array-writer.h"
#include "src/interpreter/bytecode-jump-table.h"
#include "src/interpreter/bytecode-label.h"
#include "src/interpreter/bytecode-node.h"
#include "src/interpreter/bytecode-register-optimizer.h"
......@@ -162,6 +163,12 @@ void BytecodeArrayBuilder::WriteJump(BytecodeNode* node, BytecodeLabel* label) {
bytecode_array_writer_.WriteJump(node, label);
}
void BytecodeArrayBuilder::WriteSwitch(BytecodeNode* node,
BytecodeJumpTable* jump_table) {
AttachOrEmitDeferredSourceInfo(node);
bytecode_array_writer_.WriteSwitch(node, jump_table);
}
void BytecodeArrayBuilder::OutputLdarRaw(Register reg) {
uint32_t operand = static_cast<uint32_t>(reg.ToOperand());
BytecodeNode node(BytecodeNode::Ldar(BytecodeSourceInfo(), operand));
......@@ -295,8 +302,9 @@ class BytecodeNodeBuilder {
public:
template <typename... Operands>
INLINE(static BytecodeNode Make(BytecodeArrayBuilder* builder,
BytecodeSourceInfo source_info,
Operands... operands)) {
static_assert(sizeof...(Operands) <= Bytecodes::kMaxOperands,
"too many operands for bytecode");
builder->PrepareToOutputBytecode<bytecode, accumulator_use>();
// The "OperandHelper<operand_types>::Convert(builder, operands)..." will
// expand both the OperandType... and Operands... parameter packs e.g. for:
......@@ -306,37 +314,45 @@ class BytecodeNodeBuilder {
// OperandHelper<OperandType::kReg>::Convert(builder, reg),
// OperandHelper<OperandType::kImm>::Convert(builder, immediate),
return BytecodeNode::Create<bytecode, accumulator_use, operand_types...>(
source_info,
builder->CurrentSourcePosition(bytecode),
OperandHelper<operand_types>::Convert(builder, operands)...);
}
};
#define DEFINE_BYTECODE_OUTPUT(name, ...) \
template <typename... Operands> \
void BytecodeArrayBuilder::Output##name(Operands... operands) { \
static_assert(sizeof...(Operands) <= Bytecodes::kMaxOperands, \
"too many operands for bytecode"); \
BytecodeNode node( \
BytecodeNodeBuilder<Bytecode::k##name, __VA_ARGS__>::Make< \
Operands...>(this, CurrentSourcePosition(Bytecode::k##name), \
operands...)); \
Write(&node); \
} \
\
template <typename... Operands> \
void BytecodeArrayBuilder::Output##name(BytecodeLabel* label, \
Operands... operands) { \
DCHECK(Bytecodes::IsJump(Bytecode::k##name)); \
BytecodeNode node( \
BytecodeNodeBuilder<Bytecode::k##name, __VA_ARGS__>::Make< \
Operands...>(this, CurrentSourcePosition(Bytecode::k##name), \
operands...)); \
WriteJump(&node, label); \
LeaveBasicBlock(); \
#define DEFINE_BYTECODE_OUTPUT(name, ...) \
template <typename... Operands> \
BytecodeNode BytecodeArrayBuilder::Create##name##Node( \
Operands... operands) { \
return BytecodeNodeBuilder<Bytecode::k##name, __VA_ARGS__>::Make( \
this, operands...); \
} \
\
template <typename... Operands> \
void BytecodeArrayBuilder::Output##name(Operands... operands) { \
BytecodeNode node(Create##name##Node(operands...)); \
Write(&node); \
} \
\
template <typename... Operands> \
void BytecodeArrayBuilder::Output##name(BytecodeLabel* label, \
Operands... operands) { \
DCHECK(Bytecodes::IsJump(Bytecode::k##name)); \
BytecodeNode node(Create##name##Node(operands...)); \
WriteJump(&node, label); \
LeaveBasicBlock(); \
}
BYTECODE_LIST(DEFINE_BYTECODE_OUTPUT)
#undef DEFINE_BYTECODE_OUTPUT
void BytecodeArrayBuilder::OutputSwitchOnSmiNoFeedback(
BytecodeJumpTable* jump_table) {
BytecodeNode node(CreateSwitchOnSmiNoFeedbackNode(
jump_table->constant_pool_index(), jump_table->size(),
jump_table->case_value_base()));
WriteSwitch(&node, jump_table);
LeaveBasicBlock();
}
BytecodeArrayBuilder& BytecodeArrayBuilder::BinaryOperation(Token::Value op,
Register reg,
int feedback_slot) {
......@@ -1008,6 +1024,16 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(const BytecodeLabel& target,
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(BytecodeJumpTable* jump_table,
int case_value) {
// Flush the register optimizer when binding a jump table entry to ensure
// all expected registers are valid when jumping to this location.
if (register_optimizer_) register_optimizer_->Flush();
bytecode_array_writer_.BindJumpTableEntry(jump_table, case_value);
LeaveBasicBlock();
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Jump(BytecodeLabel* label) {
DCHECK(!label->is_bound());
OutputJump(label, 0);
......@@ -1122,6 +1148,12 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::JumpLoop(BytecodeLabel* label,
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::SwitchOnSmiNoFeedback(
BytecodeJumpTable* jump_table) {
OutputSwitchOnSmiNoFeedback(jump_table);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::StackCheck(int position) {
if (position != kNoSourcePosition) {
// We need to attach a non-breakable source position to a stack
......@@ -1387,6 +1419,16 @@ size_t BytecodeArrayBuilder::GetConstantPoolEntry(const Scope* scope) {
SINGLETON_CONSTANT_ENTRY_TYPES(ENTRY_GETTER)
#undef ENTRY_GETTER
BytecodeJumpTable* BytecodeArrayBuilder::AllocateJumpTable(
int size, int case_value_base) {
DCHECK_GT(size, 0);
size_t constant_pool_index = constant_array_builder()->InsertJumpTable(size);
return new (zone())
BytecodeJumpTable(constant_pool_index, size, case_value_base, zone());
}
size_t BytecodeArrayBuilder::AllocateDeferredConstantPoolEntry() {
return constant_array_builder()->InsertDeferred();
}
......
......@@ -28,6 +28,7 @@ namespace interpreter {
class BytecodeLabel;
class BytecodeNode;
class BytecodeRegisterOptimizer;
class BytecodeJumpTable;
class Register;
class V8_EXPORT_PRIVATE BytecodeArrayBuilder final
......@@ -359,6 +360,7 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final
// Flow Control.
BytecodeArrayBuilder& Bind(BytecodeLabel* label);
BytecodeArrayBuilder& Bind(const BytecodeLabel& target, BytecodeLabel* label);
BytecodeArrayBuilder& Bind(BytecodeJumpTable* jump_table, int case_value);
BytecodeArrayBuilder& Jump(BytecodeLabel* label);
BytecodeArrayBuilder& JumpLoop(BytecodeLabel* label, int loop_depth);
......@@ -376,6 +378,8 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final
BytecodeArrayBuilder& JumpIfNotNil(BytecodeLabel* label, Token::Value op,
NilValue nil);
BytecodeArrayBuilder& SwitchOnSmiNoFeedback(BytecodeJumpTable* jump_table);
BytecodeArrayBuilder& StackCheck(int position);
// Sets the pending message to the value in the accumulator, and returns the
......@@ -413,6 +417,10 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final
// entry, so that it can be referenced by above exception handling support.
int NewHandlerEntry() { return handler_table_builder()->NewHandlerEntry(); }
// Allocates a new jump table of given |size| and |case_value_base| in the
// constant pool.
BytecodeJumpTable* AllocateJumpTable(int size, int case_value_base);
// Gets a constant pool entry.
size_t GetConstantPoolEntry(const AstRawString* raw_string);
size_t GetConstantPoolEntry(const AstValue* heap_number);
......@@ -483,14 +491,18 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final
// Returns the current source position for the given |bytecode|.
INLINE(BytecodeSourceInfo CurrentSourcePosition(Bytecode bytecode));
#define DECLARE_BYTECODE_OUTPUT(Name, ...) \
template <typename... Operands> \
INLINE(void Output##Name(Operands... operands)); \
template <typename... Operands> \
#define DECLARE_BYTECODE_OUTPUT(Name, ...) \
template <typename... Operands> \
INLINE(BytecodeNode Create##Name##Node(Operands... operands)); \
template <typename... Operands> \
INLINE(void Output##Name(Operands... operands)); \
template <typename... Operands> \
INLINE(void Output##Name(BytecodeLabel* label, Operands... operands));
BYTECODE_LIST(DECLARE_BYTECODE_OUTPUT)
#undef DECLARE_OPERAND_TYPE_INFO
INLINE(void OutputSwitchOnSmiNoFeedback(BytecodeJumpTable* jump_table));
bool RegisterIsValid(Register reg) const;
bool RegisterListIsValid(RegisterList reg_list) const;
......@@ -507,6 +519,7 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final
// Write bytecode to bytecode array.
void Write(BytecodeNode* node);
void WriteJump(BytecodeNode* node, BytecodeLabel* label);
void WriteSwitch(BytecodeNode* node, BytecodeJumpTable* label);
// Not implemented as the illegal bytecode is used inside internally
// to indicate a bytecode field is not valid or an error has occured
......
......@@ -5,6 +5,7 @@
#include "src/interpreter/bytecode-array-writer.h"
#include "src/api.h"
#include "src/interpreter/bytecode-jump-table.h"
#include "src/interpreter/bytecode-label.h"
#include "src/interpreter/bytecode-node.h"
#include "src/interpreter/bytecode-register.h"
......@@ -79,6 +80,20 @@ void BytecodeArrayWriter::WriteJump(BytecodeNode* node, BytecodeLabel* label) {
EmitJump(node, label);
}
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());
UpdateSourcePositionTable(node);
EmitSwitch(node, jump_table);
}
void BytecodeArrayWriter::BindLabel(BytecodeLabel* label) {
size_t current_offset = bytecodes()->size();
if (label->is_forward_target()) {
......@@ -106,6 +121,22 @@ void BytecodeArrayWriter::BindLabel(const BytecodeLabel& target,
// changed here.
}
void BytecodeArrayWriter::BindJumpTableEntry(BytecodeJumpTable* jump_table,
int case_value) {
DCHECK(!jump_table->is_bound(case_value));
size_t current_offset = bytecodes()->size();
size_t relative_jump = current_offset - jump_table->switch_bytecode_offset();
constant_array_builder()->SetJumpTableSmi(
jump_table->ConstantPoolEntryFor(case_value),
Smi::FromInt(static_cast<int>(relative_jump)));
jump_table->mark_bound(case_value);
InvalidateLastBytecode();
exit_seen_in_block_ = false; // Starting a new basic block.
}
void BytecodeArrayWriter::UpdateSourcePositionTable(
const BytecodeNode* const node) {
int bytecode_offset = static_cast<int>(bytecodes()->size());
......@@ -387,6 +418,16 @@ void BytecodeArrayWriter::EmitJump(BytecodeNode* node, BytecodeLabel* label) {
EmitBytecode(node);
}
void BytecodeArrayWriter::EmitSwitch(BytecodeNode* node,
BytecodeJumpTable* jump_table) {
DCHECK(Bytecodes::IsSwitch(node->bytecode()));
size_t current_offset = bytecodes()->size();
jump_table->set_switch_bytecode_offset(current_offset);
EmitBytecode(node);
}
} // namespace interpreter
} // namespace internal
} // namespace v8
......@@ -19,6 +19,7 @@ namespace interpreter {
class BytecodeLabel;
class BytecodeNode;
class BytecodeJumpTable;
class ConstantArrayBuilder;
// Class for emitting bytecode as the final stage of the bytecode
......@@ -31,8 +32,10 @@ class V8_EXPORT_PRIVATE BytecodeArrayWriter final {
void Write(BytecodeNode* node);
void WriteJump(BytecodeNode* node, BytecodeLabel* label);
void WriteSwitch(BytecodeNode* node, BytecodeJumpTable* jump_table);
void BindLabel(BytecodeLabel* label);
void BindLabel(const BytecodeLabel& target, BytecodeLabel* label);
void BindJumpTableEntry(BytecodeJumpTable* jump_table, int case_value);
Handle<BytecodeArray> ToBytecodeArray(Isolate* isolate, int register_count,
int parameter_count,
Handle<FixedArray> handler_table);
......@@ -61,6 +64,7 @@ class V8_EXPORT_PRIVATE BytecodeArrayWriter final {
void EmitBytecode(const BytecodeNode* const node);
void EmitJump(BytecodeNode* node, BytecodeLabel* label);
void EmitSwitch(BytecodeNode* node, BytecodeJumpTable* jump_table);
void UpdateSourcePositionTable(const BytecodeNode* const node);
void UpdateExitSeenInBlock(Bytecode bytecode);
......
This diff is collapsed.
......@@ -21,6 +21,7 @@ namespace interpreter {
class GlobalDeclarationsBuilder;
class LoopBuilder;
class BytecodeJumpTable;
class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
public:
......@@ -133,7 +134,7 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
void BuildNewLocalCatchContext(Scope* scope);
void BuildNewLocalWithContext(Scope* scope);
void VisitGeneratorPrologue();
void BuildGeneratorPrologue();
void VisitArgumentsObject(Variable* variable);
void VisitRestArgumentsArray(Variable* rest);
......@@ -239,7 +240,7 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
ContextScope* execution_context_;
ExpressionResultScope* execution_result_;
ZoneVector<BytecodeLabel> generator_resume_points_;
BytecodeJumpTable* generator_jump_table_;
Register generator_state_;
int loop_depth_;
};
......
// Copyright 2017 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.
#ifndef V8_INTERPRETER_BYTECODE_JUMP_TABLE_H_
#define V8_INTERPRETER_BYTECODE_JUMP_TABLE_H_
#include "src/bit-vector.h"
#include "src/zone/zone.h"
namespace v8 {
namespace internal {
namespace interpreter {
class ConstantArrayBuilder;
// A jump table for a set of targets in a bytecode array. When an entry in the
// table is bound, it represents a known position in the bytecode array. If no
// entries match, the switch falls through.
class V8_EXPORT_PRIVATE BytecodeJumpTable final : public ZoneObject {
public:
// Constructs a new BytecodeJumpTable starting at |constant_pool_index|, with
// the given |size|, where the case values of the table start at
// |case_value_base|.
BytecodeJumpTable(size_t constant_pool_index, int size, int case_value_base,
Zone* zone)
:
#ifdef DEBUG
bound_(size, zone),
#endif
constant_pool_index_(constant_pool_index),
switch_bytecode_offset_(kInvalidOffset),
size_(size),
case_value_base_(case_value_base) {
}
size_t constant_pool_index() const { return constant_pool_index_; }
size_t switch_bytecode_offset() const { return switch_bytecode_offset_; }
int case_value_base() const { return case_value_base_; }
int size() const { return size_; }
#ifdef DEBUG
bool is_bound(int case_value) const {
DCHECK_GE(case_value, case_value_base_);
DCHECK_LT(case_value, case_value_base_ + size());
return bound_.Contains(case_value - case_value_base_);
}
#endif
size_t ConstantPoolEntryFor(int case_value) {
DCHECK_GE(case_value, case_value_base_);
return constant_pool_index_ + case_value - case_value_base_;
}
private:
static const size_t kInvalidIndex = static_cast<size_t>(-1);
static const size_t kInvalidOffset = static_cast<size_t>(-1);
void mark_bound(int case_value) {
#ifdef DEBUG
DCHECK_GE(case_value, case_value_base_);
DCHECK_LT(case_value, case_value_base_ + size());
bound_.Add(case_value - case_value_base_);
#endif
}
void set_switch_bytecode_offset(size_t offset) {
DCHECK_EQ(switch_bytecode_offset_, kInvalidOffset);
switch_bytecode_offset_ = offset;
}
#ifdef DEBUG
// This bit vector is only used for DCHECKS, so only store the field in debug
// builds.
BitVector bound_;
#endif
size_t constant_pool_index_;
size_t switch_bytecode_offset_;
int size_;
int case_value_base_;
friend class BytecodeArrayWriter;
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_BYTECODE_JUMP_TABLE_H_
......@@ -64,12 +64,14 @@ class V8_EXPORT_PRIVATE BytecodeRegisterOptimizer final
// Prepares for |bytecode|.
template <Bytecode bytecode, AccumulatorUse accumulator_use>
INLINE(void PrepareForBytecode()) {
if (Bytecodes::IsJump(bytecode) || bytecode == Bytecode::kDebugger ||
if (Bytecodes::IsJump(bytecode) || Bytecodes::IsSwitch(bytecode) ||
bytecode == Bytecode::kDebugger ||
bytecode == Bytecode::kSuspendGenerator) {
// All state must be flushed before emitting
// - a jump bytecode (as the register equivalents at the jump target
// aren't
// known.
// aren't known)
// - a switch bytecode (as the register equivalents at the switch targets
// aren't known)
// - a call to the debugger (as it can manipulate locals and parameters),
// - a generator suspend (as this involves saving all registers).
Flush();
......
......@@ -284,6 +284,10 @@ namespace interpreter {
V(JumpIfJSReceiver, AccumulatorUse::kRead, OperandType::kUImm) \
V(JumpIfNotHole, AccumulatorUse::kRead, OperandType::kUImm) \
\
/* Smi-table lookup for switch statements */ \
V(SwitchOnSmiNoFeedback, AccumulatorUse::kRead, OperandType::kIdx, \
OperandType::kUImm, OperandType::kImm) \
\
/* Complex flow control For..in */ \
V(ForInPrepare, AccumulatorUse::kNone, OperandType::kReg, \
OperandType::kRegOutTriple) \
......@@ -611,6 +615,11 @@ class V8_EXPORT_PRIVATE Bytecodes final {
return IsJump(bytecode) && !IsJumpIfToBoolean(bytecode);
}
// Returns true if the bytecode is a switch.
static constexpr bool IsSwitch(Bytecode bytecode) {
return bytecode == Bytecode::kSwitchOnSmiNoFeedback;
}
// Returns true if |bytecode| has no effects. These bytecodes only manipulate
// interpreter frame state and will never throw.
static constexpr bool IsWithoutExternalSideEffects(Bytecode bytecode) {
......
......@@ -38,11 +38,13 @@ void ConstantArrayBuilder::ConstantArraySlice::Unreserve() {
}
size_t ConstantArrayBuilder::ConstantArraySlice::Allocate(
ConstantArrayBuilder::Entry entry) {
DCHECK_GT(available(), 0u);
ConstantArrayBuilder::Entry entry, size_t count) {
DCHECK_GE(available(), count);
size_t index = constants_.size();
DCHECK_LT(index, capacity());
constants_.push_back(entry);
for (size_t i = 0; i < count; ++i) {
constants_.push_back(entry);
}
return index + start_index();
}
......@@ -65,7 +67,12 @@ void ConstantArrayBuilder::ConstantArraySlice::CheckAllElementsAreUnique(
Isolate* isolate) const {
std::set<Object*> elements;
for (const Entry& entry : constants_) {
// TODO(leszeks): Ignore jump tables because they have to be contiguous,
// so they can contain duplicates.
if (entry.IsJumpTableEntry()) continue;
Handle<Object> handle = entry.ToHandle(isolate);
if (elements.find(*handle) != elements.end()) {
std::ostringstream os;
os << "Duplicate constant found: " << Brief(*handle) << std::endl;
......@@ -220,9 +227,14 @@ SINGLETON_CONSTANT_ENTRY_TYPES(INSERT_ENTRY)
ConstantArrayBuilder::index_t ConstantArrayBuilder::AllocateIndex(
ConstantArrayBuilder::Entry entry) {
return AllocateIndexArray(entry, 1);
}
ConstantArrayBuilder::index_t ConstantArrayBuilder::AllocateIndexArray(
ConstantArrayBuilder::Entry entry, size_t count) {
for (size_t i = 0; i < arraysize(idx_slice_); ++i) {
if (idx_slice_[i]->available() > 0) {
return static_cast<index_t>(idx_slice_[i]->Allocate(entry));
if (idx_slice_[i]->available() >= count) {
return static_cast<index_t>(idx_slice_[i]->Allocate(entry, count));
}
}
UNREACHABLE();
......@@ -254,11 +266,24 @@ size_t ConstantArrayBuilder::InsertDeferred() {
return AllocateIndex(Entry::Deferred());
}
size_t ConstantArrayBuilder::InsertJumpTable(size_t size) {
return AllocateIndexArray(Entry::UninitializedJumpTableSmi(), size);
}
void ConstantArrayBuilder::SetDeferredAt(size_t index, Handle<Object> object) {
ConstantArraySlice* slice = IndexToSlice(index);
return slice->At(index).SetDeferred(object);
}
void ConstantArrayBuilder::SetJumpTableSmi(size_t index, Smi* smi) {
ConstantArraySlice* slice = IndexToSlice(index);
// Allow others to reuse these Smis, but insert using emplace to avoid
// overwriting existing values in the Smi map (which may have a smaller
// operand size).
smi_map_.emplace(smi, static_cast<index_t>(index));
return slice->At(index).SetJumpTableSmi(smi);
}
OperandSize ConstantArrayBuilder::CreateReservedEntry() {
for (size_t i = 0; i < arraysize(idx_slice_); ++i) {
if (idx_slice_[i]->available() > 0) {
......@@ -311,7 +336,11 @@ Handle<Object> ConstantArrayBuilder::Entry::ToHandle(Isolate* isolate) const {
case Tag::kHandle:
return handle_;
case Tag::kSmi:
case Tag::kJumpTableSmi:
return handle(smi_, isolate);
case Tag::kUninitializedJumpTableSmi:
// TODO(leszeks): There's probably a better value we could use here.
return isolate->factory()->the_hole_value();
case Tag::kRawString:
return raw_string_->string();
case Tag::kHeapNumber:
......
......@@ -70,9 +70,18 @@ class V8_EXPORT_PRIVATE ConstantArrayBuilder final BASE_EMBEDDED {
// SetDeferredAt().
size_t InsertDeferred();
// Inserts |size| consecutive empty entries and returns the array index
// associated with the first reservation. Each entry's Smi value can be
// inserted by calling SetJumpTableSmi().
size_t InsertJumpTable(size_t size);
// Sets the deferred value at |index| to |object|.
void SetDeferredAt(size_t index, Handle<Object> object);
// Sets the jump table entry at |index| to |smi|. Note that |index| is the
// constant pool index, not the switch case value.
void SetJumpTableSmi(size_t index, Smi* smi);
// Creates a reserved entry in the constant pool and returns
// the size of the operand that'll be required to hold the entry
// when committed.
......@@ -107,14 +116,29 @@ class V8_EXPORT_PRIVATE ConstantArrayBuilder final BASE_EMBEDDED {
static Entry Deferred() { return Entry(Tag::kDeferred); }
static Entry UninitializedJumpTableSmi() {
return Entry(Tag::kUninitializedJumpTableSmi);
}
bool IsDeferred() const { return tag_ == Tag::kDeferred; }
bool IsJumpTableEntry() const {
return tag_ == Tag::kUninitializedJumpTableSmi ||
tag_ == Tag::kJumpTableSmi;
}
void SetDeferred(Handle<Object> handle) {
DCHECK(tag_ == Tag::kDeferred);
tag_ = Tag::kHandle;
handle_ = handle;
}
void SetJumpTableSmi(Smi* smi) {
DCHECK(tag_ == Tag::kUninitializedJumpTableSmi);
tag_ = Tag::kJumpTableSmi;
smi_ = smi;
}
Handle<Object> ToHandle(Isolate* isolate) const;
private:
......@@ -135,6 +159,8 @@ class V8_EXPORT_PRIVATE ConstantArrayBuilder final BASE_EMBEDDED {
kRawString,
kHeapNumber,
kScope,
kUninitializedJumpTableSmi,
kJumpTableSmi,
#define ENTRY_TAG(NAME, ...) k##NAME,
SINGLETON_CONSTANT_ENTRY_TYPES(ENTRY_TAG)
#undef ENTRY_TAG
......@@ -142,6 +168,7 @@ class V8_EXPORT_PRIVATE ConstantArrayBuilder final BASE_EMBEDDED {
};
index_t AllocateIndex(Entry constant_entry);
index_t AllocateIndexArray(Entry constant_entry, size_t size);
index_t AllocateReservedEntry(Smi* value);
struct ConstantArraySlice final : public ZoneObject {
......@@ -149,7 +176,7 @@ class V8_EXPORT_PRIVATE ConstantArrayBuilder final BASE_EMBEDDED {
OperandSize operand_size);
void Reserve();
void Unreserve();
size_t Allocate(Entry entry);
size_t Allocate(Entry entry, size_t count = 1);
Entry& At(size_t index);
const Entry& At(size_t index) const;
......
......@@ -47,21 +47,40 @@ void BlockBuilder::EndBlock() {
LoopBuilder::~LoopBuilder() {
DCHECK(continue_labels_.empty() || continue_labels_.is_bound());
DCHECK(header_labels_.empty() || header_labels_.is_bound());
BindBreakTarget();
// Restore the parent jump table.
if (generator_jump_table_location_ != nullptr) {
*generator_jump_table_location_ = parent_generator_jump_table_;
}
}
void LoopBuilder::LoopHeader(ZoneVector<BytecodeLabel>* additional_labels) {
void LoopBuilder::LoopHeader() {
// Jumps from before the loop header into the loop violate ordering
// requirements of bytecode basic blocks. The only entry into a loop
// must be the loop header. Surely breaks is okay? Not if nested
// and misplaced between the headers.
DCHECK(break_labels_.empty() && continue_labels_.empty());
builder()->Bind(&loop_header_);
if (additional_labels != nullptr) {
for (auto& label : *additional_labels) {
builder()->Bind(&label);
}
}
void LoopBuilder::LoopHeaderInGenerator(
BytecodeJumpTable** generator_jump_table, int first_resume_id,
int resume_count) {
// Bind all the resume points that are inside the loop to be at the loop
// header.
for (int id = first_resume_id; id < first_resume_id + resume_count; ++id) {
builder()->Bind(*generator_jump_table, id);
}
// Create the loop header.
LoopHeader();
// Create a new jump table for after the loop header for only these
// resume points.
generator_jump_table_location_ = generator_jump_table;
parent_generator_jump_table_ = *generator_jump_table;
*generator_jump_table =
builder()->AllocateJumpTable(resume_count, first_resume_id);
}
void LoopBuilder::JumpToHeader(int loop_depth) {
......@@ -74,11 +93,6 @@ void LoopBuilder::JumpToHeader(int loop_depth) {
builder()->JumpLoop(&loop_header_, level);
}
void LoopBuilder::EndLoop() {
BindBreakTarget();
header_labels_.BindToLabel(builder(), loop_header_);
}
void LoopBuilder::BindContinueTarget() { continue_labels_.Bind(builder()); }
SwitchBuilder::~SwitchBuilder() {
......
......@@ -90,13 +90,15 @@ class V8_EXPORT_PRIVATE LoopBuilder final : public BreakableControlFlowBuilder {
explicit LoopBuilder(BytecodeArrayBuilder* builder)
: BreakableControlFlowBuilder(builder),
continue_labels_(builder->zone()),
header_labels_(builder->zone()) {}
generator_jump_table_location_(nullptr),
parent_generator_jump_table_(nullptr) {}
~LoopBuilder();
void LoopHeader(ZoneVector<BytecodeLabel>* additional_labels = nullptr);
void LoopHeader();
void LoopHeaderInGenerator(BytecodeJumpTable** parent_generator_jump_table,
int first_resume_id, int resume_count);
void JumpToHeader(int loop_depth);
void BindContinueTarget();
void EndLoop();
// This method is called when visiting continue statements in the AST.
// Inserts a jump to an unbound label that is patched when BindContinueTarget
......@@ -111,7 +113,13 @@ class V8_EXPORT_PRIVATE LoopBuilder final : public BreakableControlFlowBuilder {
// 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.
BytecodeLabels continue_labels_;
BytecodeLabels header_labels_;
// While we're in the loop, we want to have a different jump table for
// generator switch statements. We restore it at the end of the loop.
// TODO(leszeks): Storing a pointer to the BytecodeGenerator's jump table
// field is ugly, figure out a better way to do this.
BytecodeJumpTable** generator_jump_table_location_;
BytecodeJumpTable* parent_generator_jump_table_;
};
......
This diff is collapsed.
......@@ -14786,6 +14786,19 @@ void BytecodeArray::Disassemble(std::ostream& os) {
os << " (" << jump_target << " @ " << iterator.GetJumpTargetOffset()
<< ")";
}
if (interpreter::Bytecodes::IsSwitch(iterator.current_bytecode())) {
os << " {";
bool first_entry = true;
for (const auto& entry : iterator.GetJumpTableTargetOffsets()) {
if (first_entry) {
first_entry = false;
} else {
os << ",";
}
os << " " << entry.case_value << ": @" << entry.target_offset;
}
os << " }";
}
os << std::endl;
iterator.Advance();
}
......
......@@ -1052,6 +1052,7 @@
'interpreter/bytecode-register-optimizer.h',
'interpreter/bytecode-source-info.cc',
'interpreter/bytecode-source-info.h',
'interpreter/bytecode-jump-table.h',
'interpreter/bytecode-traits.h',
'interpreter/constant-array-builder.cc',
'interpreter/constant-array-builder.h',
......
......@@ -13,17 +13,15 @@ snippet: "
"
frame size: 12
parameter count: 1
bytecode array length: 193
bytecode array length: 192
bytecodes: [
B(Ldar), R(new_target),
B(JumpIfUndefined), U8(27),
B(JumpIfUndefined), U8(26),
B(CallRuntime), U16(Runtime::k_GeneratorGetContext), R(new_target), U8(1),
B(PushContext), R(2),
B(ResumeGenerator), R(new_target),
B(Star), R(1),
B(LdaZero),
B(TestEqualStrictNoFeedback), R(1),
B(JumpIfTrue), U8(50),
B(SwitchOnSmiNoFeedback), U8(0), U8(1), I8(0),
B(LdaSmi), I8(79),
B(Star), R(3),
B(CallRuntime), U16(Runtime::kAbort), R(3), U8(1),
......@@ -110,9 +108,10 @@ bytecodes: [
/* 16 S> */ B(Return),
]
constant pool: [
Smi [52],
]
handlers: [
[53, 135, 141],
[52, 134, 140],
]
---
......@@ -122,20 +121,15 @@ snippet: "
"
frame size: 12
parameter count: 1
bytecode array length: 283
bytecode array length: 276
bytecodes: [
B(Ldar), R(new_target),
B(JumpIfUndefined), U8(33),
B(JumpIfUndefined), U8(26),
B(CallRuntime), U16(Runtime::k_GeneratorGetContext), R(new_target), U8(1),
B(PushContext), R(2),
B(ResumeGenerator), R(new_target),
B(Star), R(1),
B(LdaZero),
B(TestEqualStrictNoFeedback), R(1),
B(JumpIfTrue), U8(56),
B(LdaSmi), I8(1),
B(TestEqualStrictNoFeedback), R(1),
B(JumpIfTrue), U8(124),
B(SwitchOnSmiNoFeedback), U8(0), U8(2), I8(0),
B(LdaSmi), I8(79),
B(Star), R(3),
B(CallRuntime), U16(Runtime::kAbort), R(3), U8(1),
......@@ -261,9 +255,11 @@ bytecodes: [
/* 25 S> */ B(Return),
]
constant pool: [
Smi [52],
Smi [126],
]
handlers: [
[59, 216, 222],
[52, 209, 215],
]
---
......@@ -273,20 +269,15 @@ snippet: "
"
frame size: 18
parameter count: 1
bytecode array length: 743
bytecode array length: 736
bytecodes: [
B(Ldar), R(new_target),
B(JumpIfUndefined), U8(33),
B(JumpIfUndefined), U8(26),
B(CallRuntime), U16(Runtime::k_GeneratorGetContext), R(new_target), U8(1),
B(PushContext), R(4),
B(ResumeGenerator), R(new_target),
B(Star), R(3),
B(LdaZero),
B(TestEqualStrictNoFeedback), R(3),
B(JumpIfTrue), U8(56),
B(LdaSmi), I8(1),
B(TestEqualStrictNoFeedback), R(3),
B(JumpIfTrue), U8(146),
B(SwitchOnSmiNoFeedback), U8(0), U8(2), I8(0),
B(LdaSmi), I8(79),
B(Star), R(5),
B(CallRuntime), U16(Runtime::kAbort), R(5), U8(1),
......@@ -327,11 +318,11 @@ bytecodes: [
B(Star), R(6),
B(LdaZero),
B(Star), R(5),
B(JumpConstant), U8(12),
B(JumpConstant), U8(15),
B(Ldar), R(10),
/* 11 E> */ B(Throw),
B(Ldar), R(closure),
B(CreateBlockContext), U8(0),
B(CreateBlockContext), U8(2),
B(PushContext), R(1),
B(LdaTheHole),
B(StaCurrentContextSlot), U8(4),
......@@ -339,26 +330,25 @@ bytecodes: [
B(StaContextSlot), R(1), U8(8), U8(0),
B(Mov), R(context), R(10),
B(Mov), R(context), R(11),
/* 30 S> */ B(CreateArrayLiteral), U8(1), U8(3), U8(17),
/* 30 S> */ B(CreateArrayLiteral), U8(3), U8(3), U8(17),
B(Star), R(12),
B(LdaNamedProperty), R(12), U8(2), U8(4),
B(LdaNamedProperty), R(12), U8(4), U8(4),
B(Star), R(13),
B(CallProperty0), R(13), R(12), U8(6),
B(JumpIfJSReceiver), U8(7),
B(CallRuntime), U16(Runtime::kThrowSymbolIteratorInvalid), R(0), U8(0),
/* 30 E> */ B(StaContextSlot), R(1), U8(6), U8(0),
B(Ldar), R(3),
B(SwitchOnSmiNoFeedback), U8(5), U8(1), I8(1),
B(LdaSmi), I8(-2),
B(TestEqualStrictNoFeedback), R(3),
B(JumpIfTrue), U8(17),
B(LdaSmi), I8(1),
B(TestEqualStrictNoFeedback), R(3),
B(JumpIfTrue), U8(134),
B(JumpIfTrue), U8(11),
B(LdaSmi), I8(79),
B(Star), R(12),
B(CallRuntime), U16(Runtime::kAbort), R(12), U8(1),
/* 27 S> */ B(LdaContextSlot), R(1), U8(6), U8(0),
B(Star), R(13),
B(LdaNamedProperty), R(13), U8(3), U8(10),
B(LdaNamedProperty), R(13), U8(6), U8(10),
B(Star), R(12),
/* 27 E> */ B(CallProperty0), R(12), R(13), U8(8),
/* 27 E> */ B(StaContextSlot), R(1), U8(7), U8(0),
......@@ -371,11 +361,11 @@ bytecodes: [
B(CallRuntime), U16(Runtime::kThrowIteratorResultNotAnObject), R(12), U8(1),
B(LdaContextSlot), R(1), U8(7), U8(0),
B(Star), R(12),
B(LdaNamedProperty), R(12), U8(4), U8(12),
B(LdaNamedProperty), R(12), U8(7), U8(12),
B(JumpIfToBooleanTrue), U8(144),
B(LdaContextSlot), R(1), U8(7), U8(0),
B(Star), R(12),
B(LdaNamedProperty), R(12), U8(5), U8(14),
B(LdaNamedProperty), R(12), U8(8), U8(14),
B(StaContextSlot), R(1), U8(9), U8(0),
B(LdaSmi), I8(2),
B(StaContextSlot), R(1), U8(8), U8(0),
......@@ -383,7 +373,7 @@ bytecodes: [
B(StaContextSlot), R(1), U8(5), U8(0),
/* 16 E> */ B(StackCheck),
B(Ldar), R(closure),
B(CreateBlockContext), U8(6),
B(CreateBlockContext), U8(9),
B(PushContext), R(2),
B(LdaTheHole),
B(StaCurrentContextSlot), U8(4),
......@@ -436,7 +426,7 @@ bytecodes: [
B(Jump), U8(44),
B(Star), R(12),
B(Ldar), R(closure),
B(CreateCatchContext), R(12), U8(7), U8(8),
B(CreateCatchContext), R(12), U8(10), U8(11),
B(PushContext), R(2),
B(Star), R(11),
B(LdaContextSlot), R(1), U8(8), U8(0),
......@@ -466,7 +456,7 @@ bytecodes: [
B(JumpIfTrue), U8(150),
B(LdaContextSlot), R(1), U8(6), U8(0),
B(Star), R(11),
B(LdaNamedProperty), R(11), U8(9), U8(18),
B(LdaNamedProperty), R(11), U8(12), U8(18),
B(StaContextSlot), R(1), U8(10), U8(0),
B(LdaContextSlot), R(1), U8(10), U8(0),
B(TestUndetectable),
......@@ -483,7 +473,7 @@ bytecodes: [
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(130),
B(Star), R(11),
B(LdaConstant), U8(10),
B(LdaConstant), U8(13),
B(Star), R(12),
B(CallRuntime), U16(Runtime::kNewTypeError), R(11), U8(2),
B(Throw),
......@@ -496,7 +486,7 @@ bytecodes: [
B(Jump), U8(20),
B(Star), R(12),
B(Ldar), R(closure),
B(CreateCatchContext), R(12), U8(7), U8(11),
B(CreateCatchContext), R(12), U8(10), U8(14),
B(Star), R(11),
B(LdaTheHole),
B(SetPendingMessage),
......@@ -593,9 +583,12 @@ bytecodes: [
/* 44 S> */ B(Return),
]
constant pool: [
Smi [52],
Smi [148],
FIXED_ARRAY_TYPE,
TUPLE2_TYPE,
SYMBOL_TYPE,
Smi [142],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["next"],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["done"],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["value"],
......@@ -608,9 +601,9 @@ constant pool: [
Smi [561],
]
handlers: [
[59, 667, 673],
[140, 429, 435],
[143, 385, 387],
[522, 538, 540],
[52, 660, 666],
[133, 422, 428],
[136, 378, 380],
[515, 531, 533],
]
......@@ -235,9 +235,10 @@ TEST_F(BytecodeAnalysisTest, SimpleLoop) {
builder.StoreAccumulatorInRegister(reg_0);
expected_liveness.emplace_back("..LL", "L.LL");
interpreter::LoopBuilder loop_builder(&builder);
loop_builder.LoopHeader();
{
interpreter::LoopBuilder loop_builder(&builder);
loop_builder.LoopHeader();
builder.JumpIfTrue(ToBooleanMode::kConvertToBoolean,
loop_builder.break_labels()->New());
expected_liveness.emplace_back("L.LL", "L.L.");
......@@ -252,7 +253,6 @@ TEST_F(BytecodeAnalysisTest, SimpleLoop) {
loop_builder.JumpToHeader(0);
expected_liveness.emplace_back("L.LL", "L.LL");
}
loop_builder.EndLoop();
builder.LoadAccumulatorWithRegister(reg_2);
expected_liveness.emplace_back("..L.", "...L");
......@@ -321,9 +321,10 @@ TEST_F(BytecodeAnalysisTest, DiamondInLoop) {
builder.StoreAccumulatorInRegister(reg_0);
expected_liveness.emplace_back("...L", "L..L");
interpreter::LoopBuilder loop_builder(&builder);
loop_builder.LoopHeader();
{
interpreter::LoopBuilder loop_builder(&builder);
loop_builder.LoopHeader();
builder.JumpIfTrue(ToBooleanMode::kConvertToBoolean,
loop_builder.break_labels()->New());
expected_liveness.emplace_back("L..L", "L..L");
......@@ -350,7 +351,6 @@ TEST_F(BytecodeAnalysisTest, DiamondInLoop) {
loop_builder.JumpToHeader(0);
expected_liveness.emplace_back("L..L", "L..L");
}
loop_builder.EndLoop();
builder.Return();
expected_liveness.emplace_back("...L", "....");
......@@ -370,9 +370,10 @@ TEST_F(BytecodeAnalysisTest, KillingLoopInsideLoop) {
builder.StoreAccumulatorInRegister(reg_0);
expected_liveness.emplace_back(".L.L", "LL..");
interpreter::LoopBuilder loop_builder(&builder);
loop_builder.LoopHeader();
{
interpreter::LoopBuilder loop_builder(&builder);
loop_builder.LoopHeader();
builder.LoadAccumulatorWithRegister(reg_0);
expected_liveness.emplace_back("LL..", ".L..");
......@@ -383,9 +384,10 @@ TEST_F(BytecodeAnalysisTest, KillingLoopInsideLoop) {
loop_builder.break_labels()->New());
expected_liveness.emplace_back(".L.L", ".L.L");
interpreter::LoopBuilder inner_loop_builder(&builder);
inner_loop_builder.LoopHeader();
{
interpreter::LoopBuilder inner_loop_builder(&builder);
inner_loop_builder.LoopHeader();
builder.StoreAccumulatorInRegister(reg_0);
expected_liveness.emplace_back(".L.L", "LL.L");
......@@ -397,13 +399,11 @@ TEST_F(BytecodeAnalysisTest, KillingLoopInsideLoop) {
inner_loop_builder.JumpToHeader(1);
expected_liveness.emplace_back(".L.L", ".L.L");
}
inner_loop_builder.EndLoop();
loop_builder.BindContinueTarget();
loop_builder.JumpToHeader(0);
expected_liveness.emplace_back("LL..", "LL..");
}
loop_builder.EndLoop();
builder.Return();
expected_liveness.emplace_back("...L", "....");
......
......@@ -7,6 +7,7 @@
#include "src/ast/scopes.h"
#include "src/interpreter/bytecode-array-builder.h"
#include "src/interpreter/bytecode-array-iterator.h"
#include "src/interpreter/bytecode-jump-table.h"
#include "src/interpreter/bytecode-label.h"
#include "src/interpreter/bytecode-register-allocator.h"
#include "src/objects-inl.h"
......@@ -278,6 +279,10 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
.JumpIfJSReceiver(&end[10]);
}
// Emit Smi table switch bytecode.
BytecodeJumpTable* jump_table = builder.AllocateJumpTable(1, 0);
builder.SwitchOnSmiNoFeedback(jump_table).Bind(jump_table, 0);
// Emit set pending message bytecode.
builder.SetPendingMessage();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment