Index: src/interpreter/bytecode-generator.cc |
diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc |
index 05ee206997d99a3110b8c6ca60b49ff9c9ed821b..d4755a6dde21d19ea87595b03e32f4bf4747e1dd 100644 |
--- a/src/interpreter/bytecode-generator.cc |
+++ b/src/interpreter/bytecode-generator.cc |
@@ -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) |
: isolate_(isolate), |
zone_(zone), |
@@ -307,15 +421,11 @@ BytecodeGenerator::BytecodeGenerator(Isolate* isolate, Zone* zone) |
execution_control_(nullptr), |
execution_context_(nullptr), |
execution_result_(nullptr), |
- binary_expression_depth_(0), |
- binary_expression_hazard_set_(zone) { |
+ assignment_hazard_helper_(this) { |
InitializeAstVisitor(isolate); |
} |
-BytecodeGenerator::~BytecodeGenerator() {} |
- |
- |
Handle<BytecodeArray> BytecodeGenerator::MakeBytecode(CompilationInfo* info) { |
set_info(info); |
set_scope(info->scope()); |
@@ -506,8 +616,7 @@ void BytecodeGenerator::VisitDeclarations( |
void BytecodeGenerator::VisitExpressionStatement(ExpressionStatement* stmt) { |
- // TODO(rmcilroy): Replace this with a StatementResultScope when it exists. |
- EffectResultScope effect_scope(this); |
+ EffectResultScope statement_result_scope(this); |
VisitForEffect(stmt->expression()); |
} |
@@ -559,7 +668,7 @@ void BytecodeGenerator::VisitBreakStatement(BreakStatement* stmt) { |
void BytecodeGenerator::VisitReturnStatement(ReturnStatement* stmt) { |
- EffectResultScope effect_scope(this); |
+ EffectResultScope statement_result_scope(this); |
VisitForAccumulatorValue(stmt->expression()); |
builder()->Return(); |
} |
@@ -628,7 +737,6 @@ 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. |
@@ -764,11 +872,7 @@ void BytecodeGenerator::VisitForInAssignment(Expression* expr, |
void BytecodeGenerator::VisitForInStatement(ForInStatement* stmt) { |
- // TODO(oth): For now we need a parent scope for paths that end up |
- // 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); |
+ EffectResultScope statement_result_scope(this); |
if (stmt->subject()->IsNullLiteral() || |
stmt->subject()->IsUndefinedLiteral(isolate())) { |
@@ -1198,6 +1302,7 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable, |
switch (variable->location()) { |
case VariableLocation::LOCAL: { |
Register source(Register(variable->index())); |
+ source = assignment_hazard_helper()->GetRegisterForLoad(source); |
execution_result()->SetResultInRegister(source); |
break; |
} |
@@ -1205,6 +1310,7 @@ 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); |
+ source = assignment_hazard_helper()->GetRegisterForLoad(source); |
execution_result()->SetResultInRegister(source); |
break; |
} |
@@ -1269,16 +1375,19 @@ void BytecodeGenerator::VisitVariableAssignment(Variable* variable, |
case VariableLocation::LOCAL: { |
// TODO(rmcilroy): support const mode initialization. |
Register destination(variable->index()); |
+ destination = |
+ assignment_hazard_helper()->GetRegisterForStore(destination); |
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)); |
+ destination = |
+ assignment_hazard_helper()->GetRegisterForStore(destination); |
+ |
builder()->StoreAccumulatorInRegister(destination); |
- RecordStoreToRegister(destination); |
break; |
} |
case VariableLocation::GLOBAL: |
@@ -1826,37 +1935,33 @@ 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. |
+ AssignmentHazardScope assignment_hazard_scope(this); |
+ |
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(); |
} |
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. |
+ AssignmentHazardScope assignment_hazard_scope(this); |
+ |
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(); |
} |
@@ -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. |
void BytecodeGenerator::VisitForAccumulatorValue(Expression* expr) { |
AccumulatorResultScope accumulator_scope(this); |
@@ -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 { |
if (execution_context() == nullptr) { |
// Return the incoming function context for the outermost execution context. |