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