Chromium Code Reviews| 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()); |
| } |