Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(320)

Unified Diff: runtime/vm/precompiler.cc

Issue 1663163003: Initial split of precompilation code from compiler.cc (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: rebased Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « runtime/vm/precompiler.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: runtime/vm/precompiler.cc
diff --git a/runtime/vm/precompiler.cc b/runtime/vm/precompiler.cc
index b386e48598e7f1970d3405a34bd434b62ea4b360..ebaa9e2b2b2667080ac463bf48b971924c61e986 100644
--- a/runtime/vm/precompiler.cc
+++ b/runtime/vm/precompiler.cc
@@ -4,17 +4,41 @@
#include "vm/precompiler.h"
+#include "vm/assembler.h"
+#include "vm/ast_printer.h"
+#include "vm/branch_optimizer.h"
#include "vm/cha.h"
+#include "vm/code_generator.h"
#include "vm/code_patcher.h"
#include "vm/compiler.h"
+#include "vm/constant_propagator.h"
+#include "vm/dart_entry.h"
+#include "vm/disassembler.h"
+#include "vm/exceptions.h"
+#include "vm/flags.h"
+#include "vm/flow_graph.h"
+#include "vm/flow_graph_allocator.h"
+#include "vm/flow_graph_builder.h"
+#include "vm/flow_graph_compiler.h"
+#include "vm/flow_graph_inliner.h"
+#include "vm/flow_graph_optimizer.h"
+#include "vm/flow_graph_type_propagator.h"
#include "vm/hash_table.h"
+#include "vm/il_printer.h"
#include "vm/isolate.h"
#include "vm/log.h"
#include "vm/longjump.h"
#include "vm/object.h"
#include "vm/object_store.h"
+#include "vm/os.h"
+#include "vm/parser.h"
+#include "vm/redundancy_elimination.h"
+#include "vm/regexp_assembler.h"
+#include "vm/regexp_parser.h"
#include "vm/resolver.h"
#include "vm/symbols.h"
+#include "vm/tags.h"
+#include "vm/timer.h"
namespace dart {
@@ -29,6 +53,59 @@ DEFINE_FLAG(bool, collect_dynamic_function_names, false,
" identify unique targets");
DEFINE_FLAG(bool, print_unique_targets, false, "Print unique dynaic targets");
DEFINE_FLAG(bool, trace_precompiler, false, "Trace precompiler.");
+DEFINE_FLAG(int, max_speculative_inlining_attempts, 1,
+ "Max number of attempts with speculative inlining (precompilation only)");
+
+DECLARE_FLAG(bool, allocation_sinking);
+DECLARE_FLAG(bool, common_subexpression_elimination);
+DECLARE_FLAG(bool, constant_propagation);
+DECLARE_FLAG(bool, disassemble);
+DECLARE_FLAG(bool, disassemble_optimized);
+DECLARE_FLAG(bool, loop_invariant_code_motion);
+DECLARE_FLAG(bool, print_flow_graph);
+DECLARE_FLAG(bool, print_flow_graph_optimized);
+DECLARE_FLAG(bool, range_analysis);
+DECLARE_FLAG(bool, trace_compiler);
+DECLARE_FLAG(bool, trace_optimizing_compiler);
+DECLARE_FLAG(bool, trace_bailout);
+DECLARE_FLAG(bool, use_inlining);
+DECLARE_FLAG(bool, verify_compiler);
+DECLARE_FLAG(bool, precompilation);
+DECLARE_FLAG(bool, huge_method_cutoff_in_code_size);
+DECLARE_FLAG(bool, load_deferred_eagerly);
+DECLARE_FLAG(bool, trace_failed_optimization_attempts);
+DECLARE_FLAG(bool, trace_inlining_intervals);
+DECLARE_FLAG(bool, trace_irregexp);
+
+#ifdef DART_PRECOMPILER
+
+class PrecompileParsedFunctionHelper : public ValueObject {
+ public:
+ PrecompileParsedFunctionHelper(ParsedFunction* parsed_function,
+ bool optimized)
+ : parsed_function_(parsed_function),
+ optimized_(optimized),
+ thread_(Thread::Current()) {
+ }
+
+ bool Compile(CompilationPipeline* pipeline);
+
+ private:
+ ParsedFunction* parsed_function() const { return parsed_function_; }
+ bool optimized() const { return optimized_; }
+ Thread* thread() const { return thread_; }
+ Isolate* isolate() const { return thread_->isolate(); }
+
+ void FinalizeCompilation(Assembler* assembler,
+ FlowGraphCompiler* graph_compiler,
+ FlowGraph* flow_graph);
+
+ ParsedFunction* parsed_function_;
+ const bool optimized_;
+ Thread* const thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrecompileParsedFunctionHelper);
+};
static void Jump(const Error& error) {
@@ -407,7 +484,7 @@ void Precompiler::ProcessFunction(const Function& function) {
ASSERT(!function.is_abstract());
ASSERT(!function.IsRedirectingFactory());
- error_ = Compiler::CompileFunction(thread_, function);
+ error_ = CompileFunction(thread_, function);
if (!error_.IsNull()) {
Jump(error_);
}
@@ -591,7 +668,11 @@ void Precompiler::AddField(const Field& field) {
}
ASSERT(!Dart::IsRunningPrecompiledCode());
field.SetStaticValue(Instance::Handle(field.SavedInitialStaticValue()));
- Compiler::CompileStaticInitializer(field);
+ const Function& initializer =
+ Function::Handle(CompileStaticInitializer(field));
+ if (!initializer.IsNull()) {
+ field.SetPrecompiledInitializer(initializer);
+ }
}
const Function& function =
@@ -602,6 +683,127 @@ void Precompiler::AddField(const Field& field) {
}
+RawFunction* Precompiler::CompileStaticInitializer(const Field& field) {
+ ASSERT(field.is_static());
+ if (field.HasPrecompiledInitializer()) {
+ // TODO(rmacnak): Investigate why this happens for _enum_names.
+ OS::Print("Warning: Ignoring repeated request for initializer for %s\n",
+ field.ToCString());
+ return Function::null();
+ }
+ Thread* thread = Thread::Current();
+ StackZone zone(thread);
+
+ ParsedFunction* parsed_function = Parser::ParseStaticFieldInitializer(field);
+
+ parsed_function->AllocateVariables();
+ // Non-optimized code generator.
+ DartCompilationPipeline pipeline;
+ PrecompileParsedFunctionHelper helper(parsed_function,
+ /* optimized = */ false);
+ helper.Compile(&pipeline);
+ return parsed_function->function().raw();
+}
+
+
+RawObject* Precompiler::EvaluateStaticInitializer(const Field& field) {
+ ASSERT(field.is_static());
+ // The VM sets the field's value to transiton_sentinel prior to
+ // evaluating the initializer value.
+ ASSERT(field.StaticValue() == Object::transition_sentinel().raw());
+ LongJumpScope jump;
+ if (setjmp(*jump.Set()) == 0) {
+ // Under precompilation, the initializer may have already been compiled, in
+ // which case use it. Under lazy compilation or early in precompilation, the
+ // initializer has not yet been created, so create it now, but don't bother
+ // remembering it because it won't be used again.
+ Function& initializer = Function::Handle();
+ if (!field.HasPrecompiledInitializer()) {
+ initializer = CompileStaticInitializer(field);
+ Code::Handle(initializer.unoptimized_code()).set_var_descriptors(
+ Object::empty_var_descriptors());
+ } else {
+ initializer ^= field.PrecompiledInitializer();
+ }
+ // Invoke the function to evaluate the expression.
+ return DartEntry::InvokeFunction(initializer, Object::empty_array());
+ } else {
+ Thread* const thread = Thread::Current();
+ StackZone zone(thread);
+ const Error& error =
+ Error::Handle(thread->zone(), thread->sticky_error());
+ thread->clear_sticky_error();
+ return error.raw();
+ }
+ UNREACHABLE();
+ return Object::null();
+}
+
+
+RawObject* Precompiler::ExecuteOnce(SequenceNode* fragment) {
+ LongJumpScope jump;
+ if (setjmp(*jump.Set()) == 0) {
+ Thread* const thread = Thread::Current();
+ if (FLAG_trace_compiler) {
+ THR_Print("compiling expression: ");
+ AstPrinter::PrintNode(fragment);
+ }
+
+ // Create a dummy function object for the code generator.
+ // The function needs to be associated with a named Class: the interface
+ // Function fits the bill.
+ const char* kEvalConst = "eval_const";
+ const Function& func = Function::ZoneHandle(Function::New(
+ String::Handle(Symbols::New(kEvalConst)),
+ RawFunction::kRegularFunction,
+ true, // static function
+ false, // not const function
+ false, // not abstract
+ false, // not external
+ false, // not native
+ Class::Handle(Type::Handle(Type::Function()).type_class()),
+ fragment->token_pos()));
+
+ func.set_result_type(Object::dynamic_type());
+ func.set_num_fixed_parameters(0);
+ func.SetNumOptionalParameters(0, true);
+ // Manually generated AST, do not recompile.
+ func.SetIsOptimizable(false);
+ func.set_is_debuggable(false);
+
+ // We compile the function here, even though InvokeFunction() below
+ // would compile func automatically. We are checking fewer invariants
+ // here.
+ ParsedFunction* parsed_function = new ParsedFunction(thread, func);
+ parsed_function->SetNodeSequence(fragment);
+ fragment->scope()->AddVariable(parsed_function->EnsureExpressionTemp());
+ fragment->scope()->AddVariable(
+ parsed_function->current_context_var());
+ parsed_function->AllocateVariables();
+
+ // Non-optimized code generator.
+ DartCompilationPipeline pipeline;
+ PrecompileParsedFunctionHelper helper(parsed_function,
+ /* optimized = */ false);
+ helper.Compile(&pipeline);
+ Code::Handle(func.unoptimized_code()).set_var_descriptors(
+ Object::empty_var_descriptors());
+
+ const Object& result = PassiveObject::Handle(
+ DartEntry::InvokeFunction(func, Object::empty_array()));
+ return result.raw();
+ } else {
+ Thread* const thread = Thread::Current();
+ const Object& result =
+ PassiveObject::Handle(thread->sticky_error());
+ thread->clear_sticky_error();
+ return result.raw();
+ }
+ UNREACHABLE();
+ return Object::null();
+}
+
+
void Precompiler::AddFunction(const Function& function) {
if (enqueued_functions_.Lookup(&function) != NULL) return;
@@ -1349,4 +1551,649 @@ void Precompiler::ResetPrecompilerState() {
}
}
+
+void PrecompileParsedFunctionHelper::FinalizeCompilation(
+ Assembler* assembler,
+ FlowGraphCompiler* graph_compiler,
+ FlowGraph* flow_graph) {
+ const Function& function = parsed_function()->function();
+ Zone* const zone = thread()->zone();
+
+ CSTAT_TIMER_SCOPE(thread(), codefinalizer_timer);
+ // CreateDeoptInfo uses the object pool and needs to be done before
+ // FinalizeCode.
+ const Array& deopt_info_array =
+ Array::Handle(zone, graph_compiler->CreateDeoptInfo(assembler));
+ INC_STAT(thread(), total_code_size,
+ deopt_info_array.Length() * sizeof(uword));
+ // Allocates instruction object. Since this occurs only at safepoint,
+ // there can be no concurrent access to the instruction page.
+ const Code& code = Code::Handle(
+ Code::FinalizeCode(function, assembler, optimized()));
+ code.set_is_optimized(optimized());
+ code.set_owner(function);
+ if (!function.IsOptimizable()) {
+ // A function with huge unoptimized code can become non-optimizable
+ // after generating unoptimized code.
+ function.set_usage_counter(INT_MIN);
+ }
+
+ const Array& intervals = graph_compiler->inlined_code_intervals();
+ INC_STAT(thread(), total_code_size,
+ intervals.Length() * sizeof(uword));
+ code.SetInlinedIntervals(intervals);
+
+ const Array& inlined_id_array =
+ Array::Handle(zone, graph_compiler->InliningIdToFunction());
+ INC_STAT(thread(), total_code_size,
+ inlined_id_array.Length() * sizeof(uword));
+ code.SetInlinedIdToFunction(inlined_id_array);
+
+ const Array& caller_inlining_id_map_array =
+ Array::Handle(zone, graph_compiler->CallerInliningIdMap());
+ INC_STAT(thread(), total_code_size,
+ caller_inlining_id_map_array.Length() * sizeof(uword));
+ code.SetInlinedCallerIdMap(caller_inlining_id_map_array);
+
+ graph_compiler->FinalizePcDescriptors(code);
+ code.set_deopt_info_array(deopt_info_array);
+
+ graph_compiler->FinalizeStackmaps(code);
+ graph_compiler->FinalizeVarDescriptors(code);
+ graph_compiler->FinalizeExceptionHandlers(code);
+ graph_compiler->FinalizeStaticCallTargetsTable(code);
+
+ if (optimized()) {
+ // Installs code while at safepoint.
+ ASSERT(thread()->IsMutatorThread());
+ function.InstallOptimizedCode(code, /* is_osr = */ false);
+ } else { // not optimized.
+ function.set_unoptimized_code(code);
+ function.AttachCode(code);
+ }
+ ASSERT(!parsed_function()->HasDeferredPrefixes());
+ ASSERT(FLAG_load_deferred_eagerly);
+}
+
+
+// Return false if bailed out.
+// If optimized_result_code is not NULL then it is caller's responsibility
+// to install code.
+bool PrecompileParsedFunctionHelper::Compile(CompilationPipeline* pipeline) {
+ ASSERT(FLAG_precompilation);
+ const Function& function = parsed_function()->function();
+ if (optimized() && !function.IsOptimizable()) {
+ return false;
+ }
+ bool is_compiled = false;
+ Zone* const zone = thread()->zone();
+ TimelineStream* compiler_timeline = isolate()->GetCompilerStream();
+ CSTAT_TIMER_SCOPE(thread(), codegen_timer);
+ HANDLESCOPE(thread());
+
+ // We may reattempt compilation if the function needs to be assembled using
+ // far branches on ARM and MIPS. In the else branch of the setjmp call,
+ // done is set to false, and use_far_branches is set to true if there is a
+ // longjmp from the ARM or MIPS assemblers. In all other paths through this
+ // while loop, done is set to true. use_far_branches is always false on ia32
+ // and x64.
+ bool done = false;
+ // volatile because the variable may be clobbered by a longjmp.
+ volatile bool use_far_branches = false;
+ volatile bool use_speculative_inlining =
+ FLAG_max_speculative_inlining_attempts > 0;
+ GrowableArray<intptr_t> inlining_black_list;
+
+ while (!done) {
+ const intptr_t prev_deopt_id = thread()->deopt_id();
+ thread()->set_deopt_id(0);
+ LongJumpScope jump;
+ const intptr_t val = setjmp(*jump.Set());
+ if (val == 0) {
+ FlowGraph* flow_graph = NULL;
+
+ // Class hierarchy analysis is registered with the isolate in the
+ // constructor and unregisters itself upon destruction.
+ CHA cha(thread());
+
+ // TimerScope needs an isolate to be properly terminated in case of a
+ // LongJump.
+ {
+ CSTAT_TIMER_SCOPE(thread(), graphbuilder_timer);
+ ZoneGrowableArray<const ICData*>* ic_data_array =
+ new(zone) ZoneGrowableArray<const ICData*>();
+ TimelineDurationScope tds(thread(),
+ compiler_timeline,
+ "BuildFlowGraph");
+ flow_graph = pipeline->BuildFlowGraph(zone,
+ parsed_function(),
+ *ic_data_array,
+ Compiler::kNoOSRDeoptId);
+ }
+
+ const bool print_flow_graph =
+ (FLAG_print_flow_graph ||
+ (optimized() && FLAG_print_flow_graph_optimized)) &&
+ FlowGraphPrinter::ShouldPrint(function);
+
+ if (print_flow_graph) {
+ FlowGraphPrinter::PrintGraph("Before Optimizations", flow_graph);
+ }
+
+ if (optimized()) {
+ TimelineDurationScope tds(thread(),
+ compiler_timeline,
+ "ComputeSSA");
+ CSTAT_TIMER_SCOPE(thread(), ssa_timer);
+ // Transform to SSA (virtual register 0 and no inlining arguments).
+ flow_graph->ComputeSSA(0, NULL);
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+ if (print_flow_graph) {
+ FlowGraphPrinter::PrintGraph("After SSA", flow_graph);
+ }
+ }
+
+ // Maps inline_id_to_function[inline_id] -> function. Top scope
+ // function has inline_id 0. The map is populated by the inliner.
+ GrowableArray<const Function*> inline_id_to_function;
+ // For a given inlining-id(index) specifies the caller's inlining-id.
+ GrowableArray<intptr_t> caller_inline_id;
+ // Collect all instance fields that are loaded in the graph and
+ // have non-generic type feedback attached to them that can
+ // potentially affect optimizations.
+ if (optimized()) {
+ TimelineDurationScope tds(thread(),
+ compiler_timeline,
+ "OptimizationPasses");
+ inline_id_to_function.Add(&function);
+ // Top scope function has no caller (-1).
+ caller_inline_id.Add(-1);
+ CSTAT_TIMER_SCOPE(thread(), graphoptimizer_timer);
+
+ FlowGraphOptimizer optimizer(flow_graph,
+ use_speculative_inlining,
+ &inlining_black_list);
+ optimizer.PopulateWithICData();
+
+ optimizer.ApplyClassIds();
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+
+ FlowGraphTypePropagator::Propagate(flow_graph);
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+
+ optimizer.ApplyICData();
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+
+ // Optimize (a << b) & c patterns, merge operations.
+ // Run early in order to have more opportunity to optimize left shifts.
+ optimizer.TryOptimizePatterns();
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+
+ FlowGraphInliner::SetInliningId(flow_graph, 0);
+
+ // Inlining (mutates the flow graph)
+ if (FLAG_use_inlining) {
+ TimelineDurationScope tds2(thread(),
+ compiler_timeline,
+ "Inlining");
+ CSTAT_TIMER_SCOPE(thread(), graphinliner_timer);
+ // Propagate types to create more inlining opportunities.
+ FlowGraphTypePropagator::Propagate(flow_graph);
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+
+ // Use propagated class-ids to create more inlining opportunities.
+ optimizer.ApplyClassIds();
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+
+ FlowGraphInliner inliner(flow_graph,
+ &inline_id_to_function,
+ &caller_inline_id,
+ use_speculative_inlining,
+ &inlining_black_list);
+ inliner.Inline();
+ // Use lists are maintained and validated by the inliner.
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+ }
+
+ // Propagate types and eliminate more type tests.
+ FlowGraphTypePropagator::Propagate(flow_graph);
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+
+ {
+ TimelineDurationScope tds2(thread(),
+ compiler_timeline,
+ "ApplyClassIds");
+ // Use propagated class-ids to optimize further.
+ optimizer.ApplyClassIds();
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+ }
+
+ // Propagate types for potentially newly added instructions by
+ // ApplyClassIds(). Must occur before canonicalization.
+ FlowGraphTypePropagator::Propagate(flow_graph);
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+
+ // Do optimizations that depend on the propagated type information.
+ if (optimizer.Canonicalize()) {
+ // Invoke Canonicalize twice in order to fully canonicalize patterns
+ // like "if (a & const == 0) { }".
+ optimizer.Canonicalize();
+ }
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+
+ {
+ TimelineDurationScope tds2(thread(),
+ compiler_timeline,
+ "BranchSimplifier");
+ BranchSimplifier::Simplify(flow_graph);
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+
+ IfConverter::Simplify(flow_graph);
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+ }
+
+ if (FLAG_constant_propagation) {
+ TimelineDurationScope tds2(thread(),
+ compiler_timeline,
+ "ConstantPropagation");
+ ConstantPropagator::Optimize(flow_graph);
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+ // A canonicalization pass to remove e.g. smi checks on smi constants.
+ optimizer.Canonicalize();
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+ // Canonicalization introduced more opportunities for constant
+ // propagation.
+ ConstantPropagator::Optimize(flow_graph);
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+ }
+
+ // Optimistically convert loop phis that have a single non-smi input
+ // coming from the loop pre-header into smi-phis.
+ if (FLAG_loop_invariant_code_motion) {
+ LICM licm(flow_graph);
+ licm.OptimisticallySpecializeSmiPhis();
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+ }
+
+ // Propagate types and eliminate even more type tests.
+ // Recompute types after constant propagation to infer more precise
+ // types for uses that were previously reached by now eliminated phis.
+ FlowGraphTypePropagator::Propagate(flow_graph);
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+
+ {
+ TimelineDurationScope tds2(thread(),
+ compiler_timeline,
+ "SelectRepresentations");
+ // Where beneficial convert Smi operations into Int32 operations.
+ // Only meanigful for 32bit platforms right now.
+ optimizer.WidenSmiToInt32();
+
+ // Unbox doubles. Performed after constant propagation to minimize
+ // interference from phis merging double values and tagged
+ // values coming from dead paths.
+ optimizer.SelectRepresentations();
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+ }
+
+ {
+ TimelineDurationScope tds2(thread(),
+ compiler_timeline,
+ "CommonSubexpressionElinination");
+ if (FLAG_common_subexpression_elimination ||
+ FLAG_loop_invariant_code_motion) {
+ flow_graph->ComputeBlockEffects();
+ }
+
+ if (FLAG_common_subexpression_elimination) {
+ if (DominatorBasedCSE::Optimize(flow_graph)) {
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+ optimizer.Canonicalize();
+ // Do another round of CSE to take secondary effects into account:
+ // e.g. when eliminating dependent loads (a.x[0] + a.x[0])
+ // TODO(fschneider): Change to a one-pass optimization pass.
+ if (DominatorBasedCSE::Optimize(flow_graph)) {
+ optimizer.Canonicalize();
+ }
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+ }
+ }
+
+ // Run loop-invariant code motion right after load elimination since
+ // it depends on the numbering of loads from the previous
+ // load-elimination.
+ if (FLAG_loop_invariant_code_motion) {
+ LICM licm(flow_graph);
+ licm.Optimize();
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+ }
+ flow_graph->RemoveRedefinitions();
+ }
+
+ // Optimize (a << b) & c patterns, merge operations.
+ // Run after CSE in order to have more opportunity to merge
+ // instructions that have same inputs.
+ optimizer.TryOptimizePatterns();
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+
+ {
+ TimelineDurationScope tds2(thread(),
+ compiler_timeline,
+ "DeadStoreElimination");
+ DeadStoreElimination::Optimize(flow_graph);
+ }
+
+ if (FLAG_range_analysis) {
+ TimelineDurationScope tds2(thread(),
+ compiler_timeline,
+ "RangeAnalysis");
+ // Propagate types after store-load-forwarding. Some phis may have
+ // become smi phis that can be processed by range analysis.
+ FlowGraphTypePropagator::Propagate(flow_graph);
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+
+ // We have to perform range analysis after LICM because it
+ // optimistically moves CheckSmi through phis into loop preheaders
+ // making some phis smi.
+ optimizer.InferIntRanges();
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+ }
+
+ if (FLAG_constant_propagation) {
+ TimelineDurationScope tds2(thread(),
+ compiler_timeline,
+ "ConstantPropagator::OptimizeBranches");
+ // Constant propagation can use information from range analysis to
+ // find unreachable branch targets and eliminate branches that have
+ // the same true- and false-target.
+ ConstantPropagator::OptimizeBranches(flow_graph);
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+ }
+
+ // Recompute types after code movement was done to ensure correct
+ // reaching types for hoisted values.
+ FlowGraphTypePropagator::Propagate(flow_graph);
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+
+ {
+ TimelineDurationScope tds2(thread(),
+ compiler_timeline,
+ "TryCatchAnalyzer::Optimize");
+ // Optimize try-blocks.
+ TryCatchAnalyzer::Optimize(flow_graph);
+ }
+
+ // Detach environments from the instructions that can't deoptimize.
+ // Do it before we attempt to perform allocation sinking to minimize
+ // amount of materializations it has to perform.
+ optimizer.EliminateEnvironments();
+
+ {
+ TimelineDurationScope tds2(thread(),
+ compiler_timeline,
+ "EliminateDeadPhis");
+ DeadCodeElimination::EliminateDeadPhis(flow_graph);
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+ }
+
+ if (optimizer.Canonicalize()) {
+ optimizer.Canonicalize();
+ }
+
+ // Attempt to sink allocations of temporary non-escaping objects to
+ // the deoptimization path.
+ AllocationSinking* sinking = NULL;
+ if (FLAG_allocation_sinking &&
+ (flow_graph->graph_entry()->SuccessorCount() == 1)) {
+ TimelineDurationScope tds2(thread(),
+ compiler_timeline,
+ "AllocationSinking::Optimize");
+ // TODO(fschneider): Support allocation sinking with try-catch.
+ sinking = new AllocationSinking(flow_graph);
+ sinking->Optimize();
+ }
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+
+ DeadCodeElimination::EliminateDeadPhis(flow_graph);
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+
+ FlowGraphTypePropagator::Propagate(flow_graph);
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+
+ {
+ TimelineDurationScope tds2(thread(),
+ compiler_timeline,
+ "SelectRepresentations");
+ // Ensure that all phis inserted by optimization passes have
+ // consistent representations.
+ optimizer.SelectRepresentations();
+ }
+
+ if (optimizer.Canonicalize()) {
+ // To fully remove redundant boxing (e.g. BoxDouble used only in
+ // environments and UnboxDouble instructions) instruction we
+ // first need to replace all their uses and then fold them away.
+ // For now we just repeat Canonicalize twice to do that.
+ // TODO(vegorov): implement a separate representation folding pass.
+ optimizer.Canonicalize();
+ }
+ DEBUG_ASSERT(flow_graph->VerifyUseLists());
+
+ if (sinking != NULL) {
+ TimelineDurationScope tds2(
+ thread(),
+ compiler_timeline,
+ "AllocationSinking::DetachMaterializations");
+ // Remove all MaterializeObject instructions inserted by allocation
+ // sinking from the flow graph and let them float on the side
+ // referenced only from environments. Register allocator will consider
+ // them as part of a deoptimization environment.
+ sinking->DetachMaterializations();
+ }
+
+ // Compute and store graph informations (call & instruction counts)
+ // to be later used by the inliner.
+ FlowGraphInliner::CollectGraphInfo(flow_graph, true);
+
+ {
+ TimelineDurationScope tds2(thread(),
+ compiler_timeline,
+ "AllocateRegisters");
+ // Perform register allocation on the SSA graph.
+ FlowGraphAllocator allocator(*flow_graph);
+ allocator.AllocateRegisters();
+ }
+
+ if (print_flow_graph) {
+ FlowGraphPrinter::PrintGraph("After Optimizations", flow_graph);
+ }
+ }
+
+ ASSERT(inline_id_to_function.length() == caller_inline_id.length());
+ Assembler assembler(use_far_branches);
+ FlowGraphCompiler graph_compiler(&assembler, flow_graph,
+ *parsed_function(), optimized(),
+ inline_id_to_function,
+ caller_inline_id);
+ {
+ CSTAT_TIMER_SCOPE(thread(), graphcompiler_timer);
+ TimelineDurationScope tds(thread(),
+ compiler_timeline,
+ "CompileGraph");
+ graph_compiler.CompileGraph();
+ pipeline->FinalizeCompilation();
+ }
+ {
+ TimelineDurationScope tds(thread(),
+ compiler_timeline,
+ "FinalizeCompilation");
+ ASSERT(thread()->IsMutatorThread());
+ FinalizeCompilation(&assembler, &graph_compiler, flow_graph);
+ }
+ // Mark that this isolate now has compiled code.
+ isolate()->set_has_compiled_code(true);
+ // Exit the loop and the function with the correct result value.
+ is_compiled = true;
+ done = true;
+ } else {
+ // We bailed out or we encountered an error.
+ const Error& error = Error::Handle(thread()->sticky_error());
+
+ if (error.raw() == Object::branch_offset_error().raw()) {
+ // Compilation failed due to an out of range branch offset in the
+ // assembler. We try again (done = false) with far branches enabled.
+ done = false;
+ ASSERT(!use_far_branches);
+ use_far_branches = true;
+ } else if (error.raw() == Object::speculative_inlining_error().raw()) {
+ // The return value of setjmp is the deopt id of the check instruction
+ // that caused the bailout.
+ done = false;
+#if defined(DEBUG)
+ ASSERT(use_speculative_inlining);
+ for (intptr_t i = 0; i < inlining_black_list.length(); ++i) {
+ ASSERT(inlining_black_list[i] != val);
+ }
+#endif
+ inlining_black_list.Add(val);
+ const intptr_t max_attempts = FLAG_max_speculative_inlining_attempts;
+ if (inlining_black_list.length() >= max_attempts) {
+ use_speculative_inlining = false;
+ if (FLAG_trace_compiler || FLAG_trace_optimizing_compiler) {
+ THR_Print("Disabled speculative inlining after %" Pd " attempts.\n",
+ inlining_black_list.length());
+ }
+ }
+ } else {
+ // If the error isn't due to an out of range branch offset, we don't
+ // try again (done = true), and indicate that we did not finish
+ // compiling (is_compiled = false).
+ if (FLAG_trace_bailout) {
+ THR_Print("%s\n", error.ToErrorCString());
+ }
+ done = true;
+ }
+
+ // Clear the error if it was not a real error, but just a bailout.
+ if (error.IsLanguageError() &&
+ (LanguageError::Cast(error).kind() == Report::kBailout)) {
+ thread()->clear_sticky_error();
+ }
+ is_compiled = false;
+ }
+ // Reset global isolate state.
+ thread()->set_deopt_id(prev_deopt_id);
+ }
+ return is_compiled;
+}
+
+
+static RawError* PrecompileFunctionHelper(CompilationPipeline* pipeline,
+ const Function& function,
+ bool optimized) {
+ // Check that we optimize, except if the function is not optimizable.
+ ASSERT(FLAG_precompilation);
+ ASSERT(!function.IsOptimizable() || optimized);
+ ASSERT(!function.HasCode());
+ LongJumpScope jump;
+ if (setjmp(*jump.Set()) == 0) {
+ Thread* const thread = Thread::Current();
+ StackZone stack_zone(thread);
+ Zone* const zone = stack_zone.GetZone();
+ const bool trace_compiler =
+ FLAG_trace_compiler ||
+ (FLAG_trace_optimizing_compiler && optimized);
+ Timer per_compile_timer(trace_compiler, "Compilation time");
+ per_compile_timer.Start();
+
+ ParsedFunction* parsed_function = new(zone) ParsedFunction(
+ thread, Function::ZoneHandle(zone, function.raw()));
+ if (trace_compiler) {
+ THR_Print(
+ "Precompiling %sfunction: '%s' @ token %" Pd ", size %" Pd "\n",
+ (optimized ? "optimized " : ""),
+ function.ToFullyQualifiedCString(),
+ function.token_pos().Pos(),
+ (function.end_token_pos().Pos() - function.token_pos().Pos()));
+ }
+ INC_STAT(thread, num_functions_compiled, 1);
+ if (optimized) {
+ INC_STAT(thread, num_functions_optimized, 1);
+ }
+ {
+ HANDLESCOPE(thread);
+ const int64_t num_tokens_before = STAT_VALUE(thread, num_tokens_consumed);
+ pipeline->ParseFunction(parsed_function);
+ const int64_t num_tokens_after = STAT_VALUE(thread, num_tokens_consumed);
+ INC_STAT(thread,
+ num_func_tokens_compiled,
+ num_tokens_after - num_tokens_before);
+ }
+
+ PrecompileParsedFunctionHelper helper(parsed_function, optimized);
+ const bool success = helper.Compile(pipeline);
+ if (!success) {
+ // Encountered error.
+ Error& error = Error::Handle();
+ // We got an error during compilation.
+ error = thread->sticky_error();
+ thread->clear_sticky_error();
+ ASSERT(error.IsLanguageError() &&
+ LanguageError::Cast(error).kind() != Report::kBailout);
+ return error.raw();
+ }
+
+ per_compile_timer.Stop();
+
+ if (trace_compiler && success) {
+ THR_Print("--> '%s' entry: %#" Px " size: %" Pd " time: %" Pd64 " us\n",
+ function.ToFullyQualifiedCString(),
+ Code::Handle(function.CurrentCode()).EntryPoint(),
+ Code::Handle(function.CurrentCode()).Size(),
+ per_compile_timer.TotalElapsedTime());
+ }
+
+ if (FLAG_disassemble && FlowGraphPrinter::ShouldPrint(function)) {
+ Disassembler::DisassembleCode(function, optimized);
+ } else if (FLAG_disassemble_optimized &&
+ optimized &&
+ FlowGraphPrinter::ShouldPrint(function)) {
+ // TODO(fschneider): Print unoptimized code along with the optimized code.
+ THR_Print("*** BEGIN CODE\n");
+ Disassembler::DisassembleCode(function, true);
+ THR_Print("*** END CODE\n");
+ }
+ return Error::null();
+ } else {
+ Thread* const thread = Thread::Current();
+ StackZone stack_zone(thread);
+ Error& error = Error::Handle();
+ // We got an error during compilation.
+ error = thread->sticky_error();
+ thread->clear_sticky_error();
+ // Precompilation may encounter compile-time errors.
+ // Do not attempt to optimize functions that can cause errors.
+ function.set_is_optimizable(false);
+ return error.raw();
+ }
+ UNREACHABLE();
+ return Error::null();
+}
+
+
+RawError* Precompiler::CompileFunction(Thread* thread,
+ const Function& function) {
+ VMTagScope tagScope(thread, VMTag::kCompileUnoptimizedTagId);
+ TIMELINE_FUNCTION_COMPILATION_DURATION(thread, "Function", function);
+
+ CompilationPipeline* pipeline =
+ CompilationPipeline::New(thread->zone(), function);
+
+ ASSERT(FLAG_precompilation);
+ const bool optimized = function.IsOptimizable(); // False for natives.
+ return PrecompileFunctionHelper(pipeline, function, optimized);
+}
+
+#endif // DART_PRECOMPILER
+
} // namespace dart
« no previous file with comments | « runtime/vm/precompiler.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698