blob: 486e904bd1c2669599786ffd3c9729eb89b15b61 [file] [log] [blame]
Aart Bikd14c5952015-09-08 15:25:15 -07001/*
2 * Copyright (C) 2015 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 <limits.h>
18
19#include "induction_var_range.h"
20
21namespace art {
22
23static bool IsValidConstant32(int32_t c) {
24 return INT_MIN < c && c < INT_MAX;
25}
26
27static bool IsValidConstant64(int64_t c) {
28 return INT_MIN < c && c < INT_MAX;
29}
30
31/** Returns true if 32-bit addition can be done safely (and is not an unknown range). */
32static bool IsSafeAdd(int32_t c1, int32_t c2) {
33 if (IsValidConstant32(c1) && IsValidConstant32(c2)) {
34 return IsValidConstant64(static_cast<int64_t>(c1) + static_cast<int64_t>(c2));
35 }
36 return false;
37}
38
39/** Returns true if 32-bit subtraction can be done safely (and is not an unknown range). */
40static bool IsSafeSub(int32_t c1, int32_t c2) {
41 if (IsValidConstant32(c1) && IsValidConstant32(c2)) {
42 return IsValidConstant64(static_cast<int64_t>(c1) - static_cast<int64_t>(c2));
43 }
44 return false;
45}
46
47/** Returns true if 32-bit multiplication can be done safely (and is not an unknown range). */
48static bool IsSafeMul(int32_t c1, int32_t c2) {
49 if (IsValidConstant32(c1) && IsValidConstant32(c2)) {
50 return IsValidConstant64(static_cast<int64_t>(c1) * static_cast<int64_t>(c2));
51 }
52 return false;
53}
54
55/** Returns true if 32-bit division can be done safely (and is not an unknown range). */
56static bool IsSafeDiv(int32_t c1, int32_t c2) {
57 if (IsValidConstant32(c1) && IsValidConstant32(c2) && c2 != 0) {
58 return IsValidConstant64(static_cast<int64_t>(c1) / static_cast<int64_t>(c2));
59 }
60 return false;
61}
62
63/** Returns true for 32/64-bit integral constant within known range. */
64static bool IsIntAndGet(HInstruction* instruction, int32_t* value) {
65 if (instruction->IsIntConstant()) {
66 const int32_t c = instruction->AsIntConstant()->GetValue();
67 if (IsValidConstant32(c)) {
68 *value = c;
69 return true;
70 }
71 } else if (instruction->IsLongConstant()) {
72 const int64_t c = instruction->AsLongConstant()->GetValue();
73 if (IsValidConstant64(c)) {
74 *value = c;
75 return true;
76 }
77 }
78 return false;
79}
80
81//
82// Public class methods.
83//
84
85InductionVarRange::InductionVarRange(HInductionVarAnalysis* induction_analysis)
86 : induction_analysis_(induction_analysis) {
87}
88
89InductionVarRange::Value InductionVarRange::GetMinInduction(HInstruction* context,
90 HInstruction* instruction) {
91 HLoopInformation* loop = context->GetBlock()->GetLoopInformation();
92 if (loop != nullptr && induction_analysis_ != nullptr) {
93 return GetMin(induction_analysis_->LookupInfo(loop, instruction), GetTripCount(loop, context));
94 }
95 return Value(INT_MIN);
96}
97
98InductionVarRange::Value InductionVarRange::GetMaxInduction(HInstruction* context,
99 HInstruction* instruction) {
100 HLoopInformation* loop = context->GetBlock()->GetLoopInformation();
101 if (loop != nullptr && induction_analysis_ != nullptr) {
102 return GetMax(induction_analysis_->LookupInfo(loop, instruction), GetTripCount(loop, context));
103 }
104 return Value(INT_MAX);
105}
106
107//
108// Private class methods.
109//
110
111HInductionVarAnalysis::InductionInfo* InductionVarRange::GetTripCount(HLoopInformation* loop,
112 HInstruction* context) {
113 // The trip-count expression is only valid when the top-test is taken at least once,
114 // that means, when the analyzed context appears outside the loop header itself.
115 // Early-exit loops are okay, since in those cases, the trip-count is conservative.
116 if (context->GetBlock() != loop->GetHeader()) {
117 HInductionVarAnalysis::InductionInfo* trip =
118 induction_analysis_->LookupInfo(loop, loop->GetHeader()->GetLastInstruction());
119 if (trip != nullptr) {
120 // Wrap the trip-count representation in its own unusual NOP node, so that range analysis
121 // is able to determine the [0, TC - 1] interval without having to construct constants.
122 return induction_analysis_->CreateInvariantOp(HInductionVarAnalysis::kNop, trip, trip);
123 }
124 }
125 return nullptr;
126}
127
128InductionVarRange::Value InductionVarRange::GetFetch(HInstruction* instruction,
Aart Bik22af3be2015-09-10 12:50:58 -0700129 HInductionVarAnalysis::InductionInfo* trip,
Aart Bikd14c5952015-09-08 15:25:15 -0700130 int32_t fail_value) {
131 // Detect constants and chase the fetch a bit deeper into the HIR tree, so that it becomes
132 // more likely range analysis will compare the same instructions as terminal nodes.
133 int32_t value;
134 if (IsIntAndGet(instruction, &value)) {
135 return Value(value);
136 } else if (instruction->IsAdd()) {
137 if (IsIntAndGet(instruction->InputAt(0), &value)) {
Aart Bik22af3be2015-09-10 12:50:58 -0700138 return AddValue(Value(value),
139 GetFetch(instruction->InputAt(1), trip, fail_value), fail_value);
Aart Bikd14c5952015-09-08 15:25:15 -0700140 } else if (IsIntAndGet(instruction->InputAt(1), &value)) {
Aart Bik22af3be2015-09-10 12:50:58 -0700141 return AddValue(GetFetch(instruction->InputAt(0), trip, fail_value),
142 Value(value), fail_value);
143 }
144 } else if (fail_value < 0) {
145 // Special case: within the loop-body, minimum of trip-count is 1.
146 if (trip != nullptr && instruction == trip->op_b->fetch) {
147 return Value(1);
Aart Bikd14c5952015-09-08 15:25:15 -0700148 }
149 }
150 return Value(instruction, 1, 0);
151}
152
153InductionVarRange::Value InductionVarRange::GetMin(HInductionVarAnalysis::InductionInfo* info,
154 HInductionVarAnalysis::InductionInfo* trip) {
155 if (info != nullptr) {
156 switch (info->induction_class) {
157 case HInductionVarAnalysis::kInvariant:
158 // Invariants.
159 switch (info->operation) {
160 case HInductionVarAnalysis::kNop: // normalized: 0
161 DCHECK_EQ(info->op_a, info->op_b);
162 return Value(0);
163 case HInductionVarAnalysis::kAdd:
164 return AddValue(GetMin(info->op_a, trip), GetMin(info->op_b, trip), INT_MIN);
165 case HInductionVarAnalysis::kSub: // second max!
166 return SubValue(GetMin(info->op_a, trip), GetMax(info->op_b, trip), INT_MIN);
167 case HInductionVarAnalysis::kNeg: // second max!
168 return SubValue(Value(0), GetMax(info->op_b, trip), INT_MIN);
169 case HInductionVarAnalysis::kMul:
170 return GetMul(info->op_a, info->op_b, trip, INT_MIN);
171 case HInductionVarAnalysis::kDiv:
172 return GetDiv(info->op_a, info->op_b, trip, INT_MIN);
173 case HInductionVarAnalysis::kFetch:
Aart Bik22af3be2015-09-10 12:50:58 -0700174 return GetFetch(info->fetch, trip, INT_MIN);
Aart Bikd14c5952015-09-08 15:25:15 -0700175 }
176 break;
177 case HInductionVarAnalysis::kLinear:
178 // Minimum over linear induction a * i + b, for normalized 0 <= i < TC.
179 return AddValue(GetMul(info->op_a, trip, trip, INT_MIN),
180 GetMin(info->op_b, trip), INT_MIN);
181 case HInductionVarAnalysis::kWrapAround:
182 case HInductionVarAnalysis::kPeriodic:
183 // Minimum over all values in the wrap-around/periodic.
184 return MinValue(GetMin(info->op_a, trip), GetMin(info->op_b, trip));
185 }
186 }
187 return Value(INT_MIN);
188}
189
190InductionVarRange::Value InductionVarRange::GetMax(HInductionVarAnalysis::InductionInfo* info,
191 HInductionVarAnalysis::InductionInfo* trip) {
192 if (info != nullptr) {
193 switch (info->induction_class) {
194 case HInductionVarAnalysis::kInvariant:
195 // Invariants.
196 switch (info->operation) {
197 case HInductionVarAnalysis::kNop: // normalized: TC - 1
198 DCHECK_EQ(info->op_a, info->op_b);
199 return SubValue(GetMax(info->op_b, trip), Value(1), INT_MAX);
200 case HInductionVarAnalysis::kAdd:
201 return AddValue(GetMax(info->op_a, trip), GetMax(info->op_b, trip), INT_MAX);
202 case HInductionVarAnalysis::kSub: // second min!
203 return SubValue(GetMax(info->op_a, trip), GetMin(info->op_b, trip), INT_MAX);
204 case HInductionVarAnalysis::kNeg: // second min!
205 return SubValue(Value(0), GetMin(info->op_b, trip), INT_MAX);
206 case HInductionVarAnalysis::kMul:
207 return GetMul(info->op_a, info->op_b, trip, INT_MAX);
208 case HInductionVarAnalysis::kDiv:
209 return GetDiv(info->op_a, info->op_b, trip, INT_MAX);
210 case HInductionVarAnalysis::kFetch:
Aart Bik22af3be2015-09-10 12:50:58 -0700211 return GetFetch(info->fetch, trip, INT_MAX);
Aart Bikd14c5952015-09-08 15:25:15 -0700212 }
213 break;
214 case HInductionVarAnalysis::kLinear:
215 // Maximum over linear induction a * i + b, for normalized 0 <= i < TC.
216 return AddValue(GetMul(info->op_a, trip, trip, INT_MAX),
217 GetMax(info->op_b, trip), INT_MAX);
218 case HInductionVarAnalysis::kWrapAround:
219 case HInductionVarAnalysis::kPeriodic:
220 // Maximum over all values in the wrap-around/periodic.
221 return MaxValue(GetMax(info->op_a, trip), GetMax(info->op_b, trip));
222 }
223 }
224 return Value(INT_MAX);
225}
226
227InductionVarRange::Value InductionVarRange::GetMul(HInductionVarAnalysis::InductionInfo* info1,
228 HInductionVarAnalysis::InductionInfo* info2,
229 HInductionVarAnalysis::InductionInfo* trip,
230 int32_t fail_value) {
231 Value v1_min = GetMin(info1, trip);
232 Value v1_max = GetMax(info1, trip);
233 Value v2_min = GetMin(info2, trip);
234 Value v2_max = GetMax(info2, trip);
235 if (v1_min.a_constant == 0 && v1_min.b_constant >= 0) {
236 // Positive range vs. positive or negative range.
237 if (v2_min.a_constant == 0 && v2_min.b_constant >= 0) {
238 return (fail_value < 0) ? MulValue(v1_min, v2_min, fail_value)
239 : MulValue(v1_max, v2_max, fail_value);
240 } else if (v2_max.a_constant == 0 && v2_max.b_constant <= 0) {
241 return (fail_value < 0) ? MulValue(v1_max, v2_min, fail_value)
242 : MulValue(v1_min, v2_max, fail_value);
243 }
244 } else if (v1_min.a_constant == 0 && v1_min.b_constant <= 0) {
245 // Negative range vs. positive or negative range.
246 if (v2_min.a_constant == 0 && v2_min.b_constant >= 0) {
247 return (fail_value < 0) ? MulValue(v1_min, v2_max, fail_value)
248 : MulValue(v1_max, v2_min, fail_value);
249 } else if (v2_max.a_constant == 0 && v2_max.b_constant <= 0) {
250 return (fail_value < 0) ? MulValue(v1_max, v2_max, fail_value)
251 : MulValue(v1_min, v2_min, fail_value);
252 }
253 }
254 return Value(fail_value);
255}
256
257InductionVarRange::Value InductionVarRange::GetDiv(HInductionVarAnalysis::InductionInfo* info1,
258 HInductionVarAnalysis::InductionInfo* info2,
259 HInductionVarAnalysis::InductionInfo* trip,
260 int32_t fail_value) {
261 Value v1_min = GetMin(info1, trip);
262 Value v1_max = GetMax(info1, trip);
263 Value v2_min = GetMin(info2, trip);
264 Value v2_max = GetMax(info2, trip);
265 if (v1_min.a_constant == 0 && v1_min.b_constant >= 0) {
266 // Positive range vs. positive or negative range.
267 if (v2_min.a_constant == 0 && v2_min.b_constant >= 0) {
268 return (fail_value < 0) ? DivValue(v1_min, v2_max, fail_value)
269 : DivValue(v1_max, v2_min, fail_value);
270 } else if (v2_max.a_constant == 0 && v2_max.b_constant <= 0) {
271 return (fail_value < 0) ? DivValue(v1_max, v2_max, fail_value)
272 : DivValue(v1_min, v2_min, fail_value);
273 }
274 } else if (v1_min.a_constant == 0 && v1_min.b_constant <= 0) {
275 // Negative range vs. positive or negative range.
276 if (v2_min.a_constant == 0 && v2_min.b_constant >= 0) {
277 return (fail_value < 0) ? DivValue(v1_min, v2_min, fail_value)
278 : DivValue(v1_max, v2_max, fail_value);
279 } else if (v2_max.a_constant == 0 && v2_max.b_constant <= 0) {
280 return (fail_value < 0) ? DivValue(v1_max, v2_min, fail_value)
281 : DivValue(v1_min, v2_max, fail_value);
282 }
283 }
284 return Value(fail_value);
285}
286
287InductionVarRange::Value InductionVarRange::AddValue(Value v1, Value v2, int32_t fail_value) {
288 if (IsSafeAdd(v1.b_constant, v2.b_constant)) {
289 const int32_t b = v1.b_constant + v2.b_constant;
290 if (v1.a_constant == 0) {
291 return Value(v2.instruction, v2.a_constant, b);
292 } else if (v2.a_constant == 0) {
293 return Value(v1.instruction, v1.a_constant, b);
294 } else if (v1.instruction == v2.instruction && IsSafeAdd(v1.a_constant, v2.a_constant)) {
295 return Value(v1.instruction, v1.a_constant + v2.a_constant, b);
296 }
297 }
298 return Value(fail_value);
299}
300
301InductionVarRange::Value InductionVarRange::SubValue(Value v1, Value v2, int32_t fail_value) {
302 if (IsSafeSub(v1.b_constant, v2.b_constant)) {
303 const int32_t b = v1.b_constant - v2.b_constant;
304 if (v1.a_constant == 0 && IsSafeSub(0, v2.a_constant)) {
305 return Value(v2.instruction, -v2.a_constant, b);
306 } else if (v2.a_constant == 0) {
307 return Value(v1.instruction, v1.a_constant, b);
308 } else if (v1.instruction == v2.instruction && IsSafeSub(v1.a_constant, v2.a_constant)) {
309 return Value(v1.instruction, v1.a_constant - v2.a_constant, b);
310 }
311 }
312 return Value(fail_value);
313}
314
315InductionVarRange::Value InductionVarRange::MulValue(Value v1, Value v2, int32_t fail_value) {
316 if (v1.a_constant == 0) {
317 if (IsSafeMul(v1.b_constant, v2.a_constant) && IsSafeMul(v1.b_constant, v2.b_constant)) {
318 return Value(v2.instruction, v1.b_constant * v2.a_constant, v1.b_constant * v2.b_constant);
319 }
320 } else if (v2.a_constant == 0) {
321 if (IsSafeMul(v1.a_constant, v2.b_constant) && IsSafeMul(v1.b_constant, v2.b_constant)) {
322 return Value(v1.instruction, v1.a_constant * v2.b_constant, v1.b_constant * v2.b_constant);
323 }
324 }
325 return Value(fail_value);
326}
327
328InductionVarRange::Value InductionVarRange::DivValue(Value v1, Value v2, int32_t fail_value) {
329 if (v1.a_constant == 0 && v2.a_constant == 0) {
330 if (IsSafeDiv(v1.b_constant, v2.b_constant)) {
331 return Value(v1.b_constant / v2.b_constant);
332 }
333 }
334 return Value(fail_value);
335}
336
337InductionVarRange::Value InductionVarRange::MinValue(Value v1, Value v2) {
338 if (v1.instruction == v2.instruction && v1.a_constant == v2.a_constant) {
339 return Value(v1.instruction, v1.a_constant, std::min(v1.b_constant, v2.b_constant));
340 }
341 return Value(INT_MIN);
342}
343
344InductionVarRange::Value InductionVarRange::MaxValue(Value v1, Value v2) {
345 if (v1.instruction == v2.instruction && v1.a_constant == v2.a_constant) {
346 return Value(v1.instruction, v1.a_constant, std::max(v1.b_constant, v2.b_constant));
347 }
348 return Value(INT_MAX);
349}
350
351} // namespace art