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

Side by Side 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: 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 unified diff | Download patch
« runtime/vm/compiler.cc ('K') | « runtime/vm/precompiler.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
OLDNEW
« runtime/vm/compiler.cc ('K') | « runtime/vm/precompiler.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698