Skip to content

Commit 8af488e

Browse files
authored
Merge pull request #32953 from atrick/generalize-memaccess
Generalize the MemAccessUtils API for use outside of access enforcement
2 parents 2da7c70 + 5826e75 commit 8af488e

File tree

13 files changed

+300
-224
lines changed

13 files changed

+300
-224
lines changed

include/swift/SIL/MemAccessUtils.h

Lines changed: 207 additions & 112 deletions
Large diffs are not rendered by default.

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()) {

lib/SILOptimizer/Analysis/AccessedStorageAnalysis.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ transformCalleeStorage(const StorageAccessInfo &storage,
253253
SILValue argVal = getCallerArg(fullApply, storage.getParamIndex());
254254
if (argVal) {
255255
// Remap the argument source value and inherit the old storage info.
256-
auto calleeStorage = findAccessedStorageNonNested(argVal);
256+
auto calleeStorage = findAccessedStorage(argVal);
257257
if (calleeStorage)
258258
return StorageAccessInfo(calleeStorage, storage);
259259
}
@@ -262,7 +262,7 @@ transformCalleeStorage(const StorageAccessInfo &storage,
262262
//
263263
// This is an untested bailout. It is only reachable if the call graph
264264
// contains an edge that getCallerArg is unable to analyze OR if
265-
// findAccessedStorageNonNested returns an invalid SILValue, which won't
265+
// findAccessedStorage returns an invalid SILValue, which won't
266266
// pass SIL verification.
267267
//
268268
// FIXME: In case argVal is invalid, support Unidentified access for invalid
@@ -299,7 +299,7 @@ void AccessedStorageResult::visitBeginAccess(B *beginAccess) {
299299
return;
300300

301301
const AccessedStorage &storage =
302-
findAccessedStorageNonNested(beginAccess->getSource());
302+
findAccessedStorage(beginAccess->getSource());
303303

304304
if (storage.getKind() == AccessedStorage::Unidentified) {
305305
// This also catches invalid storage.

lib/SILOptimizer/LoopTransforms/LICM.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -700,19 +700,18 @@ static bool analyzeBeginAccess(BeginAccessInst *BI,
700700
InstSet &SideEffectInsts,
701701
AccessedStorageAnalysis *ASA,
702702
DominanceInfo *DT) {
703-
const AccessedStorage &storage =
704-
findAccessedStorageNonNested(BI->getSource());
703+
const AccessedStorage &storage = findAccessedStorage(BI->getSource());
705704
if (!storage) {
706705
return false;
707706
}
708707

709-
auto BIAccessedStorageNonNested = findAccessedStorageNonNested(BI);
708+
auto BIAccessedStorageNonNested = findAccessedStorage(BI);
710709
auto safeBeginPred = [&](BeginAccessInst *OtherBI) {
711710
if (BI == OtherBI) {
712711
return true;
713712
}
714713
return BIAccessedStorageNonNested.isDistinctFrom(
715-
findAccessedStorageNonNested(OtherBI));
714+
findAccessedStorage(OtherBI));
716715
};
717716

718717
if (!std::all_of(BeginAccesses.begin(), BeginAccesses.end(), safeBeginPred))

lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp

Lines changed: 27 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -583,16 +583,6 @@ static void diagnoseExclusivityViolation(const ConflictingAccess &Violation,
583583
.highlight(NoteAccess.getAccessLoc().getSourceRange());
584584
}
585585

586-
/// Look through a value to find the underlying storage accessed.
587-
static AccessedStorage findValidAccessedStorage(SILValue Source) {
588-
const AccessedStorage &Storage = findAccessedStorage(Source);
589-
if (!Storage) {
590-
llvm::dbgs() << "Bad memory access source: " << Source;
591-
llvm_unreachable("Unexpected access source.");
592-
}
593-
return Storage;
594-
}
595-
596586
/// Returns true when the apply calls the Standard Library swap().
597587
/// Used for fix-its to suggest replacing with Collection.swapAt()
598588
/// on exclusivity violations.
@@ -700,7 +690,7 @@ struct AccessState {
700690

701691
// Find conflicting access on each argument using AccessSummaryAnalysis.
702692
static void
703-
checkCaptureAccess(ApplySite Apply, AccessState &State,
693+
checkAccessSummary(ApplySite Apply, AccessState &State,
704694
const AccessSummaryAnalysis::FunctionSummary &FS) {
705695
for (unsigned ArgumentIndex : range(Apply.getNumArguments())) {
706696

@@ -721,7 +711,7 @@ checkCaptureAccess(ApplySite Apply, AccessState &State,
721711

722712
// A valid AccessedStorage should always be found because Unsafe accesses
723713
// are not tracked by AccessSummaryAnalysis.
724-
const AccessedStorage &Storage = findValidAccessedStorage(Argument);
714+
const AccessedStorage &Storage = identifyCapturedStorage(Argument);
725715
auto AccessIt = State.Accesses->find(Storage);
726716

727717
// Are there any accesses in progress at the time of the call?
@@ -743,7 +733,7 @@ static void checkCaptureAccess(ApplySite Apply, AccessState &State) {
743733
// dynamically replaceable.
744734
SILFunction *Callee = Apply.getCalleeFunction();
745735
if (Callee && !Callee->empty()) {
746-
checkCaptureAccess(Apply, State, State.ASA->getOrCreateSummary(Callee));
736+
checkAccessSummary(Apply, State, State.ASA->getOrCreateSummary(Callee));
747737
return;
748738
}
749739
// In the absence of AccessSummaryAnalysis, conservatively assume by-address
@@ -755,7 +745,7 @@ static void checkCaptureAccess(ApplySite Apply, AccessState &State) {
755745

756746
// A valid AccessedStorage should always be found because Unsafe accesses
757747
// are not tracked by AccessSummaryAnalysis.
758-
const AccessedStorage &Storage = findValidAccessedStorage(argOper.get());
748+
const AccessedStorage &Storage = identifyCapturedStorage(argOper.get());
759749

760750
// Are there any accesses in progress at the time of the call?
761751
auto AccessIt = State.Accesses->find(Storage);
@@ -845,8 +835,8 @@ static void checkForViolationsAtInstruction(SILInstruction &I,
845835
return;
846836

847837
SILAccessKind Kind = BAI->getAccessKind();
848-
const AccessedStorage &Storage =
849-
findValidAccessedStorage(BAI->getSource());
838+
const AccessedStorage &Storage = identifyFormalAccess(BAI);
839+
assert(Storage && "unidentified formal access");
850840
// Storage may be associated with a nested access where the outer access is
851841
// "unsafe". That's ok because the outer access can itself be treated like a
852842
// valid source, as long as we don't ask for its source.
@@ -862,14 +852,15 @@ static void checkForViolationsAtInstruction(SILInstruction &I,
862852
}
863853

864854
if (auto *EAI = dyn_cast<EndAccessInst>(&I)) {
865-
if (EAI->getBeginAccess()->getEnforcement() == SILAccessEnforcement::Unsafe)
855+
BeginAccessInst *BAI = EAI->getBeginAccess();
856+
if (BAI->getEnforcement() == SILAccessEnforcement::Unsafe)
866857
return;
867858

868-
auto It =
869-
State.Accesses->find(findValidAccessedStorage(EAI->getSource()));
859+
const AccessedStorage &Storage = identifyFormalAccess(BAI);
860+
assert(Storage && "unidentified formal access");
861+
auto It = State.Accesses->find(identifyFormalAccess(BAI));
870862
AccessInfo &Info = It->getSecond();
871863

872-
BeginAccessInst *BAI = EAI->getBeginAccess();
873864
const IndexTrieNode *SubPath = State.ASA->findSubPathAccessed(BAI);
874865
Info.endAccess(EAI, SubPath);
875866

@@ -973,7 +964,7 @@ static void checkStaticExclusivity(SILFunction &Fn, PostOrderFunctionInfo *PO,
973964
// Check that the given address-type operand is guarded by begin/end access
974965
// markers.
975966
static void checkAccessedAddress(Operand *memOper, StorageMap &Accesses) {
976-
SILValue address = memOper->get();
967+
SILValue address = getAddressAccess(memOper->get());
977968
SILInstruction *memInst = memOper->getUser();
978969

979970
auto error = [address, memInst]() {
@@ -985,6 +976,21 @@ static void checkAccessedAddress(Operand *memOper, StorageMap &Accesses) {
985976
abort();
986977
};
987978

979+
// Check if this address is guarded by an access.
980+
if (auto *BAI = dyn_cast<BeginAccessInst>(address)) {
981+
if (BAI->getEnforcement() == SILAccessEnforcement::Unsafe)
982+
return;
983+
984+
const AccessedStorage &Storage = identifyFormalAccess(BAI);
985+
assert(Storage && "unidentified formal access");
986+
AccessInfo &Info = Accesses[Storage];
987+
if (Info.hasAccessesInProgress())
988+
return;
989+
990+
error();
991+
}
992+
// --- This address is not guarded by a begin_access.
993+
988994
// If the memory instruction is only used for initialization, it doesn't need
989995
// an access marker.
990996
if (memInstMustInitialize(memOper))
@@ -1011,7 +1017,6 @@ static void checkAccessedAddress(Operand *memOper, StorageMap &Accesses) {
10111017
return;
10121018
}
10131019

1014-
// Strip off address projections, but not ref_element_addr.
10151020
const AccessedStorage &storage = findAccessedStorage(address);
10161021
// findAccessedStorage may return an invalid storage object if the address
10171022
// producer is not recognized by its whitelist. For the purpose of
@@ -1040,18 +1045,6 @@ static void checkAccessedAddress(Operand *memOper, StorageMap &Accesses) {
10401045
return;
10411046
}
10421047

1043-
// Otherwise, the address base should be an in-scope begin_access.
1044-
if (storage.getKind() == AccessedStorage::Nested) {
1045-
auto *BAI = cast<BeginAccessInst>(storage.getValue());
1046-
if (BAI->getEnforcement() == SILAccessEnforcement::Unsafe)
1047-
return;
1048-
1049-
const AccessedStorage &Storage = findValidAccessedStorage(BAI->getSource());
1050-
AccessInfo &Info = Accesses[Storage];
1051-
if (!Info.hasAccessesInProgress())
1052-
error();
1053-
return;
1054-
}
10551048
error();
10561049
}
10571050

0 commit comments

Comments
 (0)