Chromium Code Reviews| Index: src/interpreter/bytecode-generator.cc |
| diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc |
| index 22195c1d6679c0b57568e1f50148eb501fbaeec7..a4fa91becef40b03e113c859e872ee2eba0b3eaa 100644 |
| --- a/src/interpreter/bytecode-generator.cc |
| +++ b/src/interpreter/bytecode-generator.cc |
| @@ -238,8 +238,7 @@ class BytecodeGenerator::ExpressionResultScope { |
| // Scoped class used when the result of the current expression is not |
| // expected to produce a result. |
| -class BytecodeGenerator::EffectResultScope final |
| - : public ExpressionResultScope { |
| +class BytecodeGenerator::EffectResultScope : public ExpressionResultScope { |
| public: |
| explicit EffectResultScope(BytecodeGenerator* generator) |
| : ExpressionResultScope(generator, Expression::kEffect) { |
| @@ -297,6 +296,81 @@ class BytecodeGenerator::RegisterResultScope final |
| }; |
| +// Top-level result scope when evaluating expresions. It provides an |
| +// outer scope for register allocations and also ensures that |
| +// assignments in binary expressions produce the correct result by |
| +// aliasing registers to temporaries when necessary. |
| +class BytecodeGenerator::StatementResultScope : public EffectResultScope { |
|
rmcilroy
2015/11/03 14:17:52
nit - final
oth
2015/11/04 10:03:36
Done.
|
| + public: |
| + explicit StatementResultScope(BytecodeGenerator* generator) |
| + : EffectResultScope(generator), |
| + alias_mappings_(generator->zone()), |
| + aliased_locals_and_parameters_(generator->zone()), |
| + hazard_depth_(0) { |
| + DCHECK_NULL(generator->statement_execution_result()); |
| + generator->set_statement_execution_result(this); |
| + } |
| + |
| + ~StatementResultScope() { |
| + DCHECK_EQ(this, generator()->statement_execution_result()); |
| + DCHECK(hazard_depth_ == 0); |
| + generator()->set_statement_execution_result(nullptr); |
| + } |
| + |
| + Register LoadFromAliasedRegister(Register reg) { |
|
rmcilroy
2015/11/03 14:17:52
Could you add a comment explaining what the return
oth
2015/11/04 10:03:36
Done.
|
| + if (hazard_depth_ > 0) { |
| + // Insert direct mapping if none exists as register is now of interest. |
| + auto insert_result = alias_mappings_.insert(std::make_pair(reg, reg)); |
| + auto iterator = insert_result.first; |
|
rmcilroy
2015/11/03 14:17:52
nit - /s/iterator/previous_value
oth
2015/11/04 10:03:36
Renamed to 'mapping' as it's either the value just
|
| + return iterator->second; |
| + } else { |
| + return reg; |
| + } |
| + } |
| + |
| + Register StoreToAliasedRegister(Register reg) { |
| + if (hazard_depth_ == 0 || |
| + alias_mappings_.find(reg) == alias_mappings_.end()) { |
| + return reg; |
| + } else { |
| + Register alias = NewRegister(); |
| + alias_mappings_[reg] = alias; |
| + if (generator()->builder()->RegisterIsParameterOrLocal(reg)) { |
| + aliased_locals_and_parameters_.insert(reg); |
| + } |
|
rmcilroy
2015/11/03 14:17:52
nit - a few comments here would be helpful
oth
2015/11/04 10:03:36
Done.
|
| + return alias; |
| + } |
| + } |
| + |
| + void EnterAssignmentHazard() { hazard_depth_++; } |
| + |
| + void LeaveAssignmentHazard() { |
| + if (--hazard_depth_ == 0) { |
| + StopAliasingRegisters(); |
| + } |
| + } |
| + |
| + private: |
| + void StopAliasingRegisters() { |
|
rmcilroy
2015/11/03 14:17:52
nit - RestoreAliasedLocalsAndParameters?
oth
2015/11/04 10:03:36
Done. Definitely a better name.
|
| + // 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()) { |
| + builder()->MoveRegister(mapping->second, *reg); |
| + } |
| + } |
| + alias_mappings_.clear(); |
| + aliased_locals_and_parameters_.clear(); |
| + } |
| + |
| + ZoneMap<Register, Register> alias_mappings_; |
| + ZoneSet<Register> aliased_locals_and_parameters_; |
| + int hazard_depth_; |
| +}; |
| + |
| + |
| BytecodeGenerator::BytecodeGenerator(Isolate* isolate, Zone* zone) |
| : isolate_(isolate), |
| zone_(zone), |
| @@ -307,8 +381,7 @@ BytecodeGenerator::BytecodeGenerator(Isolate* isolate, Zone* zone) |
| execution_control_(nullptr), |
| execution_context_(nullptr), |
| execution_result_(nullptr), |
| - binary_expression_depth_(0), |
| - binary_expression_hazard_set_(zone) { |
| + statement_execution_result_(nullptr) { |
| InitializeAstVisitor(isolate); |
| } |
| @@ -508,8 +581,7 @@ void BytecodeGenerator::VisitDeclarations( |
| void BytecodeGenerator::VisitExpressionStatement(ExpressionStatement* stmt) { |
| - // TODO(rmcilroy): Replace this with a StatementResultScope when it exists. |
| - EffectResultScope effect_scope(this); |
| + StatementResultScope statement_result_scope(this); |
| VisitForEffect(stmt->expression()); |
| } |
| @@ -529,7 +601,7 @@ void BytecodeGenerator::VisitIfStatement(IfStatement* stmt) { |
| Visit(stmt->else_statement()); |
| } |
| } else { |
| - VisitForAccumulatorValue(stmt->condition()); |
| + VisitCondition(stmt->condition()); |
| builder()->JumpIfFalse(&else_label); |
| Visit(stmt->then_statement()); |
| if (stmt->HasElseStatement()) { |
| @@ -561,7 +633,7 @@ void BytecodeGenerator::VisitBreakStatement(BreakStatement* stmt) { |
| void BytecodeGenerator::VisitReturnStatement(ReturnStatement* stmt) { |
| - EffectResultScope effect_scope(this); |
| + StatementResultScope statement_result_scope(this); |
| VisitForAccumulatorValue(stmt->expression()); |
| builder()->Return(); |
| } |
| @@ -626,11 +698,16 @@ void BytecodeGenerator::VisitCaseClause(CaseClause* clause) { |
| } |
| +void BytecodeGenerator::VisitCondition(Expression* expr) { |
| + StatementResultScope statement_result_scope(this); |
| + VisitForAccumulatorValue(expr); |
| +} |
| + |
| + |
| void BytecodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) { |
| LoopBuilder loop_builder(builder()); |
| ControlScopeForIteration execution_control(this, stmt, &loop_builder); |
| BytecodeLabel body_label, condition_label, done_label; |
| - |
| if (stmt->cond()->ToBooleanIsFalse()) { |
| Visit(stmt->body()); |
| // Bind condition_label and done_label for processing continue and break. |
| @@ -644,7 +721,7 @@ void BytecodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) { |
| if (stmt->cond()->ToBooleanIsTrue()) { |
| builder()->Jump(&body_label); |
| } else { |
| - VisitForAccumulatorValue(stmt->cond()); |
| + VisitCondition(stmt->cond()); |
| builder()->JumpIfTrue(&body_label); |
| } |
| builder()->Bind(&done_label); |
| @@ -674,7 +751,7 @@ void BytecodeGenerator::VisitWhileStatement(WhileStatement* stmt) { |
| if (stmt->cond()->ToBooleanIsTrue()) { |
| builder()->Jump(&body_label); |
| } else { |
| - VisitForAccumulatorValue(stmt->cond()); |
| + VisitCondition(stmt->cond()); |
| builder()->JumpIfTrue(&body_label); |
| } |
| builder()->Bind(&done_label); |
| @@ -710,7 +787,7 @@ void BytecodeGenerator::VisitForStatement(ForStatement* stmt) { |
| } |
| if (stmt->cond() && !stmt->cond()->ToBooleanIsTrue()) { |
| builder()->Bind(&condition_label); |
| - VisitForAccumulatorValue(stmt->cond()); |
| + VisitCondition(stmt->cond()); |
| builder()->JumpIfTrue(&body_label); |
| } else { |
| builder()->Jump(&body_label); |
| @@ -1200,6 +1277,9 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable, |
| switch (variable->location()) { |
| case VariableLocation::LOCAL: { |
| Register source(Register(variable->index())); |
| + if (statement_execution_result()) { |
|
rmcilroy
2015/11/03 14:17:51
Should we not always have a statement_execution_re
oth
2015/11/04 10:03:36
Right, this bit is tricky. There are places we rea
|
| + source = statement_execution_result()->LoadFromAliasedRegister(source); |
|
rmcilroy
2015/11/03 14:17:51
nit - RecordLoadFrom...
oth
2015/11/04 10:03:36
GetRegisterToLoadFrom?
GetRegisterToStoreTo?
|
| + } |
| execution_result()->SetResultInRegister(source); |
| break; |
| } |
| @@ -1207,6 +1287,9 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable, |
| // The parameter indices are shifted by 1 (receiver is variable |
| // index -1 but is parameter index 0 in BytecodeArrayBuilder). |
| Register source = builder()->Parameter(variable->index() + 1); |
| + if (statement_execution_result()) { |
| + source = statement_execution_result()->LoadFromAliasedRegister(source); |
| + } |
| execution_result()->SetResultInRegister(source); |
| break; |
| } |
| @@ -1270,16 +1353,22 @@ void BytecodeGenerator::VisitVariableAssignment(Variable* variable, |
| case VariableLocation::LOCAL: { |
| // TODO(rmcilroy): support const mode initialization. |
| Register destination(variable->index()); |
| + if (statement_execution_result()) { |
|
rmcilroy
2015/11/03 14:17:51
Ditto with assignments?
oth
2015/11/04 10:03:36
Changed to assignment_hazard_scope, but have to ch
|
| + destination = |
| + statement_execution_result()->StoreToAliasedRegister(destination); |
|
rmcilroy
2015/11/03 14:17:52
nit - RecordStoreTo...
oth
2015/11/04 10:03:36
GetRegisterForStore okay?
|
| + } |
| builder()->StoreAccumulatorInRegister(destination); |
| - RecordStoreToRegister(destination); |
| break; |
| } |
| case VariableLocation::PARAMETER: { |
| // The parameter indices are shifted by 1 (receiver is variable |
| // index -1 but is parameter index 0 in BytecodeArrayBuilder). |
| Register destination(builder()->Parameter(variable->index() + 1)); |
| + if (statement_execution_result()) { |
| + destination = |
| + statement_execution_result()->StoreToAliasedRegister(destination); |
| + } |
| builder()->StoreAccumulatorInRegister(destination); |
| - RecordStoreToRegister(destination); |
| break; |
| } |
| case VariableLocation::GLOBAL: |
| @@ -1825,38 +1914,34 @@ void BytecodeGenerator::VisitBinaryOperation(BinaryOperation* binop) { |
| void BytecodeGenerator::VisitCompareOperation(CompareOperation* expr) { |
| - // TODO(oth): Remove PrepareForBinaryExpression/CompleteBinaryExpression |
| - // once we have StatementScope that tracks hazardous loads/stores. |
| - PrepareForBinaryExpression(); |
| + // The evaluation of binary comparison expressions has an assignment |
| + // hazard because the lhs may be a variable that evaluates to a |
| + // 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. |
| + statement_execution_result()->EnterAssignmentHazard(); |
| 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()); |
| builder()->CompareOperation(expr->op(), lhs, language_mode_strength()); |
| - CompleteBinaryExpression(); |
| execution_result()->SetResultInAccumulator(); |
| + statement_execution_result()->LeaveAssignmentHazard(); |
| } |
| void BytecodeGenerator::VisitArithmeticExpression(BinaryOperation* expr) { |
| - // TODO(oth): Remove PrepareForBinaryExpression/CompleteBinaryExpression |
| - // once we have StatementScope that tracks hazardous loads/stores. |
| - PrepareForBinaryExpression(); |
| + // The evaluation of binary arithmetic expressions has an assignment |
| + // hazard because the lhs may be a variable that evaluates to a |
| + // 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. |
| + statement_execution_result()->EnterAssignmentHazard(); |
|
rmcilroy
2015/11/03 14:17:52
Could we do this as a scoped class (AssignmentHaza
oth
2015/11/04 10:03:36
Done.
|
| 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()); |
| builder()->BinaryOperation(expr->op(), lhs, language_mode_strength()); |
| - CompleteBinaryExpression(); |
| execution_result()->SetResultInAccumulator(); |
| + statement_execution_result()->LeaveAssignmentHazard(); |
| } |
| @@ -2076,13 +2161,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. |
| void BytecodeGenerator::VisitForAccumulatorValue(Expression* expr) { |
| AccumulatorResultScope accumulator_scope(this); |
| @@ -2106,35 +2184,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 { |
| if (execution_context() == nullptr) { |
| // Return the incoming function context for the outermost execution context. |