OLD | NEW |
---|---|
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 #include "vm/precompiler.h" | 5 #include "vm/precompiler.h" |
6 | 6 |
7 #include "vm/assembler.h" | |
8 #include "vm/ast_printer.h" | |
7 #include "vm/cha.h" | 9 #include "vm/cha.h" |
10 #include "vm/code_generator.h" | |
8 #include "vm/code_patcher.h" | 11 #include "vm/code_patcher.h" |
9 #include "vm/compiler.h" | 12 #include "vm/compiler.h" |
13 #include "vm/constant_propagator.h" | |
14 #include "vm/dart_entry.h" | |
15 #include "vm/exceptions.h" | |
16 #include "vm/flags.h" | |
17 #include "vm/flow_graph.h" | |
18 #include "vm/flow_graph_allocator.h" | |
19 #include "vm/flow_graph_builder.h" | |
20 #include "vm/flow_graph_compiler.h" | |
21 #include "vm/flow_graph_inliner.h" | |
22 #include "vm/flow_graph_optimizer.h" | |
23 #include "vm/flow_graph_type_propagator.h" | |
10 #include "vm/hash_table.h" | 24 #include "vm/hash_table.h" |
25 #include "vm/il_printer.h" | |
11 #include "vm/isolate.h" | 26 #include "vm/isolate.h" |
12 #include "vm/log.h" | 27 #include "vm/log.h" |
13 #include "vm/longjump.h" | 28 #include "vm/longjump.h" |
14 #include "vm/object.h" | 29 #include "vm/object.h" |
15 #include "vm/object_store.h" | 30 #include "vm/object_store.h" |
31 #include "vm/os.h" | |
32 #include "vm/parser.h" | |
33 #include "vm/regexp_assembler.h" | |
34 #include "vm/regexp_parser.h" | |
16 #include "vm/resolver.h" | 35 #include "vm/resolver.h" |
17 #include "vm/symbols.h" | 36 #include "vm/symbols.h" |
37 #include "vm/tags.h" | |
38 #include "vm/timer.h" | |
18 | 39 |
19 namespace dart { | 40 namespace dart { |
20 | 41 |
21 | 42 |
22 #define T (thread()) | 43 #define T (thread()) |
23 #define I (isolate()) | 44 #define I (isolate()) |
24 #define Z (zone()) | 45 #define Z (zone()) |
25 | 46 |
26 | 47 |
27 DEFINE_FLAG(bool, collect_dynamic_function_names, false, | 48 DEFINE_FLAG(bool, collect_dynamic_function_names, false, |
28 "In precompilation collects all dynamic function names in order to" | 49 "In precompilation collects all dynamic function names in order to" |
29 " identify unique targets"); | 50 " identify unique targets"); |
30 DEFINE_FLAG(bool, print_unique_targets, false, "Print unique dynaic targets"); | 51 DEFINE_FLAG(bool, print_unique_targets, false, "Print unique dynaic targets"); |
31 DEFINE_FLAG(bool, trace_precompiler, false, "Trace precompiler."); | 52 DEFINE_FLAG(bool, trace_precompiler, false, "Trace precompiler."); |
53 DEFINE_FLAG(int, max_speculative_inlining_attempts, 1, | |
54 "Max number of attempts with speculative inlining (precompilation only)"); | |
55 | |
56 DECLARE_FLAG(bool, allocation_sinking); | |
57 DECLARE_FLAG(bool, common_subexpression_elimination); | |
58 DECLARE_FLAG(bool, constant_propagation); | |
59 DECLARE_FLAG(bool, disassemble); | |
60 DECLARE_FLAG(bool, disassemble_optimized); | |
61 DECLARE_FLAG(bool, loop_invariant_code_motion); | |
62 DECLARE_FLAG(bool, print_flow_graph); | |
63 DECLARE_FLAG(bool, print_flow_graph_optimized); | |
64 DECLARE_FLAG(bool, range_analysis); | |
65 DECLARE_FLAG(bool, trace_compiler); | |
66 DECLARE_FLAG(bool, trace_optimizing_compiler); | |
67 DECLARE_FLAG(bool, trace_bailout); | |
68 DECLARE_FLAG(bool, use_inlining); | |
69 DECLARE_FLAG(bool, verify_compiler); | |
70 DECLARE_FLAG(bool, precompilation); | |
71 DECLARE_FLAG(bool, huge_method_cutoff_in_code_size); | |
72 DECLARE_FLAG(bool, load_deferred_eagerly); | |
73 DECLARE_FLAG(bool, trace_failed_optimization_attempts); | |
74 DECLARE_FLAG(bool, trace_inlining_intervals); | |
75 DECLARE_FLAG(bool, trace_irregexp); | |
76 | |
77 | |
78 class PrecompileParsedFunctionHelper : public ValueObject { | |
79 public: | |
80 PrecompileParsedFunctionHelper(ParsedFunction* parsed_function, | |
81 bool optimized) | |
82 : parsed_function_(parsed_function), | |
83 optimized_(optimized), | |
84 thread_(Thread::Current()) { | |
85 } | |
86 | |
87 bool Compile(CompilationPipeline* pipeline); | |
88 | |
89 private: | |
90 ParsedFunction* parsed_function() const { return parsed_function_; } | |
91 bool optimized() const { return optimized_; } | |
92 Thread* thread() const { return thread_; } | |
93 Isolate* isolate() const { return thread_->isolate(); } | |
94 | |
95 void FinalizeCompilation(Assembler* assembler, | |
96 FlowGraphCompiler* graph_compiler, | |
97 FlowGraph* flow_graph); | |
98 | |
99 ParsedFunction* parsed_function_; | |
100 const bool optimized_; | |
101 Thread* const thread_; | |
102 | |
103 DISALLOW_COPY_AND_ASSIGN(PrecompileParsedFunctionHelper); | |
104 }; | |
32 | 105 |
33 | 106 |
34 static void Jump(const Error& error) { | 107 static void Jump(const Error& error) { |
35 Thread::Current()->long_jump_base()->Jump(1, error); | 108 Thread::Current()->long_jump_base()->Jump(1, error); |
36 } | 109 } |
37 | 110 |
38 | 111 |
39 RawError* Precompiler::CompileAll( | 112 RawError* Precompiler::CompileAll( |
40 Dart_QualifiedFunctionName embedder_entry_points[], | 113 Dart_QualifiedFunctionName embedder_entry_points[], |
41 bool reset_fields) { | 114 bool reset_fields) { |
(...skipping 345 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
387 THR_Print("Precompiling %" Pd " %s (%s, %s)\n", | 460 THR_Print("Precompiling %" Pd " %s (%s, %s)\n", |
388 function_count_, | 461 function_count_, |
389 function.ToLibNamePrefixedQualifiedCString(), | 462 function.ToLibNamePrefixedQualifiedCString(), |
390 function.token_pos().ToCString(), | 463 function.token_pos().ToCString(), |
391 Function::KindToCString(function.kind())); | 464 Function::KindToCString(function.kind())); |
392 } | 465 } |
393 | 466 |
394 ASSERT(!function.is_abstract()); | 467 ASSERT(!function.is_abstract()); |
395 ASSERT(!function.IsRedirectingFactory()); | 468 ASSERT(!function.IsRedirectingFactory()); |
396 | 469 |
397 error_ = Compiler::CompileFunction(thread_, function); | 470 error_ = CompileFunction(thread_, function); |
398 if (!error_.IsNull()) { | 471 if (!error_.IsNull()) { |
399 Jump(error_); | 472 Jump(error_); |
400 } | 473 } |
401 } else { | 474 } else { |
402 if (FLAG_trace_precompiler) { | 475 if (FLAG_trace_precompiler) { |
403 // This function was compiled from somewhere other than Precompiler, | 476 // This function was compiled from somewhere other than Precompiler, |
404 // such as const constructors compiled by the parser. | 477 // such as const constructors compiled by the parser. |
405 THR_Print("Already has code: %s (%s, %s)\n", | 478 THR_Print("Already has code: %s (%s, %s)\n", |
406 function.ToLibNamePrefixedQualifiedCString(), | 479 function.ToLibNamePrefixedQualifiedCString(), |
407 function.token_pos().ToCString(), | 480 function.token_pos().ToCString(), |
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
571 | 644 |
572 const bool is_initialized = value.raw() != Object::sentinel().raw(); | 645 const bool is_initialized = value.raw() != Object::sentinel().raw(); |
573 if (is_initialized && !reset_fields_) return; | 646 if (is_initialized && !reset_fields_) return; |
574 | 647 |
575 if (!field.HasPrecompiledInitializer()) { | 648 if (!field.HasPrecompiledInitializer()) { |
576 if (FLAG_trace_precompiler) { | 649 if (FLAG_trace_precompiler) { |
577 THR_Print("Precompiling initializer for %s\n", field.ToCString()); | 650 THR_Print("Precompiling initializer for %s\n", field.ToCString()); |
578 } | 651 } |
579 ASSERT(!Dart::IsRunningPrecompiledCode()); | 652 ASSERT(!Dart::IsRunningPrecompiledCode()); |
580 field.SetStaticValue(Instance::Handle(field.SavedInitialStaticValue())); | 653 field.SetStaticValue(Instance::Handle(field.SavedInitialStaticValue())); |
581 Compiler::CompileStaticInitializer(field); | 654 const Function& initializer = |
655 Function::Handle(CompileStaticInitializer(field)); | |
656 if (!initializer.IsNull()) { | |
657 field.SetPrecompiledInitializer(initializer); | |
658 } | |
582 } | 659 } |
583 | 660 |
584 const Function& function = | 661 const Function& function = |
585 Function::Handle(Z, field.PrecompiledInitializer()); | 662 Function::Handle(Z, field.PrecompiledInitializer()); |
586 AddCalleesOf(function); | 663 AddCalleesOf(function); |
587 } | 664 } |
588 } | 665 } |
589 } | 666 } |
590 | 667 |
591 | 668 |
669 RawFunction* Precompiler::CompileStaticInitializer(const Field& field) { | |
670 ASSERT(field.is_static()); | |
671 if (field.HasPrecompiledInitializer()) { | |
672 // TODO(rmacnak): Investigate why this happens for _enum_names. | |
673 OS::Print("Warning: Ignoring repeated request for initializer for %s\n", | |
674 field.ToCString()); | |
675 return Function::null(); | |
676 } | |
677 Thread* thread = Thread::Current(); | |
678 StackZone zone(thread); | |
679 | |
680 ParsedFunction* parsed_function = Parser::ParseStaticFieldInitializer(field); | |
681 | |
682 parsed_function->AllocateVariables(); | |
683 // Non-optimized code generator. | |
684 DartCompilationPipeline pipeline; | |
685 PrecompileParsedFunctionHelper helper(parsed_function, | |
686 /* optimized = */ false); | |
687 helper.Compile(&pipeline); | |
688 return parsed_function->function().raw(); | |
689 } | |
690 | |
691 | |
692 RawObject* Precompiler::EvaluateStaticInitializer(const Field& field) { | |
693 ASSERT(field.is_static()); | |
694 // The VM sets the field's value to transiton_sentinel prior to | |
695 // evaluating the initializer value. | |
696 ASSERT(field.StaticValue() == Object::transition_sentinel().raw()); | |
697 LongJumpScope jump; | |
698 if (setjmp(*jump.Set()) == 0) { | |
699 // Under precompilation, the initializer may have already been compiled, in | |
700 // which case use it. Under lazy compilation or early in precompilation, the | |
701 // initializer has not yet been created, so create it now, but don't bother | |
702 // remembering it because it won't be used again. | |
703 Function& initializer = Function::Handle(); | |
704 if (!field.HasPrecompiledInitializer()) { | |
705 initializer = CompileStaticInitializer(field); | |
706 Code::Handle(initializer.unoptimized_code()).set_var_descriptors( | |
707 Object::empty_var_descriptors()); | |
708 } else { | |
709 initializer ^= field.PrecompiledInitializer(); | |
710 } | |
711 // Invoke the function to evaluate the expression. | |
712 return DartEntry::InvokeFunction(initializer, Object::empty_array()); | |
713 } else { | |
714 Thread* const thread = Thread::Current(); | |
715 Isolate* const isolate = thread->isolate(); | |
716 StackZone zone(thread); | |
717 const Error& error = | |
718 Error::Handle(thread->zone(), isolate->object_store()->sticky_error()); | |
719 isolate->object_store()->clear_sticky_error(); | |
720 return error.raw(); | |
721 } | |
722 UNREACHABLE(); | |
723 return Object::null(); | |
724 } | |
725 | |
726 | |
727 RawObject* Precompiler::ExecuteOnce(SequenceNode* fragment) { | |
728 LongJumpScope jump; | |
729 if (setjmp(*jump.Set()) == 0) { | |
730 Thread* const thread = Thread::Current(); | |
731 if (FLAG_trace_compiler) { | |
732 THR_Print("compiling expression: "); | |
733 AstPrinter::PrintNode(fragment); | |
734 } | |
735 | |
736 // Create a dummy function object for the code generator. | |
737 // The function needs to be associated with a named Class: the interface | |
738 // Function fits the bill. | |
739 const char* kEvalConst = "eval_const"; | |
740 const Function& func = Function::ZoneHandle(Function::New( | |
741 String::Handle(Symbols::New(kEvalConst)), | |
742 RawFunction::kRegularFunction, | |
743 true, // static function | |
744 false, // not const function | |
745 false, // not abstract | |
746 false, // not external | |
747 false, // not native | |
748 Class::Handle(Type::Handle(Type::Function()).type_class()), | |
749 fragment->token_pos())); | |
750 | |
751 func.set_result_type(Object::dynamic_type()); | |
752 func.set_num_fixed_parameters(0); | |
753 func.SetNumOptionalParameters(0, true); | |
754 // Manually generated AST, do not recompile. | |
755 func.SetIsOptimizable(false); | |
756 func.set_is_debuggable(false); | |
757 | |
758 // We compile the function here, even though InvokeFunction() below | |
759 // would compile func automatically. We are checking fewer invariants | |
760 // here. | |
761 ParsedFunction* parsed_function = new ParsedFunction(thread, func); | |
762 parsed_function->SetNodeSequence(fragment); | |
763 fragment->scope()->AddVariable(parsed_function->EnsureExpressionTemp()); | |
764 fragment->scope()->AddVariable( | |
765 parsed_function->current_context_var()); | |
766 parsed_function->AllocateVariables(); | |
767 | |
768 // Non-optimized code generator. | |
769 DartCompilationPipeline pipeline; | |
770 PrecompileParsedFunctionHelper helper(parsed_function, | |
771 /* optimized = */ false); | |
772 helper.Compile(&pipeline); | |
773 Code::Handle(func.unoptimized_code()).set_var_descriptors( | |
774 Object::empty_var_descriptors()); | |
775 | |
776 const Object& result = PassiveObject::Handle( | |
777 DartEntry::InvokeFunction(func, Object::empty_array())); | |
778 return result.raw(); | |
779 } else { | |
780 Thread* const thread = Thread::Current(); | |
781 Isolate* const isolate = thread->isolate(); | |
782 const Object& result = | |
783 PassiveObject::Handle(isolate->object_store()->sticky_error()); | |
784 isolate->object_store()->clear_sticky_error(); | |
785 return result.raw(); | |
786 } | |
787 UNREACHABLE(); | |
788 return Object::null(); | |
789 } | |
790 | |
791 | |
592 void Precompiler::AddFunction(const Function& function) { | 792 void Precompiler::AddFunction(const Function& function) { |
593 if (enqueued_functions_.Lookup(&function) != NULL) return; | 793 if (enqueued_functions_.Lookup(&function) != NULL) return; |
594 | 794 |
595 enqueued_functions_.Insert(&Function::ZoneHandle(Z, function.raw())); | 795 enqueued_functions_.Insert(&Function::ZoneHandle(Z, function.raw())); |
596 pending_functions_.Add(function); | 796 pending_functions_.Add(function); |
597 changed_ = true; | 797 changed_ = true; |
598 } | 798 } |
599 | 799 |
600 | 800 |
601 bool Precompiler::IsSent(const String& selector) { | 801 bool Precompiler::IsSent(const String& selector) { |
(...skipping 663 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1265 while (it.HasNext()) { | 1465 while (it.HasNext()) { |
1266 cls = it.GetNextClass(); | 1466 cls = it.GetNextClass(); |
1267 if (cls.IsDynamicClass()) { | 1467 if (cls.IsDynamicClass()) { |
1268 continue; // class 'dynamic' is in the read-only VM isolate. | 1468 continue; // class 'dynamic' is in the read-only VM isolate. |
1269 } | 1469 } |
1270 cls.set_is_allocated(false); | 1470 cls.set_is_allocated(false); |
1271 } | 1471 } |
1272 } | 1472 } |
1273 } | 1473 } |
1274 | 1474 |
1475 | |
1476 void PrecompileParsedFunctionHelper::FinalizeCompilation( | |
1477 Assembler* assembler, | |
1478 FlowGraphCompiler* graph_compiler, | |
1479 FlowGraph* flow_graph) { | |
1480 const Function& function = parsed_function()->function(); | |
1481 Zone* const zone = thread()->zone(); | |
1482 | |
1483 CSTAT_TIMER_SCOPE(thread(), codefinalizer_timer); | |
1484 // CreateDeoptInfo uses the object pool and needs to be done before | |
1485 // FinalizeCode. | |
1486 const Array& deopt_info_array = | |
1487 Array::Handle(zone, graph_compiler->CreateDeoptInfo(assembler)); | |
1488 INC_STAT(thread(), total_code_size, | |
1489 deopt_info_array.Length() * sizeof(uword)); | |
1490 // Allocates instruction object. Since this occurs only at safepoint, | |
1491 // there can be no concurrent access to the instruction page. | |
1492 const Code& code = Code::Handle( | |
1493 Code::FinalizeCode(function, assembler, optimized())); | |
1494 code.set_is_optimized(optimized()); | |
1495 code.set_owner(function); | |
1496 if (!function.IsOptimizable()) { | |
1497 // A function with huge unoptimized code can become non-optimizable | |
1498 // after generating unoptimized code. | |
1499 function.set_usage_counter(INT_MIN); | |
1500 } | |
1501 | |
1502 const Array& intervals = graph_compiler->inlined_code_intervals(); | |
1503 INC_STAT(thread(), total_code_size, | |
1504 intervals.Length() * sizeof(uword)); | |
1505 code.SetInlinedIntervals(intervals); | |
1506 | |
1507 const Array& inlined_id_array = | |
1508 Array::Handle(zone, graph_compiler->InliningIdToFunction()); | |
1509 INC_STAT(thread(), total_code_size, | |
1510 inlined_id_array.Length() * sizeof(uword)); | |
1511 code.SetInlinedIdToFunction(inlined_id_array); | |
1512 | |
1513 const Array& caller_inlining_id_map_array = | |
1514 Array::Handle(zone, graph_compiler->CallerInliningIdMap()); | |
1515 INC_STAT(thread(), total_code_size, | |
1516 caller_inlining_id_map_array.Length() * sizeof(uword)); | |
1517 code.SetInlinedCallerIdMap(caller_inlining_id_map_array); | |
1518 | |
1519 graph_compiler->FinalizePcDescriptors(code); | |
1520 code.set_deopt_info_array(deopt_info_array); | |
1521 | |
1522 graph_compiler->FinalizeStackmaps(code); | |
1523 graph_compiler->FinalizeVarDescriptors(code); | |
1524 graph_compiler->FinalizeExceptionHandlers(code); | |
1525 graph_compiler->FinalizeStaticCallTargetsTable(code); | |
1526 | |
1527 if (optimized()) { | |
1528 // Installs code while at safepoint. | |
1529 ASSERT(thread()->IsMutatorThread()); | |
1530 function.InstallOptimizedCode(code, /* is_osr = */ false); | |
1531 } else { // not optimized. | |
1532 function.set_unoptimized_code(code); | |
1533 function.AttachCode(code); | |
1534 } | |
1535 ASSERT(!parsed_function()->HasDeferredPrefixes()); | |
1536 ASSERT(FLAG_load_deferred_eagerly); | |
1537 } | |
1538 | |
1539 | |
1540 // Return false if bailed out. | |
1541 // If optimized_result_code is not NULL then it is caller's responsibility | |
1542 // to install code. | |
1543 bool PrecompileParsedFunctionHelper::Compile(CompilationPipeline* pipeline) { | |
1544 ASSERT(FLAG_precompilation); | |
1545 const Function& function = parsed_function()->function(); | |
1546 if (optimized() && !function.IsOptimizable()) { | |
1547 return false; | |
1548 } | |
1549 bool is_compiled = false; | |
1550 Zone* const zone = thread()->zone(); | |
1551 TimelineStream* compiler_timeline = isolate()->GetCompilerStream(); | |
1552 CSTAT_TIMER_SCOPE(thread(), codegen_timer); | |
1553 HANDLESCOPE(thread()); | |
1554 | |
1555 // We may reattempt compilation if the function needs to be assembled using | |
1556 // far branches on ARM and MIPS. In the else branch of the setjmp call, | |
1557 // done is set to false, and use_far_branches is set to true if there is a | |
1558 // longjmp from the ARM or MIPS assemblers. In all other paths through this | |
1559 // while loop, done is set to true. use_far_branches is always false on ia32 | |
1560 // and x64. | |
1561 bool done = false; | |
1562 // volatile because the variable may be clobbered by a longjmp. | |
1563 volatile bool use_far_branches = false; | |
1564 volatile bool use_speculative_inlining = | |
1565 FLAG_max_speculative_inlining_attempts > 0; | |
1566 GrowableArray<intptr_t> inlining_black_list; | |
1567 | |
1568 while (!done) { | |
1569 const intptr_t prev_deopt_id = thread()->deopt_id(); | |
1570 thread()->set_deopt_id(0); | |
1571 LongJumpScope jump; | |
1572 const intptr_t val = setjmp(*jump.Set()); | |
1573 if (val == 0) { | |
1574 FlowGraph* flow_graph = NULL; | |
1575 | |
1576 // Class hierarchy analysis is registered with the isolate in the | |
1577 // constructor and unregisters itself upon destruction. | |
1578 CHA cha(thread()); | |
1579 | |
1580 // TimerScope needs an isolate to be properly terminated in case of a | |
1581 // LongJump. | |
1582 { | |
1583 CSTAT_TIMER_SCOPE(thread(), graphbuilder_timer); | |
1584 ZoneGrowableArray<const ICData*>* ic_data_array = | |
1585 new(zone) ZoneGrowableArray<const ICData*>(); | |
1586 TimelineDurationScope tds(thread(), | |
1587 compiler_timeline, | |
1588 "BuildFlowGraph"); | |
1589 flow_graph = pipeline->BuildFlowGraph(zone, | |
1590 parsed_function(), | |
1591 *ic_data_array, | |
1592 Compiler::kNoOSRDeoptId); | |
1593 } | |
1594 | |
1595 const bool print_flow_graph = | |
1596 (FLAG_print_flow_graph || | |
1597 (optimized() && FLAG_print_flow_graph_optimized)) && | |
1598 FlowGraphPrinter::ShouldPrint(function); | |
1599 | |
1600 if (print_flow_graph) { | |
1601 FlowGraphPrinter::PrintGraph("Before Optimizations", flow_graph); | |
1602 } | |
1603 | |
1604 if (optimized()) { | |
1605 TimelineDurationScope tds(thread(), | |
1606 compiler_timeline, | |
1607 "ComputeSSA"); | |
1608 CSTAT_TIMER_SCOPE(thread(), ssa_timer); | |
1609 // Transform to SSA (virtual register 0 and no inlining arguments). | |
1610 flow_graph->ComputeSSA(0, NULL); | |
1611 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1612 if (print_flow_graph) { | |
1613 FlowGraphPrinter::PrintGraph("After SSA", flow_graph); | |
1614 } | |
1615 } | |
1616 | |
1617 // Maps inline_id_to_function[inline_id] -> function. Top scope | |
1618 // function has inline_id 0. The map is populated by the inliner. | |
1619 GrowableArray<const Function*> inline_id_to_function; | |
1620 // For a given inlining-id(index) specifies the caller's inlining-id. | |
1621 GrowableArray<intptr_t> caller_inline_id; | |
1622 // Collect all instance fields that are loaded in the graph and | |
1623 // have non-generic type feedback attached to them that can | |
1624 // potentially affect optimizations. | |
1625 if (optimized()) { | |
1626 TimelineDurationScope tds(thread(), | |
1627 compiler_timeline, | |
1628 "OptimizationPasses"); | |
1629 inline_id_to_function.Add(&function); | |
1630 // Top scope function has no caller (-1). | |
1631 caller_inline_id.Add(-1); | |
1632 CSTAT_TIMER_SCOPE(thread(), graphoptimizer_timer); | |
1633 | |
1634 FlowGraphOptimizer optimizer(flow_graph, | |
1635 use_speculative_inlining, | |
1636 &inlining_black_list); | |
1637 optimizer.PopulateWithICData(); | |
1638 | |
1639 optimizer.ApplyClassIds(); | |
1640 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1641 | |
1642 FlowGraphTypePropagator::Propagate(flow_graph); | |
1643 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1644 | |
1645 optimizer.ApplyICData(); | |
1646 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1647 | |
1648 // Optimize (a << b) & c patterns, merge operations. | |
1649 // Run early in order to have more opportunity to optimize left shifts. | |
1650 optimizer.TryOptimizePatterns(); | |
1651 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1652 | |
1653 FlowGraphInliner::SetInliningId(flow_graph, 0); | |
1654 | |
1655 // Inlining (mutates the flow graph) | |
1656 if (FLAG_use_inlining) { | |
1657 TimelineDurationScope tds2(thread(), | |
1658 compiler_timeline, | |
1659 "Inlining"); | |
1660 CSTAT_TIMER_SCOPE(thread(), graphinliner_timer); | |
1661 // Propagate types to create more inlining opportunities. | |
1662 FlowGraphTypePropagator::Propagate(flow_graph); | |
1663 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1664 | |
1665 // Use propagated class-ids to create more inlining opportunities. | |
1666 optimizer.ApplyClassIds(); | |
1667 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1668 | |
1669 FlowGraphInliner inliner(flow_graph, | |
1670 &inline_id_to_function, | |
1671 &caller_inline_id, | |
1672 use_speculative_inlining, | |
1673 &inlining_black_list); | |
1674 inliner.Inline(); | |
1675 // Use lists are maintained and validated by the inliner. | |
1676 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1677 } | |
1678 | |
1679 // Propagate types and eliminate more type tests. | |
1680 FlowGraphTypePropagator::Propagate(flow_graph); | |
1681 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1682 | |
1683 { | |
1684 TimelineDurationScope tds2(thread(), | |
1685 compiler_timeline, | |
1686 "ApplyClassIds"); | |
1687 // Use propagated class-ids to optimize further. | |
1688 optimizer.ApplyClassIds(); | |
1689 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1690 } | |
1691 | |
1692 // Propagate types for potentially newly added instructions by | |
1693 // ApplyClassIds(). Must occur before canonicalization. | |
1694 FlowGraphTypePropagator::Propagate(flow_graph); | |
1695 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1696 | |
1697 // Do optimizations that depend on the propagated type information. | |
1698 if (optimizer.Canonicalize()) { | |
1699 // Invoke Canonicalize twice in order to fully canonicalize patterns | |
1700 // like "if (a & const == 0) { }". | |
1701 optimizer.Canonicalize(); | |
1702 } | |
1703 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1704 | |
1705 { | |
1706 TimelineDurationScope tds2(thread(), | |
1707 compiler_timeline, | |
1708 "BranchSimplifier"); | |
1709 BranchSimplifier::Simplify(flow_graph); | |
1710 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1711 | |
1712 IfConverter::Simplify(flow_graph); | |
1713 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1714 } | |
1715 | |
1716 if (FLAG_constant_propagation) { | |
1717 TimelineDurationScope tds2(thread(), | |
1718 compiler_timeline, | |
1719 "ConstantPropagation"); | |
1720 ConstantPropagator::Optimize(flow_graph); | |
1721 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1722 // A canonicalization pass to remove e.g. smi checks on smi constants. | |
1723 optimizer.Canonicalize(); | |
1724 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1725 // Canonicalization introduced more opportunities for constant | |
1726 // propagation. | |
1727 ConstantPropagator::Optimize(flow_graph); | |
1728 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1729 } | |
1730 | |
1731 // Optimistically convert loop phis that have a single non-smi input | |
1732 // coming from the loop pre-header into smi-phis. | |
1733 if (FLAG_loop_invariant_code_motion) { | |
1734 LICM licm(flow_graph); | |
1735 licm.OptimisticallySpecializeSmiPhis(); | |
1736 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1737 } | |
1738 | |
1739 // Propagate types and eliminate even more type tests. | |
1740 // Recompute types after constant propagation to infer more precise | |
1741 // types for uses that were previously reached by now eliminated phis. | |
1742 FlowGraphTypePropagator::Propagate(flow_graph); | |
1743 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1744 | |
1745 { | |
1746 TimelineDurationScope tds2(thread(), | |
1747 compiler_timeline, | |
1748 "SelectRepresentations"); | |
rmacnak
2016/02/04 00:36:39
Maybe we should skip these two.
Florian Schneider
2016/02/05 01:55:52
I'd rather leave them in for now. They may actuall
rmacnak
2016/02/08 22:43:23
Acknowledged.
| |
1749 // Where beneficial convert Smi operations into Int32 operations. | |
1750 // Only meanigful for 32bit platforms right now. | |
1751 optimizer.WidenSmiToInt32(); | |
1752 | |
1753 // Unbox doubles. Performed after constant propagation to minimize | |
1754 // interference from phis merging double values and tagged | |
1755 // values coming from dead paths. | |
1756 optimizer.SelectRepresentations(); | |
1757 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1758 } | |
1759 | |
1760 { | |
1761 TimelineDurationScope tds2(thread(), | |
1762 compiler_timeline, | |
1763 "CommonSubexpressionElinination"); | |
1764 if (FLAG_common_subexpression_elimination || | |
1765 FLAG_loop_invariant_code_motion) { | |
1766 flow_graph->ComputeBlockEffects(); | |
1767 } | |
1768 | |
1769 if (FLAG_common_subexpression_elimination) { | |
1770 if (DominatorBasedCSE::Optimize(flow_graph)) { | |
1771 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1772 optimizer.Canonicalize(); | |
1773 // Do another round of CSE to take secondary effects into account: | |
1774 // e.g. when eliminating dependent loads (a.x[0] + a.x[0]) | |
1775 // TODO(fschneider): Change to a one-pass optimization pass. | |
1776 if (DominatorBasedCSE::Optimize(flow_graph)) { | |
1777 optimizer.Canonicalize(); | |
1778 } | |
1779 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1780 } | |
1781 } | |
1782 | |
1783 // Run loop-invariant code motion right after load elimination since | |
1784 // it depends on the numbering of loads from the previous | |
1785 // load-elimination. | |
1786 if (FLAG_loop_invariant_code_motion) { | |
1787 LICM licm(flow_graph); | |
1788 licm.Optimize(); | |
1789 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1790 } | |
1791 flow_graph->RemoveRedefinitions(); | |
1792 } | |
1793 | |
1794 // Optimize (a << b) & c patterns, merge operations. | |
1795 // Run after CSE in order to have more opportunity to merge | |
1796 // instructions that have same inputs. | |
1797 optimizer.TryOptimizePatterns(); | |
1798 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1799 | |
1800 { | |
1801 TimelineDurationScope tds2(thread(), | |
1802 compiler_timeline, | |
1803 "DeadStoreElimination"); | |
1804 DeadStoreElimination::Optimize(flow_graph); | |
1805 } | |
1806 | |
1807 if (FLAG_range_analysis) { | |
1808 TimelineDurationScope tds2(thread(), | |
1809 compiler_timeline, | |
1810 "RangeAnalysis"); | |
1811 // Propagate types after store-load-forwarding. Some phis may have | |
1812 // become smi phis that can be processed by range analysis. | |
1813 FlowGraphTypePropagator::Propagate(flow_graph); | |
1814 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1815 | |
1816 // We have to perform range analysis after LICM because it | |
1817 // optimistically moves CheckSmi through phis into loop preheaders | |
1818 // making some phis smi. | |
1819 optimizer.InferIntRanges(); | |
1820 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1821 } | |
1822 | |
1823 if (FLAG_constant_propagation) { | |
1824 TimelineDurationScope tds2(thread(), | |
1825 compiler_timeline, | |
1826 "ConstantPropagator::OptimizeBranches"); | |
1827 // Constant propagation can use information from range analysis to | |
1828 // find unreachable branch targets and eliminate branches that have | |
1829 // the same true- and false-target. | |
1830 ConstantPropagator::OptimizeBranches(flow_graph); | |
1831 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1832 } | |
1833 | |
1834 // Recompute types after code movement was done to ensure correct | |
1835 // reaching types for hoisted values. | |
1836 FlowGraphTypePropagator::Propagate(flow_graph); | |
1837 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1838 | |
1839 { | |
1840 TimelineDurationScope tds2(thread(), | |
1841 compiler_timeline, | |
1842 "TryCatchAnalyzer::Optimize"); | |
1843 // Optimize try-blocks. | |
1844 TryCatchAnalyzer::Optimize(flow_graph); | |
1845 } | |
1846 | |
1847 // Detach environments from the instructions that can't deoptimize. | |
1848 // Do it before we attempt to perform allocation sinking to minimize | |
1849 // amount of materializations it has to perform. | |
1850 optimizer.EliminateEnvironments(); | |
1851 | |
1852 { | |
1853 TimelineDurationScope tds2(thread(), | |
1854 compiler_timeline, | |
1855 "EliminateDeadPhis"); | |
1856 DeadCodeElimination::EliminateDeadPhis(flow_graph); | |
1857 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1858 } | |
1859 | |
1860 if (optimizer.Canonicalize()) { | |
1861 optimizer.Canonicalize(); | |
1862 } | |
1863 | |
1864 // Attempt to sink allocations of temporary non-escaping objects to | |
1865 // the deoptimization path. | |
1866 AllocationSinking* sinking = NULL; | |
1867 if (FLAG_allocation_sinking && | |
1868 (flow_graph->graph_entry()->SuccessorCount() == 1)) { | |
1869 TimelineDurationScope tds2(thread(), | |
1870 compiler_timeline, | |
1871 "AllocationSinking::Optimize"); | |
1872 // TODO(fschneider): Support allocation sinking with try-catch. | |
1873 sinking = new AllocationSinking(flow_graph); | |
1874 sinking->Optimize(); | |
1875 } | |
1876 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1877 | |
1878 DeadCodeElimination::EliminateDeadPhis(flow_graph); | |
1879 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1880 | |
1881 FlowGraphTypePropagator::Propagate(flow_graph); | |
1882 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1883 | |
1884 { | |
1885 TimelineDurationScope tds2(thread(), | |
1886 compiler_timeline, | |
1887 "SelectRepresentations"); | |
1888 // Ensure that all phis inserted by optimization passes have | |
1889 // consistent representations. | |
1890 optimizer.SelectRepresentations(); | |
1891 } | |
1892 | |
1893 if (optimizer.Canonicalize()) { | |
1894 // To fully remove redundant boxing (e.g. BoxDouble used only in | |
1895 // environments and UnboxDouble instructions) instruction we | |
1896 // first need to replace all their uses and then fold them away. | |
1897 // For now we just repeat Canonicalize twice to do that. | |
1898 // TODO(vegorov): implement a separate representation folding pass. | |
1899 optimizer.Canonicalize(); | |
1900 } | |
1901 DEBUG_ASSERT(flow_graph->VerifyUseLists()); | |
1902 | |
1903 if (sinking != NULL) { | |
1904 TimelineDurationScope tds2( | |
1905 thread(), | |
1906 compiler_timeline, | |
1907 "AllocationSinking::DetachMaterializations"); | |
1908 // Remove all MaterializeObject instructions inserted by allocation | |
1909 // sinking from the flow graph and let them float on the side | |
1910 // referenced only from environments. Register allocator will consider | |
1911 // them as part of a deoptimization environment. | |
1912 sinking->DetachMaterializations(); | |
1913 } | |
1914 | |
1915 // Compute and store graph informations (call & instruction counts) | |
1916 // to be later used by the inliner. | |
1917 FlowGraphInliner::CollectGraphInfo(flow_graph, true); | |
1918 | |
1919 { | |
1920 TimelineDurationScope tds2(thread(), | |
1921 compiler_timeline, | |
1922 "AllocateRegisters"); | |
1923 // Perform register allocation on the SSA graph. | |
1924 FlowGraphAllocator allocator(*flow_graph); | |
1925 allocator.AllocateRegisters(); | |
1926 } | |
1927 | |
1928 if (print_flow_graph) { | |
1929 FlowGraphPrinter::PrintGraph("After Optimizations", flow_graph); | |
1930 } | |
1931 } | |
1932 | |
1933 ASSERT(inline_id_to_function.length() == caller_inline_id.length()); | |
1934 Assembler assembler(use_far_branches); | |
1935 FlowGraphCompiler graph_compiler(&assembler, flow_graph, | |
1936 *parsed_function(), optimized(), | |
1937 inline_id_to_function, | |
1938 caller_inline_id); | |
1939 { | |
1940 CSTAT_TIMER_SCOPE(thread(), graphcompiler_timer); | |
1941 TimelineDurationScope tds(thread(), | |
1942 compiler_timeline, | |
1943 "CompileGraph"); | |
1944 graph_compiler.CompileGraph(); | |
1945 pipeline->FinalizeCompilation(); | |
1946 } | |
1947 { | |
1948 TimelineDurationScope tds(thread(), | |
1949 compiler_timeline, | |
1950 "FinalizeCompilation"); | |
1951 ASSERT(thread()->IsMutatorThread()); | |
1952 FinalizeCompilation(&assembler, &graph_compiler, flow_graph); | |
1953 } | |
1954 // Mark that this isolate now has compiled code. | |
1955 isolate()->set_has_compiled_code(true); | |
1956 // Exit the loop and the function with the correct result value. | |
1957 is_compiled = true; | |
1958 done = true; | |
1959 } else { | |
1960 // We bailed out or we encountered an error. | |
1961 const Error& error = Error::Handle( | |
1962 isolate()->object_store()->sticky_error()); | |
1963 | |
1964 if (error.raw() == Object::branch_offset_error().raw()) { | |
1965 // Compilation failed due to an out of range branch offset in the | |
1966 // assembler. We try again (done = false) with far branches enabled. | |
1967 done = false; | |
1968 ASSERT(!use_far_branches); | |
1969 use_far_branches = true; | |
1970 } else if (error.raw() == Object::speculative_inlining_error().raw()) { | |
1971 // The return value of setjmp is the deopt id of the check instruction | |
1972 // that caused the bailout. | |
1973 done = false; | |
1974 #if defined(DEBUG) | |
1975 ASSERT(use_speculative_inlining); | |
1976 for (intptr_t i = 0; i < inlining_black_list.length(); ++i) { | |
1977 ASSERT(inlining_black_list[i] != val); | |
1978 } | |
1979 #endif | |
1980 inlining_black_list.Add(val); | |
1981 const intptr_t max_attempts = FLAG_max_speculative_inlining_attempts; | |
1982 if (inlining_black_list.length() >= max_attempts) { | |
1983 use_speculative_inlining = false; | |
1984 if (FLAG_trace_compiler || FLAG_trace_optimizing_compiler) { | |
1985 THR_Print("Disabled speculative inlining after %" Pd " attempts.\n", | |
1986 inlining_black_list.length()); | |
1987 } | |
1988 } | |
1989 } else { | |
1990 // If the error isn't due to an out of range branch offset, we don't | |
1991 // try again (done = true), and indicate that we did not finish | |
1992 // compiling (is_compiled = false). | |
1993 if (FLAG_trace_bailout) { | |
1994 THR_Print("%s\n", error.ToErrorCString()); | |
1995 } | |
1996 done = true; | |
1997 } | |
1998 | |
1999 // Clear the error if it was not a real error, but just a bailout. | |
2000 if (error.IsLanguageError() && | |
2001 (LanguageError::Cast(error).kind() == Report::kBailout)) { | |
2002 isolate()->object_store()->clear_sticky_error(); | |
2003 } | |
2004 is_compiled = false; | |
2005 } | |
2006 // Reset global isolate state. | |
2007 thread()->set_deopt_id(prev_deopt_id); | |
2008 } | |
2009 return is_compiled; | |
2010 } | |
2011 | |
2012 | |
2013 static RawError* PrecompileFunctionHelper(CompilationPipeline* pipeline, | |
2014 const Function& function, | |
2015 bool optimized) { | |
2016 // Check that we optimize, except if the function is not optimizable. | |
2017 ASSERT(FLAG_precompilation); | |
2018 ASSERT(!function.IsOptimizable() || optimized); | |
2019 ASSERT(!function.HasCode()); | |
2020 LongJumpScope jump; | |
2021 if (setjmp(*jump.Set()) == 0) { | |
2022 Thread* const thread = Thread::Current(); | |
2023 Isolate* const isolate = thread->isolate(); | |
2024 StackZone stack_zone(thread); | |
2025 Zone* const zone = stack_zone.GetZone(); | |
2026 const bool trace_compiler = | |
2027 FLAG_trace_compiler || | |
2028 (FLAG_trace_optimizing_compiler && optimized); | |
2029 Timer per_compile_timer(trace_compiler, "Compilation time"); | |
2030 per_compile_timer.Start(); | |
2031 | |
2032 ParsedFunction* parsed_function = new(zone) ParsedFunction( | |
2033 thread, Function::ZoneHandle(zone, function.raw())); | |
2034 if (trace_compiler) { | |
2035 THR_Print( | |
2036 "Precompiling %sfunction: '%s' @ token %" Pd ", size %" Pd "\n", | |
2037 (optimized ? "optimized " : ""), | |
2038 function.ToFullyQualifiedCString(), | |
2039 function.token_pos().Pos(), | |
2040 (function.end_token_pos().Pos() - function.token_pos().Pos())); | |
2041 } | |
2042 INC_STAT(thread, num_functions_compiled, 1); | |
2043 if (optimized) { | |
2044 INC_STAT(thread, num_functions_optimized, 1); | |
2045 } | |
2046 { | |
2047 HANDLESCOPE(thread); | |
2048 const int64_t num_tokens_before = STAT_VALUE(thread, num_tokens_consumed); | |
2049 pipeline->ParseFunction(parsed_function); | |
2050 const int64_t num_tokens_after = STAT_VALUE(thread, num_tokens_consumed); | |
2051 INC_STAT(thread, | |
2052 num_func_tokens_compiled, | |
2053 num_tokens_after - num_tokens_before); | |
2054 } | |
2055 | |
2056 PrecompileParsedFunctionHelper helper(parsed_function, optimized); | |
2057 const bool success = helper.Compile(pipeline); | |
2058 if (!success) { | |
2059 // Encountered error. | |
2060 Error& error = Error::Handle(); | |
2061 // We got an error during compilation. | |
2062 error = isolate->object_store()->sticky_error(); | |
2063 isolate->object_store()->clear_sticky_error(); | |
2064 ASSERT(error.IsLanguageError() && | |
2065 LanguageError::Cast(error).kind() != Report::kBailout); | |
2066 return error.raw(); | |
2067 } | |
2068 | |
2069 per_compile_timer.Stop(); | |
2070 | |
2071 if (trace_compiler && success) { | |
2072 THR_Print("--> '%s' entry: %#" Px " size: %" Pd " time: %" Pd64 " us\n", | |
2073 function.ToFullyQualifiedCString(), | |
2074 Code::Handle(function.CurrentCode()).EntryPoint(), | |
2075 Code::Handle(function.CurrentCode()).Size(), | |
2076 per_compile_timer.TotalElapsedTime()); | |
2077 } | |
2078 | |
2079 if (FLAG_disassemble && FlowGraphPrinter::ShouldPrint(function)) { | |
2080 Compiler::DisassembleCode(function, optimized); | |
2081 } else if (FLAG_disassemble_optimized && | |
2082 optimized && | |
2083 FlowGraphPrinter::ShouldPrint(function)) { | |
2084 // TODO(fschneider): Print unoptimized code along with the optimized code. | |
2085 THR_Print("*** BEGIN CODE\n"); | |
2086 Compiler::DisassembleCode(function, true); | |
2087 THR_Print("*** END CODE\n"); | |
2088 } | |
2089 return Error::null(); | |
2090 } else { | |
2091 Thread* const thread = Thread::Current(); | |
2092 Isolate* const isolate = thread->isolate(); | |
2093 StackZone stack_zone(thread); | |
2094 Error& error = Error::Handle(); | |
2095 // We got an error during compilation. | |
2096 error = isolate->object_store()->sticky_error(); | |
2097 isolate->object_store()->clear_sticky_error(); | |
2098 // Precompilation may encounter compile-time errors. | |
2099 // Do not attempt to optimize functions that can cause errors. | |
2100 function.set_is_optimizable(false); | |
2101 return error.raw(); | |
2102 } | |
2103 UNREACHABLE(); | |
2104 return Error::null(); | |
2105 } | |
2106 | |
2107 | |
2108 RawError* Precompiler::CompileFunction(Thread* thread, | |
2109 const Function& function) { | |
2110 VMTagScope tagScope(thread, VMTag::kCompileUnoptimizedTagId); | |
2111 TIMELINE_FUNCTION_COMPILATION_DURATION(thread, "Function", function); | |
2112 | |
2113 CompilationPipeline* pipeline = | |
2114 CompilationPipeline::New(thread->zone(), function); | |
2115 | |
2116 ASSERT(FLAG_precompilation); | |
2117 const bool optimized = function.IsOptimizable(); // False for natives. | |
2118 return PrecompileFunctionHelper(pipeline, function, optimized); | |
2119 } | |
2120 | |
1275 } // namespace dart | 2121 } // namespace dart |
OLD | NEW |