Index: runtime/vm/compiler.cc |
diff --git a/runtime/vm/compiler.cc b/runtime/vm/compiler.cc |
index 8d3b319d5717492eb6450219d041f96c1e0311df..610b7b7e3774e01802eda05931e9e453c19fed2b 100644 |
--- a/runtime/vm/compiler.cc |
+++ b/runtime/vm/compiler.cc |
@@ -55,6 +55,7 @@ DEFINE_FLAG(int, max_deoptimization_counter_threshold, 16, |
"How many times we allow deoptimization before we disallow optimization."); |
DEFINE_FLAG(bool, loop_invariant_code_motion, true, |
"Do loop invariant code motion."); |
+DEFINE_FLAG(charp, optimization_filter, NULL, "Optimize only named function"); |
DEFINE_FLAG(bool, print_flow_graph, false, "Print the IR flow graph."); |
DEFINE_FLAG(bool, print_flow_graph_optimized, false, |
"Print the IR flow graph when optimizing."); |
@@ -62,7 +63,13 @@ DEFINE_FLAG(bool, print_ic_data_map, false, |
"Print the deopt-id to ICData map in optimizing compiler."); |
DEFINE_FLAG(bool, print_code_source_map, false, "Print code source map."); |
DEFINE_FLAG(bool, range_analysis, true, "Enable range analysis"); |
+DEFINE_FLAG(bool, stress_test_background_compilation, false, |
+ "Keep background compiler running all the time"); |
+DEFINE_FLAG(bool, stop_on_excessive_deoptimization, false, |
+ "Debugging: stops program if deoptimizing same function too often"); |
DEFINE_FLAG(bool, trace_compiler, false, "Trace compiler operations."); |
+DEFINE_FLAG(bool, trace_failed_optimization_attempts, false, |
+ "Traces all failed optimization attempts"); |
DEFINE_FLAG(bool, trace_optimizing_compiler, false, |
"Trace only optimizing compiler operations."); |
DEFINE_FLAG(bool, trace_bailout, false, "Print bailout from ssa compiler."); |
@@ -163,6 +170,69 @@ DEFINE_RUNTIME_ENTRY(CompileFunction, 1) { |
} |
+bool Compiler::CanOptimizeFunction(Thread* thread, const Function& function) { |
+ if (FLAG_support_debugger) { |
+ Isolate* isolate = thread->isolate(); |
+ if (isolate->debugger()->IsStepping() || |
+ isolate->debugger()->HasBreakpoint(function, thread->zone())) { |
+ // We cannot set breakpoints and single step in optimized code, |
+ // so do not optimize the function. |
+ function.set_usage_counter(0); |
+ return false; |
+ } |
+ } |
+ if (function.deoptimization_counter() >= |
+ FLAG_max_deoptimization_counter_threshold) { |
+ if (FLAG_trace_failed_optimization_attempts || |
+ FLAG_stop_on_excessive_deoptimization) { |
+ THR_Print("Too many deoptimizations: %s\n", |
+ function.ToFullyQualifiedCString()); |
+ if (FLAG_stop_on_excessive_deoptimization) { |
+ FATAL("Stop on excessive deoptimization"); |
+ } |
+ } |
+ // The function will not be optimized any longer. This situation can occur |
+ // mostly with small optimization counter thresholds. |
+ function.SetIsOptimizable(false); |
+ function.set_usage_counter(INT_MIN); |
+ return false; |
+ } |
+ if (FLAG_optimization_filter != NULL) { |
+ // FLAG_optimization_filter is a comma-separated list of strings that are |
+ // matched against the fully-qualified function name. |
+ char* save_ptr; // Needed for strtok_r. |
+ const char* function_name = function.ToFullyQualifiedCString(); |
+ intptr_t len = strlen(FLAG_optimization_filter) + 1; // Length with \0. |
+ char* filter = new char[len]; |
+ strncpy(filter, FLAG_optimization_filter, len); // strtok modifies arg 1. |
+ char* token = strtok_r(filter, ",", &save_ptr); |
+ bool found = false; |
+ while (token != NULL) { |
+ if (strstr(function_name, token) != NULL) { |
+ found = true; |
+ break; |
+ } |
+ token = strtok_r(NULL, ",", &save_ptr); |
+ } |
+ delete[] filter; |
+ if (!found) { |
+ function.set_usage_counter(INT_MIN); |
+ return false; |
+ } |
+ } |
+ if (!function.IsOptimizable()) { |
+ // Huge methods (code size above --huge_method_cutoff_in_code_size) become |
+ // non-optimizable only after the code has been generated. |
+ if (FLAG_trace_failed_optimization_attempts) { |
+ THR_Print("Not optimizable: %s\n", function.ToFullyQualifiedCString()); |
+ } |
+ function.set_usage_counter(INT_MIN); |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+ |
bool Compiler::IsBackgroundCompilation() { |
// For now: compilation in non mutator thread is the background compoilation. |
return !Thread::Current()->IsMutatorThread(); |
@@ -542,7 +612,8 @@ NOT_IN_PRODUCT( |
THR_Print("--> FAIL: Loading invalidation."); |
} |
} |
- if (code_is_valid) { |
+ // Setting breakpoints at runtime could make a function non-optimizable. |
+ if (code_is_valid && Compiler::CanOptimizeFunction(thread(), function)) { |
const bool is_osr = osr_id() != Compiler::kNoOSRDeoptId; |
ASSERT(!is_osr); // OSR is not compiled in background. |
function.InstallOptimizedCode(code, is_osr); |
@@ -1611,7 +1682,6 @@ class QueueElement { |
explicit QueueElement(const Function& function) |
: next_(NULL), |
function_(function.raw()) { |
- ASSERT(Thread::Current()->IsMutatorThread()); |
} |
virtual ~QueueElement() { |
@@ -1784,6 +1854,13 @@ void BackgroundCompiler::Run() { |
function = Function::null(); |
} else { |
qelem = function_queue()->Remove(); |
+ if (FLAG_stress_test_background_compilation) { |
+ const Function& old = Function::Handle(qelem->Function()); |
+ if (Compiler::CanOptimizeFunction(thread, old)) { |
+ QueueElement* repeat_qelem = new QueueElement(old); |
+ function_queue()->Add(repeat_qelem); |
+ } |
+ } |
function = function_queue()->PeekFunction(); |
} |
} |
@@ -1925,6 +2002,12 @@ bool Compiler::IsBackgroundCompilation() { |
} |
+bool Compiler::CanOptimizeFunction(Thread* thread, const Function& function) { |
+ UNREACHABLE(); |
+ return false; |
+} |
+ |
+ |
RawError* Compiler::Compile(const Library& library, const Script& script) { |
UNREACHABLE(); |
return Error::null(); |