blob: bd903340adc8f3da8a4ae9b95e91f1cc8be3e5f2 [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,
129 int32_t fail_value) {
130 // Detect constants and chase the fetch a bit deeper into the HIR tree, so that it becomes
131 // more likely range analysis will compare the same instructions as terminal nodes.
132 int32_t value;
133 if (IsIntAndGet(instruction, &value)) {
134 return Value(value);
135 } else if (instruction->IsAdd()) {
136 if (IsIntAndGet(instruction->InputAt(0), &value)) {
137 return AddValue(Value(value), GetFetch(instruction->InputAt(1), fail_value), fail_value);
138 } else if (IsIntAndGet(instruction->InputAt(1), &value)) {
139 return AddValue(GetFetch(instruction->InputAt(0), fail_value), Value(value), fail_value);
140 }
141 }
142 return Value(instruction, 1, 0);
143}
144
145InductionVarRange::Value InductionVarRange::GetMin(HInductionVarAnalysis::InductionInfo* info,
146 HInductionVarAnalysis::InductionInfo* trip) {
147 if (info != nullptr) {
148 switch (info->induction_class) {
149 case HInductionVarAnalysis::kInvariant:
150 // Invariants.
151 switch (info->operation) {
152 case HInductionVarAnalysis::kNop: // normalized: 0
153 DCHECK_EQ(info->op_a, info->op_b);
154 return Value(0);
155 case HInductionVarAnalysis::kAdd:
156 return AddValue(GetMin(info->op_a, trip), GetMin(info->op_b, trip), INT_MIN);
157 case HInductionVarAnalysis::kSub: // second max!
158 return SubValue(GetMin(info->op_a, trip), GetMax(info->op_b, trip), INT_MIN);
159 case HInductionVarAnalysis::kNeg: // second max!
160 return SubValue(Value(0), GetMax(info->op_b, trip), INT_MIN);
161 case HInductionVarAnalysis::kMul:
162 return GetMul(info->op_a, info->op_b, trip, INT_MIN);
163 case HInductionVarAnalysis::kDiv:
164 return GetDiv(info->op_a, info->op_b, trip, INT_MIN);
165 case HInductionVarAnalysis::kFetch:
166 return GetFetch(info->fetch, INT_MIN);
167 }
168 break;
169 case HInductionVarAnalysis::kLinear:
170 // Minimum over linear induction a * i + b, for normalized 0 <= i < TC.
171 return AddValue(GetMul(info->op_a, trip, trip, INT_MIN),
172 GetMin(info->op_b, trip), INT_MIN);
173 case HInductionVarAnalysis::kWrapAround:
174 case HInductionVarAnalysis::kPeriodic:
175 // Minimum over all values in the wrap-around/periodic.
176 return MinValue(GetMin(info->op_a, trip), GetMin(info->op_b, trip));
177 }
178 }
179 return Value(INT_MIN);
180}
181
182InductionVarRange::Value InductionVarRange::GetMax(HInductionVarAnalysis::InductionInfo* info,
183 HInductionVarAnalysis::InductionInfo* trip) {
184 if (info != nullptr) {
185 switch (info->induction_class) {
186 case HInductionVarAnalysis::kInvariant:
187 // Invariants.
188 switch (info->operation) {
189 case HInductionVarAnalysis::kNop: // normalized: TC - 1
190 DCHECK_EQ(info->op_a, info->op_b);
191 return SubValue(GetMax(info->op_b, trip), Value(1), INT_MAX);
192 case HInductionVarAnalysis::kAdd:
193 return AddValue(GetMax(info->op_a, trip), GetMax(info->op_b, trip), INT_MAX);
194 case HInductionVarAnalysis::kSub: // second min!
195 return SubValue(GetMax(info->op_a, trip), GetMin(info->op_b, trip), INT_MAX);
196 case HInductionVarAnalysis::kNeg: // second min!
197 return SubValue(Value(0), GetMin(info->op_b, trip), INT_MAX);
198 case HInductionVarAnalysis::kMul:
199 return GetMul(info->op_a, info->op_b, trip, INT_MAX);
200 case HInductionVarAnalysis::kDiv:
201 return GetDiv(info->op_a, info->op_b, trip, INT_MAX);
202 case HInductionVarAnalysis::kFetch:
203 return GetFetch(info->fetch, INT_MAX);
204 }
205 break;
206 case HInductionVarAnalysis::kLinear:
207 // Maximum over linear induction a * i + b, for normalized 0 <= i < TC.
208 return AddValue(GetMul(info->op_a, trip, trip, INT_MAX),
209 GetMax(info->op_b, trip), INT_MAX);
210 case HInductionVarAnalysis::kWrapAround:
211 case HInductionVarAnalysis::kPeriodic:
212 // Maximum over all values in the wrap-around/periodic.
213 return MaxValue(GetMax(info->op_a, trip), GetMax(info->op_b, trip));
214 }
215 }
216 return Value(INT_MAX);
217}
218
219InductionVarRange::Value InductionVarRange::GetMul(HInductionVarAnalysis::InductionInfo* info1,
220 HInductionVarAnalysis::InductionInfo* info2,
221 HInductionVarAnalysis::InductionInfo* trip,
222 int32_t fail_value) {
223 Value v1_min = GetMin(info1, trip);
224 Value v1_max = GetMax(info1, trip);
225 Value v2_min = GetMin(info2, trip);
226 Value v2_max = GetMax(info2, trip);
227 if (v1_min.a_constant == 0 && v1_min.b_constant >= 0) {
228 // Positive range vs. positive or negative range.
229 if (v2_min.a_constant == 0 && v2_min.b_constant >= 0) {
230 return (fail_value < 0) ? MulValue(v1_min, v2_min, fail_value)
231 : MulValue(v1_max, v2_max, fail_value);
232 } else if (v2_max.a_constant == 0 && v2_max.b_constant <= 0) {
233 return (fail_value < 0) ? MulValue(v1_max, v2_min, fail_value)
234 : MulValue(v1_min, v2_max, fail_value);
235 }
236 } else if (v1_min.a_constant == 0 && v1_min.b_constant <= 0) {
237 // Negative range vs. positive or negative range.
238 if (v2_min.a_constant == 0 && v2_min.b_constant >= 0) {
239 return (fail_value < 0) ? MulValue(v1_min, v2_max, fail_value)
240 : MulValue(v1_max, v2_min, fail_value);
241 } else if (v2_max.a_constant == 0 && v2_max.b_constant <= 0) {
242 return (fail_value < 0) ? MulValue(v1_max, v2_max, fail_value)
243 : MulValue(v1_min, v2_min, fail_value);
244 }
245 }
246 return Value(fail_value);
247}
248
249InductionVarRange::Value InductionVarRange::GetDiv(HInductionVarAnalysis::InductionInfo* info1,
250 HInductionVarAnalysis::InductionInfo* info2,
251 HInductionVarAnalysis::InductionInfo* trip,
252 int32_t fail_value) {
253 Value v1_min = GetMin(info1, trip);
254 Value v1_max = GetMax(info1, trip);
255 Value v2_min = GetMin(info2, trip);
256 Value v2_max = GetMax(info2, trip);
257 if (v1_min.a_constant == 0 && v1_min.b_constant >= 0) {
258 // Positive range vs. positive or negative range.
259 if (v2_min.a_constant == 0 && v2_min.b_constant >= 0) {
260 return (fail_value < 0) ? DivValue(v1_min, v2_max, fail_value)
261 : DivValue(v1_max, v2_min, fail_value);
262 } else if (v2_max.a_constant == 0 && v2_max.b_constant <= 0) {
263 return (fail_value < 0) ? DivValue(v1_max, v2_max, fail_value)
264 : DivValue(v1_min, v2_min, fail_value);
265 }
266 } else if (v1_min.a_constant == 0 && v1_min.b_constant <= 0) {
267 // Negative range vs. positive or negative range.
268 if (v2_min.a_constant == 0 && v2_min.b_constant >= 0) {
269 return (fail_value < 0) ? DivValue(v1_min, v2_min, fail_value)
270 : DivValue(v1_max, v2_max, fail_value);
271 } else if (v2_max.a_constant == 0 && v2_max.b_constant <= 0) {
272 return (fail_value < 0) ? DivValue(v1_max, v2_min, fail_value)
273 : DivValue(v1_min, v2_max, fail_value);
274 }
275 }
276 return Value(fail_value);
277}
278
279InductionVarRange::Value InductionVarRange::AddValue(Value v1, Value v2, int32_t fail_value) {
280 if (IsSafeAdd(v1.b_constant, v2.b_constant)) {
281 const int32_t b = v1.b_constant + v2.b_constant;
282 if (v1.a_constant == 0) {
283 return Value(v2.instruction, v2.a_constant, b);
284 } else if (v2.a_constant == 0) {
285 return Value(v1.instruction, v1.a_constant, b);
286 } else if (v1.instruction == v2.instruction && IsSafeAdd(v1.a_constant, v2.a_constant)) {
287 return Value(v1.instruction, v1.a_constant + v2.a_constant, b);
288 }
289 }
290 return Value(fail_value);
291}
292
293InductionVarRange::Value InductionVarRange::SubValue(Value v1, Value v2, int32_t fail_value) {
294 if (IsSafeSub(v1.b_constant, v2.b_constant)) {
295 const int32_t b = v1.b_constant - v2.b_constant;
296 if (v1.a_constant == 0 && IsSafeSub(0, v2.a_constant)) {
297 return Value(v2.instruction, -v2.a_constant, b);
298 } else if (v2.a_constant == 0) {
299 return Value(v1.instruction, v1.a_constant, b);
300 } else if (v1.instruction == v2.instruction && IsSafeSub(v1.a_constant, v2.a_constant)) {
301 return Value(v1.instruction, v1.a_constant - v2.a_constant, b);
302 }
303 }
304 return Value(fail_value);
305}
306
307InductionVarRange::Value InductionVarRange::MulValue(Value v1, Value v2, int32_t fail_value) {
308 if (v1.a_constant == 0) {
309 if (IsSafeMul(v1.b_constant, v2.a_constant) && IsSafeMul(v1.b_constant, v2.b_constant)) {
310 return Value(v2.instruction, v1.b_constant * v2.a_constant, v1.b_constant * v2.b_constant);
311 }
312 } else if (v2.a_constant == 0) {
313 if (IsSafeMul(v1.a_constant, v2.b_constant) && IsSafeMul(v1.b_constant, v2.b_constant)) {
314 return Value(v1.instruction, v1.a_constant * v2.b_constant, v1.b_constant * v2.b_constant);
315 }
316 }
317 return Value(fail_value);
318}
319
320InductionVarRange::Value InductionVarRange::DivValue(Value v1, Value v2, int32_t fail_value) {
321 if (v1.a_constant == 0 && v2.a_constant == 0) {
322 if (IsSafeDiv(v1.b_constant, v2.b_constant)) {
323 return Value(v1.b_constant / v2.b_constant);
324 }
325 }
326 return Value(fail_value);
327}
328
329InductionVarRange::Value InductionVarRange::MinValue(Value v1, Value v2) {
330 if (v1.instruction == v2.instruction && v1.a_constant == v2.a_constant) {
331 return Value(v1.instruction, v1.a_constant, std::min(v1.b_constant, v2.b_constant));
332 }
333 return Value(INT_MIN);
334}
335
336InductionVarRange::Value InductionVarRange::MaxValue(Value v1, Value v2) {
337 if (v1.instruction == v2.instruction && v1.a_constant == v2.a_constant) {
338 return Value(v1.instruction, v1.a_constant, std::max(v1.b_constant, v2.b_constant));
339 }
340 return Value(INT_MAX);
341}
342
343} // namespace art