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