Index: src/hydrogen.cc |
=================================================================== |
--- src/hydrogen.cc (revision 9531) |
+++ src/hydrogen.cc (working copy) |
@@ -422,7 +422,7 @@ |
}; |
-void HGraph::Verify() const { |
+void HGraph::Verify(bool do_full_verify) const { |
for (int i = 0; i < blocks_.length(); i++) { |
HBasicBlock* block = blocks_.at(i); |
@@ -473,25 +473,27 @@ |
// Check special property of first block to have no predecessors. |
ASSERT(blocks_.at(0)->predecessors()->is_empty()); |
- // Check that the graph is fully connected. |
- ReachabilityAnalyzer analyzer(entry_block_, blocks_.length(), NULL); |
- ASSERT(analyzer.visited_count() == blocks_.length()); |
+ if (do_full_verify) { |
+ // Check that the graph is fully connected. |
+ ReachabilityAnalyzer analyzer(entry_block_, blocks_.length(), NULL); |
+ ASSERT(analyzer.visited_count() == blocks_.length()); |
- // Check that entry block dominator is NULL. |
- ASSERT(entry_block_->dominator() == NULL); |
+ // Check that entry block dominator is NULL. |
+ ASSERT(entry_block_->dominator() == NULL); |
- // Check dominators. |
- for (int i = 0; i < blocks_.length(); ++i) { |
- HBasicBlock* block = blocks_.at(i); |
- if (block->dominator() == NULL) { |
- // Only start block may have no dominator assigned to. |
- ASSERT(i == 0); |
- } else { |
- // Assert that block is unreachable if dominator must not be visited. |
- ReachabilityAnalyzer dominator_analyzer(entry_block_, |
- blocks_.length(), |
- block->dominator()); |
- ASSERT(!dominator_analyzer.reachable()->Contains(block->block_id())); |
+ // Check dominators. |
+ for (int i = 0; i < blocks_.length(); ++i) { |
+ HBasicBlock* block = blocks_.at(i); |
+ if (block->dominator() == NULL) { |
+ // Only start block may have no dominator assigned to. |
+ ASSERT(i == 0); |
+ } else { |
+ // Assert that block is unreachable if dominator must not be visited. |
+ ReachabilityAnalyzer dominator_analyzer(entry_block_, |
+ blocks_.length(), |
+ block->dominator()); |
+ ASSERT(!dominator_analyzer.reachable()->Contains(block->block_id())); |
+ } |
} |
} |
} |
@@ -850,7 +852,7 @@ |
} |
-bool HGraph::CheckPhis() { |
+bool HGraph::CheckArgumentsPhiUses() { |
int block_count = blocks_.length(); |
for (int i = 0; i < block_count; ++i) { |
for (int j = 0; j < blocks_[i]->phis()->length(); ++j) { |
@@ -863,13 +865,11 @@ |
} |
-bool HGraph::CollectPhis() { |
+bool HGraph::CheckConstPhiUses() { |
int block_count = blocks_.length(); |
- phi_list_ = new ZoneList<HPhi*>(block_count); |
for (int i = 0; i < block_count; ++i) { |
for (int j = 0; j < blocks_[i]->phis()->length(); ++j) { |
HPhi* phi = blocks_[i]->phis()->at(j); |
- phi_list_->Add(phi); |
// Check for the hole value (from an uninitialized const). |
for (int k = 0; k < phi->OperandCount(); k++) { |
if (phi->OperandAt(k) == GetConstantHole()) return false; |
@@ -880,6 +880,18 @@ |
} |
+void HGraph::CollectPhis() { |
+ int block_count = blocks_.length(); |
+ phi_list_ = new ZoneList<HPhi*>(block_count); |
+ for (int i = 0; i < block_count; ++i) { |
+ for (int j = 0; j < blocks_[i]->phis()->length(); ++j) { |
+ HPhi* phi = blocks_[i]->phis()->at(j); |
+ phi_list_->Add(phi); |
+ } |
+ } |
+} |
+ |
+ |
void HGraph::InferTypes(ZoneList<HValue*>* worklist) { |
BitVector in_worklist(GetMaximumValueID()); |
for (int i = 0; i < worklist->length(); ++i) { |
@@ -1848,7 +1860,7 @@ |
} |
if (new_value == NULL) { |
- new_value = new(zone()) HChange(value, value->representation(), to, |
+ new_value = new(zone()) HChange(value, to, |
is_truncating, deoptimize_on_undefined); |
} |
@@ -2320,17 +2332,24 @@ |
graph()->OrderBlocks(); |
graph()->AssignDominators(); |
+ |
+#ifdef DEBUG |
+ // Do a full verify after building the graph and computing dominators. |
+ graph()->Verify(true); |
+#endif |
+ |
graph()->PropagateDeoptimizingMark(); |
+ if (!graph()->CheckConstPhiUses()) { |
+ Bailout("Unsupported phi use of const variable"); |
+ return NULL; |
+ } |
graph()->EliminateRedundantPhis(); |
- if (!graph()->CheckPhis()) { |
- Bailout("Unsupported phi use of arguments object"); |
+ if (!graph()->CheckArgumentsPhiUses()) { |
+ Bailout("Unsupported phi use of arguments"); |
return NULL; |
} |
if (FLAG_eliminate_dead_phis) graph()->EliminateUnreachablePhis(); |
- if (!graph()->CollectPhis()) { |
- Bailout("Unsupported phi use of uninitialized constant"); |
- return NULL; |
- } |
+ graph()->CollectPhis(); |
HInferRepresentation rep(graph()); |
rep.Analyze(); |
@@ -3127,6 +3146,16 @@ |
} |
switch (variable->location()) { |
case Variable::UNALLOCATED: { |
+ // Handle known global constants like 'undefined' specially to avoid a |
+ // load from a global cell for them. |
+ Handle<Object> constant_value = |
+ isolate()->factory()->GlobalConstantFor(variable->name()); |
+ if (!constant_value.is_null()) { |
+ HConstant* instr = |
+ new(zone()) HConstant(constant_value, Representation::Tagged()); |
+ return ast_context()->ReturnInstruction(instr, expr->id()); |
+ } |
+ |
LookupResult lookup; |
GlobalPropertyAccess type = |
LookupGlobalProperty(variable, &lookup, false); |
@@ -3139,8 +3168,8 @@ |
if (type == kUseCell) { |
Handle<GlobalObject> global(info()->global_object()); |
Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup)); |
- bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly(); |
- HLoadGlobalCell* instr = new(zone()) HLoadGlobalCell(cell, check_hole); |
+ HLoadGlobalCell* instr = |
+ new(zone()) HLoadGlobalCell(cell, lookup.GetPropertyDetails()); |
return ast_context()->ReturnInstruction(instr, expr->id()); |
} else { |
HValue* context = environment()->LookupContext(); |
@@ -3317,7 +3346,47 @@ |
HValue* key = AddInstruction( |
new(zone()) HConstant(Handle<Object>(Smi::FromInt(i)), |
Representation::Integer32())); |
- AddInstruction(new(zone()) HStoreKeyedFastElement(elements, key, value)); |
+ if (FLAG_smi_only_arrays) { |
+ HInstruction* elements_kind = |
+ AddInstruction(new(zone()) HElementsKind(literal)); |
+ HBasicBlock* store_fast = graph()->CreateBasicBlock(); |
+ // Two empty blocks to satisfy edge split form. |
+ HBasicBlock* store_fast_edgesplit1 = graph()->CreateBasicBlock(); |
+ HBasicBlock* store_fast_edgesplit2 = graph()->CreateBasicBlock(); |
+ HBasicBlock* store_generic = graph()->CreateBasicBlock(); |
+ HBasicBlock* check_smi_only_elements = graph()->CreateBasicBlock(); |
+ HBasicBlock* join = graph()->CreateBasicBlock(); |
+ |
+ HIsSmiAndBranch* smicheck = new(zone()) HIsSmiAndBranch(value); |
+ smicheck->SetSuccessorAt(0, store_fast_edgesplit1); |
+ smicheck->SetSuccessorAt(1, check_smi_only_elements); |
+ current_block()->Finish(smicheck); |
+ store_fast_edgesplit1->Finish(new(zone()) HGoto(store_fast)); |
+ |
+ set_current_block(check_smi_only_elements); |
+ HCompareConstantEqAndBranch* smi_elements_check = |
+ new(zone()) HCompareConstantEqAndBranch(elements_kind, |
+ FAST_SMI_ONLY_ELEMENTS, |
+ Token::EQ_STRICT); |
+ smi_elements_check->SetSuccessorAt(0, store_generic); |
+ smi_elements_check->SetSuccessorAt(1, store_fast_edgesplit2); |
+ current_block()->Finish(smi_elements_check); |
+ store_fast_edgesplit2->Finish(new(zone()) HGoto(store_fast)); |
+ |
+ set_current_block(store_fast); |
+ AddInstruction(new(zone()) HStoreKeyedFastElement(elements, key, value)); |
+ store_fast->Goto(join); |
+ |
+ set_current_block(store_generic); |
+ AddInstruction(BuildStoreKeyedGeneric(literal, key, value)); |
+ store_generic->Goto(join); |
+ |
+ join->SetJoinId(expr->id()); |
+ set_current_block(join); |
+ } else { |
+ AddInstruction(new(zone()) HStoreKeyedFastElement(elements, key, value)); |
+ } |
+ |
AddSimulate(expr->GetIdForElement(i)); |
} |
return ast_context()->ReturnValue(Pop()); |
@@ -3561,10 +3630,10 @@ |
LookupResult lookup; |
GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, true); |
if (type == kUseCell) { |
- bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly(); |
Handle<GlobalObject> global(info()->global_object()); |
Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup)); |
- HInstruction* instr = new(zone()) HStoreGlobalCell(value, cell, check_hole); |
+ HInstruction* instr = |
+ new(zone()) HStoreGlobalCell(value, cell, lookup.GetPropertyDetails()); |
instr->set_position(position); |
AddInstruction(instr); |
if (instr->HasSideEffects()) AddSimulate(ast_id); |
@@ -3928,6 +3997,7 @@ |
case EXTERNAL_FLOAT_ELEMENTS: |
case EXTERNAL_DOUBLE_ELEMENTS: |
break; |
+ case FAST_SMI_ONLY_ELEMENTS: |
case FAST_ELEMENTS: |
case FAST_DOUBLE_ELEMENTS: |
case DICTIONARY_ELEMENTS: |
@@ -3944,6 +4014,30 @@ |
} |
+HInstruction* HGraphBuilder::BuildFastElementAccess(HValue* elements, |
+ HValue* checked_key, |
+ HValue* val, |
+ ElementsKind elements_kind, |
+ bool is_store) { |
+ if (is_store) { |
+ ASSERT(val != NULL); |
+ if (elements_kind == FAST_DOUBLE_ELEMENTS) { |
+ return new(zone()) HStoreKeyedFastDoubleElement( |
+ elements, checked_key, val); |
+ } else { // FAST_ELEMENTS or FAST_SMI_ONLY_ELEMENTS. |
+ return new(zone()) HStoreKeyedFastElement( |
+ elements, checked_key, val, elements_kind); |
+ } |
+ } |
+ // It's an element load (!is_store). |
+ if (elements_kind == FAST_DOUBLE_ELEMENTS) { |
+ return new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key); |
+ } else { // FAST_ELEMENTS or FAST_SMI_ONLY_ELEMENTS. |
+ return new(zone()) HLoadKeyedFastElement(elements, checked_key); |
+ } |
+} |
+ |
+ |
HInstruction* HGraphBuilder::BuildMonomorphicElementAccess(HValue* object, |
HValue* key, |
HValue* val, |
@@ -3951,17 +4045,20 @@ |
bool is_store) { |
ASSERT(expr->IsMonomorphic()); |
Handle<Map> map = expr->GetMonomorphicReceiverType(); |
- if (!map->has_fast_elements() && |
- !map->has_fast_double_elements() && |
+ AddInstruction(new(zone()) HCheckNonSmi(object)); |
+ HInstruction* mapcheck = AddInstruction(new(zone()) HCheckMap(object, map)); |
+ bool fast_smi_only_elements = map->has_fast_smi_only_elements(); |
+ bool fast_elements = map->has_fast_elements(); |
+ bool fast_double_elements = map->has_fast_double_elements(); |
+ if (!fast_smi_only_elements && |
+ !fast_elements && |
+ !fast_double_elements && |
!map->has_external_array_elements()) { |
return is_store ? BuildStoreKeyedGeneric(object, key, val) |
: BuildLoadKeyedGeneric(object, key); |
} |
- AddInstruction(new(zone()) HCheckNonSmi(object)); |
- HInstruction* mapcheck = AddInstruction(new(zone()) HCheckMap(object, map)); |
HInstruction* elements = AddInstruction(new(zone()) HLoadElements(object)); |
- bool fast_double_elements = map->has_fast_double_elements(); |
- if (is_store && map->has_fast_elements()) { |
+ if (is_store && (fast_elements || fast_smi_only_elements)) { |
AddInstruction(new(zone()) HCheckMap( |
elements, isolate()->factory()->fixed_array_map())); |
} |
@@ -3976,28 +4073,15 @@ |
return BuildExternalArrayElementAccess(external_elements, checked_key, |
val, map->elements_kind(), is_store); |
} |
- ASSERT(map->has_fast_elements() || fast_double_elements); |
+ ASSERT(fast_smi_only_elements || fast_elements || fast_double_elements); |
if (map->instance_type() == JS_ARRAY_TYPE) { |
length = AddInstruction(new(zone()) HJSArrayLength(object, mapcheck)); |
} else { |
length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements)); |
} |
checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length)); |
- if (is_store) { |
- if (fast_double_elements) { |
- return new(zone()) HStoreKeyedFastDoubleElement(elements, |
- checked_key, |
- val); |
- } else { |
- return new(zone()) HStoreKeyedFastElement(elements, checked_key, val); |
- } |
- } else { |
- if (fast_double_elements) { |
- return new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key); |
- } else { |
- return new(zone()) HLoadKeyedFastElement(elements, checked_key); |
- } |
- } |
+ return BuildFastElementAccess(elements, checked_key, val, |
+ map->elements_kind(), is_store); |
} |
@@ -4039,14 +4123,20 @@ |
HLoadExternalArrayPointer* external_elements = NULL; |
HInstruction* checked_key = NULL; |
- // FAST_ELEMENTS is assumed to be the first case. |
- STATIC_ASSERT(FAST_ELEMENTS == 0); |
+ // Generated code assumes that FAST_SMI_ONLY_ELEMENTS, FAST_ELEMENTS, |
+ // FAST_DOUBLE_ELEMENTS and DICTIONARY_ELEMENTS are handled before external |
+ // arrays. |
+ STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND); |
+ STATIC_ASSERT(FAST_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND); |
+ STATIC_ASSERT(FAST_DOUBLE_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND); |
+ STATIC_ASSERT(DICTIONARY_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND); |
- for (ElementsKind elements_kind = FAST_ELEMENTS; |
+ for (ElementsKind elements_kind = FIRST_ELEMENTS_KIND; |
elements_kind <= LAST_ELEMENTS_KIND; |
elements_kind = ElementsKind(elements_kind + 1)) { |
- // After having handled FAST_ELEMENTS and DICTIONARY_ELEMENTS, we |
- // need to add some code that's executed for all external array cases. |
+ // After having handled FAST_ELEMENTS, FAST_SMI_ONLY_ELEMENTS, |
+ // FAST_DOUBLE_ELEMENTS and DICTIONARY_ELEMENTS, we need to add some code |
+ // that's executed for all external array cases. |
STATIC_ASSERT(LAST_EXTERNAL_ARRAY_ELEMENTS_KIND == |
LAST_ELEMENTS_KIND); |
if (elements_kind == FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND |
@@ -4068,15 +4158,25 @@ |
set_current_block(if_true); |
HInstruction* access; |
- if (elements_kind == FAST_ELEMENTS || |
+ if (elements_kind == FAST_SMI_ONLY_ELEMENTS || |
+ elements_kind == FAST_ELEMENTS || |
elements_kind == FAST_DOUBLE_ELEMENTS) { |
- bool fast_double_elements = |
- elements_kind == FAST_DOUBLE_ELEMENTS; |
- if (is_store && elements_kind == FAST_ELEMENTS) { |
+ if (is_store && elements_kind == FAST_SMI_ONLY_ELEMENTS) { |
+ AddInstruction(new(zone()) HCheckSmi(val)); |
+ } |
+ if (is_store && elements_kind != FAST_DOUBLE_ELEMENTS) { |
AddInstruction(new(zone()) HCheckMap( |
elements, isolate()->factory()->fixed_array_map(), |
elements_kind_branch)); |
} |
+ // TODO(jkummerow): The need for these two blocks could be avoided |
+ // in one of two ways: |
+ // (1) Introduce ElementsKinds for JSArrays that are distinct from |
+ // those for fast objects. |
+ // (2) Put the common instructions into a third "join" block. This |
+ // requires additional AST IDs that we can deopt to from inside |
+ // that join block. They must be added to the Property class (when |
+ // it's a keyed property) and registered in the full codegen. |
HBasicBlock* if_jsarray = graph()->CreateBasicBlock(); |
HBasicBlock* if_fastobject = graph()->CreateBasicBlock(); |
HHasInstanceTypeAndBranch* typecheck = |
@@ -4086,29 +4186,15 @@ |
current_block()->Finish(typecheck); |
set_current_block(if_jsarray); |
- HInstruction* length = new(zone()) HJSArrayLength(object, typecheck); |
- AddInstruction(length); |
+ HInstruction* length; |
+ length = AddInstruction(new(zone()) HJSArrayLength(object, typecheck)); |
checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length)); |
- if (is_store) { |
- if (fast_double_elements) { |
- access = AddInstruction( |
- new(zone()) HStoreKeyedFastDoubleElement(elements, |
- checked_key, |
- val)); |
- } else { |
- access = AddInstruction( |
- new(zone()) HStoreKeyedFastElement(elements, checked_key, val)); |
- } |
- } else { |
- if (fast_double_elements) { |
- access = AddInstruction( |
- new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key)); |
- } else { |
- access = AddInstruction( |
- new(zone()) HLoadKeyedFastElement(elements, checked_key)); |
- } |
+ access = AddInstruction(BuildFastElementAccess( |
+ elements, checked_key, val, elements_kind, is_store)); |
+ if (!is_store) { |
Push(access); |
} |
+ |
*has_side_effects |= access->HasSideEffects(); |
if (position != -1) { |
access->set_position(position); |
@@ -4118,25 +4204,8 @@ |
set_current_block(if_fastobject); |
length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements)); |
checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length)); |
- if (is_store) { |
- if (fast_double_elements) { |
- access = AddInstruction( |
- new(zone()) HStoreKeyedFastDoubleElement(elements, |
- checked_key, |
- val)); |
- } else { |
- access = AddInstruction( |
- new(zone()) HStoreKeyedFastElement(elements, checked_key, val)); |
- } |
- } else { |
- if (fast_double_elements) { |
- access = AddInstruction( |
- new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key)); |
- } else { |
- access = AddInstruction( |
- new(zone()) HLoadKeyedFastElement(elements, checked_key)); |
- } |
- } |
+ access = AddInstruction(BuildFastElementAccess( |
+ elements, checked_key, val, elements_kind, is_store)); |
} else if (elements_kind == DICTIONARY_ELEMENTS) { |
if (is_store) { |
access = AddInstruction(BuildStoreKeyedGeneric(object, key, val)); |
@@ -4474,20 +4543,25 @@ |
return false; |
} |
- // No context change required. |
CompilationInfo* outer_info = info(); |
+#if !defined(V8_TARGET_ARCH_IA32) |
+ // Target must be able to use caller's context. |
if (target->context() != outer_info->closure()->context() || |
outer_info->scope()->contains_with() || |
outer_info->scope()->num_heap_slots() > 0) { |
TraceInline(target, caller, "target requires context change"); |
return false; |
} |
+#endif |
+ |
// Don't inline deeper than kMaxInliningLevels calls. |
HEnvironment* env = environment(); |
int current_level = 1; |
while (env->outer() != NULL) { |
- if (current_level == Compiler::kMaxInliningLevels) { |
+ if (current_level == (FLAG_limit_inlining |
+ ? Compiler::kMaxInliningLevels |
+ : 2 * Compiler::kMaxInliningLevels)) { |
TraceInline(target, caller, "inline depth limit reached"); |
return false; |
} |
@@ -4602,6 +4676,17 @@ |
function, |
undefined, |
call_kind); |
+#ifdef V8_TARGET_ARCH_IA32 |
+ // IA32 only, overwrite the caller's context in the deoptimization |
+ // environment with the correct one. |
+ // |
+ // TODO(kmillikin): implement the same inlining on other platforms so we |
+ // can remove the unsightly ifdefs in this function. |
+ HConstant* context = new HConstant(Handle<Context>(target->context()), |
+ Representation::Tagged()); |
+ AddInstruction(context); |
+ inner_env->BindContext(context); |
+#endif |
HBasicBlock* body_entry = CreateBasicBlock(inner_env); |
current_block()->Goto(body_entry); |
body_entry->SetJoinId(expr->ReturnId()); |
@@ -4922,8 +5007,8 @@ |
} |
} else { |
+ expr->RecordTypeFeedback(oracle(), CALL_AS_FUNCTION); |
VariableProxy* proxy = expr->expression()->AsVariableProxy(); |
- // FIXME. |
bool global_call = proxy != NULL && proxy->var()->IsUnallocated(); |
if (global_call) { |
@@ -4975,6 +5060,46 @@ |
Drop(argument_count); |
} |
+ } else if (expr->IsMonomorphic()) { |
+ // The function is on the stack in the unoptimized code during |
+ // evaluation of the arguments. |
+ CHECK_ALIVE(VisitForValue(expr->expression())); |
+ HValue* function = Top(); |
+ HValue* context = environment()->LookupContext(); |
+ HGlobalObject* global = new(zone()) HGlobalObject(context); |
+ HGlobalReceiver* receiver = new(zone()) HGlobalReceiver(global); |
+ AddInstruction(global); |
+ PushAndAdd(receiver); |
+ CHECK_ALIVE(VisitExpressions(expr->arguments())); |
+ AddInstruction(new(zone()) HCheckFunction(function, expr->target())); |
+ if (TryInline(expr)) { |
+ // The function is lingering in the deoptimization environment. |
+ // Handle it by case analysis on the AST context. |
+ if (ast_context()->IsEffect()) { |
+ Drop(1); |
+ } else if (ast_context()->IsValue()) { |
+ HValue* result = Pop(); |
+ Drop(1); |
+ Push(result); |
+ } else if (ast_context()->IsTest()) { |
+ TestContext* context = TestContext::cast(ast_context()); |
+ if (context->if_true()->HasPredecessor()) { |
+ context->if_true()->last_environment()->Drop(1); |
+ } |
+ if (context->if_false()->HasPredecessor()) { |
+ context->if_true()->last_environment()->Drop(1); |
+ } |
+ } else { |
+ UNREACHABLE(); |
+ } |
+ return; |
+ } else { |
+ call = PreProcessCall(new(zone()) HInvokeFunction(context, |
+ function, |
+ argument_count)); |
+ Drop(1); // The function. |
+ } |
+ |
} else { |
CHECK_ALIVE(VisitArgument(expr->expression())); |
HValue* context = environment()->LookupContext(); |
@@ -5668,26 +5793,36 @@ |
} |
-void HGraphBuilder::HandleLiteralCompareTypeof(CompareOperation* compare_expr, |
- Expression* expr, |
+void HGraphBuilder::HandleLiteralCompareTypeof(CompareOperation* expr, |
+ Expression* sub_expr, |
Handle<String> check) { |
- CHECK_ALIVE(VisitForTypeOf(expr)); |
- HValue* expr_value = Pop(); |
- HTypeofIsAndBranch* instr = new(zone()) HTypeofIsAndBranch(expr_value, check); |
- instr->set_position(compare_expr->position()); |
- return ast_context()->ReturnControl(instr, compare_expr->id()); |
+ CHECK_ALIVE(VisitForTypeOf(sub_expr)); |
+ HValue* value = Pop(); |
+ HTypeofIsAndBranch* instr = new(zone()) HTypeofIsAndBranch(value, check); |
+ instr->set_position(expr->position()); |
+ return ast_context()->ReturnControl(instr, expr->id()); |
} |
-void HGraphBuilder::HandleLiteralCompareUndefined( |
- CompareOperation* compare_expr, Expression* expr) { |
- CHECK_ALIVE(VisitForValue(expr)); |
- HValue* lhs = Pop(); |
- HValue* rhs = graph()->GetConstantUndefined(); |
- HCompareObjectEqAndBranch* instr = |
- new(zone()) HCompareObjectEqAndBranch(lhs, rhs); |
- instr->set_position(compare_expr->position()); |
- return ast_context()->ReturnControl(instr, compare_expr->id()); |
+bool HGraphBuilder::TryLiteralCompare(CompareOperation* expr) { |
+ Expression *sub_expr; |
+ Handle<String> check; |
+ if (expr->IsLiteralCompareTypeof(&sub_expr, &check)) { |
+ HandleLiteralCompareTypeof(expr, sub_expr, check); |
+ return true; |
+ } |
+ |
+ if (expr->IsLiteralCompareUndefined(&sub_expr)) { |
+ HandleLiteralCompareNil(expr, sub_expr, kUndefinedValue); |
+ return true; |
+ } |
+ |
+ if (expr->IsLiteralCompareNull(&sub_expr)) { |
+ HandleLiteralCompareNil(expr, sub_expr, kNullValue); |
+ return true; |
+ } |
+ |
+ return false; |
} |
@@ -5709,18 +5844,8 @@ |
} |
// Check for special cases that compare against literals. |
- Expression *sub_expr; |
- Handle<String> check; |
- if (expr->IsLiteralCompareTypeof(&sub_expr, &check)) { |
- HandleLiteralCompareTypeof(expr, sub_expr, check); |
- return; |
- } |
+ if (TryLiteralCompare(expr)) return; |
- if (expr->IsLiteralCompareUndefined(&sub_expr)) { |
- HandleLiteralCompareUndefined(expr, sub_expr); |
- return; |
- } |
- |
TypeInfo type_info = oracle()->CompareType(expr); |
// Check if this expression was ever executed according to type feedback. |
if (type_info.IsUninitialized()) { |
@@ -5824,14 +5949,18 @@ |
} |
-void HGraphBuilder::VisitCompareToNull(CompareToNull* expr) { |
+void HGraphBuilder::HandleLiteralCompareNil(CompareOperation* expr, |
+ Expression* sub_expr, |
+ NilValue nil) { |
ASSERT(!HasStackOverflow()); |
ASSERT(current_block() != NULL); |
ASSERT(current_block()->HasPredecessor()); |
- CHECK_ALIVE(VisitForValue(expr->expression())); |
+ CHECK_ALIVE(VisitForValue(sub_expr)); |
HValue* value = Pop(); |
- HIsNullAndBranch* instr = |
- new(zone()) HIsNullAndBranch(value, expr->is_strict()); |
+ EqualityKind kind = |
+ expr->op() == Token::EQ_STRICT ? kStrictEquality : kNonStrictEquality; |
+ HIsNilAndBranch* instr = new(zone()) HIsNilAndBranch(value, kind, nil); |
+ instr->set_position(expr->position()); |
return ast_context()->ReturnControl(instr, expr->id()); |
} |
@@ -5914,9 +6043,7 @@ |
CHECK_ALIVE(VisitForValue(call->arguments()->at(0))); |
HValue* value = Pop(); |
HHasInstanceTypeAndBranch* result = |
- new(zone()) HHasInstanceTypeAndBranch(value, |
- JS_FUNCTION_TYPE, |
- JS_FUNCTION_PROXY_TYPE); |
+ new(zone()) HHasInstanceTypeAndBranch(value, JS_FUNCTION_TYPE); |
return ast_context()->ReturnControl(result, call->id()); |
} |
@@ -6816,7 +6943,7 @@ |
} |
#ifdef DEBUG |
- if (graph_ != NULL) graph_->Verify(); |
+ if (graph_ != NULL) graph_->Verify(false); // No full verify. |
if (allocator_ != NULL) allocator_->Verify(); |
#endif |
} |