Chromium Code Reviews| 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 |