Skip to content

Commit 191adb0

Browse files
authored
Merge pull request swiftlang#33017 from atrick/really-add-class-tail-storage
Add AccessedStorage::Tail access kind and remove more exclusivity checks.
2 parents 858fe6c + 6ecbeef commit 191adb0

File tree

13 files changed

+348
-36
lines changed

13 files changed

+348
-36
lines changed

include/swift/SIL/MemAccessUtils.h

Lines changed: 69 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,9 @@ namespace swift {
189189
/// the base of a formal access. It may be from a ref_tail_addr, undef, or some
190190
/// recognized memory initialization pattern. Unidentified valid storage cannot
191191
/// represent any arbitrary base address--it must at least been proven not to
192-
/// correspond to any class or global variable access.
192+
/// correspond to any class or global variable access, unless it's nested within
193+
/// another access to the same object. So, Unidentified can overlap with
194+
/// Class/Global access, but it cannot be the only formal access to that memory.
193195
///
194196
/// An *invalid* AccessedStorage object is Unidentified and associated with an
195197
/// invalid SILValue. This signals that analysis has failed to recognize an
@@ -219,6 +221,7 @@ class AccessedStorage {
219221
Stack,
220222
Global,
221223
Class,
224+
Tail,
222225
Argument,
223226
Yield,
224227
Nested,
@@ -228,10 +231,16 @@ class AccessedStorage {
228231

229232
static const char *getKindName(Kind k);
230233

231-
/// Directly create an AccessedStorage for class property access.
234+
// Give object tail storage a fake property index for convenience.
235+
static constexpr unsigned TailIndex = ~0U;
236+
237+
/// Directly create an AccessedStorage for class or tail property access.
232238
static AccessedStorage forClass(SILValue object, unsigned propertyIndex) {
233239
AccessedStorage storage;
234-
storage.initKind(Class, propertyIndex);
240+
if (propertyIndex == TailIndex)
241+
storage.initKind(Tail);
242+
else
243+
storage.initKind(Class, propertyIndex);
235244
storage.value = object;
236245
return storage;
237246
}
@@ -301,7 +310,8 @@ class AccessedStorage {
301310
union {
302311
// For non-class storage, 'value' is the access base. For class storage
303312
// 'value' is the object base, where the access base is the class' stored
304-
// property.
313+
// property. For tail storage 'value' is the object base and there is no
314+
// value for the access base.
305315
SILValue value;
306316
SILGlobalVariable *global;
307317
};
@@ -334,7 +344,7 @@ class AccessedStorage {
334344
}
335345

336346
SILValue getValue() const {
337-
assert(getKind() != Global && getKind() != Class);
347+
assert(getKind() != Global && getKind() != Class && getKind() != Tail);
338348
return value;
339349
}
340350

@@ -354,7 +364,7 @@ class AccessedStorage {
354364
}
355365

356366
SILValue getObject() const {
357-
assert(getKind() == Class);
367+
assert(getKind() == Class || getKind() == Tail);
358368
return value;
359369
}
360370
unsigned getPropertyIndex() const {
@@ -374,6 +384,7 @@ class AccessedStorage {
374384
switch (getKind()) {
375385
case Box:
376386
case Stack:
387+
case Tail:
377388
case Argument:
378389
case Yield:
379390
case Nested:
@@ -396,6 +407,7 @@ class AccessedStorage {
396407
return true;
397408
case Global:
398409
case Class:
410+
case Tail:
399411
case Argument:
400412
case Yield:
401413
case Nested:
@@ -405,6 +417,11 @@ class AccessedStorage {
405417
llvm_unreachable("unhandled kind");
406418
}
407419

420+
/// Return trye if the given access is guaranteed to be within a heap object.
421+
bool isObjectAccess() const {
422+
return getKind() == Class || getKind() == Tail;
423+
}
424+
408425
/// Return true if the given access is on a 'let' lvalue.
409426
bool isLetAccess(SILFunction *F) const;
410427

@@ -420,6 +437,7 @@ class AccessedStorage {
420437
case Global:
421438
return true;
422439
case Class:
440+
case Tail:
423441
case Argument:
424442
case Yield:
425443
case Nested:
@@ -455,20 +473,47 @@ class AccessedStorage {
455473
// Return true if this storage is guaranteed not to overlap with \p other's
456474
// storage.
457475
bool isDistinctFrom(const AccessedStorage &other) const {
458-
if (isUniquelyIdentified() && other.isUniquelyIdentified()) {
459-
return !hasIdenticalBase(other);
476+
if (isUniquelyIdentified()) {
477+
if (other.isUniquelyIdentified() && !hasIdenticalBase(other))
478+
return true;
479+
480+
if (other.isObjectAccess())
481+
return true;
482+
483+
// We currently assume that Unidentified storage may overlap with
484+
// Box/Stack storage.
485+
return false;
460486
}
461-
if (getKind() != Class || other.getKind() != Class)
462-
// At least one side is an Argument or Yield, or is unidentified.
487+
if (other.isUniquelyIdentified())
488+
return other.isDistinctFrom(*this);
489+
490+
// Neither storage is uniquely identified.
491+
if (isObjectAccess()) {
492+
if (other.isObjectAccess()) {
493+
// Property access cannot overlap with Tail access.
494+
if (getKind() != other.getKind())
495+
return true;
496+
497+
// We could also check if the object types are distinct, but that only
498+
// helps if we know the relationships between class types.
499+
return getKind() == Class
500+
&& getPropertyIndex() != other.getPropertyIndex();
501+
}
502+
// Any type of nested/argument address may be within the same object.
503+
//
504+
// We also currently assume Unidentified access may be within an object
505+
// purely to handle KeyPath accesses. The deriviation of the KeyPath
506+
// address must separately appear to be a Class access so that all Class
507+
// accesses are accounted for.
463508
return false;
464-
465-
// Classes are not uniquely identified by their base. However, if the
466-
// underling objects have identical types and distinct property indices then
467-
// they are distinct storage locations.
468-
if (getObject()->getType() == other.getObject()->getType()
469-
&& getPropertyIndex() != other.getPropertyIndex()) {
470-
return true;
471509
}
510+
if (other.isObjectAccess())
511+
return other.isDistinctFrom(*this);
512+
513+
// Neither storage is from a class or tail.
514+
//
515+
// Unidentified values may alias with each other or with any kind of
516+
// nested/argument access.
472517
return false;
473518
}
474519

@@ -530,10 +575,11 @@ template <> struct DenseMapInfo<swift::AccessedStorage> {
530575
return storage.getParamIndex();
531576
case swift::AccessedStorage::Global:
532577
return DenseMapInfo<void *>::getHashValue(storage.getGlobal());
533-
case swift::AccessedStorage::Class: {
578+
case swift::AccessedStorage::Class:
534579
return llvm::hash_combine(storage.getObject(),
535580
storage.getPropertyIndex());
536-
}
581+
case swift::AccessedStorage::Tail:
582+
return DenseMapInfo<swift::SILValue>::getHashValue(storage.getObject());
537583
}
538584
llvm_unreachable("Unhandled AccessedStorageKind");
539585
}
@@ -673,6 +719,9 @@ class AccessUseDefChainVisitor {
673719
Result visitClassAccess(RefElementAddrInst *field) {
674720
return asImpl().visitBase(field, AccessedStorage::Class);
675721
}
722+
Result visitTailAccess(RefTailAddrInst *tail) {
723+
return asImpl().visitBase(tail, AccessedStorage::Tail);
724+
}
676725
Result visitArgumentAccess(SILFunctionArgument *arg) {
677726
return asImpl().visitBase(arg, AccessedStorage::Argument);
678727
}
@@ -808,7 +857,7 @@ Result AccessUseDefChainVisitor<Impl, Result>::visit(SILValue sourceAddr) {
808857
// This is a valid address producer for nested @inout argument
809858
// access, but it is never used for formal access of identified objects.
810859
case ValueKind::RefTailAddrInst:
811-
return asImpl().visitUnidentified(sourceAddr);
860+
return asImpl().visitTailAccess(cast<RefTailAddrInst>(sourceAddr));
812861

813862
// Inductive single-operand cases:
814863
// Look through address casts to find the source address.

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,10 @@ PASS(CrossModuleSerializationSetup, "cross-module-serialization-setup",
6868
"Setup serialization flags for cross-module optimization")
6969
PASS(AccessSummaryDumper, "access-summary-dump",
7070
"Dump Address Parameter Access Summary")
71+
PASS(AccessedStorageAnalysisDumper, "accessed-storage-analysis-dump",
72+
"Dump Accessed Storage Analysis Summaries")
7173
PASS(AccessedStorageDumper, "accessed-storage-dump",
72-
"Dump Accessed Storage Summary")
74+
"Dump Accessed Storage Information")
7375
PASS(AccessMarkerElimination, "access-marker-elim",
7476
"Access Marker Elimination.")
7577
PASS(AddressLowering, "address-lowering",

lib/SIL/Utils/MemAccessUtils.cpp

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,12 @@ AccessedStorage::AccessedStorage(SILValue base, Kind kind) {
119119
auto *REA = cast<RefElementAddrInst>(base);
120120
value = stripBorrow(REA->getOperand());
121121
setElementIndex(REA->getFieldNo());
122+
break;
123+
}
124+
case Tail: {
125+
auto *RTA = cast<RefTailAddrInst>(base);
126+
value = stripBorrow(RTA->getOperand());
127+
break;
122128
}
123129
}
124130
}
@@ -152,6 +158,9 @@ const ValueDecl *AccessedStorage::getDecl() const {
152158
auto *decl = getObject()->getType().getNominalOrBoundGenericNominal();
153159
return decl->getStoredProperties()[getPropertyIndex()];
154160
}
161+
case Tail:
162+
return nullptr;
163+
155164
case Argument:
156165
return getArgument()->getDecl();
157166

@@ -185,11 +194,17 @@ const char *AccessedStorage::getKindName(AccessedStorage::Kind k) {
185194
return "Global";
186195
case Class:
187196
return "Class";
197+
case Tail:
198+
return "Tail";
188199
}
189200
llvm_unreachable("unhandled kind");
190201
}
191202

192203
void AccessedStorage::print(raw_ostream &os) const {
204+
if (!*this) {
205+
os << "INVALID\n";
206+
return;
207+
}
193208
os << getKindName(getKind()) << " ";
194209
switch (getKind()) {
195210
case Box:
@@ -211,6 +226,9 @@ void AccessedStorage::print(raw_ostream &os) const {
211226
getDecl()->print(os);
212227
os << " Index: " << getPropertyIndex() << "\n";
213228
break;
229+
case Tail:
230+
os << getObject();
231+
os << " Tail\n";
214232
}
215233
}
216234

@@ -246,10 +264,16 @@ class FindPhiStorageVisitor
246264
this->visit(pointerWorklist.pop_back_val());
247265
}
248266
// If a common path component was found, recursively look for the storage.
249-
if (commonDefinition && commonDefinition.getValue()) {
250-
auto storage = storageVisitor.findStorage(commonDefinition.getValue());
251-
(void)storage; // The same storageVisitor called us. It has already
252-
// recorded the storage that it found.
267+
if (commonDefinition) {
268+
if (commonDefinition.getValue()) {
269+
auto storage = storageVisitor.findStorage(commonDefinition.getValue());
270+
(void)storage; // The same storageVisitor called us. It has already
271+
// recorded the storage that it found.
272+
} else {
273+
// If divergent paths were found, invalidate any previously discovered
274+
// storage.
275+
storageVisitor.setStorage(AccessedStorage());
276+
}
253277
}
254278
}
255279

@@ -568,6 +592,9 @@ bool swift::isPossibleFormalAccessBase(const AccessedStorage &storage,
568592
break;
569593
case AccessedStorage::Class:
570594
break;
595+
case AccessedStorage::Tail:
596+
return false;
597+
571598
case AccessedStorage::Yield:
572599
// Yields are accessed by the caller.
573600
return false;

lib/SIL/Verifier/LoadBorrowInvalidationChecker.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,11 @@ bool LoadBorrowNeverInvalidatedAnalysis::isNeverInvalidated(
486486
// validate that all uses of the project_box are not writes that overlap with
487487
// our load_borrow's result. These are things that may not be a formal access
488488
// base.
489+
//
490+
// FIXME: we don't seem to verify anywhere that a pointer_to_address cannot
491+
// itself be derived from another address that is accessible in the same
492+
// function, either because it was returned from a call or directly
493+
// address_to_pointer'd.
489494
if (isa<PointerToAddressInst>(address)) {
490495
return doesAddressHaveWriteThatInvalidatesLoadBorrow(lbi, endBorrowUses,
491496
address);
@@ -525,6 +530,9 @@ bool LoadBorrowNeverInvalidatedAnalysis::isNeverInvalidated(
525530
}
526531
case AccessedStorage::Yield: {
527532
// For now, do this. Otherwise, check for in_guaranteed, etc.
533+
//
534+
// FIXME: The yielded address could overlap with another address in this
535+
// function.
528536
return true;
529537
}
530538
case AccessedStorage::Box: {
@@ -534,9 +542,18 @@ bool LoadBorrowNeverInvalidatedAnalysis::isNeverInvalidated(
534542
case AccessedStorage::Class: {
535543
// Check that the write to the class's memory doesn't overlap with our
536544
// load_borrow.
545+
//
546+
// FIXME: how do we know that other projections of the same object don't
547+
// occur within the same function?
537548
return doesAddressHaveWriteThatInvalidatesLoadBorrow(lbi, endBorrowUses,
538549
address);
539550
}
551+
case AccessedStorage::Tail: {
552+
// This should be as strong as the Class address case, but to handle it we
553+
// need to find all aliases of the object and all tail projections within
554+
// that object.
555+
return false;
556+
}
540557
case AccessedStorage::Global: {
541558
// Check that the write to the class's memory doesn't overlap with our
542559
// load_borrow.

lib/SILOptimizer/Analysis/AccessedStorageAnalysis.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,14 +230,17 @@ transformCalleeStorage(const StorageAccessInfo &storage,
230230
case AccessedStorage::Global:
231231
// Global accesses is universal.
232232
return storage;
233-
case AccessedStorage::Class: {
233+
case AccessedStorage::Class:
234+
case AccessedStorage::Tail: {
234235
// If the object's value is an argument, translate it into a value on the
235236
// caller side.
236237
SILValue obj = storage.getObject();
237238
if (auto *arg = dyn_cast<SILFunctionArgument>(obj)) {
238239
SILValue argVal = getCallerArg(fullApply, arg->getIndex());
239240
if (argVal) {
240-
unsigned idx = storage.getPropertyIndex();
241+
unsigned idx = (storage.getKind() == AccessedStorage::Class)
242+
? storage.getPropertyIndex()
243+
: AccessedStorage::TailIndex;
241244
// Remap this storage info. The argument source value is now the new
242245
// object. The old storage info is inherited.
243246
return StorageAccessInfo(AccessedStorage::forClass(argVal, idx),

lib/SILOptimizer/Transforms/AccessEnforcementOpts.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,7 @@ void AccessConflictAndMergeAnalysis::visitMayRelease(SILInstruction *instr,
743743
// accesses can be affected by a deinitializer.
744744
auto isHeapAccess = [](AccessedStorage::Kind accessKind) {
745745
return accessKind == AccessedStorage::Class
746+
|| accessKind == AccessedStorage::Class
746747
|| accessKind == AccessedStorage::Global;
747748
};
748749
// Mark the in-scope accesses as having a nested conflict

lib/SILOptimizer/Transforms/AccessEnforcementWMO.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ const VarDecl *getDisjointAccessLocation(const AccessedStorage &storage) {
8585
}
8686
case AccessedStorage::Box:
8787
case AccessedStorage::Stack:
88+
case AccessedStorage::Tail:
8889
case AccessedStorage::Argument:
8990
case AccessedStorage::Yield:
9091
case AccessedStorage::Unidentified:

0 commit comments

Comments
 (0)