| Index: src/codegen-arm.cc
|
| ===================================================================
|
| --- src/codegen-arm.cc (revision 746)
|
| +++ src/codegen-arm.cc (working copy)
|
| @@ -35,7 +35,85 @@
|
|
|
| namespace v8 { namespace internal {
|
|
|
| +#define __ masm_->
|
| +
|
| // -------------------------------------------------------------------------
|
| +// VirtualFrame implementation.
|
| +
|
| +VirtualFrame::VirtualFrame(CodeGenerator* cgen) {
|
| + ASSERT(cgen->scope() != NULL);
|
| +
|
| + masm_ = cgen->masm();
|
| + frame_local_count_ = cgen->scope()->num_stack_slots();
|
| + parameter_count_ = cgen->scope()->num_parameters();
|
| +}
|
| +
|
| +
|
| +void VirtualFrame::Enter() {
|
| + Comment cmnt(masm_, "[ Enter JS frame");
|
| +#ifdef DEBUG
|
| + { Label done, fail;
|
| + __ tst(r1, Operand(kSmiTagMask));
|
| + __ b(eq, &fail);
|
| + __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
|
| + __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset));
|
| + __ cmp(r2, Operand(JS_FUNCTION_TYPE));
|
| + __ b(eq, &done);
|
| + __ bind(&fail);
|
| + __ stop("CodeGenerator::EnterJSFrame - r1 not a function");
|
| + __ bind(&done);
|
| + }
|
| +#endif // DEBUG
|
| +
|
| + __ stm(db_w, sp, r1.bit() | cp.bit() | fp.bit() | lr.bit());
|
| + // Adjust FP to point to saved FP.
|
| + __ add(fp, sp, Operand(2 * kPointerSize));
|
| +}
|
| +
|
| +
|
| +void VirtualFrame::Exit() {
|
| + Comment cmnt(masm_, "[ Exit JS frame");
|
| + // Drop the execution stack down to the frame pointer and restore the caller
|
| + // frame pointer and return address.
|
| + __ mov(sp, fp);
|
| + __ ldm(ia_w, sp, fp.bit() | lr.bit());
|
| +}
|
| +
|
| +
|
| +void VirtualFrame::AllocateLocals() {
|
| + if (frame_local_count_ > 0) {
|
| + Comment cmnt(masm_, "[ Allocate space for locals");
|
| + // Initialize stack slots with 'undefined' value.
|
| + __ mov(ip, Operand(Factory::undefined_value()));
|
| + for (int i = 0; i < frame_local_count_; i++) {
|
| + __ push(ip);
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +void VirtualFrame::Drop(int count) {
|
| + ASSERT(count >= 0);
|
| + if (count > 0) {
|
| + __ add(sp, sp, Operand(count * kPointerSize));
|
| + }
|
| +}
|
| +
|
| +
|
| +void VirtualFrame::Pop() { Drop(1); }
|
| +
|
| +
|
| +void VirtualFrame::Pop(Register reg) {
|
| + __ pop(reg);
|
| +}
|
| +
|
| +
|
| +void VirtualFrame::Push(Register reg) {
|
| + __ push(reg);
|
| +}
|
| +
|
| +
|
| +// -------------------------------------------------------------------------
|
| // CodeGenState implementation.
|
|
|
| CodeGenState::CodeGenState(CodeGenerator* owner)
|
| @@ -67,11 +145,9 @@
|
| }
|
|
|
|
|
| -// -----------------------------------------------------------------------------
|
| +// -------------------------------------------------------------------------
|
| // CodeGenerator implementation
|
|
|
| -#define __ masm_->
|
| -
|
| CodeGenerator::CodeGenerator(int buffer_size, Handle<Script> script,
|
| bool is_eval)
|
| : is_eval_(is_eval),
|
| @@ -79,6 +155,7 @@
|
| deferred_(8),
|
| masm_(new MacroAssembler(NULL, buffer_size)),
|
| scope_(NULL),
|
| + frame_(NULL),
|
| cc_reg_(al),
|
| state_(NULL),
|
| break_stack_height_(0) {
|
| @@ -86,7 +163,6 @@
|
|
|
|
|
| // Calling conventions:
|
| -
|
| // r0: the number of arguments
|
| // fp: frame pointer
|
| // sp: stack pointer
|
| @@ -94,13 +170,17 @@
|
| // cp: callee's context
|
|
|
| void CodeGenerator::GenCode(FunctionLiteral* fun) {
|
| - Scope* scope = fun->scope();
|
| ZoneList<Statement*>* body = fun->body();
|
|
|
| // Initialize state.
|
| - { CodeGenState state(this);
|
| - scope_ = scope;
|
| - cc_reg_ = al;
|
| + ASSERT(scope_ == NULL);
|
| + scope_ = fun->scope();
|
| + ASSERT(frame_ == NULL);
|
| + VirtualFrame virtual_frame(this);
|
| + frame_ = &virtual_frame;
|
| + cc_reg_ = al;
|
| + {
|
| + CodeGenState state(this);
|
|
|
| // Entry
|
| // stack: function, receiver, arguments, return address
|
| @@ -110,9 +190,7 @@
|
| // pp: caller's parameter pointer
|
| // cp: callee's context
|
|
|
| - { Comment cmnt(masm_, "[ enter JS frame");
|
| - EnterJSFrame();
|
| - }
|
| + frame_->Enter();
|
| // tos: code slot
|
| #ifdef DEBUG
|
| if (strlen(FLAG_stop_at) > 0 &&
|
| @@ -122,20 +200,13 @@
|
| #endif
|
|
|
| // Allocate space for locals and initialize them.
|
| - if (scope->num_stack_slots() > 0) {
|
| - Comment cmnt(masm_, "[ allocate space for locals");
|
| - // Initialize stack slots with 'undefined' value.
|
| - __ mov(ip, Operand(Factory::undefined_value()));
|
| - for (int i = 0; i < scope->num_stack_slots(); i++) {
|
| - __ push(ip);
|
| - }
|
| - }
|
| + frame_->AllocateLocals();
|
|
|
| - if (scope->num_heap_slots() > 0) {
|
| + if (scope_->num_heap_slots() > 0) {
|
| // Allocate local context.
|
| // Get outer context and create a new context based on it.
|
| - __ ldr(r0, FunctionOperand());
|
| - __ push(r0);
|
| + __ ldr(r0, frame_->Function());
|
| + frame_->Push(r0);
|
| __ CallRuntime(Runtime::kNewContext, 1); // r0 holds the result
|
|
|
| if (kDebug) {
|
| @@ -146,10 +217,10 @@
|
| __ bind(&verified_true);
|
| }
|
| // Update context local.
|
| - __ str(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
|
| + __ str(cp, frame_->Context());
|
| }
|
|
|
| - // TODO(1241774): Improve this code!!!
|
| + // TODO(1241774): Improve this code:
|
| // 1) only needed if we have a context
|
| // 2) no need to recompute context ptr every single time
|
| // 3) don't copy parameter operand code from SlotOperand!
|
| @@ -164,12 +235,12 @@
|
| // order: such a parameter is copied repeatedly into the same
|
| // context location and thus the last value is what is seen inside
|
| // the function.
|
| - for (int i = 0; i < scope->num_parameters(); i++) {
|
| - Variable* par = scope->parameter(i);
|
| + for (int i = 0; i < scope_->num_parameters(); i++) {
|
| + Variable* par = scope_->parameter(i);
|
| Slot* slot = par->slot();
|
| if (slot != NULL && slot->type() == Slot::CONTEXT) {
|
| - ASSERT(!scope->is_global_scope()); // no parameters in global scope
|
| - __ ldr(r1, ParameterOperand(i));
|
| + ASSERT(!scope_->is_global_scope()); // no parameters in global scope
|
| + __ ldr(r1, frame_->Parameter(i));
|
| // Loads r2 with context; used below in RecordWrite.
|
| __ str(r1, SlotOperand(slot, r2));
|
| // Load the offset into r3.
|
| @@ -181,59 +252,55 @@
|
| }
|
| }
|
|
|
| - // Store the arguments object.
|
| - // This must happen after context initialization because
|
| - // the arguments array may be stored in the context!
|
| - if (scope->arguments() != NULL) {
|
| - ASSERT(scope->arguments_shadow() != NULL);
|
| + // Store the arguments object. This must happen after context
|
| + // initialization because the arguments object may be stored in the
|
| + // context.
|
| + if (scope_->arguments() != NULL) {
|
| + ASSERT(scope_->arguments_shadow() != NULL);
|
| Comment cmnt(masm_, "[ allocate arguments object");
|
| - { Reference shadow_ref(this, scope->arguments_shadow());
|
| - { Reference arguments_ref(this, scope->arguments());
|
| + { Reference shadow_ref(this, scope_->arguments_shadow());
|
| + { Reference arguments_ref(this, scope_->arguments());
|
| ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT);
|
| - __ ldr(r2, FunctionOperand());
|
| + __ ldr(r2, frame_->Function());
|
| // The receiver is below the arguments, the return address,
|
| // and the frame pointer on the stack.
|
| - const int kReceiverDisplacement = 2 + scope->num_parameters();
|
| + const int kReceiverDisplacement = 2 + scope_->num_parameters();
|
| __ add(r1, fp, Operand(kReceiverDisplacement * kPointerSize));
|
| - __ mov(r0, Operand(Smi::FromInt(scope->num_parameters())));
|
| + __ mov(r0, Operand(Smi::FromInt(scope_->num_parameters())));
|
| __ stm(db_w, sp, r0.bit() | r1.bit() | r2.bit());
|
| __ CallStub(&stub);
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| arguments_ref.SetValue(NOT_CONST_INIT);
|
| }
|
| shadow_ref.SetValue(NOT_CONST_INIT);
|
| }
|
| - __ pop(r0); // Value is no longer needed.
|
| + frame_->Pop(); // Value is no longer needed.
|
| }
|
|
|
| - // Generate code to 'execute' declarations and initialize
|
| - // functions (source elements). In case of an illegal
|
| - // redeclaration we need to handle that instead of processing the
|
| - // declarations.
|
| - if (scope->HasIllegalRedeclaration()) {
|
| + // Generate code to 'execute' declarations and initialize functions
|
| + // (source elements). In case of an illegal redeclaration we need to
|
| + // handle that instead of processing the declarations.
|
| + if (scope_->HasIllegalRedeclaration()) {
|
| Comment cmnt(masm_, "[ illegal redeclarations");
|
| - scope->VisitIllegalRedeclaration(this);
|
| + scope_->VisitIllegalRedeclaration(this);
|
| } else {
|
| Comment cmnt(masm_, "[ declarations");
|
| - // ProcessDeclarations calls DeclareGlobals indirectly
|
| - ProcessDeclarations(scope->declarations());
|
| -
|
| - // Bail out if a stack-overflow exception occurred when
|
| - // processing declarations.
|
| + ProcessDeclarations(scope_->declarations());
|
| + // Bail out if a stack-overflow exception occurred when processing
|
| + // declarations.
|
| if (HasStackOverflow()) return;
|
| }
|
|
|
| if (FLAG_trace) {
|
| - // Push a valid value as the parameter. The runtime call only uses
|
| - // it as the return value to indicate non-failure.
|
| __ CallRuntime(Runtime::kTraceEnter, 0);
|
| + // Ignore the return value.
|
| }
|
| CheckStack();
|
|
|
| // Compile the body of the function in a vanilla state. Don't
|
| // bother compiling all the code if the scope has an illegal
|
| // redeclaration.
|
| - if (!scope->HasIllegalRedeclaration()) {
|
| + if (!scope_->HasIllegalRedeclaration()) {
|
| Comment cmnt(masm_, "[ function body");
|
| #ifdef DEBUG
|
| bool is_builtin = Bootstrapper::IsActive();
|
| @@ -241,6 +308,7 @@
|
| is_builtin ? FLAG_trace_builtin_calls : FLAG_trace_calls;
|
| if (should_trace) {
|
| __ CallRuntime(Runtime::kDebugTrace, 0);
|
| + // Ignore the return value.
|
| }
|
| #endif
|
| VisitStatements(body);
|
| @@ -259,19 +327,20 @@
|
| if (FLAG_trace) {
|
| // Push the return value on the stack as the parameter.
|
| // Runtime::TraceExit returns the parameter as it is.
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| __ CallRuntime(Runtime::kTraceExit, 1);
|
| }
|
|
|
| // Tear down the frame which will restore the caller's frame pointer and the
|
| // link register.
|
| - ExitJSFrame();
|
| + frame_->Exit();
|
|
|
| __ add(sp, sp, Operand((scope_->num_parameters() + 1) * kPointerSize));
|
| __ mov(pc, lr);
|
|
|
| // Code generation state must be reset.
|
| scope_ = NULL;
|
| + frame_ = NULL;
|
| ASSERT(!has_cc());
|
| ASSERT(state_ == NULL);
|
| }
|
| @@ -290,13 +359,10 @@
|
| int index = slot->index();
|
| switch (slot->type()) {
|
| case Slot::PARAMETER:
|
| - return ParameterOperand(index);
|
| + return frame_->Parameter(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::LOCAL:
|
| + return frame_->Local(index);
|
|
|
| case Slot::CONTEXT: {
|
| // Follow the context chain if necessary.
|
| @@ -331,16 +397,17 @@
|
| }
|
|
|
|
|
| -// 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
|
| -// code register and no value is pushed. If the condition code register was set,
|
| -// has_cc() is true and cc_reg_ contains the condition to test for 'true'.
|
| +// Loads a value on TOS. 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 code register and no value is pushed. If the condition code
|
| +// register was set, has_cc() is true and cc_reg_ contains the condition to
|
| +// test for 'true'.
|
| void CodeGenerator::LoadCondition(Expression* x,
|
| - TypeofState typeof_state,
|
| - Label* true_target,
|
| - Label* false_target,
|
| - bool force_cc) {
|
| + TypeofState typeof_state,
|
| + Label* true_target,
|
| + Label* false_target,
|
| + bool force_cc) {
|
| ASSERT(!has_cc());
|
|
|
| { CodeGenState new_state(this, typeof_state, true_target, false_target);
|
| @@ -348,6 +415,13 @@
|
| }
|
| if (force_cc && !has_cc()) {
|
| // Convert the TOS value to a boolean in the condition code register.
|
| + // Visiting an expression may possibly choose neither (a) to leave a
|
| + // value in the condition code register nor (b) to leave a value in TOS
|
| + // (eg, by compiling to only jumps to the targets). In that case the
|
| + // code generated by ToBoolean is wrong because it assumes the value of
|
| + // the expression in TOS. So long as there is always a value in TOS or
|
| + // the condition code register when control falls through to here (there
|
| + // is), the code generated by ToBoolean is dead and therefore safe.
|
| ToBoolean(true_target, false_target);
|
| }
|
| ASSERT(has_cc() || !force_cc);
|
| @@ -364,11 +438,11 @@
|
| Label loaded, materialize_true;
|
| __ b(cc_reg_, &materialize_true);
|
| __ mov(r0, Operand(Factory::false_value()));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| __ b(&loaded);
|
| __ bind(&materialize_true);
|
| __ mov(r0, Operand(Factory::true_value()));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| __ bind(&loaded);
|
| cc_reg_ = al;
|
| }
|
| @@ -384,7 +458,7 @@
|
| if (true_target.is_linked()) {
|
| __ bind(&true_target);
|
| __ mov(r0, Operand(Factory::true_value()));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| }
|
| // if both "true" and "false" need to be reincarnated,
|
| // jump across code for "false"
|
| @@ -394,7 +468,7 @@
|
| if (false_target.is_linked()) {
|
| __ bind(&false_target);
|
| __ mov(r0, Operand(Factory::false_value()));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| }
|
| // everything is loaded at this point
|
| __ bind(&loaded);
|
| @@ -405,14 +479,15 @@
|
|
|
| void CodeGenerator::LoadGlobal() {
|
| __ ldr(r0, GlobalObject());
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| }
|
|
|
|
|
| -void CodeGenerator::LoadGlobalReceiver(Register s) {
|
| - __ ldr(s, ContextOperand(cp, Context::GLOBAL_INDEX));
|
| - __ ldr(s, FieldMemOperand(s, GlobalObject::kGlobalReceiverOffset));
|
| - __ push(s);
|
| +void CodeGenerator::LoadGlobalReceiver(Register scratch) {
|
| + __ ldr(scratch, ContextOperand(cp, Context::GLOBAL_INDEX));
|
| + __ ldr(scratch,
|
| + FieldMemOperand(scratch, GlobalObject::kGlobalReceiverOffset));
|
| + frame_->Push(scratch);
|
| }
|
|
|
|
|
| @@ -450,7 +525,6 @@
|
|
|
| void CodeGenerator::LoadReference(Reference* ref) {
|
| Comment cmnt(masm_, "[ LoadReference");
|
| -
|
| Expression* e = ref->expression();
|
| Property* property = e->AsProperty();
|
| Variable* var = e->AsVariableProxy()->AsVariable();
|
| @@ -492,15 +566,13 @@
|
|
|
|
|
| void CodeGenerator::UnloadReference(Reference* ref) {
|
| + // Pop a reference from the stack while preserving TOS.
|
| Comment cmnt(masm_, "[ UnloadReference");
|
| -
|
| int size = ref->size();
|
| - if (size <= 0) {
|
| - // Do nothing. No popping is necessary.
|
| - } else {
|
| - __ pop(r0);
|
| - __ add(sp, sp, Operand(size * kPointerSize));
|
| - __ push(r0);
|
| + if (size > 0) {
|
| + frame_->Pop(r0);
|
| + frame_->Drop(size);
|
| + frame_->Push(r0);
|
| }
|
| }
|
|
|
| @@ -509,10 +581,10 @@
|
| // register to a boolean in the condition code register. The code
|
| // may jump to 'false_target' in case the register converts to 'false'.
|
| void CodeGenerator::ToBoolean(Label* true_target,
|
| - Label* false_target) {
|
| + Label* false_target) {
|
| // Note: The generated code snippet does not change stack variables.
|
| // Only the condition code should be set.
|
| - __ pop(r0);
|
| + frame_->Pop(r0);
|
|
|
| // Fast case checks
|
|
|
| @@ -535,10 +607,9 @@
|
| __ b(eq, true_target);
|
|
|
| // Slow case: call the runtime.
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| __ CallRuntime(Runtime::kToBool, 1);
|
| -
|
| - // Convert result (r0) to condition code
|
| + // Convert the result (r0) to a condition code.
|
| __ cmp(r0, Operand(Factory::false_value()));
|
|
|
| cc_reg_ = ne;
|
| @@ -639,8 +710,8 @@
|
| case Token::SHL:
|
| case Token::SHR:
|
| case Token::SAR: {
|
| - __ pop(r0); // r0 : y
|
| - __ pop(r1); // r1 : x
|
| + frame_->Pop(r0); // r0 : y
|
| + frame_->Pop(r1); // r1 : x
|
| GenericBinaryOpStub stub(op);
|
| __ CallStub(&stub);
|
| break;
|
| @@ -659,9 +730,9 @@
|
| }
|
|
|
| case Token::COMMA:
|
| - __ pop(r0);
|
| + frame_->Pop(r0);
|
| // simply discard left value
|
| - __ pop();
|
| + frame_->Pop();
|
| break;
|
|
|
| default:
|
| @@ -749,8 +820,8 @@
|
|
|
|
|
| void CodeGenerator::SmiOperation(Token::Value op,
|
| - Handle<Object> value,
|
| - bool reversed) {
|
| + Handle<Object> value,
|
| + bool reversed) {
|
| // NOTE: This is an attempt to inline (a bit) more of the code for
|
| // some possible smi operations (like + and -) when (at least) one
|
| // of the operands is a literal smi. With this optimization, the
|
| @@ -763,7 +834,7 @@
|
| int int_value = Smi::cast(*value)->value();
|
|
|
| Label exit;
|
| - __ pop(r0);
|
| + frame_->Pop(r0);
|
|
|
| switch (op) {
|
| case Token::ADD: {
|
| @@ -816,8 +887,8 @@
|
| case Token::SAR: {
|
| if (reversed) {
|
| __ mov(ip, Operand(value));
|
| - __ push(ip);
|
| - __ push(r0);
|
| + frame_->Push(ip);
|
| + frame_->Push(r0);
|
| GenericBinaryOperation(op);
|
|
|
| } else {
|
| @@ -867,13 +938,13 @@
|
|
|
| default:
|
| if (!reversed) {
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| __ mov(r0, Operand(value));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| } else {
|
| __ mov(ip, Operand(value));
|
| - __ push(ip);
|
| - __ push(r0);
|
| + frame_->Push(ip);
|
| + frame_->Push(r0);
|
| }
|
| GenericBinaryOperation(op);
|
| break;
|
| @@ -895,18 +966,18 @@
|
| // Implement '>' and '<=' by reversal to obtain ECMA-262 conversion order.
|
| if (cc == gt || cc == le) {
|
| cc = ReverseCondition(cc);
|
| - __ pop(r1);
|
| - __ pop(r0);
|
| + frame_->Pop(r1);
|
| + frame_->Pop(r0);
|
| } else {
|
| - __ pop(r0);
|
| - __ pop(r1);
|
| + frame_->Pop(r0);
|
| + frame_->Pop(r1);
|
| }
|
| __ orr(r2, r0, Operand(r1));
|
| __ tst(r2, Operand(kSmiTagMask));
|
| __ b(eq, &smi);
|
|
|
| // Perform non-smi comparison by runtime call.
|
| - __ push(r1);
|
| + frame_->Push(r1);
|
|
|
| // Figure out which native to call and setup the arguments.
|
| Builtins::JavaScript native;
|
| @@ -923,14 +994,14 @@
|
| ASSERT(cc == gt || cc == ge); // remaining cases
|
| ncr = LESS;
|
| }
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| __ mov(r0, Operand(Smi::FromInt(ncr)));
|
| argc = 2;
|
| }
|
|
|
| // Call the native; it returns -1 (less), 0 (equal), or 1 (greater)
|
| // tagged as a small integer.
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| __ mov(r0, Operand(argc));
|
| __ InvokeBuiltin(native, CALL_JS);
|
| __ cmp(r0, Operand(0));
|
| @@ -979,8 +1050,8 @@
|
| __ CallStub(&call_function);
|
|
|
| // Restore context and pop function from the stack.
|
| - __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
|
| - __ pop(); // discard the TOS
|
| + __ ldr(cp, frame_->Context());
|
| + frame_->Pop(); // discard the TOS
|
| }
|
|
|
|
|
| @@ -1012,10 +1083,10 @@
|
|
|
| void CodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
|
| __ mov(r0, Operand(pairs));
|
| - __ push(r0);
|
| - __ push(cp);
|
| + frame_->Push(r0);
|
| + frame_->Push(cp);
|
| __ mov(r0, Operand(Smi::FromInt(is_eval() ? 1 : 0)));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| __ CallRuntime(Runtime::kDeclareGlobals, 3);
|
| // The result is discarded.
|
| }
|
| @@ -1035,26 +1106,26 @@
|
| // during variable resolution and must have mode DYNAMIC.
|
| ASSERT(var->mode() == Variable::DYNAMIC);
|
| // For now, just do a runtime call.
|
| - __ push(cp);
|
| + frame_->Push(cp);
|
| __ mov(r0, Operand(var->name()));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| // Declaration nodes are always declared in only two modes.
|
| ASSERT(node->mode() == Variable::VAR || node->mode() == Variable::CONST);
|
| PropertyAttributes attr = node->mode() == Variable::VAR ? NONE : READ_ONLY;
|
| __ mov(r0, Operand(Smi::FromInt(attr)));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| // Push initial value, if any.
|
| // Note: For variables we must not push an initial value (such as
|
| // 'undefined') because we may have a (legal) redeclaration and we
|
| // must not destroy the current value.
|
| if (node->mode() == Variable::CONST) {
|
| __ mov(r0, Operand(Factory::the_hole_value()));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| } else if (node->fun() != NULL) {
|
| Load(node->fun());
|
| } else {
|
| __ mov(r0, Operand(0)); // no initial value!
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| }
|
| __ CallRuntime(Runtime::kDeclareContextSlot, 4);
|
| // Ignore the return value (declarations are statements).
|
| @@ -1081,7 +1152,7 @@
|
| // safe to pop the value lying on top of the reference before unloading
|
| // the reference itself (which preserves the top of stack) because we
|
| // know it is a zero-sized reference.
|
| - __ pop();
|
| + frame_->Pop();
|
| }
|
| }
|
|
|
| @@ -1092,7 +1163,7 @@
|
| Expression* expression = node->expression();
|
| expression->MarkAsStatement();
|
| Load(expression);
|
| - __ pop();
|
| + frame_->Pop();
|
| }
|
|
|
|
|
| @@ -1157,7 +1228,7 @@
|
| if (has_cc()) {
|
| cc_reg_ = al;
|
| } else {
|
| - __ pop(r0); // __ Pop(no_reg)
|
| + frame_->Pop();
|
| }
|
| }
|
|
|
| @@ -1167,10 +1238,8 @@
|
|
|
|
|
| void CodeGenerator::CleanStack(int num_bytes) {
|
| - ASSERT(num_bytes >= 0);
|
| - if (num_bytes > 0) {
|
| - __ add(sp, sp, Operand(num_bytes));
|
| - }
|
| + ASSERT(num_bytes % kPointerSize == 0);
|
| + frame_->Drop(num_bytes / kPointerSize);
|
| }
|
|
|
|
|
| @@ -1195,7 +1264,7 @@
|
| if (FLAG_debug_info) RecordStatementPosition(node);
|
| Load(node->expression());
|
| // Move the function result into r0.
|
| - __ pop(r0);
|
| + frame_->Pop(r0);
|
|
|
| __ b(&function_return_);
|
| }
|
| @@ -1214,7 +1283,7 @@
|
| __ bind(&verified_true);
|
| }
|
| // Update context local.
|
| - __ str(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
|
| + __ str(cp, frame_->Context());
|
| }
|
|
|
|
|
| @@ -1223,7 +1292,7 @@
|
| // Pop context.
|
| __ ldr(cp, ContextOperand(cp, Context::PREVIOUS_INDEX));
|
| // Update context local.
|
| - __ str(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
|
| + __ str(cp, frame_->Context());
|
| }
|
|
|
|
|
| @@ -1246,7 +1315,7 @@
|
|
|
| ASSERT(kSmiTag == 0 && kSmiTagSize <= 2);
|
|
|
| - __ pop(r0);
|
| + frame_->Pop(r0);
|
|
|
| // Test for a Smi value in a HeapNumber.
|
| Label is_smi;
|
| @@ -1256,7 +1325,7 @@
|
| __ ldrb(r1, MemOperand(r1, Map::kInstanceTypeOffset - kHeapObjectTag));
|
| __ cmp(r1, Operand(HEAP_NUMBER_TYPE));
|
| __ b(ne, fail_label);
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| __ CallRuntime(Runtime::kNumberToSmi, 1);
|
| __ bind(&is_smi);
|
|
|
| @@ -1324,8 +1393,8 @@
|
| } else {
|
| __ bind(&next);
|
| next.Unuse();
|
| - __ ldr(r0, MemOperand(sp, 0));
|
| - __ push(r0); // duplicate TOS
|
| + __ ldr(r0, frame_->Top());
|
| + frame_->Push(r0); // duplicate TOS
|
| Load(clause->label());
|
| Comparison(eq, true);
|
| Branch(false, &next);
|
| @@ -1333,7 +1402,7 @@
|
|
|
| // Entering the case statement for the first time. Remove the switch value
|
| // from the stack.
|
| - __ pop(r0);
|
| + frame_->Pop();
|
|
|
| // Generate code for the body.
|
| // This is also the target for the fall through from the previous case's
|
| @@ -1352,7 +1421,7 @@
|
| __ b(&default_case);
|
| } else {
|
| // Remove the switch value from the stack.
|
| - __ pop(r0);
|
| + frame_->Pop();
|
| }
|
|
|
| __ bind(&fall_through);
|
| @@ -1448,7 +1517,7 @@
|
|
|
| // Get the object to enumerate over (converted to JSObject).
|
| Load(node->enumerable());
|
| - __ pop(r0);
|
| + frame_->Pop(r0);
|
|
|
| // Both SpiderMonkey and kjs ignore null and undefined in contrast
|
| // to the specification. 12.6.4 mandates a call to ToObject.
|
| @@ -1473,7 +1542,7 @@
|
| __ b(hs, &jsobject);
|
|
|
| __ bind(&primitive);
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| __ mov(r0, Operand(0));
|
| __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS);
|
|
|
| @@ -1481,8 +1550,8 @@
|
| __ bind(&jsobject);
|
|
|
| // Get the set of properties (as a FixedArray or Map).
|
| - __ push(r0); // duplicate the object being enumerated
|
| - __ push(r0);
|
| + frame_->Push(r0); // duplicate the object being enumerated
|
| + frame_->Push(r0);
|
| __ CallRuntime(Runtime::kGetPropertyNamesFast, 1);
|
|
|
| // If we got a Map, we can do a fast modification check.
|
| @@ -1499,28 +1568,28 @@
|
| __ ldr(r2,
|
| FieldMemOperand(r1, DescriptorArray::kEnumCacheBridgeCacheOffset));
|
|
|
| - __ push(r0); // map
|
| - __ push(r2); // enum cache bridge cache
|
| + frame_->Push(r0); // map
|
| + frame_->Push(r2); // enum cache bridge cache
|
| __ ldr(r0, FieldMemOperand(r2, FixedArray::kLengthOffset));
|
| __ mov(r0, Operand(r0, LSL, kSmiTagSize));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| __ mov(r0, Operand(Smi::FromInt(0)));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| __ b(&entry);
|
|
|
|
|
| __ bind(&fixed_array);
|
|
|
| __ mov(r1, Operand(Smi::FromInt(0)));
|
| - __ push(r1); // insert 0 in place of Map
|
| - __ push(r0);
|
| + frame_->Push(r1); // insert 0 in place of Map
|
| + frame_->Push(r0);
|
|
|
| // Push the length of the array and the initial index onto the stack.
|
| __ ldr(r0, FieldMemOperand(r0, FixedArray::kLengthOffset));
|
| __ mov(r0, Operand(r0, LSL, kSmiTagSize));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| __ mov(r0, Operand(Smi::FromInt(0))); // init index
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
|
|
| __ b(&entry);
|
|
|
| @@ -1531,9 +1600,9 @@
|
| // Next.
|
| __ bind(node->continue_target());
|
| __ bind(&next);
|
| - __ pop(r0);
|
| + frame_->Pop(r0);
|
| __ add(r0, r0, Operand(Smi::FromInt(1)));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
|
|
| // Condition.
|
| __ bind(&entry);
|
| @@ -1543,31 +1612,31 @@
|
| // sp[2] : array or enum cache
|
| // sp[3] : 0 or map
|
| // sp[4] : enumerable
|
| - __ ldr(r0, MemOperand(sp, 0 * kPointerSize)); // load the current count
|
| - __ ldr(r1, MemOperand(sp, 1 * kPointerSize)); // load the length
|
| + __ ldr(r0, frame_->Element(0)); // load the current count
|
| + __ ldr(r1, frame_->Element(1)); // load the length
|
| __ cmp(r0, Operand(r1)); // compare to the array length
|
| __ b(hs, &cleanup);
|
|
|
| - __ ldr(r0, MemOperand(sp, 0 * kPointerSize));
|
| + __ ldr(r0, frame_->Element(0));
|
|
|
| // Get the i'th entry of the array.
|
| - __ ldr(r2, MemOperand(sp, 2 * kPointerSize));
|
| + __ ldr(r2, frame_->Element(2));
|
| __ add(r2, r2, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
|
| __ ldr(r3, MemOperand(r2, r0, LSL, kPointerSizeLog2 - kSmiTagSize));
|
|
|
| // Get Map or 0.
|
| - __ ldr(r2, MemOperand(sp, 3 * kPointerSize));
|
| + __ ldr(r2, frame_->Element(3));
|
| // Check if this (still) matches the map of the enumerable.
|
| // If not, we have to filter the key.
|
| - __ ldr(r1, MemOperand(sp, 4 * kPointerSize));
|
| + __ ldr(r1, frame_->Element(4));
|
| __ ldr(r1, FieldMemOperand(r1, HeapObject::kMapOffset));
|
| __ cmp(r1, Operand(r2));
|
| __ b(eq, &end_del_check);
|
|
|
| // Convert the entry to a string (or null if it isn't a property anymore).
|
| - __ ldr(r0, MemOperand(sp, 4 * kPointerSize)); // push enumerable
|
| - __ push(r0);
|
| - __ push(r3); // push entry
|
| + __ ldr(r0, frame_->Element(4)); // push enumerable
|
| + frame_->Push(r0);
|
| + frame_->Push(r3); // push entry
|
| __ mov(r0, Operand(1));
|
| __ InvokeBuiltin(Builtins::FILTER_KEY, CALL_JS);
|
| __ mov(r3, Operand(r0));
|
| @@ -1581,12 +1650,12 @@
|
|
|
| // Store the entry in the 'each' expression and take another spin in the loop.
|
| // r3: i'th entry of the enum cache (or string there of)
|
| - __ push(r3); // push entry
|
| + frame_->Push(r3); // push entry
|
| { Reference each(this, node->each());
|
| if (!each.is_illegal()) {
|
| if (each.size() > 0) {
|
| - __ ldr(r0, MemOperand(sp, kPointerSize * each.size()));
|
| - __ push(r0);
|
| + __ ldr(r0, frame_->Element(each.size()));
|
| + frame_->Push(r0);
|
| }
|
| // 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
|
| @@ -1598,20 +1667,20 @@
|
| // ie, now the topmost value of the non-zero sized reference), since
|
| // we will discard the top of stack after unloading the reference
|
| // anyway.
|
| - __ pop(r0);
|
| + frame_->Pop(r0);
|
| }
|
| }
|
| }
|
| // Discard the i'th entry pushed above or else the remainder of the
|
| // reference, whichever is currently on top of the stack.
|
| - __ pop();
|
| + frame_->Pop();
|
| CheckStack(); // TODO(1222600): ignore if body contains calls.
|
| __ jmp(&loop);
|
|
|
| // Cleanup.
|
| __ bind(&cleanup);
|
| __ bind(node->break_target());
|
| - __ add(sp, sp, Operand(5 * kPointerSize));
|
| + frame_->Drop(5);
|
|
|
| // Exit.
|
| __ bind(&exit);
|
| @@ -1626,11 +1695,10 @@
|
| Label try_block, exit;
|
|
|
| __ bl(&try_block);
|
| -
|
| // --- Catch block ---
|
| + frame_->Push(r0);
|
|
|
| // Store the caught exception in the catch variable.
|
| - __ push(r0);
|
| { Reference ref(this, node->catch_var());
|
| ASSERT(ref.is_slot());
|
| // Here we make use of the convenient property that it doesn't matter
|
| @@ -1640,7 +1708,7 @@
|
| }
|
|
|
| // Remove the exception from the stack.
|
| - __ pop();
|
| + frame_->Pop();
|
|
|
| VisitStatements(node->catch_block()->statements());
|
| __ b(&exit);
|
| @@ -1667,7 +1735,7 @@
|
|
|
| // Generate code for the statements in the try block.
|
| VisitStatements(node->try_block()->statements());
|
| - __ pop(r0); // Discard the result.
|
| + frame_->Pop(); // Discard the result.
|
|
|
| // Stop the introduced shadowing and count the number of required unlinks.
|
| // After shadowing stops, the original labels are unshadowed and the
|
| @@ -1680,13 +1748,14 @@
|
|
|
| // Unlink from try chain.
|
| // TOS contains code slot
|
| - const int kNextOffset = StackHandlerConstants::kNextOffset +
|
| - StackHandlerConstants::kAddressDisplacement;
|
| - __ ldr(r1, MemOperand(sp, kNextOffset)); // read next_sp
|
| + const int kNextIndex = (StackHandlerConstants::kNextOffset
|
| + + StackHandlerConstants::kAddressDisplacement)
|
| + / kPointerSize;
|
| + __ ldr(r1, frame_->Element(kNextIndex)); // read next_sp
|
| __ mov(r3, Operand(ExternalReference(Top::k_handler_address)));
|
| __ str(r1, MemOperand(r3));
|
| ASSERT(StackHandlerConstants::kCodeOffset == 0); // first field is code
|
| - __ add(sp, sp, Operand(StackHandlerConstants::kSize - kPointerSize));
|
| + frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
|
| // Code slot popped.
|
| if (nof_unlinks > 0) __ b(&exit);
|
|
|
| @@ -1702,10 +1771,10 @@
|
| __ mov(r3, Operand(ExternalReference(Top::k_handler_address)));
|
| __ ldr(sp, MemOperand(r3));
|
|
|
| - __ ldr(r1, MemOperand(sp, kNextOffset));
|
| + __ ldr(r1, frame_->Element(kNextIndex));
|
| __ str(r1, MemOperand(r3));
|
| ASSERT(StackHandlerConstants::kCodeOffset == 0); // first field is code
|
| - __ add(sp, sp, Operand(StackHandlerConstants::kSize - kPointerSize));
|
| + frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
|
| // Code slot popped.
|
|
|
| __ b(shadows[i]->original_label());
|
| @@ -1728,7 +1797,7 @@
|
|
|
| __ bl(&try_block);
|
|
|
| - __ push(r0); // save exception object on the stack
|
| + frame_->Push(r0); // save exception object on the stack
|
| // In case of thrown exceptions, this is where we continue.
|
| __ mov(r2, Operand(Smi::FromInt(THROWING)));
|
| __ b(&finally_block);
|
| @@ -1766,7 +1835,7 @@
|
|
|
| // Set the state on the stack to FALLING.
|
| __ mov(r0, Operand(Factory::undefined_value())); // fake TOS
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| __ mov(r2, Operand(Smi::FromInt(FALLING)));
|
| if (nof_unlinks > 0) __ b(&unlink);
|
|
|
| @@ -1778,11 +1847,11 @@
|
| if (shadows[i]->original_label() == &function_return_) {
|
| // If this label shadowed the function return, materialize the
|
| // return value on the stack.
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| } else {
|
| // Fake TOS for labels that shadowed breaks and continues.
|
| __ mov(r0, Operand(Factory::undefined_value()));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| }
|
| __ mov(r2, Operand(Smi::FromInt(JUMPING + i)));
|
| __ b(&unlink);
|
| @@ -1792,25 +1861,26 @@
|
| // Unlink from try chain;
|
| __ bind(&unlink);
|
|
|
| - __ pop(r0); // Store TOS in r0 across stack manipulation
|
| + frame_->Pop(r0); // Store TOS in r0 across stack manipulation
|
| // Reload sp from the top handler, because some statements that we
|
| // break from (eg, for...in) may have left stuff on the stack.
|
| __ mov(r3, Operand(ExternalReference(Top::k_handler_address)));
|
| __ ldr(sp, MemOperand(r3));
|
| - const int kNextOffset = StackHandlerConstants::kNextOffset +
|
| - StackHandlerConstants::kAddressDisplacement;
|
| - __ ldr(r1, MemOperand(sp, kNextOffset));
|
| + const int kNextIndex = (StackHandlerConstants::kNextOffset
|
| + + StackHandlerConstants::kAddressDisplacement)
|
| + / kPointerSize;
|
| + __ ldr(r1, frame_->Element(kNextIndex));
|
| __ str(r1, MemOperand(r3));
|
| ASSERT(StackHandlerConstants::kCodeOffset == 0); // first field is code
|
| - __ add(sp, sp, Operand(StackHandlerConstants::kSize - kPointerSize));
|
| + frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
|
| // Code slot popped.
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
|
|
| // --- Finally block ---
|
| __ bind(&finally_block);
|
|
|
| // Push the state on the stack.
|
| - __ push(r2);
|
| + frame_->Push(r2);
|
|
|
| // We keep two elements on the stack - the (possibly faked) result
|
| // and the state - while evaluating the finally block. Record it, so
|
| @@ -1823,8 +1893,8 @@
|
| VisitStatements(node->finally_block()->statements());
|
|
|
| // Restore state and return value or faked TOS.
|
| - __ pop(r2);
|
| - __ pop(r0);
|
| + frame_->Pop(r2);
|
| + frame_->Pop(r0);
|
| break_stack_height_ -= kFinallyStackSize;
|
|
|
| // Generate code to jump to the right destination for all used (formerly)
|
| @@ -1848,7 +1918,7 @@
|
| __ b(ne, &exit);
|
|
|
| // Rethrow exception.
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| __ CallRuntime(Runtime::kReThrow, 1);
|
|
|
| // Done.
|
| @@ -1869,12 +1939,12 @@
|
|
|
| // Push the boilerplate on the stack.
|
| __ mov(r0, Operand(boilerplate));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
|
|
| // Create a new closure.
|
| - __ push(cp);
|
| + frame_->Push(cp);
|
| __ CallRuntime(Runtime::kNewClosure, 2);
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| }
|
|
|
|
|
| @@ -1915,16 +1985,16 @@
|
| ASSERT(slot->var()->mode() == Variable::DYNAMIC);
|
|
|
| // For now, just do a runtime call.
|
| - __ push(cp);
|
| + frame_->Push(cp);
|
| __ mov(r0, Operand(slot->var()->name()));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
|
|
| if (typeof_state == INSIDE_TYPEOF) {
|
| __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
|
| } else {
|
| __ CallRuntime(Runtime::kLoadContextSlot, 2);
|
| }
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
|
|
| } else {
|
| // Note: We would like to keep the assert below, but it fires because of
|
| @@ -1933,16 +2003,16 @@
|
|
|
| // Special handling for locals allocated in registers.
|
| __ ldr(r0, SlotOperand(slot, r2));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| if (slot->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);
|
| + frame_->Pop(r0);
|
| __ cmp(r0, Operand(Factory::the_hole_value()));
|
| __ mov(r0, Operand(Factory::undefined_value()), LeaveCC, eq);
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| }
|
| }
|
| }
|
| @@ -1972,7 +2042,7 @@
|
| void CodeGenerator::VisitLiteral(Literal* node) {
|
| Comment cmnt(masm_, "[ Literal");
|
| __ mov(r0, Operand(node->handle()));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| }
|
|
|
|
|
| @@ -1982,7 +2052,7 @@
|
| // Retrieve the literal array and check the allocated entry.
|
|
|
| // Load the function of this activation.
|
| - __ ldr(r1, FunctionOperand());
|
| + __ ldr(r1, frame_->Function());
|
|
|
| // Load the literals array of the function.
|
| __ ldr(r1, FieldMemOperand(r1, JSFunction::kLiteralsOffset));
|
| @@ -1998,19 +2068,19 @@
|
|
|
| // If the entry is undefined we call the runtime system to computed
|
| // the literal.
|
| - __ push(r1); // literal array (0)
|
| + frame_->Push(r1); // literal array (0)
|
| __ mov(r0, Operand(Smi::FromInt(node->literal_index())));
|
| - __ push(r0); // literal index (1)
|
| + frame_->Push(r0); // literal index (1)
|
| __ mov(r0, Operand(node->pattern())); // RegExp pattern (2)
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| __ mov(r0, Operand(node->flags())); // RegExp flags (3)
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
|
| __ mov(r2, Operand(r0));
|
|
|
| __ bind(&done);
|
| // Push the literal.
|
| - __ push(r2);
|
| + frame_->Push(r2);
|
| }
|
|
|
|
|
| @@ -2055,7 +2125,7 @@
|
| // Retrieve the literal array and check the allocated entry.
|
|
|
| // Load the function of this activation.
|
| - __ ldr(r1, FunctionOperand());
|
| + __ ldr(r1, frame_->Function());
|
|
|
| // Load the literals array of the function.
|
| __ ldr(r1, FieldMemOperand(r1, JSFunction::kLiteralsOffset));
|
| @@ -2072,11 +2142,11 @@
|
| __ bind(deferred->exit());
|
|
|
| // Push the object literal boilerplate.
|
| - __ push(r2);
|
| + frame_->Push(r2);
|
|
|
| // Clone the boilerplate object.
|
| __ CallRuntime(Runtime::kCloneObjectLiteralBoilerplate, 1);
|
| - __ push(r0); // save the result
|
| + frame_->Push(r0); // save the result
|
| // r0: cloned object literal
|
|
|
| for (int i = 0; i < node->properties()->length(); i++) {
|
| @@ -2087,32 +2157,32 @@
|
| case ObjectLiteral::Property::CONSTANT: break;
|
| case ObjectLiteral::Property::COMPUTED: // fall through
|
| case ObjectLiteral::Property::PROTOTYPE: {
|
| - __ push(r0); // dup the result
|
| + frame_->Push(r0); // dup the result
|
| Load(key);
|
| Load(value);
|
| __ CallRuntime(Runtime::kSetProperty, 3);
|
| // restore r0
|
| - __ ldr(r0, MemOperand(sp, 0));
|
| + __ ldr(r0, frame_->Top());
|
| break;
|
| }
|
| case ObjectLiteral::Property::SETTER: {
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| Load(key);
|
| __ mov(r0, Operand(Smi::FromInt(1)));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| Load(value);
|
| __ CallRuntime(Runtime::kDefineAccessor, 4);
|
| - __ ldr(r0, MemOperand(sp, 0));
|
| + __ ldr(r0, frame_->Top());
|
| break;
|
| }
|
| case ObjectLiteral::Property::GETTER: {
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| Load(key);
|
| __ mov(r0, Operand(Smi::FromInt(0)));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| Load(value);
|
| __ CallRuntime(Runtime::kDefineAccessor, 4);
|
| - __ ldr(r0, MemOperand(sp, 0));
|
| + __ ldr(r0, frame_->Top());
|
| break;
|
| }
|
| }
|
| @@ -2125,15 +2195,15 @@
|
|
|
| // Call runtime to create the array literal.
|
| __ mov(r0, Operand(node->literals()));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| // Load the function of this frame.
|
| - __ ldr(r0, FunctionOperand());
|
| + __ ldr(r0, frame_->Function());
|
| __ ldr(r0, FieldMemOperand(r0, JSFunction::kLiteralsOffset));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| __ CallRuntime(Runtime::kCreateArrayLiteral, 2);
|
|
|
| // Push the resulting array literal on the stack.
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
|
|
| // Generate code to set the elements in the array that are not
|
| // literals.
|
| @@ -2145,10 +2215,10 @@
|
| if (value->AsLiteral() == NULL) {
|
| // The property must be set by generated code.
|
| Load(value);
|
| - __ pop(r0);
|
| + frame_->Pop(r0);
|
|
|
| // Fetch the object literal
|
| - __ ldr(r1, MemOperand(sp, 0));
|
| + __ ldr(r1, frame_->Top());
|
| // Get the elements array.
|
| __ ldr(r1, FieldMemOperand(r1, JSObject::kElementsOffset));
|
|
|
| @@ -2181,12 +2251,12 @@
|
| Literal* literal = node->value()->AsLiteral();
|
| if (literal != NULL && literal->handle()->IsSmi()) {
|
| SmiOperation(node->binary_op(), literal->handle(), false);
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
|
|
| } else {
|
| Load(node->value());
|
| GenericBinaryOperation(node->binary_op());
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| }
|
| }
|
|
|
| @@ -2216,7 +2286,7 @@
|
| Load(node->exception());
|
| __ RecordPosition(node->position());
|
| __ CallRuntime(Runtime::kThrow, 1);
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| }
|
|
|
|
|
| @@ -2257,7 +2327,7 @@
|
|
|
| // Push the name of the function and the receiver onto the stack.
|
| __ mov(r0, Operand(var->name()));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
|
|
| // Pass the global object as the receiver and let the IC stub
|
| // patch the stack to use the global proxy as 'this' in the
|
| @@ -2271,10 +2341,10 @@
|
| Handle<Code> stub = ComputeCallInitialize(args->length());
|
| __ RecordPosition(node->position());
|
| __ Call(stub, RelocInfo::CODE_TARGET_CONTEXT);
|
| - __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
|
| + __ ldr(cp, frame_->Context());
|
| // Remove the function from the stack.
|
| - __ pop();
|
| - __ push(r0);
|
| + frame_->Pop();
|
| + frame_->Push(r0);
|
|
|
| } else if (var != NULL && var->slot() != NULL &&
|
| var->slot()->type() == Slot::LOOKUP) {
|
| @@ -2283,19 +2353,19 @@
|
| // ----------------------------------
|
|
|
| // Load the function
|
| - __ push(cp);
|
| + frame_->Push(cp);
|
| __ mov(r0, Operand(var->name()));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| __ CallRuntime(Runtime::kLoadContextSlot, 2);
|
| // r0: slot value; r1: receiver
|
|
|
| // Load the receiver.
|
| - __ push(r0); // function
|
| - __ push(r1); // receiver
|
| + frame_->Push(r0); // function
|
| + frame_->Push(r1); // receiver
|
|
|
| // Call the function.
|
| CallWithArguments(args, node->position());
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
|
|
| } else if (property != NULL) {
|
| // Check if the key is a literal string.
|
| @@ -2308,7 +2378,7 @@
|
|
|
| // Push the name of the function and the receiver onto the stack.
|
| __ mov(r0, Operand(literal->handle()));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| Load(property->obj());
|
|
|
| // Load the arguments.
|
| @@ -2318,12 +2388,12 @@
|
| Handle<Code> stub = ComputeCallInitialize(args->length());
|
| __ RecordPosition(node->position());
|
| __ Call(stub, RelocInfo::CODE_TARGET);
|
| - __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
|
| + __ ldr(cp, frame_->Context());
|
|
|
| // Remove the function from the stack.
|
| - __ pop();
|
| + frame_->Pop();
|
|
|
| - __ push(r0); // push after get rid of function from the stack
|
| + frame_->Push(r0); // push after get rid of function from the stack
|
|
|
| } else {
|
| // -------------------------------------------
|
| @@ -2335,11 +2405,11 @@
|
| ref.GetValue(NOT_INSIDE_TYPEOF); // receiver
|
|
|
| // Pass receiver to called function.
|
| - __ ldr(r0, MemOperand(sp, ref.size() * kPointerSize));
|
| - __ push(r0);
|
| + __ ldr(r0, frame_->Element(ref.size()));
|
| + frame_->Push(r0);
|
| // Call the function.
|
| CallWithArguments(args, node->position());
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| }
|
|
|
| } else {
|
| @@ -2355,7 +2425,7 @@
|
|
|
| // Call the function.
|
| CallWithArguments(args, node->position());
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| }
|
| }
|
|
|
| @@ -2383,7 +2453,7 @@
|
| __ mov(r0, Operand(args->length()));
|
|
|
| // Load the function into r1 as per calling convention.
|
| - __ ldr(r1, MemOperand(sp, (args->length() + 1) * kPointerSize));
|
| + __ ldr(r1, frame_->Element(args->length() + 1));
|
|
|
| // Call the construct call builtin that handles allocation and
|
| // constructor invocation.
|
| @@ -2392,7 +2462,7 @@
|
| RelocInfo::CONSTRUCT_CALL);
|
|
|
| // Discard old TOS value and push r0 on the stack (same as Pop(), push(r0)).
|
| - __ str(r0, MemOperand(sp, 0 * kPointerSize));
|
| + __ str(r0, frame_->Top());
|
| }
|
|
|
|
|
| @@ -2400,7 +2470,7 @@
|
| ASSERT(args->length() == 1);
|
| Label leave;
|
| Load(args->at(0));
|
| - __ pop(r0); // r0 contains object.
|
| + frame_->Pop(r0); // r0 contains object.
|
| // if (object->IsSmi()) return the object.
|
| __ tst(r0, Operand(kSmiTagMask));
|
| __ b(eq, &leave);
|
| @@ -2413,7 +2483,7 @@
|
| // Load the value.
|
| __ ldr(r0, FieldMemOperand(r0, JSValue::kValueOffset));
|
| __ bind(&leave);
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| }
|
|
|
|
|
| @@ -2422,8 +2492,8 @@
|
| Label leave;
|
| Load(args->at(0)); // Load the object.
|
| Load(args->at(1)); // Load the value.
|
| - __ pop(r0); // r0 contains value
|
| - __ pop(r1); // r1 contains object
|
| + frame_->Pop(r0); // r0 contains value
|
| + frame_->Pop(r1); // r1 contains object
|
| // if (object->IsSmi()) return object.
|
| __ tst(r1, Operand(kSmiTagMask));
|
| __ b(eq, &leave);
|
| @@ -2440,14 +2510,14 @@
|
| __ RecordWrite(r1, r2, r3);
|
| // Leave.
|
| __ bind(&leave);
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| }
|
|
|
|
|
| void CodeGenerator::GenerateIsSmi(ZoneList<Expression*>* args) {
|
| ASSERT(args->length() == 1);
|
| Load(args->at(0));
|
| - __ pop(r0);
|
| + frame_->Pop(r0);
|
| __ tst(r0, Operand(kSmiTagMask));
|
| cc_reg_ = eq;
|
| }
|
| @@ -2456,7 +2526,7 @@
|
| void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList<Expression*>* args) {
|
| ASSERT(args->length() == 1);
|
| Load(args->at(0));
|
| - __ pop(r0);
|
| + frame_->Pop(r0);
|
| __ tst(r0, Operand(kSmiTagMask | 0x80000000));
|
| cc_reg_ = eq;
|
| }
|
| @@ -2468,7 +2538,7 @@
|
| void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) {
|
| ASSERT(args->length() == 2);
|
| __ mov(r0, Operand(Factory::undefined_value()));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| }
|
|
|
|
|
| @@ -2479,7 +2549,7 @@
|
| // We need the CC bits to come out as not_equal in the case where the
|
| // object is a smi. This can't be done with the usual test opcode so
|
| // we use XOR to get the right CC bits.
|
| - __ pop(r0);
|
| + frame_->Pop(r0);
|
| __ and_(r1, r0, Operand(kSmiTagMask));
|
| __ eor(r1, r1, Operand(kSmiTagMask), SetCC);
|
| __ b(ne, &answer);
|
| @@ -2503,7 +2573,7 @@
|
| // Call the shared stub to get to the arguments.length.
|
| ArgumentsAccessStub stub(ArgumentsAccessStub::READ_LENGTH);
|
| __ CallStub(&stub);
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| }
|
|
|
|
|
| @@ -2513,13 +2583,13 @@
|
| // Satisfy contract with ArgumentsAccessStub:
|
| // Load the key into r1 and the formal parameters count into r0.
|
| Load(args->at(0));
|
| - __ pop(r1);
|
| + frame_->Pop(r1);
|
| __ mov(r0, Operand(Smi::FromInt(scope_->num_parameters())));
|
|
|
| // Call the shared stub to get to arguments[key].
|
| ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT);
|
| __ CallStub(&stub);
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| }
|
|
|
|
|
| @@ -2529,8 +2599,8 @@
|
| // Load the two objects into registers and perform the comparison.
|
| Load(args->at(0));
|
| Load(args->at(1));
|
| - __ pop(r0);
|
| - __ pop(r1);
|
| + frame_->Pop(r0);
|
| + frame_->Pop(r1);
|
| __ cmp(r0, Operand(r1));
|
| cc_reg_ = eq;
|
| }
|
| @@ -2549,25 +2619,25 @@
|
|
|
| // Call the C runtime function.
|
| __ CallRuntime(function, args->length());
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
|
|
| } else {
|
| // Prepare stack for calling JS runtime function.
|
| __ mov(r0, Operand(node->name()));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| // Push the builtins object found in the current global object.
|
| __ ldr(r1, GlobalObject());
|
| __ ldr(r0, FieldMemOperand(r1, GlobalObject::kBuiltinsOffset));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
|
|
| for (int i = 0; i < args->length(); i++) Load(args->at(i));
|
|
|
| // Call the JS runtime function.
|
| Handle<Code> stub = ComputeCallInitialize(args->length());
|
| __ Call(stub, RelocInfo::CODE_TARGET);
|
| - __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
|
| - __ pop();
|
| - __ push(r0);
|
| + __ ldr(cp, frame_->Context());
|
| + frame_->Pop();
|
| + frame_->Push(r0);
|
| }
|
| }
|
|
|
| @@ -2599,20 +2669,20 @@
|
| if (variable->is_global()) {
|
| LoadGlobal();
|
| __ mov(r0, Operand(variable->name()));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| __ mov(r0, Operand(1)); // not counting receiver
|
| __ InvokeBuiltin(Builtins::DELETE, CALL_JS);
|
|
|
| } else if (slot != NULL && slot->type() == Slot::LOOKUP) {
|
| // lookup the context holding the named variable
|
| - __ push(cp);
|
| + frame_->Push(cp);
|
| __ mov(r0, Operand(variable->name()));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| __ CallRuntime(Runtime::kLookupContext, 2);
|
| // r0: context
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| __ mov(r0, Operand(variable->name()));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| __ mov(r0, Operand(1)); // not counting receiver
|
| __ InvokeBuiltin(Builtins::DELETE, CALL_JS);
|
|
|
| @@ -2625,21 +2695,21 @@
|
| } else {
|
| // Default: Result of deleting expressions is true.
|
| Load(node->expression()); // may have side-effects
|
| - __ pop();
|
| + frame_->Pop();
|
| __ mov(r0, Operand(Factory::true_value()));
|
| }
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
|
|
| } else if (op == Token::TYPEOF) {
|
| // Special case for loading the typeof expression; see comment on
|
| // LoadTypeofExpression().
|
| LoadTypeofExpression(node->expression());
|
| __ CallRuntime(Runtime::kTypeof, 1);
|
| - __ push(r0); // r0 has result
|
| + frame_->Push(r0); // r0 has result
|
|
|
| } else {
|
| Load(node->expression());
|
| - __ pop(r0);
|
| + frame_->Pop(r0);
|
| switch (op) {
|
| case Token::NOT:
|
| case Token::DELETE:
|
| @@ -2660,7 +2730,7 @@
|
| __ tst(r0, Operand(kSmiTagMask));
|
| __ b(eq, &smi_label);
|
|
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| __ mov(r0, Operand(0)); // not counting receiver
|
| __ InvokeBuiltin(Builtins::BIT_NOT, CALL_JS);
|
|
|
| @@ -2683,7 +2753,7 @@
|
| Label continue_label;
|
| __ tst(r0, Operand(kSmiTagMask));
|
| __ b(eq, &continue_label);
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| __ mov(r0, Operand(0)); // not counting receiver
|
| __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_JS);
|
| __ bind(&continue_label);
|
| @@ -2692,7 +2762,7 @@
|
| default:
|
| UNREACHABLE();
|
| }
|
| - __ push(r0); // r0 has result
|
| + frame_->Push(r0); // r0 has result
|
| }
|
| }
|
|
|
| @@ -2709,13 +2779,13 @@
|
| // Postfix: Make room for the result.
|
| if (is_postfix) {
|
| __ mov(r0, Operand(0));
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| }
|
|
|
| { Reference target(this, node->expression());
|
| if (target.is_illegal()) return;
|
| target.GetValue(NOT_INSIDE_TYPEOF);
|
| - __ pop(r0);
|
| + frame_->Pop(r0);
|
|
|
| Label slow, exit;
|
|
|
| @@ -2727,7 +2797,9 @@
|
| __ b(ne, &slow);
|
|
|
| // Postfix: Store the old value as the result.
|
| - if (is_postfix) __ str(r0, MemOperand(sp, target.size() * kPointerSize));
|
| + if (is_postfix) {
|
| + __ str(r0, frame_->Element(target.size()));
|
| + }
|
|
|
| // Perform optimistic increment/decrement.
|
| if (is_increment) {
|
| @@ -2754,7 +2826,7 @@
|
| InvokeBuiltinStub stub(InvokeBuiltinStub::ToNumber, 2);
|
| __ CallStub(&stub);
|
| // Store to result (on the stack).
|
| - __ str(r0, MemOperand(sp, target.size() * kPointerSize));
|
| + __ str(r0, frame_->Element(target.size()));
|
| }
|
|
|
| // Compute the new value by calling the right JavaScript native.
|
| @@ -2768,12 +2840,12 @@
|
|
|
| // Store the new value in the target if not const.
|
| __ bind(&exit);
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| if (!is_const) target.SetValue(NOT_CONST_INIT);
|
| }
|
|
|
| // Postfix: Discard the new value and use the old.
|
| - if (is_postfix) __ pop(r0);
|
| + if (is_postfix) frame_->Pop(r0);
|
| }
|
|
|
|
|
| @@ -2814,8 +2886,8 @@
|
| } else {
|
| Label pop_and_continue, exit;
|
|
|
| - __ ldr(r0, MemOperand(sp, 0)); // dup the stack top
|
| - __ push(r0);
|
| + __ ldr(r0, frame_->Top()); // dup the stack top
|
| + frame_->Push(r0);
|
| // Avoid popping the result if it converts to 'false' using the
|
| // standard ToBoolean() conversion as described in ECMA-262,
|
| // section 9.2, page 30.
|
| @@ -2824,7 +2896,7 @@
|
|
|
| // Pop the result of evaluating the first part.
|
| __ bind(&pop_and_continue);
|
| - __ pop(r0);
|
| + frame_->Pop(r0);
|
|
|
| // Evaluate right side expression.
|
| __ bind(&is_true);
|
| @@ -2855,8 +2927,8 @@
|
| } else {
|
| Label pop_and_continue, exit;
|
|
|
| - __ ldr(r0, MemOperand(sp, 0));
|
| - __ push(r0);
|
| + __ ldr(r0, frame_->Top());
|
| + frame_->Push(r0);
|
| // Avoid popping the result if it converts to 'true' using the
|
| // standard ToBoolean() conversion as described in ECMA-262,
|
| // section 9.2, page 30.
|
| @@ -2865,7 +2937,7 @@
|
|
|
| // Pop the result of evaluating the first part.
|
| __ bind(&pop_and_continue);
|
| - __ pop(r0);
|
| + frame_->Pop(r0);
|
|
|
| // Evaluate right side expression.
|
| __ bind(&is_false);
|
| @@ -2894,14 +2966,14 @@
|
| Load(node->right());
|
| GenericBinaryOperation(node->op());
|
| }
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| }
|
| }
|
|
|
|
|
| void CodeGenerator::VisitThisFunction(ThisFunction* node) {
|
| - __ ldr(r0, FunctionOperand());
|
| - __ push(r0);
|
| + __ ldr(r0, frame_->Function());
|
| + frame_->Push(r0);
|
| }
|
|
|
|
|
| @@ -2913,55 +2985,47 @@
|
| Expression* right = node->right();
|
| Token::Value op = node->op();
|
|
|
| - // NOTE: To make null checks efficient, we check if either left or
|
| - // right is the literal 'null'. If so, we optimize the code by
|
| - // inlining a null check instead of calling the (very) general
|
| - // runtime routine for checking equality.
|
| -
|
| + // To make null checks efficient, we check if either left or right is the
|
| + // literal 'null'. If so, we optimize the code by inlining a null check
|
| + // instead of calling the (very) general runtime routine for checking
|
| + // equality.
|
| if (op == Token::EQ || op == Token::EQ_STRICT) {
|
| bool left_is_null =
|
| - left->AsLiteral() != NULL && left->AsLiteral()->IsNull();
|
| + left->AsLiteral() != NULL && left->AsLiteral()->IsNull();
|
| bool right_is_null =
|
| - right->AsLiteral() != NULL && right->AsLiteral()->IsNull();
|
| - // The 'null' value is only equal to 'null' or 'undefined'.
|
| + right->AsLiteral() != NULL && right->AsLiteral()->IsNull();
|
| + // The 'null' value can only be equal to 'null' or 'undefined'.
|
| if (left_is_null || right_is_null) {
|
| Load(left_is_null ? right : left);
|
| - Label exit, undetectable;
|
| - __ pop(r0);
|
| + frame_->Pop(r0);
|
| __ cmp(r0, Operand(Factory::null_value()));
|
|
|
| - // The 'null' value is only equal to 'undefined' if using
|
| - // non-strict comparisons.
|
| + // The 'null' value is only equal to 'undefined' if using non-strict
|
| + // comparisons.
|
| if (op != Token::EQ_STRICT) {
|
| - __ b(eq, &exit);
|
| + __ b(eq, true_target());
|
| +
|
| __ cmp(r0, Operand(Factory::undefined_value()));
|
| + __ b(eq, true_target());
|
|
|
| - // NOTE: it can be undetectable object.
|
| - __ b(eq, &exit);
|
| __ tst(r0, Operand(kSmiTagMask));
|
| + __ b(eq, false_target());
|
|
|
| - __ b(ne, &undetectable);
|
| - __ b(false_target());
|
| -
|
| - __ bind(&undetectable);
|
| - __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
|
| - __ ldrb(r2, FieldMemOperand(r1, Map::kBitFieldOffset));
|
| - __ and_(r2, r2, Operand(1 << Map::kIsUndetectable));
|
| - __ cmp(r2, Operand(1 << Map::kIsUndetectable));
|
| + // It can be an undetectable object.
|
| + __ ldr(r0, FieldMemOperand(r0, HeapObject::kMapOffset));
|
| + __ ldrb(r0, FieldMemOperand(r0, Map::kBitFieldOffset));
|
| + __ and_(r0, r0, Operand(1 << Map::kIsUndetectable));
|
| + __ cmp(r0, Operand(1 << Map::kIsUndetectable));
|
| }
|
|
|
| - __ bind(&exit);
|
| -
|
| cc_reg_ = eq;
|
| return;
|
| }
|
| }
|
|
|
| -
|
| - // NOTE: To make typeof testing for natives implemented in
|
| - // JavaScript really efficient, we generate special code for
|
| - // expressions of the form: 'typeof <expression> == <string>'.
|
| -
|
| + // To make typeof testing for natives implemented in JavaScript really
|
| + // efficient, we generate special code for expressions of the form:
|
| + // 'typeof <expression> == <string>'.
|
| UnaryOperation* operation = left->AsUnaryOperation();
|
| if ((op == Token::EQ || op == Token::EQ_STRICT) &&
|
| (operation != NULL && operation->op() == Token::TYPEOF) &&
|
| @@ -2971,7 +3035,7 @@
|
|
|
| // Load the operand, move it to register r1.
|
| LoadTypeofExpression(operation->expression());
|
| - __ pop(r1);
|
| + frame_->Pop(r1);
|
|
|
| if (check->Equals(Heap::number_symbol())) {
|
| __ tst(r1, Operand(kSmiTagMask));
|
| @@ -2986,7 +3050,7 @@
|
|
|
| __ ldr(r1, FieldMemOperand(r1, HeapObject::kMapOffset));
|
|
|
| - // NOTE: it might be an undetectable string object
|
| + // It can be an undetectable string object.
|
| __ ldrb(r2, FieldMemOperand(r1, Map::kBitFieldOffset));
|
| __ and_(r2, r2, Operand(1 << Map::kIsUndetectable));
|
| __ cmp(r2, Operand(1 << Map::kIsUndetectable));
|
| @@ -3009,7 +3073,7 @@
|
| __ tst(r1, Operand(kSmiTagMask));
|
| __ b(eq, false_target());
|
|
|
| - // NOTE: it can be undetectable object.
|
| + // It can be an undetectable object.
|
| __ ldr(r1, FieldMemOperand(r1, HeapObject::kMapOffset));
|
| __ ldrb(r2, FieldMemOperand(r1, Map::kBitFieldOffset));
|
| __ and_(r2, r2, Operand(1 << Map::kIsUndetectable));
|
| @@ -3033,7 +3097,7 @@
|
| __ cmp(r1, Operand(Factory::null_value()));
|
| __ b(eq, true_target());
|
|
|
| - // NOTE: it might be an undetectable object.
|
| + // It can be an undetectable object.
|
| __ ldrb(r1, FieldMemOperand(r2, Map::kBitFieldOffset));
|
| __ and_(r1, r1, Operand(1 << Map::kIsUndetectable));
|
| __ cmp(r1, Operand(1 << Map::kIsUndetectable));
|
| @@ -3046,8 +3110,8 @@
|
| cc_reg_ = le;
|
|
|
| } else {
|
| - // Uncommon case: Typeof testing against a string literal that
|
| - // is never returned from the typeof operator.
|
| + // Uncommon case: typeof testing against a string literal that is
|
| + // never returned from the typeof operator.
|
| __ b(false_target());
|
| }
|
| return;
|
| @@ -3083,7 +3147,7 @@
|
| case Token::IN:
|
| __ mov(r0, Operand(1)); // not counting receiver
|
| __ InvokeBuiltin(Builtins::IN, CALL_JS);
|
| - __ push(r0);
|
| + frame_->Push(r0);
|
| break;
|
|
|
| case Token::INSTANCEOF:
|
| @@ -3108,34 +3172,6 @@
|
| }
|
|
|
|
|
| -void CodeGenerator::EnterJSFrame() {
|
| -#if defined(DEBUG)
|
| - { Label done, fail;
|
| - __ tst(r1, Operand(kSmiTagMask));
|
| - __ b(eq, &fail);
|
| - __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
|
| - __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset));
|
| - __ cmp(r2, Operand(JS_FUNCTION_TYPE));
|
| - __ b(eq, &done);
|
| - __ bind(&fail);
|
| - __ stop("CodeGenerator::EnterJSFrame - r1 not a function");
|
| - __ bind(&done);
|
| - }
|
| -#endif // DEBUG
|
| -
|
| - __ stm(db_w, sp, r1.bit() | cp.bit() | fp.bit() | lr.bit());
|
| - __ add(fp, sp, Operand(2 * kPointerSize)); // Adjust FP to point to saved FP.
|
| -}
|
| -
|
| -
|
| -void CodeGenerator::ExitJSFrame() {
|
| - // Drop the execution stack down to the frame pointer and restore the caller
|
| - // frame pointer and return address.
|
| - __ mov(sp, fp);
|
| - __ ldm(ia_w, sp, fp.bit() | lr.bit());
|
| -}
|
| -
|
| -
|
| #undef __
|
| #define __ masm->
|
|
|
| @@ -3160,6 +3196,7 @@
|
| ASSERT(!is_illegal());
|
| ASSERT(!cgen_->has_cc());
|
| MacroAssembler* masm = cgen_->masm();
|
| + VirtualFrame* frame = cgen_->frame();
|
| Property* property = expression_->AsProperty();
|
| if (property != NULL) {
|
| __ RecordPosition(property->position());
|
| @@ -3193,7 +3230,7 @@
|
| } else {
|
| __ Call(ic, RelocInfo::CODE_TARGET);
|
| }
|
| - __ push(r0);
|
| + frame->Push(r0);
|
| break;
|
| }
|
|
|
| @@ -3202,10 +3239,16 @@
|
| // distinction between expressions in a typeof and not in a typeof.
|
| Comment cmnt(masm, "[ Load from keyed Property");
|
| ASSERT(property != NULL);
|
| - // TODO(1224671): Implement inline caching for keyed loads as on ia32.
|
| - GetPropertyStub stub;
|
| - __ CallStub(&stub);
|
| - __ push(r0);
|
| + Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
|
| +
|
| + Variable* var = expression_->AsVariableProxy()->AsVariable();
|
| + if (var != NULL) {
|
| + ASSERT(var->is_global());
|
| + __ Call(ic, RelocInfo::CODE_TARGET_CONTEXT);
|
| + } else {
|
| + __ Call(ic, RelocInfo::CODE_TARGET);
|
| + }
|
| + frame->Push(r0);
|
| break;
|
| }
|
|
|
| @@ -3219,6 +3262,7 @@
|
| ASSERT(!is_illegal());
|
| ASSERT(!cgen_->has_cc());
|
| MacroAssembler* masm = cgen_->masm();
|
| + VirtualFrame* frame = cgen_->frame();
|
| Property* property = expression_->AsProperty();
|
| if (property != NULL) {
|
| __ RecordPosition(property->position());
|
| @@ -3233,9 +3277,9 @@
|
| ASSERT(slot->var()->mode() == Variable::DYNAMIC);
|
|
|
| // For now, just do a runtime call.
|
| - __ push(cp);
|
| + frame->Push(cp);
|
| __ mov(r0, Operand(slot->var()->name()));
|
| - __ push(r0);
|
| + frame->Push(r0);
|
|
|
| if (init_state == CONST_INIT) {
|
| // Same as the case for a normal store, but ignores attribute
|
| @@ -3259,7 +3303,7 @@
|
| }
|
| // Storing a variable must keep the (new) value on the expression
|
| // stack. This is necessary for compiling assignment expressions.
|
| - __ push(r0);
|
| + frame->Push(r0);
|
|
|
| } else {
|
| ASSERT(slot->var()->mode() != Variable::DYNAMIC);
|
| @@ -3285,9 +3329,9 @@
|
| // 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);
|
| + frame->Pop(r0);
|
| __ str(r0, cgen_->SlotOperand(slot, r2));
|
| - __ push(r0);
|
| + frame->Push(r0);
|
| if (slot->type() == Slot::CONTEXT) {
|
| // Skip write barrier if the written value is a smi.
|
| __ tst(r0, Operand(kSmiTagMask));
|
| @@ -3310,13 +3354,13 @@
|
| case NAMED: {
|
| Comment cmnt(masm, "[ Store to named Property");
|
| // Call the appropriate IC code.
|
| - __ pop(r0); // value
|
| + frame->Pop(r0); // value
|
| // Setup the name register.
|
| Handle<String> name(GetName());
|
| __ mov(r2, Operand(name));
|
| Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
|
| __ Call(ic, RelocInfo::CODE_TARGET);
|
| - __ push(r0);
|
| + frame->Push(r0);
|
| break;
|
| }
|
|
|
| @@ -3325,10 +3369,13 @@
|
| Property* property = expression_->AsProperty();
|
| ASSERT(property != NULL);
|
| __ RecordPosition(property->position());
|
| - __ pop(r0); // value
|
| - SetPropertyStub stub;
|
| - __ CallStub(&stub);
|
| - __ push(r0);
|
| +
|
| + // Call IC code.
|
| + Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
|
| + // TODO(1222589): Make the IC grab the values from the stack.
|
| + frame->Pop(r0); // value
|
| + __ Call(ic, RelocInfo::CODE_TARGET);
|
| + frame->Push(r0);
|
| break;
|
| }
|
|
|
| @@ -3813,8 +3860,16 @@
|
| // Restore the stack to the address of the ENTRY handler
|
| __ mov(sp, Operand(r3));
|
|
|
| - // restore parameter- and frame-pointer and pop state.
|
| - __ ldm(ia_w, sp, r3.bit() | pp.bit() | fp.bit());
|
| + // Stack layout at this point. See also PushTryHandler
|
| + // r3, sp -> next handler
|
| + // state (ENTRY)
|
| + // pp
|
| + // fp
|
| + // lr
|
| +
|
| + // Discard ENTRY state (r2 is not used), and restore parameter-
|
| + // and frame-pointer and pop state.
|
| + __ ldm(ia_w, sp, r2.bit() | r3.bit() | pp.bit() | fp.bit());
|
| // Before returning we restore the context from the frame pointer if not NULL.
|
| // The frame pointer is NULL in the exception handler of a JS entry frame.
|
| __ cmp(fp, Operand(0));
|
| @@ -4044,7 +4099,7 @@
|
| // sets it to 0 to signal the existence of the JSEntry frame.
|
| __ mov(ip, Operand(Top::pending_exception_address()));
|
| __ str(r0, MemOperand(ip));
|
| - __ mov(r0, Operand(Handle<Failure>(Failure::Exception())));
|
| + __ mov(r0, Operand(reinterpret_cast<int32_t>(Failure::Exception())));
|
| __ b(&exit);
|
|
|
| // Invoke: Link this frame into the handler chain.
|
| @@ -4229,7 +4284,10 @@
|
| // Slow-case: Non-function called.
|
| __ bind(&slow);
|
| __ mov(r0, Operand(argc_)); // Setup the number of arguments.
|
| - __ InvokeBuiltin(Builtins::CALL_NON_FUNCTION, JUMP_JS);
|
| + __ mov(r2, Operand(0));
|
| + __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION);
|
| + __ Jump(Handle<Code>(Builtins::builtin(Builtins::ArgumentsAdaptorTrampoline)),
|
| + RelocInfo::CODE_TARGET);
|
| }
|
|
|
|
|
|
|