[Interpreter] Replace BytecodeRegisterAllocator with a simple bump pointer.
There are only a few occasions where we allocate a register in an outer expression allocation scope, which makes the costly free-list approach of the BytecodeRegisterAllocator unecessary. This CL replaces all occurrences with moves to the accumulator and stores to a register allocated in the correct scope. By doing this, we can simplify the BytecodeRegisterAllocator to be a simple bump-pointer allocator with registers released in the same order as allocated. The following changes are also made: - Make BytecodeRegisterOptimizer able to use registers which have been unallocated, but not yet reused - Remove RegisterExpressionResultScope and rename AccumulatorExpressionResultScope to ValueExpressionResultScope - Introduce RegisterList to represent consecutive register allocations, and use this for operands to call bytecodes. By avoiding the free-list handling, this gives another couple of percent on CodeLoad. BUG=v8:4280 Review-Url: https://codereview.chromium.org/2369873002 Cr-Commit-Position: refs/heads/master@{#39905}
This commit is contained in:
parent
ee605756b3
commit
27fe988b85
1
BUILD.gn
1
BUILD.gn
@ -1421,7 +1421,6 @@ v8_source_set("v8_base") {
|
||||
"src/interpreter/bytecode-peephole-table.h",
|
||||
"src/interpreter/bytecode-pipeline.cc",
|
||||
"src/interpreter/bytecode-pipeline.h",
|
||||
"src/interpreter/bytecode-register-allocator.cc",
|
||||
"src/interpreter/bytecode-register-allocator.h",
|
||||
"src/interpreter/bytecode-register-optimizer.cc",
|
||||
"src/interpreter/bytecode-register-optimizer.h",
|
||||
|
@ -28,7 +28,7 @@ BytecodeArrayBuilder::BytecodeArrayBuilder(
|
||||
parameter_count_(parameter_count),
|
||||
local_register_count_(locals_count),
|
||||
context_register_count_(context_count),
|
||||
temporary_allocator_(zone, fixed_register_count()),
|
||||
register_allocator_(fixed_register_count()),
|
||||
bytecode_array_writer_(zone, &constant_array_builder_,
|
||||
source_position_mode),
|
||||
pipeline_(&bytecode_array_writer_) {
|
||||
@ -46,7 +46,8 @@ BytecodeArrayBuilder::BytecodeArrayBuilder(
|
||||
|
||||
if (FLAG_ignition_reo) {
|
||||
pipeline_ = new (zone) BytecodeRegisterOptimizer(
|
||||
zone, &temporary_allocator_, parameter_count, pipeline_);
|
||||
zone, ®ister_allocator_, fixed_register_count(), parameter_count,
|
||||
pipeline_);
|
||||
}
|
||||
|
||||
return_position_ =
|
||||
@ -69,10 +70,6 @@ Register BytecodeArrayBuilder::Parameter(int parameter_index) const {
|
||||
return Register::FromParameterIndex(parameter_index, parameter_count());
|
||||
}
|
||||
|
||||
bool BytecodeArrayBuilder::RegisterIsParameterOrLocal(Register reg) const {
|
||||
return reg.is_parameter() || reg.index() < locals_count();
|
||||
}
|
||||
|
||||
Handle<BytecodeArray> BytecodeArrayBuilder::ToBytecodeArray(Isolate* isolate) {
|
||||
DCHECK(return_seen_in_block_);
|
||||
DCHECK(!bytecode_generated_);
|
||||
@ -80,8 +77,7 @@ Handle<BytecodeArray> BytecodeArrayBuilder::ToBytecodeArray(Isolate* isolate) {
|
||||
|
||||
Handle<FixedArray> handler_table =
|
||||
handler_table_builder()->ToHandlerTable(isolate);
|
||||
return pipeline_->ToBytecodeArray(isolate,
|
||||
fixed_and_temporary_register_count(),
|
||||
return pipeline_->ToBytecodeArray(isolate, total_register_count(),
|
||||
parameter_count(), handler_table);
|
||||
}
|
||||
|
||||
@ -729,45 +725,38 @@ void BytecodeArrayBuilder::EnsureReturn() {
|
||||
}
|
||||
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::Call(Register callable,
|
||||
Register receiver_args,
|
||||
size_t receiver_args_count,
|
||||
RegisterList args,
|
||||
int feedback_slot,
|
||||
TailCallMode tail_call_mode) {
|
||||
if (tail_call_mode == TailCallMode::kDisallow) {
|
||||
Output(Bytecode::kCall, RegisterOperand(callable),
|
||||
RegisterOperand(receiver_args), UnsignedOperand(receiver_args_count),
|
||||
RegisterOperand(args.first_register()),
|
||||
UnsignedOperand(args.register_count()),
|
||||
UnsignedOperand(feedback_slot));
|
||||
} else {
|
||||
DCHECK(tail_call_mode == TailCallMode::kAllow);
|
||||
Output(Bytecode::kTailCall, RegisterOperand(callable),
|
||||
RegisterOperand(receiver_args), UnsignedOperand(receiver_args_count),
|
||||
RegisterOperand(args.first_register()),
|
||||
UnsignedOperand(args.register_count()),
|
||||
UnsignedOperand(feedback_slot));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::New(Register constructor,
|
||||
Register first_arg,
|
||||
size_t arg_count,
|
||||
RegisterList args,
|
||||
int feedback_slot_id) {
|
||||
if (!first_arg.is_valid()) {
|
||||
DCHECK_EQ(0u, arg_count);
|
||||
first_arg = Register(0);
|
||||
}
|
||||
Output(Bytecode::kNew, RegisterOperand(constructor),
|
||||
RegisterOperand(first_arg), UnsignedOperand(arg_count),
|
||||
RegisterOperand(args.first_register()),
|
||||
UnsignedOperand(args.register_count()),
|
||||
UnsignedOperand(feedback_slot_id));
|
||||
return *this;
|
||||
}
|
||||
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntime(
|
||||
Runtime::FunctionId function_id, Register first_arg, size_t arg_count) {
|
||||
Runtime::FunctionId function_id, RegisterList args) {
|
||||
DCHECK_EQ(1, Runtime::FunctionForId(function_id)->result_size);
|
||||
DCHECK(Bytecodes::SizeForUnsignedOperand(function_id) <= OperandSize::kShort);
|
||||
if (!first_arg.is_valid()) {
|
||||
DCHECK_EQ(0u, arg_count);
|
||||
first_arg = Register(0);
|
||||
}
|
||||
Bytecode bytecode;
|
||||
uint32_t id;
|
||||
if (IntrinsicsHelper::IsSupported(function_id)) {
|
||||
@ -777,29 +766,42 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntime(
|
||||
bytecode = Bytecode::kCallRuntime;
|
||||
id = static_cast<uint32_t>(function_id);
|
||||
}
|
||||
Output(bytecode, id, RegisterOperand(first_arg), UnsignedOperand(arg_count));
|
||||
Output(bytecode, id, RegisterOperand(args.first_register()),
|
||||
UnsignedOperand(args.register_count()));
|
||||
return *this;
|
||||
}
|
||||
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntime(
|
||||
Runtime::FunctionId function_id, Register arg) {
|
||||
return CallRuntime(function_id, RegisterList(arg.index(), 1));
|
||||
}
|
||||
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntime(
|
||||
Runtime::FunctionId function_id) {
|
||||
return CallRuntime(function_id, RegisterList());
|
||||
}
|
||||
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntimeForPair(
|
||||
Runtime::FunctionId function_id, RegisterList args, Register first_return) {
|
||||
DCHECK_EQ(2, Runtime::FunctionForId(function_id)->result_size);
|
||||
DCHECK(Bytecodes::SizeForUnsignedOperand(function_id) <= OperandSize::kShort);
|
||||
Output(Bytecode::kCallRuntimeForPair, static_cast<uint16_t>(function_id),
|
||||
RegisterOperand(args.first_register()),
|
||||
UnsignedOperand(args.register_count()), RegisterOperand(first_return));
|
||||
return *this;
|
||||
}
|
||||
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntimeForPair(
|
||||
Runtime::FunctionId function_id, Register first_arg, size_t arg_count,
|
||||
Register first_return) {
|
||||
DCHECK_EQ(2, Runtime::FunctionForId(function_id)->result_size);
|
||||
DCHECK(Bytecodes::SizeForUnsignedOperand(function_id) <= OperandSize::kShort);
|
||||
if (!first_arg.is_valid()) {
|
||||
DCHECK_EQ(0u, arg_count);
|
||||
first_arg = Register(0);
|
||||
}
|
||||
Output(Bytecode::kCallRuntimeForPair, static_cast<uint16_t>(function_id),
|
||||
RegisterOperand(first_arg), UnsignedOperand(arg_count),
|
||||
RegisterOperand(first_return));
|
||||
return *this;
|
||||
Runtime::FunctionId function_id, Register arg, Register first_return) {
|
||||
return CallRuntimeForPair(function_id, RegisterList(arg.index(), 1),
|
||||
first_return);
|
||||
}
|
||||
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::CallJSRuntime(
|
||||
int context_index, Register receiver_args, size_t receiver_args_count) {
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::CallJSRuntime(int context_index,
|
||||
RegisterList args) {
|
||||
Output(Bytecode::kCallJSRuntime, UnsignedOperand(context_index),
|
||||
RegisterOperand(receiver_args), UnsignedOperand(receiver_args_count));
|
||||
RegisterOperand(args.first_register()),
|
||||
UnsignedOperand(args.register_count()));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -832,10 +834,6 @@ void BytecodeArrayBuilder::SetReturnPosition() {
|
||||
latest_source_info_.MakeStatementPosition(return_position_);
|
||||
}
|
||||
|
||||
bool BytecodeArrayBuilder::TemporaryRegisterIsLive(Register reg) const {
|
||||
return temporary_register_allocator()->RegisterIsLive(reg);
|
||||
}
|
||||
|
||||
bool BytecodeArrayBuilder::RegisterIsValid(Register reg) const {
|
||||
if (!reg.is_valid()) {
|
||||
return false;
|
||||
@ -850,7 +848,7 @@ bool BytecodeArrayBuilder::RegisterIsValid(Register reg) const {
|
||||
} else if (reg.index() < fixed_register_count()) {
|
||||
return true;
|
||||
} else {
|
||||
return TemporaryRegisterIsLive(reg);
|
||||
return register_allocator()->RegisterIsLive(reg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,23 +61,14 @@ class BytecodeArrayBuilder final : public ZoneObject {
|
||||
int fixed_register_count() const { return context_count() + locals_count(); }
|
||||
|
||||
// Returns the number of fixed and temporary registers.
|
||||
int fixed_and_temporary_register_count() const {
|
||||
return fixed_register_count() + temporary_register_count();
|
||||
}
|
||||
|
||||
int temporary_register_count() const {
|
||||
return temporary_register_allocator()->allocation_count();
|
||||
int total_register_count() const {
|
||||
DCHECK_LE(fixed_register_count(),
|
||||
register_allocator()->maximum_register_count());
|
||||
return register_allocator()->maximum_register_count();
|
||||
}
|
||||
|
||||
Register Parameter(int parameter_index) const;
|
||||
|
||||
// Return true if the register |reg| represents a parameter or a
|
||||
// local.
|
||||
bool RegisterIsParameterOrLocal(Register reg) const;
|
||||
|
||||
// Returns true if the register |reg| is a live temporary register.
|
||||
bool TemporaryRegisterIsLive(Register reg) const;
|
||||
|
||||
// Constant loads to accumulator.
|
||||
BytecodeArrayBuilder& LoadConstantPoolEntry(size_t entry);
|
||||
BytecodeArrayBuilder& LoadLiteral(v8::internal::Smi* value);
|
||||
@ -191,46 +182,39 @@ class BytecodeArrayBuilder final : public ZoneObject {
|
||||
BytecodeArrayBuilder& PopContext(Register context);
|
||||
|
||||
// Call a JS function. The JSFunction or Callable to be called should be in
|
||||
// |callable|, the receiver should be in |receiver_args| and all subsequent
|
||||
// arguments should be in registers <receiver_args + 1> to
|
||||
// <receiver_args + receiver_arg_count - 1>. Type feedback is recorded in
|
||||
// the |feedback_slot| in the type feedback vector.
|
||||
// |callable|. The arguments should be in |args|, with the receiver in
|
||||
// |args[0]|. Type feedback is recorded in the |feedback_slot| in the type
|
||||
// feedback vector.
|
||||
BytecodeArrayBuilder& Call(
|
||||
Register callable, Register receiver_args, size_t receiver_arg_count,
|
||||
int feedback_slot, TailCallMode tail_call_mode = TailCallMode::kDisallow);
|
||||
|
||||
BytecodeArrayBuilder& TailCall(Register callable, Register receiver_args,
|
||||
size_t receiver_arg_count, int feedback_slot) {
|
||||
return Call(callable, receiver_args, receiver_arg_count, feedback_slot,
|
||||
TailCallMode::kAllow);
|
||||
}
|
||||
Register callable, RegisterList args, int feedback_slot,
|
||||
TailCallMode tail_call_mode = TailCallMode::kDisallow);
|
||||
|
||||
// Call the new operator. The accumulator holds the |new_target|.
|
||||
// The |constructor| is in a register followed by |arg_count|
|
||||
// consecutive arguments starting at |first_arg| for the constuctor
|
||||
// invocation.
|
||||
BytecodeArrayBuilder& New(Register constructor, Register first_arg,
|
||||
size_t arg_count, int feedback_slot);
|
||||
// The |constructor| is in a register and arguments are in |args|.
|
||||
BytecodeArrayBuilder& New(Register constructor, RegisterList args,
|
||||
int feedback_slot);
|
||||
|
||||
// Call the runtime function with |function_id|. The first argument should be
|
||||
// in |first_arg| and all subsequent arguments should be in registers
|
||||
// <first_arg + 1> to <first_arg + arg_count - 1>.
|
||||
// Call the runtime function with |function_id| and arguments |args|.
|
||||
BytecodeArrayBuilder& CallRuntime(Runtime::FunctionId function_id,
|
||||
Register first_arg, size_t arg_count);
|
||||
RegisterList args);
|
||||
// Call the runtime function with |function_id| with single argument |arg|.
|
||||
BytecodeArrayBuilder& CallRuntime(Runtime::FunctionId function_id,
|
||||
Register arg);
|
||||
// Call the runtime function with |function_id| with no arguments.
|
||||
BytecodeArrayBuilder& CallRuntime(Runtime::FunctionId function_id);
|
||||
|
||||
// Call the runtime function with |function_id| that returns a pair of values.
|
||||
// The first argument should be in |first_arg| and all subsequent arguments
|
||||
// should be in registers <first_arg + 1> to <first_arg + arg_count - 1>. The
|
||||
// return values will be returned in <first_return> and <first_return + 1>.
|
||||
// Call the runtime function with |function_id| and arguments |args|, that
|
||||
// returns a pair of values. The return values will be returned in
|
||||
// <first_return> and <first_return + 1>.
|
||||
BytecodeArrayBuilder& CallRuntimeForPair(Runtime::FunctionId function_id,
|
||||
Register first_arg, size_t arg_count,
|
||||
RegisterList args,
|
||||
Register first_return);
|
||||
// Call the runtime function with |function_id| with single argument |arg|.
|
||||
BytecodeArrayBuilder& CallRuntimeForPair(Runtime::FunctionId function_id,
|
||||
Register arg, Register first_return);
|
||||
|
||||
// Call the JS runtime function with |context_index|. The the receiver should
|
||||
// be in |receiver_args| and all subsequent arguments should be in registers
|
||||
// <receiver + 1> to <receiver + receiver_args_count - 1>.
|
||||
BytecodeArrayBuilder& CallJSRuntime(int context_index, Register receiver_args,
|
||||
size_t receiver_args_count);
|
||||
// Call the JS runtime function with |context_index| and arguments |args|.
|
||||
BytecodeArrayBuilder& CallJSRuntime(int context_index, RegisterList args);
|
||||
|
||||
// Operators (register holds the lhs value, accumulator holds the rhs value).
|
||||
// Type feedback will be recorded in the |feedback_slot|
|
||||
@ -328,34 +312,16 @@ class BytecodeArrayBuilder final : public ZoneObject {
|
||||
latest_source_info_.MakeStatementPosition(expr->position());
|
||||
}
|
||||
|
||||
// Accessors
|
||||
TemporaryRegisterAllocator* temporary_register_allocator() {
|
||||
return &temporary_allocator_;
|
||||
}
|
||||
const TemporaryRegisterAllocator* temporary_register_allocator() const {
|
||||
return &temporary_allocator_;
|
||||
}
|
||||
Zone* zone() const { return zone_; }
|
||||
|
||||
void EnsureReturn();
|
||||
|
||||
static uint32_t RegisterOperand(Register reg) {
|
||||
return static_cast<uint32_t>(reg.ToOperand());
|
||||
// Accessors
|
||||
BytecodeRegisterAllocator* register_allocator() {
|
||||
return ®ister_allocator_;
|
||||
}
|
||||
|
||||
static uint32_t SignedOperand(int value) {
|
||||
return static_cast<uint32_t>(value);
|
||||
}
|
||||
|
||||
static uint32_t UnsignedOperand(int value) {
|
||||
DCHECK_GE(value, 0);
|
||||
return static_cast<uint32_t>(value);
|
||||
}
|
||||
|
||||
static uint32_t UnsignedOperand(size_t value) {
|
||||
DCHECK_LE(value, kMaxUInt32);
|
||||
return static_cast<uint32_t>(value);
|
||||
const BytecodeRegisterAllocator* register_allocator() const {
|
||||
return ®ister_allocator_;
|
||||
}
|
||||
Zone* zone() const { return zone_; }
|
||||
|
||||
private:
|
||||
friend class BytecodeRegisterAllocator;
|
||||
@ -377,6 +343,24 @@ class BytecodeArrayBuilder final : public ZoneObject {
|
||||
uint32_t operand0 = 0, uint32_t operand1 = 0,
|
||||
uint32_t operand2 = 0, uint32_t operand3 = 0) const;
|
||||
|
||||
static uint32_t RegisterOperand(Register reg) {
|
||||
return static_cast<uint32_t>(reg.ToOperand());
|
||||
}
|
||||
|
||||
static uint32_t SignedOperand(int value) {
|
||||
return static_cast<uint32_t>(value);
|
||||
}
|
||||
|
||||
static uint32_t UnsignedOperand(int value) {
|
||||
DCHECK_GE(value, 0);
|
||||
return static_cast<uint32_t>(value);
|
||||
}
|
||||
|
||||
static uint32_t UnsignedOperand(size_t value) {
|
||||
DCHECK_LE(value, kMaxUInt32);
|
||||
return static_cast<uint32_t>(value);
|
||||
}
|
||||
|
||||
// Set position for return.
|
||||
void SetReturnPosition();
|
||||
|
||||
@ -413,7 +397,7 @@ class BytecodeArrayBuilder final : public ZoneObject {
|
||||
int local_register_count_;
|
||||
int context_register_count_;
|
||||
int return_position_;
|
||||
TemporaryRegisterAllocator temporary_allocator_;
|
||||
BytecodeRegisterAllocator register_allocator_;
|
||||
BytecodeArrayWriter bytecode_array_writer_;
|
||||
BytecodePipelineStage* pipeline_;
|
||||
BytecodeSourceInfo latest_source_info_;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -36,7 +36,6 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
|
||||
void VisitStatements(ZoneList<Statement*>* statments);
|
||||
|
||||
private:
|
||||
class AccumulatorResultScope;
|
||||
class ContextScope;
|
||||
class ControlScope;
|
||||
class ControlScopeForBreakable;
|
||||
@ -47,9 +46,9 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
|
||||
class ExpressionResultScope;
|
||||
class EffectResultScope;
|
||||
class GlobalDeclarationsBuilder;
|
||||
class RegisterResultScope;
|
||||
class RegisterAllocationScope;
|
||||
class TestResultScope;
|
||||
class ValueResultScope;
|
||||
|
||||
enum class TestFallthrough { kThen, kElse, kNone };
|
||||
|
||||
@ -73,8 +72,10 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
|
||||
// Used by flow control routines to evaluate loop condition.
|
||||
void VisitCondition(Expression* expr);
|
||||
|
||||
// Helper visitors which perform common operations.
|
||||
Register VisitArguments(ZoneList<Expression*>* arguments);
|
||||
// Visit the arguments expressions in |args| and store them in |args_regs|
|
||||
// starting at register |first_argument_register| in the list.
|
||||
void VisitArguments(ZoneList<Expression*>* args, RegisterList arg_regs,
|
||||
size_t first_argument_register = 0);
|
||||
|
||||
// Visit a keyed super property load. The optional
|
||||
// |opt_receiver_out| register will have the receiver stored to it
|
||||
@ -104,15 +105,6 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
|
||||
void VisitVariableAssignment(Variable* variable, Token::Value op,
|
||||
FeedbackVectorSlot slot);
|
||||
|
||||
void BuildNamedSuperPropertyStore(Register receiver, Register home_object,
|
||||
Register name, Register value);
|
||||
void BuildKeyedSuperPropertyStore(Register receiver, Register home_object,
|
||||
Register key, Register value);
|
||||
void BuildNamedSuperPropertyLoad(Register receiver, Register home_object,
|
||||
Register name);
|
||||
void BuildKeyedSuperPropertyLoad(Register receiver, Register home_object,
|
||||
Register key);
|
||||
|
||||
void BuildAbort(BailoutReason bailout_reason);
|
||||
void BuildThrowIfHole(Handle<String> name);
|
||||
void BuildThrowIfNotHole(Handle<String> name);
|
||||
@ -139,7 +131,6 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
|
||||
void VisitClassLiteralForRuntimeDefinition(ClassLiteral* expr);
|
||||
void VisitClassLiteralProperties(ClassLiteral* expr, Register literal,
|
||||
Register prototype);
|
||||
void VisitClassLiteralStaticPrototypeWithComputedName(Register name);
|
||||
void VisitThisFunctionVariable(Variable* variable);
|
||||
void VisitNewTargetVariable(Variable* variable);
|
||||
void VisitBlockDeclarationsAndStatements(Block* stmt);
|
||||
@ -169,13 +160,10 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
|
||||
void VisitForTest(Expression* expr, BytecodeLabels* then_labels,
|
||||
BytecodeLabels* else_labels, TestFallthrough fallthrough);
|
||||
|
||||
// Methods for tracking and remapping register.
|
||||
void RecordStoreToRegister(Register reg);
|
||||
Register LoadFromAliasedRegister(Register reg);
|
||||
|
||||
// Initialize an array of temporary registers with consecutive registers.
|
||||
template <size_t N>
|
||||
void InitializeWithConsecutiveRegisters(Register (®isters)[N]);
|
||||
// Returns the runtime function id for a store to super for the function's
|
||||
// language mode.
|
||||
inline Runtime::FunctionId StoreToSuperRuntimeId();
|
||||
inline Runtime::FunctionId StoreKeyedToSuperRuntimeId();
|
||||
|
||||
inline BytecodeArrayBuilder* builder() const { return builder_; }
|
||||
inline Zone* zone() const { return zone_; }
|
||||
@ -194,12 +182,8 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
|
||||
execution_result_ = execution_result;
|
||||
}
|
||||
ExpressionResultScope* execution_result() const { return execution_result_; }
|
||||
inline void set_register_allocator(
|
||||
RegisterAllocationScope* register_allocator) {
|
||||
register_allocator_ = register_allocator;
|
||||
}
|
||||
RegisterAllocationScope* register_allocator() const {
|
||||
return register_allocator_;
|
||||
BytecodeRegisterAllocator* register_allocator() const {
|
||||
return builder()->register_allocator();
|
||||
}
|
||||
|
||||
GlobalDeclarationsBuilder* globals_builder() { return globals_builder_; }
|
||||
@ -223,7 +207,6 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
|
||||
ControlScope* execution_control_;
|
||||
ContextScope* execution_context_;
|
||||
ExpressionResultScope* execution_result_;
|
||||
RegisterAllocationScope* register_allocator_;
|
||||
|
||||
ZoneVector<BytecodeLabel> generator_resume_points_;
|
||||
Register generator_state_;
|
||||
|
@ -1,210 +0,0 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
#include "src/interpreter/bytecode-register-allocator.h"
|
||||
|
||||
#include "src/interpreter/bytecode-array-builder.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace interpreter {
|
||||
|
||||
TemporaryRegisterAllocator::TemporaryRegisterAllocator(Zone* zone,
|
||||
int allocation_base)
|
||||
: free_temporaries_(zone),
|
||||
allocation_base_(allocation_base),
|
||||
allocation_count_(0),
|
||||
observer_(nullptr) {}
|
||||
|
||||
Register TemporaryRegisterAllocator::first_temporary_register() const {
|
||||
DCHECK(allocation_count() > 0);
|
||||
return Register(allocation_base());
|
||||
}
|
||||
|
||||
Register TemporaryRegisterAllocator::last_temporary_register() const {
|
||||
DCHECK(allocation_count() > 0);
|
||||
return Register(allocation_base() + allocation_count() - 1);
|
||||
}
|
||||
|
||||
void TemporaryRegisterAllocator::set_observer(
|
||||
TemporaryRegisterObserver* observer) {
|
||||
DCHECK(observer_ == nullptr);
|
||||
observer_ = observer;
|
||||
}
|
||||
|
||||
int TemporaryRegisterAllocator::AllocateTemporaryRegister() {
|
||||
allocation_count_ += 1;
|
||||
return allocation_base() + allocation_count() - 1;
|
||||
}
|
||||
|
||||
int TemporaryRegisterAllocator::BorrowTemporaryRegister() {
|
||||
if (free_temporaries_.empty()) {
|
||||
return AllocateTemporaryRegister();
|
||||
} else {
|
||||
auto pos = free_temporaries_.begin();
|
||||
int retval = *pos;
|
||||
free_temporaries_.erase(pos);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
int TemporaryRegisterAllocator::BorrowTemporaryRegisterNotInRange(
|
||||
int start_index, int end_index) {
|
||||
if (free_temporaries_.empty()) {
|
||||
int next_allocation = allocation_base() + allocation_count();
|
||||
while (next_allocation >= start_index && next_allocation <= end_index) {
|
||||
free_temporaries_.insert(AllocateTemporaryRegister());
|
||||
next_allocation += 1;
|
||||
}
|
||||
return AllocateTemporaryRegister();
|
||||
}
|
||||
|
||||
ZoneSet<int>::iterator index = free_temporaries_.lower_bound(start_index);
|
||||
if (index == free_temporaries_.begin()) {
|
||||
// If start_index is the first free register, check for a register
|
||||
// greater than end_index.
|
||||
index = free_temporaries_.upper_bound(end_index);
|
||||
if (index == free_temporaries_.end()) {
|
||||
return AllocateTemporaryRegister();
|
||||
}
|
||||
} else {
|
||||
// If there is a free register < start_index
|
||||
index--;
|
||||
}
|
||||
|
||||
int retval = *index;
|
||||
free_temporaries_.erase(index);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int TemporaryRegisterAllocator::PrepareForConsecutiveTemporaryRegisters(
|
||||
size_t count) {
|
||||
if (count == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// TODO(oth): replace use of set<> here for free_temporaries with a
|
||||
// more efficient structure. And/or partition into two searches -
|
||||
// one before the translation window and one after.
|
||||
|
||||
// A run will require at least |count| free temporaries.
|
||||
while (free_temporaries_.size() < count) {
|
||||
free_temporaries_.insert(AllocateTemporaryRegister());
|
||||
}
|
||||
|
||||
// Search within existing temporaries for a run.
|
||||
auto start = free_temporaries_.begin();
|
||||
size_t run_length = 0;
|
||||
for (auto run_end = start; run_end != free_temporaries_.end(); run_end++) {
|
||||
int expected = *start + static_cast<int>(run_length);
|
||||
if (*run_end != expected) {
|
||||
start = run_end;
|
||||
run_length = 0;
|
||||
}
|
||||
if (++run_length == count) {
|
||||
return *start;
|
||||
}
|
||||
}
|
||||
|
||||
// Continue run if possible across existing last temporary.
|
||||
if (allocation_count_ > 0 && (start == free_temporaries_.end() ||
|
||||
*start + static_cast<int>(run_length) !=
|
||||
last_temporary_register().index() + 1)) {
|
||||
run_length = 0;
|
||||
}
|
||||
|
||||
// Pad temporaries if extended run would cross translation boundary.
|
||||
Register reg_first(*start);
|
||||
Register reg_last(*start + static_cast<int>(count) - 1);
|
||||
|
||||
// Ensure enough registers for run.
|
||||
while (run_length++ < count) {
|
||||
free_temporaries_.insert(AllocateTemporaryRegister());
|
||||
}
|
||||
|
||||
int run_start =
|
||||
last_temporary_register().index() - static_cast<int>(count) + 1;
|
||||
return run_start;
|
||||
}
|
||||
|
||||
bool TemporaryRegisterAllocator::RegisterIsLive(Register reg) const {
|
||||
if (allocation_count_ > 0) {
|
||||
DCHECK(reg >= first_temporary_register() &&
|
||||
reg <= last_temporary_register());
|
||||
return free_temporaries_.find(reg.index()) == free_temporaries_.end();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void TemporaryRegisterAllocator::BorrowConsecutiveTemporaryRegister(
|
||||
int reg_index) {
|
||||
DCHECK(free_temporaries_.find(reg_index) != free_temporaries_.end());
|
||||
free_temporaries_.erase(reg_index);
|
||||
}
|
||||
|
||||
void TemporaryRegisterAllocator::ReturnTemporaryRegister(int reg_index) {
|
||||
DCHECK(free_temporaries_.find(reg_index) == free_temporaries_.end());
|
||||
free_temporaries_.insert(reg_index);
|
||||
if (observer_) {
|
||||
observer_->TemporaryRegisterFreeEvent(Register(reg_index));
|
||||
}
|
||||
}
|
||||
|
||||
BytecodeRegisterAllocator::BytecodeRegisterAllocator(
|
||||
Zone* zone, TemporaryRegisterAllocator* allocator)
|
||||
: base_allocator_(allocator),
|
||||
allocated_(zone),
|
||||
next_consecutive_register_(-1),
|
||||
next_consecutive_count_(-1) {}
|
||||
|
||||
BytecodeRegisterAllocator::~BytecodeRegisterAllocator() {
|
||||
for (auto i = allocated_.rbegin(); i != allocated_.rend(); i++) {
|
||||
base_allocator()->ReturnTemporaryRegister(*i);
|
||||
}
|
||||
allocated_.clear();
|
||||
}
|
||||
|
||||
Register BytecodeRegisterAllocator::NewRegister() {
|
||||
int allocated = -1;
|
||||
if (next_consecutive_count_ <= 0) {
|
||||
allocated = base_allocator()->BorrowTemporaryRegister();
|
||||
} else {
|
||||
allocated = base_allocator()->BorrowTemporaryRegisterNotInRange(
|
||||
next_consecutive_register_,
|
||||
next_consecutive_register_ + next_consecutive_count_ - 1);
|
||||
}
|
||||
allocated_.push_back(allocated);
|
||||
return Register(allocated);
|
||||
}
|
||||
|
||||
bool BytecodeRegisterAllocator::RegisterIsAllocatedInThisScope(
|
||||
Register reg) const {
|
||||
for (auto i = allocated_.begin(); i != allocated_.end(); i++) {
|
||||
if (*i == reg.index()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void BytecodeRegisterAllocator::PrepareForConsecutiveAllocations(size_t count) {
|
||||
if (static_cast<int>(count) > next_consecutive_count_) {
|
||||
next_consecutive_register_ =
|
||||
base_allocator()->PrepareForConsecutiveTemporaryRegisters(count);
|
||||
next_consecutive_count_ = static_cast<int>(count);
|
||||
}
|
||||
}
|
||||
|
||||
Register BytecodeRegisterAllocator::NextConsecutiveRegister() {
|
||||
DCHECK_GE(next_consecutive_register_, 0);
|
||||
DCHECK_GT(next_consecutive_count_, 0);
|
||||
base_allocator()->BorrowConsecutiveTemporaryRegister(
|
||||
next_consecutive_register_);
|
||||
allocated_.push_back(next_consecutive_register_);
|
||||
next_consecutive_count_--;
|
||||
return Register(next_consecutive_register_++);
|
||||
}
|
||||
|
||||
} // namespace interpreter
|
||||
} // namespace internal
|
||||
} // namespace v8
|
@ -5,6 +5,7 @@
|
||||
#ifndef V8_INTERPRETER_BYTECODE_REGISTER_ALLOCATOR_H_
|
||||
#define V8_INTERPRETER_BYTECODE_REGISTER_ALLOCATOR_H_
|
||||
|
||||
#include "src/interpreter/bytecode-register.h"
|
||||
#include "src/interpreter/bytecodes.h"
|
||||
#include "src/zone/zone-containers.h"
|
||||
|
||||
@ -12,99 +13,98 @@ namespace v8 {
|
||||
namespace internal {
|
||||
namespace interpreter {
|
||||
|
||||
class BytecodeArrayBuilder;
|
||||
class Register;
|
||||
class TemporaryRegisterObserver;
|
||||
|
||||
class TemporaryRegisterAllocator final {
|
||||
class RegisterList {
|
||||
public:
|
||||
TemporaryRegisterAllocator(Zone* zone, int start_index);
|
||||
RegisterList() : first_reg_index_(Register().index()), register_count_(0) {}
|
||||
RegisterList(int first_reg_index, int register_count)
|
||||
: first_reg_index_(first_reg_index), register_count_(register_count) {}
|
||||
|
||||
// Borrow a temporary register.
|
||||
int BorrowTemporaryRegister();
|
||||
// Returns a new RegisterList which is a truncated version of this list, with
|
||||
// |count| registers.
|
||||
const RegisterList Truncate(int new_count) {
|
||||
DCHECK_GE(new_count, 0);
|
||||
DCHECK_LT(new_count, register_count_);
|
||||
return RegisterList(first_reg_index_, new_count);
|
||||
}
|
||||
|
||||
// Borrow a temporary register from the register range outside of
|
||||
// |start_index| to |end_index|.
|
||||
int BorrowTemporaryRegisterNotInRange(int start_index, int end_index);
|
||||
const Register operator[](size_t i) const {
|
||||
DCHECK_LT(static_cast<int>(i), register_count_);
|
||||
return Register(first_reg_index_ + static_cast<int>(i));
|
||||
}
|
||||
|
||||
// Return a temporary register when no longer used.
|
||||
void ReturnTemporaryRegister(int reg_index);
|
||||
const Register first_register() const {
|
||||
return (register_count() == 0) ? Register(0) : (*this)[0];
|
||||
}
|
||||
|
||||
// Ensure a run of consecutive registers is available. Each register in
|
||||
// the range should be borrowed with BorrowConsecutiveTemporaryRegister().
|
||||
// Returns the start index of the run.
|
||||
int PrepareForConsecutiveTemporaryRegisters(size_t count);
|
||||
|
||||
// Borrow a register from a range prepared with
|
||||
// PrepareForConsecutiveTemporaryRegisters().
|
||||
void BorrowConsecutiveTemporaryRegister(int reg_index);
|
||||
|
||||
// Returns true if |reg| is a temporary register and is currently
|
||||
// borrowed.
|
||||
bool RegisterIsLive(Register reg) const;
|
||||
|
||||
// Returns the first register in the range of temporary registers.
|
||||
Register first_temporary_register() const;
|
||||
|
||||
// Returns the last register in the range of temporary registers.
|
||||
Register last_temporary_register() const;
|
||||
|
||||
// Returns the start index of temporary register allocations.
|
||||
int allocation_base() const { return allocation_base_; }
|
||||
|
||||
// Returns the number of temporary register allocations made.
|
||||
int allocation_count() const { return allocation_count_; }
|
||||
|
||||
// Sets an observer for temporary register events.
|
||||
void set_observer(TemporaryRegisterObserver* observer);
|
||||
int register_count() const { return register_count_; }
|
||||
|
||||
private:
|
||||
// Allocate a temporary register.
|
||||
int AllocateTemporaryRegister();
|
||||
|
||||
ZoneSet<int> free_temporaries_;
|
||||
int allocation_base_;
|
||||
int allocation_count_;
|
||||
TemporaryRegisterObserver* observer_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TemporaryRegisterAllocator);
|
||||
int first_reg_index_;
|
||||
int register_count_;
|
||||
};
|
||||
|
||||
class TemporaryRegisterObserver {
|
||||
public:
|
||||
virtual ~TemporaryRegisterObserver() {}
|
||||
virtual void TemporaryRegisterFreeEvent(Register reg) = 0;
|
||||
};
|
||||
|
||||
// A class that allows the instantiator to allocate temporary registers that are
|
||||
// cleaned up when scope is closed.
|
||||
// A class that allows the allocation of contiguous temporary registers.
|
||||
class BytecodeRegisterAllocator final {
|
||||
public:
|
||||
explicit BytecodeRegisterAllocator(Zone* zone,
|
||||
TemporaryRegisterAllocator* allocator);
|
||||
~BytecodeRegisterAllocator();
|
||||
Register NewRegister();
|
||||
// Enables observation of register allocation and free events.
|
||||
class Observer {
|
||||
public:
|
||||
virtual ~Observer() {}
|
||||
virtual void RegisterAllocateEvent(Register reg) = 0;
|
||||
virtual void RegisterListAllocateEvent(RegisterList reg_list) = 0;
|
||||
virtual void RegisterListFreeEvent(RegisterList reg_list) = 0;
|
||||
};
|
||||
|
||||
// Ensure |count| consecutive allocations are available.
|
||||
void PrepareForConsecutiveAllocations(size_t count);
|
||||
explicit BytecodeRegisterAllocator(int start_index)
|
||||
: next_register_index_(start_index),
|
||||
max_register_count_(start_index),
|
||||
observer_(nullptr) {}
|
||||
~BytecodeRegisterAllocator() {}
|
||||
|
||||
// Get the next consecutive allocation after calling
|
||||
// PrepareForConsecutiveAllocations.
|
||||
Register NextConsecutiveRegister();
|
||||
// Returns a new register.
|
||||
Register NewRegister() {
|
||||
Register reg(next_register_index_++);
|
||||
max_register_count_ = std::max(next_register_index_, max_register_count_);
|
||||
if (observer_) {
|
||||
observer_->RegisterAllocateEvent(reg);
|
||||
}
|
||||
return reg;
|
||||
}
|
||||
|
||||
// Returns true if |reg| is allocated in this allocator.
|
||||
bool RegisterIsAllocatedInThisScope(Register reg) const;
|
||||
// Returns a consecutive list of |count| new registers.
|
||||
RegisterList NewRegisterList(int count) {
|
||||
RegisterList reg_list(next_register_index_, count);
|
||||
next_register_index_ += count;
|
||||
max_register_count_ = std::max(next_register_index_, max_register_count_);
|
||||
if (observer_) {
|
||||
observer_->RegisterListAllocateEvent(reg_list);
|
||||
}
|
||||
return reg_list;
|
||||
}
|
||||
|
||||
// Returns true if unused consecutive allocations remain.
|
||||
bool HasConsecutiveAllocations() const { return next_consecutive_count_ > 0; }
|
||||
// Release all registers above |register_index|.
|
||||
void ReleaseRegisters(int register_index) {
|
||||
if (observer_) {
|
||||
observer_->RegisterListFreeEvent(
|
||||
RegisterList(register_index, next_register_index_ - register_index));
|
||||
}
|
||||
next_register_index_ = register_index;
|
||||
}
|
||||
|
||||
// Returns true if the register |reg| is a live register.
|
||||
bool RegisterIsLive(Register reg) const {
|
||||
return reg.index() < next_register_index_;
|
||||
}
|
||||
|
||||
void set_observer(Observer* observer) { observer_ = observer; }
|
||||
|
||||
int next_register_index() const { return next_register_index_; }
|
||||
int maximum_register_count() const { return max_register_count_; }
|
||||
|
||||
private:
|
||||
TemporaryRegisterAllocator* base_allocator() const { return base_allocator_; }
|
||||
|
||||
TemporaryRegisterAllocator* base_allocator_;
|
||||
ZoneVector<int> allocated_;
|
||||
int next_consecutive_register_;
|
||||
int next_consecutive_count_;
|
||||
int next_register_index_;
|
||||
int max_register_count_;
|
||||
Observer* observer_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(BytecodeRegisterAllocator);
|
||||
};
|
||||
|
@ -15,10 +15,12 @@ const uint32_t BytecodeRegisterOptimizer::kInvalidEquivalenceId;
|
||||
// register is materialized in the bytecode stream.
|
||||
class BytecodeRegisterOptimizer::RegisterInfo final : public ZoneObject {
|
||||
public:
|
||||
RegisterInfo(Register reg, uint32_t equivalence_id, bool materialized)
|
||||
RegisterInfo(Register reg, uint32_t equivalence_id, bool materialized,
|
||||
bool allocated)
|
||||
: register_(reg),
|
||||
equivalence_id_(equivalence_id),
|
||||
materialized_(materialized),
|
||||
allocated_(allocated),
|
||||
next_(this),
|
||||
prev_(this) {}
|
||||
|
||||
@ -48,12 +50,17 @@ class BytecodeRegisterOptimizer::RegisterInfo final : public ZoneObject {
|
||||
// exists.
|
||||
RegisterInfo* GetEquivalentToMaterialize();
|
||||
|
||||
// Marks all temporary registers of the equivalence set as unmaterialized.
|
||||
void MarkTemporariesAsUnmaterialized(Register temporary_base);
|
||||
|
||||
// Get an equivalent register. Returns this if none exists.
|
||||
RegisterInfo* GetEquivalent();
|
||||
|
||||
Register register_value() const { return register_; }
|
||||
bool materialized() const { return materialized_; }
|
||||
void set_materialized(bool materialized) { materialized_ = materialized; }
|
||||
bool allocated() const { return allocated_; }
|
||||
void set_allocated(bool allocated) { allocated_ = allocated; }
|
||||
void set_equivalence_id(uint32_t equivalence_id) {
|
||||
equivalence_id_ = equivalence_id;
|
||||
}
|
||||
@ -63,6 +70,7 @@ class BytecodeRegisterOptimizer::RegisterInfo final : public ZoneObject {
|
||||
Register register_;
|
||||
uint32_t equivalence_id_;
|
||||
bool materialized_;
|
||||
bool allocated_;
|
||||
|
||||
// Equivalence set pointers.
|
||||
RegisterInfo* next_;
|
||||
@ -155,8 +163,9 @@ BytecodeRegisterOptimizer::RegisterInfo::GetEquivalentToMaterialize() {
|
||||
if (visitor->materialized()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (best_info == nullptr ||
|
||||
visitor->register_value() < best_info->register_value()) {
|
||||
if (visitor->allocated() &&
|
||||
(best_info == nullptr ||
|
||||
visitor->register_value() < best_info->register_value())) {
|
||||
best_info = visitor;
|
||||
}
|
||||
visitor = visitor->next_;
|
||||
@ -164,17 +173,31 @@ BytecodeRegisterOptimizer::RegisterInfo::GetEquivalentToMaterialize() {
|
||||
return best_info;
|
||||
}
|
||||
|
||||
void BytecodeRegisterOptimizer::RegisterInfo::MarkTemporariesAsUnmaterialized(
|
||||
Register temporary_base) {
|
||||
DCHECK(this->register_value() < temporary_base);
|
||||
DCHECK(this->materialized());
|
||||
RegisterInfo* visitor = this->next_;
|
||||
while (visitor != this) {
|
||||
if (visitor->register_value() >= temporary_base) {
|
||||
visitor->set_materialized(false);
|
||||
}
|
||||
visitor = visitor->next_;
|
||||
}
|
||||
}
|
||||
|
||||
BytecodeRegisterOptimizer::RegisterInfo*
|
||||
BytecodeRegisterOptimizer::RegisterInfo::GetEquivalent() {
|
||||
return next_;
|
||||
}
|
||||
|
||||
BytecodeRegisterOptimizer::BytecodeRegisterOptimizer(
|
||||
Zone* zone, TemporaryRegisterAllocator* register_allocator,
|
||||
int parameter_count, BytecodePipelineStage* next_stage)
|
||||
Zone* zone, BytecodeRegisterAllocator* register_allocator,
|
||||
int fixed_registers_count, int parameter_count,
|
||||
BytecodePipelineStage* next_stage)
|
||||
: accumulator_(Register::virtual_accumulator()),
|
||||
temporary_base_(register_allocator->allocation_base()),
|
||||
max_register_index_(register_allocator->allocation_base() - 1),
|
||||
temporary_base_(fixed_registers_count),
|
||||
max_register_index_(fixed_registers_count - 1),
|
||||
register_info_table_(zone),
|
||||
equivalence_id_(0),
|
||||
next_stage_(next_stage),
|
||||
@ -199,7 +222,7 @@ BytecodeRegisterOptimizer::BytecodeRegisterOptimizer(
|
||||
static_cast<size_t>(temporary_base_.index()));
|
||||
for (size_t i = 0; i < register_info_table_.size(); ++i) {
|
||||
register_info_table_[i] = new (zone) RegisterInfo(
|
||||
RegisterFromRegisterInfoTableIndex(i), NextEquivalenceId(), true);
|
||||
RegisterFromRegisterInfoTableIndex(i), NextEquivalenceId(), true, true);
|
||||
DCHECK_EQ(register_info_table_[i]->register_value().index(),
|
||||
RegisterFromRegisterInfoTableIndex(i).index());
|
||||
}
|
||||
@ -296,7 +319,7 @@ void BytecodeRegisterOptimizer::FlushState() {
|
||||
// own equivalence set.
|
||||
RegisterInfo* equivalent;
|
||||
while ((equivalent = reg_info->GetEquivalent()) != reg_info) {
|
||||
if (!equivalent->materialized()) {
|
||||
if (equivalent->allocated() && !equivalent->materialized()) {
|
||||
OutputRegisterTransfer(reg_info, equivalent);
|
||||
}
|
||||
equivalent->MoveToNewEquivalenceSet(NextEquivalenceId(), true);
|
||||
@ -404,6 +427,13 @@ void BytecodeRegisterOptimizer::RegisterTransfer(
|
||||
// Emit a placeholder nop to maintain source position info.
|
||||
EmitNopForSourceInfo(source_info);
|
||||
}
|
||||
|
||||
bool input_is_observable = RegisterIsObservable(input_info->register_value());
|
||||
if (input_is_observable) {
|
||||
// If input is observable by the debugger, mark all other temporaries
|
||||
// registers as unmaterialized so that this register is used in preference.
|
||||
input_info->MarkTemporariesAsUnmaterialized(temporary_base_);
|
||||
}
|
||||
}
|
||||
|
||||
void BytecodeRegisterOptimizer::EmitNopForSourceInfo(
|
||||
@ -426,14 +456,14 @@ void BytecodeRegisterOptimizer::DoMov(BytecodeNode* node) {
|
||||
RegisterInfo* input_info = GetRegisterInfo(input);
|
||||
Register output = GetRegisterOutputOperand(
|
||||
1, node->bytecode(), node->operands(), node->operand_count());
|
||||
RegisterInfo* output_info = GetOrCreateRegisterInfo(output);
|
||||
RegisterInfo* output_info = GetRegisterInfo(output);
|
||||
RegisterTransfer(input_info, output_info, node->source_info_ptr());
|
||||
}
|
||||
|
||||
void BytecodeRegisterOptimizer::DoStar(BytecodeNode* node) {
|
||||
Register output = GetRegisterOutputOperand(
|
||||
0, node->bytecode(), node->operands(), node->operand_count());
|
||||
RegisterInfo* output_info = GetOrCreateRegisterInfo(output);
|
||||
RegisterInfo* output_info = GetRegisterInfo(output);
|
||||
RegisterTransfer(accumulator_info_, output_info, node->source_info_ptr());
|
||||
}
|
||||
|
||||
@ -451,7 +481,7 @@ void BytecodeRegisterOptimizer::PrepareRegisterRangeOutputOperand(
|
||||
Register start, int count) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
Register reg(start.index() + i);
|
||||
RegisterInfo* reg_info = GetOrCreateRegisterInfo(reg);
|
||||
RegisterInfo* reg_info = GetRegisterInfo(reg);
|
||||
PrepareRegisterOutputOperand(reg_info);
|
||||
}
|
||||
}
|
||||
@ -461,7 +491,7 @@ Register BytecodeRegisterOptimizer::GetEquivalentRegisterForInputOperand(
|
||||
// For a temporary register, RegInfo state may need be created. For
|
||||
// locals and parameters, the RegInfo state is created in the
|
||||
// BytecodeRegisterOptimizer constructor.
|
||||
RegisterInfo* reg_info = GetOrCreateRegisterInfo(reg);
|
||||
RegisterInfo* reg_info = GetRegisterInfo(reg);
|
||||
if (reg_info->materialized()) {
|
||||
return reg;
|
||||
} else {
|
||||
@ -570,8 +600,8 @@ Register BytecodeRegisterOptimizer::GetRegisterOutputOperand(
|
||||
BytecodeRegisterOptimizer::RegisterInfo*
|
||||
BytecodeRegisterOptimizer::GetRegisterInfo(Register reg) {
|
||||
size_t index = GetRegisterInfoTableIndex(reg);
|
||||
return (index < register_info_table_.size()) ? register_info_table_[index]
|
||||
: nullptr;
|
||||
DCHECK_LT(index, register_info_table_.size());
|
||||
return register_info_table_[index];
|
||||
}
|
||||
|
||||
BytecodeRegisterOptimizer::RegisterInfo*
|
||||
@ -592,26 +622,37 @@ BytecodeRegisterOptimizer::NewRegisterInfo(Register reg) {
|
||||
void BytecodeRegisterOptimizer::GrowRegisterMap(Register reg) {
|
||||
DCHECK(RegisterIsTemporary(reg));
|
||||
size_t index = GetRegisterInfoTableIndex(reg);
|
||||
DCHECK_GE(index, register_info_table_.size());
|
||||
if (index >= register_info_table_.size()) {
|
||||
size_t new_size = index + 1;
|
||||
size_t old_size = register_info_table_.size();
|
||||
register_info_table_.resize(new_size);
|
||||
for (size_t i = old_size; i < new_size; ++i) {
|
||||
register_info_table_[i] = new (zone()) RegisterInfo(
|
||||
RegisterFromRegisterInfoTableIndex(i), NextEquivalenceId(), false);
|
||||
register_info_table_[i] =
|
||||
new (zone()) RegisterInfo(RegisterFromRegisterInfoTableIndex(i),
|
||||
NextEquivalenceId(), false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BytecodeRegisterOptimizer::TemporaryRegisterFreeEvent(Register reg) {
|
||||
RegisterInfo* info = GetRegisterInfo(reg);
|
||||
if (info != nullptr) {
|
||||
// If register is materialized and part of equivalence set, make
|
||||
// sure another member of the set holds the value before the
|
||||
// temporary register is removed.
|
||||
if (info->materialized()) {
|
||||
CreateMaterializedEquivalent(info);
|
||||
void BytecodeRegisterOptimizer::RegisterAllocateEvent(Register reg) {
|
||||
GetOrCreateRegisterInfo(reg)->set_allocated(true);
|
||||
}
|
||||
info->MoveToNewEquivalenceSet(kInvalidEquivalenceId, false);
|
||||
|
||||
void BytecodeRegisterOptimizer::RegisterListAllocateEvent(
|
||||
RegisterList reg_list) {
|
||||
if (reg_list.register_count() != 0) {
|
||||
int first_index = reg_list.first_register().index();
|
||||
GrowRegisterMap(Register(first_index + reg_list.register_count() - 1));
|
||||
for (int i = 0; i < reg_list.register_count(); i++) {
|
||||
GetRegisterInfo(Register(first_index + i))->set_allocated(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BytecodeRegisterOptimizer::RegisterListFreeEvent(RegisterList reg_list) {
|
||||
int first_index = reg_list.first_register().index();
|
||||
for (int i = 0; i < reg_list.register_count(); i++) {
|
||||
GetRegisterInfo(Register(first_index + i))->set_allocated(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,13 +15,14 @@ namespace interpreter {
|
||||
// registers. The bytecode generator uses temporary registers
|
||||
// liberally for correctness and convenience and this stage removes
|
||||
// transfers that are not required and preserves correctness.
|
||||
class BytecodeRegisterOptimizer final : public BytecodePipelineStage,
|
||||
public TemporaryRegisterObserver,
|
||||
class BytecodeRegisterOptimizer final
|
||||
: public BytecodePipelineStage,
|
||||
public BytecodeRegisterAllocator::Observer,
|
||||
public ZoneObject {
|
||||
public:
|
||||
BytecodeRegisterOptimizer(Zone* zone,
|
||||
TemporaryRegisterAllocator* register_allocator,
|
||||
int parameter_count,
|
||||
BytecodeRegisterAllocator* register_allocator,
|
||||
int fixed_registers_count, int parameter_count,
|
||||
BytecodePipelineStage* next_stage);
|
||||
virtual ~BytecodeRegisterOptimizer() {}
|
||||
|
||||
@ -39,8 +40,10 @@ class BytecodeRegisterOptimizer final : public BytecodePipelineStage,
|
||||
|
||||
class RegisterInfo;
|
||||
|
||||
// TemporaryRegisterObserver interface.
|
||||
void TemporaryRegisterFreeEvent(Register reg) override;
|
||||
// BytecodeRegisterAllocator::Observer interface.
|
||||
void RegisterAllocateEvent(Register reg) override;
|
||||
void RegisterListAllocateEvent(RegisterList reg_list) override;
|
||||
void RegisterListFreeEvent(RegisterList reg) override;
|
||||
|
||||
// Helpers for BytecodePipelineStage interface.
|
||||
void FlushState();
|
||||
|
@ -146,6 +146,9 @@ PeepholeActionAndData PeepholeActionTableWriter::LookupActionAndData(
|
||||
Bytecode::kIllegal};
|
||||
}
|
||||
|
||||
// TODO(rmcilroy): Add elide for consecutive mov to and from the same
|
||||
// register.
|
||||
|
||||
// Remove ToBoolean coercion from conditional jumps where possible.
|
||||
if (Bytecodes::WritesBooleanToAccumulator(last)) {
|
||||
if (Bytecodes::IsJumpIfToBoolean(current)) {
|
||||
|
@ -993,7 +993,6 @@
|
||||
'interpreter/bytecode-pipeline.h',
|
||||
'interpreter/bytecode-register.cc',
|
||||
'interpreter/bytecode-register.h',
|
||||
'interpreter/bytecode-register-allocator.cc',
|
||||
'interpreter/bytecode-register-allocator.h',
|
||||
'interpreter/bytecode-register-optimizer.cc',
|
||||
'interpreter/bytecode-register-optimizer.h',
|
||||
|
@ -65,7 +65,7 @@ snippet: "
|
||||
x = x + (x = 100) + (x = 101);
|
||||
return x;
|
||||
"
|
||||
frame size: 3
|
||||
frame size: 2
|
||||
parameter count: 1
|
||||
bytecode array length: 28
|
||||
bytecodes: [
|
||||
@ -76,10 +76,10 @@ bytecodes: [
|
||||
B(Mov), R(0), R(1),
|
||||
B(Star), R(0),
|
||||
/* 57 E> */ B(Add), R(1), U8(2),
|
||||
B(Star), R(2),
|
||||
B(Star), R(1),
|
||||
B(LdaSmi), U8(101),
|
||||
B(Star), R(0),
|
||||
/* 69 E> */ B(Add), R(2), U8(3),
|
||||
/* 69 E> */ B(Add), R(1), U8(3),
|
||||
B(Star), R(0),
|
||||
/* 77 S> */ B(Nop),
|
||||
/* 87 S> */ B(Return),
|
||||
@ -96,7 +96,7 @@ snippet: "
|
||||
x++;
|
||||
return x;
|
||||
"
|
||||
frame size: 3
|
||||
frame size: 2
|
||||
parameter count: 1
|
||||
bytecode array length: 29
|
||||
bytecodes: [
|
||||
@ -106,10 +106,10 @@ bytecodes: [
|
||||
/* 46 S> */ B(LdaSmi), U8(56),
|
||||
B(Star), R(0),
|
||||
/* 61 E> */ B(Sub), R(0), U8(2),
|
||||
B(Star), R(2),
|
||||
B(Star), R(1),
|
||||
B(LdaSmi), U8(57),
|
||||
B(Star), R(0),
|
||||
/* 68 E> */ B(Add), R(2), U8(3),
|
||||
/* 68 E> */ B(Add), R(1), U8(3),
|
||||
B(Star), R(0),
|
||||
/* 75 S> */ B(Inc), U8(4),
|
||||
B(Star), R(0),
|
||||
@ -127,7 +127,7 @@ snippet: "
|
||||
var y = x + (x = 1) + (x = 2) + (x = 3);
|
||||
return y;
|
||||
"
|
||||
frame size: 4
|
||||
frame size: 3
|
||||
parameter count: 1
|
||||
bytecode array length: 37
|
||||
bytecodes: [
|
||||
@ -138,10 +138,10 @@ bytecodes: [
|
||||
B(Mov), R(0), R(2),
|
||||
B(Star), R(0),
|
||||
/* 61 E> */ B(Add), R(2), U8(2),
|
||||
B(Star), R(3),
|
||||
B(Star), R(2),
|
||||
B(LdaSmi), U8(2),
|
||||
B(Star), R(0),
|
||||
/* 71 E> */ B(Add), R(3), U8(3),
|
||||
/* 71 E> */ B(Add), R(2), U8(3),
|
||||
B(Star), R(2),
|
||||
B(LdaSmi), U8(3),
|
||||
B(Star), R(0),
|
||||
@ -161,7 +161,7 @@ snippet: "
|
||||
var x = x + (x = 1) + (x = 2) + (x = 3);
|
||||
return x;
|
||||
"
|
||||
frame size: 3
|
||||
frame size: 2
|
||||
parameter count: 1
|
||||
bytecode array length: 37
|
||||
bytecodes: [
|
||||
@ -172,10 +172,10 @@ bytecodes: [
|
||||
B(Mov), R(0), R(1),
|
||||
B(Star), R(0),
|
||||
/* 61 E> */ B(Add), R(1), U8(2),
|
||||
B(Star), R(2),
|
||||
B(Star), R(1),
|
||||
B(LdaSmi), U8(2),
|
||||
B(Star), R(0),
|
||||
/* 71 E> */ B(Add), R(2), U8(3),
|
||||
/* 71 E> */ B(Add), R(1), U8(3),
|
||||
B(Star), R(1),
|
||||
B(LdaSmi), U8(3),
|
||||
B(Star), R(0),
|
||||
@ -194,7 +194,7 @@ snippet: "
|
||||
var x = 10, y = 20;
|
||||
return x + (x = 1) + (x + 1) * (y = 2) + (y = 3) + (x = 4) + (y = 5) + y;
|
||||
"
|
||||
frame size: 5
|
||||
frame size: 4
|
||||
parameter count: 1
|
||||
bytecode array length: 72
|
||||
bytecodes: [
|
||||
@ -207,28 +207,28 @@ bytecodes: [
|
||||
B(Mov), R(0), R(2),
|
||||
B(Star), R(0),
|
||||
/* 68 E> */ B(Add), R(2), U8(2),
|
||||
B(Star), R(3),
|
||||
B(Star), R(2),
|
||||
/* 76 E> */ B(AddSmi), U8(1), R(0), U8(3),
|
||||
B(Star), R(4),
|
||||
B(Star), R(3),
|
||||
B(LdaSmi), U8(2),
|
||||
B(Star), R(1),
|
||||
/* 88 E> */ B(Mul), R(4), U8(4),
|
||||
B(Add), R(3), U8(5),
|
||||
/* 88 E> */ B(Mul), R(3), U8(4),
|
||||
B(Add), R(2), U8(5),
|
||||
B(Star), R(2),
|
||||
B(LdaSmi), U8(3),
|
||||
B(Star), R(1),
|
||||
/* 98 E> */ B(Add), R(2), U8(6),
|
||||
B(Star), R(3),
|
||||
B(Star), R(2),
|
||||
B(LdaSmi), U8(4),
|
||||
B(Star), R(0),
|
||||
/* 108 E> */ B(Add), R(3), U8(7),
|
||||
/* 108 E> */ B(Add), R(2), U8(7),
|
||||
B(Star), R(2),
|
||||
B(LdaSmi), U8(5),
|
||||
B(Star), R(1),
|
||||
/* 118 E> */ B(Add), R(2), U8(8),
|
||||
B(Star), R(3),
|
||||
B(Star), R(2),
|
||||
B(Ldar), R(1),
|
||||
/* 125 E> */ B(Add), R(3), U8(9),
|
||||
/* 125 E> */ B(Add), R(2), U8(9),
|
||||
/* 128 S> */ B(Return),
|
||||
]
|
||||
constant pool: [
|
||||
@ -241,7 +241,7 @@ snippet: "
|
||||
var x = 17;
|
||||
return 1 + x + (x++) + (++x);
|
||||
"
|
||||
frame size: 4
|
||||
frame size: 3
|
||||
parameter count: 1
|
||||
bytecode array length: 41
|
||||
bytecodes: [
|
||||
@ -252,18 +252,18 @@ bytecodes: [
|
||||
B(Star), R(1),
|
||||
B(Ldar), R(0),
|
||||
/* 57 E> */ B(Add), R(1), U8(2),
|
||||
B(Star), R(2),
|
||||
B(Star), R(1),
|
||||
B(Ldar), R(0),
|
||||
B(ToNumber), R(1),
|
||||
B(ToNumber), R(2),
|
||||
B(Inc), U8(3),
|
||||
B(Star), R(0),
|
||||
B(Ldar), R(1),
|
||||
/* 63 E> */ B(Add), R(2), U8(4),
|
||||
B(Star), R(3),
|
||||
B(Ldar), R(2),
|
||||
/* 63 E> */ B(Add), R(1), U8(4),
|
||||
B(Star), R(1),
|
||||
B(Ldar), R(0),
|
||||
B(Inc), U8(5),
|
||||
B(Star), R(0),
|
||||
/* 72 E> */ B(Add), R(3), U8(6),
|
||||
/* 72 E> */ B(Add), R(1), U8(6),
|
||||
/* 76 S> */ B(Return),
|
||||
]
|
||||
constant pool: [
|
||||
|
@ -47,7 +47,7 @@ snippet: "
|
||||
}
|
||||
return sum;
|
||||
"
|
||||
frame size: 5
|
||||
frame size: 4
|
||||
parameter count: 1
|
||||
bytecode array length: 69
|
||||
bytecodes: [
|
||||
@ -71,9 +71,9 @@ bytecodes: [
|
||||
B(Star), R(0),
|
||||
/* 142 S> */ B(Ldar), R(2),
|
||||
/* 150 E> */ B(Add), R(1), U8(7),
|
||||
B(Star), R(4),
|
||||
B(Star), R(3),
|
||||
B(LdaSmi), U8(12),
|
||||
/* 152 E> */ B(TestEqual), R(4), U8(8),
|
||||
/* 152 E> */ B(TestEqual), R(3), U8(8),
|
||||
B(JumpIfFalse), U8(4),
|
||||
/* 161 S> */ B(Jump), U8(20),
|
||||
/* 118 S> */ B(Ldar), R(2),
|
||||
|
@ -26,8 +26,8 @@ bytecodes: [
|
||||
/* 34 S> */ B(CreateClosure), U8(0), U8(2),
|
||||
/* 36 E> */ B(StaLookupSlotSloppy), U8(1),
|
||||
/* 52 S> */ B(LdaConstant), U8(2),
|
||||
B(Star), R(3),
|
||||
B(CallRuntimeForPair), U16(Runtime::kLoadLookupSlotForCall), R(3), U8(1), R(1),
|
||||
B(Star), R(4),
|
||||
B(CallRuntimeForPair), U16(Runtime::kLoadLookupSlotForCall), R(4), U8(1), R(1),
|
||||
B(LdaConstant), U8(3),
|
||||
B(Star), R(3),
|
||||
B(LdaZero),
|
||||
|
@ -35,8 +35,8 @@ bytecodes: [
|
||||
B(CallRuntime), U16(Runtime::kLoadFromSuper), R(3), U8(3),
|
||||
B(Star), R(1),
|
||||
/* 117 E> */ B(Call), R(1), R(this), U8(1), U8(2),
|
||||
B(Star), R(3),
|
||||
B(AddSmi), U8(1), R(3), U8(8),
|
||||
B(Star), R(1),
|
||||
B(AddSmi), U8(1), R(1), U8(8),
|
||||
/* 131 S> */ B(Return),
|
||||
]
|
||||
constant pool: [
|
||||
|
@ -223,8 +223,8 @@ bytecodes: [
|
||||
B(Star), R(2),
|
||||
/* 87 S> */ B(JumpIfNotHole), U8(11),
|
||||
B(LdaConstant), U8(2),
|
||||
B(Star), R(5),
|
||||
B(CallRuntime), U16(Runtime::kThrowReferenceError), R(5), U8(1),
|
||||
B(Star), R(4),
|
||||
B(CallRuntime), U16(Runtime::kThrowReferenceError), R(4), U8(1),
|
||||
B(Star), R(4),
|
||||
/* 94 E> */ B(New), R(4), R(0), U8(0), U8(4),
|
||||
/* 103 S> */ B(Return),
|
||||
|
@ -79,7 +79,7 @@ snippet: "
|
||||
function f(a, ...restArgs) { return restArgs[0] + arguments[0]; }
|
||||
f();
|
||||
"
|
||||
frame size: 5
|
||||
frame size: 4
|
||||
parameter count: 2
|
||||
bytecode array length: 26
|
||||
bytecodes: [
|
||||
@ -92,10 +92,10 @@ bytecodes: [
|
||||
/* 10 E> */ B(StackCheck),
|
||||
B(Mov), R(arg0), R(1),
|
||||
/* 29 S> */ B(LdaZero),
|
||||
/* 44 E> */ B(LdrKeyedProperty), R(0), U8(2), R(4),
|
||||
/* 44 E> */ B(LdrKeyedProperty), R(0), U8(2), R(3),
|
||||
B(LdaZero),
|
||||
/* 59 E> */ B(LdaKeyedProperty), R(2), U8(4),
|
||||
B(Add), R(4), U8(6),
|
||||
B(Add), R(3), U8(6),
|
||||
/* 64 S> */ B(Return),
|
||||
]
|
||||
constant pool: [
|
||||
|
@ -24,8 +24,8 @@ bytecodes: [
|
||||
B(StaContextSlot), R(context), U8(5), U8(0),
|
||||
/* 30 E> */ B(StackCheck),
|
||||
/* 34 S> */ B(LdaConstant), U8(0),
|
||||
B(Star), R(3),
|
||||
B(CallRuntimeForPair), U16(Runtime::kLoadLookupSlotForCall), R(3), U8(1), R(1),
|
||||
B(Star), R(4),
|
||||
B(CallRuntimeForPair), U16(Runtime::kLoadLookupSlotForCall), R(4), U8(1), R(1),
|
||||
B(LdaConstant), U8(1),
|
||||
B(Star), R(3),
|
||||
B(LdaZero),
|
||||
|
@ -146,7 +146,7 @@ snippet: "
|
||||
if (x['a'] == 20) break;
|
||||
}
|
||||
"
|
||||
frame size: 8
|
||||
frame size: 7
|
||||
parameter count: 1
|
||||
bytecode array length: 83
|
||||
bytecodes: [
|
||||
@ -168,15 +168,15 @@ bytecodes: [
|
||||
/* 67 E> */ B(StaNamedPropertySloppy), R(0), U8(2), U8(10),
|
||||
/* 62 E> */ B(StackCheck),
|
||||
/* 95 S> */ B(Nop),
|
||||
/* 100 E> */ B(LdrNamedProperty), R(0), U8(2), U8(4), R(7),
|
||||
/* 100 E> */ B(LdrNamedProperty), R(0), U8(2), U8(4), R(6),
|
||||
B(LdaSmi), U8(10),
|
||||
/* 106 E> */ B(TestEqual), R(7), U8(6),
|
||||
/* 106 E> */ B(TestEqual), R(6), U8(6),
|
||||
B(JumpIfFalse), U8(4),
|
||||
/* 113 S> */ B(Jump), U8(17),
|
||||
/* 125 S> */ B(Nop),
|
||||
/* 130 E> */ B(LdrNamedProperty), R(0), U8(2), U8(7), R(7),
|
||||
/* 130 E> */ B(LdrNamedProperty), R(0), U8(2), U8(7), R(6),
|
||||
B(LdaSmi), U8(20),
|
||||
/* 136 E> */ B(TestEqual), R(7), U8(9),
|
||||
/* 136 E> */ B(TestEqual), R(6), U8(9),
|
||||
B(JumpIfFalse), U8(4),
|
||||
/* 143 S> */ B(Jump), U8(9),
|
||||
B(ForInStep), R(5),
|
||||
|
@ -181,17 +181,17 @@ bytecodes: [
|
||||
B(LdaFalse),
|
||||
B(Star), R(6),
|
||||
B(CallRuntime), U16(Runtime::k_CreateIterResultObject), R(5), U8(2),
|
||||
B(Star), R(7),
|
||||
B(LdrContextSlot), R(context), U8(5), U8(0), R(5),
|
||||
B(Star), R(5),
|
||||
B(LdrContextSlot), R(context), U8(5), U8(0), R(6),
|
||||
B(LdaSmi), U8(1),
|
||||
B(SuspendGenerator), R(5),
|
||||
B(Ldar), R(7),
|
||||
B(SuspendGenerator), R(6),
|
||||
B(Ldar), R(5),
|
||||
/* 25 S> */ B(Return),
|
||||
B(LdaSmi), U8(-2),
|
||||
B(Star), R(1),
|
||||
B(CallRuntime), U16(Runtime::k_GeneratorGetInputOrDebugPos), R(5), U8(1),
|
||||
B(Star), R(6),
|
||||
B(CallRuntime), U16(Runtime::k_GeneratorGetResumeMode), R(5), U8(1),
|
||||
B(CallRuntime), U16(Runtime::k_GeneratorGetInputOrDebugPos), R(6), U8(1),
|
||||
B(Star), R(7),
|
||||
B(CallRuntime), U16(Runtime::k_GeneratorGetResumeMode), R(6), U8(1),
|
||||
B(Star), R(8),
|
||||
B(LdaZero),
|
||||
B(TestEqualStrict), R(8), U8(0),
|
||||
@ -202,13 +202,13 @@ bytecodes: [
|
||||
B(Jump), U8(2),
|
||||
B(LdaTrue),
|
||||
B(Star), R(10),
|
||||
B(Mov), R(6), R(9),
|
||||
B(Mov), R(7), R(9),
|
||||
B(CallRuntime), U16(Runtime::k_CreateIterResultObject), R(9), U8(2),
|
||||
B(Star), R(3),
|
||||
B(LdaSmi), U8(1),
|
||||
B(Star), R(2),
|
||||
B(Jump), U8(35),
|
||||
B(Ldar), R(6),
|
||||
B(Ldar), R(7),
|
||||
/* 16 E> */ B(Throw),
|
||||
B(LdrUndefined), R(5),
|
||||
B(LdaTrue),
|
||||
@ -267,7 +267,7 @@ snippet: "
|
||||
"
|
||||
frame size: 18
|
||||
parameter count: 1
|
||||
bytecode array length: 807
|
||||
bytecode array length: 805
|
||||
bytecodes: [
|
||||
B(Ldar), R(new_target),
|
||||
B(JumpIfUndefined), U8(28),
|
||||
@ -379,23 +379,23 @@ bytecodes: [
|
||||
/* 36 S> */ B(LdaContextSlot), R(context), U8(4), U8(0),
|
||||
B(JumpIfNotHole), U8(11),
|
||||
B(LdaConstant), U8(8),
|
||||
B(Star), R(13),
|
||||
B(CallRuntime), U16(Runtime::kThrowReferenceError), R(13), U8(1),
|
||||
B(Star), R(14),
|
||||
B(CallRuntime), U16(Runtime::kThrowReferenceError), R(14), U8(1),
|
||||
B(Star), R(12),
|
||||
B(LdaFalse),
|
||||
B(Star), R(13),
|
||||
B(CallRuntime), U16(Runtime::k_CreateIterResultObject), R(12), U8(2),
|
||||
B(Star), R(14),
|
||||
B(LdrContextSlot), R(1), U8(5), U8(0), R(12),
|
||||
B(Star), R(12),
|
||||
B(LdrContextSlot), R(1), U8(5), U8(0), R(13),
|
||||
B(LdaSmi), U8(1),
|
||||
B(SuspendGenerator), R(12),
|
||||
B(Ldar), R(14),
|
||||
B(SuspendGenerator), R(13),
|
||||
B(Ldar), R(12),
|
||||
/* 44 S> */ B(Return),
|
||||
B(LdaSmi), U8(-2),
|
||||
B(Star), R(4),
|
||||
B(CallRuntime), U16(Runtime::k_GeneratorGetInputOrDebugPos), R(12), U8(1),
|
||||
B(Star), R(13),
|
||||
B(CallRuntime), U16(Runtime::k_GeneratorGetResumeMode), R(12), U8(1),
|
||||
B(CallRuntime), U16(Runtime::k_GeneratorGetInputOrDebugPos), R(13), U8(1),
|
||||
B(Star), R(14),
|
||||
B(CallRuntime), U16(Runtime::k_GeneratorGetResumeMode), R(13), U8(1),
|
||||
B(Star), R(15),
|
||||
B(LdaZero),
|
||||
B(TestEqualStrict), R(15), U8(0),
|
||||
@ -406,7 +406,7 @@ bytecodes: [
|
||||
B(Jump), U8(2),
|
||||
B(LdaTrue),
|
||||
B(Star), R(17),
|
||||
B(Mov), R(13), R(16),
|
||||
B(Mov), R(14), R(16),
|
||||
B(CallRuntime), U16(Runtime::k_CreateIterResultObject), R(16), U8(2),
|
||||
B(PopContext), R(2),
|
||||
B(PopContext), R(2),
|
||||
@ -417,14 +417,13 @@ bytecodes: [
|
||||
B(Star), R(9),
|
||||
B(LdaZero),
|
||||
B(Star), R(8),
|
||||
B(Jump), U8(76),
|
||||
B(Ldar), R(13),
|
||||
B(Jump), U8(74),
|
||||
B(Ldar), R(14),
|
||||
/* 36 E> */ B(Throw),
|
||||
B(Ldar), R(13),
|
||||
B(PopContext), R(2),
|
||||
B(LdaZero),
|
||||
B(StaContextSlot), R(1), U8(9), U8(0),
|
||||
B(Wide), B(JumpLoop), U16(-234), U16(0),
|
||||
B(Wide), B(JumpLoop), U16(-232), U16(0),
|
||||
B(Jump), U8(44),
|
||||
B(Star), R(12),
|
||||
B(Ldar), R(closure),
|
||||
@ -585,7 +584,7 @@ constant pool: [
|
||||
FIXED_ARRAY_TYPE,
|
||||
ONE_BYTE_INTERNALIZED_STRING_TYPE ["x"],
|
||||
Smi [146],
|
||||
Smi [169],
|
||||
Smi [167],
|
||||
ONE_BYTE_INTERNALIZED_STRING_TYPE [".catch"],
|
||||
FIXED_ARRAY_TYPE,
|
||||
ONE_BYTE_INTERNALIZED_STRING_TYPE ["return"],
|
||||
@ -595,12 +594,12 @@ constant pool: [
|
||||
Smi [129],
|
||||
Smi [166],
|
||||
Smi [155],
|
||||
Smi [603],
|
||||
Smi [601],
|
||||
]
|
||||
handlers: [
|
||||
[48, 720, 726],
|
||||
[153, 460, 466],
|
||||
[156, 416, 418],
|
||||
[574, 588, 590],
|
||||
[48, 718, 724],
|
||||
[153, 458, 464],
|
||||
[156, 414, 416],
|
||||
[572, 586, 588],
|
||||
]
|
||||
|
||||
|
@ -58,7 +58,7 @@ snippet: "
|
||||
"
|
||||
frame size: 3
|
||||
parameter count: 1
|
||||
bytecode array length: 26
|
||||
bytecode array length: 29
|
||||
bytecodes: [
|
||||
B(LdaTheHole),
|
||||
B(Star), R(0),
|
||||
@ -70,6 +70,7 @@ bytecodes: [
|
||||
B(LdaConstant), U8(0),
|
||||
B(Star), R(2),
|
||||
/* 45 E> */ B(CallRuntime), U16(Runtime::kThrowReferenceError), R(2), U8(1),
|
||||
B(Mov), R(1), R(0),
|
||||
B(Mov), R(1), R(0),
|
||||
B(LdaUndefined),
|
||||
/* 52 S> */ B(Return),
|
||||
|
@ -25,8 +25,8 @@ bytecodes: [
|
||||
B(StaContextSlot), R(context), U8(5), U8(0),
|
||||
/* 10 E> */ B(StackCheck),
|
||||
/* 14 S> */ B(LdaConstant), U8(0),
|
||||
B(Star), R(3),
|
||||
B(CallRuntimeForPair), U16(Runtime::kLoadLookupSlotForCall), R(3), U8(1), R(1),
|
||||
B(Star), R(4),
|
||||
B(CallRuntimeForPair), U16(Runtime::kLoadLookupSlotForCall), R(4), U8(1), R(1),
|
||||
B(LdaConstant), U8(1),
|
||||
B(Star), R(3),
|
||||
B(LdaZero),
|
||||
@ -70,8 +70,8 @@ bytecodes: [
|
||||
B(StaContextSlot), R(context), U8(5), U8(0),
|
||||
/* 10 E> */ B(StackCheck),
|
||||
/* 14 S> */ B(LdaConstant), U8(0),
|
||||
B(Star), R(3),
|
||||
B(CallRuntimeForPair), U16(Runtime::kLoadLookupSlotForCall), R(3), U8(1), R(1),
|
||||
B(Star), R(4),
|
||||
B(CallRuntimeForPair), U16(Runtime::kLoadLookupSlotForCall), R(4), U8(1), R(1),
|
||||
B(LdaConstant), U8(1),
|
||||
B(Star), R(3),
|
||||
B(LdaZero),
|
||||
@ -118,8 +118,8 @@ bytecodes: [
|
||||
/* 14 S> */ B(LdaSmi), U8(20),
|
||||
/* 16 E> */ B(StaLookupSlotSloppy), U8(0),
|
||||
/* 22 S> */ B(LdaConstant), U8(1),
|
||||
B(Star), R(3),
|
||||
B(CallRuntimeForPair), U16(Runtime::kLoadLookupSlotForCall), R(3), U8(1), R(1),
|
||||
B(Star), R(4),
|
||||
B(CallRuntimeForPair), U16(Runtime::kLoadLookupSlotForCall), R(4), U8(1), R(1),
|
||||
B(LdaConstant), U8(2),
|
||||
B(Star), R(3),
|
||||
B(LdaZero),
|
||||
@ -167,8 +167,8 @@ bytecodes: [
|
||||
B(StaContextSlot), R(context), U8(5), U8(0),
|
||||
/* 38 E> */ B(StackCheck),
|
||||
/* 44 S> */ B(LdaConstant), U8(0),
|
||||
B(Star), R(3),
|
||||
B(CallRuntimeForPair), U16(Runtime::kLoadLookupSlotForCall), R(3), U8(1), R(1),
|
||||
B(Star), R(4),
|
||||
B(CallRuntimeForPair), U16(Runtime::kLoadLookupSlotForCall), R(4), U8(1), R(1),
|
||||
B(LdaConstant), U8(1),
|
||||
B(Star), R(3),
|
||||
B(LdaZero),
|
||||
@ -217,8 +217,8 @@ bytecodes: [
|
||||
B(StaContextSlot), R(context), U8(5), U8(0),
|
||||
/* 34 E> */ B(StackCheck),
|
||||
/* 40 S> */ B(LdaConstant), U8(0),
|
||||
B(Star), R(3),
|
||||
B(CallRuntimeForPair), U16(Runtime::kLoadLookupSlotForCall), R(3), U8(1), R(1),
|
||||
B(Star), R(4),
|
||||
B(CallRuntimeForPair), U16(Runtime::kLoadLookupSlotForCall), R(4), U8(1), R(1),
|
||||
B(LdaConstant), U8(1),
|
||||
B(Star), R(3),
|
||||
B(LdaZero),
|
||||
|
@ -157,7 +157,7 @@ bytecodes: [
|
||||
B(Star), R(5),
|
||||
B(Mov), R(0), R(1),
|
||||
B(CallRuntime), U16(Runtime::kDefineAccessorPropertyUnchecked), R(1), U8(5),
|
||||
B(Ldar), R(0),
|
||||
B(Ldar), R(1),
|
||||
/* 68 S> */ B(Return),
|
||||
]
|
||||
constant pool: [
|
||||
@ -188,7 +188,7 @@ bytecodes: [
|
||||
B(Star), R(5),
|
||||
B(Mov), R(0), R(1),
|
||||
B(CallRuntime), U16(Runtime::kDefineAccessorPropertyUnchecked), R(1), U8(5),
|
||||
B(Ldar), R(0),
|
||||
B(Ldar), R(1),
|
||||
/* 102 S> */ B(Return),
|
||||
]
|
||||
constant pool: [
|
||||
@ -220,7 +220,7 @@ bytecodes: [
|
||||
B(Star), R(5),
|
||||
B(Mov), R(0), R(1),
|
||||
B(CallRuntime), U16(Runtime::kDefineAccessorPropertyUnchecked), R(1), U8(5),
|
||||
B(Ldar), R(0),
|
||||
B(Ldar), R(1),
|
||||
/* 74 S> */ B(Return),
|
||||
]
|
||||
constant pool: [
|
||||
@ -250,7 +250,7 @@ bytecodes: [
|
||||
B(Mov), R(1), R(2),
|
||||
B(Mov), R(0), R(4),
|
||||
/* 57 E> */ B(CallRuntime), U16(Runtime::kSetProperty), R(2), U8(4),
|
||||
B(Ldar), R(1),
|
||||
B(Ldar), R(2),
|
||||
/* 62 S> */ B(Return),
|
||||
]
|
||||
constant pool: [
|
||||
@ -273,7 +273,7 @@ bytecodes: [
|
||||
B(Star), R(2),
|
||||
B(Mov), R(0), R(1),
|
||||
B(CallRuntime), U16(Runtime::kInternalSetPrototype), R(1), U8(2),
|
||||
B(Ldar), R(0),
|
||||
B(Ldar), R(1),
|
||||
/* 62 S> */ B(Return),
|
||||
]
|
||||
constant pool: [
|
||||
@ -303,7 +303,7 @@ bytecodes: [
|
||||
B(Star), R(6),
|
||||
B(Mov), R(1), R(2),
|
||||
B(CallRuntime), U16(Runtime::kDefineDataPropertyInLiteral), R(2), U8(5),
|
||||
B(Ldar), R(1),
|
||||
B(Ldar), R(2),
|
||||
/* 69 S> */ B(Return),
|
||||
]
|
||||
constant pool: [
|
||||
@ -335,7 +335,7 @@ bytecodes: [
|
||||
B(Star), R(6),
|
||||
B(Mov), R(1), R(2),
|
||||
B(CallRuntime), U16(Runtime::kDefineDataPropertyInLiteral), R(2), U8(5),
|
||||
B(Ldar), R(1),
|
||||
B(Ldar), R(2),
|
||||
/* 77 S> */ B(Return),
|
||||
]
|
||||
constant pool: [
|
||||
@ -352,7 +352,7 @@ snippet: "
|
||||
"
|
||||
frame size: 7
|
||||
parameter count: 1
|
||||
bytecode array length: 49
|
||||
bytecode array length: 46
|
||||
bytecodes: [
|
||||
/* 30 E> */ B(StackCheck),
|
||||
/* 42 S> */ B(LdaConstant), U8(0),
|
||||
@ -368,10 +368,9 @@ bytecodes: [
|
||||
B(Mov), R(1), R(2),
|
||||
B(CallRuntime), U16(Runtime::kDefineDataPropertyInLiteral), R(2), U8(5),
|
||||
B(CreateObjectLiteral), U8(1), U8(0), U8(35), R(4),
|
||||
B(Mov), R(1), R(2),
|
||||
B(Mov), R(4), R(3),
|
||||
B(CallRuntime), U16(Runtime::kInternalSetPrototype), R(2), U8(2),
|
||||
B(Ldar), R(1),
|
||||
B(Ldar), R(2),
|
||||
/* 84 S> */ B(Return),
|
||||
]
|
||||
constant pool: [
|
||||
@ -387,7 +386,7 @@ snippet: "
|
||||
"
|
||||
frame size: 7
|
||||
parameter count: 1
|
||||
bytecode array length: 73
|
||||
bytecode array length: 67
|
||||
bytecodes: [
|
||||
/* 30 E> */ B(StackCheck),
|
||||
/* 42 S> */ B(LdaConstant), U8(0),
|
||||
@ -408,7 +407,6 @@ bytecodes: [
|
||||
B(Star), R(4),
|
||||
B(LdaZero),
|
||||
B(Star), R(5),
|
||||
B(Mov), R(1), R(2),
|
||||
B(CallRuntime), U16(Runtime::kDefineGetterPropertyUnchecked), R(2), U8(4),
|
||||
B(LdaConstant), U8(3),
|
||||
B(ToName), R(3),
|
||||
@ -416,9 +414,8 @@ bytecodes: [
|
||||
B(Star), R(4),
|
||||
B(LdaZero),
|
||||
B(Star), R(5),
|
||||
B(Mov), R(1), R(2),
|
||||
B(CallRuntime), U16(Runtime::kDefineSetterPropertyUnchecked), R(2), U8(4),
|
||||
B(Ldar), R(1),
|
||||
B(Ldar), R(2),
|
||||
/* 99 S> */ B(Return),
|
||||
]
|
||||
constant pool: [
|
||||
|
@ -95,7 +95,7 @@ snippet: "
|
||||
var y = void (x * x - 1);
|
||||
return y;
|
||||
"
|
||||
frame size: 4
|
||||
frame size: 3
|
||||
parameter count: 1
|
||||
bytecode array length: 23
|
||||
bytecodes: [
|
||||
@ -104,8 +104,8 @@ bytecodes: [
|
||||
B(Star), R(0),
|
||||
/* 56 S> */ B(Nop),
|
||||
/* 66 E> */ B(Mul), R(0), U8(2),
|
||||
B(Star), R(3),
|
||||
B(SubSmi), U8(1), R(3), U8(3),
|
||||
B(Star), R(2),
|
||||
B(SubSmi), U8(1), R(2), U8(3),
|
||||
B(LdrUndefined), R(1),
|
||||
B(Ldar), R(1),
|
||||
/* 74 S> */ B(Nop),
|
||||
|
@ -10,16 +10,16 @@ wrap: yes
|
||||
snippet: "
|
||||
with ({x:42}) { return x; }
|
||||
"
|
||||
frame size: 3
|
||||
frame size: 2
|
||||
parameter count: 1
|
||||
bytecode array length: 22
|
||||
bytecodes: [
|
||||
/* 30 E> */ B(StackCheck),
|
||||
/* 34 S> */ B(CreateObjectLiteral), U8(0), U8(0), U8(1), R(1),
|
||||
B(Ldar), R(1),
|
||||
B(ToObject), R(2),
|
||||
B(ToObject), R(1),
|
||||
B(Ldar), R(closure),
|
||||
B(CreateWithContext), R(2), U8(1),
|
||||
B(CreateWithContext), R(1), U8(1),
|
||||
B(PushContext), R(0),
|
||||
/* 50 S> */ B(LdaLookupSlot), U8(2),
|
||||
B(PopContext), R(0),
|
||||
|
@ -26,8 +26,8 @@ class InvokeIntrinsicHelper {
|
||||
Handle<Object> Invoke(A... args) {
|
||||
CHECK(IntrinsicsHelper::IsSupported(function_id_));
|
||||
BytecodeArrayBuilder builder(isolate_, zone_, sizeof...(args), 0, 0);
|
||||
builder.CallRuntime(function_id_, builder.Parameter(0), sizeof...(args))
|
||||
.Return();
|
||||
RegisterList reg_list(builder.Parameter(0).index(), sizeof...(args));
|
||||
builder.CallRuntime(function_id_, reg_list).Return();
|
||||
InterpreterTester tester(isolate_, builder.ToBytecodeArray(isolate_));
|
||||
auto callable = tester.GetCallable<A...>();
|
||||
return callable(args...).ToHandleChecked();
|
||||
|
@ -1236,12 +1236,13 @@ static void TestInterpreterCall(TailCallMode tail_call_mode) {
|
||||
// Check with no args.
|
||||
{
|
||||
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 1);
|
||||
|
||||
Register reg = builder.register_allocator()->NewRegister();
|
||||
RegisterList args = builder.register_allocator()->NewRegisterList(1);
|
||||
builder.LoadNamedProperty(builder.Parameter(0), name, slot_index)
|
||||
.StoreAccumulatorInRegister(Register(0));
|
||||
.StoreAccumulatorInRegister(reg)
|
||||
.MoveRegister(builder.Parameter(0), args[0]);
|
||||
|
||||
builder.Call(Register(0), builder.Parameter(0), 1, call_slot_index,
|
||||
tail_call_mode);
|
||||
builder.Call(reg, args, call_slot_index, tail_call_mode);
|
||||
|
||||
builder.Return();
|
||||
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
|
||||
@ -1258,11 +1259,12 @@ static void TestInterpreterCall(TailCallMode tail_call_mode) {
|
||||
// Check that receiver is passed properly.
|
||||
{
|
||||
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 1);
|
||||
|
||||
Register reg = builder.register_allocator()->NewRegister();
|
||||
RegisterList args = builder.register_allocator()->NewRegisterList(1);
|
||||
builder.LoadNamedProperty(builder.Parameter(0), name, slot_index)
|
||||
.StoreAccumulatorInRegister(Register(0));
|
||||
builder.Call(Register(0), builder.Parameter(0), 1, call_slot_index,
|
||||
tail_call_mode);
|
||||
.StoreAccumulatorInRegister(reg)
|
||||
.MoveRegister(builder.Parameter(0), args[0]);
|
||||
builder.Call(reg, args, call_slot_index, tail_call_mode);
|
||||
builder.Return();
|
||||
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
|
||||
|
||||
@ -1281,17 +1283,19 @@ static void TestInterpreterCall(TailCallMode tail_call_mode) {
|
||||
// Check with two parameters (+ receiver).
|
||||
{
|
||||
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 4);
|
||||
Register reg = builder.register_allocator()->NewRegister();
|
||||
RegisterList args = builder.register_allocator()->NewRegisterList(3);
|
||||
|
||||
builder.LoadNamedProperty(builder.Parameter(0), name, slot_index)
|
||||
.StoreAccumulatorInRegister(Register(0))
|
||||
.StoreAccumulatorInRegister(reg)
|
||||
.LoadAccumulatorWithRegister(builder.Parameter(0))
|
||||
.StoreAccumulatorInRegister(Register(1))
|
||||
.StoreAccumulatorInRegister(args[0])
|
||||
.LoadLiteral(Smi::FromInt(51))
|
||||
.StoreAccumulatorInRegister(Register(2))
|
||||
.StoreAccumulatorInRegister(args[1])
|
||||
.LoadLiteral(Smi::FromInt(11))
|
||||
.StoreAccumulatorInRegister(Register(3));
|
||||
.StoreAccumulatorInRegister(args[2]);
|
||||
|
||||
builder.Call(Register(0), Register(1), 3, call_slot_index, tail_call_mode);
|
||||
builder.Call(reg, args, call_slot_index, tail_call_mode);
|
||||
|
||||
builder.Return();
|
||||
|
||||
@ -1311,33 +1315,35 @@ static void TestInterpreterCall(TailCallMode tail_call_mode) {
|
||||
// Check with 10 parameters (+ receiver).
|
||||
{
|
||||
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 12);
|
||||
Register reg = builder.register_allocator()->NewRegister();
|
||||
RegisterList args = builder.register_allocator()->NewRegisterList(11);
|
||||
|
||||
builder.LoadNamedProperty(builder.Parameter(0), name, slot_index)
|
||||
.StoreAccumulatorInRegister(Register(0))
|
||||
.StoreAccumulatorInRegister(reg)
|
||||
.LoadAccumulatorWithRegister(builder.Parameter(0))
|
||||
.StoreAccumulatorInRegister(Register(1))
|
||||
.StoreAccumulatorInRegister(args[0])
|
||||
.LoadLiteral(factory->NewStringFromAsciiChecked("a"))
|
||||
.StoreAccumulatorInRegister(Register(2))
|
||||
.StoreAccumulatorInRegister(args[1])
|
||||
.LoadLiteral(factory->NewStringFromAsciiChecked("b"))
|
||||
.StoreAccumulatorInRegister(Register(3))
|
||||
.StoreAccumulatorInRegister(args[2])
|
||||
.LoadLiteral(factory->NewStringFromAsciiChecked("c"))
|
||||
.StoreAccumulatorInRegister(Register(4))
|
||||
.StoreAccumulatorInRegister(args[3])
|
||||
.LoadLiteral(factory->NewStringFromAsciiChecked("d"))
|
||||
.StoreAccumulatorInRegister(Register(5))
|
||||
.StoreAccumulatorInRegister(args[4])
|
||||
.LoadLiteral(factory->NewStringFromAsciiChecked("e"))
|
||||
.StoreAccumulatorInRegister(Register(6))
|
||||
.StoreAccumulatorInRegister(args[5])
|
||||
.LoadLiteral(factory->NewStringFromAsciiChecked("f"))
|
||||
.StoreAccumulatorInRegister(Register(7))
|
||||
.StoreAccumulatorInRegister(args[6])
|
||||
.LoadLiteral(factory->NewStringFromAsciiChecked("g"))
|
||||
.StoreAccumulatorInRegister(Register(8))
|
||||
.StoreAccumulatorInRegister(args[7])
|
||||
.LoadLiteral(factory->NewStringFromAsciiChecked("h"))
|
||||
.StoreAccumulatorInRegister(Register(9))
|
||||
.StoreAccumulatorInRegister(args[8])
|
||||
.LoadLiteral(factory->NewStringFromAsciiChecked("i"))
|
||||
.StoreAccumulatorInRegister(Register(10))
|
||||
.StoreAccumulatorInRegister(args[9])
|
||||
.LoadLiteral(factory->NewStringFromAsciiChecked("j"))
|
||||
.StoreAccumulatorInRegister(Register(11));
|
||||
.StoreAccumulatorInRegister(args[10]);
|
||||
|
||||
builder.Call(Register(0), Register(1), 11, call_slot_index, tail_call_mode);
|
||||
builder.Call(reg, args, call_slot_index, tail_call_mode);
|
||||
|
||||
builder.Return();
|
||||
|
||||
@ -2112,12 +2118,13 @@ TEST(InterpreterCallRuntime) {
|
||||
Isolate* isolate = handles.main_isolate();
|
||||
|
||||
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 1, 0, 2);
|
||||
RegisterList args = builder.register_allocator()->NewRegisterList(2);
|
||||
|
||||
builder.LoadLiteral(Smi::FromInt(15))
|
||||
.StoreAccumulatorInRegister(Register(0))
|
||||
.StoreAccumulatorInRegister(args[0])
|
||||
.LoadLiteral(Smi::FromInt(40))
|
||||
.StoreAccumulatorInRegister(Register(1))
|
||||
.CallRuntime(Runtime::kAdd, Register(0), 2)
|
||||
.StoreAccumulatorInRegister(args[1])
|
||||
.CallRuntime(Runtime::kAdd, args)
|
||||
.Return();
|
||||
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
|
||||
|
||||
@ -2136,7 +2143,7 @@ TEST(InterpreterInvokeIntrinsic) {
|
||||
|
||||
builder.LoadLiteral(Smi::FromInt(15))
|
||||
.StoreAccumulatorInRegister(Register(0))
|
||||
.CallRuntime(Runtime::kInlineIsArray, Register(0), 1)
|
||||
.CallRuntime(Runtime::kInlineIsArray, Register(0))
|
||||
.Return();
|
||||
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
|
||||
|
||||
|
@ -33,6 +33,7 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
|
||||
Register reg(0);
|
||||
Register other(reg.index() + 1);
|
||||
Register wide(128);
|
||||
RegisterList reg_list;
|
||||
|
||||
// Emit argument creation operations.
|
||||
builder.CreateArguments(CreateArgumentsType::kMappedArguments)
|
||||
@ -123,16 +124,11 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
|
||||
.CreateObjectLiteral(factory->NewFixedArray(1), 0, 0, reg);
|
||||
|
||||
// Call operations.
|
||||
builder.Call(reg, other, 0, 1)
|
||||
.Call(reg, wide, 0, 1)
|
||||
.TailCall(reg, other, 0, 1)
|
||||
.TailCall(reg, wide, 0, 1)
|
||||
.CallRuntime(Runtime::kIsArray, reg, 1)
|
||||
.CallRuntime(Runtime::kIsArray, wide, 1)
|
||||
.CallRuntimeForPair(Runtime::kLoadLookupSlotForCall, reg, 1, other)
|
||||
.CallRuntimeForPair(Runtime::kLoadLookupSlotForCall, wide, 1, other)
|
||||
.CallJSRuntime(Context::SPREAD_ITERABLE_INDEX, reg, 1)
|
||||
.CallJSRuntime(Context::SPREAD_ITERABLE_INDEX, wide, 1);
|
||||
builder.Call(reg, reg_list, 1)
|
||||
.Call(reg, reg_list, 1, TailCallMode::kAllow)
|
||||
.CallRuntime(Runtime::kIsArray, reg)
|
||||
.CallRuntimeForPair(Runtime::kLoadLookupSlotForCall, reg_list, other)
|
||||
.CallJSRuntime(Context::SPREAD_ITERABLE_INDEX, reg_list);
|
||||
|
||||
// Emit binary operator invocations.
|
||||
builder.BinaryOperation(Token::Value::ADD, reg, 1)
|
||||
@ -179,8 +175,7 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
|
||||
builder.Delete(reg, LanguageMode::SLOPPY).Delete(reg, LanguageMode::STRICT);
|
||||
|
||||
// Emit new.
|
||||
builder.New(reg, reg, 0, 1);
|
||||
builder.New(wide, wide, 0, 1);
|
||||
builder.New(reg, reg_list, 1);
|
||||
|
||||
// Emit test operator invocations.
|
||||
builder.CompareOperation(Token::Value::EQ, reg, 1)
|
||||
@ -337,8 +332,7 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
|
||||
.ResumeGenerator(reg);
|
||||
|
||||
// Intrinsics handled by the interpreter.
|
||||
builder.CallRuntime(Runtime::kInlineIsArray, reg, 1)
|
||||
.CallRuntime(Runtime::kInlineIsArray, wide, 1);
|
||||
builder.CallRuntime(Runtime::kInlineIsArray, reg_list);
|
||||
|
||||
// Emit debugger bytecode.
|
||||
builder.Debugger();
|
||||
@ -359,7 +353,7 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
|
||||
// Generate BytecodeArray.
|
||||
Handle<BytecodeArray> the_array = builder.ToBytecodeArray(isolate());
|
||||
CHECK_EQ(the_array->frame_size(),
|
||||
builder.fixed_and_temporary_register_count() * kPointerSize);
|
||||
builder.total_register_count() * kPointerSize);
|
||||
|
||||
// Build scorecard of bytecodes encountered in the BytecodeArray.
|
||||
std::vector<int> scorecard(Bytecodes::ToByte(Bytecode::kLast) + 1);
|
||||
@ -428,21 +422,18 @@ TEST_F(BytecodeArrayBuilderTest, FrameSizesLookGood) {
|
||||
for (int contexts = 0; contexts < 4; contexts++) {
|
||||
for (int temps = 0; temps < 3; temps++) {
|
||||
BytecodeArrayBuilder builder(isolate(), zone(), 0, contexts, locals);
|
||||
BytecodeRegisterAllocator temporaries(
|
||||
zone(), builder.temporary_register_allocator());
|
||||
BytecodeRegisterAllocator* allocator(builder.register_allocator());
|
||||
for (int i = 0; i < locals + contexts; i++) {
|
||||
builder.LoadLiteral(Smi::FromInt(0));
|
||||
builder.StoreAccumulatorInRegister(Register(i));
|
||||
}
|
||||
for (int i = 0; i < temps; i++) {
|
||||
Register temp = allocator->NewRegister();
|
||||
builder.LoadLiteral(Smi::FromInt(0));
|
||||
builder.StoreAccumulatorInRegister(temporaries.NewRegister());
|
||||
}
|
||||
if (temps > 0) {
|
||||
builder.StoreAccumulatorInRegister(temp);
|
||||
// Ensure temporaries are used so not optimized away by the
|
||||
// register optimizer.
|
||||
builder.New(Register(locals + contexts), Register(locals + contexts),
|
||||
static_cast<size_t>(temps), 0);
|
||||
builder.ConvertAccumulatorToName(temp);
|
||||
}
|
||||
builder.Return();
|
||||
|
||||
@ -478,30 +469,6 @@ TEST_F(BytecodeArrayBuilderTest, Parameters) {
|
||||
}
|
||||
|
||||
|
||||
TEST_F(BytecodeArrayBuilderTest, RegisterType) {
|
||||
CanonicalHandleScope canonical(isolate());
|
||||
BytecodeArrayBuilder builder(isolate(), zone(), 10, 0, 3);
|
||||
BytecodeRegisterAllocator register_allocator(
|
||||
zone(), builder.temporary_register_allocator());
|
||||
Register temp0 = register_allocator.NewRegister();
|
||||
Register param0(builder.Parameter(0));
|
||||
Register param9(builder.Parameter(9));
|
||||
Register temp1 = register_allocator.NewRegister();
|
||||
Register reg0(0);
|
||||
Register reg1(1);
|
||||
Register reg2(2);
|
||||
Register temp2 = register_allocator.NewRegister();
|
||||
CHECK_EQ(builder.RegisterIsParameterOrLocal(temp0), false);
|
||||
CHECK_EQ(builder.RegisterIsParameterOrLocal(temp1), false);
|
||||
CHECK_EQ(builder.RegisterIsParameterOrLocal(temp2), false);
|
||||
CHECK_EQ(builder.RegisterIsParameterOrLocal(param0), true);
|
||||
CHECK_EQ(builder.RegisterIsParameterOrLocal(param9), true);
|
||||
CHECK_EQ(builder.RegisterIsParameterOrLocal(reg0), true);
|
||||
CHECK_EQ(builder.RegisterIsParameterOrLocal(reg1), true);
|
||||
CHECK_EQ(builder.RegisterIsParameterOrLocal(reg2), true);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(BytecodeArrayBuilderTest, Constants) {
|
||||
CanonicalHandleScope canonical(isolate());
|
||||
BytecodeArrayBuilder builder(isolate(), zone(), 0, 0, 0);
|
||||
|
@ -54,9 +54,9 @@ TEST_F(BytecodeArrayIteratorTest, IteratesBytecodeArray) {
|
||||
.LoadNamedProperty(reg_1, name, feedback_slot)
|
||||
.BinaryOperation(Token::Value::ADD, reg_0, 3)
|
||||
.StoreAccumulatorInRegister(param)
|
||||
.CallRuntimeForPair(Runtime::kLoadLookupSlotForCall, param, 1, reg_0)
|
||||
.CallRuntimeForPair(Runtime::kLoadLookupSlotForCall, param, reg_0)
|
||||
.ForInPrepare(reg_0, reg_0)
|
||||
.CallRuntime(Runtime::kLoadIC_Miss, reg_0, 1)
|
||||
.CallRuntime(Runtime::kLoadIC_Miss, reg_0)
|
||||
.Debugger()
|
||||
.LoadGlobal(0x10000000, TypeofMode::NOT_INSIDE_TYPEOF)
|
||||
.Return();
|
||||
|
@ -12,199 +12,83 @@ namespace v8 {
|
||||
namespace internal {
|
||||
namespace interpreter {
|
||||
|
||||
class TemporaryRegisterAllocatorTest : public TestWithIsolateAndZone {
|
||||
public:
|
||||
TemporaryRegisterAllocatorTest() : allocator_(zone(), 0) {}
|
||||
~TemporaryRegisterAllocatorTest() override {}
|
||||
TemporaryRegisterAllocator* allocator() { return &allocator_; }
|
||||
|
||||
private:
|
||||
TemporaryRegisterAllocator allocator_;
|
||||
};
|
||||
|
||||
TEST_F(TemporaryRegisterAllocatorTest, FirstAllocation) {
|
||||
CHECK_EQ(allocator()->allocation_count(), 0);
|
||||
int reg0_index = allocator()->BorrowTemporaryRegister();
|
||||
CHECK_EQ(reg0_index, 0);
|
||||
CHECK_EQ(allocator()->allocation_count(), 1);
|
||||
CHECK(allocator()->RegisterIsLive(Register(reg0_index)));
|
||||
allocator()->ReturnTemporaryRegister(reg0_index);
|
||||
CHECK(!allocator()->RegisterIsLive(Register(reg0_index)));
|
||||
CHECK_EQ(allocator()->allocation_count(), 1);
|
||||
CHECK(allocator()->first_temporary_register() == Register(0));
|
||||
CHECK(allocator()->last_temporary_register() == Register(0));
|
||||
}
|
||||
|
||||
TEST_F(TemporaryRegisterAllocatorTest, SimpleAllocations) {
|
||||
for (int i = 0; i < 13; i++) {
|
||||
int reg_index = allocator()->BorrowTemporaryRegister();
|
||||
CHECK_EQ(reg_index, i);
|
||||
CHECK_EQ(allocator()->allocation_count(), i + 1);
|
||||
}
|
||||
for (int i = 0; i < 13; i++) {
|
||||
CHECK(allocator()->RegisterIsLive(Register(i)));
|
||||
allocator()->ReturnTemporaryRegister(i);
|
||||
CHECK(!allocator()->RegisterIsLive(Register(i)));
|
||||
int reg_index = allocator()->BorrowTemporaryRegister();
|
||||
CHECK_EQ(reg_index, i);
|
||||
CHECK_EQ(allocator()->allocation_count(), 13);
|
||||
}
|
||||
for (int i = 0; i < 13; i++) {
|
||||
CHECK(allocator()->RegisterIsLive(Register(i)));
|
||||
allocator()->ReturnTemporaryRegister(i);
|
||||
CHECK(!allocator()->RegisterIsLive(Register(i)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TemporaryRegisterAllocatorTest, SimpleRangeAllocation) {
|
||||
static const int kRunLength = 7;
|
||||
int start = allocator()->PrepareForConsecutiveTemporaryRegisters(kRunLength);
|
||||
CHECK(!allocator()->RegisterIsLive(Register(start)));
|
||||
for (int i = 0; i < kRunLength; i++) {
|
||||
CHECK(!allocator()->RegisterIsLive(Register(start + i)));
|
||||
allocator()->BorrowConsecutiveTemporaryRegister(start + i);
|
||||
CHECK(allocator()->RegisterIsLive(Register(start + i)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TemporaryRegisterAllocatorTest, RangeAllocationAbuttingFree) {
|
||||
static const int kFreeCount = 3;
|
||||
static const int kRunLength = 6;
|
||||
|
||||
for (int i = 0; i < kFreeCount; i++) {
|
||||
int to_free = allocator()->BorrowTemporaryRegister();
|
||||
CHECK_EQ(to_free, i);
|
||||
}
|
||||
for (int i = 0; i < kFreeCount; i++) {
|
||||
allocator()->ReturnTemporaryRegister(i);
|
||||
}
|
||||
|
||||
int start = allocator()->PrepareForConsecutiveTemporaryRegisters(kRunLength);
|
||||
CHECK(!allocator()->RegisterIsLive(Register(start)));
|
||||
for (int i = 0; i < kRunLength; i++) {
|
||||
CHECK(!allocator()->RegisterIsLive(Register(start + i)));
|
||||
allocator()->BorrowConsecutiveTemporaryRegister(start + i);
|
||||
CHECK(allocator()->RegisterIsLive(Register(start + i)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TemporaryRegisterAllocatorTest, RangeAllocationAbuttingHole) {
|
||||
static const int kPreAllocatedCount = 7;
|
||||
static const int kPreAllocatedFreeCount = 6;
|
||||
static const int kRunLength = 8;
|
||||
|
||||
for (int i = 0; i < kPreAllocatedCount; i++) {
|
||||
int to_free = allocator()->BorrowTemporaryRegister();
|
||||
CHECK_EQ(to_free, i);
|
||||
}
|
||||
for (int i = 0; i < kPreAllocatedFreeCount; i++) {
|
||||
allocator()->ReturnTemporaryRegister(i);
|
||||
}
|
||||
int start = allocator()->PrepareForConsecutiveTemporaryRegisters(kRunLength);
|
||||
CHECK(!allocator()->RegisterIsLive(Register(start)));
|
||||
CHECK_EQ(start, kPreAllocatedCount);
|
||||
for (int i = 0; i < kRunLength; i++) {
|
||||
CHECK(!allocator()->RegisterIsLive(Register(start + i)));
|
||||
allocator()->BorrowConsecutiveTemporaryRegister(start + i);
|
||||
CHECK(allocator()->RegisterIsLive(Register(start + i)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TemporaryRegisterAllocatorTest, RangeAllocationAvailableInTemporaries) {
|
||||
static const int kNotRunLength = 13;
|
||||
static const int kRunLength = 8;
|
||||
|
||||
// Allocate big batch
|
||||
for (int i = 0; i < kNotRunLength * 2 + kRunLength; i++) {
|
||||
int allocated = allocator()->BorrowTemporaryRegister();
|
||||
CHECK_EQ(allocated, i);
|
||||
}
|
||||
// Free every other register either side of target.
|
||||
for (int i = 0; i < kNotRunLength; i++) {
|
||||
if ((i & 2) == 1) {
|
||||
allocator()->ReturnTemporaryRegister(i);
|
||||
allocator()->ReturnTemporaryRegister(kNotRunLength + kRunLength + i);
|
||||
}
|
||||
}
|
||||
// Free all registers for target.
|
||||
for (int i = kNotRunLength; i < kNotRunLength + kRunLength; i++) {
|
||||
allocator()->ReturnTemporaryRegister(i);
|
||||
}
|
||||
|
||||
int start = allocator()->PrepareForConsecutiveTemporaryRegisters(kRunLength);
|
||||
CHECK_EQ(start, kNotRunLength);
|
||||
for (int i = 0; i < kRunLength; i++) {
|
||||
CHECK(!allocator()->RegisterIsLive(Register(start + i)));
|
||||
allocator()->BorrowConsecutiveTemporaryRegister(start + i);
|
||||
CHECK(allocator()->RegisterIsLive(Register(start + i)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TemporaryRegisterAllocatorTest, NotInRange) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
int reg = allocator()->BorrowTemporaryRegisterNotInRange(2, 5);
|
||||
CHECK(reg == i || (reg > 2 && reg == i + 4));
|
||||
}
|
||||
for (int i = 0; i < 10; i++) {
|
||||
if (i < 2) {
|
||||
allocator()->ReturnTemporaryRegister(i);
|
||||
} else {
|
||||
allocator()->ReturnTemporaryRegister(i + 4);
|
||||
}
|
||||
}
|
||||
int reg0 = allocator()->BorrowTemporaryRegisterNotInRange(0, 3);
|
||||
CHECK_EQ(reg0, 4);
|
||||
int reg1 = allocator()->BorrowTemporaryRegisterNotInRange(3, 10);
|
||||
CHECK_EQ(reg1, 2);
|
||||
int reg2 = allocator()->BorrowTemporaryRegisterNotInRange(2, 6);
|
||||
CHECK_EQ(reg2, 1);
|
||||
allocator()->ReturnTemporaryRegister(reg0);
|
||||
allocator()->ReturnTemporaryRegister(reg1);
|
||||
allocator()->ReturnTemporaryRegister(reg2);
|
||||
}
|
||||
|
||||
class BytecodeRegisterAllocatorTest : public TestWithIsolateAndZone {
|
||||
public:
|
||||
BytecodeRegisterAllocatorTest() {}
|
||||
BytecodeRegisterAllocatorTest() : allocator_(0) {}
|
||||
~BytecodeRegisterAllocatorTest() override {}
|
||||
|
||||
BytecodeRegisterAllocator* allocator() { return &allocator_; }
|
||||
|
||||
private:
|
||||
BytecodeRegisterAllocator allocator_;
|
||||
};
|
||||
|
||||
TEST_F(BytecodeRegisterAllocatorTest, TemporariesRecycled) {
|
||||
BytecodeArrayBuilder builder(isolate(), zone(), 0, 0, 0);
|
||||
TEST_F(BytecodeRegisterAllocatorTest, SimpleAllocations) {
|
||||
CHECK_EQ(allocator()->maximum_register_count(), 0);
|
||||
Register reg0 = allocator()->NewRegister();
|
||||
CHECK_EQ(reg0.index(), 0);
|
||||
CHECK_EQ(allocator()->maximum_register_count(), 1);
|
||||
CHECK_EQ(allocator()->next_register_index(), 1);
|
||||
CHECK(allocator()->RegisterIsLive(reg0));
|
||||
|
||||
int first;
|
||||
{
|
||||
BytecodeRegisterAllocator allocator(zone(),
|
||||
builder.temporary_register_allocator());
|
||||
first = allocator.NewRegister().index();
|
||||
allocator.NewRegister();
|
||||
allocator.NewRegister();
|
||||
allocator.NewRegister();
|
||||
allocator()->ReleaseRegisters(0);
|
||||
CHECK(!allocator()->RegisterIsLive(reg0));
|
||||
CHECK_EQ(allocator()->maximum_register_count(), 1);
|
||||
CHECK_EQ(allocator()->next_register_index(), 0);
|
||||
|
||||
reg0 = allocator()->NewRegister();
|
||||
Register reg1 = allocator()->NewRegister();
|
||||
CHECK_EQ(reg0.index(), 0);
|
||||
CHECK_EQ(reg1.index(), 1);
|
||||
CHECK(allocator()->RegisterIsLive(reg0));
|
||||
CHECK(allocator()->RegisterIsLive(reg1));
|
||||
CHECK_EQ(allocator()->maximum_register_count(), 2);
|
||||
CHECK_EQ(allocator()->next_register_index(), 2);
|
||||
|
||||
allocator()->ReleaseRegisters(1);
|
||||
CHECK(allocator()->RegisterIsLive(reg0));
|
||||
CHECK(!allocator()->RegisterIsLive(reg1));
|
||||
CHECK_EQ(allocator()->maximum_register_count(), 2);
|
||||
CHECK_EQ(allocator()->next_register_index(), 1);
|
||||
}
|
||||
|
||||
int second;
|
||||
{
|
||||
BytecodeRegisterAllocator allocator(zone(),
|
||||
builder.temporary_register_allocator());
|
||||
second = allocator.NewRegister().index();
|
||||
}
|
||||
TEST_F(BytecodeRegisterAllocatorTest, RegisterListAllocations) {
|
||||
CHECK_EQ(allocator()->maximum_register_count(), 0);
|
||||
RegisterList reg_list = allocator()->NewRegisterList(3);
|
||||
CHECK_EQ(reg_list.first_register().index(), 0);
|
||||
CHECK_EQ(reg_list.register_count(), 3);
|
||||
CHECK_EQ(reg_list[0].index(), 0);
|
||||
CHECK_EQ(reg_list[1].index(), 1);
|
||||
CHECK_EQ(reg_list[2].index(), 2);
|
||||
CHECK_EQ(allocator()->maximum_register_count(), 3);
|
||||
CHECK_EQ(allocator()->next_register_index(), 3);
|
||||
CHECK(allocator()->RegisterIsLive(reg_list[2]));
|
||||
|
||||
CHECK_EQ(first, second);
|
||||
}
|
||||
Register reg = allocator()->NewRegister();
|
||||
RegisterList reg_list_2 = allocator()->NewRegisterList(2);
|
||||
CHECK_EQ(reg.index(), 3);
|
||||
CHECK_EQ(reg_list_2.first_register().index(), 4);
|
||||
CHECK_EQ(reg_list_2.register_count(), 2);
|
||||
CHECK_EQ(reg_list_2[0].index(), 4);
|
||||
CHECK_EQ(reg_list_2[1].index(), 5);
|
||||
CHECK_EQ(allocator()->maximum_register_count(), 6);
|
||||
CHECK_EQ(allocator()->next_register_index(), 6);
|
||||
CHECK(allocator()->RegisterIsLive(reg));
|
||||
CHECK(allocator()->RegisterIsLive(reg_list_2[1]));
|
||||
|
||||
TEST_F(BytecodeRegisterAllocatorTest, ConsecutiveRegisters) {
|
||||
BytecodeArrayBuilder builder(isolate(), zone(), 0, 0, 0);
|
||||
BytecodeRegisterAllocator allocator(zone(),
|
||||
builder.temporary_register_allocator());
|
||||
allocator.PrepareForConsecutiveAllocations(4);
|
||||
Register reg0 = allocator.NextConsecutiveRegister();
|
||||
Register other = allocator.NewRegister();
|
||||
Register reg1 = allocator.NextConsecutiveRegister();
|
||||
Register reg2 = allocator.NextConsecutiveRegister();
|
||||
Register reg3 = allocator.NextConsecutiveRegister();
|
||||
USE(other);
|
||||
allocator()->ReleaseRegisters(reg.index());
|
||||
CHECK(!allocator()->RegisterIsLive(reg));
|
||||
CHECK(!allocator()->RegisterIsLive(reg_list_2[0]));
|
||||
CHECK(!allocator()->RegisterIsLive(reg_list_2[1]));
|
||||
CHECK(allocator()->RegisterIsLive(reg_list[2]));
|
||||
CHECK_EQ(allocator()->maximum_register_count(), 6);
|
||||
CHECK_EQ(allocator()->next_register_index(), 3);
|
||||
|
||||
CHECK(Register::AreContiguous(reg0, reg1, reg2, reg3));
|
||||
RegisterList empty_reg_list = allocator()->NewRegisterList(0);
|
||||
CHECK_EQ(empty_reg_list.first_register().index(), 0);
|
||||
CHECK_EQ(empty_reg_list.register_count(), 0);
|
||||
CHECK_EQ(allocator()->maximum_register_count(), 6);
|
||||
CHECK_EQ(allocator()->next_register_index(), 3);
|
||||
}
|
||||
|
||||
} // namespace interpreter
|
||||
|
@ -22,10 +22,10 @@ class BytecodeRegisterOptimizerTest : public BytecodePipelineStage,
|
||||
~BytecodeRegisterOptimizerTest() override { delete register_allocator_; }
|
||||
|
||||
void Initialize(int number_of_parameters, int number_of_locals) {
|
||||
register_allocator_ =
|
||||
new TemporaryRegisterAllocator(zone(), number_of_locals);
|
||||
register_optimizer_ = new (zone()) BytecodeRegisterOptimizer(
|
||||
zone(), register_allocator_, number_of_parameters, this);
|
||||
register_allocator_ = new BytecodeRegisterAllocator(number_of_locals);
|
||||
register_optimizer_ = new (zone())
|
||||
BytecodeRegisterOptimizer(zone(), register_allocator_, number_of_locals,
|
||||
number_of_parameters, this);
|
||||
}
|
||||
|
||||
void Write(BytecodeNode* node) override { output_.push_back(*node); }
|
||||
@ -40,15 +40,13 @@ class BytecodeRegisterOptimizerTest : public BytecodePipelineStage,
|
||||
return Handle<BytecodeArray>();
|
||||
}
|
||||
|
||||
TemporaryRegisterAllocator* allocator() { return register_allocator_; }
|
||||
BytecodeRegisterAllocator* allocator() { return register_allocator_; }
|
||||
BytecodeRegisterOptimizer* optimizer() { return register_optimizer_; }
|
||||
|
||||
Register NewTemporary() {
|
||||
return Register(allocator()->BorrowTemporaryRegister());
|
||||
}
|
||||
Register NewTemporary() { return allocator()->NewRegister(); }
|
||||
|
||||
void KillTemporary(Register reg) {
|
||||
allocator()->ReturnTemporaryRegister(reg.index());
|
||||
void ReleaseTemporaries(Register reg) {
|
||||
allocator()->ReleaseRegisters(reg.index());
|
||||
}
|
||||
|
||||
size_t write_count() const { return output_.size(); }
|
||||
@ -56,7 +54,7 @@ class BytecodeRegisterOptimizerTest : public BytecodePipelineStage,
|
||||
const std::vector<BytecodeNode>* output() { return &output_; }
|
||||
|
||||
private:
|
||||
TemporaryRegisterAllocator* register_allocator_;
|
||||
BytecodeRegisterAllocator* register_allocator_;
|
||||
BytecodeRegisterOptimizer* register_optimizer_;
|
||||
|
||||
std::vector<BytecodeNode> output_;
|
||||
@ -130,7 +128,7 @@ TEST_F(BytecodeRegisterOptimizerTest, TemporaryNotEmitted) {
|
||||
BytecodeNode node1(Bytecode::kStar, NewTemporary().ToOperand());
|
||||
optimizer()->Write(&node1);
|
||||
CHECK_EQ(write_count(), 0);
|
||||
KillTemporary(temp);
|
||||
ReleaseTemporaries(temp);
|
||||
CHECK_EQ(write_count(), 0);
|
||||
BytecodeNode node2(Bytecode::kReturn);
|
||||
optimizer()->Write(&node2);
|
||||
@ -140,6 +138,61 @@ TEST_F(BytecodeRegisterOptimizerTest, TemporaryNotEmitted) {
|
||||
CHECK_EQ(output()->at(1).bytecode(), Bytecode::kReturn);
|
||||
}
|
||||
|
||||
TEST_F(BytecodeRegisterOptimizerTest, ReleasedRegisterUsed) {
|
||||
Initialize(3, 1);
|
||||
BytecodeNode node0(Bytecode::kLdaSmi, 3);
|
||||
optimizer()->Write(&node0);
|
||||
CHECK_EQ(write_count(), 1);
|
||||
Register temp0 = NewTemporary();
|
||||
Register temp1 = NewTemporary();
|
||||
BytecodeNode node1(Bytecode::kStar, temp1.ToOperand());
|
||||
optimizer()->Write(&node1);
|
||||
CHECK_EQ(write_count(), 1);
|
||||
BytecodeNode node2(Bytecode::kLdaSmi, 1);
|
||||
optimizer()->Write(&node2);
|
||||
CHECK_EQ(write_count(), 3);
|
||||
BytecodeNode node3(Bytecode::kMov, temp1.ToOperand(), temp0.ToOperand());
|
||||
optimizer()->Write(&node3);
|
||||
CHECK_EQ(write_count(), 3);
|
||||
ReleaseTemporaries(temp1);
|
||||
CHECK_EQ(write_count(), 3);
|
||||
BytecodeNode node4(Bytecode::kLdar, temp0.ToOperand());
|
||||
optimizer()->Write(&node4);
|
||||
CHECK_EQ(write_count(), 3);
|
||||
BytecodeNode node5(Bytecode::kReturn);
|
||||
optimizer()->Write(&node5);
|
||||
CHECK_EQ(write_count(), 5);
|
||||
CHECK_EQ(output()->at(3).bytecode(), Bytecode::kLdar);
|
||||
CHECK_EQ(output()->at(3).operand(0), temp1.ToOperand());
|
||||
CHECK_EQ(output()->at(4).bytecode(), Bytecode::kReturn);
|
||||
}
|
||||
|
||||
TEST_F(BytecodeRegisterOptimizerTest, ReleasedRegisterNotFlushed) {
|
||||
Initialize(3, 1);
|
||||
BytecodeNode node0(Bytecode::kLdaSmi, 3);
|
||||
optimizer()->Write(&node0);
|
||||
CHECK_EQ(write_count(), 1);
|
||||
Register temp0 = NewTemporary();
|
||||
Register temp1 = NewTemporary();
|
||||
BytecodeNode node1(Bytecode::kStar, temp0.ToOperand());
|
||||
optimizer()->Write(&node1);
|
||||
CHECK_EQ(write_count(), 1);
|
||||
BytecodeNode node2(Bytecode::kStar, temp1.ToOperand());
|
||||
optimizer()->Write(&node2);
|
||||
CHECK_EQ(write_count(), 1);
|
||||
ReleaseTemporaries(temp1);
|
||||
BytecodeLabel label;
|
||||
BytecodeNode jump(Bytecode::kJump, 0, nullptr);
|
||||
optimizer()->WriteJump(&jump, &label);
|
||||
BytecodeNode node3(Bytecode::kReturn);
|
||||
optimizer()->Write(&node3);
|
||||
CHECK_EQ(write_count(), 4);
|
||||
CHECK_EQ(output()->at(1).bytecode(), Bytecode::kStar);
|
||||
CHECK_EQ(output()->at(1).operand(0), temp0.ToOperand());
|
||||
CHECK_EQ(output()->at(2).bytecode(), Bytecode::kJump);
|
||||
CHECK_EQ(output()->at(3).bytecode(), Bytecode::kReturn);
|
||||
}
|
||||
|
||||
TEST_F(BytecodeRegisterOptimizerTest, StoresToLocalsImmediate) {
|
||||
Initialize(3, 1);
|
||||
Register parameter = Register::FromParameterIndex(1, 3);
|
||||
|
Loading…
Reference in New Issue
Block a user