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_(std::nullopt),
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_.has_value()) {
52       size_t number_of_instructions = GetGraph()->GetCurrentInstructionId();
53       candidate_fence_targets_.emplace(
54           &scoped_allocator_, number_of_instructions, /*expandable=*/ false, kArenaAllocCFRE);
55     }
56 
57     for (HInstruction* input : constructor_fence->GetInputs()) {
58       candidate_fence_targets_->SetBit(input->GetId());
59     }
60   }
61 
VisitBoundType(HBoundType * bound_type)62   void VisitBoundType(HBoundType* bound_type) override {
63     VisitAlias(bound_type);
64   }
65 
VisitNullCheck(HNullCheck * null_check)66   void VisitNullCheck(HNullCheck* null_check) override {
67     VisitAlias(null_check);
68   }
69 
VisitSelect(HSelect * select)70   void VisitSelect(HSelect* select) override {
71     VisitAlias(select);
72   }
73 
VisitInstanceFieldSet(HInstanceFieldSet * instruction)74   void VisitInstanceFieldSet(HInstanceFieldSet* instruction) override {
75     HInstruction* value = instruction->InputAt(1);
76     VisitSetLocation(instruction, value);
77   }
78 
VisitStaticFieldSet(HStaticFieldSet * instruction)79   void VisitStaticFieldSet(HStaticFieldSet* instruction) override {
80     HInstruction* value = instruction->InputAt(1);
81     VisitSetLocation(instruction, value);
82   }
83 
VisitArraySet(HArraySet * instruction)84   void VisitArraySet(HArraySet* instruction) override {
85     HInstruction* value = instruction->InputAt(2);
86     VisitSetLocation(instruction, value);
87   }
88 
VisitDeoptimize(HDeoptimize * instruction)89   void VisitDeoptimize([[maybe_unused]] HDeoptimize* instruction) override {
90     // Pessimize: Merge all fences.
91     MergeCandidateFences();
92   }
93 
VisitInvokeStaticOrDirect(HInvokeStaticOrDirect * invoke)94   void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) override {
95     HandleInvoke(invoke);
96   }
97 
VisitInvokeVirtual(HInvokeVirtual * invoke)98   void VisitInvokeVirtual(HInvokeVirtual* invoke) override {
99     HandleInvoke(invoke);
100   }
101 
VisitInvokeInterface(HInvokeInterface * invoke)102   void VisitInvokeInterface(HInvokeInterface* invoke) override {
103     HandleInvoke(invoke);
104   }
105 
VisitInvokeUnresolved(HInvokeUnresolved * invoke)106   void VisitInvokeUnresolved(HInvokeUnresolved* invoke) override {
107     HandleInvoke(invoke);
108   }
109 
VisitInvokePolymorphic(HInvokePolymorphic * invoke)110   void VisitInvokePolymorphic(HInvokePolymorphic* invoke) override {
111     HandleInvoke(invoke);
112   }
113 
VisitClinitCheck(HClinitCheck * clinit)114   void VisitClinitCheck(HClinitCheck* clinit) override {
115     HandleInvoke(clinit);
116   }
117 
VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet * instruction)118   void VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* instruction) override {
119     // Conservatively treat it as an invocation.
120     HandleInvoke(instruction);
121   }
122 
VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet * instruction)123   void VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet* instruction) override {
124     // Conservatively treat it as an invocation.
125     HandleInvoke(instruction);
126   }
127 
VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet * instruction)128   void VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet* instruction) override {
129     // Conservatively treat it as an invocation.
130     HandleInvoke(instruction);
131   }
132 
VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet * instruction)133   void VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet* instruction) override {
134     // Conservatively treat it as an invocation.
135     HandleInvoke(instruction);
136   }
137 
138  private:
HandleInvoke(HInstruction * invoke)139   void HandleInvoke(HInstruction* invoke) {
140     // An object is considered "published" if it escapes into an invoke as any of the parameters.
141     if (HasInterestingPublishTargetAsInput(invoke)) {
142         MergeCandidateFences();
143     }
144   }
145 
146   // Called by any instruction visitor that may create an alias.
147   // These instructions may create an alias:
148   // - BoundType
149   // - NullCheck
150   // - Select
151   //
152   // These also create an alias, but are not handled by this function:
153   // - Phi: propagates values across blocks, but we always merge at the end of a block.
154   // - Invoke: this is handled by HandleInvoke.
VisitAlias(HInstruction * aliasing_inst)155   void VisitAlias(HInstruction* aliasing_inst) {
156     // An object is considered "published" if it becomes aliased by other instructions.
157     if (HasInterestingPublishTargetAsInput(aliasing_inst))  {
158       MergeCandidateFences();
159     }
160   }
161 
VisitSetLocation(HInstruction * inst,HInstruction * store_input)162   void VisitSetLocation([[maybe_unused]] HInstruction* inst, HInstruction* store_input) {
163     if (candidate_fences_.empty()) {
164       // There is no need to look at inputs if there are no candidate fence targets.
165       DCHECK_IMPLIES(candidate_fence_targets_.has_value(),
166                      !candidate_fence_targets_->IsAnyBitSet());
167       return;
168     }
169     // An object is considered "published" if it's stored onto the heap.
170     // Sidenote: A later "LSE" pass can still remove the fence if it proves the
171     // object doesn't actually escape.
172     if (IsInterestingPublishTarget(store_input)) {
173       // Merge all constructor fences that we've seen since
174       // the last interesting store (or since the beginning).
175       MergeCandidateFences();
176     }
177   }
178 
HasInterestingPublishTargetAsInput(HInstruction * inst)179   bool HasInterestingPublishTargetAsInput(HInstruction* inst) {
180     if (candidate_fences_.empty()) {
181       // There is no need to look at inputs if there are no candidate fence targets.
182       DCHECK_IMPLIES(candidate_fence_targets_.has_value(),
183                      !candidate_fence_targets_->IsAnyBitSet());
184       return false;
185     }
186     for (HInstruction* input : inst->GetInputs()) {
187       if (IsInterestingPublishTarget(input)) {
188         return true;
189       }
190     }
191 
192     return false;
193   }
194 
195   // Merges all the existing fences we've seen so far into the last-most fence.
196   //
197   // This resets the list of candidate fences and their targets back to {}.
MergeCandidateFences()198   void MergeCandidateFences() {
199     if (candidate_fences_.empty()) {
200       // Nothing to do, need 1+ fences to merge.
201       return;
202     }
203 
204     // The merge target is always the "last" candidate fence.
205     HConstructorFence* merge_target = candidate_fences_.back();
206     candidate_fences_.pop_back();
207 
208     for (HConstructorFence* fence : candidate_fences_) {
209       DCHECK_NE(merge_target, fence);
210       merge_target->Merge(fence);
211       MaybeRecordStat(stats_, MethodCompilationStat::kConstructorFenceRemovedCFRE);
212     }
213 
214     if (kCfreLogFenceInputCount) {
215       LOG(INFO) << "CFRE-MergeCandidateFences: Post-merge fence input count "
216                 << merge_target->InputCount();
217     }
218 
219     // Each merge acts as a cut-off point. The optimization is reset completely.
220     // In theory, we could push the fence as far as its publish, but in practice
221     // there is no benefit to this extra complexity unless we also reordered
222     // the stores to come later.
223     candidate_fences_.clear();
224     DCHECK(candidate_fence_targets_.has_value());
225     candidate_fence_targets_->ClearAllBits();
226   }
227 
228   // A publishing 'store' is only interesting if the value being stored
229   // is one of the fence `targets` in `candidate_fences`.
IsInterestingPublishTarget(HInstruction * store_input) const230   bool IsInterestingPublishTarget(HInstruction* store_input) const {
231     DCHECK(candidate_fence_targets_.has_value());
232     return candidate_fence_targets_->IsBitSet(store_input->GetId());
233   }
234 
235   // Phase-local heap memory allocator for CFRE optimizer.
236   ScopedArenaAllocator scoped_allocator_;
237 
238   // Set of constructor fences that we've seen in the current block.
239   // Each constructor fences acts as a guard for one or more `targets`.
240   // There exist no stores to any `targets` between any of these fences.
241   //
242   // Fences are in succession order (e.g. fence[i] succeeds fence[i-1]
243   // within the same basic block).
244   ScopedArenaVector<HConstructorFence*> candidate_fences_;
245 
246   // Stores a set of the fence targets, to allow faster lookup of whether
247   // a detected publish is a target of one of the candidate fences.
248   std::optional<ArenaBitVector> candidate_fence_targets_;
249 
250   // Used to record stats about the optimization.
251   OptimizingCompilerStats* const stats_;
252 
253   DISALLOW_COPY_AND_ASSIGN(CFREVisitor);
254 };
255 
Run()256 bool ConstructorFenceRedundancyElimination::Run() {
257   CFREVisitor cfre_visitor(graph_, stats_);
258 
259   // Arbitrarily visit in reverse-post order.
260   // The exact block visit order does not matter, as the algorithm
261   // only operates on a single block at a time.
262   cfre_visitor.VisitReversePostOrder();
263   return true;
264 }
265 
266 }  // namespace art
267