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
This commit is contained in:
kmillikin@chromium.org 2009-10-23 10:42:14 +00:00
parent ef95097334
commit e82a07c0bd
9 changed files with 256 additions and 30 deletions

View 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

View 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");
}
}

View File

@ -319,11 +319,6 @@ void FastCodeGenerator::VisitCountOperation(CountOperation* expr) {
}
void FastCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) {
UNREACHABLE();
}
void FastCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
UNREACHABLE();
}

View 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.

View 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,

View 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

View 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.

View 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 {

View 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