Skip to content

Commit 23c2579

Browse files
committed
Implement SILArgument and select_enum handling for alias analysis
1 parent f8143bb commit 23c2579

File tree

3 files changed

+214
-2
lines changed

3 files changed

+214
-2
lines changed

include/swift/SILAnalysis/AliasAnalysis.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ class AliasAnalysis : public SILAnalysis {
7070

7171
SideEffectAnalysis *getSideEffectAnalysis() const { return SEA; }
7272

73+
/// Perform alias analysis on SILValues with multiple underlying objects.
74+
AliasResult handleMultiUnderlyingObjectAlias(SILValue V1, SILValue V2);
75+
7376
/// Perform an alias query to see if V1, V2 refer to the same values.
7477
AliasResult alias(SILValue V1, SILValue V2, SILType TBAAType1 = SILType(),
7578
SILType TBAAType2 = SILType());

lib/SILAnalysis/AliasAnalysis.cpp

Lines changed: 116 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828

2929
using namespace swift;
3030

31+
using SILValueSet = llvm::DenseSet<SILValue>;
32+
using SILValueList = llvm::SmallVector<SILValue, 8>;
33+
3134
//===----------------------------------------------------------------------===//
3235
// AA Debugging
3336
//===----------------------------------------------------------------------===//
@@ -99,6 +102,80 @@ llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &OS,
99102
}
100103
}
101104

105+
/// Return true if the SILValue is the result of multiple SILValues, e.g.
106+
/// select_enum, silargument.
107+
static bool isMultiUnderlyingObjectValue(SILValue V) {
108+
if (isa<SelectEnumInst>(V))
109+
return true;
110+
// We are only interested in basic block SILArguments as those are the
111+
// ones we can collect all the possible incoming values.
112+
if (SILArgument *SA = dyn_cast<SILArgument>(V))
113+
if (!SA->isFunctionArg())
114+
return true;
115+
return false;
116+
}
117+
118+
/// Get the first level underlying objects, e.g. in case of select_enum,
119+
/// look to the possible underlying objects from all the cases.
120+
static bool getFirstLevelUnderlyingObjects(SILValue V, SILValueSet &Cache,
121+
SILValueList &WorkList) {
122+
// Look through SILArgument.
123+
if (auto *SA = dyn_cast<SILArgument>(V)) {
124+
SILValueList Args;
125+
bool Succ = SA->getIncomingValues(Args);
126+
// Unable to get SILValue for every predecessor.
127+
if (!Succ)
128+
return false;
129+
for (auto &X : Args) {
130+
if (Cache.count(X))
131+
continue;
132+
// We have not seen this SILValue before.
133+
Cache.insert(X);
134+
WorkList.push_back(X);
135+
}
136+
}
137+
138+
// Look through SelectEnumInst.
139+
if (auto *SE = dyn_cast<SelectEnumInst>(V)) {
140+
unsigned CaseNum = SE->getNumCases();
141+
for (unsigned i = 0; i < CaseNum; ++i) {
142+
SILValue C = SE->getCase(i).second;
143+
if (Cache.count(C))
144+
continue;
145+
// We have not seen this SILValue before.
146+
Cache.insert(C);
147+
WorkList.push_back(C);
148+
}
149+
}
150+
return true;
151+
}
152+
153+
/// Collect all the underlying objects for the given SILValue. Return false
154+
/// if fail to collect all possible underlying objects.
155+
static bool getTransistiveUnderlyingObjects(SILValue V, SILValueList &Objs) {
156+
// Cache keeps track of what has been processed, so that we do not collected
157+
// it again.
158+
SILValueSet Cache;
159+
SILValueList WorkList;
160+
161+
// Start with the given SILValue.
162+
WorkList.push_back(V);
163+
while(!WorkList.empty()) {
164+
SILValue V = WorkList.pop_back_val();
165+
if (isMultiUnderlyingObjectValue(V)) {
166+
if (getFirstLevelUnderlyingObjects(V, Cache, WorkList))
167+
continue;
168+
// Failed to get all possible underlying value, bail out.
169+
return false;
170+
}
171+
172+
// This is single base SILValue.
173+
Objs.push_back(V);
174+
Cache.insert(V);
175+
}
176+
return true;
177+
}
178+
102179
//===----------------------------------------------------------------------===//
103180
// Unequal Base Object AA
104181
//===----------------------------------------------------------------------===//
@@ -527,6 +604,37 @@ static bool typesMayAlias(SILType T1, SILType T2, SILType TBAA1Ty,
527604
// Entry Points
528605
//===----------------------------------------------------------------------===//
529606

607+
AliasAnalysis::AliasResult
608+
AliasAnalysis::handleMultiUnderlyingObjectAlias(SILValue V1, SILValue V2) {
609+
const unsigned AliasQueryLimit = 16;
610+
SmallVector<SILValue, 8> V1Base, V2Base;
611+
// Collect the transistive closure of all the SILValue V1 and V2 can
612+
// point to. If for some reason we can not collect all the possible
613+
// underlying objects, return MayAlias to be conservative.
614+
if (!getTransistiveUnderlyingObjects(V1, V1Base) ||
615+
!getTransistiveUnderlyingObjects(V2, V2Base))
616+
return AliasResult::MayAlias;
617+
618+
// An mxn alias analysis query, bail out if this results in too many
619+
// alias queries.
620+
if (V1Base.size() * V2Base.size() > AliasQueryLimit)
621+
return AliasResult::MayAlias;
622+
623+
// Return MayAlias if any pair does not have a NoAlias relation.
624+
for (auto &M : V1Base) {
625+
for (auto &N : V2Base) {
626+
AliasAnalysis::AliasResult R = alias(M, N, findTypedAccessType(M),
627+
findTypedAccessType(N));
628+
// Return MayAlias whenever we have 1 non-NoAlias pair. This is a
629+
// tradeoff between compilation time and being conservative.
630+
if (R != AliasResult::NoAlias)
631+
return AliasResult::MayAlias;
632+
}
633+
}
634+
635+
return AliasResult::NoAlias;
636+
}
637+
530638
/// The main AA entry point. Performs various analyses on V1, V2 in an attempt
531639
/// to disambiguate the two values.
532640
AliasAnalysis::AliasResult AliasAnalysis::alias(SILValue V1, SILValue V2,
@@ -589,8 +697,13 @@ AliasAnalysis::AliasResult AliasAnalysis::aliasInner(SILValue V1, SILValue V2,
589697
}
590698
}
591699

592-
// Ok, we need to actually compute an Alias Analysis result for V1, V2. Begin
593-
// by finding the "base" of V1, V2 by stripping off all casts and GEPs.
700+
// Ok, we need to actually compute an Alias Analysis result for V1, V2. First
701+
// find whether V1, V2 potentially have multiple underlying objects.
702+
if (isMultiUnderlyingObjectValue(V1) || isMultiUnderlyingObjectValue(V2))
703+
return handleMultiUnderlyingObjectAlias(V1, V2);
704+
705+
// At this point, V1 and V2 are both single base SIlValues, begin by
706+
// finding the "base" of V1, V2 by stripping off all casts and GEPs.
594707
SILValue O1 = getUnderlyingObject(V1);
595708
SILValue O2 = getUnderlyingObject(V2);
596709
DEBUG(llvm::dbgs() << " Underlying V1:" << *O1.getDef());
@@ -626,6 +739,7 @@ AliasAnalysis::AliasResult AliasAnalysis::aliasInner(SILValue V1, SILValue V2,
626739
return AliasResult::MayAlias;
627740
}
628741

742+
629743
/// Check if this is the address of a "let" member.
630744
/// Nobody can write into let members.
631745
bool swift::isLetPointer(SILValue V) {

test/SILAnalysis/basic-aa.sil

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,101 @@
55
import Builtin
66
import Swift
77

8+
///////////////////////
9+
// Type Declarations //
10+
///////////////////////
11+
12+
enum E {
13+
case A
14+
case B
15+
case C
16+
}
17+
18+
class Base { }
19+
20+
class Derived1 : Base {}
21+
22+
class foo {
23+
@sil_stored var a: Int { get set }
24+
deinit
25+
init()
26+
}
27+
28+
sil @foo_init : $@convention(thin) (@thick foo.Type) -> @owned foo
29+
30+
// CHECK-LABEL: silargument_alias_check
31+
32+
// CHECK: PAIR #23.
33+
// CHECK: (0): %1 = alloc_ref $foo
34+
// CHECK: (0): %12 = argument of bb3 : $foo
35+
// CHECK: NoAlias
36+
sil hidden @silargument_alias_check : $@convention(thin) (Bool) -> () {
37+
// %0 // users: %1, %2
38+
bb0(%0 : $Bool):
39+
%1 = alloc_ref $foo
40+
%2 = struct_extract %0 : $Bool, #Bool._value // user: %3
41+
cond_br %2, bb1, bb2 // id: %3
42+
43+
bb1: // Preds: bb0
44+
%4 = function_ref @foo_init : $@convention(thin) (@thick foo.Type) -> @owned foo // user: %6
45+
%5 = metatype $@thick foo.Type // user: %6
46+
%6 = apply %4(%5) : $@convention(thin) (@thick foo.Type) -> @owned foo // user: %7
47+
br bb3(%6 : $foo) // id: %7
48+
49+
bb2: // Preds: bb0
50+
%8 = function_ref @foo_init : $@convention(thin) (@thick foo.Type) -> @owned foo // user: %10
51+
%9 = metatype $@thick foo.Type // user: %10
52+
%10 = apply %8(%9) : $@convention(thin) (@thick foo.Type) -> @owned foo // user: %11
53+
br bb3(%10 : $foo) // id: %11
54+
55+
// %12 // users: %13, %16, %19, %20
56+
bb3(%12 : $foo): // Preds: bb1 bb2
57+
strong_retain %12 : $foo // id: %13
58+
%14 = integer_literal $Builtin.Int64, 12 // user: %15
59+
%15 = struct $Int (%14 : $Builtin.Int64) // user: %17
60+
%16 = ref_element_addr %12 : $foo, #foo.a // user: %17
61+
store %15 to %16 : $*Int // id: %17
62+
%18 = tuple ()
63+
strong_release %12 : $foo // id: %19
64+
strong_release %12 : $foo // id: %20
65+
%21 = tuple () // user: %22
66+
return %21 : $() // id: %22
67+
}
68+
69+
70+
// CHECK-LABEL: select_enum_and_local_object
71+
72+
// CHECK: PAIR #61.
73+
// CHECK: (0): %5 = alloc_ref $Base
74+
// CHECK: (0): %6 = select_enum %0 : $E, case #E.A!enumelt: %1, default %2 : $Base
75+
// CHECK: NoAlias
76+
sil @select_enum_and_local_object : $@convention(thin) (E, @owned Base, @owned Base, @owned Base, @owned Base) -> Builtin.Int64 {
77+
bb0(%0 : $E, %1 : $Base, %2 : $Base, %3 : $Base, %4 : $Base):
78+
%5 = alloc_ref $Base
79+
%6 = select_enum %0 : $E, case #E.A!enumelt: %1, default %2 : $Base // user: %7
80+
checked_cast_br %6 : $Base to $Derived1, bb1, bb2 // id: %7
81+
82+
bb1(%8 : $Derived1): // Preds: bb0
83+
%9 = integer_literal $Builtin.Int64, 1 // user: %10
84+
br bb5(%9 : $Builtin.Int64) // id: %10
85+
86+
bb2: // Preds: bb0
87+
%11 = select_enum %0 : $E, case #E.B!enumelt: %3, default %4 : $Base // user: %12
88+
checked_cast_br %11 : $Base to $Derived1, bb3, bb4 // id: %12
89+
90+
bb3(%13 : $Derived1): // Preds: bb2
91+
%14 = integer_literal $Builtin.Int64, 2 // user: %15
92+
br bb5(%14 : $Builtin.Int64) // id: %15
93+
94+
bb4: // Preds: bb2
95+
%16 = integer_literal $Builtin.Int64, 3 // user: %17
96+
br bb5(%16 : $Builtin.Int64) // id: %17
97+
98+
// %18 // user: %19
99+
bb5(%18 : $Builtin.Int64): // Preds: bb1 bb3 bb4
100+
return %18 : $Builtin.Int64 // id: %19
101+
}
102+
8103
// Address Arguments don't alias if they are arguments to the first BB.
9104
//
10105
// CHECK-LABEL: @address_args_dont_alias_in_first_bb

0 commit comments

Comments
 (0)