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_analysis.h"
18*795d594fSAndroid Build Coastguard Worker
19*795d594fSAndroid Build Coastguard Worker #include "base/scoped_arena_containers.h"
20*795d594fSAndroid Build Coastguard Worker #include "induction_var_range.h"
21*795d594fSAndroid Build Coastguard Worker
22*795d594fSAndroid Build Coastguard Worker namespace art HIDDEN {
23*795d594fSAndroid Build Coastguard Worker
24*795d594fSAndroid Build Coastguard Worker /**
25*795d594fSAndroid Build Coastguard Worker * Returns true if the from/to types denote a narrowing, integral conversion (precision loss).
26*795d594fSAndroid Build Coastguard Worker */
IsNarrowingIntegralConversion(DataType::Type from,DataType::Type to)27*795d594fSAndroid Build Coastguard Worker static bool IsNarrowingIntegralConversion(DataType::Type from, DataType::Type to) {
28*795d594fSAndroid Build Coastguard Worker switch (from) {
29*795d594fSAndroid Build Coastguard Worker case DataType::Type::kInt64:
30*795d594fSAndroid Build Coastguard Worker return to == DataType::Type::kUint8 ||
31*795d594fSAndroid Build Coastguard Worker to == DataType::Type::kInt8 ||
32*795d594fSAndroid Build Coastguard Worker to == DataType::Type::kUint16 ||
33*795d594fSAndroid Build Coastguard Worker to == DataType::Type::kInt16 ||
34*795d594fSAndroid Build Coastguard Worker to == DataType::Type::kInt32;
35*795d594fSAndroid Build Coastguard Worker case DataType::Type::kInt32:
36*795d594fSAndroid Build Coastguard Worker return to == DataType::Type::kUint8 ||
37*795d594fSAndroid Build Coastguard Worker to == DataType::Type::kInt8 ||
38*795d594fSAndroid Build Coastguard Worker to == DataType::Type::kUint16 ||
39*795d594fSAndroid Build Coastguard Worker to == DataType::Type::kInt16;
40*795d594fSAndroid Build Coastguard Worker case DataType::Type::kUint16:
41*795d594fSAndroid Build Coastguard Worker case DataType::Type::kInt16:
42*795d594fSAndroid Build Coastguard Worker return to == DataType::Type::kUint8 || to == DataType::Type::kInt8;
43*795d594fSAndroid Build Coastguard Worker default:
44*795d594fSAndroid Build Coastguard Worker return false;
45*795d594fSAndroid Build Coastguard Worker }
46*795d594fSAndroid Build Coastguard Worker }
47*795d594fSAndroid Build Coastguard Worker
48*795d594fSAndroid Build Coastguard Worker /**
49*795d594fSAndroid Build Coastguard Worker * Returns result of implicit widening type conversion done in HIR.
50*795d594fSAndroid Build Coastguard Worker */
ImplicitConversion(DataType::Type type)51*795d594fSAndroid Build Coastguard Worker static DataType::Type ImplicitConversion(DataType::Type type) {
52*795d594fSAndroid Build Coastguard Worker switch (type) {
53*795d594fSAndroid Build Coastguard Worker case DataType::Type::kBool:
54*795d594fSAndroid Build Coastguard Worker case DataType::Type::kUint8:
55*795d594fSAndroid Build Coastguard Worker case DataType::Type::kInt8:
56*795d594fSAndroid Build Coastguard Worker case DataType::Type::kUint16:
57*795d594fSAndroid Build Coastguard Worker case DataType::Type::kInt16:
58*795d594fSAndroid Build Coastguard Worker return DataType::Type::kInt32;
59*795d594fSAndroid Build Coastguard Worker default:
60*795d594fSAndroid Build Coastguard Worker return type;
61*795d594fSAndroid Build Coastguard Worker }
62*795d594fSAndroid Build Coastguard Worker }
63*795d594fSAndroid Build Coastguard Worker
64*795d594fSAndroid Build Coastguard Worker /**
65*795d594fSAndroid Build Coastguard Worker * Returns true if loop is guarded by "a cmp b" on entry.
66*795d594fSAndroid Build Coastguard Worker */
IsGuardedBy(const HLoopInformation * loop,IfCondition cmp,HInstruction * a,HInstruction * b)67*795d594fSAndroid Build Coastguard Worker static bool IsGuardedBy(const HLoopInformation* loop,
68*795d594fSAndroid Build Coastguard Worker IfCondition cmp,
69*795d594fSAndroid Build Coastguard Worker HInstruction* a,
70*795d594fSAndroid Build Coastguard Worker HInstruction* b) {
71*795d594fSAndroid Build Coastguard Worker // Chase back through straightline code to the first potential
72*795d594fSAndroid Build Coastguard Worker // block that has a control dependence.
73*795d594fSAndroid Build Coastguard Worker // guard: if (x) bypass
74*795d594fSAndroid Build Coastguard Worker // |
75*795d594fSAndroid Build Coastguard Worker // entry: straightline code
76*795d594fSAndroid Build Coastguard Worker // |
77*795d594fSAndroid Build Coastguard Worker // preheader
78*795d594fSAndroid Build Coastguard Worker // |
79*795d594fSAndroid Build Coastguard Worker // header
80*795d594fSAndroid Build Coastguard Worker HBasicBlock* guard = loop->GetPreHeader();
81*795d594fSAndroid Build Coastguard Worker HBasicBlock* entry = loop->GetHeader();
82*795d594fSAndroid Build Coastguard Worker while (guard->GetPredecessors().size() == 1 &&
83*795d594fSAndroid Build Coastguard Worker guard->GetSuccessors().size() == 1) {
84*795d594fSAndroid Build Coastguard Worker entry = guard;
85*795d594fSAndroid Build Coastguard Worker guard = guard->GetSinglePredecessor();
86*795d594fSAndroid Build Coastguard Worker }
87*795d594fSAndroid Build Coastguard Worker // Find guard.
88*795d594fSAndroid Build Coastguard Worker HInstruction* control = guard->GetLastInstruction();
89*795d594fSAndroid Build Coastguard Worker if (!control->IsIf()) {
90*795d594fSAndroid Build Coastguard Worker return false;
91*795d594fSAndroid Build Coastguard Worker }
92*795d594fSAndroid Build Coastguard Worker HIf* ifs = control->AsIf();
93*795d594fSAndroid Build Coastguard Worker HInstruction* if_expr = ifs->InputAt(0);
94*795d594fSAndroid Build Coastguard Worker if (if_expr->IsCondition()) {
95*795d594fSAndroid Build Coastguard Worker IfCondition other_cmp = ifs->IfTrueSuccessor() == entry
96*795d594fSAndroid Build Coastguard Worker ? if_expr->AsCondition()->GetCondition()
97*795d594fSAndroid Build Coastguard Worker : if_expr->AsCondition()->GetOppositeCondition();
98*795d594fSAndroid Build Coastguard Worker if (if_expr->InputAt(0) == a && if_expr->InputAt(1) == b) {
99*795d594fSAndroid Build Coastguard Worker return cmp == other_cmp;
100*795d594fSAndroid Build Coastguard Worker } else if (if_expr->InputAt(1) == a && if_expr->InputAt(0) == b) {
101*795d594fSAndroid Build Coastguard Worker switch (cmp) {
102*795d594fSAndroid Build Coastguard Worker case kCondLT: return other_cmp == kCondGT;
103*795d594fSAndroid Build Coastguard Worker case kCondLE: return other_cmp == kCondGE;
104*795d594fSAndroid Build Coastguard Worker case kCondGT: return other_cmp == kCondLT;
105*795d594fSAndroid Build Coastguard Worker case kCondGE: return other_cmp == kCondLE;
106*795d594fSAndroid Build Coastguard Worker default: LOG(FATAL) << "unexpected cmp: " << cmp;
107*795d594fSAndroid Build Coastguard Worker }
108*795d594fSAndroid Build Coastguard Worker }
109*795d594fSAndroid Build Coastguard Worker }
110*795d594fSAndroid Build Coastguard Worker return false;
111*795d594fSAndroid Build Coastguard Worker }
112*795d594fSAndroid Build Coastguard Worker
113*795d594fSAndroid Build Coastguard Worker /* Finds first loop header phi use. */
FindFirstLoopHeaderPhiUse(const HLoopInformation * loop,HInstruction * instruction)114*795d594fSAndroid Build Coastguard Worker HInstruction* FindFirstLoopHeaderPhiUse(const HLoopInformation* loop, HInstruction* instruction) {
115*795d594fSAndroid Build Coastguard Worker for (const HUseListNode<HInstruction*>& use : instruction->GetUses()) {
116*795d594fSAndroid Build Coastguard Worker if (use.GetUser()->GetBlock() == loop->GetHeader() &&
117*795d594fSAndroid Build Coastguard Worker use.GetUser()->IsPhi() &&
118*795d594fSAndroid Build Coastguard Worker use.GetUser()->InputAt(1) == instruction) {
119*795d594fSAndroid Build Coastguard Worker return use.GetUser();
120*795d594fSAndroid Build Coastguard Worker }
121*795d594fSAndroid Build Coastguard Worker }
122*795d594fSAndroid Build Coastguard Worker return nullptr;
123*795d594fSAndroid Build Coastguard Worker }
124*795d594fSAndroid Build Coastguard Worker
125*795d594fSAndroid Build Coastguard Worker /**
126*795d594fSAndroid Build Coastguard Worker * Relinks the Phi structure after break-loop rewriting.
127*795d594fSAndroid Build Coastguard Worker */
FixOutsideUse(const HLoopInformation * loop,HInstruction * instruction,HInstruction * replacement,bool rewrite)128*795d594fSAndroid Build Coastguard Worker static bool FixOutsideUse(const HLoopInformation* loop,
129*795d594fSAndroid Build Coastguard Worker HInstruction* instruction,
130*795d594fSAndroid Build Coastguard Worker HInstruction* replacement,
131*795d594fSAndroid Build Coastguard Worker bool rewrite) {
132*795d594fSAndroid Build Coastguard Worker // Deal with regular uses.
133*795d594fSAndroid Build Coastguard Worker const HUseList<HInstruction*>& uses = instruction->GetUses();
134*795d594fSAndroid Build Coastguard Worker for (auto it = uses.begin(), end = uses.end(); it != end; ) {
135*795d594fSAndroid Build Coastguard Worker HInstruction* user = it->GetUser();
136*795d594fSAndroid Build Coastguard Worker size_t index = it->GetIndex();
137*795d594fSAndroid Build Coastguard Worker ++it; // increment prior to potential removal
138*795d594fSAndroid Build Coastguard Worker if (user->GetBlock()->GetLoopInformation() != loop) {
139*795d594fSAndroid Build Coastguard Worker if (replacement == nullptr) {
140*795d594fSAndroid Build Coastguard Worker return false;
141*795d594fSAndroid Build Coastguard Worker } else if (rewrite) {
142*795d594fSAndroid Build Coastguard Worker user->ReplaceInput(replacement, index);
143*795d594fSAndroid Build Coastguard Worker }
144*795d594fSAndroid Build Coastguard Worker }
145*795d594fSAndroid Build Coastguard Worker }
146*795d594fSAndroid Build Coastguard Worker // Deal with environment uses.
147*795d594fSAndroid Build Coastguard Worker const HUseList<HEnvironment*>& env_uses = instruction->GetEnvUses();
148*795d594fSAndroid Build Coastguard Worker for (auto it = env_uses.begin(), end = env_uses.end(); it != end;) {
149*795d594fSAndroid Build Coastguard Worker HEnvironment* user = it->GetUser();
150*795d594fSAndroid Build Coastguard Worker size_t index = it->GetIndex();
151*795d594fSAndroid Build Coastguard Worker ++it; // increment prior to potential removal
152*795d594fSAndroid Build Coastguard Worker if (user->GetHolder()->GetBlock()->GetLoopInformation() != loop) {
153*795d594fSAndroid Build Coastguard Worker if (replacement == nullptr) {
154*795d594fSAndroid Build Coastguard Worker return false;
155*795d594fSAndroid Build Coastguard Worker } else if (rewrite) {
156*795d594fSAndroid Build Coastguard Worker user->ReplaceInput(replacement, index);
157*795d594fSAndroid Build Coastguard Worker }
158*795d594fSAndroid Build Coastguard Worker }
159*795d594fSAndroid Build Coastguard Worker }
160*795d594fSAndroid Build Coastguard Worker return true;
161*795d594fSAndroid Build Coastguard Worker }
162*795d594fSAndroid Build Coastguard Worker
163*795d594fSAndroid Build Coastguard Worker /**
164*795d594fSAndroid Build Coastguard Worker * Test and rewrite the loop body of a break-loop. Returns true on success.
165*795d594fSAndroid Build Coastguard Worker */
RewriteBreakLoopBody(const HLoopInformation * loop,HBasicBlock * body,HInstruction * cond,HInstruction * index,HInstruction * upper,bool rewrite)166*795d594fSAndroid Build Coastguard Worker static bool RewriteBreakLoopBody(const HLoopInformation* loop,
167*795d594fSAndroid Build Coastguard Worker HBasicBlock* body,
168*795d594fSAndroid Build Coastguard Worker HInstruction* cond,
169*795d594fSAndroid Build Coastguard Worker HInstruction* index,
170*795d594fSAndroid Build Coastguard Worker HInstruction* upper,
171*795d594fSAndroid Build Coastguard Worker bool rewrite) {
172*795d594fSAndroid Build Coastguard Worker // Deal with Phis. Outside use prohibited, except for index (which gets exit value).
173*795d594fSAndroid Build Coastguard Worker for (HInstructionIterator it(loop->GetHeader()->GetPhis()); !it.Done(); it.Advance()) {
174*795d594fSAndroid Build Coastguard Worker HInstruction* exit_value = it.Current() == index ? upper : nullptr;
175*795d594fSAndroid Build Coastguard Worker if (!FixOutsideUse(loop, it.Current(), exit_value, rewrite)) {
176*795d594fSAndroid Build Coastguard Worker return false;
177*795d594fSAndroid Build Coastguard Worker }
178*795d594fSAndroid Build Coastguard Worker }
179*795d594fSAndroid Build Coastguard Worker // Deal with other statements in header.
180*795d594fSAndroid Build Coastguard Worker for (HInstruction* m = cond->GetPrevious(); m && !m->IsSuspendCheck();) {
181*795d594fSAndroid Build Coastguard Worker HInstruction* p = m->GetPrevious();
182*795d594fSAndroid Build Coastguard Worker if (rewrite) {
183*795d594fSAndroid Build Coastguard Worker m->MoveBefore(body->GetFirstInstruction(), false);
184*795d594fSAndroid Build Coastguard Worker }
185*795d594fSAndroid Build Coastguard Worker if (!FixOutsideUse(loop, m, FindFirstLoopHeaderPhiUse(loop, m), rewrite)) {
186*795d594fSAndroid Build Coastguard Worker return false;
187*795d594fSAndroid Build Coastguard Worker }
188*795d594fSAndroid Build Coastguard Worker m = p;
189*795d594fSAndroid Build Coastguard Worker }
190*795d594fSAndroid Build Coastguard Worker return true;
191*795d594fSAndroid Build Coastguard Worker }
192*795d594fSAndroid Build Coastguard Worker
193*795d594fSAndroid Build Coastguard Worker //
194*795d594fSAndroid Build Coastguard Worker // Class members.
195*795d594fSAndroid Build Coastguard Worker //
196*795d594fSAndroid Build Coastguard Worker
197*795d594fSAndroid Build Coastguard Worker struct HInductionVarAnalysis::NodeInfo {
NodeInfoart::HInductionVarAnalysis::NodeInfo198*795d594fSAndroid Build Coastguard Worker explicit NodeInfo(uint32_t d) : depth(d), done(false) {}
199*795d594fSAndroid Build Coastguard Worker uint32_t depth;
200*795d594fSAndroid Build Coastguard Worker bool done;
201*795d594fSAndroid Build Coastguard Worker };
202*795d594fSAndroid Build Coastguard Worker
203*795d594fSAndroid Build Coastguard Worker struct HInductionVarAnalysis::StackEntry {
StackEntryart::HInductionVarAnalysis::StackEntry204*795d594fSAndroid Build Coastguard Worker StackEntry(HInstruction* insn, NodeInfo* info, size_t link = std::numeric_limits<size_t>::max())
205*795d594fSAndroid Build Coastguard Worker : instruction(insn),
206*795d594fSAndroid Build Coastguard Worker node_info(info),
207*795d594fSAndroid Build Coastguard Worker user_link(link),
208*795d594fSAndroid Build Coastguard Worker num_visited_inputs(0u),
209*795d594fSAndroid Build Coastguard Worker low_depth(info->depth) {}
210*795d594fSAndroid Build Coastguard Worker
211*795d594fSAndroid Build Coastguard Worker HInstruction* instruction;
212*795d594fSAndroid Build Coastguard Worker NodeInfo* node_info;
213*795d594fSAndroid Build Coastguard Worker size_t user_link; // Stack index of the user that is visiting this input.
214*795d594fSAndroid Build Coastguard Worker size_t num_visited_inputs;
215*795d594fSAndroid Build Coastguard Worker size_t low_depth;
216*795d594fSAndroid Build Coastguard Worker };
217*795d594fSAndroid Build Coastguard Worker
HInductionVarAnalysis(HGraph * graph,OptimizingCompilerStats * stats,const char * name)218*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::HInductionVarAnalysis(HGraph* graph,
219*795d594fSAndroid Build Coastguard Worker OptimizingCompilerStats* stats,
220*795d594fSAndroid Build Coastguard Worker const char* name)
221*795d594fSAndroid Build Coastguard Worker : HOptimization(graph, name, stats),
222*795d594fSAndroid Build Coastguard Worker induction_(std::less<const HLoopInformation*>(),
223*795d594fSAndroid Build Coastguard Worker graph->GetAllocator()->Adapter(kArenaAllocInductionVarAnalysis)),
224*795d594fSAndroid Build Coastguard Worker cycles_(std::less<HPhi*>(), graph->GetAllocator()->Adapter(kArenaAllocInductionVarAnalysis)) {
225*795d594fSAndroid Build Coastguard Worker }
226*795d594fSAndroid Build Coastguard Worker
Run()227*795d594fSAndroid Build Coastguard Worker bool HInductionVarAnalysis::Run() {
228*795d594fSAndroid Build Coastguard Worker // Detects sequence variables (generalized induction variables) during an outer to inner
229*795d594fSAndroid Build Coastguard Worker // traversal of all loops using Gerlek's algorithm. The order is important to enable
230*795d594fSAndroid Build Coastguard Worker // range analysis on outer loop while visiting inner loops.
231*795d594fSAndroid Build Coastguard Worker
232*795d594fSAndroid Build Coastguard Worker if (IsPathologicalCase()) {
233*795d594fSAndroid Build Coastguard Worker MaybeRecordStat(stats_, MethodCompilationStat::kNotVarAnalyzedPathological);
234*795d594fSAndroid Build Coastguard Worker return false;
235*795d594fSAndroid Build Coastguard Worker }
236*795d594fSAndroid Build Coastguard Worker
237*795d594fSAndroid Build Coastguard Worker for (HBasicBlock* graph_block : graph_->GetReversePostOrder()) {
238*795d594fSAndroid Build Coastguard Worker // Don't analyze irreducible loops.
239*795d594fSAndroid Build Coastguard Worker if (graph_block->IsLoopHeader() && !graph_block->GetLoopInformation()->IsIrreducible()) {
240*795d594fSAndroid Build Coastguard Worker VisitLoop(graph_block->GetLoopInformation());
241*795d594fSAndroid Build Coastguard Worker }
242*795d594fSAndroid Build Coastguard Worker }
243*795d594fSAndroid Build Coastguard Worker return !induction_.empty();
244*795d594fSAndroid Build Coastguard Worker }
245*795d594fSAndroid Build Coastguard Worker
VisitLoop(const HLoopInformation * loop)246*795d594fSAndroid Build Coastguard Worker void HInductionVarAnalysis::VisitLoop(const HLoopInformation* loop) {
247*795d594fSAndroid Build Coastguard Worker ScopedArenaAllocator local_allocator(graph_->GetArenaStack());
248*795d594fSAndroid Build Coastguard Worker ScopedArenaSafeMap<HInstruction*, NodeInfo> visited_instructions(
249*795d594fSAndroid Build Coastguard Worker std::less<HInstruction*>(), local_allocator.Adapter(kArenaAllocInductionVarAnalysis));
250*795d594fSAndroid Build Coastguard Worker
251*795d594fSAndroid Build Coastguard Worker // Find strongly connected components (SSCs) in the SSA graph of this loop using Tarjan's
252*795d594fSAndroid Build Coastguard Worker // algorithm. Due to the descendant-first nature, classification happens "on-demand".
253*795d594fSAndroid Build Coastguard Worker size_t global_depth = 0;
254*795d594fSAndroid Build Coastguard Worker for (HBlocksInLoopIterator it_loop(*loop); !it_loop.Done(); it_loop.Advance()) {
255*795d594fSAndroid Build Coastguard Worker HBasicBlock* loop_block = it_loop.Current();
256*795d594fSAndroid Build Coastguard Worker DCHECK(loop_block->IsInLoop());
257*795d594fSAndroid Build Coastguard Worker if (loop_block->GetLoopInformation() != loop) {
258*795d594fSAndroid Build Coastguard Worker continue; // Inner loops visited later.
259*795d594fSAndroid Build Coastguard Worker }
260*795d594fSAndroid Build Coastguard Worker // Visit phi-operations and instructions.
261*795d594fSAndroid Build Coastguard Worker for (HInstructionIterator it(loop_block->GetPhis()); !it.Done(); it.Advance()) {
262*795d594fSAndroid Build Coastguard Worker global_depth = TryVisitNodes(loop, it.Current(), global_depth, &visited_instructions);
263*795d594fSAndroid Build Coastguard Worker }
264*795d594fSAndroid Build Coastguard Worker for (HInstructionIterator it(loop_block->GetInstructions()); !it.Done(); it.Advance()) {
265*795d594fSAndroid Build Coastguard Worker global_depth = TryVisitNodes(loop, it.Current(), global_depth, &visited_instructions);
266*795d594fSAndroid Build Coastguard Worker }
267*795d594fSAndroid Build Coastguard Worker }
268*795d594fSAndroid Build Coastguard Worker
269*795d594fSAndroid Build Coastguard Worker // Determine the loop's trip-count.
270*795d594fSAndroid Build Coastguard Worker VisitControl(loop);
271*795d594fSAndroid Build Coastguard Worker }
272*795d594fSAndroid Build Coastguard Worker
TryVisitNodes(const HLoopInformation * loop,HInstruction * start_instruction,size_t global_depth,ScopedArenaSafeMap<HInstruction *,NodeInfo> * visited_instructions)273*795d594fSAndroid Build Coastguard Worker size_t HInductionVarAnalysis::TryVisitNodes(
274*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
275*795d594fSAndroid Build Coastguard Worker HInstruction* start_instruction,
276*795d594fSAndroid Build Coastguard Worker size_t global_depth,
277*795d594fSAndroid Build Coastguard Worker /*inout*/ ScopedArenaSafeMap<HInstruction*, NodeInfo>* visited_instructions) {
278*795d594fSAndroid Build Coastguard Worker // This is recursion-free version of the SCC search algorithm. We have limited stack space,
279*795d594fSAndroid Build Coastguard Worker // so recursion with the depth dependent on the input is undesirable as such depth is unlimited.
280*795d594fSAndroid Build Coastguard Worker auto [it, inserted] =
281*795d594fSAndroid Build Coastguard Worker visited_instructions->insert(std::make_pair(start_instruction, NodeInfo(global_depth + 1u)));
282*795d594fSAndroid Build Coastguard Worker if (!inserted) {
283*795d594fSAndroid Build Coastguard Worker return global_depth;
284*795d594fSAndroid Build Coastguard Worker }
285*795d594fSAndroid Build Coastguard Worker NodeInfo* start_info = &it->second;
286*795d594fSAndroid Build Coastguard Worker ++global_depth;
287*795d594fSAndroid Build Coastguard Worker DCHECK_EQ(global_depth, start_info->depth);
288*795d594fSAndroid Build Coastguard Worker
289*795d594fSAndroid Build Coastguard Worker ScopedArenaVector<StackEntry> stack(visited_instructions->get_allocator());
290*795d594fSAndroid Build Coastguard Worker stack.push_back({start_instruction, start_info});
291*795d594fSAndroid Build Coastguard Worker
292*795d594fSAndroid Build Coastguard Worker size_t current_entry = 0u;
293*795d594fSAndroid Build Coastguard Worker while (!stack.empty()) {
294*795d594fSAndroid Build Coastguard Worker StackEntry& entry = stack[current_entry];
295*795d594fSAndroid Build Coastguard Worker
296*795d594fSAndroid Build Coastguard Worker // Look for unvisited inputs (also known as "descentants").
297*795d594fSAndroid Build Coastguard Worker bool visit_input = false;
298*795d594fSAndroid Build Coastguard Worker auto inputs = entry.instruction->GetInputs();
299*795d594fSAndroid Build Coastguard Worker while (entry.num_visited_inputs != inputs.size()) {
300*795d594fSAndroid Build Coastguard Worker HInstruction* input = inputs[entry.num_visited_inputs];
301*795d594fSAndroid Build Coastguard Worker ++entry.num_visited_inputs;
302*795d594fSAndroid Build Coastguard Worker // If the definition is either outside the loop (loop invariant entry value)
303*795d594fSAndroid Build Coastguard Worker // or assigned in inner loop (inner exit value), the input is not visited.
304*795d594fSAndroid Build Coastguard Worker if (input->GetBlock()->GetLoopInformation() != loop) {
305*795d594fSAndroid Build Coastguard Worker continue;
306*795d594fSAndroid Build Coastguard Worker }
307*795d594fSAndroid Build Coastguard Worker // Try visiting the input. If already visited, update `entry.low_depth`.
308*795d594fSAndroid Build Coastguard Worker auto [input_it, input_inserted] =
309*795d594fSAndroid Build Coastguard Worker visited_instructions->insert(std::make_pair(input, NodeInfo(global_depth + 1u)));
310*795d594fSAndroid Build Coastguard Worker NodeInfo* input_info = &input_it->second;
311*795d594fSAndroid Build Coastguard Worker if (input_inserted) {
312*795d594fSAndroid Build Coastguard Worker // Push the input on the `stack` and visit it now.
313*795d594fSAndroid Build Coastguard Worker ++global_depth;
314*795d594fSAndroid Build Coastguard Worker DCHECK_EQ(global_depth, input_info->depth);
315*795d594fSAndroid Build Coastguard Worker stack.push_back({input, input_info, current_entry});
316*795d594fSAndroid Build Coastguard Worker current_entry = stack.size() - 1u;
317*795d594fSAndroid Build Coastguard Worker visit_input = true;
318*795d594fSAndroid Build Coastguard Worker break;
319*795d594fSAndroid Build Coastguard Worker } else if (!input_info->done && input_info->depth < entry.low_depth) {
320*795d594fSAndroid Build Coastguard Worker entry.low_depth = input_it->second.depth;
321*795d594fSAndroid Build Coastguard Worker }
322*795d594fSAndroid Build Coastguard Worker continue;
323*795d594fSAndroid Build Coastguard Worker }
324*795d594fSAndroid Build Coastguard Worker if (visit_input) {
325*795d594fSAndroid Build Coastguard Worker continue; // Process the new top of the stack.
326*795d594fSAndroid Build Coastguard Worker }
327*795d594fSAndroid Build Coastguard Worker
328*795d594fSAndroid Build Coastguard Worker // All inputs of the current node have been visited.
329*795d594fSAndroid Build Coastguard Worker // Check if we have found an input below this entry on the stack.
330*795d594fSAndroid Build Coastguard Worker DCHECK(!entry.node_info->done);
331*795d594fSAndroid Build Coastguard Worker size_t previous_entry = entry.user_link;
332*795d594fSAndroid Build Coastguard Worker if (entry.node_info->depth > entry.low_depth) {
333*795d594fSAndroid Build Coastguard Worker DCHECK_LT(previous_entry, current_entry) << entry.node_info->depth << " " << entry.low_depth;
334*795d594fSAndroid Build Coastguard Worker entry.node_info->depth = entry.low_depth;
335*795d594fSAndroid Build Coastguard Worker if (stack[previous_entry].low_depth > entry.low_depth) {
336*795d594fSAndroid Build Coastguard Worker stack[previous_entry].low_depth = entry.low_depth;
337*795d594fSAndroid Build Coastguard Worker }
338*795d594fSAndroid Build Coastguard Worker } else {
339*795d594fSAndroid Build Coastguard Worker // Classify the SCC we have just found.
340*795d594fSAndroid Build Coastguard Worker ArrayRef<StackEntry> stack_tail = ArrayRef<StackEntry>(stack).SubArray(current_entry);
341*795d594fSAndroid Build Coastguard Worker for (StackEntry& tail_entry : stack_tail) {
342*795d594fSAndroid Build Coastguard Worker tail_entry.node_info->done = true;
343*795d594fSAndroid Build Coastguard Worker }
344*795d594fSAndroid Build Coastguard Worker if (current_entry + 1u == stack.size() && !entry.instruction->IsLoopHeaderPhi()) {
345*795d594fSAndroid Build Coastguard Worker ClassifyTrivial(loop, entry.instruction);
346*795d594fSAndroid Build Coastguard Worker } else {
347*795d594fSAndroid Build Coastguard Worker ClassifyNonTrivial(loop, ArrayRef<const StackEntry>(stack_tail));
348*795d594fSAndroid Build Coastguard Worker }
349*795d594fSAndroid Build Coastguard Worker stack.erase(stack.begin() + current_entry, stack.end());
350*795d594fSAndroid Build Coastguard Worker }
351*795d594fSAndroid Build Coastguard Worker current_entry = previous_entry;
352*795d594fSAndroid Build Coastguard Worker }
353*795d594fSAndroid Build Coastguard Worker
354*795d594fSAndroid Build Coastguard Worker return global_depth;
355*795d594fSAndroid Build Coastguard Worker }
356*795d594fSAndroid Build Coastguard Worker
357*795d594fSAndroid Build Coastguard Worker /**
358*795d594fSAndroid Build Coastguard Worker * Since graph traversal may enter a SCC at any position, an initial representation may be rotated,
359*795d594fSAndroid Build Coastguard Worker * along dependences, viz. any of (a, b, c, d), (d, a, b, c) (c, d, a, b), (b, c, d, a) assuming
360*795d594fSAndroid Build Coastguard Worker * a chain of dependences (mutual independent items may occur in arbitrary order). For proper
361*795d594fSAndroid Build Coastguard Worker * classification, the lexicographically first loop-phi is rotated to the front. We do that
362*795d594fSAndroid Build Coastguard Worker * as we extract the SCC instructions.
363*795d594fSAndroid Build Coastguard Worker */
ExtractScc(ArrayRef<const StackEntry> stack_tail,ScopedArenaVector<HInstruction * > * scc)364*795d594fSAndroid Build Coastguard Worker void HInductionVarAnalysis::ExtractScc(ArrayRef<const StackEntry> stack_tail,
365*795d594fSAndroid Build Coastguard Worker ScopedArenaVector<HInstruction*>* scc) {
366*795d594fSAndroid Build Coastguard Worker // Find very first loop-phi.
367*795d594fSAndroid Build Coastguard Worker HInstruction* phi = nullptr;
368*795d594fSAndroid Build Coastguard Worker size_t split_pos = 0;
369*795d594fSAndroid Build Coastguard Worker const size_t size = stack_tail.size();
370*795d594fSAndroid Build Coastguard Worker for (size_t i = 0; i != size; ++i) {
371*795d594fSAndroid Build Coastguard Worker const StackEntry& entry = stack_tail[i];
372*795d594fSAndroid Build Coastguard Worker HInstruction* instruction = entry.instruction;
373*795d594fSAndroid Build Coastguard Worker if (instruction->IsLoopHeaderPhi()) {
374*795d594fSAndroid Build Coastguard Worker // All loop Phis in SCC come from the same loop header.
375*795d594fSAndroid Build Coastguard Worker HBasicBlock* block = instruction->GetBlock();
376*795d594fSAndroid Build Coastguard Worker DCHECK(block->GetLoopInformation()->GetHeader() == block);
377*795d594fSAndroid Build Coastguard Worker DCHECK(phi == nullptr || phi->GetBlock() == block);
378*795d594fSAndroid Build Coastguard Worker if (phi == nullptr || block->GetPhis().FoundBefore(instruction, phi)) {
379*795d594fSAndroid Build Coastguard Worker phi = instruction;
380*795d594fSAndroid Build Coastguard Worker split_pos = i + 1u;
381*795d594fSAndroid Build Coastguard Worker }
382*795d594fSAndroid Build Coastguard Worker }
383*795d594fSAndroid Build Coastguard Worker }
384*795d594fSAndroid Build Coastguard Worker
385*795d594fSAndroid Build Coastguard Worker // Extract SCC in two chunks.
386*795d594fSAndroid Build Coastguard Worker DCHECK(scc->empty());
387*795d594fSAndroid Build Coastguard Worker scc->reserve(size);
388*795d594fSAndroid Build Coastguard Worker for (const StackEntry& entry : ReverseRange(stack_tail.SubArray(/*pos=*/ 0u, split_pos))) {
389*795d594fSAndroid Build Coastguard Worker scc->push_back(entry.instruction);
390*795d594fSAndroid Build Coastguard Worker }
391*795d594fSAndroid Build Coastguard Worker for (const StackEntry& entry : ReverseRange(stack_tail.SubArray(/*pos=*/ split_pos))) {
392*795d594fSAndroid Build Coastguard Worker scc->push_back(entry.instruction);
393*795d594fSAndroid Build Coastguard Worker }
394*795d594fSAndroid Build Coastguard Worker DCHECK_EQ(scc->size(), stack_tail.size());
395*795d594fSAndroid Build Coastguard Worker }
396*795d594fSAndroid Build Coastguard Worker
ClassifyTrivial(const HLoopInformation * loop,HInstruction * instruction)397*795d594fSAndroid Build Coastguard Worker void HInductionVarAnalysis::ClassifyTrivial(const HLoopInformation* loop,
398*795d594fSAndroid Build Coastguard Worker HInstruction* instruction) {
399*795d594fSAndroid Build Coastguard Worker const HBasicBlock* context = instruction->GetBlock();
400*795d594fSAndroid Build Coastguard Worker DataType::Type type = instruction->GetType();
401*795d594fSAndroid Build Coastguard Worker InductionInfo* info = nullptr;
402*795d594fSAndroid Build Coastguard Worker if (instruction->IsPhi()) {
403*795d594fSAndroid Build Coastguard Worker info = TransferPhi(loop, instruction, /*input_index*/ 0, /*adjust_input_size*/ 0);
404*795d594fSAndroid Build Coastguard Worker } else if (instruction->IsAdd()) {
405*795d594fSAndroid Build Coastguard Worker info = TransferAddSub(context,
406*795d594fSAndroid Build Coastguard Worker loop,
407*795d594fSAndroid Build Coastguard Worker LookupInfo(loop, instruction->InputAt(0)),
408*795d594fSAndroid Build Coastguard Worker LookupInfo(loop, instruction->InputAt(1)),
409*795d594fSAndroid Build Coastguard Worker kAdd,
410*795d594fSAndroid Build Coastguard Worker type);
411*795d594fSAndroid Build Coastguard Worker } else if (instruction->IsSub()) {
412*795d594fSAndroid Build Coastguard Worker info = TransferAddSub(context,
413*795d594fSAndroid Build Coastguard Worker loop,
414*795d594fSAndroid Build Coastguard Worker LookupInfo(loop, instruction->InputAt(0)),
415*795d594fSAndroid Build Coastguard Worker LookupInfo(loop, instruction->InputAt(1)),
416*795d594fSAndroid Build Coastguard Worker kSub,
417*795d594fSAndroid Build Coastguard Worker type);
418*795d594fSAndroid Build Coastguard Worker } else if (instruction->IsNeg()) {
419*795d594fSAndroid Build Coastguard Worker info = TransferNeg(context, loop, LookupInfo(loop, instruction->InputAt(0)), type);
420*795d594fSAndroid Build Coastguard Worker } else if (instruction->IsMul()) {
421*795d594fSAndroid Build Coastguard Worker info = TransferMul(context,
422*795d594fSAndroid Build Coastguard Worker loop,
423*795d594fSAndroid Build Coastguard Worker LookupInfo(loop, instruction->InputAt(0)),
424*795d594fSAndroid Build Coastguard Worker LookupInfo(loop, instruction->InputAt(1)),
425*795d594fSAndroid Build Coastguard Worker type);
426*795d594fSAndroid Build Coastguard Worker } else if (instruction->IsShl()) {
427*795d594fSAndroid Build Coastguard Worker HInstruction* mulc = GetShiftConstant(loop, instruction, /*initial*/ nullptr);
428*795d594fSAndroid Build Coastguard Worker if (mulc != nullptr) {
429*795d594fSAndroid Build Coastguard Worker info = TransferMul(context,
430*795d594fSAndroid Build Coastguard Worker loop,
431*795d594fSAndroid Build Coastguard Worker LookupInfo(loop, instruction->InputAt(0)),
432*795d594fSAndroid Build Coastguard Worker LookupInfo(loop, mulc),
433*795d594fSAndroid Build Coastguard Worker type);
434*795d594fSAndroid Build Coastguard Worker }
435*795d594fSAndroid Build Coastguard Worker } else if (instruction->IsSelect()) {
436*795d594fSAndroid Build Coastguard Worker info = TransferPhi(loop, instruction, /*input_index*/ 0, /*adjust_input_size*/ 1);
437*795d594fSAndroid Build Coastguard Worker } else if (instruction->IsTypeConversion()) {
438*795d594fSAndroid Build Coastguard Worker info = TransferConversion(LookupInfo(loop, instruction->InputAt(0)),
439*795d594fSAndroid Build Coastguard Worker instruction->AsTypeConversion()->GetInputType(),
440*795d594fSAndroid Build Coastguard Worker instruction->AsTypeConversion()->GetResultType());
441*795d594fSAndroid Build Coastguard Worker } else if (instruction->IsBoundsCheck()) {
442*795d594fSAndroid Build Coastguard Worker info = LookupInfo(loop, instruction->InputAt(0)); // Pass-through.
443*795d594fSAndroid Build Coastguard Worker }
444*795d594fSAndroid Build Coastguard Worker
445*795d594fSAndroid Build Coastguard Worker // Successfully classified?
446*795d594fSAndroid Build Coastguard Worker if (info != nullptr) {
447*795d594fSAndroid Build Coastguard Worker AssignInfo(loop, instruction, info);
448*795d594fSAndroid Build Coastguard Worker }
449*795d594fSAndroid Build Coastguard Worker }
450*795d594fSAndroid Build Coastguard Worker
ClassifyNonTrivial(const HLoopInformation * loop,ArrayRef<const StackEntry> stack_tail)451*795d594fSAndroid Build Coastguard Worker void HInductionVarAnalysis::ClassifyNonTrivial(const HLoopInformation* loop,
452*795d594fSAndroid Build Coastguard Worker ArrayRef<const StackEntry> stack_tail) {
453*795d594fSAndroid Build Coastguard Worker const size_t size = stack_tail.size();
454*795d594fSAndroid Build Coastguard Worker DCHECK_GE(size, 1u);
455*795d594fSAndroid Build Coastguard Worker DataType::Type type = stack_tail.back().instruction->GetType();
456*795d594fSAndroid Build Coastguard Worker
457*795d594fSAndroid Build Coastguard Worker ScopedArenaAllocator local_allocator(graph_->GetArenaStack());
458*795d594fSAndroid Build Coastguard Worker ScopedArenaVector<HInstruction*> scc(local_allocator.Adapter(kArenaAllocInductionVarAnalysis));
459*795d594fSAndroid Build Coastguard Worker ExtractScc(ArrayRef<const StackEntry>(stack_tail), &scc);
460*795d594fSAndroid Build Coastguard Worker
461*795d594fSAndroid Build Coastguard Worker // Analyze from loop-phi onwards.
462*795d594fSAndroid Build Coastguard Worker HInstruction* phi = scc[0];
463*795d594fSAndroid Build Coastguard Worker if (!phi->IsLoopHeaderPhi()) {
464*795d594fSAndroid Build Coastguard Worker return;
465*795d594fSAndroid Build Coastguard Worker }
466*795d594fSAndroid Build Coastguard Worker
467*795d594fSAndroid Build Coastguard Worker // External link should be loop invariant.
468*795d594fSAndroid Build Coastguard Worker InductionInfo* initial = LookupInfo(loop, phi->InputAt(0));
469*795d594fSAndroid Build Coastguard Worker if (initial == nullptr || initial->induction_class != kInvariant) {
470*795d594fSAndroid Build Coastguard Worker return;
471*795d594fSAndroid Build Coastguard Worker }
472*795d594fSAndroid Build Coastguard Worker
473*795d594fSAndroid Build Coastguard Worker // Store interesting cycle in each loop phi.
474*795d594fSAndroid Build Coastguard Worker for (size_t i = 0; i < size; i++) {
475*795d594fSAndroid Build Coastguard Worker if (scc[i]->IsLoopHeaderPhi()) {
476*795d594fSAndroid Build Coastguard Worker AssignCycle(scc[i]->AsPhi(), ArrayRef<HInstruction* const>(scc));
477*795d594fSAndroid Build Coastguard Worker }
478*795d594fSAndroid Build Coastguard Worker }
479*795d594fSAndroid Build Coastguard Worker
480*795d594fSAndroid Build Coastguard Worker // Singleton is wrap-around induction if all internal links have the same meaning.
481*795d594fSAndroid Build Coastguard Worker if (size == 1) {
482*795d594fSAndroid Build Coastguard Worker InductionInfo* update = TransferPhi(loop, phi, /*input_index*/ 1, /*adjust_input_size*/ 0);
483*795d594fSAndroid Build Coastguard Worker if (update != nullptr) {
484*795d594fSAndroid Build Coastguard Worker AssignInfo(loop, phi, CreateInduction(kWrapAround,
485*795d594fSAndroid Build Coastguard Worker kNop,
486*795d594fSAndroid Build Coastguard Worker initial,
487*795d594fSAndroid Build Coastguard Worker update,
488*795d594fSAndroid Build Coastguard Worker /*fetch*/ nullptr,
489*795d594fSAndroid Build Coastguard Worker type));
490*795d594fSAndroid Build Coastguard Worker }
491*795d594fSAndroid Build Coastguard Worker return;
492*795d594fSAndroid Build Coastguard Worker }
493*795d594fSAndroid Build Coastguard Worker
494*795d594fSAndroid Build Coastguard Worker // Inspect remainder of the cycle that resides in `scc`. The `cycle` mapping assigns
495*795d594fSAndroid Build Coastguard Worker // temporary meaning to its nodes, seeded from the phi instruction and back.
496*795d594fSAndroid Build Coastguard Worker ScopedArenaSafeMap<HInstruction*, InductionInfo*> cycle(
497*795d594fSAndroid Build Coastguard Worker std::less<HInstruction*>(), local_allocator.Adapter(kArenaAllocInductionVarAnalysis));
498*795d594fSAndroid Build Coastguard Worker for (size_t i = 1; i < size; i++) {
499*795d594fSAndroid Build Coastguard Worker HInstruction* instruction = scc[i];
500*795d594fSAndroid Build Coastguard Worker InductionInfo* update = nullptr;
501*795d594fSAndroid Build Coastguard Worker if (instruction->IsPhi()) {
502*795d594fSAndroid Build Coastguard Worker update = SolvePhiAllInputs(loop, phi, instruction, cycle, type);
503*795d594fSAndroid Build Coastguard Worker } else if (instruction->IsAdd()) {
504*795d594fSAndroid Build Coastguard Worker update = SolveAddSub(loop,
505*795d594fSAndroid Build Coastguard Worker phi,
506*795d594fSAndroid Build Coastguard Worker instruction,
507*795d594fSAndroid Build Coastguard Worker instruction->InputAt(0),
508*795d594fSAndroid Build Coastguard Worker instruction->InputAt(1),
509*795d594fSAndroid Build Coastguard Worker kAdd,
510*795d594fSAndroid Build Coastguard Worker cycle,
511*795d594fSAndroid Build Coastguard Worker type);
512*795d594fSAndroid Build Coastguard Worker } else if (instruction->IsSub()) {
513*795d594fSAndroid Build Coastguard Worker update = SolveAddSub(loop,
514*795d594fSAndroid Build Coastguard Worker phi,
515*795d594fSAndroid Build Coastguard Worker instruction,
516*795d594fSAndroid Build Coastguard Worker instruction->InputAt(0),
517*795d594fSAndroid Build Coastguard Worker instruction->InputAt(1),
518*795d594fSAndroid Build Coastguard Worker kSub,
519*795d594fSAndroid Build Coastguard Worker cycle,
520*795d594fSAndroid Build Coastguard Worker type);
521*795d594fSAndroid Build Coastguard Worker } else if (instruction->IsMul()) {
522*795d594fSAndroid Build Coastguard Worker update = SolveOp(
523*795d594fSAndroid Build Coastguard Worker loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kMul, type);
524*795d594fSAndroid Build Coastguard Worker } else if (instruction->IsDiv()) {
525*795d594fSAndroid Build Coastguard Worker update = SolveOp(
526*795d594fSAndroid Build Coastguard Worker loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kDiv, type);
527*795d594fSAndroid Build Coastguard Worker } else if (instruction->IsRem()) {
528*795d594fSAndroid Build Coastguard Worker update = SolveOp(
529*795d594fSAndroid Build Coastguard Worker loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kRem, type);
530*795d594fSAndroid Build Coastguard Worker } else if (instruction->IsShl()) {
531*795d594fSAndroid Build Coastguard Worker HInstruction* mulc = GetShiftConstant(loop, instruction, /*initial*/ nullptr);
532*795d594fSAndroid Build Coastguard Worker if (mulc != nullptr) {
533*795d594fSAndroid Build Coastguard Worker update = SolveOp(loop, phi, instruction, instruction->InputAt(0), mulc, kMul, type);
534*795d594fSAndroid Build Coastguard Worker }
535*795d594fSAndroid Build Coastguard Worker } else if (instruction->IsShr() || instruction->IsUShr()) {
536*795d594fSAndroid Build Coastguard Worker HInstruction* divc = GetShiftConstant(loop, instruction, initial);
537*795d594fSAndroid Build Coastguard Worker if (divc != nullptr) {
538*795d594fSAndroid Build Coastguard Worker update = SolveOp(loop, phi, instruction, instruction->InputAt(0), divc, kDiv, type);
539*795d594fSAndroid Build Coastguard Worker }
540*795d594fSAndroid Build Coastguard Worker } else if (instruction->IsXor()) {
541*795d594fSAndroid Build Coastguard Worker update = SolveOp(
542*795d594fSAndroid Build Coastguard Worker loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kXor, type);
543*795d594fSAndroid Build Coastguard Worker } else if (instruction->IsEqual()) {
544*795d594fSAndroid Build Coastguard Worker update = SolveTest(loop, phi, instruction, /*opposite_value=*/ 0, type);
545*795d594fSAndroid Build Coastguard Worker } else if (instruction->IsNotEqual()) {
546*795d594fSAndroid Build Coastguard Worker update = SolveTest(loop, phi, instruction, /*opposite_value=*/ 1, type);
547*795d594fSAndroid Build Coastguard Worker } else if (instruction->IsSelect()) {
548*795d594fSAndroid Build Coastguard Worker // Select acts like Phi.
549*795d594fSAndroid Build Coastguard Worker update = SolvePhi(instruction, /*input_index=*/ 0, /*adjust_input_size=*/ 1, cycle);
550*795d594fSAndroid Build Coastguard Worker } else if (instruction->IsTypeConversion()) {
551*795d594fSAndroid Build Coastguard Worker update = SolveConversion(loop, phi, instruction->AsTypeConversion(), cycle, &type);
552*795d594fSAndroid Build Coastguard Worker }
553*795d594fSAndroid Build Coastguard Worker if (update == nullptr) {
554*795d594fSAndroid Build Coastguard Worker return;
555*795d594fSAndroid Build Coastguard Worker }
556*795d594fSAndroid Build Coastguard Worker cycle.Put(instruction, update);
557*795d594fSAndroid Build Coastguard Worker }
558*795d594fSAndroid Build Coastguard Worker
559*795d594fSAndroid Build Coastguard Worker // Success if all internal links received the same temporary meaning.
560*795d594fSAndroid Build Coastguard Worker InductionInfo* induction = SolvePhi(phi, /*input_index=*/ 1, /*adjust_input_size=*/ 0, cycle);
561*795d594fSAndroid Build Coastguard Worker if (induction != nullptr) {
562*795d594fSAndroid Build Coastguard Worker switch (induction->induction_class) {
563*795d594fSAndroid Build Coastguard Worker case kInvariant:
564*795d594fSAndroid Build Coastguard Worker // Construct combined stride of the linear induction.
565*795d594fSAndroid Build Coastguard Worker induction = CreateInduction(kLinear, kNop, induction, initial, /*fetch*/ nullptr, type);
566*795d594fSAndroid Build Coastguard Worker FALLTHROUGH_INTENDED;
567*795d594fSAndroid Build Coastguard Worker case kPolynomial:
568*795d594fSAndroid Build Coastguard Worker case kGeometric:
569*795d594fSAndroid Build Coastguard Worker case kWrapAround:
570*795d594fSAndroid Build Coastguard Worker // Classify first phi and then the rest of the cycle "on-demand".
571*795d594fSAndroid Build Coastguard Worker // Statements are scanned in order.
572*795d594fSAndroid Build Coastguard Worker AssignInfo(loop, phi, induction);
573*795d594fSAndroid Build Coastguard Worker for (size_t i = 1; i < size; i++) {
574*795d594fSAndroid Build Coastguard Worker ClassifyTrivial(loop, scc[i]);
575*795d594fSAndroid Build Coastguard Worker }
576*795d594fSAndroid Build Coastguard Worker break;
577*795d594fSAndroid Build Coastguard Worker case kPeriodic:
578*795d594fSAndroid Build Coastguard Worker // Classify all elements in the cycle with the found periodic induction while
579*795d594fSAndroid Build Coastguard Worker // rotating each first element to the end. Lastly, phi is classified.
580*795d594fSAndroid Build Coastguard Worker // Statements are scanned in reverse order.
581*795d594fSAndroid Build Coastguard Worker for (size_t i = size - 1; i >= 1; i--) {
582*795d594fSAndroid Build Coastguard Worker AssignInfo(loop, scc[i], induction);
583*795d594fSAndroid Build Coastguard Worker induction = RotatePeriodicInduction(induction->op_b, induction->op_a, type);
584*795d594fSAndroid Build Coastguard Worker }
585*795d594fSAndroid Build Coastguard Worker AssignInfo(loop, phi, induction);
586*795d594fSAndroid Build Coastguard Worker break;
587*795d594fSAndroid Build Coastguard Worker default:
588*795d594fSAndroid Build Coastguard Worker break;
589*795d594fSAndroid Build Coastguard Worker }
590*795d594fSAndroid Build Coastguard Worker }
591*795d594fSAndroid Build Coastguard Worker }
592*795d594fSAndroid Build Coastguard Worker
RotatePeriodicInduction(InductionInfo * induction,InductionInfo * last,DataType::Type type)593*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::RotatePeriodicInduction(
594*795d594fSAndroid Build Coastguard Worker InductionInfo* induction,
595*795d594fSAndroid Build Coastguard Worker InductionInfo* last,
596*795d594fSAndroid Build Coastguard Worker DataType::Type type) {
597*795d594fSAndroid Build Coastguard Worker // Rotates a periodic induction of the form
598*795d594fSAndroid Build Coastguard Worker // (a, b, c, d, e)
599*795d594fSAndroid Build Coastguard Worker // into
600*795d594fSAndroid Build Coastguard Worker // (b, c, d, e, a)
601*795d594fSAndroid Build Coastguard Worker // in preparation of assigning this to the previous variable in the sequence.
602*795d594fSAndroid Build Coastguard Worker if (induction->induction_class == kInvariant) {
603*795d594fSAndroid Build Coastguard Worker return CreateInduction(kPeriodic,
604*795d594fSAndroid Build Coastguard Worker kNop,
605*795d594fSAndroid Build Coastguard Worker induction,
606*795d594fSAndroid Build Coastguard Worker last,
607*795d594fSAndroid Build Coastguard Worker /*fetch*/ nullptr,
608*795d594fSAndroid Build Coastguard Worker type);
609*795d594fSAndroid Build Coastguard Worker }
610*795d594fSAndroid Build Coastguard Worker return CreateInduction(kPeriodic,
611*795d594fSAndroid Build Coastguard Worker kNop,
612*795d594fSAndroid Build Coastguard Worker induction->op_a,
613*795d594fSAndroid Build Coastguard Worker RotatePeriodicInduction(induction->op_b, last, type),
614*795d594fSAndroid Build Coastguard Worker /*fetch*/ nullptr,
615*795d594fSAndroid Build Coastguard Worker type);
616*795d594fSAndroid Build Coastguard Worker }
617*795d594fSAndroid Build Coastguard Worker
TransferPhi(const HLoopInformation * loop,HInstruction * phi,size_t input_index,size_t adjust_input_size)618*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferPhi(
619*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
620*795d594fSAndroid Build Coastguard Worker HInstruction* phi,
621*795d594fSAndroid Build Coastguard Worker size_t input_index,
622*795d594fSAndroid Build Coastguard Worker size_t adjust_input_size) {
623*795d594fSAndroid Build Coastguard Worker // Match all phi inputs from input_index onwards exactly.
624*795d594fSAndroid Build Coastguard Worker HInputsRef inputs = phi->GetInputs();
625*795d594fSAndroid Build Coastguard Worker DCHECK_LT(input_index, inputs.size());
626*795d594fSAndroid Build Coastguard Worker InductionInfo* a = LookupInfo(loop, inputs[input_index]);
627*795d594fSAndroid Build Coastguard Worker for (size_t i = input_index + 1, n = inputs.size() - adjust_input_size; i < n; i++) {
628*795d594fSAndroid Build Coastguard Worker InductionInfo* b = LookupInfo(loop, inputs[i]);
629*795d594fSAndroid Build Coastguard Worker if (!InductionEqual(a, b)) {
630*795d594fSAndroid Build Coastguard Worker return nullptr;
631*795d594fSAndroid Build Coastguard Worker }
632*795d594fSAndroid Build Coastguard Worker }
633*795d594fSAndroid Build Coastguard Worker return a;
634*795d594fSAndroid Build Coastguard Worker }
635*795d594fSAndroid Build Coastguard Worker
TransferAddSub(const HBasicBlock * context,const HLoopInformation * loop,InductionInfo * a,InductionInfo * b,InductionOp op,DataType::Type type)636*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferAddSub(
637*795d594fSAndroid Build Coastguard Worker const HBasicBlock* context,
638*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
639*795d594fSAndroid Build Coastguard Worker InductionInfo* a,
640*795d594fSAndroid Build Coastguard Worker InductionInfo* b,
641*795d594fSAndroid Build Coastguard Worker InductionOp op,
642*795d594fSAndroid Build Coastguard Worker DataType::Type type) {
643*795d594fSAndroid Build Coastguard Worker // Transfer over an addition or subtraction: any invariant, linear, polynomial, geometric,
644*795d594fSAndroid Build Coastguard Worker // wrap-around, or periodic can be combined with an invariant to yield a similar result.
645*795d594fSAndroid Build Coastguard Worker // Two linear or two polynomial inputs can be combined too. Other combinations fail.
646*795d594fSAndroid Build Coastguard Worker if (a != nullptr && b != nullptr) {
647*795d594fSAndroid Build Coastguard Worker if (IsNarrowingLinear(a) || IsNarrowingLinear(b)) {
648*795d594fSAndroid Build Coastguard Worker return nullptr; // no transfer
649*795d594fSAndroid Build Coastguard Worker } else if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
650*795d594fSAndroid Build Coastguard Worker return CreateInvariantOp(context, loop, op, a, b); // direct invariant
651*795d594fSAndroid Build Coastguard Worker } else if ((a->induction_class == kLinear && b->induction_class == kLinear) ||
652*795d594fSAndroid Build Coastguard Worker (a->induction_class == kPolynomial && b->induction_class == kPolynomial)) {
653*795d594fSAndroid Build Coastguard Worker // Rule induc(a, b) + induc(a', b') -> induc(a + a', b + b').
654*795d594fSAndroid Build Coastguard Worker InductionInfo* new_a = TransferAddSub(context, loop, a->op_a, b->op_a, op, type);
655*795d594fSAndroid Build Coastguard Worker InductionInfo* new_b = TransferAddSub(context, loop, a->op_b, b->op_b, op, type);
656*795d594fSAndroid Build Coastguard Worker if (new_a != nullptr && new_b != nullptr) {
657*795d594fSAndroid Build Coastguard Worker return CreateInduction(a->induction_class, a->operation, new_a, new_b, a->fetch, type);
658*795d594fSAndroid Build Coastguard Worker }
659*795d594fSAndroid Build Coastguard Worker } else if (a->induction_class == kInvariant) {
660*795d594fSAndroid Build Coastguard Worker // Rule a + induc(a', b') -> induc(a', a + b') or induc(a + a', a + b').
661*795d594fSAndroid Build Coastguard Worker InductionInfo* new_a = b->op_a;
662*795d594fSAndroid Build Coastguard Worker InductionInfo* new_b = TransferAddSub(context, loop, a, b->op_b, op, type);
663*795d594fSAndroid Build Coastguard Worker if (b->induction_class == kWrapAround || b->induction_class == kPeriodic) {
664*795d594fSAndroid Build Coastguard Worker new_a = TransferAddSub(context, loop, a, new_a, op, type);
665*795d594fSAndroid Build Coastguard Worker } else if (op == kSub) { // Negation required.
666*795d594fSAndroid Build Coastguard Worker new_a = TransferNeg(context, loop, new_a, type);
667*795d594fSAndroid Build Coastguard Worker }
668*795d594fSAndroid Build Coastguard Worker if (new_a != nullptr && new_b != nullptr) {
669*795d594fSAndroid Build Coastguard Worker return CreateInduction(b->induction_class, b->operation, new_a, new_b, b->fetch, type);
670*795d594fSAndroid Build Coastguard Worker }
671*795d594fSAndroid Build Coastguard Worker } else if (b->induction_class == kInvariant) {
672*795d594fSAndroid Build Coastguard Worker // Rule induc(a, b) + b' -> induc(a, b + b') or induc(a + b', b + b').
673*795d594fSAndroid Build Coastguard Worker InductionInfo* new_a = a->op_a;
674*795d594fSAndroid Build Coastguard Worker InductionInfo* new_b = TransferAddSub(context, loop, a->op_b, b, op, type);
675*795d594fSAndroid Build Coastguard Worker if (a->induction_class == kWrapAround || a->induction_class == kPeriodic) {
676*795d594fSAndroid Build Coastguard Worker new_a = TransferAddSub(context, loop, new_a, b, op, type);
677*795d594fSAndroid Build Coastguard Worker }
678*795d594fSAndroid Build Coastguard Worker if (new_a != nullptr && new_b != nullptr) {
679*795d594fSAndroid Build Coastguard Worker return CreateInduction(a->induction_class, a->operation, new_a, new_b, a->fetch, type);
680*795d594fSAndroid Build Coastguard Worker }
681*795d594fSAndroid Build Coastguard Worker }
682*795d594fSAndroid Build Coastguard Worker }
683*795d594fSAndroid Build Coastguard Worker return nullptr;
684*795d594fSAndroid Build Coastguard Worker }
685*795d594fSAndroid Build Coastguard Worker
TransferNeg(const HBasicBlock * context,const HLoopInformation * loop,InductionInfo * a,DataType::Type type)686*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferNeg(
687*795d594fSAndroid Build Coastguard Worker const HBasicBlock* context,
688*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
689*795d594fSAndroid Build Coastguard Worker InductionInfo* a,
690*795d594fSAndroid Build Coastguard Worker DataType::Type type) {
691*795d594fSAndroid Build Coastguard Worker // Transfer over a unary negation: an invariant, linear, polynomial, geometric (mul),
692*795d594fSAndroid Build Coastguard Worker // wrap-around, or periodic input yields a similar but negated induction as result.
693*795d594fSAndroid Build Coastguard Worker if (a != nullptr) {
694*795d594fSAndroid Build Coastguard Worker if (IsNarrowingLinear(a)) {
695*795d594fSAndroid Build Coastguard Worker return nullptr; // no transfer
696*795d594fSAndroid Build Coastguard Worker } else if (a->induction_class == kInvariant) {
697*795d594fSAndroid Build Coastguard Worker return CreateInvariantOp(context, loop, kNeg, nullptr, a); // direct invariant
698*795d594fSAndroid Build Coastguard Worker } else if (a->induction_class != kGeometric || a->operation == kMul) {
699*795d594fSAndroid Build Coastguard Worker // Rule - induc(a, b) -> induc(-a, -b).
700*795d594fSAndroid Build Coastguard Worker InductionInfo* new_a = TransferNeg(context, loop, a->op_a, type);
701*795d594fSAndroid Build Coastguard Worker InductionInfo* new_b = TransferNeg(context, loop, a->op_b, type);
702*795d594fSAndroid Build Coastguard Worker if (new_a != nullptr && new_b != nullptr) {
703*795d594fSAndroid Build Coastguard Worker return CreateInduction(a->induction_class, a->operation, new_a, new_b, a->fetch, type);
704*795d594fSAndroid Build Coastguard Worker }
705*795d594fSAndroid Build Coastguard Worker }
706*795d594fSAndroid Build Coastguard Worker }
707*795d594fSAndroid Build Coastguard Worker return nullptr;
708*795d594fSAndroid Build Coastguard Worker }
709*795d594fSAndroid Build Coastguard Worker
TransferMul(const HBasicBlock * context,const HLoopInformation * loop,InductionInfo * a,InductionInfo * b,DataType::Type type)710*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferMul(
711*795d594fSAndroid Build Coastguard Worker const HBasicBlock* context,
712*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
713*795d594fSAndroid Build Coastguard Worker InductionInfo* a,
714*795d594fSAndroid Build Coastguard Worker InductionInfo* b,
715*795d594fSAndroid Build Coastguard Worker DataType::Type type) {
716*795d594fSAndroid Build Coastguard Worker // Transfer over a multiplication: any invariant, linear, polynomial, geometric (mul),
717*795d594fSAndroid Build Coastguard Worker // wrap-around, or periodic can be multiplied with an invariant to yield a similar
718*795d594fSAndroid Build Coastguard Worker // but multiplied result. Two non-invariant inputs cannot be multiplied, however.
719*795d594fSAndroid Build Coastguard Worker if (a != nullptr && b != nullptr) {
720*795d594fSAndroid Build Coastguard Worker if (IsNarrowingLinear(a) || IsNarrowingLinear(b)) {
721*795d594fSAndroid Build Coastguard Worker return nullptr; // no transfer
722*795d594fSAndroid Build Coastguard Worker } else if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
723*795d594fSAndroid Build Coastguard Worker return CreateInvariantOp(context, loop, kMul, a, b); // direct invariant
724*795d594fSAndroid Build Coastguard Worker } else if (a->induction_class == kInvariant && (b->induction_class != kGeometric ||
725*795d594fSAndroid Build Coastguard Worker b->operation == kMul)) {
726*795d594fSAndroid Build Coastguard Worker // Rule a * induc(a', b') -> induc(a * a', b * b').
727*795d594fSAndroid Build Coastguard Worker InductionInfo* new_a = TransferMul(context, loop, a, b->op_a, type);
728*795d594fSAndroid Build Coastguard Worker InductionInfo* new_b = TransferMul(context, loop, a, b->op_b, type);
729*795d594fSAndroid Build Coastguard Worker if (new_a != nullptr && new_b != nullptr) {
730*795d594fSAndroid Build Coastguard Worker return CreateInduction(b->induction_class, b->operation, new_a, new_b, b->fetch, type);
731*795d594fSAndroid Build Coastguard Worker }
732*795d594fSAndroid Build Coastguard Worker } else if (b->induction_class == kInvariant && (a->induction_class != kGeometric ||
733*795d594fSAndroid Build Coastguard Worker a->operation == kMul)) {
734*795d594fSAndroid Build Coastguard Worker // Rule induc(a, b) * b' -> induc(a * b', b * b').
735*795d594fSAndroid Build Coastguard Worker InductionInfo* new_a = TransferMul(context, loop, a->op_a, b, type);
736*795d594fSAndroid Build Coastguard Worker InductionInfo* new_b = TransferMul(context, loop, a->op_b, b, type);
737*795d594fSAndroid Build Coastguard Worker if (new_a != nullptr && new_b != nullptr) {
738*795d594fSAndroid Build Coastguard Worker return CreateInduction(a->induction_class, a->operation, new_a, new_b, a->fetch, type);
739*795d594fSAndroid Build Coastguard Worker }
740*795d594fSAndroid Build Coastguard Worker }
741*795d594fSAndroid Build Coastguard Worker }
742*795d594fSAndroid Build Coastguard Worker return nullptr;
743*795d594fSAndroid Build Coastguard Worker }
744*795d594fSAndroid Build Coastguard Worker
TransferConversion(InductionInfo * a,DataType::Type from,DataType::Type to)745*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferConversion(
746*795d594fSAndroid Build Coastguard Worker InductionInfo* a,
747*795d594fSAndroid Build Coastguard Worker DataType::Type from,
748*795d594fSAndroid Build Coastguard Worker DataType::Type to) {
749*795d594fSAndroid Build Coastguard Worker if (a != nullptr) {
750*795d594fSAndroid Build Coastguard Worker // Allow narrowing conversion on linear induction in certain cases:
751*795d594fSAndroid Build Coastguard Worker // induction is already at narrow type, or can be made narrower.
752*795d594fSAndroid Build Coastguard Worker if (IsNarrowingIntegralConversion(from, to) &&
753*795d594fSAndroid Build Coastguard Worker a->induction_class == kLinear &&
754*795d594fSAndroid Build Coastguard Worker (a->type == to || IsNarrowingIntegralConversion(a->type, to))) {
755*795d594fSAndroid Build Coastguard Worker return CreateInduction(kLinear, kNop, a->op_a, a->op_b, a->fetch, to);
756*795d594fSAndroid Build Coastguard Worker }
757*795d594fSAndroid Build Coastguard Worker }
758*795d594fSAndroid Build Coastguard Worker return nullptr;
759*795d594fSAndroid Build Coastguard Worker }
760*795d594fSAndroid Build Coastguard Worker
SolvePhi(HInstruction * phi,size_t input_index,size_t adjust_input_size,const ScopedArenaSafeMap<HInstruction *,InductionInfo * > & cycle)761*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolvePhi(
762*795d594fSAndroid Build Coastguard Worker HInstruction* phi,
763*795d594fSAndroid Build Coastguard Worker size_t input_index,
764*795d594fSAndroid Build Coastguard Worker size_t adjust_input_size,
765*795d594fSAndroid Build Coastguard Worker const ScopedArenaSafeMap<HInstruction*, InductionInfo*>& cycle) {
766*795d594fSAndroid Build Coastguard Worker // Match all phi inputs from input_index onwards exactly.
767*795d594fSAndroid Build Coastguard Worker HInputsRef inputs = phi->GetInputs();
768*795d594fSAndroid Build Coastguard Worker DCHECK_LT(input_index, inputs.size());
769*795d594fSAndroid Build Coastguard Worker auto ita = cycle.find(inputs[input_index]);
770*795d594fSAndroid Build Coastguard Worker if (ita != cycle.end()) {
771*795d594fSAndroid Build Coastguard Worker for (size_t i = input_index + 1, n = inputs.size() - adjust_input_size; i < n; i++) {
772*795d594fSAndroid Build Coastguard Worker auto itb = cycle.find(inputs[i]);
773*795d594fSAndroid Build Coastguard Worker if (itb == cycle.end() ||
774*795d594fSAndroid Build Coastguard Worker !HInductionVarAnalysis::InductionEqual(ita->second, itb->second)) {
775*795d594fSAndroid Build Coastguard Worker return nullptr;
776*795d594fSAndroid Build Coastguard Worker }
777*795d594fSAndroid Build Coastguard Worker }
778*795d594fSAndroid Build Coastguard Worker return ita->second;
779*795d594fSAndroid Build Coastguard Worker }
780*795d594fSAndroid Build Coastguard Worker return nullptr;
781*795d594fSAndroid Build Coastguard Worker }
782*795d594fSAndroid Build Coastguard Worker
SolvePhiAllInputs(const HLoopInformation * loop,HInstruction * entry_phi,HInstruction * phi,const ScopedArenaSafeMap<HInstruction *,InductionInfo * > & cycle,DataType::Type type)783*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolvePhiAllInputs(
784*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
785*795d594fSAndroid Build Coastguard Worker HInstruction* entry_phi,
786*795d594fSAndroid Build Coastguard Worker HInstruction* phi,
787*795d594fSAndroid Build Coastguard Worker const ScopedArenaSafeMap<HInstruction*, InductionInfo*>& cycle,
788*795d594fSAndroid Build Coastguard Worker DataType::Type type) {
789*795d594fSAndroid Build Coastguard Worker // Match all phi inputs.
790*795d594fSAndroid Build Coastguard Worker InductionInfo* match = SolvePhi(phi, /*input_index=*/ 0, /*adjust_input_size=*/ 0, cycle);
791*795d594fSAndroid Build Coastguard Worker if (match != nullptr) {
792*795d594fSAndroid Build Coastguard Worker return match;
793*795d594fSAndroid Build Coastguard Worker }
794*795d594fSAndroid Build Coastguard Worker
795*795d594fSAndroid Build Coastguard Worker // Otherwise, try to solve for a periodic seeded from phi onward.
796*795d594fSAndroid Build Coastguard Worker // Only tight multi-statement cycles are considered in order to
797*795d594fSAndroid Build Coastguard Worker // simplify rotating the periodic during the final classification.
798*795d594fSAndroid Build Coastguard Worker if (phi->IsLoopHeaderPhi() && phi->InputCount() == 2) {
799*795d594fSAndroid Build Coastguard Worker InductionInfo* a = LookupInfo(loop, phi->InputAt(0));
800*795d594fSAndroid Build Coastguard Worker if (a != nullptr && a->induction_class == kInvariant) {
801*795d594fSAndroid Build Coastguard Worker if (phi->InputAt(1) == entry_phi) {
802*795d594fSAndroid Build Coastguard Worker InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
803*795d594fSAndroid Build Coastguard Worker return CreateInduction(kPeriodic, kNop, a, initial, /*fetch*/ nullptr, type);
804*795d594fSAndroid Build Coastguard Worker }
805*795d594fSAndroid Build Coastguard Worker InductionInfo* b = SolvePhi(phi, /*input_index=*/ 1, /*adjust_input_size=*/ 0, cycle);
806*795d594fSAndroid Build Coastguard Worker if (b != nullptr && b->induction_class == kPeriodic) {
807*795d594fSAndroid Build Coastguard Worker return CreateInduction(kPeriodic, kNop, a, b, /*fetch*/ nullptr, type);
808*795d594fSAndroid Build Coastguard Worker }
809*795d594fSAndroid Build Coastguard Worker }
810*795d594fSAndroid Build Coastguard Worker }
811*795d594fSAndroid Build Coastguard Worker return nullptr;
812*795d594fSAndroid Build Coastguard Worker }
813*795d594fSAndroid Build Coastguard Worker
SolveAddSub(const HLoopInformation * loop,HInstruction * entry_phi,HInstruction * instruction,HInstruction * x,HInstruction * y,InductionOp op,const ScopedArenaSafeMap<HInstruction *,InductionInfo * > & cycle,DataType::Type type)814*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveAddSub(
815*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
816*795d594fSAndroid Build Coastguard Worker HInstruction* entry_phi,
817*795d594fSAndroid Build Coastguard Worker HInstruction* instruction,
818*795d594fSAndroid Build Coastguard Worker HInstruction* x,
819*795d594fSAndroid Build Coastguard Worker HInstruction* y,
820*795d594fSAndroid Build Coastguard Worker InductionOp op,
821*795d594fSAndroid Build Coastguard Worker const ScopedArenaSafeMap<HInstruction*, InductionInfo*>& cycle,
822*795d594fSAndroid Build Coastguard Worker DataType::Type type) {
823*795d594fSAndroid Build Coastguard Worker const HBasicBlock* context = instruction->GetBlock();
824*795d594fSAndroid Build Coastguard Worker auto main_solve_add_sub = [&]() -> HInductionVarAnalysis::InductionInfo* {
825*795d594fSAndroid Build Coastguard Worker // Solve within a cycle over an addition or subtraction.
826*795d594fSAndroid Build Coastguard Worker InductionInfo* b = LookupInfo(loop, y);
827*795d594fSAndroid Build Coastguard Worker if (b != nullptr) {
828*795d594fSAndroid Build Coastguard Worker if (b->induction_class == kInvariant) {
829*795d594fSAndroid Build Coastguard Worker // Adding or subtracting an invariant value, seeded from phi,
830*795d594fSAndroid Build Coastguard Worker // keeps adding to the stride of the linear induction.
831*795d594fSAndroid Build Coastguard Worker if (x == entry_phi) {
832*795d594fSAndroid Build Coastguard Worker return (op == kAdd) ? b : CreateInvariantOp(context, loop, kNeg, nullptr, b);
833*795d594fSAndroid Build Coastguard Worker }
834*795d594fSAndroid Build Coastguard Worker auto it = cycle.find(x);
835*795d594fSAndroid Build Coastguard Worker if (it != cycle.end()) {
836*795d594fSAndroid Build Coastguard Worker InductionInfo* a = it->second;
837*795d594fSAndroid Build Coastguard Worker if (a->induction_class == kInvariant) {
838*795d594fSAndroid Build Coastguard Worker return CreateInvariantOp(context, loop, op, a, b);
839*795d594fSAndroid Build Coastguard Worker }
840*795d594fSAndroid Build Coastguard Worker }
841*795d594fSAndroid Build Coastguard Worker } else if (b->induction_class == kLinear && b->type == type) {
842*795d594fSAndroid Build Coastguard Worker // Solve within a tight cycle that adds a term that is already classified as a linear
843*795d594fSAndroid Build Coastguard Worker // induction for a polynomial induction k = k + i (represented as sum over linear terms).
844*795d594fSAndroid Build Coastguard Worker if (x == entry_phi &&
845*795d594fSAndroid Build Coastguard Worker entry_phi->InputCount() == 2 &&
846*795d594fSAndroid Build Coastguard Worker instruction == entry_phi->InputAt(1)) {
847*795d594fSAndroid Build Coastguard Worker InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
848*795d594fSAndroid Build Coastguard Worker InductionInfo* new_a = op == kAdd ? b : TransferNeg(context, loop, b, type);
849*795d594fSAndroid Build Coastguard Worker if (new_a != nullptr) {
850*795d594fSAndroid Build Coastguard Worker return CreateInduction(kPolynomial, kNop, new_a, initial, /*fetch*/ nullptr, type);
851*795d594fSAndroid Build Coastguard Worker }
852*795d594fSAndroid Build Coastguard Worker }
853*795d594fSAndroid Build Coastguard Worker }
854*795d594fSAndroid Build Coastguard Worker }
855*795d594fSAndroid Build Coastguard Worker return nullptr;
856*795d594fSAndroid Build Coastguard Worker };
857*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* result = main_solve_add_sub();
858*795d594fSAndroid Build Coastguard Worker if (result == nullptr) {
859*795d594fSAndroid Build Coastguard Worker // Try some alternatives before failing.
860*795d594fSAndroid Build Coastguard Worker if (op == kAdd) {
861*795d594fSAndroid Build Coastguard Worker // Try the other way around for an addition.
862*795d594fSAndroid Build Coastguard Worker std::swap(x, y);
863*795d594fSAndroid Build Coastguard Worker result = main_solve_add_sub();
864*795d594fSAndroid Build Coastguard Worker } else if (op == kSub) {
865*795d594fSAndroid Build Coastguard Worker // Solve within a tight cycle that is formed by exactly two instructions,
866*795d594fSAndroid Build Coastguard Worker // one phi and one update, for a periodic idiom of the form k = c - k.
867*795d594fSAndroid Build Coastguard Worker if (y == entry_phi && entry_phi->InputCount() == 2 && instruction == entry_phi->InputAt(1)) {
868*795d594fSAndroid Build Coastguard Worker InductionInfo* a = LookupInfo(loop, x);
869*795d594fSAndroid Build Coastguard Worker if (a != nullptr && a->induction_class == kInvariant) {
870*795d594fSAndroid Build Coastguard Worker InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
871*795d594fSAndroid Build Coastguard Worker result = CreateInduction(kPeriodic,
872*795d594fSAndroid Build Coastguard Worker kNop,
873*795d594fSAndroid Build Coastguard Worker CreateInvariantOp(context, loop, kSub, a, initial),
874*795d594fSAndroid Build Coastguard Worker initial,
875*795d594fSAndroid Build Coastguard Worker /*fetch*/ nullptr,
876*795d594fSAndroid Build Coastguard Worker type);
877*795d594fSAndroid Build Coastguard Worker }
878*795d594fSAndroid Build Coastguard Worker }
879*795d594fSAndroid Build Coastguard Worker }
880*795d594fSAndroid Build Coastguard Worker }
881*795d594fSAndroid Build Coastguard Worker return result;
882*795d594fSAndroid Build Coastguard Worker }
883*795d594fSAndroid Build Coastguard Worker
SolveOp(const HLoopInformation * loop,HInstruction * entry_phi,HInstruction * instruction,HInstruction * x,HInstruction * y,InductionOp op,DataType::Type type)884*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveOp(const HLoopInformation* loop,
885*795d594fSAndroid Build Coastguard Worker HInstruction* entry_phi,
886*795d594fSAndroid Build Coastguard Worker HInstruction* instruction,
887*795d594fSAndroid Build Coastguard Worker HInstruction* x,
888*795d594fSAndroid Build Coastguard Worker HInstruction* y,
889*795d594fSAndroid Build Coastguard Worker InductionOp op,
890*795d594fSAndroid Build Coastguard Worker DataType::Type type) {
891*795d594fSAndroid Build Coastguard Worker // Solve within a tight cycle for a binary operation k = k op c or, for some op, k = c op k.
892*795d594fSAndroid Build Coastguard Worker if (entry_phi->InputCount() == 2 && instruction == entry_phi->InputAt(1)) {
893*795d594fSAndroid Build Coastguard Worker InductionInfo* c = nullptr;
894*795d594fSAndroid Build Coastguard Worker InductionInfo* b = LookupInfo(loop, y);
895*795d594fSAndroid Build Coastguard Worker if (b != nullptr && b->induction_class == kInvariant && entry_phi == x) {
896*795d594fSAndroid Build Coastguard Worker c = b;
897*795d594fSAndroid Build Coastguard Worker } else if (op != kDiv && op != kRem) {
898*795d594fSAndroid Build Coastguard Worker InductionInfo* a = LookupInfo(loop, x);
899*795d594fSAndroid Build Coastguard Worker if (a != nullptr && a->induction_class == kInvariant && entry_phi == y) {
900*795d594fSAndroid Build Coastguard Worker c = a;
901*795d594fSAndroid Build Coastguard Worker }
902*795d594fSAndroid Build Coastguard Worker }
903*795d594fSAndroid Build Coastguard Worker // Found suitable operand left or right?
904*795d594fSAndroid Build Coastguard Worker if (c != nullptr) {
905*795d594fSAndroid Build Coastguard Worker const HBasicBlock* context = instruction->GetBlock();
906*795d594fSAndroid Build Coastguard Worker InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
907*795d594fSAndroid Build Coastguard Worker switch (op) {
908*795d594fSAndroid Build Coastguard Worker case kMul:
909*795d594fSAndroid Build Coastguard Worker case kDiv:
910*795d594fSAndroid Build Coastguard Worker // Restrict base of geometric induction to direct fetch.
911*795d594fSAndroid Build Coastguard Worker if (c->operation == kFetch) {
912*795d594fSAndroid Build Coastguard Worker return CreateInduction(kGeometric,
913*795d594fSAndroid Build Coastguard Worker op,
914*795d594fSAndroid Build Coastguard Worker initial,
915*795d594fSAndroid Build Coastguard Worker CreateConstant(0, type),
916*795d594fSAndroid Build Coastguard Worker c->fetch,
917*795d594fSAndroid Build Coastguard Worker type);
918*795d594fSAndroid Build Coastguard Worker }
919*795d594fSAndroid Build Coastguard Worker break;
920*795d594fSAndroid Build Coastguard Worker case kRem:
921*795d594fSAndroid Build Coastguard Worker // Idiomatic MOD wrap-around induction.
922*795d594fSAndroid Build Coastguard Worker return CreateInduction(kWrapAround,
923*795d594fSAndroid Build Coastguard Worker kNop,
924*795d594fSAndroid Build Coastguard Worker initial,
925*795d594fSAndroid Build Coastguard Worker CreateInvariantOp(context, loop, kRem, initial, c),
926*795d594fSAndroid Build Coastguard Worker /*fetch*/ nullptr,
927*795d594fSAndroid Build Coastguard Worker type);
928*795d594fSAndroid Build Coastguard Worker case kXor:
929*795d594fSAndroid Build Coastguard Worker // Idiomatic XOR periodic induction.
930*795d594fSAndroid Build Coastguard Worker return CreateInduction(kPeriodic,
931*795d594fSAndroid Build Coastguard Worker kNop,
932*795d594fSAndroid Build Coastguard Worker CreateInvariantOp(context, loop, kXor, initial, c),
933*795d594fSAndroid Build Coastguard Worker initial,
934*795d594fSAndroid Build Coastguard Worker /*fetch*/ nullptr,
935*795d594fSAndroid Build Coastguard Worker type);
936*795d594fSAndroid Build Coastguard Worker default:
937*795d594fSAndroid Build Coastguard Worker LOG(FATAL) << op;
938*795d594fSAndroid Build Coastguard Worker UNREACHABLE();
939*795d594fSAndroid Build Coastguard Worker }
940*795d594fSAndroid Build Coastguard Worker }
941*795d594fSAndroid Build Coastguard Worker }
942*795d594fSAndroid Build Coastguard Worker return nullptr;
943*795d594fSAndroid Build Coastguard Worker }
944*795d594fSAndroid Build Coastguard Worker
SolveTest(const HLoopInformation * loop,HInstruction * entry_phi,HInstruction * instruction,int64_t opposite_value,DataType::Type type)945*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveTest(const HLoopInformation* loop,
946*795d594fSAndroid Build Coastguard Worker HInstruction* entry_phi,
947*795d594fSAndroid Build Coastguard Worker HInstruction* instruction,
948*795d594fSAndroid Build Coastguard Worker int64_t opposite_value,
949*795d594fSAndroid Build Coastguard Worker DataType::Type type) {
950*795d594fSAndroid Build Coastguard Worker // Detect hidden XOR construction in x = (x == false) or x = (x != true).
951*795d594fSAndroid Build Coastguard Worker const HBasicBlock* context = instruction->GetBlock();
952*795d594fSAndroid Build Coastguard Worker HInstruction* x = instruction->InputAt(0);
953*795d594fSAndroid Build Coastguard Worker HInstruction* y = instruction->InputAt(1);
954*795d594fSAndroid Build Coastguard Worker int64_t value = -1;
955*795d594fSAndroid Build Coastguard Worker if (IsExact(context, loop, LookupInfo(loop, x), &value) && value == opposite_value) {
956*795d594fSAndroid Build Coastguard Worker return SolveOp(loop, entry_phi, instruction, graph_->GetIntConstant(1), y, kXor, type);
957*795d594fSAndroid Build Coastguard Worker } else if (IsExact(context, loop, LookupInfo(loop, y), &value) && value == opposite_value) {
958*795d594fSAndroid Build Coastguard Worker return SolveOp(loop, entry_phi, instruction, x, graph_->GetIntConstant(1), kXor, type);
959*795d594fSAndroid Build Coastguard Worker }
960*795d594fSAndroid Build Coastguard Worker return nullptr;
961*795d594fSAndroid Build Coastguard Worker }
962*795d594fSAndroid Build Coastguard Worker
SolveConversion(const HLoopInformation * loop,HInstruction * entry_phi,HTypeConversion * conversion,const ScopedArenaSafeMap<HInstruction *,InductionInfo * > & cycle,DataType::Type * type)963*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveConversion(
964*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
965*795d594fSAndroid Build Coastguard Worker HInstruction* entry_phi,
966*795d594fSAndroid Build Coastguard Worker HTypeConversion* conversion,
967*795d594fSAndroid Build Coastguard Worker const ScopedArenaSafeMap<HInstruction*, InductionInfo*>& cycle,
968*795d594fSAndroid Build Coastguard Worker /*inout*/ DataType::Type* type) {
969*795d594fSAndroid Build Coastguard Worker DataType::Type from = conversion->GetInputType();
970*795d594fSAndroid Build Coastguard Worker DataType::Type to = conversion->GetResultType();
971*795d594fSAndroid Build Coastguard Worker // A narrowing conversion is allowed as *last* operation of the cycle of a linear induction
972*795d594fSAndroid Build Coastguard Worker // with an initial value that fits the type, provided that the narrowest encountered type is
973*795d594fSAndroid Build Coastguard Worker // recorded with the induction to account for the precision loss. The narrower induction does
974*795d594fSAndroid Build Coastguard Worker // *not* transfer to any wider operations, however, since these may yield out-of-type values
975*795d594fSAndroid Build Coastguard Worker if (entry_phi->InputCount() == 2 && conversion == entry_phi->InputAt(1)) {
976*795d594fSAndroid Build Coastguard Worker int64_t min = DataType::MinValueOfIntegralType(to);
977*795d594fSAndroid Build Coastguard Worker int64_t max = DataType::MaxValueOfIntegralType(to);
978*795d594fSAndroid Build Coastguard Worker int64_t value = 0;
979*795d594fSAndroid Build Coastguard Worker const HBasicBlock* context = conversion->GetBlock();
980*795d594fSAndroid Build Coastguard Worker InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
981*795d594fSAndroid Build Coastguard Worker if (IsNarrowingIntegralConversion(from, to) &&
982*795d594fSAndroid Build Coastguard Worker IsAtLeast(context, loop, initial, &value) && value >= min &&
983*795d594fSAndroid Build Coastguard Worker IsAtMost(context, loop, initial, &value) && value <= max) {
984*795d594fSAndroid Build Coastguard Worker auto it = cycle.find(conversion->GetInput());
985*795d594fSAndroid Build Coastguard Worker if (it != cycle.end() && it->second->induction_class == kInvariant) {
986*795d594fSAndroid Build Coastguard Worker *type = to;
987*795d594fSAndroid Build Coastguard Worker return it->second;
988*795d594fSAndroid Build Coastguard Worker }
989*795d594fSAndroid Build Coastguard Worker }
990*795d594fSAndroid Build Coastguard Worker }
991*795d594fSAndroid Build Coastguard Worker return nullptr;
992*795d594fSAndroid Build Coastguard Worker }
993*795d594fSAndroid Build Coastguard Worker
994*795d594fSAndroid Build Coastguard Worker //
995*795d594fSAndroid Build Coastguard Worker // Loop trip count analysis methods.
996*795d594fSAndroid Build Coastguard Worker //
997*795d594fSAndroid Build Coastguard Worker
VisitControl(const HLoopInformation * loop)998*795d594fSAndroid Build Coastguard Worker void HInductionVarAnalysis::VisitControl(const HLoopInformation* loop) {
999*795d594fSAndroid Build Coastguard Worker HInstruction* control = loop->GetHeader()->GetLastInstruction();
1000*795d594fSAndroid Build Coastguard Worker if (control->IsIf()) {
1001*795d594fSAndroid Build Coastguard Worker HIf* ifs = control->AsIf();
1002*795d594fSAndroid Build Coastguard Worker HBasicBlock* if_true = ifs->IfTrueSuccessor();
1003*795d594fSAndroid Build Coastguard Worker HBasicBlock* if_false = ifs->IfFalseSuccessor();
1004*795d594fSAndroid Build Coastguard Worker HInstruction* if_expr = ifs->InputAt(0);
1005*795d594fSAndroid Build Coastguard Worker // Determine if loop has following structure in header.
1006*795d594fSAndroid Build Coastguard Worker // loop-header: ....
1007*795d594fSAndroid Build Coastguard Worker // if (condition) goto X
1008*795d594fSAndroid Build Coastguard Worker if (if_expr->IsCondition()) {
1009*795d594fSAndroid Build Coastguard Worker HCondition* condition = if_expr->AsCondition();
1010*795d594fSAndroid Build Coastguard Worker const HBasicBlock* context = condition->GetBlock();
1011*795d594fSAndroid Build Coastguard Worker InductionInfo* a = LookupInfo(loop, condition->InputAt(0));
1012*795d594fSAndroid Build Coastguard Worker InductionInfo* b = LookupInfo(loop, condition->InputAt(1));
1013*795d594fSAndroid Build Coastguard Worker DataType::Type type = ImplicitConversion(condition->InputAt(0)->GetType());
1014*795d594fSAndroid Build Coastguard Worker // Determine if the loop control uses a known sequence on an if-exit (X outside) or on
1015*795d594fSAndroid Build Coastguard Worker // an if-iterate (X inside), expressed as if-iterate when passed into VisitCondition().
1016*795d594fSAndroid Build Coastguard Worker if (a == nullptr || b == nullptr) {
1017*795d594fSAndroid Build Coastguard Worker return; // Loop control is not a sequence.
1018*795d594fSAndroid Build Coastguard Worker } else if (if_true->GetLoopInformation() != loop && if_false->GetLoopInformation() == loop) {
1019*795d594fSAndroid Build Coastguard Worker VisitCondition(context, loop, if_false, a, b, type, condition->GetOppositeCondition());
1020*795d594fSAndroid Build Coastguard Worker } else if (if_true->GetLoopInformation() == loop && if_false->GetLoopInformation() != loop) {
1021*795d594fSAndroid Build Coastguard Worker VisitCondition(context, loop, if_true, a, b, type, condition->GetCondition());
1022*795d594fSAndroid Build Coastguard Worker }
1023*795d594fSAndroid Build Coastguard Worker }
1024*795d594fSAndroid Build Coastguard Worker }
1025*795d594fSAndroid Build Coastguard Worker }
1026*795d594fSAndroid Build Coastguard Worker
VisitCondition(const HBasicBlock * context,const HLoopInformation * loop,HBasicBlock * body,InductionInfo * a,InductionInfo * b,DataType::Type type,IfCondition cmp)1027*795d594fSAndroid Build Coastguard Worker void HInductionVarAnalysis::VisitCondition(const HBasicBlock* context,
1028*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
1029*795d594fSAndroid Build Coastguard Worker HBasicBlock* body,
1030*795d594fSAndroid Build Coastguard Worker InductionInfo* a,
1031*795d594fSAndroid Build Coastguard Worker InductionInfo* b,
1032*795d594fSAndroid Build Coastguard Worker DataType::Type type,
1033*795d594fSAndroid Build Coastguard Worker IfCondition cmp) {
1034*795d594fSAndroid Build Coastguard Worker if (a->induction_class == kInvariant && b->induction_class == kLinear) {
1035*795d594fSAndroid Build Coastguard Worker // Swap condition if induction is at right-hand-side (e.g. U > i is same as i < U).
1036*795d594fSAndroid Build Coastguard Worker switch (cmp) {
1037*795d594fSAndroid Build Coastguard Worker case kCondLT: VisitCondition(context, loop, body, b, a, type, kCondGT); break;
1038*795d594fSAndroid Build Coastguard Worker case kCondLE: VisitCondition(context, loop, body, b, a, type, kCondGE); break;
1039*795d594fSAndroid Build Coastguard Worker case kCondGT: VisitCondition(context, loop, body, b, a, type, kCondLT); break;
1040*795d594fSAndroid Build Coastguard Worker case kCondGE: VisitCondition(context, loop, body, b, a, type, kCondLE); break;
1041*795d594fSAndroid Build Coastguard Worker case kCondNE: VisitCondition(context, loop, body, b, a, type, kCondNE); break;
1042*795d594fSAndroid Build Coastguard Worker default: break;
1043*795d594fSAndroid Build Coastguard Worker }
1044*795d594fSAndroid Build Coastguard Worker } else if (a->induction_class == kLinear && b->induction_class == kInvariant) {
1045*795d594fSAndroid Build Coastguard Worker // Analyze condition with induction at left-hand-side (e.g. i < U).
1046*795d594fSAndroid Build Coastguard Worker InductionInfo* lower_expr = a->op_b;
1047*795d594fSAndroid Build Coastguard Worker InductionInfo* upper_expr = b;
1048*795d594fSAndroid Build Coastguard Worker InductionInfo* stride_expr = a->op_a;
1049*795d594fSAndroid Build Coastguard Worker // Test for constant stride and integral condition.
1050*795d594fSAndroid Build Coastguard Worker int64_t stride_value = 0;
1051*795d594fSAndroid Build Coastguard Worker if (!IsExact(context, loop, stride_expr, &stride_value)) {
1052*795d594fSAndroid Build Coastguard Worker return; // unknown stride
1053*795d594fSAndroid Build Coastguard Worker } else if (type != DataType::Type::kInt32 && type != DataType::Type::kInt64) {
1054*795d594fSAndroid Build Coastguard Worker return; // not integral
1055*795d594fSAndroid Build Coastguard Worker }
1056*795d594fSAndroid Build Coastguard Worker // Since loops with a i != U condition will not be normalized by the method below, first
1057*795d594fSAndroid Build Coastguard Worker // try to rewrite a break-loop with terminating condition i != U into an equivalent loop
1058*795d594fSAndroid Build Coastguard Worker // with non-strict end condition i <= U or i >= U if such a rewriting is possible and safe.
1059*795d594fSAndroid Build Coastguard Worker if (cmp == kCondNE && RewriteBreakLoop(context, loop, body, stride_value, type)) {
1060*795d594fSAndroid Build Coastguard Worker cmp = stride_value > 0 ? kCondLE : kCondGE;
1061*795d594fSAndroid Build Coastguard Worker }
1062*795d594fSAndroid Build Coastguard Worker // If this rewriting failed, try to rewrite condition i != U into strict end condition i < U
1063*795d594fSAndroid Build Coastguard Worker // or i > U if this end condition is reached exactly (tested by verifying if the loop has a
1064*795d594fSAndroid Build Coastguard Worker // unit stride and the non-strict condition would be always taken).
1065*795d594fSAndroid Build Coastguard Worker if (cmp == kCondNE &&
1066*795d594fSAndroid Build Coastguard Worker ((stride_value == +1 && IsTaken(context, loop, lower_expr, upper_expr, kCondLE)) ||
1067*795d594fSAndroid Build Coastguard Worker (stride_value == -1 && IsTaken(context, loop, lower_expr, upper_expr, kCondGE)))) {
1068*795d594fSAndroid Build Coastguard Worker cmp = stride_value > 0 ? kCondLT : kCondGT;
1069*795d594fSAndroid Build Coastguard Worker }
1070*795d594fSAndroid Build Coastguard Worker // A mismatch between the type of condition and the induction is only allowed if the,
1071*795d594fSAndroid Build Coastguard Worker // necessarily narrower, induction range fits the narrower control.
1072*795d594fSAndroid Build Coastguard Worker if (type != a->type &&
1073*795d594fSAndroid Build Coastguard Worker !FitsNarrowerControl(context, loop, lower_expr, upper_expr, stride_value, a->type, cmp)) {
1074*795d594fSAndroid Build Coastguard Worker return; // mismatched type
1075*795d594fSAndroid Build Coastguard Worker }
1076*795d594fSAndroid Build Coastguard Worker // Normalize a linear loop control with a nonzero stride:
1077*795d594fSAndroid Build Coastguard Worker // stride > 0, either i < U or i <= U
1078*795d594fSAndroid Build Coastguard Worker // stride < 0, either i > U or i >= U
1079*795d594fSAndroid Build Coastguard Worker if ((stride_value > 0 && (cmp == kCondLT || cmp == kCondLE)) ||
1080*795d594fSAndroid Build Coastguard Worker (stride_value < 0 && (cmp == kCondGT || cmp == kCondGE))) {
1081*795d594fSAndroid Build Coastguard Worker VisitTripCount(context, loop, lower_expr, upper_expr, stride_expr, stride_value, type, cmp);
1082*795d594fSAndroid Build Coastguard Worker }
1083*795d594fSAndroid Build Coastguard Worker }
1084*795d594fSAndroid Build Coastguard Worker }
1085*795d594fSAndroid Build Coastguard Worker
VisitTripCount(const HBasicBlock * context,const HLoopInformation * loop,InductionInfo * lower_expr,InductionInfo * upper_expr,InductionInfo * stride_expr,int64_t stride_value,DataType::Type type,IfCondition cmp)1086*795d594fSAndroid Build Coastguard Worker void HInductionVarAnalysis::VisitTripCount(const HBasicBlock* context,
1087*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
1088*795d594fSAndroid Build Coastguard Worker InductionInfo* lower_expr,
1089*795d594fSAndroid Build Coastguard Worker InductionInfo* upper_expr,
1090*795d594fSAndroid Build Coastguard Worker InductionInfo* stride_expr,
1091*795d594fSAndroid Build Coastguard Worker int64_t stride_value,
1092*795d594fSAndroid Build Coastguard Worker DataType::Type type,
1093*795d594fSAndroid Build Coastguard Worker IfCondition cmp) {
1094*795d594fSAndroid Build Coastguard Worker // Any loop of the general form:
1095*795d594fSAndroid Build Coastguard Worker //
1096*795d594fSAndroid Build Coastguard Worker // for (i = L; i <= U; i += S) // S > 0
1097*795d594fSAndroid Build Coastguard Worker // or for (i = L; i >= U; i += S) // S < 0
1098*795d594fSAndroid Build Coastguard Worker // .. i ..
1099*795d594fSAndroid Build Coastguard Worker //
1100*795d594fSAndroid Build Coastguard Worker // can be normalized into:
1101*795d594fSAndroid Build Coastguard Worker //
1102*795d594fSAndroid Build Coastguard Worker // for (n = 0; n < TC; n++) // where TC = (U + S - L) / S
1103*795d594fSAndroid Build Coastguard Worker // .. L + S * n ..
1104*795d594fSAndroid Build Coastguard Worker //
1105*795d594fSAndroid Build Coastguard Worker // taking the following into consideration:
1106*795d594fSAndroid Build Coastguard Worker //
1107*795d594fSAndroid Build Coastguard Worker // (1) Using the same precision, the TC (trip-count) expression should be interpreted as
1108*795d594fSAndroid Build Coastguard Worker // an unsigned entity, for example, as in the following loop that uses the full range:
1109*795d594fSAndroid Build Coastguard Worker // for (int i = INT_MIN; i < INT_MAX; i++) // TC = UINT_MAX
1110*795d594fSAndroid Build Coastguard Worker // (2) The TC is only valid if the loop is taken, otherwise TC = 0, as in:
1111*795d594fSAndroid Build Coastguard Worker // for (int i = 12; i < U; i++) // TC = 0 when U <= 12
1112*795d594fSAndroid Build Coastguard Worker // If this cannot be determined at compile-time, the TC is only valid within the
1113*795d594fSAndroid Build Coastguard Worker // loop-body proper, not the loop-header unless enforced with an explicit taken-test.
1114*795d594fSAndroid Build Coastguard Worker // (3) The TC is only valid if the loop is finite, otherwise TC has no value, as in:
1115*795d594fSAndroid Build Coastguard Worker // for (int i = 0; i <= U; i++) // TC = Inf when U = INT_MAX
1116*795d594fSAndroid Build Coastguard Worker // If this cannot be determined at compile-time, the TC is only valid when enforced
1117*795d594fSAndroid Build Coastguard Worker // with an explicit finite-test.
1118*795d594fSAndroid Build Coastguard Worker // (4) For loops which early-exits, the TC forms an upper bound, as in:
1119*795d594fSAndroid Build Coastguard Worker // for (int i = 0; i < 10 && ....; i++) // TC <= 10
1120*795d594fSAndroid Build Coastguard Worker InductionInfo* trip_count = upper_expr;
1121*795d594fSAndroid Build Coastguard Worker const bool is_taken = IsTaken(context, loop, lower_expr, upper_expr, cmp);
1122*795d594fSAndroid Build Coastguard Worker const bool is_finite = IsFinite(context, loop, upper_expr, stride_value, type, cmp);
1123*795d594fSAndroid Build Coastguard Worker const bool cancels = (cmp == kCondLT || cmp == kCondGT) && std::abs(stride_value) == 1;
1124*795d594fSAndroid Build Coastguard Worker if (!cancels) {
1125*795d594fSAndroid Build Coastguard Worker // Convert exclusive integral inequality into inclusive integral inequality,
1126*795d594fSAndroid Build Coastguard Worker // viz. condition i < U is i <= U - 1 and condition i > U is i >= U + 1.
1127*795d594fSAndroid Build Coastguard Worker if (cmp == kCondLT) {
1128*795d594fSAndroid Build Coastguard Worker trip_count = CreateInvariantOp(context, loop, kSub, trip_count, CreateConstant(1, type));
1129*795d594fSAndroid Build Coastguard Worker } else if (cmp == kCondGT) {
1130*795d594fSAndroid Build Coastguard Worker trip_count = CreateInvariantOp(context, loop, kAdd, trip_count, CreateConstant(1, type));
1131*795d594fSAndroid Build Coastguard Worker }
1132*795d594fSAndroid Build Coastguard Worker // Compensate for stride.
1133*795d594fSAndroid Build Coastguard Worker trip_count = CreateInvariantOp(context, loop, kAdd, trip_count, stride_expr);
1134*795d594fSAndroid Build Coastguard Worker }
1135*795d594fSAndroid Build Coastguard Worker trip_count = CreateInvariantOp(context, loop, kSub, trip_count, lower_expr);
1136*795d594fSAndroid Build Coastguard Worker trip_count = CreateInvariantOp(context, loop, kDiv, trip_count, stride_expr);
1137*795d594fSAndroid Build Coastguard Worker // Assign the trip-count expression to the loop control. Clients that use the information
1138*795d594fSAndroid Build Coastguard Worker // should be aware that the expression is only valid under the conditions listed above.
1139*795d594fSAndroid Build Coastguard Worker InductionOp tcKind = kTripCountInBodyUnsafe; // needs both tests
1140*795d594fSAndroid Build Coastguard Worker if (is_taken && is_finite) {
1141*795d594fSAndroid Build Coastguard Worker tcKind = kTripCountInLoop; // needs neither test
1142*795d594fSAndroid Build Coastguard Worker } else if (is_finite) {
1143*795d594fSAndroid Build Coastguard Worker tcKind = kTripCountInBody; // needs taken-test
1144*795d594fSAndroid Build Coastguard Worker } else if (is_taken) {
1145*795d594fSAndroid Build Coastguard Worker tcKind = kTripCountInLoopUnsafe; // needs finite-test
1146*795d594fSAndroid Build Coastguard Worker }
1147*795d594fSAndroid Build Coastguard Worker InductionOp op = kNop;
1148*795d594fSAndroid Build Coastguard Worker switch (cmp) {
1149*795d594fSAndroid Build Coastguard Worker case kCondLT: op = kLT; break;
1150*795d594fSAndroid Build Coastguard Worker case kCondLE: op = kLE; break;
1151*795d594fSAndroid Build Coastguard Worker case kCondGT: op = kGT; break;
1152*795d594fSAndroid Build Coastguard Worker case kCondGE: op = kGE; break;
1153*795d594fSAndroid Build Coastguard Worker default: LOG(FATAL) << "CONDITION UNREACHABLE";
1154*795d594fSAndroid Build Coastguard Worker }
1155*795d594fSAndroid Build Coastguard Worker // Associate trip count with control instruction, rather than the condition (even
1156*795d594fSAndroid Build Coastguard Worker // though it's its use) since former provides a convenient use-free placeholder.
1157*795d594fSAndroid Build Coastguard Worker HInstruction* control = loop->GetHeader()->GetLastInstruction();
1158*795d594fSAndroid Build Coastguard Worker InductionInfo* taken_test = CreateInvariantOp(context, loop, op, lower_expr, upper_expr);
1159*795d594fSAndroid Build Coastguard Worker DCHECK(control->IsIf());
1160*795d594fSAndroid Build Coastguard Worker AssignInfo(loop, control, CreateTripCount(tcKind, trip_count, taken_test, type));
1161*795d594fSAndroid Build Coastguard Worker }
1162*795d594fSAndroid Build Coastguard Worker
IsTaken(const HBasicBlock * context,const HLoopInformation * loop,InductionInfo * lower_expr,InductionInfo * upper_expr,IfCondition cmp)1163*795d594fSAndroid Build Coastguard Worker bool HInductionVarAnalysis::IsTaken(const HBasicBlock* context,
1164*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
1165*795d594fSAndroid Build Coastguard Worker InductionInfo* lower_expr,
1166*795d594fSAndroid Build Coastguard Worker InductionInfo* upper_expr,
1167*795d594fSAndroid Build Coastguard Worker IfCondition cmp) {
1168*795d594fSAndroid Build Coastguard Worker int64_t lower_value;
1169*795d594fSAndroid Build Coastguard Worker int64_t upper_value;
1170*795d594fSAndroid Build Coastguard Worker switch (cmp) {
1171*795d594fSAndroid Build Coastguard Worker case kCondLT:
1172*795d594fSAndroid Build Coastguard Worker return IsAtMost(context, loop, lower_expr, &lower_value)
1173*795d594fSAndroid Build Coastguard Worker && IsAtLeast(context, loop, upper_expr, &upper_value)
1174*795d594fSAndroid Build Coastguard Worker && lower_value < upper_value;
1175*795d594fSAndroid Build Coastguard Worker case kCondLE:
1176*795d594fSAndroid Build Coastguard Worker return IsAtMost(context, loop, lower_expr, &lower_value)
1177*795d594fSAndroid Build Coastguard Worker && IsAtLeast(context, loop, upper_expr, &upper_value)
1178*795d594fSAndroid Build Coastguard Worker && lower_value <= upper_value;
1179*795d594fSAndroid Build Coastguard Worker case kCondGT:
1180*795d594fSAndroid Build Coastguard Worker return IsAtLeast(context, loop, lower_expr, &lower_value)
1181*795d594fSAndroid Build Coastguard Worker && IsAtMost(context, loop, upper_expr, &upper_value)
1182*795d594fSAndroid Build Coastguard Worker && lower_value > upper_value;
1183*795d594fSAndroid Build Coastguard Worker case kCondGE:
1184*795d594fSAndroid Build Coastguard Worker return IsAtLeast(context, loop, lower_expr, &lower_value)
1185*795d594fSAndroid Build Coastguard Worker && IsAtMost(context, loop, upper_expr, &upper_value)
1186*795d594fSAndroid Build Coastguard Worker && lower_value >= upper_value;
1187*795d594fSAndroid Build Coastguard Worker default:
1188*795d594fSAndroid Build Coastguard Worker LOG(FATAL) << "CONDITION UNREACHABLE";
1189*795d594fSAndroid Build Coastguard Worker UNREACHABLE();
1190*795d594fSAndroid Build Coastguard Worker }
1191*795d594fSAndroid Build Coastguard Worker }
1192*795d594fSAndroid Build Coastguard Worker
IsFinite(const HBasicBlock * context,const HLoopInformation * loop,InductionInfo * upper_expr,int64_t stride_value,DataType::Type type,IfCondition cmp)1193*795d594fSAndroid Build Coastguard Worker bool HInductionVarAnalysis::IsFinite(const HBasicBlock* context,
1194*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
1195*795d594fSAndroid Build Coastguard Worker InductionInfo* upper_expr,
1196*795d594fSAndroid Build Coastguard Worker int64_t stride_value,
1197*795d594fSAndroid Build Coastguard Worker DataType::Type type,
1198*795d594fSAndroid Build Coastguard Worker IfCondition cmp) {
1199*795d594fSAndroid Build Coastguard Worker int64_t min = DataType::MinValueOfIntegralType(type);
1200*795d594fSAndroid Build Coastguard Worker int64_t max = DataType::MaxValueOfIntegralType(type);
1201*795d594fSAndroid Build Coastguard Worker // Some rules under which it is certain at compile-time that the loop is finite.
1202*795d594fSAndroid Build Coastguard Worker int64_t value;
1203*795d594fSAndroid Build Coastguard Worker switch (cmp) {
1204*795d594fSAndroid Build Coastguard Worker case kCondLT:
1205*795d594fSAndroid Build Coastguard Worker return stride_value == 1 ||
1206*795d594fSAndroid Build Coastguard Worker (IsAtMost(context, loop, upper_expr, &value) && value <= (max - stride_value + 1));
1207*795d594fSAndroid Build Coastguard Worker case kCondLE:
1208*795d594fSAndroid Build Coastguard Worker return (IsAtMost(context, loop, upper_expr, &value) && value <= (max - stride_value));
1209*795d594fSAndroid Build Coastguard Worker case kCondGT:
1210*795d594fSAndroid Build Coastguard Worker return stride_value == -1 ||
1211*795d594fSAndroid Build Coastguard Worker (IsAtLeast(context, loop, upper_expr, &value) && value >= (min - stride_value - 1));
1212*795d594fSAndroid Build Coastguard Worker case kCondGE:
1213*795d594fSAndroid Build Coastguard Worker return (IsAtLeast(context, loop, upper_expr, &value) && value >= (min - stride_value));
1214*795d594fSAndroid Build Coastguard Worker default:
1215*795d594fSAndroid Build Coastguard Worker LOG(FATAL) << "CONDITION UNREACHABLE";
1216*795d594fSAndroid Build Coastguard Worker UNREACHABLE();
1217*795d594fSAndroid Build Coastguard Worker }
1218*795d594fSAndroid Build Coastguard Worker }
1219*795d594fSAndroid Build Coastguard Worker
FitsNarrowerControl(const HBasicBlock * context,const HLoopInformation * loop,InductionInfo * lower_expr,InductionInfo * upper_expr,int64_t stride_value,DataType::Type type,IfCondition cmp)1220*795d594fSAndroid Build Coastguard Worker bool HInductionVarAnalysis::FitsNarrowerControl(const HBasicBlock* context,
1221*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
1222*795d594fSAndroid Build Coastguard Worker InductionInfo* lower_expr,
1223*795d594fSAndroid Build Coastguard Worker InductionInfo* upper_expr,
1224*795d594fSAndroid Build Coastguard Worker int64_t stride_value,
1225*795d594fSAndroid Build Coastguard Worker DataType::Type type,
1226*795d594fSAndroid Build Coastguard Worker IfCondition cmp) {
1227*795d594fSAndroid Build Coastguard Worker int64_t min = DataType::MinValueOfIntegralType(type);
1228*795d594fSAndroid Build Coastguard Worker int64_t max = DataType::MaxValueOfIntegralType(type);
1229*795d594fSAndroid Build Coastguard Worker // Inclusive test need one extra.
1230*795d594fSAndroid Build Coastguard Worker if (stride_value != 1 && stride_value != -1) {
1231*795d594fSAndroid Build Coastguard Worker return false; // non-unit stride
1232*795d594fSAndroid Build Coastguard Worker } else if (cmp == kCondLE) {
1233*795d594fSAndroid Build Coastguard Worker max--;
1234*795d594fSAndroid Build Coastguard Worker } else if (cmp == kCondGE) {
1235*795d594fSAndroid Build Coastguard Worker min++;
1236*795d594fSAndroid Build Coastguard Worker }
1237*795d594fSAndroid Build Coastguard Worker // Do both bounds fit the range?
1238*795d594fSAndroid Build Coastguard Worker int64_t value = 0;
1239*795d594fSAndroid Build Coastguard Worker return IsAtLeast(context, loop, lower_expr, &value) && value >= min &&
1240*795d594fSAndroid Build Coastguard Worker IsAtMost(context, loop, lower_expr, &value) && value <= max &&
1241*795d594fSAndroid Build Coastguard Worker IsAtLeast(context, loop, upper_expr, &value) && value >= min &&
1242*795d594fSAndroid Build Coastguard Worker IsAtMost(context, loop, upper_expr, &value) && value <= max;
1243*795d594fSAndroid Build Coastguard Worker }
1244*795d594fSAndroid Build Coastguard Worker
RewriteBreakLoop(const HBasicBlock * context,const HLoopInformation * loop,HBasicBlock * body,int64_t stride_value,DataType::Type type)1245*795d594fSAndroid Build Coastguard Worker bool HInductionVarAnalysis::RewriteBreakLoop(const HBasicBlock* context,
1246*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
1247*795d594fSAndroid Build Coastguard Worker HBasicBlock* body,
1248*795d594fSAndroid Build Coastguard Worker int64_t stride_value,
1249*795d594fSAndroid Build Coastguard Worker DataType::Type type) {
1250*795d594fSAndroid Build Coastguard Worker // Only accept unit stride.
1251*795d594fSAndroid Build Coastguard Worker if (std::abs(stride_value) != 1) {
1252*795d594fSAndroid Build Coastguard Worker return false;
1253*795d594fSAndroid Build Coastguard Worker }
1254*795d594fSAndroid Build Coastguard Worker // Simple terminating i != U condition, used nowhere else.
1255*795d594fSAndroid Build Coastguard Worker HIf* ifs = loop->GetHeader()->GetLastInstruction()->AsIf();
1256*795d594fSAndroid Build Coastguard Worker HInstruction* cond = ifs->InputAt(0);
1257*795d594fSAndroid Build Coastguard Worker if (ifs->GetPrevious() != cond || !cond->HasOnlyOneNonEnvironmentUse()) {
1258*795d594fSAndroid Build Coastguard Worker return false;
1259*795d594fSAndroid Build Coastguard Worker }
1260*795d594fSAndroid Build Coastguard Worker int c = LookupInfo(loop, cond->InputAt(0))->induction_class == kLinear ? 0 : 1;
1261*795d594fSAndroid Build Coastguard Worker HInstruction* index = cond->InputAt(c);
1262*795d594fSAndroid Build Coastguard Worker HInstruction* upper = cond->InputAt(1 - c);
1263*795d594fSAndroid Build Coastguard Worker // Safe to rewrite into i <= U?
1264*795d594fSAndroid Build Coastguard Worker IfCondition cmp = stride_value > 0 ? kCondLE : kCondGE;
1265*795d594fSAndroid Build Coastguard Worker if (!index->IsPhi() ||
1266*795d594fSAndroid Build Coastguard Worker !IsFinite(context, loop, LookupInfo(loop, upper), stride_value, type, cmp)) {
1267*795d594fSAndroid Build Coastguard Worker return false;
1268*795d594fSAndroid Build Coastguard Worker }
1269*795d594fSAndroid Build Coastguard Worker // Body consists of update to index i only, used nowhere else.
1270*795d594fSAndroid Build Coastguard Worker if (body->GetSuccessors().size() != 1 ||
1271*795d594fSAndroid Build Coastguard Worker body->GetSingleSuccessor() != loop->GetHeader() ||
1272*795d594fSAndroid Build Coastguard Worker !body->GetPhis().IsEmpty() ||
1273*795d594fSAndroid Build Coastguard Worker body->GetInstructions().IsEmpty() ||
1274*795d594fSAndroid Build Coastguard Worker body->GetFirstInstruction() != index->InputAt(1) ||
1275*795d594fSAndroid Build Coastguard Worker !body->GetFirstInstruction()->HasOnlyOneNonEnvironmentUse() ||
1276*795d594fSAndroid Build Coastguard Worker !body->GetFirstInstruction()->GetNext()->IsGoto()) {
1277*795d594fSAndroid Build Coastguard Worker return false;
1278*795d594fSAndroid Build Coastguard Worker }
1279*795d594fSAndroid Build Coastguard Worker // Always taken or guarded by enclosing condition.
1280*795d594fSAndroid Build Coastguard Worker if (!IsTaken(context, loop, LookupInfo(loop, index)->op_b, LookupInfo(loop, upper), cmp) &&
1281*795d594fSAndroid Build Coastguard Worker !IsGuardedBy(loop, cmp, index->InputAt(0), upper)) {
1282*795d594fSAndroid Build Coastguard Worker return false;
1283*795d594fSAndroid Build Coastguard Worker }
1284*795d594fSAndroid Build Coastguard Worker // Test if break-loop body can be written, and do so on success.
1285*795d594fSAndroid Build Coastguard Worker if (RewriteBreakLoopBody(loop, body, cond, index, upper, /*rewrite*/ false)) {
1286*795d594fSAndroid Build Coastguard Worker RewriteBreakLoopBody(loop, body, cond, index, upper, /*rewrite*/ true);
1287*795d594fSAndroid Build Coastguard Worker } else {
1288*795d594fSAndroid Build Coastguard Worker return false;
1289*795d594fSAndroid Build Coastguard Worker }
1290*795d594fSAndroid Build Coastguard Worker // Rewrite condition in HIR.
1291*795d594fSAndroid Build Coastguard Worker if (ifs->IfTrueSuccessor() != body) {
1292*795d594fSAndroid Build Coastguard Worker cmp = (cmp == kCondLE) ? kCondGT : kCondLT;
1293*795d594fSAndroid Build Coastguard Worker }
1294*795d594fSAndroid Build Coastguard Worker HInstruction* rep = nullptr;
1295*795d594fSAndroid Build Coastguard Worker switch (cmp) {
1296*795d594fSAndroid Build Coastguard Worker case kCondLT: rep = new (graph_->GetAllocator()) HLessThan(index, upper); break;
1297*795d594fSAndroid Build Coastguard Worker case kCondGT: rep = new (graph_->GetAllocator()) HGreaterThan(index, upper); break;
1298*795d594fSAndroid Build Coastguard Worker case kCondLE: rep = new (graph_->GetAllocator()) HLessThanOrEqual(index, upper); break;
1299*795d594fSAndroid Build Coastguard Worker case kCondGE: rep = new (graph_->GetAllocator()) HGreaterThanOrEqual(index, upper); break;
1300*795d594fSAndroid Build Coastguard Worker default: LOG(FATAL) << cmp; UNREACHABLE();
1301*795d594fSAndroid Build Coastguard Worker }
1302*795d594fSAndroid Build Coastguard Worker loop->GetHeader()->ReplaceAndRemoveInstructionWith(cond, rep);
1303*795d594fSAndroid Build Coastguard Worker return true;
1304*795d594fSAndroid Build Coastguard Worker }
1305*795d594fSAndroid Build Coastguard Worker
1306*795d594fSAndroid Build Coastguard Worker //
1307*795d594fSAndroid Build Coastguard Worker // Helper methods.
1308*795d594fSAndroid Build Coastguard Worker //
1309*795d594fSAndroid Build Coastguard Worker
AssignInfo(const HLoopInformation * loop,HInstruction * instruction,InductionInfo * info)1310*795d594fSAndroid Build Coastguard Worker void HInductionVarAnalysis::AssignInfo(const HLoopInformation* loop,
1311*795d594fSAndroid Build Coastguard Worker HInstruction* instruction,
1312*795d594fSAndroid Build Coastguard Worker InductionInfo* info) {
1313*795d594fSAndroid Build Coastguard Worker auto it = induction_.find(loop);
1314*795d594fSAndroid Build Coastguard Worker if (it == induction_.end()) {
1315*795d594fSAndroid Build Coastguard Worker it = induction_.Put(loop,
1316*795d594fSAndroid Build Coastguard Worker ArenaSafeMap<HInstruction*, InductionInfo*>(
1317*795d594fSAndroid Build Coastguard Worker std::less<HInstruction*>(),
1318*795d594fSAndroid Build Coastguard Worker graph_->GetAllocator()->Adapter(kArenaAllocInductionVarAnalysis)));
1319*795d594fSAndroid Build Coastguard Worker }
1320*795d594fSAndroid Build Coastguard Worker it->second.Put(instruction, info);
1321*795d594fSAndroid Build Coastguard Worker }
1322*795d594fSAndroid Build Coastguard Worker
LookupInfo(const HLoopInformation * loop,HInstruction * instruction)1323*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::LookupInfo(
1324*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
1325*795d594fSAndroid Build Coastguard Worker HInstruction* instruction) {
1326*795d594fSAndroid Build Coastguard Worker auto it = induction_.find(loop);
1327*795d594fSAndroid Build Coastguard Worker if (it != induction_.end()) {
1328*795d594fSAndroid Build Coastguard Worker auto loop_it = it->second.find(instruction);
1329*795d594fSAndroid Build Coastguard Worker if (loop_it != it->second.end()) {
1330*795d594fSAndroid Build Coastguard Worker return loop_it->second;
1331*795d594fSAndroid Build Coastguard Worker }
1332*795d594fSAndroid Build Coastguard Worker }
1333*795d594fSAndroid Build Coastguard Worker if (loop->IsDefinedOutOfTheLoop(instruction)) {
1334*795d594fSAndroid Build Coastguard Worker InductionInfo* info = CreateInvariantFetch(instruction);
1335*795d594fSAndroid Build Coastguard Worker AssignInfo(loop, instruction, info);
1336*795d594fSAndroid Build Coastguard Worker return info;
1337*795d594fSAndroid Build Coastguard Worker }
1338*795d594fSAndroid Build Coastguard Worker return nullptr;
1339*795d594fSAndroid Build Coastguard Worker }
1340*795d594fSAndroid Build Coastguard Worker
CreateConstant(int64_t value,DataType::Type type)1341*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateConstant(int64_t value,
1342*795d594fSAndroid Build Coastguard Worker DataType::Type type) {
1343*795d594fSAndroid Build Coastguard Worker HInstruction* constant;
1344*795d594fSAndroid Build Coastguard Worker switch (type) {
1345*795d594fSAndroid Build Coastguard Worker case DataType::Type::kFloat64: constant = graph_->GetDoubleConstant(value); break;
1346*795d594fSAndroid Build Coastguard Worker case DataType::Type::kFloat32: constant = graph_->GetFloatConstant(value); break;
1347*795d594fSAndroid Build Coastguard Worker case DataType::Type::kInt64: constant = graph_->GetLongConstant(value); break;
1348*795d594fSAndroid Build Coastguard Worker default: constant = graph_->GetIntConstant(value); break;
1349*795d594fSAndroid Build Coastguard Worker }
1350*795d594fSAndroid Build Coastguard Worker return CreateInvariantFetch(constant);
1351*795d594fSAndroid Build Coastguard Worker }
1352*795d594fSAndroid Build Coastguard Worker
CreateSimplifiedInvariant(const HBasicBlock * context,const HLoopInformation * loop,InductionOp op,InductionInfo * a,InductionInfo * b)1353*795d594fSAndroid Build Coastguard Worker HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInvariant(
1354*795d594fSAndroid Build Coastguard Worker const HBasicBlock* context,
1355*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
1356*795d594fSAndroid Build Coastguard Worker InductionOp op,
1357*795d594fSAndroid Build Coastguard Worker InductionInfo* a,
1358*795d594fSAndroid Build Coastguard Worker InductionInfo* b) {
1359*795d594fSAndroid Build Coastguard Worker // Perform some light-weight simplifications during construction of a new invariant.
1360*795d594fSAndroid Build Coastguard Worker // This often safes memory and yields a more concise representation of the induction.
1361*795d594fSAndroid Build Coastguard Worker // More exhaustive simplifications are done by later phases once induction nodes are
1362*795d594fSAndroid Build Coastguard Worker // translated back into HIR code (e.g. by loop optimizations or BCE).
1363*795d594fSAndroid Build Coastguard Worker int64_t value = -1;
1364*795d594fSAndroid Build Coastguard Worker if (IsExact(context, loop, a, &value)) {
1365*795d594fSAndroid Build Coastguard Worker if (value == 0) {
1366*795d594fSAndroid Build Coastguard Worker // Simplify 0 + b = b, 0 ^ b = b, 0 * b = 0.
1367*795d594fSAndroid Build Coastguard Worker if (op == kAdd || op == kXor) {
1368*795d594fSAndroid Build Coastguard Worker return b;
1369*795d594fSAndroid Build Coastguard Worker } else if (op == kMul) {
1370*795d594fSAndroid Build Coastguard Worker return a;
1371*795d594fSAndroid Build Coastguard Worker }
1372*795d594fSAndroid Build Coastguard Worker } else if (op == kMul) {
1373*795d594fSAndroid Build Coastguard Worker // Simplify 1 * b = b, -1 * b = -b
1374*795d594fSAndroid Build Coastguard Worker if (value == 1) {
1375*795d594fSAndroid Build Coastguard Worker return b;
1376*795d594fSAndroid Build Coastguard Worker } else if (value == -1) {
1377*795d594fSAndroid Build Coastguard Worker return CreateSimplifiedInvariant(context, loop, kNeg, nullptr, b);
1378*795d594fSAndroid Build Coastguard Worker }
1379*795d594fSAndroid Build Coastguard Worker }
1380*795d594fSAndroid Build Coastguard Worker }
1381*795d594fSAndroid Build Coastguard Worker if (IsExact(context, loop, b, &value)) {
1382*795d594fSAndroid Build Coastguard Worker if (value == 0) {
1383*795d594fSAndroid Build Coastguard Worker // Simplify a + 0 = a, a - 0 = a, a ^ 0 = a, a * 0 = 0, -0 = 0.
1384*795d594fSAndroid Build Coastguard Worker if (op == kAdd || op == kSub || op == kXor) {
1385*795d594fSAndroid Build Coastguard Worker return a;
1386*795d594fSAndroid Build Coastguard Worker } else if (op == kMul || op == kNeg) {
1387*795d594fSAndroid Build Coastguard Worker return b;
1388*795d594fSAndroid Build Coastguard Worker }
1389*795d594fSAndroid Build Coastguard Worker } else if (op == kMul || op == kDiv) {
1390*795d594fSAndroid Build Coastguard Worker // Simplify a * 1 = a, a / 1 = a, a * -1 = -a, a / -1 = -a
1391*795d594fSAndroid Build Coastguard Worker if (value == 1) {
1392*795d594fSAndroid Build Coastguard Worker return a;
1393*795d594fSAndroid Build Coastguard Worker } else if (value == -1) {
1394*795d594fSAndroid Build Coastguard Worker return CreateSimplifiedInvariant(context, loop, kNeg, nullptr, a);
1395*795d594fSAndroid Build Coastguard Worker }
1396*795d594fSAndroid Build Coastguard Worker }
1397*795d594fSAndroid Build Coastguard Worker } else if (b->operation == kNeg) {
1398*795d594fSAndroid Build Coastguard Worker // Simplify a + (-b) = a - b, a - (-b) = a + b, -(-b) = b.
1399*795d594fSAndroid Build Coastguard Worker if (op == kAdd) {
1400*795d594fSAndroid Build Coastguard Worker return CreateSimplifiedInvariant(context, loop, kSub, a, b->op_b);
1401*795d594fSAndroid Build Coastguard Worker } else if (op == kSub) {
1402*795d594fSAndroid Build Coastguard Worker return CreateSimplifiedInvariant(context, loop, kAdd, a, b->op_b);
1403*795d594fSAndroid Build Coastguard Worker } else if (op == kNeg) {
1404*795d594fSAndroid Build Coastguard Worker return b->op_b;
1405*795d594fSAndroid Build Coastguard Worker }
1406*795d594fSAndroid Build Coastguard Worker } else if (b->operation == kSub) {
1407*795d594fSAndroid Build Coastguard Worker // Simplify - (a - b) = b - a.
1408*795d594fSAndroid Build Coastguard Worker if (op == kNeg) {
1409*795d594fSAndroid Build Coastguard Worker return CreateSimplifiedInvariant(context, loop, kSub, b->op_b, b->op_a);
1410*795d594fSAndroid Build Coastguard Worker }
1411*795d594fSAndroid Build Coastguard Worker }
1412*795d594fSAndroid Build Coastguard Worker return new (graph_->GetAllocator()) InductionInfo(
1413*795d594fSAndroid Build Coastguard Worker kInvariant, op, a, b, nullptr, ImplicitConversion(b->type));
1414*795d594fSAndroid Build Coastguard Worker }
1415*795d594fSAndroid Build Coastguard Worker
GetShiftConstant(const HLoopInformation * loop,HInstruction * instruction,InductionInfo * initial)1416*795d594fSAndroid Build Coastguard Worker HInstruction* HInductionVarAnalysis::GetShiftConstant(const HLoopInformation* loop,
1417*795d594fSAndroid Build Coastguard Worker HInstruction* instruction,
1418*795d594fSAndroid Build Coastguard Worker InductionInfo* initial) {
1419*795d594fSAndroid Build Coastguard Worker DCHECK(instruction->IsShl() || instruction->IsShr() || instruction->IsUShr());
1420*795d594fSAndroid Build Coastguard Worker const HBasicBlock* context = instruction->GetBlock();
1421*795d594fSAndroid Build Coastguard Worker // Shift-rights are only the same as division for non-negative initial inputs.
1422*795d594fSAndroid Build Coastguard Worker // Otherwise we would round incorrectly.
1423*795d594fSAndroid Build Coastguard Worker if (initial != nullptr) {
1424*795d594fSAndroid Build Coastguard Worker int64_t value = -1;
1425*795d594fSAndroid Build Coastguard Worker if (!IsAtLeast(context, loop, initial, &value) || value < 0) {
1426*795d594fSAndroid Build Coastguard Worker return nullptr;
1427*795d594fSAndroid Build Coastguard Worker }
1428*795d594fSAndroid Build Coastguard Worker }
1429*795d594fSAndroid Build Coastguard Worker // Obtain the constant needed to treat shift as equivalent multiplication or division.
1430*795d594fSAndroid Build Coastguard Worker // This yields an existing instruction if the constant is already there. Otherwise, this
1431*795d594fSAndroid Build Coastguard Worker // has a side effect on the HIR. The restriction on the shift factor avoids generating a
1432*795d594fSAndroid Build Coastguard Worker // negative constant (viz. 1 << 31 and 1L << 63 set the sign bit). The code assumes that
1433*795d594fSAndroid Build Coastguard Worker // generalization for shift factors outside [0,32) and [0,64) ranges is done earlier.
1434*795d594fSAndroid Build Coastguard Worker InductionInfo* b = LookupInfo(loop, instruction->InputAt(1));
1435*795d594fSAndroid Build Coastguard Worker int64_t value = -1;
1436*795d594fSAndroid Build Coastguard Worker if (IsExact(context, loop, b, &value)) {
1437*795d594fSAndroid Build Coastguard Worker DataType::Type type = instruction->InputAt(0)->GetType();
1438*795d594fSAndroid Build Coastguard Worker if (type == DataType::Type::kInt32 && 0 <= value && value < 31) {
1439*795d594fSAndroid Build Coastguard Worker return graph_->GetIntConstant(1 << value);
1440*795d594fSAndroid Build Coastguard Worker }
1441*795d594fSAndroid Build Coastguard Worker if (type == DataType::Type::kInt64 && 0 <= value && value < 63) {
1442*795d594fSAndroid Build Coastguard Worker return graph_->GetLongConstant(1L << value);
1443*795d594fSAndroid Build Coastguard Worker }
1444*795d594fSAndroid Build Coastguard Worker }
1445*795d594fSAndroid Build Coastguard Worker return nullptr;
1446*795d594fSAndroid Build Coastguard Worker }
1447*795d594fSAndroid Build Coastguard Worker
AssignCycle(HPhi * phi,ArrayRef<HInstruction * const> scc)1448*795d594fSAndroid Build Coastguard Worker void HInductionVarAnalysis::AssignCycle(HPhi* phi, ArrayRef<HInstruction* const> scc) {
1449*795d594fSAndroid Build Coastguard Worker ArenaSet<HInstruction*>* set = &cycles_.Put(phi, ArenaSet<HInstruction*>(
1450*795d594fSAndroid Build Coastguard Worker graph_->GetAllocator()->Adapter(kArenaAllocInductionVarAnalysis)))->second;
1451*795d594fSAndroid Build Coastguard Worker for (HInstruction* i : scc) {
1452*795d594fSAndroid Build Coastguard Worker set->insert(i);
1453*795d594fSAndroid Build Coastguard Worker }
1454*795d594fSAndroid Build Coastguard Worker }
1455*795d594fSAndroid Build Coastguard Worker
LookupCycle(HPhi * phi)1456*795d594fSAndroid Build Coastguard Worker ArenaSet<HInstruction*>* HInductionVarAnalysis::LookupCycle(HPhi* phi) {
1457*795d594fSAndroid Build Coastguard Worker auto it = cycles_.find(phi);
1458*795d594fSAndroid Build Coastguard Worker if (it != cycles_.end()) {
1459*795d594fSAndroid Build Coastguard Worker return &it->second;
1460*795d594fSAndroid Build Coastguard Worker }
1461*795d594fSAndroid Build Coastguard Worker return nullptr;
1462*795d594fSAndroid Build Coastguard Worker }
1463*795d594fSAndroid Build Coastguard Worker
IsExact(const HBasicBlock * context,const HLoopInformation * loop,InductionInfo * info,int64_t * value)1464*795d594fSAndroid Build Coastguard Worker bool HInductionVarAnalysis::IsExact(const HBasicBlock* context,
1465*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
1466*795d594fSAndroid Build Coastguard Worker InductionInfo* info,
1467*795d594fSAndroid Build Coastguard Worker /*out*/int64_t* value) {
1468*795d594fSAndroid Build Coastguard Worker InductionVarRange range(this);
1469*795d594fSAndroid Build Coastguard Worker return range.IsConstant(context, loop, info, InductionVarRange::kExact, value);
1470*795d594fSAndroid Build Coastguard Worker }
1471*795d594fSAndroid Build Coastguard Worker
IsAtMost(const HBasicBlock * context,const HLoopInformation * loop,InductionInfo * info,int64_t * value)1472*795d594fSAndroid Build Coastguard Worker bool HInductionVarAnalysis::IsAtMost(const HBasicBlock* context,
1473*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
1474*795d594fSAndroid Build Coastguard Worker InductionInfo* info,
1475*795d594fSAndroid Build Coastguard Worker /*out*/int64_t* value) {
1476*795d594fSAndroid Build Coastguard Worker InductionVarRange range(this);
1477*795d594fSAndroid Build Coastguard Worker return range.IsConstant(context, loop, info, InductionVarRange::kAtMost, value);
1478*795d594fSAndroid Build Coastguard Worker }
1479*795d594fSAndroid Build Coastguard Worker
IsAtLeast(const HBasicBlock * context,const HLoopInformation * loop,InductionInfo * info,int64_t * value)1480*795d594fSAndroid Build Coastguard Worker bool HInductionVarAnalysis::IsAtLeast(const HBasicBlock* context,
1481*795d594fSAndroid Build Coastguard Worker const HLoopInformation* loop,
1482*795d594fSAndroid Build Coastguard Worker InductionInfo* info,
1483*795d594fSAndroid Build Coastguard Worker /*out*/int64_t* value) {
1484*795d594fSAndroid Build Coastguard Worker InductionVarRange range(this);
1485*795d594fSAndroid Build Coastguard Worker return range.IsConstant(context, loop, info, InductionVarRange::kAtLeast, value);
1486*795d594fSAndroid Build Coastguard Worker }
1487*795d594fSAndroid Build Coastguard Worker
IsNarrowingLinear(InductionInfo * info)1488*795d594fSAndroid Build Coastguard Worker bool HInductionVarAnalysis::IsNarrowingLinear(InductionInfo* info) {
1489*795d594fSAndroid Build Coastguard Worker return info != nullptr &&
1490*795d594fSAndroid Build Coastguard Worker info->induction_class == kLinear &&
1491*795d594fSAndroid Build Coastguard Worker (info->type == DataType::Type::kUint8 ||
1492*795d594fSAndroid Build Coastguard Worker info->type == DataType::Type::kInt8 ||
1493*795d594fSAndroid Build Coastguard Worker info->type == DataType::Type::kUint16 ||
1494*795d594fSAndroid Build Coastguard Worker info->type == DataType::Type::kInt16 ||
1495*795d594fSAndroid Build Coastguard Worker (info->type == DataType::Type::kInt32 && (info->op_a->type == DataType::Type::kInt64 ||
1496*795d594fSAndroid Build Coastguard Worker info->op_b->type == DataType::Type::kInt64)));
1497*795d594fSAndroid Build Coastguard Worker }
1498*795d594fSAndroid Build Coastguard Worker
InductionEqual(InductionInfo * info1,InductionInfo * info2)1499*795d594fSAndroid Build Coastguard Worker bool HInductionVarAnalysis::InductionEqual(InductionInfo* info1,
1500*795d594fSAndroid Build Coastguard Worker InductionInfo* info2) {
1501*795d594fSAndroid Build Coastguard Worker // Test structural equality only, without accounting for simplifications.
1502*795d594fSAndroid Build Coastguard Worker if (info1 != nullptr && info2 != nullptr) {
1503*795d594fSAndroid Build Coastguard Worker return
1504*795d594fSAndroid Build Coastguard Worker info1->induction_class == info2->induction_class &&
1505*795d594fSAndroid Build Coastguard Worker info1->operation == info2->operation &&
1506*795d594fSAndroid Build Coastguard Worker info1->fetch == info2->fetch &&
1507*795d594fSAndroid Build Coastguard Worker info1->type == info2->type &&
1508*795d594fSAndroid Build Coastguard Worker InductionEqual(info1->op_a, info2->op_a) &&
1509*795d594fSAndroid Build Coastguard Worker InductionEqual(info1->op_b, info2->op_b);
1510*795d594fSAndroid Build Coastguard Worker }
1511*795d594fSAndroid Build Coastguard Worker // Otherwise only two nullptrs are considered equal.
1512*795d594fSAndroid Build Coastguard Worker return info1 == info2;
1513*795d594fSAndroid Build Coastguard Worker }
1514*795d594fSAndroid Build Coastguard Worker
FetchToString(HInstruction * fetch)1515*795d594fSAndroid Build Coastguard Worker std::string HInductionVarAnalysis::FetchToString(HInstruction* fetch) {
1516*795d594fSAndroid Build Coastguard Worker DCHECK(fetch != nullptr);
1517*795d594fSAndroid Build Coastguard Worker if (fetch->IsIntConstant()) {
1518*795d594fSAndroid Build Coastguard Worker return std::to_string(fetch->AsIntConstant()->GetValue());
1519*795d594fSAndroid Build Coastguard Worker } else if (fetch->IsLongConstant()) {
1520*795d594fSAndroid Build Coastguard Worker return std::to_string(fetch->AsLongConstant()->GetValue());
1521*795d594fSAndroid Build Coastguard Worker }
1522*795d594fSAndroid Build Coastguard Worker return std::to_string(fetch->GetId()) + ":" + fetch->DebugName();
1523*795d594fSAndroid Build Coastguard Worker }
1524*795d594fSAndroid Build Coastguard Worker
InductionToString(InductionInfo * info)1525*795d594fSAndroid Build Coastguard Worker std::string HInductionVarAnalysis::InductionToString(InductionInfo* info) {
1526*795d594fSAndroid Build Coastguard Worker if (info != nullptr) {
1527*795d594fSAndroid Build Coastguard Worker if (info->induction_class == kInvariant) {
1528*795d594fSAndroid Build Coastguard Worker std::string inv = "(";
1529*795d594fSAndroid Build Coastguard Worker inv += InductionToString(info->op_a);
1530*795d594fSAndroid Build Coastguard Worker switch (info->operation) {
1531*795d594fSAndroid Build Coastguard Worker case kNop: inv += " @ "; break;
1532*795d594fSAndroid Build Coastguard Worker case kAdd: inv += " + "; break;
1533*795d594fSAndroid Build Coastguard Worker case kSub:
1534*795d594fSAndroid Build Coastguard Worker case kNeg: inv += " - "; break;
1535*795d594fSAndroid Build Coastguard Worker case kMul: inv += " * "; break;
1536*795d594fSAndroid Build Coastguard Worker case kDiv: inv += " / "; break;
1537*795d594fSAndroid Build Coastguard Worker case kRem: inv += " % "; break;
1538*795d594fSAndroid Build Coastguard Worker case kXor: inv += " ^ "; break;
1539*795d594fSAndroid Build Coastguard Worker case kLT: inv += " < "; break;
1540*795d594fSAndroid Build Coastguard Worker case kLE: inv += " <= "; break;
1541*795d594fSAndroid Build Coastguard Worker case kGT: inv += " > "; break;
1542*795d594fSAndroid Build Coastguard Worker case kGE: inv += " >= "; break;
1543*795d594fSAndroid Build Coastguard Worker case kFetch: inv += FetchToString(info->fetch); break;
1544*795d594fSAndroid Build Coastguard Worker case kTripCountInLoop: inv += " (TC-loop) "; break;
1545*795d594fSAndroid Build Coastguard Worker case kTripCountInBody: inv += " (TC-body) "; break;
1546*795d594fSAndroid Build Coastguard Worker case kTripCountInLoopUnsafe: inv += " (TC-loop-unsafe) "; break;
1547*795d594fSAndroid Build Coastguard Worker case kTripCountInBodyUnsafe: inv += " (TC-body-unsafe) "; break;
1548*795d594fSAndroid Build Coastguard Worker }
1549*795d594fSAndroid Build Coastguard Worker inv += InductionToString(info->op_b);
1550*795d594fSAndroid Build Coastguard Worker inv += ")";
1551*795d594fSAndroid Build Coastguard Worker return inv;
1552*795d594fSAndroid Build Coastguard Worker } else {
1553*795d594fSAndroid Build Coastguard Worker if (info->induction_class == kLinear) {
1554*795d594fSAndroid Build Coastguard Worker DCHECK(info->operation == kNop);
1555*795d594fSAndroid Build Coastguard Worker return "(" + InductionToString(info->op_a) + " * i + " +
1556*795d594fSAndroid Build Coastguard Worker InductionToString(info->op_b) + "):" +
1557*795d594fSAndroid Build Coastguard Worker DataType::PrettyDescriptor(info->type);
1558*795d594fSAndroid Build Coastguard Worker } else if (info->induction_class == kPolynomial) {
1559*795d594fSAndroid Build Coastguard Worker DCHECK(info->operation == kNop);
1560*795d594fSAndroid Build Coastguard Worker return "poly(sum_lt(" + InductionToString(info->op_a) + ") + " +
1561*795d594fSAndroid Build Coastguard Worker InductionToString(info->op_b) + "):" +
1562*795d594fSAndroid Build Coastguard Worker DataType::PrettyDescriptor(info->type);
1563*795d594fSAndroid Build Coastguard Worker } else if (info->induction_class == kGeometric) {
1564*795d594fSAndroid Build Coastguard Worker DCHECK(info->operation == kMul || info->operation == kDiv);
1565*795d594fSAndroid Build Coastguard Worker DCHECK(info->fetch != nullptr);
1566*795d594fSAndroid Build Coastguard Worker return "geo(" + InductionToString(info->op_a) + " * " +
1567*795d594fSAndroid Build Coastguard Worker FetchToString(info->fetch) +
1568*795d594fSAndroid Build Coastguard Worker (info->operation == kMul ? " ^ i + " : " ^ -i + ") +
1569*795d594fSAndroid Build Coastguard Worker InductionToString(info->op_b) + "):" +
1570*795d594fSAndroid Build Coastguard Worker DataType::PrettyDescriptor(info->type);
1571*795d594fSAndroid Build Coastguard Worker } else if (info->induction_class == kWrapAround) {
1572*795d594fSAndroid Build Coastguard Worker DCHECK(info->operation == kNop);
1573*795d594fSAndroid Build Coastguard Worker return "wrap(" + InductionToString(info->op_a) + ", " +
1574*795d594fSAndroid Build Coastguard Worker InductionToString(info->op_b) + "):" +
1575*795d594fSAndroid Build Coastguard Worker DataType::PrettyDescriptor(info->type);
1576*795d594fSAndroid Build Coastguard Worker } else if (info->induction_class == kPeriodic) {
1577*795d594fSAndroid Build Coastguard Worker DCHECK(info->operation == kNop);
1578*795d594fSAndroid Build Coastguard Worker return "periodic(" + InductionToString(info->op_a) + ", " +
1579*795d594fSAndroid Build Coastguard Worker InductionToString(info->op_b) + "):" +
1580*795d594fSAndroid Build Coastguard Worker DataType::PrettyDescriptor(info->type);
1581*795d594fSAndroid Build Coastguard Worker }
1582*795d594fSAndroid Build Coastguard Worker }
1583*795d594fSAndroid Build Coastguard Worker }
1584*795d594fSAndroid Build Coastguard Worker return "";
1585*795d594fSAndroid Build Coastguard Worker }
1586*795d594fSAndroid Build Coastguard Worker
CalculateLoopHeaderPhisInARow(HPhi * initial_phi,ScopedArenaSafeMap<HPhi *,int> & cached_values,ScopedArenaAllocator & allocator)1587*795d594fSAndroid Build Coastguard Worker void HInductionVarAnalysis::CalculateLoopHeaderPhisInARow(
1588*795d594fSAndroid Build Coastguard Worker HPhi* initial_phi,
1589*795d594fSAndroid Build Coastguard Worker ScopedArenaSafeMap<HPhi*, int>& cached_values,
1590*795d594fSAndroid Build Coastguard Worker ScopedArenaAllocator& allocator) {
1591*795d594fSAndroid Build Coastguard Worker DCHECK(initial_phi->IsLoopHeaderPhi());
1592*795d594fSAndroid Build Coastguard Worker ScopedArenaQueue<HPhi*> worklist(allocator.Adapter(kArenaAllocInductionVarAnalysis));
1593*795d594fSAndroid Build Coastguard Worker worklist.push(initial_phi);
1594*795d594fSAndroid Build Coastguard Worker // Used to check which phis are in the current chain we are checking.
1595*795d594fSAndroid Build Coastguard Worker ScopedArenaSet<HPhi*> phis_in_chain(allocator.Adapter(kArenaAllocInductionVarAnalysis));
1596*795d594fSAndroid Build Coastguard Worker while (!worklist.empty()) {
1597*795d594fSAndroid Build Coastguard Worker HPhi* current_phi = worklist.front();
1598*795d594fSAndroid Build Coastguard Worker DCHECK(current_phi->IsLoopHeaderPhi());
1599*795d594fSAndroid Build Coastguard Worker if (cached_values.find(current_phi) != cached_values.end()) {
1600*795d594fSAndroid Build Coastguard Worker // Already processed.
1601*795d594fSAndroid Build Coastguard Worker worklist.pop();
1602*795d594fSAndroid Build Coastguard Worker continue;
1603*795d594fSAndroid Build Coastguard Worker }
1604*795d594fSAndroid Build Coastguard Worker
1605*795d594fSAndroid Build Coastguard Worker phis_in_chain.insert(current_phi);
1606*795d594fSAndroid Build Coastguard Worker int max_value = 0;
1607*795d594fSAndroid Build Coastguard Worker bool pushed_other_phis = false;
1608*795d594fSAndroid Build Coastguard Worker for (size_t index = 0; index < current_phi->InputCount(); index++) {
1609*795d594fSAndroid Build Coastguard Worker // If the input is not a loop header phi, we only have 1 (current_phi).
1610*795d594fSAndroid Build Coastguard Worker int current_value = 1;
1611*795d594fSAndroid Build Coastguard Worker if (current_phi->InputAt(index)->IsLoopHeaderPhi()) {
1612*795d594fSAndroid Build Coastguard Worker HPhi* loop_header_phi = current_phi->InputAt(index)->AsPhi();
1613*795d594fSAndroid Build Coastguard Worker auto it = cached_values.find(loop_header_phi);
1614*795d594fSAndroid Build Coastguard Worker if (it != cached_values.end()) {
1615*795d594fSAndroid Build Coastguard Worker current_value += it->second;
1616*795d594fSAndroid Build Coastguard Worker } else if (phis_in_chain.find(current_phi) == phis_in_chain.end()) {
1617*795d594fSAndroid Build Coastguard Worker // Push phis which aren't in the chain already to be processed.
1618*795d594fSAndroid Build Coastguard Worker pushed_other_phis = true;
1619*795d594fSAndroid Build Coastguard Worker worklist.push(loop_header_phi);
1620*795d594fSAndroid Build Coastguard Worker }
1621*795d594fSAndroid Build Coastguard Worker // Phis in the chain will get processed later. We keep `current_value` as 1 to avoid
1622*795d594fSAndroid Build Coastguard Worker // double counting `loop_header_phi`.
1623*795d594fSAndroid Build Coastguard Worker }
1624*795d594fSAndroid Build Coastguard Worker max_value = std::max(max_value, current_value);
1625*795d594fSAndroid Build Coastguard Worker }
1626*795d594fSAndroid Build Coastguard Worker
1627*795d594fSAndroid Build Coastguard Worker if (!pushed_other_phis) {
1628*795d594fSAndroid Build Coastguard Worker // Only finish processing after all inputs were processed.
1629*795d594fSAndroid Build Coastguard Worker worklist.pop();
1630*795d594fSAndroid Build Coastguard Worker phis_in_chain.erase(current_phi);
1631*795d594fSAndroid Build Coastguard Worker cached_values.FindOrAdd(current_phi, max_value);
1632*795d594fSAndroid Build Coastguard Worker }
1633*795d594fSAndroid Build Coastguard Worker }
1634*795d594fSAndroid Build Coastguard Worker }
1635*795d594fSAndroid Build Coastguard Worker
IsPathologicalCase()1636*795d594fSAndroid Build Coastguard Worker bool HInductionVarAnalysis::IsPathologicalCase() {
1637*795d594fSAndroid Build Coastguard Worker ScopedArenaAllocator local_allocator(graph_->GetArenaStack());
1638*795d594fSAndroid Build Coastguard Worker ScopedArenaSafeMap<HPhi*, int> cached_values(
1639*795d594fSAndroid Build Coastguard Worker std::less<HPhi*>(), local_allocator.Adapter(kArenaAllocInductionVarAnalysis));
1640*795d594fSAndroid Build Coastguard Worker
1641*795d594fSAndroid Build Coastguard Worker // Due to how our induction passes work, we will take a lot of time compiling if we have several
1642*795d594fSAndroid Build Coastguard Worker // loop header phis in a row. If we have more than 15 different loop header phis in a row, we
1643*795d594fSAndroid Build Coastguard Worker // don't perform the analysis.
1644*795d594fSAndroid Build Coastguard Worker constexpr int kMaximumLoopHeaderPhisInARow = 15;
1645*795d594fSAndroid Build Coastguard Worker
1646*795d594fSAndroid Build Coastguard Worker for (HBasicBlock* block : graph_->GetReversePostOrder()) {
1647*795d594fSAndroid Build Coastguard Worker if (!block->IsLoopHeader()) {
1648*795d594fSAndroid Build Coastguard Worker continue;
1649*795d594fSAndroid Build Coastguard Worker }
1650*795d594fSAndroid Build Coastguard Worker
1651*795d594fSAndroid Build Coastguard Worker for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
1652*795d594fSAndroid Build Coastguard Worker DCHECK(it.Current()->IsLoopHeaderPhi());
1653*795d594fSAndroid Build Coastguard Worker HPhi* phi = it.Current()->AsPhi();
1654*795d594fSAndroid Build Coastguard Worker CalculateLoopHeaderPhisInARow(phi, cached_values, local_allocator);
1655*795d594fSAndroid Build Coastguard Worker DCHECK(cached_values.find(phi) != cached_values.end())
1656*795d594fSAndroid Build Coastguard Worker << " we should have a value for Phi " << phi->GetId()
1657*795d594fSAndroid Build Coastguard Worker << " in block " << phi->GetBlock()->GetBlockId();
1658*795d594fSAndroid Build Coastguard Worker if (cached_values.find(phi)->second > kMaximumLoopHeaderPhisInARow) {
1659*795d594fSAndroid Build Coastguard Worker return true;
1660*795d594fSAndroid Build Coastguard Worker }
1661*795d594fSAndroid Build Coastguard Worker }
1662*795d594fSAndroid Build Coastguard Worker }
1663*795d594fSAndroid Build Coastguard Worker
1664*795d594fSAndroid Build Coastguard Worker return false;
1665*795d594fSAndroid Build Coastguard Worker }
1666*795d594fSAndroid Build Coastguard Worker
1667*795d594fSAndroid Build Coastguard Worker } // namespace art
1668