Initial implementation of top-level compilation of expressions in test

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
This commit is contained in:
kmillikin@chromium.org 2009-10-30 14:06:48 +00:00
parent b7c0b738c7
commit ab3e85eeb4
7 changed files with 674 additions and 250 deletions

View 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

View File

@ -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) {}

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

View 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

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

View 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

View 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