Simple toplevel code generator support for short-circuited boolean OR
authorkmillikin@chromium.org <kmillikin@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 23 Oct 2009 10:42:14 +0000 (10:42 +0000)
committerkmillikin@chromium.org <kmillikin@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 23 Oct 2009 10:42:14 +0000 (10:42 +0000)
in a non-test (ie, value or effect) context.  (It is implicitly not in
a test context because the code generator does not support expressions
in a test context yet.)

Compilation is essentially the same as in the optimized code
generator.  The expression (e0 || e1) is compiled as if it were
(let (temp = e0) temp ? temp : e1).

On ia32 and x64 a single shared ToBoolean stub is used to convert a
value to a flag.  The inlined checks assumed by the stub are reordered
to compare to undefined (the common case in toplevel code?) first.  On
ARM a call to the runtime is used.  In the interest of code size no
checks are yet inlined on ARM.

Review URL: http://codereview.chromium.org/334006

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

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

index 6248367f25de1a77e5f4177dac6c885d6e13b1c7..97feae5d735f37e5bfb73cf99fdf6dd75d4a6fbc 100644 (file)
@@ -479,4 +479,61 @@ void FastCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
 }
 
 
+void FastCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) {
+  // Compile a short-circuited boolean or operation in a non-test
+  // context.
+  ASSERT(expr->op() == Token::OR);
+  // Compile (e0 || e1) as if it were
+  // (let (temp = e0) temp ? temp : e1).
+
+  Label done;
+  Location destination = expr->location();
+  ASSERT(!destination.is_constant());
+
+  Expression* left = expr->left();
+  Location left_source = left->location();
+  ASSERT(!left_source.is_nowhere());
+
+  Expression* right = expr->right();
+  Location right_source = right->location();
+  ASSERT(!right_source.is_nowhere());
+
+  Visit(left);
+  // 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_source.is_temporary()) {
+    if (destination.is_temporary()) {
+      __ ldr(r0, MemOperand(sp));
+      __ push(r0);
+    }
+  } else {
+    ASSERT(left->AsLiteral() != NULL);
+    __ mov(r0, Operand(left->AsLiteral()->handle()));
+    __ push(r0);
+    if (destination.is_temporary()) __ push(r0);
+  }
+  // The left-hand value is in on top of the stack.  It is duplicated on the
+  // stack iff the destination location is temporary.
+  __ CallRuntime(Runtime::kToBool, 1);
+  __ LoadRoot(ip, Heap::kTrueValueRootIndex);
+  __ cmp(r0, ip);
+  __ b(eq, &done);
+
+  // Discard the left-hand value if present on the stack.
+  if (destination.is_temporary()) __ pop();
+  Visit(right);
+
+  // Save or discard the right-hand value as needed.
+  if (destination.is_temporary() && right_source.is_constant()) {
+    ASSERT(right->AsLiteral() != NULL);
+    __ mov(ip, Operand(right->AsLiteral()->handle()));
+    __ push(ip);
+  } else if (destination.is_nowhere() && right_source.is_temporary()) {
+    __ pop();
+  }
+
+  __ bind(&done);
+}
+
 } }  // namespace v8::internal
index 6453ac10d682c47353dfa42ac38a16dfefc33246..e422bf7960852132faef37263ec94f5b7b1f7336 100644 (file)
@@ -750,7 +750,16 @@ void CodeGenSelector::VisitCountOperation(CountOperation* expr) {
 
 
 void CodeGenSelector::VisitBinaryOperation(BinaryOperation* expr) {
-  BAILOUT("BinaryOperation");
+  switch (expr->op()) {
+    case Token::OR:
+      Visit(expr->left());
+      CHECK_BAILOUT;
+      Visit(expr->right());
+      break;
+
+    default:
+      BAILOUT("Unsupported binary operation");
+  }
 }
 
 
index 09806e8c13b493d4c59567a19ee529b207eee4d3..d0c264a7eff156a29637bd3d3a40227402cc4ca1 100644 (file)
@@ -319,11 +319,6 @@ void FastCodeGenerator::VisitCountOperation(CountOperation* expr) {
 }
 
 
-void FastCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) {
-  UNREACHABLE();
-}
-
-
 void FastCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
   UNREACHABLE();
 }
index d22e989c3342fdb5cd68004c731dc6b0d767c82f..a339e90c202c9a92daa8227a3b7db5729066dba3 100644 (file)
@@ -697,18 +697,6 @@ void CodeGenerator::UnloadReference(Reference* ref) {
 }
 
 
-class ToBooleanStub: public CodeStub {
- public:
-  ToBooleanStub() { }
-
-  void Generate(MacroAssembler* masm);
-
- private:
-  Major MajorKey() { return ToBoolean; }
-  int MinorKey() { return 0; }
-};
-
-
 // ECMA-262, section 9.2, page 30: ToBoolean(). Pop the top of stack and
 // convert it to a boolean in the condition code register or jump to
 // 'false_target'/'true_target' as appropriate.
index e914aff84a296ed0055f6fbe8c26b6418ae46bf5..a37bffea6b2a0bd5b025bfc1ab0cfc1050321c61 100644 (file)
@@ -626,6 +626,18 @@ class CodeGenerator: public AstVisitor {
 };
 
 
+class ToBooleanStub: public CodeStub {
+ public:
+  ToBooleanStub() { }
+
+  void Generate(MacroAssembler* masm);
+
+ private:
+  Major MajorKey() { return ToBoolean; }
+  int MinorKey() { return 0; }
+};
+
+
 // Flag that indicates whether how to generate code for the stub.
 enum GenericBinaryFlags {
   NO_GENERIC_BINARY_FLAGS = 0,
index 2e10ab4745a7a12e4a2bc9939deb2efc9c94f961..663d13677c9107ad67d981243892a339d7e5a6aa 100644 (file)
@@ -460,4 +460,86 @@ void FastCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
 }
 
 
+void FastCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) {
+  // Compile a short-circuited boolean or operation in a non-test
+  // context.
+  ASSERT(expr->op() == Token::OR);
+  // Compile (e0 || e1) as if it were
+  // (let (temp = e0) temp ? temp : e1).
+
+  Label eval_right, done;
+  Location destination = expr->location();
+  ASSERT(!destination.is_constant());
+
+  Expression* left = expr->left();
+  Location left_source = left->location();
+  ASSERT(!left_source.is_nowhere());
+
+  Expression* right = expr->right();
+  Location right_source = right->location();
+  ASSERT(!right_source.is_nowhere());
+
+  Visit(left);
+  // 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.
+  if (left_source.is_temporary()) {
+    if (destination.is_temporary()) {
+      // Copy the left-hand value into eax because we may need it as the
+      // final result.
+      __ mov(eax, Operand(esp, 0));
+    } else {
+      // Pop the left-hand value into eax because we will not need it as the
+      // final result.
+      __ pop(eax);
+    }
+  } else {
+    // Load the left-hand value into eax.  Put it on the stack if we may
+    // need it.
+    ASSERT(left->AsLiteral() != NULL);
+    __ mov(eax, left->AsLiteral()->handle());
+    if (destination.is_temporary()) __ push(eax);
+  }
+  // The left-hand value is in eax.  It is also on the stack iff the
+  // destination location is temporary.
+
+  // Perform fast checks assumed by the stub.
+  __ cmp(eax, Factory::undefined_value());  // The undefined value is false.
+  __ j(equal, &eval_right);
+  __ cmp(eax, Factory::true_value());  // True is true.
+  __ j(equal, &done);
+  __ cmp(eax, Factory::false_value());  // False is false.
+  __ j(equal, &eval_right);
+  ASSERT(kSmiTag == 0);
+  __ test(eax, Operand(eax));  // The smi zero is false.
+  __ j(zero, &eval_right);
+  __ test(eax, Immediate(kSmiTagMask));  // All other smis are true.
+  __ j(zero, &done);
+
+  // Call the stub for all other cases.
+  __ push(eax);
+  ToBooleanStub stub;
+  __ CallStub(&stub);
+  __ test(eax, Operand(eax));  // The stub returns nonzero for true.
+  __ j(not_zero, &done);
+
+  __ bind(&eval_right);
+  // Discard the left-hand value if present on the stack.
+  if (destination.is_temporary()) {
+    __ add(Operand(esp), Immediate(kPointerSize));
+  }
+  Visit(right);
+
+  // Save or discard the right-hand value as needed.
+  if (destination.is_temporary() && right_source.is_constant()) {
+    ASSERT(right->AsLiteral() != NULL);
+    __ push(Immediate(right->AsLiteral()->handle()));
+  } else if (destination.is_nowhere() && right_source.is_temporary()) {
+    __ add(Operand(esp), Immediate(kPointerSize));
+  }
+
+  __ bind(&done);
+}
+
+
 } }  // namespace v8::internal
index d36af8c794baff92b507bbcb3cfbb4e9c33a5eaf..e4168873fa0c4b7283cd4701af41bbc09ee7dc5f 100644 (file)
@@ -4239,18 +4239,6 @@ void CodeGenerator::LoadCondition(Expression* x,
 }
 
 
-class ToBooleanStub: public CodeStub {
- public:
-  ToBooleanStub() { }
-
-  void Generate(MacroAssembler* masm);
-
- private:
-  Major MajorKey() { return ToBoolean; }
-  int MinorKey() { return 0; }
-};
-
-
 // ECMA-262, section 9.2, page 30: ToBoolean(). Pop the top of stack and
 // convert it to a boolean in the condition code register or jump to
 // 'false_target'/'true_target' as appropriate.
index 541da30ba3b5b10efffa95dd432ca381c7b380d8..56b88b74d1379a248a25b3d17f2af15140e1abc2 100644 (file)
@@ -635,6 +635,18 @@ class CodeGenerator: public AstVisitor {
 // which is declared in code-stubs.h.
 
 
+class ToBooleanStub: public CodeStub {
+ public:
+  ToBooleanStub() { }
+
+  void Generate(MacroAssembler* masm);
+
+ private:
+  Major MajorKey() { return ToBoolean; }
+  int MinorKey() { return 0; }
+};
+
+
 // Flag that indicates whether or not the code that handles smi arguments
 // should be placed in the stub, inlined, or omitted entirely.
 enum GenericBinaryFlags {
index 334e437ff4dcb2e9b128af67c07dfd68f2521556..46d8dc4ba67dfe174333a9cf5c4a8f51a45fb637 100644 (file)
@@ -473,4 +473,87 @@ void FastCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
 }
 
 
+void FastCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) {
+  // Compile a short-circuited boolean or operation in a non-test
+  // context.
+  ASSERT(expr->op() == Token::OR);
+  // Compile (e0 || e1) as if it were
+  // (let (temp = e0) temp ? temp : e1).
+
+  Label eval_right, done;
+  Location destination = expr->location();
+  ASSERT(!destination.is_constant());
+
+  Expression* left = expr->left();
+  Location left_source = left->location();
+  ASSERT(!left_source.is_nowhere());
+
+  Expression* right = expr->right();
+  Location right_source = right->location();
+  ASSERT(!right_source.is_nowhere());
+
+  Visit(left);
+  // 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.
+  if (left_source.is_temporary()) {
+    if (destination.is_temporary()) {
+      // Copy the left-hand value into rax because we may need it as the
+      // final result.
+      __ movq(rax, Operand(rsp, 0));
+    } else {
+      // Pop the left-hand value into rax because we will not need it as the
+      // final result.
+      __ pop(rax);
+    }
+  } else {
+    // Load the left-hand value into rax.  Put it on the stack if we may
+    // need it.
+    ASSERT(left->AsLiteral() != NULL);
+    __ Move(rax, left->AsLiteral()->handle());
+    if (destination.is_temporary()) __ push(rax);
+  }
+  // The left-hand value is in rax.  It is also on the stack iff the
+  // destination location is temporary.
+
+  // Perform fast checks assumed by the stub.
+  // The undefined value is false.
+  __ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
+  __ j(equal, &eval_right);
+  __ CompareRoot(rax, Heap::kTrueValueRootIndex);  // True is true.
+  __ j(equal, &done);
+  __ CompareRoot(rax, Heap::kFalseValueRootIndex);  // False is false.
+  __ j(equal, &eval_right);
+  ASSERT(kSmiTag == 0);
+  __ SmiCompare(rax, Smi::FromInt(0));  // The smi zero is false.
+  __ j(equal, &eval_right);
+  Condition is_smi = masm_->CheckSmi(rax);  // All other smis are true.
+  __ j(is_smi, &done);
+
+  // Call the stub for all other cases.
+  __ push(rax);
+  ToBooleanStub stub;
+  __ CallStub(&stub);
+  __ testq(rax, rax);  // The stub returns nonzero for true.
+  __ j(not_zero, &done);
+
+  __ bind(&eval_right);
+  // Discard the left-hand value if present on the stack.
+  if (destination.is_temporary()) {
+    __ addq(rsp, Immediate(kPointerSize));
+  }
+  Visit(right);
+
+  // Save or discard the right-hand value as needed.
+  if (destination.is_temporary() && right_source.is_constant()) {
+    ASSERT(right->AsLiteral() != NULL);
+    __ Push(right->AsLiteral()->handle());
+  } else if (destination.is_nowhere() && right_source.is_temporary()) {
+    __ addq(rsp, Immediate(kPointerSize));
+  }
+
+  __ bind(&done);
+}
+
+
 } }  // namespace v8::internal