• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 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 if (hvalue.val->IsMovableObject()) {
369             ASSERT(!loop->IsIrreducible());
370             auto head = loop->GetHeader();
371             // Add cand value to all SaveStates in this loop and inner loops
372             ssb_.SearchAndCreateMissingObjInSaveState(head->GetGraph(), hvalue.val, head->GetLastInst(), nullptr, head);
373         }
374     }
375 
376     /// Add eliminations inside loops if there is no overwrites on backedges.
FinalizeLoops(Graph * graph,const ArenaVector<Loop * > & rpoLoops)377     void FinalizeLoops(Graph *graph, const ArenaVector<Loop *> &rpoLoops)
378     {
379         for (int eqClass = Lse::EquivClass::EQ_ARRAY; eqClass != Lse::EquivClass::EQ_LAST; eqClass++) {
380             for (auto loopIt = rpoLoops.rbegin(); loopIt != rpoLoops.rend(); loopIt++) {
381                 auto loop = *loopIt;
382                 auto &phis = heaps_[eqClass].second.at(loop);
383                 COMPILER_LOG(DEBUG, LSE_OPT) << "Finalizing loop #" << loop->GetId();
384                 FinalizeLoopsWithPhiCands(graph, loop, phis);
385             }
386 
387             COMPILER_LOG(DEBUG, LSE_OPT) << "Fixing elimination list after backedge substitutions";
388             for (auto &entry : eliminations_) {
389                 auto hvalue = entry.second;
390                 if (eliminations_.find(hvalue.val) == eliminations_.end()) {
391                     continue;
392                 }
393 
394                 [[maybe_unused]] auto initial = hvalue.val;
395                 while (eliminations_.find(hvalue.val) != eliminations_.end()) {
396                     auto elimValue = eliminations_[hvalue.val];
397                     COMPILER_LOG(DEBUG, LSE_OPT) << "\t" << LogInst(hvalue.val)
398                                                  << " is eliminated. Trying to replace by " << LogInst(elimValue.val);
399                     hvalue = elimValue;
400                     ASSERT_PRINT(initial != hvalue.val, "A cyclic elimination has been detected");
401                 }
402                 entry.second = hvalue;
403             }
404         }
405     }
406 
ExistsPathWithoutShadowingStores(BasicBlock * start,BasicBlock * block,Marker marker)407     bool ExistsPathWithoutShadowingStores(BasicBlock *start, BasicBlock *block, Marker marker)
408     {
409         if (block->IsEndBlock()) {
410             // Found a path without shadowing stores
411             return true;
412         }
413         if (block->IsMarked(marker)) {
414             // Found a path with shadowing store
415             return false;
416         }
417         ASSERT(start->GetLoop() == block->GetLoop());
418         for (auto succ : block->GetSuccsBlocks()) {
419             if (block->GetLoop() != succ->GetLoop()) {
420                 // Edge to a different loop. We currently don't carry heap values through loops and don't
421                 // handle irreducible loops, so we can't be sure there are shadows on this path.
422                 return true;
423             }
424             if (succ == block->GetLoop()->GetHeader()) {
425                 // If next block is a loop header for this block's loop it means that instruction shadows itself
426                 return true;
427             }
428             if (ExistsPathWithoutShadowingStores(start, succ, marker)) {
429                 return true;
430             }
431         }
432         return false;
433     }
434 
FinalizeShadowedStores()435     void FinalizeShadowedStores()
436     {
437         // We want to see if there are no paths from the store that evade any of its shadow stores
438         for (auto &entry : shadowedStores_) {
439             auto inst = entry.first;
440             auto marker = inst->GetBasicBlock()->GetGraph()->NewMarker();
441             auto &shadows = shadowedStores_.at(inst);
442             for (auto shadow : shadows) {
443                 shadow->GetBasicBlock()->SetMarker(marker);
444             }
445             if (!ExistsPathWithoutShadowingStores(inst->GetBasicBlock(), inst->GetBasicBlock(), marker)) {
446                 COMPILER_LOG(DEBUG, LSE_OPT) << LogInst(inst) << " is fully shadowed by aliased stores";
447                 auto alive = InstStoredValue(entry.second[0]);
448                 auto eliminated = eliminations_.find(alive);
449                 if (eliminated != eliminations_.end()) {
450                     alive = eliminated->second.val;
451                 }
452                 const Lse::HeapValue heap = {entry.second[0], alive, false, false};
453                 MarkForElimination(inst, entry.second[0], &heap);
454             }
455             inst->GetBasicBlock()->GetGraph()->EraseMarker(marker);
456         }
457     }
458 
459 private:
460     /// Return a MUST_ALIASed heap entry, nullptr if not present.
GetHeapValue(Inst * inst)461     const Lse::HeapValue *GetHeapValue(Inst *inst)
462     {
463         auto &blockHeap = heaps_[GetEquivClass(inst)].first.at(inst->GetBasicBlock());
464         for (auto &entry : blockHeap) {
465             aliasCalls_++;
466             if (aa_.CheckInstAlias(inst, entry.first) == MUST_ALIAS) {
467                 return &entry.second;
468             }
469         }
470         return nullptr;
471     }
472 
473     /// Update phi candidates with aliased accesses
UpdatePhis(Inst * inst)474     void UpdatePhis(Inst *inst)
475     {
476         Loop *loop = inst->GetBasicBlock()->GetLoop();
477 
478         while (!loop->IsRoot()) {
479             auto &phis = heaps_[GetEquivClass(inst)].second.at(loop);
480             for (auto &[mem, values] : phis) {
481                 aliasCalls_++;
482                 if (aa_.CheckInstAlias(inst, mem) != NO_ALIAS) {
483                     values.push_back(inst);
484                 }
485             }
486             loop = loop->GetOuterLoop();
487         }
488     }
489 
EraseAliasedValues(Lse::BasicBlockHeap & blockHeap,Inst * inst,Inst * baseObject,uint32_t & encounters)490     void EraseAliasedValues(Lse::BasicBlockHeap &blockHeap, Inst *inst, Inst *baseObject, uint32_t &encounters)
491     {
492         for (auto heapIter = blockHeap.begin(), heapLast = blockHeap.end(); heapIter != heapLast;) {
493             auto hinst = heapIter->first;
494             ASSERT(GetEquivClass(inst) == GetEquivClass(hinst));
495             aliasCalls_++;
496             if (aa_.CheckInstAlias(inst, hinst) == NO_ALIAS) {
497                 // Keep track if it's the same object but with different offset
498                 heapIter++;
499                 aliasCalls_++;
500                 if (aa_.CheckRefAlias(baseObject, hinst->GetDataFlowInput(0)) == MUST_ALIAS) {
501                     encounters++;
502                 }
503             } else {
504                 COMPILER_LOG(DEBUG, LSE_OPT)
505                     << "\tDrop from heap { " << LogInst(hinst) << ", v" << heapIter->second.val->GetId() << "}";
506                 heapIter = blockHeap.erase(heapIter);
507             }
508         }
509     }
510 
FinalizeLoopsWithPhiCands(Graph * graph,Loop * loop,ArenaMap<Inst *,InstVector> & phis)511     void FinalizeLoopsWithPhiCands(Graph *graph, Loop *loop, ArenaMap<Inst *, InstVector> &phis)
512     {
513         InstVector instsMustAlias(graph->GetLocalAllocator()->Adapter());
514         for (auto &[cand, insts] : phis) {
515             if (insts.empty()) {
516                 COMPILER_LOG(DEBUG, LSE_OPT) << "Skipping phi candidate " << LogInst(cand) << " (no users)";
517                 continue;
518             }
519 
520             instsMustAlias.clear();
521 
522             COMPILER_LOG(DEBUG, LSE_OPT) << "Processing phi candidate: " << LogInst(cand);
523             bool hasStores = false;
524             bool hasLoads = false;
525             bool valid = true;
526 
527             for (auto inst : insts) {
528                 // Skip eliminated instructions
529                 if (eliminations_.find(inst) != eliminations_.end()) {
530                     continue;
531                 }
532                 auto aliasResult = aa_.CheckInstAlias(cand, inst);
533                 if (aliasResult == MAY_ALIAS && inst->IsLoad()) {
534                     // Ignore MAY_ALIAS loads, they won't interfere with our analysis
535                     continue;
536                 } else if (aliasResult == MAY_ALIAS) {  // NOLINT(readability-else-after-return)
537                     // If we have a MAY_ALIAS store we can't be sure about our values
538                     // in phi creation
539                     ASSERT(inst->IsStore());
540                     COMPILER_LOG(DEBUG, LSE_OPT)
541                         << "Skipping phi candidate " << LogInst(cand) << ": MAY_ALIAS by " << LogInst(inst);
542                     valid = false;
543                     break;
544                 }
545                 ASSERT(aliasResult == MUST_ALIAS);
546 
547                 if (inst->IsStore() && inst->GetBasicBlock()->GetLoop() != loop) {
548                     // We can handle if loads are in inner loop, but if a store is in inner loop
549                     // then we can't replace anything
550                     COMPILER_LOG(DEBUG, LSE_OPT)
551                         << "Skipping phi candidate " << LogInst(cand) << ": " << LogInst(inst) << " is in inner loop";
552                     valid = false;
553                     break;
554                 }
555 
556                 instsMustAlias.push_back(inst);
557                 if (inst->IsStore()) {
558                     hasStores = true;
559                 } else if (inst->IsLoad()) {
560                     hasLoads = true;
561                 }
562             }
563             // Other than validity, it's also possible that all instructions are already eliminated
564             if (!valid || instsMustAlias.empty()) {
565                 continue;
566             }
567 
568             TryLoopDoElimination(cand, loop, &instsMustAlias, hasLoads, hasStores);
569         }
570     }
571 
TryLoopDoElimination(Inst * cand,Loop * loop,InstVector * insts,bool hasLoads,bool hasStores)572     void TryLoopDoElimination(Inst *cand, Loop *loop, InstVector *insts, bool hasLoads, bool hasStores)
573     {
574         if (hasStores) {
575             if (!hasLoads) {
576                 // Nothing to replace
577                 COMPILER_LOG(DEBUG, LSE_OPT)
578                     << "Skipping phi candidate " << LogInst(cand) << ": no loads to convert to phi";
579                 return;
580             }
581 
582             auto phi = cand->GetBasicBlock()->GetGraph()->CreateInstPhi(cand->GetType(), cand->GetPc());
583             loop->GetHeader()->AppendPhi(phi);
584 
585             if (!ProcessBackedges(phi, loop, cand, insts)) {
586                 loop->GetHeader()->RemoveInst(phi);
587                 return;
588             }
589 
590             LoopDoElimination(cand, loop, phi, insts);
591         } else {
592             ASSERT(hasLoads);
593             // Without stores, we can replace all MUST_ALIAS loads with instruction itself
594             LoopDoElimination(cand, loop, nullptr, insts);
595         }
596     }
597 
598 private:
599     AliasAnalysis &aa_;
600     Lse::HeapEqClasses &heaps_;
601     /* Map of instructions to be deleted with values to replace them with */
602     Lse::BasicBlockHeap eliminations_;
603     ArenaMap<Inst *, std::vector<Inst *>> shadowedStores_;
604     SaveStateBridgesBuilder ssb_;
605     InstVector disabledObjects_;
606     uint32_t aliasCalls_ {0};
607 };
608 
609 /// Returns true if the instruction invalidates the whole heap
IsHeapInvalidatingInst(Inst * inst)610 static bool IsHeapInvalidatingInst(Inst *inst)
611 {
612     switch (inst->GetOpcode()) {
613         case Opcode::LoadStatic:
614             return inst->CastToLoadStatic()->GetVolatile();
615         case Opcode::LoadObject:
616             return inst->CastToLoadObject()->GetVolatile();
617         case Opcode::InitObject:
618         case Opcode::InitClass:
619         case Opcode::LoadAndInitClass:
620         case Opcode::UnresolvedLoadAndInitClass:
621         case Opcode::UnresolvedStoreStatic:
622         case Opcode::ResolveObjectField:
623         case Opcode::ResolveObjectFieldStatic:
624             return true;  //  runtime calls invalidate the heap
625         case Opcode::CallVirtual:
626             return !inst->CastToCallVirtual()->IsInlined();
627         case Opcode::CallResolvedVirtual:
628             return !inst->CastToCallResolvedVirtual()->IsInlined();
629         case Opcode::CallStatic:
630             return !inst->CastToCallStatic()->IsInlined();
631         case Opcode::CallDynamic:
632             return !inst->CastToCallDynamic()->IsInlined();
633         case Opcode::CallResolvedStatic:
634             return !inst->CastToCallResolvedStatic()->IsInlined();
635         case Opcode::Monitor:
636             return inst->CastToMonitor()->IsEntry();
637         default:
638             return inst->GetFlag(compiler::inst_flags::HEAP_INV);
639     }
640 }
641 
642 /// Returns true if the instruction reads from heap and we're not sure about its effects
IsHeapReadingInst(Inst * inst)643 static bool IsHeapReadingInst(Inst *inst)
644 {
645     if (inst->CanThrow()) {
646         return true;
647     }
648     if (inst->IsIntrinsic()) {
649         for (auto input : inst->GetInputs()) {
650             if (input.GetInst()->GetType() == DataType::REFERENCE) {
651                 return true;
652             }
653         }
654     }
655     if (inst->IsStore() && IsVolatileMemInst(inst)) {
656         // Heap writes before the volatile store should be visible from another thread after a corresponding volatile
657         // load
658         return true;
659     }
660     if (inst->GetOpcode() == Opcode::Monitor) {
661         return inst->CastToMonitor()->IsExit();
662     }
663     return false;
664 }
665 
CanEliminateInstruction(Inst * inst)666 bool Lse::CanEliminateInstruction(Inst *inst)
667 {
668     if (inst->IsBarrier()) {
669         COMPILER_LOG(DEBUG, LSE_OPT) << LogInst(inst) << " was suppressed: a barrier";
670         return false;
671     }
672     auto loop = inst->GetBasicBlock()->GetLoop();
673     if (loop->IsIrreducible()) {
674         COMPILER_LOG(DEBUG, LSE_OPT) << LogInst(inst) << " was suppressed: an irreducible loop";
675         return false;
676     }
677     if (loop->IsOsrLoop()) {
678         COMPILER_LOG(DEBUG, LSE_OPT) << LogInst(inst) << " was suppressed: an OSR loop";
679         return false;
680     }
681     if (loop->IsTryCatchLoop()) {
682         COMPILER_LOG(DEBUG, LSE_OPT) << LogInst(inst) << " was suppressed: an try-catch loop";
683         return false;
684     }
685     return true;
686 }
687 
InitializeHeap(BasicBlock * block,HeapEqClasses * heaps)688 void Lse::InitializeHeap(BasicBlock *block, HeapEqClasses *heaps)
689 {
690     for (int eqClass = Lse::EquivClass::EQ_ARRAY; eqClass != Lse::EquivClass::EQ_LAST; eqClass++) {
691         auto &heap = (*heaps)[eqClass].first;
692         auto &phiCands = (*heaps)[eqClass].second;
693         [[maybe_unused]] auto it = heap.emplace(block, GetGraph()->GetLocalAllocator()->Adapter());
694         ASSERT(it.second);
695         if (block->IsLoopHeader()) {
696             COMPILER_LOG(DEBUG, LSE_OPT) << "Append loop #" << block->GetLoop()->GetId();
697             if (std::find(rpoLoops_.begin(), rpoLoops_.end(), block->GetLoop()) == rpoLoops_.end()) {
698                 rpoLoops_.emplace_back(block->GetLoop());
699             }
700             [[maybe_unused]] auto phit = phiCands.emplace(block->GetLoop(), GetGraph()->GetLocalAllocator()->Adapter());
701             ASSERT(phit.second);
702         }
703     }
704 }
705 
706 /**
707  * While entering in the loop we put all heap values obtained from loads as phi candidates.
708  * Further phi candidates would replace MUST_ALIAS accesses in the loop if no aliased stores were met.
709  */
MergeHeapValuesForLoop(BasicBlock * block,HeapEqClasses * heaps)710 void Lse::MergeHeapValuesForLoop(BasicBlock *block, HeapEqClasses *heaps)
711 {
712     ASSERT(block->IsLoopHeader());
713 
714     // Do not eliminate anything in irreducible or osr loops
715     auto loop = block->GetLoop();
716     if (loop->IsIrreducible() || loop->IsOsrLoop() || loop->IsTryCatchLoop()) {
717         return;
718     }
719 
720     auto preheader = loop->GetPreHeader();
721 
722     for (int eqClass = Lse::EquivClass::EQ_ARRAY; eqClass != Lse::EquivClass::EQ_LAST; eqClass++) {
723         auto &preheaderHeap = (*heaps)[eqClass].first.at(preheader);
724 
725         auto &blockPhis = (*heaps)[eqClass].second.at(loop);
726         for (auto mem : preheaderHeap) {
727             blockPhis.try_emplace(mem.second.origin, GetGraph()->GetLocalAllocator()->Adapter());
728             COMPILER_LOG(DEBUG, LSE_OPT) << LogInst(mem.first) << " is a phi cand for BB #" << block->GetId();
729         }
730     }
731 }
732 
733 /// Merge heap values for passed block from its direct predecessors.
MergeHeapValuesForBlock(BasicBlock * block,HeapEqClasses * heaps,Marker phiFixupMrk)734 size_t Lse::MergeHeapValuesForBlock(BasicBlock *block, HeapEqClasses *heaps, Marker phiFixupMrk)
735 {
736     size_t aliasCalls = 0;
737     for (int eqClass = Lse::EquivClass::EQ_ARRAY; eqClass != Lse::EquivClass::EQ_LAST; eqClass++) {
738         auto &heap = (*heaps)[eqClass].first;
739         auto &blockHeap = heap.at(block);
740         /* Copy a heap of one of predecessors */
741         auto preds = block->GetPredsBlocks();
742         auto predIt = preds.begin();
743         if (predIt != preds.end()) {
744             blockHeap.insert(heap.at(*predIt).begin(), heap.at(*predIt).end());
745             predIt++;
746         }
747 
748         aliasCalls += ProcessHeapValuesForBlock(&heap, block, phiFixupMrk);
749     }
750     return aliasCalls;
751 }
752 
ProcessHeapValuesForBlock(Heap * heap,BasicBlock * block,Marker phiFixupMrk)753 size_t Lse::ProcessHeapValuesForBlock(Heap *heap, BasicBlock *block, Marker phiFixupMrk)
754 {
755     size_t aliasCalls = 0;
756     auto &blockHeap = heap->at(block);
757     auto preds = block->GetPredsBlocks();
758     auto predIt = preds.begin();
759     if (predIt != preds.end()) {
760         predIt++;
761     }
762 
763     /* Erase from the heap anything that disappeared or was changed in other predecessors */
764     while (predIt != preds.end()) {
765         auto predHeap = heap->at(*predIt);
766         auto heapIt = blockHeap.begin();
767         while (heapIt != blockHeap.end()) {
768             auto &heapValue = heapIt->second;
769             auto predInstIt = ProcessPredecessorHeap(predHeap, heapValue, block, heapIt->first, &aliasCalls);
770             if (predInstIt == predHeap.end() ||
771                 !ProcessHeapValues(heapValue, block, predInstIt, {preds.begin(), predIt}, phiFixupMrk)) {
772                 heapIt = blockHeap.erase(heapIt);
773                 continue;
774             }
775             if (predInstIt->second.val == heapValue.val) {
776                 heapIt->second.read |= predInstIt->second.read;
777             }
778 
779             heapIt++;
780         }
781         predIt++;
782     }
783     return aliasCalls;
784 }
785 
ProcessPredecessorHeap(BasicBlockHeap & predHeap,HeapValue & heapValue,BasicBlock * block,Inst * curInst,size_t * aliasCalls)786 Lse::BasicBlockHeapIter Lse::ProcessPredecessorHeap(BasicBlockHeap &predHeap, HeapValue &heapValue, BasicBlock *block,
787                                                     Inst *curInst, size_t *aliasCalls)
788 {
789     auto predInstIt = predHeap.begin();
790     while (predInstIt != predHeap.end()) {
791         if (predInstIt->first == curInst) {
792             break;
793         }
794         (*aliasCalls)++;
795         if (GetGraph()->CheckInstAlias(curInst, predInstIt->first) == MUST_ALIAS) {
796             break;
797         }
798         predInstIt++;
799     }
800 
801     if (predInstIt == predHeap.end()) {
802         // If this is a phi we're creating during merge, delete it
803         if (heapValue.val->IsPhi() && heapValue.local) {
804             block->RemoveInst(heapValue.val);
805         }
806     }
807     return predInstIt;
808 }
809 
ProcessHeapValues(HeapValue & heapValue,BasicBlock * block,BasicBlockHeapIter predInstIt,PredBlocksItersPair iters,Marker phiFixupMrk)810 bool Lse::ProcessHeapValues(HeapValue &heapValue, BasicBlock *block, BasicBlockHeapIter predInstIt,
811                             PredBlocksItersPair iters, Marker phiFixupMrk)
812 {
813     auto [predsBegin, predIt] = iters;
814     if (predInstIt->second.val != heapValue.val) {
815         // Try to create a phi instead
816         // We limit possible phis to cases where value originated in the same predecessor
817         // in order to not increase register usage too much
818         if (block->GetLoop()->IsIrreducible() || block->IsCatch() ||
819             predInstIt->second.origin->GetBasicBlock() != *predIt) {
820             // If this is a phi we're creating during merge, delete it
821             if (heapValue.val->IsPhi() && heapValue.local) {
822                 block->RemoveInst(heapValue.val);
823             }
824             return false;
825         }
826         if (heapValue.val->IsPhi() && heapValue.local) {
827             heapValue.val->AppendInput(predInstIt->second.val);
828         } else {
829             // Values can only originate in a single block. If this predecessor is not
830             // the second predecessor, that means that this value did not originate in other
831             // predecessors, thus we don't create a phi
832             if (heapValue.origin->GetBasicBlock() != *(predsBegin) || std::distance(predsBegin, predIt) > 1) {
833                 return false;
834             }
835             auto phi = block->GetGraph()->CreateInstPhi(heapValue.origin->GetType(), heapValue.origin->GetPc());
836             block->AppendPhi(phi);
837             phi->AppendInput(heapValue.val);
838             phi->AppendInput(predInstIt->second.val);
839             phi->SetMarker(phiFixupMrk);
840             heapValue.val = phi;
841             heapValue.local = true;
842         }
843     }
844     return true;
845 }
846 
847 /**
848  * When creating phis while merging predecessor heaps, we don't know yet if
849  * we're creating a useful phi, and can't fix SaveStates because of that.
850  * Do that here.
851  */
FixupPhisInBlock(BasicBlock * block,Marker phiFixupMrk)852 void Lse::FixupPhisInBlock(BasicBlock *block, Marker phiFixupMrk)
853 {
854     for (auto phiInst : block->PhiInstsSafe()) {
855         auto phi = phiInst->CastToPhi();
856         if (!phi->IsMarked(phiFixupMrk)) {
857             continue;
858         }
859         if (!phi->HasUsers()) {
860             block->RemoveInst(phi);
861         } else if (GetGraph()->IsBytecodeOptimizer() || !phi->IsReferenceOrAny()) {
862             continue;
863         }
864         // Here case: !GetGraph()->IsBytecodeOptimizer() && phi->IsReferenceOrAny()
865         for (auto i = 0U; i < phi->GetInputsCount(); i++) {
866             auto input = phi->GetInput(i);
867             if (input.GetInst()->IsMovableObject()) {
868                 auto bb = phi->GetPhiInputBb(i);
869                 ssb_.SearchAndCreateMissingObjInSaveState(GetGraph(), input.GetInst(), bb->GetLastInst(), nullptr, bb);
870             }
871         }
872     }
873 }
874 
875 /**
876  * Returns the elimination code in two letter format.
877  *
878  * The first letter describes a [L]oad or [S]tore that was eliminated.
879  * The second letter describes the dominant [L]oad or [S]tore that is the
880  * reason why instruction was eliminated.
881  */
GetEliminationCode(Inst * inst,Inst * origin)882 const char *Lse::GetEliminationCode(Inst *inst, Inst *origin)
883 {
884     ASSERT(inst->IsMemory() && (origin->IsMemory() || origin->IsPhi()));
885     if (inst->IsLoad()) {
886         if (origin->IsLoad()) {
887             return "LL";
888         }
889         if (origin->IsStore()) {
890             return "LS";
891         }
892         if (origin->IsPhi()) {
893             return "LP";
894         }
895     }
896     if (inst->IsStore()) {
897         if (origin->IsLoad()) {
898             return "SL";
899         }
900         if (origin->IsStore()) {
901             return "SS";
902         }
903         if (origin->IsPhi()) {
904             return "SP";
905         }
906     }
907     UNREACHABLE();
908 }
909 
910 /**
911  * In the codegen of bytecode optimizer, we don't have corresponding pandasm
912  * for the IR `Cast` of with some pairs of input types and output types. So
913  * in the bytecode optimizer mode, we need to avoid generating such `Cast` IR.
914  * The following function gives the list of legal pairs of types.
915  * This function should not be used in compiler mode.
916  */
917 
IsTypeLegalForCast(DataType::Type output,DataType::Type input)918 static bool IsTypeLegalForCast(DataType::Type output, DataType::Type input)
919 {
920     ASSERT(output != input);
921     switch (input) {
922         case DataType::INT32:
923         case DataType::INT64:
924         case DataType::FLOAT64:
925             switch (output) {
926                 case DataType::FLOAT64:
927                 case DataType::INT64:
928                 case DataType::UINT32:
929                 case DataType::INT32:
930                 case DataType::INT16:
931                 case DataType::UINT16:
932                 case DataType::INT8:
933                 case DataType::UINT8:
934                 case DataType::ANY:
935                     return true;
936                 default:
937                     return false;
938             }
939         case DataType::REFERENCE:
940             return output == DataType::ANY;
941         default:
942             return false;
943     }
944 }
945 
946 /**
947  * Replace inputs of INST with VALUE and delete this INST.  If deletion led to
948  * appearance of instruction that has no users delete this instruction too.
949  */
DeleteInstruction(Inst * inst,Inst * value)950 void Lse::DeleteInstruction(Inst *inst, Inst *value)
951 {
952     // Have to cast a value to the type of eliminated inst. Actually required only for loads.
953     if (inst->GetType() != value->GetType() && inst->HasUsers()) {
954         ASSERT(inst->GetType() != DataType::REFERENCE && value->GetType() != DataType::REFERENCE);
955         // We will do nothing in bytecode optimizer mode when the types are not legal for cast.
956         if (GetGraph()->IsBytecodeOptimizer() && !IsTypeLegalForCast(inst->GetType(), value->GetType())) {
957             COMPILER_LOG(DEBUG, LSE_OPT) << LogInst(inst) << " was not eliminated: requires an inappropriate cast";
958             return;
959         }
960         auto cast = GetGraph()->CreateInstCast(inst->GetType(), inst->GetPc(), value, value->GetType());
961         inst->InsertAfter(cast);
962         value = cast;
963     }
964     inst->ReplaceUsers(value);
965 
966     ArenaQueue<Inst *> queue(GetGraph()->GetLocalAllocator()->Adapter());
967     queue.push(inst);
968     while (!queue.empty()) {
969         Inst *frontInst = queue.front();
970         BasicBlock *block = frontInst->GetBasicBlock();
971         queue.pop();
972 
973         // Have been already deleted or could not be deleted
974         if (block == nullptr || frontInst->HasUsers()) {
975             continue;
976         }
977 
978         for (auto &input : frontInst->GetInputs()) {
979             /* Delete only instructions that has no data flow impact */
980             if (input.GetInst()->HasPseudoDestination()) {
981                 queue.push(input.GetInst());
982             }
983         }
984         block->RemoveInst(frontInst);
985         applied_ = true;
986     }
987 }
988 
DeleteInstructions(const BasicBlockHeap & eliminated)989 void Lse::DeleteInstructions(const BasicBlockHeap &eliminated)
990 {
991     for (auto elim : eliminated) {
992         Inst *inst = elim.first;
993         Inst *origin = elim.second.origin;
994         Inst *value = elim.second.val;
995 
996         ASSERT_DO(eliminated.find(value) == eliminated.end(),
997                   (std::cerr << "Instruction:\n", inst->Dump(&std::cerr),
998                    std::cerr << "is replaced by eliminated value:\n", value->Dump(&std::cerr)));
999 
1000         while (origin->GetBasicBlock() == nullptr) {
1001             auto elimIt = eliminated.find(origin);
1002             ASSERT(elimIt != eliminated.end());
1003             origin = elimIt->second.origin;
1004         }
1005 
1006         GetGraph()->GetEventWriter().EventLse(inst->GetId(), inst->GetPc(), origin->GetId(), origin->GetPc(),
1007                                               GetEliminationCode(inst, origin));
1008         // Try to update savestates
1009         if (!GetGraph()->IsBytecodeOptimizer() && value->IsMovableObject()) {
1010             if (!value->IsPhi() && origin->IsMovableObject() && origin->IsLoad() && origin->IsDominate(inst)) {
1011                 // this branch is not required, but can be faster if origin is closer to inst than value
1012                 ssb_.SearchAndCreateMissingObjInSaveState(GetGraph(), origin, inst);
1013             } else {
1014                 ssb_.SearchAndCreateMissingObjInSaveState(GetGraph(), value, inst);
1015             }
1016         }
1017         DeleteInstruction(inst, value);
1018     }
1019 }
1020 
ApplyHoistToCandidate(Loop * loop,Inst * alive)1021 void Lse::ApplyHoistToCandidate(Loop *loop, Inst *alive)
1022 {
1023     ASSERT(alive->IsLoad());
1024     COMPILER_LOG(DEBUG, LSE_OPT) << " v" << alive->GetId();
1025     if (alive->GetBasicBlock()->GetLoop() != loop) {
1026         COMPILER_LOG(DEBUG, LSE_OPT) << "\tFailed because inst is part of a more inner loop";
1027         return;
1028     }
1029     if (GetGraph()->IsInstThrowable(alive)) {
1030         COMPILER_LOG(DEBUG, LSE_OPT) << "\tFailed because inst is throwable";
1031         return;
1032     }
1033     for (const auto &input : alive->GetInputs()) {
1034         if (!input.GetInst()->GetBasicBlock()->IsDominate(loop->GetPreHeader())) {
1035             COMPILER_LOG(DEBUG, LSE_OPT) << "\tFailed because of def-use chain of inputs: " << LogInst(input.GetInst());
1036             return;
1037         }
1038     }
1039     const auto &rpo = GetGraph()->GetBlocksRPO();
1040     auto blockIter = std::find(rpo.rbegin(), rpo.rend(), alive->GetBasicBlock());
1041     ASSERT(blockIter != rpo.rend());
1042     auto inst = alive->GetPrev();
1043     while (*blockIter != loop->GetPreHeader()) {
1044         while (inst != nullptr) {
1045             if (IsHeapInvalidatingInst(inst) || (inst->IsMemory() && GetEquivClass(inst) == GetEquivClass(alive) &&
1046                                                  GetGraph()->CheckInstAlias(inst, alive) != NO_ALIAS)) {
1047                 COMPILER_LOG(DEBUG, LSE_OPT) << "\tFailed because of invalidating inst:" << LogInst(inst);
1048                 return;
1049             }
1050             inst = inst->GetPrev();
1051         }
1052         blockIter++;
1053         inst = (*blockIter)->GetLastInst();
1054     }
1055     alive->GetBasicBlock()->EraseInst(alive, true);
1056     auto lastInst = loop->GetPreHeader()->GetLastInst();
1057     if (lastInst != nullptr && lastInst->IsControlFlow()) {
1058         loop->GetPreHeader()->InsertBefore(alive, lastInst);
1059     } else {
1060         loop->GetPreHeader()->AppendInst(alive);
1061     }
1062     if (!GetGraph()->IsBytecodeOptimizer() && alive->IsMovableObject()) {
1063         ASSERT(!loop->IsIrreducible());
1064         // loop backedges will be walked inside SSB
1065         ssb_.SearchAndCreateMissingObjInSaveState(GetGraph(), alive, loop->GetHeader()->GetLastInst(), nullptr,
1066                                                   loop->GetHeader());
1067     }
1068     applied_ = true;
1069 }
1070 
TryToHoistLoadFromLoop(Loop * loop,HeapEqClasses * heaps,const BasicBlockHeap * eliminated)1071 void Lse::TryToHoistLoadFromLoop(Loop *loop, HeapEqClasses *heaps, const BasicBlockHeap *eliminated)
1072 {
1073     for (auto innerLoop : loop->GetInnerLoops()) {
1074         TryToHoistLoadFromLoop(innerLoop, heaps, eliminated);
1075     }
1076 
1077     if (loop->IsIrreducible() || loop->IsOsrLoop()) {
1078         return;
1079     }
1080 
1081     auto &backBbs = loop->GetBackEdges();
1082     beAlive_.clear();
1083 
1084     // Initiate alive set
1085     auto backBb = backBbs.begin();
1086     ASSERT(backBb != backBbs.end());
1087     for (int eqClass = Lse::EquivClass::EQ_ARRAY; eqClass != Lse::EquivClass::EQ_LAST; eqClass++) {
1088         for (const auto &entry : (*heaps)[eqClass].first.at(*backBb)) {
1089             // Do not touch Stores and eliminated ones
1090             if (entry.first->IsLoad() && eliminated->find(entry.first) == eliminated->end()) {
1091                 beAlive_.insert(entry.first);
1092             }
1093         }
1094     }
1095 
1096     // Throw values not alive on other backedges
1097     while (++backBb != backBbs.end()) {
1098         auto alive = beAlive_.begin();
1099         while (alive != beAlive_.end()) {
1100             auto &heap = heaps->at(GetEquivClass(*alive)).first;
1101             if (heap.at(*backBb).find(*alive) == heap.at(*backBb).end()) {
1102                 alive = beAlive_.erase(alive);
1103             } else {
1104                 alive++;
1105             }
1106         }
1107     }
1108     COMPILER_LOG(DEBUG, LSE_OPT) << "Loop #" << loop->GetId() << " has the following motion candidates:";
1109     for (auto alive : beAlive_) {
1110         ApplyHoistToCandidate(loop, alive);
1111     }
1112 }
1113 
ProcessAllBBs(LseVisitor & visitor,HeapEqClasses * heaps,Marker phiFixupMrk)1114 void Lse::ProcessAllBBs(LseVisitor &visitor, HeapEqClasses *heaps, Marker phiFixupMrk)
1115 {
1116     InstVector invs(GetGraph()->GetLocalAllocator()->Adapter());
1117     size_t aliasCalls = 0;
1118     for (auto block : GetGraph()->GetBlocksRPO()) {
1119         COMPILER_LOG(DEBUG, LSE_OPT) << "Processing BB " << block->GetId();
1120         InitializeHeap(block, heaps);
1121 
1122         if (block->IsLoopHeader()) {
1123             MergeHeapValuesForLoop(block, heaps);
1124         } else {
1125             aliasCalls += MergeHeapValuesForBlock(block, heaps, phiFixupMrk);
1126         }
1127 
1128         for (auto inst : block->Insts()) {
1129             if (IsHeapReadingInst(inst)) {
1130                 visitor.SetHeapAsRead(block);
1131             }
1132             if (IsHeapInvalidatingInst(inst)) {
1133                 COMPILER_LOG(DEBUG, LSE_OPT) << LogInst(inst) << " invalidates heap";
1134                 visitor.InvalidateHeap(block);
1135             } else if (inst->IsLoad()) {
1136                 visitor.VisitLoad(inst);
1137             } else if (inst->IsStore()) {
1138                 visitor.VisitStore(inst, InstStoredValue(inst));
1139             }
1140             if (inst->IsIntrinsic()) {
1141                 visitor.VisitIntrinsic(inst, &invs);
1142             }
1143             // If we call Alias Analysis too much, we assume that this block has too many
1144             // instructions and we should bail in favor of performance.
1145             if (visitor.GetAliasAnalysisCallCount() + aliasCalls > Lse::AA_CALLS_LIMIT) {
1146                 COMPILER_LOG(DEBUG, LSE_OPT) << "Exiting BB " << block->GetId() << ": too many Alias Analysis calls";
1147                 visitor.InvalidateHeap(block);
1148                 break;
1149             }
1150         }
1151         visitor.ClearLocalValuesFromHeap(block);
1152         visitor.ResetLimits();
1153     }
1154 }
1155 
RunImpl()1156 bool Lse::RunImpl()
1157 {
1158     if (GetGraph()->IsBytecodeOptimizer() && GetGraph()->IsDynamicMethod()) {
1159         COMPILER_LOG(DEBUG, LSE_OPT) << "Load-Store Elimination skipped: es bytecode optimizer";
1160         return false;
1161     }
1162 
1163     HeapEqClasses heaps(GetGraph()->GetLocalAllocator()->Adapter());
1164     for (int eqClass = Lse::EquivClass::EQ_ARRAY; eqClass != Lse::EquivClass::EQ_LAST; eqClass++) {
1165         std::pair<Heap, PhiCands> heapPhi(GetGraph()->GetLocalAllocator()->Adapter(),
1166                                           GetGraph()->GetLocalAllocator()->Adapter());
1167         heaps.emplace_back(heapPhi);
1168     }
1169 
1170     GetGraph()->RunPass<LoopAnalyzer>();
1171     GetGraph()->RunPass<AliasAnalysis>();
1172 
1173     LseVisitor visitor(GetGraph(), &heaps);
1174     auto markerHolder = MarkerHolder(GetGraph());
1175     auto phiFixupMrk = markerHolder.GetMarker();
1176 
1177     ProcessAllBBs(visitor, &heaps, phiFixupMrk);
1178 
1179     visitor.FinalizeShadowedStores();
1180     visitor.FinalizeLoops(GetGraph(), rpoLoops_);
1181 
1182     auto &eliminated = visitor.GetEliminations();
1183     GetGraph()->RunPass<DominatorsTree>();
1184     if (hoistLoads_) {
1185         for (auto loop : GetGraph()->GetRootLoop()->GetInnerLoops()) {
1186             TryToHoistLoadFromLoop(loop, &heaps, &eliminated);
1187         }
1188     }
1189 
1190     DeleteInstructions(visitor.GetEliminations());
1191 
1192     for (auto block : GetGraph()->GetBlocksRPO()) {
1193         FixupPhisInBlock(block, phiFixupMrk);
1194     }
1195 
1196     COMPILER_LOG(DEBUG, LSE_OPT) << "Load-Store Elimination complete";
1197     return applied_;
1198 }
1199 }  // namespace ark::compiler
1200