Skip to content

Commit 5826e75

Browse files
committed
Generalize the MemAccessUtils API.
For use outside access enforcement passes. Add isUniquelyIdentifiedAfterEnforcement. Rename functions for clarity and generality. Rename isUniquelyIdentifiedOrClass to isFormalAccessBase. Rename findAccessedStorage to identifyFormalAccess. Rename findAccessedStorageNonNested to findAccessedStorage. Part of generalizing the utility for use outside the access enforcement passes.
1 parent 73c4fb9 commit 5826e75

File tree

13 files changed

+191
-158
lines changed

13 files changed

+191
-158
lines changed

include/swift/SIL/MemAccessUtils.h

Lines changed: 98 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -77,33 +77,52 @@ namespace swift {
7777

7878
/// Get the base address of a formal access by stripping access markers.
7979
///
80-
/// If \p v is an address, then the returned value is also an address
81-
/// (pointer-to-address is not stripped).
82-
SILValue stripAccessMarkers(SILValue v);
80+
/// Postcondition: If \p v is an address, then the returned value is also an
81+
/// address (pointer-to-address is not stripped).
82+
inline SILValue stripAccessMarkers(SILValue v) {
83+
while (auto *bai = dyn_cast<BeginAccessInst>(v)) {
84+
v = bai->getOperand();
85+
}
86+
return v;
87+
}
8388

84-
/// Return a non-null address-type SingleValueInstruction if \p v is the result
85-
/// of an address projection that may be inside of a formal access, such as
89+
/// An address projection that may be inside of a formal access, such as
8690
/// (begin_borrow, struct_element_addr, tuple_element_addr).
87-
///
88-
/// The resulting projection must have an address-type operand at index zero
89-
/// representing the projected address.
90-
SingleValueInstruction *isAccessProjection(SILValue v);
91+
struct AccessProjection {
92+
SingleValueInstruction *projectionInst = nullptr;
9193

92-
/// Attempt to return the address corresponding to a variable's formal access
93-
/// by stripping indexing and address projections.
94-
///
95-
/// \p v must be an address.
94+
/// If \p v is not a recognized access projection the result is invalid.
95+
AccessProjection(SILValue v) {
96+
switch (v->getKind()) {
97+
default:
98+
break;
99+
100+
case ValueKind::StructElementAddrInst:
101+
case ValueKind::TupleElementAddrInst:
102+
case ValueKind::UncheckedTakeEnumDataAddrInst:
103+
case ValueKind::TailAddrInst:
104+
case ValueKind::IndexAddrInst:
105+
projectionInst = cast<SingleValueInstruction>(v);
106+
};
107+
}
108+
109+
operator bool() const { return projectionInst != nullptr; }
110+
111+
SILValue baseAddress() const { return projectionInst->getOperand(0); }
112+
};
113+
114+
/// Return the base address after stripping access projections. If \p v is an
115+
/// access projection, return the enclosing begin_access. Otherwise, return a
116+
/// "best effort" base address.
96117
///
97-
/// Returns an address. If the a formal access was successfully identified, and
98-
/// access markers have not yet been removed, then the returned address is
99-
/// produced by a begin_access marker.
118+
/// Precondition: \p v must be an address.
100119
///
101120
/// To get the base address of the formal access behind the access marker,
102121
/// either call stripAccessMarkers() on the returned value, or call
103122
/// getAccessedAddress() on \p v.
104123
///
105-
/// To identify the underlying storage object of the access, use
106-
/// findAccessedStorage() on either \p v or the returned address.
124+
/// To identify the underlying storage object of the access, call
125+
/// findAccessedStorage() either on \p v or on the returned address.
107126
SILValue getAddressAccess(SILValue v);
108127

109128
/// Convenience for stripAccessMarkers(getAddressAccess(v)).
@@ -324,9 +343,9 @@ class AccessedStorage {
324343
return getElementIndex();
325344
}
326345

327-
SILArgument *getArgument() const {
346+
SILFunctionArgument *getArgument() const {
328347
assert(getKind() == Argument);
329-
return cast<SILArgument>(value);
348+
return cast<SILFunctionArgument>(value);
330349
}
331350

332351
SILGlobalVariable *getGlobal() const {
@@ -386,6 +405,7 @@ class AccessedStorage {
386405
llvm_unreachable("unhandled kind");
387406
}
388407

408+
/// Return true if the given access is on a 'let' lvalue.
389409
bool isLetAccess(SILFunction *F) const;
390410

391411
/// If this is a uniquely identified formal access, then it cannot
@@ -409,12 +429,31 @@ class AccessedStorage {
409429
llvm_unreachable("unhandled kind");
410430
}
411431

412-
bool isUniquelyIdentifiedOrClass() const {
432+
/// Return true if this a uniquely identified formal access location assuming
433+
/// exclusivity enforcement. Do not use this to optimize access markers.
434+
bool isUniquelyIdentifiedAfterEnforcement() const {
413435
if (isUniquelyIdentified())
414436
return true;
415-
return (getKind() == Class);
437+
438+
return getKind() == Argument
439+
&& getArgument()
440+
->getArgumentConvention()
441+
.isExclusiveIndirectParameter();
416442
}
417443

444+
/// Return true if this identifies the base of a formal access location.
445+
///
446+
/// Most formal access bases are uniquely identified, but class access
447+
/// may alias other references to the same object.
448+
bool isFormalAccessBase() const {
449+
if (isUniquelyIdentified())
450+
return true;
451+
452+
return getKind() == Class;
453+
}
454+
455+
// Return true if this storage is guaranteed not to overlap with \p other's
456+
// storage.
418457
bool isDistinctFrom(const AccessedStorage &other) const {
419458
if (isUniquelyIdentified() && other.isUniquelyIdentified()) {
420459
return !hasIdenticalBase(other);
@@ -507,37 +546,50 @@ template <> struct DenseMapInfo<swift::AccessedStorage> {
507546

508547
namespace swift {
509548

510-
/// Given an address accessed by an instruction that reads or modifies
511-
/// memory, return an AccessedStorage object that identifies the formal access.
512-
///
513-
/// The returned AccessedStorage represents the best attempt to find the base of
514-
/// the storage object being accessed at `sourceAddr`. This may be a fully
515-
/// identified storage base of known kind, or a valid but Unidentified storage
516-
/// object, such as a SILPhiArgument.
549+
/// Given an address used by an instruction that reads or writes memory, return
550+
/// the AccessedStorage value that identifies the formally accessed memory,
551+
/// looking through any nested formal accesses to find the underlying storage.
517552
///
518-
/// This may return an invalid storage object if the address producer is not
519-
/// recognized by a whitelist of recognizable access patterns. The result must
520-
/// always be valid when `sourceAddr` is used for formal memory access, i.e. as
521-
/// the operand of begin_access.
522-
///
523-
/// If `sourceAddr` is produced by a begin_access, this returns a Nested
524-
/// AccessedStorage kind. This is useful for exclusivity checking to distinguish
525-
/// between a nested access vs. a conflict.
553+
/// This may return invalid storage for a memory operation that is not part of
554+
/// a formal access or when the outermost formal access has Unsafe enforcement.
526555
AccessedStorage findAccessedStorage(SILValue sourceAddr);
527556

528-
/// Given an address accessed by an instruction that reads or modifies
529-
/// memory, return an AccessedStorage that identifies the formal access, looking
530-
/// through any Nested access to find the original storage.
557+
// Helper for identifyFormalAccess.
558+
AccessedStorage identifyAccessedStorageImpl(SILValue sourceAddr);
559+
560+
/// Return an AccessedStorage object that identifies the formal access
561+
/// represented by \p beginAccess.
562+
///
563+
/// If the given access is nested within an outer access, return a Nested
564+
/// AccessedStorage kind. This is useful for exclusivity checking to distinguish
565+
/// between nested access vs. conflicting access on the same storage.
531566
///
532-
/// This is identical to findAccessedStorage(), but never returns Nested
533-
/// storage and may return invalid storage for nested access when the outer
534-
/// access has Unsafe enforcement.
535-
AccessedStorage findAccessedStorageNonNested(SILValue sourceAddr);
567+
/// May return an invalid storage for either:
568+
/// - A \p beginAccess with Unsafe enforcement
569+
/// - Non-OSSA form in which address-type block args are allowed
570+
inline AccessedStorage identifyFormalAccess(BeginAccessInst *beginAccess) {
571+
return identifyAccessedStorageImpl(beginAccess->getSource());
572+
}
573+
574+
inline AccessedStorage
575+
identifyFormalAccess(BeginUnpairedAccessInst *beginAccess) {
576+
return identifyAccessedStorageImpl(beginAccess->getSource());
577+
}
578+
579+
/// Return a valid AccessedStorage object for an address captured by a no-escape
580+
/// closure. A no-escape closure may capture a regular storage address without
581+
/// guarding it with an access marker. If the captured address does come from an
582+
/// access marker, then this returns a Nested AccessedStorage kind.
583+
inline AccessedStorage identifyCapturedStorage(SILValue capturedAddress) {
584+
auto storage = identifyAccessedStorageImpl(capturedAddress);
585+
assert(storage && "captured access has invalid storage");
586+
return storage;
587+
}
536588

537589
} // end namespace swift
538590

539591
//===----------------------------------------------------------------------===//
540-
// MARK: Helper API
592+
// MARK: Helper API for specific formal access patterns
541593
//===----------------------------------------------------------------------===//
542594

543595
namespace swift {
@@ -583,7 +635,7 @@ void checkSwitchEnumBlockArg(SILPhiArgument *arg);
583635
///
584636
/// If this returns false, then the address can be safely accessed without
585637
/// a begin_access marker. To determine whether to emit begin_access:
586-
/// storage = findAccessedStorage(address)
638+
/// storage = identifyFormalAccess(address)
587639
/// needsAccessMarker = storage && isPossibleFormalAccessBase(storage)
588640
///
589641
/// Warning: This is only valid for SIL with well-formed accesses. For example,

include/swift/SILOptimizer/Analysis/ValueTracking.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,13 @@ bool isExclusiveArgument(SILValue V);
3636
/// does not look through init/open_existential_addr.
3737
bool pointsToLocalObject(SILValue V);
3838

39-
/// Returns true if \p V is a uniquely identified address or reference. It may
40-
/// be any of:
39+
/// Returns true if \p V is a uniquely identified address or reference. Two
40+
/// uniquely identified pointers with distinct roots cannot alias. However, a
41+
/// uniquely identified pointer may alias with unidentified pointers. For
42+
/// example, the uniquely identified pointer may escape to a call that returns an
43+
/// alias of that pointer.
44+
///
45+
/// It may be any of:
4146
///
4247
/// - an address projection based on a locally allocated address with no
4348
/// indirection

lib/SIL/Utils/MemAccessUtils.cpp

Lines changed: 35 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -25,40 +25,17 @@ using namespace swift;
2525
// MARK: General Helpers
2626
//===----------------------------------------------------------------------===//
2727

28-
SILValue swift::stripAccessMarkers(SILValue v) {
29-
while (auto *bai = dyn_cast<BeginAccessInst>(v)) {
30-
v = bai->getOperand();
31-
}
32-
return v;
33-
}
34-
35-
// The resulting projection must have an address-type operand at index zero
36-
// representing the projected address.
37-
SingleValueInstruction *swift::isAccessProjection(SILValue v) {
38-
switch (v->getKind()) {
39-
default:
40-
return nullptr;
41-
42-
case ValueKind::StructElementAddrInst:
43-
case ValueKind::TupleElementAddrInst:
44-
case ValueKind::UncheckedTakeEnumDataAddrInst:
45-
case ValueKind::TailAddrInst:
46-
case ValueKind::IndexAddrInst:
47-
return cast<SingleValueInstruction>(v);
48-
};
49-
}
50-
5128
// TODO: When the optimizer stops stripping begin_access markers, then we should
5229
// be able to assert that the result is a BeginAccessInst and the default case
5330
// is unreachable.
5431
SILValue swift::getAddressAccess(SILValue v) {
5532
while (true) {
5633
assert(v->getType().isAddress());
57-
auto *projection = isAccessProjection(v);
34+
auto projection = AccessProjection(v);
5835
if (!projection)
5936
return v;
6037

61-
v = projection->getOperand(0);
38+
v = projection.baseAddress();
6239
}
6340
}
6441

@@ -240,26 +217,26 @@ void AccessedStorage::print(raw_ostream &os) const {
240217
void AccessedStorage::dump() const { print(llvm::dbgs()); }
241218

242219
namespace {
243-
struct FindAccessedStorageVisitor
244-
: public AccessUseDefChainVisitor<FindAccessedStorageVisitor>
245-
{
220+
template <typename Impl>
221+
struct FindAccessedStorageVisitorBase : public AccessUseDefChainVisitor<Impl> {
222+
protected:
246223
SmallVector<SILValue, 8> addressWorklist;
247224
SmallPtrSet<SILPhiArgument *, 4> visitedPhis;
248225
Optional<AccessedStorage> storage;
249226

250227
public:
251-
FindAccessedStorageVisitor(SILValue firstSourceAddr)
252-
: addressWorklist({firstSourceAddr})
253-
{}
254-
255-
AccessedStorage doIt() {
228+
FindAccessedStorageVisitorBase(SILValue firstSourceAddr)
229+
: addressWorklist({firstSourceAddr}) {}
230+
231+
// Main entry point.
232+
AccessedStorage findStorage() {
256233
while (!addressWorklist.empty()) {
257-
visit(addressWorklist.pop_back_val());
234+
this->visit(addressWorklist.pop_back_val());
258235
}
259236

260237
return storage.getValueOr(AccessedStorage());
261238
}
262-
239+
263240
void setStorage(AccessedStorage foundStorage) {
264241
if (!storage) {
265242
storage = foundStorage;
@@ -292,20 +269,32 @@ struct FindAccessedStorageVisitor
292269
addressWorklist.push_back(parentAddr);
293270
}
294271
};
272+
struct FindAccessedStorageVisitor
273+
: public FindAccessedStorageVisitorBase<FindAccessedStorageVisitor> {
274+
275+
FindAccessedStorageVisitor(SILValue sourceAddr)
276+
: FindAccessedStorageVisitorBase(sourceAddr) {}
277+
278+
void visitNestedAccess(BeginAccessInst *access) {
279+
addressWorklist.push_back(access->getSource());
280+
}
281+
};
282+
283+
struct IdentifyAccessedStorageVisitor
284+
: public FindAccessedStorageVisitorBase<IdentifyAccessedStorageVisitor> {
285+
286+
IdentifyAccessedStorageVisitor(SILValue sourceAddr)
287+
: FindAccessedStorageVisitorBase(sourceAddr) {}
288+
};
289+
295290
} // namespace
296291

297292
AccessedStorage swift::findAccessedStorage(SILValue sourceAddr) {
298-
return FindAccessedStorageVisitor(sourceAddr).doIt();
293+
return FindAccessedStorageVisitor(sourceAddr).findStorage();
299294
}
300295

301-
AccessedStorage swift::findAccessedStorageNonNested(SILValue sourceAddr) {
302-
while (true) {
303-
const AccessedStorage &storage = findAccessedStorage(sourceAddr);
304-
if (!storage || storage.getKind() != AccessedStorage::Nested)
305-
return storage;
306-
307-
sourceAddr = cast<BeginAccessInst>(storage.getValue())->getSource();
308-
}
296+
AccessedStorage swift::identifyAccessedStorageImpl(SILValue sourceAddr) {
297+
return IdentifyAccessedStorageVisitor(sourceAddr).findStorage();
309298
}
310299

311300
//===----------------------------------------------------------------------===//
@@ -485,10 +474,9 @@ bool swift::isPossibleFormalAccessBase(const AccessedStorage &storage,
485474
case AccessedStorage::Nested: {
486475
// A begin_access is considered a separate base for the purpose of conflict
487476
// checking. However, for the purpose of inserting unenforced markers and
488-
// performaing verification, it needs to be ignored.
477+
// performing verification, it needs to be ignored.
489478
auto *BAI = cast<BeginAccessInst>(storage.getValue());
490-
const AccessedStorage &nestedStorage =
491-
findAccessedStorage(BAI->getSource());
479+
const AccessedStorage &nestedStorage = identifyFormalAccess(BAI);
492480
if (!nestedStorage)
493481
return false;
494482

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1948,20 +1948,20 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
19481948
// also requires that Unidentified access fit a whitelist on known
19491949
// non-internal globals or class properties.
19501950
//
1951-
// First check that findAccessedStorage returns without asserting. For
1951+
// First check that identifyFormalAccess returns without asserting. For
19521952
// Unsafe enforcement, that is sufficient. For any other enforcement
19531953
// level also require that it returns a valid AccessedStorage object.
19541954
// Unsafe enforcement is used for some unrecognizable access patterns,
19551955
// like debugger variables. The compiler never cares about the source of
19561956
// those accesses.
1957-
findAccessedStorage(BAI->getSource());
1957+
identifyFormalAccess(BAI);
19581958
// FIXME: rdar://57291811 - the following check for valid storage will be
19591959
// reenabled shortly. A fix is planned. In the meantime, the possiblity that
19601960
// a real miscompilation could be caused by this failure is insignificant.
19611961
// I will probably enable a much broader SILVerification of address-type
19621962
// block arguments first to ensure we never hit this check again.
19631963
/*
1964-
AccessedStorage storage = findAccessedStorage(BAI->getSource());
1964+
AccessedStorage storage = identifyFormalAccess(BAI);
19651965
if (BAI->getEnforcement() != SILAccessEnforcement::Unsafe)
19661966
require(storage, "Unknown formal access pattern");
19671967
*/
@@ -2000,8 +2000,8 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
20002000
break;
20012001
}
20022002

2003-
// First check that findAccessedStorage never asserts.
2004-
AccessedStorage storage = findAccessedStorage(BUAI->getSource());
2003+
// First check that identifyFormalAccess never asserts.
2004+
AccessedStorage storage = identifyFormalAccess(BUAI);
20052005
// Only allow Unsafe and Builtin access to have invalid storage.
20062006
if (BUAI->getEnforcement() != SILAccessEnforcement::Unsafe
20072007
&& !BUAI->isFromBuiltin()) {

0 commit comments

Comments
 (0)