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