• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "constructor_fence_redundancy_elimination.h"
18 
19 #include "base/arena_allocator.h"
20 #include "base/arena_bit_vector.h"
21 #include "base/bit_vector-inl.h"
22 #include "base/scoped_arena_allocator.h"
23 #include "base/scoped_arena_containers.h"
24 
25 namespace art HIDDEN {
26 
27 static constexpr bool kCfreLogFenceInputCount = false;
28 
29 // TODO: refactor this code by reusing escape analysis.
30 class CFREVisitor final : public HGraphVisitor {
31  public:
CFREVisitor(HGraph * graph,OptimizingCompilerStats * stats)32   CFREVisitor(HGraph* graph, OptimizingCompilerStats* stats)
33       : HGraphVisitor(graph),
34         scoped_allocator_(graph->GetArenaStack()),
35         candidate_fences_(scoped_allocator_.Adapter(kArenaAllocCFRE)),
36         candidate_fence_targets_(),
37         stats_(stats) {}
38 
VisitBasicBlock(HBasicBlock * block)39   void VisitBasicBlock(HBasicBlock* block) override {
40     // Visit all non-Phi instructions in the block.
41     VisitNonPhiInstructions(block);
42 
43     // If there were any unmerged fences left, merge them together,
44     // the objects are considered 'published' at the end of the block.
45     MergeCandidateFences();
46   }
47 
VisitConstructorFence(HConstructorFence * constructor_fence)48   void VisitConstructorFence(HConstructorFence* constructor_fence) override {
49     candidate_fences_.push_back(constructor_fence);
50 
51     if (candidate_fence_targets_.SizeInBits() == 0u) {
52       size_t number_of_instructions = GetGraph()->GetCurrentInstructionId();
53       candidate_fence_targets_ = ArenaBitVector::CreateFixedSize(
54           &scoped_allocator_, number_of_instructions, kArenaAllocCFRE);
55     } else {
56       DCHECK_EQ(candidate_fence_targets_.SizeInBits(),
57                 static_cast<size_t>(GetGraph()->GetCurrentInstructionId()));
58     }
59 
60     for (HInstruction* input : constructor_fence->GetInputs()) {
61       candidate_fence_targets_.SetBit(input->GetId());
62     }
63   }
64 
VisitBoundType(HBoundType * bound_type)65   void VisitBoundType(HBoundType* bound_type) override {
66     VisitAlias(bound_type);
67   }
68 
VisitNullCheck(HNullCheck * null_check)69   void VisitNullCheck(HNullCheck* null_check) override {
70     VisitAlias(null_check);
71   }
72 
VisitSelect(HSelect * select)73   void VisitSelect(HSelect* select) override {
74     VisitAlias(select);
75   }
76 
VisitInstanceFieldSet(HInstanceFieldSet * instruction)77   void VisitInstanceFieldSet(HInstanceFieldSet* instruction) override {
78     HInstruction* value = instruction->InputAt(1);
79     VisitSetLocation(instruction, value);
80   }
81 
VisitStaticFieldSet(HStaticFieldSet * instruction)82   void VisitStaticFieldSet(HStaticFieldSet* instruction) override {
83     HInstruction* value = instruction->InputAt(1);
84     VisitSetLocation(instruction, value);
85   }
86 
VisitArraySet(HArraySet * instruction)87   void VisitArraySet(HArraySet* instruction) override {
88     HInstruction* value = instruction->InputAt(2);
89     VisitSetLocation(instruction, value);
90   }
91 
VisitDeoptimize(HDeoptimize * instruction)92   void VisitDeoptimize([[maybe_unused]] HDeoptimize* instruction) override {
93     // Pessimize: Merge all fences.
94     MergeCandidateFences();
95   }
96 
VisitInvokeStaticOrDirect(HInvokeStaticOrDirect * invoke)97   void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) override {
98     HandleInvoke(invoke);
99   }
100 
VisitInvokeVirtual(HInvokeVirtual * invoke)101   void VisitInvokeVirtual(HInvokeVirtual* invoke) override {
102     HandleInvoke(invoke);
103   }
104 
VisitInvokeInterface(HInvokeInterface * invoke)105   void VisitInvokeInterface(HInvokeInterface* invoke) override {
106     HandleInvoke(invoke);
107   }
108 
VisitInvokeUnresolved(HInvokeUnresolved * invoke)109   void VisitInvokeUnresolved(HInvokeUnresolved* invoke) override {
110     HandleInvoke(invoke);
111   }
112 
VisitInvokePolymorphic(HInvokePolymorphic * invoke)113   void VisitInvokePolymorphic(HInvokePolymorphic* invoke) override {
114     HandleInvoke(invoke);
115   }
116 
VisitClinitCheck(HClinitCheck * clinit)117   void VisitClinitCheck(HClinitCheck* clinit) override {
118     HandleInvoke(clinit);
119   }
120 
VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet * instruction)121   void VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* instruction) override {
122     // Conservatively treat it as an invocation.
123     HandleInvoke(instruction);
124   }
125 
VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet * instruction)126   void VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet* instruction) override {
127     // Conservatively treat it as an invocation.
128     HandleInvoke(instruction);
129   }
130 
VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet * instruction)131   void VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet* instruction) override {
132     // Conservatively treat it as an invocation.
133     HandleInvoke(instruction);
134   }
135 
VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet * instruction)136   void VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet* instruction) override {
137     // Conservatively treat it as an invocation.
138     HandleInvoke(instruction);
139   }
140 
141  private:
HandleInvoke(HInstruction * invoke)142   void HandleInvoke(HInstruction* invoke) {
143     // An object is considered "published" if it escapes into an invoke as any of the parameters.
144     if (HasInterestingPublishTargetAsInput(invoke)) {
145         MergeCandidateFences();
146     }
147   }
148 
149   // Called by any instruction visitor that may create an alias.
150   // These instructions may create an alias:
151   // - BoundType
152   // - NullCheck
153   // - Select
154   //
155   // These also create an alias, but are not handled by this function:
156   // - Phi: propagates values across blocks, but we always merge at the end of a block.
157   // - Invoke: this is handled by HandleInvoke.
VisitAlias(HInstruction * aliasing_inst)158   void VisitAlias(HInstruction* aliasing_inst) {
159     // An object is considered "published" if it becomes aliased by other instructions.
160     if (HasInterestingPublishTargetAsInput(aliasing_inst))  {
161       MergeCandidateFences();
162     }
163   }
164 
VisitSetLocation(HInstruction * inst,HInstruction * store_input)165   void VisitSetLocation([[maybe_unused]] HInstruction* inst, HInstruction* store_input) {
166     if (candidate_fences_.empty()) {
167       // There is no need to look at inputs if there are no candidate fence targets.
168       DCHECK(!candidate_fence_targets_.IsAnyBitSet());
169       return;
170     }
171     // An object is considered "published" if it's stored onto the heap.
172     // Sidenote: A later "LSE" pass can still remove the fence if it proves the
173     // object doesn't actually escape.
174     if (IsInterestingPublishTarget(store_input)) {
175       // Merge all constructor fences that we've seen since
176       // the last interesting store (or since the beginning).
177       MergeCandidateFences();
178     }
179   }
180 
HasInterestingPublishTargetAsInput(HInstruction * inst)181   bool HasInterestingPublishTargetAsInput(HInstruction* inst) {
182     if (candidate_fences_.empty()) {
183       // There is no need to look at inputs if there are no candidate fence targets.
184       DCHECK(!candidate_fence_targets_.IsAnyBitSet());
185       return false;
186     }
187     for (HInstruction* input : inst->GetInputs()) {
188       if (IsInterestingPublishTarget(input)) {
189         return true;
190       }
191     }
192 
193     return false;
194   }
195 
196   // Merges all the existing fences we've seen so far into the last-most fence.
197   //
198   // This resets the list of candidate fences and their targets back to {}.
MergeCandidateFences()199   void MergeCandidateFences() {
200     if (candidate_fences_.empty()) {
201       // Nothing to do, need 1+ fences to merge.
202       return;
203     }
204 
205     // The merge target is always the "last" candidate fence.
206     HConstructorFence* merge_target = candidate_fences_.back();
207     candidate_fences_.pop_back();
208 
209     for (HConstructorFence* fence : candidate_fences_) {
210       DCHECK_NE(merge_target, fence);
211       merge_target->Merge(fence);
212       MaybeRecordStat(stats_, MethodCompilationStat::kConstructorFenceRemovedCFRE);
213     }
214 
215     if (kCfreLogFenceInputCount) {
216       LOG(INFO) << "CFRE-MergeCandidateFences: Post-merge fence input count "
217                 << merge_target->InputCount();
218     }
219 
220     // Each merge acts as a cut-off point. The optimization is reset completely.
221     // In theory, we could push the fence as far as its publish, but in practice
222     // there is no benefit to this extra complexity unless we also reordered
223     // the stores to come later.
224     candidate_fences_.clear();
225     DCHECK_EQ(candidate_fence_targets_.SizeInBits(),
226               static_cast<size_t>(GetGraph()->GetCurrentInstructionId()));
227     candidate_fence_targets_.ClearAllBits();
228   }
229 
230   // A publishing 'store' is only interesting if the value being stored
231   // is one of the fence `targets` in `candidate_fences`.
IsInterestingPublishTarget(HInstruction * store_input) const232   bool IsInterestingPublishTarget(HInstruction* store_input) const {
233     DCHECK_EQ(candidate_fence_targets_.SizeInBits(),
234               static_cast<size_t>(GetGraph()->GetCurrentInstructionId()));
235     return candidate_fence_targets_.IsBitSet(store_input->GetId());
236   }
237 
238   // Phase-local heap memory allocator for CFRE optimizer.
239   ScopedArenaAllocator scoped_allocator_;
240 
241   // Set of constructor fences that we've seen in the current block.
242   // Each constructor fences acts as a guard for one or more `targets`.
243   // There exist no stores to any `targets` between any of these fences.
244   //
245   // Fences are in succession order (e.g. fence[i] succeeds fence[i-1]
246   // within the same basic block).
247   ScopedArenaVector<HConstructorFence*> candidate_fences_;
248 
249   // Stores a set of the fence targets, to allow faster lookup of whether
250   // a detected publish is a target of one of the candidate fences.
251   BitVectorView<size_t> candidate_fence_targets_;
252 
253   // Used to record stats about the optimization.
254   OptimizingCompilerStats* const stats_;
255 
256   DISALLOW_COPY_AND_ASSIGN(CFREVisitor);
257 };
258 
Run()259 bool ConstructorFenceRedundancyElimination::Run() {
260   CFREVisitor cfre_visitor(graph_, stats_);
261 
262   // Arbitrarily visit in reverse-post order.
263   // The exact block visit order does not matter, as the algorithm
264   // only operates on a single block at a time.
265   cfre_visitor.VisitReversePostOrder();
266   return true;
267 }
268 
269 }  // namespace art
270