Commit a1ba971c authored by oth's avatar oth Committed by Commit bot

[Interpreter] Enable assignments in expressions.

This change introduces register re-mapping to avoid assignment hazards
in binary expressions. Expressions that cause problems typically have
the form y = x + (x = 4);. The problem occurs because the lhs value
evaluates to the register holding x. The rhs updates that register and
then applying the operation would use the new value as the lhs.

By tracking loads and stores in binary expressions the generator is now
able to detect when condition occurs and uses a temporary register for
the rhs value. When the binary expression evaluation is complete the
variable is updated with the latest temporary.

A new bytecode Mov performs this update without touching the
accumulator.

BUG=v8:4280
LOG=N

Review URL: https://codereview.chromium.org/1412683011

Cr-Commit-Position: refs/heads/master@{#32141}
parent 0ec6db47
...@@ -330,6 +330,13 @@ void BytecodeGraphBuilder::VisitStar( ...@@ -330,6 +330,13 @@ void BytecodeGraphBuilder::VisitStar(
} }
void BytecodeGraphBuilder::VisitMov(
const interpreter::BytecodeArrayIterator& iterator) {
Node* value = environment()->LookupRegister(iterator.GetRegisterOperand(0));
environment()->BindRegister(iterator.GetRegisterOperand(1), value);
}
void BytecodeGraphBuilder::BuildLoadGlobal( void BytecodeGraphBuilder::BuildLoadGlobal(
const interpreter::BytecodeArrayIterator& iterator, const interpreter::BytecodeArrayIterator& iterator,
TypeofMode typeof_mode) { TypeofMode typeof_mode) {
......
...@@ -288,6 +288,13 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadAccumulatorWithRegister( ...@@ -288,6 +288,13 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadAccumulatorWithRegister(
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreAccumulatorInRegister( BytecodeArrayBuilder& BytecodeArrayBuilder::StoreAccumulatorInRegister(
Register reg) { Register reg) {
// TODO(oth): Avoid storing the accumulator in the register if the
// previous bytecode loaded the accumulator with the same register.
//
// TODO(oth): If the previous bytecode is a MOV into this register,
// the previous instruction can be removed. The logic for determining
// these redundant MOVs appears complex.
Output(Bytecode::kStar, reg.ToOperand());
if (!IsRegisterInAccumulator(reg)) { if (!IsRegisterInAccumulator(reg)) {
Output(Bytecode::kStar, reg.ToOperand()); Output(Bytecode::kStar, reg.ToOperand());
} }
...@@ -295,6 +302,14 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StoreAccumulatorInRegister( ...@@ -295,6 +302,14 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StoreAccumulatorInRegister(
} }
BytecodeArrayBuilder& BytecodeArrayBuilder::MoveRegister(Register from,
Register to) {
DCHECK(from != to);
Output(Bytecode::kMov, from.ToOperand(), to.ToOperand());
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadGlobal( BytecodeArrayBuilder& BytecodeArrayBuilder::LoadGlobal(
size_t name_index, int feedback_slot, LanguageMode language_mode, size_t name_index, int feedback_slot, LanguageMode language_mode,
TypeofMode typeof_mode) { TypeofMode typeof_mode) {
......
...@@ -97,6 +97,9 @@ class BytecodeArrayBuilder { ...@@ -97,6 +97,9 @@ class BytecodeArrayBuilder {
BytecodeArrayBuilder& LoadAccumulatorWithRegister(Register reg); BytecodeArrayBuilder& LoadAccumulatorWithRegister(Register reg);
BytecodeArrayBuilder& StoreAccumulatorInRegister(Register reg); BytecodeArrayBuilder& StoreAccumulatorInRegister(Register reg);
// Register-register transfer.
BytecodeArrayBuilder& MoveRegister(Register from, Register to);
// Named load property. // Named load property.
BytecodeArrayBuilder& LoadNamedProperty(Register object, size_t name_index, BytecodeArrayBuilder& LoadNamedProperty(Register object, size_t name_index,
int feedback_slot, int feedback_slot,
......
...@@ -297,6 +297,120 @@ class BytecodeGenerator::RegisterResultScope final ...@@ -297,6 +297,120 @@ class BytecodeGenerator::RegisterResultScope final
}; };
BytecodeGenerator::AssignmentHazardHelper::AssignmentHazardHelper(
BytecodeGenerator* generator)
: generator_(generator),
alias_mappings_(generator->zone()),
aliased_locals_and_parameters_(generator->zone()),
execution_result_(nullptr),
scope_depth_(0) {}
void BytecodeGenerator::AssignmentHazardHelper::EnterScope() {
DCHECK_GE(scope_depth_, 0);
if (scope_depth_++ == 0) {
execution_result_ = generator_->execution_result();
}
}
void BytecodeGenerator::AssignmentHazardHelper::LeaveScope() {
DCHECK_GT(scope_depth_, 0);
if (--scope_depth_ == 0) {
DCHECK_EQ(execution_result_, generator_->execution_result());
RestoreAliasedLocalsAndParameters();
}
}
// Returns a register that a load instruction should use when
// loading from |reg|. This allows an alias for a modified version
// of |reg| to be used within a hazard regions.
MUST_USE_RESULT Register
BytecodeGenerator::AssignmentHazardHelper::GetRegisterForLoad(Register reg) {
if (scope_depth_ == 0) {
return reg;
} else {
// A load from |reg| is to be issued. The register is placed in
// the mappings table initially mapping to itself. Future stores
// will update the mapping with temporaries thus preserving the
// original register's value.
//
// NB This insert only updates the table if no mapping exists
// already (std::map::insert semantics).
auto insert_result =
alias_mappings_.insert(std::make_pair(reg.index(), reg.index()));
auto mapping = insert_result.first;
// Return the current alias for reg.
return Register(mapping->second);
}
}
// Returns a register that a store instruction should use when
// loading from |reg|. This allows an alias for a modified version
// of |reg| to be used within hazard regions.
MUST_USE_RESULT Register
BytecodeGenerator::AssignmentHazardHelper::GetRegisterForStore(Register reg) {
if (scope_depth_ == 0 ||
alias_mappings_.find(reg.index()) == alias_mappings_.end()) {
// If not in a hazard region or a load for this register has not
// occurred no mapping is necessary.
return reg;
} else {
// Storing to a register with 1 or more loads issued. The
// register is mapped to a temporary alias so we don't overwrite
// the lhs value, e.g. y = x + (x = 1); has a register for x on
// the lhs and needs a new register x' for the upcoming store on
// the rhs as the original x is an input to the add operation.
Register alias = execution_result_->NewRegister();
alias_mappings_[reg.index()] = alias.index();
if (generator_->builder()->RegisterIsParameterOrLocal(reg)) {
// Keep track of registers that need to be restored on exit
// from the assignment hazard region.
aliased_locals_and_parameters_.insert(reg.index());
}
return alias;
}
}
void BytecodeGenerator::AssignmentHazardHelper::
RestoreAliasedLocalsAndParameters() {
DCHECK(scope_depth_ == 0);
// Move temporary registers holding values for locals and
// parameters back into their local and parameter registers.
for (auto reg = aliased_locals_and_parameters_.begin();
reg != aliased_locals_and_parameters_.end(); reg++) {
auto mapping = alias_mappings_.find(*reg);
if (mapping != alias_mappings_.end()) {
generator_->builder()->MoveRegister(Register(mapping->second),
Register(*reg));
}
}
alias_mappings_.clear();
aliased_locals_and_parameters_.clear();
}
class BytecodeGenerator::AssignmentHazardScope final {
public:
explicit AssignmentHazardScope(BytecodeGenerator* generator)
: generator_(generator) {
generator_->assignment_hazard_helper()->EnterScope();
}
~AssignmentHazardScope() {
generator_->assignment_hazard_helper()->LeaveScope();
}
private:
BytecodeGenerator* generator_;
DISALLOW_COPY_AND_ASSIGN(AssignmentHazardScope);
};
BytecodeGenerator::BytecodeGenerator(Isolate* isolate, Zone* zone) BytecodeGenerator::BytecodeGenerator(Isolate* isolate, Zone* zone)
: isolate_(isolate), : isolate_(isolate),
zone_(zone), zone_(zone),
...@@ -307,15 +421,11 @@ BytecodeGenerator::BytecodeGenerator(Isolate* isolate, Zone* zone) ...@@ -307,15 +421,11 @@ BytecodeGenerator::BytecodeGenerator(Isolate* isolate, Zone* zone)
execution_control_(nullptr), execution_control_(nullptr),
execution_context_(nullptr), execution_context_(nullptr),
execution_result_(nullptr), execution_result_(nullptr),
binary_expression_depth_(0), assignment_hazard_helper_(this) {
binary_expression_hazard_set_(zone) {
InitializeAstVisitor(isolate); InitializeAstVisitor(isolate);
} }
BytecodeGenerator::~BytecodeGenerator() {}
Handle<BytecodeArray> BytecodeGenerator::MakeBytecode(CompilationInfo* info) { Handle<BytecodeArray> BytecodeGenerator::MakeBytecode(CompilationInfo* info) {
set_info(info); set_info(info);
set_scope(info->scope()); set_scope(info->scope());
...@@ -506,8 +616,7 @@ void BytecodeGenerator::VisitDeclarations( ...@@ -506,8 +616,7 @@ void BytecodeGenerator::VisitDeclarations(
void BytecodeGenerator::VisitExpressionStatement(ExpressionStatement* stmt) { void BytecodeGenerator::VisitExpressionStatement(ExpressionStatement* stmt) {
// TODO(rmcilroy): Replace this with a StatementResultScope when it exists. EffectResultScope statement_result_scope(this);
EffectResultScope effect_scope(this);
VisitForEffect(stmt->expression()); VisitForEffect(stmt->expression());
} }
...@@ -559,7 +668,7 @@ void BytecodeGenerator::VisitBreakStatement(BreakStatement* stmt) { ...@@ -559,7 +668,7 @@ void BytecodeGenerator::VisitBreakStatement(BreakStatement* stmt) {
void BytecodeGenerator::VisitReturnStatement(ReturnStatement* stmt) { void BytecodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
EffectResultScope effect_scope(this); EffectResultScope statement_result_scope(this);
VisitForAccumulatorValue(stmt->expression()); VisitForAccumulatorValue(stmt->expression());
builder()->Return(); builder()->Return();
} }
...@@ -628,7 +737,6 @@ void BytecodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) { ...@@ -628,7 +737,6 @@ void BytecodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) {
LoopBuilder loop_builder(builder()); LoopBuilder loop_builder(builder());
ControlScopeForIteration execution_control(this, stmt, &loop_builder); ControlScopeForIteration execution_control(this, stmt, &loop_builder);
BytecodeLabel body_label, condition_label, done_label; BytecodeLabel body_label, condition_label, done_label;
if (stmt->cond()->ToBooleanIsFalse()) { if (stmt->cond()->ToBooleanIsFalse()) {
Visit(stmt->body()); Visit(stmt->body());
// Bind condition_label and done_label for processing continue and break. // Bind condition_label and done_label for processing continue and break.
...@@ -764,11 +872,7 @@ void BytecodeGenerator::VisitForInAssignment(Expression* expr, ...@@ -764,11 +872,7 @@ void BytecodeGenerator::VisitForInAssignment(Expression* expr,
void BytecodeGenerator::VisitForInStatement(ForInStatement* stmt) { void BytecodeGenerator::VisitForInStatement(ForInStatement* stmt) {
// TODO(oth): For now we need a parent scope for paths that end up EffectResultScope statement_result_scope(this);
// in VisitLiteral which can allocate in the parent scope. A future
// CL in preparation will add a StatementResultScope that will
// remove the need for this EffectResultScope.
EffectResultScope result_scope(this);
if (stmt->subject()->IsNullLiteral() || if (stmt->subject()->IsNullLiteral() ||
stmt->subject()->IsUndefinedLiteral(isolate())) { stmt->subject()->IsUndefinedLiteral(isolate())) {
...@@ -1198,6 +1302,7 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable, ...@@ -1198,6 +1302,7 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable,
switch (variable->location()) { switch (variable->location()) {
case VariableLocation::LOCAL: { case VariableLocation::LOCAL: {
Register source(Register(variable->index())); Register source(Register(variable->index()));
source = assignment_hazard_helper()->GetRegisterForLoad(source);
execution_result()->SetResultInRegister(source); execution_result()->SetResultInRegister(source);
break; break;
} }
...@@ -1205,6 +1310,7 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable, ...@@ -1205,6 +1310,7 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable,
// The parameter indices are shifted by 1 (receiver is variable // The parameter indices are shifted by 1 (receiver is variable
// index -1 but is parameter index 0 in BytecodeArrayBuilder). // index -1 but is parameter index 0 in BytecodeArrayBuilder).
Register source = builder()->Parameter(variable->index() + 1); Register source = builder()->Parameter(variable->index() + 1);
source = assignment_hazard_helper()->GetRegisterForLoad(source);
execution_result()->SetResultInRegister(source); execution_result()->SetResultInRegister(source);
break; break;
} }
...@@ -1269,16 +1375,19 @@ void BytecodeGenerator::VisitVariableAssignment(Variable* variable, ...@@ -1269,16 +1375,19 @@ void BytecodeGenerator::VisitVariableAssignment(Variable* variable,
case VariableLocation::LOCAL: { case VariableLocation::LOCAL: {
// TODO(rmcilroy): support const mode initialization. // TODO(rmcilroy): support const mode initialization.
Register destination(variable->index()); Register destination(variable->index());
destination =
assignment_hazard_helper()->GetRegisterForStore(destination);
builder()->StoreAccumulatorInRegister(destination); builder()->StoreAccumulatorInRegister(destination);
RecordStoreToRegister(destination);
break; break;
} }
case VariableLocation::PARAMETER: { case VariableLocation::PARAMETER: {
// The parameter indices are shifted by 1 (receiver is variable // The parameter indices are shifted by 1 (receiver is variable
// index -1 but is parameter index 0 in BytecodeArrayBuilder). // index -1 but is parameter index 0 in BytecodeArrayBuilder).
Register destination(builder()->Parameter(variable->index() + 1)); Register destination(builder()->Parameter(variable->index() + 1));
destination =
assignment_hazard_helper()->GetRegisterForStore(destination);
builder()->StoreAccumulatorInRegister(destination); builder()->StoreAccumulatorInRegister(destination);
RecordStoreToRegister(destination);
break; break;
} }
case VariableLocation::GLOBAL: case VariableLocation::GLOBAL:
...@@ -1826,37 +1935,33 @@ void BytecodeGenerator::VisitBinaryOperation(BinaryOperation* binop) { ...@@ -1826,37 +1935,33 @@ void BytecodeGenerator::VisitBinaryOperation(BinaryOperation* binop) {
void BytecodeGenerator::VisitCompareOperation(CompareOperation* expr) { void BytecodeGenerator::VisitCompareOperation(CompareOperation* expr) {
// TODO(oth): Remove PrepareForBinaryExpression/CompleteBinaryExpression // The evaluation of binary comparison expressions has an assignment
// once we have StatementScope that tracks hazardous loads/stores. // hazard because the lhs may be a variable that evaluates to a
PrepareForBinaryExpression(); // local or parameter and the rhs may modify that, e.g. y = x + (x = 1)
// To get a correct result the generator treats the inner assigment
// as being made to a temporary x' that is spilled on exit of the
// assignment hazard.
AssignmentHazardScope assignment_hazard_scope(this);
Register lhs = VisitForRegisterValue(expr->left()); Register lhs = VisitForRegisterValue(expr->left());
if (builder()->RegisterIsParameterOrLocal(lhs)) {
// Result was returned in an existing local or parameter. See if
// it needs to be moved to a temporary.
// TODO(oth) LoadFromAliasedRegister call into VisitVariableLoad().
lhs = LoadFromAliasedRegister(lhs);
}
VisitForAccumulatorValue(expr->right()); VisitForAccumulatorValue(expr->right());
builder()->CompareOperation(expr->op(), lhs, language_mode_strength()); builder()->CompareOperation(expr->op(), lhs, language_mode_strength());
CompleteBinaryExpression();
execution_result()->SetResultInAccumulator(); execution_result()->SetResultInAccumulator();
} }
void BytecodeGenerator::VisitArithmeticExpression(BinaryOperation* expr) { void BytecodeGenerator::VisitArithmeticExpression(BinaryOperation* expr) {
// TODO(oth): Remove PrepareForBinaryExpression/CompleteBinaryExpression // The evaluation of binary arithmetic expressions has an assignment
// once we have StatementScope that tracks hazardous loads/stores. // hazard because the lhs may be a variable that evaluates to a
PrepareForBinaryExpression(); // local or parameter and the rhs may modify that, e.g. y = x + (x = 1)
// To get a correct result the generator treats the inner assigment
// as being made to a temporary x' that is spilled on exit of the
// assignment hazard.
AssignmentHazardScope assignment_hazard_scope(this);
Register lhs = VisitForRegisterValue(expr->left()); Register lhs = VisitForRegisterValue(expr->left());
if (builder()->RegisterIsParameterOrLocal(lhs)) {
// Result was returned in an existing local or parameter. See if
// it needs to be moved to a temporary.
// TODO(oth) LoadFromAliasedRegister call into VisitVariableLoad().
lhs = LoadFromAliasedRegister(lhs);
}
VisitForAccumulatorValue(expr->right()); VisitForAccumulatorValue(expr->right());
builder()->BinaryOperation(expr->op(), lhs, language_mode_strength()); builder()->BinaryOperation(expr->op(), lhs, language_mode_strength());
CompleteBinaryExpression();
execution_result()->SetResultInAccumulator(); execution_result()->SetResultInAccumulator();
} }
...@@ -2074,13 +2179,6 @@ void BytecodeGenerator::VisitFunctionClosureForContext() { ...@@ -2074,13 +2179,6 @@ void BytecodeGenerator::VisitFunctionClosureForContext() {
} }
void BytecodeGenerator::PrepareForBinaryExpression() {
if (binary_expression_depth_++ == 0) {
binary_expression_hazard_set_.clear();
}
}
// Visits the expression |expr| and places the result in the accumulator. // Visits the expression |expr| and places the result in the accumulator.
void BytecodeGenerator::VisitForAccumulatorValue(Expression* expr) { void BytecodeGenerator::VisitForAccumulatorValue(Expression* expr) {
AccumulatorResultScope accumulator_scope(this); AccumulatorResultScope accumulator_scope(this);
...@@ -2104,35 +2202,6 @@ Register BytecodeGenerator::VisitForRegisterValue(Expression* expr) { ...@@ -2104,35 +2202,6 @@ Register BytecodeGenerator::VisitForRegisterValue(Expression* expr) {
} }
Register BytecodeGenerator::LoadFromAliasedRegister(Register reg) {
// TODO(oth): Follow on CL to load from re-map here.
DCHECK(builder()->RegisterIsParameterOrLocal(reg));
if (binary_expression_depth_ > 0) {
binary_expression_hazard_set_.insert(reg.index());
}
return reg;
}
void BytecodeGenerator::RecordStoreToRegister(Register reg) {
DCHECK(builder()->RegisterIsParameterOrLocal(reg));
if (binary_expression_depth_ > 0) {
// TODO(oth): a store to a register that's be loaded needs to be
// remapped.
DCHECK(binary_expression_hazard_set_.find(reg.index()) ==
binary_expression_hazard_set_.end());
}
}
void BytecodeGenerator::CompleteBinaryExpression() {
DCHECK(binary_expression_depth_ > 0);
binary_expression_depth_ -= 1;
// TODO(oth): spill remapped registers into origins.
// TODO(oth): make statement/top-level.
}
Register BytecodeGenerator::NextContextRegister() const { Register BytecodeGenerator::NextContextRegister() const {
if (execution_context() == nullptr) { if (execution_context() == nullptr) {
// Return the incoming function context for the outermost execution context. // Return the incoming function context for the outermost execution context.
......
...@@ -13,10 +13,9 @@ namespace v8 { ...@@ -13,10 +13,9 @@ namespace v8 {
namespace internal { namespace internal {
namespace interpreter { namespace interpreter {
class BytecodeGenerator : public AstVisitor { class BytecodeGenerator final : public AstVisitor {
public: public:
BytecodeGenerator(Isolate* isolate, Zone* zone); BytecodeGenerator(Isolate* isolate, Zone* zone);
virtual ~BytecodeGenerator();
Handle<BytecodeArray> MakeBytecode(CompilationInfo* info); Handle<BytecodeArray> MakeBytecode(CompilationInfo* info);
...@@ -36,6 +35,37 @@ class BytecodeGenerator : public AstVisitor { ...@@ -36,6 +35,37 @@ class BytecodeGenerator : public AstVisitor {
class EffectResultScope; class EffectResultScope;
class AccumulatorResultScope; class AccumulatorResultScope;
class RegisterResultScope; class RegisterResultScope;
class AssignmentHazardScope;
// Helper class that aliases locals and parameters when assignment
// hazards occur in binary expressions. For y = x + (x = 1) has an
// assignment hazard because the lhs evaluates to the register
// holding x and the rhs (x = 1) potentially updates x. When this
// hazard is detected, the rhs uses a temporary to hold the newer
// value of x while preserving the lhs for the binary expresion
// evaluation. The newer value is spilled to x at the end of the
// binary expression evaluation.
class AssignmentHazardHelper final {
public:
explicit AssignmentHazardHelper(BytecodeGenerator* generator);
MUST_USE_RESULT Register GetRegisterForLoad(Register reg);
MUST_USE_RESULT Register GetRegisterForStore(Register reg);
private:
friend class AssignmentHazardScope;
void EnterScope();
void LeaveScope();
void RestoreAliasedLocalsAndParameters();
BytecodeGenerator* generator_;
ZoneMap<int, int> alias_mappings_;
ZoneSet<int> aliased_locals_and_parameters_;
ExpressionResultScope* execution_result_;
int scope_depth_;
DISALLOW_COPY_AND_ASSIGN(AssignmentHazardHelper);
};
void MakeBytecodeBody(); void MakeBytecodeBody();
Register NextContextRegister() const; Register NextContextRegister() const;
...@@ -54,6 +84,9 @@ class BytecodeGenerator : public AstVisitor { ...@@ -54,6 +84,9 @@ class BytecodeGenerator : public AstVisitor {
void VisitNot(UnaryOperation* expr); void VisitNot(UnaryOperation* expr);
void VisitDelete(UnaryOperation* expr); void VisitDelete(UnaryOperation* expr);
// Used by flow control routines to evaluate loop condition.
void VisitCondition(Expression* expr);
// Helper visitors which perform common operations. // Helper visitors which perform common operations.
Register VisitArguments(ZoneList<Expression*>* arguments); Register VisitArguments(ZoneList<Expression*>* arguments);
...@@ -84,17 +117,12 @@ class BytecodeGenerator : public AstVisitor { ...@@ -84,17 +117,12 @@ class BytecodeGenerator : public AstVisitor {
Register value_out); Register value_out);
void VisitForInAssignment(Expression* expr, FeedbackVectorSlot slot); void VisitForInAssignment(Expression* expr, FeedbackVectorSlot slot);
// Visitors for obtaining expression result in the accumulator, in a // Visitors for obtaining expression result in the accumulator, in a
// register, or just getting the effect. // register, or just getting the effect.
void VisitForAccumulatorValue(Expression* expression); void VisitForAccumulatorValue(Expression* expression);
MUST_USE_RESULT Register VisitForRegisterValue(Expression* expression); MUST_USE_RESULT Register VisitForRegisterValue(Expression* expression);
void VisitForEffect(Expression* node); void VisitForEffect(Expression* node);
// Methods marking the start and end of binary expressions.
void PrepareForBinaryExpression();
void CompleteBinaryExpression();
// Methods for tracking and remapping register. // Methods for tracking and remapping register.
void RecordStoreToRegister(Register reg); void RecordStoreToRegister(Register reg);
Register LoadFromAliasedRegister(Register reg); Register LoadFromAliasedRegister(Register reg);
...@@ -121,6 +149,9 @@ class BytecodeGenerator : public AstVisitor { ...@@ -121,6 +149,9 @@ class BytecodeGenerator : public AstVisitor {
execution_result_ = execution_result; execution_result_ = execution_result;
} }
ExpressionResultScope* execution_result() const { return execution_result_; } ExpressionResultScope* execution_result() const { return execution_result_; }
inline AssignmentHazardHelper* assignment_hazard_helper() {
return &assignment_hazard_helper_;
}
ZoneVector<Handle<Object>>* globals() { return &globals_; } ZoneVector<Handle<Object>>* globals() { return &globals_; }
inline LanguageMode language_mode() const; inline LanguageMode language_mode() const;
...@@ -136,9 +167,7 @@ class BytecodeGenerator : public AstVisitor { ...@@ -136,9 +167,7 @@ class BytecodeGenerator : public AstVisitor {
ControlScope* execution_control_; ControlScope* execution_control_;
ContextScope* execution_context_; ContextScope* execution_context_;
ExpressionResultScope* execution_result_; ExpressionResultScope* execution_result_;
AssignmentHazardHelper assignment_hazard_helper_;
int binary_expression_depth_;
ZoneSet<int> binary_expression_hazard_set_;
}; };
} // namespace interpreter } // namespace interpreter
......
...@@ -70,6 +70,9 @@ namespace interpreter { ...@@ -70,6 +70,9 @@ namespace interpreter {
V(Ldar, OperandType::kReg8) \ V(Ldar, OperandType::kReg8) \
V(Star, OperandType::kReg8) \ V(Star, OperandType::kReg8) \
\ \
/* Register-register transfers */ \
V(Mov, OperandType::kReg8, OperandType::kReg8) \
\
/* LoadIC operations */ \ /* LoadIC operations */ \
V(LoadICSloppy, OperandType::kReg8, OperandType::kIdx8, OperandType::kIdx8) \ V(LoadICSloppy, OperandType::kReg8, OperandType::kIdx8, OperandType::kIdx8) \
V(LoadICStrict, OperandType::kReg8, OperandType::kIdx8, OperandType::kIdx8) \ V(LoadICStrict, OperandType::kReg8, OperandType::kIdx8, OperandType::kIdx8) \
......
...@@ -200,6 +200,18 @@ void Interpreter::DoStar(compiler::InterpreterAssembler* assembler) { ...@@ -200,6 +200,18 @@ void Interpreter::DoStar(compiler::InterpreterAssembler* assembler) {
} }
// Mov <src> <dst>
//
// Stores the value of register <src> to register <dst>.
void Interpreter::DoMov(compiler::InterpreterAssembler* assembler) {
Node* src_index = __ BytecodeOperandReg(0);
Node* src_value = __ LoadRegister(src_index);
Node* dst_index = __ BytecodeOperandReg(1);
__ StoreRegister(src_value, dst_index);
__ Dispatch();
}
void Interpreter::DoLoadGlobal(Callable ic, void Interpreter::DoLoadGlobal(Callable ic,
compiler::InterpreterAssembler* assembler) { compiler::InterpreterAssembler* assembler) {
// Get the global object. // Get the global object.
...@@ -216,7 +228,6 @@ void Interpreter::DoLoadGlobal(Callable ic, ...@@ -216,7 +228,6 @@ void Interpreter::DoLoadGlobal(Callable ic,
Node* result = __ CallIC(ic.descriptor(), code_target, global, name, smi_slot, Node* result = __ CallIC(ic.descriptor(), code_target, global, name, smi_slot,
type_feedback_vector); type_feedback_vector);
__ SetAccumulator(result); __ SetAccumulator(result);
__ Dispatch(); __ Dispatch();
} }
......
...@@ -5264,6 +5264,225 @@ TEST(RemoveRedundantLdar) { ...@@ -5264,6 +5264,225 @@ TEST(RemoveRedundantLdar) {
} }
} }
TEST(AssignmentsInBinaryExpression) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
ExpectedSnippet<const char*> snippets[] = {
{"var x = 0, y = 1;\n"
"return (x = 2, y = 3, x = 4, y = 5)",
2 * kPointerSize,
1,
24,
{
B(LdaZero), B(Star), R(0), //
B(LdaSmi8), U8(1), //
B(Star), R(1), //
B(LdaSmi8), U8(2), //
B(Star), R(0), //
B(LdaSmi8), U8(3), //
B(Star), R(1), //
B(LdaSmi8), U8(4), //
B(Star), R(0), //
B(LdaSmi8), U8(5), //
B(Star), R(1), //
B(Return), //
},
0},
{"var x = 55;\n"
"var y = (x = 100);\n"
"return y",
2 * kPointerSize,
1,
11,
{
B(LdaSmi8), U8(55), //
B(Star), R(0), //
B(LdaSmi8), U8(100), //
B(Star), R(0), //
B(Star), R(1), //
B(Return), //
},
0},
{"var x = 55;\n"
"x = x + (x = 100) + (x = 101);\n"
"return x;",
4 * kPointerSize,
1,
24,
{
B(LdaSmi8), U8(55), //
B(Star), R(0), //
B(LdaSmi8), U8(100), //
B(Star), R(1), //
B(Add), R(0), //
B(Star), R(2), //
B(LdaSmi8), U8(101), //
B(Star), R(3), //
B(Add), R(2), //
B(Mov), R(3), R(0), //
B(Star), R(0), //
B(Return), //
},
0},
{"var x = 55;\n"
"x = (x = 56) - x + (x = 57);\n"
"x++;\n"
"return x;",
3 * kPointerSize,
1,
34,
{
B(LdaSmi8), U8(55), //
B(Star), R(0), //
B(LdaSmi8), U8(56), //
B(Star), R(0), //
B(Star), R(1), //
B(Ldar), R(0), //
B(Sub), R(1), //
B(Star), R(2), //
B(LdaSmi8), U8(57), //
B(Star), R(1), //
B(Add), R(2), //
B(Mov), R(1), R(0), //
B(Star), R(0), //
B(ToNumber), //
B(Star), R(1), //
B(Inc), //
B(Star), R(0), //
B(Return), //
},
0},
{"var x = 55;\n"
"var y = x + (x = 1) + (x = 2) + (x = 3);\n"
"return y;",
6 * kPointerSize,
1,
32,
{
B(LdaSmi8), U8(55), //
B(Star), R(0), //
B(LdaSmi8), U8(1), //
B(Star), R(2), //
B(Add), R(0), //
B(Star), R(3), //
B(LdaSmi8), U8(2), //
B(Star), R(4), //
B(Add), R(3), //
B(Star), R(5), //
B(LdaSmi8), U8(3), //
B(Star), R(3), //
B(Add), R(5), //
B(Mov), R(3), R(0), //
B(Star), R(1), //
B(Return), //
},
0},
{"var x = 55;\n"
"var x = x + (x = 1) + (x = 2) + (x = 3);\n"
"return x;",
5 * kPointerSize,
1,
32,
{
B(LdaSmi8), U8(55), //
B(Star), R(0), //
B(LdaSmi8), U8(1), //
B(Star), R(1), //
B(Add), R(0), //
B(Star), R(2), //
B(LdaSmi8), U8(2), //
B(Star), R(3), //
B(Add), R(2), //
B(Star), R(4), //
B(LdaSmi8), U8(3), //
B(Star), R(2), //
B(Add), R(4), //
B(Mov), R(2), R(0), //
B(Star), R(0), //
B(Return), //
},
0},
{"var x = 10, y = 20;\n"
"return x + (x = 1) + (x + 1) * (y = 2) + (y = 3) + (x = 4) + (y = 5) + "
"y;\n",
6 * kPointerSize,
1,
64,
{
B(LdaSmi8), U8(10), //
B(Star), R(0), //
B(LdaSmi8), U8(20), //
B(Star), R(1), //
B(LdaSmi8), U8(1), //
B(Star), R(2), //
B(Add), R(0), //
B(Star), R(3), //
B(LdaSmi8), U8(1), //
B(Add), R(2), //
B(Star), R(4), //
B(LdaSmi8), U8(2), //
B(Star), R(1), //
B(Mul), R(4), //
B(Add), R(3), //
B(Star), R(4), //
B(LdaSmi8), U8(3), //
B(Star), R(1), //
B(Add), R(4), //
B(Star), R(3), //
B(LdaSmi8), U8(4), //
B(Star), R(4), //
B(Add), R(3), //
B(Star), R(5), //
B(LdaSmi8), U8(5), //
B(Star), R(1), //
B(Add), R(5), //
B(Star), R(3), //
B(Ldar), R(1), //
B(Add), R(3), //
B(Mov), R(4), R(0), //
B(Return), //
},
0},
{"var x = 17;\n"
"return 1 + x + (x++) + (++x);\n",
5 * kPointerSize,
1,
40,
{
B(LdaSmi8), U8(17), //
B(Star), R(0), //
B(LdaSmi8), U8(1), //
B(Star), R(1), //
B(Ldar), R(0), //
B(Add), R(1), //
B(Star), R(2), //
B(Ldar), R(0), //
B(ToNumber), //
B(Star), R(1), //
B(Inc), //
B(Star), R(3), //
B(Ldar), R(1), //
B(Add), R(2), //
B(Star), R(4), //
B(Ldar), R(3), //
B(ToNumber), //
B(Inc), //
B(Star), R(1), //
B(Add), R(4), //
B(Mov), R(1), R(0), //
B(Return), //
},
0}};
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
} // namespace interpreter } // namespace interpreter
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -2617,7 +2617,6 @@ TEST(InterpreterBasicLoops) { ...@@ -2617,7 +2617,6 @@ TEST(InterpreterBasicLoops) {
TEST(InterpreterForIn) { TEST(InterpreterForIn) {
HandleAndZoneScope handles; HandleAndZoneScope handles;
// TODO(oth): Add a test here for delete mid-loop when delete is ready.
std::pair<const char*, int> for_in_samples[] = { std::pair<const char*, int> for_in_samples[] = {
{"function f() {\n" {"function f() {\n"
" var r = -1;\n" " var r = -1;\n"
...@@ -2950,6 +2949,148 @@ TEST(InterpreterNewTarget) { ...@@ -2950,6 +2949,148 @@ TEST(InterpreterNewTarget) {
CHECK(new_target_name->SameValue(*factory->NewStringFromStaticChars("f"))); CHECK(new_target_name->SameValue(*factory->NewStringFromStaticChars("f")));
} }
TEST(InterpreterAssignmentInExpressions) {
HandleAndZoneScope handles;
std::pair<const char*, int> samples[] = {
{"function f() {\n"
" var x = 7;\n"
" var y = x + (x = 1) + (x = 2);\n"
" return y;\n"
"}",
10},
{"function f() {\n"
" var x = 7;\n"
" var y = x + (x = 1) + (x = 2);\n"
" return x;\n"
"}",
2},
{"function f() {\n"
" var x = 55;\n"
" x = x + (x = 100) + (x = 101);\n"
" return x;\n"
"}",
256},
{"function f() {\n"
" var x = 7;\n"
" return ++x + x + x++;\n"
"}",
24},
{"function f() {\n"
" var x = 7;\n"
" var y = 1 + ++x + x + x++;\n"
" return x;\n"
"}",
9},
{"function f() {\n"
" var x = 7;\n"
" var y = ++x + x + x++;\n"
" return x;\n"
"}",
9},
{"function f() {\n"
" var x = 7, y = 100, z = 1000;\n"
" return x + (x += 3) + y + (y *= 10) + (z *= 7) + z;\n"
"}",
15117},
{"function f() {\n"
" var inner = function (x) { return x + (x = 2) + (x = 4) + x; };\n"
" return inner(1);\n"
"}",
11},
{"function f() {\n"
" var x = 1, y = 2;\n"
" x = x + (x = 3) + y + (y = 4), y = y + (y = 5) + y + x;\n"
" return x + y;\n"
"}",
10 + 24},
{"function f() {\n"
" var x = 0;\n"
" var y = x | (x = 1) | (x = 2);\n"
" return x;\n"
"}",
2},
{"function f() {\n"
" var x = 0;\n"
" var y = x || (x = 1);\n"
" return x;\n"
"}",
1},
{"function f() {\n"
" var x = 1;\n"
" var y = x && (x = 2) && (x = 3);\n"
" return x;\n"
"}",
3},
{"function f() {\n"
" var x = 1;\n"
" var y = x || (x = 2);\n"
" return x;\n"
"}",
1},
{"function f() {\n"
" var x = 1;\n"
" x = (x << (x = 3)) | (x = 16);\n"
" return x;\n"
"}",
24},
{"function f() {\n"
" var r = 7;\n"
" var s = 11;\n"
" var t = 13;\n"
" var u = r + s + t + (r = 10) + (s = 20) +"
" (t = (r + s)) + r + s + t;\n"
" return r + s + t + u;\n"
"}",
211},
{"function f() {\n"
" var r = 7;\n"
" var s = 11;\n"
" var t = 13;\n"
" return r > (3 * s * (s = 1)) ? (t + (t += 1)) : (r + (r = 4));\n"
"}",
11},
{"function f() {\n"
" var r = 7;\n"
" var s = 11;\n"
" var t = 13;\n"
" return r > (3 * s * (s = 0)) ? (t + (t += 1)) : (r + (r = 4));\n"
"}",
27},
{"function f() {\n"
" var r = 7;\n"
" var s = 11;\n"
" var t = 13;\n"
" return (r + (r = 5)) > s ? r : t;\n"
"}",
5},
{"function f(a) {\n"
" return a + (arguments[0] = 10);\n"
"}",
50},
{"function f(a) {\n"
" return a + (arguments[0] = 10) + a;\n"
"}",
60},
{"function f(a) {\n"
" return a + (arguments[0] = 10) + arguments[0];\n"
"}",
60},
};
const int arg_value = 40;
for (size_t i = 0; i < arraysize(samples); i++) {
InterpreterTester tester(handles.main_isolate(), samples[i].first);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> return_val =
callable(handle(Smi::FromInt(arg_value), handles.main_isolate()))
.ToHandleChecked();
CHECK_EQ(Handle<Smi>::cast(return_val)->value(), samples[i].second);
}
}
} // namespace interpreter } // namespace interpreter
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -22,12 +22,12 @@ class BytecodeArrayBuilderTest : public TestWithIsolateAndZone { ...@@ -22,12 +22,12 @@ class BytecodeArrayBuilderTest : public TestWithIsolateAndZone {
TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) { TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
BytecodeArrayBuilder builder(isolate(), zone()); BytecodeArrayBuilder builder(isolate(), zone());
builder.set_locals_count(1); builder.set_locals_count(2);
builder.set_context_count(1); builder.set_context_count(1);
builder.set_parameter_count(0); builder.set_parameter_count(0);
CHECK_EQ(builder.locals_count(), 1); CHECK_EQ(builder.locals_count(), 2);
CHECK_EQ(builder.context_count(), 1); CHECK_EQ(builder.context_count(), 1);
CHECK_EQ(builder.fixed_register_count(), 2); CHECK_EQ(builder.fixed_register_count(), 3);
// Emit constant loads. // Emit constant loads.
builder.LoadLiteral(Smi::FromInt(0)) builder.LoadLiteral(Smi::FromInt(0))
...@@ -46,6 +46,10 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) { ...@@ -46,6 +46,10 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
.LoadNull() .LoadNull()
.StoreAccumulatorInRegister(reg); .StoreAccumulatorInRegister(reg);
// Emit register-register transfer.
Register other(1);
builder.MoveRegister(reg, other);
// Emit global load / store operations. // Emit global load / store operations.
builder.LoadGlobal(0, 1, LanguageMode::SLOPPY, TypeofMode::NOT_INSIDE_TYPEOF) builder.LoadGlobal(0, 1, LanguageMode::SLOPPY, TypeofMode::NOT_INSIDE_TYPEOF)
.LoadGlobal(0, 1, LanguageMode::STRICT, TypeofMode::NOT_INSIDE_TYPEOF) .LoadGlobal(0, 1, LanguageMode::STRICT, TypeofMode::NOT_INSIDE_TYPEOF)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment