When inlining fails, disable optimization of the proper function.
Also, refactor disabling of optimization to make it easier to ensure that both SharedFunctionInfo and Code get disabled. R=whesse@chromium.org BUG= TEST= Review URL: http://codereview.chromium.org/7033020 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7963 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
742a001c00
commit
47157e8a56
@ -94,6 +94,7 @@ CompilationInfo::CompilationInfo(Handle<JSFunction> closure)
|
||||
}
|
||||
|
||||
|
||||
// Disable optimization for the rest of the compilation pipeline.
|
||||
void CompilationInfo::DisableOptimization() {
|
||||
bool is_optimizable_closure =
|
||||
FLAG_optimize_closures &&
|
||||
@ -105,6 +106,14 @@ void CompilationInfo::DisableOptimization() {
|
||||
}
|
||||
|
||||
|
||||
void CompilationInfo::AbortOptimization() {
|
||||
Handle<Code> code(shared_info()->code());
|
||||
SetCode(code);
|
||||
Isolate* isolate = code->GetIsolate();
|
||||
isolate->compilation_cache()->MarkForLazyOptimizing(closure());
|
||||
}
|
||||
|
||||
|
||||
// Determine whether to use the full compiler for all code. If the flag
|
||||
// --always-full-compiler is specified this is the case. For the virtual frame
|
||||
// based compiler the full compiler is also used if a debugger is connected, as
|
||||
@ -156,31 +165,6 @@ static void FinishOptimization(Handle<JSFunction> function, int64_t start) {
|
||||
}
|
||||
|
||||
|
||||
static void AbortAndDisable(CompilationInfo* info) {
|
||||
// Disable optimization for the shared function info and mark the
|
||||
// code as non-optimizable. The marker on the shared function info
|
||||
// is there because we flush non-optimized code thereby loosing the
|
||||
// non-optimizable information for the code. When the code is
|
||||
// regenerated and set on the shared function info it is marked as
|
||||
// non-optimizable if optimization is disabled for the shared
|
||||
// function info.
|
||||
Handle<SharedFunctionInfo> shared = info->shared_info();
|
||||
shared->set_optimization_disabled(true);
|
||||
Handle<Code> code = Handle<Code>(shared->code());
|
||||
ASSERT(code->kind() == Code::FUNCTION);
|
||||
code->set_optimizable(false);
|
||||
info->SetCode(code);
|
||||
Isolate* isolate = code->GetIsolate();
|
||||
isolate->compilation_cache()->MarkForLazyOptimizing(info->closure());
|
||||
if (FLAG_trace_opt) {
|
||||
PrintF("[disabled optimization for: ");
|
||||
info->closure()->PrintName();
|
||||
PrintF(" / %" V8PRIxPTR "]\n",
|
||||
reinterpret_cast<intptr_t>(*info->closure()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool MakeCrankshaftCode(CompilationInfo* info) {
|
||||
// Test if we can optimize this function when asked to. We can only
|
||||
// do this after the scopes are computed.
|
||||
@ -214,7 +198,9 @@ static bool MakeCrankshaftCode(CompilationInfo* info) {
|
||||
const int kMaxOptCount =
|
||||
FLAG_deopt_every_n_times == 0 ? Compiler::kDefaultMaxOptCount : 1000;
|
||||
if (info->shared_info()->opt_count() > kMaxOptCount) {
|
||||
AbortAndDisable(info);
|
||||
info->AbortOptimization();
|
||||
Handle<JSFunction> closure = info->closure();
|
||||
info->shared_info()->DisableOptimization(*closure);
|
||||
// True indicates the compilation pipeline is still going, not
|
||||
// necessarily that we optimized the code.
|
||||
return true;
|
||||
@ -231,7 +217,9 @@ static bool MakeCrankshaftCode(CompilationInfo* info) {
|
||||
Scope* scope = info->scope();
|
||||
if ((scope->num_parameters() + 1) > limit ||
|
||||
scope->num_stack_slots() > limit) {
|
||||
AbortAndDisable(info);
|
||||
info->AbortOptimization();
|
||||
Handle<JSFunction> closure = info->closure();
|
||||
info->shared_info()->DisableOptimization(*closure);
|
||||
// True indicates the compilation pipeline is still going, not
|
||||
// necessarily that we optimized the code.
|
||||
return true;
|
||||
@ -304,9 +292,14 @@ static bool MakeCrankshaftCode(CompilationInfo* info) {
|
||||
}
|
||||
}
|
||||
|
||||
// Compilation with the Hydrogen compiler failed. Keep using the
|
||||
// shared code but mark it as unoptimizable.
|
||||
AbortAndDisable(info);
|
||||
// Keep using the shared code.
|
||||
info->AbortOptimization();
|
||||
if (!builder.inline_bailout()) {
|
||||
// Mark the shared code as unoptimizable unless it was an inlined
|
||||
// function that bailed out.
|
||||
Handle<JSFunction> closure = info->closure();
|
||||
info->shared_info()->DisableOptimization(*closure);
|
||||
}
|
||||
// True indicates the compilation pipeline is still going, not necessarily
|
||||
// that we optimized the code.
|
||||
return true;
|
||||
|
@ -144,6 +144,10 @@ class CompilationInfo BASE_EMBEDDED {
|
||||
return V8::UseCrankshaft() && !closure_.is_null();
|
||||
}
|
||||
|
||||
// Disable all optimization attempts of this info for the rest of the
|
||||
// current compilation pipeline.
|
||||
void AbortOptimization();
|
||||
|
||||
private:
|
||||
Isolate* isolate_;
|
||||
|
||||
|
@ -521,6 +521,22 @@ HConstant* HGraph::GetConstantFalse() {
|
||||
return GetConstant(&constant_false_, isolate()->heap()->false_value());
|
||||
}
|
||||
|
||||
HGraphBuilder::HGraphBuilder(CompilationInfo* info,
|
||||
TypeFeedbackOracle* oracle)
|
||||
: function_state_(NULL),
|
||||
initial_function_state_(this, info, oracle),
|
||||
ast_context_(NULL),
|
||||
break_scope_(NULL),
|
||||
graph_(NULL),
|
||||
current_block_(NULL),
|
||||
inlined_count_(0),
|
||||
zone_(info->isolate()->zone()),
|
||||
inline_bailout_(false) {
|
||||
// This is not initialized in the initializer list because the
|
||||
// constructor for the initial state relies on function_state_ == NULL
|
||||
// to know it's the initial state.
|
||||
function_state_= &initial_function_state_;
|
||||
}
|
||||
|
||||
HBasicBlock* HGraphBuilder::CreateJoin(HBasicBlock* first,
|
||||
HBasicBlock* second,
|
||||
@ -4040,8 +4056,9 @@ bool HGraphBuilder::TryInline(Call* expr) {
|
||||
|
||||
// Precondition: call is monomorphic and we have found a target with the
|
||||
// appropriate arity.
|
||||
Handle<JSFunction> target = expr->target();
|
||||
Handle<JSFunction> caller = info()->closure();
|
||||
Handle<JSFunction> target = expr->target();
|
||||
Handle<SharedFunctionInfo> target_shared(target->shared());
|
||||
|
||||
// Do a quick check on source code length to avoid parsing large
|
||||
// inlining candidates.
|
||||
@ -4078,7 +4095,7 @@ bool HGraphBuilder::TryInline(Call* expr) {
|
||||
}
|
||||
|
||||
// Don't inline recursive functions.
|
||||
if (target->shared() == outer_info->closure()->shared()) {
|
||||
if (*target_shared == outer_info->closure()->shared()) {
|
||||
TraceInline(target, caller, "target is recursive");
|
||||
return false;
|
||||
}
|
||||
@ -4098,7 +4115,7 @@ bool HGraphBuilder::TryInline(Call* expr) {
|
||||
if (target_info.isolate()->has_pending_exception()) {
|
||||
// Parse or scope error, never optimize this function.
|
||||
SetStackOverflow();
|
||||
target->shared()->set_optimization_disabled(true);
|
||||
target_shared->DisableOptimization(*target);
|
||||
}
|
||||
TraceInline(target, caller, "parse failure");
|
||||
return false;
|
||||
@ -4127,7 +4144,6 @@ bool HGraphBuilder::TryInline(Call* expr) {
|
||||
|
||||
// Don't inline functions that uses the arguments object or that
|
||||
// have a mismatching number of parameters.
|
||||
Handle<SharedFunctionInfo> target_shared(target->shared());
|
||||
int arity = expr->arguments()->length();
|
||||
if (function->scope()->arguments() != NULL ||
|
||||
arity != target_shared->formal_parameter_count()) {
|
||||
@ -4185,6 +4201,8 @@ bool HGraphBuilder::TryInline(Call* expr) {
|
||||
// Bail out if the inline function did, as we cannot residualize a call
|
||||
// instead.
|
||||
TraceInline(target, caller, "inline graph construction failed");
|
||||
target_shared->DisableOptimization(*target);
|
||||
inline_bailout_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -660,20 +660,7 @@ class HGraphBuilder: public AstVisitor {
|
||||
BreakAndContinueScope* next_;
|
||||
};
|
||||
|
||||
HGraphBuilder(CompilationInfo* info, TypeFeedbackOracle* oracle)
|
||||
: function_state_(NULL),
|
||||
initial_function_state_(this, info, oracle),
|
||||
ast_context_(NULL),
|
||||
break_scope_(NULL),
|
||||
graph_(NULL),
|
||||
current_block_(NULL),
|
||||
inlined_count_(0),
|
||||
zone_(info->isolate()->zone()) {
|
||||
// This is not initialized in the initializer list because the
|
||||
// constructor for the initial state relies on function_state_ == NULL
|
||||
// to know it's the initial state.
|
||||
function_state_= &initial_function_state_;
|
||||
}
|
||||
HGraphBuilder(CompilationInfo* info, TypeFeedbackOracle* oracle);
|
||||
|
||||
HGraph* CreateGraph();
|
||||
|
||||
@ -688,6 +675,8 @@ class HGraphBuilder: public AstVisitor {
|
||||
return current_block()->last_environment();
|
||||
}
|
||||
|
||||
bool inline_bailout() { return inline_bailout_; }
|
||||
|
||||
// Adding instructions.
|
||||
HInstruction* AddInstruction(HInstruction* instr);
|
||||
void AddSimulate(int id);
|
||||
@ -976,6 +965,8 @@ class HGraphBuilder: public AstVisitor {
|
||||
|
||||
Zone* zone_;
|
||||
|
||||
bool inline_bailout_;
|
||||
|
||||
friend class FunctionState; // Pushes and pops the state stack.
|
||||
friend class AstContext; // Pushes and pops the AST context stack.
|
||||
|
||||
|
@ -6096,6 +6096,29 @@ void SharedFunctionInfo::EnableDeoptimizationSupport(Code* recompiled) {
|
||||
}
|
||||
|
||||
|
||||
void SharedFunctionInfo::DisableOptimization(JSFunction* function) {
|
||||
// Disable optimization for the shared function info and mark the
|
||||
// code as non-optimizable. The marker on the shared function info
|
||||
// is there because we flush non-optimized code thereby loosing the
|
||||
// non-optimizable information for the code. When the code is
|
||||
// regenerated and set on the shared function info it is marked as
|
||||
// non-optimizable if optimization is disabled for the shared
|
||||
// function info.
|
||||
set_optimization_disabled(true);
|
||||
// Code should be the lazy compilation stub or else unoptimized. If the
|
||||
// latter, disable optimization for the code too.
|
||||
ASSERT(code()->kind() == Code::FUNCTION || code()->kind() == Code::BUILTIN);
|
||||
if (code()->kind() == Code::FUNCTION) {
|
||||
code()->set_optimizable(false);
|
||||
}
|
||||
if (FLAG_trace_opt) {
|
||||
PrintF("[disabled optimization for: ");
|
||||
function->PrintName();
|
||||
PrintF(" / %" V8PRIxPTR "]\n", reinterpret_cast<intptr_t>(function));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool SharedFunctionInfo::VerifyBailoutId(int id) {
|
||||
// TODO(srdjan): debugging ARM crashes in hydrogen. OK to disable while
|
||||
// we are always bailing out on ARM.
|
||||
|
@ -4368,6 +4368,11 @@ class SharedFunctionInfo: public HeapObject {
|
||||
// Enable deoptimization support through recompiled code.
|
||||
void EnableDeoptimizationSupport(Code* recompiled);
|
||||
|
||||
// Disable (further) attempted optimization of all functions sharing this
|
||||
// shared function info. The function is the one we actually tried to
|
||||
// optimize.
|
||||
void DisableOptimization(JSFunction* function);
|
||||
|
||||
// Lookup the bailout ID and ASSERT that it exists in the non-optimized
|
||||
// code, returns whether it asserted (i.e., always true if assertions are
|
||||
// disabled).
|
||||
|
Loading…
Reference in New Issue
Block a user