Index: src/x64/full-codegen-x64.cc |
=================================================================== |
--- src/x64/full-codegen-x64.cc (revision 5324) |
+++ src/x64/full-codegen-x64.cc (working copy) |
@@ -2627,30 +2627,7 @@ |
case Token::TYPEOF: { |
Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)"); |
- VariableProxy* proxy = expr->expression()->AsVariableProxy(); |
- if (proxy != NULL && |
- !proxy->var()->is_this() && |
- proxy->var()->is_global()) { |
- Comment cmnt(masm_, "Global variable"); |
- __ Move(rcx, proxy->name()); |
- __ movq(rax, CodeGenerator::GlobalObject()); |
- Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); |
- // Use a regular load, not a contextual load, to avoid a reference |
- // error. |
- __ Call(ic, RelocInfo::CODE_TARGET); |
- __ push(rax); |
- } else if (proxy != NULL && |
- proxy->var()->slot() != NULL && |
- proxy->var()->slot()->type() == Slot::LOOKUP) { |
- __ push(rsi); |
- __ Push(proxy->name()); |
- __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2); |
- __ push(rax); |
- } else { |
- // This expression cannot throw a reference error at the top level. |
- VisitForValue(expr->expression(), kStack); |
- } |
- |
+ VisitForTypeofValue(expr->expression(), kStack); |
__ CallRuntime(Runtime::kTypeof, 1); |
Apply(context_, rax); |
break; |
@@ -2888,26 +2865,110 @@ |
} |
-void FullCodeGenerator::EmitNullCompare(bool strict, |
- Register obj, |
- Register null_const, |
- Label* if_true, |
- Label* if_false, |
- Register scratch) { |
- __ cmpq(obj, null_const); |
- if (strict) { |
- Split(equal, if_true, if_false, NULL); |
+void FullCodeGenerator::VisitForTypeofValue(Expression* expr, Location where) { |
+ VariableProxy* proxy = expr->AsVariableProxy(); |
+ if (proxy != NULL && !proxy->var()->is_this() && proxy->var()->is_global()) { |
+ Comment cmnt(masm_, "Global variable"); |
+ __ Move(rcx, proxy->name()); |
+ __ movq(rax, CodeGenerator::GlobalObject()); |
+ Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); |
+ // Use a regular load, not a contextual load, to avoid a reference |
+ // error. |
+ __ Call(ic, RelocInfo::CODE_TARGET); |
+ if (where == kStack) __ push(rax); |
+ } else if (proxy != NULL && |
+ proxy->var()->slot() != NULL && |
+ proxy->var()->slot()->type() == Slot::LOOKUP) { |
+ __ push(rsi); |
+ __ Push(proxy->name()); |
+ __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2); |
+ if (where == kStack) __ push(rax); |
} else { |
+ // This expression cannot throw a reference error at the top level. |
+ VisitForValue(expr, where); |
+ } |
+} |
+ |
+ |
+bool FullCodeGenerator::TryLiteralCompare(Token::Value op, |
+ Expression* left, |
+ Expression* right, |
+ Label* if_true, |
+ Label* if_false, |
+ 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); |
+ |
+ VisitForTypeofValue(left_unary->expression(), kAccumulator); |
+ if (check->Equals(Heap::number_symbol())) { |
+ Condition is_smi = masm_->CheckSmi(rax); |
+ __ j(is_smi, if_true); |
+ __ movq(rax, FieldOperand(rax, HeapObject::kMapOffset)); |
+ __ CompareRoot(rax, Heap::kHeapNumberMapRootIndex); |
+ Split(equal, if_true, if_false, fall_through); |
+ } else if (check->Equals(Heap::string_symbol())) { |
+ Condition is_smi = masm_->CheckSmi(rax); |
+ __ j(is_smi, if_false); |
+ // Check for undetectable objects => false. |
+ __ movq(rdx, FieldOperand(rax, HeapObject::kMapOffset)); |
+ __ testb(FieldOperand(rdx, Map::kBitFieldOffset), |
+ Immediate(1 << Map::kIsUndetectable)); |
+ __ j(not_zero, if_false); |
+ __ CmpInstanceType(rdx, FIRST_NONSTRING_TYPE); |
+ Split(below, if_true, if_false, fall_through); |
+ } else if (check->Equals(Heap::boolean_symbol())) { |
+ __ CompareRoot(rax, Heap::kTrueValueRootIndex); |
__ j(equal, if_true); |
- __ CompareRoot(obj, Heap::kUndefinedValueRootIndex); |
+ __ CompareRoot(rax, Heap::kFalseValueRootIndex); |
+ Split(equal, if_true, if_false, fall_through); |
+ } else if (check->Equals(Heap::undefined_symbol())) { |
+ __ CompareRoot(rax, Heap::kUndefinedValueRootIndex); |
__ j(equal, if_true); |
- __ JumpIfSmi(obj, if_false); |
- // It can be an undetectable object. |
- __ movq(scratch, FieldOperand(obj, HeapObject::kMapOffset)); |
- __ testb(FieldOperand(scratch, Map::kBitFieldOffset), |
+ Condition is_smi = masm_->CheckSmi(rax); |
+ __ j(is_smi, if_false); |
+ // Check for undetectable objects => true. |
+ __ movq(rdx, FieldOperand(rax, HeapObject::kMapOffset)); |
+ __ testb(FieldOperand(rdx, Map::kBitFieldOffset), |
Immediate(1 << Map::kIsUndetectable)); |
- Split(not_zero, if_true, if_false, NULL); |
+ Split(not_zero, if_true, if_false, fall_through); |
+ } else if (check->Equals(Heap::function_symbol())) { |
+ Condition is_smi = masm_->CheckSmi(rax); |
+ __ j(is_smi, if_false); |
+ __ CmpObjectType(rax, JS_FUNCTION_TYPE, rdx); |
+ __ j(equal, if_true); |
+ // Regular expressions => 'function' (they are callable). |
+ __ CmpInstanceType(rdx, JS_REGEXP_TYPE); |
+ Split(equal, if_true, if_false, fall_through); |
+ } else if (check->Equals(Heap::object_symbol())) { |
+ Condition is_smi = masm_->CheckSmi(rax); |
+ __ j(is_smi, if_false); |
+ __ CompareRoot(rax, Heap::kNullValueRootIndex); |
+ __ j(equal, if_true); |
+ // Regular expressions => 'function', not 'object'. |
+ __ CmpObjectType(rax, JS_REGEXP_TYPE, rdx); |
+ __ j(equal, if_false); |
+ // Check for undetectable objects => false. |
+ __ testb(FieldOperand(rdx, Map::kBitFieldOffset), |
+ Immediate(1 << Map::kIsUndetectable)); |
+ __ j(not_zero, if_false); |
+ // Check for JS objects => true. |
+ __ CmpInstanceType(rdx, FIRST_JS_OBJECT_TYPE); |
+ __ j(below, if_false); |
+ __ CmpInstanceType(rdx, LAST_JS_OBJECT_TYPE); |
+ Split(below_equal, if_true, if_false, fall_through); |
+ } else { |
+ if (if_false != fall_through) __ jmp(if_false); |
} |
+ |
+ return true; |
} |
@@ -2921,6 +2982,16 @@ |
Label* if_false = NULL; |
PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
+ // First we try a fast inlined version of the compare when one of |
+ // the operands is a literal. |
+ Token::Value op = expr->op(); |
+ Expression* left = expr->left(); |
+ Expression* right = expr->right(); |
+ if (TryLiteralCompare(op, left, right, if_true, if_false, NULL)) { |
+ Apply(context_, if_true, if_false); |
+ return; |
+ } |
+ |
VisitForValue(expr->left(), kStack); |
switch (expr->op()) { |
case Token::IN: |
@@ -2948,24 +3019,10 @@ |
case Token::EQ_STRICT: |
strict = true; |
// Fall through. |
- case Token::EQ: { |
+ case Token::EQ: |
cc = equal; |
__ pop(rdx); |
- // If either operand is constant null we do a fast compare |
- // against null. |
- Literal* right_literal = expr->right()->AsLiteral(); |
- Literal* left_literal = expr->left()->AsLiteral(); |
- if (right_literal != NULL && right_literal->handle()->IsNull()) { |
- EmitNullCompare(strict, rdx, rax, if_true, if_false, rcx); |
- Apply(context_, if_true, if_false); |
- return; |
- } else if (left_literal != NULL && left_literal->handle()->IsNull()) { |
- EmitNullCompare(strict, rax, rdx, if_true, if_false, rcx); |
- Apply(context_, if_true, if_false); |
- return; |
- } |
break; |
- } |
case Token::LT: |
cc = less; |
__ pop(rdx); |