Initial implementation of top-level compilation of expressions in test
authorkmillikin@chromium.org <kmillikin@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 30 Oct 2009 14:06:48 +0000 (14:06 +0000)
committerkmillikin@chromium.org <kmillikin@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 30 Oct 2009 14:06:48 +0000 (14:06 +0000)
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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3187 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/arm/fast-codegen-arm.cc
src/ast.h
src/compiler.cc
src/fast-codegen.cc
src/fast-codegen.h
src/ia32/fast-codegen-ia32.cc
src/x64/fast-codegen-x64.cc

index ebf15aa..d592284 100644 (file)
@@ -123,15 +123,51 @@ void FastCodeGenerator::Generate(FunctionLiteral* fun) {
 }
 
 
-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 @@ void FastCodeGenerator::Move(Expression::Context context, Literal* expr) {
       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,10 +201,49 @@ void FastCodeGenerator::DropAndMove(Expression::Context context,
     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) {
   // Call the runtime to declare the globals.
   // The context is the first argument.
@@ -369,6 +447,28 @@ void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
     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 @@ void FastCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
     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 @@ void FastCodeGenerator::VisitAssignment(Assignment* expr) {
           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;
+        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;
+        }
       }
-      // Do the slot assignment.
-      __ str(ip, MemOperand(fp, SlotOffset(var->slot())));
     }
   }
 }
@@ -734,11 +884,19 @@ void FastCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
         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::VisitBinaryOperation(BinaryOperation* expr) {
 }
 
 
-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
index 4c661e1..590a3f8 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -162,9 +162,21 @@ class Statement: public AstNode {
 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) {}
index 38a41a2..297ada3 100644 (file)
@@ -849,7 +849,45 @@ void CodeGenSelector::VisitBinaryOperation(BinaryOperation* expr) {
       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;
index 397fe34..e2ec9cc 100644 (file)
@@ -35,6 +35,8 @@
 namespace v8 {
 namespace internal {
 
+#define __ ACCESS_MASM(masm_)
+
 Handle<Code> FastCodeGenerator::MakeCode(FunctionLiteral* fun,
                                          Handle<Script> script,
                                          bool is_eval) {
@@ -71,21 +73,6 @@ int FastCodeGenerator::SlotOffset(Slot* slot) {
 }
 
 
-// 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(
     ZoneList<Declaration*>* declarations) {
   int length = declarations->length();
@@ -202,6 +189,80 @@ void FastCodeGenerator::SetSourcePosition(int 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) {
   UNREACHABLE();
 }
@@ -339,4 +400,7 @@ void FastCodeGenerator::VisitThisFunction(ThisFunction* expr) {
 }
 
 
+#undef __
+
+
 } }  // namespace v8::internal
index 26f031e..f0ea85d 100644 (file)
@@ -39,7 +39,12 @@ namespace internal {
 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 @@ class FastCodeGenerator: public AstVisitor {
   // 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 @@ class FastCodeGenerator: public AstVisitor {
   bool is_eval_;
   Label return_label_;
 
+  Label* true_label_;
+  Label* false_label_;
+
   DISALLOW_COPY_AND_ASSIGN(FastCodeGenerator);
 };
 
index 01f8bd4..6ce355e 100644 (file)
@@ -117,6 +117,39 @@ void FastCodeGenerator::Generate(FunctionLiteral* fun) {
 }
 
 
+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) {
   switch (context) {
     case Expression::kUninitialized:
@@ -126,6 +159,12 @@ void FastCodeGenerator::Move(Expression::Context context, Slot* source) {
     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 @@ void FastCodeGenerator::Move(Expression::Context context, Literal* expr) {
     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,10 +199,63 @@ void FastCodeGenerator::DropAndMove(Expression::Context context,
     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) {
   // Call the runtime to declare the globals.
   __ push(esi);  // The context is the first argument.
@@ -366,6 +464,28 @@ void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
     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 @@ void FastCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
     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 @@ void FastCodeGenerator::VisitAssignment(Assignment* expr) {
           __ 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 @@ void FastCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
         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::VisitBinaryOperation(BinaryOperation* expr) {
 }
 
 
-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
index 5ea11fc..3804d81 100644 (file)
@@ -125,6 +125,41 @@ void FastCodeGenerator::Generate(FunctionLiteral* fun) {
 }
 
 
+
+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) {
   switch (context) {
     case Expression::kUninitialized:
@@ -134,6 +169,12 @@ void FastCodeGenerator::Move(Expression::Context context, Slot* source) {
     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 @@ void FastCodeGenerator::Move(Expression::Context context, Literal* expr) {
     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,10 +209,65 @@ void FastCodeGenerator::DropAndMove(Expression::Context context,
     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) {
   // Call the runtime to declare the globals.
   __ push(rsi);  // The context is the first argument.
@@ -376,6 +478,28 @@ void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
     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 @@ void FastCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
     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 @@ void FastCodeGenerator::VisitAssignment(Assignment* expr) {
           __ 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 @@ void FastCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
         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;
@@ -810,93 +992,4 @@ void FastCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) {
 }
 
 
-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