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

Unified Diff: src/codegen-arm.cc

Issue 6527: Move code generation for storing to a reference out of the AST nodes, and... (Closed) Base URL: http://v8.googlecode.com/svn/branches/bleeding_edge/
Patch Set: '' Created 12 years, 2 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
« no previous file with comments | « src/ast.h ('k') | src/codegen-ia32.cc » ('j') | src/codegen-ia32.cc » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/codegen-arm.cc
===================================================================
--- src/codegen-arm.cc (revision 453)
+++ src/codegen-arm.cc (working copy)
@@ -50,6 +50,8 @@
// For properties, we keep either one (named) or two (indexed) values
// on the execution stack to represent the reference.
+enum InitState { CONST_INIT, NOT_CONST_INIT };
+
class Reference BASE_EMBEDDED {
public:
// The values of the types is important, see size().
@@ -70,6 +72,8 @@
bool is_slot() const { return type_ == SLOT; }
bool is_property() const { return type_ == NAMED || type_ == KEYED; }
+ void SetValue(InitState init_state);
+
private:
ArmCodeGenerator* cgen_;
Expression* expression_;
@@ -80,14 +84,10 @@
// -------------------------------------------------------------------------
// Code generation state
-// The state is passed down the AST by the code generator. It is passed
-// implicitly (in a member variable) to the non-static code generator member
-// functions, and explicitly (as an argument) to the static member functions
-// and the AST node member functions.
-//
-// The state is threaded through the call stack. Constructing a state
-// implicitly pushes it on the owning code generator's stack of states, and
-// destroying one implicitly pops it.
+// The state is passed down the AST by the code generator (and back up, in
+// the form of the state of the label pair). It is threaded through the
+// call stack. Constructing a state implicitly pushes it on the owning code
+// generator's stack of states, and destroying one implicitly pops it.
class CodeGenState BASE_EMBEDDED {
public:
@@ -190,12 +190,12 @@
return ContextOperand(cp, Context::GLOBAL_INDEX);
}
- static MemOperand ContextOperand(Register context, int index) {
+ MemOperand ContextOperand(Register context, int index) const {
return MemOperand(context, Context::SlotOffset(index));
}
- static MemOperand ParameterOperand(const CodeGenerator* cgen, int index) {
- int num_parameters = cgen->scope()->num_parameters();
+ MemOperand ParameterOperand(int index) const {
+ int num_parameters = scope()->num_parameters();
// index -2 corresponds to the activated closure, -1 corresponds
// to the receiver
ASSERT(-2 <= index && index < num_parameters);
@@ -203,22 +203,12 @@
return MemOperand(fp, offset);
}
- MemOperand ParameterOperand(int index) const {
- return ParameterOperand(this, index);
- }
-
MemOperand FunctionOperand() const {
return MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset);
}
- static MemOperand SlotOperand(CodeGenerator* cgen,
- Slot* slot,
- Register tmp);
+ MemOperand SlotOperand(Slot* slot, Register tmp);
- MemOperand SlotOperand(Slot* slot, Register tmp) {
- return SlotOperand(this, slot, tmp);
- }
-
void LoadCondition(Expression* x, CodeGenState::AccessType access,
Label* true_target, Label* false_target, bool force_cc);
void Load(Expression* x,
@@ -246,38 +236,11 @@
Visit(ref->expression());
}
- // 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(this, ref, NOT_CONST_INIT);
- }
-
- // 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(this, ref, CONST_INIT);
- }
-
// 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);
- // 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(CodeGenerator* cgen,
- Reference* ref,
- Expression* key);
-
-
void ToBoolean(Label* true_target, Label* false_target);
void GenericBinaryOperation(Token::Value op);
@@ -601,9 +564,9 @@
__ stm(db_w, sp, r0.bit() | r1.bit() | r2.bit());
__ CallStub(&stub);
__ push(r0);
- SetValue(&arguments_ref);
+ arguments_ref.SetValue(NOT_CONST_INIT);
}
- SetValue(&shadow_ref);
+ shadow_ref.SetValue(NOT_CONST_INIT);
}
__ pop(r0); // Value is no longer needed.
}
@@ -685,6 +648,60 @@
}
+MemOperand ArmCodeGenerator::SlotOperand(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).
+ // We need to implement assignments to read-only variables.
+ // Ideally, we should do this during AST generation (by converting
+ // such assignments into expression statements); however, in general
+ // we may not be able to make the decision until past AST generation,
+ // that is when the entire program is known.
+ ASSERT(slot != NULL);
+ int index = slot->index();
+ switch (slot->type()) {
+ case Slot::PARAMETER:
+ return ParameterOperand(index);
+
+ case Slot::LOCAL: {
+ ASSERT(0 <= index && index < scope()->num_stack_slots());
+ const int kLocalOffset = JavaScriptFrameConstants::kLocal0Offset;
+ return MemOperand(fp, kLocalOffset - index * kPointerSize);
+ }
+
+ case Slot::CONTEXT: {
+ // 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());
+ 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));
+ // Load the function context (which is the incoming, outer context).
+ __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset));
+ context = tmp;
+ }
+ // We may have a 'with' context now. Get the function context.
+ // (In fact this mov may never be the needed, since the scope analysis
+ // may not permit a direct context access in this case and thus we are
+ // always at a function context. However it is safe to dereference be-
+ // 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));
+ return ContextOperand(tmp, index);
+ }
+
+ default:
+ UNREACHABLE();
+ return MemOperand(r0, 0);
+ }
+}
+
+
// Loads a value on the stack. If it is a boolean value, the result may have
// been (partially) translated into branches, or it may have set the condition
// code register. If force_cc is set, the value is forced to set the condition
@@ -1469,7 +1486,7 @@
Reference target(this, node->proxy());
ASSERT(target.is_slot());
Load(val);
- SetValue(&target);
+ target.SetValue(NOT_CONST_INIT);
// Get rid of the assigned value (declarations are statements). It's
// safe to pop the value lying on top of the reference before unloading
// the reference itself (which preserves the top of stack) because we
@@ -1962,7 +1979,7 @@
// If the reference was to a slot we rely on the convenient property
// that it doesn't matter whether a value (eg, r3 pushed above) is
// right on top of or right underneath a zero-sized reference.
- SetValue(&each);
+ each.SetValue(NOT_CONST_INIT);
if (each.size() > 0) {
// It's safe to pop the value lying on top of the reference before
// unloading the reference itself (which preserves the top of stack,
@@ -2007,7 +2024,7 @@
// Here we make use of the convenient property that it doesn't matter
// whether a value is immediately on top of or underneath a zero-sized
// reference.
- SetValue(&ref);
+ ref.SetValue(NOT_CONST_INIT);
}
// Remove the exception from the stack.
@@ -2564,9 +2581,9 @@
// Dynamic constant initializations must use the function context
// and initialize the actual constant declared. Dynamic variable
// initializations are simply assignments and use SetValue.
- InitConst(&target);
+ target.SetValue(CONST_INIT);
} else {
- SetValue(&target);
+ target.SetValue(NOT_CONST_INIT);
}
}
}
@@ -3130,7 +3147,7 @@
// Store the new value in the target if not const.
__ bind(&exit);
__ push(r0);
- if (!is_const) SetValue(&target);
+ if (!is_const) target.SetValue(NOT_CONST_INIT);
}
// Postfix: Discard the new value and use the old.
@@ -3500,177 +3517,134 @@
#undef __
#define __ masm->
-MemOperand ArmCodeGenerator::SlotOperand(CodeGenerator* cgen,
- 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).
- // We need to implement assignments to read-only variables.
- // Ideally, we should do this during AST generation (by converting
- // such assignments into expression statements); however, in general
- // we may not be able to make the decision until past AST generation,
- // that is when the entire program is known.
- ASSERT(slot != NULL);
- int index = slot->index();
- switch (slot->type()) {
- case Slot::PARAMETER:
- return ParameterOperand(cgen, index);
+void Reference::SetValue(InitState init_state) {
+ ASSERT(!is_illegal());
+ ASSERT(!cgen_->has_cc());
+ MacroAssembler* masm = cgen_->masm();
+ switch (type_) {
+ case SLOT: {
+ Comment cmnt(masm, "[ Store to Slot");
+ Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
+ ASSERT(slot != NULL);
+ if (slot->type() == Slot::LOOKUP) {
+ ASSERT(slot->var()->mode() == Variable::DYNAMIC);
- case Slot::LOCAL: {
- ASSERT(0 <= index &&
- index < cgen->scope()->num_stack_slots() &&
- index >= 0);
- int local_offset = JavaScriptFrameConstants::kLocal0Offset -
- index * kPointerSize;
- return MemOperand(fp, local_offset);
- }
+ // For now, just do a runtime call.
+ __ push(cp);
+ __ mov(r0, Operand(slot->var()->name()));
+ __ push(r0);
- case Slot::CONTEXT: {
- MacroAssembler* masm = cgen->masm();
- // Follow the context chain if necessary.
- ASSERT(!tmp.is(cp)); // do not overwrite context register
- Register context = cp;
- int chain_length =
- cgen->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));
- // Load the function context (which is the incoming, outer context).
- __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset));
- context = tmp;
- }
- // We may have a 'with' context now. Get the function context.
- // (In fact this mov may never be the needed, since the scope analysis
- // may not permit a direct context access in this case and thus we are
- // always at a function context. However it is safe to dereference be-
- // 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));
- return ContextOperand(tmp, index);
- }
+ if (init_state == 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.
+ __ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
+ } else {
+ __ CallRuntime(Runtime::kStoreContextSlot, 3);
+ }
+ // Storing a variable must keep the (new) value on the expression
+ // stack. This is necessary for compiling assignment expressions.
+ __ push(r0);
- default:
- UNREACHABLE();
- return MemOperand(r0, 0);
- }
-}
+ } else {
+ ASSERT(slot->var()->mode() != Variable::DYNAMIC);
+ Label exit;
+ if (init_state == CONST_INIT) {
+ ASSERT(slot->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");
+ __ ldr(r2, cgen_->SlotOperand(slot, r2));
+ __ cmp(r2, Operand(Factory::the_hole_value()));
+ __ b(ne, &exit);
+ }
-void Property::GenerateStoreCode(CodeGenerator* cgen,
- Reference* ref,
- InitState init_state) {
- MacroAssembler* masm = cgen->masm();
- Comment cmnt(masm, "[ Store to Property");
- __ RecordPosition(position());
- ArmCodeGenerator::SetReferenceProperty(cgen, ref, key());
-}
-
-
-void VariableProxy::GenerateStoreCode(CodeGenerator* cgen,
- Reference* ref,
- InitState init_state) {
- MacroAssembler* masm = cgen->masm();
- Comment cmnt(masm, "[ Store to VariableProxy");
- Variable* node = var();
-
- Expression* expr = node->rewrite();
- if (expr != NULL) {
- expr->GenerateStoreCode(cgen, ref, init_state);
- } else {
- ASSERT(node->is_global());
- if (node->AsProperty() != NULL) {
- __ RecordPosition(node->AsProperty()->position());
+ // We must execute the store. 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 slot->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, cgen_->SlotOperand(slot, r2));
+ __ push(r0);
+ if (slot->type() == Slot::CONTEXT) {
+ // Skip write barrier if the written value is a smi.
+ __ tst(r0, Operand(kSmiTagMask));
+ __ b(eq, &exit);
+ // r2 is loaded with context when calling SlotOperand above.
+ int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
+ __ mov(r3, Operand(offset));
+ __ RecordWrite(r2, r3, r1);
+ }
+ // If we definitely did not jump over the assignment, we do not need
+ // to bind the exit label. Doing so can defeat peephole
+ // optimization.
+ if (init_state == CONST_INIT || slot->type() == Slot::CONTEXT) {
+ __ bind(&exit);
+ }
+ }
+ break;
}
- Expression* key = new Literal(node->name());
- ArmCodeGenerator::SetReferenceProperty(cgen, ref, key);
- }
-}
+ case NAMED: {
+ Comment cmnt(masm, "[ Store to named Property");
+ Property* property = expression_->AsProperty();
+ Handle<String> name;
+ if (property == NULL) {
+ // Global variable reference treated as named property access.
+ VariableProxy* proxy = expression_->AsVariableProxy();
+ ASSERT(proxy->AsVariable() != NULL);
+ ASSERT(proxy->AsVariable()->is_global());
+ name = proxy->name();
+ } else {
+ Literal* raw_name = property->key()->AsLiteral();
+ ASSERT(raw_name != NULL);
+ name = Handle<String>(String::cast(*raw_name->handle()));
+ __ RecordPosition(property->position());
+ }
-void Slot::GenerateStoreCode(CodeGenerator* cgen,
- Reference* ref,
- InitState init_state) {
- MacroAssembler* masm = cgen->masm();
- Comment cmnt(masm, "[ Store to Slot");
-
- if (type() == Slot::LOOKUP) {
- ASSERT(var()->mode() == Variable::DYNAMIC);
-
- // For now, just do a runtime call.
- __ push(cp);
- __ mov(r0, Operand(var()->name()));
- __ push(r0);
-
- if (init_state == 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.
- __ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
- } else {
- __ CallRuntime(Runtime::kStoreContextSlot, 3);
+ // Call the appropriate IC code.
+ __ pop(r0); // value
+ // Setup the name register.
+ __ mov(r2, Operand(name));
+ Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
+ __ Call(ic, RelocInfo::CODE_TARGET);
+ __ push(r0);
+ break;
}
- // Storing a variable must keep the (new) value on the expression
- // stack. This is necessary for compiling assignment expressions.
- __ push(r0);
- } else {
- ASSERT(var()->mode() != Variable::DYNAMIC);
-
- Label exit;
- if (init_state == 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");
- __ ldr(r2, ArmCodeGenerator::SlotOperand(cgen, this, r2));
- __ cmp(r2, Operand(Factory::the_hole_value()));
- __ b(ne, &exit);
+ case KEYED: {
+ Comment cmnt(masm, "[ Store to keyed Property");
+ Property* property = expression_->AsProperty();
+ ASSERT(property != NULL);
+ __ RecordPosition(property->position());
+ __ pop(r0); // value
+ SetPropertyStub stub;
+ __ CallStub(&stub);
+ __ push(r0);
+ break;
}
- // 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.
- __ pop(r0);
- __ str(r0, ArmCodeGenerator::SlotOperand(cgen, this, r2));
- __ push(r0);
-
- if (type() == Slot::CONTEXT) {
- // Skip write barrier if the written value is a smi.
- __ tst(r0, Operand(kSmiTagMask));
- __ b(eq, &exit);
- // r2 is loaded with context when calling SlotOperand above.
- int offset = FixedArray::kHeaderSize + index() * kPointerSize;
- __ mov(r3, Operand(offset));
- __ RecordWrite(r2, r3, r1);
- }
- // If we definitely did not jump over the assignment, we do not need to
- // bind the exit label. Doing so can defeat peephole optimization.
- if (init_state == CONST_INIT || type() == Slot::CONTEXT) {
- __ bind(&exit);
- }
+ default:
+ UNREACHABLE();
}
}
@@ -4517,36 +4491,6 @@
}
-void ArmCodeGenerator::SetReferenceProperty(CodeGenerator* cgen,
- Reference* ref,
- Expression* key) {
- ASSERT(!ref->is_illegal());
- MacroAssembler* masm = cgen->masm();
-
- if (ref->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.
- __ pop(r0); // value
- // Setup the name register.
- __ mov(r2, Operand(name));
- Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
- __ Call(ic, RelocInfo::CODE_TARGET);
-
- } else {
- // Access keyed property.
- ASSERT(ref->type() == Reference::KEYED);
-
- __ pop(r0); // value
- SetPropertyStub stub;
- __ CallStub(&stub);
- }
- __ push(r0);
-}
-
-
void CallFunctionStub::Generate(MacroAssembler* masm) {
Label slow;
// Get the function to call from the stack.
« no previous file with comments | « src/ast.h ('k') | src/codegen-ia32.cc » ('j') | src/codegen-ia32.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698