| Index: src/x64/codegen-x64.cc
|
| ===================================================================
|
| --- src/x64/codegen-x64.cc (revision 2269)
|
| +++ src/x64/codegen-x64.cc (working copy)
|
| @@ -283,7 +283,7 @@
|
| frame_->CallRuntime(Runtime::kTraceEnter, 0);
|
| // Ignore the return value.
|
| }
|
| - // CheckStack();
|
| + CheckStack();
|
|
|
| // Compile the body of the function in a vanilla state. Don't
|
| // bother compiling all the code if the scope has an illegal
|
| @@ -409,6 +409,35 @@
|
| #endif
|
|
|
|
|
| +class DeferredStackCheck: public DeferredCode {
|
| + public:
|
| + DeferredStackCheck() {
|
| + set_comment("[ DeferredStackCheck");
|
| + }
|
| +
|
| + virtual void Generate();
|
| +};
|
| +
|
| +
|
| +void DeferredStackCheck::Generate() {
|
| + StackCheckStub stub;
|
| + __ CallStub(&stub);
|
| +}
|
| +
|
| +
|
| +void CodeGenerator::CheckStack() {
|
| + if (FLAG_check_stack) {
|
| + DeferredStackCheck* deferred = new DeferredStackCheck;
|
| + ExternalReference stack_guard_limit =
|
| + ExternalReference::address_of_stack_guard_limit();
|
| + __ movq(kScratchRegister, stack_guard_limit);
|
| + __ cmpq(rsp, Operand(kScratchRegister, 0));
|
| + deferred->Branch(below);
|
| + deferred->BindExit();
|
| + }
|
| +}
|
| +
|
| +
|
| void CodeGenerator::VisitStatements(ZoneList<Statement*>* statements) {
|
| ASSERT(!in_spilled_code());
|
| for (int i = 0; has_valid_frame() && i < statements->length(); i++) {
|
| @@ -658,10 +687,340 @@
|
| UNIMPLEMENTED();
|
| }
|
|
|
| -void CodeGenerator::VisitLoopStatement(LoopStatement* a) {
|
| - UNIMPLEMENTED();
|
| +
|
| +void CodeGenerator::VisitLoopStatement(LoopStatement* node) {
|
| + ASSERT(!in_spilled_code());
|
| + Comment cmnt(masm_, "[ LoopStatement");
|
| + CodeForStatementPosition(node);
|
| + node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
|
| +
|
| + // 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);
|
| + info = ALWAYS_TRUE;
|
| + } else {
|
| + Literal* lit = node->cond()->AsLiteral();
|
| + if (lit != NULL) {
|
| + if (lit->IsTrue()) {
|
| + info = ALWAYS_TRUE;
|
| + } else if (lit->IsFalse()) {
|
| + info = ALWAYS_FALSE;
|
| + }
|
| + }
|
| + }
|
| +
|
| + switch (node->type()) {
|
| + case LoopStatement::DO_LOOP: {
|
| + JumpTarget body(JumpTarget::BIDIRECTIONAL);
|
| + IncrementLoopNesting();
|
| +
|
| + // Label the top of the loop for the backward jump if necessary.
|
| + if (info == ALWAYS_TRUE) {
|
| + // Use the continue target.
|
| + node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
|
| + node->continue_target()->Bind();
|
| + } else if (info == ALWAYS_FALSE) {
|
| + // No need to label it.
|
| + node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
|
| + } else {
|
| + // Continue is the test, so use the backward body target.
|
| + ASSERT(info == DONT_KNOW);
|
| + node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
|
| + body.Bind();
|
| + }
|
| +
|
| + CheckStack(); // TODO(1222600): ignore if body contains calls.
|
| + Visit(node->body());
|
| +
|
| + // Compile the test.
|
| + if (info == ALWAYS_TRUE) {
|
| + // If control flow can fall off the end of the body, jump back
|
| + // to the top and bind the break target at the exit.
|
| + if (has_valid_frame()) {
|
| + node->continue_target()->Jump();
|
| + }
|
| + if (node->break_target()->is_linked()) {
|
| + node->break_target()->Bind();
|
| + }
|
| +
|
| + } else if (info == ALWAYS_FALSE) {
|
| + // We may have had continues or breaks in the body.
|
| + if (node->continue_target()->is_linked()) {
|
| + node->continue_target()->Bind();
|
| + }
|
| + if (node->break_target()->is_linked()) {
|
| + node->break_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 (node->continue_target()->is_linked()) {
|
| + node->continue_target()->Bind();
|
| + }
|
| + if (has_valid_frame()) {
|
| + ControlDestination dest(&body, node->break_target(), false);
|
| + LoadCondition(node->cond(), NOT_INSIDE_TYPEOF, &dest, true);
|
| + }
|
| + if (node->break_target()->is_linked()) {
|
| + node->break_target()->Bind();
|
| + }
|
| + }
|
| + break;
|
| + }
|
| +
|
| + case LoopStatement::WHILE_LOOP: {
|
| + // Do not duplicate conditions that may have function literal
|
| + // subexpressions. This can cause us to compile the function
|
| + // literal twice.
|
| + bool test_at_bottom = !node->may_have_function_literal();
|
| +
|
| + IncrementLoopNesting();
|
| +
|
| + // If the condition is always false and has no side effects, we
|
| + // do not need to compile anything.
|
| + if (info == ALWAYS_FALSE) break;
|
| +
|
| + JumpTarget body;
|
| + if (test_at_bottom) {
|
| + body.set_direction(JumpTarget::BIDIRECTIONAL);
|
| + }
|
| +
|
| + // Based on the condition analysis, compile the test as necessary.
|
| + if (info == ALWAYS_TRUE) {
|
| + // We will not compile the test expression. Label the top of
|
| + // the loop with the continue target.
|
| + node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
|
| + node->continue_target()->Bind();
|
| + } else {
|
| + ASSERT(info == DONT_KNOW); // ALWAYS_FALSE cannot reach here.
|
| + if (test_at_bottom) {
|
| + // Continue is the test at the bottom, no need to label the
|
| + // test at the top. The body is a backward target.
|
| + node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
|
| + } else {
|
| + // Label the test at the top as the continue target. The
|
| + // body is a forward-only target.
|
| + node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
|
| + node->continue_target()->Bind();
|
| + }
|
| + // Compile the test with the body as the true target and
|
| + // preferred fall-through and with the break target as the
|
| + // false target.
|
| + ControlDestination dest(&body, node->break_target(), true);
|
| + LoadCondition(node->cond(), NOT_INSIDE_TYPEOF, &dest, true);
|
| +
|
| + if (dest.false_was_fall_through()) {
|
| + // If we got the break target as fall-through, the test may
|
| + // have been unconditionally false (if there are no jumps to
|
| + // the body).
|
| + if (!body.is_linked()) break;
|
| +
|
| + // Otherwise, jump around the body on the fall through and
|
| + // then bind the body target.
|
| + node->break_target()->Unuse();
|
| + node->break_target()->Jump();
|
| + body.Bind();
|
| + }
|
| + }
|
| +
|
| + CheckStack(); // TODO(1222600): ignore if body contains calls.
|
| + Visit(node->body());
|
| +
|
| + // Based on the condition analysis, compile the backward jump as
|
| + // necessary.
|
| + if (info == ALWAYS_TRUE) {
|
| + // The loop body has been labeled with the continue target.
|
| + if (has_valid_frame()) {
|
| + node->continue_target()->Jump();
|
| + }
|
| + } else {
|
| + ASSERT(info == DONT_KNOW); // ALWAYS_FALSE cannot reach here.
|
| + if (test_at_bottom) {
|
| + // If we have chosen to recompile the test at the bottom,
|
| + // then it is the continue target.
|
| + if (node->continue_target()->is_linked()) {
|
| + node->continue_target()->Bind();
|
| + }
|
| + if (has_valid_frame()) {
|
| + // The break target is the fall-through (body is a backward
|
| + // jump from here and thus an invalid fall-through).
|
| + ControlDestination dest(&body, node->break_target(), false);
|
| + LoadCondition(node->cond(), NOT_INSIDE_TYPEOF, &dest, true);
|
| + }
|
| + } else {
|
| + // If we have chosen not to recompile the test at the
|
| + // bottom, jump back to the one at the top.
|
| + if (has_valid_frame()) {
|
| + node->continue_target()->Jump();
|
| + }
|
| + }
|
| + }
|
| +
|
| + // The break target may be already bound (by the condition), or
|
| + // there may not be a valid frame. Bind it only if needed.
|
| + if (node->break_target()->is_linked()) {
|
| + node->break_target()->Bind();
|
| + }
|
| + break;
|
| + }
|
| +
|
| + case LoopStatement::FOR_LOOP: {
|
| + // Do not duplicate conditions that may have function literal
|
| + // subexpressions. This can cause us to compile the function
|
| + // literal twice.
|
| + bool test_at_bottom = !node->may_have_function_literal();
|
| +
|
| + // Compile the init expression if present.
|
| + if (node->init() != NULL) {
|
| + Visit(node->init());
|
| + }
|
| +
|
| + IncrementLoopNesting();
|
| +
|
| + // If the condition is always false and has no side effects, we
|
| + // do not need to compile anything else.
|
| + if (info == ALWAYS_FALSE) break;
|
| +
|
| + // Target for backward edge if no test at the bottom, otherwise
|
| + // unused.
|
| + JumpTarget loop(JumpTarget::BIDIRECTIONAL);
|
| +
|
| + // Target for backward edge if there is a test at the bottom,
|
| + // otherwise used as target for test at the top.
|
| + JumpTarget body;
|
| + if (test_at_bottom) {
|
| + body.set_direction(JumpTarget::BIDIRECTIONAL);
|
| + }
|
| +
|
| + // Based on the condition analysis, compile the test as necessary.
|
| + if (info == ALWAYS_TRUE) {
|
| + // We will not compile the test expression. Label the top of
|
| + // the loop.
|
| + if (node->next() == NULL) {
|
| + // Use the continue target if there is no update expression.
|
| + node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
|
| + node->continue_target()->Bind();
|
| + } else {
|
| + // Otherwise use the backward loop target.
|
| + node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
|
| + loop.Bind();
|
| + }
|
| + } else {
|
| + ASSERT(info == DONT_KNOW);
|
| + if (test_at_bottom) {
|
| + // Continue is either the update expression or the test at
|
| + // the bottom, no need to label the test at the top.
|
| + node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
|
| + } else if (node->next() == NULL) {
|
| + // We are not recompiling the test at the bottom and there
|
| + // is no update expression.
|
| + node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
|
| + node->continue_target()->Bind();
|
| + } else {
|
| + // We are not recompiling the test at the bottom and there
|
| + // is an update expression.
|
| + node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
|
| + loop.Bind();
|
| + }
|
| +
|
| + // Compile the test with the body as the true target and
|
| + // preferred fall-through and with the break target as the
|
| + // false target.
|
| + ControlDestination dest(&body, node->break_target(), true);
|
| + LoadCondition(node->cond(), NOT_INSIDE_TYPEOF, &dest, true);
|
| +
|
| + if (dest.false_was_fall_through()) {
|
| + // If we got the break target as fall-through, the test may
|
| + // have been unconditionally false (if there are no jumps to
|
| + // the body).
|
| + if (!body.is_linked()) break;
|
| +
|
| + // Otherwise, jump around the body on the fall through and
|
| + // then bind the body target.
|
| + node->break_target()->Unuse();
|
| + node->break_target()->Jump();
|
| + body.Bind();
|
| + }
|
| + }
|
| +
|
| + CheckStack(); // TODO(1222600): ignore if body contains calls.
|
| + Visit(node->body());
|
| +
|
| + // If there is an update expression, compile it if necessary.
|
| + if (node->next() != NULL) {
|
| + if (node->continue_target()->is_linked()) {
|
| + node->continue_target()->Bind();
|
| + }
|
| +
|
| + // Control can reach the update by falling out of the body or
|
| + // by a continue.
|
| + if (has_valid_frame()) {
|
| + // Record the 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.
|
| + CodeForStatementPosition(node);
|
| + Visit(node->next());
|
| + }
|
| + }
|
| +
|
| + // Based on the condition analysis, compile the backward jump as
|
| + // necessary.
|
| + if (info == ALWAYS_TRUE) {
|
| + if (has_valid_frame()) {
|
| + if (node->next() == NULL) {
|
| + node->continue_target()->Jump();
|
| + } else {
|
| + loop.Jump();
|
| + }
|
| + }
|
| + } else {
|
| + ASSERT(info == DONT_KNOW); // ALWAYS_FALSE cannot reach here.
|
| + if (test_at_bottom) {
|
| + if (node->continue_target()->is_linked()) {
|
| + // We can have dangling jumps to the continue target if
|
| + // there was no update expression.
|
| + node->continue_target()->Bind();
|
| + }
|
| + // Control can reach the test at the bottom by falling out
|
| + // of the body, by a continue in the body, or from the
|
| + // update expression.
|
| + if (has_valid_frame()) {
|
| + // The break target is the fall-through (body is a
|
| + // backward jump from here).
|
| + ControlDestination dest(&body, node->break_target(), false);
|
| + LoadCondition(node->cond(), NOT_INSIDE_TYPEOF, &dest, true);
|
| + }
|
| + } else {
|
| + // Otherwise, jump back to the test at the top.
|
| + if (has_valid_frame()) {
|
| + if (node->next() == NULL) {
|
| + node->continue_target()->Jump();
|
| + } else {
|
| + loop.Jump();
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + // The break target may be already bound (by the condition), or
|
| + // there may not be a valid frame. Bind it only if needed.
|
| + if (node->break_target()->is_linked()) {
|
| + node->break_target()->Bind();
|
| + }
|
| + break;
|
| + }
|
| + }
|
| +
|
| + DecrementLoopNesting();
|
| + node->continue_target()->Unuse();
|
| + node->break_target()->Unuse();
|
| }
|
|
|
| +
|
| void CodeGenerator::VisitForInStatement(ForInStatement* a) {
|
| UNIMPLEMENTED();
|
| }
|
| @@ -1271,17 +1630,17 @@
|
| frame_->EmitPush(esi);
|
| frame_->EmitPush(Immediate(var->name()));
|
| frame_->CallRuntime(Runtime::kLoadContextSlot, 2);
|
| - // The runtime call returns a pair of values in eax and edx. The
|
| - // looked-up function is in eax and the receiver is in edx. These
|
| + // The runtime call returns a pair of values in rax and rdx. The
|
| + // looked-up function is in rax and the receiver is in rdx. These
|
| // register references are not ref counted here. We spill them
|
| // eagerly since they are arguments to an inevitable call (and are
|
| // not sharable by the arguments).
|
| - ASSERT(!allocator()->is_used(eax));
|
| - frame_->EmitPush(eax);
|
| + ASSERT(!allocator()->is_used(rax));
|
| + frame_->EmitPush(rax);
|
|
|
| // Load the receiver.
|
| - ASSERT(!allocator()->is_used(edx));
|
| - frame_->EmitPush(edx);
|
| + ASSERT(!allocator()->is_used(rdx));
|
| + frame_->EmitPush(rdx);
|
|
|
| // Call the function.
|
| CallWithArguments(args, node->position());
|
| @@ -2783,7 +3142,7 @@
|
| Register dst);
|
|
|
| // Test if operands are smi or number objects (fp). Requirements:
|
| - // operand_1 in eax, operand_2 in edx; falls through on float
|
| + // operand_1 in rax, operand_2 in rdx; falls through on float
|
| // operands, jumps to the non_float label otherwise.
|
| static void CheckFloatOperands(MacroAssembler* masm,
|
| Label* non_float);
|
| @@ -3008,7 +3367,7 @@
|
| __ Move(rcx, name_);
|
| Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
|
| __ Call(ic, RelocInfo::CODE_TARGET);
|
| - // The call must be followed by a test eax instruction to indicate
|
| + // The call must be followed by a test rax instruction to indicate
|
| // that the inobject property case was inlined.
|
| //
|
| // Store the delta to the map check instruction here in the test
|
| @@ -3991,7 +4350,7 @@
|
|
|
| // TODO(1243847): Use cmov below once CpuFeatures are properly hooked up.
|
| Label below_lbl, above_lbl;
|
| - // use edx, eax to convert unsigned to signed comparison
|
| + // use rdx, rax to convert unsigned to signed comparison
|
| __ j(below, &below_lbl);
|
| __ j(above, &above_lbl);
|
|
|
| @@ -4004,7 +4363,7 @@
|
|
|
| __ bind(&above_lbl);
|
| __ movq(rax, Immediate(1));
|
| - __ ret(2 * kPointerSize); // eax, edx were pushed
|
| + __ ret(2 * kPointerSize); // rax, rdx were pushed
|
|
|
| // Fast negative check for symbol-to-symbol equality.
|
| __ bind(&check_for_symbols);
|
| @@ -4013,7 +4372,7 @@
|
| BranchIfNonSymbol(masm, &call_builtin, rdx);
|
|
|
| // We've already checked for object identity, so if both operands
|
| - // are symbols they aren't equal. Register eax already holds a
|
| + // are symbols they aren't equal. Register rax already holds a
|
| // non-zero value, which indicates not equal, so just return.
|
| __ ret(2 * kPointerSize);
|
| }
|
| @@ -4065,10 +4424,6 @@
|
| }
|
|
|
|
|
| -void StackCheckStub::Generate(MacroAssembler* masm) {
|
| -}
|
| -
|
| -
|
| class CallFunctionStub: public CodeStub {
|
| public:
|
| CallFunctionStub(int argc, InLoopFlag in_loop)
|
| @@ -4604,7 +4959,20 @@
|
|
|
| // Stub classes have public member named masm, not masm_.
|
|
|
| +void StackCheckStub::Generate(MacroAssembler* masm) {
|
| + // Because builtins always remove the receiver from the stack, we
|
| + // have to fake one to avoid underflowing the stack. The receiver
|
| + // must be inserted below the return address on the stack so we
|
| + // temporarily store that in a register.
|
| + __ pop(rax);
|
| + __ push(Immediate(Smi::FromInt(0)));
|
| + __ push(rax);
|
|
|
| + // Do tail-call to runtime routine.
|
| + __ TailCallRuntime(ExternalReference(Runtime::kStackGuard), 1);
|
| +}
|
| +
|
| +
|
| void FloatingPointHelper::AllocateHeapNumber(MacroAssembler* masm,
|
| Label* need_gc,
|
| Register scratch,
|
|
|