Index: src/hydrogen.cc |
diff --git a/src/hydrogen.cc b/src/hydrogen.cc |
index 6bd1a6b0e05bfb77c838507703d9bb08fec01f7f..4a0f792398449d6e2d19b38ce7137ef1d067193c 100644 |
--- a/src/hydrogen.cc |
+++ b/src/hydrogen.cc |
@@ -536,7 +536,6 @@ void HBasicBlock::FinishExit(HControlInstruction* instruction) { |
HGraph::HGraph(CompilationInfo* info) |
: HSubgraph(this), |
next_block_id_(0), |
- info_(info), |
blocks_(8), |
values_(16), |
phi_list_(NULL) { |
@@ -545,12 +544,7 @@ HGraph::HGraph(CompilationInfo* info) |
} |
-bool HGraph::AllowCodeMotion() const { |
- return info()->shared_info()->opt_count() + 1 < Compiler::kDefaultMaxOptCount; |
-} |
- |
- |
-Handle<Code> HGraph::Compile() { |
+Handle<Code> HGraph::Compile(CompilationInfo* info) { |
int values = GetMaximumValueID(); |
if (values > LAllocator::max_initial_value_ids()) { |
if (FLAG_trace_bailout) PrintF("Function is too big\n"); |
@@ -558,7 +552,7 @@ Handle<Code> HGraph::Compile() { |
} |
LAllocator allocator(values, this); |
- LChunkBuilder builder(this, &allocator); |
+ LChunkBuilder builder(info, this, &allocator); |
LChunk* chunk = builder.Build(); |
if (chunk == NULL) return Handle<Code>::null(); |
@@ -569,7 +563,7 @@ Handle<Code> HGraph::Compile() { |
if (!FLAG_use_lithium) return Handle<Code>::null(); |
MacroAssembler assembler(NULL, 0); |
- LCodeGen generator(chunk, &assembler, info()); |
+ LCodeGen generator(chunk, &assembler, info); |
if (FLAG_eliminate_empty_blocks) { |
chunk->MarkEmptyBlocks(); |
@@ -579,13 +573,13 @@ Handle<Code> HGraph::Compile() { |
if (FLAG_trace_codegen) { |
PrintF("Crankshaft Compiler - "); |
} |
- CodeGenerator::MakeCodePrologue(info()); |
+ CodeGenerator::MakeCodePrologue(info); |
Code::Flags flags = |
Code::ComputeFlags(Code::OPTIMIZED_FUNCTION, NOT_IN_LOOP); |
Handle<Code> code = |
- CodeGenerator::MakeCodeEpilogue(&assembler, flags, info()); |
+ CodeGenerator::MakeCodeEpilogue(&assembler, flags, info); |
generator.FinishCode(code); |
- CodeGenerator::PrintCode(code, info()); |
+ CodeGenerator::PrintCode(code, info); |
return code; |
} |
return Handle<Code>::null(); |
@@ -1176,8 +1170,9 @@ void HStackCheckEliminator::RemoveStackCheck(HBasicBlock* block) { |
class HGlobalValueNumberer BASE_EMBEDDED { |
public: |
- explicit HGlobalValueNumberer(HGraph* graph) |
+ explicit HGlobalValueNumberer(HGraph* graph, CompilationInfo* info) |
: graph_(graph), |
+ info_(info), |
block_side_effects_(graph_->blocks()->length()), |
loop_side_effects_(graph_->blocks()->length()) { |
ASSERT(Heap::allow_allocation(false)); |
@@ -1197,9 +1192,14 @@ class HGlobalValueNumberer BASE_EMBEDDED { |
void ProcessLoopBlock(HBasicBlock* block, |
HBasicBlock* before_loop, |
int loop_kills); |
+ bool AllowCodeMotion(); |
bool ShouldMove(HInstruction* instr, HBasicBlock* loop_header); |
+ HGraph* graph() { return graph_; } |
+ CompilationInfo* info() { return info_; } |
+ |
HGraph* graph_; |
+ CompilationInfo* info_; |
// A map of block IDs to their side effects. |
ZoneList<int> block_side_effects_; |
@@ -1300,10 +1300,15 @@ void HGlobalValueNumberer::ProcessLoopBlock(HBasicBlock* block, |
} |
+bool HGlobalValueNumberer::AllowCodeMotion() { |
+ return info()->shared_info()->opt_count() + 1 < Compiler::kDefaultMaxOptCount; |
+} |
+ |
+ |
bool HGlobalValueNumberer::ShouldMove(HInstruction* instr, |
HBasicBlock* loop_header) { |
// If we've disabled code motion, don't move any instructions. |
- if (!graph_->AllowCodeMotion()) return false; |
+ if (!AllowCodeMotion()) return false; |
// If --aggressive-loop-invariant-motion, move everything except change |
// instructions. |
@@ -1848,6 +1853,47 @@ void HGraph::ComputeMinusZeroChecks() { |
} |
+// Implementation of utility class to encapsulate the translation state for |
+// a (possibly inlined) function. |
+FunctionState::FunctionState(HGraphBuilder* owner, |
+ CompilationInfo* info, |
+ TypeFeedbackOracle* oracle) |
+ : owner_(owner), |
+ compilation_info_(info), |
+ oracle_(oracle), |
+ call_context_(NULL), |
+ function_return_(NULL), |
+ test_context_(NULL), |
+ outer_(owner->function_state()) { |
+ if (outer_ != NULL) { |
+ // State for an inline function. |
+ if (owner->ast_context()->IsTest()) { |
+ HBasicBlock* if_true = owner->graph()->CreateBasicBlock(); |
+ HBasicBlock* if_false = owner->graph()->CreateBasicBlock(); |
+ if_true->MarkAsInlineReturnTarget(); |
+ if_false->MarkAsInlineReturnTarget(); |
+ // The AstContext constructor pushed on the context stack. This newed |
+ // instance is the reason that AstContext can't be BASE_EMBEDDED. |
+ test_context_ = new TestContext(owner, if_true, if_false); |
+ } else { |
+ function_return_ = owner->graph()->CreateBasicBlock(); |
+ function_return()->MarkAsInlineReturnTarget(); |
+ } |
+ // Set this after possibly allocating a new TestContext above. |
+ call_context_ = owner->ast_context(); |
+ } |
+ |
+ // Push on the state stack. |
+ owner->set_function_state(this); |
+} |
+ |
+ |
+FunctionState::~FunctionState() { |
+ delete test_context_; |
+ owner_->set_function_state(outer_); |
+} |
+ |
+ |
// Implementation of utility classes to represent an expression's context in |
// the AST. |
AstContext::AstContext(HGraphBuilder* owner, Expression::Context kind) |
@@ -2018,8 +2064,8 @@ class HGraphBuilder::SubgraphScope BASE_EMBEDDED { |
void HGraphBuilder::Bailout(const char* reason) { |
if (FLAG_trace_bailout) { |
- SmartPointer<char> debug_name = graph()->debug_name()->ToCString(); |
- PrintF("Bailout in HGraphBuilder: @\"%s\": %s\n", *debug_name, reason); |
+ SmartPointer<char> name(info()->shared_info()->DebugName()->ToCString()); |
+ PrintF("Bailout in HGraphBuilder: @\"%s\": %s\n", *name, reason); |
} |
SetStackOverflow(); |
} |
@@ -2066,16 +2112,16 @@ void HGraphBuilder::VisitExpressions(ZoneList<Expression*>* exprs) { |
} |
-HGraph* HGraphBuilder::CreateGraph(CompilationInfo* info) { |
+HGraph* HGraphBuilder::CreateGraph() { |
ASSERT(subgraph() == NULL); |
- graph_ = new HGraph(info); |
+ graph_ = new HGraph(info()); |
{ |
HPhase phase("Block building"); |
graph()->Initialize(CreateBasicBlock(graph()->start_environment())); |
current_subgraph_ = graph(); |
- Scope* scope = info->scope(); |
+ Scope* scope = info()->scope(); |
if (scope->HasIllegalRedeclaration()) { |
Bailout("function with illegal redeclaration"); |
return NULL; |
@@ -2102,9 +2148,9 @@ HGraph* HGraphBuilder::CreateGraph(CompilationInfo* info) { |
HEnvironment* initial_env = environment()->CopyWithoutHistory(); |
HBasicBlock* body_entry = CreateBasicBlock(initial_env); |
current_block()->Goto(body_entry); |
- body_entry->SetJoinId(info->function()->id()); |
+ body_entry->SetJoinId(info()->function()->id()); |
set_current_block(body_entry); |
- VisitStatements(info->function()->body()); |
+ VisitStatements(info()->function()->body()); |
if (HasStackOverflow()) return NULL; |
if (current_block() != NULL) { |
@@ -2142,7 +2188,7 @@ HGraph* HGraphBuilder::CreateGraph(CompilationInfo* info) { |
// Perform common subexpression elimination and loop-invariant code motion. |
if (FLAG_use_gvn) { |
HPhase phase("Global value numbering", graph()); |
- HGlobalValueNumberer gvn(graph()); |
+ HGlobalValueNumberer gvn(graph(), info()); |
gvn.Analyze(); |
} |
@@ -2423,7 +2469,7 @@ void HGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) { |
return_value = environment()->Pop(); |
} |
current_block()->AddLeaveInlined(return_value, |
- function_return_); |
+ function_return()); |
set_current_block(NULL); |
} |
} |
@@ -2607,13 +2653,13 @@ void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { |
} |
} |
-bool HGraph::HasOsrEntryAt(IterationStatement* statement) { |
+bool HGraphBuilder::HasOsrEntryAt(IterationStatement* statement) { |
return statement->OsrEntryId() == info()->osr_ast_id(); |
} |
void HGraphBuilder::PreProcessOsrEntry(IterationStatement* statement) { |
- if (!graph()->HasOsrEntryAt(statement)) return; |
+ if (!HasOsrEntryAt(statement)) return; |
HBasicBlock* non_osr_entry = graph()->CreateBasicBlock(); |
HBasicBlock* osr_entry = graph()->CreateBasicBlock(); |
@@ -2780,7 +2826,7 @@ void HGraphBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) { |
void HGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) { |
Handle<SharedFunctionInfo> shared_info = |
- Compiler::BuildFunctionInfo(expr, graph_->info()->script()); |
+ Compiler::BuildFunctionInfo(expr, info()->script()); |
CHECK_BAILOUT; |
HFunctionLiteral* instr = |
new HFunctionLiteral(shared_info, expr->pretenure()); |
@@ -2822,10 +2868,10 @@ void HGraphBuilder::LookupGlobalPropertyCell(Variable* var, |
if (var->is_this()) { |
BAILOUT("global this reference"); |
} |
- if (!graph()->info()->has_global_object()) { |
+ if (!info()->has_global_object()) { |
BAILOUT("no global object to optimize VariableProxy"); |
} |
- Handle<GlobalObject> global(graph()->info()->global_object()); |
+ Handle<GlobalObject> global(info()->global_object()); |
global->Lookup(*var->name(), lookup); |
if (!lookup->IsProperty()) { |
BAILOUT("global variable cell not yet introduced"); |
@@ -2846,7 +2892,7 @@ HValue* HGraphBuilder::BuildContextChainWalk(Variable* var) { |
ASSERT(var->IsContextSlot()); |
HInstruction* context = new HContext; |
AddInstruction(context); |
- int length = graph()->info()->scope()->ContextChainLength(var->scope()); |
+ int length = info()->scope()->ContextChainLength(var->scope()); |
while (length-- > 0) { |
context = new HOuterContext(context); |
AddInstruction(context); |
@@ -2877,7 +2923,7 @@ void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) { |
LookupGlobalPropertyCell(variable, &lookup, false); |
CHECK_BAILOUT; |
- Handle<GlobalObject> global(graph()->info()->global_object()); |
+ Handle<GlobalObject> global(info()->global_object()); |
// TODO(3039103): Handle global property load through an IC call when access |
// checks are enabled. |
if (global->IsAccessCheckNeeded()) { |
@@ -3147,17 +3193,20 @@ void HGraphBuilder::HandlePolymorphicStoreNamedField(Assignment* expr, |
HValue* value, |
ZoneMapList* types, |
Handle<String> name) { |
+ // 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. |
int count = 0; |
HBasicBlock* join = NULL; |
for (int i = 0; i < types->length() && count < kMaxStorePolymorphism; ++i) { |
Handle<Map> map = types->at(i); |
LookupResult lookup; |
if (ComputeStoredField(map, name, &lookup)) { |
- ++count; |
- if (join == NULL) { |
+ if (count == 0) { |
AddInstruction(new HCheckNonSmi(object)); // Only needed once. |
join = graph()->CreateBasicBlock(); |
} |
+ ++count; |
HBasicBlock* if_true = graph()->CreateBasicBlock(); |
HBasicBlock* if_false = graph()->CreateBasicBlock(); |
HCompareMap* compare = new HCompareMap(object, map, if_true, if_false); |
@@ -3176,14 +3225,20 @@ void HGraphBuilder::HandlePolymorphicStoreNamedField(Assignment* expr, |
} |
} |
- // Finish up. We need a generic IC if there were types we couldn't |
- // resolve statically or if we want to handle maps we've never seen. |
- if (count < types->length() || !FLAG_deoptimize_uncommon_cases) { |
+ // 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) { |
fschneider
2011/03/07 11:29:47
Remove extra space.
|
+ current_block()->FinishExit(new HDeoptimize); |
+ } else { |
HInstruction* instr = BuildStoreNamedGeneric(object, name, value); |
instr->set_position(expr->position()); |
AddInstruction(instr); |
- if (join == NULL) { |
+ if (join != NULL) { |
+ if (!ast_context()->IsEffect()) Push(value); |
+ current_block()->Goto(join); |
+ } else { |
// The HSimulate for the store should not see the stored value in |
// effect contexts (it is not materialized at expr->id() in the |
// unoptimized code). |
@@ -3197,22 +3252,14 @@ void HGraphBuilder::HandlePolymorphicStoreNamedField(Assignment* expr, |
} |
} |
ast_context()->ReturnValue(value); |
- } else { |
- if (!ast_context()->IsEffect()) Push(value); |
- current_block()->Goto(join); |
- join->SetJoinId(expr->id()); |
- set_current_block(join); |
- if (!ast_context()->IsEffect()) ast_context()->ReturnValue(Pop()); |
- } |
- |
- } else { |
- current_block()->FinishExit(new HDeoptimize); |
- set_current_block(join); |
- if (join != NULL) { |
- join->SetJoinId(expr->id()); |
- if (!ast_context()->IsEffect()) ast_context()->ReturnValue(Pop()); |
+ return; |
} |
} |
+ |
+ ASSERT(join != NULL); |
+ join->SetJoinId(expr->id()); |
+ set_current_block(join); |
+ if (!ast_context()->IsEffect()) ast_context()->ReturnValue(Pop()); |
} |
@@ -3293,7 +3340,7 @@ void HGraphBuilder::HandleGlobalVariableAssignment(Variable* var, |
CHECK_BAILOUT; |
bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly(); |
- Handle<GlobalObject> global(graph()->info()->global_object()); |
+ Handle<GlobalObject> global(info()->global_object()); |
Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup)); |
HInstruction* instr = new HStoreGlobal(value, cell, check_hole); |
instr->set_position(position); |
@@ -3491,64 +3538,62 @@ void HGraphBuilder::HandlePolymorphicLoadNamedField(Property* expr, |
HValue* object, |
ZoneMapList* types, |
Handle<String> name) { |
- int number_of_types = Min(types->length(), kMaxLoadPolymorphism); |
- ZoneMapList maps(number_of_types); |
- ZoneList<HSubgraph*> subgraphs(number_of_types); |
- bool needs_generic = (types->length() > kMaxLoadPolymorphism); |
- |
- // 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) { |
+ // 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. |
+ int count = 0; |
+ HBasicBlock* join = NULL; |
+ for (int i = 0; i < types->length() && count < kMaxLoadPolymorphism; ++i) { |
Handle<Map> map = types->at(i); |
LookupResult lookup; |
map->LookupInDescriptors(NULL, *name, &lookup); |
if (lookup.IsProperty() && lookup.type() == FIELD) { |
- HSubgraph* subgraph = CreateBranchSubgraph(environment()); |
- SubgraphScope scope(this, subgraph); |
+ if (count == 0) { |
+ AddInstruction(new HCheckNonSmi(object)); // Only needed once. |
+ join = graph()->CreateBasicBlock(); |
+ } |
+ ++count; |
+ HBasicBlock* if_true = graph()->CreateBasicBlock(); |
+ HBasicBlock* if_false = graph()->CreateBasicBlock(); |
+ HCompareMap* compare = new HCompareMap(object, map, if_true, if_false); |
+ current_block()->Finish(compare); |
+ |
+ set_current_block(if_true); |
HLoadNamedField* instr = |
BuildLoadNamedField(object, expr, map, &lookup, false); |
instr->set_position(expr->position()); |
- instr->ClearFlag(HValue::kUseGVN); // Don't do GVN on polymorphic loads. |
- PushAndAdd(instr); |
- maps.Add(map); |
- subgraphs.Add(subgraph); |
- } else { |
- needs_generic = true; |
- } |
+ instr->ClearFlag(HValue::kUseGVN); |
+ AddInstruction(instr); |
+ if (!ast_context()->IsEffect()) Push(instr); |
+ current_block()->Goto(join); |
+ |
+ set_current_block(if_false); |
+ } |
fschneider
2011/03/07 11:29:47
Remove extra space.
Kevin Millikin (Chromium)
2011/03/07 11:51:52
Thanks.
|
} |
- // If none of the properties were named fields we generate a |
- // generic load. |
- 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 { |
HInstruction* instr = BuildLoadNamedGeneric(object, expr); |
instr->set_position(expr->position()); |
- ast_context()->ReturnInstruction(instr, expr->id()); |
- } else { |
- // Build subgraph for generic load 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 { |
- HInstruction* instr = BuildLoadNamedGeneric(object, expr); |
- instr->set_position(expr->position()); |
- PushAndAdd(instr); |
- } |
- } |
- HBasicBlock* new_exit_block = |
- BuildTypeSwitch(object, &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 (current_block() != NULL && !ast_context()->IsEffect()) { |
- ast_context()->ReturnValue(Pop()); |
+ if (join != NULL) { |
+ AddInstruction(instr); |
+ if (!ast_context()->IsEffect()) Push(instr); |
+ current_block()->Goto(join); |
+ } else { |
+ ast_context()->ReturnInstruction(instr, expr->id()); |
+ return; |
} |
} |
+ |
+ ASSERT(join != NULL); |
+ join->SetJoinId(expr->id()); |
+ set_current_block(join); |
+ if (!ast_context()->IsEffect()) ast_context()->ReturnValue(Pop()); |
} |
@@ -3916,14 +3961,17 @@ void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr, |
} |
-void HGraphBuilder::TraceInline(Handle<JSFunction> target, bool result) { |
- SmartPointer<char> callee = target->shared()->DebugName()->ToCString(); |
- SmartPointer<char> caller = |
- graph()->info()->function()->debug_name()->ToCString(); |
- if (result) { |
- PrintF("Inlined %s called from %s.\n", *callee, *caller); |
- } else { |
- PrintF("Do not inline %s called from %s.\n", *callee, *caller); |
+void HGraphBuilder::TraceInline(Handle<JSFunction> target, const char* reason) { |
+ if (FLAG_trace_inlining) { |
+ SmartPointer<char> callee = target->shared()->DebugName()->ToCString(); |
+ SmartPointer<char> caller = |
+ info()->function()->debug_name()->ToCString(); |
+ if (reason == NULL) { |
+ PrintF("Inlined %s called from %s.\n", *callee, *caller); |
+ } else { |
+ PrintF("Did not inline %s called from %s (%s).\n", |
+ *callee, *caller, reason); |
+ } |
} |
} |
@@ -3938,123 +3986,122 @@ bool HGraphBuilder::TryInline(Call* expr) { |
// Do a quick check on source code length to avoid parsing large |
// inlining candidates. |
if (FLAG_limit_inlining && target->shared()->SourceSize() > kMaxSourceSize) { |
- if (FLAG_trace_inlining) TraceInline(target, false); |
+ TraceInline(target, "target text too big"); |
return false; |
} |
// Target must be inlineable. |
- if (!target->IsInlineable()) return false; |
+ if (!target->IsInlineable()) { |
+ TraceInline(target, "target not inlineable"); |
+ return false; |
+ } |
// No context change required. |
- CompilationInfo* outer_info = graph()->info(); |
+ CompilationInfo* outer_info = info(); |
if (target->context() != outer_info->closure()->context() || |
outer_info->scope()->contains_with() || |
outer_info->scope()->num_heap_slots() > 0) { |
+ TraceInline(target, "target requires context change"); |
return false; |
} |
// Don't inline deeper than two calls. |
HEnvironment* env = environment(); |
- if (env->outer() != NULL && env->outer()->outer() != NULL) return false; |
+ if (env->outer() != NULL && env->outer()->outer() != NULL) { |
+ TraceInline(target, "inline depth limit reached"); |
+ return false; |
+ } |
// Don't inline recursive functions. |
- if (target->shared() == outer_info->closure()->shared()) return false; |
+ if (target->shared() == outer_info->closure()->shared()) { |
+ TraceInline(target, "target is recursive"); |
+ return false; |
+ } |
// We don't want to add more than a certain number of nodes from inlining. |
if (FLAG_limit_inlining && inlined_count_ > kMaxInlinedNodes) { |
- if (FLAG_trace_inlining) TraceInline(target, false); |
+ TraceInline(target, "cumulative AST node limit reached"); |
return false; |
} |
int count_before = AstNode::Count(); |
// Parse and allocate variables. |
- CompilationInfo inner_info(target); |
- if (!ParserApi::Parse(&inner_info) || |
- !Scope::Analyze(&inner_info)) { |
+ CompilationInfo target_info(target); |
+ if (!ParserApi::Parse(&target_info) || |
+ !Scope::Analyze(&target_info)) { |
if (Top::has_pending_exception()) { |
+ // Parse or scope error, never optimize this function. |
SetStackOverflow(); |
- // Stop trying to optimize and inline this function. |
target->shared()->set_optimization_disabled(true); |
} |
+ TraceInline(target, "parse failure"); |
+ return false; |
+ } |
+ |
+ if (target_info.scope()->num_heap_slots() > 0) { |
+ TraceInline(target, "target has context-allocated variables"); |
return false; |
} |
- if (inner_info.scope()->num_heap_slots() > 0) return false; |
- FunctionLiteral* function = inner_info.function(); |
+ FunctionLiteral* function = target_info.function(); |
// Count the number of AST nodes added by inlining this call. |
int nodes_added = AstNode::Count() - count_before; |
if (FLAG_limit_inlining && nodes_added > kMaxInlinedSize) { |
- if (FLAG_trace_inlining) TraceInline(target, false); |
+ TraceInline(target, "target AST is too large"); |
return false; |
} |
// Check if we can handle all declarations in the inlined functions. |
- VisitDeclarations(inner_info.scope()->declarations()); |
+ VisitDeclarations(target_info.scope()->declarations()); |
if (HasStackOverflow()) { |
+ TraceInline(target, "target has non-trivial declaration"); |
ClearStackOverflow(); |
return false; |
} |
// Don't inline functions that uses the arguments object or that |
// have a mismatching number of parameters. |
- Handle<SharedFunctionInfo> shared(target->shared()); |
+ Handle<SharedFunctionInfo> target_shared(target->shared()); |
int arity = expr->arguments()->length(); |
if (function->scope()->arguments() != NULL || |
- arity != shared->formal_parameter_count()) { |
+ arity != target_shared->formal_parameter_count()) { |
+ TraceInline(target, "target requires special argument handling"); |
return false; |
} |
// All statements in the body must be inlineable. |
for (int i = 0, count = function->body()->length(); i < count; ++i) { |
- if (!function->body()->at(i)->IsInlineable()) return false; |
+ if (!function->body()->at(i)->IsInlineable()) { |
+ TraceInline(target, "target contains unsupported syntax"); |
+ return false; |
+ } |
} |
// Generate the deoptimization data for the unoptimized version of |
// the target function if we don't already have it. |
- if (!shared->has_deoptimization_support()) { |
+ if (!target_shared->has_deoptimization_support()) { |
// Note that we compile here using the same AST that we will use for |
// generating the optimized inline code. |
- inner_info.EnableDeoptimizationSupport(); |
- if (!FullCodeGenerator::MakeCode(&inner_info)) return false; |
- shared->EnableDeoptimizationSupport(*inner_info.code()); |
- Compiler::RecordFunctionCompilation( |
- Logger::FUNCTION_TAG, &inner_info, shared); |
+ target_info.EnableDeoptimizationSupport(); |
+ if (!FullCodeGenerator::MakeCode(&target_info)) { |
+ TraceInline(target, "could not generate deoptimization info"); |
+ return false; |
+ } |
+ target_shared->EnableDeoptimizationSupport(*target_info.code()); |
+ Compiler::RecordFunctionCompilation(Logger::FUNCTION_TAG, |
+ &target_info, |
+ target_shared); |
} |
+ // ---------------------------------------------------------------- |
// Save the pending call context and type feedback oracle. Set up new ones |
// for the inlined function. |
- ASSERT(shared->has_deoptimization_support()); |
- AstContext* saved_call_context = call_context(); |
- HBasicBlock* saved_function_return = function_return(); |
- TypeFeedbackOracle* saved_oracle = oracle(); |
- // On-stack replacement cannot target inlined functions. Since we don't |
- // use a separate CompilationInfo structure for the inlined function, we |
- // save and restore the AST ID in the original compilation info. |
- int saved_osr_ast_id = graph()->info()->osr_ast_id(); |
- |
- TestContext* test_context = NULL; |
- if (ast_context()->IsTest()) { |
- // Inlined body is treated as if it occurs in an 'inlined' call context |
- // with true and false blocks that will forward to the real ones. |
- HBasicBlock* if_true = graph()->CreateBasicBlock(); |
- HBasicBlock* if_false = graph()->CreateBasicBlock(); |
- if_true->MarkAsInlineReturnTarget(); |
- if_false->MarkAsInlineReturnTarget(); |
- // AstContext constructor pushes on the context stack. |
- test_context = new TestContext(this, if_true, if_false); |
- function_return_ = NULL; |
- } else { |
- // Inlined body is treated as if it occurs in the original call context. |
- function_return_ = graph()->CreateBasicBlock(); |
- function_return_->MarkAsInlineReturnTarget(); |
- } |
- call_context_ = ast_context(); |
- TypeFeedbackOracle new_oracle( |
- Handle<Code>(shared->code()), |
+ ASSERT(target_shared->has_deoptimization_support()); |
+ TypeFeedbackOracle target_oracle( |
+ Handle<Code>(target_shared->code()), |
Handle<Context>(target->context()->global_context())); |
- oracle_ = &new_oracle; |
- graph()->info()->SetOsrAstId(AstNode::kNoNumber); |
+ FunctionState target_state(this, &target_info, &target_oracle); |
HSubgraph* body = CreateInlinedSubgraph(env, target, function); |
body->exit_block()->AddInstruction(new HEnterInlined(target, function)); |
@@ -4062,26 +4109,22 @@ bool HGraphBuilder::TryInline(Call* expr) { |
if (HasStackOverflow()) { |
// Bail out if the inline function did, as we cannot residualize a call |
// instead. |
- delete test_context; |
- call_context_ = saved_call_context; |
- function_return_ = saved_function_return; |
- oracle_ = saved_oracle; |
- graph()->info()->SetOsrAstId(saved_osr_ast_id); |
+ TraceInline(target, "inline graph construction failed"); |
return false; |
} |
// Update inlined nodes count. |
inlined_count_ += nodes_added; |
- if (FLAG_trace_inlining) TraceInline(target, true); |
+ TraceInline(target, NULL); |
if (body->exit_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 (test_context == NULL) { |
- ASSERT(function_return_ != NULL); |
- body->exit_block()->AddLeaveInlined(return_value, function_return_); |
+ if (inlined_test_context() == NULL) { |
+ ASSERT(function_return() != NULL); |
+ body->exit_block()->AddLeaveInlined(return_value, 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 |
@@ -4094,8 +4137,10 @@ bool HGraphBuilder::TryInline(Call* expr) { |
body->exit_block()->Finish(test); |
HValue* const no_return_value = NULL; |
- empty_true->AddLeaveInlined(no_return_value, test_context->if_true()); |
- empty_false->AddLeaveInlined(no_return_value, test_context->if_false()); |
+ empty_true->AddLeaveInlined(no_return_value, |
+ inlined_test_context()->if_true()); |
+ empty_false->AddLeaveInlined(no_return_value, |
+ inlined_test_context()->if_false()); |
} |
body->set_exit_block(NULL); |
} |
@@ -4107,13 +4152,14 @@ bool HGraphBuilder::TryInline(Call* expr) { |
current_block()->Finish(new HGoto(body->entry_block())); |
// Fix up the function exits. |
- if (test_context != NULL) { |
- HBasicBlock* if_true = test_context->if_true(); |
- HBasicBlock* if_false = test_context->if_false(); |
+ if (inlined_test_context() != NULL) { |
+ HBasicBlock* if_true = inlined_test_context()->if_true(); |
+ HBasicBlock* if_false = inlined_test_context()->if_false(); |
if_true->SetJoinId(expr->id()); |
if_false->SetJoinId(expr->id()); |
- ASSERT(ast_context() == test_context); |
- delete test_context; // Destructor pops from expression context stack. |
+ ASSERT(ast_context() == inlined_test_context()); |
+ // Pop the return test context from the expression context stack. |
+ ClearInlinedTestContext(); |
// Forward to the real test context. |
HValue* const no_return_value = NULL; |
@@ -4137,15 +4183,10 @@ bool HGraphBuilder::TryInline(Call* expr) { |
set_current_block(NULL); |
} else { |
- function_return_->SetJoinId(expr->id()); |
- set_current_block(function_return_); |
+ function_return()->SetJoinId(expr->id()); |
+ set_current_block(function_return()); |
} |
- call_context_ = saved_call_context; |
- function_return_ = saved_function_return; |
- oracle_ = saved_oracle; |
- graph()->info()->SetOsrAstId(saved_osr_ast_id); |
- |
return true; |
} |
@@ -4253,7 +4294,7 @@ bool HGraphBuilder::TryCallApply(Call* expr) { |
Property* prop = callee->AsProperty(); |
ASSERT(prop != NULL); |
- if (graph()->info()->scope()->arguments() == NULL) return false; |
+ if (info()->scope()->arguments() == NULL) return false; |
Handle<String> name = prop->key()->AsLiteral()->AsPropertyName(); |
if (!name->IsEqualTo(CStrVector("apply"))) return false; |
@@ -4409,10 +4450,9 @@ void HGraphBuilder::VisitCall(Call* expr) { |
// If there is a global property cell for the name at compile time and |
// access check is not enabled we assume that the function will not change |
// and generate optimized code for calling the function. |
- CompilationInfo* info = graph()->info(); |
- bool known_global_function = info->has_global_object() && |
- !info->global_object()->IsAccessCheckNeeded() && |
- expr->ComputeGlobalTarget(Handle<GlobalObject>(info->global_object()), |
+ bool known_global_function = info()->has_global_object() && |
+ !info()->global_object()->IsAccessCheckNeeded() && |
+ expr->ComputeGlobalTarget(Handle<GlobalObject>(info()->global_object()), |
var->name()); |
if (known_global_function) { |
// Push the global object instead of the global receiver because |
@@ -5042,7 +5082,7 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { |
HValue* left = Pop(); |
Token::Value op = expr->op(); |
- TypeInfo info = oracle()->CompareType(expr); |
+ TypeInfo type_info = oracle()->CompareType(expr); |
HInstruction* instr = NULL; |
if (op == Token::INSTANCEOF) { |
// Check to see if the rhs of the instanceof is a global function not |
@@ -5051,12 +5091,11 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { |
Handle<JSFunction> target = Handle<JSFunction>::null(); |
Variable* var = expr->right()->AsVariableProxy()->AsVariable(); |
bool global_function = (var != NULL) && var->is_global() && !var->is_this(); |
- CompilationInfo* info = graph()->info(); |
if (global_function && |
- info->has_global_object() && |
- !info->global_object()->IsAccessCheckNeeded()) { |
+ info()->has_global_object() && |
+ !info()->global_object()->IsAccessCheckNeeded()) { |
Handle<String> name = var->name(); |
- Handle<GlobalObject> global(info->global_object()); |
+ Handle<GlobalObject> global(info()->global_object()); |
LookupResult lookup; |
global->Lookup(*name, &lookup); |
if (lookup.IsProperty() && |
@@ -5083,7 +5122,7 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { |
} |
} else if (op == Token::IN) { |
BAILOUT("Unsupported comparison: in"); |
- } else if (info.IsNonPrimitive()) { |
+ } else if (type_info.IsNonPrimitive()) { |
switch (op) { |
case Token::EQ: |
case Token::EQ_STRICT: { |
@@ -5100,7 +5139,7 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { |
} |
} else { |
HCompare* compare = new HCompare(left, right, op); |
- Representation r = ToRepresentation(info); |
+ Representation r = ToRepresentation(type_info); |
compare->SetInputRepresentation(r); |
instr = compare; |
} |