Revision: 3187 Author: [email protected] Date: Fri Oct 30 07:06:48 2009 Log: Initial implementation of top-level compilation of expressions in test context. Test contexts are used for the left subexpressions of short-circuited boolean operators. The right subexpressions inherit their expression context from the binary op expression.
Compilation of short-circuited operations in effect and test context is straightforward: effect(e0 || e1) = test(e0, L0, L1) L1: effect(e1) L0: test(e0 || e1, L0, L1) = test(e0, L0, L2) L2: test(e1, L0, L1) Because the value of the first subexpression may be needed as the value of the whole expression in a value context, we introduce a hybrid value/test contest (the value is needed if true, but not if false). value(e0 || e1) = value/test(e0, L0, L1) L1: value(e1) L0: The compilation of value/test and test/value (introduced by boolean AND) is: value/test(e0 || e1, L0, L1) = value/test(e0, L0, L2) L2: value/test(e1, L0, L1) test/value(e0 || e1, L0, L1) = test(e0, L0, L2) L2: test/value(e1, L0, L1) Boolean AND is the dual. The AST nodes themselves (not their parents) are responsible for producing the proper result (effect, value, or control flow) depending on their context. Review URL: http://codereview.chromium.org/339082 http://code.google.com/p/v8/source/detail?r=3187 Modified: /branches/bleeding_edge/src/arm/fast-codegen-arm.cc /branches/bleeding_edge/src/ast.h /branches/bleeding_edge/src/compiler.cc /branches/bleeding_edge/src/fast-codegen.cc /branches/bleeding_edge/src/fast-codegen.h /branches/bleeding_edge/src/ia32/fast-codegen-ia32.cc /branches/bleeding_edge/src/x64/fast-codegen-x64.cc ======================================= --- /branches/bleeding_edge/src/arm/fast-codegen-arm.cc Fri Oct 30 04:32:42 2009 +++ /branches/bleeding_edge/src/arm/fast-codegen-arm.cc Fri Oct 30 07:06:48 2009 @@ -123,15 +123,51 @@ } -void FastCodeGenerator::Move(Expression::Context context, Slot* source) { +void FastCodeGenerator::Move(Expression::Context context, Register source) { switch (context) { case Expression::kUninitialized: UNREACHABLE(); case Expression::kEffect: break; case Expression::kValue: + __ push(source); + break; + case Expression::kTest: + TestAndBranch(source, true_label_, false_label_); + break; + case Expression::kValueTest: { + Label discard; + __ push(source); + TestAndBranch(source, true_label_, &discard); + __ bind(&discard); + __ pop(); + __ jmp(false_label_); + break; + } + case Expression::kTestValue: { + Label discard; + __ push(source); + TestAndBranch(source, &discard, false_label_); + __ bind(&discard); + __ pop(); + __ jmp(true_label_); + } + } +} + + +void FastCodeGenerator::Move(Expression::Context context, Slot* source) { + switch (context) { + case Expression::kUninitialized: + UNREACHABLE(); + case Expression::kEffect: + break; + case Expression::kValue: // Fall through. + case Expression::kTest: // Fall through. + case Expression::kValueTest: // Fall through. + case Expression::kTestValue: __ ldr(ip, MemOperand(fp, SlotOffset(source))); - __ push(ip); + Move(context, ip); break; } } @@ -143,9 +179,12 @@ UNREACHABLE(); case Expression::kEffect: break; - case Expression::kValue: + case Expression::kValue: // Fall through. + case Expression::kTest: // Fall through. + case Expression::kValueTest: // Fall through. + case Expression::kTestValue: __ mov(ip, Operand(expr->handle())); - __ push(ip); + Move(context, ip); break; } } @@ -162,8 +201,47 @@ case Expression::kValue: __ str(source, MemOperand(sp)); break; + case Expression::kTest: + ASSERT(!source.is(sp)); + __ pop(); + TestAndBranch(source, true_label_, false_label_); + break; + case Expression::kValueTest: { + Label discard; + __ str(source, MemOperand(sp)); + TestAndBranch(source, true_label_, &discard); + __ bind(&discard); + __ pop(); + __ jmp(false_label_); + break; + } + case Expression::kTestValue: { + Label discard; + __ str(source, MemOperand(sp)); + TestAndBranch(source, &discard, false_label_); + __ bind(&discard); + __ pop(); + __ jmp(true_label_); + break; + } } } + + +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::DeclareGlobals(Handle<FixedArray> pairs) { @@ -369,6 +447,28 @@ 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); + __ pop(); + __ jmp(false_label_); + break; + } + case Expression::kTestValue: { + Label discard; + if (!result_saved) __ push(r0); + TestAndBranch(r0, &discard, false_label_); + __ bind(&discard); + __ pop(); + __ jmp(true_label_); + break; + } } } @@ -446,6 +546,28 @@ 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); + __ pop(); + __ jmp(false_label_); + break; + } + case Expression::kTestValue: { + Label discard; + if (!result_saved) __ push(r0); + TestAndBranch(r0, &discard, false_label_); + __ bind(&discard); + __ pop(); + __ jmp(true_label_); + break; + } } } @@ -530,16 +652,44 @@ UNREACHABLE(); case Expression::kEffect: // Case 'var = temp'. Discard right-hand-side temporary. - __ pop(ip); + __ pop(r0); + __ str(r0, MemOperand(fp, SlotOffset(var->slot()))); break; case Expression::kValue: // Case 'temp1 <- (var = temp0)'. Preserve right-hand-side // temporary on the stack. - __ ldr(ip, MemOperand(sp)); + __ ldr(r0, MemOperand(sp)); + __ str(r0, MemOperand(fp, SlotOffset(var->slot()))); break; - } - // Do the slot assignment. - __ str(ip, MemOperand(fp, SlotOffset(var->slot()))); + case Expression::kTest: + // Case 'if (var = temp) ...'. + __ pop(r0); + __ str(r0, MemOperand(fp, SlotOffset(var->slot()))); + TestAndBranch(r0, true_label_, false_label_); + break; + case Expression::kValueTest: { + // Case '(var = temp) || ...' in value context. + Label discard; + __ ldr(r0, MemOperand(sp)); + __ str(r0, MemOperand(fp, SlotOffset(var->slot()))); + TestAndBranch(r0, true_label_, &discard); + __ bind(&discard); + __ pop(); + __ jmp(false_label_); + break; + } + case Expression::kTestValue: { + // Case '(var = temp) && ...' in value context. + Label discard; + __ ldr(r0, MemOperand(sp)); + __ str(r0, MemOperand(fp, SlotOffset(var->slot()))); + TestAndBranch(r0, &discard, false_label_); + __ bind(&discard); + __ pop(); + __ jmp(true_label_); + break; + } + } } } } @@ -734,11 +884,19 @@ case Expression::kUninitialized: UNREACHABLE(); break; + case Expression::kEffect: + break; case Expression::kValue: __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); __ push(ip); break; - case Expression::kEffect: + case Expression::kTestValue: + // Value is false so it's needed. + __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); + __ push(ip); + case Expression::kTest: // Fall through. + case Expression::kValueTest: + __ jmp(false_label_); break; } break; @@ -794,52 +952,4 @@ } -void FastCodeGenerator::EmitLogicalOperation(BinaryOperation* expr) { - // Compile a short-circuited boolean operation in a non-test context. - - // Compile (e0 || e1) as if it were - // (let (temp = e0) temp ? temp : e1). - // Compile (e0 && e1) as if it were - // (let (temp = e0) !temp ? temp : e1). - - Label done; - Expression::Context context = expr->context(); - Expression* left = expr->left(); - Expression* right = expr->right(); - - // Call the runtime to find the boolean value of the left-hand - // subexpression. Duplicate the value if it may be needed as the final - // result. - if (left->AsLiteral() != NULL) { - __ mov(r0, Operand(left->AsLiteral()->handle())); - __ push(r0); - if (context == Expression::kValue) __ push(r0); - } else { - Visit(left); - ASSERT_EQ(Expression::kValue, left->context()); - if (context == Expression::kValue) { - __ ldr(r0, MemOperand(sp)); - __ push(r0); - } - } - // The left-hand value is in on top of the stack. It is duplicated on the - // stack iff the destination location is value. - __ CallRuntime(Runtime::kToBool, 1); - if (expr->op() == Token::OR) { - __ LoadRoot(ip, Heap::kTrueValueRootIndex); - } else { - __ LoadRoot(ip, Heap::kFalseValueRootIndex); - } - __ cmp(r0, ip); - __ b(eq, &done); - - // Discard the left-hand value if present on the stack. - if (context == Expression::kValue) __ pop(); - // Save or discard the right-hand value as needed. - Visit(right); - ASSERT_EQ(context, right->context()); - - __ bind(&done); -} - } } // namespace v8::internal ======================================= --- /branches/bleeding_edge/src/ast.h Fri Oct 30 01:36:46 2009 +++ /branches/bleeding_edge/src/ast.h Fri Oct 30 07:06:48 2009 @@ -162,9 +162,21 @@ class Expression: public AstNode { public: enum Context { + // Not assigned a context yet, or else will not be visited during + // code generation. kUninitialized, + // Evaluated for its side effects. kEffect, - kValue + // Evaluated for its value (and side effects). + kValue, + // Evaluated for control flow (and side effects). + kTest, + // Evaluated for control flow and side effects. Value is also + // needed if true. + kValueTest, + // Evaluated for control flow and side effects. Value is also + // needed if false. + kTestValue }; Expression() : context_(kUninitialized) {} ======================================= --- /branches/bleeding_edge/src/compiler.cc Fri Oct 30 04:32:42 2009 +++ /branches/bleeding_edge/src/compiler.cc Fri Oct 30 07:06:48 2009 @@ -849,7 +849,45 @@ break; case Token::OR: - ProcessExpression(expr->left(), Expression::kValue); + switch (context_) { + case Expression::kUninitialized: + UNREACHABLE(); + case Expression::kEffect: // Fall through. + case Expression::kTest: // Fall through. + case Expression::kTestValue: + // The left subexpression's value is not needed, it is in a pure + // test context. + ProcessExpression(expr->left(), Expression::kTest); + break; + case Expression::kValue: // Fall through. + case Expression::kValueTest: + // The left subexpression's value is needed, it is in a hybrid + // value/test context. + ProcessExpression(expr->left(), Expression::kValueTest); + break; + } + CHECK_BAILOUT; + ProcessExpression(expr->right(), context_); + break; + + case Token::AND: + switch (context_) { + case Expression::kUninitialized: + UNREACHABLE(); + case Expression::kEffect: // Fall through. + case Expression::kTest: // Fall through. + case Expression::kValueTest: + // The left subexpression's value is not needed, it is in a pure + // test context. + ProcessExpression(expr->left(), Expression::kTest); + break; + case Expression::kValue: // Fall through. + case Expression::kTestValue: + // The left subexpression's value is needed, it is in a hybrid + // test/value context. + ProcessExpression(expr->left(), Expression::kTestValue); + break; + } CHECK_BAILOUT; ProcessExpression(expr->right(), context_); break; ======================================= --- /branches/bleeding_edge/src/fast-codegen.cc Fri Oct 30 04:32:42 2009 +++ /branches/bleeding_edge/src/fast-codegen.cc Fri Oct 30 07:06:48 2009 @@ -35,6 +35,8 @@ namespace v8 { namespace internal { +#define __ ACCESS_MASM(masm_) + Handle<Code> FastCodeGenerator::MakeCode(FunctionLiteral* fun, Handle<Script> script, bool is_eval) { @@ -69,21 +71,6 @@ } return offset; } - - -// All platform macro assemblers in {ia32,x64,arm} have a push(Register) -// function. -void FastCodeGenerator::Move(Expression::Context context, Register source) { - switch (context) { - case Expression::kUninitialized: - UNREACHABLE(); - case Expression::kEffect: - break; - case Expression::kValue: - masm_->push(source); - break; - } -} void FastCodeGenerator::VisitDeclarations( @@ -200,6 +187,80 @@ masm_->RecordPosition(pos); } } + + +void FastCodeGenerator::EmitLogicalOperation(BinaryOperation* expr) { +#ifdef DEBUG + Expression::Context expected = Expression::kUninitialized; + switch (expr->context()) { + case Expression::kUninitialized: + UNREACHABLE(); + case Expression::kEffect: // Fall through. + case Expression::kTest: + // The value of the left subexpression is not needed. + expected = Expression::kTest; + break; + case Expression::kValue: + // The value of the left subexpression is needed and its specific + // context depends on the operator. + expected = (expr->op() == Token::OR) + ? Expression::kValueTest + : Expression::kTestValue; + break; + case Expression::kValueTest: + // The value of the left subexpression is needed for OR. + expected = (expr->op() == Token::OR) + ? Expression::kValueTest + : Expression::kTest; + break; + case Expression::kTestValue: + // The value of the left subexpression is needed for AND. + expected = (expr->op() == Token::OR) + ? Expression::kTest + : Expression::kTestValue; + break; + } + ASSERT_EQ(expected, expr->left()->context()); + ASSERT_EQ(expr->context(), expr->right()->context()); +#endif + + Label eval_right, done; + Label* saved_true = true_label_; + Label* saved_false = false_label_; + + // Set up the appropriate context for the left subexpression based on the + // operation and our own context. + if (expr->op() == Token::OR) { + // If there is no usable true label in the OR expression's context, use + // the end of this expression, otherwise inherit the same true label. + if (expr->context() == Expression::kEffect || + expr->context() == Expression::kValue) { + true_label_ = &done; + } + // The false label is the label of the second subexpression. + false_label_ = &eval_right; + } else { + ASSERT_EQ(Token::AND, expr->op()); + // The true label is the label of the second subexpression. + true_label_ = &eval_right; + // If there is no usable false label in the AND expression's context, + // use the end of the expression, otherwise inherit the same false + // label. + if (expr->context() == Expression::kEffect || + expr->context() == Expression::kValue) { + false_label_ = &done; + } + } + + Visit(expr->left()); + true_label_ = saved_true; + false_label_ = saved_false; + + __ bind(&eval_right); + Visit(expr->right()); + + __ bind(&done); +} void FastCodeGenerator::VisitDeclaration(Declaration* decl) { @@ -337,6 +398,9 @@ void FastCodeGenerator::VisitThisFunction(ThisFunction* expr) { UNREACHABLE(); } + + +#undef __ } } // namespace v8::internal ======================================= --- /branches/bleeding_edge/src/fast-codegen.h Fri Oct 30 03:22:31 2009 +++ /branches/bleeding_edge/src/fast-codegen.h Fri Oct 30 07:06:48 2009 @@ -39,7 +39,12 @@ class FastCodeGenerator: public AstVisitor { public: FastCodeGenerator(MacroAssembler* masm, Handle<Script> script, bool is_eval) - : masm_(masm), function_(NULL), script_(script), is_eval_(is_eval) { + : masm_(masm), + function_(NULL), + script_(script), + is_eval_(is_eval), + true_label_(NULL), + false_label_(NULL) { } static Handle<Code> MakeCode(FunctionLiteral* fun, @@ -59,6 +64,10 @@ // If destination is TOS, just overwrite TOS with source. void DropAndMove(Expression::Context destination, Register source); + // 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 VisitDeclarations(ZoneList<Declaration*>* declarations); Handle<JSFunction> BuildBoilerplate(FunctionLiteral* fun); void DeclareGlobals(Handle<FixedArray> pairs); @@ -85,6 +94,9 @@ bool is_eval_; Label return_label_; + Label* true_label_; + Label* false_label_; + DISALLOW_COPY_AND_ASSIGN(FastCodeGenerator); }; ======================================= --- /branches/bleeding_edge/src/ia32/fast-codegen-ia32.cc Fri Oct 30 04:32:42 2009 +++ /branches/bleeding_edge/src/ia32/fast-codegen-ia32.cc Fri Oct 30 07:06:48 2009 @@ -115,6 +115,39 @@ } } } + + +void FastCodeGenerator::Move(Expression::Context context, Register source) { + switch (context) { + case Expression::kUninitialized: + UNREACHABLE(); + case Expression::kEffect: + break; + case Expression::kValue: + __ push(source); + break; + case Expression::kTest: + TestAndBranch(source, true_label_, false_label_); + break; + case Expression::kValueTest: { + Label discard; + __ push(source); + TestAndBranch(source, true_label_, &discard); + __ bind(&discard); + __ add(Operand(esp), Immediate(kPointerSize)); + __ jmp(false_label_); + break; + } + case Expression::kTestValue: { + Label discard; + __ push(source); + TestAndBranch(source, &discard, false_label_); + __ bind(&discard); + __ add(Operand(esp), Immediate(kPointerSize)); + __ jmp(true_label_); + } + } +} void FastCodeGenerator::Move(Expression::Context context, Slot* source) { @@ -126,6 +159,12 @@ case Expression::kValue: __ push(Operand(ebp, SlotOffset(source))); break; + case Expression::kTest: // Fall through. + case Expression::kValueTest: // Fall through. + case Expression::kTestValue: + __ mov(eax, Operand(ebp, SlotOffset(source))); + Move(context, eax); + break; } } @@ -139,6 +178,12 @@ case Expression::kValue: __ push(Immediate(expr->handle())); break; + case Expression::kTest: // Fall through. + case Expression::kValueTest: // Fall through. + case Expression::kTestValue: + __ mov(eax, expr->handle()); + Move(context, eax); + break; } } @@ -154,8 +199,61 @@ case Expression::kValue: __ mov(Operand(esp, 0), source); break; + case Expression::kTest: + ASSERT(!source.is(esp)); + __ add(Operand(esp), Immediate(kPointerSize)); + TestAndBranch(source, true_label_, false_label_); + break; + case Expression::kValueTest: { + Label discard; + __ mov(Operand(esp, 0), source); + TestAndBranch(source, true_label_, &discard); + __ bind(&discard); + __ add(Operand(esp), Immediate(kPointerSize)); + __ jmp(false_label_); + break; + } + case Expression::kTestValue: { + Label discard; + __ mov(Operand(esp, 0), source); + TestAndBranch(source, &discard, false_label_); + __ bind(&discard); + __ add(Operand(esp), Immediate(kPointerSize)); + __ jmp(true_label_); + break; + } } } + + +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::DeclareGlobals(Handle<FixedArray> pairs) { @@ -366,6 +464,28 @@ 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); + __ add(Operand(esp), Immediate(kPointerSize)); + __ jmp(false_label_); + break; + } + case Expression::kTestValue: { + Label discard; + if (!result_saved) __ push(eax); + TestAndBranch(eax, &discard, false_label_); + __ bind(&discard); + __ add(Operand(esp), Immediate(kPointerSize)); + __ jmp(true_label_); + break; + } } } @@ -440,6 +560,28 @@ 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); + __ add(Operand(esp), Immediate(kPointerSize)); + __ jmp(false_label_); + break; + } + case Expression::kTestValue: { + Label discard; + if (!result_saved) __ push(eax); + TestAndBranch(eax, &discard, false_label_); + __ bind(&discard); + __ add(Operand(esp), Immediate(kPointerSize)); + __ jmp(true_label_); + break; + } } } @@ -535,6 +677,34 @@ __ mov(eax, Operand(esp, 0)); __ mov(Operand(ebp, SlotOffset(var->slot())), eax); break; + case Expression::kTest: + // Case 'if (var = temp) ...'. + __ pop(eax); + __ mov(Operand(ebp, SlotOffset(var->slot())), eax); + TestAndBranch(eax, true_label_, false_label_); + break; + case Expression::kValueTest: { + // Case '(var = temp) || ...' in value context. + Label discard; + __ mov(eax, Operand(esp, 0)); + __ mov(Operand(ebp, SlotOffset(var->slot())), eax); + TestAndBranch(eax, true_label_, &discard); + __ bind(&discard); + __ add(Operand(esp), Immediate(kPointerSize)); + __ jmp(false_label_); + break; + } + case Expression::kTestValue: { + // Case '(var = temp) && ...' in value context. + Label discard; + __ mov(eax, Operand(esp, 0)); + __ mov(Operand(ebp, SlotOffset(var->slot())), eax); + TestAndBranch(eax, &discard, false_label_); + __ bind(&discard); + __ add(Operand(esp), Immediate(kPointerSize)); + __ jmp(true_label_); + break; + } } } } @@ -740,10 +910,18 @@ case Expression::kUninitialized: UNREACHABLE(); break; + case Expression::kEffect: + break; case Expression::kValue: __ push(Immediate(Factory::undefined_value())); break; - case Expression::kEffect: + case Expression::kTestValue: + // Value is false so it's needed. + __ push(Immediate(Factory::undefined_value())); + // Fall through. + case Expression::kTest: // Fall through. + case Expression::kValueTest: + __ jmp(false_label_); break; } break; @@ -798,90 +976,7 @@ } -void FastCodeGenerator::EmitLogicalOperation(BinaryOperation* expr) { - // Compile a short-circuited boolean operation in a non-test context. - - // Compile (e0 || e1) or (e0 && e1) as if it were - // (let (temp = e0) temp [or !temp, for &&] ? temp : e1). - - Label eval_right, done; - Label *left_true, *left_false; // Where to branch to if lhs has that value. - if (expr->op() == Token::OR) { - left_true = &done; - left_false = &eval_right; - } else { - left_true = &eval_right; - left_false = &done; - } - Expression::Context context = expr->context(); - Expression* left = expr->left(); - Expression* right = expr->right(); - - // Use the shared ToBoolean stub to find the boolean value of the - // left-hand subexpression. Load the value into eax to perform some - // inlined checks assumed by the stub. - - // Compile the left-hand value into eax. Put it on the stack if we may - // need it as the value of the whole expression. - if (left->AsLiteral() != NULL) { - __ mov(eax, left->AsLiteral()->handle()); - if (context == Expression::kValue) __ push(eax); - } else { - Visit(left); - ASSERT_EQ(Expression::kValue, left->context()); - switch (context) { - case Expression::kUninitialized: - UNREACHABLE(); - case Expression::kEffect: - // Pop the left-hand value into eax because we will not need it as the - // final result. - __ pop(eax); - break; - case Expression::kValue: - // Copy the left-hand value into eax because we may need it as the - // final result. - __ mov(eax, Operand(esp, 0)); - break; - } - } - // The left-hand value is in eax. It is also on the stack iff the - // destination location is value. - - // Perform fast checks assumed by the stub. - __ cmp(eax, Factory::undefined_value()); // The undefined value is false. - __ j(equal, left_false); - __ cmp(eax, Factory::true_value()); // True is true. - __ j(equal, left_true); - __ cmp(eax, Factory::false_value()); // False is false. - __ j(equal, left_false); - ASSERT_EQ(0, kSmiTag); - __ test(eax, Operand(eax)); // The smi zero is false. - __ j(zero, left_false); - __ test(eax, Immediate(kSmiTagMask)); // All other smis are true. - __ j(zero, left_true); - - // Call the stub for all other cases. - __ push(eax); - ToBooleanStub stub; - __ CallStub(&stub); - __ test(eax, Operand(eax)); // The stub returns nonzero for true. - if (expr->op() == Token::OR) { - __ j(not_zero, &done); - } else { - __ j(zero, &done); - } - - __ bind(&eval_right); - // Discard the left-hand value if present on the stack. - if (context == Expression::kValue) { - __ add(Operand(esp), Immediate(kPointerSize)); - } - // Save or discard the right-hand value as needed. - Visit(right); - ASSERT_EQ(context, right->context()); - - __ bind(&done); -} +#undef __ } } // namespace v8::internal ======================================= --- /branches/bleeding_edge/src/x64/fast-codegen-x64.cc Fri Oct 30 04:32:42 2009 +++ /branches/bleeding_edge/src/x64/fast-codegen-x64.cc Fri Oct 30 07:06:48 2009 @@ -123,6 +123,41 @@ } } } + + + +void FastCodeGenerator::Move(Expression::Context context, Register source) { + switch (context) { + case Expression::kUninitialized: + UNREACHABLE(); + case Expression::kEffect: + break; + case Expression::kValue: + __ push(source); + break; + case Expression::kTest: + TestAndBranch(source, true_label_, false_label_); + break; + case Expression::kValueTest: { + Label discard; + __ push(source); + TestAndBranch(source, true_label_, &discard); + __ bind(&discard); + __ addq(rsp, Immediate(kPointerSize)); + __ jmp(false_label_); + break; + } + case Expression::kTestValue: { + Label discard; + __ push(source); + TestAndBranch(source, &discard, false_label_); + __ bind(&discard); + __ addq(rsp, Immediate(kPointerSize)); + __ jmp(true_label_); + break; + } + } +} void FastCodeGenerator::Move(Expression::Context context, Slot* source) { @@ -134,6 +169,12 @@ case Expression::kValue: __ push(Operand(rbp, SlotOffset(source))); break; + case Expression::kTest: // Fall through. + case Expression::kValueTest: // Fall through. + case Expression::kTestValue: + __ movq(rax, Operand(rbp, SlotOffset(source))); + Move(context, rax); + break; } } @@ -147,6 +188,12 @@ case Expression::kValue: __ Push(expr->handle()); break; + case Expression::kTest: // Fall through. + case Expression::kValueTest: // Fall through. + case Expression::kTestValue: + __ Move(rax, expr->handle()); + Move(context, rax); + break; } } @@ -162,8 +209,63 @@ case Expression::kValue: __ movq(Operand(rsp, 0), source); break; + case Expression::kTest: + ASSERT(!source.is(rsp)); + __ addq(rsp, Immediate(kPointerSize)); + TestAndBranch(source, true_label_, false_label_); + break; + case Expression::kValueTest: { + Label discard; + __ movq(Operand(rsp, 0), source); + TestAndBranch(source, true_label_, &discard); + __ bind(&discard); + __ addq(rsp, Immediate(kPointerSize)); + __ jmp(false_label_); + break; + } + case Expression::kTestValue: { + Label discard; + __ movq(Operand(rsp, 0), source); + TestAndBranch(source, &discard, false_label_); + __ bind(&discard); + __ addq(rsp, Immediate(kPointerSize)); + __ jmp(true_label_); + break; + } } } + + +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::DeclareGlobals(Handle<FixedArray> pairs) { @@ -376,6 +478,28 @@ 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); + __ addq(rsp, Immediate(kPointerSize)); + __ jmp(false_label_); + break; + } + case Expression::kTestValue: { + Label discard; + if (!result_saved) __ push(rax); + TestAndBranch(rax, &discard, false_label_); + __ bind(&discard); + __ addq(rsp, Immediate(kPointerSize)); + __ jmp(true_label_); + break; + } } } @@ -450,6 +574,28 @@ 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); + __ addq(rsp, Immediate(kPointerSize)); + __ jmp(false_label_); + break; + } + case Expression::kTestValue: { + Label discard; + if (!result_saved) __ push(rax); + TestAndBranch(rax, &discard, false_label_); + __ bind(&discard); + __ addq(rsp, Immediate(kPointerSize)); + __ jmp(true_label_); + break; + } } } @@ -544,6 +690,34 @@ __ movq(kScratchRegister, Operand(rsp, 0)); __ movq(Operand(rbp, SlotOffset(var->slot())), kScratchRegister); break; + case Expression::kTest: + // Case 'if (var = temp) ...'. + __ pop(rax); + __ movq(Operand(rbp, SlotOffset(var->slot())), rax); + TestAndBranch(rax, true_label_, false_label_); + break; + case Expression::kValueTest: { + // Case '(var = temp) || ...' in value context. + Label discard; + __ movq(rax, Operand(rsp, 0)); + __ movq(Operand(rbp, SlotOffset(var->slot())), rax); + TestAndBranch(rax, true_label_, &discard); + __ bind(&discard); + __ addq(rsp, Immediate(kPointerSize)); + __ jmp(false_label_); + break; + } + case Expression::kTestValue: { + // Case '(var = temp) && ...' in value context. + Label discard; + __ movq(rax, Operand(rsp, 0)); + __ movq(Operand(rbp, SlotOffset(var->slot())), rax); + TestAndBranch(rax, &discard, false_label_); + __ bind(&discard); + __ addq(rsp, Immediate(kPointerSize)); + __ jmp(true_label_); + break; + } } } } @@ -752,10 +926,18 @@ case Expression::kUninitialized: UNREACHABLE(); break; + case Expression::kEffect: + break; case Expression::kValue: __ PushRoot(Heap::kUndefinedValueRootIndex); break; - case Expression::kEffect: + case Expression::kTestValue: + // Value is false so it's needed. + __ PushRoot(Heap::kUndefinedValueRootIndex); + // Fall through. + case Expression::kTest: // Fall through. + case Expression::kValueTest: + __ jmp(false_label_); break; } break; @@ -808,95 +990,6 @@ UNREACHABLE(); } } - - -void FastCodeGenerator::EmitLogicalOperation(BinaryOperation* expr) { - // Compile a short-circuited boolean operation in a non-test context. - - // Compile (e0 || e1) as if it were - // (let (temp = e0) temp ? temp : e1). - // Compile (e0 && e1) as if it were - // (let (temp = e0) !temp ? temp : e1). - - Label eval_right, done; - Label *left_true, *left_false; // Where to branch to if lhs has that value. - if (expr->op() == Token::OR) { - left_true = &done; - left_false = &eval_right; - } else { - left_true = &eval_right; - left_false = &done; - } - Expression::Context context = expr->context(); - Expression* left = expr->left(); - Expression* right = expr->right(); - - // Use the shared ToBoolean stub to find the boolean value of the - // left-hand subexpression. Load the value into rax to perform some - // inlined checks assumed by the stub. - - // Compile the left-hand value into rax. Put it on the stack if we may - // need it as the value of the whole expression. - if (left->AsLiteral() != NULL) { - __ Move(rax, left->AsLiteral()->handle()); - if (context == Expression::kValue) __ push(rax); - } else { - Visit(left); - ASSERT_EQ(Expression::kValue, left->context()); - switch (context) { - case Expression::kUninitialized: - UNREACHABLE(); - case Expression::kEffect: - // Pop the left-hand value into rax because we will not need it as the - // final result. - __ pop(rax); - break; - case Expression::kValue: - // Copy the left-hand value into rax because we may need it as the - // final result. - __ movq(rax, Operand(rsp, 0)); - break; - } - } - // The left-hand value is in rax. It is also on the stack iff the - // destination location is value. - - // Perform fast checks assumed by the stub. - // The undefined value is false. - __ CompareRoot(rax, Heap::kUndefinedValueRootIndex); - __ j(equal, left_false); - __ CompareRoot(rax, Heap::kTrueValueRootIndex); // True is true. - __ j(equal, left_true); - __ CompareRoot(rax, Heap::kFalseValueRootIndex); // False is false. - __ j(equal, left_false); - ASSERT(kSmiTag == 0); - __ SmiCompare(rax, Smi::FromInt(0)); // The smi zero is false. - __ j(equal, left_false); - Condition is_smi = masm_->CheckSmi(rax); // All other smis are true. - __ j(is_smi, left_true); - - // Call the stub for all other cases. - __ push(rax); - ToBooleanStub stub; - __ CallStub(&stub); - __ testq(rax, rax); // The stub returns nonzero for true. - if (expr->op() == Token::OR) { - __ j(not_zero, &done); - } else { - __ j(zero, &done); - } - - __ bind(&eval_right); - // Discard the left-hand value if present on the stack. - if (context == Expression::kValue) { - __ addq(rsp, Immediate(kPointerSize)); - } - // Save or discard the right-hand value as needed. - Visit(right); - ASSERT_EQ(context, right->context()); - - __ bind(&done); -} } } // namespace v8::internal --~--~---------~--~----~------------~-------~--~----~ v8-dev mailing list [email protected] http://groups.google.com/group/v8-dev -~----------~----~----~----~------~----~------~--~---
