Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(456)

Unified Diff: src/interpreter/bytecode-generator.cc

Issue 1412683011: [Interpreter] Enable assignments in expressions. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Additional tests for conditional expressions. Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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.

Powered by Google App Engine
This is Rietveld 408576698