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