• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "optimizer/ir/analysis.h"
17 #include "optimizer/ir/basicblock.h"
18 #include "optimizer/ir/graph.h"
19 #include "optimizer/analysis/alias_analysis.h"
20 #include "compiler_logger.h"
21 
22 /**
23  * See  "Efficient Field-sensitive pointer analysis for C" by "David J. Pearce
24  * and Paul H. J. Kelly and Chris Hankin
25  *
26  * We treat each IR Inst as a constraint that may be applied to a set of
27  * aliases of some virtual register.  Virtual registers are used as constraint
28  * variables as well.
29  *
30  * In order to solve the system of set constraints, the following is done:
31  *
32  * 1. Each constraint variable x has a solution set associated with it, Sol(x).
33  * Implemented through AliasAnalysis::points_to_ that contains mapping of
34  * virtual register to possible aliases.
35  *
36  * 2. Constraints are separated into direct, copy.
37  *
38  * - Direct constraints are constraints that require no extra processing, such
39  * as P = &Q.
40  *
41  * - Copy constraints are those of the form P = Q.  Such semantic can be
42  * obtained through NullCheck, Mov, and Phi instructions.
43  *
44  * 3. All direct constraints of the form P = &Q are processed, such that Q is
45  * added to Sol(P).
46  *
47  * 4. A directed graph is built out of the copy constraints.  Each constraint
48  * variable is a node in the graph, and an edge from Q to P is added for each
49  * copy constraint of the form P = Q.
50  *
51  * 5. The graph is then walked, and solution sets are propagated along the copy
52  * edges, such that an edge from Q to P causes Sol(P) <- Sol(P) union Sol(Q).
53  *
54  * 6. The process of walking the graph is iterated until no solution sets
55  * change.
56  *
57  * To add new instructions to alias analysis please consider following:
58  *     - IsLocalAlias method: to add instructions that create new object
59  *     - ParseInstruction method: to add a new instruction alias analysis works for
60  *     - AliasAnalysis class: to add a visitor for a new instruction that should be analyzed
61  *
62  * NOTE(Evgenii Kudriashov): Prior to walking the graph in steps 5 and 6, We
63  * need to perform static cycle elimination on the constraint graph, as well as
64  * off-line variable substitution.
65  *
66  * NOTE(Evgenii Kudriashov): To add flow-sensitivity the "Flow-sensitive
67  * pointer analysis for millions of lines of code" by Ben Hardekopf and Calvin
68  * Lin may be considered.
69  *
70  * NOTE(Evgenii Kudriashov): After implementing VRP and SCEV the "Loop-Oriented
71  * Array- and Field-Sensitive Pointer Analysis for Automatic SIMD
72  * Vectorization" by Yulei Sui, Xiaokang Fan, Hao Zhou, and Jingling Xue may be
73  * considered to add advanced analysis of array indices.
74  */
75 
76 namespace ark::compiler {
77 
IsLocalCreated() const78 bool PointerWithInfo::IsLocalCreated() const
79 {
80     for (const auto &pointer : PointsTo()) {
81         if (!(pointer.IsObject() && pointer.IsLocalCreatedAlias())) {
82             return false;
83         }
84     }
85     return true;
86 }
87 
AliasAnalysis(Graph * graph)88 AliasAnalysis::AliasAnalysis(Graph *graph) : Analysis(graph), pointerInfo_(graph->GetAllocator()->Adapter()) {}
89 
GetBlocksToVisit() const90 const ArenaVector<BasicBlock *> &AliasAnalysis::GetBlocksToVisit() const
91 {
92     return GetGraph()->GetBlocksRPO();
93 }
94 
TypeToStr(AliasType t)95 [[maybe_unused]] static std::string TypeToStr(AliasType t)
96 {
97     switch (t) {
98         case MAY_ALIAS:
99             return "MAY_ALIAS";
100         case NO_ALIAS:
101             return "NO_ALIAS";
102         case MUST_ALIAS:
103             return "MUST_ALIAS";
104         case ALIAS_IF_BASE_EQUALS:
105             return "ALIAS_IF_BASE_EQUALS";
106         default:
107             UNREACHABLE();
108     }
109 }
110 
RunImpl()111 bool AliasAnalysis::RunImpl()
112 {
113     Init();
114 
115     VisitGraph();
116 
117     for (const auto &[from, _] : *direct_) {
118         CreatePointerInfo(from);
119     }
120     for (const auto &[from, to] : *copy_) {
121         CreatePointerInfo(from);
122         CreatePointerInfo(to);
123     }
124 
125     // Initialize solution sets
126     for (auto pair : *direct_) {
127         ASSERT(pair.first.GetBase() == nullptr || pair.first.GetBase()->GetOpcode() != Opcode::NullCheck);
128         ASSERT(pair.second.GetBase() == nullptr || pair.second.GetBase()->GetOpcode() != Opcode::NullCheck);
129         GetPointerInfo(pair.first).Insert(pair.second);
130     }
131 
132     // Build graphs
133     for (auto pair : *copy_) {
134         auto it = chains_->try_emplace(pair.first, GetGraph()->GetLocalAllocator()->Adapter());
135         ASSERT(pair.first.GetBase() == nullptr || pair.first.GetBase()->GetOpcode() != Opcode::NullCheck);
136         ASSERT(pair.second.GetBase() == nullptr || pair.second.GetBase()->GetOpcode() != Opcode::NullCheck);
137         it.first->second.push_back(pair.second);
138     }
139 
140     FindEscapingPointers();
141     SolveConstraints();
142 
143 #ifndef NDEBUG
144     if (CompilerLogger::IsComponentEnabled(CompilerLoggerComponents::ALIAS_ANALYSIS)) {
145         std::ostringstream out;
146         DumpCopy(&out);
147         DumpDirect(&out);
148         DumpChains(&out);
149         Dump(&out);
150         COMPILER_LOG(DEBUG, ALIAS_ANALYSIS) << out.str();
151     }
152 #endif
153     return true;
154 }
155 
Init()156 void AliasAnalysis::Init()
157 {
158     auto allocator = GetGraph()->GetLocalAllocator();
159     chains_ = allocator->New<Pointer::Map<ArenaVector<Pointer>>>(allocator->Adapter());
160     reverseChains_ = allocator->New<Pointer::Map<ArenaVector<Pointer>>>(allocator->Adapter());
161     direct_ = allocator->New<PointerPairVector>(allocator->Adapter());
162     copy_ = allocator->New<PointerPairVector>(allocator->Adapter());
163     ASSERT(chains_ != nullptr);
164     ASSERT(reverseChains_ != nullptr);
165     ASSERT(direct_ != nullptr);
166     ASSERT(copy_ != nullptr);
167     AliasVisitor::Init(allocator);
168     pointerInfo_.clear();
169 }
170 
PointerLess(const Pointer & lhs,const Pointer & rhs)171 static bool PointerLess(const Pointer &lhs, const Pointer &rhs)
172 {
173     if (lhs.GetBase() == rhs.GetBase()) {
174         return lhs.GetImm() < rhs.GetImm();
175     }
176     if (lhs.GetBase() == nullptr) {
177         return true;
178     }
179     if (rhs.GetBase() == nullptr) {
180         return false;
181     }
182     return lhs.GetBase()->GetId() < rhs.GetBase()->GetId();
183 }
184 
DumpChains(std::ostream * out) const185 void AliasAnalysis::DumpChains(std::ostream *out) const
186 {
187     ArenaVector<Pointer> sortedKeys(GetGraph()->GetLocalAllocator()->Adapter());
188     for (auto &pair : *chains_) {
189         sortedKeys.push_back(pair.first);
190     }
191     if (sortedKeys.empty()) {
192         (*out) << "\nThe chains is empty" << std::endl;
193         return;
194     }
195     std::sort(sortedKeys.begin(), sortedKeys.end(), PointerLess);
196 
197     (*out) << "\nThe chains are the following: {" << std::endl;
198     for (auto &p : sortedKeys) {
199         (*out) << "\t";
200         p.Dump(out);
201         (*out) << ": {";
202 
203         // Sort by instruction ID to add more readability to logs
204         ArenaVector<Pointer> sorted(chains_->at(p), GetGraph()->GetLocalAllocator()->Adapter());
205         std::sort(sorted.begin(), sorted.end(), PointerLess);
206         auto edge = sorted.begin();
207         if (edge != sorted.end()) {
208             edge->Dump(out);
209             while (++edge != sorted.end()) {
210                 (*out) << ", ";
211                 edge->Dump(out);
212             }
213         }
214         (*out) << "}" << std::endl;
215     }
216     (*out) << "}" << std::endl;
217 }
218 
DumpDirect(std::ostream * out) const219 void AliasAnalysis::DumpDirect(std::ostream *out) const
220 {
221     (*out) << "\nThe direct are the following:" << std::endl;
222     for (auto &pair : *direct_) {
223         (*out) << "{ ";
224         pair.first.Dump(out);
225         (*out) << " , ";
226         pair.second.Dump(out);
227         (*out) << " }" << std::endl;
228     }
229 }
230 
DumpCopy(std::ostream * out) const231 void AliasAnalysis::DumpCopy(std::ostream *out) const
232 {
233     (*out) << "\nThe copy are the following:" << std::endl;
234     for (auto &pair : *copy_) {
235         (*out) << "{ ";
236         pair.first.Dump(out);
237         (*out) << " , ";
238         pair.second.Dump(out);
239         (*out) << " }" << std::endl;
240     }
241 }
242 
Dump(std::ostream * out) const243 void AliasAnalysis::Dump(std::ostream *out) const
244 {
245     ArenaVector<Pointer> sortedKeys(GetGraph()->GetLocalAllocator()->Adapter());
246     for (const auto &pair : pointerInfo_) {
247         sortedKeys.push_back(pair.first);
248     }
249     if (sortedKeys.empty()) {
250         (*out) << "\nThe solution set is empty" << std::endl;
251         return;
252     }
253     std::sort(sortedKeys.begin(), sortedKeys.end(), PointerLess);
254 
255     (*out) << "\nThe solution set is the following:" << std::endl;
256     for (auto &p : sortedKeys) {
257         (*out) << "\t";
258         p.Dump(out);
259         const auto &info = GetPointerInfo(p);
260         if (info.IsLocal()) {
261             (*out) << "(local)";
262         }
263         if (info.IsVolatile()) {
264             (*out) << "(v)";
265         }
266         (*out) << ": {";
267 
268         // Sort by instruction ID to add more readability to logs
269         auto values = info.PointsTo();
270         ArenaVector<Pointer> sorted(values.begin(), values.end(), GetGraph()->GetLocalAllocator()->Adapter());
271         std::sort(sorted.begin(), sorted.end(), PointerLess);
272         auto iter = sorted.begin();
273         if (iter != sorted.end()) {
274             iter->Dump(out);
275             while (++iter != sorted.end()) {
276                 (*out) << ", ";
277                 iter->Dump(out);
278             }
279         }
280         (*out) << "}" << std::endl;
281     }
282 }
283 
284 template <bool REFINED>
CheckInstAlias(Inst * mem1,Inst * mem2) const285 AliasType AliasAnalysis::CheckInstAlias(Inst *mem1, Inst *mem2) const
286 {
287     ASSERT(mem1->IsMemory() && mem2->IsMemory());
288     Pointer p1 = {};
289     Pointer p2 = {};
290 
291     if (!ParseInstruction(mem1, &p1) || !ParseInstruction(mem2, &p2)) {
292         return MAY_ALIAS;
293     }
294 
295     // Instructions with different types cannot alias each other. Handle
296     // difference only in numeric types for now.
297     if (IsTypeNumeric(mem1->GetType()) && IsTypeNumeric(mem2->GetType()) &&
298         GetTypeSize(mem1->GetType(), GetGraph()->GetArch()) != GetTypeSize(mem2->GetType(), GetGraph()->GetArch())) {
299         return NO_ALIAS;
300     }
301     // Instructions with a primitive type and the reference type cannot alias each other.
302     if ((IsTypeNumeric(mem1->GetType()) && mem2->IsReferenceOrAny()) ||
303         (IsTypeNumeric(mem2->GetType()) && mem1->IsReferenceOrAny())) {
304         return NO_ALIAS;
305     }
306 
307     return CheckMemAddress<REFINED>(p1, p2, IsVolatileMemInst(mem1) || IsVolatileMemInst(mem2));
308 }
309 
310 template AliasType AliasAnalysis::CheckInstAlias<false>(Inst *, Inst *) const;
311 template AliasType AliasAnalysis::CheckInstAlias<true>(Inst *, Inst *) const;
312 
CheckRefAlias(Inst * ref1,Inst * ref2) const313 AliasType AliasAnalysis::CheckRefAlias(Inst *ref1, Inst *ref2) const
314 {
315     ASSERT(ref1->IsReferenceOrAny());
316     ASSERT(ref2->IsReferenceOrAny());
317     return CheckMemAddress<false>(Pointer::CreateObject(ref1), Pointer::CreateObject(ref2), false);
318 }
319 
CheckMemAddressEmptyIntersectionCase(const PointerWithInfo & base1,const PointerWithInfo & base2,const Pointer & p1,const Pointer & p2) const320 AliasType AliasAnalysis::CheckMemAddressEmptyIntersectionCase(const PointerWithInfo &base1,
321                                                               const PointerWithInfo &base2, const Pointer &p1,
322                                                               const Pointer &p2) const
323 {
324     // If at least one set of aliases consists of only local aliases then there is NO_ALIAS
325     if (base1.IsLocal() || base2.IsLocal()) {
326         return NO_ALIAS;
327     }
328     // If BOTH sets of aliases consists of only local created aliases then there is NO_ALIAS
329     if (base1.IsLocalCreated() && base2.IsLocalCreated()) {
330         return NO_ALIAS;
331     }
332     // Different fields cannot alias each other even if they are not created locally
333     if (p1.GetType() == OBJECT_FIELD && !p1.HasSameOffset(p2)) {
334         return NO_ALIAS;
335     }
336     if (p1.GetType() == ARRAY_ELEMENT) {
337         auto equal = IsSameOffsets(p1.GetIdx(), p2.GetIdx());
338         // If it is known that indices are different OR Imm indices are different then there is
339         // no alias.  If they are both different we can't certainly say so.
340         if ((equal == Trilean::FALSE && p1.GetImm() == p2.GetImm()) ||
341             (equal == Trilean::TRUE && p1.GetImm() != p2.GetImm())) {
342             return NO_ALIAS;
343         }
344     }
345     if (p1.GetType() == DICTIONARY_ELEMENT) {
346         auto equal = IsSameOffsets(p1.GetIdx(), p2.GetIdx());
347         if (equal == Trilean::FALSE && p1.GetImm() == p2.GetImm()) {
348             return NO_ALIAS;
349         }
350     }
351     return MAY_ALIAS;
352 }
353 
354 /**
355  * We have 5 types of pointers: OBJECT, OBJECT_FIELD, POOL_CONSTANT,
356  * STATIC_FIELD and ARRAY_ELEMENT.  They correspond to groups of memory storing
357  * and loading instructions.  Assuming that these groups cannot load the same
358  * memory address (at IR level), it is true that different types of pointers
359  * cannot alias each other.
360  *
361  * Global pointers such as STATIC_FIELD and POOL_CONSTANT are checked through
362  * their unique TypeId.
363  *
364  * For OBJECT_FIELDs we look at objects they referenced to.  If they refer to
365  * the same objects then depending on TypeId they MUST_ALIAS or NO_ALIAS.  If
366  * the situation of the referenced objects is unclear then they MAY_ALIAS.
367  *
368  * The same is for ARRAY_ELEMENT.  Instead of TypeId we look at index and compare them.
369  *
370  * All function's arguments MAY_ALIAS each other.  Created objects in the
371  * function may not alias arguments.
372  */
373 template <bool REFINED>
CheckMemAddress(const Pointer & p1,const Pointer & p2,bool isVolatile) const374 AliasType AliasAnalysis::CheckMemAddress(const Pointer &p1, const Pointer &p2, bool isVolatile) const
375 {
376     ASSERT(p1.GetType() != UNKNOWN_OFFSET && p2.GetType() != UNKNOWN_OFFSET);
377     if (p1.GetType() != p2.GetType()) {
378         return NO_ALIAS;
379     }
380 
381     if (p1.GetType() == STATIC_FIELD || p2.GetType() == STATIC_FIELD || p1.GetType() == POOL_CONSTANT ||
382         p2.GetType() == POOL_CONSTANT) {
383         if (p1.HasSameOffset(p2)) {
384             return MUST_ALIAS;
385         }
386         return NO_ALIAS;
387     }
388     ASSERT(p1.GetBase() != nullptr && p2.GetBase() != nullptr);
389 
390     if (auto base = p1.GetBase(); base == p2.GetBase()) {
391         return SingleIntersectionAliasing(p1, p2, nullptr, isVolatile);
392     }
393 
394     auto baseObj1 = Pointer::CreateObject(p1.GetBase());
395     auto baseObj2 = Pointer::CreateObject(p2.GetBase());
396     ASSERT_DO(pointerInfo_.find(baseObj1) != pointerInfo_.end(),
397               (std::cerr << "Undefined inst in AliasAnalysis: ", p1.GetBase()->Dump(&std::cerr)));
398     ASSERT_DO(pointerInfo_.find(baseObj2) != pointerInfo_.end(),
399               (std::cerr << "Undefined inst in AliasAnalysis: ", p2.GetBase()->Dump(&std::cerr)));
400     const auto &base1 = GetPointerInfo(baseObj1);
401     const auto &base2 = GetPointerInfo(baseObj2);
402     const auto &aliases1 = base1.PointsTo();
403     const auto &aliases2 = base2.PointsTo();
404     // Find the intersection
405     const Pointer *intersection = nullptr;
406     for (auto &alias : aliases1) {
407         if (aliases2.find(alias) != aliases2.end()) {
408             intersection = &alias;
409             break;
410         }
411     }
412 
413     // The only common intersection
414     if (intersection != nullptr && aliases1.size() == 1 && aliases2.size() == 1) {
415         return SingleIntersectionAliasing<REFINED>(p1, p2, &base1, isVolatile);
416     }
417     // Empty intersection: check that both addresses are not parameters
418     if (intersection == nullptr) {
419         return CheckMemAddressEmptyIntersectionCase(base1, base2, p1, p2);
420     }
421     return MAY_ALIAS;
422 }
423 
CombineIdxAndImm(const Pointer * p)424 static uint64_t CombineIdxAndImm(const Pointer *p)
425 {
426     uint64_t sum = 0;
427     if (p->GetIdx() != nullptr) {
428         auto idx = p->GetIdx();
429         ASSERT(idx->IsConst());
430         ASSERT(!DataType::IsFloatType(idx->GetType()));
431         sum = idx->CastToConstant()->GetRawValue();
432     }
433     sum += p->GetImm();
434     return sum;
435 }
436 
AliasingTwoArrayPointers(const Pointer * p1,const Pointer * p2)437 AliasType AliasAnalysis::AliasingTwoArrayPointers(const Pointer *p1, const Pointer *p2)
438 {
439     // Structure of Pointer: base, idx, imm
440     // Base may be same or not. We should compare fields in order: idx, combine {idx + imm}.
441     // Is necessary compare sum, because LoadArrayI (..., nullptr, 2) and StoreArray(..., Const{2}, 0) will alias,
442     // but in separate idx and imm are different.
443 
444     // 1) Compare idx. If they same, compare Imm part -> ALIAS_IF_BASE_EQUALS, NO_ALIAS
445     if (AliasAnalysis::IsSameOffsets(p1->GetIdx(), p2->GetIdx()) == Trilean::TRUE) {
446         return p1->GetImm() == p2->GetImm() ? ALIAS_IF_BASE_EQUALS : NO_ALIAS;
447     }
448     // 2) If still one of indexes is not constant or nullptr -> MAY_ALIAS
449     for (auto *pointer : {p1, p2}) {
450         if (pointer->GetIdx() != nullptr && !pointer->GetIdx()->IsConst()) {
451             return MAY_ALIAS;
452         }
453     }
454     // 3) Combine idx(is constant) and imm for compare
455     if (CombineIdxAndImm(p1) == CombineIdxAndImm(p2)) {
456         return ALIAS_IF_BASE_EQUALS;
457     }
458     return NO_ALIAS;
459 }
460 
461 /// Checks aliasing if P1 and P2 point to the one single object.
462 /* static */
463 template <bool REFINED>
SingleIntersectionAliasing(const Pointer & p1,const Pointer & p2,const PointerWithInfo * commonBase,bool isVolatile) const464 AliasType AliasAnalysis::SingleIntersectionAliasing(const Pointer &p1, const Pointer &p2,
465                                                     const PointerWithInfo *commonBase, bool isVolatile) const
466 {
467     // check that PointerInfo for intersection exists
468     ASSERT(commonBase == nullptr || commonBase->PointsTo().size() == 1U);
469     ASSERT(p1.GetType() == p2.GetType());
470     switch (p1.GetType()) {
471         case ARRAY_ELEMENT: {
472             if (auto type = AliasingTwoArrayPointers(&p1, &p2); type != ALIAS_IF_BASE_EQUALS) {
473                 return type;
474             }
475             break;
476         }
477         case DICTIONARY_ELEMENT:
478             // It is dinamic case, there are less guarantees
479             if (IsSameOffsets(p1.GetIdx(), p2.GetIdx()) != Trilean::TRUE) {
480                 return MAY_ALIAS;
481             }
482             if (p1.GetImm() != p2.GetImm()) {
483                 return MAY_ALIAS;
484             }
485             break;
486         case OBJECT_FIELD:
487             if (!p1.HasSameOffset(p2)) {
488                 return NO_ALIAS;
489             }
490             break;
491         case OBJECT:
492             break;
493         default:
494             UNREACHABLE();
495     }
496     if (commonBase != nullptr && commonBase->IsLocal()) {
497         // if base is local, volatility does not matter
498         return MUST_ALIAS;
499     }
500     if (isVolatile) {
501         return MAY_ALIAS;
502     }
503     if (commonBase == nullptr) {
504         // p1 and p2 have the same base instruction
505         return MUST_ALIAS;
506     }
507     ASSERT(commonBase->GetType() == OBJECT);
508     if (commonBase->IsVolatile()) {
509         // base of p1 and base of p2 can be different if they are loaded from some object
510         // at different moments
511         return MAY_ALIAS;
512     }
513 
514     auto basePointsTo = *commonBase->PointsTo().begin();
515     if (basePointsTo.GetType() == OBJECT || basePointsTo.GetType() == POOL_CONSTANT) {
516         // p1 and p2 have the same base instruction
517         return MUST_ALIAS;
518     }
519     if (REFINED) {
520         return ALIAS_IF_BASE_EQUALS;
521     }
522     return MAY_ALIAS;
523 }
524 
SolveConstraintsMainLoop(const PointerWithInfo * ref,PointerWithInfo * edge,bool & added)525 void AliasAnalysis::SolveConstraintsMainLoop(const PointerWithInfo *ref, PointerWithInfo *edge, bool &added)
526 {
527     added |= edge->UpdateLocal(ref->IsLocal());
528     if (ref->IsVolatile() && !edge->IsVolatile()) {
529         added = true;
530         edge->SetVolatile(true);
531     }
532     if (ref->GetBase() != edge->GetBase() || edge->GetType() == STATIC_FIELD || edge->GetType() == OBJECT) {
533         for (const auto &alias : ref->PointsTo()) {
534             ASSERT(alias.GetBase() == nullptr || alias.GetBase()->GetOpcode() != Opcode::NullCheck);
535             added |= edge->Insert(alias);
536         }
537         return;
538     }
539 
540     ASSERT(ref->GetBase() == edge->GetBase());
541     ASSERT(edge->GetType() == OBJECT_FIELD || edge->GetType() == ARRAY_ELEMENT ||
542            edge->GetType() == DICTIONARY_ELEMENT || edge->GetType() == RAW_OFFSET || edge->GetType() == UNKNOWN_OFFSET);
543 
544     bool onlyObjects = true;
545     for (auto alias : ref->PointsTo()) {
546         ASSERT(alias.GetBase() == nullptr || alias.GetBase()->GetOpcode() != Opcode::NullCheck);
547         if (alias.GetType() != OBJECT) {
548             onlyObjects = false;
549             continue;
550         }
551 
552         // NOTE: do we need it if onlyObjects is false?
553         Pointer p {};
554         if (edge->GetType() == OBJECT_FIELD) {
555             // Propagating from object to fields: A{a} -> A.F{a.f}
556             p = Pointer::CreateObjectField(alias.GetBase(), edge->GetImm(), edge->GetTypePtr());
557         } else if (edge->GetType() == ARRAY_ELEMENT) {
558             // Propagating from object to elements: A{a} -> A[i]{a[i]}
559             p = Pointer::CreateArrayElement(alias.GetBase(), edge->GetIdx(), edge->GetImm());
560         } else if (edge->GetType() == RAW_OFFSET) {
561             // Propagating from object to elements: A{a} -> A[i]{a[i]}
562             p = Pointer::CreateRawOffset(alias.GetBase(), edge->GetIdx(), edge->GetImm());
563         } else if (edge->GetType() == DICTIONARY_ELEMENT) {
564             // Propagating from object to elements: A{a} -> A[i]{a[i]}
565             p = Pointer::CreateDictionaryElement(alias.GetBase(), edge->GetIdx());
566         } else if (edge->GetType() == UNKNOWN_OFFSET) {
567             p = Pointer::CreateUnknownOffset(alias.GetBase());
568         } else {
569             UNREACHABLE();
570         }
571         added |= edge->Insert(p);
572     }
573 
574     if (!onlyObjects) {
575         // In case A{a[j]} -> A[i] we propagate symbolic name: A{a[j]} -> A[i]{A[i]}
576         added |= edge->Insert(edge->GetPointer());
577     }
578 }
579 
CreatePointerInfo(const Pointer & pointer,bool isVolatile)580 void AliasAnalysis::CreatePointerInfo(const Pointer &pointer, bool isVolatile)
581 {
582     auto it = pointerInfo_.find(pointer);
583     if (it == pointerInfo_.end()) {
584         pointerInfo_.emplace(pointer, PointerInfo {Pointer::Set {GetGraph()->GetAllocator()->Adapter()},
585                                                    pointer.IsNotEscapingAlias(), isVolatile});
586     } else if (isVolatile) {
587         it->second.isVolatile = true;
588     }
589 }
590 
GetPointerInfo(const Pointer & pointer) const591 const PointerWithInfo &AliasAnalysis::GetPointerInfo(const Pointer &pointer) const
592 {
593     auto it = pointerInfo_.find(pointer);
594     ASSERT_DO(it != pointerInfo_.end(),
595               (std::cerr << "No info for pointer: ", pointer.Dump(&std::cerr), GetGraph()->Dump(&std::cerr)));
596     return PointerWithInfo::FromPair(*it);
597 }
598 
GetPointerInfo(const Pointer & pointer)599 PointerWithInfo &AliasAnalysis::GetPointerInfo(const Pointer &pointer)
600 {
601     return const_cast<PointerWithInfo &>(static_cast<const AliasAnalysis *>(this)->GetPointerInfo(pointer));
602 }
603 
SetVolatile(const Pointer & pointer,Inst * memInst)604 void AliasAnalysis::SetVolatile(const Pointer &pointer, Inst *memInst)
605 {
606     ASSERT(memInst->IsMemory());
607     if (IsVolatileMemInst(memInst)) {
608         CreatePointerInfo(pointer, true);
609         ASSERT(GetPointerInfo(pointer).IsVolatile());
610     }
611 }
612 
AddConstantDirectEdge(Inst * inst,uint32_t id)613 void AliasAnalysis::AddConstantDirectEdge(Inst *inst, uint32_t id)
614 {
615     direct_->push_back({Pointer::CreateObject(inst), Pointer::CreatePoolConstant(id)});
616 }
617 
FindEscapingPointers()618 void AliasAnalysis::FindEscapingPointers()
619 {
620     ArenaVector<const PointerWithInfo *> worklist(GetGraph()->GetLocalAllocator()->Adapter());
621     // propagate "escaping" property backward by copy-edges
622     for (const auto &pair : pointerInfo_) {
623         const auto *info = &PointerWithInfo::FromPair(pair);
624         if (info->IsLocal()) {
625             continue;
626         }
627         worklist.push_back(info);
628         while (!worklist.empty()) {
629             auto to = worklist.back();
630             worklist.pop_back();
631             if (auto it = reverseChains_->find(to->GetPointer()); it != reverseChains_->end()) {
632                 PropagateEscaped(it->second, worklist);
633             }
634         }
635     }
636     // pointer is local alias if it is created locally and does not escape
637     for (auto &pair : pointerInfo_) {
638         auto &info = PointerWithInfo::FromPair(pair);
639         info.UpdateLocal(info.GetPointer().IsLocalCreatedAlias());
640     }
641 }
642 
AddEscapeEdge(const Pointer & from,const Pointer & to)643 void AliasAnalysis::AddEscapeEdge(const Pointer &from, const Pointer &to)
644 {
645     auto it = reverseChains_->try_emplace(from, GetGraph()->GetLocalAllocator()->Adapter());
646     it.first->second.push_back(to);
647 }
648 
PropagateEscaped(const ArenaVector<Pointer> & escaping,ArenaVector<const PointerWithInfo * > & worklist)649 void AliasAnalysis::PropagateEscaped(const ArenaVector<Pointer> &escaping,
650                                      ArenaVector<const PointerWithInfo *> &worklist)
651 {
652     for (const auto &from : escaping) {
653         auto &fromInfo = GetPointerInfo(from);
654         if (fromInfo.UpdateLocal(false)) {
655             worklist.push_back(&fromInfo);
656         }
657     }
658 }
659 
660 /**
661  * Here we propagate solutions obtained from direct constraints through copy
662  * constraints e.g: we have a node A with solution {a} and the node A was
663  * copied to B and C (this->chains_ maintains these links), and C was copied to
664  * D.
665  *
666  *    A{a} -> B
667  *        \-> C -> D
668  *
669  * After first iteration (iterating A node) we will obtain
670  *
671  *     A{a} -> B{a}
672  *         \-> C{a} -> D
673  *
674  * After second iteration (iterating B node) nothing changes
675  *
676  * After third iteration (iterating C node):
677  *
678  * A{a} -> B{a}
679  *     \-> C{a} -> D{a}
680  *
681  * For complex nodes (OBJECT_FIELD, ARRAY_ELEMENT) we create auxiliary nodes e.g.
682  * if a field F was accessed from object A then we have two nodes:
683  *
684  * A{a} -> A.F
685  *
686  * And solutions from A would be propagated as following:
687  *
688  * A{a} -> A.F{a.F}
689  *
690  * The function works using worklist to process only updated nodes.
691  */
SolveConstraints()692 void AliasAnalysis::SolveConstraints()
693 {
694     ArenaQueue<PointerWithInfo *> worklist(GetGraph()->GetLocalAllocator()->Adapter());
695     for (auto &pair : *direct_) {
696         if (chains_->find(pair.first) != chains_->end()) {
697             worklist.push(&GetPointerInfo(pair.first));
698         }
699     }
700 
701     while (!worklist.empty()) {
702         const PointerWithInfo *ref = worklist.front();
703         ASSERT(ref->GetBase() == nullptr || ref->GetBase()->GetOpcode() != Opcode::NullCheck);
704         for (const auto &edgePointer : chains_->at(ref->GetPointer())) {
705             auto *edge = &GetPointerInfo(edgePointer);
706             // POOL_CONSTANT cannot be assignee
707             ASSERT(edge->GetType() != POOL_CONSTANT);
708             bool added = false;
709 
710             SolveConstraintsMainLoop(ref, edge, added);
711 
712             if (added && chains_->find(edgePointer) != chains_->end()) {
713                 worklist.push(edge);
714             }
715             ASSERT(!edge->PointsTo().empty());
716         }
717         worklist.pop();
718     }
719 }
720 
721 /**
722  * Compares offsets. Since we have a situation when we cannot determine either equal offsets or not,
723  * we use three valued logic. It is handy when we want to know whether offsets are definitely
724  * different or definitely equal.
725  */
726 /* static */
IsSameOffsets(const Inst * off1,const Inst * off2)727 AliasAnalysis::Trilean AliasAnalysis::IsSameOffsets(const Inst *off1, const Inst *off2)
728 {
729     if (off1 == off2) {
730         return Trilean::TRUE;
731     }
732     if (off1 == nullptr || off2 == nullptr) {
733         return Trilean::FALSE;
734     }
735 
736     if (off1->GetVN() != INVALID_VN && off2->GetVN() != INVALID_VN && off1->GetVN() == off2->GetVN()) {
737         return Trilean::TRUE;
738     }
739 
740     if (off1->IsConst() && off2->IsConst() &&
741         off1->CastToConstant()->GetRawValue() != off2->CastToConstant()->GetRawValue()) {
742         return Trilean::FALSE;
743     }
744 
745     return Trilean::UNKNOWN;
746 }
747 
748 }  // namespace ark::compiler
749