blob: 66ec377f0a4d5f7143489dd5fe8ad7cec7fcc8ad [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"
Vladimir Markoca6fff82017-10-03 14:49:14 +010020#include "base/scoped_arena_allocator.h"
21#include "base/scoped_arena_containers.h"
Igor Murashkindd018df2017-08-09 10:38:31 -070022
VladimĂ­r Marko434d9682022-11-04 14:04:17 +000023namespace art HIDDEN {
Igor Murashkindd018df2017-08-09 10:38:31 -070024
25static constexpr bool kCfreLogFenceInputCount = false;
26
27// TODO: refactor this code by reusing escape analysis.
28class CFREVisitor : public HGraphVisitor {
29 public:
30 CFREVisitor(HGraph* graph, OptimizingCompilerStats* stats)
31 : HGraphVisitor(graph),
Vladimir Markoca6fff82017-10-03 14:49:14 +010032 scoped_allocator_(graph->GetArenaStack()),
Igor Murashkindd018df2017-08-09 10:38:31 -070033 candidate_fences_(scoped_allocator_.Adapter(kArenaAllocCFRE)),
34 candidate_fence_targets_(scoped_allocator_.Adapter(kArenaAllocCFRE)),
35 stats_(stats) {}
36
Roland Levillainbbc6e7e2018-08-24 16:58:47 +010037 void VisitBasicBlock(HBasicBlock* block) override {
Igor Murashkindd018df2017-08-09 10:38:31 -070038 // 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
Roland Levillainbbc6e7e2018-08-24 16:58:47 +010046 void VisitConstructorFence(HConstructorFence* constructor_fence) override {
Igor Murashkindd018df2017-08-09 10:38:31 -070047 candidate_fences_.push_back(constructor_fence);
48
49 for (size_t input_idx = 0; input_idx < constructor_fence->InputCount(); ++input_idx) {
Vladimir Marko54159c62018-06-20 14:30:08 +010050 candidate_fence_targets_.insert(constructor_fence->InputAt(input_idx));
Igor Murashkindd018df2017-08-09 10:38:31 -070051 }
52 }
53
Roland Levillainbbc6e7e2018-08-24 16:58:47 +010054 void VisitBoundType(HBoundType* bound_type) override {
Igor Murashkindd018df2017-08-09 10:38:31 -070055 VisitAlias(bound_type);
56 }
57
Roland Levillainbbc6e7e2018-08-24 16:58:47 +010058 void VisitNullCheck(HNullCheck* null_check) override {
Igor Murashkindd018df2017-08-09 10:38:31 -070059 VisitAlias(null_check);
60 }
61
Roland Levillainbbc6e7e2018-08-24 16:58:47 +010062 void VisitSelect(HSelect* select) override {
Igor Murashkindd018df2017-08-09 10:38:31 -070063 VisitAlias(select);
64 }
65
Roland Levillainbbc6e7e2018-08-24 16:58:47 +010066 void VisitInstanceFieldSet(HInstanceFieldSet* instruction) override {
Igor Murashkindd018df2017-08-09 10:38:31 -070067 HInstruction* value = instruction->InputAt(1);
68 VisitSetLocation(instruction, value);
69 }
70
Roland Levillainbbc6e7e2018-08-24 16:58:47 +010071 void VisitStaticFieldSet(HStaticFieldSet* instruction) override {
Igor Murashkindd018df2017-08-09 10:38:31 -070072 HInstruction* value = instruction->InputAt(1);
73 VisitSetLocation(instruction, value);
74 }
75
Roland Levillainbbc6e7e2018-08-24 16:58:47 +010076 void VisitArraySet(HArraySet* instruction) override {
Igor Murashkindd018df2017-08-09 10:38:31 -070077 HInstruction* value = instruction->InputAt(2);
78 VisitSetLocation(instruction, value);
79 }
80
Andreas Gampefa6a1b02018-09-07 08:11:55 -070081 void VisitDeoptimize(HDeoptimize* instruction ATTRIBUTE_UNUSED) override {
Igor Murashkindd018df2017-08-09 10:38:31 -070082 // Pessimize: Merge all fences.
83 MergeCandidateFences();
84 }
85
Roland Levillainbbc6e7e2018-08-24 16:58:47 +010086 void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) override {
Igor Murashkindd018df2017-08-09 10:38:31 -070087 HandleInvoke(invoke);
88 }
89
Roland Levillainbbc6e7e2018-08-24 16:58:47 +010090 void VisitInvokeVirtual(HInvokeVirtual* invoke) override {
Igor Murashkindd018df2017-08-09 10:38:31 -070091 HandleInvoke(invoke);
92 }
93
Roland Levillainbbc6e7e2018-08-24 16:58:47 +010094 void VisitInvokeInterface(HInvokeInterface* invoke) override {
Igor Murashkindd018df2017-08-09 10:38:31 -070095 HandleInvoke(invoke);
96 }
97
Roland Levillainbbc6e7e2018-08-24 16:58:47 +010098 void VisitInvokeUnresolved(HInvokeUnresolved* invoke) override {
Igor Murashkindd018df2017-08-09 10:38:31 -070099 HandleInvoke(invoke);
100 }
101
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100102 void VisitInvokePolymorphic(HInvokePolymorphic* invoke) override {
Igor Murashkindd018df2017-08-09 10:38:31 -0700103 HandleInvoke(invoke);
104 }
105
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100106 void VisitClinitCheck(HClinitCheck* clinit) override {
Igor Murashkindd018df2017-08-09 10:38:31 -0700107 HandleInvoke(clinit);
108 }
109
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100110 void VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* instruction) override {
Igor Murashkindd018df2017-08-09 10:38:31 -0700111 // Conservatively treat it as an invocation.
112 HandleInvoke(instruction);
113 }
114
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100115 void VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet* instruction) override {
Igor Murashkindd018df2017-08-09 10:38:31 -0700116 // Conservatively treat it as an invocation.
117 HandleInvoke(instruction);
118 }
119
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100120 void VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet* instruction) override {
Igor Murashkindd018df2017-08-09 10:38:31 -0700121 // Conservatively treat it as an invocation.
122 HandleInvoke(instruction);
123 }
124
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100125 void VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet* instruction) override {
Igor Murashkindd018df2017-08-09 10:38:31 -0700126 // Conservatively treat it as an invocation.
127 HandleInvoke(instruction);
128 }
129
130 private:
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.
147 void VisitAlias(HInstruction* aliasing_inst) {
148 // An object is considered "published" if it becomes aliased by other instructions.
149 if (HasInterestingPublishTargetAsInput(aliasing_inst)) {
Igor Murashkindd018df2017-08-09 10:38:31 -0700150 MergeCandidateFences();
151 }
152 }
153
154 void VisitSetLocation(HInstruction* inst ATTRIBUTE_UNUSED, HInstruction* store_input) {
155 // An object is considered "published" if it's stored onto the heap.
156 // Sidenote: A later "LSE" pass can still remove the fence if it proves the
157 // object doesn't actually escape.
158 if (IsInterestingPublishTarget(store_input)) {
159 // Merge all constructor fences that we've seen since
160 // the last interesting store (or since the beginning).
161 MergeCandidateFences();
162 }
163 }
164
165 bool HasInterestingPublishTargetAsInput(HInstruction* inst) {
166 for (size_t input_count = 0; input_count < inst->InputCount(); ++input_count) {
167 if (IsInterestingPublishTarget(inst->InputAt(input_count))) {
168 return true;
169 }
170 }
171
172 return false;
173 }
174
175 // Merges all the existing fences we've seen so far into the last-most fence.
176 //
177 // This resets the list of candidate fences and their targets back to {}.
178 void MergeCandidateFences() {
179 if (candidate_fences_.empty()) {
180 // Nothing to do, need 1+ fences to merge.
181 return;
182 }
183
184 // The merge target is always the "last" candidate fence.
185 HConstructorFence* merge_target = candidate_fences_[candidate_fences_.size() - 1];
186
187 for (HConstructorFence* fence : candidate_fences_) {
188 MaybeMerge(merge_target, fence);
189 }
190
191 if (kCfreLogFenceInputCount) {
192 LOG(INFO) << "CFRE-MergeCandidateFences: Post-merge fence input count "
193 << merge_target->InputCount();
194 }
195
196 // Each merge acts as a cut-off point. The optimization is reset completely.
197 // In theory, we could push the fence as far as its publish, but in practice
198 // there is no benefit to this extra complexity unless we also reordered
199 // the stores to come later.
200 candidate_fences_.clear();
Vladimir Marko54159c62018-06-20 14:30:08 +0100201 candidate_fence_targets_.clear();
Igor Murashkindd018df2017-08-09 10:38:31 -0700202 }
203
204 // A publishing 'store' is only interesting if the value being stored
205 // is one of the fence `targets` in `candidate_fences`.
206 bool IsInterestingPublishTarget(HInstruction* store_input) const {
Vladimir Marko54159c62018-06-20 14:30:08 +0100207 return candidate_fence_targets_.find(store_input) != candidate_fence_targets_.end();
Igor Murashkindd018df2017-08-09 10:38:31 -0700208 }
209
210 void MaybeMerge(HConstructorFence* target, HConstructorFence* src) {
211 if (target == src) {
212 return; // Don't merge a fence into itself.
213 // This is mostly for stats-purposes, we don't want to count merge(x,x)
214 // as removing a fence because it's a no-op.
215 }
216
217 target->Merge(src);
218
219 MaybeRecordStat(stats_, MethodCompilationStat::kConstructorFenceRemovedCFRE);
220 }
221
Vladimir Markoca6fff82017-10-03 14:49:14 +0100222 // Phase-local heap memory allocator for CFRE optimizer.
223 ScopedArenaAllocator scoped_allocator_;
Igor Murashkindd018df2017-08-09 10:38:31 -0700224
225 // Set of constructor fences that we've seen in the current block.
226 // Each constructor fences acts as a guard for one or more `targets`.
227 // There exist no stores to any `targets` between any of these fences.
228 //
229 // Fences are in succession order (e.g. fence[i] succeeds fence[i-1]
230 // within the same basic block).
Vladimir Markoca6fff82017-10-03 14:49:14 +0100231 ScopedArenaVector<HConstructorFence*> candidate_fences_;
Igor Murashkindd018df2017-08-09 10:38:31 -0700232
233 // Stores a set of the fence targets, to allow faster lookup of whether
234 // a detected publish is a target of one of the candidate fences.
Vladimir Markoca6fff82017-10-03 14:49:14 +0100235 ScopedArenaHashSet<HInstruction*> candidate_fence_targets_;
Igor Murashkindd018df2017-08-09 10:38:31 -0700236
237 // Used to record stats about the optimization.
238 OptimizingCompilerStats* const stats_;
239
240 DISALLOW_COPY_AND_ASSIGN(CFREVisitor);
241};
242
Aart Bik24773202018-04-26 10:28:51 -0700243bool ConstructorFenceRedundancyElimination::Run() {
Igor Murashkindd018df2017-08-09 10:38:31 -0700244 CFREVisitor cfre_visitor(graph_, stats_);
245
246 // Arbitrarily visit in reverse-post order.
247 // The exact block visit order does not matter, as the algorithm
248 // only operates on a single block at a time.
249 cfre_visitor.VisitReversePostOrder();
Aart Bik24773202018-04-26 10:28:51 -0700250 return true;
Igor Murashkindd018df2017-08-09 10:38:31 -0700251}
252
253} // namespace art