Commit 628a2e2a authored by keuchel@chromium.org's avatar keuchel@chromium.org

Better codegen for '<expression> === void <literal>'.

Detect the pattern in both, the full compiler and crankshaft and generate direct pointer
comparisons. Along the way I cleaned up 'typeof <expression> == <string literal>' comparisons
as well by lifting platform independent code and checking the symmetric case.

BUG=v8:1440
TEST=cctest/test-api.cc

Review URL: http://codereview.chromium.org/7216008

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8420 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 22abf6a5
...@@ -3964,25 +3964,13 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) { ...@@ -3964,25 +3964,13 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) {
} }
bool FullCodeGenerator::TryLiteralCompare(Token::Value op, void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr,
Expression* left, Handle<String> check,
Expression* right, Label* if_true,
Label* if_true, Label* if_false,
Label* if_false, Label* fall_through) {
Label* fall_through) {
if (op != Token::EQ && op != Token::EQ_STRICT) return false;
// Check for the pattern: typeof <expression> == <string literal>.
Literal* right_literal = right->AsLiteral();
if (right_literal == NULL) return false;
Handle<Object> right_literal_value = right_literal->handle();
if (!right_literal_value->IsString()) return false;
UnaryOperation* left_unary = left->AsUnaryOperation();
if (left_unary == NULL || left_unary->op() != Token::TYPEOF) return false;
Handle<String> check = Handle<String>::cast(right_literal_value);
{ AccumulatorValueContext context(this); { AccumulatorValueContext context(this);
VisitForTypeofValue(left_unary->expression()); VisitForTypeofValue(expr);
} }
PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
...@@ -4036,8 +4024,18 @@ bool FullCodeGenerator::TryLiteralCompare(Token::Value op, ...@@ -4036,8 +4024,18 @@ bool FullCodeGenerator::TryLiteralCompare(Token::Value op,
} else { } else {
if (if_false != fall_through) __ jmp(if_false); if (if_false != fall_through) __ jmp(if_false);
} }
}
void FullCodeGenerator::EmitLiteralCompareUndefined(Expression* expr,
Label* if_true,
Label* if_false,
Label* fall_through) {
VisitForAccumulatorValue(expr);
PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
return true; __ CompareRoot(r0, Heap::kUndefinedValueRootIndex);
Split(eq, if_true, if_false, fall_through);
} }
...@@ -4057,14 +4055,12 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { ...@@ -4057,14 +4055,12 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
// First we try a fast inlined version of the compare when one of // First we try a fast inlined version of the compare when one of
// the operands is a literal. // the operands is a literal.
Token::Value op = expr->op(); if (TryLiteralCompare(expr, if_true, if_false, fall_through)) {
Expression* left = expr->left();
Expression* right = expr->right();
if (TryLiteralCompare(op, left, right, if_true, if_false, fall_through)) {
context()->Plug(if_true, if_false); context()->Plug(if_true, if_false);
return; return;
} }
Token::Value op = expr->op();
VisitForStackValue(expr->left()); VisitForStackValue(expr->left());
switch (op) { switch (op) {
case Token::IN: case Token::IN:
......
...@@ -337,6 +337,59 @@ bool BinaryOperation::ResultOverwriteAllowed() { ...@@ -337,6 +337,59 @@ bool BinaryOperation::ResultOverwriteAllowed() {
} }
bool CompareOperation::IsLiteralCompareTypeof(Expression** expr,
Handle<String>* check) {
if (op_ != Token::EQ && op_ != Token::EQ_STRICT) return false;
UnaryOperation* left_unary = left_->AsUnaryOperation();
UnaryOperation* right_unary = right_->AsUnaryOperation();
Literal* left_literal = left_->AsLiteral();
Literal* right_literal = right_->AsLiteral();
// Check for the pattern: typeof <expression> == <string literal>.
if (left_unary != NULL && left_unary->op() == Token::TYPEOF &&
right_literal != NULL && right_literal->handle()->IsString()) {
*expr = left_unary->expression();
*check = Handle<String>::cast(right_literal->handle());
return true;
}
// Check for the pattern: <string literal> == typeof <expression>.
if (right_unary != NULL && right_unary->op() == Token::TYPEOF &&
left_literal != NULL && left_literal->handle()->IsString()) {
*expr = right_unary->expression();
*check = Handle<String>::cast(left_literal->handle());
return true;
}
return false;
}
bool CompareOperation::IsLiteralCompareUndefined(Expression** expr) {
if (op_ != Token::EQ_STRICT) return false;
UnaryOperation* left_unary = left_->AsUnaryOperation();
UnaryOperation* right_unary = right_->AsUnaryOperation();
// Check for the pattern: <expression> === void <literal>.
if (right_unary != NULL && right_unary->op() == Token::VOID &&
right_unary->expression()->AsLiteral() != NULL) {
*expr = left_;
return true;
}
// Check for the pattern: void <literal> === <expression>.
if (left_unary != NULL && left_unary->op() == Token::VOID &&
left_unary->expression()->AsLiteral() != NULL) {
*expr = right_;
return true;
}
return false;
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Inlining support // Inlining support
......
...@@ -1473,6 +1473,10 @@ class CompareOperation: public Expression { ...@@ -1473,6 +1473,10 @@ class CompareOperation: public Expression {
bool IsSmiCompare() { return compare_type_ == SMI_ONLY; } bool IsSmiCompare() { return compare_type_ == SMI_ONLY; }
bool IsObjectCompare() { return compare_type_ == OBJECT_ONLY; } bool IsObjectCompare() { return compare_type_ == OBJECT_ONLY; }
// Match special cases.
bool IsLiteralCompareTypeof(Expression** expr, Handle<String>* check);
bool IsLiteralCompareUndefined(Expression** expr);
private: private:
Token::Value op_; Token::Value op_;
Expression* left_; Expression* left_;
......
...@@ -1290,6 +1290,26 @@ int FullCodeGenerator::TryCatch::Exit(int stack_depth) { ...@@ -1290,6 +1290,26 @@ int FullCodeGenerator::TryCatch::Exit(int stack_depth) {
} }
bool FullCodeGenerator::TryLiteralCompare(CompareOperation* compare,
Label* if_true,
Label* if_false,
Label* fall_through) {
Expression *expr;
Handle<String> check;
if (compare->IsLiteralCompareTypeof(&expr, &check)) {
EmitLiteralCompareTypeof(expr, check, if_true, if_false, fall_through);
return true;
}
if (compare->IsLiteralCompareUndefined(&expr)) {
EmitLiteralCompareUndefined(expr, if_true, if_false, fall_through);
return true;
}
return false;
}
#undef __ #undef __
......
...@@ -362,13 +362,26 @@ class FullCodeGenerator: public AstVisitor { ...@@ -362,13 +362,26 @@ class FullCodeGenerator: public AstVisitor {
// Try to perform a comparison as a fast inlined literal compare if // Try to perform a comparison as a fast inlined literal compare if
// the operands allow it. Returns true if the compare operations // the operands allow it. Returns true if the compare operations
// has been matched and all code generated; false otherwise. // has been matched and all code generated; false otherwise.
bool TryLiteralCompare(Token::Value op, bool TryLiteralCompare(CompareOperation* compare,
Expression* left,
Expression* right,
Label* if_true, Label* if_true,
Label* if_false, Label* if_false,
Label* fall_through); Label* fall_through);
// Platform-specific code for comparing the type of a value with
// a given literal string.
void EmitLiteralCompareTypeof(Expression* expr,
Handle<String> check,
Label* if_true,
Label* if_false,
Label* fall_through);
// Platform-specific code for strict equality comparison with
// the undefined value.
void EmitLiteralCompareUndefined(Expression* expr,
Label* if_true,
Label* if_false,
Label* fall_through);
// Bailout support. // Bailout support.
void PrepareForBailout(Expression* node, State state); void PrepareForBailout(Expression* node, State state);
void PrepareForBailoutForId(int id, State state); void PrepareForBailoutForId(int id, State state);
......
...@@ -5490,6 +5490,29 @@ Representation HGraphBuilder::ToRepresentation(TypeInfo info) { ...@@ -5490,6 +5490,29 @@ Representation HGraphBuilder::ToRepresentation(TypeInfo info) {
} }
void HGraphBuilder::HandleLiteralCompareTypeof(CompareOperation* compare_expr,
Expression* expr,
Handle<String> check) {
CHECK_ALIVE(VisitForTypeOf(expr));
HValue* expr_value = Pop();
HInstruction* instr = new(zone()) HTypeofIs(expr_value, check);
instr->set_position(compare_expr->position());
ast_context()->ReturnInstruction(instr, compare_expr->id());
}
void HGraphBuilder::HandleLiteralCompareUndefined(
CompareOperation* compare_expr, Expression* expr) {
CHECK_ALIVE(VisitForValue(expr));
HValue* lhs = Pop();
HValue* rhs = graph()->GetConstantUndefined();
HInstruction* instr =
new(zone()) HCompareObjectEq(lhs, rhs);
instr->set_position(compare_expr->position());
ast_context()->ReturnInstruction(instr, compare_expr->id());
}
void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
ASSERT(!HasStackOverflow()); ASSERT(!HasStackOverflow());
ASSERT(current_block() != NULL); ASSERT(current_block() != NULL);
...@@ -5506,18 +5529,16 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { ...@@ -5506,18 +5529,16 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
return; return;
} }
// Check for the pattern: typeof <expression> == <string literal>. // Check for special cases that compare against literals.
UnaryOperation* left_unary = expr->left()->AsUnaryOperation(); Expression *sub_expr;
Literal* right_literal = expr->right()->AsLiteral(); Handle<String> check;
if ((expr->op() == Token::EQ || expr->op() == Token::EQ_STRICT) && if (expr->IsLiteralCompareTypeof(&sub_expr, &check)) {
left_unary != NULL && left_unary->op() == Token::TYPEOF && HandleLiteralCompareTypeof(expr, sub_expr, check);
right_literal != NULL && right_literal->handle()->IsString()) { return;
CHECK_ALIVE(VisitForTypeOf(left_unary->expression())); }
HValue* left = Pop();
HInstruction* instr = new(zone()) HTypeofIs(left, if (expr->IsLiteralCompareUndefined(&sub_expr)) {
Handle<String>::cast(right_literal->handle())); HandleLiteralCompareUndefined(expr, sub_expr);
instr->set_position(expr->position());
ast_context()->ReturnInstruction(instr, expr->id());
return; return;
} }
......
...@@ -881,6 +881,11 @@ class HGraphBuilder: public AstVisitor { ...@@ -881,6 +881,11 @@ class HGraphBuilder: public AstVisitor {
HValue* receiver, HValue* receiver,
ZoneMapList* types, ZoneMapList* types,
Handle<String> name); Handle<String> name);
void HandleLiteralCompareTypeof(CompareOperation* compare_expr,
Expression* expr,
Handle<String> check);
void HandleLiteralCompareUndefined(CompareOperation* compare_expr,
Expression* expr);
HStringCharCodeAt* BuildStringCharCodeAt(HValue* string, HStringCharCodeAt* BuildStringCharCodeAt(HValue* string,
HValue* index); HValue* index);
......
...@@ -3930,25 +3930,13 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) { ...@@ -3930,25 +3930,13 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) {
} }
bool FullCodeGenerator::TryLiteralCompare(Token::Value op, void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr,
Expression* left, Handle<String> check,
Expression* right, Label* if_true,
Label* if_true, Label* if_false,
Label* if_false, Label* fall_through) {
Label* fall_through) {
if (op != Token::EQ && op != Token::EQ_STRICT) return false;
// Check for the pattern: typeof <expression> == <string literal>.
Literal* right_literal = right->AsLiteral();
if (right_literal == NULL) return false;
Handle<Object> right_literal_value = right_literal->handle();
if (!right_literal_value->IsString()) return false;
UnaryOperation* left_unary = left->AsUnaryOperation();
if (left_unary == NULL || left_unary->op() != Token::TYPEOF) return false;
Handle<String> check = Handle<String>::cast(right_literal_value);
{ AccumulatorValueContext context(this); { AccumulatorValueContext context(this);
VisitForTypeofValue(left_unary->expression()); VisitForTypeofValue(expr);
} }
PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
...@@ -3998,8 +3986,18 @@ bool FullCodeGenerator::TryLiteralCompare(Token::Value op, ...@@ -3998,8 +3986,18 @@ bool FullCodeGenerator::TryLiteralCompare(Token::Value op,
} else { } else {
if (if_false != fall_through) __ jmp(if_false); if (if_false != fall_through) __ jmp(if_false);
} }
}
void FullCodeGenerator::EmitLiteralCompareUndefined(Expression* expr,
Label* if_true,
Label* if_false,
Label* fall_through) {
VisitForAccumulatorValue(expr);
PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
return true; __ cmp(eax, isolate()->factory()->undefined_value());
Split(equal, if_true, if_false, fall_through);
} }
...@@ -4019,14 +4017,12 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { ...@@ -4019,14 +4017,12 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
// First we try a fast inlined version of the compare when one of // First we try a fast inlined version of the compare when one of
// the operands is a literal. // the operands is a literal.
Token::Value op = expr->op(); if (TryLiteralCompare(expr, if_true, if_false, fall_through)) {
Expression* left = expr->left();
Expression* right = expr->right();
if (TryLiteralCompare(op, left, right, if_true, if_false, fall_through)) {
context()->Plug(if_true, if_false); context()->Plug(if_true, if_false);
return; return;
} }
Token::Value op = expr->op();
VisitForStackValue(expr->left()); VisitForStackValue(expr->left());
switch (expr->op()) { switch (expr->op()) {
case Token::IN: case Token::IN:
......
...@@ -3907,25 +3907,13 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) { ...@@ -3907,25 +3907,13 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) {
} }
bool FullCodeGenerator::TryLiteralCompare(Token::Value op, void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr,
Expression* left, Handle<String> check,
Expression* right, Label* if_true,
Label* if_true, Label* if_false,
Label* if_false, Label* fall_through) {
Label* fall_through) {
if (op != Token::EQ && op != Token::EQ_STRICT) return false;
// Check for the pattern: typeof <expression> == <string literal>.
Literal* right_literal = right->AsLiteral();
if (right_literal == NULL) return false;
Handle<Object> right_literal_value = right_literal->handle();
if (!right_literal_value->IsString()) return false;
UnaryOperation* left_unary = left->AsUnaryOperation();
if (left_unary == NULL || left_unary->op() != Token::TYPEOF) return false;
Handle<String> check = Handle<String>::cast(right_literal_value);
{ AccumulatorValueContext context(this); { AccumulatorValueContext context(this);
VisitForTypeofValue(left_unary->expression()); VisitForTypeofValue(expr);
} }
PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
...@@ -3976,8 +3964,18 @@ bool FullCodeGenerator::TryLiteralCompare(Token::Value op, ...@@ -3976,8 +3964,18 @@ bool FullCodeGenerator::TryLiteralCompare(Token::Value op,
} else { } else {
if (if_false != fall_through) __ jmp(if_false); if (if_false != fall_through) __ jmp(if_false);
} }
}
void FullCodeGenerator::EmitLiteralCompareUndefined(Expression* expr,
Label* if_true,
Label* if_false,
Label* fall_through) {
VisitForAccumulatorValue(expr);
PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
return true; __ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
Split(equal, if_true, if_false, fall_through);
} }
...@@ -3996,14 +3994,12 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { ...@@ -3996,14 +3994,12 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
// First we try a fast inlined version of the compare when one of // First we try a fast inlined version of the compare when one of
// the operands is a literal. // the operands is a literal.
Token::Value op = expr->op(); if (TryLiteralCompare(expr, if_true, if_false, fall_through)) {
Expression* left = expr->left();
Expression* right = expr->right();
if (TryLiteralCompare(op, left, right, if_true, if_false, fall_through)) {
context()->Plug(if_true, if_false); context()->Plug(if_true, if_false);
return; return;
} }
Token::Value op = expr->op();
VisitForStackValue(expr->left()); VisitForStackValue(expr->left());
switch (op) { switch (op) {
case Token::IN: case Token::IN:
......
...@@ -3862,6 +3862,49 @@ THREADED_TEST(UndetectableObject) { ...@@ -3862,6 +3862,49 @@ THREADED_TEST(UndetectableObject) {
} }
THREADED_TEST(VoidLiteral) {
v8::HandleScope scope;
LocalContext env;
Local<v8::FunctionTemplate> desc =
v8::FunctionTemplate::New(0, v8::Handle<Value>());
desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
Local<v8::Object> obj = desc->GetFunction()->NewInstance();
env->Global()->Set(v8_str("undetectable"), obj);
ExpectBoolean("undefined == void 0", true);
ExpectBoolean("undetectable == void 0", true);
ExpectBoolean("null == void 0", true);
ExpectBoolean("undefined === void 0", true);
ExpectBoolean("undetectable === void 0", false);
ExpectBoolean("null === void 0", false);
ExpectBoolean("void 0 == undefined", true);
ExpectBoolean("void 0 == undetectable", true);
ExpectBoolean("void 0 == null", true);
ExpectBoolean("void 0 === undefined", true);
ExpectBoolean("void 0 === undetectable", false);
ExpectBoolean("void 0 === null", false);
ExpectString("(function() {"
" try {"
" return x === void 0;"
" } catch(e) {"
" return e.toString();"
" }"
"})()",
"ReferenceError: x is not defined");
ExpectString("(function() {"
" try {"
" return void 0 === x;"
" } catch(e) {"
" return e.toString();"
" }"
"})()",
"ReferenceError: x is not defined");
}
THREADED_TEST(ExtensibleOnUndetectable) { THREADED_TEST(ExtensibleOnUndetectable) {
v8::HandleScope scope; v8::HandleScope scope;
......
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