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/scoped_arena_allocator.h"
21 #include "base/scoped_arena_containers.h"
22
23 namespace art {
24
25 static constexpr bool kCfreLogFenceInputCount = false;
26
27 // TODO: refactor this code by reusing escape analysis.
28 class CFREVisitor : public HGraphVisitor {
29 public:
CFREVisitor(HGraph * graph,OptimizingCompilerStats * stats)30 CFREVisitor(HGraph* graph, OptimizingCompilerStats* stats)
31 : HGraphVisitor(graph),
32 scoped_allocator_(graph->GetArenaStack()),
33 candidate_fences_(scoped_allocator_.Adapter(kArenaAllocCFRE)),
34 candidate_fence_targets_(scoped_allocator_.Adapter(kArenaAllocCFRE)),
35 stats_(stats) {}
36
VisitBasicBlock(HBasicBlock * block)37 void VisitBasicBlock(HBasicBlock* block) override {
38 // Visit all instructions in block.
39 HGraphVisitor::VisitBasicBlock(block);
40
41 // If there were any unmerged fences left, merge them together,
42 // the objects are considered 'published' at the end of the block.
43 MergeCandidateFences();
44 }
45
VisitConstructorFence(HConstructorFence * constructor_fence)46 void VisitConstructorFence(HConstructorFence* constructor_fence) override {
47 candidate_fences_.push_back(constructor_fence);
48
49 for (size_t input_idx = 0; input_idx < constructor_fence->InputCount(); ++input_idx) {
50 candidate_fence_targets_.insert(constructor_fence->InputAt(input_idx));
51 }
52 }
53
VisitBoundType(HBoundType * bound_type)54 void VisitBoundType(HBoundType* bound_type) override {
55 VisitAlias(bound_type);
56 }
57
VisitNullCheck(HNullCheck * null_check)58 void VisitNullCheck(HNullCheck* null_check) override {
59 VisitAlias(null_check);
60 }
61
VisitSelect(HSelect * select)62 void VisitSelect(HSelect* select) override {
63 VisitAlias(select);
64 }
65
VisitInstanceFieldSet(HInstanceFieldSet * instruction)66 void VisitInstanceFieldSet(HInstanceFieldSet* instruction) override {
67 HInstruction* value = instruction->InputAt(1);
68 VisitSetLocation(instruction, value);
69 }
70
VisitStaticFieldSet(HStaticFieldSet * instruction)71 void VisitStaticFieldSet(HStaticFieldSet* instruction) override {
72 HInstruction* value = instruction->InputAt(1);
73 VisitSetLocation(instruction, value);
74 }
75
VisitArraySet(HArraySet * instruction)76 void VisitArraySet(HArraySet* instruction) override {
77 HInstruction* value = instruction->InputAt(2);
78 VisitSetLocation(instruction, value);
79 }
80
VisitDeoptimize(HDeoptimize * instruction ATTRIBUTE_UNUSED)81 void VisitDeoptimize(HDeoptimize* instruction ATTRIBUTE_UNUSED) override {
82 // Pessimize: Merge all fences.
83 MergeCandidateFences();
84 }
85
VisitInvokeStaticOrDirect(HInvokeStaticOrDirect * invoke)86 void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) override {
87 HandleInvoke(invoke);
88 }
89
VisitInvokeVirtual(HInvokeVirtual * invoke)90 void VisitInvokeVirtual(HInvokeVirtual* invoke) override {
91 HandleInvoke(invoke);
92 }
93
VisitInvokeInterface(HInvokeInterface * invoke)94 void VisitInvokeInterface(HInvokeInterface* invoke) override {
95 HandleInvoke(invoke);
96 }
97
VisitInvokeUnresolved(HInvokeUnresolved * invoke)98 void VisitInvokeUnresolved(HInvokeUnresolved* invoke) override {
99 HandleInvoke(invoke);
100 }
101
VisitInvokePolymorphic(HInvokePolymorphic * invoke)102 void VisitInvokePolymorphic(HInvokePolymorphic* invoke) override {
103 HandleInvoke(invoke);
104 }
105
VisitClinitCheck(HClinitCheck * clinit)106 void VisitClinitCheck(HClinitCheck* clinit) override {
107 HandleInvoke(clinit);
108 }
109
VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet * instruction)110 void VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* instruction) override {
111 // Conservatively treat it as an invocation.
112 HandleInvoke(instruction);
113 }
114
VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet * instruction)115 void VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet* instruction) override {
116 // Conservatively treat it as an invocation.
117 HandleInvoke(instruction);
118 }
119
VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet * instruction)120 void VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet* instruction) override {
121 // Conservatively treat it as an invocation.
122 HandleInvoke(instruction);
123 }
124
VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet * instruction)125 void VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet* instruction) override {
126 // Conservatively treat it as an invocation.
127 HandleInvoke(instruction);
128 }
129
130 private:
HandleInvoke(HInstruction * invoke)131 void HandleInvoke(HInstruction* invoke) {
132 // An object is considered "published" if it escapes into an invoke as any of the parameters.
133 if (HasInterestingPublishTargetAsInput(invoke)) {
134 MergeCandidateFences();
135 }
136 }
137
138 // Called by any instruction visitor that may create an alias.
139 // These instructions may create an alias:
140 // - BoundType
141 // - NullCheck
142 // - Select
143 //
144 // These also create an alias, but are not handled by this function:
145 // - Phi: propagates values across blocks, but we always merge at the end of a block.
146 // - Invoke: this is handled by HandleInvoke.
VisitAlias(HInstruction * aliasing_inst)147 void VisitAlias(HInstruction* aliasing_inst) {
148 // An object is considered "published" if it becomes aliased by other instructions.
149 if (HasInterestingPublishTargetAsInput(aliasing_inst)) {
150 // Note that constructing a "NullCheck" for new-instance, new-array,
151 // or a 'this' (receiver) reference is impossible.
152 //
153 // If by some reason we actually encounter such a NullCheck(FenceTarget),
154 // we LOG(WARNING).
155 if (UNLIKELY(aliasing_inst->IsNullCheck())) {
156 LOG(kIsDebugBuild ? FATAL : WARNING)
157 << "Unexpected instruction: NullCheck; should not be legal in graph";
158 // We then do a best-effort to handle this case.
159 }
160 MergeCandidateFences();
161 }
162 }
163
VisitSetLocation(HInstruction * inst ATTRIBUTE_UNUSED,HInstruction * store_input)164 void VisitSetLocation(HInstruction* inst ATTRIBUTE_UNUSED, HInstruction* store_input) {
165 // An object is considered "published" if it's stored onto the heap.
166 // Sidenote: A later "LSE" pass can still remove the fence if it proves the
167 // object doesn't actually escape.
168 if (IsInterestingPublishTarget(store_input)) {
169 // Merge all constructor fences that we've seen since
170 // the last interesting store (or since the beginning).
171 MergeCandidateFences();
172 }
173 }
174
HasInterestingPublishTargetAsInput(HInstruction * inst)175 bool HasInterestingPublishTargetAsInput(HInstruction* inst) {
176 for (size_t input_count = 0; input_count < inst->InputCount(); ++input_count) {
177 if (IsInterestingPublishTarget(inst->InputAt(input_count))) {
178 return true;
179 }
180 }
181
182 return false;
183 }
184
185 // Merges all the existing fences we've seen so far into the last-most fence.
186 //
187 // This resets the list of candidate fences and their targets back to {}.
MergeCandidateFences()188 void MergeCandidateFences() {
189 if (candidate_fences_.empty()) {
190 // Nothing to do, need 1+ fences to merge.
191 return;
192 }
193
194 // The merge target is always the "last" candidate fence.
195 HConstructorFence* merge_target = candidate_fences_[candidate_fences_.size() - 1];
196
197 for (HConstructorFence* fence : candidate_fences_) {
198 MaybeMerge(merge_target, fence);
199 }
200
201 if (kCfreLogFenceInputCount) {
202 LOG(INFO) << "CFRE-MergeCandidateFences: Post-merge fence input count "
203 << merge_target->InputCount();
204 }
205
206 // Each merge acts as a cut-off point. The optimization is reset completely.
207 // In theory, we could push the fence as far as its publish, but in practice
208 // there is no benefit to this extra complexity unless we also reordered
209 // the stores to come later.
210 candidate_fences_.clear();
211 candidate_fence_targets_.clear();
212 }
213
214 // A publishing 'store' is only interesting if the value being stored
215 // is one of the fence `targets` in `candidate_fences`.
IsInterestingPublishTarget(HInstruction * store_input) const216 bool IsInterestingPublishTarget(HInstruction* store_input) const {
217 return candidate_fence_targets_.find(store_input) != candidate_fence_targets_.end();
218 }
219
MaybeMerge(HConstructorFence * target,HConstructorFence * src)220 void MaybeMerge(HConstructorFence* target, HConstructorFence* src) {
221 if (target == src) {
222 return; // Don't merge a fence into itself.
223 // This is mostly for stats-purposes, we don't want to count merge(x,x)
224 // as removing a fence because it's a no-op.
225 }
226
227 target->Merge(src);
228
229 MaybeRecordStat(stats_, MethodCompilationStat::kConstructorFenceRemovedCFRE);
230 }
231
232 // Phase-local heap memory allocator for CFRE optimizer.
233 ScopedArenaAllocator scoped_allocator_;
234
235 // Set of constructor fences that we've seen in the current block.
236 // Each constructor fences acts as a guard for one or more `targets`.
237 // There exist no stores to any `targets` between any of these fences.
238 //
239 // Fences are in succession order (e.g. fence[i] succeeds fence[i-1]
240 // within the same basic block).
241 ScopedArenaVector<HConstructorFence*> candidate_fences_;
242
243 // Stores a set of the fence targets, to allow faster lookup of whether
244 // a detected publish is a target of one of the candidate fences.
245 ScopedArenaHashSet<HInstruction*> candidate_fence_targets_;
246
247 // Used to record stats about the optimization.
248 OptimizingCompilerStats* const stats_;
249
250 DISALLOW_COPY_AND_ASSIGN(CFREVisitor);
251 };
252
Run()253 bool ConstructorFenceRedundancyElimination::Run() {
254 CFREVisitor cfre_visitor(graph_, stats_);
255
256 // Arbitrarily visit in reverse-post order.
257 // The exact block visit order does not matter, as the algorithm
258 // only operates on a single block at a time.
259 cfre_visitor.VisitReversePostOrder();
260 return true;
261 }
262
263 } // namespace art
264