diff --git a/src/arm/fast-codegen-arm.cc b/src/arm/fast-codegen-arm.cc index a6b35b070c..a67916f7c9 100644 --- a/src/arm/fast-codegen-arm.cc +++ b/src/arm/fast-codegen-arm.cc @@ -214,20 +214,57 @@ void FastCodeGenerator::EmitReturnSequence(int position) { } -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())) __ mov(result_register(), reg); + break; + case kStack: + __ push(reg); + break; + } + break; + + case Expression::kValueTest: + case Expression::kTestValue: + // Push an extra copy of the value in case it's needed. + __ push(reg); + // Fall through. + + case Expression::kTest: + // We always call the runtime on ARM, so push the value as argument. + __ push(reg); + 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: case Expression::kTest: case Expression::kValueTest: case Expression::kTestValue: - Move(scratch, slot); - Apply(context, scratch); + // On ARM we have to move the value into a register to do anything + // with it. + Move(result_register(), slot); + Apply(context, result_register()); break; } } @@ -239,12 +276,15 @@ void FastCodeGenerator::Apply(Expression::Context context, Literal* lit) { UNREACHABLE(); case Expression::kEffect: break; + // Nothing to do. case Expression::kValue: case Expression::kTest: case Expression::kValueTest: case Expression::kTestValue: - __ mov(ip, Operand(lit->handle())); - Apply(context, ip); + // On ARM we have to move the value into a register to do anything + // with it. + __ mov(result_register(), Operand(lit->handle())); + Apply(context, result_register()); break; } } @@ -254,32 +294,31 @@ void FastCodeGenerator::ApplyTOS(Expression::Context context) { 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::kValueTest: + case Expression::kTestValue: + // Duplicate the value on the stack in case it's needed. + __ ldr(ip, MemOperand(sp)); + __ push(ip); + // Fall through. + case Expression::kTest: - __ pop(r0); - TestAndBranch(r0, true_label_, false_label_); + DoTest(context); break; - case Expression::kValueTest: { - Label discard; - __ ldr(r0, MemOperand(sp, 0)); - TestAndBranch(r0, true_label_, &discard); - __ bind(&discard); - __ Drop(1); - __ jmp(false_label_); - break; - } - case Expression::kTestValue: { - Label discard; - __ ldr(r0, MemOperand(sp, 0)); - TestAndBranch(r0, &discard, false_label_); - __ bind(&discard); - __ Drop(1); - __ jmp(true_label_); - } } } @@ -292,32 +331,161 @@ void FastCodeGenerator::DropAndApply(int count, switch (context) { case Expression::kUninitialized: UNREACHABLE(); + case Expression::kEffect: __ Drop(count); break; + case Expression::kValue: + switch (location_) { + case kAccumulator: + __ Drop(count); + if (!reg.is(result_register())) __ mov(result_register(), reg); + break; + case kStack: + if (count > 1) __ Drop(count - 1); + __ str(reg, MemOperand(sp)); + break; + } + break; + + case Expression::kTest: if (count > 1) __ Drop(count - 1); __ str(reg, MemOperand(sp)); + DoTest(context); break; + + case Expression::kValueTest: + case Expression::kTestValue: + if (count == 1) { + __ str(reg, MemOperand(sp)); + __ push(reg); + } else { // count > 1 + __ Drop(count - 2); + __ str(reg, MemOperand(sp, kPointerSize)); + __ str(reg, MemOperand(sp)); + } + 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; + __ bind(materialize_true); + __ mov(result_register(), Operand(Factory::true_value())); + __ jmp(&done); + __ bind(materialize_false); + __ mov(result_register(), Operand(Factory::false_value())); + __ bind(&done); + switch (location_) { + case kAccumulator: + break; + case kStack: + __ push(result_register()); + break; + } + break; + } + case Expression::kTest: - __ Drop(count); - TestAndBranch(reg, true_label_, false_label_); break; + + case Expression::kValueTest: + __ bind(materialize_true); + __ mov(result_register(), Operand(Factory::true_value())); + switch (location_) { + case kAccumulator: + break; + case kStack: + __ push(result_register()); + break; + } + __ jmp(true_label_); + break; + + case Expression::kTestValue: + __ bind(materialize_false); + __ mov(result_register(), Operand(Factory::false_value())); + switch (location_) { + case kAccumulator: + break; + case kStack: + __ push(result_register()); + break; + } + __ jmp(false_label_); + break; + } +} + + +void FastCodeGenerator::DoTest(Expression::Context context) { + // The value to test is pushed on the stack, and duplicated on the stack + // if necessary (for value/test and test/value contexts). + ASSERT_NE(NULL, true_label_); + ASSERT_NE(NULL, false_label_); + + // Call the runtime to find the boolean value of the source and then + // translate it into control flow to the pair of labels. + __ CallRuntime(Runtime::kToBool, 1); + __ LoadRoot(ip, Heap::kTrueValueRootIndex); + __ cmp(r0, ip); + + // Complete based on the context. + switch (context) { + case Expression::kUninitialized: + case Expression::kEffect: + case Expression::kValue: + UNREACHABLE(); + + case Expression::kTest: + __ b(eq, true_label_); + __ jmp(false_label_); + break; + case Expression::kValueTest: { Label discard; - if (count > 1) __ Drop(count - 1); - __ str(reg, MemOperand(sp)); - TestAndBranch(reg, true_label_, &discard); + switch (location_) { + case kAccumulator: + __ b(ne, &discard); + __ pop(result_register()); + __ jmp(true_label_); + break; + case kStack: + __ b(eq, true_label_); + break; + } __ bind(&discard); __ Drop(1); __ jmp(false_label_); break; } + case Expression::kTestValue: { Label discard; - if (count > 1) __ Drop(count - 1); - __ str(reg, MemOperand(sp)); - TestAndBranch(reg, &discard, false_label_); + switch (location_) { + case kAccumulator: + __ b(eq, &discard); + __ pop(result_register()); + __ jmp(false_label_); + break; + case kStack: + __ b(ne, false_label_); + break; + } __ bind(&discard); __ Drop(1); __ jmp(true_label_); @@ -348,12 +516,11 @@ MemOperand FastCodeGenerator::EmitSlotSearch(Slot* slot, Register scratch) { void FastCodeGenerator::Move(Register destination, Slot* source) { // Use destination as scratch. - MemOperand location = EmitSlotSearch(source, destination); - __ ldr(destination, location); + MemOperand slot_operand = EmitSlotSearch(source, destination); + __ ldr(destination, slot_operand); } - void FastCodeGenerator::Move(Slot* dst, Register src, Register scratch1, @@ -370,23 +537,6 @@ void FastCodeGenerator::Move(Slot* dst, } - -void FastCodeGenerator::TestAndBranch(Register source, - Label* true_label, - Label* false_label) { - ASSERT_NE(NULL, true_label); - ASSERT_NE(NULL, false_label); - // Call the runtime to find the boolean value of the source and then - // translate it into control flow to the pair of labels. - __ push(source); - __ CallRuntime(Runtime::kToBool, 1); - __ LoadRoot(ip, Heap::kTrueValueRootIndex); - __ cmp(r0, ip); - __ b(eq, true_label); - __ jmp(false_label); -} - - void FastCodeGenerator::VisitDeclaration(Declaration* decl) { Comment cmnt(masm_, "[ Declaration"); Variable* var = decl->proxy()->var(); @@ -402,9 +552,8 @@ void FastCodeGenerator::VisitDeclaration(Declaration* decl) { __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); __ str(ip, MemOperand(fp, SlotOffset(slot))); } else if (decl->fun() != NULL) { - Visit(decl->fun()); - __ pop(ip); - __ str(ip, MemOperand(fp, SlotOffset(slot))); + VisitForValue(decl->fun(), kAccumulator); + __ str(result_register(), MemOperand(fp, SlotOffset(slot))); } break; @@ -426,13 +575,13 @@ void FastCodeGenerator::VisitDeclaration(Declaration* decl) { __ str(ip, CodeGenerator::ContextOperand(cp, slot->index())); // No write barrier since the_hole_value is in old space. } else if (decl->fun() != NULL) { - Visit(decl->fun()); - __ pop(r0); - __ str(r0, CodeGenerator::ContextOperand(cp, slot->index())); + VisitForValue(decl->fun(), kAccumulator); + __ str(result_register(), + CodeGenerator::ContextOperand(cp, slot->index())); int offset = Context::SlotOffset(slot->index()); __ mov(r2, Operand(offset)); // We know that we have written a function, which is not a smi. - __ RecordWrite(cp, r2, r0); + __ RecordWrite(cp, r2, result_register()); } break; @@ -453,7 +602,8 @@ void FastCodeGenerator::VisitDeclaration(Declaration* decl) { __ stm(db_w, sp, cp.bit() | r2.bit() | r1.bit() | r0.bit()); } else if (decl->fun() != NULL) { __ stm(db_w, sp, cp.bit() | r2.bit() | r1.bit()); - Visit(decl->fun()); // Initial value for function decl. + // Push initial value for function declaration. + VisitForValue(decl->fun(), kStack); } else { __ mov(r0, Operand(Smi::FromInt(0))); // No initial value! __ stm(db_w, sp, cp.bit() | r2.bit() | r1.bit() | r0.bit()); @@ -467,17 +617,13 @@ void FastCodeGenerator::VisitDeclaration(Declaration* decl) { 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(r0); + VisitForValue(decl->fun(), kAccumulator); } else { - __ LoadRoot(r0, Heap::kTheHoleValueRootIndex); + __ LoadRoot(result_register(), Heap::kTheHoleValueRootIndex); } Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); @@ -558,7 +704,7 @@ void FastCodeGenerator::EmitVariableLoad(Variable* var, break; } } - Apply(context, slot, r0); + Apply(context, slot); } else { Comment cmnt(masm_, "Variable rewritten to property"); // A variable has been rewritten into an explicit access to an object @@ -639,9 +785,8 @@ void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { __ CallRuntime(Runtime::kCreateObjectLiteralShallow, 3); } - // If result_saved == true: The result is saved on top of the - // stack and in r0. - // If result_saved == false: The result not on the stack, just in r0. + // If result_saved is true the result is on top of the stack. If + // result_saved is false the result is in r0. bool result_saved = false; for (int i = 0; i < expr->properties()->length(); i++) { @@ -657,80 +802,47 @@ void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { switch (property->kind()) { case ObjectLiteral::Property::CONSTANT: UNREACHABLE(); - - case ObjectLiteral::Property::MATERIALIZED_LITERAL: // Fall through. + case ObjectLiteral::Property::MATERIALIZED_LITERAL: ASSERT(!CompileTimeValue::IsCompileTimeValue(property->value())); + // Fall through. case ObjectLiteral::Property::COMPUTED: if (key->handle()->IsSymbol()) { - Visit(value); - ASSERT_EQ(Expression::kValue, value->context()); - __ pop(r0); + VisitForValue(value, kAccumulator); __ mov(r2, Operand(key->handle())); Handle ic(Builtins::builtin(Builtins::StoreIC_Initialize)); __ Call(ic, RelocInfo::CODE_TARGET); // StoreIC leaves the receiver on the stack. - __ ldr(r0, MemOperand(sp)); // Restore result into r0. break; } // Fall through. - case ObjectLiteral::Property::PROTOTYPE: + // Duplicate receiver on stack. + __ ldr(r0, MemOperand(sp)); __ push(r0); - Visit(key); - ASSERT_EQ(Expression::kValue, key->context()); - Visit(value); - ASSERT_EQ(Expression::kValue, value->context()); + VisitForValue(key, kStack); + VisitForValue(value, kStack); __ CallRuntime(Runtime::kSetProperty, 3); - __ ldr(r0, MemOperand(sp)); // Restore result into r0. break; - case ObjectLiteral::Property::GETTER: case ObjectLiteral::Property::SETTER: + // Duplicate receiver on stack. + __ ldr(r0, MemOperand(sp)); __ push(r0); - Visit(key); - ASSERT_EQ(Expression::kValue, key->context()); + VisitForValue(key, kStack); __ mov(r1, Operand(property->kind() == ObjectLiteral::Property::SETTER ? Smi::FromInt(1) : Smi::FromInt(0))); __ push(r1); - Visit(value); - ASSERT_EQ(Expression::kValue, value->context()); + VisitForValue(value, kStack); __ CallRuntime(Runtime::kDefineAccessor, 4); - __ ldr(r0, MemOperand(sp)); // Restore result into r0 break; } } - switch (expr->context()) { - case Expression::kUninitialized: - UNREACHABLE(); - case Expression::kEffect: - if (result_saved) __ Drop(1); - break; - case Expression::kValue: - if (!result_saved) __ push(r0); - break; - case Expression::kTest: - if (result_saved) __ pop(r0); - TestAndBranch(r0, true_label_, false_label_); - break; - case Expression::kValueTest: { - Label discard; - if (!result_saved) __ push(r0); - TestAndBranch(r0, true_label_, &discard); - __ bind(&discard); - __ Drop(1); - __ jmp(false_label_); - break; - } - case Expression::kTestValue: { - Label discard; - if (!result_saved) __ push(r0); - TestAndBranch(r0, &discard, false_label_); - __ bind(&discard); - __ Drop(1); - __ jmp(true_label_); - break; - } + + if (result_saved) { + ApplyTOS(expr->context()); + } else { + Apply(expr->context(), r0); } } @@ -766,53 +878,24 @@ void FastCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { __ push(r0); result_saved = true; } - Visit(subexpr); - ASSERT_EQ(Expression::kValue, subexpr->context()); + VisitForValue(subexpr, kAccumulator); // Store the subexpression value in the array's elements. - __ pop(r0); // Subexpression value. __ ldr(r1, MemOperand(sp)); // Copy of array literal. __ ldr(r1, FieldMemOperand(r1, JSObject::kElementsOffset)); int offset = FixedArray::kHeaderSize + (i * kPointerSize); - __ str(r0, FieldMemOperand(r1, offset)); + __ str(result_register(), FieldMemOperand(r1, offset)); // Update the write barrier for the array store with r0 as the scratch // register. __ mov(r2, Operand(offset)); - __ RecordWrite(r1, r2, r0); + __ RecordWrite(r1, r2, result_register()); } - switch (expr->context()) { - case Expression::kUninitialized: - UNREACHABLE(); - case Expression::kEffect: - if (result_saved) __ Drop(1); - break; - case Expression::kValue: - if (!result_saved) __ push(r0); - break; - case Expression::kTest: - if (result_saved) __ pop(r0); - TestAndBranch(r0, true_label_, false_label_); - break; - case Expression::kValueTest: { - Label discard; - if (!result_saved) __ push(r0); - TestAndBranch(r0, true_label_, &discard); - __ bind(&discard); - __ Drop(1); - __ jmp(false_label_); - break; - } - case Expression::kTestValue: { - Label discard; - if (!result_saved) __ push(r0); - TestAndBranch(r0, &discard, false_label_); - __ bind(&discard); - __ Drop(1); - __ jmp(true_label_); - break; - } + if (result_saved) { + ApplyTOS(expr->context()); + } else { + Apply(expr->context(), r0); } } @@ -833,12 +916,10 @@ void FastCodeGenerator::EmitKeyedPropertyLoad(Property* prop) { } -void FastCodeGenerator::EmitCompoundAssignmentOp(Token::Value op, - Expression::Context context) { - __ pop(r0); +void FastCodeGenerator::EmitBinaryOp(Token::Value op, + Expression::Context context) { __ pop(r1); - GenericBinaryOpStub stub(op, - NO_OVERWRITE); + GenericBinaryOpStub stub(op, NO_OVERWRITE); __ CallStub(&stub); Apply(context, r0); } @@ -852,7 +933,6 @@ void FastCodeGenerator::EmitVariableAssignment(Variable* var, // Assignment to a global variable. Use inline caching for the // assignment. Right-hand-side value is passed in r0, variable name in // r2, and the global object on the stack. - __ pop(r0); __ mov(r2, Operand(var->name())); __ ldr(ip, CodeGenerator::GlobalObject()); __ push(ip); @@ -865,77 +945,20 @@ void FastCodeGenerator::EmitVariableAssignment(Variable* var, Slot* slot = var->slot(); switch (slot->type()) { case Slot::LOCAL: - case Slot::PARAMETER: { - MemOperand target = MemOperand(fp, SlotOffset(slot)); - switch (context) { - case Expression::kUninitialized: - UNREACHABLE(); - case Expression::kEffect: - // Perform assignment and discard value. - __ pop(r0); - __ str(r0, target); - break; - case Expression::kValue: - // Perform assignment and preserve value. - __ ldr(r0, MemOperand(sp)); - __ str(r0, target); - break; - case Expression::kTest: - // Perform assignment and test (and discard) value. - __ pop(r0); - __ str(r0, target); - TestAndBranch(r0, true_label_, false_label_); - break; - case Expression::kValueTest: { - Label discard; - __ ldr(r0, MemOperand(sp)); - __ str(r0, target); - TestAndBranch(r0, true_label_, &discard); - __ bind(&discard); - __ Drop(1); - __ jmp(false_label_); - break; - } - case Expression::kTestValue: { - Label discard; - __ ldr(r0, MemOperand(sp)); - __ str(r0, target); - TestAndBranch(r0, &discard, false_label_); - __ bind(&discard); - __ Drop(1); - __ jmp(true_label_); - break; - } - } + case Slot::PARAMETER: + __ str(result_register(), MemOperand(fp, SlotOffset(slot))); break; - } case Slot::CONTEXT: { MemOperand target = EmitSlotSearch(slot, r1); - __ pop(r0); - __ str(r0, target); + __ str(result_register(), target); // RecordWrite may destroy all its register arguments. - if (context == Expression::kValue) { - __ push(r0); - } else if (context != Expression::kEffect) { - __ mov(r3, r0); - } + __ mov(r3, result_register()); int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; - // Update the write barrier for the array store with r0 as the scratch - // register. Skip the write barrier if the value written (r1) is a smi. - // The smi test is part of RecordWrite on other platforms, not on arm. - Label exit; - __ tst(r0, Operand(kSmiTagMask)); - __ b(eq, &exit); - __ mov(r2, Operand(offset)); - __ RecordWrite(r1, r2, r0); - __ bind(&exit); - if (context != Expression::kEffect && context != Expression::kValue) { - Apply(context, r3); - } + __ RecordWrite(r1, r2, r3); break; } @@ -943,6 +966,7 @@ void FastCodeGenerator::EmitVariableAssignment(Variable* var, UNREACHABLE(); break; } + Apply(context, result_register()); } else { // Variables rewritten as properties are not treated as variables in // assignments. @@ -961,15 +985,15 @@ void FastCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { // change to slow case to avoid the quadratic behavior of repeatedly // adding fast properties. if (expr->starts_initialization_block()) { - __ ldr(ip, MemOperand(sp, kPointerSize)); // Receiver is under value. + __ push(result_register()); + __ ldr(ip, MemOperand(sp, kPointerSize)); // Receiver is now under value. __ push(ip); __ CallRuntime(Runtime::kToSlowProperties, 1); + __ pop(result_register()); } // Record source code position before IC call. SetSourcePosition(expr->position()); - - __ pop(r0); __ mov(r2, Operand(prop->key()->AsLiteral()->handle())); Handle ic(Builtins::builtin(Builtins::StoreIC_Initialize)); __ Call(ic, RelocInfo::CODE_TARGET); @@ -994,23 +1018,23 @@ void FastCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { // change to slow case to avoid the quadratic behavior of repeatedly // adding fast properties. if (expr->starts_initialization_block()) { - // Receiver is under the key and value. + __ push(result_register()); + // Receiver is now under the key and value. __ ldr(ip, MemOperand(sp, 2 * kPointerSize)); __ push(ip); __ CallRuntime(Runtime::kToSlowProperties, 1); + __ pop(result_register()); } // Record source code position before IC call. SetSourcePosition(expr->position()); - - __ pop(r0); Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); __ Call(ic, RelocInfo::CODE_TARGET); // If the assignment ends an initialization block, revert to fast case. if (expr->ends_initialization_block()) { __ push(r0); // Result of assignment, saved even if not needed. - // Reciever is under the key and value. + // Receiver is under the key and value. __ ldr(ip, MemOperand(sp, 2 * kPointerSize)); __ push(ip); __ CallRuntime(Runtime::kToFastProperties, 1); @@ -1027,14 +1051,14 @@ void FastCodeGenerator::VisitProperty(Property* expr) { Expression* key = expr->key(); // Evaluate receiver. - Visit(expr->obj()); + VisitForValue(expr->obj(), kStack); if (key->IsPropertyName()) { EmitNamedPropertyLoad(expr); // Drop receiver left on the stack by IC. DropAndApply(1, expr->context(), r0); } else { - Visit(expr->key()); + VisitForValue(expr->key(), kStack); EmitKeyedPropertyLoad(expr); // Drop key and receiver left on the stack by IC. DropAndApply(2, expr->context(), r0); @@ -1048,8 +1072,7 @@ void FastCodeGenerator::EmitCallWithIC(Call* expr, ZoneList* 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()); @@ -1069,7 +1092,7 @@ void FastCodeGenerator::EmitCallWithStub(Call* expr) { ZoneList* 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()); @@ -1109,13 +1132,13 @@ void FastCodeGenerator::VisitCall(Call* expr) { // Call to a named property, use call IC. __ mov(r0, Operand(key->handle())); __ push(r0); - 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 ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); @@ -1142,7 +1165,7 @@ void FastCodeGenerator::VisitCall(Call* expr) { loop_depth() == 0) { lit->set_try_fast_codegen(true); } - Visit(fun); + VisitForValue(fun, kStack); // Load global receiver object. __ ldr(r1, CodeGenerator::GlobalObject()); __ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset)); @@ -1159,8 +1182,7 @@ void FastCodeGenerator::VisitCallNew(CallNew* expr) { // 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()); + VisitForValue(expr->expression(), kStack); // Push global object (receiver). __ ldr(r0, CodeGenerator::GlobalObject()); @@ -1169,10 +1191,7 @@ void FastCodeGenerator::VisitCallNew(CallNew* expr) { ZoneList* 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 @@ -1207,8 +1226,7 @@ void FastCodeGenerator::VisitCallRuntime(CallRuntime* expr) { // 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()) { @@ -1232,8 +1250,8 @@ void FastCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { switch (expr->op()) { case Token::VOID: { Comment cmnt(masm_, "[ UnaryOperation (VOID)"); - Visit(expr->expression()); ASSERT_EQ(Expression::kEffect, expr->expression()->context()); + Visit(expr->expression()); switch (expr->context()) { case Expression::kUninitialized: UNREACHABLE(); @@ -1241,13 +1259,26 @@ void FastCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { case Expression::kEffect: break; case Expression::kValue: - __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); - __ push(ip); + __ LoadRoot(result_register(), Heap::kUndefinedValueRootIndex); + switch (location_) { + case kAccumulator: + break; + case kStack: + __ push(result_register()); + break; + } break; case Expression::kTestValue: // Value is false so it's needed. - __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); - __ push(ip); + __ LoadRoot(result_register(), Heap::kUndefinedValueRootIndex); + switch (location_) { + case kAccumulator: + break; + case kStack: + __ push(result_register()); + break; + } + // Fall through. case Expression::kTest: case Expression::kValueTest: __ jmp(false_label_); @@ -1260,49 +1291,34 @@ void FastCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { 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: - VisitForControl(expr->expression(), &done, &done); - __ bind(&done); + if_true = &done; + if_false = &done; break; - case Expression::kValue: - VisitForControl(expr->expression(), &push_false, &push_true); - __ bind(&push_true); - __ LoadRoot(ip, Heap::kTrueValueRootIndex); - __ push(ip); - __ jmp(&done); - __ bind(&push_false); - __ LoadRoot(ip, Heap::kFalseValueRootIndex); - __ push(ip); - __ bind(&done); + if_true = &materialize_false; + if_false = &materialize_true; 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); - __ LoadRoot(ip, Heap::kTrueValueRootIndex); - __ push(ip); - __ jmp(true_label_); + if_false = &materialize_true; break; - case Expression::kTestValue: - VisitForControl(expr->expression(), &push_false, true_label_); - __ bind(&push_false); - __ LoadRoot(ip, Heap::kFalseValueRootIndex); - __ push(ip); - __ 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; } @@ -1332,7 +1348,7 @@ void FastCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { __ push(r0); } else { // This expression cannot throw a reference error at the top level. - Visit(expr->expression()); + VisitForValue(expr->expression(), kStack); } __ CallRuntime(Runtime::kTypeof, 1); @@ -1365,8 +1381,11 @@ void FastCodeGenerator::VisitCountOperation(CountOperation* expr) { // 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) { @@ -1374,13 +1393,11 @@ void FastCodeGenerator::VisitCountOperation(CountOperation* expr) { __ mov(ip, Operand(Smi::FromInt(0))); __ push(ip); } - Visit(prop->obj()); - ASSERT_EQ(Expression::kValue, prop->obj()->context()); + VisitForValue(prop->obj(), kStack); if (assign_type == NAMED_PROPERTY) { EmitNamedPropertyLoad(prop); } else { - Visit(prop->key()); - ASSERT_EQ(Expression::kValue, prop->key()->context()); + VisitForValue(prop->key(), kStack); EmitKeyedPropertyLoad(prop); } __ push(r0); @@ -1399,8 +1416,8 @@ void FastCodeGenerator::VisitCountOperation(CountOperation* expr) { break; case Expression::kValue: case Expression::kTest: - case Expression::kTestValue: 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. @@ -1429,7 +1446,6 @@ void FastCodeGenerator::VisitCountOperation(CountOperation* expr) { // Store the value returned in r0. switch (assign_type) { case VARIABLE: - __ push(r0); if (expr->is_postfix()) { EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), Expression::kEffect); @@ -1499,21 +1515,12 @@ void FastCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) { 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()); - - Visit(expr->left()); - Visit(expr->right()); - __ pop(r0); - __ pop(r1); - GenericBinaryOpStub stub(expr->op(), - NO_OVERWRITE); - __ CallStub(&stub); - Apply(expr->context(), r0); - + case Token::SAR: + VisitForValue(expr->left(), kStack); + VisitForValue(expr->right(), kAccumulator); + EmitBinaryOp(expr->op(), expr->context()); break; - } + default: UNREACHABLE(); } @@ -1522,14 +1529,10 @@ void FastCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) { 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_; @@ -1542,30 +1545,32 @@ void FastCodeGenerator::VisitCompareOperation(CompareOperation* expr) { if_false = &done; break; case Expression::kValue: - if_true = &push_true; - if_false = &push_false; + 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_JS); __ LoadRoot(ip, Heap::kTrueValueRootIndex); __ cmp(r0, ip); __ b(eq, if_true); __ jmp(if_false); break; - } case Token::INSTANCEOF: { + VisitForValue(expr->right(), kStack); InstanceofStub stub; __ CallStub(&stub); __ tst(r0, r0); @@ -1575,6 +1580,7 @@ void FastCodeGenerator::VisitCompareOperation(CompareOperation* expr) { } default: { + VisitForValue(expr->right(), kAccumulator); Condition cc = eq; bool strict = false; switch (expr->op()) { @@ -1583,29 +1589,26 @@ void FastCodeGenerator::VisitCompareOperation(CompareOperation* expr) { // Fall through case Token::EQ: cc = eq; - __ pop(r0); __ pop(r1); break; case Token::LT: cc = lt; - __ pop(r0); __ pop(r1); break; case Token::GT: // Reverse left and right sides to obtain ECMA-262 conversion order. cc = lt; - __ pop(r1); + __ mov(r1, result_register()); __ pop(r0); break; case Token::LTE: // Reverse left and right sides to obtain ECMA-262 conversion order. cc = ge; - __ pop(r1); + __ mov(r1, result_register()); __ pop(r0); break; case Token::GTE: cc = ge; - __ pop(r0); __ pop(r1); break; case Token::IN: @@ -1635,43 +1638,7 @@ void FastCodeGenerator::VisitCompareOperation(CompareOperation* expr) { // 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); - __ LoadRoot(ip, Heap::kTrueValueRootIndex); - __ push(ip); - __ jmp(&done); - __ bind(&push_false); - __ LoadRoot(ip, Heap::kFalseValueRootIndex); - __ push(ip); - __ bind(&done); - break; - - case Expression::kTest: - break; - - case Expression::kValueTest: - __ bind(&push_true); - __ LoadRoot(ip, Heap::kTrueValueRootIndex); - __ push(ip); - __ jmp(true_label_); - break; - - case Expression::kTestValue: - __ bind(&push_false); - __ LoadRoot(ip, Heap::kFalseValueRootIndex); - __ push(ip); - __ jmp(false_label_); - break; - } + Apply(expr->context(), if_true, if_false); } diff --git a/src/fast-codegen.cc b/src/fast-codegen.cc index d5d01b3d9e..64335a6df1 100644 --- a/src/fast-codegen.cc +++ b/src/fast-codegen.cc @@ -75,39 +75,6 @@ int FastCodeGenerator::SlotOffset(Slot* slot) { } -void FastCodeGenerator::Apply(Expression::Context context, Register reg) { - switch (context) { - case Expression::kUninitialized: - UNREACHABLE(); - case Expression::kEffect: - break; - case Expression::kValue: - __ push(reg); - break; - case Expression::kTest: - TestAndBranch(reg, true_label_, false_label_); - break; - case Expression::kValueTest: { - Label discard; - __ push(reg); - TestAndBranch(reg, true_label_, &discard); - __ bind(&discard); - __ Drop(1); - __ jmp(false_label_); - break; - } - case Expression::kTestValue: { - Label discard; - __ push(reg); - TestAndBranch(reg, &discard, false_label_); - __ bind(&discard); - __ Drop(1); - __ jmp(true_label_); - } - } -} - - void FastCodeGenerator::VisitDeclarations( ZoneList* declarations) { int length = declarations->length(); @@ -345,14 +312,7 @@ void FastCodeGenerator::VisitReturnStatement(ReturnStatement* stmt) { Comment cmnt(masm_, "[ ReturnStatement"); SetStatementPosition(stmt); Expression* expr = stmt->expression(); - // Complete the statement based on the type of the subexpression. - if (expr->AsLiteral() != NULL) { - __ Move(result_register(), expr->AsLiteral()->handle()); - } else { - ASSERT_EQ(Expression::kValue, expr->context()); - Visit(expr); - __ pop(result_register()); - } + VisitForValue(expr, kAccumulator); // Exit all nested statements. NestedStatement* current = nesting_stack_; @@ -371,7 +331,7 @@ void FastCodeGenerator::VisitWithEnterStatement(WithEnterStatement* stmt) { Comment cmnt(masm_, "[ WithEnterStatement"); SetStatementPosition(stmt); - Visit(stmt->expression()); + VisitForValue(stmt->expression(), kStack); if (stmt->is_catch_block()) { __ CallRuntime(Runtime::kPushCatchContext, 1); } else { @@ -658,21 +618,19 @@ void FastCodeGenerator::VisitAssignment(Assignment* expr) { // Nothing to do here. break; case NAMED_PROPERTY: - Visit(prop->obj()); - ASSERT_EQ(Expression::kValue, prop->obj()->context()); + VisitForValue(prop->obj(), kStack); break; case KEYED_PROPERTY: - Visit(prop->obj()); - ASSERT_EQ(Expression::kValue, prop->obj()->context()); - Visit(prop->key()); - ASSERT_EQ(Expression::kValue, prop->key()->context()); + VisitForValue(prop->obj(), kStack); + VisitForValue(prop->key(), kStack); break; } // If we have a compound assignment: Get value of LHS expression and // store in on top of the stack. - // Note: Relies on kValue context being 'stack'. if (expr->is_compound()) { + Location saved_location = location_; + location_ = kStack; switch (assign_type) { case VARIABLE: EmitVariableLoad(expr->target()->AsVariableProxy()->var(), @@ -687,16 +645,19 @@ void FastCodeGenerator::VisitAssignment(Assignment* expr) { __ push(result_register()); break; } + location_ = saved_location; } // Evaluate RHS expression. Expression* rhs = expr->value(); - ASSERT_EQ(Expression::kValue, rhs->context()); - Visit(rhs); + VisitForValue(rhs, kAccumulator); // If we have a compount assignment: Apply operator. if (expr->is_compound()) { - EmitCompoundAssignmentOp(expr->binary_op(), Expression::kValue); + Location saved_location = location_; + location_ = kAccumulator; + EmitBinaryOp(expr->binary_op(), Expression::kValue); + location_ = saved_location; } // Record source position before possible IC call. @@ -723,11 +684,8 @@ void FastCodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* expr) { // assign the exception value to the catch variable. Comment cmnt(masm_, "[ CatchExtensionObject"); - // Push key string. - ASSERT_EQ(Expression::kValue, expr->key()->context()); - Visit(expr->key()); - ASSERT_EQ(Expression::kValue, expr->value()->context()); - Visit(expr->value()); + VisitForValue(expr->key(), kStack); + VisitForValue(expr->value(), kStack); // Create catch extension object. __ CallRuntime(Runtime::kCreateCatchExtensionObject, 2); @@ -738,8 +696,7 @@ void FastCodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* expr) { void FastCodeGenerator::VisitThrow(Throw* expr) { Comment cmnt(masm_, "[ Throw"); - Visit(expr->exception()); - // Exception is on stack. + VisitForValue(expr->exception(), kStack); __ CallRuntime(Runtime::kThrow, 1); // Never returns here. } diff --git a/src/fast-codegen.h b/src/fast-codegen.h index 597688ff29..49e97e264e 100644 --- a/src/fast-codegen.h +++ b/src/fast-codegen.h @@ -47,6 +47,7 @@ class FastCodeGenerator: public AstVisitor { is_eval_(is_eval), nesting_stack_(NULL), loop_depth_(0), + location_(kStack), true_label_(NULL), false_label_(NULL) { } @@ -210,25 +211,37 @@ class FastCodeGenerator: public AstVisitor { DISALLOW_COPY_AND_ASSIGN(ForIn); }; + enum Location { + kAccumulator, + kStack + }; int SlotOffset(Slot* slot); - // Emit code to complete the evaluation of an expression based on its - // expression context and given its value is in a register, non-lookup - // slot, or a literal. + // Emit code to convert a pure value (in a register, slot, as a literal, + // or on top of the stack) into the result expected according to an + // expression context. void Apply(Expression::Context context, Register reg); - void Apply(Expression::Context context, Slot* slot, Register scratch); + void Apply(Expression::Context context, Slot* slot); void Apply(Expression::Context context, Literal* lit); - - // Emit code to complete the evaluation of an expression based on its - // expression context and given its value is on top of the stack. void ApplyTOS(Expression::Context context); - // Emit code to discard count elements from the top of stack, then - // complete the evaluation of an expression based on its expression - // context and given its value is in a register. + // Emit code to discard count elements from the top of stack, then convert + // a pure value into the result expected according to an expression + // context. void DropAndApply(int count, Expression::Context context, Register reg); + // Emit code to convert pure control flow to a pair of labels into the + // result expected according to an expression context. + void Apply(Expression::Context context, + Label* materialize_true, + Label* materialize_false); + + // Helper function to convert a pure value into a test context. The value + // is expected on the stack or the accumulator, depending on the platform. + // See the platform-specific implementation for details. + void DoTest(Expression::Context context); + void Move(Slot* dst, Register source, Register scratch1, Register scratch2); void Move(Register dst, Slot* source); @@ -237,9 +250,13 @@ class FastCodeGenerator: public AstVisitor { // register. MemOperand EmitSlotSearch(Slot* slot, Register scratch); - // Test the JavaScript value in source as if in a test context, compile - // control flow to a pair of labels. - void TestAndBranch(Register source, Label* true_label, Label* false_label); + void VisitForValue(Expression* expr, Location where) { + ASSERT(expr->context() == Expression::kValue); + Location saved_location = location_; + location_ = where; + Visit(expr); + location_ = saved_location; + } void VisitForControl(Expression* expr, Label* if_true, Label* if_false) { ASSERT(expr->context() == Expression::kTest || @@ -277,20 +294,21 @@ class FastCodeGenerator: public AstVisitor { // The receiver and the key is left on the stack by the IC. void EmitKeyedPropertyLoad(Property* expr); - // Apply the compound assignment operator. Expects both operands on top - // of the stack. - void EmitCompoundAssignmentOp(Token::Value op, Expression::Context context); + // Apply the compound assignment operator. Expects the left operand on top + // of the stack and the right one in the accumulator. + void EmitBinaryOp(Token::Value op, Expression::Context context); // Complete a variable assignment. The right-hand-side value is expected - // on top of the stack. + // in the accumulator. void EmitVariableAssignment(Variable* var, Expression::Context context); - // Complete a named property assignment. The receiver and right-hand-side - // value are expected on top of the stack. + // Complete a named property assignment. The receiver is expected on top + // of the stack and the right-hand-side value in the accumulator. void EmitNamedPropertyAssignment(Assignment* expr); - // Complete a keyed property assignment. The reciever, key, and - // right-hand-side value are expected on top of the stack. + // Complete a keyed property assignment. The receiver and key are + // expected on top of the stack and the right-hand-side value in the + // accumulator. void EmitKeyedPropertyAssignment(Assignment* expr); void SetFunctionPosition(FunctionLiteral* fun); @@ -338,6 +356,7 @@ class FastCodeGenerator: public AstVisitor { NestedStatement* nesting_stack_; int loop_depth_; + Location location_; Label* true_label_; Label* false_label_; diff --git a/src/ia32/fast-codegen-ia32.cc b/src/ia32/fast-codegen-ia32.cc index c8d0b0023e..27cdf7bca1 100644 --- a/src/ia32/fast-codegen-ia32.cc +++ b/src/ia32/fast-codegen-ia32.cc @@ -29,9 +29,9 @@ #include "codegen-inl.h" #include "compiler.h" +#include "debug.h" #include "fast-codegen.h" #include "parser.h" -#include "debug.h" namespace v8 { namespace internal { @@ -116,7 +116,7 @@ void FastCodeGenerator::Generate(FunctionLiteral* fun) { __ push(Immediate(Smi::FromInt(fun->num_parameters()))); // Arguments to ArgumentsAccessStub: // function, receiver address, parameter count. - // The stub will rewrite receiever and parameter count if the previous + // The stub will rewrite receiver and parameter count if the previous // stack frame was an arguments adapter frame. ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT); __ CallStub(&stub); @@ -193,24 +193,73 @@ void FastCodeGenerator::EmitReturnSequence(int position) { } -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())) __ mov(result_register(), reg); + break; + case kStack: + __ push(reg); + break; + } + break; + + case Expression::kValueTest: + case Expression::kTestValue: + // Push an extra copy of the value in case it's needed. + __ push(reg); + // Fall through. + + case Expression::kTest: + // For simplicity we always test the accumulator register. + if (!reg.is(result_register())) __ mov(result_register(), reg); + 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: + __ mov(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); + __ push(result_register()); + DoTest(context); break; } } @@ -221,15 +270,30 @@ void FastCodeGenerator::Apply(Expression::Context context, Literal* lit) { case Expression::kUninitialized: UNREACHABLE(); case Expression::kEffect: + // Nothing to do. break; case Expression::kValue: - __ push(Immediate(lit->handle())); + switch (location_) { + case kAccumulator: + __ mov(result_register(), lit->handle()); + break; + case kStack: + // Immediates can be pushed directly. + __ push(Immediate(lit->handle())); + break; + } break; + case Expression::kTest: + __ mov(result_register(), lit->handle()); + DoTest(context); + break; + case Expression::kValueTest: case Expression::kTestValue: - __ mov(eax, lit->handle()); - Apply(context, eax); + __ mov(result_register(), lit->handle()); + __ push(result_register()); + DoTest(context); break; } } @@ -239,32 +303,31 @@ void FastCodeGenerator::ApplyTOS(Expression::Context context) { 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(eax); - TestAndBranch(eax, true_label_, false_label_); + __ pop(result_register()); + DoTest(context); break; - case Expression::kValueTest: { - Label discard; - __ mov(eax, Operand(esp, 0)); - TestAndBranch(eax, true_label_, &discard); - __ bind(&discard); - __ Drop(1); - __ jmp(false_label_); + + case Expression::kValueTest: + case Expression::kTestValue: + __ mov(result_register(), Operand(esp, 0)); + DoTest(context); break; - } - case Expression::kTestValue: { - Label discard; - __ mov(eax, Operand(esp, 0)); - TestAndBranch(eax, &discard, false_label_); - __ bind(&discard); - __ Drop(1); - __ jmp(true_label_); - } } } @@ -277,37 +340,181 @@ void FastCodeGenerator::DropAndApply(int count, switch (context) { case Expression::kUninitialized: UNREACHABLE(); + case Expression::kEffect: __ Drop(count); break; + case Expression::kValue: - if (count > 1) __ Drop(count - 1); - __ mov(Operand(esp, 0), reg); + switch (location_) { + case kAccumulator: + __ Drop(count); + if (!reg.is(result_register())) __ mov(result_register(), reg); + break; + case kStack: + if (count > 1) __ Drop(count - 1); + __ mov(Operand(esp, 0), reg); + break; + } break; + case Expression::kTest: __ Drop(count); - TestAndBranch(reg, true_label_, false_label_); + if (!reg.is(result_register())) __ mov(result_register(), reg); + DoTest(context); break; - case Expression::kValueTest: { - Label discard; + + case Expression::kValueTest: + case Expression::kTestValue: if (count > 1) __ Drop(count - 1); - __ mov(Operand(esp, 0), reg); - TestAndBranch(reg, true_label_, &discard); + if (!reg.is(result_register())) __ mov(result_register(), reg); + __ mov(Operand(esp, 0), result_register()); + 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); + + case Expression::kValue: { + Label done; + switch (location_) { + case kAccumulator: + __ bind(materialize_true); + __ mov(result_register(), Factory::true_value()); + __ jmp(&done); + __ bind(materialize_false); + __ mov(result_register(), Factory::false_value()); + break; + case kStack: + __ bind(materialize_true); + __ push(Immediate(Factory::true_value())); + __ jmp(&done); + __ bind(materialize_false); + __ push(Immediate(Factory::false_value())); + break; + } + __ bind(&done); + break; + } + + case Expression::kTest: + break; + + case Expression::kValueTest: + __ bind(materialize_true); + switch (location_) { + case kAccumulator: + __ mov(result_register(), Factory::true_value()); + break; + case kStack: + __ push(Immediate(Factory::true_value())); + break; + } + __ jmp(true_label_); + break; + + case Expression::kTestValue: + __ bind(materialize_false); + switch (location_) { + case kAccumulator: + __ mov(result_register(), Factory::false_value()); + break; + case kStack: + __ push(Immediate(Factory::false_value())); + break; + } + __ jmp(false_label_); + break; + } +} + + +void FastCodeGenerator::DoTest(Expression::Context context) { + // The value to test is in the accumulator, and duplicated on the stack if + // necessary (for value/test and test/value contexts). + ASSERT_NE(NULL, true_label_); + ASSERT_NE(NULL, false_label_); + + // If there is a value on the stack, use a discard label for the + // value-is-unneeded branch in the inlined part of the test. + Label discard; + Label* if_true = + (context == Expression::kTestValue) ? &discard : true_label_; + Label* if_false = + (context == Expression::kValueTest) ? &discard : false_label_; + + // Emit the inlined tests assumed by the stub. + __ cmp(result_register(), Factory::undefined_value()); + __ j(equal, if_false); + __ cmp(result_register(), Factory::true_value()); + __ j(equal, if_true); + __ cmp(result_register(), Factory::false_value()); + __ j(equal, if_false); + ASSERT_EQ(0, kSmiTag); + __ test(result_register(), Operand(result_register())); + __ j(zero, if_false); + __ test(result_register(), Immediate(kSmiTagMask)); + __ j(zero, if_true); + + // Call the ToBoolean stub for all other cases. + ToBooleanStub stub; + __ push(result_register()); + __ CallStub(&stub); + __ test(eax, Operand(eax)); + + // 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); - __ mov(Operand(esp, 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; - } } } @@ -353,36 +560,6 @@ void FastCodeGenerator::Move(Slot* dst, } -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. - __ cmp(source, Factory::undefined_value()); // The undefined value is false. - __ j(equal, false_label); - __ cmp(source, Factory::true_value()); // True is true. - __ j(equal, true_label); - __ cmp(source, Factory::false_value()); // False is false. - __ j(equal, false_label); - ASSERT_EQ(0, kSmiTag); - __ test(source, Operand(source)); // The smi zero is false. - __ j(zero, false_label); - __ test(source, Immediate(kSmiTagMask)); // All other smis are true. - __ j(zero, true_label); - - // Call the stub for all other cases. - __ push(source); - ToBooleanStub stub; - __ CallStub(&stub); - __ test(eax, Operand(eax)); // 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(); @@ -398,8 +575,8 @@ void FastCodeGenerator::VisitDeclaration(Declaration* decl) { __ mov(Operand(ebp, SlotOffset(slot)), Immediate(Factory::the_hole_value())); } else if (decl->fun() != NULL) { - Visit(decl->fun()); - __ pop(Operand(ebp, SlotOffset(slot))); + VisitForValue(decl->fun(), kAccumulator); + __ mov(Operand(ebp, SlotOffset(slot)), result_register()); } break; @@ -421,11 +598,11 @@ void FastCodeGenerator::VisitDeclaration(Declaration* decl) { __ mov(CodeGenerator::ContextOperand(esi, slot->index()), eax); // No write barrier since the hole value is in old space. } else if (decl->fun() != NULL) { - Visit(decl->fun()); - __ pop(eax); - __ mov(CodeGenerator::ContextOperand(esi, slot->index()), eax); + VisitForValue(decl->fun(), kAccumulator); + __ mov(CodeGenerator::ContextOperand(esi, slot->index()), + result_register()); int offset = Context::SlotOffset(slot->index()); - __ RecordWrite(esi, offset, eax, ecx); + __ RecordWrite(esi, offset, result_register(), ecx); } break; @@ -445,7 +622,7 @@ void FastCodeGenerator::VisitDeclaration(Declaration* decl) { if (decl->mode() == Variable::CONST) { __ push(Immediate(Factory::the_hole_value())); } else if (decl->fun() != NULL) { - Visit(decl->fun()); + VisitForValue(decl->fun(), kStack); } else { __ push(Immediate(Smi::FromInt(0))); // No initial value! } @@ -458,23 +635,20 @@ void FastCodeGenerator::VisitDeclaration(Declaration* decl) { 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(eax); + VisitForValue(decl->fun(), kAccumulator); } else { - __ Set(eax, Immediate(Factory::the_hole_value())); + __ mov(result_register(), Factory::the_hole_value()); } Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); __ call(ic, RelocInfo::CODE_TARGET); // Absence of a test eax instruction following the call // indicates that none of the load was inlined. + __ nop(); // Value in eax is ignored (declarations are statements). Receiver // and key on stack are discarded. @@ -554,7 +728,7 @@ void FastCodeGenerator::EmitVariableLoad(Variable* var, break; } } - Apply(context, slot, eax); + Apply(context, slot); } else { Comment cmnt(masm_, "Variable rewritten to property"); // A variable has been rewritten into an explicit access to an object @@ -634,9 +808,8 @@ void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { __ CallRuntime(Runtime::kCreateObjectLiteralShallow, 3); } - // If result_saved == true: The result is saved on top of the - // stack and in eax. - // If result_saved == false: The result not on the stack, just in eax. + // If result_saved is true the result is on top of the stack. If + // result_saved is false the result is in eax. bool result_saved = false; for (int i = 0; i < expr->properties()->length(); i++) { @@ -650,77 +823,44 @@ void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { result_saved = true; } switch (property->kind()) { - case ObjectLiteral::Property::MATERIALIZED_LITERAL: // Fall through. + 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(eax); + VisitForValue(value, kAccumulator); __ mov(ecx, Immediate(key->handle())); Handle ic(Builtins::builtin(Builtins::StoreIC_Initialize)); __ call(ic, RelocInfo::CODE_TARGET); + __ nop(); // StoreIC leaves the receiver on the stack. - __ mov(eax, Operand(esp, 0)); // Restore result into eax. break; } // Fall through. case ObjectLiteral::Property::PROTOTYPE: - __ push(eax); - Visit(key); - ASSERT_EQ(Expression::kValue, key->context()); - Visit(value); - ASSERT_EQ(Expression::kValue, value->context()); + __ push(Operand(esp, 0)); // Duplicate receiver. + VisitForValue(key, kStack); + VisitForValue(value, kStack); __ CallRuntime(Runtime::kSetProperty, 3); - __ mov(eax, Operand(esp, 0)); // Restore result into eax. break; case ObjectLiteral::Property::SETTER: case ObjectLiteral::Property::GETTER: - __ push(eax); - Visit(key); - ASSERT_EQ(Expression::kValue, key->context()); + __ push(Operand(esp, 0)); // Duplicate receiver. + VisitForValue(key, kStack); __ push(Immediate(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); - __ mov(eax, Operand(esp, 0)); // Restore result into eax. 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(eax); - break; - case Expression::kTest: - if (result_saved) __ pop(eax); - TestAndBranch(eax, true_label_, false_label_); - break; - case Expression::kValueTest: { - Label discard; - if (!result_saved) __ push(eax); - TestAndBranch(eax, true_label_, &discard); - __ bind(&discard); - __ Drop(1); - __ jmp(false_label_); - break; - } - case Expression::kTestValue: { - Label discard; - if (!result_saved) __ push(eax); - TestAndBranch(eax, &discard, false_label_); - __ bind(&discard); - __ Drop(1); - __ jmp(true_label_); - break; - } + + if (result_saved) { + ApplyTOS(expr->context()); + } else { + Apply(expr->context(), eax); } } @@ -755,51 +895,22 @@ void FastCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { __ push(eax); result_saved = true; } - Visit(subexpr); - ASSERT_EQ(Expression::kValue, subexpr->context()); + VisitForValue(subexpr, kAccumulator); // Store the subexpression value in the array's elements. - __ pop(eax); // Subexpression value. __ mov(ebx, Operand(esp, 0)); // Copy of array literal. __ mov(ebx, FieldOperand(ebx, JSObject::kElementsOffset)); int offset = FixedArray::kHeaderSize + (i * kPointerSize); - __ mov(FieldOperand(ebx, offset), eax); + __ mov(FieldOperand(ebx, offset), result_register()); // Update the write barrier for the array store. - __ RecordWrite(ebx, offset, eax, ecx); + __ RecordWrite(ebx, offset, result_register(), ecx); } - switch (expr->context()) { - case Expression::kUninitialized: - UNREACHABLE(); - case Expression::kEffect: - if (result_saved) __ Drop(1); - break; - case Expression::kValue: - if (!result_saved) __ push(eax); - break; - case Expression::kTest: - if (result_saved) __ pop(eax); - TestAndBranch(eax, true_label_, false_label_); - break; - case Expression::kValueTest: { - Label discard; - if (!result_saved) __ push(eax); - TestAndBranch(eax, true_label_, &discard); - __ bind(&discard); - __ Drop(1); - __ jmp(false_label_); - break; - } - case Expression::kTestValue: { - Label discard; - if (!result_saved) __ push(eax); - TestAndBranch(eax, &discard, false_label_); - __ bind(&discard); - __ Drop(1); - __ jmp(true_label_); - break; - } + if (result_saved) { + ApplyTOS(expr->context()); + } else { + Apply(expr->context(), eax); } } @@ -822,8 +933,9 @@ void FastCodeGenerator::EmitKeyedPropertyLoad(Property* prop) { } -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); @@ -840,7 +952,6 @@ void FastCodeGenerator::EmitVariableAssignment(Variable* var, // Assignment to a global variable. Use inline caching for the // assignment. Right-hand-side value is passed in eax, variable name in // ecx, and the global object on the stack. - __ pop(eax); __ mov(ecx, var->name()); __ push(CodeGenerator::GlobalObject()); Handle ic(Builtins::builtin(Builtins::StoreIC_Initialize)); @@ -853,66 +964,18 @@ void FastCodeGenerator::EmitVariableAssignment(Variable* var, Slot* slot = var->slot(); switch (slot->type()) { case Slot::LOCAL: - case Slot::PARAMETER: { - Operand target = Operand(ebp, 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. - __ mov(eax, Operand(esp, 0)); - __ mov(target, eax); - break; - case Expression::kTest: - // Perform assignment and test (and discard) value. - __ pop(eax); - __ mov(target, eax); - TestAndBranch(eax, true_label_, false_label_); - break; - case Expression::kValueTest: { - Label discard; - __ mov(eax, Operand(esp, 0)); - __ mov(target, eax); - TestAndBranch(eax, true_label_, &discard); - __ bind(&discard); - __ Drop(1); - __ jmp(false_label_); - break; - } - case Expression::kTestValue: { - Label discard; - __ mov(eax, Operand(esp, 0)); - __ mov(target, eax); - TestAndBranch(eax, &discard, false_label_); - __ bind(&discard); - __ Drop(1); - __ jmp(true_label_); - break; - } - } + case Slot::PARAMETER: + __ mov(Operand(ebp, SlotOffset(slot)), result_register()); break; - } case Slot::CONTEXT: { MemOperand target = EmitSlotSearch(slot, ecx); - __ pop(eax); - __ mov(target, eax); + __ mov(target, result_register()); // RecordWrite may destroy all its register arguments. - if (context == Expression::kValue) { - __ push(eax); - } else if (context != Expression::kEffect) { - __ mov(edx, eax); - } + __ mov(edx, result_register()); int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; - __ RecordWrite(ecx, offset, eax, ebx); - if (context != Expression::kEffect && context != Expression::kValue) { - Apply(context, edx); - } + __ RecordWrite(ecx, offset, edx, ebx); break; } @@ -920,6 +983,8 @@ void FastCodeGenerator::EmitVariableAssignment(Variable* var, UNREACHABLE(); break; } + Apply(context, result_register()); + } else { // Variables rewritten as properties are not treated as variables in // assignments. @@ -938,17 +1003,18 @@ void FastCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { // change to slow case to avoid the quadratic behavior of repeatedly // adding fast properties. if (expr->starts_initialization_block()) { - __ push(Operand(esp, kPointerSize)); // Receiver is under value. + __ push(result_register()); + __ push(Operand(esp, kPointerSize)); // Receiver is now under value. __ CallRuntime(Runtime::kToSlowProperties, 1); + __ pop(result_register()); } // Record source code position before IC call. SetSourcePosition(expr->position()); - - __ pop(eax); __ mov(ecx, prop->key()->AsLiteral()->handle()); Handle 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()) { @@ -969,15 +1035,15 @@ void FastCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { // 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(esp, 2 * kPointerSize)); __ CallRuntime(Runtime::kToSlowProperties, 1); + __ pop(result_register()); } // Record source code position before IC call. SetSourcePosition(expr->position()); - - __ pop(eax); Handle 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 @@ -987,7 +1053,7 @@ void FastCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { // If the assignment ends an initialization block, revert to fast case. if (expr->ends_initialization_block()) { __ push(eax); // Result of assignment, saved even if not needed. - // Reciever is under the key and value. + // Receiver is under the key and value. __ push(Operand(esp, 2 * kPointerSize)); __ CallRuntime(Runtime::kToFastProperties, 1); __ pop(eax); @@ -1003,14 +1069,14 @@ void FastCodeGenerator::VisitProperty(Property* expr) { Expression* key = expr->key(); // Evaluate the receiver. - Visit(expr->obj()); + VisitForValue(expr->obj(), kStack); if (key->IsPropertyName()) { EmitNamedPropertyLoad(expr); // Drop receiver left on the stack by IC. DropAndApply(1, expr->context(), eax); } else { - Visit(expr->key()); + VisitForValue(expr->key(), kStack); EmitKeyedPropertyLoad(expr); // Drop key and receiver left on the stack by IC. DropAndApply(2, expr->context(), eax); @@ -1025,8 +1091,7 @@ void FastCodeGenerator::EmitCallWithIC(Call* expr, ZoneList* 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); } __ Set(ecx, Immediate(name)); // Record source position of the IC call. @@ -1045,7 +1110,7 @@ void FastCodeGenerator::EmitCallWithStub(Call* expr) { ZoneList* 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()); @@ -1079,13 +1144,13 @@ void FastCodeGenerator::VisitCall(Call* expr) { Literal* key = prop->key()->AsLiteral(); if (key != NULL && key->handle()->IsSymbol()) { // Call to a named property, use call IC. - 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 ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); @@ -1118,7 +1183,7 @@ void FastCodeGenerator::VisitCall(Call* expr) { loop_depth() == 0) { lit->set_try_fast_codegen(true); } - Visit(fun); + VisitForValue(fun, kStack); // Load global receiver object. __ mov(ebx, CodeGenerator::GlobalObject()); __ push(FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset)); @@ -1134,8 +1199,7 @@ void FastCodeGenerator::VisitCallNew(CallNew* expr) { // 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()); + VisitForValue(expr->expression(), kStack); // Push global object (receiver). __ push(CodeGenerator::GlobalObject()); @@ -1144,10 +1208,7 @@ void FastCodeGenerator::VisitCallNew(CallNew* expr) { ZoneList* 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 @@ -1180,8 +1241,7 @@ void FastCodeGenerator::VisitCallRuntime(CallRuntime* expr) { // 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()) { @@ -1204,8 +1264,8 @@ void FastCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { switch (expr->op()) { case Token::VOID: { Comment cmnt(masm_, "[ UnaryOperation (VOID)"); - Visit(expr->expression()); ASSERT_EQ(Expression::kEffect, expr->expression()->context()); + Visit(expr->expression()); switch (expr->context()) { case Expression::kUninitialized: UNREACHABLE(); @@ -1213,11 +1273,25 @@ void FastCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { case Expression::kEffect: break; case Expression::kValue: - __ push(Immediate(Factory::undefined_value())); + switch (location_) { + case kAccumulator: + __ mov(result_register(), Factory::undefined_value()); + break; + case kStack: + __ push(Immediate(Factory::undefined_value())); + break; + } break; case Expression::kTestValue: // Value is false so it's needed. - __ push(Immediate(Factory::undefined_value())); + switch (location_) { + case kAccumulator: + __ mov(result_register(), Factory::undefined_value()); + break; + case kStack: + __ push(Immediate(Factory::undefined_value())); + break; + } // Fall through. case Expression::kTest: case Expression::kValueTest: @@ -1231,45 +1305,34 @@ void FastCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { 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: - VisitForControl(expr->expression(), &done, &done); - __ bind(&done); + if_true = &done; + if_false = &done; break; - case Expression::kValue: - VisitForControl(expr->expression(), &push_false, &push_true); - __ bind(&push_true); - __ push(Immediate(Factory::true_value())); - __ jmp(&done); - __ bind(&push_false); - __ push(Immediate(Factory::false_value())); - __ bind(&done); + if_true = &materialize_false; + if_false = &materialize_true; 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); - __ push(Immediate(Factory::true_value())); - __ jmp(true_label_); + if_false = &materialize_true; break; - case Expression::kTestValue: - VisitForControl(expr->expression(), &push_false, true_label_); - __ bind(&push_false); - __ push(Immediate(Factory::false_value())); - __ 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; } @@ -1298,7 +1361,7 @@ void FastCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { __ push(eax); } else { // This expression cannot throw a reference error at the top level. - Visit(expr->expression()); + VisitForValue(expr->expression(), kStack); } __ CallRuntime(Runtime::kTypeof, 1); @@ -1331,21 +1394,22 @@ void FastCodeGenerator::VisitCountOperation(CountOperation* expr) { // 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(Immediate(Smi::FromInt(0))); } - Visit(prop->obj()); - ASSERT_EQ(Expression::kValue, prop->obj()->context()); + VisitForValue(prop->obj(), kStack); if (assign_type == NAMED_PROPERTY) { EmitNamedPropertyLoad(prop); } else { - Visit(prop->key()); - ASSERT_EQ(Expression::kValue, prop->key()->context()); + VisitForValue(prop->key(), kStack); EmitKeyedPropertyLoad(prop); } __ push(eax); @@ -1364,8 +1428,8 @@ void FastCodeGenerator::VisitCountOperation(CountOperation* expr) { break; case Expression::kValue: case Expression::kTest: - case Expression::kTestValue: 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. @@ -1395,7 +1459,6 @@ void FastCodeGenerator::VisitCountOperation(CountOperation* expr) { // Store the value returned in eax. switch (assign_type) { case VARIABLE: - __ push(eax); if (expr->is_postfix()) { EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), Expression::kEffect); @@ -1471,20 +1534,12 @@ void FastCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) { 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()); - - Visit(expr->left()); - Visit(expr->right()); - GenericBinaryOpStub stub(expr->op(), - NO_OVERWRITE, - NO_GENERIC_BINARY_FLAGS); - __ CallStub(&stub); - Apply(expr->context(), eax); - + case Token::SAR: + VisitForValue(expr->left(), kStack); + VisitForValue(expr->right(), kAccumulator); + EmitBinaryOp(expr->op(), expr->context()); break; - } + default: UNREACHABLE(); } @@ -1493,14 +1548,10 @@ void FastCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) { 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_; @@ -1513,29 +1564,31 @@ void FastCodeGenerator::VisitCompareOperation(CompareOperation* expr) { if_false = &done; break; case Expression::kValue: - if_true = &push_true; - if_false = &push_false; + 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); __ cmp(eax, Factory::true_value()); __ j(equal, if_true); __ jmp(if_false); break; - } case Token::INSTANCEOF: { + VisitForValue(expr->right(), kStack); InstanceofStub stub; __ CallStub(&stub); __ test(eax, Operand(eax)); @@ -1545,6 +1598,7 @@ void FastCodeGenerator::VisitCompareOperation(CompareOperation* expr) { } default: { + VisitForValue(expr->right(), kAccumulator); Condition cc = no_condition; bool strict = false; switch (expr->op()) { @@ -1553,29 +1607,26 @@ void FastCodeGenerator::VisitCompareOperation(CompareOperation* expr) { // Fall through case Token::EQ: cc = equal; - __ pop(eax); __ pop(edx); break; case Token::LT: cc = less; - __ pop(eax); __ pop(edx); break; case Token::GT: // Reverse left and right sizes to obtain ECMA-262 conversion order. cc = less; - __ pop(edx); + __ mov(edx, result_register()); __ pop(eax); break; case Token::LTE: // Reverse left and right sizes to obtain ECMA-262 conversion order. cc = greater_equal; - __ pop(edx); + __ mov(edx, result_register()); __ pop(eax); break; case Token::GTE: cc = greater_equal; - __ pop(eax); __ pop(edx); break; case Token::IN: @@ -1606,39 +1657,7 @@ void FastCodeGenerator::VisitCompareOperation(CompareOperation* expr) { // 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); - __ push(Immediate(Factory::true_value())); - __ jmp(&done); - __ bind(&push_false); - __ push(Immediate(Factory::false_value())); - __ bind(&done); - break; - - case Expression::kTest: - break; - - case Expression::kValueTest: - __ bind(&push_true); - __ push(Immediate(Factory::true_value())); - __ jmp(true_label_); - break; - - case Expression::kTestValue: - __ bind(&push_false); - __ push(Immediate(Factory::false_value())); - __ jmp(false_label_); - break; - } + Apply(expr->context(), if_true, if_false); } diff --git a/src/x64/fast-codegen-x64.cc b/src/x64/fast-codegen-x64.cc index dea8df4d81..b87f03e6d0 100644 --- a/src/x64/fast-codegen-x64.cc +++ b/src/x64/fast-codegen-x64.cc @@ -62,11 +62,9 @@ void FastCodeGenerator::Generate(FunctionLiteral* fun) { { 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); @@ -202,24 +200,73 @@ void FastCodeGenerator::EmitReturnSequence(int position) { } -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::kValueTest: + case Expression::kTestValue: + // Push an extra copy of the value in case it's needed. + __ push(reg); + // Fall through. + + case Expression::kTest: + // For simplicity we always test the accumulator register. + if (!reg.is(result_register())) __ movq(result_register(), reg); + 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); + __ push(result_register()); + DoTest(context); break; } } @@ -230,15 +277,29 @@ void FastCodeGenerator::Apply(Expression::Context context, Literal* lit) { 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()); + __ push(result_register()); + DoTest(context); break; } } @@ -248,32 +309,31 @@ void FastCodeGenerator::ApplyTOS(Expression::Context context) { 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: + __ movq(result_register(), Operand(rsp, 0)); + 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 +346,181 @@ void FastCodeGenerator::DropAndApply(int count, 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; + + case Expression::kValueTest: + case Expression::kTestValue: if (count > 1) __ Drop(count - 1); - __ movq(Operand(rsp, 0), reg); - TestAndBranch(reg, true_label_, &discard); + if (!reg.is(result_register())) __ movq(result_register(), reg); + __ movq(Operand(rsp, 0), result_register()); + 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); + + 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, and duplicated on the stack if + // necessary (for value/test and test/value contexts). + ASSERT_NE(NULL, true_label_); + ASSERT_NE(NULL, false_label_); + + // If there is a value on the stack, use a discard label for the + // value-is-unneeded branch in the inlined part of the test. + Label discard; + Label* if_true = + (context == Expression::kTestValue) ? &discard : true_label_; + Label* if_false = + (context == Expression::kValueTest) ? &discard : false_label_; + + // 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); + + // 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 +566,6 @@ void FastCodeGenerator::Move(Slot* dst, } -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 +581,8 @@ void FastCodeGenerator::VisitDeclaration(Declaration* decl) { __ 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 +605,11 @@ void FastCodeGenerator::VisitDeclaration(Declaration* decl) { 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 +629,7 @@ void FastCodeGenerator::VisitDeclaration(Declaration* decl) { 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 +642,20 @@ void FastCodeGenerator::VisitDeclaration(Declaration* decl) { 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 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 +700,7 @@ void FastCodeGenerator::VisitVariableProxy(VariableProxy* expr) { void FastCodeGenerator::EmitVariableLoad(Variable* var, - Expression::Context context) { + Expression::Context context) { Expression* rewrite = var->rewrite(); if (rewrite == NULL) { ASSERT(var->is_global()); @@ -547,14 +715,13 @@ void FastCodeGenerator::EmitVariableLoad(Variable* var, // 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 +734,7 @@ void FastCodeGenerator::EmitVariableLoad(Variable* var, 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 +746,9 @@ void FastCodeGenerator::EmitVariableLoad(Variable* var, // "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 +768,7 @@ void FastCodeGenerator::EmitVariableLoad(Variable* var, __ 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 +796,6 @@ void FastCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) { __ Push(expr->pattern()); __ Push(expr->flags()); __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4); - // Label done: __ bind(&done); Apply(expr->context(), rax); } @@ -647,9 +813,8 @@ void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { __ 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 +828,45 @@ void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { 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 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,51 +901,22 @@ void FastCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { __ 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); } } @@ -835,8 +939,9 @@ void FastCodeGenerator::EmitKeyedPropertyLoad(Property* prop) { } -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); @@ -853,7 +958,6 @@ void FastCodeGenerator::EmitVariableAssignment(Variable* var, // 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 ic(Builtins::builtin(Builtins::StoreIC_Initialize)); @@ -865,66 +969,18 @@ void FastCodeGenerator::EmitVariableAssignment(Variable* var, 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; } @@ -932,6 +988,7 @@ void FastCodeGenerator::EmitVariableAssignment(Variable* var, UNREACHABLE(); break; } + Apply(context, result_register()); } else { // Variables rewritten as properties are not treated as variables in // assignments. @@ -950,17 +1007,18 @@ void FastCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { // 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()); } // Record source code position before IC call. SetSourcePosition(expr->position()); - - __ pop(rax); __ Move(rcx, prop->key()->AsLiteral()->handle()); Handle 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()) { @@ -981,15 +1039,15 @@ void FastCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { // 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()); } // Record source code position before IC call. SetSourcePosition(expr->position()); - - __ pop(rax); Handle 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 @@ -999,7 +1057,7 @@ void FastCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { // 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); @@ -1015,14 +1073,14 @@ void FastCodeGenerator::VisitProperty(Property* expr) { Expression* key = expr->key(); // Evaluate receiver. - Visit(expr->obj()); + VisitForValue(expr->obj(), kStack); if (key->IsPropertyName()) { EmitNamedPropertyLoad(expr); // Drop receiver left on the stack by IC. DropAndApply(1, expr->context(), rax); } else { - Visit(expr->key()); + VisitForValue(expr->key(), kStack); EmitKeyedPropertyLoad(expr); // Drop key and receiver left on the stack by IC. DropAndApply(2, expr->context(), rax); @@ -1037,8 +1095,7 @@ void FastCodeGenerator::EmitCallWithIC(Call* expr, ZoneList* 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()); @@ -1059,7 +1116,7 @@ void FastCodeGenerator::EmitCallWithStub(Call* expr) { ZoneList* 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()); @@ -1097,13 +1154,13 @@ void FastCodeGenerator::VisitCall(Call* expr) { 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 ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); @@ -1136,7 +1193,7 @@ void FastCodeGenerator::VisitCall(Call* expr) { 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)); @@ -1152,9 +1209,7 @@ void FastCodeGenerator::VisitCallNew(CallNew* expr) { // 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()); @@ -1163,10 +1218,7 @@ void FastCodeGenerator::VisitCallNew(CallNew* expr) { ZoneList* 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 @@ -1200,8 +1252,7 @@ void FastCodeGenerator::VisitCallRuntime(CallRuntime* expr) { // 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()) { @@ -1224,8 +1275,8 @@ void FastCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { switch (expr->op()) { case Token::VOID: { Comment cmnt(masm_, "[ UnaryOperation (VOID)"); - Visit(expr->expression()); ASSERT_EQ(Expression::kEffect, expr->expression()->context()); + Visit(expr->expression()); switch (expr->context()) { case Expression::kUninitialized: UNREACHABLE(); @@ -1233,11 +1284,25 @@ void FastCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { 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: @@ -1251,45 +1316,34 @@ void FastCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { 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::kValue: - VisitForControl(expr->expression(), &push_false, &push_true); - __ bind(&push_true); - __ PushRoot(Heap::kTrueValueRootIndex); - __ jmp(&done); - __ bind(&push_false); - __ PushRoot(Heap::kFalseValueRootIndex); - __ bind(&done); - break; - case Expression::kEffect: - VisitForControl(expr->expression(), &done, &done); - __ bind(&done); + if_true = &done; + if_false = &done; + break; + case Expression::kValue: + if_true = &materialize_false; + if_false = &materialize_true; 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; } @@ -1318,7 +1372,7 @@ void FastCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { __ 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); @@ -1351,21 +1405,22 @@ void FastCodeGenerator::VisitCountOperation(CountOperation* expr) { // 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); } else { - Visit(prop->key()); - ASSERT_EQ(Expression::kValue, prop->key()->context()); + VisitForValue(prop->key(), kStack); EmitKeyedPropertyLoad(prop); } __ push(rax); @@ -1382,10 +1437,10 @@ void FastCodeGenerator::VisitCountOperation(CountOperation* expr) { 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. @@ -1415,7 +1470,6 @@ void FastCodeGenerator::VisitCountOperation(CountOperation* expr) { // Store the value returned in rax. switch (assign_type) { case VARIABLE: - __ push(rax); if (expr->is_postfix()) { EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), Expression::kEffect); @@ -1490,20 +1544,12 @@ void FastCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) { 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()); - - Visit(expr->left()); - Visit(expr->right()); - GenericBinaryOpStub stub(expr->op(), - NO_OVERWRITE, - NO_GENERIC_BINARY_FLAGS); - __ CallStub(&stub); - Apply(expr->context(), rax); - + case Token::SAR: + VisitForValue(expr->left(), kStack); + VisitForValue(expr->right(), kAccumulator); + EmitBinaryOp(expr->op(), expr->context()); break; - } + default: UNREACHABLE(); } @@ -1512,14 +1558,10 @@ void FastCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) { 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_; @@ -1527,34 +1569,36 @@ void FastCodeGenerator::VisitCompareOperation(CompareOperation* expr) { 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); @@ -1564,6 +1608,7 @@ void FastCodeGenerator::VisitCompareOperation(CompareOperation* expr) { } default: { + VisitForValue(expr->right(), kAccumulator); Condition cc = no_condition; bool strict = false; switch (expr->op()) { @@ -1572,29 +1617,26 @@ void FastCodeGenerator::VisitCompareOperation(CompareOperation* expr) { // 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: @@ -1622,39 +1664,7 @@ void FastCodeGenerator::VisitCompareOperation(CompareOperation* expr) { // 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); } diff --git a/test/mjsunit/compiler/short-circuit.js b/test/mjsunit/compiler/short-circuit.js new file mode 100644 index 0000000000..42100e765d --- /dev/null +++ b/test/mjsunit/compiler/short-circuit.js @@ -0,0 +1,102 @@ +// Copyright 2009 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Test some expression contexts involving short-circuit boolean +// operations that did not otherwise have test coverage. + + +var x = 42; + +// Literals in value/test context. +assertEquals(x, function () { return 0 || x }()); +assertEquals(1, function () { return 1 || x }()); + +// Literals in test/value context. +assertEquals(0, function () { return 0 && x }()); +assertEquals(x, function () { return 1 && x }()); + +// A value on top of the stack in value/test context. +assertEquals(x, function(y) { return y++ || x }(0)); +assertEquals(1, function(y) { return y++ || x }(1)); + +// A value on top of the stack in a test/value context. +assertEquals(0, function(y) { return y++ && x }(0)); +assertEquals(x, function(y) { return y++ && x }(1)); + +// An object literal in value context. +assertEquals(0, function () { return {x: 0}}().x); + +// An object literal in value/test context. +assertEquals(0, function () { return {x: 0} || this }().x); + +// An object literal in test/value context. +assertEquals(x, function () { return {x: 0} && this }().x); + +// An array literal in value/test context. +assertEquals(0, function () { return [0,1] || new Array(x,1) }()[0]); + +// An array literal in test/value context. +assertEquals(x, function () { return [0,1] && new Array(x,1) }()[0]); + +// Slot assignment in value/test context. +assertEquals(x, function (y) { return (y = 0) || x }("?")); +assertEquals(1, function (y) { return (y = 1) || x }("?")); + +// Slot assignment in test/value context. +assertEquals(0, function (y) { return (y = 0) && x }("?")); +assertEquals(x, function (y) { return (y = 1) && x }("?")); + +// void in value context. +assertEquals(void 0, function () { return void x }()); + +// void in value/test context. +assertEquals(x, function () { return (void x) || x }()); + +// void in test/value context. +assertEquals(void 0, function () { return (void x) && x }()); + +// Unary not in value context. +assertEquals(false, function () { return !x }()); + +// Unary not in value/test context. +assertEquals(true, function (y) { return !y || x }(0)); +assertEquals(x, function (y) { return !y || x }(1)); + +// Unary not in test/value context. +assertEquals(x, function (y) { return !y && x }(0)); +assertEquals(false, function (y) { return !y && x }(1)); + +// Comparison in value context. +assertEquals(false, function () { return x < x; }()); + +// Comparison in value/test context. +assertEquals(x, function () { return x < x || x; }()); +assertEquals(true, function () { return x <= x || x; }()); + +// Comparison in test/value context. +assertEquals(false, function () { return x < x && x; }()); +assertEquals(x, function () { return x <= x && x; }());