@@ -77,33 +77,52 @@ namespace swift {
77
77
78
78
// / Get the base address of a formal access by stripping access markers.
79
79
// /
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
+ }
83
88
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
86
90
// / (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 ;
91
93
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.
96
117
// /
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.
100
119
// /
101
120
// / To get the base address of the formal access behind the access marker,
102
121
// / either call stripAccessMarkers() on the returned value, or call
103
122
// / getAccessedAddress() on \p v.
104
123
// /
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.
107
126
SILValue getAddressAccess (SILValue v);
108
127
109
128
// / Convenience for stripAccessMarkers(getAddressAccess(v)).
@@ -324,9 +343,9 @@ class AccessedStorage {
324
343
return getElementIndex ();
325
344
}
326
345
327
- SILArgument *getArgument () const {
346
+ SILFunctionArgument *getArgument () const {
328
347
assert (getKind () == Argument);
329
- return cast<SILArgument >(value);
348
+ return cast<SILFunctionArgument >(value);
330
349
}
331
350
332
351
SILGlobalVariable *getGlobal () const {
@@ -386,6 +405,7 @@ class AccessedStorage {
386
405
llvm_unreachable (" unhandled kind" );
387
406
}
388
407
408
+ // / Return true if the given access is on a 'let' lvalue.
389
409
bool isLetAccess (SILFunction *F) const ;
390
410
391
411
// / If this is a uniquely identified formal access, then it cannot
@@ -409,12 +429,31 @@ class AccessedStorage {
409
429
llvm_unreachable (" unhandled kind" );
410
430
}
411
431
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 {
413
435
if (isUniquelyIdentified ())
414
436
return true ;
415
- return (getKind () == Class);
437
+
438
+ return getKind () == Argument
439
+ && getArgument ()
440
+ ->getArgumentConvention ()
441
+ .isExclusiveIndirectParameter ();
416
442
}
417
443
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.
418
457
bool isDistinctFrom (const AccessedStorage &other) const {
419
458
if (isUniquelyIdentified () && other.isUniquelyIdentified ()) {
420
459
return !hasIdenticalBase (other);
@@ -507,37 +546,50 @@ template <> struct DenseMapInfo<swift::AccessedStorage> {
507
546
508
547
namespace swift {
509
548
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.
517
552
// /
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.
526
555
AccessedStorage findAccessedStorage (SILValue sourceAddr);
527
556
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.
531
566
// /
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
+ }
536
588
537
589
} // end namespace swift
538
590
539
591
// ===----------------------------------------------------------------------===//
540
- // MARK: Helper API
592
+ // MARK: Helper API for specific formal access patterns
541
593
// ===----------------------------------------------------------------------===//
542
594
543
595
namespace swift {
@@ -583,7 +635,7 @@ void checkSwitchEnumBlockArg(SILPhiArgument *arg);
583
635
// /
584
636
// / If this returns false, then the address can be safely accessed without
585
637
// / a begin_access marker. To determine whether to emit begin_access:
586
- // / storage = findAccessedStorage (address)
638
+ // / storage = identifyFormalAccess (address)
587
639
// / needsAccessMarker = storage && isPossibleFormalAccessBase(storage)
588
640
// /
589
641
// / Warning: This is only valid for SIL with well-formed accesses. For example,
0 commit comments