1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker * Copyright (C) 2015 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker *
4*795d594fSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker *
8*795d594fSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker *
10*795d594fSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker */
16*795d594fSAndroid Build Coastguard Worker
17*795d594fSAndroid Build Coastguard Worker #include "induction_var_range.h"
18*795d594fSAndroid Build Coastguard Worker
19*795d594fSAndroid Build Coastguard Worker #include <limits>
20*795d594fSAndroid Build Coastguard Worker #include "optimizing/nodes.h"
21*795d594fSAndroid Build Coastguard Worker
22*795d594fSAndroid Build Coastguard Worker namespace art HIDDEN {
23*795d594fSAndroid Build Coastguard Worker
24*795d594fSAndroid Build Coastguard Worker /** Returns true if 64-bit constant fits in 32-bit constant. */
CanLongValueFitIntoInt(int64_t c)25*795d594fSAndroid Build Coastguard Worker static bool CanLongValueFitIntoInt(int64_t c) {
26*795d594fSAndroid Build Coastguard Worker return std::numeric_limits<int32_t>::min() <= c && c <= std::numeric_limits<int32_t>::max();
27*795d594fSAndroid Build Coastguard Worker }
28*795d594fSAndroid Build Coastguard Worker
29*795d594fSAndroid Build Coastguard Worker /** Returns true if 32-bit addition can be done safely. */
IsSafeAdd(int32_t c1,int32_t c2)30*795d594fSAndroid Build Coastguard Worker static bool IsSafeAdd(int32_t c1, int32_t c2) {
31*795d594fSAndroid Build Coastguard Worker return CanLongValueFitIntoInt(static_cast<int64_t>(c1) + static_cast<int64_t>(c2));
32*795d594fSAndroid Build Coastguard Worker }
33*795d594fSAndroid Build Coastguard Worker
34*795d594fSAndroid Build Coastguard Worker /** Returns true if 32-bit subtraction can be done safely. */
IsSafeSub(int32_t c1,int32_t c2)35*795d594fSAndroid Build Coastguard Worker static bool IsSafeSub(int32_t c1, int32_t c2) {
36*795d594fSAndroid Build Coastguard Worker return CanLongValueFitIntoInt(static_cast<int64_t>(c1) - static_cast<int64_t>(c2));
37*795d594fSAndroid Build Coastguard Worker }
38*795d594fSAndroid Build Coastguard Worker
39*795d594fSAndroid Build Coastguard Worker /** Returns true if 32-bit multiplication can be done safely. */
IsSafeMul(int32_t c1,int32_t c2)40*795d594fSAndroid Build Coastguard Worker static bool IsSafeMul(int32_t c1, int32_t c2) {
41*795d594fSAndroid Build Coastguard Worker return CanLongValueFitIntoInt(static_cast<int64_t>(c1) * static_cast<int64_t>(c2));
42*795d594fSAndroid Build Coastguard Worker }
43*795d594fSAndroid Build Coastguard Worker
44*795d594fSAndroid Build Coastguard Worker /** Returns true if 32-bit division can be done safely. */
IsSafeDiv(int32_t c1,int32_t c2)45*795d594fSAndroid Build Coastguard Worker static bool IsSafeDiv(int32_t c1, int32_t c2) {
46*795d594fSAndroid Build Coastguard Worker return c2 != 0 && CanLongValueFitIntoInt(static_cast<int64_t>(c1) / static_cast<int64_t>(c2));
47*795d594fSAndroid Build Coastguard Worker }
48*795d594fSAndroid Build Coastguard Worker
49*795d594fSAndroid Build Coastguard Worker /** Computes a * b for a,b > 0 (at least until first overflow happens). */
SafeMul(int64_t a,int64_t b,bool * overflow)50*795d594fSAndroid Build Coastguard Worker static int64_t SafeMul(int64_t a, int64_t b, /*out*/ bool* overflow) {
51*795d594fSAndroid Build Coastguard Worker if (a > 0 && b > 0 && a > (std::numeric_limits<int64_t>::max() / b)) {
52*795d594fSAndroid Build Coastguard Worker *overflow = true;
53*795d594fSAndroid Build Coastguard Worker }
54*795d594fSAndroid Build Coastguard Worker return a * b;
55*795d594fSAndroid Build Coastguard Worker }
56*795d594fSAndroid Build Coastguard Worker
57*795d594fSAndroid Build Coastguard Worker /** Returns b^e for b,e > 0. Sets overflow if arithmetic wrap-around occurred. */
IntPow(int64_t b,int64_t e,bool * overflow)58*795d594fSAndroid Build Coastguard Worker static int64_t IntPow(int64_t b, int64_t e, /*out*/ bool* overflow) {
59*795d594fSAndroid Build Coastguard Worker DCHECK_LT(0, b);
60*795d594fSAndroid Build Coastguard Worker DCHECK_LT(0, e);
61*795d594fSAndroid Build Coastguard Worker int64_t pow = 1;
62*795d594fSAndroid Build Coastguard Worker while (e) {
63*795d594fSAndroid Build Coastguard Worker if (e & 1) {
64*795d594fSAndroid Build Coastguard Worker pow = SafeMul(pow, b, overflow);
65*795d594fSAndroid Build Coastguard Worker }
66*795d594fSAndroid Build Coastguard Worker e >>= 1;
67*795d594fSAndroid Build Coastguard Worker if (e) {
68*795d594fSAndroid Build Coastguard Worker b = SafeMul(b, b, overflow);
69*795d594fSAndroid Build Coastguard Worker }
70*795d594fSAndroid Build Coastguard Worker }
71*795d594fSAndroid Build Coastguard Worker return pow;
72*795d594fSAndroid Build Coastguard Worker }
73*795d594fSAndroid Build Coastguard Worker
74*795d594fSAndroid Build Coastguard Worker /** Hunts "under the hood" for a suitable instruction at the hint. */
IsMaxAtHint(HInstruction * instruction,HInstruction * hint,HInstruction ** suitable)75*795d594fSAndroid Build Coastguard Worker static bool IsMaxAtHint(
76*795d594fSAndroid Build Coastguard Worker HInstruction* instruction, HInstruction* hint, /*out*/HInstruction** suitable) {
77*795d594fSAndroid Build Coastguard Worker if (instruction->IsMin()) {
78*795d594fSAndroid Build Coastguard Worker // For MIN(x, y), return most suitable x or y as maximum.
79*795d594fSAndroid Build Coastguard Worker return IsMaxAtHint(instruction->InputAt(0), hint, suitable) ||
80*795d594fSAndroid Build Coastguard Worker IsMaxAtHint(instruction->InputAt(1), hint, suitable);
81*795d594fSAndroid Build Coastguard Worker } else {
82*795d594fSAndroid Build Coastguard Worker *suitable = instruction;
83*795d594fSAndroid Build Coastguard Worker return HuntForDeclaration(instruction) == hint;
84*795d594fSAndroid Build Coastguard Worker }
85*795d594fSAndroid Build Coastguard Worker }
86*795d594fSAndroid Build Coastguard Worker
87*795d594fSAndroid Build Coastguard Worker /** Post-analysis simplification of a minimum value that makes the bound more useful to clients. */
SimplifyMin(InductionVarRange::Value v)88*795d594fSAndroid Build Coastguard Worker static InductionVarRange::Value SimplifyMin(InductionVarRange::Value v) {
89*795d594fSAndroid Build Coastguard Worker if (v.is_known && v.a_constant == 1 && v.b_constant <= 0) {
90*795d594fSAndroid Build Coastguard Worker // If a == 1, instruction >= 0 and b <= 0, just return the constant b.
91*795d594fSAndroid Build Coastguard Worker // No arithmetic wrap-around can occur.
92*795d594fSAndroid Build Coastguard Worker if (IsGEZero(v.instruction)) {
93*795d594fSAndroid Build Coastguard Worker return InductionVarRange::Value(v.b_constant);
94*795d594fSAndroid Build Coastguard Worker }
95*795d594fSAndroid Build Coastguard Worker }
96*795d594fSAndroid Build Coastguard Worker return v;
97*795d594fSAndroid Build Coastguard Worker }
98*795d594fSAndroid Build Coastguard Worker
99*795d594fSAndroid Build Coastguard Worker /** Post-analysis simplification of a maximum value that makes the bound more useful to clients. */
SimplifyMax(InductionVarRange::Value v,HInstruction * hint)100*795d594fSAndroid Build Coastguard Worker static InductionVarRange::Value SimplifyMax(InductionVarRange::Value v, HInstruction* hint) {
101*795d594fSAndroid Build Coastguard Worker if (v.is_known && v.a_constant >= 1) {
102*795d594fSAndroid Build Coastguard Worker // An upper bound a * (length / a) + b, where a >= 1, can be conservatively rewritten as
103*795d594fSAndroid Build Coastguard Worker // length + b because length >= 0 is true.
104*795d594fSAndroid Build Coastguard Worker int64_t value;
105*795d594fSAndroid Build Coastguard Worker if (v.instruction->IsDiv() &&
106*795d594fSAndroid Build Coastguard Worker v.instruction->InputAt(0)->IsArrayLength() &&
107*795d594fSAndroid Build Coastguard Worker IsInt64AndGet(v.instruction->InputAt(1), &value) && v.a_constant == value) {
108*795d594fSAndroid Build Coastguard Worker return InductionVarRange::Value(v.instruction->InputAt(0), 1, v.b_constant);
109*795d594fSAndroid Build Coastguard Worker }
110*795d594fSAndroid Build Coastguard Worker // If a == 1, the most suitable one suffices as maximum value.
111*795d594fSAndroid Build Coastguard Worker HInstruction* suitable = nullptr;
112*795d594fSAndroid Build Coastguard Worker if (v.a_constant == 1 && IsMaxAtHint(v.instruction, hint, &suitable)) {
113*795d594fSAndroid Build Coastguard Worker return InductionVarRange::Value(suitable, 1, v.b_constant);
114*795d594fSAndroid Build Coastguard Worker }
115*795d594fSAndroid Build Coastguard Worker }
116*795d594fSAndroid Build Coastguard Worker return v;
117*795d594fSAndroid Build Coastguard Worker }
118*795d594fSAndroid Build Coastguard Worker
119*795d594fSAndroid Build Coastguard Worker /** Tests for a constant value. */
IsConstantValue(InductionVarRange::Value v)120*795d594fSAndroid Build Coastguard Worker static bool IsConstantValue(InductionVarRange::Value v) {
121*795d594fSAndroid Build Coastguard Worker return v.is_known && v.a_constant == 0;
122*795d594fSAndroid Build Coastguard Worker }
123*795d594fSAndroid Build Coastguard Worker
124*795d594fSAndroid Build Coastguard Worker /** Corrects a value for type to account for arithmetic wrap-around in lower precision. */
CorrectForType(InductionVarRange::Value v,DataType::Type type)125*795d594fSAndroid Build Coastguard Worker static InductionVarRange::Value CorrectForType(InductionVarRange::Value v, DataType::Type type) {
126*795d594fSAndroid Build Coastguard Worker switch (type) {
127*795d594fSAndroid Build Coastguard Worker case DataType::Type::kUint8:
128*795d594fSAndroid Build Coastguard Worker case DataType::Type::kInt8:
129*795d594fSAndroid Build Coastguard Worker case DataType::Type::kUint16:
130*795d594fSAndroid Build Coastguard Worker case DataType::Type::kInt16: {
131*795d594fSAndroid Build Coastguard Worker // Constants within range only.
132*795d594fSAndroid Build Coastguard Worker // TODO: maybe some room for improvement, like allowing widening conversions
133*795d594fSAndroid Build Coastguard Worker int32_t min = DataType::MinValueOfIntegralType(type);
134*795d594fSAndroid Build Coastguard Worker int32_t max = DataType::MaxValueOfIntegralType(type);
135*795d594fSAndroid Build Coastguard Worker return (IsConstantValue(v) && min <= v.b_constant && v.b_constant <= max)
136*795d594fSAndroid Build Coastguard Worker ? v
137*795d594fSAndroid Build Coastguard Worker : InductionVarRange::Value();
138*795d594fSAndroid Build Coastguard Worker }
139*795d594fSAndroid Build Coastguard Worker default:
140*795d594fSAndroid Build Coastguard Worker return v;
141*795d594fSAndroid Build Coastguard Worker }
142*795d594fSAndroid Build Coastguard Worker }
143*795d594fSAndroid Build Coastguard Worker
144*795d594fSAndroid Build Coastguard Worker /** Inserts an instruction. */
Insert(HBasicBlock * block,HInstruction * instruction)145*795d594fSAndroid Build Coastguard Worker static HInstruction* Insert(HBasicBlock* block, HInstruction* instruction) {
146*795d594fSAndroid Build Coastguard Worker DCHECK(block != nullptr);
147*795d594fSAndroid Build Coastguard Worker DCHECK(block->GetLastInstruction() != nullptr) << block->GetBlockId();
148*795d594fSAndroid Build Coastguard Worker DCHECK(instruction != nullptr);
149*795d594fSAndroid Build Coastguard Worker block->InsertInstructionBefore(instruction, block->GetLastInstruction());
150*795d594fSAndroid Build Coastguard Worker return instruction;
151*795d594fSAndroid Build Coastguard Worker }
152*795d594fSAndroid Build Coastguard Worker
153*795d594fSAndroid Build Coastguard Worker /** Obtains loop's control instruction. */
GetLoopControl(const HLoopInformation * loop)154*795d594fSAndroid Build Coastguard Worker static HInstruction* GetLoopControl(const HLoopInformation* loop) {
155*795d594fSAndroid Build Coastguard Worker DCHECK(loop != nullptr);
156*795d594fSAndroid Build Coastguard Worker return loop->GetHeader()->GetLastInstruction();
157*795d594fSAndroid Build Coastguard Worker }
158*795d594fSAndroid Build Coastguard Worker
159*795d594fSAndroid Build Coastguard Worker /** Determines whether the `context` is in the body of the `loop`. */
IsContextInBody(const HBasicBlock * context,const HLoopInformation * loop)160*795d594fSAndroid Build Coastguard Worker static bool IsContextInBody(const HBasicBlock* context, const HLoopInformation* loop) {
161*795d594fSAndroid Build Coastguard Worker DCHECK(loop != nullptr);
162*795d594fSAndroid Build Coastguard Worker // We're currently classifying trip count only for the exit condition from loop header.
163*795d594fSAndroid Build Coastguard Worker // All other blocks in the loop are considered loop body.
164*795d594fSAndroid Build Coastguard Worker return context != loop->GetHeader() && loop->Contains(*context);
165*795d594fSAndroid Build Coastguard Worker }
166*795d594fSAndroid Build Coastguard Worker
167*795d594fSAndroid Build Coastguard Worker /** Determines whether to use the full trip count for given `context`, `loop` and `is_min`. */
UseFullTripCount(const HBasicBlock * context,const HLoopInformation * loop,bool is_min)168*795d594fSAndroid Build Coastguard Worker bool UseFullTripCount(const HBasicBlock* context, const HLoopInformation* loop, bool is_min) {
169*795d594fSAndroid Build Coastguard Worker // We're currently classifying trip count only for the exit condition from loop header.
170*795d594fSAndroid Build Coastguard Worker // So, we should call this helper function only if the loop control is an `HIf` with
171*795d594fSAndroid Build Coastguard Worker // one edge leaving the loop. The loop header is the only block that's both inside
172*795d594fSAndroid Build Coastguard Worker // the loop and not in the loop body.
173*795d594fSAndroid Build Coastguard Worker DCHECK(GetLoopControl(loop)->IsIf());
174*795d594fSAndroid Build Coastguard Worker DCHECK_NE(loop->Contains(*GetLoopControl(loop)->AsIf()->IfTrueSuccessor()),
175*795d594fSAndroid Build Coastguard Worker loop->Contains(*GetLoopControl(loop)->AsIf()->IfFalseSuccessor()));
176*795d594fSAndroid Build Coastguard Worker if (loop->Contains(*context)) {
177*795d594fSAndroid Build Coastguard Worker // Use the full trip count if determining the maximum and context is not in the loop body.
178*795d594fSAndroid Build Coastguard Worker DCHECK_NE(context == loop->GetHeader(), IsContextInBody(context, loop));
179*795d594fSAndroid Build Coastguard Worker return !is_min && context == loop->GetHeader();
180*795d594fSAndroid Build Coastguard Worker } else {
181*795d594fSAndroid Build Coastguard Worker // Trip count after the loop is always the maximum (ignoring `is_min`),
182*795d594fSAndroid Build Coastguard Worker // as long as the `context` is dominated by the loop control exit block.
183*795d594fSAndroid Build Coastguard Worker // If there are additional exit edges, the value is unknown on those paths.
184*795d594fSAndroid Build Coastguard Worker HInstruction* loop_control = GetLoopControl(loop);
185*795d594fSAndroid Build Coastguard Worker HBasicBlock* then_block = loop_control->AsIf()->IfTrueSuccessor();
186*795d594fSAndroid Build Coastguard Worker HBasicBlock* else_block = loop_control->AsIf()->IfFalseSuccessor();
187*795d594fSAndroid Build Coastguard Worker HBasicBlock* loop_exit_block = loop->Contains(*then_block) ? else_block : then_block;
188*795d594fSAndroid Build Coastguard Worker return loop_exit_block->Dominates(context);
189*795d594fSAndroid Build Coastguard Worker }
190*795d594fSAndroid Build Coastguard Worker }
191*795d594fSAndroid Build Coastguard Worker
192*795d594fSAndroid Build Coastguard Worker //
193*795d594fSAndroid Build Coastguard Worker // Public class methods.
194*795d594fSAndroid Build Coastguard Worker //
195*795d594fSAndroid Build Coastguard Worker
InductionVarRange(HInductionVarAnalysis * induction_analysis)196*795d594fSAndroid Build Coastguard Worker InductionVarRange::InductionVarRange(HInductionVarAnalysis* induction_analysis)
197*795d594fSAndroid Build Coastguard Worker : induction_analysis_(induction_analysis),
198*795d594fSAndroid Build Coastguard Worker chase_hint_(nullptr) {
199*795d594fSAndroid Build Coastguard Worker DCHECK(induction_analysis != nullptr);
200*795d594fSAndroid Build Coastguard Worker }
201*795d594fSAndroid Build Coastguard Worker
GetInductionRange(const HBasicBlock * context,HInstruction * instruction,HInstruction * chase_hint,Value * min_val,Value * max_val,bool * needs_finite_test)202*795d594fSAndroid Build Coastguard Worker bool InductionVarRange::GetInductionRange(const HBasicBlock* context,
203*795d594fSAndroid Build Coastguard Worker HInstruction* instruction,
204*795d594fSAndroid Build Coastguard Worker HInstruction* chase_hint,
205*795d594fSAndroid Build Coastguard Worker /*out*/Value* min_val,
206*795d594fSAndroid Build Coastguard Worker /*out*/Value* max_val,
207*795d594fSAndroid Build Coastguard Worker /*out*/bool* needs_finite_test) {
208*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop = nullptr;
209*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* info = nullptr;
210*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* trip = nullptr;
211*795d594fSAndroid Build Coastguard Worker if (!HasInductionInfo(context, instruction, &loop, &info, &trip)) {
212*795d594fSAndroid Build Coastguard Worker return false;
213*795d594fSAndroid Build Coastguard Worker }
214*795d594fSAndroid Build Coastguard Worker // Type int or lower (this is not too restrictive since intended clients, like
215*795d594fSAndroid Build Coastguard Worker // bounds check elimination, will have truncated higher precision induction
216*795d594fSAndroid Build Coastguard Worker // at their use point already).
217*795d594fSAndroid Build Coastguard Worker switch (info->type) {
218*795d594fSAndroid Build Coastguard Worker case DataType::Type::kUint8:
219*795d594fSAndroid Build Coastguard Worker case DataType::Type::kInt8:
220*795d594fSAndroid Build Coastguard Worker case DataType::Type::kUint16:
221*795d594fSAndroid Build Coastguard Worker case DataType::Type::kInt16:
222*795d594fSAndroid Build Coastguard Worker case DataType::Type::kInt32:
223*795d594fSAndroid Build Coastguard Worker break;
224*795d594fSAndroid Build Coastguard Worker default:
225*795d594fSAndroid Build Coastguard Worker return false;
226*795d594fSAndroid Build Coastguard Worker }
227*795d594fSAndroid Build Coastguard Worker // Find range.
228*795d594fSAndroid Build Coastguard Worker chase_hint_ = chase_hint;
229*795d594fSAndroid Build Coastguard Worker int64_t stride_value = 0;
230*795d594fSAndroid Build Coastguard Worker *min_val = SimplifyMin(GetVal(context, loop, info, trip, /*is_min=*/ true));
231*795d594fSAndroid Build Coastguard Worker *max_val = SimplifyMax(GetVal(context, loop, info, trip, /*is_min=*/ false), chase_hint);
232*795d594fSAndroid Build Coastguard Worker *needs_finite_test =
233*795d594fSAndroid Build Coastguard Worker NeedsTripCount(context, loop, info, &stride_value) && IsUnsafeTripCount(trip);
234*795d594fSAndroid Build Coastguard Worker chase_hint_ = nullptr;
235*795d594fSAndroid Build Coastguard Worker // Retry chasing constants for wrap-around (merge sensitive).
236*795d594fSAndroid Build Coastguard Worker if (!min_val->is_known && info->induction_class == HInductionVarAnalysis::kWrapAround) {
237*795d594fSAndroid Build Coastguard Worker *min_val = SimplifyMin(GetVal(context, loop, info, trip, /*is_min=*/ true));
238*795d594fSAndroid Build Coastguard Worker }
239*795d594fSAndroid Build Coastguard Worker return true;
240*795d594fSAndroid Build Coastguard Worker }
241*795d594fSAndroid Build Coastguard Worker
CanGenerateRange(const HBasicBlock * context,HInstruction * instruction,bool * needs_finite_test,bool * needs_taken_test)242*795d594fSAndroid Build Coastguard Worker bool InductionVarRange::CanGenerateRange(const HBasicBlock* context,
243*795d594fSAndroid Build Coastguard Worker HInstruction* instruction,
244*795d594fSAndroid Build Coastguard Worker /*out*/bool* needs_finite_test,
245*795d594fSAndroid Build Coastguard Worker /*out*/bool* needs_taken_test) {
246*795d594fSAndroid Build Coastguard Worker bool is_last_value = false;
247*795d594fSAndroid Build Coastguard Worker int64_t stride_value = 0;
248*795d594fSAndroid Build Coastguard Worker return GenerateRangeOrLastValue(context,
249*795d594fSAndroid Build Coastguard Worker instruction,
250*795d594fSAndroid Build Coastguard Worker is_last_value,
251*795d594fSAndroid Build Coastguard Worker nullptr,
252*795d594fSAndroid Build Coastguard Worker nullptr,
253*795d594fSAndroid Build Coastguard Worker nullptr,
254*795d594fSAndroid Build Coastguard Worker nullptr,
255*795d594fSAndroid Build Coastguard Worker nullptr, // nothing generated yet
256*795d594fSAndroid Build Coastguard Worker &stride_value,
257*795d594fSAndroid Build Coastguard Worker needs_finite_test,
258*795d594fSAndroid Build Coastguard Worker needs_taken_test) &&
259*795d594fSAndroid Build Coastguard Worker (stride_value == -1 ||
260*795d594fSAndroid Build Coastguard Worker stride_value == 0 ||
261*795d594fSAndroid Build Coastguard Worker stride_value == 1); // avoid arithmetic wrap-around anomalies.
262*795d594fSAndroid Build Coastguard Worker }
263*795d594fSAndroid Build Coastguard Worker
GenerateRange(const HBasicBlock * context,HInstruction * instruction,HGraph * graph,HBasicBlock * block,HInstruction ** lower,HInstruction ** upper)264*795d594fSAndroid Build Coastguard Worker void InductionVarRange::GenerateRange(const HBasicBlock* context,
265*795d594fSAndroid Build Coastguard Worker HInstruction* instruction,
266*795d594fSAndroid Build Coastguard Worker HGraph* graph,
267*795d594fSAndroid Build Coastguard Worker HBasicBlock* block,
268*795d594fSAndroid Build Coastguard Worker /*out*/HInstruction** lower,
269*795d594fSAndroid Build Coastguard Worker /*out*/HInstruction** upper) {
270*795d594fSAndroid Build Coastguard Worker bool is_last_value = false;
271*795d594fSAndroid Build Coastguard Worker int64_t stride_value = 0;
272*795d594fSAndroid Build Coastguard Worker bool b1, b2; // unused
273*795d594fSAndroid Build Coastguard Worker if (!GenerateRangeOrLastValue(context,
274*795d594fSAndroid Build Coastguard Worker instruction,
275*795d594fSAndroid Build Coastguard Worker is_last_value,
276*795d594fSAndroid Build Coastguard Worker graph,
277*795d594fSAndroid Build Coastguard Worker block,
278*795d594fSAndroid Build Coastguard Worker lower,
279*795d594fSAndroid Build Coastguard Worker upper,
280*795d594fSAndroid Build Coastguard Worker nullptr,
281*795d594fSAndroid Build Coastguard Worker &stride_value,
282*795d594fSAndroid Build Coastguard Worker &b1,
283*795d594fSAndroid Build Coastguard Worker &b2) ||
284*795d594fSAndroid Build Coastguard Worker (stride_value != -1 &&
285*795d594fSAndroid Build Coastguard Worker stride_value != 0 &&
286*795d594fSAndroid Build Coastguard Worker stride_value != 1)) {
287*795d594fSAndroid Build Coastguard Worker LOG(FATAL) << "Failed precondition: CanGenerateRange()";
288*795d594fSAndroid Build Coastguard Worker }
289*795d594fSAndroid Build Coastguard Worker }
290*795d594fSAndroid Build Coastguard Worker
GenerateTakenTest(HInstruction * loop_control,HGraph * graph,HBasicBlock * block)291*795d594fSAndroid Build Coastguard Worker HInstruction* InductionVarRange::GenerateTakenTest(HInstruction* loop_control,
292*795d594fSAndroid Build Coastguard Worker HGraph* graph,
293*795d594fSAndroid Build Coastguard Worker HBasicBlock* block) {
294*795d594fSAndroid Build Coastguard Worker const HBasicBlock* context = loop_control->GetBlock();
295*795d594fSAndroid Build Coastguard Worker HInstruction* taken_test = nullptr;
296*795d594fSAndroid Build Coastguard Worker bool is_last_value = false;
297*795d594fSAndroid Build Coastguard Worker int64_t stride_value = 0;
298*795d594fSAndroid Build Coastguard Worker bool b1, b2; // unused
299*795d594fSAndroid Build Coastguard Worker if (!GenerateRangeOrLastValue(context,
300*795d594fSAndroid Build Coastguard Worker loop_control,
301*795d594fSAndroid Build Coastguard Worker is_last_value,
302*795d594fSAndroid Build Coastguard Worker graph,
303*795d594fSAndroid Build Coastguard Worker block,
304*795d594fSAndroid Build Coastguard Worker nullptr,
305*795d594fSAndroid Build Coastguard Worker nullptr,
306*795d594fSAndroid Build Coastguard Worker &taken_test,
307*795d594fSAndroid Build Coastguard Worker &stride_value,
308*795d594fSAndroid Build Coastguard Worker &b1,
309*795d594fSAndroid Build Coastguard Worker &b2) ||
310*795d594fSAndroid Build Coastguard Worker (stride_value != -1 &&
311*795d594fSAndroid Build Coastguard Worker stride_value != 0 &&
312*795d594fSAndroid Build Coastguard Worker stride_value != 1)) {
313*795d594fSAndroid Build Coastguard Worker LOG(FATAL) << "Failed precondition: CanGenerateRange()";
314*795d594fSAndroid Build Coastguard Worker }
315*795d594fSAndroid Build Coastguard Worker return taken_test;
316*795d594fSAndroid Build Coastguard Worker }
317*795d594fSAndroid Build Coastguard Worker
CanGenerateLastValue(HInstruction * instruction)318*795d594fSAndroid Build Coastguard Worker bool InductionVarRange::CanGenerateLastValue(HInstruction* instruction) {
319*795d594fSAndroid Build Coastguard Worker const HBasicBlock* context = instruction->GetBlock();
320*795d594fSAndroid Build Coastguard Worker bool is_last_value = true;
321*795d594fSAndroid Build Coastguard Worker int64_t stride_value = 0;
322*795d594fSAndroid Build Coastguard Worker bool needs_finite_test = false;
323*795d594fSAndroid Build Coastguard Worker bool needs_taken_test = false;
324*795d594fSAndroid Build Coastguard Worker return GenerateRangeOrLastValue(context,
325*795d594fSAndroid Build Coastguard Worker instruction,
326*795d594fSAndroid Build Coastguard Worker is_last_value,
327*795d594fSAndroid Build Coastguard Worker nullptr,
328*795d594fSAndroid Build Coastguard Worker nullptr,
329*795d594fSAndroid Build Coastguard Worker nullptr,
330*795d594fSAndroid Build Coastguard Worker nullptr,
331*795d594fSAndroid Build Coastguard Worker nullptr, // nothing generated yet
332*795d594fSAndroid Build Coastguard Worker &stride_value,
333*795d594fSAndroid Build Coastguard Worker &needs_finite_test,
334*795d594fSAndroid Build Coastguard Worker &needs_taken_test)
335*795d594fSAndroid Build Coastguard Worker && !needs_finite_test && !needs_taken_test;
336*795d594fSAndroid Build Coastguard Worker }
337*795d594fSAndroid Build Coastguard Worker
GenerateLastValue(HInstruction * instruction,HGraph * graph,HBasicBlock * block)338*795d594fSAndroid Build Coastguard Worker HInstruction* InductionVarRange::GenerateLastValue(HInstruction* instruction,
339*795d594fSAndroid Build Coastguard Worker HGraph* graph,
340*795d594fSAndroid Build Coastguard Worker HBasicBlock* block) {
341*795d594fSAndroid Build Coastguard Worker const HBasicBlock* context = instruction->GetBlock();
342*795d594fSAndroid Build Coastguard Worker HInstruction* last_value = nullptr;
343*795d594fSAndroid Build Coastguard Worker bool is_last_value = true;
344*795d594fSAndroid Build Coastguard Worker int64_t stride_value = 0;
345*795d594fSAndroid Build Coastguard Worker bool needs_finite_test = false;
346*795d594fSAndroid Build Coastguard Worker bool needs_taken_test = false;
347*795d594fSAndroid Build Coastguard Worker if (!GenerateRangeOrLastValue(context,
348*795d594fSAndroid Build Coastguard Worker instruction,
349*795d594fSAndroid Build Coastguard Worker is_last_value,
350*795d594fSAndroid Build Coastguard Worker graph,
351*795d594fSAndroid Build Coastguard Worker block,
352*795d594fSAndroid Build Coastguard Worker &last_value,
353*795d594fSAndroid Build Coastguard Worker &last_value,
354*795d594fSAndroid Build Coastguard Worker nullptr,
355*795d594fSAndroid Build Coastguard Worker &stride_value,
356*795d594fSAndroid Build Coastguard Worker &needs_finite_test,
357*795d594fSAndroid Build Coastguard Worker &needs_taken_test) ||
358*795d594fSAndroid Build Coastguard Worker needs_finite_test ||
359*795d594fSAndroid Build Coastguard Worker needs_taken_test) {
360*795d594fSAndroid Build Coastguard Worker LOG(FATAL) << "Failed precondition: CanGenerateLastValue()";
361*795d594fSAndroid Build Coastguard Worker }
362*795d594fSAndroid Build Coastguard Worker return last_value;
363*795d594fSAndroid Build Coastguard Worker }
364*795d594fSAndroid Build Coastguard Worker
Replace(HInstruction * instruction,HInstruction * fetch,HInstruction * replacement)365*795d594fSAndroid Build Coastguard Worker void InductionVarRange::Replace(HInstruction* instruction,
366*795d594fSAndroid Build Coastguard Worker HInstruction* fetch,
367*795d594fSAndroid Build Coastguard Worker HInstruction* replacement) {
368*795d594fSAndroid Build Coastguard Worker for (HLoopInformation* lp = instruction->GetBlock()->GetLoopInformation(); // closest enveloping loop
369*795d594fSAndroid Build Coastguard Worker lp != nullptr;
370*795d594fSAndroid Build Coastguard Worker lp = lp->GetPreHeader()->GetLoopInformation()) {
371*795d594fSAndroid Build Coastguard Worker // Update loop's InductionInfo about fetch.
372*795d594fSAndroid Build Coastguard Worker ReplaceInduction(induction_analysis_->LookupInfo(lp, fetch), fetch, replacement);
373*795d594fSAndroid Build Coastguard Worker // Update loop's trip-count information.
374*795d594fSAndroid Build Coastguard Worker ReplaceInduction(induction_analysis_->LookupInfo(lp, GetLoopControl(lp)), fetch, replacement);
375*795d594fSAndroid Build Coastguard Worker }
376*795d594fSAndroid Build Coastguard Worker }
377*795d594fSAndroid Build Coastguard Worker
IsFinite(const HLoopInformation * loop,int64_t * trip_count) const378*795d594fSAndroid Build Coastguard Worker bool InductionVarRange::IsFinite(const HLoopInformation* loop, /*out*/ int64_t* trip_count) const {
379*795d594fSAndroid Build Coastguard Worker bool is_constant_unused = false;
380*795d594fSAndroid Build Coastguard Worker return CheckForFiniteAndConstantProps(loop, &is_constant_unused, trip_count);
381*795d594fSAndroid Build Coastguard Worker }
382*795d594fSAndroid Build Coastguard Worker
HasKnownTripCount(const HLoopInformation * loop,int64_t * trip_count) const383*795d594fSAndroid Build Coastguard Worker bool InductionVarRange::HasKnownTripCount(const HLoopInformation* loop,
384*795d594fSAndroid Build Coastguard Worker /*out*/ int64_t* trip_count) const {
385*795d594fSAndroid Build Coastguard Worker bool is_constant = false;
386*795d594fSAndroid Build Coastguard Worker CheckForFiniteAndConstantProps(loop, &is_constant, trip_count);
387*795d594fSAndroid Build Coastguard Worker // Set negative trip counts as 0, since it means that no trips would happen. Note that if the
388*795d594fSAndroid Build Coastguard Worker // `is_constant` value is false, `trip_count` would be disregareded.
389*795d594fSAndroid Build Coastguard Worker if (*trip_count < 0) {
390*795d594fSAndroid Build Coastguard Worker *trip_count = 0;
391*795d594fSAndroid Build Coastguard Worker }
392*795d594fSAndroid Build Coastguard Worker return is_constant;
393*795d594fSAndroid Build Coastguard Worker }
394*795d594fSAndroid Build Coastguard Worker
IsUnitStride(const HBasicBlock * context,HInstruction * instruction,HGraph * graph,HInstruction ** offset) const395*795d594fSAndroid Build Coastguard Worker bool InductionVarRange::IsUnitStride(const HBasicBlock* context,
396*795d594fSAndroid Build Coastguard Worker HInstruction* instruction,
397*795d594fSAndroid Build Coastguard Worker HGraph* graph,
398*795d594fSAndroid Build Coastguard Worker /*out*/ HInstruction** offset) const {
399*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop = nullptr;
400*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* info = nullptr;
401*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* trip = nullptr;
402*795d594fSAndroid Build Coastguard Worker if (HasInductionInfo(context, instruction, &loop, &info, &trip)) {
403*795d594fSAndroid Build Coastguard Worker if (info->induction_class == HInductionVarAnalysis::kLinear &&
404*795d594fSAndroid Build Coastguard Worker !HInductionVarAnalysis::IsNarrowingLinear(info)) {
405*795d594fSAndroid Build Coastguard Worker int64_t stride_value = 0;
406*795d594fSAndroid Build Coastguard Worker if (IsConstant(context, loop, info->op_a, kExact, &stride_value) && stride_value == 1) {
407*795d594fSAndroid Build Coastguard Worker int64_t off_value = 0;
408*795d594fSAndroid Build Coastguard Worker if (IsConstant(context, loop, info->op_b, kExact, &off_value)) {
409*795d594fSAndroid Build Coastguard Worker *offset = graph->GetConstant(info->op_b->type, off_value);
410*795d594fSAndroid Build Coastguard Worker } else if (info->op_b->operation == HInductionVarAnalysis::kFetch) {
411*795d594fSAndroid Build Coastguard Worker *offset = info->op_b->fetch;
412*795d594fSAndroid Build Coastguard Worker } else {
413*795d594fSAndroid Build Coastguard Worker return false;
414*795d594fSAndroid Build Coastguard Worker }
415*795d594fSAndroid Build Coastguard Worker return true;
416*795d594fSAndroid Build Coastguard Worker }
417*795d594fSAndroid Build Coastguard Worker }
418*795d594fSAndroid Build Coastguard Worker }
419*795d594fSAndroid Build Coastguard Worker return false;
420*795d594fSAndroid Build Coastguard Worker }
421*795d594fSAndroid Build Coastguard Worker
GenerateTripCount(const HLoopInformation * loop,HGraph * graph,HBasicBlock * block)422*795d594fSAndroid Build Coastguard Worker HInstruction* InductionVarRange::GenerateTripCount(const HLoopInformation* loop,
423*795d594fSAndroid Build Coastguard Worker HGraph* graph,
424*795d594fSAndroid Build Coastguard Worker HBasicBlock* block) {
425*795d594fSAndroid Build Coastguard Worker HInstruction* loop_control = GetLoopControl(loop);
426*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo *trip = induction_analysis_->LookupInfo(loop, loop_control);
427*795d594fSAndroid Build Coastguard Worker if (trip != nullptr && !IsUnsafeTripCount(trip)) {
428*795d594fSAndroid Build Coastguard Worker const HBasicBlock* context = loop_control->GetBlock();
429*795d594fSAndroid Build Coastguard Worker HInstruction* taken_test = nullptr;
430*795d594fSAndroid Build Coastguard Worker HInstruction* trip_expr = nullptr;
431*795d594fSAndroid Build Coastguard Worker if (IsBodyTripCount(trip)) {
432*795d594fSAndroid Build Coastguard Worker if (!GenerateCode(context,
433*795d594fSAndroid Build Coastguard Worker loop,
434*795d594fSAndroid Build Coastguard Worker trip->op_b,
435*795d594fSAndroid Build Coastguard Worker /*trip=*/ nullptr,
436*795d594fSAndroid Build Coastguard Worker graph,
437*795d594fSAndroid Build Coastguard Worker block,
438*795d594fSAndroid Build Coastguard Worker /*is_min=*/ false,
439*795d594fSAndroid Build Coastguard Worker &taken_test)) {
440*795d594fSAndroid Build Coastguard Worker return nullptr;
441*795d594fSAndroid Build Coastguard Worker }
442*795d594fSAndroid Build Coastguard Worker }
443*795d594fSAndroid Build Coastguard Worker if (GenerateCode(context,
444*795d594fSAndroid Build Coastguard Worker loop,
445*795d594fSAndroid Build Coastguard Worker trip->op_a,
446*795d594fSAndroid Build Coastguard Worker /*trip=*/ nullptr,
447*795d594fSAndroid Build Coastguard Worker graph,
448*795d594fSAndroid Build Coastguard Worker block,
449*795d594fSAndroid Build Coastguard Worker /*is_min=*/ false,
450*795d594fSAndroid Build Coastguard Worker &trip_expr)) {
451*795d594fSAndroid Build Coastguard Worker if (taken_test != nullptr) {
452*795d594fSAndroid Build Coastguard Worker HInstruction* zero = graph->GetConstant(trip->type, 0);
453*795d594fSAndroid Build Coastguard Worker ArenaAllocator* allocator = graph->GetAllocator();
454*795d594fSAndroid Build Coastguard Worker trip_expr = Insert(block, new (allocator) HSelect(taken_test, trip_expr, zero, kNoDexPc));
455*795d594fSAndroid Build Coastguard Worker }
456*795d594fSAndroid Build Coastguard Worker return trip_expr;
457*795d594fSAndroid Build Coastguard Worker }
458*795d594fSAndroid Build Coastguard Worker }
459*795d594fSAndroid Build Coastguard Worker return nullptr;
460*795d594fSAndroid Build Coastguard Worker }
461*795d594fSAndroid Build Coastguard Worker
462*795d594fSAndroid Build Coastguard Worker //
463*795d594fSAndroid Build Coastguard Worker // Private class methods.
464*795d594fSAndroid Build Coastguard Worker //
465*795d594fSAndroid Build Coastguard Worker
CheckForFiniteAndConstantProps(const HLoopInformation * loop,bool * is_constant,int64_t * trip_count) const466*795d594fSAndroid Build Coastguard Worker bool InductionVarRange::CheckForFiniteAndConstantProps(const HLoopInformation* loop,
467*795d594fSAndroid Build Coastguard Worker /*out*/ bool* is_constant,
468*795d594fSAndroid Build Coastguard Worker /*out*/ int64_t* trip_count) const {
469*795d594fSAndroid Build Coastguard Worker HInstruction* loop_control = GetLoopControl(loop);
470*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo *trip = induction_analysis_->LookupInfo(loop, loop_control);
471*795d594fSAndroid Build Coastguard Worker if (trip != nullptr && !IsUnsafeTripCount(trip)) {
472*795d594fSAndroid Build Coastguard Worker const HBasicBlock* context = loop_control->GetBlock();
473*795d594fSAndroid Build Coastguard Worker *is_constant = IsConstant(context, loop, trip->op_a, kExact, trip_count);
474*795d594fSAndroid Build Coastguard Worker return true;
475*795d594fSAndroid Build Coastguard Worker }
476*795d594fSAndroid Build Coastguard Worker return false;
477*795d594fSAndroid Build Coastguard Worker }
478*795d594fSAndroid Build Coastguard Worker
IsConstant(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,ConstantRequest request,int64_t * value) const479*795d594fSAndroid Build Coastguard Worker bool InductionVarRange::IsConstant(const HBasicBlock* context,
480*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
481*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* info,
482*795d594fSAndroid Build Coastguard Worker ConstantRequest request,
483*795d594fSAndroid Build Coastguard Worker /*out*/ int64_t* value) const {
484*795d594fSAndroid Build Coastguard Worker if (info != nullptr) {
485*795d594fSAndroid Build Coastguard Worker // A direct 32-bit or 64-bit constant fetch. This immediately satisfies
486*795d594fSAndroid Build Coastguard Worker // any of the three requests (kExact, kAtMost, and KAtLeast).
487*795d594fSAndroid Build Coastguard Worker if (info->induction_class == HInductionVarAnalysis::kInvariant &&
488*795d594fSAndroid Build Coastguard Worker info->operation == HInductionVarAnalysis::kFetch) {
489*795d594fSAndroid Build Coastguard Worker if (IsInt64AndGet(info->fetch, value)) {
490*795d594fSAndroid Build Coastguard Worker return true;
491*795d594fSAndroid Build Coastguard Worker }
492*795d594fSAndroid Build Coastguard Worker }
493*795d594fSAndroid Build Coastguard Worker // Try range analysis on the invariant, only accept a proper range
494*795d594fSAndroid Build Coastguard Worker // to avoid arithmetic wrap-around anomalies.
495*795d594fSAndroid Build Coastguard Worker Value min_val = GetVal(context, loop, info, /*trip=*/ nullptr, /*is_min=*/ true);
496*795d594fSAndroid Build Coastguard Worker Value max_val = GetVal(context, loop, info, /*trip=*/ nullptr, /*is_min=*/ false);
497*795d594fSAndroid Build Coastguard Worker if (IsConstantValue(min_val) &&
498*795d594fSAndroid Build Coastguard Worker IsConstantValue(max_val) && min_val.b_constant <= max_val.b_constant) {
499*795d594fSAndroid Build Coastguard Worker if ((request == kExact && min_val.b_constant == max_val.b_constant) || request == kAtMost) {
500*795d594fSAndroid Build Coastguard Worker *value = max_val.b_constant;
501*795d594fSAndroid Build Coastguard Worker return true;
502*795d594fSAndroid Build Coastguard Worker } else if (request == kAtLeast) {
503*795d594fSAndroid Build Coastguard Worker *value = min_val.b_constant;
504*795d594fSAndroid Build Coastguard Worker return true;
505*795d594fSAndroid Build Coastguard Worker }
506*795d594fSAndroid Build Coastguard Worker }
507*795d594fSAndroid Build Coastguard Worker }
508*795d594fSAndroid Build Coastguard Worker return false;
509*795d594fSAndroid Build Coastguard Worker }
510*795d594fSAndroid Build Coastguard Worker
HasInductionInfo(const HBasicBlock * context,HInstruction * instruction,const HLoopInformation ** loop,HInductionVarAnalysis::InductionInfo ** info,HInductionVarAnalysis::InductionInfo ** trip) const511*795d594fSAndroid Build Coastguard Worker bool InductionVarRange::HasInductionInfo(
512*795d594fSAndroid Build Coastguard Worker const HBasicBlock* context,
513*795d594fSAndroid Build Coastguard Worker HInstruction* instruction,
514*795d594fSAndroid Build Coastguard Worker /*out*/ const HLoopInformation** loop,
515*795d594fSAndroid Build Coastguard Worker /*out*/ HInductionVarAnalysis::InductionInfo** info,
516*795d594fSAndroid Build Coastguard Worker /*out*/ HInductionVarAnalysis::InductionInfo** trip) const {
517*795d594fSAndroid Build Coastguard Worker DCHECK(context != nullptr);
518*795d594fSAndroid Build Coastguard Worker HLoopInformation* lp = context->GetLoopInformation(); // closest enveloping loop
519*795d594fSAndroid Build Coastguard Worker if (lp != nullptr) {
520*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* i = induction_analysis_->LookupInfo(lp, instruction);
521*795d594fSAndroid Build Coastguard Worker if (i != nullptr) {
522*795d594fSAndroid Build Coastguard Worker *loop = lp;
523*795d594fSAndroid Build Coastguard Worker *info = i;
524*795d594fSAndroid Build Coastguard Worker *trip = induction_analysis_->LookupInfo(lp, GetLoopControl(lp));
525*795d594fSAndroid Build Coastguard Worker return true;
526*795d594fSAndroid Build Coastguard Worker }
527*795d594fSAndroid Build Coastguard Worker }
528*795d594fSAndroid Build Coastguard Worker return false;
529*795d594fSAndroid Build Coastguard Worker }
530*795d594fSAndroid Build Coastguard Worker
IsWellBehavedTripCount(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * trip) const531*795d594fSAndroid Build Coastguard Worker bool InductionVarRange::IsWellBehavedTripCount(const HBasicBlock* context,
532*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
533*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* trip) const {
534*795d594fSAndroid Build Coastguard Worker if (trip != nullptr) {
535*795d594fSAndroid Build Coastguard Worker // Both bounds that define a trip-count are well-behaved if they either are not defined
536*795d594fSAndroid Build Coastguard Worker // in any loop, or are contained in a proper interval. This allows finding the min/max
537*795d594fSAndroid Build Coastguard Worker // of an expression by chasing outward.
538*795d594fSAndroid Build Coastguard Worker InductionVarRange range(induction_analysis_);
539*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* lower = trip->op_b->op_a;
540*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* upper = trip->op_b->op_b;
541*795d594fSAndroid Build Coastguard Worker int64_t not_used = 0;
542*795d594fSAndroid Build Coastguard Worker return
543*795d594fSAndroid Build Coastguard Worker (!HasFetchInLoop(lower) || range.IsConstant(context, loop, lower, kAtLeast, ¬_used)) &&
544*795d594fSAndroid Build Coastguard Worker (!HasFetchInLoop(upper) || range.IsConstant(context, loop, upper, kAtLeast, ¬_used));
545*795d594fSAndroid Build Coastguard Worker }
546*795d594fSAndroid Build Coastguard Worker return true;
547*795d594fSAndroid Build Coastguard Worker }
548*795d594fSAndroid Build Coastguard Worker
HasFetchInLoop(HInductionVarAnalysis::InductionInfo * info) const549*795d594fSAndroid Build Coastguard Worker bool InductionVarRange::HasFetchInLoop(HInductionVarAnalysis::InductionInfo* info) const {
550*795d594fSAndroid Build Coastguard Worker if (info != nullptr) {
551*795d594fSAndroid Build Coastguard Worker if (info->induction_class == HInductionVarAnalysis::kInvariant &&
552*795d594fSAndroid Build Coastguard Worker info->operation == HInductionVarAnalysis::kFetch) {
553*795d594fSAndroid Build Coastguard Worker return info->fetch->GetBlock()->GetLoopInformation() != nullptr;
554*795d594fSAndroid Build Coastguard Worker }
555*795d594fSAndroid Build Coastguard Worker return HasFetchInLoop(info->op_a) || HasFetchInLoop(info->op_b);
556*795d594fSAndroid Build Coastguard Worker }
557*795d594fSAndroid Build Coastguard Worker return false;
558*795d594fSAndroid Build Coastguard Worker }
559*795d594fSAndroid Build Coastguard Worker
NeedsTripCount(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,int64_t * stride_value) const560*795d594fSAndroid Build Coastguard Worker bool InductionVarRange::NeedsTripCount(const HBasicBlock* context,
561*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
562*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* info,
563*795d594fSAndroid Build Coastguard Worker int64_t* stride_value) const {
564*795d594fSAndroid Build Coastguard Worker if (info != nullptr) {
565*795d594fSAndroid Build Coastguard Worker if (info->induction_class == HInductionVarAnalysis::kLinear) {
566*795d594fSAndroid Build Coastguard Worker return IsConstant(context, loop, info->op_a, kExact, stride_value);
567*795d594fSAndroid Build Coastguard Worker } else if (info->induction_class == HInductionVarAnalysis::kPolynomial) {
568*795d594fSAndroid Build Coastguard Worker return NeedsTripCount(context, loop, info->op_a, stride_value);
569*795d594fSAndroid Build Coastguard Worker } else if (info->induction_class == HInductionVarAnalysis::kWrapAround) {
570*795d594fSAndroid Build Coastguard Worker return NeedsTripCount(context, loop, info->op_b, stride_value);
571*795d594fSAndroid Build Coastguard Worker }
572*795d594fSAndroid Build Coastguard Worker }
573*795d594fSAndroid Build Coastguard Worker return false;
574*795d594fSAndroid Build Coastguard Worker }
575*795d594fSAndroid Build Coastguard Worker
IsBodyTripCount(HInductionVarAnalysis::InductionInfo * trip) const576*795d594fSAndroid Build Coastguard Worker bool InductionVarRange::IsBodyTripCount(HInductionVarAnalysis::InductionInfo* trip) const {
577*795d594fSAndroid Build Coastguard Worker if (trip != nullptr) {
578*795d594fSAndroid Build Coastguard Worker if (trip->induction_class == HInductionVarAnalysis::kInvariant) {
579*795d594fSAndroid Build Coastguard Worker return trip->operation == HInductionVarAnalysis::kTripCountInBody ||
580*795d594fSAndroid Build Coastguard Worker trip->operation == HInductionVarAnalysis::kTripCountInBodyUnsafe;
581*795d594fSAndroid Build Coastguard Worker }
582*795d594fSAndroid Build Coastguard Worker }
583*795d594fSAndroid Build Coastguard Worker return false;
584*795d594fSAndroid Build Coastguard Worker }
585*795d594fSAndroid Build Coastguard Worker
IsUnsafeTripCount(HInductionVarAnalysis::InductionInfo * trip) const586*795d594fSAndroid Build Coastguard Worker bool InductionVarRange::IsUnsafeTripCount(HInductionVarAnalysis::InductionInfo* trip) const {
587*795d594fSAndroid Build Coastguard Worker if (trip != nullptr) {
588*795d594fSAndroid Build Coastguard Worker if (trip->induction_class == HInductionVarAnalysis::kInvariant) {
589*795d594fSAndroid Build Coastguard Worker return trip->operation == HInductionVarAnalysis::kTripCountInBodyUnsafe ||
590*795d594fSAndroid Build Coastguard Worker trip->operation == HInductionVarAnalysis::kTripCountInLoopUnsafe;
591*795d594fSAndroid Build Coastguard Worker }
592*795d594fSAndroid Build Coastguard Worker }
593*795d594fSAndroid Build Coastguard Worker return false;
594*795d594fSAndroid Build Coastguard Worker }
595*795d594fSAndroid Build Coastguard Worker
GetLinear(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,bool is_min) const596*795d594fSAndroid Build Coastguard Worker InductionVarRange::Value InductionVarRange::GetLinear(const HBasicBlock* context,
597*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
598*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* info,
599*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* trip,
600*795d594fSAndroid Build Coastguard Worker bool is_min) const {
601*795d594fSAndroid Build Coastguard Worker DCHECK(info != nullptr);
602*795d594fSAndroid Build Coastguard Worker DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kLinear);
603*795d594fSAndroid Build Coastguard Worker // Detect common situation where an offset inside the trip-count cancels out during range
604*795d594fSAndroid Build Coastguard Worker // analysis (finding max a * (TC - 1) + OFFSET for a == 1 and TC = UPPER - OFFSET or finding
605*795d594fSAndroid Build Coastguard Worker // min a * (TC - 1) + OFFSET for a == -1 and TC = OFFSET - UPPER) to avoid losing information
606*795d594fSAndroid Build Coastguard Worker // with intermediate results that only incorporate single instructions.
607*795d594fSAndroid Build Coastguard Worker if (trip != nullptr) {
608*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* trip_expr = trip->op_a;
609*795d594fSAndroid Build Coastguard Worker if (trip_expr->type == info->type && trip_expr->operation == HInductionVarAnalysis::kSub) {
610*795d594fSAndroid Build Coastguard Worker int64_t stride_value = 0;
611*795d594fSAndroid Build Coastguard Worker if (IsConstant(context, loop, info->op_a, kExact, &stride_value)) {
612*795d594fSAndroid Build Coastguard Worker if (!is_min && stride_value == 1) {
613*795d594fSAndroid Build Coastguard Worker // Test original trip's negative operand (trip_expr->op_b) against offset of induction.
614*795d594fSAndroid Build Coastguard Worker if (HInductionVarAnalysis::InductionEqual(trip_expr->op_b, info->op_b)) {
615*795d594fSAndroid Build Coastguard Worker // Analyze cancelled trip with just the positive operand (trip_expr->op_a).
616*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo cancelled_trip(
617*795d594fSAndroid Build Coastguard Worker trip->induction_class,
618*795d594fSAndroid Build Coastguard Worker trip->operation,
619*795d594fSAndroid Build Coastguard Worker trip_expr->op_a,
620*795d594fSAndroid Build Coastguard Worker trip->op_b,
621*795d594fSAndroid Build Coastguard Worker nullptr,
622*795d594fSAndroid Build Coastguard Worker trip->type);
623*795d594fSAndroid Build Coastguard Worker return GetVal(context, loop, &cancelled_trip, trip, is_min);
624*795d594fSAndroid Build Coastguard Worker }
625*795d594fSAndroid Build Coastguard Worker } else if (is_min && stride_value == -1) {
626*795d594fSAndroid Build Coastguard Worker // Test original trip's positive operand (trip_expr->op_a) against offset of induction.
627*795d594fSAndroid Build Coastguard Worker if (HInductionVarAnalysis::InductionEqual(trip_expr->op_a, info->op_b)) {
628*795d594fSAndroid Build Coastguard Worker // Analyze cancelled trip with just the negative operand (trip_expr->op_b).
629*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo neg(
630*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::kInvariant,
631*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::kNeg,
632*795d594fSAndroid Build Coastguard Worker nullptr,
633*795d594fSAndroid Build Coastguard Worker trip_expr->op_b,
634*795d594fSAndroid Build Coastguard Worker nullptr,
635*795d594fSAndroid Build Coastguard Worker trip->type);
636*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo cancelled_trip(
637*795d594fSAndroid Build Coastguard Worker trip->induction_class, trip->operation, &neg, trip->op_b, nullptr, trip->type);
638*795d594fSAndroid Build Coastguard Worker return SubValue(Value(0), GetVal(context, loop, &cancelled_trip, trip, !is_min));
639*795d594fSAndroid Build Coastguard Worker }
640*795d594fSAndroid Build Coastguard Worker }
641*795d594fSAndroid Build Coastguard Worker }
642*795d594fSAndroid Build Coastguard Worker }
643*795d594fSAndroid Build Coastguard Worker }
644*795d594fSAndroid Build Coastguard Worker // General rule of linear induction a * i + b, for normalized 0 <= i < TC.
645*795d594fSAndroid Build Coastguard Worker return AddValue(GetMul(context, loop, info->op_a, trip, trip, is_min),
646*795d594fSAndroid Build Coastguard Worker GetVal(context, loop, info->op_b, trip, is_min));
647*795d594fSAndroid Build Coastguard Worker }
648*795d594fSAndroid Build Coastguard Worker
GetPolynomial(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,bool is_min) const649*795d594fSAndroid Build Coastguard Worker InductionVarRange::Value InductionVarRange::GetPolynomial(
650*795d594fSAndroid Build Coastguard Worker const HBasicBlock* context,
651*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
652*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* info,
653*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* trip,
654*795d594fSAndroid Build Coastguard Worker bool is_min) const {
655*795d594fSAndroid Build Coastguard Worker DCHECK(info != nullptr);
656*795d594fSAndroid Build Coastguard Worker DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kPolynomial);
657*795d594fSAndroid Build Coastguard Worker int64_t a = 0;
658*795d594fSAndroid Build Coastguard Worker int64_t b = 0;
659*795d594fSAndroid Build Coastguard Worker if (IsConstant(context, loop, info->op_a->op_a, kExact, &a) &&
660*795d594fSAndroid Build Coastguard Worker CanLongValueFitIntoInt(a) &&
661*795d594fSAndroid Build Coastguard Worker a >= 0 &&
662*795d594fSAndroid Build Coastguard Worker IsConstant(context, loop, info->op_a->op_b, kExact, &b) &&
663*795d594fSAndroid Build Coastguard Worker CanLongValueFitIntoInt(b) &&
664*795d594fSAndroid Build Coastguard Worker b >= 0) {
665*795d594fSAndroid Build Coastguard Worker // Evaluate bounds on sum_i=0^m-1(a * i + b) + c with a,b >= 0 for
666*795d594fSAndroid Build Coastguard Worker // maximum index value m as a * (m * (m-1)) / 2 + b * m + c.
667*795d594fSAndroid Build Coastguard Worker // Do not simply return `c` as minimum because the trip count may be non-zero
668*795d594fSAndroid Build Coastguard Worker // if the `context` is after the `loop` (and therefore ignoring `is_min`).
669*795d594fSAndroid Build Coastguard Worker Value c = GetVal(context, loop, info->op_b, trip, is_min);
670*795d594fSAndroid Build Coastguard Worker Value m = GetVal(context, loop, trip, trip, is_min);
671*795d594fSAndroid Build Coastguard Worker Value t = DivValue(MulValue(m, SubValue(m, Value(1))), Value(2));
672*795d594fSAndroid Build Coastguard Worker Value x = MulValue(Value(a), t);
673*795d594fSAndroid Build Coastguard Worker Value y = MulValue(Value(b), m);
674*795d594fSAndroid Build Coastguard Worker return AddValue(AddValue(x, y), c);
675*795d594fSAndroid Build Coastguard Worker }
676*795d594fSAndroid Build Coastguard Worker return Value();
677*795d594fSAndroid Build Coastguard Worker }
678*795d594fSAndroid Build Coastguard Worker
GetGeometric(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,bool is_min) const679*795d594fSAndroid Build Coastguard Worker InductionVarRange::Value InductionVarRange::GetGeometric(const HBasicBlock* context,
680*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
681*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* info,
682*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* trip,
683*795d594fSAndroid Build Coastguard Worker bool is_min) const {
684*795d594fSAndroid Build Coastguard Worker DCHECK(info != nullptr);
685*795d594fSAndroid Build Coastguard Worker DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kGeometric);
686*795d594fSAndroid Build Coastguard Worker int64_t a = 0;
687*795d594fSAndroid Build Coastguard Worker int64_t f = 0;
688*795d594fSAndroid Build Coastguard Worker if (IsConstant(context, loop, info->op_a, kExact, &a) &&
689*795d594fSAndroid Build Coastguard Worker CanLongValueFitIntoInt(a) &&
690*795d594fSAndroid Build Coastguard Worker IsInt64AndGet(info->fetch, &f) && f >= 1) {
691*795d594fSAndroid Build Coastguard Worker // Conservative bounds on a * f^-i + b with f >= 1 can be computed without
692*795d594fSAndroid Build Coastguard Worker // trip count. Other forms would require a much more elaborate evaluation.
693*795d594fSAndroid Build Coastguard Worker const bool is_min_a = a >= 0 ? is_min : !is_min;
694*795d594fSAndroid Build Coastguard Worker if (info->operation == HInductionVarAnalysis::kDiv) {
695*795d594fSAndroid Build Coastguard Worker Value b = GetVal(context, loop, info->op_b, trip, is_min);
696*795d594fSAndroid Build Coastguard Worker return is_min_a ? b : AddValue(Value(a), b);
697*795d594fSAndroid Build Coastguard Worker }
698*795d594fSAndroid Build Coastguard Worker }
699*795d594fSAndroid Build Coastguard Worker return Value();
700*795d594fSAndroid Build Coastguard Worker }
701*795d594fSAndroid Build Coastguard Worker
GetFetch(const HBasicBlock * context,const HLoopInformation * loop,HInstruction * instruction,HInductionVarAnalysis::InductionInfo * trip,bool is_min) const702*795d594fSAndroid Build Coastguard Worker InductionVarRange::Value InductionVarRange::GetFetch(const HBasicBlock* context,
703*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
704*795d594fSAndroid Build Coastguard Worker HInstruction* instruction,
705*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* trip,
706*795d594fSAndroid Build Coastguard Worker bool is_min) const {
707*795d594fSAndroid Build Coastguard Worker // Special case when chasing constants: single instruction that denotes trip count in the
708*795d594fSAndroid Build Coastguard Worker // loop-body is minimal 1 and maximal, with safe trip-count, max int,
709*795d594fSAndroid Build Coastguard Worker if (chase_hint_ == nullptr &&
710*795d594fSAndroid Build Coastguard Worker IsContextInBody(context, loop) &&
711*795d594fSAndroid Build Coastguard Worker trip != nullptr &&
712*795d594fSAndroid Build Coastguard Worker instruction == trip->op_a->fetch) {
713*795d594fSAndroid Build Coastguard Worker if (is_min) {
714*795d594fSAndroid Build Coastguard Worker return Value(1);
715*795d594fSAndroid Build Coastguard Worker } else if (!instruction->IsConstant() && !IsUnsafeTripCount(trip)) {
716*795d594fSAndroid Build Coastguard Worker return Value(std::numeric_limits<int32_t>::max());
717*795d594fSAndroid Build Coastguard Worker }
718*795d594fSAndroid Build Coastguard Worker }
719*795d594fSAndroid Build Coastguard Worker // Unless at a constant or hint, chase the instruction a bit deeper into the HIR tree, so that
720*795d594fSAndroid Build Coastguard Worker // it becomes more likely range analysis will compare the same instructions as terminal nodes.
721*795d594fSAndroid Build Coastguard Worker int64_t value;
722*795d594fSAndroid Build Coastguard Worker if (IsInt64AndGet(instruction, &value) && CanLongValueFitIntoInt(value)) {
723*795d594fSAndroid Build Coastguard Worker // Proper constant reveals best information.
724*795d594fSAndroid Build Coastguard Worker return Value(static_cast<int32_t>(value));
725*795d594fSAndroid Build Coastguard Worker } else if (instruction == chase_hint_) {
726*795d594fSAndroid Build Coastguard Worker // At hint, fetch is represented by itself.
727*795d594fSAndroid Build Coastguard Worker return Value(instruction, 1, 0);
728*795d594fSAndroid Build Coastguard Worker } else if (instruction->IsAdd()) {
729*795d594fSAndroid Build Coastguard Worker // Incorporate suitable constants in the chased value.
730*795d594fSAndroid Build Coastguard Worker if (IsInt64AndGet(instruction->InputAt(0), &value) && CanLongValueFitIntoInt(value)) {
731*795d594fSAndroid Build Coastguard Worker return AddValue(Value(static_cast<int32_t>(value)),
732*795d594fSAndroid Build Coastguard Worker GetFetch(context, loop, instruction->InputAt(1), trip, is_min));
733*795d594fSAndroid Build Coastguard Worker } else if (IsInt64AndGet(instruction->InputAt(1), &value) && CanLongValueFitIntoInt(value)) {
734*795d594fSAndroid Build Coastguard Worker return AddValue(GetFetch(context, loop, instruction->InputAt(0), trip, is_min),
735*795d594fSAndroid Build Coastguard Worker Value(static_cast<int32_t>(value)));
736*795d594fSAndroid Build Coastguard Worker }
737*795d594fSAndroid Build Coastguard Worker } else if (instruction->IsSub()) {
738*795d594fSAndroid Build Coastguard Worker // Incorporate suitable constants in the chased value.
739*795d594fSAndroid Build Coastguard Worker if (IsInt64AndGet(instruction->InputAt(0), &value) && CanLongValueFitIntoInt(value)) {
740*795d594fSAndroid Build Coastguard Worker return SubValue(Value(static_cast<int32_t>(value)),
741*795d594fSAndroid Build Coastguard Worker GetFetch(context, loop, instruction->InputAt(1), trip, !is_min));
742*795d594fSAndroid Build Coastguard Worker } else if (IsInt64AndGet(instruction->InputAt(1), &value) && CanLongValueFitIntoInt(value)) {
743*795d594fSAndroid Build Coastguard Worker return SubValue(GetFetch(context, loop, instruction->InputAt(0), trip, is_min),
744*795d594fSAndroid Build Coastguard Worker Value(static_cast<int32_t>(value)));
745*795d594fSAndroid Build Coastguard Worker }
746*795d594fSAndroid Build Coastguard Worker } else if (instruction->IsArrayLength()) {
747*795d594fSAndroid Build Coastguard Worker // Exploit length properties when chasing constants or chase into a new array declaration.
748*795d594fSAndroid Build Coastguard Worker if (chase_hint_ == nullptr) {
749*795d594fSAndroid Build Coastguard Worker return is_min ? Value(0) : Value(std::numeric_limits<int32_t>::max());
750*795d594fSAndroid Build Coastguard Worker } else if (instruction->InputAt(0)->IsNewArray()) {
751*795d594fSAndroid Build Coastguard Worker return GetFetch(
752*795d594fSAndroid Build Coastguard Worker context, loop, instruction->InputAt(0)->AsNewArray()->GetLength(), trip, is_min);
753*795d594fSAndroid Build Coastguard Worker }
754*795d594fSAndroid Build Coastguard Worker } else if (instruction->IsTypeConversion()) {
755*795d594fSAndroid Build Coastguard Worker // Since analysis is 32-bit (or narrower), chase beyond widening along the path.
756*795d594fSAndroid Build Coastguard Worker // For example, this discovers the length in: for (long i = 0; i < a.length; i++);
757*795d594fSAndroid Build Coastguard Worker if (instruction->AsTypeConversion()->GetInputType() == DataType::Type::kInt32 &&
758*795d594fSAndroid Build Coastguard Worker instruction->AsTypeConversion()->GetResultType() == DataType::Type::kInt64) {
759*795d594fSAndroid Build Coastguard Worker return GetFetch(context, loop, instruction->InputAt(0), trip, is_min);
760*795d594fSAndroid Build Coastguard Worker }
761*795d594fSAndroid Build Coastguard Worker }
762*795d594fSAndroid Build Coastguard Worker // Chase an invariant fetch that is defined by another loop if the trip-count used
763*795d594fSAndroid Build Coastguard Worker // so far is well-behaved in both bounds and the next trip-count is safe.
764*795d594fSAndroid Build Coastguard Worker // Example:
765*795d594fSAndroid Build Coastguard Worker // for (int i = 0; i <= 100; i++) // safe
766*795d594fSAndroid Build Coastguard Worker // for (int j = 0; j <= i; j++) // well-behaved
767*795d594fSAndroid Build Coastguard Worker // j is in range [0, i ] (if i is chase hint)
768*795d594fSAndroid Build Coastguard Worker // or in range [0, 100] (otherwise)
769*795d594fSAndroid Build Coastguard Worker // Example:
770*795d594fSAndroid Build Coastguard Worker // for (i = 0; i < 100; ++i)
771*795d594fSAndroid Build Coastguard Worker // <some-code>
772*795d594fSAndroid Build Coastguard Worker // for (j = 0; j < 10; ++j)
773*795d594fSAndroid Build Coastguard Worker // sum += i; // The `i` is a "fetch" of a loop Phi from the previous loop.
774*795d594fSAndroid Build Coastguard Worker const HLoopInformation* next_loop = nullptr;
775*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* next_info = nullptr;
776*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* next_trip = nullptr;
777*795d594fSAndroid Build Coastguard Worker if (HasInductionInfo(instruction->GetBlock(), instruction, &next_loop, &next_info, &next_trip) &&
778*795d594fSAndroid Build Coastguard Worker IsWellBehavedTripCount(context, next_loop, trip) &&
779*795d594fSAndroid Build Coastguard Worker !IsUnsafeTripCount(next_trip)) {
780*795d594fSAndroid Build Coastguard Worker return GetVal(context, next_loop, next_info, next_trip, is_min);
781*795d594fSAndroid Build Coastguard Worker }
782*795d594fSAndroid Build Coastguard Worker // Fetch is represented by itself.
783*795d594fSAndroid Build Coastguard Worker return Value(instruction, 1, 0);
784*795d594fSAndroid Build Coastguard Worker }
785*795d594fSAndroid Build Coastguard Worker
GetVal(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,bool is_min) const786*795d594fSAndroid Build Coastguard Worker InductionVarRange::Value InductionVarRange::GetVal(const HBasicBlock* context,
787*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
788*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* info,
789*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* trip,
790*795d594fSAndroid Build Coastguard Worker bool is_min) const {
791*795d594fSAndroid Build Coastguard Worker if (info != nullptr) {
792*795d594fSAndroid Build Coastguard Worker switch (info->induction_class) {
793*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kInvariant:
794*795d594fSAndroid Build Coastguard Worker // Invariants.
795*795d594fSAndroid Build Coastguard Worker switch (info->operation) {
796*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kAdd:
797*795d594fSAndroid Build Coastguard Worker return AddValue(GetVal(context, loop, info->op_a, trip, is_min),
798*795d594fSAndroid Build Coastguard Worker GetVal(context, loop, info->op_b, trip, is_min));
799*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kSub: // second reversed!
800*795d594fSAndroid Build Coastguard Worker return SubValue(GetVal(context, loop, info->op_a, trip, is_min),
801*795d594fSAndroid Build Coastguard Worker GetVal(context, loop, info->op_b, trip, !is_min));
802*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kNeg: // second reversed!
803*795d594fSAndroid Build Coastguard Worker return SubValue(Value(0),
804*795d594fSAndroid Build Coastguard Worker GetVal(context, loop, info->op_b, trip, !is_min));
805*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kMul:
806*795d594fSAndroid Build Coastguard Worker return GetMul(context, loop, info->op_a, info->op_b, trip, is_min);
807*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kDiv:
808*795d594fSAndroid Build Coastguard Worker return GetDiv(context, loop, info->op_a, info->op_b, trip, is_min);
809*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kRem:
810*795d594fSAndroid Build Coastguard Worker return GetRem(context, loop, info->op_a, info->op_b);
811*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kXor:
812*795d594fSAndroid Build Coastguard Worker return GetXor(context, loop, info->op_a, info->op_b);
813*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kFetch:
814*795d594fSAndroid Build Coastguard Worker return GetFetch(context, loop, info->fetch, trip, is_min);
815*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kTripCountInLoop:
816*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kTripCountInLoopUnsafe:
817*795d594fSAndroid Build Coastguard Worker if (UseFullTripCount(context, loop, is_min)) {
818*795d594fSAndroid Build Coastguard Worker // Return the full trip count (do not subtract 1 as we do in loop body).
819*795d594fSAndroid Build Coastguard Worker return GetVal(context, loop, info->op_a, trip, is_min);
820*795d594fSAndroid Build Coastguard Worker }
821*795d594fSAndroid Build Coastguard Worker FALLTHROUGH_INTENDED;
822*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kTripCountInBody:
823*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kTripCountInBodyUnsafe:
824*795d594fSAndroid Build Coastguard Worker if (is_min) {
825*795d594fSAndroid Build Coastguard Worker return Value(0);
826*795d594fSAndroid Build Coastguard Worker } else if (IsContextInBody(context, loop)) {
827*795d594fSAndroid Build Coastguard Worker return SubValue(GetVal(context, loop, info->op_a, trip, is_min), Value(1));
828*795d594fSAndroid Build Coastguard Worker }
829*795d594fSAndroid Build Coastguard Worker break;
830*795d594fSAndroid Build Coastguard Worker default:
831*795d594fSAndroid Build Coastguard Worker break;
832*795d594fSAndroid Build Coastguard Worker }
833*795d594fSAndroid Build Coastguard Worker break;
834*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kLinear:
835*795d594fSAndroid Build Coastguard Worker return CorrectForType(GetLinear(context, loop, info, trip, is_min), info->type);
836*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kPolynomial:
837*795d594fSAndroid Build Coastguard Worker return GetPolynomial(context, loop, info, trip, is_min);
838*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kGeometric:
839*795d594fSAndroid Build Coastguard Worker return GetGeometric(context, loop, info, trip, is_min);
840*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kWrapAround:
841*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kPeriodic:
842*795d594fSAndroid Build Coastguard Worker return MergeVal(GetVal(context, loop, info->op_a, trip, is_min),
843*795d594fSAndroid Build Coastguard Worker GetVal(context, loop, info->op_b, trip, is_min),
844*795d594fSAndroid Build Coastguard Worker is_min);
845*795d594fSAndroid Build Coastguard Worker }
846*795d594fSAndroid Build Coastguard Worker }
847*795d594fSAndroid Build Coastguard Worker return Value();
848*795d594fSAndroid Build Coastguard Worker }
849*795d594fSAndroid Build Coastguard Worker
GetMul(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info1,HInductionVarAnalysis::InductionInfo * info2,HInductionVarAnalysis::InductionInfo * trip,bool is_min) const850*795d594fSAndroid Build Coastguard Worker InductionVarRange::Value InductionVarRange::GetMul(const HBasicBlock* context,
851*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
852*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* info1,
853*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* info2,
854*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* trip,
855*795d594fSAndroid Build Coastguard Worker bool is_min) const {
856*795d594fSAndroid Build Coastguard Worker // Constant times range.
857*795d594fSAndroid Build Coastguard Worker int64_t value = 0;
858*795d594fSAndroid Build Coastguard Worker if (IsConstant(context, loop, info1, kExact, &value)) {
859*795d594fSAndroid Build Coastguard Worker return MulRangeAndConstant(context, loop, value, info2, trip, is_min);
860*795d594fSAndroid Build Coastguard Worker } else if (IsConstant(context, loop, info2, kExact, &value)) {
861*795d594fSAndroid Build Coastguard Worker return MulRangeAndConstant(context, loop, value, info1, trip, is_min);
862*795d594fSAndroid Build Coastguard Worker }
863*795d594fSAndroid Build Coastguard Worker // Interval ranges.
864*795d594fSAndroid Build Coastguard Worker Value v1_min = GetVal(context, loop, info1, trip, /*is_min=*/ true);
865*795d594fSAndroid Build Coastguard Worker Value v1_max = GetVal(context, loop, info1, trip, /*is_min=*/ false);
866*795d594fSAndroid Build Coastguard Worker Value v2_min = GetVal(context, loop, info2, trip, /*is_min=*/ true);
867*795d594fSAndroid Build Coastguard Worker Value v2_max = GetVal(context, loop, info2, trip, /*is_min=*/ false);
868*795d594fSAndroid Build Coastguard Worker // Positive range vs. positive or negative range.
869*795d594fSAndroid Build Coastguard Worker if (IsConstantValue(v1_min) && v1_min.b_constant >= 0) {
870*795d594fSAndroid Build Coastguard Worker if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) {
871*795d594fSAndroid Build Coastguard Worker return is_min ? MulValue(v1_min, v2_min) : MulValue(v1_max, v2_max);
872*795d594fSAndroid Build Coastguard Worker } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) {
873*795d594fSAndroid Build Coastguard Worker return is_min ? MulValue(v1_max, v2_min) : MulValue(v1_min, v2_max);
874*795d594fSAndroid Build Coastguard Worker }
875*795d594fSAndroid Build Coastguard Worker }
876*795d594fSAndroid Build Coastguard Worker // Negative range vs. positive or negative range.
877*795d594fSAndroid Build Coastguard Worker if (IsConstantValue(v1_max) && v1_max.b_constant <= 0) {
878*795d594fSAndroid Build Coastguard Worker if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) {
879*795d594fSAndroid Build Coastguard Worker return is_min ? MulValue(v1_min, v2_max) : MulValue(v1_max, v2_min);
880*795d594fSAndroid Build Coastguard Worker } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) {
881*795d594fSAndroid Build Coastguard Worker return is_min ? MulValue(v1_max, v2_max) : MulValue(v1_min, v2_min);
882*795d594fSAndroid Build Coastguard Worker }
883*795d594fSAndroid Build Coastguard Worker }
884*795d594fSAndroid Build Coastguard Worker return Value();
885*795d594fSAndroid Build Coastguard Worker }
886*795d594fSAndroid Build Coastguard Worker
GetDiv(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info1,HInductionVarAnalysis::InductionInfo * info2,HInductionVarAnalysis::InductionInfo * trip,bool is_min) const887*795d594fSAndroid Build Coastguard Worker InductionVarRange::Value InductionVarRange::GetDiv(const HBasicBlock* context,
888*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
889*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* info1,
890*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* info2,
891*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* trip,
892*795d594fSAndroid Build Coastguard Worker bool is_min) const {
893*795d594fSAndroid Build Coastguard Worker // Range divided by constant.
894*795d594fSAndroid Build Coastguard Worker int64_t value = 0;
895*795d594fSAndroid Build Coastguard Worker if (IsConstant(context, loop, info2, kExact, &value)) {
896*795d594fSAndroid Build Coastguard Worker return DivRangeAndConstant(context, loop, value, info1, trip, is_min);
897*795d594fSAndroid Build Coastguard Worker }
898*795d594fSAndroid Build Coastguard Worker // Interval ranges.
899*795d594fSAndroid Build Coastguard Worker Value v1_min = GetVal(context, loop, info1, trip, /*is_min=*/ true);
900*795d594fSAndroid Build Coastguard Worker Value v1_max = GetVal(context, loop, info1, trip, /*is_min=*/ false);
901*795d594fSAndroid Build Coastguard Worker Value v2_min = GetVal(context, loop, info2, trip, /*is_min=*/ true);
902*795d594fSAndroid Build Coastguard Worker Value v2_max = GetVal(context, loop, info2, trip, /*is_min=*/ false);
903*795d594fSAndroid Build Coastguard Worker // Positive range vs. positive or negative range.
904*795d594fSAndroid Build Coastguard Worker if (IsConstantValue(v1_min) && v1_min.b_constant >= 0) {
905*795d594fSAndroid Build Coastguard Worker if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) {
906*795d594fSAndroid Build Coastguard Worker return is_min ? DivValue(v1_min, v2_max) : DivValue(v1_max, v2_min);
907*795d594fSAndroid Build Coastguard Worker } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) {
908*795d594fSAndroid Build Coastguard Worker return is_min ? DivValue(v1_max, v2_max) : DivValue(v1_min, v2_min);
909*795d594fSAndroid Build Coastguard Worker }
910*795d594fSAndroid Build Coastguard Worker }
911*795d594fSAndroid Build Coastguard Worker // Negative range vs. positive or negative range.
912*795d594fSAndroid Build Coastguard Worker if (IsConstantValue(v1_max) && v1_max.b_constant <= 0) {
913*795d594fSAndroid Build Coastguard Worker if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) {
914*795d594fSAndroid Build Coastguard Worker return is_min ? DivValue(v1_min, v2_min) : DivValue(v1_max, v2_max);
915*795d594fSAndroid Build Coastguard Worker } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) {
916*795d594fSAndroid Build Coastguard Worker return is_min ? DivValue(v1_max, v2_min) : DivValue(v1_min, v2_max);
917*795d594fSAndroid Build Coastguard Worker }
918*795d594fSAndroid Build Coastguard Worker }
919*795d594fSAndroid Build Coastguard Worker return Value();
920*795d594fSAndroid Build Coastguard Worker }
921*795d594fSAndroid Build Coastguard Worker
GetRem(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info1,HInductionVarAnalysis::InductionInfo * info2) const922*795d594fSAndroid Build Coastguard Worker InductionVarRange::Value InductionVarRange::GetRem(
923*795d594fSAndroid Build Coastguard Worker const HBasicBlock* context,
924*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
925*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* info1,
926*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* info2) const {
927*795d594fSAndroid Build Coastguard Worker int64_t v1 = 0;
928*795d594fSAndroid Build Coastguard Worker int64_t v2 = 0;
929*795d594fSAndroid Build Coastguard Worker // Only accept exact values.
930*795d594fSAndroid Build Coastguard Worker if (IsConstant(context, loop, info1, kExact, &v1) &&
931*795d594fSAndroid Build Coastguard Worker IsConstant(context, loop, info2, kExact, &v2) &&
932*795d594fSAndroid Build Coastguard Worker v2 != 0) {
933*795d594fSAndroid Build Coastguard Worker int64_t value = v1 % v2;
934*795d594fSAndroid Build Coastguard Worker if (CanLongValueFitIntoInt(value)) {
935*795d594fSAndroid Build Coastguard Worker return Value(static_cast<int32_t>(value));
936*795d594fSAndroid Build Coastguard Worker }
937*795d594fSAndroid Build Coastguard Worker }
938*795d594fSAndroid Build Coastguard Worker return Value();
939*795d594fSAndroid Build Coastguard Worker }
940*795d594fSAndroid Build Coastguard Worker
GetXor(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info1,HInductionVarAnalysis::InductionInfo * info2) const941*795d594fSAndroid Build Coastguard Worker InductionVarRange::Value InductionVarRange::GetXor(
942*795d594fSAndroid Build Coastguard Worker const HBasicBlock* context,
943*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
944*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* info1,
945*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* info2) const {
946*795d594fSAndroid Build Coastguard Worker int64_t v1 = 0;
947*795d594fSAndroid Build Coastguard Worker int64_t v2 = 0;
948*795d594fSAndroid Build Coastguard Worker // Only accept exact values.
949*795d594fSAndroid Build Coastguard Worker if (IsConstant(context, loop, info1, kExact, &v1) &&
950*795d594fSAndroid Build Coastguard Worker IsConstant(context, loop, info2, kExact, &v2)) {
951*795d594fSAndroid Build Coastguard Worker int64_t value = v1 ^ v2;
952*795d594fSAndroid Build Coastguard Worker if (CanLongValueFitIntoInt(value)) {
953*795d594fSAndroid Build Coastguard Worker return Value(static_cast<int32_t>(value));
954*795d594fSAndroid Build Coastguard Worker }
955*795d594fSAndroid Build Coastguard Worker }
956*795d594fSAndroid Build Coastguard Worker return Value();
957*795d594fSAndroid Build Coastguard Worker }
958*795d594fSAndroid Build Coastguard Worker
MulRangeAndConstant(const HBasicBlock * context,const HLoopInformation * loop,int64_t value,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,bool is_min) const959*795d594fSAndroid Build Coastguard Worker InductionVarRange::Value InductionVarRange::MulRangeAndConstant(
960*795d594fSAndroid Build Coastguard Worker const HBasicBlock* context,
961*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
962*795d594fSAndroid Build Coastguard Worker int64_t value,
963*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* info,
964*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* trip,
965*795d594fSAndroid Build Coastguard Worker bool is_min) const {
966*795d594fSAndroid Build Coastguard Worker if (CanLongValueFitIntoInt(value)) {
967*795d594fSAndroid Build Coastguard Worker Value c(static_cast<int32_t>(value));
968*795d594fSAndroid Build Coastguard Worker return MulValue(GetVal(context, loop, info, trip, is_min == value >= 0), c);
969*795d594fSAndroid Build Coastguard Worker }
970*795d594fSAndroid Build Coastguard Worker return Value();
971*795d594fSAndroid Build Coastguard Worker }
972*795d594fSAndroid Build Coastguard Worker
DivRangeAndConstant(const HBasicBlock * context,const HLoopInformation * loop,int64_t value,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,bool is_min) const973*795d594fSAndroid Build Coastguard Worker InductionVarRange::Value InductionVarRange::DivRangeAndConstant(
974*795d594fSAndroid Build Coastguard Worker const HBasicBlock* context,
975*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
976*795d594fSAndroid Build Coastguard Worker int64_t value,
977*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* info,
978*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* trip,
979*795d594fSAndroid Build Coastguard Worker bool is_min) const {
980*795d594fSAndroid Build Coastguard Worker if (CanLongValueFitIntoInt(value)) {
981*795d594fSAndroid Build Coastguard Worker Value c(static_cast<int32_t>(value));
982*795d594fSAndroid Build Coastguard Worker return DivValue(GetVal(context, loop, info, trip, is_min == value >= 0), c);
983*795d594fSAndroid Build Coastguard Worker }
984*795d594fSAndroid Build Coastguard Worker return Value();
985*795d594fSAndroid Build Coastguard Worker }
986*795d594fSAndroid Build Coastguard Worker
AddValue(Value v1,Value v2) const987*795d594fSAndroid Build Coastguard Worker InductionVarRange::Value InductionVarRange::AddValue(Value v1, Value v2) const {
988*795d594fSAndroid Build Coastguard Worker if (v1.is_known && v2.is_known && IsSafeAdd(v1.b_constant, v2.b_constant)) {
989*795d594fSAndroid Build Coastguard Worker int32_t b = v1.b_constant + v2.b_constant;
990*795d594fSAndroid Build Coastguard Worker if (v1.a_constant == 0) {
991*795d594fSAndroid Build Coastguard Worker return Value(v2.instruction, v2.a_constant, b);
992*795d594fSAndroid Build Coastguard Worker } else if (v2.a_constant == 0) {
993*795d594fSAndroid Build Coastguard Worker return Value(v1.instruction, v1.a_constant, b);
994*795d594fSAndroid Build Coastguard Worker } else if (v1.instruction == v2.instruction && IsSafeAdd(v1.a_constant, v2.a_constant)) {
995*795d594fSAndroid Build Coastguard Worker return Value(v1.instruction, v1.a_constant + v2.a_constant, b);
996*795d594fSAndroid Build Coastguard Worker }
997*795d594fSAndroid Build Coastguard Worker }
998*795d594fSAndroid Build Coastguard Worker return Value();
999*795d594fSAndroid Build Coastguard Worker }
1000*795d594fSAndroid Build Coastguard Worker
SubValue(Value v1,Value v2) const1001*795d594fSAndroid Build Coastguard Worker InductionVarRange::Value InductionVarRange::SubValue(Value v1, Value v2) const {
1002*795d594fSAndroid Build Coastguard Worker if (v1.is_known && v2.is_known && IsSafeSub(v1.b_constant, v2.b_constant)) {
1003*795d594fSAndroid Build Coastguard Worker int32_t b = v1.b_constant - v2.b_constant;
1004*795d594fSAndroid Build Coastguard Worker if (v1.a_constant == 0 && IsSafeSub(0, v2.a_constant)) {
1005*795d594fSAndroid Build Coastguard Worker return Value(v2.instruction, -v2.a_constant, b);
1006*795d594fSAndroid Build Coastguard Worker } else if (v2.a_constant == 0) {
1007*795d594fSAndroid Build Coastguard Worker return Value(v1.instruction, v1.a_constant, b);
1008*795d594fSAndroid Build Coastguard Worker } else if (v1.instruction == v2.instruction && IsSafeSub(v1.a_constant, v2.a_constant)) {
1009*795d594fSAndroid Build Coastguard Worker return Value(v1.instruction, v1.a_constant - v2.a_constant, b);
1010*795d594fSAndroid Build Coastguard Worker }
1011*795d594fSAndroid Build Coastguard Worker }
1012*795d594fSAndroid Build Coastguard Worker return Value();
1013*795d594fSAndroid Build Coastguard Worker }
1014*795d594fSAndroid Build Coastguard Worker
MulValue(Value v1,Value v2) const1015*795d594fSAndroid Build Coastguard Worker InductionVarRange::Value InductionVarRange::MulValue(Value v1, Value v2) const {
1016*795d594fSAndroid Build Coastguard Worker if (v1.is_known && v2.is_known) {
1017*795d594fSAndroid Build Coastguard Worker if (v1.a_constant == 0) {
1018*795d594fSAndroid Build Coastguard Worker if (IsSafeMul(v1.b_constant, v2.a_constant) && IsSafeMul(v1.b_constant, v2.b_constant)) {
1019*795d594fSAndroid Build Coastguard Worker return Value(v2.instruction, v1.b_constant * v2.a_constant, v1.b_constant * v2.b_constant);
1020*795d594fSAndroid Build Coastguard Worker }
1021*795d594fSAndroid Build Coastguard Worker } else if (v2.a_constant == 0) {
1022*795d594fSAndroid Build Coastguard Worker if (IsSafeMul(v1.a_constant, v2.b_constant) && IsSafeMul(v1.b_constant, v2.b_constant)) {
1023*795d594fSAndroid Build Coastguard Worker return Value(v1.instruction, v1.a_constant * v2.b_constant, v1.b_constant * v2.b_constant);
1024*795d594fSAndroid Build Coastguard Worker }
1025*795d594fSAndroid Build Coastguard Worker }
1026*795d594fSAndroid Build Coastguard Worker }
1027*795d594fSAndroid Build Coastguard Worker return Value();
1028*795d594fSAndroid Build Coastguard Worker }
1029*795d594fSAndroid Build Coastguard Worker
DivValue(Value v1,Value v2) const1030*795d594fSAndroid Build Coastguard Worker InductionVarRange::Value InductionVarRange::DivValue(Value v1, Value v2) const {
1031*795d594fSAndroid Build Coastguard Worker if (v1.is_known && v2.is_known && v1.a_constant == 0 && v2.a_constant == 0) {
1032*795d594fSAndroid Build Coastguard Worker if (IsSafeDiv(v1.b_constant, v2.b_constant)) {
1033*795d594fSAndroid Build Coastguard Worker return Value(v1.b_constant / v2.b_constant);
1034*795d594fSAndroid Build Coastguard Worker }
1035*795d594fSAndroid Build Coastguard Worker }
1036*795d594fSAndroid Build Coastguard Worker return Value();
1037*795d594fSAndroid Build Coastguard Worker }
1038*795d594fSAndroid Build Coastguard Worker
MergeVal(Value v1,Value v2,bool is_min) const1039*795d594fSAndroid Build Coastguard Worker InductionVarRange::Value InductionVarRange::MergeVal(Value v1, Value v2, bool is_min) const {
1040*795d594fSAndroid Build Coastguard Worker if (v1.is_known && v2.is_known) {
1041*795d594fSAndroid Build Coastguard Worker if (v1.instruction == v2.instruction && v1.a_constant == v2.a_constant) {
1042*795d594fSAndroid Build Coastguard Worker return Value(v1.instruction, v1.a_constant,
1043*795d594fSAndroid Build Coastguard Worker is_min ? std::min(v1.b_constant, v2.b_constant)
1044*795d594fSAndroid Build Coastguard Worker : std::max(v1.b_constant, v2.b_constant));
1045*795d594fSAndroid Build Coastguard Worker }
1046*795d594fSAndroid Build Coastguard Worker }
1047*795d594fSAndroid Build Coastguard Worker return Value();
1048*795d594fSAndroid Build Coastguard Worker }
1049*795d594fSAndroid Build Coastguard Worker
GenerateRangeOrLastValue(const HBasicBlock * context,HInstruction * instruction,bool is_last_value,HGraph * graph,HBasicBlock * block,HInstruction ** lower,HInstruction ** upper,HInstruction ** taken_test,int64_t * stride_value,bool * needs_finite_test,bool * needs_taken_test) const1050*795d594fSAndroid Build Coastguard Worker bool InductionVarRange::GenerateRangeOrLastValue(const HBasicBlock* context,
1051*795d594fSAndroid Build Coastguard Worker HInstruction* instruction,
1052*795d594fSAndroid Build Coastguard Worker bool is_last_value,
1053*795d594fSAndroid Build Coastguard Worker HGraph* graph,
1054*795d594fSAndroid Build Coastguard Worker HBasicBlock* block,
1055*795d594fSAndroid Build Coastguard Worker /*out*/HInstruction** lower,
1056*795d594fSAndroid Build Coastguard Worker /*out*/HInstruction** upper,
1057*795d594fSAndroid Build Coastguard Worker /*out*/HInstruction** taken_test,
1058*795d594fSAndroid Build Coastguard Worker /*out*/int64_t* stride_value,
1059*795d594fSAndroid Build Coastguard Worker /*out*/bool* needs_finite_test,
1060*795d594fSAndroid Build Coastguard Worker /*out*/bool* needs_taken_test) const {
1061*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop = nullptr;
1062*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* info = nullptr;
1063*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* trip = nullptr;
1064*795d594fSAndroid Build Coastguard Worker if (!HasInductionInfo(context, instruction, &loop, &info, &trip) || trip == nullptr) {
1065*795d594fSAndroid Build Coastguard Worker return false; // codegen needs all information, including tripcount
1066*795d594fSAndroid Build Coastguard Worker }
1067*795d594fSAndroid Build Coastguard Worker // Determine what tests are needed. A finite test is needed if the evaluation code uses the
1068*795d594fSAndroid Build Coastguard Worker // trip-count and the loop maybe unsafe (because in such cases, the index could "overshoot"
1069*795d594fSAndroid Build Coastguard Worker // the computed range). A taken test is needed for any unknown trip-count, even if evaluation
1070*795d594fSAndroid Build Coastguard Worker // code does not use the trip-count explicitly (since there could be an implicit relation
1071*795d594fSAndroid Build Coastguard Worker // between e.g. an invariant subscript and a not-taken condition).
1072*795d594fSAndroid Build Coastguard Worker *stride_value = 0;
1073*795d594fSAndroid Build Coastguard Worker *needs_finite_test = NeedsTripCount(context, loop, info, stride_value) && IsUnsafeTripCount(trip);
1074*795d594fSAndroid Build Coastguard Worker *needs_taken_test = IsBodyTripCount(trip);
1075*795d594fSAndroid Build Coastguard Worker // Handle last value request.
1076*795d594fSAndroid Build Coastguard Worker if (is_last_value) {
1077*795d594fSAndroid Build Coastguard Worker DCHECK(!IsContextInBody(context, loop));
1078*795d594fSAndroid Build Coastguard Worker switch (info->induction_class) {
1079*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kLinear:
1080*795d594fSAndroid Build Coastguard Worker if (*stride_value > 0) {
1081*795d594fSAndroid Build Coastguard Worker lower = nullptr;
1082*795d594fSAndroid Build Coastguard Worker return GenerateLastValueLinear(
1083*795d594fSAndroid Build Coastguard Worker context, loop, info, trip, graph, block, /*is_min=*/false, upper, needs_taken_test);
1084*795d594fSAndroid Build Coastguard Worker } else {
1085*795d594fSAndroid Build Coastguard Worker upper = nullptr;
1086*795d594fSAndroid Build Coastguard Worker return GenerateLastValueLinear(
1087*795d594fSAndroid Build Coastguard Worker context, loop, info, trip, graph, block, /*is_min=*/true, lower, needs_taken_test);
1088*795d594fSAndroid Build Coastguard Worker }
1089*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kPolynomial:
1090*795d594fSAndroid Build Coastguard Worker return GenerateLastValuePolynomial(context, loop, info, trip, graph, block, lower);
1091*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kGeometric:
1092*795d594fSAndroid Build Coastguard Worker return GenerateLastValueGeometric(context, loop, info, trip, graph, block, lower);
1093*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kWrapAround:
1094*795d594fSAndroid Build Coastguard Worker return GenerateLastValueWrapAround(context, loop, info, trip, graph, block, lower);
1095*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kPeriodic:
1096*795d594fSAndroid Build Coastguard Worker return GenerateLastValuePeriodic(
1097*795d594fSAndroid Build Coastguard Worker context, loop, info, trip, graph, block, lower, needs_taken_test);
1098*795d594fSAndroid Build Coastguard Worker default:
1099*795d594fSAndroid Build Coastguard Worker return false;
1100*795d594fSAndroid Build Coastguard Worker }
1101*795d594fSAndroid Build Coastguard Worker }
1102*795d594fSAndroid Build Coastguard Worker // Code generation for taken test: generate the code when requested or otherwise analyze
1103*795d594fSAndroid Build Coastguard Worker // if code generation is feasible when taken test is needed.
1104*795d594fSAndroid Build Coastguard Worker if (taken_test != nullptr) {
1105*795d594fSAndroid Build Coastguard Worker return GenerateCode(context,
1106*795d594fSAndroid Build Coastguard Worker loop,
1107*795d594fSAndroid Build Coastguard Worker trip->op_b,
1108*795d594fSAndroid Build Coastguard Worker /*trip=*/ nullptr,
1109*795d594fSAndroid Build Coastguard Worker graph,
1110*795d594fSAndroid Build Coastguard Worker block,
1111*795d594fSAndroid Build Coastguard Worker /*is_min=*/ false,
1112*795d594fSAndroid Build Coastguard Worker taken_test);
1113*795d594fSAndroid Build Coastguard Worker } else if (*needs_taken_test) {
1114*795d594fSAndroid Build Coastguard Worker if (!GenerateCode(context,
1115*795d594fSAndroid Build Coastguard Worker loop,
1116*795d594fSAndroid Build Coastguard Worker trip->op_b,
1117*795d594fSAndroid Build Coastguard Worker /*trip=*/ nullptr,
1118*795d594fSAndroid Build Coastguard Worker /*graph=*/ nullptr,
1119*795d594fSAndroid Build Coastguard Worker /*block=*/ nullptr,
1120*795d594fSAndroid Build Coastguard Worker /*is_min=*/ false,
1121*795d594fSAndroid Build Coastguard Worker /*result=*/ nullptr)) {
1122*795d594fSAndroid Build Coastguard Worker return false;
1123*795d594fSAndroid Build Coastguard Worker }
1124*795d594fSAndroid Build Coastguard Worker }
1125*795d594fSAndroid Build Coastguard Worker // Code generation for lower and upper.
1126*795d594fSAndroid Build Coastguard Worker return
1127*795d594fSAndroid Build Coastguard Worker // Success on lower if invariant (not set), or code can be generated.
1128*795d594fSAndroid Build Coastguard Worker ((info->induction_class == HInductionVarAnalysis::kInvariant) ||
1129*795d594fSAndroid Build Coastguard Worker GenerateCode(context, loop, info, trip, graph, block, /*is_min=*/ true, lower)) &&
1130*795d594fSAndroid Build Coastguard Worker // And success on upper.
1131*795d594fSAndroid Build Coastguard Worker GenerateCode(context, loop, info, trip, graph, block, /*is_min=*/ false, upper);
1132*795d594fSAndroid Build Coastguard Worker }
1133*795d594fSAndroid Build Coastguard Worker
GenerateLastValueLinear(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,HGraph * graph,HBasicBlock * block,bool is_min,HInstruction ** result,bool * needs_taken_test) const1134*795d594fSAndroid Build Coastguard Worker bool InductionVarRange::GenerateLastValueLinear(const HBasicBlock* context,
1135*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
1136*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* info,
1137*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* trip,
1138*795d594fSAndroid Build Coastguard Worker HGraph* graph,
1139*795d594fSAndroid Build Coastguard Worker HBasicBlock* block,
1140*795d594fSAndroid Build Coastguard Worker bool is_min,
1141*795d594fSAndroid Build Coastguard Worker /*out*/ HInstruction** result,
1142*795d594fSAndroid Build Coastguard Worker /*inout*/ bool* needs_taken_test) const {
1143*795d594fSAndroid Build Coastguard Worker DataType::Type type = info->type;
1144*795d594fSAndroid Build Coastguard Worker // Avoid any narrowing linear induction or any type mismatch between the linear induction and the
1145*795d594fSAndroid Build Coastguard Worker // trip count expression.
1146*795d594fSAndroid Build Coastguard Worker if (HInductionVarAnalysis::IsNarrowingLinear(info) || trip->type != type) {
1147*795d594fSAndroid Build Coastguard Worker return false;
1148*795d594fSAndroid Build Coastguard Worker }
1149*795d594fSAndroid Build Coastguard Worker
1150*795d594fSAndroid Build Coastguard Worker // Stride value must be a known constant that fits into int32. The stride will be the `i` in `a *
1151*795d594fSAndroid Build Coastguard Worker // i + b`.
1152*795d594fSAndroid Build Coastguard Worker int64_t stride_value = 0;
1153*795d594fSAndroid Build Coastguard Worker if (!IsConstant(context, loop, info->op_a, kExact, &stride_value) ||
1154*795d594fSAndroid Build Coastguard Worker !CanLongValueFitIntoInt(stride_value)) {
1155*795d594fSAndroid Build Coastguard Worker return false;
1156*795d594fSAndroid Build Coastguard Worker }
1157*795d594fSAndroid Build Coastguard Worker
1158*795d594fSAndroid Build Coastguard Worker // We require the calculation of `a` to not overflow.
1159*795d594fSAndroid Build Coastguard Worker const bool is_min_a = stride_value >= 0 ? is_min : !is_min;
1160*795d594fSAndroid Build Coastguard Worker HInstruction* opa;
1161*795d594fSAndroid Build Coastguard Worker HInstruction* opb;
1162*795d594fSAndroid Build Coastguard Worker if (!GenerateCode(context,
1163*795d594fSAndroid Build Coastguard Worker loop,
1164*795d594fSAndroid Build Coastguard Worker trip,
1165*795d594fSAndroid Build Coastguard Worker trip,
1166*795d594fSAndroid Build Coastguard Worker graph,
1167*795d594fSAndroid Build Coastguard Worker block,
1168*795d594fSAndroid Build Coastguard Worker is_min_a,
1169*795d594fSAndroid Build Coastguard Worker &opa,
1170*795d594fSAndroid Build Coastguard Worker /*allow_potential_overflow=*/false) ||
1171*795d594fSAndroid Build Coastguard Worker !GenerateCode(context, loop, info->op_b, trip, graph, block, is_min, &opb)) {
1172*795d594fSAndroid Build Coastguard Worker return false;
1173*795d594fSAndroid Build Coastguard Worker }
1174*795d594fSAndroid Build Coastguard Worker
1175*795d594fSAndroid Build Coastguard Worker if (graph != nullptr) {
1176*795d594fSAndroid Build Coastguard Worker ArenaAllocator* allocator = graph->GetAllocator();
1177*795d594fSAndroid Build Coastguard Worker HInstruction* oper;
1178*795d594fSAndroid Build Coastguard Worker // Emit instructions for `a * i + b`. These are fine to overflow as they would have overflown
1179*795d594fSAndroid Build Coastguard Worker // also if we had kept the loop.
1180*795d594fSAndroid Build Coastguard Worker if (stride_value == 1) {
1181*795d594fSAndroid Build Coastguard Worker oper = new (allocator) HAdd(type, opa, opb);
1182*795d594fSAndroid Build Coastguard Worker } else if (stride_value == -1) {
1183*795d594fSAndroid Build Coastguard Worker oper = new (graph->GetAllocator()) HSub(type, opb, opa);
1184*795d594fSAndroid Build Coastguard Worker } else {
1185*795d594fSAndroid Build Coastguard Worker HInstruction* mul = new (allocator) HMul(type, graph->GetConstant(type, stride_value), opa);
1186*795d594fSAndroid Build Coastguard Worker oper = new (allocator) HAdd(type, Insert(block, mul), opb);
1187*795d594fSAndroid Build Coastguard Worker }
1188*795d594fSAndroid Build Coastguard Worker *result = Insert(block, oper);
1189*795d594fSAndroid Build Coastguard Worker }
1190*795d594fSAndroid Build Coastguard Worker
1191*795d594fSAndroid Build Coastguard Worker if (*needs_taken_test) {
1192*795d594fSAndroid Build Coastguard Worker if (TryGenerateTakenTest(context, loop, trip->op_b, graph, block, result, opb)) {
1193*795d594fSAndroid Build Coastguard Worker *needs_taken_test = false; // taken care of
1194*795d594fSAndroid Build Coastguard Worker } else {
1195*795d594fSAndroid Build Coastguard Worker return false;
1196*795d594fSAndroid Build Coastguard Worker }
1197*795d594fSAndroid Build Coastguard Worker }
1198*795d594fSAndroid Build Coastguard Worker
1199*795d594fSAndroid Build Coastguard Worker return true;
1200*795d594fSAndroid Build Coastguard Worker }
1201*795d594fSAndroid Build Coastguard Worker
GenerateLastValuePolynomial(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,HGraph * graph,HBasicBlock * block,HInstruction ** result) const1202*795d594fSAndroid Build Coastguard Worker bool InductionVarRange::GenerateLastValuePolynomial(const HBasicBlock* context,
1203*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
1204*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* info,
1205*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* trip,
1206*795d594fSAndroid Build Coastguard Worker HGraph* graph,
1207*795d594fSAndroid Build Coastguard Worker HBasicBlock* block,
1208*795d594fSAndroid Build Coastguard Worker /*out*/HInstruction** result) const {
1209*795d594fSAndroid Build Coastguard Worker DCHECK(info != nullptr);
1210*795d594fSAndroid Build Coastguard Worker DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kPolynomial);
1211*795d594fSAndroid Build Coastguard Worker // Detect known coefficients and trip count (always taken).
1212*795d594fSAndroid Build Coastguard Worker int64_t a = 0;
1213*795d594fSAndroid Build Coastguard Worker int64_t b = 0;
1214*795d594fSAndroid Build Coastguard Worker int64_t m = 0;
1215*795d594fSAndroid Build Coastguard Worker if (IsConstant(context, loop, info->op_a->op_a, kExact, &a) &&
1216*795d594fSAndroid Build Coastguard Worker IsConstant(context, loop, info->op_a->op_b, kExact, &b) &&
1217*795d594fSAndroid Build Coastguard Worker IsConstant(context, loop, trip->op_a, kExact, &m) &&
1218*795d594fSAndroid Build Coastguard Worker m >= 1) {
1219*795d594fSAndroid Build Coastguard Worker // Evaluate bounds on sum_i=0^m-1(a * i + b) + c for known
1220*795d594fSAndroid Build Coastguard Worker // maximum index value m as a * (m * (m-1)) / 2 + b * m + c.
1221*795d594fSAndroid Build Coastguard Worker HInstruction* c = nullptr;
1222*795d594fSAndroid Build Coastguard Worker if (GenerateCode(context,
1223*795d594fSAndroid Build Coastguard Worker loop,
1224*795d594fSAndroid Build Coastguard Worker info->op_b,
1225*795d594fSAndroid Build Coastguard Worker /*trip=*/ nullptr,
1226*795d594fSAndroid Build Coastguard Worker graph,
1227*795d594fSAndroid Build Coastguard Worker block,
1228*795d594fSAndroid Build Coastguard Worker /*is_min=*/ false,
1229*795d594fSAndroid Build Coastguard Worker graph ? &c : nullptr)) {
1230*795d594fSAndroid Build Coastguard Worker if (graph != nullptr) {
1231*795d594fSAndroid Build Coastguard Worker DataType::Type type = info->type;
1232*795d594fSAndroid Build Coastguard Worker int64_t sum = a * ((m * (m - 1)) / 2) + b * m;
1233*795d594fSAndroid Build Coastguard Worker if (type != DataType::Type::kInt64) {
1234*795d594fSAndroid Build Coastguard Worker sum = static_cast<int32_t>(sum); // okay to truncate
1235*795d594fSAndroid Build Coastguard Worker }
1236*795d594fSAndroid Build Coastguard Worker *result =
1237*795d594fSAndroid Build Coastguard Worker Insert(block, new (graph->GetAllocator()) HAdd(type, graph->GetConstant(type, sum), c));
1238*795d594fSAndroid Build Coastguard Worker }
1239*795d594fSAndroid Build Coastguard Worker return true;
1240*795d594fSAndroid Build Coastguard Worker }
1241*795d594fSAndroid Build Coastguard Worker }
1242*795d594fSAndroid Build Coastguard Worker return false;
1243*795d594fSAndroid Build Coastguard Worker }
1244*795d594fSAndroid Build Coastguard Worker
GenerateLastValueGeometric(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,HGraph * graph,HBasicBlock * block,HInstruction ** result) const1245*795d594fSAndroid Build Coastguard Worker bool InductionVarRange::GenerateLastValueGeometric(const HBasicBlock* context,
1246*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
1247*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* info,
1248*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* trip,
1249*795d594fSAndroid Build Coastguard Worker HGraph* graph,
1250*795d594fSAndroid Build Coastguard Worker HBasicBlock* block,
1251*795d594fSAndroid Build Coastguard Worker /*out*/HInstruction** result) const {
1252*795d594fSAndroid Build Coastguard Worker DCHECK(info != nullptr);
1253*795d594fSAndroid Build Coastguard Worker DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kGeometric);
1254*795d594fSAndroid Build Coastguard Worker // Detect known base and trip count (always taken).
1255*795d594fSAndroid Build Coastguard Worker int64_t f = 0;
1256*795d594fSAndroid Build Coastguard Worker int64_t m = 0;
1257*795d594fSAndroid Build Coastguard Worker if (IsInt64AndGet(info->fetch, &f) &&
1258*795d594fSAndroid Build Coastguard Worker f >= 1 &&
1259*795d594fSAndroid Build Coastguard Worker IsConstant(context, loop, trip->op_a, kExact, &m) &&
1260*795d594fSAndroid Build Coastguard Worker m >= 1) {
1261*795d594fSAndroid Build Coastguard Worker HInstruction* opa = nullptr;
1262*795d594fSAndroid Build Coastguard Worker HInstruction* opb = nullptr;
1263*795d594fSAndroid Build Coastguard Worker if (GenerateCode(
1264*795d594fSAndroid Build Coastguard Worker context, loop, info->op_a, /*trip=*/ nullptr, graph, block, /*is_min=*/ false, &opa) &&
1265*795d594fSAndroid Build Coastguard Worker GenerateCode(
1266*795d594fSAndroid Build Coastguard Worker context, loop, info->op_b, /*trip=*/ nullptr, graph, block, /*is_min=*/ false, &opb)) {
1267*795d594fSAndroid Build Coastguard Worker if (graph != nullptr) {
1268*795d594fSAndroid Build Coastguard Worker DataType::Type type = info->type;
1269*795d594fSAndroid Build Coastguard Worker // Compute f ^ m for known maximum index value m.
1270*795d594fSAndroid Build Coastguard Worker bool overflow = false;
1271*795d594fSAndroid Build Coastguard Worker int64_t fpow = IntPow(f, m, &overflow);
1272*795d594fSAndroid Build Coastguard Worker if (info->operation == HInductionVarAnalysis::kDiv) {
1273*795d594fSAndroid Build Coastguard Worker // For division, any overflow truncates to zero.
1274*795d594fSAndroid Build Coastguard Worker if (overflow || (type != DataType::Type::kInt64 && !CanLongValueFitIntoInt(fpow))) {
1275*795d594fSAndroid Build Coastguard Worker fpow = 0;
1276*795d594fSAndroid Build Coastguard Worker }
1277*795d594fSAndroid Build Coastguard Worker } else if (type != DataType::Type::kInt64) {
1278*795d594fSAndroid Build Coastguard Worker // For multiplication, okay to truncate to required precision.
1279*795d594fSAndroid Build Coastguard Worker DCHECK(info->operation == HInductionVarAnalysis::kMul);
1280*795d594fSAndroid Build Coastguard Worker fpow = static_cast<int32_t>(fpow);
1281*795d594fSAndroid Build Coastguard Worker }
1282*795d594fSAndroid Build Coastguard Worker // Generate code.
1283*795d594fSAndroid Build Coastguard Worker if (fpow == 0) {
1284*795d594fSAndroid Build Coastguard Worker // Special case: repeated mul/div always yields zero.
1285*795d594fSAndroid Build Coastguard Worker *result = graph->GetConstant(type, 0);
1286*795d594fSAndroid Build Coastguard Worker } else {
1287*795d594fSAndroid Build Coastguard Worker // Last value: a * f ^ m + b or a * f ^ -m + b.
1288*795d594fSAndroid Build Coastguard Worker HInstruction* e = nullptr;
1289*795d594fSAndroid Build Coastguard Worker ArenaAllocator* allocator = graph->GetAllocator();
1290*795d594fSAndroid Build Coastguard Worker if (info->operation == HInductionVarAnalysis::kMul) {
1291*795d594fSAndroid Build Coastguard Worker e = new (allocator) HMul(type, opa, graph->GetConstant(type, fpow));
1292*795d594fSAndroid Build Coastguard Worker } else {
1293*795d594fSAndroid Build Coastguard Worker e = new (allocator) HDiv(type, opa, graph->GetConstant(type, fpow), kNoDexPc);
1294*795d594fSAndroid Build Coastguard Worker }
1295*795d594fSAndroid Build Coastguard Worker *result = Insert(block, new (allocator) HAdd(type, Insert(block, e), opb));
1296*795d594fSAndroid Build Coastguard Worker }
1297*795d594fSAndroid Build Coastguard Worker }
1298*795d594fSAndroid Build Coastguard Worker return true;
1299*795d594fSAndroid Build Coastguard Worker }
1300*795d594fSAndroid Build Coastguard Worker }
1301*795d594fSAndroid Build Coastguard Worker return false;
1302*795d594fSAndroid Build Coastguard Worker }
1303*795d594fSAndroid Build Coastguard Worker
GenerateLastValueWrapAround(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,HGraph * graph,HBasicBlock * block,HInstruction ** result) const1304*795d594fSAndroid Build Coastguard Worker bool InductionVarRange::GenerateLastValueWrapAround(const HBasicBlock* context,
1305*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
1306*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* info,
1307*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* trip,
1308*795d594fSAndroid Build Coastguard Worker HGraph* graph,
1309*795d594fSAndroid Build Coastguard Worker HBasicBlock* block,
1310*795d594fSAndroid Build Coastguard Worker /*out*/HInstruction** result) const {
1311*795d594fSAndroid Build Coastguard Worker DCHECK(info != nullptr);
1312*795d594fSAndroid Build Coastguard Worker DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kWrapAround);
1313*795d594fSAndroid Build Coastguard Worker // Count depth.
1314*795d594fSAndroid Build Coastguard Worker int32_t depth = 0;
1315*795d594fSAndroid Build Coastguard Worker for (; info->induction_class == HInductionVarAnalysis::kWrapAround;
1316*795d594fSAndroid Build Coastguard Worker info = info->op_b, ++depth) {}
1317*795d594fSAndroid Build Coastguard Worker // Handle wrap(x, wrap(.., y)) if trip count reaches an invariant at end.
1318*795d594fSAndroid Build Coastguard Worker // TODO: generalize, but be careful to adjust the terminal.
1319*795d594fSAndroid Build Coastguard Worker int64_t m = 0;
1320*795d594fSAndroid Build Coastguard Worker if (info->induction_class == HInductionVarAnalysis::kInvariant &&
1321*795d594fSAndroid Build Coastguard Worker IsConstant(context, loop, trip->op_a, kExact, &m) &&
1322*795d594fSAndroid Build Coastguard Worker m >= depth) {
1323*795d594fSAndroid Build Coastguard Worker return GenerateCode(
1324*795d594fSAndroid Build Coastguard Worker context, loop, info, /*trip=*/ nullptr, graph, block, /*is_min=*/ false, result);
1325*795d594fSAndroid Build Coastguard Worker }
1326*795d594fSAndroid Build Coastguard Worker return false;
1327*795d594fSAndroid Build Coastguard Worker }
1328*795d594fSAndroid Build Coastguard Worker
GenerateLastValuePeriodic(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,HGraph * graph,HBasicBlock * block,HInstruction ** result,bool * needs_taken_test) const1329*795d594fSAndroid Build Coastguard Worker bool InductionVarRange::GenerateLastValuePeriodic(const HBasicBlock* context,
1330*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
1331*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* info,
1332*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* trip,
1333*795d594fSAndroid Build Coastguard Worker HGraph* graph,
1334*795d594fSAndroid Build Coastguard Worker HBasicBlock* block,
1335*795d594fSAndroid Build Coastguard Worker /*out*/ HInstruction** result,
1336*795d594fSAndroid Build Coastguard Worker /*inout*/ bool* needs_taken_test) const {
1337*795d594fSAndroid Build Coastguard Worker DCHECK(info != nullptr);
1338*795d594fSAndroid Build Coastguard Worker DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kPeriodic);
1339*795d594fSAndroid Build Coastguard Worker // Count period and detect all-invariants.
1340*795d594fSAndroid Build Coastguard Worker int64_t period = 1;
1341*795d594fSAndroid Build Coastguard Worker bool all_invariants = true;
1342*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* p = info;
1343*795d594fSAndroid Build Coastguard Worker for (; p->induction_class == HInductionVarAnalysis::kPeriodic; p = p->op_b, ++period) {
1344*795d594fSAndroid Build Coastguard Worker DCHECK_EQ(p->op_a->induction_class, HInductionVarAnalysis::kInvariant);
1345*795d594fSAndroid Build Coastguard Worker if (p->op_a->operation != HInductionVarAnalysis::kFetch) {
1346*795d594fSAndroid Build Coastguard Worker all_invariants = false;
1347*795d594fSAndroid Build Coastguard Worker }
1348*795d594fSAndroid Build Coastguard Worker }
1349*795d594fSAndroid Build Coastguard Worker DCHECK_EQ(p->induction_class, HInductionVarAnalysis::kInvariant);
1350*795d594fSAndroid Build Coastguard Worker if (p->operation != HInductionVarAnalysis::kFetch) {
1351*795d594fSAndroid Build Coastguard Worker all_invariants = false;
1352*795d594fSAndroid Build Coastguard Worker }
1353*795d594fSAndroid Build Coastguard Worker // Don't rely on FP arithmetic to be precise, unless the full period
1354*795d594fSAndroid Build Coastguard Worker // consist of pre-computed expressions only.
1355*795d594fSAndroid Build Coastguard Worker if (info->type == DataType::Type::kFloat32 || info->type == DataType::Type::kFloat64) {
1356*795d594fSAndroid Build Coastguard Worker if (!all_invariants) {
1357*795d594fSAndroid Build Coastguard Worker return false;
1358*795d594fSAndroid Build Coastguard Worker }
1359*795d594fSAndroid Build Coastguard Worker }
1360*795d594fSAndroid Build Coastguard Worker // Handle any periodic(x, periodic(.., y)) for known maximum index value m.
1361*795d594fSAndroid Build Coastguard Worker int64_t m = 0;
1362*795d594fSAndroid Build Coastguard Worker if (IsConstant(context, loop, trip->op_a, kExact, &m) && m >= 1) {
1363*795d594fSAndroid Build Coastguard Worker int64_t li = m % period;
1364*795d594fSAndroid Build Coastguard Worker for (int64_t i = 0; i < li; info = info->op_b, i++) {}
1365*795d594fSAndroid Build Coastguard Worker if (info->induction_class == HInductionVarAnalysis::kPeriodic) {
1366*795d594fSAndroid Build Coastguard Worker info = info->op_a;
1367*795d594fSAndroid Build Coastguard Worker }
1368*795d594fSAndroid Build Coastguard Worker return GenerateCode(
1369*795d594fSAndroid Build Coastguard Worker context, loop, info, /*trip=*/ nullptr, graph, block, /*is_min=*/ false, result);
1370*795d594fSAndroid Build Coastguard Worker }
1371*795d594fSAndroid Build Coastguard Worker // Handle periodic(x, y) using even/odd-select on trip count. Enter trip count expression
1372*795d594fSAndroid Build Coastguard Worker // directly to obtain the maximum index value t even if taken test is needed.
1373*795d594fSAndroid Build Coastguard Worker HInstruction* x = nullptr;
1374*795d594fSAndroid Build Coastguard Worker HInstruction* y = nullptr;
1375*795d594fSAndroid Build Coastguard Worker HInstruction* t = nullptr;
1376*795d594fSAndroid Build Coastguard Worker
1377*795d594fSAndroid Build Coastguard Worker // Overflows when the stride is equal to `1` are fine since the periodicity is
1378*795d594fSAndroid Build Coastguard Worker // `2` and the lowest bit is the same. Similar with `-1`.
1379*795d594fSAndroid Build Coastguard Worker auto allow_potential_overflow = [&]() {
1380*795d594fSAndroid Build Coastguard Worker int64_t stride_value = 0;
1381*795d594fSAndroid Build Coastguard Worker return IsConstant(context, loop, trip->op_a->op_b, kExact, &stride_value) &&
1382*795d594fSAndroid Build Coastguard Worker (stride_value == 1 || stride_value == -1);
1383*795d594fSAndroid Build Coastguard Worker };
1384*795d594fSAndroid Build Coastguard Worker
1385*795d594fSAndroid Build Coastguard Worker if (period == 2 &&
1386*795d594fSAndroid Build Coastguard Worker GenerateCode(context,
1387*795d594fSAndroid Build Coastguard Worker loop,
1388*795d594fSAndroid Build Coastguard Worker info->op_a,
1389*795d594fSAndroid Build Coastguard Worker /*trip=*/ nullptr,
1390*795d594fSAndroid Build Coastguard Worker graph,
1391*795d594fSAndroid Build Coastguard Worker block,
1392*795d594fSAndroid Build Coastguard Worker /*is_min=*/ false,
1393*795d594fSAndroid Build Coastguard Worker graph ? &x : nullptr) &&
1394*795d594fSAndroid Build Coastguard Worker GenerateCode(context,
1395*795d594fSAndroid Build Coastguard Worker loop,
1396*795d594fSAndroid Build Coastguard Worker info->op_b,
1397*795d594fSAndroid Build Coastguard Worker /*trip=*/ nullptr,
1398*795d594fSAndroid Build Coastguard Worker graph,
1399*795d594fSAndroid Build Coastguard Worker block,
1400*795d594fSAndroid Build Coastguard Worker /*is_min=*/ false,
1401*795d594fSAndroid Build Coastguard Worker graph ? &y : nullptr) &&
1402*795d594fSAndroid Build Coastguard Worker GenerateCode(context,
1403*795d594fSAndroid Build Coastguard Worker loop,
1404*795d594fSAndroid Build Coastguard Worker trip->op_a,
1405*795d594fSAndroid Build Coastguard Worker /*trip=*/ nullptr,
1406*795d594fSAndroid Build Coastguard Worker graph,
1407*795d594fSAndroid Build Coastguard Worker block,
1408*795d594fSAndroid Build Coastguard Worker /*is_min=*/ false,
1409*795d594fSAndroid Build Coastguard Worker graph ? &t : nullptr,
1410*795d594fSAndroid Build Coastguard Worker allow_potential_overflow())) {
1411*795d594fSAndroid Build Coastguard Worker // During actual code generation (graph != nullptr), generate is_even ? x : y.
1412*795d594fSAndroid Build Coastguard Worker if (graph != nullptr) {
1413*795d594fSAndroid Build Coastguard Worker DataType::Type type = trip->type;
1414*795d594fSAndroid Build Coastguard Worker ArenaAllocator* allocator = graph->GetAllocator();
1415*795d594fSAndroid Build Coastguard Worker HInstruction* msk =
1416*795d594fSAndroid Build Coastguard Worker Insert(block, new (allocator) HAnd(type, t, graph->GetConstant(type, 1)));
1417*795d594fSAndroid Build Coastguard Worker HInstruction* is_even =
1418*795d594fSAndroid Build Coastguard Worker Insert(block, new (allocator) HEqual(msk, graph->GetConstant(type, 0), kNoDexPc));
1419*795d594fSAndroid Build Coastguard Worker *result = Insert(block, new (graph->GetAllocator()) HSelect(is_even, x, y, kNoDexPc));
1420*795d594fSAndroid Build Coastguard Worker }
1421*795d594fSAndroid Build Coastguard Worker
1422*795d594fSAndroid Build Coastguard Worker if (*needs_taken_test) {
1423*795d594fSAndroid Build Coastguard Worker if (TryGenerateTakenTest(context, loop, trip->op_b, graph, block, result, x)) {
1424*795d594fSAndroid Build Coastguard Worker *needs_taken_test = false; // taken care of
1425*795d594fSAndroid Build Coastguard Worker } else {
1426*795d594fSAndroid Build Coastguard Worker return false;
1427*795d594fSAndroid Build Coastguard Worker }
1428*795d594fSAndroid Build Coastguard Worker }
1429*795d594fSAndroid Build Coastguard Worker return true;
1430*795d594fSAndroid Build Coastguard Worker }
1431*795d594fSAndroid Build Coastguard Worker return false;
1432*795d594fSAndroid Build Coastguard Worker }
1433*795d594fSAndroid Build Coastguard Worker
GenerateCode(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HInductionVarAnalysis::InductionInfo * trip,HGraph * graph,HBasicBlock * block,bool is_min,HInstruction ** result,bool allow_potential_overflow) const1434*795d594fSAndroid Build Coastguard Worker bool InductionVarRange::GenerateCode(const HBasicBlock* context,
1435*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
1436*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* info,
1437*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* trip,
1438*795d594fSAndroid Build Coastguard Worker HGraph* graph, // when set, code is generated
1439*795d594fSAndroid Build Coastguard Worker HBasicBlock* block,
1440*795d594fSAndroid Build Coastguard Worker bool is_min,
1441*795d594fSAndroid Build Coastguard Worker /*out*/ HInstruction** result,
1442*795d594fSAndroid Build Coastguard Worker bool allow_potential_overflow) const {
1443*795d594fSAndroid Build Coastguard Worker if (info != nullptr) {
1444*795d594fSAndroid Build Coastguard Worker // If during codegen, the result is not needed (nullptr), simply return success.
1445*795d594fSAndroid Build Coastguard Worker if (graph != nullptr && result == nullptr) {
1446*795d594fSAndroid Build Coastguard Worker return true;
1447*795d594fSAndroid Build Coastguard Worker }
1448*795d594fSAndroid Build Coastguard Worker // Handle current operation.
1449*795d594fSAndroid Build Coastguard Worker DataType::Type type = info->type;
1450*795d594fSAndroid Build Coastguard Worker HInstruction* opa = nullptr;
1451*795d594fSAndroid Build Coastguard Worker HInstruction* opb = nullptr;
1452*795d594fSAndroid Build Coastguard Worker switch (info->induction_class) {
1453*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kInvariant:
1454*795d594fSAndroid Build Coastguard Worker // Invariants (note that since invariants only have other invariants as
1455*795d594fSAndroid Build Coastguard Worker // sub expressions, viz. no induction, there is no need to adjust is_min).
1456*795d594fSAndroid Build Coastguard Worker switch (info->operation) {
1457*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kAdd:
1458*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kSub:
1459*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kMul:
1460*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kDiv:
1461*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kRem:
1462*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kXor:
1463*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kLT:
1464*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kLE:
1465*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kGT:
1466*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kGE:
1467*795d594fSAndroid Build Coastguard Worker if (GenerateCode(context,
1468*795d594fSAndroid Build Coastguard Worker loop,
1469*795d594fSAndroid Build Coastguard Worker info->op_a,
1470*795d594fSAndroid Build Coastguard Worker trip,
1471*795d594fSAndroid Build Coastguard Worker graph,
1472*795d594fSAndroid Build Coastguard Worker block,
1473*795d594fSAndroid Build Coastguard Worker is_min,
1474*795d594fSAndroid Build Coastguard Worker &opa,
1475*795d594fSAndroid Build Coastguard Worker allow_potential_overflow) &&
1476*795d594fSAndroid Build Coastguard Worker GenerateCode(context,
1477*795d594fSAndroid Build Coastguard Worker loop,
1478*795d594fSAndroid Build Coastguard Worker info->op_b,
1479*795d594fSAndroid Build Coastguard Worker trip,
1480*795d594fSAndroid Build Coastguard Worker graph,
1481*795d594fSAndroid Build Coastguard Worker block,
1482*795d594fSAndroid Build Coastguard Worker is_min,
1483*795d594fSAndroid Build Coastguard Worker &opb,
1484*795d594fSAndroid Build Coastguard Worker allow_potential_overflow)) {
1485*795d594fSAndroid Build Coastguard Worker // Check for potentially invalid operations.
1486*795d594fSAndroid Build Coastguard Worker if (!allow_potential_overflow) {
1487*795d594fSAndroid Build Coastguard Worker switch (info->operation) {
1488*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kAdd:
1489*795d594fSAndroid Build Coastguard Worker return TryGenerateAddWithoutOverflow(
1490*795d594fSAndroid Build Coastguard Worker context, loop, info, graph, opa, opb, result);
1491*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kSub:
1492*795d594fSAndroid Build Coastguard Worker return TryGenerateSubWithoutOverflow(context, loop, info, graph, opa, result);
1493*795d594fSAndroid Build Coastguard Worker default:
1494*795d594fSAndroid Build Coastguard Worker // The rest of the operations are not relevant in the cases where
1495*795d594fSAndroid Build Coastguard Worker // `allow_potential_overflow` is false. Fall through to the allowed overflow
1496*795d594fSAndroid Build Coastguard Worker // case.
1497*795d594fSAndroid Build Coastguard Worker break;
1498*795d594fSAndroid Build Coastguard Worker }
1499*795d594fSAndroid Build Coastguard Worker }
1500*795d594fSAndroid Build Coastguard Worker
1501*795d594fSAndroid Build Coastguard Worker // Overflows here are accepted.
1502*795d594fSAndroid Build Coastguard Worker if (graph != nullptr) {
1503*795d594fSAndroid Build Coastguard Worker HInstruction* operation = nullptr;
1504*795d594fSAndroid Build Coastguard Worker switch (info->operation) {
1505*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kAdd:
1506*795d594fSAndroid Build Coastguard Worker operation = new (graph->GetAllocator()) HAdd(type, opa, opb); break;
1507*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kSub:
1508*795d594fSAndroid Build Coastguard Worker operation = new (graph->GetAllocator()) HSub(type, opa, opb); break;
1509*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kMul:
1510*795d594fSAndroid Build Coastguard Worker operation = new (graph->GetAllocator()) HMul(type, opa, opb, kNoDexPc); break;
1511*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kDiv:
1512*795d594fSAndroid Build Coastguard Worker operation = new (graph->GetAllocator()) HDiv(type, opa, opb, kNoDexPc); break;
1513*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kRem:
1514*795d594fSAndroid Build Coastguard Worker operation = new (graph->GetAllocator()) HRem(type, opa, opb, kNoDexPc); break;
1515*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kXor:
1516*795d594fSAndroid Build Coastguard Worker operation = new (graph->GetAllocator()) HXor(type, opa, opb); break;
1517*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kLT:
1518*795d594fSAndroid Build Coastguard Worker operation = new (graph->GetAllocator()) HLessThan(opa, opb); break;
1519*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kLE:
1520*795d594fSAndroid Build Coastguard Worker operation = new (graph->GetAllocator()) HLessThanOrEqual(opa, opb); break;
1521*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kGT:
1522*795d594fSAndroid Build Coastguard Worker operation = new (graph->GetAllocator()) HGreaterThan(opa, opb); break;
1523*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kGE:
1524*795d594fSAndroid Build Coastguard Worker operation = new (graph->GetAllocator()) HGreaterThanOrEqual(opa, opb); break;
1525*795d594fSAndroid Build Coastguard Worker default:
1526*795d594fSAndroid Build Coastguard Worker LOG(FATAL) << "unknown operation";
1527*795d594fSAndroid Build Coastguard Worker }
1528*795d594fSAndroid Build Coastguard Worker *result = Insert(block, operation);
1529*795d594fSAndroid Build Coastguard Worker }
1530*795d594fSAndroid Build Coastguard Worker return true;
1531*795d594fSAndroid Build Coastguard Worker }
1532*795d594fSAndroid Build Coastguard Worker break;
1533*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kNeg:
1534*795d594fSAndroid Build Coastguard Worker if (GenerateCode(context,
1535*795d594fSAndroid Build Coastguard Worker loop,
1536*795d594fSAndroid Build Coastguard Worker info->op_b,
1537*795d594fSAndroid Build Coastguard Worker trip,
1538*795d594fSAndroid Build Coastguard Worker graph,
1539*795d594fSAndroid Build Coastguard Worker block,
1540*795d594fSAndroid Build Coastguard Worker !is_min,
1541*795d594fSAndroid Build Coastguard Worker &opb,
1542*795d594fSAndroid Build Coastguard Worker allow_potential_overflow)) {
1543*795d594fSAndroid Build Coastguard Worker if (graph != nullptr) {
1544*795d594fSAndroid Build Coastguard Worker *result = Insert(block, new (graph->GetAllocator()) HNeg(type, opb));
1545*795d594fSAndroid Build Coastguard Worker }
1546*795d594fSAndroid Build Coastguard Worker return true;
1547*795d594fSAndroid Build Coastguard Worker }
1548*795d594fSAndroid Build Coastguard Worker break;
1549*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kFetch:
1550*795d594fSAndroid Build Coastguard Worker if (graph != nullptr) {
1551*795d594fSAndroid Build Coastguard Worker *result = info->fetch; // already in HIR
1552*795d594fSAndroid Build Coastguard Worker }
1553*795d594fSAndroid Build Coastguard Worker return true;
1554*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kTripCountInLoop:
1555*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kTripCountInLoopUnsafe:
1556*795d594fSAndroid Build Coastguard Worker if (UseFullTripCount(context, loop, is_min)) {
1557*795d594fSAndroid Build Coastguard Worker // Generate the full trip count (do not subtract 1 as we do in loop body).
1558*795d594fSAndroid Build Coastguard Worker return GenerateCode(context,
1559*795d594fSAndroid Build Coastguard Worker loop,
1560*795d594fSAndroid Build Coastguard Worker info->op_a,
1561*795d594fSAndroid Build Coastguard Worker trip,
1562*795d594fSAndroid Build Coastguard Worker graph,
1563*795d594fSAndroid Build Coastguard Worker block,
1564*795d594fSAndroid Build Coastguard Worker is_min,
1565*795d594fSAndroid Build Coastguard Worker result,
1566*795d594fSAndroid Build Coastguard Worker allow_potential_overflow);
1567*795d594fSAndroid Build Coastguard Worker }
1568*795d594fSAndroid Build Coastguard Worker FALLTHROUGH_INTENDED;
1569*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kTripCountInBody:
1570*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kTripCountInBodyUnsafe:
1571*795d594fSAndroid Build Coastguard Worker if (is_min) {
1572*795d594fSAndroid Build Coastguard Worker if (graph != nullptr) {
1573*795d594fSAndroid Build Coastguard Worker *result = graph->GetConstant(type, 0);
1574*795d594fSAndroid Build Coastguard Worker }
1575*795d594fSAndroid Build Coastguard Worker return true;
1576*795d594fSAndroid Build Coastguard Worker } else if (IsContextInBody(context, loop) ||
1577*795d594fSAndroid Build Coastguard Worker (context == loop->GetHeader() && !allow_potential_overflow)) {
1578*795d594fSAndroid Build Coastguard Worker if (GenerateCode(context,
1579*795d594fSAndroid Build Coastguard Worker loop,
1580*795d594fSAndroid Build Coastguard Worker info->op_a,
1581*795d594fSAndroid Build Coastguard Worker trip,
1582*795d594fSAndroid Build Coastguard Worker graph,
1583*795d594fSAndroid Build Coastguard Worker block,
1584*795d594fSAndroid Build Coastguard Worker is_min,
1585*795d594fSAndroid Build Coastguard Worker &opb,
1586*795d594fSAndroid Build Coastguard Worker allow_potential_overflow)) {
1587*795d594fSAndroid Build Coastguard Worker if (graph != nullptr) {
1588*795d594fSAndroid Build Coastguard Worker if (IsContextInBody(context, loop)) {
1589*795d594fSAndroid Build Coastguard Worker ArenaAllocator* allocator = graph->GetAllocator();
1590*795d594fSAndroid Build Coastguard Worker *result =
1591*795d594fSAndroid Build Coastguard Worker Insert(block, new (allocator) HSub(type, opb, graph->GetConstant(type, 1)));
1592*795d594fSAndroid Build Coastguard Worker } else {
1593*795d594fSAndroid Build Coastguard Worker // We want to generate the full trip count since we want the last value. This
1594*795d594fSAndroid Build Coastguard Worker // will be combined with an `is_taken` test so we don't want to subtract one.
1595*795d594fSAndroid Build Coastguard Worker DCHECK(context == loop->GetHeader());
1596*795d594fSAndroid Build Coastguard Worker // TODO(solanes): Remove the !allow_potential_overflow restriction and allow
1597*795d594fSAndroid Build Coastguard Worker // other parts e.g. BCE to take advantage of this.
1598*795d594fSAndroid Build Coastguard Worker DCHECK(!allow_potential_overflow);
1599*795d594fSAndroid Build Coastguard Worker *result = opb;
1600*795d594fSAndroid Build Coastguard Worker }
1601*795d594fSAndroid Build Coastguard Worker }
1602*795d594fSAndroid Build Coastguard Worker return true;
1603*795d594fSAndroid Build Coastguard Worker }
1604*795d594fSAndroid Build Coastguard Worker }
1605*795d594fSAndroid Build Coastguard Worker break;
1606*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kNop:
1607*795d594fSAndroid Build Coastguard Worker LOG(FATAL) << "unexpected invariant nop";
1608*795d594fSAndroid Build Coastguard Worker } // switch invariant operation
1609*795d594fSAndroid Build Coastguard Worker break;
1610*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kLinear: {
1611*795d594fSAndroid Build Coastguard Worker // Linear induction a * i + b, for normalized 0 <= i < TC. For ranges, this should
1612*795d594fSAndroid Build Coastguard Worker // be restricted to a unit stride to avoid arithmetic wrap-around situations that
1613*795d594fSAndroid Build Coastguard Worker // are harder to guard against. For a last value, requesting min/max based on any
1614*795d594fSAndroid Build Coastguard Worker // known stride yields right value. Always avoid any narrowing linear induction or
1615*795d594fSAndroid Build Coastguard Worker // any type mismatch between the linear induction and the trip count expression.
1616*795d594fSAndroid Build Coastguard Worker // TODO: careful runtime type conversions could generalize this latter restriction.
1617*795d594fSAndroid Build Coastguard Worker if (!HInductionVarAnalysis::IsNarrowingLinear(info) && trip->type == type) {
1618*795d594fSAndroid Build Coastguard Worker int64_t stride_value = 0;
1619*795d594fSAndroid Build Coastguard Worker if (IsConstant(context, loop, info->op_a, kExact, &stride_value) &&
1620*795d594fSAndroid Build Coastguard Worker CanLongValueFitIntoInt(stride_value)) {
1621*795d594fSAndroid Build Coastguard Worker const bool is_min_a = stride_value >= 0 ? is_min : !is_min;
1622*795d594fSAndroid Build Coastguard Worker if (GenerateCode(context,
1623*795d594fSAndroid Build Coastguard Worker loop,
1624*795d594fSAndroid Build Coastguard Worker trip,
1625*795d594fSAndroid Build Coastguard Worker trip,
1626*795d594fSAndroid Build Coastguard Worker graph,
1627*795d594fSAndroid Build Coastguard Worker block,
1628*795d594fSAndroid Build Coastguard Worker is_min_a,
1629*795d594fSAndroid Build Coastguard Worker &opa,
1630*795d594fSAndroid Build Coastguard Worker allow_potential_overflow) &&
1631*795d594fSAndroid Build Coastguard Worker GenerateCode(context,
1632*795d594fSAndroid Build Coastguard Worker loop,
1633*795d594fSAndroid Build Coastguard Worker info->op_b,
1634*795d594fSAndroid Build Coastguard Worker trip,
1635*795d594fSAndroid Build Coastguard Worker graph,
1636*795d594fSAndroid Build Coastguard Worker block,
1637*795d594fSAndroid Build Coastguard Worker is_min,
1638*795d594fSAndroid Build Coastguard Worker &opb,
1639*795d594fSAndroid Build Coastguard Worker allow_potential_overflow)) {
1640*795d594fSAndroid Build Coastguard Worker if (graph != nullptr) {
1641*795d594fSAndroid Build Coastguard Worker ArenaAllocator* allocator = graph->GetAllocator();
1642*795d594fSAndroid Build Coastguard Worker HInstruction* oper;
1643*795d594fSAndroid Build Coastguard Worker if (stride_value == 1) {
1644*795d594fSAndroid Build Coastguard Worker oper = new (allocator) HAdd(type, opa, opb);
1645*795d594fSAndroid Build Coastguard Worker } else if (stride_value == -1) {
1646*795d594fSAndroid Build Coastguard Worker oper = new (graph->GetAllocator()) HSub(type, opb, opa);
1647*795d594fSAndroid Build Coastguard Worker } else {
1648*795d594fSAndroid Build Coastguard Worker HInstruction* mul =
1649*795d594fSAndroid Build Coastguard Worker new (allocator) HMul(type, graph->GetConstant(type, stride_value), opa);
1650*795d594fSAndroid Build Coastguard Worker oper = new (allocator) HAdd(type, Insert(block, mul), opb);
1651*795d594fSAndroid Build Coastguard Worker }
1652*795d594fSAndroid Build Coastguard Worker *result = Insert(block, oper);
1653*795d594fSAndroid Build Coastguard Worker }
1654*795d594fSAndroid Build Coastguard Worker return true;
1655*795d594fSAndroid Build Coastguard Worker }
1656*795d594fSAndroid Build Coastguard Worker }
1657*795d594fSAndroid Build Coastguard Worker }
1658*795d594fSAndroid Build Coastguard Worker break;
1659*795d594fSAndroid Build Coastguard Worker }
1660*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kPolynomial:
1661*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kGeometric:
1662*795d594fSAndroid Build Coastguard Worker break;
1663*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kWrapAround:
1664*795d594fSAndroid Build Coastguard Worker case HInductionVarAnalysis::kPeriodic: {
1665*795d594fSAndroid Build Coastguard Worker // Wrap-around and periodic inductions are restricted to constants only, so that extreme
1666*795d594fSAndroid Build Coastguard Worker // values are easy to test at runtime without complications of arithmetic wrap-around.
1667*795d594fSAndroid Build Coastguard Worker Value extreme = GetVal(context, loop, info, trip, is_min);
1668*795d594fSAndroid Build Coastguard Worker if (IsConstantValue(extreme)) {
1669*795d594fSAndroid Build Coastguard Worker if (graph != nullptr) {
1670*795d594fSAndroid Build Coastguard Worker *result = graph->GetConstant(type, extreme.b_constant);
1671*795d594fSAndroid Build Coastguard Worker }
1672*795d594fSAndroid Build Coastguard Worker return true;
1673*795d594fSAndroid Build Coastguard Worker }
1674*795d594fSAndroid Build Coastguard Worker break;
1675*795d594fSAndroid Build Coastguard Worker }
1676*795d594fSAndroid Build Coastguard Worker } // switch induction class
1677*795d594fSAndroid Build Coastguard Worker }
1678*795d594fSAndroid Build Coastguard Worker return false;
1679*795d594fSAndroid Build Coastguard Worker }
1680*795d594fSAndroid Build Coastguard Worker
TryGenerateAddWithoutOverflow(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HGraph * graph,HInstruction * opa,HInstruction * opb,HInstruction ** result) const1681*795d594fSAndroid Build Coastguard Worker bool InductionVarRange::TryGenerateAddWithoutOverflow(const HBasicBlock* context,
1682*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
1683*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* info,
1684*795d594fSAndroid Build Coastguard Worker HGraph* graph,
1685*795d594fSAndroid Build Coastguard Worker /*in*/ HInstruction* opa,
1686*795d594fSAndroid Build Coastguard Worker /*in*/ HInstruction* opb,
1687*795d594fSAndroid Build Coastguard Worker /*out*/ HInstruction** result) const {
1688*795d594fSAndroid Build Coastguard Worker // Calculate `a + b` making sure we can't overflow.
1689*795d594fSAndroid Build Coastguard Worker int64_t val_a;
1690*795d594fSAndroid Build Coastguard Worker const bool a_is_const = IsConstant(context, loop, info->op_a, kExact, &val_a);
1691*795d594fSAndroid Build Coastguard Worker int64_t val_b;
1692*795d594fSAndroid Build Coastguard Worker const bool b_is_const = IsConstant(context, loop, info->op_b, kExact, &val_b);
1693*795d594fSAndroid Build Coastguard Worker if (a_is_const && b_is_const) {
1694*795d594fSAndroid Build Coastguard Worker // Calculate `a + b` and use that. Note that even when the values are known,
1695*795d594fSAndroid Build Coastguard Worker // their addition can still overflow.
1696*795d594fSAndroid Build Coastguard Worker Value add_val = AddValue(Value(val_a), Value(val_b));
1697*795d594fSAndroid Build Coastguard Worker if (add_val.is_known) {
1698*795d594fSAndroid Build Coastguard Worker DCHECK(IsConstantValue(add_val));
1699*795d594fSAndroid Build Coastguard Worker // Known value not overflowing.
1700*795d594fSAndroid Build Coastguard Worker if (graph != nullptr) {
1701*795d594fSAndroid Build Coastguard Worker *result = graph->GetConstant(info->type, add_val.b_constant);
1702*795d594fSAndroid Build Coastguard Worker }
1703*795d594fSAndroid Build Coastguard Worker return true;
1704*795d594fSAndroid Build Coastguard Worker }
1705*795d594fSAndroid Build Coastguard Worker }
1706*795d594fSAndroid Build Coastguard Worker
1707*795d594fSAndroid Build Coastguard Worker // When `a` is `0`, we can just use `b`.
1708*795d594fSAndroid Build Coastguard Worker if (a_is_const && val_a == 0) {
1709*795d594fSAndroid Build Coastguard Worker if (graph != nullptr) {
1710*795d594fSAndroid Build Coastguard Worker *result = opb;
1711*795d594fSAndroid Build Coastguard Worker }
1712*795d594fSAndroid Build Coastguard Worker return true;
1713*795d594fSAndroid Build Coastguard Worker }
1714*795d594fSAndroid Build Coastguard Worker
1715*795d594fSAndroid Build Coastguard Worker if (b_is_const && val_b == 0) {
1716*795d594fSAndroid Build Coastguard Worker if (graph != nullptr) {
1717*795d594fSAndroid Build Coastguard Worker *result = opa;
1718*795d594fSAndroid Build Coastguard Worker }
1719*795d594fSAndroid Build Coastguard Worker return true;
1720*795d594fSAndroid Build Coastguard Worker }
1721*795d594fSAndroid Build Coastguard Worker
1722*795d594fSAndroid Build Coastguard Worker // Couldn't safely calculate the addition.
1723*795d594fSAndroid Build Coastguard Worker return false;
1724*795d594fSAndroid Build Coastguard Worker }
1725*795d594fSAndroid Build Coastguard Worker
TryGenerateSubWithoutOverflow(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HGraph * graph,HInstruction * opa,HInstruction ** result) const1726*795d594fSAndroid Build Coastguard Worker bool InductionVarRange::TryGenerateSubWithoutOverflow(const HBasicBlock* context,
1727*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
1728*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* info,
1729*795d594fSAndroid Build Coastguard Worker HGraph* graph,
1730*795d594fSAndroid Build Coastguard Worker /*in*/ HInstruction* opa,
1731*795d594fSAndroid Build Coastguard Worker /*out*/ HInstruction** result) const {
1732*795d594fSAndroid Build Coastguard Worker // Calculate `a - b` making sure we can't overflow.
1733*795d594fSAndroid Build Coastguard Worker int64_t val_b;
1734*795d594fSAndroid Build Coastguard Worker if (!IsConstant(context, loop, info->op_b, kExact, &val_b)) {
1735*795d594fSAndroid Build Coastguard Worker // If b is unknown, a - b can potentially overflow for any value of a since b
1736*795d594fSAndroid Build Coastguard Worker // can be Integer.MIN_VALUE.
1737*795d594fSAndroid Build Coastguard Worker return false;
1738*795d594fSAndroid Build Coastguard Worker }
1739*795d594fSAndroid Build Coastguard Worker
1740*795d594fSAndroid Build Coastguard Worker int64_t val_a;
1741*795d594fSAndroid Build Coastguard Worker if (IsConstant(context, loop, info->op_a, kExact, &val_a)) {
1742*795d594fSAndroid Build Coastguard Worker // Calculate `a - b` and use that. Note that even when the values are known,
1743*795d594fSAndroid Build Coastguard Worker // their subtraction can still overflow.
1744*795d594fSAndroid Build Coastguard Worker Value sub_val = SubValue(Value(val_a), Value(val_b));
1745*795d594fSAndroid Build Coastguard Worker if (sub_val.is_known) {
1746*795d594fSAndroid Build Coastguard Worker DCHECK(IsConstantValue(sub_val));
1747*795d594fSAndroid Build Coastguard Worker // Known value not overflowing.
1748*795d594fSAndroid Build Coastguard Worker if (graph != nullptr) {
1749*795d594fSAndroid Build Coastguard Worker *result = graph->GetConstant(info->type, sub_val.b_constant);
1750*795d594fSAndroid Build Coastguard Worker }
1751*795d594fSAndroid Build Coastguard Worker return true;
1752*795d594fSAndroid Build Coastguard Worker }
1753*795d594fSAndroid Build Coastguard Worker }
1754*795d594fSAndroid Build Coastguard Worker
1755*795d594fSAndroid Build Coastguard Worker // When `b` is `0`, we can just use `a`.
1756*795d594fSAndroid Build Coastguard Worker if (val_b == 0) {
1757*795d594fSAndroid Build Coastguard Worker if (graph != nullptr) {
1758*795d594fSAndroid Build Coastguard Worker *result = opa;
1759*795d594fSAndroid Build Coastguard Worker }
1760*795d594fSAndroid Build Coastguard Worker return true;
1761*795d594fSAndroid Build Coastguard Worker }
1762*795d594fSAndroid Build Coastguard Worker
1763*795d594fSAndroid Build Coastguard Worker // Couldn't safely calculate the subtraction.
1764*795d594fSAndroid Build Coastguard Worker return false;
1765*795d594fSAndroid Build Coastguard Worker }
1766*795d594fSAndroid Build Coastguard Worker
TryGenerateTakenTest(const HBasicBlock * context,const HLoopInformation * loop,HInductionVarAnalysis::InductionInfo * info,HGraph * graph,HBasicBlock * block,HInstruction ** result,HInstruction * not_taken_result) const1767*795d594fSAndroid Build Coastguard Worker bool InductionVarRange::TryGenerateTakenTest(const HBasicBlock* context,
1768*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
1769*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* info,
1770*795d594fSAndroid Build Coastguard Worker HGraph* graph,
1771*795d594fSAndroid Build Coastguard Worker HBasicBlock* block,
1772*795d594fSAndroid Build Coastguard Worker /*inout*/ HInstruction** result,
1773*795d594fSAndroid Build Coastguard Worker /*inout*/ HInstruction* not_taken_result) const {
1774*795d594fSAndroid Build Coastguard Worker HInstruction* is_taken = nullptr;
1775*795d594fSAndroid Build Coastguard Worker if (GenerateCode(context,
1776*795d594fSAndroid Build Coastguard Worker loop,
1777*795d594fSAndroid Build Coastguard Worker info,
1778*795d594fSAndroid Build Coastguard Worker /*trip=*/nullptr,
1779*795d594fSAndroid Build Coastguard Worker graph,
1780*795d594fSAndroid Build Coastguard Worker block,
1781*795d594fSAndroid Build Coastguard Worker /*is_min=*/false,
1782*795d594fSAndroid Build Coastguard Worker graph != nullptr ? &is_taken : nullptr)) {
1783*795d594fSAndroid Build Coastguard Worker if (graph != nullptr) {
1784*795d594fSAndroid Build Coastguard Worker ArenaAllocator* allocator = graph->GetAllocator();
1785*795d594fSAndroid Build Coastguard Worker *result =
1786*795d594fSAndroid Build Coastguard Worker Insert(block, new (allocator) HSelect(is_taken, *result, not_taken_result, kNoDexPc));
1787*795d594fSAndroid Build Coastguard Worker }
1788*795d594fSAndroid Build Coastguard Worker return true;
1789*795d594fSAndroid Build Coastguard Worker } else {
1790*795d594fSAndroid Build Coastguard Worker return false;
1791*795d594fSAndroid Build Coastguard Worker }
1792*795d594fSAndroid Build Coastguard Worker }
1793*795d594fSAndroid Build Coastguard Worker
ReplaceInduction(HInductionVarAnalysis::InductionInfo * info,HInstruction * fetch,HInstruction * replacement)1794*795d594fSAndroid Build Coastguard Worker void InductionVarRange::ReplaceInduction(HInductionVarAnalysis::InductionInfo* info,
1795*795d594fSAndroid Build Coastguard Worker HInstruction* fetch,
1796*795d594fSAndroid Build Coastguard Worker HInstruction* replacement) {
1797*795d594fSAndroid Build Coastguard Worker if (info != nullptr) {
1798*795d594fSAndroid Build Coastguard Worker if (info->induction_class == HInductionVarAnalysis::kInvariant &&
1799*795d594fSAndroid Build Coastguard Worker info->operation == HInductionVarAnalysis::kFetch &&
1800*795d594fSAndroid Build Coastguard Worker info->fetch == fetch) {
1801*795d594fSAndroid Build Coastguard Worker info->fetch = replacement;
1802*795d594fSAndroid Build Coastguard Worker }
1803*795d594fSAndroid Build Coastguard Worker ReplaceInduction(info->op_a, fetch, replacement);
1804*795d594fSAndroid Build Coastguard Worker ReplaceInduction(info->op_b, fetch, replacement);
1805*795d594fSAndroid Build Coastguard Worker }
1806*795d594fSAndroid Build Coastguard Worker }
1807*795d594fSAndroid Build Coastguard Worker
1808*795d594fSAndroid Build Coastguard Worker } // namespace art
1809