| 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.
|
|
|