 Chromium Code Reviews
 Chromium Code Reviews Issue 1889:
  Remove some of the state-dependent behavior from the code generator....  (Closed) 
  Base URL: http://v8.googlecode.com/svn/branches/bleeding_edge/
    
  
    Issue 1889:
  Remove some of the state-dependent behavior from the code generator....  (Closed) 
  Base URL: http://v8.googlecode.com/svn/branches/bleeding_edge/| Index: src/codegen-arm.cc | 
| =================================================================== | 
| --- src/codegen-arm.cc (revision 246) | 
| +++ src/codegen-arm.cc (working copy) | 
| @@ -109,9 +109,7 @@ | 
| enum AccessType { | 
| UNDEFINED, | 
| LOAD, | 
| - LOAD_TYPEOF_EXPR, | 
| - STORE, | 
| - INIT_CONST | 
| + LOAD_TYPEOF_EXPR | 
| }; | 
| CodeGenState() | 
| @@ -181,7 +179,6 @@ | 
| // The following are used by class Reference. | 
| void LoadReference(Reference* ref); | 
| void UnloadReference(Reference* ref); | 
| - friend class Reference; | 
| // State | 
| bool has_cc() const { return cc_reg_ != al; } | 
| @@ -197,22 +194,33 @@ | 
| return ContextOperand(cp, Context::GLOBAL_INDEX); | 
| } | 
| - MemOperand ContextOperand(Register context, int index) const { | 
| + static MemOperand ContextOperand(Register context, int index) { | 
| return MemOperand(context, Context::SlotOffset(index)); | 
| } | 
| - MemOperand ParameterOperand(int index) const { | 
| + static MemOperand ParameterOperand(Scope* scope, int index) { | 
| // index -2 corresponds to the activated closure, -1 corresponds | 
| // to the receiver | 
| - ASSERT(-2 <= index && index < scope_->num_parameters()); | 
| + ASSERT(-2 <= index && index < scope->num_parameters()); | 
| int offset = JavaScriptFrameConstants::kParam0Offset - index * kPointerSize; | 
| return MemOperand(pp, offset); | 
| } | 
| + MemOperand ParameterOperand(int index) const { | 
| + return ParameterOperand(scope_, index); | 
| + } | 
| + | 
| MemOperand FunctionOperand() const { return ParameterOperand(-2); } | 
| - MemOperand SlotOperand(Slot* slot, Register tmp); | 
| + static MemOperand SlotOperand(MacroAssembler* masm, | 
| + Scope* scope, | 
| + Slot* slot, | 
| + Register tmp); | 
| + MemOperand SlotOperand(Slot* slot, Register tmp) { | 
| + return SlotOperand(masm_, scope_, slot, tmp); | 
| + } | 
| + | 
| void LoadCondition(Expression* x, CodeGenState::AccessType access, | 
| Label* true_target, Label* false_target, bool force_cc); | 
| void Load(Expression* x, | 
| @@ -227,22 +235,48 @@ | 
| // through the context chain. | 
| void LoadTypeofExpression(Expression* x); | 
| + | 
| // References | 
| - void AccessReference(Reference* ref, CodeGenState::AccessType access); | 
| - void GetValue(Reference* ref) { AccessReference(ref, CodeGenState::LOAD); } | 
| - void SetValue(Reference* ref) { AccessReference(ref, CodeGenState::STORE); } | 
| - void InitConst(Reference* ref) { | 
| - AccessReference(ref, CodeGenState::INIT_CONST); | 
| + // Generate code to fetch the value of a reference. The reference is | 
| + // expected to be on top of the expression stack. It is left in place and | 
| + // its value is pushed on top of it. | 
| + void GetValue(Reference* ref); | 
| + | 
| + // Generate code to store a value in a reference. The stored value is | 
| + // expected on top of the expression stack, with the reference immediately | 
| + // below it. The expression stack is left unchanged. | 
| + void SetValue(Reference* ref) { | 
| + ASSERT(!has_cc()); | 
| + ASSERT(!ref->is_illegal()); | 
| + ref->expression()->GenerateStoreCode(masm_, scope_, ref, false); | 
| } | 
| - void ToBoolean(Label* true_target, Label* false_target); | 
| + // Generate code to store a value in a reference. The stored value is | 
| + // expected on top of the expression stack, with the reference immediately | 
| + // below it. The expression stack is left unchanged. | 
| + void InitConst(Reference* ref) { | 
| + ASSERT(!has_cc()); | 
| + ASSERT(!ref->is_illegal()); | 
| + ref->expression()->GenerateStoreCode(masm_, scope_, ref, true); | 
| + } | 
| + // Generate code to fetch a value from a property of a reference. The | 
| + // reference is expected on top of the expression stack. It is left in | 
| + // place and its value is pushed on top of it. | 
| + void GetReferenceProperty(Expression* key); | 
| - // Access property from the reference (must be at the TOS). | 
| - void AccessReferenceProperty(Expression* key, | 
| - CodeGenState::AccessType access); | 
| + // Generate code to store a value in a property of a reference. The | 
| + // stored value is expected on top of the expression stack, with the | 
| + // reference immediately below it. The expression stack is left | 
| + // unchanged. | 
| + static void SetReferenceProperty(MacroAssembler* masm, | 
| + Reference* ref, | 
| + Expression* key); | 
| + | 
| + void ToBoolean(Label* true_target, Label* false_target); | 
| + | 
| void GenericBinaryOperation(Token::Value op); | 
| void Comparison(Condition cc, bool strict = false); | 
| @@ -297,6 +331,11 @@ | 
| virtual void GenerateFastCharCodeAt(ZoneList<Expression*>* args); | 
| virtual void GenerateObjectEquals(ZoneList<Expression*>* args); | 
| + | 
| + friend class Reference; | 
| + friend class Property; | 
| + friend class VariableProxy; | 
| + friend class Slot; | 
| }; | 
| @@ -599,7 +638,10 @@ | 
| } | 
| -MemOperand ArmCodeGenerator::SlotOperand(Slot* slot, Register tmp) { | 
| +MemOperand ArmCodeGenerator::SlotOperand(MacroAssembler* masm, | 
| + Scope* scope, | 
| + Slot* slot, | 
| + Register tmp) { | 
| // Currently, this assertion will fail if we try to assign to | 
| // a constant variable that is constant because it is read-only | 
| // (such as the variable referring to a named function expression). | 
| @@ -612,11 +654,11 @@ | 
| int index = slot->index(); | 
| switch (slot->type()) { | 
| case Slot::PARAMETER: | 
| - return ParameterOperand(index); | 
| + return ParameterOperand(scope, index); | 
| case Slot::LOCAL: { | 
| ASSERT(0 <= index && | 
| - index < scope_->num_stack_slots() && | 
| + index < scope->num_stack_slots() && | 
| index >= 0); | 
| int local_offset = JavaScriptFrameConstants::kLocal0Offset - | 
| index * kPointerSize; | 
| @@ -627,15 +669,15 @@ | 
| // Follow the context chain if necessary. | 
| ASSERT(!tmp.is(cp)); // do not overwrite context register | 
| Register context = cp; | 
| - int chain_length = scope_->ContextChainLength(slot->var()->scope()); | 
| + int chain_length = scope->ContextChainLength(slot->var()->scope()); | 
| for (int i = chain_length; i-- > 0;) { | 
| // Load the closure. | 
| // (All contexts, even 'with' contexts, have a closure, | 
| // and it is the same for all contexts inside a function. | 
| // There is no need to go to the function context first.) | 
| - __ ldr(tmp, ContextOperand(context, Context::CLOSURE_INDEX)); | 
| + masm->ldr(tmp, ContextOperand(context, Context::CLOSURE_INDEX)); | 
| // Load the function context (which is the incoming, outer context). | 
| - __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset)); | 
| + masm->ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset)); | 
| context = tmp; | 
| } | 
| // We may have a 'with' context now. Get the function context. | 
| @@ -645,7 +687,7 @@ | 
| // cause the function context of a function context is itself. Before | 
| // deleting this mov we should try to create a counter-example first, | 
| // though...) | 
| - __ ldr(tmp, ContextOperand(context, Context::FCONTEXT_INDEX)); | 
| + masm->ldr(tmp, ContextOperand(context, Context::FCONTEXT_INDEX)); | 
| return ContextOperand(tmp, index); | 
| } | 
| @@ -822,18 +864,128 @@ | 
| } | 
| -void ArmCodeGenerator::AccessReference(Reference* ref, | 
| - CodeGenState::AccessType access) { | 
| +void ArmCodeGenerator::GetValue(Reference* ref) { | 
| ASSERT(!has_cc()); | 
| - ASSERT(ref->type() != Reference::ILLEGAL); | 
| + ASSERT(!ref->is_illegal()); | 
| CodeGenState* old_state = state_; | 
| - CodeGenState new_state(access, ref, true_target(), false_target()); | 
| + CodeGenState new_state(CodeGenState::LOAD, ref, true_target(), | 
| + false_target()); | 
| state_ = &new_state; | 
| Visit(ref->expression()); | 
| state_ = old_state; | 
| } | 
| +void Property::GenerateStoreCode(MacroAssembler* masm, | 
| + Scope* scope, | 
| + Reference* ref, | 
| + bool is_const_init) { | 
| + Comment cmnt(masm, "[ Store to Property"); | 
| + masm->RecordPosition(position()); | 
| + ArmCodeGenerator::SetReferenceProperty(masm, ref, key()); | 
| +} | 
| + | 
| + | 
| +void VariableProxy::GenerateStoreCode(MacroAssembler* masm, | 
| + Scope* scope, | 
| + Reference* ref, | 
| + bool is_const_init) { | 
| + Comment cmnt(masm, "[ Store to VariableProxy"); | 
| + Variable* node = var(); | 
| + | 
| + Expression* expr = node->rewrite(); | 
| + if (expr != NULL) { | 
| + expr->GenerateStoreCode(masm, scope, ref, is_const_init); | 
| + } else { | 
| + ASSERT(node->is_global()); | 
| + if (node->AsProperty() != NULL) { | 
| + masm->RecordPosition(node->AsProperty()->position()); | 
| + } | 
| + ArmCodeGenerator::SetReferenceProperty(masm, ref, | 
| + new Literal(node->name())); | 
| + } | 
| +} | 
| + | 
| + | 
| +void Slot::GenerateStoreCode(MacroAssembler* masm, | 
| + Scope* scope, | 
| + Reference* ref, | 
| + bool is_const_init) { | 
| + Comment cmnt(masm, "[ Store to Slot"); | 
| + | 
| + if (type() == Slot::LOOKUP) { | 
| + ASSERT(var()->mode() == Variable::DYNAMIC); | 
| + | 
| + // For now, just do a runtime call. | 
| + masm->push(cp); | 
| + masm->mov(r0, Operand(var()->name())); | 
| + masm->push(r0); | 
| + | 
| + if (is_const_init) { | 
| + // Same as the case for a normal store, but ignores attribute | 
| + // (e.g. READ_ONLY) of context slot so that we can initialize const | 
| + // properties (introduced via eval("const foo = (some expr);")). Also, | 
| + // uses the current function context instead of the top context. | 
| + // | 
| + // Note that we must declare the foo upon entry of eval(), via a | 
| + // context slot declaration, but we cannot initialize it at the same | 
| + // time, because the const declaration may be at the end of the eval | 
| + // code (sigh...) and the const variable may have been used before | 
| + // (where its value is 'undefined'). Thus, we can only do the | 
| + // initialization when we actually encounter the expression and when | 
| + // the expression operands are defined and valid, and thus we need the | 
| + // split into 2 operations: declaration of the context slot followed | 
| + // by initialization. | 
| + masm->CallRuntime(Runtime::kInitializeConstContextSlot, 3); | 
| + } else { | 
| + masm->CallRuntime(Runtime::kStoreContextSlot, 3); | 
| + } | 
| + // Storing a variable must keep the (new) value on the expression | 
| + // stack. This is necessary for compiling assignment expressions. | 
| + masm->push(r0); | 
| + | 
| + } else { | 
| + ASSERT(var()->mode() != Variable::DYNAMIC); | 
| + | 
| + Label exit; | 
| + if (is_const_init) { | 
| + ASSERT(var()->mode() == Variable::CONST); | 
| + // Only the first const initialization must be executed (the slot | 
| + // still contains 'the hole' value). When the assignment is executed, | 
| + // the code is identical to a normal store (see below). | 
| + Comment cmnt(masm, "[ Init const"); | 
| + masm->ldr(r2, ArmCodeGenerator::SlotOperand(masm, scope, this, r2)); | 
| + masm->cmp(r2, Operand(Factory::the_hole_value())); | 
| + masm->b(ne, &exit); | 
| + } | 
| + | 
| + // We must execute the store. | 
| + // r2 may be loaded with context; used below in RecordWrite. | 
| + // Storing a variable must keep the (new) value on the stack. This is | 
| + // necessary for compiling assignment expressions. | 
| + // | 
| + // Note: We will reach here even with var()->mode() == Variable::CONST | 
| + // because of const declarations which will initialize consts to 'the | 
| + // hole' value and by doing so, end up calling this code. r2 may be | 
| + // loaded with context; used below in RecordWrite. | 
| + masm->pop(r0); | 
| + masm->str(r0, ArmCodeGenerator::SlotOperand(masm, scope, this, r2)); | 
| + masm->push(r0); | 
| + | 
| + if (type() == Slot::CONTEXT) { | 
| + // Skip write barrier if the written value is a smi. | 
| + masm->tst(r0, Operand(kSmiTagMask)); | 
| + masm->b(eq, &exit); | 
| + // r2 is loaded with context when calling SlotOperand above. | 
| + int offset = FixedArray::kHeaderSize + index() * kPointerSize; | 
| + masm->mov(r3, Operand(offset)); | 
| + masm->RecordWrite(r2, r3, r1); | 
| + } | 
| + masm->bind(&exit); | 
| + } | 
| +} | 
| + | 
| + | 
| // ECMA-262, section 9.2, page 30: ToBoolean(). Convert the given | 
| // register to a boolean in the condition code register. The code | 
| // may jump to 'false_target' in case the register converts to 'false'. | 
| @@ -1836,42 +1988,29 @@ | 
| #define __ masm_-> | 
| -void ArmCodeGenerator::AccessReferenceProperty( | 
| - Expression* key, | 
| - CodeGenState::AccessType access) { | 
| +void ArmCodeGenerator::GetReferenceProperty(Expression* key) { | 
| Reference::Type type = ref()->type(); | 
| - ASSERT(type != Reference::ILLEGAL); | 
| + ASSERT(!ref()->is_illegal()); | 
| - // TODO(1241834): Make sure that this is sufficient. If there is a chance | 
| - // that reference errors can be thrown below, we must distinguish | 
| - // between the 2 kinds of loads (typeof expression loads must not | 
| - // throw a reference error). | 
| - bool is_load = (access == CodeGenState::LOAD || | 
| - access == CodeGenState::LOAD_TYPEOF_EXPR); | 
| - | 
| + // TODO(1241834): Make sure that this it is safe to ignore the distinction | 
| + // between access types LOAD and LOAD_TYPEOF_EXPR. If there is a chance | 
| + // that reference errors can be thrown below, we must distinguish between | 
| + // the two kinds of loads (typeof expression loads must not throw a | 
| + // reference error). | 
| if (type == Reference::NAMED) { | 
| // Compute the name of the property. | 
| Literal* literal = key->AsLiteral(); | 
| Handle<String> name(String::cast(*literal->handle())); | 
| // Call the appropriate IC code. | 
| - if (is_load) { | 
| - // Setup the name register. | 
| - __ mov(r2, Operand(name)); | 
| - Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); | 
| - Variable* var = ref()->expression()->AsVariableProxy()->AsVariable(); | 
| - if (var != NULL) { | 
| - ASSERT(var->is_global()); | 
| - __ Call(ic, code_target_context); | 
| - } else { | 
| - __ Call(ic, code_target); | 
| - } | 
| - | 
| + // Setup the name register. | 
| + __ mov(r2, Operand(name)); | 
| + Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); | 
| + Variable* var = ref()->expression()->AsVariableProxy()->AsVariable(); | 
| + if (var != NULL) { | 
| + ASSERT(var->is_global()); | 
| + __ Call(ic, code_target_context); | 
| } else { | 
| - __ pop(r0); // value | 
| - // Setup the name register. | 
| - __ mov(r2, Operand(name)); | 
| - Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); | 
| __ Call(ic, code_target); | 
| } | 
| @@ -1879,21 +2018,44 @@ | 
| // Access keyed property. | 
| ASSERT(type == Reference::KEYED); | 
| - if (is_load) { | 
| - // TODO(1224671): Implement inline caching for keyed loads as on ia32. | 
| - GetPropertyStub stub; | 
| - __ CallStub(&stub); | 
| - | 
| - } else { | 
| - __ pop(r0); // value | 
| - SetPropertyStub stub; | 
| - __ CallStub(&stub); | 
| - } | 
| + // TODO(1224671): Implement inline caching for keyed loads as on ia32. | 
| + GetPropertyStub stub; | 
| + __ CallStub(&stub); | 
| } | 
| __ push(r0); | 
| } | 
| +void ArmCodeGenerator::SetReferenceProperty(MacroAssembler* masm, | 
| + Reference* ref, | 
| + Expression* key) { | 
| + Reference::Type type = ref->type(); | 
| + ASSERT(!ref->is_illegal()); | 
| 
iposva
2008/09/10 18:01:44
Nit: Don't you want to assert before accessing any
 
Kevin Millikin (Chromium)
2008/09/11 07:15:55
Changed.  Illegal references have a type, so it's
 | 
| + | 
| + if (type == Reference::NAMED) { | 
| + // Compute the name of the property. | 
| + Literal* literal = key->AsLiteral(); | 
| + Handle<String> name(String::cast(*literal->handle())); | 
| + | 
| + // Call the appropriate IC code. | 
| + masm->pop(r0); // value | 
| + // Setup the name register. | 
| + masm->mov(r2, Operand(name)); | 
| + Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); | 
| + masm->Call(ic, code_target); | 
| + | 
| + } else { | 
| + // Access keyed property. | 
| + ASSERT(type == Reference::KEYED); | 
| + | 
| + masm->pop(r0); // value | 
| + SetPropertyStub stub; | 
| + masm->CallStub(&stub); | 
| + } | 
| + masm->push(r0); | 
| +} | 
| + | 
| + | 
| void ArmCodeGenerator::GenericBinaryOperation(Token::Value op) { | 
| // sp[0] : y | 
| // sp[1] : x | 
| @@ -3116,6 +3278,7 @@ | 
| void ArmCodeGenerator::VisitSlot(Slot* node) { | 
| + ASSERT(access() != CodeGenState::UNDEFINED); | 
| Comment cmnt(masm_, "[ Slot"); | 
| if (node->type() == Slot::LOOKUP) { | 
| @@ -3126,161 +3289,54 @@ | 
| __ mov(r0, Operand(node->var()->name())); | 
| __ push(r0); | 
| - switch (access()) { | 
| - case CodeGenState::UNDEFINED: | 
| - UNREACHABLE(); | 
| - break; | 
| - | 
| - case CodeGenState::LOAD: | 
| - __ CallRuntime(Runtime::kLoadContextSlot, 2); | 
| - __ push(r0); | 
| - break; | 
| - | 
| - case CodeGenState::LOAD_TYPEOF_EXPR: | 
| - __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2); | 
| - __ push(r0); | 
| - break; | 
| - | 
| - case CodeGenState::STORE: | 
| - // Storing a variable must keep the (new) value on the stack. This | 
| - // is necessary for compiling assignment expressions. | 
| - __ CallRuntime(Runtime::kStoreContextSlot, 3); | 
| - __ push(r0); | 
| - // result (TOS) is the value that was stored | 
| - break; | 
| - | 
| - case CodeGenState::INIT_CONST: | 
| - // Same as STORE but ignores attribute (e.g. READ_ONLY) of | 
| - // context slot so that we can initialize const properties | 
| - // (introduced via eval("const foo = (some expr);")). Also, | 
| - // uses the current function context instead of the top | 
| - // context. | 
| - // | 
| - // Note that we must declare the foo upon entry of eval(), | 
| - // via a context slot declaration, but we cannot initialize | 
| - // it at the same time, because the const declaration may | 
| - // be at the end of the eval code (sigh...) and the const | 
| - // variable may have been used before (where its value is | 
| - // 'undefined'). Thus, we can only do the initialization | 
| - // when we actually encounter the expression and when the | 
| - // expression operands are defined and valid, and thus we | 
| - // need the split into 2 operations: declaration of the | 
| - // context slot followed by initialization. | 
| - __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); | 
| - __ push(r0); | 
| - break; | 
| + if (access() == CodeGenState::LOAD) { | 
| + __ CallRuntime(Runtime::kLoadContextSlot, 2); | 
| + } else { | 
| + // CodeGenState::LOAD_TYPEOF_EXPR. | 
| 
iposva
2008/09/10 18:01:44
Please assert that the CodeGenState is LOAD_TYPEOF
 
Kevin Millikin (Chromium)
2008/09/11 07:15:55
Done.  Earlier assert already ruled out UNDEFINED,
 | 
| + __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2); | 
| } | 
| + __ push(r0); | 
| } else { | 
| - // Note: We would like to keep the assert below, but it fires because | 
| - // of some nasty code in LoadTypeofExpression() which should be removed... | 
| + // Note: We would like to keep the assert below, but it fires because of | 
| + // some nasty code in LoadTypeofExpression() which should be removed... | 
| // ASSERT(node->var()->mode() != Variable::DYNAMIC); | 
| - switch (access()) { | 
| - case CodeGenState::UNDEFINED: | 
| - UNREACHABLE(); | 
| - break; | 
| - | 
| - case CodeGenState::LOAD: // fall through | 
| - case CodeGenState::LOAD_TYPEOF_EXPR: | 
| - // Special handling for locals allocated in registers. | 
| - __ ldr(r0, SlotOperand(node, r2)); | 
| - __ push(r0); | 
| - if (node->var()->mode() == Variable::CONST) { | 
| - // Const slots may contain 'the hole' value (the constant hasn't | 
| - // been initialized yet) which needs to be converted into the | 
| - // 'undefined' value. | 
| - Comment cmnt(masm_, "[ Unhole const"); | 
| - __ pop(r0); | 
| - __ cmp(r0, Operand(Factory::the_hole_value())); | 
| - __ mov(r0, Operand(Factory::undefined_value()), LeaveCC, eq); | 
| - __ push(r0); | 
| - } | 
| - break; | 
| - | 
| - case CodeGenState::INIT_CONST: { | 
| - ASSERT(node->var()->mode() == Variable::CONST); | 
| - // Only the first const initialization must be executed (the slot | 
| - // still contains 'the hole' value). When the assignment is executed, | 
| - // the code is identical to a normal store (see below). | 
| - { Comment cmnt(masm_, "[ Init const"); | 
| - Label L; | 
| - __ ldr(r2, SlotOperand(node, r2)); | 
| - __ cmp(r2, Operand(Factory::the_hole_value())); | 
| - __ b(ne, &L); | 
| - // We must execute the store. | 
| - // r2 may be loaded with context; used below in RecordWrite. | 
| - __ ldr(r0, MemOperand(sp, 0)); | 
| - __ str(r0, SlotOperand(node, r2)); | 
| - if (node->type() == Slot::CONTEXT) { | 
| - // Skip write barrier if the written value is a smi. | 
| - Label exit; | 
| - __ tst(r0, Operand(kSmiTagMask)); | 
| - __ b(eq, &exit); | 
| - // r2 is loaded with context when calling SlotOperand above. | 
| - int offset = FixedArray::kHeaderSize + node->index() * kPointerSize; | 
| - __ mov(r3, Operand(offset)); | 
| - __ RecordWrite(r2, r3, r1); | 
| - __ bind(&exit); | 
| - } | 
| - __ bind(&L); | 
| - } | 
| - break; | 
| - } | 
| - | 
| - case CodeGenState::STORE: { | 
| - // Storing a variable must keep the (new) value on the stack. This | 
| - // is necessary for compiling assignment expressions. | 
| - // Special handling for locals allocated in registers. | 
| - // | 
| - // Note: We will reach here even with node->var()->mode() == | 
| - // Variable::CONST because of const declarations which will | 
| - // initialize consts to 'the hole' value and by doing so, end | 
| - // up calling this code. | 
| - // r2 may be loaded with context; used below in RecordWrite. | 
| - __ pop(r0); | 
| - __ str(r0, SlotOperand(node, r2)); | 
| - __ push(r0); | 
| - if (node->type() == Slot::CONTEXT) { | 
| - // Skip write barrier if the written value is a smi. | 
| - Label exit; | 
| - __ tst(r0, Operand(kSmiTagMask)); | 
| - __ b(eq, &exit); | 
| - // r2 is loaded with context when calling SlotOperand above. | 
| - int offset = FixedArray::kHeaderSize + node->index() * kPointerSize; | 
| - __ mov(r3, Operand(offset)); | 
| - __ RecordWrite(r2, r3, r1); | 
| - __ bind(&exit); | 
| - } | 
| - break; | 
| - } | 
| + // Special handling for locals allocated in registers. | 
| + __ ldr(r0, SlotOperand(node, r2)); | 
| + __ push(r0); | 
| + if (node->var()->mode() == Variable::CONST) { | 
| + // Const slots may contain 'the hole' value (the constant hasn't been | 
| + // initialized yet) which needs to be converted into the 'undefined' | 
| + // value. | 
| + Comment cmnt(masm_, "[ Unhole const"); | 
| + __ pop(r0); | 
| + __ cmp(r0, Operand(Factory::the_hole_value())); | 
| + __ mov(r0, Operand(Factory::undefined_value()), LeaveCC, eq); | 
| + __ push(r0); | 
| } | 
| } | 
| } | 
| -void ArmCodeGenerator::VisitVariableProxy(VariableProxy* proxy_node) { | 
| +void ArmCodeGenerator::VisitVariableProxy(VariableProxy* node) { | 
| Comment cmnt(masm_, "[ VariableProxy"); | 
| - Variable* node = proxy_node->var(); | 
| + Variable* var_node = node->var(); | 
| - Expression* x = node->rewrite(); | 
| - if (x != NULL) { | 
| - Visit(x); | 
| - return; | 
| - } | 
| - | 
| - ASSERT(node->is_global()); | 
| - if (is_referenced()) { | 
| - if (node->AsProperty() != NULL) { | 
| - __ RecordPosition(node->AsProperty()->position()); | 
| + Expression* expr = var_node->rewrite(); | 
| + if (expr != NULL) { | 
| + Visit(expr); | 
| + } else { | 
| + ASSERT(var_node->is_global()); | 
| + if (is_referenced()) { | 
| + if (var_node->AsProperty() != NULL) { | 
| + __ RecordPosition(var_node->AsProperty()->position()); | 
| + } | 
| + GetReferenceProperty(new Literal(var_node->name())); | 
| + } else { | 
| + Reference property(this, node); | 
| + GetValue(&property); | 
| } | 
| - AccessReferenceProperty(new Literal(node->name()), access()); | 
| - | 
| - } else { | 
| - // All stores are through references. | 
| - ASSERT(access() != CodeGenState::STORE); | 
| - Reference property(this, proxy_node); | 
| - GetValue(&property); | 
| } | 
| } | 
| @@ -3541,13 +3597,11 @@ | 
| void ArmCodeGenerator::VisitProperty(Property* node) { | 
| Comment cmnt(masm_, "[ Property"); | 
| + | 
| if (is_referenced()) { | 
| __ RecordPosition(node->position()); | 
| - AccessReferenceProperty(node->key(), access()); | 
| - | 
| + GetReferenceProperty(node->key()); | 
| } else { | 
| - // All stores are through references. | 
| - ASSERT(access() != CodeGenState::STORE); | 
| Reference property(this, node); | 
| __ RecordPosition(node->position()); | 
| GetValue(&property); |