blob: 3daa6472de914c0071863b47762a7526205d9261 [file] [log] [blame]
xueliang.zhongc239a2b2017-04-27 15:31:37 +01001/*
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 "load_store_analysis.h"
18
Vladimir Marko0a516052019-10-14 13:00:44 +000019namespace art {
xueliang.zhongc239a2b2017-04-27 15:31:37 +010020
21// A cap for the number of heap locations to prevent pathological time/space consumption.
22// The number of heap locations for most of the methods stays below this threshold.
23constexpr size_t kMaxNumberOfHeapLocations = 32;
24
xueliang.zhongb50b16a2017-09-19 17:43:29 +010025// Test if two integer ranges [l1,h1] and [l2,h2] overlap.
26// Note that the ranges are inclusive on both ends.
27// l1|------|h1
28// l2|------|h2
29static bool CanIntegerRangesOverlap(int64_t l1, int64_t h1, int64_t l2, int64_t h2) {
30 return std::max(l1, l2) <= std::min(h1, h2);
31}
xueliang.zhong016c0f12017-05-12 18:16:31 +010032
xueliang.zhongb50b16a2017-09-19 17:43:29 +010033static bool CanBinaryOpAndIndexAlias(const HBinaryOperation* idx1,
34 const size_t vector_length1,
35 const HInstruction* idx2,
36 const size_t vector_length2) {
37 if (!IsAddOrSub(idx1)) {
xueliang.zhong016c0f12017-05-12 18:16:31 +010038 // We currently only support Add and Sub operations.
39 return true;
40 }
xueliang.zhongb50b16a2017-09-19 17:43:29 +010041 if (idx1->AsBinaryOperation()->GetLeastConstantLeft() != idx2) {
42 // Cannot analyze [i+CONST1] and [j].
43 return true;
44 }
45 if (!idx1->GetConstantRight()->IsIntConstant()) {
xueliang.zhong016c0f12017-05-12 18:16:31 +010046 return true;
47 }
48
xueliang.zhongb50b16a2017-09-19 17:43:29 +010049 // Since 'i' are the same in [i+CONST] and [i],
50 // further compare [CONST] and [0].
51 int64_t l1 = idx1->IsAdd() ?
52 idx1->GetConstantRight()->AsIntConstant()->GetValue() :
53 -idx1->GetConstantRight()->AsIntConstant()->GetValue();
54 int64_t l2 = 0;
55 int64_t h1 = l1 + (vector_length1 - 1);
56 int64_t h2 = l2 + (vector_length2 - 1);
57 return CanIntegerRangesOverlap(l1, h1, l2, h2);
xueliang.zhong016c0f12017-05-12 18:16:31 +010058}
59
xueliang.zhongb50b16a2017-09-19 17:43:29 +010060static bool CanBinaryOpsAlias(const HBinaryOperation* idx1,
61 const size_t vector_length1,
62 const HBinaryOperation* idx2,
63 const size_t vector_length2) {
64 if (!IsAddOrSub(idx1) || !IsAddOrSub(idx2)) {
65 // We currently only support Add and Sub operations.
66 return true;
67 }
68 if (idx1->AsBinaryOperation()->GetLeastConstantLeft() !=
69 idx2->AsBinaryOperation()->GetLeastConstantLeft()) {
70 // Cannot analyze [i+CONST1] and [j+CONST2].
71 return true;
72 }
73 if (!idx1->GetConstantRight()->IsIntConstant() ||
74 !idx2->GetConstantRight()->IsIntConstant()) {
xueliang.zhong016c0f12017-05-12 18:16:31 +010075 return true;
76 }
77
xueliang.zhongb50b16a2017-09-19 17:43:29 +010078 // Since 'i' are the same in [i+CONST1] and [i+CONST2],
79 // further compare [CONST1] and [CONST2].
80 int64_t l1 = idx1->IsAdd() ?
81 idx1->GetConstantRight()->AsIntConstant()->GetValue() :
82 -idx1->GetConstantRight()->AsIntConstant()->GetValue();
83 int64_t l2 = idx2->IsAdd() ?
84 idx2->GetConstantRight()->AsIntConstant()->GetValue() :
85 -idx2->GetConstantRight()->AsIntConstant()->GetValue();
86 int64_t h1 = l1 + (vector_length1 - 1);
87 int64_t h2 = l2 + (vector_length2 - 1);
88 return CanIntegerRangesOverlap(l1, h1, l2, h2);
xueliang.zhong016c0f12017-05-12 18:16:31 +010089}
90
Alex Light86fe9b82020-11-16 16:54:01 +000091// Make sure we mark any writes/potential writes to heap-locations within partially
92// escaped values as escaping.
93void ReferenceInfo::PrunePartialEscapeWrites() {
94 if (!subgraph_.IsValid()) {
95 // All paths escape.
96 return;
97 }
98 HGraph* graph = reference_->GetBlock()->GetGraph();
99 ArenaBitVector additional_exclusions(
100 allocator_, graph->GetBlocks().size(), false, kArenaAllocLSA);
101 for (const HUseListNode<HInstruction*>& use : reference_->GetUses()) {
102 const HInstruction* user = use.GetUser();
103 const bool possible_exclusion =
104 !additional_exclusions.IsBitSet(user->GetBlock()->GetBlockId()) &&
105 subgraph_.ContainsBlock(user->GetBlock());
106 const bool is_written_to =
107 (user->IsUnresolvedInstanceFieldSet() || user->IsUnresolvedStaticFieldSet() ||
108 user->IsInstanceFieldSet() || user->IsStaticFieldSet() || user->IsArraySet()) &&
109 (reference_ == user->InputAt(0));
110 if (possible_exclusion && is_written_to &&
111 std::any_of(subgraph_.UnreachableBlocks().begin(),
112 subgraph_.UnreachableBlocks().end(),
113 [&](const HBasicBlock* excluded) -> bool {
114 return reference_->GetBlock()->GetGraph()->PathBetween(excluded,
115 user->GetBlock());
116 })) {
117 // This object had memory written to it somewhere, if it escaped along
118 // some paths prior to the current block this write also counts as an
119 // escape.
120 additional_exclusions.SetBit(user->GetBlock()->GetBlockId());
121 }
122 }
123 if (UNLIKELY(additional_exclusions.IsAnyBitSet())) {
124 for (uint32_t exc : additional_exclusions.Indexes()) {
125 subgraph_.RemoveBlock(graph->GetBlocks()[exc]);
126 }
127 }
128}
129
130bool HeapLocationCollector::InstructionEligibleForLSERemoval(HInstruction* inst) const {
131 if (inst->IsNewInstance()) {
132 return !inst->AsNewInstance()->NeedsChecks();
133 } else if (inst->IsNewArray()) {
134 HInstruction* array_length = inst->AsNewArray()->GetLength();
135 bool known_array_length =
136 array_length->IsIntConstant() && array_length->AsIntConstant()->GetValue() >= 0;
137 return known_array_length &&
138 std::all_of(inst->GetUses().cbegin(),
139 inst->GetUses().cend(),
140 [&](const HUseListNode<HInstruction*>& user) {
141 if (user.GetUser()->IsArrayGet() || user.GetUser()->IsArraySet()) {
142 return user.GetUser()->InputAt(1)->IsIntConstant();
143 }
144 return true;
145 });
146 } else {
147 return false;
148 }
149}
150
151void HeapLocationCollector::DumpReferenceStats(OptimizingCompilerStats* stats) {
152 if (stats == nullptr) {
153 return;
154 }
155 std::vector<bool> seen_instructions(GetGraph()->GetCurrentInstructionId(), false);
156 for (auto hl : heap_locations_) {
157 auto ri = hl->GetReferenceInfo();
158 if (ri == nullptr || seen_instructions[ri->GetReference()->GetId()]) {
159 continue;
160 }
161 auto instruction = ri->GetReference();
162 seen_instructions[instruction->GetId()] = true;
163 if (ri->IsSingletonAndRemovable()) {
164 if (InstructionEligibleForLSERemoval(instruction)) {
165 MaybeRecordStat(stats, MethodCompilationStat::kFullLSEPossible);
166 }
167 }
168 // TODO This is an estimate of the number of allocations we will be able
169 // to (partially) remove. As additional work is done this can be refined.
170 if (ri->IsPartialSingleton() && instruction->IsNewInstance() &&
171 ri->GetNoEscapeSubgraph()->ContainsBlock(instruction->GetBlock()) &&
172 !ri->GetNoEscapeSubgraph()->GetExcludedCohorts().empty() &&
173 InstructionEligibleForLSERemoval(instruction)) {
174 MaybeRecordStat(stats, MethodCompilationStat::kPartialLSEPossible);
175 }
176 }
177}
178
xueliang.zhongb50b16a2017-09-19 17:43:29 +0100179bool HeapLocationCollector::CanArrayElementsAlias(const HInstruction* idx1,
180 const size_t vector_length1,
181 const HInstruction* idx2,
182 const size_t vector_length2) const {
xueliang.zhong016c0f12017-05-12 18:16:31 +0100183 DCHECK(idx1 != nullptr);
184 DCHECK(idx2 != nullptr);
xueliang.zhongb50b16a2017-09-19 17:43:29 +0100185 DCHECK_GE(vector_length1, HeapLocation::kScalar);
186 DCHECK_GE(vector_length2, HeapLocation::kScalar);
xueliang.zhong016c0f12017-05-12 18:16:31 +0100187
xueliang.zhongb50b16a2017-09-19 17:43:29 +0100188 // [i] and [i].
xueliang.zhong016c0f12017-05-12 18:16:31 +0100189 if (idx1 == idx2) {
xueliang.zhong016c0f12017-05-12 18:16:31 +0100190 return true;
191 }
xueliang.zhongb50b16a2017-09-19 17:43:29 +0100192
193 // [CONST1] and [CONST2].
xueliang.zhong016c0f12017-05-12 18:16:31 +0100194 if (idx1->IsIntConstant() && idx2->IsIntConstant()) {
xueliang.zhongb50b16a2017-09-19 17:43:29 +0100195 int64_t l1 = idx1->AsIntConstant()->GetValue();
196 int64_t l2 = idx2->AsIntConstant()->GetValue();
197 // To avoid any overflow in following CONST+vector_length calculation,
198 // use int64_t instead of int32_t.
199 int64_t h1 = l1 + (vector_length1 - 1);
200 int64_t h2 = l2 + (vector_length2 - 1);
201 return CanIntegerRangesOverlap(l1, h1, l2, h2);
xueliang.zhong016c0f12017-05-12 18:16:31 +0100202 }
203
xueliang.zhongb50b16a2017-09-19 17:43:29 +0100204 // [i+CONST] and [i].
205 if (idx1->IsBinaryOperation() &&
206 idx1->AsBinaryOperation()->GetConstantRight() != nullptr &&
207 idx1->AsBinaryOperation()->GetLeastConstantLeft() == idx2) {
208 return CanBinaryOpAndIndexAlias(idx1->AsBinaryOperation(),
209 vector_length1,
210 idx2,
211 vector_length2);
xueliang.zhong016c0f12017-05-12 18:16:31 +0100212 }
213
xueliang.zhongb50b16a2017-09-19 17:43:29 +0100214 // [i] and [i+CONST].
215 if (idx2->IsBinaryOperation() &&
216 idx2->AsBinaryOperation()->GetConstantRight() != nullptr &&
217 idx2->AsBinaryOperation()->GetLeastConstantLeft() == idx1) {
218 return CanBinaryOpAndIndexAlias(idx2->AsBinaryOperation(),
219 vector_length2,
220 idx1,
221 vector_length1);
222 }
223
224 // [i+CONST1] and [i+CONST2].
225 if (idx1->IsBinaryOperation() &&
226 idx1->AsBinaryOperation()->GetConstantRight() != nullptr &&
227 idx2->IsBinaryOperation() &&
228 idx2->AsBinaryOperation()->GetConstantRight() != nullptr) {
229 return CanBinaryOpsAlias(idx1->AsBinaryOperation(),
230 vector_length1,
231 idx2->AsBinaryOperation(),
232 vector_length2);
xueliang.zhong016c0f12017-05-12 18:16:31 +0100233 }
234
235 // By default, MAY alias.
236 return true;
237}
238
Aart Bik24773202018-04-26 10:28:51 -0700239bool LoadStoreAnalysis::Run() {
xueliang.zhongc239a2b2017-04-27 15:31:37 +0100240 for (HBasicBlock* block : graph_->GetReversePostOrder()) {
241 heap_location_collector_.VisitBasicBlock(block);
242 }
243
244 if (heap_location_collector_.GetNumberOfHeapLocations() > kMaxNumberOfHeapLocations) {
245 // Bail out if there are too many heap locations to deal with.
246 heap_location_collector_.CleanUp();
Aart Bik24773202018-04-26 10:28:51 -0700247 return false;
xueliang.zhongc239a2b2017-04-27 15:31:37 +0100248 }
249 if (!heap_location_collector_.HasHeapStores()) {
250 // Without heap stores, this pass would act mostly as GVN on heap accesses.
251 heap_location_collector_.CleanUp();
Aart Bik24773202018-04-26 10:28:51 -0700252 return false;
xueliang.zhongc239a2b2017-04-27 15:31:37 +0100253 }
254 if (heap_location_collector_.HasVolatile() || heap_location_collector_.HasMonitorOps()) {
255 // Don't do load/store elimination if the method has volatile field accesses or
256 // monitor operations, for now.
257 // TODO: do it right.
258 heap_location_collector_.CleanUp();
Aart Bik24773202018-04-26 10:28:51 -0700259 return false;
xueliang.zhongc239a2b2017-04-27 15:31:37 +0100260 }
261
262 heap_location_collector_.BuildAliasingMatrix();
Alex Light86fe9b82020-11-16 16:54:01 +0000263 heap_location_collector_.DumpReferenceStats(stats_);
Aart Bik24773202018-04-26 10:28:51 -0700264 return true;
xueliang.zhongc239a2b2017-04-27 15:31:37 +0100265}
266
267} // namespace art