Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(36)

Unified Diff: src/codegen-arm.cc

Issue 10829: Finish porting jump target changes to the ARM platform. The v8 test... (Closed) Base URL: http://v8.googlecode.com/svn/branches/experimental/toiger/
Patch Set: '' Created 12 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/codegen-arm.h ('k') | src/codegen-ia32.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/codegen-arm.cc
===================================================================
--- src/codegen-arm.cc (revision 737)
+++ src/codegen-arm.cc (working copy)
@@ -32,7 +32,6 @@
#include "debug.h"
#include "scopes.h"
#include "runtime.h"
-#include "virtual-frame-arm-inl.h"
namespace v8 { namespace internal {
@@ -101,8 +100,7 @@
ASSERT(scope_ == NULL);
scope_ = fun->scope();
ASSERT(frame_ == NULL);
- VirtualFrame virtual_frame(this);
- frame_ = &virtual_frame;
+ set_frame(new VirtualFrame(this));
cc_reg_ = al;
function_return_.set_code_generator(this);
{
@@ -126,21 +124,21 @@
#endif
// Allocate space for locals and initialize them.
- frame_->AllocateLocals();
+ frame_->AllocateStackSlots(scope_->num_stack_slots());
if (scope_->num_heap_slots() > 0) {
// Allocate local context.
// Get outer context and create a new context based on it.
__ ldr(r0, frame_->Function());
frame_->Push(r0);
- __ CallRuntime(Runtime::kNewContext, 1); // r0 holds the result
+ frame_->CallRuntime(Runtime::kNewContext, 1); // r0 holds the result
if (kDebug) {
- Label verified_true;
+ JumpTarget verified_true(this);
__ cmp(r0, Operand(cp));
- __ b(eq, &verified_true);
+ verified_true.Branch(eq);
__ stop("NewContext: r0 is expected to be the same as cp");
- __ bind(&verified_true);
+ verified_true.Bind();
}
// Update context local.
__ str(cp, frame_->Context());
@@ -166,7 +164,7 @@
Slot* slot = par->slot();
if (slot != NULL && slot->type() == Slot::CONTEXT) {
ASSERT(!scope_->is_global_scope()); // no parameters in global scope
- __ ldr(r1, frame_->Parameter(i));
+ __ ldr(r1, frame_->ParameterAt(i));
// Loads r2 with context; used below in RecordWrite.
__ str(r1, SlotOperand(slot, r2));
// Load the offset into r3.
@@ -193,14 +191,15 @@
const int kReceiverDisplacement = 2 + scope_->num_parameters();
__ add(r1, fp, Operand(kReceiverDisplacement * kPointerSize));
__ mov(r0, Operand(Smi::FromInt(scope_->num_parameters())));
+ frame_->Adjust(3);
__ stm(db_w, sp, r0.bit() | r1.bit() | r2.bit());
- __ CallStub(&stub);
+ frame_->CallStub(&stub, 3);
frame_->Push(r0);
arguments_ref.SetValue(NOT_CONST_INIT);
}
shadow_ref.SetValue(NOT_CONST_INIT);
}
- frame_->Pop(); // Value is no longer needed.
+ frame_->Drop(); // Value is no longer needed.
}
// Generate code to 'execute' declarations and initialize functions
@@ -218,7 +217,7 @@
}
if (FLAG_trace) {
- __ CallRuntime(Runtime::kTraceEnter, 0);
+ frame_->CallRuntime(Runtime::kTraceEnter, 0);
// Ignore the return value.
}
CheckStack();
@@ -233,7 +232,7 @@
bool should_trace =
is_builtin ? FLAG_trace_builtin_calls : FLAG_trace_calls;
if (should_trace) {
- __ CallRuntime(Runtime::kDebugTrace, 0);
+ frame_->CallRuntime(Runtime::kDebugTrace, 0);
// Ignore the return value.
}
#endif
@@ -241,28 +240,31 @@
}
}
- // exit
- // r0: result
- // sp: stack pointer
- // fp: frame pointer
- // pp: parameter pointer
- // cp: callee's context
- __ mov(r0, Operand(Factory::undefined_value()));
+ // Generate the return sequence if necessary.
+ if (frame_ != NULL || function_return_.is_linked()) {
+ // exit
+ // r0: result
+ // sp: stack pointer
+ // fp: frame pointer
+ // pp: parameter pointer
+ // cp: callee's context
+ __ mov(r0, Operand(Factory::undefined_value()));
- function_return_.Bind();
- if (FLAG_trace) {
- // Push the return value on the stack as the parameter.
- // Runtime::TraceExit returns the parameter as it is.
- frame_->Push(r0);
- __ CallRuntime(Runtime::kTraceExit, 1);
- }
+ function_return_.Bind();
+ if (FLAG_trace) {
+ // Push the return value on the stack as the parameter.
+ // Runtime::TraceExit returns the parameter as it is.
+ frame_->Push(r0);
+ frame_->CallRuntime(Runtime::kTraceExit, 1);
+ }
- // Tear down the frame which will restore the caller's frame pointer and the
- // link register.
- frame_->Exit();
+ // Tear down the frame which will restore the caller's frame pointer and
+ // the link register.
+ frame_->Exit();
- __ add(sp, sp, Operand((scope_->num_parameters() + 1) * kPointerSize));
- __ mov(pc, lr);
+ __ add(sp, sp, Operand((scope_->num_parameters() + 1) * kPointerSize));
+ __ mov(pc, lr);
+ }
// Code generation state must be reset.
scope_ = NULL;
@@ -285,10 +287,10 @@
int index = slot->index();
switch (slot->type()) {
case Slot::PARAMETER:
- return frame_->Parameter(index);
+ return frame_->ParameterAt(index);
case Slot::LOCAL:
- return frame_->Local(index);
+ return frame_->LocalAt(index);
case Slot::CONTEXT: {
// Follow the context chain if necessary.
@@ -339,18 +341,11 @@
{ CodeGenState new_state(this, typeof_state, true_target, false_target);
Visit(x);
}
- if (force_cc && !has_cc()) {
+ if (force_cc && frame_ != NULL && !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);
+ ASSERT(!force_cc || frame_ == NULL || has_cc());
}
@@ -360,45 +355,49 @@
LoadCondition(x, typeof_state, &true_target, &false_target, false);
if (has_cc()) {
- // convert cc_reg_ into a bool
- Label loaded, materialize_true;
- __ b(cc_reg_, &materialize_true);
+ // Convert cc_reg_ into a boolean value.
+ JumpTarget loaded(this);
+ JumpTarget materialize_true(this);
+ materialize_true.Branch(cc_reg_);
__ mov(r0, Operand(Factory::false_value()));
frame_->Push(r0);
- __ b(&loaded);
- __ bind(&materialize_true);
+ loaded.Jump();
+ materialize_true.Bind();
__ mov(r0, Operand(Factory::true_value()));
frame_->Push(r0);
- __ bind(&loaded);
+ loaded.Bind();
cc_reg_ = al;
}
if (true_target.is_linked() || false_target.is_linked()) {
- // we have at least one condition value
- // that has been "translated" into a branch,
- // thus it needs to be loaded explicitly again
- Label loaded;
- __ b(&loaded); // don't lose current TOS
+ // We have at least one condition value that has been "translated"
+ // into a branch, thus it needs to be loaded explicitly.
+ JumpTarget loaded(this);
+ if (frame_ != NULL) {
+ loaded.Jump(); // Don't lose the current TOS.
+ }
bool both = true_target.is_linked() && false_target.is_linked();
- // reincarnate "true", if necessary
+ // Load "true" if necessary.
if (true_target.is_linked()) {
true_target.Bind();
__ mov(r0, Operand(Factory::true_value()));
frame_->Push(r0);
}
- // if both "true" and "false" need to be reincarnated,
- // jump across code for "false"
- if (both)
- __ b(&loaded);
- // reincarnate "false", if necessary
+ // If both "true" and "false" need to be loaded jump across the code for
+ // "false".
+ if (both) {
+ loaded.Jump();
+ }
+ // Load "false" if necessary.
if (false_target.is_linked()) {
false_target.Bind();
__ mov(r0, Operand(Factory::false_value()));
frame_->Push(r0);
}
- // everything is loaded at this point
- __ bind(&loaded);
+ // A value is loaded on all paths reaching this point.
+ loaded.Bind();
}
+ ASSERT(frame_ != NULL);
ASSERT(!has_cc());
}
@@ -486,7 +485,7 @@
} else {
// Anything else is a runtime error.
Load(e);
- __ CallRuntime(Runtime::kThrowReferenceError, 1);
+ frame_->CallRuntime(Runtime::kThrowReferenceError, 1);
}
}
@@ -534,7 +533,7 @@
// Slow case: call the runtime.
frame_->Push(r0);
- __ CallRuntime(Runtime::kToBool, 1);
+ frame_->CallRuntime(Runtime::kToBool, 1);
// Convert the result (r0) to a condition code.
__ cmp(r0, Operand(Factory::false_value()));
@@ -639,26 +638,26 @@
frame_->Pop(r0); // r0 : y
frame_->Pop(r1); // r1 : x
GenericBinaryOpStub stub(op);
- __ CallStub(&stub);
+ frame_->CallStub(&stub, 0);
break;
}
case Token::DIV: {
__ mov(r0, Operand(1));
- __ InvokeBuiltin(Builtins::DIV, CALL_JS);
+ frame_->InvokeBuiltin(Builtins::DIV, CALL_JS, 2);
break;
}
case Token::MOD: {
__ mov(r0, Operand(1));
- __ InvokeBuiltin(Builtins::MOD, CALL_JS);
+ frame_->InvokeBuiltin(Builtins::MOD, CALL_JS, 2);
break;
}
case Token::COMMA:
frame_->Pop(r0);
// simply discard left value
- frame_->Pop();
+ frame_->Drop();
break;
default:
@@ -759,7 +758,7 @@
int int_value = Smi::cast(*value)->value();
- Label exit;
+ JumpTarget exit(this);
frame_->Pop(r0);
switch (op) {
@@ -876,7 +875,7 @@
break;
}
- __ bind(&exit);
+ exit.Bind();
}
@@ -888,7 +887,8 @@
// Strict only makes sense for equality comparisons.
ASSERT(!strict || cc == eq);
- Label exit, smi;
+ JumpTarget exit(this);
+ JumpTarget smi(this);
// Implement '>' and '<=' by reversal to obtain ECMA-262 conversion order.
if (cc == gt || cc == le) {
cc = ReverseCondition(cc);
@@ -900,17 +900,16 @@
}
__ orr(r2, r0, Operand(r1));
__ tst(r2, Operand(kSmiTagMask));
- __ b(eq, &smi);
+ smi.Branch(eq);
// Perform non-smi comparison by runtime call.
frame_->Push(r1);
// Figure out which native to call and setup the arguments.
Builtins::JavaScript native;
- int argc;
+ int arg_count = 1;
if (cc == eq) {
native = strict ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
- argc = 1;
} else {
native = Builtins::COMPARE;
int ncr; // NaN compare result
@@ -921,23 +920,23 @@
ncr = LESS;
}
frame_->Push(r0);
+ arg_count++;
__ 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.
frame_->Push(r0);
- __ mov(r0, Operand(argc));
- __ InvokeBuiltin(native, CALL_JS);
+ __ mov(r0, Operand(arg_count));
+ frame_->InvokeBuiltin(native, CALL_JS, arg_count + 1);
__ cmp(r0, Operand(0));
- __ b(&exit);
+ exit.Jump();
// test smi equality by pointer comparison.
- __ bind(&smi);
+ smi.Bind();
__ cmp(r1, Operand(r0));
- __ bind(&exit);
+ exit.Bind();
cc_reg_ = cc;
}
@@ -964,7 +963,8 @@
void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
int position) {
// Push the arguments ("left-to-right") on the stack.
- for (int i = 0; i < args->length(); i++) {
+ int arg_count = args->length();
+ for (int i = 0; i < arg_count; i++) {
Load(args->at(i));
}
@@ -972,12 +972,12 @@
__ RecordPosition(position);
// Use the shared code stub to call the function.
- CallFunctionStub call_function(args->length());
- __ CallStub(&call_function);
+ CallFunctionStub call_function(arg_count);
+ frame_->CallStub(&call_function, arg_count + 1);
// Restore context and pop function from the stack.
__ ldr(cp, frame_->Context());
- frame_->Pop(); // discard the TOS
+ frame_->Drop(); // discard the TOS
}
@@ -993,18 +993,27 @@
if (FLAG_check_stack) {
Comment cmnt(masm_, "[ check stack");
StackCheckStub stub;
- __ CallStub(&stub);
+ frame_->CallStub(&stub, 0);
}
}
+void CodeGenerator::VisitStatements(ZoneList<Statement*>* statements) {
+ for (int i = 0; frame_ != NULL && i < statements->length(); i++) {
+ Visit(statements->at(i));
+ }
+}
+
+
void CodeGenerator::VisitBlock(Block* node) {
Comment cmnt(masm_, "[ Block");
if (FLAG_debug_info) RecordStatementPosition(node);
node->set_break_stack_height(break_stack_height_);
node->break_target()->set_code_generator(this);
VisitStatements(node->statements());
- node->break_target()->Bind();
+ if (node->break_target()->is_linked()) {
+ node->break_target()->Bind();
+ }
}
@@ -1014,7 +1023,7 @@
frame_->Push(cp);
__ mov(r0, Operand(Smi::FromInt(is_eval() ? 1 : 0)));
frame_->Push(r0);
- __ CallRuntime(Runtime::kDeclareGlobals, 3);
+ frame_->CallRuntime(Runtime::kDeclareGlobals, 3);
// The result is discarded.
}
@@ -1054,7 +1063,7 @@
__ mov(r0, Operand(0)); // no initial value!
frame_->Push(r0);
}
- __ CallRuntime(Runtime::kDeclareContextSlot, 4);
+ frame_->CallRuntime(Runtime::kDeclareContextSlot, 4);
// Ignore the return value (declarations are statements).
return;
}
@@ -1079,7 +1088,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.
- frame_->Pop();
+ frame_->Drop();
}
}
@@ -1090,7 +1099,7 @@
Expression* expression = node->expression();
expression->MarkAsStatement();
Load(expression);
- frame_->Pop();
+ frame_->Drop();
}
@@ -1102,8 +1111,8 @@
void CodeGenerator::VisitIfStatement(IfStatement* node) {
Comment cmnt(masm_, "[ IfStatement");
- // Generate different code depending on which
- // parts of the if statement are present or not.
+ // Generate different code depending on which parts of the if statement
+ // are present or not.
bool has_then_stm = node->HasThenStatement();
bool has_else_stm = node->HasElseStatement();
@@ -1116,14 +1125,22 @@
JumpTarget else_(this);
// if (cond)
LoadCondition(node->condition(), NOT_INSIDE_TYPEOF, &then, &else_, true);
- Branch(false, &else_);
+ if (frame_ != NULL) {
+ Branch(false, &else_);
+ }
// then
- then.Bind();
- Visit(node->then_statement());
- exit.Jump();
+ if (frame_ != NULL || then.is_linked()) {
+ then.Bind();
+ Visit(node->then_statement());
+ }
+ if (frame_ != NULL) {
+ exit.Jump();
+ }
// else
- else_.Bind();
- Visit(node->else_statement());
+ if (else_.is_linked()) {
+ else_.Bind();
+ Visit(node->else_statement());
+ }
} else if (has_then_stm) {
Comment cmnt(masm_, "[ IfThen");
@@ -1131,10 +1148,14 @@
JumpTarget then(this);
// if (cond)
LoadCondition(node->condition(), NOT_INSIDE_TYPEOF, &then, &exit, true);
- Branch(false, &exit);
+ if (frame_ != NULL) {
+ Branch(false, &exit);
+ }
// then
- then.Bind();
- Visit(node->then_statement());
+ if (frame_ != NULL || then.is_linked()) {
+ then.Bind();
+ Visit(node->then_statement());
+ }
} else if (has_else_stm) {
Comment cmnt(masm_, "[ IfElse");
@@ -1142,25 +1163,33 @@
JumpTarget else_(this);
// if (!cond)
LoadCondition(node->condition(), NOT_INSIDE_TYPEOF, &exit, &else_, true);
- Branch(true, &exit);
+ if (frame_ != NULL) {
+ Branch(true, &exit);
+ }
// else
- else_.Bind();
- Visit(node->else_statement());
+ if (frame_ != NULL || else_.is_linked()) {
+ else_.Bind();
+ Visit(node->else_statement());
+ }
} else {
Comment cmnt(masm_, "[ If");
ASSERT(!has_then_stm && !has_else_stm);
// if (cond)
LoadCondition(node->condition(), NOT_INSIDE_TYPEOF, &exit, &exit, false);
- if (has_cc()) {
- cc_reg_ = al;
- } else {
- frame_->Pop();
+ if (frame_ != NULL) {
+ if (has_cc()) {
+ cc_reg_ = al;
+ } else {
+ frame_->Drop();
+ }
}
}
// end
- exit.Bind();
+ if (exit.is_linked()) {
+ exit.Bind();
+ }
}
@@ -1201,13 +1230,13 @@
Comment cmnt(masm_, "[ WithEnterStatement");
if (FLAG_debug_info) RecordStatementPosition(node);
Load(node->expression());
- __ CallRuntime(Runtime::kPushContext, 1);
+ frame_->CallRuntime(Runtime::kPushContext, 1);
if (kDebug) {
- Label verified_true;
+ JumpTarget verified_true(this);
__ cmp(r0, Operand(cp));
- __ b(eq, &verified_true);
+ verified_true.Branch(eq);
__ stop("PushContext: r0 is expected to be the same as cp");
- __ bind(&verified_true);
+ verified_true.Bind();
}
// Update context local.
__ str(cp, frame_->Context());
@@ -1245,16 +1274,16 @@
frame_->Pop(r0);
// Test for a Smi value in a HeapNumber.
- Label is_smi;
+ JumpTarget is_smi(this);
__ tst(r0, Operand(kSmiTagMask));
- __ b(eq, &is_smi);
+ is_smi.Branch(eq);
__ ldr(r1, MemOperand(r0, HeapObject::kMapOffset - kHeapObjectTag));
__ ldrb(r1, MemOperand(r1, Map::kInstanceTypeOffset - kHeapObjectTag));
__ cmp(r1, Operand(HEAP_NUMBER_TYPE));
fail_label->Branch(ne);
frame_->Push(r0);
- __ CallRuntime(Runtime::kNumberToSmi, 1);
- __ bind(&is_smi);
+ frame_->CallRuntime(Runtime::kNumberToSmi, 1);
+ is_smi.Bind();
if (min_index != 0) {
// Small positive numbers can be immediate operands.
@@ -1284,6 +1313,7 @@
// Table containing branch operations.
for (int i = 0; i < range; i++) {
case_targets[i]->Jump();
+ frame_ = new VirtualFrame(table_start.expected_frame());
}
GenerateFastCaseSwitchCases(node, case_labels, &table_start);
@@ -1302,61 +1332,80 @@
return;
}
- JumpTarget next(this);
- Label fall_through, default_case;
+ JumpTarget next_test(this);
+ JumpTarget fall_through(this);
+ JumpTarget default_entry(this);
+ JumpTarget default_exit(this);
ZoneList<CaseClause*>* cases = node->cases();
int length = cases->length();
+ CaseClause* default_clause = NULL;
for (int i = 0; i < length; i++) {
CaseClause* clause = cases->at(i);
+ if (clause->is_default()) {
+ // Remember the default clause and compile it at the end.
+ default_clause = clause;
+ continue;
+ }
- Comment cmnt(masm_, "[ case clause");
+ Comment cmnt(masm_, "[ Case clause");
+ // Compile the test.
+ next_test.Bind();
+ next_test.Unuse();
+ // Duplicate TOS.
+ __ ldr(r0, frame_->Top());
+ frame_->Push(r0);
+ Load(clause->label());
+ Comparison(eq, true);
+ Branch(false, &next_test);
- 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.
- next.Jump();
+ // Before entering the body from the test, remove the switch value from
+ // the stack.
+ frame_->Drop();
- // 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
- __ bind(&default_case);
+ // Label the body so that fall through is enabled.
+ if (i > 0 && cases->at(i - 1)->is_default()) {
+ default_exit.Bind();
} else {
- next.Bind();
- next.Unuse();
- __ ldr(r0, frame_->Top());
- frame_->Push(r0); // duplicate TOS
- Load(clause->label());
- Comparison(eq, true);
- Branch(false, &next);
+ fall_through.Bind();
+ fall_through.Unuse();
}
+ VisitStatements(clause->statements());
- // Entering the case statement for the first time. Remove the switch value
- // from the stack.
- frame_->Pop();
+ // If control flow can fall through from the body, jump to the next body
+ // or the end of the statement.
+ if (frame_ != NULL) {
+ if (i < length - 1 && cases->at(i + 1)->is_default()) {
+ default_entry.Jump();
+ } else {
+ fall_through.Jump();
+ }
+ }
+ }
- // 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());
- __ b(&fall_through);
+ // The final "test" removes the switch value.
+ next_test.Bind();
+ frame_->Drop();
+
+ // If there is a default clause, compile it.
+ if (default_clause != NULL) {
+ Comment cmnt(masm_, "[ Default clause");
+ default_entry.Bind();
+ VisitStatements(default_clause->statements());
+ // If control flow can fall out of the default and there is a case after
+ // it, jup to that case's body.
+ if (frame_ != NULL && default_exit.is_bound()) {
+ default_exit.Jump();
+ }
}
- next.Bind();
- // 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.
- frame_->Pop();
+ if (fall_through.is_linked()) {
+ fall_through.Bind();
}
- __ bind(&fall_through);
- node->break_target()->Bind();
+ if (node->break_target()->is_linked()) {
+ node->break_target()->Bind();
+ }
}
@@ -1367,7 +1416,8 @@
node->break_target()->set_code_generator(this);
node->continue_target()->set_code_generator(this);
- // simple condition analysis
+ // Simple condition analysis. ALWAYS_TRUE and ALWAYS_FALSE represent a
+ // known result for the test expression, with no side effects.
enum { ALWAYS_TRUE, ALWAYS_FALSE, DONT_KNOW } info = DONT_KNOW;
if (node->cond() == NULL) {
ASSERT(node->type() == LoopStatement::FOR_LOOP);
@@ -1383,55 +1433,153 @@
}
}
- JumpTarget loop(this);
- Label entry;
+ switch (node->type()) {
+ case LoopStatement::DO_LOOP: {
+ JumpTarget body(this);
+ // Label the body.
+ if (info == ALWAYS_TRUE) {
+ node->continue_target()->Bind();
+ } else if (info == ALWAYS_FALSE) {
+ // There is no need, we will never jump back.
+ } else {
+ ASSERT(info == DONT_KNOW);
+ body.Bind();
+ }
+ CheckStack(); // TODO(1222600): ignore if body contains calls.
+ Visit(node->body());
- // init
- if (node->init() != NULL) {
- ASSERT(node->type() == LoopStatement::FOR_LOOP);
- Visit(node->init());
- }
- if (node->type() != LoopStatement::DO_LOOP && info != ALWAYS_TRUE) {
- __ b(&entry);
- }
+ // Compile the "test".
+ if (info == ALWAYS_TRUE) {
+ if (frame_ != NULL) {
+ // If control can fall off the end of the body, jump back to the
+ // top.
+ node->continue_target()->Jump();
+ }
+ } else if (info == ALWAYS_FALSE) {
+ // If we have a continue in the body, we only have to bind its jump
+ // target.
+ if (node->continue_target()->is_linked()) {
+ node->continue_target()->Bind();
+ }
+ } else {
+ ASSERT(info == DONT_KNOW);
+ // We have to compile the test expression if it can be reached by
+ // control flow falling out of the body or via continue.
+ if (frame_ != NULL || node->continue_target()->is_linked()) {
+ node->continue_target()->Bind();
+ LoadCondition(node->cond(), NOT_INSIDE_TYPEOF,
+ &body, node->break_target(), true);
+ if (frame_ != NULL) {
+ // A NULL frame here indicates that control did not fall out of
+ // the test expression.
+ Branch(true, &body);
+ }
+ }
+ }
+ break;
+ }
- // body
- loop.Bind();
- Visit(node->body());
+ case LoopStatement::WHILE_LOOP: {
+ JumpTarget body(this);
+ // Generate the loop header.
+ if (info == ALWAYS_TRUE) {
+ // Merely label the body with the continue target.
+ node->continue_target()->Bind();
+ } else if (info == ALWAYS_FALSE) {
+ // There is no need to even compile the test or body.
+ break;
+ } else {
+ // Compile the test labeled with the continue target and label the
+ // body with the body target.
+ ASSERT(info == DONT_KNOW);
+ node->continue_target()->Bind();
+ LoadCondition(node->cond(), NOT_INSIDE_TYPEOF,
+ &body, node->break_target(), true);
+ if (frame_ != NULL) {
+ // A NULL frame indicates that control did not fall out of the
+ // test expression.
+ Branch(false, node->break_target());
+ }
+ if (frame_ != NULL || body.is_linked()) {
+ body.Bind();
+ }
+ }
+ if (frame_ != NULL) {
+ CheckStack(); // TODO(1222600): ignore if body contains calls.
+ Visit(node->body());
- // next
- node->continue_target()->Bind();
- if (node->next() != NULL) {
- // Record source position of the statement as this code which is after the
- // code for the body actually belongs to the loop statement and not the
- // body.
- if (FLAG_debug_info) __ RecordPosition(node->statement_pos());
- ASSERT(node->type() == LoopStatement::FOR_LOOP);
- Visit(node->next());
- }
+ // If control flow can fall out of the body, jump back to the top.
+ if (frame_ != NULL) {
+ node->continue_target()->Jump();
+ }
+ }
+ break;
+ }
- // cond
- __ bind(&entry);
- switch (info) {
- case ALWAYS_TRUE:
- CheckStack(); // TODO(1222600): ignore if body contains calls.
- loop.Jump();
+ case LoopStatement::FOR_LOOP: {
+ JumpTarget loop(this);
+ JumpTarget body(this);
+ if (node->init() != NULL) {
+ Visit(node->init());
+ }
+
+ // There is no need to compile the test or body.
+ if (info == ALWAYS_FALSE) break;
+
+ // If there is no update statement, label the top of the loop with the
+ // continue target, otherwise with the loop target.
+ if (node->next() == NULL) {
+ node->continue_target()->Bind();
+ } else {
+ loop.Bind();
+ }
+
+ // If the test is always true, there is no need to compile it.
+ if (info == DONT_KNOW) {
+ LoadCondition(node->cond(), NOT_INSIDE_TYPEOF,
+ &body, node->break_target(), true);
+ if (frame_ != NULL) {
+ Branch(false, node->break_target());
+ }
+ if (frame_ != NULL || body.is_linked()) {
+ body.Bind();
+ }
+ }
+
+ if (frame_ != NULL) {
+ CheckStack(); // TODO(1222600): ignore if body contains calls.
+ Visit(node->body());
+
+ if (node->next() == NULL) {
+ // If there is no update statement and control flow can fall out
+ // of the loop, jump directly to the continue label.
+ if (frame_ != NULL) {
+ node->continue_target()->Jump();
+ }
+ } else {
+ // If there is an update statement and control flow can reach it
+ // via falling out of the body of the loop or continuing, we
+ // compile the update statement.
+ if (frame_ != NULL || node->continue_target()->is_linked()) {
+ node->continue_target()->Bind();
+ // Record source position of the statement as this code which is
+ // after the code for the body actually belongs to the loop
+ // statement and not the body.
+ RecordStatementPosition(node);
+ __ RecordPosition(node->statement_pos());
+ ASSERT(node->type() == LoopStatement::FOR_LOOP);
+ Visit(node->next());
+ loop.Jump();
+ }
+ }
+ }
break;
- case ALWAYS_FALSE:
- break;
- case DONT_KNOW:
- CheckStack(); // TODO(1222600): ignore if body contains calls.
- LoadCondition(node->cond(),
- NOT_INSIDE_TYPEOF,
- &loop,
- node->break_target(),
- true);
- Branch(true, &loop);
- break;
+ }
}
- // exit
- node->break_target()->Bind();
+ if (node->break_target()->is_linked()) {
+ node->break_target()->Bind();
+ }
}
@@ -1448,19 +1596,24 @@
node->break_target()->set_code_generator(this);
node->continue_target()->set_code_generator(this);
- Label loop, next, entry, cleanup, exit, primitive, jsobject;
- Label filter_key, end_del_check, fixed_array, non_string;
+ JumpTarget primitive(this);
+ JumpTarget jsobject(this);
+ JumpTarget fixed_array(this);
+ JumpTarget entry(this);
+ JumpTarget end_del_check(this);
+ JumpTarget cleanup(this);
+ JumpTarget exit(this);
// Get the object to enumerate over (converted to JSObject).
Load(node->enumerable());
- frame_->Pop(r0);
// Both SpiderMonkey and kjs ignore null and undefined in contrast
// to the specification. 12.6.4 mandates a call to ToObject.
+ frame_->Pop(r0);
__ cmp(r0, Operand(Factory::undefined_value()));
- __ b(eq, &exit);
+ exit.Branch(eq);
__ cmp(r0, Operand(Factory::null_value()));
- __ b(eq, &exit);
+ exit.Branch(eq);
// Stack layout in body:
// [iteration counter (Smi)]
@@ -1471,31 +1624,31 @@
// Check if enumerable is already a JSObject
__ tst(r0, Operand(kSmiTagMask));
- __ b(eq, &primitive);
+ primitive.Branch(eq);
__ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
__ ldrb(r1, FieldMemOperand(r1, Map::kInstanceTypeOffset));
__ cmp(r1, Operand(FIRST_JS_OBJECT_TYPE));
- __ b(hs, &jsobject);
+ jsobject.Branch(hs);
- __ bind(&primitive);
+ primitive.Bind();
frame_->Push(r0);
__ mov(r0, Operand(0));
- __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS);
+ frame_->InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS, 1);
- __ bind(&jsobject);
+ jsobject.Bind();
// Get the set of properties (as a FixedArray or Map).
frame_->Push(r0); // duplicate the object being enumerated
frame_->Push(r0);
- __ CallRuntime(Runtime::kGetPropertyNamesFast, 1);
+ frame_->CallRuntime(Runtime::kGetPropertyNamesFast, 1);
// If we got a Map, we can do a fast modification check.
// Otherwise, we got a FixedArray, and we have to do a slow check.
__ mov(r2, Operand(r0));
__ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset));
__ cmp(r1, Operand(Factory::meta_map()));
- __ b(ne, &fixed_array);
+ fixed_array.Branch(ne);
// Get enum cache
__ mov(r1, Operand(r0));
@@ -1511,10 +1664,10 @@
frame_->Push(r0);
__ mov(r0, Operand(Smi::FromInt(0)));
frame_->Push(r0);
- __ b(&entry);
+ entry.Jump();
- __ bind(&fixed_array);
+ fixed_array.Bind();
__ mov(r1, Operand(Smi::FromInt(0)));
frame_->Push(r1); // insert 0 in place of Map
@@ -1527,70 +1680,55 @@
__ mov(r0, Operand(Smi::FromInt(0))); // init index
frame_->Push(r0);
- __ b(&entry);
-
- // Body.
- __ bind(&loop);
- Visit(node->body());
-
- // Next.
- node->continue_target()->Bind();
- __ bind(&next);
- frame_->Pop(r0);
- __ add(r0, r0, Operand(Smi::FromInt(1)));
- frame_->Push(r0);
-
// Condition.
- __ bind(&entry);
+ entry.Bind();
// sp[0] : index
// sp[1] : array/enum cache length
// sp[2] : array or enum cache
// sp[3] : 0 or map
// sp[4] : enumerable
- __ ldr(r0, frame_->Element(0)); // load the current count
- __ ldr(r1, frame_->Element(1)); // load the length
+ __ ldr(r0, frame_->ElementAt(0)); // load the current count
+ __ ldr(r1, frame_->ElementAt(1)); // load the length
__ cmp(r0, Operand(r1)); // compare to the array length
- __ b(hs, &cleanup);
+ cleanup.Branch(hs);
- __ ldr(r0, frame_->Element(0));
+ __ ldr(r0, frame_->ElementAt(0));
// Get the i'th entry of the array.
- __ ldr(r2, frame_->Element(2));
+ __ ldr(r2, frame_->ElementAt(2));
__ add(r2, r2, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
__ ldr(r3, MemOperand(r2, r0, LSL, kPointerSizeLog2 - kSmiTagSize));
// Get Map or 0.
- __ ldr(r2, frame_->Element(3));
+ __ ldr(r2, frame_->ElementAt(3));
// Check if this (still) matches the map of the enumerable.
// If not, we have to filter the key.
- __ ldr(r1, frame_->Element(4));
+ __ ldr(r1, frame_->ElementAt(4));
__ ldr(r1, FieldMemOperand(r1, HeapObject::kMapOffset));
__ cmp(r1, Operand(r2));
- __ b(eq, &end_del_check);
+ end_del_check.Branch(eq);
// Convert the entry to a string (or null if it isn't a property anymore).
- __ ldr(r0, frame_->Element(4)); // push enumerable
+ __ ldr(r0, frame_->ElementAt(4)); // push enumerable
frame_->Push(r0);
frame_->Push(r3); // push entry
__ mov(r0, Operand(1));
- __ InvokeBuiltin(Builtins::FILTER_KEY, CALL_JS);
+ frame_->InvokeBuiltin(Builtins::FILTER_KEY, CALL_JS, 2);
__ mov(r3, Operand(r0));
// If the property has been removed while iterating, we just skip it.
__ cmp(r3, Operand(Factory::null_value()));
- __ b(eq, &next);
+ node->continue_target()->Branch(eq);
-
- __ bind(&end_del_check);
-
- // 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)
+ end_del_check.Bind();
+ // 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)
frame_->Push(r3); // push entry
{ Reference each(this, node->each());
if (!each.is_illegal()) {
if (each.size() > 0) {
- __ ldr(r0, frame_->Element(each.size()));
+ __ ldr(r0, frame_->ElementAt(each.size()));
frame_->Push(r0);
}
// If the reference was to a slot we rely on the convenient property
@@ -1609,17 +1747,26 @@
}
// Discard the i'th entry pushed above or else the remainder of the
// reference, whichever is currently on top of the stack.
- frame_->Pop();
+ frame_->Drop();
+
+ // Body.
CheckStack(); // TODO(1222600): ignore if body contains calls.
- __ jmp(&loop);
+ Visit(node->body());
+ // Next.
+ node->continue_target()->Bind();
+ frame_->Pop(r0);
+ __ add(r0, r0, Operand(Smi::FromInt(1)));
+ frame_->Push(r0);
+ entry.Jump();
+
// Cleanup.
- __ bind(&cleanup);
+ cleanup.Bind();
node->break_target()->Bind();
frame_->Drop(5);
// Exit.
- __ bind(&exit);
+ exit.Bind();
break_stack_height_ -= kForInStackSize;
}
@@ -1628,9 +1775,10 @@
void CodeGenerator::VisitTryCatch(TryCatch* node) {
Comment cmnt(masm_, "[ TryCatch");
- Label try_block, exit;
+ JumpTarget try_block(this);
+ JumpTarget exit(this);
- __ bl(&try_block);
+ try_block.Call();
// --- Catch block ---
frame_->Push(r0);
@@ -1644,16 +1792,19 @@
}
// Remove the exception from the stack.
- frame_->Pop();
+ frame_->Drop();
VisitStatements(node->catch_block()->statements());
- __ b(&exit);
+ if (frame_ != NULL) {
+ exit.Jump();
+ }
// --- Try block ---
- __ bind(&try_block);
+ try_block.Bind();
- __ PushTryHandler(IN_JAVASCRIPT, TRY_CATCH_HANDLER);
+ frame_->PushTryHandler(TRY_CATCH_HANDLER);
+ int handler_height = frame_->height();
// Shadow the labels for all escapes from the try block, including
// returns. During shadowing, the original label is hidden as the
@@ -1671,7 +1822,9 @@
// Generate code for the statements in the try block.
VisitStatements(node->try_block()->statements());
- frame_->Pop(); // Discard the result.
+ if (frame_ != NULL) {
+ frame_->Drop(); // Discard the result.
+ }
// Stop the introduced shadowing and count the number of required unlinks.
// After shadowing stops, the original labels are unshadowed and the
@@ -1682,18 +1835,21 @@
if (shadows[i]->is_linked()) nof_unlinks++;
}
- // Unlink from try chain.
- // TOS contains code slot
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
- frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
- // Code slot popped.
- if (nof_unlinks > 0) __ b(&exit);
+ // If we can fall off the end of the try block, unlink from try chain.
+ if (frame_ != NULL) {
+ __ ldr(r1, frame_->ElementAt(kNextIndex)); // read next_sp
+ __ mov(r3, Operand(ExternalReference(Top::k_handler_address)));
+ __ str(r1, MemOperand(r3));
+ ASSERT(StackHandlerConstants::kCodeOffset == 0); // first field is code
+ frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
+ // Code slot popped.
+ if (nof_unlinks > 0) {
+ exit.Jump();
+ }
+ }
// Generate unlink code for the (formerly) shadowing labels that have been
// jumped to.
@@ -1706,18 +1862,19 @@
// break from (eg, for...in) may have left stuff on the stack.
__ mov(r3, Operand(ExternalReference(Top::k_handler_address)));
__ ldr(sp, MemOperand(r3));
+ frame_->Forget(frame_->height() - handler_height);
- __ ldr(r1, frame_->Element(kNextIndex));
+ __ ldr(r1, frame_->ElementAt(kNextIndex));
__ str(r1, MemOperand(r3));
ASSERT(StackHandlerConstants::kCodeOffset == 0); // first field is code
frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
// Code slot popped.
-
+ frame_->Forget(1);
shadows[i]->original_target()->Jump();
}
}
- __ bind(&exit);
+ exit.Bind();
}
@@ -1729,20 +1886,23 @@
// break/continue from within the try block.
enum { FALLING, THROWING, JUMPING };
- Label exit, unlink, try_block, finally_block;
+ JumpTarget unlink(this);
+ JumpTarget try_block(this);
+ JumpTarget finally_block(this);
- __ bl(&try_block);
+ try_block.Call();
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);
+ finally_block.Jump();
// --- Try block ---
- __ bind(&try_block);
+ try_block.Bind();
- __ PushTryHandler(IN_JAVASCRIPT, TRY_FINALLY_HANDLER);
+ frame_->PushTryHandler(TRY_FINALLY_HANDLER);
+ int handler_height = frame_->height();
// Shadow the labels for all escapes from the try block, including
// returns. Shadowing hides the original label as the LabelShadow and
@@ -1769,11 +1929,16 @@
if (shadows[i]->is_linked()) nof_unlinks++;
}
- // Set the state on the stack to FALLING.
- __ mov(r0, Operand(Factory::undefined_value())); // fake TOS
- frame_->Push(r0);
- __ mov(r2, Operand(Smi::FromInt(FALLING)));
- if (nof_unlinks > 0) __ b(&unlink);
+ // If we can fall off the end of the try block, set the state on the stack
+ // to FALLING.
+ if (frame_ != NULL) {
+ __ mov(r0, Operand(Factory::undefined_value())); // fake TOS
+ frame_->Push(r0);
+ __ mov(r2, Operand(Smi::FromInt(FALLING)));
+ if (nof_unlinks > 0) {
+ unlink.Jump();
+ }
+ }
// Generate code to set the state for the (formerly) shadowing labels that
// have been jumped to.
@@ -1790,30 +1955,32 @@
frame_->Push(r0);
}
__ mov(r2, Operand(Smi::FromInt(JUMPING + i)));
- __ b(&unlink);
+ unlink.Jump();
}
}
// Unlink from try chain;
- __ bind(&unlink);
+ unlink.Bind();
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));
+ frame_->Forget(frame_->height() - handler_height);
const int kNextIndex = (StackHandlerConstants::kNextOffset
+ StackHandlerConstants::kAddressDisplacement)
/ kPointerSize;
- __ ldr(r1, frame_->Element(kNextIndex));
+ __ ldr(r1, frame_->ElementAt(kNextIndex));
__ str(r1, MemOperand(r3));
ASSERT(StackHandlerConstants::kCodeOffset == 0); // first field is code
frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
// Code slot popped.
+ frame_->Forget(1);
frame_->Push(r0);
// --- Finally block ---
- __ bind(&finally_block);
+ finally_block.Bind();
// Push the state on the stack.
frame_->Push(r2);
@@ -1828,44 +1995,47 @@
// Generate code for the statements in the finally block.
VisitStatements(node->finally_block()->statements());
- // Restore state and return value or faked TOS.
- frame_->Pop(r2);
- frame_->Pop(r0);
break_stack_height_ -= kFinallyStackSize;
+ if (frame_ != NULL) {
+ JumpTarget exit(this);
+ // Restore state and return value or faked TOS.
+ frame_->Pop(r2);
+ frame_->Pop(r0);
- // Generate code to jump to the right destination for all used (formerly)
- // shadowing labels.
- for (int i = 0; i <= nof_escapes; i++) {
- if (shadows[i]->is_bound()) {
- __ cmp(r2, Operand(Smi::FromInt(JUMPING + i)));
- if (shadows[i]->original_target() != &function_return_) {
- Label next;
- __ b(ne, &next);
- shadows[i]->original_target()->Jump();
- __ bind(&next);
- } else {
- shadows[i]->original_target()->Branch(eq);
+ // Generate code to jump to the right destination for all used (formerly)
+ // shadowing labels.
+ for (int i = 0; i <= nof_escapes; i++) {
+ if (shadows[i]->is_bound()) {
+ __ cmp(r2, Operand(Smi::FromInt(JUMPING + i)));
+ if (shadows[i]->original_target() != &function_return_) {
+ JumpTarget next(this);
+ next.Branch(ne);
+ shadows[i]->original_target()->Jump();
+ next.Bind();
+ } else {
+ shadows[i]->original_target()->Branch(eq);
+ }
}
}
- }
- // Check if we need to rethrow the exception.
- __ cmp(r2, Operand(Smi::FromInt(THROWING)));
- __ b(ne, &exit);
+ // Check if we need to rethrow the exception.
+ __ cmp(r2, Operand(Smi::FromInt(THROWING)));
+ exit.Branch(ne);
- // Rethrow exception.
- frame_->Push(r0);
- __ CallRuntime(Runtime::kReThrow, 1);
+ // Rethrow exception.
+ frame_->Push(r0);
+ frame_->CallRuntime(Runtime::kReThrow, 1);
- // Done.
- __ bind(&exit);
+ // Done.
+ exit.Bind();
+ }
}
void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) {
Comment cmnt(masm_, "[ DebuggerStatament");
if (FLAG_debug_info) RecordStatementPosition(node);
- __ CallRuntime(Runtime::kDebugBreak, 0);
+ frame_->CallRuntime(Runtime::kDebugBreak, 0);
// Ignore the return value.
}
@@ -1879,7 +2049,7 @@
// Create a new closure.
frame_->Push(cp);
- __ CallRuntime(Runtime::kNewClosure, 2);
+ frame_->CallRuntime(Runtime::kNewClosure, 2);
frame_->Push(r0);
}
@@ -1906,15 +2076,15 @@
Comment cmnt(masm_, "[ Conditional");
JumpTarget then(this);
JumpTarget else_(this);
- Label exit;
+ JumpTarget exit(this);
LoadCondition(node->condition(), NOT_INSIDE_TYPEOF, &then, &else_, true);
Branch(false, &else_);
then.Bind();
Load(node->then_expression(), typeof_state());
- __ b(&exit);
+ exit.Jump();
else_.Bind();
Load(node->else_expression(), typeof_state());
- __ bind(&exit);
+ exit.Bind();
}
@@ -1928,9 +2098,9 @@
frame_->Push(r0);
if (typeof_state == INSIDE_TYPEOF) {
- __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
+ frame_->CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
} else {
- __ CallRuntime(Runtime::kLoadContextSlot, 2);
+ frame_->CallRuntime(Runtime::kLoadContextSlot, 2);
}
frame_->Push(r0);
@@ -2000,9 +2170,9 @@
FixedArray::kHeaderSize + node->literal_index() * kPointerSize;
__ ldr(r2, FieldMemOperand(r1, literal_offset));
- Label done;
+ JumpTarget done(this);
__ cmp(r2, Operand(Factory::undefined_value()));
- __ b(ne, &done);
+ done.Branch(ne);
// If the entry is undefined we call the runtime system to computed
// the literal.
@@ -2013,10 +2183,10 @@
frame_->Push(r0);
__ mov(r0, Operand(node->flags())); // RegExp flags (3)
frame_->Push(r0);
- __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
+ frame_->CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
__ mov(r2, Operand(r0));
- __ bind(&done);
+ done.Bind();
// Push the literal.
frame_->Push(r2);
}
@@ -2083,7 +2253,7 @@
frame_->Push(r2);
// Clone the boilerplate object.
- __ CallRuntime(Runtime::kCloneObjectLiteralBoilerplate, 1);
+ frame_->CallRuntime(Runtime::kCloneObjectLiteralBoilerplate, 1);
frame_->Push(r0); // save the result
// r0: cloned object literal
@@ -2098,7 +2268,7 @@
frame_->Push(r0); // dup the result
Load(key);
Load(value);
- __ CallRuntime(Runtime::kSetProperty, 3);
+ frame_->CallRuntime(Runtime::kSetProperty, 3);
// restore r0
__ ldr(r0, frame_->Top());
break;
@@ -2109,7 +2279,7 @@
__ mov(r0, Operand(Smi::FromInt(1)));
frame_->Push(r0);
Load(value);
- __ CallRuntime(Runtime::kDefineAccessor, 4);
+ frame_->CallRuntime(Runtime::kDefineAccessor, 4);
__ ldr(r0, frame_->Top());
break;
}
@@ -2119,7 +2289,7 @@
__ mov(r0, Operand(Smi::FromInt(0)));
frame_->Push(r0);
Load(value);
- __ CallRuntime(Runtime::kDefineAccessor, 4);
+ frame_->CallRuntime(Runtime::kDefineAccessor, 4);
__ ldr(r0, frame_->Top());
break;
}
@@ -2138,7 +2308,7 @@
__ ldr(r0, frame_->Function());
__ ldr(r0, FieldMemOperand(r0, JSFunction::kLiteralsOffset));
frame_->Push(r0);
- __ CallRuntime(Runtime::kCreateArrayLiteral, 2);
+ frame_->CallRuntime(Runtime::kCreateArrayLiteral, 2);
// Push the resulting array literal on the stack.
frame_->Push(r0);
@@ -2177,7 +2347,13 @@
if (FLAG_debug_info) RecordStatementPosition(node);
Reference target(this, node->target());
- if (target.is_illegal()) return;
+ if (target.is_illegal()) {
+ // Fool the virtual frame into thinking that we left the assignment's
+ // value on the frame.
+ __ mov(r0, Operand(Smi::FromInt(0)));
+ frame_->Push(r0);
+ return;
+ }
if (node->op() == Token::ASSIGN ||
node->op() == Token::INIT_VAR ||
@@ -2223,7 +2399,7 @@
Load(node->exception());
__ RecordPosition(node->position());
- __ CallRuntime(Runtime::kThrow, 1);
+ frame_->CallRuntime(Runtime::kThrow, 1);
frame_->Push(r0);
}
@@ -2273,15 +2449,19 @@
LoadGlobal();
// Load the arguments.
- for (int i = 0; i < args->length(); i++) Load(args->at(i));
+ int arg_count = args->length();
+ for (int i = 0; i < arg_count; i++) {
+ Load(args->at(i));
+ }
// Setup the receiver register and call the IC initialization code.
- Handle<Code> stub = ComputeCallInitialize(args->length());
+ Handle<Code> stub = ComputeCallInitialize(arg_count);
__ RecordPosition(node->position());
- __ Call(stub, RelocInfo::CODE_TARGET_CONTEXT);
+ frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET_CONTEXT,
+ arg_count + 1);
__ ldr(cp, frame_->Context());
// Remove the function from the stack.
- frame_->Pop();
+ frame_->Drop();
frame_->Push(r0);
} else if (var != NULL && var->slot() != NULL &&
@@ -2294,7 +2474,7 @@
frame_->Push(cp);
__ mov(r0, Operand(var->name()));
frame_->Push(r0);
- __ CallRuntime(Runtime::kLoadContextSlot, 2);
+ frame_->CallRuntime(Runtime::kLoadContextSlot, 2);
// r0: slot value; r1: receiver
// Load the receiver.
@@ -2320,16 +2500,19 @@
Load(property->obj());
// Load the arguments.
- for (int i = 0; i < args->length(); i++) Load(args->at(i));
+ int arg_count = args->length();
+ for (int i = 0; i < arg_count; i++) {
+ Load(args->at(i));
+ }
// Set the receiver register and call the IC initialization code.
- Handle<Code> stub = ComputeCallInitialize(args->length());
+ Handle<Code> stub = ComputeCallInitialize(arg_count);
__ RecordPosition(node->position());
- __ Call(stub, RelocInfo::CODE_TARGET);
+ frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1);
__ ldr(cp, frame_->Context());
// Remove the function from the stack.
- frame_->Pop();
+ frame_->Drop();
frame_->Push(r0); // push after get rid of function from the stack
@@ -2343,7 +2526,7 @@
ref.GetValue(NOT_INSIDE_TYPEOF); // receiver
// Pass receiver to called function.
- __ ldr(r0, frame_->Element(ref.size()));
+ __ ldr(r0, frame_->ElementAt(ref.size()));
frame_->Push(r0);
// Call the function.
CallWithArguments(args, node->position());
@@ -2385,19 +2568,22 @@
// Push the arguments ("left-to-right") on the stack.
ZoneList<Expression*>* args = node->arguments();
- for (int i = 0; i < args->length(); i++) Load(args->at(i));
+ int arg_count = args->length();
+ for (int i = 0; i < arg_count; i++) {
+ Load(args->at(i));
+ }
// r0: the number of arguments.
- __ mov(r0, Operand(args->length()));
+ __ mov(r0, Operand(arg_count));
// Load the function into r1 as per calling convention.
- __ ldr(r1, frame_->Element(args->length() + 1));
+ __ ldr(r1, frame_->ElementAt(arg_count + 1));
// Call the construct call builtin that handles allocation and
// constructor invocation.
__ RecordPosition(RelocInfo::POSITION);
- __ Call(Handle<Code>(Builtins::builtin(Builtins::JSConstructCall)),
- RelocInfo::CONSTRUCT_CALL);
+ Handle<Code> ic(Builtins::builtin(Builtins::JSConstructCall));
+ frame_->CallCodeObject(ic, RelocInfo::CONSTRUCT_CALL, arg_count + 1);
// Discard old TOS value and push r0 on the stack (same as Pop(), push(r0)).
__ str(r0, frame_->Top());
@@ -2406,48 +2592,48 @@
void CodeGenerator::GenerateValueOf(ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);
- Label leave;
+ JumpTarget leave(this);
Load(args->at(0));
frame_->Pop(r0); // r0 contains object.
// if (object->IsSmi()) return the object.
__ tst(r0, Operand(kSmiTagMask));
- __ b(eq, &leave);
+ leave.Branch(eq);
// It is a heap object - get map.
__ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
__ ldrb(r1, FieldMemOperand(r1, Map::kInstanceTypeOffset));
// if (!object->IsJSValue()) return the object.
__ cmp(r1, Operand(JS_VALUE_TYPE));
- __ b(ne, &leave);
+ leave.Branch(ne);
// Load the value.
__ ldr(r0, FieldMemOperand(r0, JSValue::kValueOffset));
- __ bind(&leave);
+ leave.Bind();
frame_->Push(r0);
}
void CodeGenerator::GenerateSetValueOf(ZoneList<Expression*>* args) {
ASSERT(args->length() == 2);
- Label leave;
+ JumpTarget leave(this);
Load(args->at(0)); // Load the object.
Load(args->at(1)); // Load the value.
frame_->Pop(r0); // r0 contains value
frame_->Pop(r1); // r1 contains object
// if (object->IsSmi()) return object.
__ tst(r1, Operand(kSmiTagMask));
- __ b(eq, &leave);
+ leave.Branch(eq);
// It is a heap object - get map.
__ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
__ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset));
// if (!object->IsJSValue()) return object.
__ cmp(r2, Operand(JS_VALUE_TYPE));
- __ b(ne, &leave);
+ leave.Branch(ne);
// Store the value.
__ str(r0, FieldMemOperand(r1, JSValue::kValueOffset));
// Update the write barrier.
__ mov(r2, Operand(JSValue::kValueOffset - kHeapObjectTag));
__ RecordWrite(r1, r2, r3);
// Leave.
- __ bind(&leave);
+ leave.Bind();
frame_->Push(r0);
}
@@ -2483,20 +2669,20 @@
void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);
Load(args->at(0));
- Label answer;
+ JumpTarget answer(this);
// 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.
frame_->Pop(r0);
__ and_(r1, r0, Operand(kSmiTagMask));
__ eor(r1, r1, Operand(kSmiTagMask), SetCC);
- __ b(ne, &answer);
+ answer.Branch(ne);
// 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);
+ answer.Bind();
cc_reg_ = eq;
}
@@ -2510,7 +2696,7 @@
// Call the shared stub to get to the arguments.length.
ArgumentsAccessStub stub(ArgumentsAccessStub::READ_LENGTH);
- __ CallStub(&stub);
+ frame_->CallStub(&stub, 0);
frame_->Push(r0);
}
@@ -2526,7 +2712,7 @@
// Call the shared stub to get to arguments[key].
ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT);
- __ CallStub(&stub);
+ frame_->CallStub(&stub, 0);
frame_->Push(r0);
}
@@ -2545,7 +2731,9 @@
void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
- if (CheckForInlineRuntimeCall(node)) return;
+ if (CheckForInlineRuntimeCall(node)) {
+ return;
+ }
ZoneList<Expression*>* args = node->arguments();
Comment cmnt(masm_, "[ CallRuntime");
@@ -2553,10 +2741,13 @@
if (function != NULL) {
// Push the arguments ("left-to-right").
- for (int i = 0; i < args->length(); i++) Load(args->at(i));
+ int arg_count = args->length();
+ for (int i = 0; i < arg_count; i++) {
+ Load(args->at(i));
+ }
// Call the C runtime function.
- __ CallRuntime(function, args->length());
+ frame_->CallRuntime(function, arg_count);
frame_->Push(r0);
} else {
@@ -2568,13 +2759,16 @@
__ ldr(r0, FieldMemOperand(r1, GlobalObject::kBuiltinsOffset));
frame_->Push(r0);
- for (int i = 0; i < args->length(); i++) Load(args->at(i));
+ int arg_count = args->length();
+ for (int i = 0; i < arg_count; i++) {
+ Load(args->at(i));
+ }
// Call the JS runtime function.
Handle<Code> stub = ComputeCallInitialize(args->length());
- __ Call(stub, RelocInfo::CODE_TARGET);
+ frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1);
__ ldr(cp, frame_->Context());
- frame_->Pop();
+ frame_->Drop();
frame_->Push(r0);
}
}
@@ -2600,7 +2794,7 @@
Load(property->obj());
Load(property->key());
__ mov(r0, Operand(1)); // not counting receiver
- __ InvokeBuiltin(Builtins::DELETE, CALL_JS);
+ frame_->InvokeBuiltin(Builtins::DELETE, CALL_JS, 2);
} else if (variable != NULL) {
Slot* slot = variable->slot();
@@ -2609,20 +2803,20 @@
__ mov(r0, Operand(variable->name()));
frame_->Push(r0);
__ mov(r0, Operand(1)); // not counting receiver
- __ InvokeBuiltin(Builtins::DELETE, CALL_JS);
+ frame_->InvokeBuiltin(Builtins::DELETE, CALL_JS, 2);
} else if (slot != NULL && slot->type() == Slot::LOOKUP) {
// lookup the context holding the named variable
frame_->Push(cp);
__ mov(r0, Operand(variable->name()));
frame_->Push(r0);
- __ CallRuntime(Runtime::kLookupContext, 2);
+ frame_->CallRuntime(Runtime::kLookupContext, 2);
// r0: context
frame_->Push(r0);
__ mov(r0, Operand(variable->name()));
frame_->Push(r0);
__ mov(r0, Operand(1)); // not counting receiver
- __ InvokeBuiltin(Builtins::DELETE, CALL_JS);
+ frame_->InvokeBuiltin(Builtins::DELETE, CALL_JS, 2);
} else {
// Default: Result of deleting non-global, not dynamically
@@ -2633,7 +2827,7 @@
} else {
// Default: Result of deleting expressions is true.
Load(node->expression()); // may have side-effects
- frame_->Pop();
+ frame_->Drop();
__ mov(r0, Operand(Factory::true_value()));
}
frame_->Push(r0);
@@ -2642,7 +2836,7 @@
// Special case for loading the typeof expression; see comment on
// LoadTypeofExpression().
LoadTypeofExpression(node->expression());
- __ CallRuntime(Runtime::kTypeof, 1);
+ frame_->CallRuntime(Runtime::kTypeof, 1);
frame_->Push(r0); // r0 has result
} else {
@@ -2657,26 +2851,26 @@
case Token::SUB: {
UnarySubStub stub;
- __ CallStub(&stub);
+ frame_->CallStub(&stub, 0);
break;
}
case Token::BIT_NOT: {
// smi check
- Label smi_label;
- Label continue_label;
+ JumpTarget smi_label(this);
+ JumpTarget continue_label(this);
__ tst(r0, Operand(kSmiTagMask));
- __ b(eq, &smi_label);
+ smi_label.Branch(eq);
frame_->Push(r0);
__ mov(r0, Operand(0)); // not counting receiver
- __ InvokeBuiltin(Builtins::BIT_NOT, CALL_JS);
+ frame_->InvokeBuiltin(Builtins::BIT_NOT, CALL_JS, 1);
- __ b(&continue_label);
- __ bind(&smi_label);
+ continue_label.Jump();
+ smi_label.Bind();
__ mvn(r0, Operand(r0));
__ bic(r0, r0, Operand(kSmiTagMask)); // bit-clear inverted smi-tag
- __ bind(&continue_label);
+ continue_label.Bind();
break;
}
@@ -2688,13 +2882,13 @@
case Token::ADD: {
// Smi check.
- Label continue_label;
+ JumpTarget continue_label(this);
__ tst(r0, Operand(kSmiTagMask));
- __ b(eq, &continue_label);
+ continue_label.Branch(eq);
frame_->Push(r0);
__ mov(r0, Operand(0)); // not counting receiver
- __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_JS);
- __ bind(&continue_label);
+ frame_->InvokeBuiltin(Builtins::TO_NUMBER, CALL_JS, 1);
+ continue_label.Bind();
break;
}
default:
@@ -2721,22 +2915,31 @@
}
{ Reference target(this, node->expression());
- if (target.is_illegal()) return;
+ if (target.is_illegal()) {
+ // Spoof the virtual frame to have the expected height (one higher
+ // than on entry).
+ if (!is_postfix) {
+ __ mov(r0, Operand(Smi::FromInt(0)));
+ frame_->Push(r0);
+ }
+ return;
+ }
target.GetValue(NOT_INSIDE_TYPEOF);
frame_->Pop(r0);
- Label slow, exit;
+ JumpTarget slow(this);
+ JumpTarget exit(this);
// Load the value (1) into register r1.
__ mov(r1, Operand(Smi::FromInt(1)));
// Check for smi operand.
__ tst(r0, Operand(kSmiTagMask));
- __ b(ne, &slow);
+ slow.Branch(ne);
// Postfix: Store the old value as the result.
if (is_postfix) {
- __ str(r0, frame_->Element(target.size()));
+ __ str(r0, frame_->ElementAt(target.size()));
}
// Perform optimistic increment/decrement.
@@ -2747,7 +2950,7 @@
}
// If the increment/decrement didn't overflow, we're done.
- __ b(vc, &exit);
+ exit.Branch(vc);
// Revert optimistic increment/decrement.
if (is_increment) {
@@ -2757,27 +2960,27 @@
}
// Slow case: Convert to number.
- __ bind(&slow);
+ slow.Bind();
// Postfix: Convert the operand to a number and store it as the result.
if (is_postfix) {
InvokeBuiltinStub stub(InvokeBuiltinStub::ToNumber, 2);
- __ CallStub(&stub);
+ frame_->CallStub(&stub, 0);
// Store to result (on the stack).
- __ str(r0, frame_->Element(target.size()));
+ __ str(r0, frame_->ElementAt(target.size()));
}
// Compute the new value by calling the right JavaScript native.
if (is_increment) {
InvokeBuiltinStub stub(InvokeBuiltinStub::Inc, 1);
- __ CallStub(&stub);
+ frame_->CallStub(&stub, 0);
} else {
InvokeBuiltinStub stub(InvokeBuiltinStub::Dec, 1);
- __ CallStub(&stub);
+ frame_->CallStub(&stub, 0);
}
// Store the new value in the target if not const.
- __ bind(&exit);
+ exit.Bind();
frame_->Push(r0);
if (!is_const) target.SetValue(NOT_CONST_INIT);
}
@@ -2938,31 +3141,32 @@
// The 'null' value is only equal to 'null' or 'undefined'.
if (left_is_null || right_is_null) {
Load(left_is_null ? right : left);
- Label exit, undetectable;
+ JumpTarget exit(this);
+ JumpTarget undetectable(this);
frame_->Pop(r0);
__ cmp(r0, Operand(Factory::null_value()));
// The 'null' value is only equal to 'undefined' if using
// non-strict comparisons.
if (op != Token::EQ_STRICT) {
- __ b(eq, &exit);
+ exit.Branch(eq);
__ cmp(r0, Operand(Factory::undefined_value()));
// NOTE: it can be undetectable object.
- __ b(eq, &exit);
+ exit.Branch(eq);
__ tst(r0, Operand(kSmiTagMask));
- __ b(ne, &undetectable);
+ undetectable.Branch(ne);
false_target()->Jump();
- __ bind(&undetectable);
+ undetectable.Bind();
__ 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));
}
- __ bind(&exit);
+ exit.Bind();
cc_reg_ = eq;
return;
@@ -3094,13 +3298,13 @@
case Token::IN:
__ mov(r0, Operand(1)); // not counting receiver
- __ InvokeBuiltin(Builtins::IN, CALL_JS);
+ frame_->InvokeBuiltin(Builtins::IN, CALL_JS, 2);
frame_->Push(r0);
break;
case Token::INSTANCEOF:
__ mov(r0, Operand(1)); // not counting receiver
- __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_JS);
+ frame_->InvokeBuiltin(Builtins::INSTANCE_OF, CALL_JS, 2);
__ tst(r0, Operand(r0));
cc_reg_ = eq;
break;
@@ -3174,9 +3378,9 @@
Variable* var = expression_->AsVariableProxy()->AsVariable();
if (var != NULL) {
ASSERT(var->is_global());
- __ Call(ic, RelocInfo::CODE_TARGET_CONTEXT);
+ frame->CallCodeObject(ic, RelocInfo::CODE_TARGET_CONTEXT, 0);
} else {
- __ Call(ic, RelocInfo::CODE_TARGET);
+ frame->CallCodeObject(ic, RelocInfo::CODE_TARGET, 0);
}
frame->Push(r0);
break;
@@ -3189,7 +3393,7 @@
ASSERT(property != NULL);
// TODO(1224671): Implement inline caching for keyed loads as on ia32.
GetPropertyStub stub;
- __ CallStub(&stub);
+ frame->CallStub(&stub, 0);
frame->Push(r0);
break;
}
@@ -3239,9 +3443,9 @@
// 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);
+ frame->CallRuntime(Runtime::kInitializeConstContextSlot, 3);
} else {
- __ CallRuntime(Runtime::kStoreContextSlot, 3);
+ frame->CallRuntime(Runtime::kStoreContextSlot, 3);
}
// Storing a variable must keep the (new) value on the expression
// stack. This is necessary for compiling assignment expressions.
@@ -3250,7 +3454,7 @@
} else {
ASSERT(slot->var()->mode() != Variable::DYNAMIC);
- Label exit;
+ JumpTarget exit(cgen_);
if (init_state == CONST_INIT) {
ASSERT(slot->var()->mode() == Variable::CONST);
// Only the first const initialization must be executed (the slot
@@ -3259,7 +3463,7 @@
Comment cmnt(masm, "[ Init const");
__ ldr(r2, cgen_->SlotOperand(slot, r2));
__ cmp(r2, Operand(Factory::the_hole_value()));
- __ b(ne, &exit);
+ exit.Branch(ne);
}
// We must execute the store. Storing a variable must keep the
@@ -3277,7 +3481,7 @@
if (slot->type() == Slot::CONTEXT) {
// Skip write barrier if the written value is a smi.
__ tst(r0, Operand(kSmiTagMask));
- __ b(eq, &exit);
+ exit.Branch(eq);
// r2 is loaded with context when calling SlotOperand above.
int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
__ mov(r3, Operand(offset));
@@ -3287,7 +3491,7 @@
// to bind the exit label. Doing so can defeat peephole
// optimization.
if (init_state == CONST_INIT || slot->type() == Slot::CONTEXT) {
- __ bind(&exit);
+ exit.Bind();
}
}
break;
@@ -3301,7 +3505,7 @@
Handle<String> name(GetName());
__ mov(r2, Operand(name));
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
- __ Call(ic, RelocInfo::CODE_TARGET);
+ frame->CallCodeObject(ic, RelocInfo::CODE_TARGET, 0);
frame->Push(r0);
break;
}
@@ -3313,7 +3517,7 @@
__ RecordPosition(property->position());
frame->Pop(r0); // value
SetPropertyStub stub;
- __ CallStub(&stub);
+ frame->CallStub(&stub, 0);
frame->Push(r0);
break;
}
« no previous file with comments | « src/codegen-arm.h ('k') | src/codegen-ia32.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698