Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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.h" | 5 #include "vm/flow_graph.h" |
| 6 | 6 |
| 7 #include "vm/bit_vector.h" | 7 #include "vm/bit_vector.h" |
| 8 #include "vm/flow_graph_builder.h" | 8 #include "vm/flow_graph_builder.h" |
| 9 #include "vm/intermediate_language.h" | 9 #include "vm/intermediate_language.h" |
| 10 #include "vm/growable_array.h" | 10 #include "vm/growable_array.h" |
| 11 #include "vm/object_store.h" | |
| 11 #include "vm/report.h" | 12 #include "vm/report.h" |
| 12 | 13 |
| 13 namespace dart { | 14 namespace dart { |
| 14 | 15 |
| 15 DECLARE_FLAG(bool, reorder_basic_blocks); | 16 DECLARE_FLAG(bool, reorder_basic_blocks); |
| 16 DECLARE_FLAG(bool, trace_optimization); | 17 DECLARE_FLAG(bool, trace_optimization); |
| 17 DECLARE_FLAG(bool, verify_compiler); | 18 DECLARE_FLAG(bool, verify_compiler); |
| 18 | 19 |
| 19 | 20 |
| 20 FlowGraph::FlowGraph(const FlowGraphBuilder& builder, | 21 FlowGraph::FlowGraph(const FlowGraphBuilder& builder, |
| 21 GraphEntryInstr* graph_entry, | 22 GraphEntryInstr* graph_entry, |
| 22 intptr_t max_block_id) | 23 intptr_t max_block_id) |
| 23 : isolate_(Isolate::Current()), | 24 : isolate_(Isolate::Current()), |
| 24 parent_(), | 25 parent_(), |
| 25 current_ssa_temp_index_(0), | 26 current_ssa_temp_index_(0), |
| 26 max_block_id_(max_block_id), | 27 max_block_id_(max_block_id), |
| 27 builder_(builder), | 28 builder_(builder), |
| 28 parsed_function_(*builder.parsed_function()), | 29 parsed_function_(*builder.parsed_function()), |
| 29 num_copied_params_(builder.num_copied_params()), | 30 num_copied_params_(builder.num_copied_params()), |
| 30 num_non_copied_params_(builder.num_non_copied_params()), | 31 num_non_copied_params_(builder.num_non_copied_params()), |
| 31 num_stack_locals_(builder.num_stack_locals()), | 32 num_stack_locals_(builder.num_stack_locals()), |
| 32 graph_entry_(graph_entry), | 33 graph_entry_(graph_entry), |
| 33 preorder_(), | 34 preorder_(), |
| 34 postorder_(), | 35 postorder_(), |
| 35 reverse_postorder_(), | 36 reverse_postorder_(), |
| 36 optimized_block_order_(), | 37 optimized_block_order_(), |
| 37 constant_null_(NULL), | 38 constant_null_(NULL), |
| 38 constant_dead_(NULL), | 39 constant_dead_(NULL), |
| 40 constant_empty_context_(NULL), | |
| 41 current_context_(NULL), | |
| 39 block_effects_(NULL), | 42 block_effects_(NULL), |
| 40 licm_allowed_(true), | 43 licm_allowed_(true), |
| 41 loop_headers_(NULL), | 44 loop_headers_(NULL), |
| 42 loop_invariant_loads_(NULL), | 45 loop_invariant_loads_(NULL), |
| 43 guarded_fields_(builder.guarded_fields()), | 46 guarded_fields_(builder.guarded_fields()), |
| 44 deferred_prefixes_(builder.deferred_prefixes()), | 47 deferred_prefixes_(builder.deferred_prefixes()), |
| 45 captured_parameters_( | 48 captured_parameters_( |
| 46 new(isolate_) BitVector(isolate_, variable_count())) { | 49 new(isolate_) BitVector(isolate_, variable_count())) { |
| 47 DiscoverBlocks(); | 50 DiscoverBlocks(); |
| 48 } | 51 } |
| (...skipping 409 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 458 } | 461 } |
| 459 assigned_vars_.Add(kill); | 462 assigned_vars_.Add(kill); |
| 460 } | 463 } |
| 461 | 464 |
| 462 return assigned_vars_; | 465 return assigned_vars_; |
| 463 } | 466 } |
| 464 | 467 |
| 465 // Returns true if the value set by the given store reaches any load from the | 468 // Returns true if the value set by the given store reaches any load from the |
| 466 // same local variable. | 469 // same local variable. |
| 467 bool IsStoreAlive(BlockEntryInstr* block, StoreLocalInstr* store) { | 470 bool IsStoreAlive(BlockEntryInstr* block, StoreLocalInstr* store) { |
| 471 if (store->local().index() == flow_graph_->CurrentContextVar()->index()) { | |
|
Vyacheslav Egorov (Google)
2014/10/28 13:44:57
is it possible just to do
store->local().Equals(f
Florian Schneider
2014/10/28 19:04:31
Done.
| |
| 472 return true; | |
| 473 } | |
| 474 | |
| 468 if (store->is_dead()) { | 475 if (store->is_dead()) { |
| 469 return false; | 476 return false; |
| 470 } | 477 } |
| 471 | |
| 472 if (store->is_last()) { | 478 if (store->is_last()) { |
| 473 const intptr_t index = store->local().BitIndexIn(num_non_copied_params_); | 479 const intptr_t index = store->local().BitIndexIn(num_non_copied_params_); |
| 474 return GetLiveOutSet(block)->Contains(index); | 480 return GetLiveOutSet(block)->Contains(index); |
| 475 } | 481 } |
| 476 | 482 |
| 477 return true; | 483 return true; |
| 478 } | 484 } |
| 479 | 485 |
| 480 // Returns true if the given load is the last for the local and the value | 486 // Returns true if the given load is the last for the local and the value |
| 481 // of the local will not flow into another one. | 487 // of the local will not flow into another one. |
| 482 bool IsLastLoad(BlockEntryInstr* block, LoadLocalInstr* load) { | 488 bool IsLastLoad(BlockEntryInstr* block, LoadLocalInstr* load) { |
| 489 if (load->local().index() == flow_graph_->CurrentContextVar()->index()) { | |
|
Vyacheslav Egorov (Google)
2014/10/28 13:44:57
ditto
Florian Schneider
2014/10/28 19:04:30
Done.
| |
| 490 return false; | |
| 491 } | |
| 483 const intptr_t index = load->local().BitIndexIn(num_non_copied_params_); | 492 const intptr_t index = load->local().BitIndexIn(num_non_copied_params_); |
| 484 return load->is_last() && !GetLiveOutSet(block)->Contains(index); | 493 return load->is_last() && !GetLiveOutSet(block)->Contains(index); |
| 485 } | 494 } |
| 486 | 495 |
| 487 private: | 496 private: |
| 488 virtual void ComputeInitialSets(); | 497 virtual void ComputeInitialSets(); |
| 489 | 498 |
| 490 const FlowGraph* flow_graph_; | 499 const FlowGraph* flow_graph_; |
| 491 const intptr_t num_non_copied_params_; | 500 const intptr_t num_non_copied_params_; |
| 492 GrowableArray<BitVector*> assigned_vars_; | 501 GrowableArray<BitVector*> assigned_vars_; |
| (...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 715 | 724 |
| 716 // Initialize has_already and work. | 725 // Initialize has_already and work. |
| 717 for (intptr_t block_index = 0; block_index < block_count; ++block_index) { | 726 for (intptr_t block_index = 0; block_index < block_count; ++block_index) { |
| 718 has_already.Add(-1); | 727 has_already.Add(-1); |
| 719 work.Add(-1); | 728 work.Add(-1); |
| 720 } | 729 } |
| 721 | 730 |
| 722 // Insert phis for each variable in turn. | 731 // Insert phis for each variable in turn. |
| 723 GrowableArray<BlockEntryInstr*> worklist; | 732 GrowableArray<BlockEntryInstr*> worklist; |
| 724 for (intptr_t var_index = 0; var_index < variable_count(); ++var_index) { | 733 for (intptr_t var_index = 0; var_index < variable_count(); ++var_index) { |
| 734 bool always_live = var_index == CurrentContextEnvIndex(); | |
|
Vyacheslav Egorov (Google)
2014/10/28 13:44:57
const bool is_always_alive
Florian Schneider
2014/10/28 19:04:30
Done.
| |
| 725 // Add to the worklist each block containing an assignment. | 735 // Add to the worklist each block containing an assignment. |
| 726 for (intptr_t block_index = 0; block_index < block_count; ++block_index) { | 736 for (intptr_t block_index = 0; block_index < block_count; ++block_index) { |
| 727 if (assigned_vars[block_index]->Contains(var_index)) { | 737 if (assigned_vars[block_index]->Contains(var_index)) { |
| 728 work[block_index] = var_index; | 738 work[block_index] = var_index; |
| 729 worklist.Add(preorder[block_index]); | 739 worklist.Add(preorder[block_index]); |
| 730 } | 740 } |
| 731 } | 741 } |
| 732 | 742 |
| 733 while (!worklist.is_empty()) { | 743 while (!worklist.is_empty()) { |
| 734 BlockEntryInstr* current = worklist.RemoveLast(); | 744 BlockEntryInstr* current = worklist.RemoveLast(); |
| 735 // Ensure a phi for each block in the dominance frontier of current. | 745 // Ensure a phi for each block in the dominance frontier of current. |
| 736 for (BitVector::Iterator it(dom_frontier[current->preorder_number()]); | 746 for (BitVector::Iterator it(dom_frontier[current->preorder_number()]); |
| 737 !it.Done(); | 747 !it.Done(); |
| 738 it.Advance()) { | 748 it.Advance()) { |
| 739 int index = it.Current(); | 749 int index = it.Current(); |
| 740 if (has_already[index] < var_index) { | 750 if (has_already[index] < var_index) { |
| 741 BlockEntryInstr* block = preorder[index]; | 751 BlockEntryInstr* block = preorder[index]; |
| 742 ASSERT(block->IsJoinEntry()); | 752 ASSERT(block->IsJoinEntry()); |
| 743 block->AsJoinEntry()->InsertPhi(var_index, variable_count()); | 753 block->AsJoinEntry()->InsertPhi(var_index, |
|
Vyacheslav Egorov (Google)
2014/10/28 13:44:57
You can also make InsertPhi return the phi it crea
Florian Schneider
2014/10/28 19:04:31
Done.
| |
| 754 variable_count(), | |
| 755 always_live); | |
| 744 has_already[index] = var_index; | 756 has_already[index] = var_index; |
| 745 if (work[index] < var_index) { | 757 if (work[index] < var_index) { |
| 746 work[index] = var_index; | 758 work[index] = var_index; |
| 747 worklist.Add(block); | 759 worklist.Add(block); |
| 748 } | 760 } |
| 749 } | 761 } |
| 750 } | 762 } |
| 751 } | 763 } |
| 752 } | 764 } |
| 753 } | 765 } |
| 754 | 766 |
| 755 | 767 |
| 756 void FlowGraph::Rename(GrowableArray<PhiInstr*>* live_phis, | 768 void FlowGraph::Rename(GrowableArray<PhiInstr*>* live_phis, |
| 757 VariableLivenessAnalysis* variable_liveness, | 769 VariableLivenessAnalysis* variable_liveness, |
| 758 ZoneGrowableArray<Definition*>* inlining_parameters) { | 770 ZoneGrowableArray<Definition*>* inlining_parameters) { |
| 759 GraphEntryInstr* entry = graph_entry(); | 771 GraphEntryInstr* entry = graph_entry(); |
| 760 | 772 |
| 761 // Initial renaming environment. | 773 // Initial renaming environment. |
| 762 GrowableArray<Definition*> env(variable_count()); | 774 GrowableArray<Definition*> env(variable_count()); |
| 763 | 775 |
| 764 // Add global constants to the initial definitions. | 776 // Add global constants to the initial definitions. |
| 765 constant_null_ = GetConstant(Object::ZoneHandle()); | 777 constant_null_ = GetConstant(Object::ZoneHandle()); |
| 766 constant_dead_ = GetConstant(Symbols::OptimizedOut()); | 778 constant_dead_ = GetConstant(Symbols::OptimizedOut()); |
| 779 constant_empty_context_ = GetConstant(Context::Handle( | |
| 780 isolate()->object_store()->empty_context())); | |
| 767 | 781 |
| 768 // Add parameters to the initial definitions and renaming environment. | 782 // Add parameters to the initial definitions and renaming environment. |
| 769 if (inlining_parameters != NULL) { | 783 if (inlining_parameters != NULL) { |
| 770 // Use known parameters. | 784 // Use known parameters. |
| 771 ASSERT(parameter_count() == inlining_parameters->length()); | 785 ASSERT(parameter_count() == inlining_parameters->length()); |
| 772 for (intptr_t i = 0; i < parameter_count(); ++i) { | 786 for (intptr_t i = 0; i < parameter_count(); ++i) { |
| 773 Definition* defn = (*inlining_parameters)[i]; | 787 Definition* defn = (*inlining_parameters)[i]; |
| 774 AllocateSSAIndexes(defn); | 788 AllocateSSAIndexes(defn); |
| 775 AddToInitialDefinitions(defn); | 789 AddToInitialDefinitions(defn); |
| 776 env.Add(defn); | 790 env.Add(defn); |
| 777 } | 791 } |
| 778 } else { | 792 } else { |
| 779 // Create new parameters. For functions compiled for OSR, the locals | 793 // Create new parameters. For functions compiled for OSR, the locals |
| 780 // are unknown and so treated like parameters. | 794 // are unknown and so treated like parameters. |
| 781 intptr_t count = IsCompiledForOsr() ? variable_count() : parameter_count(); | 795 intptr_t count = IsCompiledForOsr() ? variable_count() : parameter_count(); |
| 782 for (intptr_t i = 0; i < count; ++i) { | 796 for (intptr_t i = 0; i < count; ++i) { |
| 783 ParameterInstr* param = new(isolate()) ParameterInstr(i, entry); | 797 ParameterInstr* param = new(isolate()) ParameterInstr(i, entry); |
| 784 param->set_ssa_temp_index(alloc_ssa_temp_index()); // New SSA temp. | 798 param->set_ssa_temp_index(alloc_ssa_temp_index()); // New SSA temp. |
| 785 AddToInitialDefinitions(param); | 799 AddToInitialDefinitions(param); |
| 786 env.Add(param); | 800 env.Add(param); |
| 787 } | 801 } |
| 788 } | 802 } |
| 789 | 803 |
| 790 // Initialize all locals with #null in the renaming environment. For OSR, | 804 // Initialize all locals with #null in the renaming environment. For OSR, |
|
Vyacheslav Egorov (Google)
2014/10/28 13:44:57
Comment is out of date.
Florian Schneider
2014/10/28 19:04:31
Done.
| |
| 791 // the locals have already been handled as parameters. | 805 // the locals have already been handled as parameters. |
| 792 if (!IsCompiledForOsr()) { | 806 if (!IsCompiledForOsr()) { |
| 793 for (intptr_t i = parameter_count(); i < variable_count(); ++i) { | 807 for (intptr_t i = parameter_count(); i < variable_count(); ++i) { |
| 794 env.Add(constant_null()); | 808 if (i == CurrentContextEnvIndex()) { |
| 809 if (parsed_function().function().IsClosureFunction()) { | |
| 810 CurrentContextInstr* context = new CurrentContextInstr(); | |
| 811 context->set_ssa_temp_index(alloc_ssa_temp_index()); // New SSA temp. | |
| 812 AddToInitialDefinitions(context); | |
| 813 env.Add(context); | |
| 814 } else { | |
| 815 env.Add(constant_empty_context()); | |
| 816 } | |
| 817 } else { | |
| 818 env.Add(constant_null()); | |
| 819 } | |
| 795 } | 820 } |
| 796 } | 821 } |
| 797 | 822 |
| 798 if (entry->SuccessorCount() > 1) { | 823 if (entry->SuccessorCount() > 1) { |
| 799 // Functions with try-catch have a fixed area of stack slots reserved | 824 // Functions with try-catch have a fixed area of stack slots reserved |
| 800 // so that all local variables are stored at a known location when | 825 // so that all local variables are stored at a known location when |
| 801 // on entry to the catch. | 826 // on entry to the catch. |
| 802 entry->set_fixed_slot_count(num_stack_locals() + num_copied_params()); | 827 entry->set_fixed_slot_count(num_stack_locals() + num_copied_params()); |
| 803 } | 828 } |
| 804 RenameRecursive(entry, &env, live_phis, variable_liveness); | 829 RenameRecursive(entry, &env, live_phis, variable_liveness); |
| 805 } | 830 } |
| 806 | 831 |
| 807 | 832 |
| 808 void FlowGraph::AttachEnvironment(Instruction* instr, | 833 void FlowGraph::AttachEnvironment(Instruction* instr, |
| 809 GrowableArray<Definition*>* env) { | 834 GrowableArray<Definition*>* env) { |
| 810 Environment* deopt_env = | 835 Environment* deopt_env = |
| 811 Environment::From(isolate(), | 836 Environment::From(isolate(), |
| 812 *env, | 837 *env, |
| 813 num_non_copied_params_, | 838 num_non_copied_params_, |
| 814 &parsed_function_); | 839 &parsed_function_); |
| 815 // TODO(fschneider): Add predicates CanEagerlyDeoptimize and | |
| 816 // CanLazilyDeoptimize to instructions to generally deal with instructions | |
| 817 // that have pushed arguments and input operands. | |
| 818 // Right now, closure calls are the only instructions that have both. They | |
| 819 // also don't have an eager deoptimziation point, so the environment attached | |
| 820 // here is only used for after the call. | |
| 821 if (instr->IsClosureCall()) { | 840 if (instr->IsClosureCall()) { |
| 822 deopt_env = deopt_env->DeepCopy(isolate(), | 841 deopt_env = deopt_env->DeepCopy(isolate(), |
| 823 deopt_env->Length() - instr->InputCount()); | 842 deopt_env->Length() - instr->InputCount()); |
| 824 } | 843 } |
| 825 instr->SetEnvironment(deopt_env); | 844 instr->SetEnvironment(deopt_env); |
| 826 for (Environment::DeepIterator it(deopt_env); !it.Done(); it.Advance()) { | 845 for (Environment::DeepIterator it(deopt_env); !it.Done(); it.Advance()) { |
| 827 Value* use = it.CurrentValue(); | 846 Value* use = it.CurrentValue(); |
| 828 use->definition()->AddEnvUse(use); | 847 use->definition()->AddEnvUse(use); |
| 829 } | 848 } |
| 830 if (instr->CanDeoptimize()) { | 849 if (instr->CanDeoptimize()) { |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 865 param->set_ssa_temp_index(alloc_ssa_temp_index()); // New SSA temp. | 884 param->set_ssa_temp_index(alloc_ssa_temp_index()); // New SSA temp. |
| 866 (*env)[i] = param; | 885 (*env)[i] = param; |
| 867 block_entry->AsCatchBlockEntry()->initial_definitions()->Add(param); | 886 block_entry->AsCatchBlockEntry()->initial_definitions()->Add(param); |
| 868 } | 887 } |
| 869 } | 888 } |
| 870 | 889 |
| 871 // Prune non-live variables at block entry by replacing their environment | 890 // Prune non-live variables at block entry by replacing their environment |
| 872 // slots with null. | 891 // slots with null. |
| 873 BitVector* live_in = variable_liveness->GetLiveInSet(block_entry); | 892 BitVector* live_in = variable_liveness->GetLiveInSet(block_entry); |
| 874 for (intptr_t i = 0; i < variable_count(); i++) { | 893 for (intptr_t i = 0; i < variable_count(); i++) { |
| 875 if (!live_in->Contains(i)) { | 894 if (!live_in->Contains(i) && (i != CurrentContextEnvIndex())) { |
|
Vyacheslav Egorov (Google)
2014/10/28 13:44:57
Should not we instead ensure that live_in->Contain
Florian Schneider
2014/10/28 19:04:31
Yes, I can change the VariableLiveness accordingly
| |
| 876 (*env)[i] = constant_dead(); | 895 (*env)[i] = constant_dead(); |
| 877 } | 896 } |
| 878 } | 897 } |
| 879 | 898 |
| 880 // Attach environment to the block entry. | 899 // Attach environment to the block entry. |
| 881 AttachEnvironment(block_entry, env); | 900 AttachEnvironment(block_entry, env); |
| 882 | 901 |
| 883 // 2. Process normal instructions. | 902 // 2. Process normal instructions. |
| 884 for (ForwardInstructionIterator it(block_entry); !it.Done(); it.Advance()) { | 903 for (ForwardInstructionIterator it(block_entry); !it.Done(); it.Advance()) { |
| 885 Instruction* current = it.Current(); | 904 Instruction* current = it.Current(); |
| (...skipping 422 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1308 } | 1327 } |
| 1309 | 1328 |
| 1310 | 1329 |
| 1311 bool BlockEffects::IsSideEffectFreePath(BlockEntryInstr* from, | 1330 bool BlockEffects::IsSideEffectFreePath(BlockEntryInstr* from, |
| 1312 BlockEntryInstr* to) const { | 1331 BlockEntryInstr* to) const { |
| 1313 return available_at_[to->postorder_number()]->Contains( | 1332 return available_at_[to->postorder_number()]->Contains( |
| 1314 from->postorder_number()); | 1333 from->postorder_number()); |
| 1315 } | 1334 } |
| 1316 | 1335 |
| 1317 } // namespace dart | 1336 } // namespace dart |
| OLD | NEW |