| Index: src/x64/fast-codegen-x64.cc
|
| ===================================================================
|
| --- src/x64/fast-codegen-x64.cc (revision 3684)
|
| +++ src/x64/fast-codegen-x64.cc (working copy)
|
| @@ -62,11 +62,9 @@
|
|
|
| { Comment cmnt(masm_, "[ Allocate locals");
|
| int locals_count = fun->scope()->num_stack_slots();
|
| - if (locals_count <= 1) {
|
| - if (locals_count > 0) {
|
| - __ PushRoot(Heap::kUndefinedValueRootIndex);
|
| - }
|
| - } else {
|
| + if (locals_count == 1) {
|
| + __ PushRoot(Heap::kUndefinedValueRootIndex);
|
| + } else if (locals_count > 1) {
|
| __ LoadRoot(rdx, Heap::kUndefinedValueRootIndex);
|
| for (int i = 0; i < locals_count; i++) {
|
| __ push(rdx);
|
| @@ -132,6 +130,10 @@
|
| Move(dot_arguments_slot, rcx, rbx, rdx);
|
| }
|
|
|
| + { Comment cmnt(masm_, "[ Declarations");
|
| + VisitDeclarations(fun->scope()->declarations());
|
| + }
|
| +
|
| { Comment cmnt(masm_, "[ Stack check");
|
| Label ok;
|
| __ CompareRoot(rsp, Heap::kStackLimitRootIndex);
|
| @@ -141,10 +143,6 @@
|
| __ bind(&ok);
|
| }
|
|
|
| - { Comment cmnt(masm_, "[ Declarations");
|
| - VisitDeclarations(fun->scope()->declarations());
|
| - }
|
| -
|
| if (FLAG_trace) {
|
| __ CallRuntime(Runtime::kTraceEnter, 0);
|
| }
|
| @@ -202,24 +200,86 @@
|
| }
|
|
|
|
|
| -void FastCodeGenerator::Apply(Expression::Context context,
|
| - Slot* slot,
|
| - Register scratch) {
|
| +void FastCodeGenerator::Apply(Expression::Context context, Register reg) {
|
| switch (context) {
|
| case Expression::kUninitialized:
|
| UNREACHABLE();
|
| +
|
| case Expression::kEffect:
|
| + // Nothing to do.
|
| break;
|
| +
|
| + case Expression::kValue:
|
| + // Move value into place.
|
| + switch (location_) {
|
| + case kAccumulator:
|
| + if (!reg.is(result_register())) __ movq(result_register(), reg);
|
| + break;
|
| + case kStack:
|
| + __ push(reg);
|
| + break;
|
| + }
|
| + break;
|
| +
|
| + case Expression::kTest:
|
| + // For simplicity we always test the accumulator register.
|
| + if (!reg.is(result_register())) __ movq(result_register(), reg);
|
| + DoTest(context);
|
| + break;
|
| +
|
| + case Expression::kValueTest:
|
| + case Expression::kTestValue:
|
| + if (!reg.is(result_register())) __ movq(result_register(), reg);
|
| + switch (location_) {
|
| + case kAccumulator:
|
| + break;
|
| + case kStack:
|
| + __ push(result_register());
|
| + break;
|
| + }
|
| + DoTest(context);
|
| + break;
|
| + }
|
| +}
|
| +
|
| +
|
| +void FastCodeGenerator::Apply(Expression::Context context, Slot* slot) {
|
| + switch (context) {
|
| + case Expression::kUninitialized:
|
| + UNREACHABLE();
|
| + case Expression::kEffect:
|
| + // Nothing to do.
|
| + break;
|
| case Expression::kValue: {
|
| - MemOperand location = EmitSlotSearch(slot, scratch);
|
| - __ push(location);
|
| + MemOperand slot_operand = EmitSlotSearch(slot, result_register());
|
| + switch (location_) {
|
| + case kAccumulator:
|
| + __ movq(result_register(), slot_operand);
|
| + break;
|
| + case kStack:
|
| + // Memory operands can be pushed directly.
|
| + __ push(slot_operand);
|
| + break;
|
| + }
|
| break;
|
| }
|
| +
|
| case Expression::kTest:
|
| + Move(result_register(), slot);
|
| + DoTest(context);
|
| + break;
|
| +
|
| case Expression::kValueTest:
|
| case Expression::kTestValue:
|
| - Move(scratch, slot);
|
| - Apply(context, scratch);
|
| + Move(result_register(), slot);
|
| + switch (location_) {
|
| + case kAccumulator:
|
| + break;
|
| + case kStack:
|
| + __ push(result_register());
|
| + break;
|
| + }
|
| + DoTest(context);
|
| break;
|
| }
|
| }
|
| @@ -230,15 +290,35 @@
|
| case Expression::kUninitialized:
|
| UNREACHABLE();
|
| case Expression::kEffect:
|
| + // Nothing to do.
|
| break;
|
| case Expression::kValue:
|
| - __ Push(lit->handle());
|
| + switch (location_) {
|
| + case kAccumulator:
|
| + __ Move(result_register(), lit->handle());
|
| + break;
|
| + case kStack:
|
| + __ Push(lit->handle());
|
| + break;
|
| + }
|
| break;
|
| +
|
| case Expression::kTest:
|
| + __ Move(result_register(), lit->handle());
|
| + DoTest(context);
|
| + break;
|
| +
|
| case Expression::kValueTest:
|
| case Expression::kTestValue:
|
| - __ Move(rax, lit->handle());
|
| - Apply(context, rax);
|
| + __ Move(result_register(), lit->handle());
|
| + switch (location_) {
|
| + case kAccumulator:
|
| + break;
|
| + case kStack:
|
| + __ push(result_register());
|
| + break;
|
| + }
|
| + DoTest(context);
|
| break;
|
| }
|
| }
|
| @@ -248,32 +328,38 @@
|
| switch (context) {
|
| case Expression::kUninitialized:
|
| UNREACHABLE();
|
| +
|
| case Expression::kEffect:
|
| __ Drop(1);
|
| break;
|
| +
|
| case Expression::kValue:
|
| + switch (location_) {
|
| + case kAccumulator:
|
| + __ pop(result_register());
|
| + break;
|
| + case kStack:
|
| + break;
|
| + }
|
| break;
|
| +
|
| case Expression::kTest:
|
| - __ pop(rax);
|
| - TestAndBranch(rax, true_label_, false_label_);
|
| + __ pop(result_register());
|
| + DoTest(context);
|
| break;
|
| - case Expression::kValueTest: {
|
| - Label discard;
|
| - __ movq(rax, Operand(rsp, 0));
|
| - TestAndBranch(rax, true_label_, &discard);
|
| - __ bind(&discard);
|
| - __ Drop(1);
|
| - __ jmp(false_label_);
|
| +
|
| + case Expression::kValueTest:
|
| + case Expression::kTestValue:
|
| + switch (location_) {
|
| + case kAccumulator:
|
| + __ pop(result_register());
|
| + break;
|
| + case kStack:
|
| + __ movq(result_register(), Operand(rsp, 0));
|
| + break;
|
| + }
|
| + DoTest(context);
|
| break;
|
| - }
|
| - case Expression::kTestValue: {
|
| - Label discard;
|
| - __ movq(rax, Operand(rsp, 0));
|
| - TestAndBranch(rax, &discard, false_label_);
|
| - __ bind(&discard);
|
| - __ Drop(1);
|
| - __ jmp(true_label_);
|
| - }
|
| }
|
| }
|
|
|
| @@ -286,37 +372,244 @@
|
| switch (context) {
|
| case Expression::kUninitialized:
|
| UNREACHABLE();
|
| +
|
| case Expression::kEffect:
|
| __ Drop(count);
|
| break;
|
| +
|
| case Expression::kValue:
|
| - if (count > 1) __ Drop(count - 1);
|
| - __ movq(Operand(rsp, 0), reg);
|
| + switch (location_) {
|
| + case kAccumulator:
|
| + __ Drop(count);
|
| + if (!reg.is(result_register())) __ movq(result_register(), reg);
|
| + break;
|
| + case kStack:
|
| + if (count > 1) __ Drop(count - 1);
|
| + __ movq(Operand(rsp, 0), reg);
|
| + break;
|
| + }
|
| break;
|
| +
|
| case Expression::kTest:
|
| __ Drop(count);
|
| - TestAndBranch(reg, true_label_, false_label_);
|
| + if (!reg.is(result_register())) __ movq(result_register(), reg);
|
| + DoTest(context);
|
| break;
|
| - case Expression::kValueTest: {
|
| - Label discard;
|
| - if (count > 1) __ Drop(count - 1);
|
| - __ movq(Operand(rsp, 0), reg);
|
| - TestAndBranch(reg, true_label_, &discard);
|
| +
|
| + case Expression::kValueTest:
|
| + case Expression::kTestValue:
|
| + switch (location_) {
|
| + case kAccumulator:
|
| + __ Drop(count);
|
| + if (!reg.is(result_register())) __ movq(result_register(), reg);
|
| + break;
|
| + case kStack:
|
| + if (count > 1) __ Drop(count - 1);
|
| + __ movq(result_register(), reg);
|
| + __ movq(Operand(rsp, 0), result_register());
|
| + break;
|
| + }
|
| + DoTest(context);
|
| + break;
|
| + }
|
| +}
|
| +
|
| +
|
| +void FastCodeGenerator::Apply(Expression::Context context,
|
| + Label* materialize_true,
|
| + Label* materialize_false) {
|
| + switch (context) {
|
| + case Expression::kUninitialized:
|
| +
|
| + case Expression::kEffect:
|
| + ASSERT_EQ(materialize_true, materialize_false);
|
| + __ bind(materialize_true);
|
| + break;
|
| +
|
| + case Expression::kValue: {
|
| + Label done;
|
| + switch (location_) {
|
| + case kAccumulator:
|
| + __ bind(materialize_true);
|
| + __ Move(result_register(), Factory::true_value());
|
| + __ jmp(&done);
|
| + __ bind(materialize_false);
|
| + __ Move(result_register(), Factory::false_value());
|
| + break;
|
| + case kStack:
|
| + __ bind(materialize_true);
|
| + __ Push(Factory::true_value());
|
| + __ jmp(&done);
|
| + __ bind(materialize_false);
|
| + __ Push(Factory::false_value());
|
| + break;
|
| + }
|
| + __ bind(&done);
|
| + break;
|
| + }
|
| +
|
| + case Expression::kTest:
|
| + break;
|
| +
|
| + case Expression::kValueTest:
|
| + __ bind(materialize_true);
|
| + switch (location_) {
|
| + case kAccumulator:
|
| + __ Move(result_register(), Factory::true_value());
|
| + break;
|
| + case kStack:
|
| + __ Push(Factory::true_value());
|
| + break;
|
| + }
|
| + __ jmp(true_label_);
|
| + break;
|
| +
|
| + case Expression::kTestValue:
|
| + __ bind(materialize_false);
|
| + switch (location_) {
|
| + case kAccumulator:
|
| + __ Move(result_register(), Factory::false_value());
|
| + break;
|
| + case kStack:
|
| + __ Push(Factory::false_value());
|
| + break;
|
| + }
|
| + __ jmp(false_label_);
|
| + break;
|
| + }
|
| +}
|
| +
|
| +
|
| +void FastCodeGenerator::DoTest(Expression::Context context) {
|
| + // The value to test is in the accumulator. If the value might be needed
|
| + // on the stack (value/test and test/value contexts with a stack location
|
| + // desired), then the value is already duplicated on the stack.
|
| + ASSERT_NE(NULL, true_label_);
|
| + ASSERT_NE(NULL, false_label_);
|
| +
|
| + // In value/test and test/value expression contexts with stack as the
|
| + // desired location, there is already an extra value on the stack. Use a
|
| + // label to discard it if unneeded.
|
| + Label discard;
|
| + Label* if_true = true_label_;
|
| + Label* if_false = false_label_;
|
| + switch (context) {
|
| + case Expression::kUninitialized:
|
| + case Expression::kEffect:
|
| + case Expression::kValue:
|
| + UNREACHABLE();
|
| + case Expression::kTest:
|
| + break;
|
| + case Expression::kValueTest:
|
| + switch (location_) {
|
| + case kAccumulator:
|
| + break;
|
| + case kStack:
|
| + if_false = &discard;
|
| + break;
|
| + }
|
| + break;
|
| + case Expression::kTestValue:
|
| + switch (location_) {
|
| + case kAccumulator:
|
| + break;
|
| + case kStack:
|
| + if_true = &discard;
|
| + break;
|
| + }
|
| + break;
|
| + }
|
| +
|
| + // Emit the inlined tests assumed by the stub.
|
| + __ CompareRoot(result_register(), Heap::kUndefinedValueRootIndex);
|
| + __ j(equal, if_false);
|
| + __ CompareRoot(result_register(), Heap::kTrueValueRootIndex);
|
| + __ j(equal, if_true);
|
| + __ CompareRoot(result_register(), Heap::kFalseValueRootIndex);
|
| + __ j(equal, if_false);
|
| + ASSERT_EQ(0, kSmiTag);
|
| + __ SmiCompare(result_register(), Smi::FromInt(0));
|
| + __ j(equal, if_false);
|
| + Condition is_smi = masm_->CheckSmi(result_register());
|
| + __ j(is_smi, if_true);
|
| +
|
| + // Save a copy of the value if it may be needed and isn't already saved.
|
| + switch (context) {
|
| + case Expression::kUninitialized:
|
| + case Expression::kEffect:
|
| + case Expression::kValue:
|
| + UNREACHABLE();
|
| + case Expression::kTest:
|
| + break;
|
| + case Expression::kValueTest:
|
| + switch (location_) {
|
| + case kAccumulator:
|
| + __ push(result_register());
|
| + break;
|
| + case kStack:
|
| + break;
|
| + }
|
| + break;
|
| + case Expression::kTestValue:
|
| + switch (location_) {
|
| + case kAccumulator:
|
| + __ push(result_register());
|
| + break;
|
| + case kStack:
|
| + break;
|
| + }
|
| + break;
|
| + }
|
| +
|
| + // Call the ToBoolean stub for all other cases.
|
| + ToBooleanStub stub;
|
| + __ push(result_register());
|
| + __ CallStub(&stub);
|
| + __ testq(rax, rax);
|
| +
|
| + // The stub returns nonzero for true. Complete based on the context.
|
| + switch (context) {
|
| + case Expression::kUninitialized:
|
| + case Expression::kEffect:
|
| + case Expression::kValue:
|
| + UNREACHABLE();
|
| +
|
| + case Expression::kTest:
|
| + __ j(not_zero, true_label_);
|
| + __ jmp(false_label_);
|
| + break;
|
| +
|
| + case Expression::kValueTest:
|
| + switch (location_) {
|
| + case kAccumulator:
|
| + __ j(zero, &discard);
|
| + __ pop(result_register());
|
| + __ jmp(true_label_);
|
| + break;
|
| + case kStack:
|
| + __ j(not_zero, true_label_);
|
| + break;
|
| + }
|
| __ bind(&discard);
|
| __ Drop(1);
|
| __ jmp(false_label_);
|
| break;
|
| - }
|
| - case Expression::kTestValue: {
|
| - Label discard;
|
| - if (count > 1) __ Drop(count - 1);
|
| - __ movq(Operand(rsp, 0), reg);
|
| - TestAndBranch(reg, &discard, false_label_);
|
| +
|
| + case Expression::kTestValue:
|
| + switch (location_) {
|
| + case kAccumulator:
|
| + __ j(not_zero, &discard);
|
| + __ pop(result_register());
|
| + __ jmp(false_label_);
|
| + break;
|
| + case kStack:
|
| + __ j(zero, false_label_);
|
| + break;
|
| + }
|
| __ bind(&discard);
|
| __ Drop(1);
|
| __ jmp(true_label_);
|
| break;
|
| - }
|
| }
|
| }
|
|
|
| @@ -362,38 +655,6 @@
|
| }
|
|
|
|
|
| -void FastCodeGenerator::TestAndBranch(Register source,
|
| - Label* true_label,
|
| - Label* false_label) {
|
| - ASSERT_NE(NULL, true_label);
|
| - ASSERT_NE(NULL, false_label);
|
| - // Use the shared ToBoolean stub to compile the value in the register into
|
| - // control flow to the code generator's true and false labels. Perform
|
| - // the fast checks assumed by the stub.
|
| -
|
| - // The undefined value is false.
|
| - __ CompareRoot(source, Heap::kUndefinedValueRootIndex);
|
| - __ j(equal, false_label);
|
| - __ CompareRoot(source, Heap::kTrueValueRootIndex); // True is true.
|
| - __ j(equal, true_label);
|
| - __ CompareRoot(source, Heap::kFalseValueRootIndex); // False is false.
|
| - __ j(equal, false_label);
|
| - ASSERT_EQ(0, kSmiTag);
|
| - __ SmiCompare(source, Smi::FromInt(0)); // The smi zero is false.
|
| - __ j(equal, false_label);
|
| - Condition is_smi = masm_->CheckSmi(source); // All other smis are true.
|
| - __ j(is_smi, true_label);
|
| -
|
| - // Call the stub for all other cases.
|
| - __ push(source);
|
| - ToBooleanStub stub;
|
| - __ CallStub(&stub);
|
| - __ testq(rax, rax); // The stub returns nonzero for true.
|
| - __ j(not_zero, true_label);
|
| - __ jmp(false_label);
|
| -}
|
| -
|
| -
|
| void FastCodeGenerator::VisitDeclaration(Declaration* decl) {
|
| Comment cmnt(masm_, "[ Declaration");
|
| Variable* var = decl->proxy()->var();
|
| @@ -409,8 +670,8 @@
|
| __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex);
|
| __ movq(Operand(rbp, SlotOffset(slot)), kScratchRegister);
|
| } else if (decl->fun() != NULL) {
|
| - Visit(decl->fun());
|
| - __ pop(Operand(rbp, SlotOffset(slot)));
|
| + VisitForValue(decl->fun(), kAccumulator);
|
| + __ movq(Operand(rbp, SlotOffset(slot)), result_register());
|
| }
|
| break;
|
|
|
| @@ -433,11 +694,11 @@
|
| kScratchRegister);
|
| // No write barrier since the hole value is in old space.
|
| } else if (decl->fun() != NULL) {
|
| - Visit(decl->fun());
|
| - __ pop(rax);
|
| - __ movq(CodeGenerator::ContextOperand(rsi, slot->index()), rax);
|
| + VisitForValue(decl->fun(), kAccumulator);
|
| + __ movq(CodeGenerator::ContextOperand(rsi, slot->index()),
|
| + result_register());
|
| int offset = Context::SlotOffset(slot->index());
|
| - __ RecordWrite(rsi, offset, rax, rcx);
|
| + __ RecordWrite(rsi, offset, result_register(), rcx);
|
| }
|
| break;
|
|
|
| @@ -457,7 +718,7 @@
|
| if (decl->mode() == Variable::CONST) {
|
| __ PushRoot(Heap::kTheHoleValueRootIndex);
|
| } else if (decl->fun() != NULL) {
|
| - Visit(decl->fun());
|
| + VisitForValue(decl->fun(), kStack);
|
| } else {
|
| __ Push(Smi::FromInt(0)); // no initial value!
|
| }
|
| @@ -470,24 +731,20 @@
|
| if (decl->fun() != NULL || decl->mode() == Variable::CONST) {
|
| // We are declaring a function or constant that rewrites to a
|
| // property. Use (keyed) IC to set the initial value.
|
| - ASSERT_EQ(Expression::kValue, prop->obj()->context());
|
| - Visit(prop->obj());
|
| - ASSERT_EQ(Expression::kValue, prop->key()->context());
|
| - Visit(prop->key());
|
| + VisitForValue(prop->obj(), kStack);
|
| + VisitForValue(prop->key(), kStack);
|
|
|
| if (decl->fun() != NULL) {
|
| - ASSERT_EQ(Expression::kValue, decl->fun()->context());
|
| - Visit(decl->fun());
|
| - __ pop(rax);
|
| + VisitForValue(decl->fun(), kAccumulator);
|
| } else {
|
| - __ LoadRoot(rax, Heap::kTheHoleValueRootIndex);
|
| + __ LoadRoot(result_register(), Heap::kTheHoleValueRootIndex);
|
| }
|
|
|
| Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
|
| __ call(ic, RelocInfo::CODE_TARGET);
|
| -
|
| // Absence of a test rax instruction following the call
|
| // indicates that none of the load was inlined.
|
| + __ nop();
|
|
|
| // Value in rax is ignored (declarations are statements). Receiver
|
| // and key on stack are discarded.
|
| @@ -532,7 +789,7 @@
|
|
|
|
|
| void FastCodeGenerator::EmitVariableLoad(Variable* var,
|
| - Expression::Context context) {
|
| + Expression::Context context) {
|
| Expression* rewrite = var->rewrite();
|
| if (rewrite == NULL) {
|
| ASSERT(var->is_global());
|
| @@ -547,14 +804,13 @@
|
| // indicate that the inobject property case was inlined. Ensure there
|
| // is no test rax instruction here.
|
| __ nop();
|
| -
|
| DropAndApply(1, context, rax);
|
| } else if (rewrite->AsSlot() != NULL) {
|
| Slot* slot = rewrite->AsSlot();
|
| if (FLAG_debug_code) {
|
| switch (slot->type()) {
|
| - case Slot::LOCAL:
|
| - case Slot::PARAMETER: {
|
| + case Slot::PARAMETER:
|
| + case Slot::LOCAL: {
|
| Comment cmnt(masm_, "Stack slot");
|
| break;
|
| }
|
| @@ -567,7 +823,7 @@
|
| break;
|
| }
|
| }
|
| - Apply(context, slot, rax);
|
| + Apply(context, slot);
|
| } else {
|
| Comment cmnt(masm_, "Variable rewritten to property");
|
| // A variable has been rewritten into an explicit access to an object
|
| @@ -579,9 +835,9 @@
|
| // "slot[literal]".
|
|
|
| // Assert that the object is in a slot.
|
| - Variable* object = property->obj()->AsVariableProxy()->AsVariable();
|
| - ASSERT_NOT_NULL(object);
|
| - Slot* object_slot = object->slot();
|
| + Variable* object_var = property->obj()->AsVariableProxy()->AsVariable();
|
| + ASSERT_NOT_NULL(object_var);
|
| + Slot* object_slot = object_var->slot();
|
| ASSERT_NOT_NULL(object_slot);
|
|
|
| // Load the object.
|
| @@ -601,7 +857,7 @@
|
| __ call(ic, RelocInfo::CODE_TARGET);
|
| // Notice: We must not have a "test rax, ..." instruction after the
|
| // call. It is treated specially by the LoadIC code.
|
| -
|
| + __ nop();
|
| // Drop key and object left on the stack by IC, and push the result.
|
| DropAndApply(2, context, rax);
|
| }
|
| @@ -629,7 +885,6 @@
|
| __ Push(expr->pattern());
|
| __ Push(expr->flags());
|
| __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
|
| - // Label done:
|
| __ bind(&done);
|
| Apply(expr->context(), rax);
|
| }
|
| @@ -647,9 +902,8 @@
|
| __ CallRuntime(Runtime::kCreateObjectLiteralShallow, 3);
|
| }
|
|
|
| - // If result_saved == true: The result is saved on top of the
|
| - // stack and in rax.
|
| - // If result_saved == false: The result not on the stack, just in rax.
|
| + // If result_saved is true the result is on top of the stack. If
|
| + // result_saved is false the result is in rax.
|
| bool result_saved = false;
|
|
|
| for (int i = 0; i < expr->properties()->length(); i++) {
|
| @@ -663,77 +917,45 @@
|
| result_saved = true;
|
| }
|
| switch (property->kind()) {
|
| + case ObjectLiteral::Property::CONSTANT:
|
| + UNREACHABLE();
|
| case ObjectLiteral::Property::MATERIALIZED_LITERAL:
|
| ASSERT(!CompileTimeValue::IsCompileTimeValue(value));
|
| + // Fall through.
|
| case ObjectLiteral::Property::COMPUTED:
|
| if (key->handle()->IsSymbol()) {
|
| - Visit(value);
|
| - ASSERT_EQ(Expression::kValue, value->context());
|
| - __ pop(rax);
|
| + VisitForValue(value, kAccumulator);
|
| __ Move(rcx, key->handle());
|
| Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
|
| __ call(ic, RelocInfo::CODE_TARGET);
|
| + __ nop();
|
| // StoreIC leaves the receiver on the stack.
|
| - __ movq(rax, Operand(rsp, 0)); // Restore result back into rax.
|
| break;
|
| }
|
| // Fall through.
|
| case ObjectLiteral::Property::PROTOTYPE:
|
| - __ push(rax);
|
| - Visit(key);
|
| - ASSERT_EQ(Expression::kValue, key->context());
|
| - Visit(value);
|
| - ASSERT_EQ(Expression::kValue, value->context());
|
| + __ push(Operand(rsp, 0)); // Duplicate receiver.
|
| + VisitForValue(key, kStack);
|
| + VisitForValue(value, kStack);
|
| __ CallRuntime(Runtime::kSetProperty, 3);
|
| - __ movq(rax, Operand(rsp, 0)); // Restore result into rax.
|
| break;
|
| case ObjectLiteral::Property::SETTER:
|
| case ObjectLiteral::Property::GETTER:
|
| - __ push(rax);
|
| - Visit(key);
|
| - ASSERT_EQ(Expression::kValue, key->context());
|
| + __ push(Operand(rsp, 0)); // Duplicate receiver.
|
| + VisitForValue(key, kStack);
|
| __ Push(property->kind() == ObjectLiteral::Property::SETTER ?
|
| Smi::FromInt(1) :
|
| Smi::FromInt(0));
|
| - Visit(value);
|
| - ASSERT_EQ(Expression::kValue, value->context());
|
| + VisitForValue(value, kStack);
|
| __ CallRuntime(Runtime::kDefineAccessor, 4);
|
| - __ movq(rax, Operand(rsp, 0)); // Restore result into rax.
|
| break;
|
| - default: UNREACHABLE();
|
| }
|
| }
|
| - switch (expr->context()) {
|
| - case Expression::kUninitialized:
|
| - UNREACHABLE();
|
| - case Expression::kEffect:
|
| - if (result_saved) __ Drop(1);
|
| - break;
|
| - case Expression::kValue:
|
| - if (!result_saved) __ push(rax);
|
| - break;
|
| - case Expression::kTest:
|
| - if (result_saved) __ pop(rax);
|
| - TestAndBranch(rax, true_label_, false_label_);
|
| - break;
|
| - case Expression::kValueTest: {
|
| - Label discard;
|
| - if (!result_saved) __ push(rax);
|
| - TestAndBranch(rax, true_label_, &discard);
|
| - __ bind(&discard);
|
| - __ Drop(1);
|
| - __ jmp(false_label_);
|
| - break;
|
| - }
|
| - case Expression::kTestValue: {
|
| - Label discard;
|
| - if (!result_saved) __ push(rax);
|
| - TestAndBranch(rax, &discard, false_label_);
|
| - __ bind(&discard);
|
| - __ Drop(1);
|
| - __ jmp(true_label_);
|
| - break;
|
| - }
|
| +
|
| + if (result_saved) {
|
| + ApplyTOS(expr->context());
|
| + } else {
|
| + Apply(expr->context(), rax);
|
| }
|
| }
|
|
|
| @@ -768,77 +990,47 @@
|
| __ push(rax);
|
| result_saved = true;
|
| }
|
| - Visit(subexpr);
|
| - ASSERT_EQ(Expression::kValue, subexpr->context());
|
| + VisitForValue(subexpr, kAccumulator);
|
|
|
| // Store the subexpression value in the array's elements.
|
| - __ pop(rax); // Subexpression value.
|
| __ movq(rbx, Operand(rsp, 0)); // Copy of array literal.
|
| __ movq(rbx, FieldOperand(rbx, JSObject::kElementsOffset));
|
| int offset = FixedArray::kHeaderSize + (i * kPointerSize);
|
| - __ movq(FieldOperand(rbx, offset), rax);
|
| + __ movq(FieldOperand(rbx, offset), result_register());
|
|
|
| // Update the write barrier for the array store.
|
| - __ RecordWrite(rbx, offset, rax, rcx);
|
| + __ RecordWrite(rbx, offset, result_register(), rcx);
|
| }
|
|
|
| - switch (expr->context()) {
|
| - case Expression::kUninitialized:
|
| - UNREACHABLE();
|
| - case Expression::kEffect:
|
| - if (result_saved) __ Drop(1);
|
| - break;
|
| - case Expression::kValue:
|
| - if (!result_saved) __ push(rax);
|
| - break;
|
| - case Expression::kTest:
|
| - if (result_saved) __ pop(rax);
|
| - TestAndBranch(rax, true_label_, false_label_);
|
| - break;
|
| - case Expression::kValueTest: {
|
| - Label discard;
|
| - if (!result_saved) __ push(rax);
|
| - TestAndBranch(rax, true_label_, &discard);
|
| - __ bind(&discard);
|
| - __ Drop(1);
|
| - __ jmp(false_label_);
|
| - break;
|
| - }
|
| - case Expression::kTestValue: {
|
| - Label discard;
|
| - if (!result_saved) __ push(rax);
|
| - TestAndBranch(rax, &discard, false_label_);
|
| - __ bind(&discard);
|
| - __ Drop(1);
|
| - __ jmp(true_label_);
|
| - break;
|
| - }
|
| + if (result_saved) {
|
| + ApplyTOS(expr->context());
|
| + } else {
|
| + Apply(expr->context(), rax);
|
| }
|
| }
|
|
|
|
|
| -void FastCodeGenerator::EmitNamedPropertyLoad(Property* prop,
|
| - Expression::Context context) {
|
| +void FastCodeGenerator::EmitNamedPropertyLoad(Property* prop) {
|
| SetSourcePosition(prop->position());
|
| Literal* key = prop->key()->AsLiteral();
|
| __ Move(rcx, key->handle());
|
| Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
|
| __ Call(ic, RelocInfo::CODE_TARGET);
|
| - Apply(context, rax);
|
| + __ nop();
|
| }
|
|
|
|
|
| -void FastCodeGenerator::EmitKeyedPropertyLoad(Property* prop,
|
| - Expression::Context context) {
|
| +void FastCodeGenerator::EmitKeyedPropertyLoad(Property* prop) {
|
| SetSourcePosition(prop->position());
|
| Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
|
| __ Call(ic, RelocInfo::CODE_TARGET);
|
| - Apply(context, rax);
|
| + __ nop();
|
| }
|
|
|
|
|
| -void FastCodeGenerator::EmitCompoundAssignmentOp(Token::Value op,
|
| - Expression::Context context) {
|
| +void FastCodeGenerator::EmitBinaryOp(Token::Value op,
|
| + Expression::Context context) {
|
| + __ push(result_register());
|
| GenericBinaryOpStub stub(op,
|
| NO_OVERWRITE,
|
| NO_GENERIC_BINARY_FLAGS);
|
| @@ -855,7 +1047,6 @@
|
| // Assignment to a global variable. Use inline caching for the
|
| // assignment. Right-hand-side value is passed in rax, variable name in
|
| // rcx, and the global object on the stack.
|
| - __ pop(rax);
|
| __ Move(rcx, var->name());
|
| __ push(CodeGenerator::GlobalObject());
|
| Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
|
| @@ -867,66 +1058,18 @@
|
| Slot* slot = var->slot();
|
| switch (slot->type()) {
|
| case Slot::LOCAL:
|
| - case Slot::PARAMETER: {
|
| - Operand target = Operand(rbp, SlotOffset(slot));
|
| - switch (context) {
|
| - case Expression::kUninitialized:
|
| - UNREACHABLE();
|
| - case Expression::kEffect:
|
| - // Perform assignment and discard value.
|
| - __ pop(target);
|
| - break;
|
| - case Expression::kValue:
|
| - // Perform assignment and preserve value.
|
| - __ movq(rax, Operand(rsp, 0));
|
| - __ movq(target, rax);
|
| - break;
|
| - case Expression::kTest:
|
| - // Perform assignment and test (and discard) value.
|
| - __ pop(rax);
|
| - __ movq(target, rax);
|
| - TestAndBranch(rax, true_label_, false_label_);
|
| - break;
|
| - case Expression::kValueTest: {
|
| - Label discard;
|
| - __ movq(rax, Operand(rsp, 0));
|
| - __ movq(target, rax);
|
| - TestAndBranch(rax, true_label_, &discard);
|
| - __ bind(&discard);
|
| - __ Drop(1);
|
| - __ jmp(false_label_);
|
| - break;
|
| - }
|
| - case Expression::kTestValue: {
|
| - Label discard;
|
| - __ movq(rax, Operand(rsp, 0));
|
| - __ movq(target, rax);
|
| - TestAndBranch(rax, &discard, false_label_);
|
| - __ bind(&discard);
|
| - __ Drop(1);
|
| - __ jmp(true_label_);
|
| - break;
|
| - }
|
| - }
|
| + case Slot::PARAMETER:
|
| + __ movq(Operand(rbp, SlotOffset(slot)), result_register());
|
| break;
|
| - }
|
|
|
| case Slot::CONTEXT: {
|
| MemOperand target = EmitSlotSearch(slot, rcx);
|
| - __ pop(rax);
|
| - __ movq(target, rax);
|
| + __ movq(target, result_register());
|
|
|
| // RecordWrite may destroy all its register arguments.
|
| - if (context == Expression::kValue) {
|
| - __ push(rax);
|
| - } else if (context != Expression::kEffect) {
|
| - __ movq(rdx, rax);
|
| - }
|
| + __ movq(rdx, result_register());
|
| int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
|
| - __ RecordWrite(rcx, offset, rax, rbx);
|
| - if (context != Expression::kEffect && context != Expression::kValue) {
|
| - Apply(context, rdx);
|
| - }
|
| + __ RecordWrite(rcx, offset, rdx, rbx);
|
| break;
|
| }
|
|
|
| @@ -934,6 +1077,7 @@
|
| UNREACHABLE();
|
| break;
|
| }
|
| + Apply(context, result_register());
|
| } else {
|
| // Variables rewritten as properties are not treated as variables in
|
| // assignments.
|
| @@ -952,14 +1096,18 @@
|
| // change to slow case to avoid the quadratic behavior of repeatedly
|
| // adding fast properties.
|
| if (expr->starts_initialization_block()) {
|
| - __ push(Operand(rsp, kPointerSize)); // Receiver is under value.
|
| + __ push(result_register());
|
| + __ push(Operand(rsp, kPointerSize)); // Receiver is now under value.
|
| __ CallRuntime(Runtime::kToSlowProperties, 1);
|
| + __ pop(result_register());
|
| }
|
|
|
| - __ pop(rax);
|
| + // Record source code position before IC call.
|
| + SetSourcePosition(expr->position());
|
| __ Move(rcx, prop->key()->AsLiteral()->handle());
|
| Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
|
| __ Call(ic, RelocInfo::CODE_TARGET);
|
| + __ nop();
|
|
|
| // If the assignment ends an initialization block, revert to fast case.
|
| if (expr->ends_initialization_block()) {
|
| @@ -980,12 +1128,15 @@
|
| // change to slow case to avoid the quadratic behavior of repeatedly
|
| // adding fast properties.
|
| if (expr->starts_initialization_block()) {
|
| - // Reciever is under the key and value.
|
| + __ push(result_register());
|
| + // Receiver is now under the key and value.
|
| __ push(Operand(rsp, 2 * kPointerSize));
|
| __ CallRuntime(Runtime::kToSlowProperties, 1);
|
| + __ pop(result_register());
|
| }
|
|
|
| - __ pop(rax);
|
| + // Record source code position before IC call.
|
| + SetSourcePosition(expr->position());
|
| Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
|
| __ Call(ic, RelocInfo::CODE_TARGET);
|
| // This nop signals to the IC that there is no inlined code at the call
|
| @@ -995,7 +1146,7 @@
|
| // If the assignment ends an initialization block, revert to fast case.
|
| if (expr->ends_initialization_block()) {
|
| __ push(rax); // Result of assignment, saved even if not needed.
|
| - // Reciever is under the key and value.
|
| + // Receiver is under the key and value.
|
| __ push(Operand(rsp, 2 * kPointerSize));
|
| __ CallRuntime(Runtime::kToFastProperties, 1);
|
| __ pop(rax);
|
| @@ -1010,30 +1161,16 @@
|
| Comment cmnt(masm_, "[ Property");
|
| Expression* key = expr->key();
|
|
|
| - // Record the source position for the property load.
|
| - SetSourcePosition(expr->position());
|
| -
|
| // Evaluate receiver.
|
| - Visit(expr->obj());
|
| + VisitForValue(expr->obj(), kStack);
|
|
|
| if (key->IsPropertyName()) {
|
| - // Do a named property load. The IC expects the property name in rcx
|
| - // and the receiver on the stack.
|
| - __ Move(rcx, key->AsLiteral()->handle());
|
| - Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
|
| - __ call(ic, RelocInfo::CODE_TARGET);
|
| - // By emitting a nop we make sure that we do not have a "test rax,..."
|
| - // instruction after the call it is treated specially by the LoadIC code.
|
| - __ nop();
|
| + EmitNamedPropertyLoad(expr);
|
| + // Drop receiver left on the stack by IC.
|
| DropAndApply(1, expr->context(), rax);
|
| } else {
|
| - // Do a keyed property load.
|
| - Visit(expr->key());
|
| - Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
|
| - __ call(ic, RelocInfo::CODE_TARGET);
|
| - // Notice: We must not have a "test rax, ..." instruction after the
|
| - // call. It is treated specially by the LoadIC code.
|
| - __ nop();
|
| + VisitForValue(expr->key(), kStack);
|
| + EmitKeyedPropertyLoad(expr);
|
| // Drop key and receiver left on the stack by IC.
|
| DropAndApply(2, expr->context(), rax);
|
| }
|
| @@ -1047,15 +1184,15 @@
|
| ZoneList<Expression*>* args = expr->arguments();
|
| int arg_count = args->length();
|
| for (int i = 0; i < arg_count; i++) {
|
| - Visit(args->at(i));
|
| - ASSERT_EQ(Expression::kValue, args->at(i)->context());
|
| + VisitForValue(args->at(i), kStack);
|
| }
|
| // Record source position for debugger.
|
| SetSourcePosition(expr->position());
|
| // Call the IC initialization code.
|
| + InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
|
| Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count,
|
| - NOT_IN_LOOP);
|
| - __ call(ic, mode);
|
| + in_loop);
|
| + __ Call(ic, mode);
|
| // Restore context register.
|
| __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
|
| // Discard the function left on TOS.
|
| @@ -1068,7 +1205,7 @@
|
| ZoneList<Expression*>* args = expr->arguments();
|
| int arg_count = args->length();
|
| for (int i = 0; i < arg_count; i++) {
|
| - Visit(args->at(i));
|
| + VisitForValue(args->at(i), kStack);
|
| }
|
| // Record source position for debugger.
|
| SetSourcePosition(expr->position());
|
| @@ -1106,13 +1243,13 @@
|
| if (key != NULL && key->handle()->IsSymbol()) {
|
| // Call to a named property, use call IC.
|
| __ Push(key->handle());
|
| - Visit(prop->obj());
|
| + VisitForValue(prop->obj(), kStack);
|
| EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET);
|
| } else {
|
| // Call to a keyed property, use keyed load IC followed by function
|
| // call.
|
| - Visit(prop->obj());
|
| - Visit(prop->key());
|
| + VisitForValue(prop->obj(), kStack);
|
| + VisitForValue(prop->key(), kStack);
|
| // Record source code position for IC call.
|
| SetSourcePosition(prop->position());
|
| Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
|
| @@ -1145,7 +1282,7 @@
|
| loop_depth() == 0) {
|
| lit->set_try_fast_codegen(true);
|
| }
|
| - Visit(fun);
|
| + VisitForValue(fun, kStack);
|
| // Load global receiver object.
|
| __ movq(rbx, CodeGenerator::GlobalObject());
|
| __ push(FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset));
|
| @@ -1161,9 +1298,7 @@
|
| // expression in new calls must be evaluated before the
|
| // arguments.
|
| // Push function on the stack.
|
| - Visit(expr->expression());
|
| - ASSERT_EQ(Expression::kValue, expr->expression()->context());
|
| - // If location is value, already on the stack,
|
| + VisitForValue(expr->expression(), kStack);
|
|
|
| // Push global object (receiver).
|
| __ push(CodeGenerator::GlobalObject());
|
| @@ -1172,10 +1307,7 @@
|
| ZoneList<Expression*>* args = expr->arguments();
|
| int arg_count = args->length();
|
| for (int i = 0; i < arg_count; i++) {
|
| - Visit(args->at(i));
|
| - ASSERT_EQ(Expression::kValue, args->at(i)->context());
|
| - // If location is value, it is already on the stack,
|
| - // so nothing to do here.
|
| + VisitForValue(args->at(i), kStack);
|
| }
|
|
|
| // Call the construct call builtin that handles allocation and
|
| @@ -1209,8 +1341,7 @@
|
| // Push the arguments ("left-to-right").
|
| int arg_count = args->length();
|
| for (int i = 0; i < arg_count; i++) {
|
| - Visit(args->at(i));
|
| - ASSERT_EQ(Expression::kValue, args->at(i)->context());
|
| + VisitForValue(args->at(i), kStack);
|
| }
|
|
|
| if (expr->is_jsruntime()) {
|
| @@ -1233,8 +1364,8 @@
|
| switch (expr->op()) {
|
| case Token::VOID: {
|
| Comment cmnt(masm_, "[ UnaryOperation (VOID)");
|
| + ASSERT_EQ(Expression::kEffect, expr->expression()->context());
|
| Visit(expr->expression());
|
| - ASSERT_EQ(Expression::kEffect, expr->expression()->context());
|
| switch (expr->context()) {
|
| case Expression::kUninitialized:
|
| UNREACHABLE();
|
| @@ -1242,11 +1373,25 @@
|
| case Expression::kEffect:
|
| break;
|
| case Expression::kValue:
|
| - __ PushRoot(Heap::kUndefinedValueRootIndex);
|
| + switch (location_) {
|
| + case kAccumulator:
|
| + __ LoadRoot(result_register(), Heap::kUndefinedValueRootIndex);
|
| + break;
|
| + case kStack:
|
| + __ PushRoot(Heap::kUndefinedValueRootIndex);
|
| + break;
|
| + }
|
| break;
|
| case Expression::kTestValue:
|
| // Value is false so it's needed.
|
| - __ PushRoot(Heap::kUndefinedValueRootIndex);
|
| + switch (location_) {
|
| + case kAccumulator:
|
| + __ LoadRoot(result_register(), Heap::kUndefinedValueRootIndex);
|
| + break;
|
| + case kStack:
|
| + __ PushRoot(Heap::kUndefinedValueRootIndex);
|
| + break;
|
| + }
|
| // Fall through.
|
| case Expression::kTest:
|
| case Expression::kValueTest:
|
| @@ -1260,45 +1405,34 @@
|
| Comment cmnt(masm_, "[ UnaryOperation (NOT)");
|
| ASSERT_EQ(Expression::kTest, expr->expression()->context());
|
|
|
| - Label push_true, push_false, done;
|
| + Label materialize_true, materialize_false, done;
|
| + // Initially assume a pure test context. Notice that the labels are
|
| + // swapped.
|
| + Label* if_true = false_label_;
|
| + Label* if_false = true_label_;
|
| switch (expr->context()) {
|
| case Expression::kUninitialized:
|
| UNREACHABLE();
|
| break;
|
| -
|
| + case Expression::kEffect:
|
| + if_true = &done;
|
| + if_false = &done;
|
| + break;
|
| case Expression::kValue:
|
| - VisitForControl(expr->expression(), &push_false, &push_true);
|
| - __ bind(&push_true);
|
| - __ PushRoot(Heap::kTrueValueRootIndex);
|
| - __ jmp(&done);
|
| - __ bind(&push_false);
|
| - __ PushRoot(Heap::kFalseValueRootIndex);
|
| - __ bind(&done);
|
| + if_true = &materialize_false;
|
| + if_false = &materialize_true;
|
| break;
|
| -
|
| - case Expression::kEffect:
|
| - VisitForControl(expr->expression(), &done, &done);
|
| - __ bind(&done);
|
| - break;
|
| -
|
| case Expression::kTest:
|
| - VisitForControl(expr->expression(), false_label_, true_label_);
|
| break;
|
| -
|
| case Expression::kValueTest:
|
| - VisitForControl(expr->expression(), false_label_, &push_true);
|
| - __ bind(&push_true);
|
| - __ PushRoot(Heap::kTrueValueRootIndex);
|
| - __ jmp(true_label_);
|
| + if_false = &materialize_true;
|
| break;
|
| -
|
| case Expression::kTestValue:
|
| - VisitForControl(expr->expression(), &push_false, true_label_);
|
| - __ bind(&push_false);
|
| - __ PushRoot(Heap::kFalseValueRootIndex);
|
| - __ jmp(false_label_);
|
| + if_true = &materialize_false;
|
| break;
|
| }
|
| + VisitForControl(expr->expression(), if_true, if_false);
|
| + Apply(expr->context(), if_false, if_true); // Labels swapped.
|
| break;
|
| }
|
|
|
| @@ -1327,7 +1461,7 @@
|
| __ push(rax);
|
| } else {
|
| // This expression cannot throw a reference error at the top level.
|
| - Visit(expr->expression());
|
| + VisitForValue(expr->expression(), kStack);
|
| }
|
|
|
| __ CallRuntime(Runtime::kTypeof, 1);
|
| @@ -1360,23 +1494,25 @@
|
| // Evaluate expression and get value.
|
| if (assign_type == VARIABLE) {
|
| ASSERT(expr->expression()->AsVariableProxy()->var() != NULL);
|
| + Location saved_location = location_;
|
| + location_ = kStack;
|
| EmitVariableLoad(expr->expression()->AsVariableProxy()->var(),
|
| Expression::kValue);
|
| + location_ = saved_location;
|
| } else {
|
| // Reserve space for result of postfix operation.
|
| if (expr->is_postfix() && expr->context() != Expression::kEffect) {
|
| ASSERT(expr->context() != Expression::kUninitialized);
|
| __ Push(Smi::FromInt(0));
|
| }
|
| - Visit(prop->obj());
|
| - ASSERT_EQ(Expression::kValue, prop->obj()->context());
|
| + VisitForValue(prop->obj(), kStack);
|
| if (assign_type == NAMED_PROPERTY) {
|
| - EmitNamedPropertyLoad(prop, Expression::kValue);
|
| + EmitNamedPropertyLoad(prop);
|
| } else {
|
| - Visit(prop->key());
|
| - ASSERT_EQ(Expression::kValue, prop->key()->context());
|
| - EmitKeyedPropertyLoad(prop, Expression::kValue);
|
| + VisitForValue(prop->key(), kStack);
|
| + EmitKeyedPropertyLoad(prop);
|
| }
|
| + __ push(rax);
|
| }
|
|
|
| // Convert to number.
|
| @@ -1390,10 +1526,10 @@
|
| case Expression::kEffect:
|
| // Do not save result.
|
| break;
|
| - case Expression::kValue: // Fall through
|
| - case Expression::kTest: // Fall through
|
| - case Expression::kTestValue: // Fall through
|
| + case Expression::kValue:
|
| + case Expression::kTest:
|
| case Expression::kValueTest:
|
| + case Expression::kTestValue:
|
| // Save the result on the stack. If we have a named or keyed property
|
| // we store the result under the receiver that is currently on top
|
| // of the stack.
|
| @@ -1412,19 +1548,17 @@
|
| }
|
| }
|
|
|
| - // Call runtime for +1/-1.
|
| + // Call stub for +1/-1.
|
| __ push(rax);
|
| __ Push(Smi::FromInt(1));
|
| - if (expr->op() == Token::INC) {
|
| - __ CallRuntime(Runtime::kNumberAdd, 2);
|
| - } else {
|
| - __ CallRuntime(Runtime::kNumberSub, 2);
|
| - }
|
| + GenericBinaryOpStub stub(expr->binary_op(),
|
| + NO_OVERWRITE,
|
| + NO_GENERIC_BINARY_FLAGS);
|
| + __ CallStub(&stub);
|
|
|
| // Store the value returned in rax.
|
| switch (assign_type) {
|
| case VARIABLE:
|
| - __ push(rax);
|
| if (expr->is_postfix()) {
|
| EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
|
| Expression::kEffect);
|
| @@ -1499,20 +1633,12 @@
|
| case Token::BIT_XOR:
|
| case Token::SHL:
|
| case Token::SHR:
|
| - case Token::SAR: {
|
| - ASSERT_EQ(Expression::kValue, expr->left()->context());
|
| - ASSERT_EQ(Expression::kValue, expr->right()->context());
|
| + case Token::SAR:
|
| + VisitForValue(expr->left(), kStack);
|
| + VisitForValue(expr->right(), kAccumulator);
|
| + EmitBinaryOp(expr->op(), expr->context());
|
| + break;
|
|
|
| - Visit(expr->left());
|
| - Visit(expr->right());
|
| - GenericBinaryOpStub stub(expr->op(),
|
| - NO_OVERWRITE,
|
| - NO_GENERIC_BINARY_FLAGS);
|
| - __ CallStub(&stub);
|
| - Apply(expr->context(), rax);
|
| -
|
| - break;
|
| - }
|
| default:
|
| UNREACHABLE();
|
| }
|
| @@ -1521,14 +1647,10 @@
|
|
|
| void FastCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
|
| Comment cmnt(masm_, "[ CompareOperation");
|
| - ASSERT_EQ(Expression::kValue, expr->left()->context());
|
| - ASSERT_EQ(Expression::kValue, expr->right()->context());
|
| - Visit(expr->left());
|
| - Visit(expr->right());
|
|
|
| // Always perform the comparison for its control flow. Pack the result
|
| // into the expression's context after the comparison is performed.
|
| - Label push_true, push_false, done;
|
| + Label materialize_true, materialize_false, done;
|
| // Initially assume we are in a test context.
|
| Label* if_true = true_label_;
|
| Label* if_false = false_label_;
|
| @@ -1536,34 +1658,36 @@
|
| case Expression::kUninitialized:
|
| UNREACHABLE();
|
| break;
|
| - case Expression::kValue:
|
| - if_true = &push_true;
|
| - if_false = &push_false;
|
| - break;
|
| case Expression::kEffect:
|
| if_true = &done;
|
| if_false = &done;
|
| break;
|
| + case Expression::kValue:
|
| + if_true = &materialize_true;
|
| + if_false = &materialize_false;
|
| + break;
|
| case Expression::kTest:
|
| break;
|
| case Expression::kValueTest:
|
| - if_true = &push_true;
|
| + if_true = &materialize_true;
|
| break;
|
| case Expression::kTestValue:
|
| - if_false = &push_false;
|
| + if_false = &materialize_false;
|
| break;
|
| }
|
|
|
| + VisitForValue(expr->left(), kStack);
|
| switch (expr->op()) {
|
| - case Token::IN: {
|
| + case Token::IN:
|
| + VisitForValue(expr->right(), kStack);
|
| __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION);
|
| __ CompareRoot(rax, Heap::kTrueValueRootIndex);
|
| __ j(equal, if_true);
|
| __ jmp(if_false);
|
| break;
|
| - }
|
|
|
| case Token::INSTANCEOF: {
|
| + VisitForValue(expr->right(), kStack);
|
| InstanceofStub stub;
|
| __ CallStub(&stub);
|
| __ testq(rax, rax);
|
| @@ -1573,6 +1697,7 @@
|
| }
|
|
|
| default: {
|
| + VisitForValue(expr->right(), kAccumulator);
|
| Condition cc = no_condition;
|
| bool strict = false;
|
| switch (expr->op()) {
|
| @@ -1581,29 +1706,26 @@
|
| // Fall through.
|
| case Token::EQ:
|
| cc = equal;
|
| - __ pop(rax);
|
| __ pop(rdx);
|
| break;
|
| case Token::LT:
|
| cc = less;
|
| - __ pop(rax);
|
| __ pop(rdx);
|
| break;
|
| case Token::GT:
|
| // Reverse left and right sizes to obtain ECMA-262 conversion order.
|
| cc = less;
|
| - __ pop(rdx);
|
| + __ movq(rdx, result_register());
|
| __ pop(rax);
|
| break;
|
| case Token::LTE:
|
| // Reverse left and right sizes to obtain ECMA-262 conversion order.
|
| cc = greater_equal;
|
| - __ pop(rdx);
|
| + __ movq(rdx, result_register());
|
| __ pop(rax);
|
| break;
|
| case Token::GTE:
|
| cc = greater_equal;
|
| - __ pop(rax);
|
| __ pop(rdx);
|
| break;
|
| case Token::IN:
|
| @@ -1631,39 +1753,7 @@
|
|
|
| // Convert the result of the comparison into one expected for this
|
| // expression's context.
|
| - switch (expr->context()) {
|
| - case Expression::kUninitialized:
|
| - UNREACHABLE();
|
| - break;
|
| -
|
| - case Expression::kEffect:
|
| - __ bind(&done);
|
| - break;
|
| -
|
| - case Expression::kValue:
|
| - __ bind(&push_true);
|
| - __ PushRoot(Heap::kTrueValueRootIndex);
|
| - __ jmp(&done);
|
| - __ bind(&push_false);
|
| - __ PushRoot(Heap::kFalseValueRootIndex);
|
| - __ bind(&done);
|
| - break;
|
| -
|
| - case Expression::kTest:
|
| - break;
|
| -
|
| - case Expression::kValueTest:
|
| - __ bind(&push_true);
|
| - __ PushRoot(Heap::kTrueValueRootIndex);
|
| - __ jmp(true_label_);
|
| - break;
|
| -
|
| - case Expression::kTestValue:
|
| - __ bind(&push_false);
|
| - __ PushRoot(Heap::kFalseValueRootIndex);
|
| - __ jmp(false_label_);
|
| - break;
|
| - }
|
| + Apply(expr->context(), if_true, if_false);
|
| }
|
|
|
|
|
|
|