Reduced code size by using shorter instruction encoding when possible.
Added a --help option to the shell sample and to the d8 shell.
Added visual studio project files for building the ARM simulator.
Fixed a number of ARM simulator issues.
Fixed bug in out-of-memory handling on ARM.
Implemented shell support for passing arguments to a script from the command line.
Fixed bug in date code that made certain date functions return -0 instead of 0 for dates before the epoch.
Restricted applications of eval so it can only be used in the context of the associated global object.
Treat byte-order marks as whitespace characters.
git-svn-id: http://v8.googlecode.com/svn/trunk@768 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/src/codegen-arm.cc b/src/codegen-arm.cc
index 4c2f465..e5120ae 100644
--- a/src/codegen-arm.cc
+++ b/src/codegen-arm.cc
@@ -35,6 +35,84 @@
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.
@@ -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);
}