| Index: src/arm/fast-codegen-arm.cc
|
| diff --git a/src/arm/fast-codegen-arm.cc b/src/arm/fast-codegen-arm.cc
|
| index 81474665a26032efff34c7e59496dc3901136c69..2107f8c1a0ef3eb90f739f0bd9bc0d71b173e747 100644
|
| --- a/src/arm/fast-codegen-arm.cc
|
| +++ b/src/arm/fast-codegen-arm.cc
|
| @@ -73,16 +73,46 @@ void FastCodeGenerator::Generate(FunctionLiteral* fun) {
|
|
|
| bool function_in_register = true;
|
|
|
| + // Possibly allocate a local context.
|
| + if (fun->scope()->num_heap_slots() > 0) {
|
| + Comment cmnt(masm_, "[ Allocate local context");
|
| + // Argument to NewContext is the function, which is in r1.
|
| + __ push(r1);
|
| + __ CallRuntime(Runtime::kNewContext, 1);
|
| + function_in_register = false;
|
| + // Context is returned in both r0 and cp. It replaces the context
|
| + // passed to us. It's saved in the stack and kept live in cp.
|
| + __ str(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
|
| + // Copy any necessary parameters into the context.
|
| + int num_parameters = fun->scope()->num_parameters();
|
| + for (int i = 0; i < num_parameters; i++) {
|
| + Slot* slot = fun->scope()->parameter(i)->slot();
|
| + if (slot != NULL && slot->type() == Slot::CONTEXT) {
|
| + int parameter_offset = StandardFrameConstants::kCallerSPOffset +
|
| + (num_parameters - 1 - i) * kPointerSize;
|
| + // Load parameter from stack.
|
| + __ ldr(r0, MemOperand(fp, parameter_offset));
|
| + // Store it in the context
|
| + __ str(r0, MemOperand(cp, Context::SlotOffset(slot->index())));
|
| + }
|
| + }
|
| + }
|
| +
|
| Variable* arguments = fun->scope()->arguments()->AsVariable();
|
| if (arguments != NULL) {
|
| // Function uses arguments object.
|
| Comment cmnt(masm_, "[ Allocate arguments object");
|
| - __ mov(r3, r1);
|
| + if (!function_in_register) {
|
| + // Load this again, if it's used by the local context below.
|
| + __ ldr(r3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
|
| + } else {
|
| + __ mov(r3, r1);
|
| + }
|
| // Receiver is just before the parameters on the caller's stack.
|
| __ add(r2, fp, Operand(StandardFrameConstants::kCallerSPOffset +
|
| fun->num_parameters() * kPointerSize));
|
| __ mov(r1, Operand(Smi::FromInt(fun->num_parameters())));
|
| - __ stm(db_w, sp, r1.bit() | r2.bit() | r3.bit());
|
| + __ stm(db_w, sp, r3.bit() | r2.bit() | r1.bit());
|
|
|
| // Arguments to ArgumentsAccessStub:
|
| // function, receiver address, parameter count.
|
| @@ -90,33 +120,12 @@ void FastCodeGenerator::Generate(FunctionLiteral* fun) {
|
| // stack frame was an arguments adapter frame.
|
| ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT);
|
| __ CallStub(&stub);
|
| - __ str(r0, MemOperand(fp, SlotOffset(arguments->slot())));
|
| + // Duplicate the value; move-to-slot operation might clobber registers.
|
| + __ mov(r3, r0);
|
| + Move(arguments->slot(), r0, r1, r2);
|
| Slot* dot_arguments_slot =
|
| fun->scope()->arguments_shadow()->AsVariable()->slot();
|
| - __ str(r0, MemOperand(fp, SlotOffset(dot_arguments_slot)));
|
| - function_in_register = false;
|
| - }
|
| -
|
| - // Possibly allocate a local context.
|
| - if (fun->scope()->num_heap_slots() > 0) {
|
| - Comment cmnt(masm_, "[ Allocate local context");
|
| - if (!function_in_register) {
|
| - // Load this again, if it's used by the local context below.
|
| - __ ldr(r1, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
|
| - }
|
| - // Argument to NewContext is the function, which is in r1.
|
| - __ push(r1);
|
| - __ CallRuntime(Runtime::kNewContext, 1);
|
| - // Context is returned in both r0 and cp. It replaces the context
|
| - // passed to us. It's saved in the stack and kept live in cp.
|
| - __ str(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
|
| -#ifdef DEBUG
|
| - // Assert we do not have to copy any parameters into the context.
|
| - for (int i = 0, len = fun->scope()->num_parameters(); i < len; i++) {
|
| - Slot* slot = fun->scope()->parameter(i)->slot();
|
| - ASSERT(slot != NULL && slot->type() != Slot::CONTEXT);
|
| - }
|
| -#endif
|
| + Move(dot_arguments_slot, r3, r1, r2);
|
| }
|
|
|
| // Check the stack for overflow or break request.
|
| @@ -238,7 +247,42 @@ void FastCodeGenerator::Move(Expression::Context context, Register source) {
|
| }
|
|
|
|
|
| -void FastCodeGenerator::Move(Expression::Context context, Slot* source) {
|
| +template <>
|
| +MemOperand FastCodeGenerator::CreateSlotOperand<MemOperand>(
|
| + Slot* source,
|
| + Register scratch) {
|
| + switch (source->type()) {
|
| + case Slot::PARAMETER:
|
| + case Slot::LOCAL:
|
| + return MemOperand(fp, SlotOffset(source));
|
| + case Slot::CONTEXT: {
|
| + int context_chain_length =
|
| + function_->scope()->ContextChainLength(source->var()->scope());
|
| + __ LoadContext(scratch, context_chain_length);
|
| + return CodeGenerator::ContextOperand(scratch, source->index());
|
| + break;
|
| + }
|
| + case Slot::LOOKUP:
|
| + UNIMPLEMENTED();
|
| + // Fall-through.
|
| + default:
|
| + UNREACHABLE();
|
| + return MemOperand(r0, 0); // Dead code to make the compiler happy.
|
| + }
|
| +}
|
| +
|
| +
|
| +void FastCodeGenerator::Move(Register dst, Slot* source) {
|
| + // Use dst as scratch.
|
| + MemOperand location = CreateSlotOperand<MemOperand>(source, dst);
|
| + __ ldr(dst, location);
|
| +}
|
| +
|
| +
|
| +
|
| +void FastCodeGenerator::Move(Expression::Context context,
|
| + Slot* source,
|
| + Register scratch) {
|
| switch (context) {
|
| case Expression::kUninitialized:
|
| UNREACHABLE();
|
| @@ -248,8 +292,8 @@ void FastCodeGenerator::Move(Expression::Context context, Slot* source) {
|
| case Expression::kTest: // Fall through.
|
| case Expression::kValueTest: // Fall through.
|
| case Expression::kTestValue:
|
| - __ ldr(ip, MemOperand(fp, SlotOffset(source)));
|
| - Move(context, ip);
|
| + Move(scratch, source);
|
| + Move(context, scratch);
|
| break;
|
| }
|
| }
|
| @@ -272,24 +316,60 @@ void FastCodeGenerator::Move(Expression::Context context, Literal* expr) {
|
| }
|
|
|
|
|
| +void FastCodeGenerator::Move(Slot* dst,
|
| + Register src,
|
| + Register scratch1,
|
| + Register scratch2) {
|
| + switch (dst->type()) {
|
| + case Slot::PARAMETER:
|
| + case Slot::LOCAL:
|
| + __ str(src, MemOperand(fp, SlotOffset(dst)));
|
| + break;
|
| + case Slot::CONTEXT: {
|
| + int context_chain_length =
|
| + function_->scope()->ContextChainLength(dst->var()->scope());
|
| + __ LoadContext(scratch1, context_chain_length);
|
| + int index = Context::SlotOffset(dst->index());
|
| + __ mov(scratch2, Operand(index));
|
| + __ str(src, MemOperand(scratch1, index));
|
| + __ RecordWrite(scratch1, scratch2, src);
|
| + break;
|
| + }
|
| + case Slot::LOOKUP:
|
| + UNIMPLEMENTED();
|
| + default:
|
| + UNREACHABLE();
|
| + }
|
| +}
|
| +
|
| +
|
| +
|
| void FastCodeGenerator::DropAndMove(Expression::Context context,
|
| - Register source) {
|
| + Register source,
|
| + int drop_count) {
|
| + ASSERT(drop_count > 0);
|
| switch (context) {
|
| case Expression::kUninitialized:
|
| UNREACHABLE();
|
| case Expression::kEffect:
|
| - __ pop();
|
| + __ add(sp, sp, Operand(drop_count * kPointerSize));
|
| break;
|
| case Expression::kValue:
|
| + if (drop_count > 1) {
|
| + __ add(sp, sp, Operand((drop_count - 1) * kPointerSize));
|
| + }
|
| __ str(source, MemOperand(sp));
|
| break;
|
| case Expression::kTest:
|
| ASSERT(!source.is(sp));
|
| - __ pop();
|
| + __ add(sp, sp, Operand(drop_count * kPointerSize));
|
| TestAndBranch(source, true_label_, false_label_);
|
| break;
|
| case Expression::kValueTest: {
|
| Label discard;
|
| + if (drop_count > 1) {
|
| + __ add(sp, sp, Operand((drop_count - 1) * kPointerSize));
|
| + }
|
| __ str(source, MemOperand(sp));
|
| TestAndBranch(source, true_label_, &discard);
|
| __ bind(&discard);
|
| @@ -299,6 +379,9 @@ void FastCodeGenerator::DropAndMove(Expression::Context context,
|
| }
|
| case Expression::kTestValue: {
|
| Label discard;
|
| + if (drop_count > 1) {
|
| + __ add(sp, sp, Operand((drop_count - 1) * kPointerSize));
|
| + }
|
| __ str(source, MemOperand(sp));
|
| TestAndBranch(source, &discard, false_label_);
|
| __ bind(&discard);
|
| @@ -467,53 +550,60 @@ void FastCodeGenerator::VisitVariableProxy(VariableProxy* expr) {
|
| DropAndMove(expr->context(), r0);
|
| } else if (rewrite->AsSlot() != NULL) {
|
| Slot* slot = rewrite->AsSlot();
|
| - ASSERT_NE(NULL, slot);
|
| - switch (slot->type()) {
|
| - case Slot::LOCAL:
|
| - case Slot::PARAMETER: {
|
| - Comment cmnt(masm_, "Stack slot");
|
| - Move(expr->context(), rewrite->AsSlot());
|
| - break;
|
| - }
|
| -
|
| - case Slot::CONTEXT: {
|
| - Comment cmnt(masm_, "Context slot");
|
| - int chain_length =
|
| - function_->scope()->ContextChainLength(slot->var()->scope());
|
| - if (chain_length > 0) {
|
| - // Move up the chain of contexts to the context containing the slot.
|
| - __ ldr(r0, CodeGenerator::ContextOperand(cp, Context::CLOSURE_INDEX));
|
| - // Load the function context (which is the incoming, outer context).
|
| - __ ldr(r0, FieldMemOperand(r0, JSFunction::kContextOffset));
|
| - for (int i = 1; i < chain_length; i++) {
|
| - __ ldr(r0,
|
| - CodeGenerator::ContextOperand(r0, Context::CLOSURE_INDEX));
|
| - // Load the function context (which is the incoming, outer context).
|
| - __ ldr(r0, FieldMemOperand(r0, JSFunction::kContextOffset));
|
| - }
|
| - // The context may be an intermediate context, not a function context.
|
| - __ ldr(r0,
|
| - CodeGenerator::ContextOperand(r0, Context::FCONTEXT_INDEX));
|
| - } else { // Slot is in the current context.
|
| - __ ldr(r0,
|
| - CodeGenerator::ContextOperand(cp, Context::FCONTEXT_INDEX));
|
| + if (FLAG_debug_code) {
|
| + switch (slot->type()) {
|
| + case Slot::LOCAL:
|
| + case Slot::PARAMETER: {
|
| + Comment cmnt(masm_, "Stack slot");
|
| + break;
|
| }
|
| - __ ldr(r0, CodeGenerator::ContextOperand(r0, slot->index()));
|
| - Move(expr->context(), r0);
|
| - break;
|
| + case Slot::CONTEXT: {
|
| + Comment cmnt(masm_, "Context slot");
|
| + break;
|
| + }
|
| + case Slot::LOOKUP:
|
| + UNIMPLEMENTED();
|
| + break;
|
| + default:
|
| + UNREACHABLE();
|
| }
|
| -
|
| - case Slot::LOOKUP:
|
| - UNREACHABLE();
|
| - break;
|
| }
|
| + Move(expr->context(), slot, r0);
|
| } else {
|
| - // The parameter variable has been rewritten into an explict access to
|
| - // the arguments object.
|
| + // A variable has been rewritten into an explicit access to
|
| + // an object property.
|
| Property* property = rewrite->AsProperty();
|
| ASSERT_NOT_NULL(property);
|
| - ASSERT_EQ(expr->context(), property->context());
|
| - Visit(property);
|
| +
|
| + // Currently the only parameter expressions that can occur are
|
| + // on the form "slot[literal]".
|
| +
|
| + // Check that the object is in a slot.
|
| + Variable* object_var = property->obj()->AsVariableProxy()->AsVariable();
|
| + ASSERT_NOT_NULL(object_var);
|
| + Slot* object_slot = object_var->slot();
|
| + ASSERT_NOT_NULL(object_slot);
|
| +
|
| + // Load the object.
|
| + Move(r2, object_slot);
|
| +
|
| + // Check that the key is a smi.
|
| + Literal* key_literal = property->key()->AsLiteral();
|
| + ASSERT_NOT_NULL(key_literal);
|
| + ASSERT(key_literal->handle()->IsSmi());
|
| +
|
| + // Load the key.
|
| + __ mov(r1, Operand(key_literal->handle()));
|
| +
|
| + // Push both as arguments to ic.
|
| + __ stm(db_w, sp, r2.bit() | r1.bit());
|
| +
|
| + // Do a KEYED property load.
|
| + Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
|
| + __ Call(ic, RelocInfo::CODE_TARGET);
|
| +
|
| + // Drop key and object left on the stack by IC, and push the result.
|
| + DropAndMove(expr->context(), r0, 2);
|
| }
|
| }
|
|
|
| @@ -785,7 +875,7 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) {
|
| // Overwrite the global object on the stack with the result if needed.
|
| DropAndMove(expr->context(), r0);
|
|
|
| - } else {
|
| + } else if (var->slot()) {
|
| Slot* slot = var->slot();
|
| ASSERT_NOT_NULL(slot); // Variables rewritten as properties not handled.
|
| switch (slot->type()) {
|
| @@ -884,6 +974,35 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) {
|
| UNREACHABLE();
|
| break;
|
| }
|
| + } else {
|
| + Property* property = var->rewrite()->AsProperty();
|
| + ASSERT_NOT_NULL(property);
|
| +
|
| + // Load object and key onto the stack.
|
| + Slot* object_slot = property->obj()->AsSlot();
|
| + ASSERT_NOT_NULL(object_slot);
|
| + Move(Expression::kValue, object_slot, r0);
|
| +
|
| + Literal* key_literal = property->key()->AsLiteral();
|
| + ASSERT_NOT_NULL(key_literal);
|
| + Move(Expression::kValue, key_literal);
|
| +
|
| + // Value to store was pushed before object and key on the stack.
|
| + __ ldr(r0, MemOperand(sp, 2 * kPointerSize));
|
| +
|
| + // Arguments to ic is value in r0, object and key on stack.
|
| + Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
|
| + __ Call(ic, RelocInfo::CODE_TARGET);
|
| +
|
| + if (expr->context() == Expression::kEffect) {
|
| + __ add(sp, sp, Operand(3 * kPointerSize));
|
| + } else if (expr->context() == Expression::kValue) {
|
| + // Value is still on the stack in esp[2 * kPointerSize]
|
| + __ add(sp, sp, Operand(2 * kPointerSize));
|
| + } else {
|
| + __ ldr(r0, MemOperand(sp, 2 * kPointerSize));
|
| + DropAndMove(expr->context(), r0, 3);
|
| + }
|
| }
|
| }
|
|
|
|
|