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

Side by Side Diff: runtime/vm/flow_graph_inliner.cc

Issue 1568613007: Don't suppress compile-time errors when inlining. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 11 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
« no previous file with comments | « runtime/vm/compiler.cc ('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) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, 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/flow_graph_inliner.h" 5 #include "vm/flow_graph_inliner.h"
6 6
7 #include "vm/block_scheduler.h" 7 #include "vm/block_scheduler.h"
8 #include "vm/compiler.h" 8 #include "vm/compiler.h"
9 #include "vm/flags.h" 9 #include "vm/flags.h"
10 #include "vm/flow_graph.h" 10 #include "vm/flow_graph.h"
(...skipping 669 matching lines...) Expand 10 before | Expand all | Expand 10 after
680 inlining_recursion_depth_ >= FLAG_inlining_recursion_depth_threshold) { 680 inlining_recursion_depth_ >= FLAG_inlining_recursion_depth_threshold) {
681 TRACE_INLINING(THR_Print(" Bailout: recursive function\n")); 681 TRACE_INLINING(THR_Print(" Bailout: recursive function\n"));
682 PRINT_INLINING_TREE("Recursive function", 682 PRINT_INLINING_TREE("Recursive function",
683 &call_data->caller, &function, call_data->call); 683 &call_data->caller, &function, call_data->call);
684 return false; 684 return false;
685 } 685 }
686 686
687 // Save and clear deopt id. 687 // Save and clear deopt id.
688 const intptr_t prev_deopt_id = thread()->deopt_id(); 688 const intptr_t prev_deopt_id = thread()->deopt_id();
689 thread()->set_deopt_id(0); 689 thread()->set_deopt_id(0);
690 // Install bailout jump. 690 Error& error = Error::Handle();
691 LongJumpScope jump; 691 {
692 if (setjmp(*jump.Set()) == 0) { 692 // Install bailout jump.
693 // Parse the callee function. 693 LongJumpScope jump;
694 bool in_cache; 694 if (setjmp(*jump.Set()) == 0) {
695 ParsedFunction* parsed_function; 695 // Parse the callee function.
696 { 696 bool in_cache;
697 CSTAT_TIMER_SCOPE(thread(), graphinliner_parse_timer); 697 ParsedFunction* parsed_function;
698 parsed_function = GetParsedFunction(function, &in_cache); 698 {
699 } 699 CSTAT_TIMER_SCOPE(thread(), graphinliner_parse_timer);
700 700 parsed_function = GetParsedFunction(function, &in_cache);
701 // Load IC data for the callee. 701 }
702 ZoneGrowableArray<const ICData*>* ic_data_array = 702
703 new(Z) ZoneGrowableArray<const ICData*>(); 703 // Load IC data for the callee.
704 const bool clone_descriptors = Compiler::IsBackgroundCompilation(); 704 ZoneGrowableArray<const ICData*>* ic_data_array =
705 function.RestoreICDataMap(ic_data_array, clone_descriptors); 705 new(Z) ZoneGrowableArray<const ICData*>();
706 706 const bool clone_descriptors = Compiler::IsBackgroundCompilation();
707 // Build the callee graph. 707 function.RestoreICDataMap(ic_data_array, clone_descriptors);
708 InlineExitCollector* exit_collector = 708
709 new(Z) InlineExitCollector(caller_graph_, call); 709 // Build the callee graph.
710 FlowGraphBuilder builder(*parsed_function, 710 InlineExitCollector* exit_collector =
711 *ic_data_array, 711 new(Z) InlineExitCollector(caller_graph_, call);
712 exit_collector, 712 FlowGraphBuilder builder(*parsed_function,
713 Compiler::kNoOSRDeoptId); 713 *ic_data_array,
714 builder.SetInitialBlockId(caller_graph_->max_block_id()); 714 exit_collector,
715 FlowGraph* callee_graph; 715 Compiler::kNoOSRDeoptId);
716 { 716 builder.SetInitialBlockId(caller_graph_->max_block_id());
717 CSTAT_TIMER_SCOPE(thread(), graphinliner_build_timer); 717 FlowGraph* callee_graph;
718 callee_graph = builder.BuildGraph(); 718 {
719 } 719 CSTAT_TIMER_SCOPE(thread(), graphinliner_build_timer);
720 720 callee_graph = builder.BuildGraph();
721 // The parameter stubs are a copy of the actual arguments providing 721 }
722 // concrete information about the values, for example constant values, 722
723 // without linking between the caller and callee graphs. 723 // The parameter stubs are a copy of the actual arguments providing
724 // TODO(zerny): Put more information in the stubs, eg, type information. 724 // concrete information about the values, for example constant values,
725 ZoneGrowableArray<Definition*>* param_stubs = 725 // without linking between the caller and callee graphs.
726 new(Z) ZoneGrowableArray<Definition*>( 726 // TODO(zerny): Put more information in the stubs, eg, type information.
727 function.NumParameters()); 727 ZoneGrowableArray<Definition*>* param_stubs =
728 728 new(Z) ZoneGrowableArray<Definition*>(
729 // Create a parameter stub for each fixed positional parameter. 729 function.NumParameters());
730 for (intptr_t i = 0; i < function.num_fixed_parameters(); ++i) { 730
731 param_stubs->Add(CreateParameterStub(i, (*arguments)[i], callee_graph)); 731 // Create a parameter stub for each fixed positional parameter.
732 } 732 for (intptr_t i = 0; i < function.num_fixed_parameters(); ++i) {
733 733 param_stubs->Add(CreateParameterStub(i, (*arguments)[i],
734 // If the callee has optional parameters, rebuild the argument and stub 734 callee_graph));
735 // arrays so that actual arguments are in one-to-one with the formal 735 }
736 // parameters. 736
737 if (function.HasOptionalParameters()) { 737 // If the callee has optional parameters, rebuild the argument and stub
738 TRACE_INLINING(THR_Print(" adjusting for optional parameters\n")); 738 // arrays so that actual arguments are in one-to-one with the formal
739 if (!AdjustForOptionalParameters(*parsed_function, 739 // parameters.
740 argument_names, 740 if (function.HasOptionalParameters()) {
741 arguments, 741 TRACE_INLINING(THR_Print(" adjusting for optional parameters\n"));
742 param_stubs, 742 if (!AdjustForOptionalParameters(*parsed_function,
743 callee_graph)) { 743 argument_names,
744 function.set_is_inlinable(false); 744 arguments,
745 TRACE_INLINING(THR_Print(" Bailout: optional arg mismatch\n")); 745 param_stubs,
746 PRINT_INLINING_TREE("Optional arg mismatch", 746 callee_graph)) {
747 function.set_is_inlinable(false);
748 TRACE_INLINING(THR_Print(" Bailout: optional arg mismatch\n"));
749 PRINT_INLINING_TREE("Optional arg mismatch",
750 &call_data->caller, &function, call_data->call);
751 return false;
752 }
753 }
754
755 // After treating optional parameters the actual/formal count must
756 // match.
757 ASSERT(arguments->length() == function.NumParameters());
758 ASSERT(param_stubs->length() == callee_graph->parameter_count());
759
760 // Update try-index of the callee graph.
761 BlockEntryInstr* call_block = call_data->call->GetBlock();
762 if (call_block->InsideTryBlock()) {
763 intptr_t try_index = call_block->try_index();
764 for (BlockIterator it = callee_graph->reverse_postorder_iterator();
765 !it.Done(); it.Advance()) {
766 BlockEntryInstr* block = it.Current();
767 block->set_try_index(try_index);
768 }
769 }
770
771 BlockScheduler block_scheduler(callee_graph);
772 block_scheduler.AssignEdgeWeights();
773
774 {
775 CSTAT_TIMER_SCOPE(thread(), graphinliner_ssa_timer);
776 // Compute SSA on the callee graph, catching bailouts.
777 callee_graph->ComputeSSA(caller_graph_->max_virtual_register_number(),
778 param_stubs);
779 DEBUG_ASSERT(callee_graph->VerifyUseLists());
780 }
781
782 {
783 CSTAT_TIMER_SCOPE(thread(), graphinliner_opt_timer);
784 // TODO(fschneider): Improve suppression of speculative inlining.
785 // Deopt-ids overlap between caller and callee.
786 FlowGraphOptimizer optimizer(callee_graph,
787 inliner_->use_speculative_inlining_,
788 inliner_->inlining_black_list_);
789 if (Compiler::always_optimize()) {
790 optimizer.PopulateWithICData();
791
792 optimizer.ApplyClassIds();
793 DEBUG_ASSERT(callee_graph->VerifyUseLists());
794
795 FlowGraphTypePropagator::Propagate(callee_graph);
796 DEBUG_ASSERT(callee_graph->VerifyUseLists());
797 }
798 optimizer.ApplyICData();
799 DEBUG_ASSERT(callee_graph->VerifyUseLists());
800
801 // Optimize (a << b) & c patterns, merge instructions. Must occur
802 // before 'SelectRepresentations' which inserts conversion nodes.
803 optimizer.TryOptimizePatterns();
804 DEBUG_ASSERT(callee_graph->VerifyUseLists());
805 }
806
807 if (FLAG_trace_inlining &&
808 (FLAG_print_flow_graph || FLAG_print_flow_graph_optimized)) {
809 THR_Print("Callee graph for inlining %s\n",
810 function.ToFullyQualifiedCString());
811 FlowGraphPrinter printer(*callee_graph);
812 printer.PrintBlocks();
813 }
814
815 // Collect information about the call site and caller graph.
816 // TODO(zerny): Do this after CP and dead code elimination.
817 intptr_t constants_count = 0;
818 for (intptr_t i = 0; i < param_stubs->length(); ++i) {
819 if ((*param_stubs)[i]->IsConstant()) ++constants_count;
820 }
821
822 FlowGraphInliner::CollectGraphInfo(callee_graph);
823 const intptr_t size = function.optimized_instruction_count();
824 const intptr_t call_site_count = function.optimized_call_site_count();
825
826 function.set_optimized_instruction_count(size);
827 function.set_optimized_call_site_count(call_site_count);
828
829 // Use heuristics do decide if this call should be inlined.
830 if (!ShouldWeInline(function, size, call_site_count, constants_count)) {
831 // If size is larger than all thresholds, don't consider it again.
832 if ((size > FLAG_inlining_size_threshold) &&
833 (call_site_count > FLAG_inlining_callee_call_sites_threshold) &&
834 (size > FLAG_inlining_constant_arguments_min_size_threshold) &&
835 (size > FLAG_inlining_constant_arguments_max_size_threshold)) {
836 function.set_is_inlinable(false);
837 }
838 thread()->set_deopt_id(prev_deopt_id);
839 TRACE_INLINING(THR_Print(" Bailout: heuristics with "
840 "code size: %" Pd ", "
841 "call sites: %" Pd ", "
842 "const args: %" Pd "\n",
843 size,
844 call_site_count,
845 constants_count));
846 PRINT_INLINING_TREE("Heuristic fail",
747 &call_data->caller, &function, call_data->call); 847 &call_data->caller, &function, call_data->call);
748 return false; 848 return false;
749 } 849 }
850
851 // Inline dispatcher methods regardless of the current depth.
852 const intptr_t depth =
853 (function.IsInvokeFieldDispatcher() ||
854 function.IsNoSuchMethodDispatcher()) ? 0 : inlining_depth_;
855 collected_call_sites_->FindCallSites(callee_graph, depth,
856 &inlined_info_);
857
858 // Add the function to the cache.
859 if (!in_cache) {
860 function_cache_.Add(parsed_function);
861 }
862
863 // Build succeeded so we restore the bailout jump.
864 inlined_ = true;
865 inlined_size_ += size;
866 if (is_recursive_call) {
867 inlined_recursive_call_ = true;
868 }
869 thread()->set_deopt_id(prev_deopt_id);
870
871 call_data->callee_graph = callee_graph;
872 call_data->parameter_stubs = param_stubs;
873 call_data->exit_collector = exit_collector;
874
875 // When inlined, we add the guarded fields of the callee to the caller's
876 // list of guarded fields.
877 for (intptr_t i = 0;
878 i < callee_graph->guarded_fields()->length();
879 ++i) {
880 FlowGraph::AddToGuardedFields(caller_graph_->guarded_fields(),
881 (*callee_graph->guarded_fields())[i]);
882 }
883 // When inlined, we add the deferred prefixes of the callee to the
884 // caller's list of deferred prefixes.
885 caller_graph()->AddToDeferredPrefixes(
886 callee_graph->deferred_prefixes());
887
888 FlowGraphInliner::SetInliningId(callee_graph,
889 inliner_->NextInlineId(callee_graph->function(),
890 call_data->caller_inlining_id_));
891 TRACE_INLINING(THR_Print(" Success\n"));
892 PRINT_INLINING_TREE(NULL,
893 &call_data->caller, &function, call);
894 return true;
895 } else {
896 error = isolate()->object_store()->sticky_error();
897 isolate()->object_store()->clear_sticky_error();
898 ASSERT(error.IsLanguageError());
899
900 if (LanguageError::Cast(error).kind() == Report::kBailout) {
901 thread()->set_deopt_id(prev_deopt_id);
902 TRACE_INLINING(THR_Print(" Bailout: %s\n",
903 error.ToErrorCString()));
904 PRINT_INLINING_TREE("Bailout",
905 &call_data->caller, &function, call);
906 return false;
907 } else {
908 // Fall through to exit long jump scope.
909 }
750 } 910 }
751
752 // After treating optional parameters the actual/formal count must match.
753 ASSERT(arguments->length() == function.NumParameters());
754 ASSERT(param_stubs->length() == callee_graph->parameter_count());
755
756 // Update try-index of the callee graph.
757 BlockEntryInstr* call_block = call_data->call->GetBlock();
758 if (call_block->InsideTryBlock()) {
759 intptr_t try_index = call_block->try_index();
760 for (BlockIterator it = callee_graph->reverse_postorder_iterator();
761 !it.Done(); it.Advance()) {
762 BlockEntryInstr* block = it.Current();
763 block->set_try_index(try_index);
764 }
765 }
766
767 BlockScheduler block_scheduler(callee_graph);
768 block_scheduler.AssignEdgeWeights();
769
770 {
771 CSTAT_TIMER_SCOPE(thread(), graphinliner_ssa_timer);
772 // Compute SSA on the callee graph, catching bailouts.
773 callee_graph->ComputeSSA(caller_graph_->max_virtual_register_number(),
774 param_stubs);
775 DEBUG_ASSERT(callee_graph->VerifyUseLists());
776 }
777
778 {
779 CSTAT_TIMER_SCOPE(thread(), graphinliner_opt_timer);
780 // TODO(fschneider): Improve suppression of speculative inlining.
781 // Deopt-ids overlap between caller and callee.
782 FlowGraphOptimizer optimizer(callee_graph,
783 inliner_->use_speculative_inlining_,
784 inliner_->inlining_black_list_);
785 if (Compiler::always_optimize()) {
786 optimizer.PopulateWithICData();
787
788 optimizer.ApplyClassIds();
789 DEBUG_ASSERT(callee_graph->VerifyUseLists());
790
791 FlowGraphTypePropagator::Propagate(callee_graph);
792 DEBUG_ASSERT(callee_graph->VerifyUseLists());
793 }
794 optimizer.ApplyICData();
795 DEBUG_ASSERT(callee_graph->VerifyUseLists());
796
797 // Optimize (a << b) & c patterns, merge instructions. Must occur before
798 // 'SelectRepresentations' which inserts conversion nodes.
799 optimizer.TryOptimizePatterns();
800 DEBUG_ASSERT(callee_graph->VerifyUseLists());
801 }
802
803 if (FLAG_trace_inlining &&
804 (FLAG_print_flow_graph || FLAG_print_flow_graph_optimized)) {
805 THR_Print("Callee graph for inlining %s\n",
806 function.ToFullyQualifiedCString());
807 FlowGraphPrinter printer(*callee_graph);
808 printer.PrintBlocks();
809 }
810
811 // Collect information about the call site and caller graph.
812 // TODO(zerny): Do this after CP and dead code elimination.
813 intptr_t constants_count = 0;
814 for (intptr_t i = 0; i < param_stubs->length(); ++i) {
815 if ((*param_stubs)[i]->IsConstant()) ++constants_count;
816 }
817
818 FlowGraphInliner::CollectGraphInfo(callee_graph);
819 const intptr_t size = function.optimized_instruction_count();
820 const intptr_t call_site_count = function.optimized_call_site_count();
821
822 function.set_optimized_instruction_count(size);
823 function.set_optimized_call_site_count(call_site_count);
824
825 // Use heuristics do decide if this call should be inlined.
826 if (!ShouldWeInline(function, size, call_site_count, constants_count)) {
827 // If size is larger than all thresholds, don't consider it again.
828 if ((size > FLAG_inlining_size_threshold) &&
829 (call_site_count > FLAG_inlining_callee_call_sites_threshold) &&
830 (size > FLAG_inlining_constant_arguments_min_size_threshold) &&
831 (size > FLAG_inlining_constant_arguments_max_size_threshold)) {
832 function.set_is_inlinable(false);
833 }
834 thread()->set_deopt_id(prev_deopt_id);
835 TRACE_INLINING(THR_Print(" Bailout: heuristics with "
836 "code size: %" Pd ", "
837 "call sites: %" Pd ", "
838 "const args: %" Pd "\n",
839 size,
840 call_site_count,
841 constants_count));
842 PRINT_INLINING_TREE("Heuristic fail",
843 &call_data->caller, &function, call_data->call);
844 return false;
845 }
846
847 // Inline dispatcher methods regardless of the current depth.
848 const intptr_t depth =
849 (function.IsInvokeFieldDispatcher() ||
850 function.IsNoSuchMethodDispatcher()) ? 0 : inlining_depth_;
851 collected_call_sites_->FindCallSites(callee_graph, depth, &inlined_info_);
852
853 // Add the function to the cache.
854 if (!in_cache) {
855 function_cache_.Add(parsed_function);
856 }
857
858 // Build succeeded so we restore the bailout jump.
859 inlined_ = true;
860 inlined_size_ += size;
861 if (is_recursive_call) {
862 inlined_recursive_call_ = true;
863 }
864 thread()->set_deopt_id(prev_deopt_id);
865
866 call_data->callee_graph = callee_graph;
867 call_data->parameter_stubs = param_stubs;
868 call_data->exit_collector = exit_collector;
869
870 // When inlined, we add the guarded fields of the callee to the caller's
871 // list of guarded fields.
872 for (intptr_t i = 0; i < callee_graph->guarded_fields()->length(); ++i) {
873 FlowGraph::AddToGuardedFields(caller_graph_->guarded_fields(),
874 (*callee_graph->guarded_fields())[i]);
875 }
876 // When inlined, we add the deferred prefixes of the callee to the
877 // caller's list of deferred prefixes.
878 caller_graph()->AddToDeferredPrefixes(callee_graph->deferred_prefixes());
879
880 FlowGraphInliner::SetInliningId(callee_graph,
881 inliner_->NextInlineId(callee_graph->function(),
882 call_data->caller_inlining_id_));
883 TRACE_INLINING(THR_Print(" Success\n"));
884 PRINT_INLINING_TREE(NULL,
885 &call_data->caller, &function, call);
886 return true;
887 } else {
888 Error& error = Error::Handle();
889 error = isolate()->object_store()->sticky_error();
890 isolate()->object_store()->clear_sticky_error();
891 thread()->set_deopt_id(prev_deopt_id);
892 TRACE_INLINING(THR_Print(" Bailout: %s\n", error.ToErrorCString()));
893 PRINT_INLINING_TREE("Bailout",
894 &call_data->caller, &function, call);
895 return false;
896 } 911 }
912
913 // Propagate a compile-time error. Only in precompilation do we attempt to
914 // inline functions that have never been compiled before; when JITing we
915 // should only see compile-time errors in unoptimized compilation.
916 ASSERT(Compiler::always_optimize());
917 Thread::Current()->long_jump_base()->Jump(1, error);
918 UNREACHABLE();
919 return false;
897 } 920 }
898 921
899 void PrintInlinedInfo(const Function& top) { 922 void PrintInlinedInfo(const Function& top) {
900 if (inlined_info_.length() > 0) { 923 if (inlined_info_.length() > 0) {
901 THR_Print("Inlining into: '%s' growth: %f (%" Pd " -> %" Pd ")\n", 924 THR_Print("Inlining into: '%s' growth: %f (%" Pd " -> %" Pd ")\n",
902 top.ToFullyQualifiedCString(), 925 top.ToFullyQualifiedCString(),
903 GrowthFactor(), 926 GrowthFactor(),
904 initial_size_, 927 initial_size_,
905 inlined_size_); 928 inlined_size_);
906 PrintInlinedInfoFor(top, 1); 929 PrintInlinedInfoFor(top, 1);
(...skipping 1005 matching lines...) Expand 10 before | Expand all | Expand 10 after
1912 intptr_t FlowGraphInliner::NextInlineId(const Function& function, 1935 intptr_t FlowGraphInliner::NextInlineId(const Function& function,
1913 intptr_t parent_id) { 1936 intptr_t parent_id) {
1914 const intptr_t id = inline_id_to_function_->length(); 1937 const intptr_t id = inline_id_to_function_->length();
1915 inline_id_to_function_->Add(&function); 1938 inline_id_to_function_->Add(&function);
1916 caller_inline_id_->Add(parent_id); 1939 caller_inline_id_->Add(parent_id);
1917 return id; 1940 return id;
1918 } 1941 }
1919 1942
1920 1943
1921 } // namespace dart 1944 } // namespace dart
OLDNEW
« no previous file with comments | « runtime/vm/compiler.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698