• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "compiler_logger.h"
17 #include "optimizer/analysis/alias_analysis.h"
18 #include "optimizer/analysis/bounds_analysis.h"
19 #include "optimizer/analysis/dominators_tree.h"
20 #include "optimizer/analysis/loop_analyzer.h"
21 #include "optimizer/ir/inst.h"
22 #include "optimizer/optimizations/licm.h"
23 #include "optimizer/ir/basicblock.h"
24 #include "optimizer/ir/analysis.h"
25 
26 namespace panda::compiler {
Licm(Graph * graph,uint32_t hoistLimit)27 Licm::Licm(Graph *graph, uint32_t hoistLimit)
28     : Optimization(graph), hoistLimit_(hoistLimit), hoistableInstructions_(graph->GetLocalAllocator()->Adapter())
29 {
30 }
31 
32 // NOTE (a.popov) Use `LoopTransform` base class similarly `LoopUnroll`, `LoopPeeling`
RunImpl()33 bool Licm::RunImpl()
34 {
35     if (!GetGraph()->GetAnalysis<LoopAnalyzer>().IsValid()) {
36         GetGraph()->GetAnalysis<LoopAnalyzer>().Run();
37     }
38 
39     ASSERT(GetGraph()->GetRootLoop() != nullptr);
40     if (!GetGraph()->GetRootLoop()->GetInnerLoops().empty()) {
41         markerLoopExit_ = GetGraph()->NewMarker();
42         MarkLoopExits(GetGraph(), markerLoopExit_);
43         for (auto loop : GetGraph()->GetRootLoop()->GetInnerLoops()) {
44             LoopSearchDFS(loop);
45         }
46         GetGraph()->EraseMarker(markerLoopExit_);
47     }
48     return (hoistedInstCount_ != 0);
49 }
50 
InvalidateAnalyses()51 void Licm::InvalidateAnalyses()
52 {
53     GetGraph()->InvalidateAnalysis<BoundsAnalysis>();
54     GetGraph()->InvalidateAnalysis<AliasAnalysis>();
55 }
56 
57 /*
58  * Check if block is loop exit, exclude loop header
59  */
IsBlockLoopExit(BasicBlock * block)60 bool Licm::IsBlockLoopExit(BasicBlock *block)
61 {
62     return block->IsMarked(markerLoopExit_) && !block->IsLoopHeader();
63 }
64 
65 /*
66  * Search loops in DFS order to visit inner loops firstly
67  */
LoopSearchDFS(Loop * loop)68 void Licm::LoopSearchDFS(Loop *loop)
69 {
70     ASSERT(loop != nullptr);
71     for (auto innerLoop : loop->GetInnerLoops()) {
72         LoopSearchDFS(innerLoop);
73     }
74     if (IsLoopVisited(*loop)) {
75         VisitLoop(loop);
76     }
77 }
78 
IsLoopVisited(const Loop & loop) const79 bool Licm::IsLoopVisited(const Loop &loop) const
80 {
81     if (loop.IsIrreducible()) {
82         COMPILER_LOG(DEBUG, LOOP_TRANSFORM) << "Irreducible loop isn't visited, id = " << loop.GetId();
83         return false;
84     }
85     if (loop.IsOsrLoop()) {
86         COMPILER_LOG(DEBUG, LOOP_TRANSFORM) << "OSR entry isn't visited, loop id = " << loop.GetId();
87         return false;
88     }
89     if (loop.IsTryCatchLoop()) {
90         COMPILER_LOG(DEBUG, LOOP_TRANSFORM) << "Try-catch loop isn't visited, loop id = " << loop.GetId();
91         return false;
92     }
93     if (hoistedInstCount_ >= hoistLimit_) {
94         COMPILER_LOG(DEBUG, LICM_OPT) << "Limit is exceeded, loop isn't visited, id = " << loop.GetId();
95         return false;
96     }
97     COMPILER_LOG(DEBUG, LICM_OPT) << "Visit Loop, id = " << loop.GetId();
98     return true;
99 }
100 
AllInputsDominate(Inst * inst,Inst * dom)101 bool AllInputsDominate(Inst *inst, Inst *dom)
102 {
103     for (auto input : inst->GetInputs()) {
104         if (!input.GetInst()->IsDominate(dom)) {
105             return false;
106         }
107     }
108     return true;
109 }
110 
TryAppendHoistableInst(Inst * inst,BasicBlock * block,Loop * loop)111 void Licm::TryAppendHoistableInst(Inst *inst, BasicBlock *block, Loop *loop)
112 {
113     if (hoistedInstCount_ >= hoistLimit_) {
114         COMPILER_LOG(DEBUG, LICM_OPT) << "Limit is exceeded, Can't hoist instruction with id = " << inst->GetId();
115         return;
116     }
117     if (inst->IsResolver()) {
118         // If it is not "do-while" or "while(true)" loops then the resolver
119         // can not be hoisted out as it might perform class loading/initialization.
120         if (block != loop->GetHeader() && loop->GetHeader()->IsMarked(markerLoopExit_)) {
121             COMPILER_LOG(DEBUG, LICM_OPT)
122                 << "Header is a loop exit, Can't hoist (resolver) instruction with id = " << inst->GetId();
123             return;
124         }
125         // Don't increment hoisted instruction count until check
126         // if there is another suitable SaveState for the resolver.
127         hoistableInstructions_.push_back(inst);
128         inst->SetMarker(markerHoistInst_);
129     } else {
130         COMPILER_LOG(DEBUG, LICM_OPT) << "Hoist instruction with id = " << inst->GetId();
131         hoistableInstructions_.push_back(inst);
132         inst->SetMarker(markerHoistInst_);
133         hoistedInstCount_++;
134     }
135 }
136 
MoveInstructions(BasicBlock * preHeader,Loop * loop)137 void Licm::MoveInstructions(BasicBlock *preHeader, Loop *loop)
138 {
139     // Find position to move: either add first instruction to the empty block or after the last instruction
140     Inst *lastInst = preHeader->GetLastInst();
141 
142     // Move instructions
143     for (auto inst : hoistableInstructions_) {
144         Inst *target = nullptr;
145         if (inst->IsResolver()) {
146             auto ss = FindSaveStateForResolver(inst, preHeader);
147             if (ss != nullptr) {
148                 ASSERT(ss->IsSaveState());
149                 COMPILER_LOG(DEBUG, LICM_OPT) << "Hoist (resolver) instruction with id = " << inst->GetId();
150                 inst->SetSaveState(ss);
151                 inst->SetPc(ss->GetPc());
152                 hoistedInstCount_++;
153             } else {
154                 continue;
155             }
156         } else if (inst->IsMovableObject()) {
157             target = inst->GetPrev();
158             if (target == nullptr) {
159                 target = inst->GetNext();
160             }
161             if (target == nullptr) {
162                 target = GetGraph()->CreateInstNOP();
163                 inst->GetBasicBlock()->InsertAfter(target, inst);
164             }
165         }
166         inst->GetBasicBlock()->EraseInst(inst);
167         if (lastInst == nullptr || lastInst->IsPhi()) {
168             preHeader->AppendInst(inst);
169             lastInst = inst;
170         } else {
171             ASSERT(lastInst->GetBasicBlock() == preHeader);
172             Inst *ss = preHeader->FindSaveStateDeoptimize();
173             if (ss != nullptr && AllInputsDominate(inst, ss)) {
174                 ss->InsertBefore(inst);
175             } else {
176                 lastInst->InsertAfter(inst);
177                 lastInst = inst;
178             }
179         }
180         GetGraph()->GetEventWriter().EventLicm(inst->GetId(), inst->GetPc(), loop->GetId(),
181                                                preHeader->GetLoop()->GetId());
182         if (inst->IsMovableObject()) {
183             ssb_.SearchAndCreateMissingObjInSaveState(GetGraph(), inst, target);
184         }
185     }
186 }
187 
188 /*
189  * Iterate over all instructions in the loop and move hoistable instructions to the loop preheader
190  */
VisitLoop(Loop * loop)191 void Licm::VisitLoop(Loop *loop)
192 {
193     auto markerHolder = MarkerHolder(GetGraph());
194     markerHoistInst_ = markerHolder.GetMarker();
195     hoistableInstructions_.clear();
196     // Collect instruction, which can be hoisted
197     for (auto block : loop->GetBlocks()) {
198         ASSERT(block->GetLoop() == loop);
199         for (auto inst : block->InstsSafe()) {
200             if (!IsInstHoistable(inst)) {
201                 continue;
202             }
203             TryAppendHoistableInst(inst, block, loop);
204         }
205     }
206     auto preHeader = loop->GetPreHeader();
207     ASSERT(preHeader != nullptr);
208     auto header = loop->GetHeader();
209     if (preHeader->GetSuccsBlocks().size() > 1) {
210         ASSERT(GetGraph()->IsAnalysisValid<DominatorsTree>());
211         // Create new pre-header with single successor: loop header
212         auto newPreHeader = preHeader->InsertNewBlockToSuccEdge(header);
213         preHeader->GetLoop()->AppendBlock(newPreHeader);
214         // Fix Dominators info
215         auto &domTree = GetGraph()->GetAnalysis<DominatorsTree>();
216         domTree.SetValid(true);
217         ASSERT(header->GetDominator() == preHeader);
218         preHeader->RemoveDominatedBlock(header);
219         domTree.SetDomPair(newPreHeader, header);
220         domTree.SetDomPair(preHeader, newPreHeader);
221         // Change pre-header
222         loop->SetPreHeader(newPreHeader);
223         preHeader = newPreHeader;
224     }
225     MoveInstructions(preHeader, loop);
226 }
227 
FindUnsafeInstBetween(BasicBlock * domBb,BasicBlock * currBb,Marker visited,const Inst * resolver,Inst * startInst=nullptr)228 static bool FindUnsafeInstBetween(BasicBlock *domBb, BasicBlock *currBb, Marker visited, const Inst *resolver,
229                                   Inst *startInst = nullptr)
230 {
231     ASSERT(resolver->IsResolver());
232 
233     if (domBb == currBb) {
234         return false;
235     }
236     if (currBb->SetMarker(visited)) {
237         return false;
238     }
239     // Do not cross a try block because the resolver
240     // can throw NoSuch{Method|Field}Exception
241     if (currBb->IsTry()) {
242         return true;
243     }
244     // Field and Method resolvers may call GetField and InitializeClass methods
245     // of the class linker resulting to class loading/initialization.
246     // An order of class initialization must be preserved, so the resolver
247     // must not cross other instructions performing class initialization
248     // We have to be conservative with respect to calls.
249     for (auto inst : InstSafeReverseIter(*currBb, startInst)) {
250         if (inst->IsClassInst() || inst->IsResolver() || inst->IsCall()) {
251             return true;
252         }
253     }
254     for (auto pred : currBb->GetPredsBlocks()) {
255         if (FindUnsafeInstBetween(domBb, pred, visited, resolver)) {
256             return true;
257         }
258     }
259     return false;
260 }
261 
FindSaveStateForResolver(Inst * resolver,const BasicBlock * preHeader)262 Inst *Licm::FindSaveStateForResolver(Inst *resolver, const BasicBlock *preHeader)
263 {
264     ASSERT(preHeader->GetSuccsBlocks().size() == 1);
265     // Lookup for an appropriate SaveState in the preheader
266     Inst *ss = nullptr;
267     for (const auto &inst : preHeader->InstsSafeReverse()) {
268         if (inst->GetOpcode() == Opcode::SaveState) {
269             // Give up further checking if SaveState came from an inlined function.
270             if (static_cast<SaveStateInst *>(inst)->GetCallerInst() != nullptr) {
271                 return nullptr;
272             }
273             for (auto i = preHeader->GetLastInst(); i != inst; i = i->GetPrev()) {
274                 if (i->IsMovableObject()) {
275                     // In this case we have to avoid instructions, which may trigger GC and move objects,
276                     // thus we can not move the resolver to the pre_header and link with SaveState.
277                     return nullptr;
278                 }
279             }
280             ss = inst;
281             break;
282         }
283     }
284     if (ss == nullptr) {
285         return nullptr;
286     }
287     // Check if the path from the resolver instruction to the SaveState block is safe
288     ASSERT(ss->IsDominate(resolver));
289     MarkerHolder visited(GetGraph());
290     if (FindUnsafeInstBetween(ss->GetBasicBlock(), resolver->GetBasicBlock(), visited.GetMarker(), resolver,
291                               resolver->GetPrev())) {
292         return nullptr;
293     }
294     return ss;
295 }
296 
297 /*
298  * Instruction is not hoistable if one of the following conditions are true:
299  *  - it has input which is defined in the same loop and not mark as hoistable
300  *  - it has input which is not dominate instruction's loop preheader
301  */
InstInputDominatesPreheader(Inst * inst)302 bool Licm::InstInputDominatesPreheader(Inst *inst)
303 {
304     ASSERT(!inst->IsPhi());
305     auto instLoop = inst->GetBasicBlock()->GetLoop();
306     for (auto input : inst->GetInputs()) {
307         // Graph is in SSA form and the instructions not PHI,
308         // so every 'input' must dominate 'inst'.
309         ASSERT(input.GetInst()->IsDominate(inst));
310         auto inputLoop = input.GetInst()->GetBasicBlock()->GetLoop();
311         if (instLoop == inputLoop) {
312             if (inst->IsResolver() && input.GetInst()->IsSaveState()) {
313                 // Resolver is coupled with SaveState that is not hoistable.
314                 // We will try to find an appropriate SaveState in the preheader.
315                 continue;
316             }
317             if (!input.GetInst()->IsMarked(markerHoistInst_)) {
318                 return false;
319             }
320         } else if (instLoop->GetOuterLoop() == inputLoop->GetOuterLoop()) {
321             if (!input.GetInst()->GetBasicBlock()->IsDominate(instLoop->GetPreHeader())) {
322                 return false;
323             }
324         } else if (!instLoop->IsInside(inputLoop)) {
325             return false;
326         }
327     }
328     return true;
329 }
330 
331 /*
332  * Hoistable instruction must dominate all loop exits
333  */
InstDominatesLoopExits(Inst * inst)334 bool Licm::InstDominatesLoopExits(Inst *inst)
335 {
336     auto instLoop = inst->GetBasicBlock()->GetLoop();
337     for (auto block : instLoop->GetBlocks()) {
338         if (IsBlockLoopExit(block) && !inst->GetBasicBlock()->IsDominate(block)) {
339             return false;
340         }
341     }
342     return true;
343 }
344 
345 /*
346  * Check if instruction can be moved to the loop preheader
347  */
IsInstHoistable(Inst * inst)348 bool Licm::IsInstHoistable(Inst *inst)
349 {
350     ASSERT(!inst->IsPhi());
351     if (inst->IsNotHoistable()) {
352         return false;
353     }
354     // Do not hoist the resolver if it came from an inlined function.
355     if (inst->IsResolver() && inst->GetSaveState()->GetCallerInst() != nullptr) {
356         return false;
357     }
358     if (inst->GetOpcode() == Opcode::LenArray &&
359         !BoundsAnalysis::IsInstNotNull(inst->GetDataFlowInput(0), inst->GetBasicBlock()->GetLoop()->GetHeader())) {
360         return false;
361     }
362     return InstInputDominatesPreheader(inst) && InstDominatesLoopExits(inst) && !GetGraph()->IsInstThrowable(inst);
363 }
364 }  // namespace panda::compiler
365