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
parent b7c0b738
...@@ -123,15 +123,51 @@ void FastCodeGenerator::Generate(FunctionLiteral* fun) { ...@@ -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) { switch (context) {
case Expression::kUninitialized: case Expression::kUninitialized:
UNREACHABLE(); UNREACHABLE();
case Expression::kEffect: case Expression::kEffect:
break; break;
case Expression::kValue: 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))); __ ldr(ip, MemOperand(fp, SlotOffset(source)));
__ push(ip); Move(context, ip);
break; break;
} }
} }
...@@ -143,9 +179,12 @@ void FastCodeGenerator::Move(Expression::Context context, Literal* expr) { ...@@ -143,9 +179,12 @@ void FastCodeGenerator::Move(Expression::Context context, Literal* expr) {
UNREACHABLE(); UNREACHABLE();
case Expression::kEffect: case Expression::kEffect:
break; 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())); __ mov(ip, Operand(expr->handle()));
__ push(ip); Move(context, ip);
break; break;
} }
} }
...@@ -162,10 +201,49 @@ void FastCodeGenerator::DropAndMove(Expression::Context context, ...@@ -162,10 +201,49 @@ void FastCodeGenerator::DropAndMove(Expression::Context context,
case Expression::kValue: case Expression::kValue:
__ str(source, MemOperand(sp)); __ str(source, MemOperand(sp));
break; 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) { void FastCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
// Call the runtime to declare the globals. // Call the runtime to declare the globals.
// The context is the first argument. // The context is the first argument.
...@@ -369,6 +447,28 @@ void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { ...@@ -369,6 +447,28 @@ void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
case Expression::kValue: case Expression::kValue:
if (!result_saved) __ push(r0); if (!result_saved) __ push(r0);
break; 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) { ...@@ -446,6 +546,28 @@ void FastCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
case Expression::kValue: case Expression::kValue:
if (!result_saved) __ push(r0); if (!result_saved) __ push(r0);
break; 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) { ...@@ -530,16 +652,44 @@ void FastCodeGenerator::VisitAssignment(Assignment* expr) {
UNREACHABLE(); UNREACHABLE();
case Expression::kEffect: case Expression::kEffect:
// Case 'var = temp'. Discard right-hand-side temporary. // Case 'var = temp'. Discard right-hand-side temporary.
__ pop(ip); __ pop(r0);
__ str(r0, MemOperand(fp, SlotOffset(var->slot())));
break; break;
case Expression::kValue: case Expression::kValue:
// Case 'temp1 <- (var = temp0)'. Preserve right-hand-side // Case 'temp1 <- (var = temp0)'. Preserve right-hand-side
// temporary on the stack. // 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; break;
} }
// Do the slot assignment. }
__ str(ip, MemOperand(fp, SlotOffset(var->slot())));
} }
} }
} }
...@@ -734,11 +884,19 @@ void FastCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { ...@@ -734,11 +884,19 @@ void FastCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
case Expression::kUninitialized: case Expression::kUninitialized:
UNREACHABLE(); UNREACHABLE();
break; break;
case Expression::kEffect:
break;
case Expression::kValue: case Expression::kValue:
__ LoadRoot(ip, Heap::kUndefinedValueRootIndex); __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
__ push(ip); __ push(ip);
break; 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;
} }
break; break;
...@@ -794,52 +952,4 @@ void FastCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) { ...@@ -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 } } // namespace v8::internal
...@@ -162,9 +162,21 @@ class Statement: public AstNode { ...@@ -162,9 +162,21 @@ class Statement: public AstNode {
class Expression: public AstNode { class Expression: public AstNode {
public: public:
enum Context { enum Context {
// Not assigned a context yet, or else will not be visited during
// code generation.
kUninitialized, kUninitialized,
// Evaluated for its side effects.
kEffect, 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) {} Expression() : context_(kUninitialized) {}
......
...@@ -849,7 +849,45 @@ void CodeGenSelector::VisitBinaryOperation(BinaryOperation* expr) { ...@@ -849,7 +849,45 @@ void CodeGenSelector::VisitBinaryOperation(BinaryOperation* expr) {
break; break;
case Token::OR: 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; CHECK_BAILOUT;
ProcessExpression(expr->right(), context_); ProcessExpression(expr->right(), context_);
break; break;
......
...@@ -35,6 +35,8 @@ ...@@ -35,6 +35,8 @@
namespace v8 { namespace v8 {
namespace internal { namespace internal {
#define __ ACCESS_MASM(masm_)
Handle<Code> FastCodeGenerator::MakeCode(FunctionLiteral* fun, Handle<Code> FastCodeGenerator::MakeCode(FunctionLiteral* fun,
Handle<Script> script, Handle<Script> script,
bool is_eval) { bool is_eval) {
...@@ -71,21 +73,6 @@ int FastCodeGenerator::SlotOffset(Slot* slot) { ...@@ -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( void FastCodeGenerator::VisitDeclarations(
ZoneList<Declaration*>* declarations) { ZoneList<Declaration*>* declarations) {
int length = declarations->length(); int length = declarations->length();
...@@ -202,6 +189,80 @@ void FastCodeGenerator::SetSourcePosition(int pos) { ...@@ -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) { void FastCodeGenerator::VisitDeclaration(Declaration* decl) {
UNREACHABLE(); UNREACHABLE();
} }
...@@ -339,4 +400,7 @@ void FastCodeGenerator::VisitThisFunction(ThisFunction* expr) { ...@@ -339,4 +400,7 @@ void FastCodeGenerator::VisitThisFunction(ThisFunction* expr) {
} }
#undef __
} } // namespace v8::internal } } // namespace v8::internal
...@@ -39,7 +39,12 @@ namespace internal { ...@@ -39,7 +39,12 @@ namespace internal {
class FastCodeGenerator: public AstVisitor { class FastCodeGenerator: public AstVisitor {
public: public:
FastCodeGenerator(MacroAssembler* masm, Handle<Script> script, bool is_eval) 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, static Handle<Code> MakeCode(FunctionLiteral* fun,
...@@ -59,6 +64,10 @@ class FastCodeGenerator: public AstVisitor { ...@@ -59,6 +64,10 @@ class FastCodeGenerator: public AstVisitor {
// If destination is TOS, just overwrite TOS with source. // If destination is TOS, just overwrite TOS with source.
void DropAndMove(Expression::Context destination, Register 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); void VisitDeclarations(ZoneList<Declaration*>* declarations);
Handle<JSFunction> BuildBoilerplate(FunctionLiteral* fun); Handle<JSFunction> BuildBoilerplate(FunctionLiteral* fun);
void DeclareGlobals(Handle<FixedArray> pairs); void DeclareGlobals(Handle<FixedArray> pairs);
...@@ -85,6 +94,9 @@ class FastCodeGenerator: public AstVisitor { ...@@ -85,6 +94,9 @@ class FastCodeGenerator: public AstVisitor {
bool is_eval_; bool is_eval_;
Label return_label_; Label return_label_;
Label* true_label_;
Label* false_label_;
DISALLOW_COPY_AND_ASSIGN(FastCodeGenerator); DISALLOW_COPY_AND_ASSIGN(FastCodeGenerator);
}; };
......
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment