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