Skip to content

Commit 7145a80

Browse files
committed
Fix MemBehavior calls to getAccessedAddress to avoid an assert.
Fixes <rdar://60046018> assert: (v->getType().isAddress()) in getAccessedAddress. MemBehavior compares known memory accesses with some arbitrary value, which may not be an address. However, we should not call utilities that work on accessed addresses with an arbitrary value. This assert is a result of very recent changes to gradually make memory access utilities more type safe and introduce the concept of a canonical accessed address. In the future, we may even have a wrapper type for such a thing. In the SIL optimizer, there are several different notions of what constitutes the base of a memory access. Mismatches can lead to subtle bugs.
1 parent b3e8661 commit 7145a80

File tree

3 files changed

+80
-12
lines changed

3 files changed

+80
-12
lines changed

lib/SILOptimizer/Analysis/MemoryBehavior.cpp

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ class MemoryBehaviorVisitor
5656
/// The value we are attempting to discover memory behavior relative to.
5757
SILValue V;
5858

59+
/// Cache either the address of the access corresponding to memory at 'V', or
60+
/// 'V' itself if it isn't recognized as part of an access. The cached value
61+
/// is always a valid SILValue.
62+
SILValue cachedValueAddress;
63+
5964
Optional<bool> cachedIsLetValue;
6065

6166
/// The SILType of the value.
@@ -77,11 +82,21 @@ class MemoryBehaviorVisitor
7782
return *TypedAccessTy;
7883
}
7984

85+
/// If 'V' is an address projection within a formal access, return the
86+
/// canonical address of the formal access. Otherwise, return 'V' itself,
87+
/// which is either a reference or unknown pointer or address.
88+
SILValue getValueAddress() {
89+
if (!cachedValueAddress) {
90+
cachedValueAddress = V->getType().isAddress() ? getAccessedAddress(V) : V;
91+
}
92+
return cachedValueAddress;
93+
}
94+
8095
/// Return true if 'V's accessed address is that of a let variables.
8196
bool isLetValue() {
8297
if (!cachedIsLetValue) {
8398
cachedIsLetValue =
84-
V->getType().isAddress() && isLetAddress(getAccessedAddress(V));
99+
V->getType().isAddress() && isLetAddress(getValueAddress());
85100
}
86101
return cachedIsLetValue.getValue();
87102
}
@@ -124,15 +139,11 @@ class MemoryBehaviorVisitor
124139

125140
MemBehavior visitBeginAccessInst(BeginAccessInst *beginAccess) {
126141
switch (beginAccess->getAccessKind()) {
127-
case SILAccessKind::Init:
128142
case SILAccessKind::Deinit:
129-
// No address aliasing is expected for variable initialization or
130-
// deinitialization.
131-
if (stripAccessMarkers(beginAccess) != getAccessedAddress(V))
132-
return MemBehavior::None;
133-
134-
return MemBehavior::MayWrite;
135-
143+
// A [deinit] only directly reads from the object. The fact that it frees
144+
// memory is modeled more precisely by the release operations within the
145+
// deinit scope. Therefore, handle it like a [read] here...
146+
LLVM_FALLTHROUGH;
136147
case SILAccessKind::Read:
137148
if (!mayAlias(beginAccess->getSource()))
138149
return MemBehavior::None;
@@ -141,10 +152,14 @@ class MemoryBehaviorVisitor
141152

142153
case SILAccessKind::Modify:
143154
if (isLetValue()) {
144-
assert(stripAccessMarkers(beginAccess) != getAccessedAddress(V)
155+
assert(stripAccessMarkers(beginAccess) != getValueAddress()
145156
&& "let modification not allowed");
146157
return MemBehavior::None;
147158
}
159+
// [modify] has a special case for ignoring 'let's, but otherwise is
160+
// identical to an [init]...
161+
LLVM_FALLTHROUGH;
162+
case SILAccessKind::Init:
148163
if (!mayAlias(beginAccess->getSource()))
149164
return MemBehavior::None;
150165

@@ -229,15 +244,15 @@ MemBehavior MemoryBehaviorVisitor::visitLoadInst(LoadInst *LI) {
229244
return MemBehavior::None;
230245

231246
LLVM_DEBUG(llvm::dbgs() << " Could not prove that load inst does not alias "
232-
"pointer. Returning may read.");
247+
"pointer. Returning may read.\n");
233248
return MemBehavior::MayRead;
234249
}
235250

236251
MemBehavior MemoryBehaviorVisitor::visitStoreInst(StoreInst *SI) {
237252
// No store besides the initialization of a "let"-variable
238253
// can have any effect on the value of this "let" variable.
239254
if (isLetValue()
240-
&& (getAccessedAddress(SI->getDest()) != getAccessedAddress(V))) {
255+
&& (getAccessedAddress(SI->getDest()) != getValueAddress())) {
241256
return MemBehavior::None;
242257
}
243258
// If the store dest cannot alias the pointer in question, then the

test/SILOptimizer/access_marker_verify.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,3 +1080,27 @@ public func getStaticProp() -> HasStaticProp {
10801080
// CHECK: [[ADR:%.*]] = pointer_to_address [[RP]] : $Builtin.RawPointer to [strict] $*HasStaticProp
10811081
// CHECK: load [copy] [[ADR]] : $*HasStaticProp
10821082
// CHECK-LABEL: } // end sil function '$s20access_marker_verify13getStaticPropAA03HaseF0CyF'
1083+
1084+
// ===-----------------------------------------------------------------------===
1085+
// Test computeMemBehavior with a non-address value. In this case, the
1086+
// address for an access of StreamClass.buffer is compared with the
1087+
// ContiguousArrayStorageBase value.
1088+
//
1089+
// <rdar://60046018> assert: (v->getType().isAddress()) in getAccessedAddress.
1090+
1091+
public final class StreamClass {
1092+
private var buffer: [UInt8]
1093+
1094+
public init() {
1095+
self.buffer = []
1096+
}
1097+
}
1098+
1099+
// CHECK-LABEL: sil [ossa] @$s20access_marker_verify25testNonAddressMemBehavioryySiF : $@convention(thin) (Int) -> () {
1100+
// The relevant SIL instructions for this test don't appear until the string interpolation is inlined.
1101+
// Just make sure it does not assert.
1102+
// CHECK-LABEL: } // end sil function '$s20access_marker_verify25testNonAddressMemBehavioryySiF'
1103+
public func testNonAddressMemBehavior(_ N: Int) {
1104+
let listOfStrings: [String] = (0..<10).map { "This is the number: \($0)!\n" }
1105+
let stream = StreamClass()
1106+
}

test/SILOptimizer/mem-behavior-all.sil

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,32 @@ bb0(%0 : $*Builtin.Int32):
112112
end_access %write : $*Builtin.Int32
113113
return %val : $Builtin.Int32
114114
}
115+
116+
// ===-----------------------------------------------------------------------===
117+
// Test the effect of a [deinit] access on a global 'let'
118+
//
119+
// Test <rdar://60046018> Assert: (v->getType().isAddress()), getAccessedAddress
120+
121+
sil_global hidden [let] @globalC : $C
122+
123+
// CHECK-LABEL: @testDeinitInstVsNonAddressValue
124+
// CHECK: PAIR #5.
125+
// CHECK-NEXT: load %{{.*}} : $*C
126+
// CHECK-NEXT: begin_access [deinit] [static] %{{.*}} : $*Builtin.Int32
127+
// CHECK-NEXT: r=1,w=0,se=0
128+
// CHECK: PAIR #16.
129+
// CHECK-NEXT: %6 = begin_access [deinit] [static] %5 : $*Builtin.Int32 // user: %7
130+
// CHECK-NEXT: %2 = load %1 : $*C // user: %3
131+
// CHECK-NEXT: r=1,w=0,se=0
132+
sil hidden @testDeinitInstVsNonAddressValue : $@convention(thin) (@guaranteed C) -> () {
133+
bb0(%0 : $C):
134+
%1 = global_addr @globalC : $*C
135+
%2 = load %1 : $*C
136+
%3 = ref_element_addr %2 : $C, #C.prop
137+
%4 = load %3 : $*Builtin.Int32
138+
%5 = ref_element_addr %0 : $C, #C.prop
139+
%6 = begin_access [deinit] [static] %5 : $*Builtin.Int32
140+
end_access %6 : $*Builtin.Int32
141+
%8 = tuple ()
142+
return %8 : $()
143+
}

0 commit comments

Comments
 (0)