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:
parent
ef95097334
commit
e82a07c0bd
@ -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
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -319,11 +319,6 @@ void FastCodeGenerator::VisitCountOperation(CountOperation* expr) {
|
||||
}
|
||||
|
||||
|
||||
void FastCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
|
||||
void FastCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user