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