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