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

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

Issue 1634153002: [Interpreter] Adds support for const/let variables to interpreter. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: rebased the patch Created 4 years, 11 months 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 ebfdf33c581585628e491e43102b366e18f6ea77..05037e0eaa44a7577259a32dac8d38072819ae48 100644
--- a/src/interpreter/bytecode-generator.cc
+++ b/src/interpreter/bytecode-generator.cc
@@ -718,7 +718,9 @@ void BytecodeGenerator::VisitFunctionDeclaration(FunctionDeclaration* decl) {
case VariableLocation::PARAMETER:
case VariableLocation::LOCAL: {
VisitForAccumulatorValue(decl->fun());
- VisitVariableAssignment(variable, FeedbackVectorSlot::Invalid());
+ DCHECK(variable->mode() == LET || variable->mode() == VAR);
+ VisitVariableAssignment(variable, Token::INIT,
+ FeedbackVectorSlot::Invalid());
break;
}
case VariableLocation::CONTEXT: {
@@ -1000,7 +1002,7 @@ void BytecodeGenerator::VisitForInAssignment(Expression* expr,
switch (assign_type) {
case VARIABLE: {
Variable* variable = expr->AsVariableProxy()->var();
- VisitVariableAssignment(variable, slot);
+ VisitVariableAssignment(variable, Token::ASSIGN, slot);
break;
}
case NAMED_PROPERTY: {
@@ -1504,14 +1506,25 @@ void BytecodeGenerator::VisitVariableProxy(VariableProxy* proxy) {
VisitVariableLoad(proxy->var(), proxy->VariableFeedbackSlot());
}
+void BytecodeGenerator::BuildHoleCheckForVariableLoad(VariableMode mode,
+ Handle<String> name) {
+ if (mode == CONST_LEGACY) {
+ BytecodeLabel end_label;
+ builder()->JumpIfNotHole(&end_label).LoadUndefined().Bind(&end_label);
rmcilroy 2016/01/26 14:37:28 Newlines between builder operations (throughout).
mythria 2016/02/01 10:02:18 I added newlines. cl format removes them. Shall I
rmcilroy 2016/02/03 12:28:25 I think we should override this if possible, it is
+ } else if (mode == LET || mode == CONST) {
+ BuildThrowIfHole(name);
+ }
+}
void BytecodeGenerator::VisitVariableLoad(Variable* variable,
FeedbackVectorSlot slot,
TypeofMode typeof_mode) {
+ VariableMode mode = variable->mode();
switch (variable->location()) {
case VariableLocation::LOCAL: {
Register source(Register(variable->index()));
builder()->LoadAccumulatorWithRegister(source);
+ BuildHoleCheckForVariableLoad(mode, variable->name());
execution_result()->SetResultInAccumulator();
break;
}
@@ -1520,6 +1533,7 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable,
// index -1 but is parameter index 0 in BytecodeArrayBuilder).
Register source = builder()->Parameter(variable->index() + 1);
builder()->LoadAccumulatorWithRegister(source);
+ BuildHoleCheckForVariableLoad(mode, variable->name());
execution_result()->SetResultInAccumulator();
break;
}
@@ -1552,10 +1566,10 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable,
.StoreAccumulatorInRegister(context_reg);
}
}
+
builder()->LoadContextSlot(context_reg, variable->index());
+ BuildHoleCheckForVariableLoad(mode, variable->name());
execution_result()->SetResultInAccumulator();
- // TODO(rmcilroy): Perform check for uninitialized legacy const, const and
- // let variables.
break;
}
case VariableLocation::LOOKUP: {
@@ -1581,21 +1595,119 @@ Register BytecodeGenerator::VisitVariableLoadForRegisterValue(
return register_scope.ResultRegister();
}
+void BytecodeGenerator::BuildThrowIfHole(Handle<String> name) {
+ Register name_reg = register_allocator()->NewRegister();
+ BytecodeLabel end_label;
+ builder()
+ ->JumpIfNotHole(&end_label)
+ .LoadLiteral(name)
+ .StoreAccumulatorInRegister(name_reg)
+ .CallRuntime(Runtime::kThrowReferenceError, name_reg, 1)
+ .Bind(&end_label);
+}
+
+void BytecodeGenerator::BuildThrowIfNotHole(Handle<String> name) {
+ Register name_reg = register_allocator()->NewRegister();
+ BytecodeLabel end_label;
+ builder()
+ ->JumpIfHole(&end_label)
+ .LoadLiteral(name)
+ .StoreAccumulatorInRegister(name_reg)
+ .CallRuntime(Runtime::kThrowReferenceError, name_reg, 1)
+ .Bind(&end_label);
+}
+
+void BytecodeGenerator::BuildThrowReassignConstant(Register destination,
+ Handle<String> name) {
+ Register name_reg = register_allocator()->NewRegister();
+ BytecodeLabel else_label;
+ builder()
+ ->LoadLiteral(name)
+ .StoreAccumulatorInRegister(name_reg)
+ .LoadAccumulatorWithRegister(destination)
+ .JumpIfNotHole(&else_label)
+ .CallRuntime(Runtime::kThrowReferenceError, name_reg, 1)
+ .Bind(&else_label)
+ .CallRuntime(Runtime::kThrowConstAssignError, name_reg, 1);
+}
+
+void BytecodeGenerator::BuildThrowReassignConstant(Handle<String> name) {
rmcilroy 2016/01/26 14:37:28 Could we merge both BuildThrowReassignConstant (an
mythria 2016/02/01 10:02:18 Done.
+ Register name_reg = register_allocator()->NewRegister();
+ BytecodeLabel else_label;
+ builder()
+ ->JumpIfNotHole(&else_label)
+ .LoadLiteral(name)
rmcilroy 2016/01/26 14:37:28 Move LoadLiteral / StoreAccumulatorInRegister(name
mythria 2016/02/01 10:02:18 For throwing const reassign error, we don't need t
+ .StoreAccumulatorInRegister(name_reg)
+ .CallRuntime(Runtime::kThrowReferenceError, name_reg, 1)
+ .Bind(&else_label)
+ .LoadLiteral(name)
+ .StoreAccumulatorInRegister(name_reg)
+ .CallRuntime(Runtime::kThrowConstAssignError, name_reg, 1);
+}
void BytecodeGenerator::VisitVariableAssignment(Variable* variable,
+ Token::Value op,
FeedbackVectorSlot slot) {
+ VariableMode mode = variable->mode();
+ RegisterAllocationScope assignment_register_scope(this);
+ BytecodeLabel end_label;
+ bool hole_check_required =
+ (mode == CONST_LEGACY) || (mode == LET && op != Token::INIT) ||
+ (mode == CONST && op != Token::INIT) ||
+ (mode == CONST && op == Token::INIT && variable->is_this());
switch (variable->location()) {
+ case VariableLocation::PARAMETER:
case VariableLocation::LOCAL: {
- // TODO(rmcilroy): support const mode initialization.
- Register destination(variable->index());
- builder()->StoreAccumulatorInRegister(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));
- builder()->StoreAccumulatorInRegister(destination);
+ Register destination;
+ if (VariableLocation::PARAMETER == variable->location()) {
+ destination = Register(builder()->Parameter(variable->index() + 1));
+ } else {
+ destination = Register(variable->index());
+ }
+
+ if (!hole_check_required) {
+ builder()->StoreAccumulatorInRegister(destination);
+ } else if (mode == CONST && op != Token::INIT) {
rmcilroy 2016/01/26 14:37:28 As discussed, could you merge the code to do the h
mythria 2016/02/01 10:02:18 Done. I did not pull const_legacy && Token::Init t
+ // Assigning to constant is not allowed. Throw an appropriate
+ // exception. Accumulator is not restored back.
+ BuildThrowReassignConstant(destination, variable->name());
+ } else if (mode == CONST_LEGACY && op != Token::INIT) {
+ // TODO(mythria): Find a test case for strict mode legacy constants.
rmcilroy 2016/01/26 14:37:28 Legacy const doesn't exist for strict mode, right?
mythria 2016/02/01 10:02:18 Done. Yes, it was a old comment. Retained the DCHE
+ DCHECK(!is_strict(language_mode()));
+ } else {
+ // Store accumulator to a temporary register and load destination to
+ // perform hole check.
+ BytecodeLabel end_label;
+ Register value_temp = register_allocator()->NewRegister();
+ builder()
+ ->StoreAccumulatorInRegister(value_temp)
+ .LoadAccumulatorWithRegister(destination);
+ if (mode == CONST_LEGACY && op == Token::INIT) {
+ // If the destination is already initialized silently ignore this
+ // assignment. Restore the accumulator.
+ builder()
+ ->JumpIfNotHole(&end_label)
+ .MoveRegister(value_temp, destination)
+ .Bind(&end_label)
+ .LoadAccumulatorWithRegister(value_temp);
+ } else if (mode == LET && op != Token::INIT) {
+ BuildThrowIfHole(variable->name());
+ // Move does not help here, because accumulator should be restored to
+ // value_temp as well.
+ builder()
+ ->LoadAccumulatorWithRegister(value_temp)
+ .StoreAccumulatorInRegister(destination);
+ } else {
+ DCHECK(variable->is_this() && mode == CONST && op == Token::INIT);
+ // Perform an initialization check for 'this'. 'this' variable is the
+ // only variable being able to trigger bind operations outside the TDZ
+ // via 'super' calls.
+ BuildThrowIfNotHole(variable->name());
+ builder()
+ ->LoadAccumulatorWithRegister(value_temp)
+ .StoreAccumulatorInRegister(destination);
+ }
+ }
break;
}
case VariableLocation::GLOBAL:
@@ -1605,14 +1717,16 @@ void BytecodeGenerator::VisitVariableAssignment(Variable* variable,
break;
}
case VariableLocation::CONTEXT: {
- // TODO(rmcilroy): support const mode initialization.
int depth = execution_context()->ContextChainDepth(variable->scope());
ContextScope* context = execution_context()->Previous(depth);
Register context_reg;
+ Register value_temp;
+ bool value_in_accumulator = true;
rmcilroy 2016/01/26 14:37:28 Let's not try to be smart with "value_in_accumulat
mythria 2016/02/01 10:02:18 Done.
+
if (context) {
context_reg = context->reg();
} else {
- Register value_temp = register_allocator()->NewRegister();
+ value_temp = register_allocator()->NewRegister();
context_reg = register_allocator()->NewRegister();
// Walk the context chain to find the context at the given depth.
// TODO(rmcilroy): Perform this work in a bytecode handler once we have
@@ -1628,15 +1742,85 @@ void BytecodeGenerator::VisitVariableAssignment(Variable* variable,
->LoadContextSlot(context_reg, Context::PREVIOUS_INDEX)
.StoreAccumulatorInRegister(context_reg);
}
- builder()->LoadAccumulatorWithRegister(value_temp);
+ value_in_accumulator = false;
+ }
+
+ if (!hole_check_required) {
+ if (!value_in_accumulator) {
+ builder()->LoadAccumulatorWithRegister(value_temp);
+ }
+ builder()->StoreContextSlot(context_reg, variable->index());
+ } else if (mode == CONST && op != Token::INIT) {
+ // Assigning to constant is not allowed. Throw an appropriate
+ // exception. Accumulator is not restored back.
+ builder()->LoadContextSlot(context_reg, variable->index());
+ BuildThrowReassignConstant(variable->name());
+ } else if (mode == CONST_LEGACY && op != Token::INIT) {
+ // TODO(mythria): Find a test case for strict mode legacy constants.
rmcilroy 2016/01/26 14:37:28 ditto.
mythria 2016/02/01 10:02:18 Done.
+ DCHECK(!is_strict(language_mode()));
+ // Accumulator has to be restored back.
+ if (!value_in_accumulator) {
+ builder()->LoadAccumulatorWithRegister(value_temp);
+ }
+ } else {
+ // Store accumulator to a temporary register and load destination to
+ // perform hole check.
+ if (value_in_accumulator) {
+ value_temp = register_allocator()->NewRegister();
+ builder()->StoreAccumulatorInRegister(value_temp);
+ }
+ builder()->LoadContextSlot(context_reg, variable->index());
+ BytecodeLabel end_label;
+ if (mode == CONST_LEGACY && op == Token::INIT) {
+ // If the destination is already initialized silently ignore this
+ // assignment and restore back the accumulator.
rmcilroy 2016/01/26 14:37:28 Not sure what "restore back the accumulator" means
mythria 2016/02/01 10:02:18 Done.
+ builder()
+ ->JumpIfNotHole(&end_label)
+ .LoadAccumulatorWithRegister(value_temp)
+ .StoreContextSlot(context_reg, variable->index())
+ .Bind(&end_label)
+ .LoadAccumulatorWithRegister(value_temp);
+ } else if (mode == LET && op != Token::INIT) {
+ BuildThrowIfHole(variable->name());
+ builder()
+ ->LoadAccumulatorWithRegister(value_temp)
+ .StoreContextSlot(context_reg, variable->index());
+ } else {
+ DCHECK(variable->is_this() && mode == CONST && op == Token::INIT);
+ // Perform an initialization check for 'this'. 'this' variable is the
+ // only variable being able to trigger bind operations outside the TDZ
rmcilroy 2016/01/26 14:37:28 /s/variable being able/variable able
mythria 2016/02/01 10:02:18 Done.
+ // via 'super' calls.
+ BuildThrowIfNotHole(variable->name());
+ builder()
+ ->LoadAccumulatorWithRegister(value_temp)
+ .StoreContextSlot(context_reg, variable->index());
+ }
}
- builder()->StoreContextSlot(context_reg, variable->index());
break;
}
case VariableLocation::LOOKUP: {
- // TODO(mythria): Use Runtime::kInitializeLegacyConstLookupSlot for
- // initializations of const declarations.
- builder()->StoreLookupSlot(variable->name(), language_mode());
+ if (mode == CONST_LEGACY && op == Token::INIT) {
+ register_allocator()->PrepareForConsecutiveAllocations(3);
+ Register value = register_allocator()->NextConsecutiveRegister();
+ Register context = register_allocator()->NextConsecutiveRegister();
+ Register name = register_allocator()->NextConsecutiveRegister();
+
+ // InitializeLegacyConstLookupSlot runtime call returns the 'value'
+ // passed to it. So, accumulator will have its original contents when
+ // runtime call returns.
+ builder()
+ ->StoreAccumulatorInRegister(value)
+ .MoveRegister(execution_context()->reg(), context)
+ .LoadLiteral(variable->name())
+ .StoreAccumulatorInRegister(name)
+ .CallRuntime(Runtime::kInitializeLegacyConstLookupSlot, value, 3);
+ } else if (mode == CONST_LEGACY && op != Token::INIT) {
+ // TODO(mythria): Find a test case for strict mode. We should throw in
+ // strict mode.
+ DCHECK(!is_strict(language_mode()));
+ } else {
+ builder()->StoreLookupSlot(variable->name(), language_mode());
+ }
break;
}
}
@@ -1729,7 +1913,7 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) {
// TODO(oth): The VisitVariableAssignment() call is hard to reason about.
// Is the value in the accumulator safe? Yes, but scary.
Variable* variable = expr->target()->AsVariableProxy()->var();
- VisitVariableAssignment(variable, slot);
+ VisitVariableAssignment(variable, expr->op(), slot);
break;
}
case NAMED_PROPERTY:
@@ -2155,7 +2339,7 @@ void BytecodeGenerator::VisitCountOperation(CountOperation* expr) {
switch (assign_type) {
case VARIABLE: {
Variable* variable = expr->expression()->AsVariableProxy()->var();
- VisitVariableAssignment(variable, feedback_slot);
+ VisitVariableAssignment(variable, expr->op(), feedback_slot);
break;
}
case NAMED_PROPERTY: {
@@ -2419,7 +2603,8 @@ void BytecodeGenerator::VisitArgumentsObject(Variable* variable) {
? CreateArgumentsType::kUnmappedArguments
: CreateArgumentsType::kMappedArguments;
builder()->CreateArguments(type);
- VisitVariableAssignment(variable, FeedbackVectorSlot::Invalid());
+ VisitVariableAssignment(variable, Token::ASSIGN,
+ FeedbackVectorSlot::Invalid());
}
@@ -2431,7 +2616,7 @@ void BytecodeGenerator::VisitThisFunctionVariable(Variable* variable) {
// Store the closure we were called with in the given variable.
builder()->LoadAccumulatorWithRegister(Register::function_closure());
- VisitVariableAssignment(variable, FeedbackVectorSlot::Invalid());
+ VisitVariableAssignment(variable, Token::INIT, FeedbackVectorSlot::Invalid());
}
@@ -2440,7 +2625,7 @@ void BytecodeGenerator::VisitNewTargetVariable(Variable* variable) {
// Store the new target we were called with in the given variable.
builder()->LoadAccumulatorWithRegister(Register::new_target());
- VisitVariableAssignment(variable, FeedbackVectorSlot::Invalid());
+ VisitVariableAssignment(variable, Token::INIT, FeedbackVectorSlot::Invalid());
}

Powered by Google App Engine
This is Rietveld 408576698