• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2025 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 "compiler/optimizer/ir/analysis.h"
18 #include "optimizer/ir/basicblock.h"
19 #include "optimizer/ir/inst.h"
20 #include "optimizer/analysis/alias_analysis.h"
21 #include "optimizer/analysis/dominators_tree.h"
22 #include "optimizer/analysis/rpo.h"
23 #include "optimizer/analysis/loop_analyzer.h"
24 #include "optimizer/optimizations/lse.h"
25 
26 namespace ark::compiler {
27 
LogInst(const Inst * inst)28 static std::string LogInst(const Inst *inst)
29 {
30     return "v" + std::to_string(inst->GetId()) + " (" + GetOpcodeString(inst->GetOpcode()) + ")";
31 }
32 
GetEquivClass(Inst * inst)33 static Lse::EquivClass GetEquivClass(Inst *inst)
34 {
35     switch (inst->GetOpcode()) {
36         case Opcode::LoadArray:
37         case Opcode::LoadArrayI:
38         case Opcode::StoreArray:
39         case Opcode::StoreArrayI:
40         case Opcode::LoadArrayPair:
41         case Opcode::LoadArrayPairI:
42         case Opcode::StoreArrayPair:
43         case Opcode::StoreArrayPairI:
44         case Opcode::LoadConstArray:
45         case Opcode::FillConstArray:
46             return Lse::EquivClass::EQ_ARRAY;
47         case Opcode::LoadStatic:
48         case Opcode::StoreStatic:
49         case Opcode::UnresolvedStoreStatic:
50         case Opcode::LoadResolvedObjectFieldStatic:
51         case Opcode::StoreResolvedObjectFieldStatic:
52             return Lse::EquivClass::EQ_STATIC;
53         case Opcode::LoadString:
54         case Opcode::LoadType:
55         case Opcode::UnresolvedLoadType:
56             return Lse::EquivClass::EQ_POOL;
57         default:
58             return Lse::EquivClass::EQ_OBJECT;
59     }
60 }
61 
62 class LseVisitor {
63 public:
LseVisitor(Graph * graph,Lse::HeapEqClasses * heaps)64     explicit LseVisitor(Graph *graph, Lse::HeapEqClasses *heaps)
65         : aa_(graph->GetAnalysis<AliasAnalysis>()),
66           heaps_(*heaps),
67           eliminations_(graph->GetLocalAllocator()->Adapter()),
68           shadowedStores_(graph->GetLocalAllocator()->Adapter()),
69           disabledObjects_(graph->GetLocalAllocator()->Adapter())
70     {
71     }
72 
73     NO_MOVE_SEMANTIC(LseVisitor);
74     NO_COPY_SEMANTIC(LseVisitor);
75     ~LseVisitor() = default;
76 
MarkForElimination(Inst * inst,Inst * reason,const Lse::HeapValue * hvalue)77     void MarkForElimination(Inst *inst, Inst *reason, const Lse::HeapValue *hvalue)
78     {
79         if (Lse::CanEliminateInstruction(inst)) {
80             auto heap = *hvalue;
81             while (eliminations_.find(heap.val) != eliminations_.end()) {
82                 heap = eliminations_[heap.val];
83             }
84             eliminations_[inst] = heap;
85             COMPILER_LOG(DEBUG, LSE_OPT) << LogInst(inst) << " is eliminated because of " << LogInst(reason);
86         }
87     }
88 
CheckMustAlias(Inst * baseObject)89     bool CheckMustAlias(Inst *baseObject)
90     {
91         for (auto disabledObject : disabledObjects_) {
92             aliasCalls_++;
93             if (aa_.CheckRefAlias(baseObject, disabledObject) == MUST_ALIAS) {
94                 return true;
95             }
96         }
97         return false;
98     }
99 
VisitStore(Inst * inst,Inst * val)100     void VisitStore(Inst *inst, Inst *val)
101     {
102         if (val == nullptr) {
103             /* Instruction has no stored value (e.g. FillConstArray) */
104             return;
105         }
106         auto baseObject = inst->GetDataFlowInput(0);
107         if (CheckMustAlias(baseObject)) {
108             return;
109         }
110         auto hvalue = GetHeapValue(inst);
111         /* Value can be eliminated already */
112         auto alive = val;
113         auto eliminated = eliminations_.find(val);
114         if (eliminated != eliminations_.end()) {
115             alive = eliminated->second.val;
116         }
117         /* If store was assigned to VAL then we can eliminate the second assignment */
118         if (hvalue != nullptr && hvalue->val == alive) {
119             MarkForElimination(inst, hvalue->origin, hvalue);
120             return;
121         }
122         if (hvalue != nullptr && hvalue->origin->IsStore() && !hvalue->read) {
123             if (hvalue->origin->GetBasicBlock() == inst->GetBasicBlock()) {
124                 const Lse::HeapValue heap = {inst, alive, false, false};
125                 MarkForElimination(hvalue->origin, inst, &heap);
126             } else {
127                 shadowedStores_[hvalue->origin].push_back(inst);
128             }
129         }
130         COMPILER_LOG(DEBUG, LSE_OPT) << LogInst(inst) << " updated heap with v" << alive->GetId();
131 
132         /* Stores added to eliminations_ above aren't checked versus phis -> no double instruction elimination */
133         UpdatePhis(inst);
134 
135         auto &blockHeap = heaps_[GetEquivClass(inst)].first.at(inst->GetBasicBlock());
136         uint32_t encounters = 0;
137         /* Erase all aliased values, because they may be overwritten */
138         EraseAliasedValues(blockHeap, inst, baseObject, encounters);
139 
140         // If we reached limit for this object, remove all its MUST_ALIASed instructions from heap
141         // and disable this object for this BB
142         if (encounters > Lse::LS_ACCESS_LIMIT) {
143             for (auto heapIter = blockHeap.begin(), heapLast = blockHeap.end(); heapIter != heapLast;) {
144                 auto hinst = heapIter->first;
145                 aliasCalls_++;
146                 if (HasBaseObject(hinst) && aa_.CheckRefAlias(baseObject, hinst->GetDataFlowInput(0)) == MUST_ALIAS) {
147                     COMPILER_LOG(DEBUG, LSE_OPT)
148                         << "\tDrop from heap { " << LogInst(hinst) << ", v" << heapIter->second.val->GetId() << "}";
149                     heapIter = blockHeap.erase(heapIter);
150                 } else {
151                     heapIter++;
152                 }
153             }
154             disabledObjects_.push_back(baseObject);
155             return;
156         }
157 
158         /* Set value of the inst to VAL */
159         blockHeap[inst] = {inst, alive, false, false};
160     }
161 
VisitLoad(Inst * inst)162     void VisitLoad(Inst *inst)
163     {
164         if (HasBaseObject(inst)) {
165             auto input = inst->GetDataFlowInput(0);
166             for (auto disabledObject : disabledObjects_) {
167                 aliasCalls_++;
168                 if (aa_.CheckRefAlias(input, disabledObject) == MUST_ALIAS) {
169                     return;
170                 }
171             }
172         }
173         /* If we have a heap value for this load instruction then we can eliminate it */
174         auto hvalue = GetHeapValue(inst);
175         if (hvalue != nullptr) {
176             MarkForElimination(inst, hvalue->origin, hvalue);
177             return;
178         }
179         COMPILER_LOG(DEBUG, LSE_OPT) << LogInst(inst) << " updated heap with v" << inst->GetId();
180 
181         /* Loads added to eliminations_ above are not checked versus phis -> no double instruction elimination */
182         UpdatePhis(inst);
183 
184         /* Otherwise set the value of instruction to itself and update MUST_ALIASes */
185         heaps_[GetEquivClass(inst)].first.at(inst->GetBasicBlock())[inst] = {inst, inst, true, false};
186     }
187 
HasBaseObject(Inst * inst)188     bool HasBaseObject(Inst *inst)
189     {
190         ASSERT(inst->IsMemory());
191         if (inst->GetInputsCount() == 0 || inst->GetInput(0).GetInst()->IsSaveState() ||
192             inst->GetDataFlowInput(0)->GetType() == DataType::POINTER) {
193             return false;
194         }
195         ASSERT(inst->GetDataFlowInput(0)->IsReferenceOrAny());
196         return true;
197     }
198 
VisitIntrinsicCheckInvariant(Inst * inv)199     void VisitIntrinsicCheckInvariant(Inst *inv)
200     {
201         for (int eqClass = Lse::EquivClass::EQ_ARRAY; eqClass != Lse::EquivClass::EQ_LAST; eqClass++) {
202             auto &blockHeap = heaps_[eqClass].first.at(inv->GetBasicBlock());
203             for (auto heapIter = blockHeap.begin(), heapLast = blockHeap.end(); heapIter != heapLast;) {
204                 auto hinst = heapIter->first;
205                 aliasCalls_++;
206 
207                 if (!HasBaseObject(hinst) || aa_.CheckRefAlias(inv, hinst->GetDataFlowInput(0)) == NO_ALIAS) {
208                     heapIter++;
209                 } else {
210                     COMPILER_LOG(DEBUG, LSE_OPT)
211                         << "\tDrop from heap { " << LogInst(hinst) << ", v" << heapIter->second.val->GetId() << "}";
212                     heapIter = blockHeap.erase(heapIter);
213                 }
214             }
215         }
216     }
VisitIntrinsic(Inst * inst,InstVector * invs)217     void VisitIntrinsic(Inst *inst, InstVector *invs)
218     {
219         // CC-OFFNXT(C_RULE_SWITCH_BRANCH_CHECKER) autogenerated code
220         switch (inst->CastToIntrinsic()->GetIntrinsicId()) {
221 #include "intrinsics_lse_heap_inv_args.inl"
222             default:
223                 return;
224         }
225         for (auto inv : *invs) {
226             VisitIntrinsicCheckInvariant(inv);
227         }
228         invs->clear();
229     }
230 
231     /// Completely resets the accumulated state: heap and phi candidates.
InvalidateHeap(BasicBlock * block)232     void InvalidateHeap(BasicBlock *block)
233     {
234         for (int eqClass = Lse::EquivClass::EQ_ARRAY; eqClass != Lse::EquivClass::EQ_LAST; eqClass++) {
235             heaps_[eqClass].first.at(block).clear();
236             auto loop = block->GetLoop();
237             while (!loop->IsRoot()) {
238                 heaps_[eqClass].second.at(loop).clear();
239                 loop = loop->GetOuterLoop();
240             }
241         }
242         disabledObjects_.clear();
243     }
244 
245     /// Clears heap of local only values as we don't want them to affect analysis further
ClearLocalValuesFromHeap(BasicBlock * block)246     void ClearLocalValuesFromHeap(BasicBlock *block)
247     {
248         for (int eqClass = Lse::EquivClass::EQ_ARRAY; eqClass != Lse::EquivClass::EQ_LAST; eqClass++) {
249             auto &blockHeap = heaps_[eqClass].first.at(block);
250 
251             auto heapIt = blockHeap.begin();
252             while (heapIt != blockHeap.end()) {
253                 if (heapIt->second.local) {
254                     heapIt = blockHeap.erase(heapIt);
255                 } else {
256                     heapIt++;
257                 }
258             }
259         }
260     }
261 
262     /// Release objects and reset Alias Analysis count
ResetLimits()263     void ResetLimits()
264     {
265         disabledObjects_.clear();
266         aliasCalls_ = 0;
267     }
268 
GetAliasAnalysisCallCount()269     uint32_t GetAliasAnalysisCallCount()
270     {
271         return aliasCalls_;
272     }
273 
274     /// Marks all values currently on heap as potentially read
SetHeapAsRead(BasicBlock * block)275     void SetHeapAsRead(BasicBlock *block)
276     {
277         for (int eqClass = Lse::EquivClass::EQ_ARRAY; eqClass != Lse::EquivClass::EQ_LAST; eqClass++) {
278             auto &bheap = heaps_[eqClass].first.at(block);
279             for (auto it = bheap.begin(); it != bheap.end();) {
280                 it->second.read = true;
281                 it++;
282             }
283         }
284     }
285 
GetEliminations()286     auto &GetEliminations()
287     {
288         return eliminations_;
289     }
290 
ProcessBackedges(PhiInst * phi,Loop * loop,Inst * cand,InstVector * insts)291     bool ProcessBackedges(PhiInst *phi, Loop *loop, Inst *cand, InstVector *insts)
292     {
293         // Now find which values are alive for each backedge. Due to MUST_ALIAS requirement,
294         // there should only be one
295         auto &heap = heaps_[GetEquivClass(cand)].first;
296         for (auto bb : loop->GetHeader()->GetPredsBlocks()) {
297             if (bb == loop->GetPreHeader()) {
298                 phi->AppendInput(cand->IsStore() ? InstStoredValue(cand) : cand);
299                 continue;
300             }
301             auto &blockHeap = heap.at(bb);
302             Inst *alive = nullptr;
303             for (auto &entry : blockHeap) {
304                 if (std::find(insts->begin(), insts->end(), entry.first) != insts->end()) {
305                     alive = entry.first;
306                     break;
307                 }
308             }
309             if (alive == nullptr) {
310                 COMPILER_LOG(DEBUG, LSE_OPT)
311                     << "Skipping phi candidate " << LogInst(cand) << ": no alive insts found for backedge block";
312                 return false;
313             }
314             // There are several cases when a MUST_ALIAS load is alive at backedge while stores
315             // exist in the loop. The one we're interested here is inner loops.
316             if (alive->IsLoad() && !loop->GetInnerLoops().empty()) {
317                 auto aliveIt = std::find(insts->rbegin(), insts->rend(), alive);
318                 // We've checked that no stores exist in inner loops earlier, which allows us to just check
319                 // the first store before the load
320                 auto it = std::find_if(aliveIt, insts->rend(), [](auto *inst) { return inst->IsStore(); });
321                 if (it != insts->rend() && (*it)->IsDominate(alive)) {
322                     auto val = heap.at((*it)->GetBasicBlock())[(*it)].val;
323                     // Use the store instead of load
324                     phi->AppendInput(val);
325                     // And eliminate load
326                     struct Lse::HeapValue hvalue = {*it, val, false, false};
327                     MarkForElimination(alive, *it, &hvalue);
328                     continue;
329                 }
330             }
331             phi->AppendInput(blockHeap[alive].val);
332         }
333         return true;
334     }
335 
LoopDoElimination(Inst * cand,Loop * loop,PhiInst * phi,InstVector * insts)336     void LoopDoElimination(Inst *cand, Loop *loop, PhiInst *phi, InstVector *insts)
337     {
338         auto replacement = phi != nullptr ? phi : cand;
339         struct Lse::HeapValue hvalue = {
340             replacement, replacement->IsStore() ? InstStoredValue(replacement) : replacement, false, false};
341         for (auto inst : *insts) {
342             // No need to check MUST_ALIAS again, but we need to check for double elim
343             if (eliminations_.find(inst) != eliminations_.end()) {
344                 continue;
345             }
346 
347             // Don't replace loads that are also phi inputs
348             if (phi != nullptr &&
349                 std::find_if(phi->GetInputs().begin(), phi->GetInputs().end(),
350                              [&inst](const Input &x) { return x.GetInst() == inst; }) != phi->GetInputs().end()) {
351                 continue;
352             }
353             if (inst->IsLoad()) {
354                 MarkForElimination(inst, replacement, &hvalue);
355             }
356         }
357         // And fix savestates for loads
358         if (phi != nullptr) {
359             for (size_t i = 0; i < phi->GetInputsCount(); i++) {
360                 auto bb = loop->GetHeader()->GetPredecessor(i);
361                 auto phiInput = phi->GetInput(i).GetInst();
362                 if (bb == loop->GetPreHeader() && cand->IsLoad() && cand->IsMovableObject()) {
363                     ssb_.SearchAndCreateMissingObjInSaveState(bb->GetGraph(), cand, phi);
364                 } else if (phiInput->IsMovableObject()) {
365                     ssb_.SearchAndCreateMissingObjInSaveState(bb->GetGraph(), phiInput, bb->GetLastInst(), nullptr, bb);
366                 }
367             }
368         } else {
369             ASSERT(hvalue.val != nullptr);
370             if (hvalue.val->IsMovableObject()) {
371                 ASSERT(!loop->IsIrreducible());
372                 auto head = loop->GetHeader();
373                 // Add cand value to all SaveStates in this loop and inner loops
374                 ssb_.SearchAndCreateMissingObjInSaveState(head->GetGraph(), hvalue.val, head->GetLastInst(), nullptr,
375                                                           head);
376             }
377         }
378     }
379 
380     /// Add eliminations inside loops if there is no overwrites on backedges.
FinalizeLoops(Graph * graph,const ArenaVector<Loop * > & rpoLoops)381     void FinalizeLoops(Graph *graph, const ArenaVector<Loop *> &rpoLoops)
382     {
383         for (int eqClass = Lse::EquivClass::EQ_ARRAY; eqClass != Lse::EquivClass::EQ_LAST; eqClass++) {
384             for (auto loopIt = rpoLoops.rbegin(); loopIt != rpoLoops.rend(); loopIt++) {
385                 auto loop = *loopIt;
386                 auto &phis = heaps_[eqClass].second.at(loop);
387                 COMPILER_LOG(DEBUG, LSE_OPT) << "Finalizing loop #" << loop->GetId();
388                 FinalizeLoopsWithPhiCands(graph, loop, phis);
389             }
390 
391             COMPILER_LOG(DEBUG, LSE_OPT) << "Fixing elimination list after backedge substitutions";
392             for (auto &entry : eliminations_) {
393                 auto hvalue = entry.second;
394                 if (eliminations_.find(hvalue.val) == eliminations_.end()) {
395                     continue;
396                 }
397 
398                 [[maybe_unused]] auto initial = hvalue.val;
399                 while (eliminations_.find(hvalue.val) != eliminations_.end()) {
400                     auto elimValue = eliminations_[hvalue.val];
401                     COMPILER_LOG(DEBUG, LSE_OPT) << "\t" << LogInst(hvalue.val)
402                                                  << " is eliminated. Trying to replace by " << LogInst(elimValue.val);
403                     hvalue = elimValue;
404                     ASSERT_PRINT(initial != hvalue.val, "A cyclic elimination has been detected");
405                 }
406                 entry.second = hvalue;
407             }
408         }
409     }
410 
ExistsPathWithoutShadowingStores(BasicBlock * start,BasicBlock * block,Marker marker)411     bool ExistsPathWithoutShadowingStores(BasicBlock *start, BasicBlock *block, Marker marker)
412     {
413         if (block->IsEndBlock()) {
414             // Found a path without shadowing stores
415             return true;
416         }
417         if (block->IsMarked(marker)) {
418             // Found a path with shadowing store
419             return false;
420         }
421         ASSERT(start->GetLoop() == block->GetLoop());
422         for (auto succ : block->GetSuccsBlocks()) {
423             if (block->GetLoop() != succ->GetLoop()) {
424                 // Edge to a different loop. We currently don't carry heap values through loops and don't
425                 // handle irreducible loops, so we can't be sure there are shadows on this path.
426                 return true;
427             }
428             if (succ == block->GetLoop()->GetHeader()) {
429                 // If next block is a loop header for this block's loop it means that instruction shadows itself
430                 return true;
431             }
432             if (ExistsPathWithoutShadowingStores(start, succ, marker)) {
433                 return true;
434             }
435         }
436         return false;
437     }
438 
FinalizeShadowedStores()439     void FinalizeShadowedStores()
440     {
441         // We want to see if there are no paths from the store that evade any of its shadow stores
442         for (auto &entry : shadowedStores_) {
443             auto inst = entry.first;
444             auto marker = inst->GetBasicBlock()->GetGraph()->NewMarker();
445             auto &shadows = shadowedStores_.at(inst);
446             for (auto shadow : shadows) {
447                 shadow->GetBasicBlock()->SetMarker(marker);
448             }
449             if (!ExistsPathWithoutShadowingStores(inst->GetBasicBlock(), inst->GetBasicBlock(), marker)) {
450                 COMPILER_LOG(DEBUG, LSE_OPT) << LogInst(inst) << " is fully shadowed by aliased stores";
451                 auto alive = InstStoredValue(entry.second[0]);
452                 auto eliminated = eliminations_.find(alive);
453                 if (eliminated != eliminations_.end()) {
454                     alive = eliminated->second.val;
455                 }
456                 const Lse::HeapValue heap = {entry.second[0], alive, false, false};
457                 MarkForElimination(inst, entry.second[0], &heap);
458             }
459             inst->GetBasicBlock()->GetGraph()->EraseMarker(marker);
460         }
461     }
462 
CheckRefMustAlias(Inst * first,Inst * second)463     bool CheckRefMustAlias(Inst *first, Inst *second)
464     {
465         ASSERT(first != second);
466         auto firstIt = eliminations_.find(first);
467         if (firstIt != eliminations_.end() && firstIt->second.origin == second) {
468             return true;
469         }
470         auto secondIt = eliminations_.find(second);
471         if (secondIt != eliminations_.end() && secondIt->second.origin == first) {
472             return true;
473         }
474         return firstIt != eliminations_.end() && secondIt != eliminations_.end() &&
475                firstIt->second.val == secondIt->second.val;
476     }
477 
CheckInstMustAlias(Inst * first,Inst * second)478     bool CheckInstMustAlias(Inst *first, Inst *second)
479     {
480         aliasCalls_++;
481         auto aliasType = aa_.CheckInstAlias<true>(first, second);
482         COMPILER_LOG(DEBUG, LSE_OPT) << "Alias type: " << aliasType;
483         if (aliasType != ALIAS_IF_BASE_EQUALS) {
484             return aliasType == MUST_ALIAS;
485         }
486         return CheckRefMustAlias(first->GetDataFlowInput(0), second->GetDataFlowInput(0));
487     }
488 
489 private:
490     /// Return a MUST_ALIASed heap entry, nullptr if not present.
GetHeapValue(Inst * inst)491     const Lse::HeapValue *GetHeapValue(Inst *inst)
492     {
493         auto &blockHeap = heaps_[GetEquivClass(inst)].first.at(inst->GetBasicBlock());
494         for (auto &entry : blockHeap) {
495             if (CheckInstMustAlias(inst, entry.first)) {
496                 return &entry.second;
497             }
498         }
499         return nullptr;
500     }
501 
502     /// Update phi candidates with aliased accesses
UpdatePhis(Inst * inst)503     void UpdatePhis(Inst *inst)
504     {
505         Loop *loop = inst->GetBasicBlock()->GetLoop();
506 
507         while (!loop->IsRoot()) {
508             auto &phis = heaps_[GetEquivClass(inst)].second.at(loop);
509             for (auto &[mem, values] : phis) {
510                 aliasCalls_++;
511                 if (aa_.CheckInstAlias(inst, mem) != NO_ALIAS) {
512                     values.push_back(inst);
513                 }
514             }
515             loop = loop->GetOuterLoop();
516         }
517     }
518 
EraseAliasedValues(Lse::BasicBlockHeap & blockHeap,Inst * inst,Inst * baseObject,uint32_t & encounters)519     void EraseAliasedValues(Lse::BasicBlockHeap &blockHeap, Inst *inst, Inst *baseObject, uint32_t &encounters)
520     {
521         for (auto heapIter = blockHeap.begin(), heapLast = blockHeap.end(); heapIter != heapLast;) {
522             auto hinst = heapIter->first;
523             ASSERT(GetEquivClass(inst) == GetEquivClass(hinst));
524             aliasCalls_++;
525             if (aa_.CheckInstAlias(inst, hinst) == NO_ALIAS) {
526                 // Keep track if it's the same object but with different offset
527                 heapIter++;
528                 aliasCalls_++;
529                 if (aa_.CheckRefAlias(baseObject, hinst->GetDataFlowInput(0)) == MUST_ALIAS) {
530                     encounters++;
531                 }
532             } else {
533                 COMPILER_LOG(DEBUG, LSE_OPT)
534                     << "\tDrop from heap { " << LogInst(hinst) << ", v" << heapIter->second.val->GetId() << "}";
535                 heapIter = blockHeap.erase(heapIter);
536             }
537         }
538     }
539 
FinalizeLoopsWithPhiCands(Graph * graph,Loop * loop,ArenaMap<Inst *,InstVector> & phis)540     void FinalizeLoopsWithPhiCands(Graph *graph, Loop *loop, ArenaMap<Inst *, InstVector> &phis)
541     {
542         InstVector instsMustAlias(graph->GetLocalAllocator()->Adapter());
543         for (auto &[cand, insts] : phis) {
544             if (insts.empty()) {
545                 COMPILER_LOG(DEBUG, LSE_OPT) << "Skipping phi candidate " << LogInst(cand) << " (no users)";
546                 continue;
547             }
548 
549             instsMustAlias.clear();
550 
551             COMPILER_LOG(DEBUG, LSE_OPT) << "Processing phi candidate: " << LogInst(cand);
552             bool hasStores = false;
553             bool hasLoads = false;
554             bool valid = true;
555 
556             for (auto inst : insts) {
557                 // Skip eliminated instructions
558                 if (eliminations_.find(inst) != eliminations_.end()) {
559                     continue;
560                 }
561                 auto aliasResult = aa_.CheckInstAlias(cand, inst);
562                 if (aliasResult == MAY_ALIAS && inst->IsLoad()) {
563                     // Ignore MAY_ALIAS loads, they won't interfere with our analysis
564                     continue;
565                 } else if (aliasResult == MAY_ALIAS) {  // NOLINT(readability-else-after-return)
566                     // If we have a MAY_ALIAS store we can't be sure about our values
567                     // in phi creation
568                     ASSERT(inst->IsStore());
569                     COMPILER_LOG(DEBUG, LSE_OPT)
570                         << "Skipping phi candidate " << LogInst(cand) << ": MAY_ALIAS by " << LogInst(inst);
571                     valid = false;
572                     break;
573                 }
574                 ASSERT(aliasResult == MUST_ALIAS);
575 
576                 if (inst->IsStore() && inst->GetBasicBlock()->GetLoop() != loop) {
577                     // We can handle if loads are in inner loop, but if a store is in inner loop
578                     // then we can't replace anything
579                     COMPILER_LOG(DEBUG, LSE_OPT)
580                         << "Skipping phi candidate " << LogInst(cand) << ": " << LogInst(inst) << " is in inner loop";
581                     valid = false;
582                     break;
583                 }
584 
585                 instsMustAlias.push_back(inst);
586                 if (inst->IsStore()) {
587                     hasStores = true;
588                 } else if (inst->IsLoad()) {
589                     hasLoads = true;
590                 }
591             }
592             // Other than validity, it's also possible that all instructions are already eliminated
593             if (!valid || instsMustAlias.empty()) {
594                 continue;
595             }
596 
597             TryLoopDoElimination(cand, loop, &instsMustAlias, hasLoads, hasStores);
598         }
599     }
600 
TryLoopDoElimination(Inst * cand,Loop * loop,InstVector * insts,bool hasLoads,bool hasStores)601     void TryLoopDoElimination(Inst *cand, Loop *loop, InstVector *insts, bool hasLoads, bool hasStores)
602     {
603         if (hasStores) {
604             if (!hasLoads) {
605                 // Nothing to replace
606                 COMPILER_LOG(DEBUG, LSE_OPT)
607                     << "Skipping phi candidate " << LogInst(cand) << ": no loads to convert to phi";
608                 return;
609             }
610 
611             auto phi = cand->GetBasicBlock()->GetGraph()->CreateInstPhi(cand->GetType(), cand->GetPc());
612             loop->GetHeader()->AppendPhi(phi);
613 
614             if (!ProcessBackedges(phi, loop, cand, insts)) {
615                 loop->GetHeader()->RemoveInst(phi);
616                 return;
617             }
618 
619             LoopDoElimination(cand, loop, phi, insts);
620         } else {
621             ASSERT(hasLoads);
622             // Without stores, we can replace all MUST_ALIAS loads with instruction itself
623             LoopDoElimination(cand, loop, nullptr, insts);
624         }
625     }
626 
627 private:
628     AliasAnalysis &aa_;
629     Lse::HeapEqClasses &heaps_;
630     /* Map of instructions to be deleted with values to replace them with */
631     Lse::BasicBlockHeap eliminations_;
632     ArenaMap<Inst *, std::vector<Inst *>> shadowedStores_;
633     SaveStateBridgesBuilder ssb_;
634     InstVector disabledObjects_;
635     uint32_t aliasCalls_ {0};
636 };
637 
638 /// Returns true if the instruction invalidates the whole heap
IsHeapInvalidatingInst(Inst * inst)639 bool Lse::IsHeapInvalidatingInst(Inst *inst)
640 {
641     switch (inst->GetOpcode()) {
642         case Opcode::LoadStatic:
643             return inst->CastToLoadStatic()->GetVolatile();
644         case Opcode::LoadObject:
645             return inst->CastToLoadObject()->GetVolatile();
646         case Opcode::InitObject:
647         case Opcode::InitClass:
648         case Opcode::LoadAndInitClass:
649         case Opcode::UnresolvedLoadAndInitClass:
650         case Opcode::UnresolvedStoreStatic:
651         case Opcode::ResolveObjectField:
652         case Opcode::ResolveObjectFieldStatic:
653             return true;  //  runtime calls invalidate the heap
654         case Opcode::CallVirtual:
655             return !inst->CastToCallVirtual()->IsInlined();
656         case Opcode::CallResolvedVirtual:
657             return !inst->CastToCallResolvedVirtual()->IsInlined();
658         case Opcode::CallStatic:
659             return !inst->CastToCallStatic()->IsInlined();
660         case Opcode::CallDynamic:
661             return !inst->CastToCallDynamic()->IsInlined();
662         case Opcode::CallResolvedStatic:
663             return !inst->CastToCallResolvedStatic()->IsInlined();
664         case Opcode::Monitor:
665             return inst->CastToMonitor()->IsEntry();
666         default:
667             return inst->GetFlag(compiler::inst_flags::HEAP_INV);
668     }
669 }
670 
671 /// Returns true if the instruction reads from heap and we're not sure about its effects
IsHeapReadingInst(Inst * inst)672 static bool IsHeapReadingInst(Inst *inst)
673 {
674     if (inst->CanThrow()) {
675         return true;
676     }
677     if (inst->IsIntrinsic()) {
678         for (auto input : inst->GetInputs()) {
679             if (input.GetInst()->GetType() == DataType::REFERENCE) {
680                 return true;
681             }
682         }
683     }
684     if (inst->IsStore() && IsVolatileMemInst(inst)) {
685         // Heap writes before the volatile store should be visible from another thread after a corresponding volatile
686         // load
687         return true;
688     }
689     if (inst->GetOpcode() == Opcode::Monitor) {
690         return inst->CastToMonitor()->IsExit();
691     }
692     return false;
693 }
694 
CanEliminateInstruction(Inst * inst)695 bool Lse::CanEliminateInstruction(Inst *inst)
696 {
697     if (inst->IsBarrier()) {
698         COMPILER_LOG(DEBUG, LSE_OPT) << LogInst(inst) << " was suppressed: a barrier";
699         return false;
700     }
701     auto loop = inst->GetBasicBlock()->GetLoop();
702     if (loop->IsIrreducible()) {
703         COMPILER_LOG(DEBUG, LSE_OPT) << LogInst(inst) << " was suppressed: an irreducible loop";
704         return false;
705     }
706     if (loop->IsOsrLoop()) {
707         COMPILER_LOG(DEBUG, LSE_OPT) << LogInst(inst) << " was suppressed: an OSR loop";
708         return false;
709     }
710     if (loop->IsTryCatchLoop()) {
711         COMPILER_LOG(DEBUG, LSE_OPT) << LogInst(inst) << " was suppressed: an try-catch loop";
712         return false;
713     }
714     return true;
715 }
716 
InitializeHeap(BasicBlock * block,HeapEqClasses * heaps)717 void Lse::InitializeHeap(BasicBlock *block, HeapEqClasses *heaps)
718 {
719     for (int eqClass = Lse::EquivClass::EQ_ARRAY; eqClass != Lse::EquivClass::EQ_LAST; eqClass++) {
720         auto &heap = (*heaps)[eqClass].first;
721         auto &phiCands = (*heaps)[eqClass].second;
722         [[maybe_unused]] auto it = heap.emplace(block, GetGraph()->GetLocalAllocator()->Adapter());
723         ASSERT(it.second);
724         if (block->IsLoopHeader()) {
725             COMPILER_LOG(DEBUG, LSE_OPT) << "Append loop #" << block->GetLoop()->GetId();
726             if (std::find(rpoLoops_.begin(), rpoLoops_.end(), block->GetLoop()) == rpoLoops_.end()) {
727                 rpoLoops_.emplace_back(block->GetLoop());
728             }
729             [[maybe_unused]] auto phit = phiCands.emplace(block->GetLoop(), GetGraph()->GetLocalAllocator()->Adapter());
730             ASSERT(phit.second);
731         }
732     }
733 }
734 
735 /**
736  * While entering in the loop we put all heap values obtained from loads as phi candidates.
737  * Further phi candidates would replace MUST_ALIAS accesses in the loop if no aliased stores were met.
738  */
MergeHeapValuesForLoop(BasicBlock * block,HeapEqClasses * heaps)739 void Lse::MergeHeapValuesForLoop(BasicBlock *block, HeapEqClasses *heaps)
740 {
741     ASSERT(block->IsLoopHeader());
742 
743     // Do not eliminate anything in irreducible or osr loops
744     auto loop = block->GetLoop();
745     if (loop->IsIrreducible() || loop->IsOsrLoop() || loop->IsTryCatchLoop()) {
746         return;
747     }
748 
749     auto preheader = loop->GetPreHeader();
750 
751     for (int eqClass = Lse::EquivClass::EQ_ARRAY; eqClass != Lse::EquivClass::EQ_LAST; eqClass++) {
752         auto &preheaderHeap = (*heaps)[eqClass].first.at(preheader);
753 
754         auto &blockPhis = (*heaps)[eqClass].second.at(loop);
755         for (auto mem : preheaderHeap) {
756             blockPhis.try_emplace(mem.second.origin, GetGraph()->GetLocalAllocator()->Adapter());
757             COMPILER_LOG(DEBUG, LSE_OPT) << LogInst(mem.first) << " is a phi cand for BB #" << block->GetId();
758         }
759     }
760 }
761 
762 /// Merge heap values for passed block from its direct predecessors.
MergeHeapValuesForBlock(BasicBlock * block,HeapEqClasses * heaps,Marker phiFixupMrk)763 void Lse::MergeHeapValuesForBlock(BasicBlock *block, HeapEqClasses *heaps, Marker phiFixupMrk)
764 {
765     for (int eqClass = Lse::EquivClass::EQ_ARRAY; eqClass != Lse::EquivClass::EQ_LAST; eqClass++) {
766         auto &heap = (*heaps)[eqClass].first;
767         auto &blockHeap = heap.at(block);
768         /* Copy a heap of one of predecessors */
769         auto preds = block->GetPredsBlocks();
770         auto predIt = preds.begin();
771         if (predIt != preds.end()) {
772             blockHeap.insert(heap.at(*predIt).begin(), heap.at(*predIt).end());
773             predIt++;
774         }
775 
776         ProcessHeapValuesForBlock(&heap, block, phiFixupMrk);
777     }
778 }
779 
ProcessHeapValuesForBlock(Heap * heap,BasicBlock * block,Marker phiFixupMrk)780 void Lse::ProcessHeapValuesForBlock(Heap *heap, BasicBlock *block, Marker phiFixupMrk)
781 {
782     auto &blockHeap = heap->at(block);
783     auto preds = block->GetPredsBlocks();
784     auto predIt = preds.begin();
785     if (predIt != preds.end()) {
786         predIt++;
787     }
788 
789     /* Erase from the heap anything that disappeared or was changed in other predecessors */
790     while (predIt != preds.end()) {
791         auto predHeap = heap->at(*predIt);
792         auto heapIt = blockHeap.begin();
793         while (heapIt != blockHeap.end()) {
794             auto &heapValue = heapIt->second;
795             auto predInstIt = ProcessPredecessorHeap(predHeap, heapValue, block, heapIt->first);
796             if (predInstIt == predHeap.end() ||
797                 !ProcessHeapValues(heapValue, block, predInstIt, {preds.begin(), predIt}, phiFixupMrk)) {
798                 heapIt = blockHeap.erase(heapIt);
799                 continue;
800             }
801             if (predInstIt->second.val == heapValue.val) {
802                 heapIt->second.read |= predInstIt->second.read;
803             }
804 
805             heapIt++;
806         }
807         predIt++;
808     }
809 }
810 
ProcessPredecessorHeap(BasicBlockHeap & predHeap,HeapValue & heapValue,BasicBlock * block,Inst * curInst)811 Lse::BasicBlockHeapIter Lse::ProcessPredecessorHeap(BasicBlockHeap &predHeap, HeapValue &heapValue, BasicBlock *block,
812                                                     Inst *curInst)
813 {
814     auto predInstIt = predHeap.begin();
815     while (predInstIt != predHeap.end()) {
816         if (predInstIt->first == curInst) {
817             break;
818         }
819         if (visitor_->CheckInstMustAlias(curInst, predInstIt->first)) {
820             break;
821         }
822         predInstIt++;
823     }
824 
825     if (predInstIt == predHeap.end()) {
826         // If this is a phi we're creating during merge, delete it
827         if (heapValue.val->IsPhi() && heapValue.local) {
828             block->RemoveInst(heapValue.val);
829         }
830     }
831     return predInstIt;
832 }
833 
ProcessHeapValues(HeapValue & heapValue,BasicBlock * block,BasicBlockHeapIter predInstIt,PredBlocksItersPair iters,Marker phiFixupMrk)834 bool Lse::ProcessHeapValues(HeapValue &heapValue, BasicBlock *block, BasicBlockHeapIter predInstIt,
835                             PredBlocksItersPair iters, Marker phiFixupMrk)
836 {
837     auto [predsBegin, predIt] = iters;
838     if (predInstIt->second.val != heapValue.val) {
839         // Try to create a phi instead
840         // We limit possible phis to cases where value originated in the same predecessor
841         // in order to not increase register usage too much
842         if (block->GetLoop()->IsIrreducible() || block->IsCatch() ||
843             predInstIt->second.origin->GetBasicBlock() != *predIt) {
844             // If this is a phi we're creating during merge, delete it
845             if (heapValue.val->IsPhi() && heapValue.local) {
846                 block->RemoveInst(heapValue.val);
847             }
848             return false;
849         }
850         if (heapValue.val->IsPhi() && heapValue.local) {
851             heapValue.val->AppendInput(predInstIt->second.val);
852         } else {
853             // Values can only originate in a single block. If this predecessor is not
854             // the second predecessor, that means that this value did not originate in other
855             // predecessors, thus we don't create a phi
856             if (heapValue.origin->GetBasicBlock() != *(predsBegin) || std::distance(predsBegin, predIt) > 1) {
857                 return false;
858             }
859             auto phi = block->GetGraph()->CreateInstPhi(heapValue.origin->GetType(), heapValue.origin->GetPc());
860             block->AppendPhi(phi);
861             phi->AppendInput(heapValue.val);
862             phi->AppendInput(predInstIt->second.val);
863             phi->SetMarker(phiFixupMrk);
864             heapValue.val = phi;
865             heapValue.local = true;
866         }
867     }
868     return true;
869 }
870 
871 /**
872  * When creating phis while merging predecessor heaps, we don't know yet if
873  * we're creating a useful phi, and can't fix SaveStates because of that.
874  * Do that here.
875  */
FixupPhisInBlock(BasicBlock * block,Marker phiFixupMrk)876 void Lse::FixupPhisInBlock(BasicBlock *block, Marker phiFixupMrk)
877 {
878     for (auto phiInst : block->PhiInstsSafe()) {
879         auto phi = phiInst->CastToPhi();
880         if (!phi->IsMarked(phiFixupMrk)) {
881             continue;
882         }
883         if (!phi->HasUsers()) {
884             block->RemoveInst(phi);
885         } else if (GetGraph()->IsBytecodeOptimizer() || !phi->IsReferenceOrAny()) {
886             continue;
887         }
888         // Here case: !GetGraph()->IsBytecodeOptimizer() && phi->IsReferenceOrAny()
889         for (auto i = 0U; i < phi->GetInputsCount(); i++) {
890             auto input = phi->GetInput(i);
891             if (input.GetInst()->IsMovableObject()) {
892                 auto bb = phi->GetPhiInputBb(i);
893                 ssb_.SearchAndCreateMissingObjInSaveState(GetGraph(), input.GetInst(), bb->GetLastInst(), nullptr, bb);
894             }
895         }
896     }
897 }
898 
899 /**
900  * Returns the elimination code in two letter format.
901  *
902  * The first letter describes a [L]oad or [S]tore that was eliminated.
903  * The second letter describes the dominant [L]oad or [S]tore that is the
904  * reason why instruction was eliminated.
905  */
GetEliminationCode(Inst * inst,Inst * origin)906 const char *Lse::GetEliminationCode(Inst *inst, Inst *origin)
907 {
908     ASSERT(inst->IsMemory() && (origin->IsMemory() || origin->IsPhi()));
909     if (inst->IsLoad()) {
910         if (origin->IsLoad()) {
911             return "LL";
912         }
913         if (origin->IsStore()) {
914             return "LS";
915         }
916         if (origin->IsPhi()) {
917             return "LP";
918         }
919     }
920     if (inst->IsStore()) {
921         if (origin->IsLoad()) {
922             return "SL";
923         }
924         if (origin->IsStore()) {
925             return "SS";
926         }
927         if (origin->IsPhi()) {
928             return "SP";
929         }
930     }
931     UNREACHABLE();
932 }
933 
934 /**
935  * In the codegen of bytecode optimizer, we don't have corresponding pandasm
936  * for the IR `Cast` of with some pairs of input types and output types. So
937  * in the bytecode optimizer mode, we need to avoid generating such `Cast` IR.
938  * The following function gives the list of legal pairs of types.
939  * This function should not be used in compiler mode.
940  */
941 
IsTypeLegalForCast(DataType::Type output,DataType::Type input)942 static bool IsTypeLegalForCast(DataType::Type output, DataType::Type input)
943 {
944     ASSERT(output != input);
945     switch (input) {
946         case DataType::INT32:
947         case DataType::INT64:
948         case DataType::FLOAT64:
949             switch (output) {
950                 case DataType::FLOAT64:
951                 case DataType::INT64:
952                 case DataType::UINT32:
953                 case DataType::INT32:
954                 case DataType::INT16:
955                 case DataType::UINT16:
956                 case DataType::INT8:
957                 case DataType::UINT8:
958                 case DataType::ANY:
959                     return true;
960                 default:
961                     return false;
962             }
963         case DataType::REFERENCE:
964             return output == DataType::ANY;
965         default:
966             return false;
967     }
968 }
969 
970 /**
971  * Replace inputs of INST with VALUE and delete this INST.  If deletion led to
972  * appearance of instruction that has no users delete this instruction too.
973  */
DeleteInstruction(Inst * inst,Inst * value)974 void Lse::DeleteInstruction(Inst *inst, Inst *value)
975 {
976     // Have to cast a value to the type of eliminated inst. Actually required only for loads.
977     if (inst->GetType() != value->GetType() && inst->HasUsers()) {
978         ASSERT(inst->GetType() != DataType::REFERENCE && value->GetType() != DataType::REFERENCE);
979         // We will do nothing in bytecode optimizer mode when the types are not legal for cast.
980         if (GetGraph()->IsBytecodeOptimizer() && !IsTypeLegalForCast(inst->GetType(), value->GetType())) {
981             COMPILER_LOG(DEBUG, LSE_OPT) << LogInst(inst) << " was not eliminated: requires an inappropriate cast";
982             return;
983         }
984         auto cast = GetGraph()->CreateInstCast(inst->GetType(), inst->GetPc(), value, value->GetType());
985         inst->InsertAfter(cast);
986         value = cast;
987     }
988     inst->ReplaceUsers(value);
989 
990     ArenaQueue<Inst *> queue(GetGraph()->GetLocalAllocator()->Adapter());
991     queue.push(inst);
992     while (!queue.empty()) {
993         Inst *frontInst = queue.front();
994         BasicBlock *block = frontInst->GetBasicBlock();
995         queue.pop();
996 
997         // Have been already deleted or could not be deleted
998         if (block == nullptr || frontInst->HasUsers()) {
999             continue;
1000         }
1001 
1002         for (auto &input : frontInst->GetInputs()) {
1003             /* Delete only instructions that has no data flow impact */
1004             if (input.GetInst()->HasPseudoDestination()) {
1005                 queue.push(input.GetInst());
1006             }
1007         }
1008         block->RemoveInst(frontInst);
1009         applied_ = true;
1010     }
1011 }
1012 
DeleteInstructions(const BasicBlockHeap & eliminated)1013 void Lse::DeleteInstructions(const BasicBlockHeap &eliminated)
1014 {
1015     for (auto elim : eliminated) {
1016         Inst *inst = elim.first;
1017         Inst *origin = elim.second.origin;
1018         Inst *value = elim.second.val;
1019 
1020         ASSERT_DO(eliminated.find(value) == eliminated.end(),
1021                   (std::cerr << "Instruction:\n", inst->Dump(&std::cerr),
1022                    std::cerr << "is replaced by eliminated value:\n", value->Dump(&std::cerr)));
1023 
1024         while (origin->GetBasicBlock() == nullptr) {
1025             auto elimIt = eliminated.find(origin);
1026             ASSERT(elimIt != eliminated.end());
1027             origin = elimIt->second.origin;
1028         }
1029 
1030         GetGraph()->GetEventWriter().EventLse(inst->GetId(), inst->GetPc(), origin->GetId(), origin->GetPc(),
1031                                               GetEliminationCode(inst, origin));
1032         // Try to update savestates
1033         if (!GetGraph()->IsBytecodeOptimizer() && value->IsMovableObject()) {
1034             if (!value->IsPhi() && origin->IsMovableObject() && origin->IsLoad() && origin->IsDominate(inst)) {
1035                 // this branch is not required, but can be faster if origin is closer to inst than value
1036                 ssb_.SearchAndCreateMissingObjInSaveState(GetGraph(), origin, inst);
1037             } else {
1038                 ssb_.SearchAndCreateMissingObjInSaveState(GetGraph(), value, inst);
1039             }
1040         }
1041         DeleteInstruction(inst, value);
1042     }
1043 }
1044 
ApplyHoistToCandidate(Loop * loop,Inst * alive)1045 void Lse::ApplyHoistToCandidate(Loop *loop, Inst *alive)
1046 {
1047     ASSERT(alive->IsLoad());
1048     COMPILER_LOG(DEBUG, LSE_OPT) << " v" << alive->GetId();
1049     if (alive->GetBasicBlock()->GetLoop() != loop) {
1050         COMPILER_LOG(DEBUG, LSE_OPT) << "\tFailed because inst is part of a more inner loop";
1051         return;
1052     }
1053     if (GetGraph()->IsInstThrowable(alive)) {
1054         COMPILER_LOG(DEBUG, LSE_OPT) << "\tFailed because inst is throwable";
1055         return;
1056     }
1057     for (const auto &input : alive->GetInputs()) {
1058         if (!input.GetInst()->GetBasicBlock()->IsDominate(loop->GetPreHeader())) {
1059             COMPILER_LOG(DEBUG, LSE_OPT) << "\tFailed because of def-use chain of inputs: " << LogInst(input.GetInst());
1060             return;
1061         }
1062     }
1063     const auto &rpo = GetGraph()->GetBlocksRPO();
1064     auto blockIter = std::find(rpo.rbegin(), rpo.rend(), alive->GetBasicBlock());
1065     ASSERT(blockIter != rpo.rend());
1066     auto inst = alive->GetPrev();
1067     while (*blockIter != loop->GetPreHeader()) {
1068         while (inst != nullptr) {
1069             if (IsHeapInvalidatingInst(inst) || (inst->IsMemory() && GetEquivClass(inst) == GetEquivClass(alive) &&
1070                                                  GetGraph()->CheckInstAlias(inst, alive) != NO_ALIAS)) {
1071                 COMPILER_LOG(DEBUG, LSE_OPT) << "\tFailed because of invalidating inst:" << LogInst(inst);
1072                 return;
1073             }
1074             inst = inst->GetPrev();
1075         }
1076         blockIter++;
1077         inst = (*blockIter)->GetLastInst();
1078     }
1079     alive->GetBasicBlock()->EraseInst(alive, true);
1080     auto lastInst = loop->GetPreHeader()->GetLastInst();
1081     if (lastInst != nullptr && lastInst->IsControlFlow()) {
1082         loop->GetPreHeader()->InsertBefore(alive, lastInst);
1083     } else {
1084         loop->GetPreHeader()->AppendInst(alive);
1085     }
1086     if (!GetGraph()->IsBytecodeOptimizer() && alive->IsMovableObject()) {
1087         ASSERT(!loop->IsIrreducible());
1088         // loop backedges will be walked inside SSB
1089         ssb_.SearchAndCreateMissingObjInSaveState(GetGraph(), alive, loop->GetHeader()->GetLastInst(), nullptr,
1090                                                   loop->GetHeader());
1091     }
1092     applied_ = true;
1093 }
1094 
TryToHoistLoadFromLoop(Loop * loop,HeapEqClasses * heaps,const BasicBlockHeap * eliminated)1095 void Lse::TryToHoistLoadFromLoop(Loop *loop, HeapEqClasses *heaps, const BasicBlockHeap *eliminated)
1096 {
1097     for (auto innerLoop : loop->GetInnerLoops()) {
1098         TryToHoistLoadFromLoop(innerLoop, heaps, eliminated);
1099     }
1100 
1101     if (loop->IsIrreducible() || loop->IsOsrLoop()) {
1102         return;
1103     }
1104 
1105     auto &backBbs = loop->GetBackEdges();
1106     beAlive_.clear();
1107 
1108     // Initiate alive set
1109     auto backBb = backBbs.begin();
1110     ASSERT(backBb != backBbs.end());
1111     for (int eqClass = Lse::EquivClass::EQ_ARRAY; eqClass != Lse::EquivClass::EQ_LAST; eqClass++) {
1112         for (const auto &entry : (*heaps)[eqClass].first.at(*backBb)) {
1113             // Do not touch Stores and eliminated ones
1114             if (entry.first->IsLoad() && eliminated->find(entry.first) == eliminated->end()) {
1115                 beAlive_.insert(entry.first);
1116             }
1117         }
1118     }
1119 
1120     // Throw values not alive on other backedges
1121     while (++backBb != backBbs.end()) {
1122         auto alive = beAlive_.begin();
1123         while (alive != beAlive_.end()) {
1124             auto &heap = heaps->at(GetEquivClass(*alive)).first;
1125             if (heap.at(*backBb).find(*alive) == heap.at(*backBb).end()) {
1126                 alive = beAlive_.erase(alive);
1127             } else {
1128                 alive++;
1129             }
1130         }
1131     }
1132     COMPILER_LOG(DEBUG, LSE_OPT) << "Loop #" << loop->GetId() << " has the following motion candidates:";
1133     for (auto alive : beAlive_) {
1134         ApplyHoistToCandidate(loop, alive);
1135     }
1136 }
1137 
ProcessAllBBs(HeapEqClasses * heaps,Marker phiFixupMrk)1138 void Lse::ProcessAllBBs(HeapEqClasses *heaps, Marker phiFixupMrk)
1139 {
1140     InstVector invs(GetGraph()->GetLocalAllocator()->Adapter());
1141     for (auto block : GetGraph()->GetBlocksRPO()) {
1142         COMPILER_LOG(DEBUG, LSE_OPT) << "Processing BB " << block->GetId();
1143         InitializeHeap(block, heaps);
1144 
1145         if (block->IsLoopHeader()) {
1146             MergeHeapValuesForLoop(block, heaps);
1147         } else {
1148             MergeHeapValuesForBlock(block, heaps, phiFixupMrk);
1149         }
1150 
1151         for (auto inst : block->Insts()) {
1152             if (IsHeapReadingInst(inst)) {
1153                 visitor_->SetHeapAsRead(block);
1154             }
1155             if (IsHeapInvalidatingInst(inst)) {
1156                 COMPILER_LOG(DEBUG, LSE_OPT) << LogInst(inst) << " invalidates heap";
1157                 visitor_->InvalidateHeap(block);
1158             } else if (inst->IsLoad()) {
1159                 visitor_->VisitLoad(inst);
1160             } else if (inst->IsStore()) {
1161                 visitor_->VisitStore(inst, InstStoredValue(inst));
1162             }
1163             if (inst->IsIntrinsic()) {
1164                 visitor_->VisitIntrinsic(inst, &invs);
1165             }
1166             // If we call Alias Analysis too much, we assume that this block has too many
1167             // instructions and we should bail in favor of performance.
1168             if (visitor_->GetAliasAnalysisCallCount() > Lse::AA_CALLS_LIMIT) {
1169                 COMPILER_LOG(DEBUG, LSE_OPT) << "Exiting BB " << block->GetId() << ": too many Alias Analysis calls";
1170                 visitor_->InvalidateHeap(block);
1171                 break;
1172             }
1173         }
1174         visitor_->ClearLocalValuesFromHeap(block);
1175         visitor_->ResetLimits();
1176     }
1177 }
1178 
RunImpl()1179 bool Lse::RunImpl()
1180 {
1181     if (GetGraph()->IsBytecodeOptimizer() && GetGraph()->IsDynamicMethod()) {
1182         COMPILER_LOG(DEBUG, LSE_OPT) << "Load-Store Elimination skipped: es bytecode optimizer";
1183         return false;
1184     }
1185 
1186     HeapEqClasses heaps(GetGraph()->GetLocalAllocator()->Adapter());
1187     for (int eqClass = Lse::EquivClass::EQ_ARRAY; eqClass != Lse::EquivClass::EQ_LAST; eqClass++) {
1188         std::pair<Heap, PhiCands> heapPhi(GetGraph()->GetLocalAllocator()->Adapter(),
1189                                           GetGraph()->GetLocalAllocator()->Adapter());
1190         heaps.emplace_back(heapPhi);
1191     }
1192 
1193     GetGraph()->RunPass<LoopAnalyzer>();
1194     GetGraph()->RunPass<AliasAnalysis>();
1195 
1196     LseVisitor visitorInstance(GetGraph(), &heaps);
1197     visitor_ = &visitorInstance;
1198     auto markerHolder = MarkerHolder(GetGraph());
1199     auto phiFixupMrk = markerHolder.GetMarker();
1200 
1201     ProcessAllBBs(&heaps, phiFixupMrk);
1202 
1203     visitor_->FinalizeShadowedStores();
1204     visitor_->FinalizeLoops(GetGraph(), rpoLoops_);
1205 
1206     auto &eliminated = visitor_->GetEliminations();
1207     GetGraph()->RunPass<DominatorsTree>();
1208     if (hoistLoads_) {
1209         for (auto loop : GetGraph()->GetRootLoop()->GetInnerLoops()) {
1210             TryToHoistLoadFromLoop(loop, &heaps, &eliminated);
1211         }
1212     }
1213 
1214     DeleteInstructions(visitor_->GetEliminations());
1215 
1216     for (auto block : GetGraph()->GetBlocksRPO()) {
1217         FixupPhisInBlock(block, phiFixupMrk);
1218     }
1219 
1220     COMPILER_LOG(DEBUG, LSE_OPT) << "Load-Store Elimination complete";
1221     return applied_;
1222 }
1223 }  // namespace ark::compiler
1224