Index: src/hydrogen.cc |
diff --git a/src/hydrogen.cc b/src/hydrogen.cc |
index 158bfbe369bc8477b5ec8601315285343e8da27f..ff5ce2e2a1daaa7036e5c16d821be784f8715a44 100644 |
--- a/src/hydrogen.cc |
+++ b/src/hydrogen.cc |
@@ -150,6 +150,10 @@ void HBasicBlock::Finish(HControlInstruction* end) { |
void HBasicBlock::Goto(HBasicBlock* block, bool include_stack_check) { |
+ if (block->IsInlineReturnTarget()) { |
+ AddInstruction(new HLeaveInlined); |
+ last_environment_ = last_environment()->outer(); |
+ } |
AddSimulate(AstNode::kNoNumber); |
HGoto* instr = new HGoto(block); |
instr->set_include_stack_check(include_stack_check); |
@@ -157,6 +161,18 @@ void HBasicBlock::Goto(HBasicBlock* block, bool include_stack_check) { |
} |
+void HBasicBlock::AddLeaveInlined(HValue* return_value, HBasicBlock* target) { |
+ ASSERT(target->IsInlineReturnTarget()); |
+ ASSERT(return_value != NULL); |
+ AddInstruction(new HLeaveInlined); |
+ last_environment_ = last_environment()->outer(); |
+ last_environment()->Push(return_value); |
+ AddSimulate(AstNode::kNoNumber); |
+ HGoto* instr = new HGoto(target); |
+ Finish(instr); |
+} |
+ |
+ |
void HBasicBlock::SetInitialEnvironment(HEnvironment* env) { |
ASSERT(!HasEnvironment()); |
ASSERT(first() == NULL); |
@@ -1979,20 +1995,8 @@ void TestContext::BuildBranch(HValue* value) { |
HTest* test = new HTest(value, empty_true, empty_false); |
builder->current_block()->Finish(test); |
- HValue* const no_return_value = NULL; |
- HBasicBlock* true_target = if_true(); |
- if (true_target->IsInlineReturnTarget()) { |
- empty_true->AddLeaveInlined(no_return_value, true_target); |
- } else { |
- empty_true->Goto(true_target); |
- } |
- |
- HBasicBlock* false_target = if_false(); |
- if (false_target->IsInlineReturnTarget()) { |
- empty_false->AddLeaveInlined(no_return_value, false_target); |
- } else { |
- empty_false->Goto(false_target); |
- } |
+ empty_true->Goto(if_true(), false); |
+ empty_false->Goto(if_false(), false); |
builder->set_current_block(NULL); |
} |
@@ -2308,18 +2312,6 @@ HBasicBlock* HGraphBuilder::CreateBasicBlock(HEnvironment* env) { |
} |
-HSubgraph* HGraphBuilder::CreateInlinedSubgraph(HEnvironment* outer, |
- Handle<JSFunction> target, |
- FunctionLiteral* function) { |
- HConstant* undefined = graph()->GetConstantUndefined(); |
- HEnvironment* inner = |
- outer->CopyForInlining(target, function, true, undefined); |
- HSubgraph* subgraph = new HSubgraph(graph()); |
- subgraph->Initialize(CreateBasicBlock(inner)); |
- return subgraph; |
-} |
- |
- |
HSubgraph* HGraphBuilder::CreateEmptySubgraph() { |
HSubgraph* subgraph = new HSubgraph(graph()); |
subgraph->Initialize(graph()->CreateBasicBlock()); |
@@ -2327,14 +2319,6 @@ HSubgraph* HGraphBuilder::CreateEmptySubgraph() { |
} |
-HSubgraph* HGraphBuilder::CreateBranchSubgraph(HEnvironment* env) { |
- HSubgraph* subgraph = new HSubgraph(graph()); |
- HEnvironment* new_env = env->Copy(); |
- subgraph->Initialize(CreateBasicBlock(new_env)); |
- return subgraph; |
-} |
- |
- |
HBasicBlock* HGraphBuilder::CreateLoopHeaderBlock() { |
HBasicBlock* header = graph()->CreateBasicBlock(); |
HEnvironment* entry_env = environment()->CopyAsLoopHeader(header); |
@@ -2458,20 +2442,16 @@ void HGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) { |
VisitForControl(stmt->expression(), |
test->if_true(), |
test->if_false()); |
+ } else if (context->IsEffect()) { |
+ VISIT_FOR_EFFECT(stmt->expression()); |
+ current_block()->Goto(function_return(), false); |
} else { |
- HValue* return_value = NULL; |
- if (context->IsEffect()) { |
- VISIT_FOR_EFFECT(stmt->expression()); |
- return_value = graph()->GetConstantUndefined(); |
- } else { |
- ASSERT(context->IsValue()); |
- VISIT_FOR_VALUE(stmt->expression()); |
- return_value = environment()->Pop(); |
- } |
- current_block()->AddLeaveInlined(return_value, |
- function_return()); |
- set_current_block(NULL); |
+ ASSERT(context->IsValue()); |
+ VISIT_FOR_VALUE(stmt->expression()); |
+ HValue* return_value = environment()->Pop(); |
+ current_block()->AddLeaveInlined(return_value, function_return()); |
} |
+ set_current_block(NULL); |
} |
} |
@@ -3049,55 +3029,6 @@ void HGraphBuilder::VisitCatchExtensionObject(CatchExtensionObject* expr) { |
} |
-HBasicBlock* HGraphBuilder::BuildTypeSwitch(HValue* receiver, |
- ZoneMapList* maps, |
- ZoneList<HSubgraph*>* body_graphs, |
- HSubgraph* default_graph, |
- int join_id) { |
- ASSERT(maps->length() == body_graphs->length()); |
- HBasicBlock* join_block = graph()->CreateBasicBlock(); |
- AddInstruction(new HCheckNonSmi(receiver)); |
- |
- for (int i = 0; i < maps->length(); ++i) { |
- // Build the branches, connect all the target subgraphs to the join |
- // block. Use the default as a target of the last branch. |
- HSubgraph* if_true = body_graphs->at(i); |
- HSubgraph* if_false = (i == maps->length() - 1) |
- ? default_graph |
- : CreateBranchSubgraph(environment()); |
- HCompareMap* compare = |
- new HCompareMap(receiver, |
- maps->at(i), |
- if_true->entry_block(), |
- if_false->entry_block()); |
- current_block()->Finish(compare); |
- |
- if (if_true->exit_block() != NULL) { |
- // In an effect context the value of the type switch is not needed. |
- // There is no need to merge it at the join block only to discard it. |
- if (ast_context()->IsEffect()) { |
- if_true->exit_block()->last_environment()->Drop(1); |
- } |
- if_true->exit_block()->Goto(join_block); |
- } |
- |
- set_current_block(if_false->exit_block()); |
- } |
- |
- // Connect the default if necessary. |
- if (current_block() != NULL) { |
- if (ast_context()->IsEffect()) { |
- environment()->Drop(1); |
- } |
- current_block()->Goto(join_block); |
- } |
- |
- if (join_block->predecessors()->is_empty()) return NULL; |
- join_block->SetJoinId(join_id); |
- return join_block; |
-} |
- |
- |
// Sets the lookup result and returns true if the store can be inlined. |
static bool ComputeStoredField(Handle<Map> type, |
Handle<String> name, |
@@ -3885,22 +3816,26 @@ void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr, |
HValue* receiver, |
ZoneMapList* types, |
Handle<String> name) { |
- int argument_count = expr->arguments()->length() + 1; // Plus receiver. |
- int number_of_types = Min(types->length(), kMaxCallPolymorphism); |
- ZoneMapList maps(number_of_types); |
- ZoneList<HSubgraph*> subgraphs(number_of_types); |
- bool needs_generic = (types->length() > kMaxCallPolymorphism); |
- |
- // Build subgraphs for each of the specific maps. |
- // |
// TODO(ager): We should recognize when the prototype chains for different |
// maps are identical. In that case we can avoid repeatedly generating the |
// same prototype map checks. |
- for (int i = 0; i < number_of_types; ++i) { |
+ int argument_count = expr->arguments()->length() + 1; // Includes receiver. |
+ int count = 0; |
+ HBasicBlock* join = NULL; |
+ for (int i = 0; i < types->length() && count < kMaxCallPolymorphism; ++i) { |
Handle<Map> map = types->at(i); |
if (expr->ComputeTarget(map, name)) { |
- HSubgraph* subgraph = CreateBranchSubgraph(environment()); |
- SubgraphScope scope(this, subgraph); |
+ if (count == 0) { |
+ AddInstruction(new HCheckNonSmi(receiver)); // Only needed once. |
+ join = graph()->CreateBasicBlock(); |
+ } |
+ ++count; |
+ HBasicBlock* if_true = graph()->CreateBasicBlock(); |
+ HBasicBlock* if_false = graph()->CreateBasicBlock(); |
+ HCompareMap* compare = new HCompareMap(receiver, map, if_true, if_false); |
+ current_block()->Finish(compare); |
+ |
+ set_current_block(if_true); |
AddCheckConstantFunction(expr, receiver, map, false); |
if (FLAG_trace_inlining && FLAG_polymorphic_inlining) { |
PrintF("Trying to inline the polymorphic call to %s\n", |
@@ -3911,53 +3846,49 @@ void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr, |
// during hydrogen processing. |
CHECK_BAILOUT; |
HCallConstantFunction* call = |
- new HCallConstantFunction(expr->target(), argument_count); |
+ new HCallConstantFunction(expr->target(), argument_count); |
call->set_position(expr->position()); |
PreProcessCall(call); |
- PushAndAdd(call); |
+ AddInstruction(call); |
+ if (!ast_context()->IsEffect()) Push(call); |
} |
- maps.Add(map); |
- subgraphs.Add(subgraph); |
- } else { |
- needs_generic = true; |
+ |
+ if (current_block() != NULL) current_block()->Goto(join); |
+ set_current_block(if_false); |
} |
} |
- // If we couldn't compute the target for any of the maps just perform an |
- // IC call. |
- if (maps.length() == 0) { |
+ // Finish up. Unconditionally deoptimize if we've handled all the maps we |
+ // know about and do not want to handle ones we've never seen. Otherwise |
+ // use a generic IC. |
+ if (count == types->length() && FLAG_deoptimize_uncommon_cases) { |
+ current_block()->FinishExit(new HDeoptimize); |
+ } else { |
HContext* context = new HContext; |
AddInstruction(context); |
HCallNamed* call = new HCallNamed(context, name, argument_count); |
call->set_position(expr->position()); |
PreProcessCall(call); |
- ast_context()->ReturnInstruction(call, expr->id()); |
- } else { |
- // Build subgraph for generic call through IC. |
- HSubgraph* default_graph = CreateBranchSubgraph(environment()); |
- { SubgraphScope scope(this, default_graph); |
- if (!needs_generic && FLAG_deoptimize_uncommon_cases) { |
- default_graph->exit_block()->FinishExit(new HDeoptimize()); |
- default_graph->set_exit_block(NULL); |
- } else { |
- HContext* context = new HContext; |
- AddInstruction(context); |
- HCallNamed* call = new HCallNamed(context, name, argument_count); |
- call->set_position(expr->position()); |
- PreProcessCall(call); |
- PushAndAdd(call); |
- } |
- } |
- HBasicBlock* new_exit_block = |
- BuildTypeSwitch(receiver, &maps, &subgraphs, default_graph, expr->id()); |
- set_current_block(new_exit_block); |
- // In an effect context, we did not materialized the value in the |
- // predecessor environments so there's no need to handle it here. |
- if (new_exit_block != NULL && !ast_context()->IsEffect()) { |
- ast_context()->ReturnValue(Pop()); |
+ if (join != NULL) { |
+ AddInstruction(call); |
+ if (!ast_context()->IsEffect()) Push(call); |
+ current_block()->Goto(join); |
+ } else { |
+ ast_context()->ReturnInstruction(call, expr->id()); |
+ return; |
} |
} |
+ |
+ // We assume that control flow is always live after an expression. So |
+ // even without predecessors to the join block, we set it as the exit |
+ // block and continue by adding instructions there. |
+ ASSERT(join != NULL); |
+ set_current_block(join); |
+ if (join->HasPredecessor()) { |
+ join->SetJoinId(expr->id()); |
+ if (!ast_context()->IsEffect()) ast_context()->ReturnValue(Pop()); |
+ } |
} |
@@ -4103,9 +4034,16 @@ bool HGraphBuilder::TryInline(Call* expr) { |
Handle<Context>(target->context()->global_context())); |
FunctionState target_state(this, &target_info, &target_oracle); |
- HSubgraph* body = CreateInlinedSubgraph(env, target, function); |
- body->exit_block()->AddInstruction(new HEnterInlined(target, function)); |
- AddToSubgraph(body, function->body()); |
+ HConstant* undefined = graph()->GetConstantUndefined(); |
+ HEnvironment* inner_env = |
+ environment()->CopyForInlining(target, function, true, undefined); |
+ HBasicBlock* body_entry = CreateBasicBlock(inner_env); |
+ current_block()->Goto(body_entry); |
+ |
+ body_entry->SetJoinId(expr->ReturnId()); |
+ set_current_block(body_entry); |
+ AddInstruction(new HEnterInlined(target, function)); |
+ VisitStatements(function->body()); |
if (HasStackOverflow()) { |
// Bail out if the inline function did, as we cannot residualize a call |
// instead. |
@@ -4118,13 +4056,17 @@ bool HGraphBuilder::TryInline(Call* expr) { |
TraceInline(target, NULL); |
- if (body->exit_block() != NULL) { |
+ if (current_block() != NULL) { |
// Add a return of undefined if control can fall off the body. In a |
// test context, undefined is false. |
- HValue* return_value = graph()->GetConstantUndefined(); |
if (inlined_test_context() == NULL) { |
ASSERT(function_return() != NULL); |
- body->exit_block()->AddLeaveInlined(return_value, function_return()); |
+ ASSERT(call_context()->IsEffect() || call_context()->IsValue()); |
+ if (call_context()->IsEffect()) { |
+ current_block()->Goto(function_return(), false); |
+ } else { |
+ current_block()->AddLeaveInlined(undefined, function_return()); |
+ } |
} else { |
// The graph builder assumes control can reach both branches of a |
// test, so we materialize the undefined value and test it rather than |
@@ -4133,24 +4075,14 @@ bool HGraphBuilder::TryInline(Call* expr) { |
// TODO(3168478): refactor to avoid this. |
HBasicBlock* empty_true = graph()->CreateBasicBlock(); |
HBasicBlock* empty_false = graph()->CreateBasicBlock(); |
- HTest* test = new HTest(return_value, empty_true, empty_false); |
- body->exit_block()->Finish(test); |
- |
- HValue* const no_return_value = NULL; |
- empty_true->AddLeaveInlined(no_return_value, |
- inlined_test_context()->if_true()); |
- empty_false->AddLeaveInlined(no_return_value, |
- inlined_test_context()->if_false()); |
+ HTest* test = new HTest(undefined, empty_true, empty_false); |
+ current_block()->Finish(test); |
+ |
+ empty_true->Goto(inlined_test_context()->if_true(), false); |
+ empty_false->Goto(inlined_test_context()->if_false(), false); |
} |
- body->set_exit_block(NULL); |
} |
- // Record the environment at the inlined function call. |
- AddSimulate(expr->ReturnId()); |
- |
- // Jump to the function entry (without re-recording the environment). |
- current_block()->Finish(new HGoto(body->entry_block())); |
- |
// Fix up the function exits. |
if (inlined_test_context() != NULL) { |
HBasicBlock* if_true = inlined_test_context()->if_true(); |
@@ -4162,20 +4094,10 @@ bool HGraphBuilder::TryInline(Call* expr) { |
ClearInlinedTestContext(); |
// Forward to the real test context. |
- HValue* const no_return_value = NULL; |
HBasicBlock* true_target = TestContext::cast(ast_context())->if_true(); |
- if (true_target->IsInlineReturnTarget()) { |
- if_true->AddLeaveInlined(no_return_value, true_target); |
- } else { |
- if_true->Goto(true_target); |
- } |
- |
HBasicBlock* false_target = TestContext::cast(ast_context())->if_false(); |
- if (false_target->IsInlineReturnTarget()) { |
- if_false->AddLeaveInlined(no_return_value, false_target); |
- } else { |
- if_false->Goto(false_target); |
- } |
+ if_true->Goto(true_target, false); |
+ if_false->Goto(false_target, false); |
// TODO(kmillikin): Come up with a better way to handle this. It is too |
// subtle. NULL here indicates that the enclosing context has no control |
@@ -4191,16 +4113,6 @@ bool HGraphBuilder::TryInline(Call* expr) { |
} |
-void HBasicBlock::AddLeaveInlined(HValue* return_value, HBasicBlock* target) { |
- ASSERT(target->IsInlineReturnTarget()); |
- AddInstruction(new HLeaveInlined); |
- HEnvironment* outer = last_environment()->outer(); |
- if (return_value != NULL) outer->Push(return_value); |
- UpdateEnvironment(outer); |
- Goto(target); |
-} |
- |
- |
bool HGraphBuilder::TryInlineBuiltinFunction(Call* expr, |
HValue* receiver, |
Handle<Map> receiver_map, |
@@ -4407,16 +4319,6 @@ void HGraphBuilder::VisitCall(Call* expr) { |
AddCheckConstantFunction(expr, receiver, receiver_map, true); |
if (TryInline(expr)) { |
- if (current_block() != NULL) { |
- HValue* return_value = Pop(); |
- // If we inlined a function in a test context then we need to emit |
- // a simulate here to shadow the ones at the end of the |
- // predecessor blocks. Those environments contain the return |
- // value on top and do not correspond to any actual state of the |
- // unoptimized code. |
- if (ast_context()->IsEffect()) AddSimulate(expr->id()); |
- ast_context()->ReturnValue(return_value); |
- } |
return; |
} else { |
// Check for bailout, as the TryInline call in the if condition above |
@@ -4480,16 +4382,6 @@ void HGraphBuilder::VisitCall(Call* expr) { |
environment()->SetExpressionStackAt(receiver_index, global_receiver); |
if (TryInline(expr)) { |
- if (current_block() != NULL) { |
- HValue* return_value = Pop(); |
- // If we inlined a function in a test context then we need to |
- // emit a simulate here to shadow the ones at the end of the |
- // predecessor blocks. Those environments contain the return |
- // value on top and do not correspond to any actual state of the |
- // unoptimized code. |
- if (ast_context()->IsEffect()) AddSimulate(expr->id()); |
- ast_context()->ReturnValue(return_value); |
- } |
return; |
} |
// Check for bailout, as trying to inline might fail due to bailout |