Generalized the EvalCache into a CompilationCache and enabled it for scripts too. The current strategy is to retire all entries whenever a mark-sweep collection is started.
Fixed bug where switch statements containing only a default case would lead to an unbalanced stack (issue 69).
Fixed bug that made access to the function in a named function expression impossible in certain situations (issue 24).
Fixed even more build issues.
Optimized calling conventions on ARM. The conventions on ARM and IA-32 now match.
Removed static initializers for flags and counters.
Improved inline caching behavior for uncommon cases where lazily loading Date and RegExp code could force certain code paths go megamorphic.
Removed arguments adaption for builtins written in C++. This makes Array.prototype.push and Array.prototype.pop slightly faster.
git-svn-id: http://v8.googlecode.com/svn/trunk@329 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/src/codegen-arm.cc b/src/codegen-arm.cc
index fa58084..9a17746 100644
--- a/src/codegen-arm.cc
+++ b/src/codegen-arm.cc
@@ -37,34 +37,6 @@
namespace v8 { namespace internal {
-DEFINE_bool(trace, false, "trace function calls");
-DECLARE_bool(debug_info);
-DECLARE_bool(debug_code);
-
-#ifdef ENABLE_DISASSEMBLER
-DEFINE_bool(print_code, false, "print generated code");
-#endif
-
-#ifdef DEBUG
-DECLARE_bool(gc_greedy);
-DEFINE_bool(trace_codegen, false,
- "print name of functions for which code is generated");
-DEFINE_bool(print_builtin_code, false, "print generated code for builtins");
-DEFINE_bool(print_source, false, "pretty print source code");
-DEFINE_bool(print_builtin_source, false,
- "pretty print source code for builtins");
-DEFINE_bool(print_ast, false, "print source AST");
-DEFINE_bool(print_builtin_ast, false, "print source AST for builtins");
-DEFINE_bool(trace_calls, false, "trace calls");
-DEFINE_bool(trace_builtin_calls, false, "trace builtins calls");
-DEFINE_string(stop_at, "", "function name where to insert a breakpoint");
-#endif // DEBUG
-
-
-DEFINE_bool(check_stack, true,
- "check stack for overflow, interrupt, breakpoint");
-
-
class ArmCodeGenerator;
@@ -101,35 +73,46 @@
};
-// -----------------------------------------------------------------------------
+// -------------------------------------------------------------------------
// 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.
+
class CodeGenState BASE_EMBEDDED {
public:
enum AccessType {
UNDEFINED,
LOAD,
- LOAD_TYPEOF_EXPR,
- STORE,
- INIT_CONST
+ LOAD_TYPEOF_EXPR
};
- CodeGenState()
- : access_(UNDEFINED),
- ref_(NULL),
- true_target_(NULL),
- false_target_(NULL) {
- }
+ // Create an initial code generator state. Destroying the initial state
+ // leaves the code generator with a NULL state.
+ explicit CodeGenState(ArmCodeGenerator* owner);
- CodeGenState(AccessType access,
- Reference* ref,
+ // Create a code generator state based on a code generator's current
+ // state. The new state has its own access type and pair of branch
+ // labels, and no reference.
+ CodeGenState(ArmCodeGenerator* owner,
+ AccessType access,
Label* true_target,
- Label* false_target)
- : access_(access),
- ref_(ref),
- true_target_(true_target),
- false_target_(false_target) {
- }
+ Label* false_target);
+
+ // Create a code generator state based on a code generator's current
+ // state. The new state has an access type of LOAD, its own reference,
+ // and inherits the pair of branch labels of the current state.
+ CodeGenState(ArmCodeGenerator* owner, Reference* ref);
+
+ // Destroy a code generator state and restore the owning code generator's
+ // previous state.
+ ~CodeGenState();
AccessType access() const { return access_; }
Reference* ref() const { return ref_; }
@@ -137,10 +120,12 @@
Label* false_target() const { return false_target_; }
private:
+ ArmCodeGenerator* owner_;
AccessType access_;
Reference* ref_;
Label* true_target_;
Label* false_target_;
+ CodeGenState* previous_;
};
@@ -155,6 +140,9 @@
MacroAssembler* masm() { return masm_; }
+ CodeGenState* state() { return state_; }
+ void set_state(CodeGenState* state) { state_ = state; }
+
private:
// Assembler
MacroAssembler* masm_; // to generate code
@@ -181,7 +169,6 @@
// The following are used by class Reference.
void LoadReference(Reference* ref);
void UnloadReference(Reference* ref);
- friend class Reference;
// State
bool has_cc() const { return cc_reg_ != al; }
@@ -197,21 +184,34 @@
return ContextOperand(cp, Context::GLOBAL_INDEX);
}
- MemOperand ContextOperand(Register context, int index) const {
+ static MemOperand ContextOperand(Register context, int index) {
return MemOperand(context, Context::SlotOffset(index));
}
- MemOperand ParameterOperand(int index) const {
+ static MemOperand ParameterOperand(Scope* scope, int index) {
// index -2 corresponds to the activated closure, -1 corresponds
// to the receiver
- ASSERT(-2 <= index && index < scope_->num_parameters());
- int offset = JavaScriptFrameConstants::kParam0Offset - index * kPointerSize;
- return MemOperand(pp, offset);
+ ASSERT(-2 <= index && index < scope->num_parameters());
+ int offset = (1 + scope->num_parameters() - index) * kPointerSize;
+ return MemOperand(fp, offset);
}
- MemOperand FunctionOperand() const { return ParameterOperand(-2); }
+ MemOperand ParameterOperand(int index) const {
+ return ParameterOperand(scope_, index);
+ }
- MemOperand SlotOperand(Slot* slot, Register tmp);
+ MemOperand FunctionOperand() const {
+ return MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset);
+ }
+
+ static MemOperand SlotOperand(MacroAssembler* masm,
+ Scope* scope,
+ Slot* slot,
+ Register tmp);
+
+ MemOperand SlotOperand(Slot* slot, Register tmp) {
+ return SlotOperand(masm_, scope_, slot, tmp);
+ }
void LoadCondition(Expression* x, CodeGenState::AccessType access,
Label* true_target, Label* false_target, bool force_cc);
@@ -227,22 +227,53 @@
// through the context chain.
void LoadTypeofExpression(Expression* x);
- // References
- void AccessReference(Reference* ref, CodeGenState::AccessType access);
- void GetValue(Reference* ref) { AccessReference(ref, CodeGenState::LOAD); }
- void SetValue(Reference* ref) { AccessReference(ref, CodeGenState::STORE); }
- void InitConst(Reference* ref) {
- AccessReference(ref, CodeGenState::INIT_CONST);
+ // References
+
+ // Generate code to fetch the value of a reference. The reference is
+ // expected to be on top of the expression stack. It is left in place and
+ // its value is pushed on top of it.
+ void GetValue(Reference* ref) {
+ ASSERT(!has_cc());
+ ASSERT(!ref->is_illegal());
+ CodeGenState new_state(this, ref);
+ 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(masm_, scope_, 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(masm_, scope_, 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(MacroAssembler* masm,
+ Reference* ref,
+ Expression* key);
+
+
void ToBoolean(Label* true_target, Label* false_target);
-
- // Access property from the reference (must be at the TOS).
- void AccessReferenceProperty(Expression* key,
- CodeGenState::AccessType access);
-
void GenericBinaryOperation(Token::Value op);
void Comparison(Condition cc, bool strict = false);
@@ -271,19 +302,9 @@
void RecordStatementPosition(Node* node);
// Activation frames
- void EnterJSFrame(int argc); // preserves r1
- void ExitJSFrame(ExitJSFlag flag = RETURN); // preserves r0-r2
+ void EnterJSFrame();
+ void ExitJSFrame();
- virtual void GenerateShiftDownAndTailCall(ZoneList<Expression*>* args);
- virtual void GenerateSetThisFunction(ZoneList<Expression*>* args);
- virtual void GenerateGetThisFunction(ZoneList<Expression*>* args);
- virtual void GenerateSetThis(ZoneList<Expression*>* args);
- virtual void GenerateGetArgumentsLength(ZoneList<Expression*>* args);
- virtual void GenerateSetArgumentsLength(ZoneList<Expression*>* args);
- virtual void GenerateTailCallWithArguments(ZoneList<Expression*>* args);
- virtual void GenerateSetArgument(ZoneList<Expression*>* args);
- virtual void GenerateSquashFrame(ZoneList<Expression*>* args);
- virtual void GenerateExpandFrame(ZoneList<Expression*>* args);
virtual void GenerateIsSmi(ZoneList<Expression*>* args);
virtual void GenerateIsNonNegativeSmi(ZoneList<Expression*>* args);
virtual void GenerateIsArray(ZoneList<Expression*>* args);
@@ -297,9 +318,59 @@
virtual void GenerateFastCharCodeAt(ZoneList<Expression*>* args);
virtual void GenerateObjectEquals(ZoneList<Expression*>* args);
+
+ friend class Reference;
+ friend class Property;
+ friend class VariableProxy;
+ friend class Slot;
};
+// -------------------------------------------------------------------------
+// CodeGenState implementation.
+
+CodeGenState::CodeGenState(ArmCodeGenerator* owner)
+ : owner_(owner),
+ access_(UNDEFINED),
+ ref_(NULL),
+ true_target_(NULL),
+ false_target_(NULL),
+ previous_(NULL) {
+ owner_->set_state(this);
+}
+
+
+CodeGenState::CodeGenState(ArmCodeGenerator* owner,
+ AccessType access,
+ Label* true_target,
+ Label* false_target)
+ : owner_(owner),
+ access_(access),
+ ref_(NULL),
+ true_target_(true_target),
+ false_target_(false_target),
+ previous_(owner->state()) {
+ owner_->set_state(this);
+}
+
+
+CodeGenState::CodeGenState(ArmCodeGenerator* owner, Reference* ref)
+ : owner_(owner),
+ access_(LOAD),
+ ref_(ref),
+ true_target_(owner->state()->true_target_),
+ false_target_(owner->state()->false_target_),
+ previous_(owner->state()) {
+ owner_->set_state(this);
+}
+
+
+CodeGenState::~CodeGenState() {
+ ASSERT(owner_->state() == this);
+ owner_->set_state(previous_);
+}
+
+
// -----------------------------------------------------------------------------
// ArmCodeGenerator implementation
@@ -415,8 +486,7 @@
ZoneList<Statement*>* body = fun->body();
// Initialize state.
- { CodeGenState state;
- state_ = &state;
+ { CodeGenState state(this);
scope_ = scope;
cc_reg_ = al;
@@ -429,7 +499,7 @@
// cp: callee's context
{ Comment cmnt(masm_, "[ enter JS frame");
- EnterJSFrame(scope->num_parameters());
+ EnterJSFrame();
}
// tos: code slot
#ifdef DEBUG
@@ -487,9 +557,7 @@
Slot* slot = par->slot();
if (slot != NULL && slot->type() == Slot::CONTEXT) {
ASSERT(!scope->is_global_scope()); // no parameters in global scope
- int parameter_offset =
- JavaScriptFrameConstants::kParam0Offset - i * kPointerSize;
- __ ldr(r1, MemOperand(pp, parameter_offset));
+ __ ldr(r1, ParameterOperand(i));
// Loads r2 with context; used below in RecordWrite.
__ str(r1, SlotOperand(slot, r2));
// Load the offset into r3.
@@ -570,8 +638,6 @@
#endif
VisitStatements(body);
}
-
- state_ = NULL;
}
// exit
@@ -590,8 +656,13 @@
__ CallRuntime(Runtime::kTraceExit, 1);
}
+ // Tear down the frame which will restore the caller's frame pointer and the
+ // link register.
ExitJSFrame();
+ __ add(sp, sp, Operand((scope_->num_parameters() + 1) * kPointerSize));
+ __ mov(pc, lr);
+
// Code generation state must be reset.
scope_ = NULL;
ASSERT(!has_cc());
@@ -599,7 +670,10 @@
}
-MemOperand ArmCodeGenerator::SlotOperand(Slot* slot, Register tmp) {
+MemOperand ArmCodeGenerator::SlotOperand(MacroAssembler* masm,
+ Scope* scope,
+ Slot* slot,
+ Register tmp) {
// Currently, this assertion will fail if we try to assign to
// a constant variable that is constant because it is read-only
// (such as the variable referring to a named function expression).
@@ -612,11 +686,11 @@
int index = slot->index();
switch (slot->type()) {
case Slot::PARAMETER:
- return ParameterOperand(index);
+ return ParameterOperand(scope, index);
case Slot::LOCAL: {
ASSERT(0 <= index &&
- index < scope_->num_stack_slots() &&
+ index < scope->num_stack_slots() &&
index >= 0);
int local_offset = JavaScriptFrameConstants::kLocal0Offset -
index * kPointerSize;
@@ -627,15 +701,15 @@
// Follow the context chain if necessary.
ASSERT(!tmp.is(cp)); // do not overwrite context register
Register context = cp;
- int chain_length = scope_->ContextChainLength(slot->var()->scope());
+ int chain_length = scope->ContextChainLength(slot->var()->scope());
for (int i = chain_length; i-- > 0;) {
// Load the closure.
// (All contexts, even 'with' contexts, have a closure,
// and it is the same for all contexts inside a function.
// There is no need to go to the function context first.)
- __ ldr(tmp, ContextOperand(context, Context::CLOSURE_INDEX));
+ masm->ldr(tmp, ContextOperand(context, Context::CLOSURE_INDEX));
// Load the function context (which is the incoming, outer context).
- __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset));
+ masm->ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset));
context = tmp;
}
// We may have a 'with' context now. Get the function context.
@@ -645,7 +719,7 @@
// cause the function context of a function context is itself. Before
// deleting this mov we should try to create a counter-example first,
// though...)
- __ ldr(tmp, ContextOperand(context, Context::FCONTEXT_INDEX));
+ masm->ldr(tmp, ContextOperand(context, Context::FCONTEXT_INDEX));
return ContextOperand(tmp, index);
}
@@ -670,11 +744,9 @@
access == CodeGenState::LOAD_TYPEOF_EXPR);
ASSERT(!has_cc() && !is_referenced());
- CodeGenState* old_state = state_;
- CodeGenState new_state(access, NULL, true_target, false_target);
- state_ = &new_state;
- Visit(x);
- state_ = old_state;
+ { CodeGenState new_state(this, access, true_target, false_target);
+ Visit(x);
+ }
if (force_cc && !has_cc()) {
// Convert the TOS value to a boolean in the condition code register.
ToBoolean(true_target, false_target);
@@ -822,15 +894,117 @@
}
-void ArmCodeGenerator::AccessReference(Reference* ref,
- CodeGenState::AccessType access) {
- ASSERT(!has_cc());
- ASSERT(ref->type() != Reference::ILLEGAL);
- CodeGenState* old_state = state_;
- CodeGenState new_state(access, ref, true_target(), false_target());
- state_ = &new_state;
- Visit(ref->expression());
- state_ = old_state;
+void Property::GenerateStoreCode(MacroAssembler* masm,
+ Scope* scope,
+ Reference* ref,
+ InitState init_state) {
+ Comment cmnt(masm, "[ Store to Property");
+ masm->RecordPosition(position());
+ ArmCodeGenerator::SetReferenceProperty(masm, ref, key());
+}
+
+
+void VariableProxy::GenerateStoreCode(MacroAssembler* masm,
+ Scope* scope,
+ Reference* ref,
+ InitState init_state) {
+ Comment cmnt(masm, "[ Store to VariableProxy");
+ Variable* node = var();
+
+ Expression* expr = node->rewrite();
+ if (expr != NULL) {
+ expr->GenerateStoreCode(masm, scope, ref, init_state);
+ } else {
+ ASSERT(node->is_global());
+ if (node->AsProperty() != NULL) {
+ masm->RecordPosition(node->AsProperty()->position());
+ }
+ ArmCodeGenerator::SetReferenceProperty(masm, ref,
+ new Literal(node->name()));
+ }
+}
+
+
+void Slot::GenerateStoreCode(MacroAssembler* masm,
+ Scope* scope,
+ Reference* ref,
+ InitState init_state) {
+ Comment cmnt(masm, "[ Store to Slot");
+
+ if (type() == Slot::LOOKUP) {
+ ASSERT(var()->mode() == Variable::DYNAMIC);
+
+ // For now, just do a runtime call.
+ masm->push(cp);
+ masm->mov(r0, Operand(var()->name()));
+ masm->push(r0);
+
+ if (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.
+ masm->CallRuntime(Runtime::kInitializeConstContextSlot, 3);
+ } else {
+ masm->CallRuntime(Runtime::kStoreContextSlot, 3);
+ }
+ // Storing a variable must keep the (new) value on the expression
+ // stack. This is necessary for compiling assignment expressions.
+ masm->push(r0);
+
+ } else {
+ ASSERT(var()->mode() != Variable::DYNAMIC);
+
+ Label exit;
+ if (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");
+ masm->ldr(r2, ArmCodeGenerator::SlotOperand(masm, scope, this, r2));
+ masm->cmp(r2, Operand(Factory::the_hole_value()));
+ masm->b(ne, &exit);
+ }
+
+ // We must execute the store.
+ // r2 may be loaded with context; used below in RecordWrite.
+ // Storing a variable must keep the (new) value on the stack. This is
+ // necessary for compiling assignment expressions.
+ //
+ // Note: We will reach here even with var()->mode() == Variable::CONST
+ // because of const declarations which will initialize consts to 'the
+ // hole' value and by doing so, end up calling this code. r2 may be
+ // loaded with context; used below in RecordWrite.
+ masm->pop(r0);
+ masm->str(r0, ArmCodeGenerator::SlotOperand(masm, scope, this, r2));
+ masm->push(r0);
+
+ if (type() == Slot::CONTEXT) {
+ // Skip write barrier if the written value is a smi.
+ masm->tst(r0, Operand(kSmiTagMask));
+ masm->b(eq, &exit);
+ // r2 is loaded with context when calling SlotOperand above.
+ int offset = FixedArray::kHeaderSize + index() * kPointerSize;
+ masm->mov(r3, Operand(offset));
+ masm->RecordWrite(r2, r3, r1);
+ }
+ // 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) {
+ masm->bind(&exit);
+ }
+ }
}
@@ -1125,7 +1299,7 @@
__ push(r1);
__ push(r0);
__ mov(r0, Operand(1)); // set number of arguments
- __ InvokeBuiltin("ADD", 1, JUMP_JS);
+ __ InvokeBuiltin(Builtins::ADD, JUMP_JS);
// done
__ bind(&exit);
break;
@@ -1148,7 +1322,7 @@
__ push(r1);
__ push(r0);
__ mov(r0, Operand(1)); // set number of arguments
- __ InvokeBuiltin("SUB", 1, JUMP_JS);
+ __ InvokeBuiltin(Builtins::SUB, JUMP_JS);
// done
__ bind(&exit);
break;
@@ -1178,7 +1352,7 @@
__ push(r1);
__ push(r0);
__ mov(r0, Operand(1)); // set number of arguments
- __ InvokeBuiltin("MUL", 1, JUMP_JS);
+ __ InvokeBuiltin(Builtins::MUL, JUMP_JS);
// done
__ bind(&exit);
break;
@@ -1205,10 +1379,17 @@
__ push(r0);
__ mov(r0, Operand(1)); // 1 argument (not counting receiver).
switch (op_) {
- case Token::BIT_OR: __ InvokeBuiltin("BIT_OR", 1, JUMP_JS); break;
- case Token::BIT_AND: __ InvokeBuiltin("BIT_AND", 1, JUMP_JS); break;
- case Token::BIT_XOR: __ InvokeBuiltin("BIT_XOR", 1, JUMP_JS); break;
- default: UNREACHABLE();
+ case Token::BIT_OR:
+ __ InvokeBuiltin(Builtins::BIT_OR, JUMP_JS);
+ break;
+ case Token::BIT_AND:
+ __ InvokeBuiltin(Builtins::BIT_AND, JUMP_JS);
+ break;
+ case Token::BIT_XOR:
+ __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_JS);
+ break;
+ default:
+ UNREACHABLE();
}
__ bind(&exit);
break;
@@ -1266,9 +1447,9 @@
__ push(r0);
__ mov(r0, Operand(1)); // 1 argument (not counting receiver).
switch (op_) {
- case Token::SAR: __ InvokeBuiltin("SAR", 1, JUMP_JS); break;
- case Token::SHR: __ InvokeBuiltin("SHR", 1, JUMP_JS); break;
- case Token::SHL: __ InvokeBuiltin("SHL", 1, JUMP_JS); break;
+ case Token::SAR: __ InvokeBuiltin(Builtins::SAR, JUMP_JS); break;
+ case Token::SHR: __ InvokeBuiltin(Builtins::SHR, JUMP_JS); break;
+ case Token::SHL: __ InvokeBuiltin(Builtins::SHL, JUMP_JS); break;
default: UNREACHABLE();
}
__ bind(&exit);
@@ -1324,7 +1505,7 @@
__ bind(&slow);
__ push(r0);
__ mov(r0, Operand(0)); // set number of arguments
- __ InvokeBuiltin("UNARY_MINUS", 0, JUMP_JS);
+ __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_JS);
__ bind(&done);
masm->StubReturn(1);
@@ -1360,45 +1541,15 @@
__ push(r0);
__ mov(r0, Operand(0)); // set number of arguments
switch (kind_) {
- case ToNumber: __ InvokeBuiltin("TO_NUMBER", 0, JUMP_JS); break;
- case Inc: __ InvokeBuiltin("INC", 0, JUMP_JS); break;
- case Dec: __ InvokeBuiltin("DEC", 0, JUMP_JS); break;
+ case ToNumber: __ InvokeBuiltin(Builtins::TO_NUMBER, JUMP_JS); break;
+ case Inc: __ InvokeBuiltin(Builtins::INC, JUMP_JS); break;
+ case Dec: __ InvokeBuiltin(Builtins::DEC, JUMP_JS); break;
default: UNREACHABLE();
}
masm->StubReturn(argc_);
}
-class JSExitStub : public CodeStub {
- public:
- enum Kind { Inc, Dec, ToNumber };
-
- explicit JSExitStub(ExitJSFlag flag) : flag_(flag) { }
-
- private:
- ExitJSFlag flag_;
-
- Major MajorKey() { return JSExit; }
- int MinorKey() { return static_cast<int>(flag_); }
- void Generate(MacroAssembler* masm);
-
- const char* GetName() { return "JSExitStub"; }
-
-#ifdef DEBUG
- void Print() {
- PrintF("JSExitStub flag %d)\n", static_cast<int>(flag_));
- }
-#endif
-};
-
-
-void JSExitStub::Generate(MacroAssembler* masm) {
- __ ExitJSFrame(flag_);
- masm->StubReturn(1);
-}
-
-
-
void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
// r0 holds exception
ASSERT(StackHandlerConstants::kSize == 6 * kPointerSize); // adjust this code
@@ -1545,7 +1696,7 @@
__ mov(r3, Operand(Top::context_address()));
__ ldr(cp, MemOperand(r3));
__ mov(sp, Operand(fp)); // respect ABI stack constraint
- __ ldm(ia, sp, pp.bit() | fp.bit() | sp.bit() | pc.bit());
+ __ ldm(ia, sp, fp.bit() | sp.bit() | pc.bit());
// check if we should retry or throw exception
Label retry;
@@ -1599,13 +1750,13 @@
// ip = sp + kPointerSize*args_len;
__ add(ip, sp, Operand(r0, LSL, kPointerSizeLog2));
- // all JS callee-saved are saved and traversed by GC; push in reverse order:
- // JS callee-saved, caller_pp, caller_fp, sp_on_exit (ip==pp), caller_pc
- __ stm(db_w, sp, pp.bit() | fp.bit() | ip.bit() | lr.bit());
+ // push in reverse order:
+ // caller_fp, sp_on_exit, caller_pc
+ __ stm(db_w, sp, fp.bit() | ip.bit() | lr.bit());
__ mov(fp, Operand(sp)); // setup new frame pointer
// Store the current context in top.
- __ mov(ip, Operand(Top::context_address()));
+ __ mov(ip, Operand(ExternalReference(Top::k_context_address)));
__ str(cp, MemOperand(ip));
// remember top frame
@@ -1682,33 +1833,40 @@
// Called from C, so do not pop argc and args on exit (preserve sp)
// No need to save register-passed args
// Save callee-saved registers (incl. cp, pp, and fp), sp, and lr
- __ mov(ip, Operand(sp));
- __ stm(db_w, sp, kCalleeSaved | ip.bit() | lr.bit());
-
- // Setup frame pointer
- __ mov(fp, Operand(sp));
-
- // Add constructor mark.
- __ mov(ip, Operand(is_construct ? 1 : 0));
- __ push(ip);
-
- // Move arguments into registers expected by Builtins::JSEntryTrampoline
- // preserve r0-r3, set r4, r5-r7 may be clobbered
+ __ stm(db_w, sp, kCalleeSaved | lr.bit());
// Get address of argv, see stm above.
- __ add(r4, sp, Operand((kNumCalleeSaved + 3)*kPointerSize));
+ // r0: code entry
+ // r1: function
+ // r2: receiver
+ // r3: argc
+ __ add(r4, sp, Operand((kNumCalleeSaved + 1)*kPointerSize));
__ ldr(r4, MemOperand(r4)); // argv
- // Save copies of the top frame descriptors on the stack.
- __ mov(ip, Operand(ExternalReference(Top::k_c_entry_fp_address)));
- __ ldr(r6, MemOperand(ip));
- __ stm(db_w, sp, r6.bit());
+ // Push a frame with special values setup to mark it as an entry frame.
+ // r0: code entry
+ // r1: function
+ // r2: receiver
+ // r3: argc
+ // r4: argv
+ int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY;
+ __ mov(r8, Operand(-1)); // Push a bad frame pointer to fail if it is used.
+ __ mov(r7, Operand(~ArgumentsAdaptorFrame::SENTINEL));
+ __ mov(r6, Operand(Smi::FromInt(marker)));
+ __ mov(r5, Operand(ExternalReference(Top::k_c_entry_fp_address)));
+ __ ldr(r5, MemOperand(r5));
+ __ stm(db_w, sp, r5.bit() | r6.bit() | r7.bit() | r8.bit());
+
+ // Setup frame pointer for the frame to be pushed.
+ __ add(fp, sp, Operand(-EntryFrameConstants::kCallerFPOffset));
// Call a faked try-block that does the invoke.
__ bl(&invoke);
// Caught exception: Store result (exception) in the pending
// exception field in the JSEnv and return a failure sentinel.
+ // Coming in here the fp will be invalid because the PushTryHandler below
+ // 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())));
@@ -1716,7 +1874,7 @@
// Invoke: Link this frame into the handler chain.
__ bind(&invoke);
- // Must preserve r0-r3, r5-r7 are available.
+ // Must preserve r0-r4, r5-r7 are available.
__ PushTryHandler(IN_JS_ENTRY, JS_ENTRY_HANDLER);
// If an exception not caught by another handler occurs, this handler returns
// control to the code after the bl(&invoke) above, which restores all
@@ -1764,18 +1922,18 @@
__ bind(&exit); // r0 holds result
// Restore the top frame descriptors from the stack.
- __ ldm(ia_w, sp, r3.bit());
+ __ pop(r3);
__ mov(ip, Operand(ExternalReference(Top::k_c_entry_fp_address)));
__ str(r3, MemOperand(ip));
- // Remove constructor mark.
- __ pop();
+ // Reset the stack to the callee saved registers.
+ __ add(sp, sp, Operand(-EntryFrameConstants::kCallerFPOffset));
- // Restore callee-saved registers, sp, and return.
+ // Restore callee-saved registers and return.
#ifdef DEBUG
if (FLAG_debug_code) __ mov(lr, Operand(pc));
#endif
- __ ldm(ia, sp, kCalleeSaved | sp.bit() | pc.bit());
+ __ ldm(ia_w, sp, kCalleeSaved | pc.bit());
}
@@ -1802,31 +1960,82 @@
void ArgumentsAccessStub::Generate(MacroAssembler* masm) {
- if (is_length_) {
- __ ldr(r0, MemOperand(fp, JavaScriptFrameConstants::kArgsLengthOffset));
- __ mov(r0, Operand(r0, LSL, kSmiTagSize));
- __ Ret();
- } else {
- // Check that the key is a smi.
- Label slow;
- __ tst(r0, Operand(kSmiTagMask));
+ // ----------- S t a t e -------------
+ // -- r0: formal number of parameters for the calling function
+ // -- r1: key (if value access)
+ // -- lr: return address
+ // -----------------------------------
+
+ // Check that the key is a smi for non-length accesses.
+ Label slow;
+ if (!is_length_) {
+ __ tst(r1, Operand(kSmiTagMask));
__ b(ne, &slow);
+ }
- // Get the actual number of arguments passed and do bounds
- // check. Use unsigned comparison to get negative check for free.
- __ ldr(r1, MemOperand(fp, JavaScriptFrameConstants::kArgsLengthOffset));
- __ cmp(r0, Operand(r1, LSL, kSmiTagSize));
- __ b(hs, &slow);
+ // Check if the calling frame is an arguments adaptor frame.
+ // r0: formal number of parameters
+ // r1: key (if access)
+ Label adaptor;
+ __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
+ __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
+ __ cmp(r3, Operand(ArgumentsAdaptorFrame::SENTINEL));
+ __ b(eq, &adaptor);
- // Load the argument directly from the stack and return.
- __ sub(r1, pp, Operand(r0, LSL, kPointerSizeLog2 - kSmiTagSize));
- __ ldr(r0, MemOperand(r1, JavaScriptFrameConstants::kParam0Offset));
- __ Ret();
+ static const int kParamDisplacement =
+ StandardFrameConstants::kCallerSPOffset - kPointerSize;
- // Slow-case: Handle non-smi or out-of-bounds access to arguments
- // by calling the runtime system.
+ if (is_length_) {
+ // Nothing to do: the formal length of parameters has been passed in r0
+ // by the calling function.
+ } else {
+ // Check index against formal parameter count. Use unsigned comparison to
+ // get the negative check for free.
+ // r0: formal number of parameters
+ // r1: index
+ __ cmp(r1, r0);
+ __ b(cs, &slow);
+
+ // Read the argument from the current frame.
+ __ sub(r3, r0, r1);
+ __ add(r3, fp, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize));
+ __ ldr(r0, MemOperand(r3, kParamDisplacement));
+ }
+
+ // Return to the calling function.
+ __ mov(pc, lr);
+
+ // An arguments adaptor frame is present. Find the length or the actual
+ // argument in the calling frame.
+ // r0: formal number of parameters
+ // r1: key
+ // r2: adaptor frame pointer
+ __ bind(&adaptor);
+ // Read the arguments length from the adaptor frame. This is the result if
+ // only accessing the length, otherwise it is used in accessing the value
+ __ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
+
+ if (!is_length_) {
+ // Check index against actual arguments count. Use unsigned comparison to
+ // get the negative check for free.
+ // r0: actual number of parameter
+ // r1: index
+ // r2: adaptor frame point
+ __ cmp(r1, r0);
+ __ b(cs, &slow);
+
+ // Read the argument from the adaptor frame.
+ __ sub(r3, r0, r1);
+ __ add(r3, r2, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize));
+ __ ldr(r0, MemOperand(r3, kParamDisplacement));
+ }
+
+ // Return to the calling function.
+ __ mov(pc, lr);
+
+ if (!is_length_) {
__ bind(&slow);
- __ push(r0);
+ __ push(r1);
__ TailCallRuntime(ExternalReference(Runtime::kGetArgumentsProperty), 1);
}
}
@@ -1836,42 +2045,29 @@
#define __ masm_->
-void ArmCodeGenerator::AccessReferenceProperty(
- Expression* key,
- CodeGenState::AccessType access) {
+void ArmCodeGenerator::GetReferenceProperty(Expression* key) {
+ ASSERT(!ref()->is_illegal());
Reference::Type type = ref()->type();
- ASSERT(type != Reference::ILLEGAL);
- // TODO(1241834): Make sure that this is sufficient. If there is a chance
- // that reference errors can be thrown below, we must distinguish
- // between the 2 kinds of loads (typeof expression loads must not
- // throw a reference error).
- bool is_load = (access == CodeGenState::LOAD ||
- access == CodeGenState::LOAD_TYPEOF_EXPR);
-
+ // TODO(1241834): Make sure that this it is safe to ignore the distinction
+ // between access types LOAD and LOAD_TYPEOF_EXPR. If there is a chance
+ // that reference errors can be thrown below, we must distinguish between
+ // the two kinds of loads (typeof expression loads must not throw a
+ // reference error).
if (type == Reference::NAMED) {
// Compute the name of the property.
Literal* literal = key->AsLiteral();
Handle<String> name(String::cast(*literal->handle()));
// Call the appropriate IC code.
- if (is_load) {
- // Setup the name register.
- __ mov(r2, Operand(name));
- Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
- Variable* var = ref()->expression()->AsVariableProxy()->AsVariable();
- if (var != NULL) {
- ASSERT(var->is_global());
- __ Call(ic, code_target_context);
- } else {
- __ Call(ic, code_target);
- }
-
+ // Setup the name register.
+ __ mov(r2, Operand(name));
+ Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
+ Variable* var = ref()->expression()->AsVariableProxy()->AsVariable();
+ if (var != NULL) {
+ ASSERT(var->is_global());
+ __ Call(ic, code_target_context);
} else {
- __ pop(r0); // value
- // Setup the name register.
- __ mov(r2, Operand(name));
- Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
__ Call(ic, code_target);
}
@@ -1879,21 +2075,44 @@
// Access keyed property.
ASSERT(type == Reference::KEYED);
- if (is_load) {
- // TODO(1224671): Implement inline caching for keyed loads as on ia32.
- GetPropertyStub stub;
- __ CallStub(&stub);
-
- } else {
- __ pop(r0); // value
- SetPropertyStub stub;
- __ CallStub(&stub);
- }
+ // TODO(1224671): Implement inline caching for keyed loads as on ia32.
+ GetPropertyStub stub;
+ __ CallStub(&stub);
}
__ push(r0);
}
+void ArmCodeGenerator::SetReferenceProperty(MacroAssembler* masm,
+ Reference* ref,
+ Expression* key) {
+ ASSERT(!ref->is_illegal());
+ Reference::Type type = ref->type();
+
+ if (type == Reference::NAMED) {
+ // Compute the name of the property.
+ Literal* literal = key->AsLiteral();
+ Handle<String> name(String::cast(*literal->handle()));
+
+ // Call the appropriate IC code.
+ masm->pop(r0); // value
+ // Setup the name register.
+ masm->mov(r2, Operand(name));
+ Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
+ masm->Call(ic, code_target);
+
+ } else {
+ // Access keyed property.
+ ASSERT(type == Reference::KEYED);
+
+ masm->pop(r0); // value
+ SetPropertyStub stub;
+ masm->CallStub(&stub);
+ }
+ masm->push(r0);
+}
+
+
void ArmCodeGenerator::GenericBinaryOperation(Token::Value op) {
// sp[0] : y
// sp[1] : x
@@ -1919,13 +2138,13 @@
case Token::DIV: {
__ mov(r0, Operand(1));
- __ InvokeBuiltin("DIV", 1, CALL_JS);
+ __ InvokeBuiltin(Builtins::DIV, CALL_JS);
break;
}
case Token::MOD: {
__ mov(r0, Operand(1));
- __ InvokeBuiltin("MOD", 1, CALL_JS);
+ __ InvokeBuiltin(Builtins::MOD, CALL_JS);
break;
}
@@ -2180,13 +2399,13 @@
__ push(r1);
// Figure out which native to call and setup the arguments.
- const char* native;
+ Builtins::JavaScript native;
int argc;
if (cc == eq) {
- native = strict ? "STRICT_EQUALS" : "EQUALS";
+ native = strict ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
argc = 1;
} else {
- native = "COMPARE";
+ native = Builtins::COMPARE;
int ncr; // NaN compare result
if (cc == lt || cc == le) {
ncr = GREATER;
@@ -2203,7 +2422,7 @@
// tagged as a small integer.
__ push(r0);
__ mov(r0, Operand(argc));
- __ InvokeBuiltin(native, argc, CALL_JS);
+ __ InvokeBuiltin(native, CALL_JS);
__ cmp(r0, Operand(0));
__ b(&exit);
@@ -2260,7 +2479,7 @@
// Slow-case: Non-function called.
masm->bind(&slow);
masm->mov(r0, Operand(argc_)); // Setup the number of arguments.
- masm->InvokeBuiltin("CALL_NON_FUNCTION", argc_, JUMP_JS);
+ masm->InvokeBuiltin(Builtins::CALL_NON_FUNCTION, JUMP_JS);
}
@@ -2542,19 +2761,14 @@
Comment cmnt(masm_, "[ case clause");
if (clause->is_default()) {
+ // Continue matching cases. The program will execute the default case's
+ // statements if it does not match any of the cases.
+ __ b(&next);
+
// Bind the default case label, so we can branch to it when we
// have compared against all other cases.
ASSERT(default_case.is_unused()); // at most one default clause
-
- // If the default case is the first (but not only) case, we have
- // to jump past it for now. Once we're done with the remaining
- // clauses, we'll branch back here. If it isn't the first case,
- // we jump past it by avoiding to chain it into the next chain.
- if (length > 1) {
- if (i == 0) __ b(&next);
- __ bind(&default_case);
- }
-
+ __ bind(&default_case);
} else {
__ bind(&next);
next.Unuse();
@@ -2563,11 +2777,16 @@
Load(clause->label());
Comparison(eq, true);
Branch(false, &next);
- // Entering the case statement -> remove the switch value from the stack
- __ pop(r0);
}
+ // Entering the case statement for the first time. Remove the switch value
+ // from the stack.
+ __ pop(r0);
+
// Generate code for the body.
+ // This is also the target for the fall through from the previous case's
+ // statements which has to skip over the matching code and the popping of
+ // the switch value.
__ bind(&fall_through);
fall_through.Unuse();
VisitStatements(clause->statements());
@@ -2575,10 +2794,14 @@
}
__ bind(&next);
- // Reached the end of the case statements -> remove the switch value
- // from the stack.
- __ pop(r0); // __ Pop(no_reg)
- if (default_case.is_bound()) __ b(&default_case);
+ // Reached the end of the case statements without matching any of the cases.
+ if (default_case.is_bound()) {
+ // A default case exists -> execute its statements.
+ __ b(&default_case);
+ } else {
+ // Remove the switch value from the stack.
+ __ pop(r0);
+ }
__ bind(&fall_through);
__ bind(node->break_target());
@@ -2700,7 +2923,7 @@
__ bind(&primitive);
__ push(r0);
__ mov(r0, Operand(0));
- __ InvokeBuiltin("TO_OBJECT", 0, CALL_JS);
+ __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS);
__ bind(&jsobject);
@@ -2794,7 +3017,7 @@
__ push(r0);
__ push(r3); // push entry
__ mov(r0, Operand(1));
- __ InvokeBuiltin("FILTER_KEY", 1, CALL_JS);
+ __ InvokeBuiltin(Builtins::FILTER_KEY, CALL_JS);
__ mov(r3, Operand(r0));
// If the property has been removed while iterating, we just skip it.
@@ -3116,6 +3339,7 @@
void ArmCodeGenerator::VisitSlot(Slot* node) {
+ ASSERT(access() != CodeGenState::UNDEFINED);
Comment cmnt(masm_, "[ Slot");
if (node->type() == Slot::LOOKUP) {
@@ -3126,161 +3350,54 @@
__ mov(r0, Operand(node->var()->name()));
__ push(r0);
- switch (access()) {
- case CodeGenState::UNDEFINED:
- UNREACHABLE();
- break;
-
- case CodeGenState::LOAD:
- __ CallRuntime(Runtime::kLoadContextSlot, 2);
- __ push(r0);
- break;
-
- case CodeGenState::LOAD_TYPEOF_EXPR:
- __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
- __ push(r0);
- break;
-
- case CodeGenState::STORE:
- // Storing a variable must keep the (new) value on the stack. This
- // is necessary for compiling assignment expressions.
- __ CallRuntime(Runtime::kStoreContextSlot, 3);
- __ push(r0);
- // result (TOS) is the value that was stored
- break;
-
- case CodeGenState::INIT_CONST:
- // Same as STORE but ignores attribute (e.g. READ_ONLY) of
- // context slot so that we can initialize const properties
- // (introduced via eval("const foo = (some expr);")). Also,
- // uses the current function context instead of the top
- // context.
- //
- // Note that we must declare the foo upon entry of eval(),
- // via a context slot declaration, but we cannot initialize
- // it at the same time, because the const declaration may
- // be at the end of the eval code (sigh...) and the const
- // variable may have been used before (where its value is
- // 'undefined'). Thus, we can only do the initialization
- // when we actually encounter the expression and when the
- // expression operands are defined and valid, and thus we
- // need the split into 2 operations: declaration of the
- // context slot followed by initialization.
- __ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
- __ push(r0);
- break;
+ if (access() == CodeGenState::LOAD) {
+ __ CallRuntime(Runtime::kLoadContextSlot, 2);
+ } else {
+ ASSERT(access() == CodeGenState::LOAD_TYPEOF_EXPR);
+ __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
}
+ __ push(r0);
} else {
- // Note: We would like to keep the assert below, but it fires because
- // of some nasty code in LoadTypeofExpression() which should be removed...
+ // Note: We would like to keep the assert below, but it fires because of
+ // some nasty code in LoadTypeofExpression() which should be removed...
// ASSERT(node->var()->mode() != Variable::DYNAMIC);
- switch (access()) {
- case CodeGenState::UNDEFINED:
- UNREACHABLE();
- break;
-
- case CodeGenState::LOAD: // fall through
- case CodeGenState::LOAD_TYPEOF_EXPR:
- // Special handling for locals allocated in registers.
- __ ldr(r0, SlotOperand(node, r2));
- __ push(r0);
- if (node->var()->mode() == Variable::CONST) {
- // Const slots may contain 'the hole' value (the constant hasn't
- // been initialized yet) which needs to be converted into the
- // 'undefined' value.
- Comment cmnt(masm_, "[ Unhole const");
- __ pop(r0);
- __ cmp(r0, Operand(Factory::the_hole_value()));
- __ mov(r0, Operand(Factory::undefined_value()), LeaveCC, eq);
- __ push(r0);
- }
- break;
-
- case CodeGenState::INIT_CONST: {
- ASSERT(node->var()->mode() == Variable::CONST);
- // Only the first const initialization must be executed (the slot
- // still contains 'the hole' value). When the assignment is executed,
- // the code is identical to a normal store (see below).
- { Comment cmnt(masm_, "[ Init const");
- Label L;
- __ ldr(r2, SlotOperand(node, r2));
- __ cmp(r2, Operand(Factory::the_hole_value()));
- __ b(ne, &L);
- // We must execute the store.
- // r2 may be loaded with context; used below in RecordWrite.
- __ ldr(r0, MemOperand(sp, 0));
- __ str(r0, SlotOperand(node, r2));
- if (node->type() == Slot::CONTEXT) {
- // Skip write barrier if the written value is a smi.
- Label exit;
- __ tst(r0, Operand(kSmiTagMask));
- __ b(eq, &exit);
- // r2 is loaded with context when calling SlotOperand above.
- int offset = FixedArray::kHeaderSize + node->index() * kPointerSize;
- __ mov(r3, Operand(offset));
- __ RecordWrite(r2, r3, r1);
- __ bind(&exit);
- }
- __ bind(&L);
- }
- break;
- }
-
- case CodeGenState::STORE: {
- // Storing a variable must keep the (new) value on the stack. This
- // is necessary for compiling assignment expressions.
- // Special handling for locals allocated in registers.
- //
- // Note: We will reach here even with node->var()->mode() ==
- // Variable::CONST because of const declarations which will
- // initialize consts to 'the hole' value and by doing so, end
- // up calling this code.
- // r2 may be loaded with context; used below in RecordWrite.
- __ pop(r0);
- __ str(r0, SlotOperand(node, r2));
- __ push(r0);
- if (node->type() == Slot::CONTEXT) {
- // Skip write barrier if the written value is a smi.
- Label exit;
- __ tst(r0, Operand(kSmiTagMask));
- __ b(eq, &exit);
- // r2 is loaded with context when calling SlotOperand above.
- int offset = FixedArray::kHeaderSize + node->index() * kPointerSize;
- __ mov(r3, Operand(offset));
- __ RecordWrite(r2, r3, r1);
- __ bind(&exit);
- }
- break;
- }
+ // Special handling for locals allocated in registers.
+ __ ldr(r0, SlotOperand(node, r2));
+ __ push(r0);
+ if (node->var()->mode() == Variable::CONST) {
+ // Const slots may contain 'the hole' value (the constant hasn't been
+ // initialized yet) which needs to be converted into the 'undefined'
+ // value.
+ Comment cmnt(masm_, "[ Unhole const");
+ __ pop(r0);
+ __ cmp(r0, Operand(Factory::the_hole_value()));
+ __ mov(r0, Operand(Factory::undefined_value()), LeaveCC, eq);
+ __ push(r0);
}
}
}
-void ArmCodeGenerator::VisitVariableProxy(VariableProxy* proxy_node) {
+void ArmCodeGenerator::VisitVariableProxy(VariableProxy* node) {
Comment cmnt(masm_, "[ VariableProxy");
- Variable* node = proxy_node->var();
+ Variable* var_node = node->var();
- Expression* x = node->rewrite();
- if (x != NULL) {
- Visit(x);
- return;
- }
-
- ASSERT(node->is_global());
- if (is_referenced()) {
- if (node->AsProperty() != NULL) {
- __ RecordPosition(node->AsProperty()->position());
- }
- AccessReferenceProperty(new Literal(node->name()), access());
-
+ Expression* expr = var_node->rewrite();
+ if (expr != NULL) {
+ Visit(expr);
} else {
- // All stores are through references.
- ASSERT(access() != CodeGenState::STORE);
- Reference property(this, proxy_node);
- GetValue(&property);
+ ASSERT(var_node->is_global());
+ if (is_referenced()) {
+ if (var_node->AsProperty() != NULL) {
+ __ RecordPosition(var_node->AsProperty()->position());
+ }
+ GetReferenceProperty(new Literal(var_node->name()));
+ } else {
+ Reference property(this, node);
+ GetValue(&property);
+ }
}
}
@@ -3298,7 +3415,7 @@
// Retrieve the literal array and check the allocated entry.
// Load the function of this activation.
- __ ldr(r1, MemOperand(pp, 0));
+ __ ldr(r1, FunctionOperand());
// Load the literals array of the function.
__ ldr(r1, FieldMemOperand(r1, JSFunction::kLiteralsOffset));
@@ -3371,7 +3488,7 @@
// Retrieve the literal array and check the allocated entry.
// Load the function of this activation.
- __ ldr(r1, MemOperand(pp, 0));
+ __ ldr(r1, FunctionOperand());
// Load the literals array of the function.
__ ldr(r1, FieldMemOperand(r1, JSFunction::kLiteralsOffset));
@@ -3442,12 +3559,9 @@
// Call runtime to create the array literal.
__ mov(r0, Operand(node->literals()));
__ push(r0);
- // TODO(1332579): The second argument to CreateArrayLiteral is
- // supposed to be the literals array of the function of this frame.
- // Until the new ARM calling convention is in place, that function
- // is not always available. Therefore, on ARM we pass in the hole
- // until the new calling convention is in place.
- __ mov(r0, Operand(Factory::the_hole_value()));
+ // Load the function of this frame.
+ __ ldr(r0, FunctionOperand());
+ __ ldr(r0, FieldMemOperand(r0, JSFunction::kLiteralsOffset));
__ push(r0);
__ CallRuntime(Runtime::kCreateArrayLiteral, 2);
@@ -3541,13 +3655,11 @@
void ArmCodeGenerator::VisitProperty(Property* node) {
Comment cmnt(masm_, "[ Property");
+
if (is_referenced()) {
__ RecordPosition(node->position());
- AccessReferenceProperty(node->key(), access());
-
+ GetReferenceProperty(node->key());
} else {
- // All stores are through references.
- ASSERT(access() != CodeGenState::STORE);
Reference property(this, node);
__ RecordPosition(node->position());
GetValue(&property);
@@ -3702,6 +3814,9 @@
// r0: the number of arguments.
__ mov(r0, Operand(args->length()));
+ // Load the function into r1 as per calling convention.
+ __ ldr(r1, MemOperand(sp, (args->length() + 1) * kPointerSize));
+
// Call the construct call builtin that handles allocation and
// constructor invocation.
__ RecordPosition(position);
@@ -3713,48 +3828,6 @@
}
-void ArmCodeGenerator::GenerateSetThisFunction(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
- __ ldr(r0, MemOperand(sp, 0));
- __ str(r0, MemOperand(pp, JavaScriptFrameConstants::kFunctionOffset));
-}
-
-
-void ArmCodeGenerator::GenerateGetThisFunction(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 0);
- __ ldr(r0, MemOperand(pp, JavaScriptFrameConstants::kFunctionOffset));
- __ push(r0);
-}
-
-
-void ArmCodeGenerator::GenerateSetThis(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
- __ ldr(r0, MemOperand(sp, 0));
- __ str(r0, MemOperand(pp, JavaScriptFrameConstants::kReceiverOffset));
-}
-
-
-void ArmCodeGenerator::GenerateSetArgumentsLength(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
- __ pop(r0);
- __ mov(r0, Operand(r0, LSR, kSmiTagSize));
- __ str(r0, MemOperand(fp, JavaScriptFrameConstants::kArgsLengthOffset));
- __ mov(r0, Operand(Smi::FromInt(0))); // return a meaningful value
- __ push(r0);
-}
-
-
-void ArmCodeGenerator::GenerateGetArgumentsLength(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- __ ldr(r0, MemOperand(fp, JavaScriptFrameConstants::kArgsLengthOffset));
- __ mov(r0, Operand(r0, LSL, kSmiTagSize));
- __ push(r0);
-}
-
-
void ArmCodeGenerator::GenerateValueOf(ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);
Label leave;
@@ -3803,135 +3876,6 @@
}
-void ArmCodeGenerator::GenerateTailCallWithArguments(
- ZoneList<Expression*>* args) {
- // r0 = number of arguments (smi)
- ASSERT(args->length() == 1);
- Load(args->at(0));
- __ pop(r0);
- __ mov(r0, Operand(r0, LSR, kSmiTagSize));
-
- // r1 = new function (previously written to stack)
- __ ldr(r1, MemOperand(pp, JavaScriptFrameConstants::kFunctionOffset));
-
- // Reset parameter pointer and frame pointer to previous frame
- ExitJSFrame(DO_NOT_RETURN);
-
- // Jump (tail-call) to the function in register r1.
- __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
- __ ldr(r1, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
- __ ldr(r1, FieldMemOperand(r1, SharedFunctionInfo::kCodeOffset));
- __ add(pc, r1, Operand(Code::kHeaderSize - kHeapObjectTag));
-}
-
-
-void ArmCodeGenerator::GenerateSetArgument(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 3);
- // r0 = args[i]; r1 = i
- Comment cmnt(masm_, "[ GenerateSetArgument");
- Load(args->at(1)); // args[i] (value)
- Load(args->at(0)); // i
- __ pop(r1); // i
- __ pop(r0); // value
-#if defined(DEBUG)
- { Label L;
- __ tst(r1, Operand(kSmiTagMask));
- __ b(eq, &L);
- __ stop("SMI expected");
- __ bind(&L);
- }
-#endif // defined(DEBUG)
- __ add(r2, pp, Operand(JavaScriptFrameConstants::kParam0Offset));
- __ str(r0,
- MemOperand(r2, r1, LSL, kPointerSizeLog2 - kSmiTagSize, NegOffset));
- __ push(r0);
-}
-
-
-void ArmCodeGenerator::GenerateSquashFrame(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 2);
- Load(args->at(0)); // old number of arguments
- Load(args->at(1)); // new number of arguments, r1 > r0
- __ pop(r0);
- __ mov(r0, Operand(r0, LSR, kSmiTagSize));
- __ pop(r1);
- __ mov(r1, Operand(r1, LSR, kSmiTagSize));
- // r1 = number of words to move stack.
- __ sub(r1, r1, Operand(r0));
- // r2 is source.
- __ add(r2, fp, Operand(StandardFrameConstants::kCallerPCOffset));
- // Move down frame pointer fp.
- __ add(fp, fp, Operand(r1, LSL, kPointerSizeLog2));
- // r1 is destination.
- __ add(r1, fp, Operand(StandardFrameConstants::kCallerPCOffset));
-
- Label move;
- __ bind(&move);
- __ ldr(r3, MemOperand(r2, -kPointerSize, PostIndex));
- __ str(r3, MemOperand(r1, -kPointerSize, PostIndex));
- __ cmp(r2, Operand(sp));
- __ b(ne, &move);
- __ ldr(r3, MemOperand(r2));
- __ str(r3, MemOperand(r1));
-
- // Move down stack pointer esp.
- __ mov(sp, Operand(r1));
- // Put something GC-able in r0.
- __ mov(r0, Operand(Smi::FromInt(0)));
- __ push(r0);
-}
-
-
-void ArmCodeGenerator::GenerateExpandFrame(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 2);
- Load(args->at(1));
- Load(args->at(0));
- __ pop(r0); // new number of arguments
- __ pop(r1); // old number of arguments, r1 > r0
- __ mov(r1, Operand(r1, LSR, kSmiTagSize));
-
- // r1 = number of words to move stack.
- __ sub(r1, r1, Operand(r0, LSR, kSmiTagSize));
- Label end_of_expand_frame;
- if (FLAG_check_stack) {
- Label not_too_big;
- __ sub(r2, sp, Operand(r1, LSL, kPointerSizeLog2));
- __ mov(ip, Operand(ExternalReference::address_of_stack_guard_limit()));
- __ ldr(ip, MemOperand(ip));
- __ cmp(r2, Operand(ip));
- __ b(gt, ¬_too_big);
- __ mov(r0, Operand(Factory::false_value()));
- __ b(&end_of_expand_frame);
- __ bind(¬_too_big);
- }
- // r3 is source.
- __ mov(r3, Operand(sp));
- // r0 is copy limit + 1 word
- __ add(r0, fp,
- Operand(StandardFrameConstants::kCallerPCOffset + kPointerSize));
- // Move up frame pointer fp.
- __ sub(fp, fp, Operand(r1, LSL, kPointerSizeLog2));
- // Move up stack pointer sp.
- __ sub(sp, sp, Operand(r1, LSL, kPointerSizeLog2));
- // r1 is destination (r1 = source - r1).
- __ mov(r2, Operand(0));
- __ sub(r2, r2, Operand(r1, LSL, kPointerSizeLog2));
- __ add(r1, r3, Operand(r2));
-
- Label move;
- __ bind(&move);
- __ ldr(r2, MemOperand(r3, kPointerSize, PostIndex));
- __ str(r2, MemOperand(r1, kPointerSize, PostIndex));
- __ cmp(r3, Operand(r0));
- __ b(ne, &move);
-
- // Put success value in top of stack
- __ mov(r0, Operand(Factory::true_value()));
- __ bind(&end_of_expand_frame);
- __ push(r0);
-}
-
-
void ArmCodeGenerator::GenerateIsSmi(ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);
Load(args->at(0));
@@ -3950,7 +3894,6 @@
}
-
// This should generate code that performs a charCodeAt() call or returns
// undefined in order to trigger the slow case, Runtime_StringCharCodeAt.
// It is not yet implemented on ARM, so it always goes to the slow case.
@@ -3961,11 +3904,23 @@
}
-
-// This is used in the implementation of apply on ia32 but it is not
-// used on ARM yet.
void ArmCodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
- __ stop("ArmCodeGenerator::GenerateIsArray");
+ ASSERT(args->length() == 1);
+ Load(args->at(0));
+ Label answer;
+ // 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);
+ __ and_(r1, r0, Operand(kSmiTagMask));
+ __ eor(r1, r1, Operand(kSmiTagMask), SetCC);
+ __ b(ne, &answer);
+ // It is a heap object - get the map.
+ __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
+ __ ldrb(r1, FieldMemOperand(r1, Map::kInstanceTypeOffset));
+ // Check if the object is a JS array or not.
+ __ cmp(r1, Operand(JS_ARRAY_TYPE));
+ __ bind(&answer);
cc_reg_ = eq;
}
@@ -3987,11 +3942,11 @@
void ArmCodeGenerator::GenerateArgumentsAccess(ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);
- // Load the key onto the stack and set register r1 to the formal
- // parameters count for the currently executing function.
+ // Satisfy contract with ArgumentsAccessStub:
+ // Load the key into r1 and the formal parameters count into r0.
Load(args->at(0));
- __ pop(r0);
- __ mov(r1, Operand(Smi::FromInt(scope_->num_parameters())));
+ __ pop(r1);
+ __ mov(r0, Operand(Smi::FromInt(scope_->num_parameters())));
// Call the shared stub to get to arguments[key].
ArgumentsAccessStub stub(false);
@@ -4013,48 +3968,8 @@
}
-void ArmCodeGenerator::GenerateShiftDownAndTailCall(
- ZoneList<Expression*>* args) {
- // r0 = number of arguments
- ASSERT(args->length() == 1);
- Load(args->at(0));
- __ pop(r0);
- __ mov(r0, Operand(r0, LSR, kSmiTagSize));
-
- // Get the 'this' function and exit the frame without returning.
- __ ldr(r1, MemOperand(pp, JavaScriptFrameConstants::kFunctionOffset));
- ExitJSFrame(DO_NOT_RETURN);
- // return address in lr
-
- // Move arguments one element down the stack.
- Label move;
- Label moved;
- __ sub(r2, r0, Operand(0), SetCC);
- __ b(eq, &moved);
- __ bind(&move);
- __ sub(ip, r2, Operand(1));
- __ ldr(r3, MemOperand(sp, ip, LSL, kPointerSizeLog2));
- __ str(r3, MemOperand(sp, r2, LSL, kPointerSizeLog2));
- __ sub(r2, r2, Operand(1), SetCC);
- __ b(ne, &move);
- __ bind(&moved);
-
- // Remove the TOS (copy of last argument)
- __ pop();
-
- // Jump (tail-call) to the function in register r1.
- __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
- __ ldr(r1, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
- __ ldr(r1, FieldMemOperand(r1, SharedFunctionInfo::kCodeOffset));
- __ add(pc, r1, Operand(Code::kHeaderSize - kHeapObjectTag));
-
- return;
-}
-
-
void ArmCodeGenerator::VisitCallRuntime(CallRuntime* node) {
- if (CheckForInlineRuntimeCall(node))
- return;
+ if (CheckForInlineRuntimeCall(node)) return;
ZoneList<Expression*>* args = node->arguments();
Comment cmnt(masm_, "[ CallRuntime");
@@ -4109,7 +4024,7 @@
Load(property->obj());
Load(property->key());
__ mov(r0, Operand(1)); // not counting receiver
- __ InvokeBuiltin("DELETE", 1, CALL_JS);
+ __ InvokeBuiltin(Builtins::DELETE, CALL_JS);
} else if (variable != NULL) {
Slot* slot = variable->slot();
@@ -4118,7 +4033,7 @@
__ mov(r0, Operand(variable->name()));
__ push(r0);
__ mov(r0, Operand(1)); // not counting receiver
- __ InvokeBuiltin("DELETE", 1, CALL_JS);
+ __ InvokeBuiltin(Builtins::DELETE, CALL_JS);
} else if (slot != NULL && slot->type() == Slot::LOOKUP) {
// lookup the context holding the named variable
@@ -4131,7 +4046,7 @@
__ mov(r0, Operand(variable->name()));
__ push(r0);
__ mov(r0, Operand(1)); // not counting receiver
- __ InvokeBuiltin("DELETE", 1, CALL_JS);
+ __ InvokeBuiltin(Builtins::DELETE, CALL_JS);
} else {
// Default: Result of deleting non-global, not dynamically
@@ -4179,7 +4094,7 @@
__ push(r0);
__ mov(r0, Operand(0)); // not counting receiver
- __ InvokeBuiltin("BIT_NOT", 0, CALL_JS);
+ __ InvokeBuiltin(Builtins::BIT_NOT, CALL_JS);
__ b(&continue_label);
__ bind(&smi_label);
@@ -4202,7 +4117,7 @@
__ b(eq, &continue_label);
__ push(r0);
__ mov(r0, Operand(0)); // not counting receiver
- __ InvokeBuiltin("TO_NUMBER", 0, CALL_JS);
+ __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_JS);
__ bind(&continue_label);
break;
}
@@ -4600,13 +4515,13 @@
case Token::IN:
__ mov(r0, Operand(1)); // not counting receiver
- __ InvokeBuiltin("IN", 1, CALL_JS);
+ __ InvokeBuiltin(Builtins::IN, CALL_JS);
__ push(r0);
break;
case Token::INSTANCEOF:
__ mov(r0, Operand(1)); // not counting receiver
- __ InvokeBuiltin("INSTANCE_OF", 1, CALL_JS);
+ __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_JS);
__ push(r0);
break;
@@ -4625,14 +4540,31 @@
}
-void ArmCodeGenerator::EnterJSFrame(int argc) {
- __ EnterJSFrame(argc);
+void ArmCodeGenerator::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("ArmCodeGenerator::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 ArmCodeGenerator::ExitJSFrame(ExitJSFlag flag) {
- JSExitStub stub(flag);
- __ CallJSExitStub(&stub);
+void ArmCodeGenerator::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());
}