From e82a07c0bd891487f5fa94fba2774ebddb3fddfe Mon Sep 17 00:00:00 2001 From: "kmillikin@chromium.org" Date: Fri, 23 Oct 2009 10:42:14 +0000 Subject: [PATCH] Simple toplevel code generator support for short-circuited boolean OR 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 | 57 +++++++++++++++++++++++++++++ src/compiler.cc | 11 +++++- src/fast-codegen.cc | 5 --- src/ia32/codegen-ia32.cc | 12 ------- src/ia32/codegen-ia32.h | 12 +++++++ src/ia32/fast-codegen-ia32.cc | 82 ++++++++++++++++++++++++++++++++++++++++++ src/x64/codegen-x64.cc | 12 ------- src/x64/codegen-x64.h | 12 +++++++ src/x64/fast-codegen-x64.cc | 83 +++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 256 insertions(+), 30 deletions(-) diff --git a/src/arm/fast-codegen-arm.cc b/src/arm/fast-codegen-arm.cc index 6248367..97feae5 100644 --- a/src/arm/fast-codegen-arm.cc +++ b/src/arm/fast-codegen-arm.cc @@ -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 diff --git a/src/compiler.cc b/src/compiler.cc index 6453ac1..e422bf7 100644 --- a/src/compiler.cc +++ b/src/compiler.cc @@ -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"); + } } diff --git a/src/fast-codegen.cc b/src/fast-codegen.cc index 09806e8..d0c264a 100644 --- a/src/fast-codegen.cc +++ b/src/fast-codegen.cc @@ -319,11 +319,6 @@ void FastCodeGenerator::VisitCountOperation(CountOperation* expr) { } -void FastCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) { - UNREACHABLE(); -} - - void FastCodeGenerator::VisitCompareOperation(CompareOperation* expr) { UNREACHABLE(); } diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc index d22e989..a339e90 100644 --- a/src/ia32/codegen-ia32.cc +++ b/src/ia32/codegen-ia32.cc @@ -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. diff --git a/src/ia32/codegen-ia32.h b/src/ia32/codegen-ia32.h index e914aff..a37bffe 100644 --- a/src/ia32/codegen-ia32.h +++ b/src/ia32/codegen-ia32.h @@ -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, diff --git a/src/ia32/fast-codegen-ia32.cc b/src/ia32/fast-codegen-ia32.cc index 2e10ab4..663d136 100644 --- a/src/ia32/fast-codegen-ia32.cc +++ b/src/ia32/fast-codegen-ia32.cc @@ -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 diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc index d36af8c..e416887 100644 --- a/src/x64/codegen-x64.cc +++ b/src/x64/codegen-x64.cc @@ -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. diff --git a/src/x64/codegen-x64.h b/src/x64/codegen-x64.h index 541da30..56b88b7 100644 --- a/src/x64/codegen-x64.h +++ b/src/x64/codegen-x64.h @@ -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 { diff --git a/src/x64/fast-codegen-x64.cc b/src/x64/fast-codegen-x64.cc index 334e437..46d8dc4 100644 --- a/src/x64/fast-codegen-x64.cc +++ b/src/x64/fast-codegen-x64.cc @@ -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 -- 2.7.4