Skip to content

Commit 8fec025

Browse files
authored
Merge pull request #17623 from atrick/accessverify-fix
Fixes for access marker verification.
2 parents 1a8a70e + 6ed43af commit 8fec025

19 files changed

+207
-77
lines changed

include/swift/SIL/MemAccessUtils.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,8 @@ AccessedStorage findAccessedStorage(SILValue sourceAddr);
357357
/// through any Nested access to find the original storage.
358358
///
359359
/// This is identical to findAccessedStorage(), but never returns Nested
360-
/// storage.
360+
/// storage and may return invalid storage for nested access when the outer
361+
/// access has Unsafe enforcement.
361362
AccessedStorage findAccessedStorageNonNested(SILValue sourceAddr);
362363

363364
/// Return true if the given address operand is used by a memory operation that
@@ -368,6 +369,8 @@ bool memInstMustInitialize(Operand *memOper);
368369
/// Return true if the given address producer may be the source of a formal
369370
/// access (a read or write of a potentially aliased, user visible variable).
370371
///
372+
/// `storage` must be a valid AccessedStorage object.
373+
///
371374
/// If this returns false, then the address can be safely accessed without
372375
/// a begin_access marker. To determine whether to emit begin_access:
373376
/// storage = findAccessedStorage(address)

lib/SIL/SILVerifier.cpp

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1625,12 +1625,9 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
16251625
// Unsafe enforcement is used for some unrecognizable access patterns,
16261626
// like debugger variables. The compiler never cares about the source of
16271627
// those accesses.
1628-
findAccessedStorage(BAI->getSource());
1629-
// FIXME: Also require that a valid storage location is returned.
1630-
//
1631-
// AccessedStorage storage = findAccessedStorage(BAI->getSource());
1632-
// if (BAI->getEnforcement() != SILAccessEnforcement::Unsafe)
1633-
// require(storage, "Unknown formal access pattern");
1628+
AccessedStorage storage = findAccessedStorage(BAI->getSource());
1629+
if (BAI->getEnforcement() != SILAccessEnforcement::Unsafe)
1630+
require(storage, "Unknown formal access pattern");
16341631
}
16351632

16361633
void checkEndAccessInst(EndAccessInst *EAI) {
@@ -1667,15 +1664,12 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
16671664
}
16681665

16691666
// First check that findAccessedStorage never asserts.
1670-
findAccessedStorage(BUAI->getSource());
1671-
// FIXME: Also require that a valid storage location is returned.
1672-
//
1667+
AccessedStorage storage = findAccessedStorage(BUAI->getSource());
16731668
// Only allow Unsafe and Builtin access to have invalid storage.
1674-
// AccessedStorage storage = findAccessedStorage(BAI->getSource());
1675-
// if (BUAI->getEnforcement() != SILAccessEnforcement::Unsafe
1676-
// && !BUAI->isFromBuiltin()) {
1677-
// require(storage, "Unknown formal access pattern");
1678-
// }
1669+
if (BUAI->getEnforcement() != SILAccessEnforcement::Unsafe
1670+
&& !BUAI->isFromBuiltin()) {
1671+
require(storage, "Unknown formal access pattern");
1672+
}
16791673
}
16801674

16811675
void checkEndUnpairedAccessInst(EndUnpairedAccessInst *I) {

lib/SILGen/SILGenApply.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5298,7 +5298,7 @@ emitMaterializeForSetAccessor(SILLocation loc, SILDeclRef materializeForSet,
52985298
if (auto genericFnType = dyn_cast<GenericFunctionType>(origAccessType))
52995299
genericSig = genericFnType.getGenericSignature();
53005300

5301-
return MaterializedLValue(ManagedValue::forUnmanaged(address),
5301+
return MaterializedLValue(ManagedValue::forLValue(address),
53025302
origSelfType, genericSig,
53035303
optionalCallback, callbackStorage);
53045304
}

lib/SILGen/SILGenLValue.cpp

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,15 @@ static SILValue enterAccessScope(SILGenFunction &SGF, SILLocation loc,
490490
return addr;
491491
}
492492

493+
static ManagedValue enterAccessScope(SILGenFunction &SGF, SILLocation loc,
494+
ManagedValue addr, LValueTypeData typeData,
495+
AccessKind accessKind,
496+
SILAccessEnforcement enforcement) {
497+
return ManagedValue::forLValue(
498+
enterAccessScope(SGF, loc, addr.getLValueAddress(), typeData,
499+
accessKind, enforcement));
500+
}
501+
493502
// Find the base of the formal access at `address`. If the base requires an
494503
// access marker, then create a begin_access on `address`. Return the
495504
// address to be used for the access.
@@ -503,11 +512,8 @@ SILValue UnenforcedAccess::beginAccess(SILGenFunction &SGF, SILLocation loc,
503512
return address;
504513

505514
const AccessedStorage &storage = findAccessedStorage(address);
506-
if (!storage) {
507-
llvm::dbgs() << "Bad memory access source: " << address;
508-
llvm_unreachable("Unexpected access source.");
509-
}
510-
if (!isPossibleFormalAccessBase(storage, &SGF.F))
515+
// Unsafe access may have invalid storage (e.g. a RawPointer).
516+
if (storage && !isPossibleFormalAccessBase(storage, &SGF.F))
511517
return address;
512518

513519
auto BAI =
@@ -1310,11 +1316,15 @@ namespace {
13101316
// of whether the base is trivial because even a trivial base
13111317
// may be value-dependent on something non-trivial.
13121318
if (base) {
1313-
SILValue temporary = materialized.temporary.getValue();
1314-
materialized.temporary = ManagedValue::forUnmanaged(
1319+
SILValue temporary = materialized.temporary.getLValueAddress();
1320+
materialized.temporary = ManagedValue::forLValue(
13151321
SGF.B.createMarkDependence(loc, temporary, base.getValue()));
13161322
}
13171323
}
1324+
// Enter an access scope for the temporary.
1325+
materialized.temporary =
1326+
enterAccessScope(SGF, loc, materialized.temporary, getTypeData(),
1327+
accessKind, SILAccessEnforcement::Unsafe);
13181328

13191329
// TODO: maybe needsWriteback should be a thin function pointer
13201330
// to which we pass the base? That would let us use direct
@@ -1628,31 +1638,38 @@ namespace {
16281638
IsSuper,
16291639
IsDirectAccessorUse, std::move(args.subscripts), SubstFieldType);
16301640
}
1641+
16311642
switch (cast<AccessorDecl>(addressor.getDecl())->getAddressorKind()) {
16321643
case AddressorKind::NotAddressor:
16331644
llvm_unreachable("not an addressor!");
16341645

16351646
// For unsafe addressors, we have no owner pointer to manage.
16361647
case AddressorKind::Unsafe:
16371648
assert(!result.second);
1638-
return result.first;
1649+
break;
16391650

16401651
// For owning addressors, we can just let the owner get released
16411652
// at an appropriate point.
16421653
case AddressorKind::Owning:
16431654
case AddressorKind::NativeOwning:
1644-
return result.first;
1655+
break;
16451656

16461657
// For pinning addressors, we have to push a writeback.
16471658
case AddressorKind::NativePinning: {
16481659
std::unique_ptr<LogicalPathComponent>
16491660
component(new UnpinPseudoComponent(getTypeData()));
16501661
pushWriteback(SGF, loc, std::move(component), result.second,
16511662
MaterializedLValue());
1652-
return result.first;
1663+
break;
16531664
}
16541665
}
1655-
llvm_unreachable("bad addressor kind");
1666+
1667+
// Enter an unsafe access scope for the access.
1668+
auto addr = result.first;
1669+
addr = enterAccessScope(SGF, loc, addr, getTypeData(), accessKind,
1670+
SILAccessEnforcement::Unsafe);
1671+
1672+
return addr;
16561673
}
16571674

16581675
void dump(raw_ostream &OS, unsigned indent) const override {

lib/SILOptimizer/Analysis/AccessedStorageAnalysis.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ bool FunctionAccessedStorage::mergeAccesses(
174174
for (auto &rawStorageInfo : otherStorageAccesses) {
175175
const StorageAccessInfo &otherStorageInfo =
176176
transformStorage(rawStorageInfo);
177-
// transformStorage() returns invalid storage object for local storage
177+
// If transformStorage() returns invalid storage object for local storage,
178178
// that should not be merged with the caller.
179179
if (!otherStorageInfo)
180180
continue;
@@ -272,7 +272,9 @@ transformCalleeStorage(const StorageAccessInfo &storage,
272272
SILValue argVal = getCallerArg(fullApply, storage.getParamIndex());
273273
if (argVal) {
274274
// Remap the argument source value and inherit the old storage info.
275-
return StorageAccessInfo(findAccessedStorageNonNested(argVal), storage);
275+
auto calleeStorage = findAccessedStorageNonNested(argVal);
276+
if (calleeStorage)
277+
return StorageAccessInfo(calleeStorage, storage);
276278
}
277279
// If the argument can't be transformed, demote it to an unidentified
278280
// access.

lib/SILOptimizer/IPO/GlobalOpt.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,10 @@ replaceLoadsByKnownValue(BuiltinInst *CallToOnce, SILFunction *AddrF,
678678
auto *GetterRef = B.createFunctionRef(Call->getLoc(), GetterF);
679679
auto *NewAI = B.createApply(Call->getLoc(), GetterRef, Args, false);
680680

681+
// FIXME: This is asserting that a specific SIL sequence follows an
682+
// addressor! SIL passes should never do this without first specifying a
683+
// structural SIL property independent of the SILOptimizer and enforced by
684+
// the SILVerifier.
681685
for (auto Use : Call->getUses()) {
682686
auto *PTAI = dyn_cast<PointerToAddressInst>(Use->getUser());
683687
assert(PTAI && "All uses should be pointer_to_address");

lib/SILOptimizer/Mandatory/AccessMarkerElimination.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@
3030
using namespace swift;
3131

3232
// This temporary option allows markers during optimization passes. Enabling
33-
// this flag causes this pass to preserve only dynamic checks when dynamic
34-
// checking is enabled. Otherwise, this pass removes all checks.
33+
// this flag causes this pass to preserve all access markers. Otherwise, it only
34+
// preserved "dynamic" markers.
3535
llvm::cl::opt<bool> EnableOptimizedAccessMarkers(
36-
"sil-optimized-access-markers", llvm::cl::init(true),
36+
"sil-optimized-access-markers", llvm::cl::init(false),
3737
llvm::cl::desc("Enable memory access markers during optimization passes."));
3838

3939
namespace {
@@ -70,8 +70,8 @@ struct AccessMarkerElimination {
7070

7171
bool AccessMarkerElimination::shouldPreserveAccess(
7272
SILAccessEnforcement enforcement) {
73-
if (!EnableOptimizedAccessMarkers)
74-
return false;
73+
if (EnableOptimizedAccessMarkers || Mod->getOptions().VerifyExclusivity)
74+
return true;
7575

7676
switch (enforcement) {
7777
case SILAccessEnforcement::Static:

lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,8 @@ static void checkCaptureAccess(ApplySite Apply, AccessState &State) {
734734
SILValue Argument = Apply.getArgument(ArgumentIndex);
735735
assert(Argument->getType().isAddress());
736736

737+
// A valid AccessedStorage should alway sbe found because Unsafe accesses
738+
// are not tracked by AccessSummaryAnalysis.
737739
const AccessedStorage &Storage = findValidAccessedStorage(Argument);
738740
auto AccessIt = State.Accesses->find(Storage);
739741

@@ -817,6 +819,9 @@ static void checkForViolationsAtInstruction(SILInstruction &I,
817819
SILAccessKind Kind = BAI->getAccessKind();
818820
const AccessedStorage &Storage =
819821
findValidAccessedStorage(BAI->getSource());
822+
// Storage may be associated with a nested access where the outer access is
823+
// "unsafe". That's ok because the outer access can itself be treated like a
824+
// valid source, as long as we don't ask for its source.
820825
AccessInfo &Info = (*State.Accesses)[Storage];
821826
const IndexTrieNode *SubPath = State.ASA->findSubPathAccessed(BAI);
822827
if (auto Conflict = shouldReportAccess(Info, Kind, SubPath)) {
@@ -1071,7 +1076,16 @@ static void checkAccessedAddress(Operand *memOper, StorageMap &Accesses) {
10711076
// initialization, not a formal memory access. The strength of
10721077
// verification rests on the completeness of the opcode list inside
10731078
// findAccessedStorage.
1074-
if (!storage || !isPossibleFormalAccessBase(storage, memInst->getFunction()))
1079+
//
1080+
// For the purpose of verification, an unidentified access is
1081+
// unenforced. These occur in cases like global addressors and local buffers
1082+
// that make use of RawPointers.
1083+
if (!storage || storage.getKind() == AccessedStorage::Unidentified)
1084+
return;
1085+
1086+
// Some identifiable addresses can also be recognized as local initialization
1087+
// or other patterns that don't qualify as formal access.
1088+
if (!isPossibleFormalAccessBase(storage, memInst->getFunction()))
10751089
return;
10761090

10771091
// A box or stack variable may represent lvalues, but they can only conflict

lib/SILOptimizer/Utils/Local.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1409,6 +1409,10 @@ swift::analyzeStaticInitializer(SILValue V,
14091409
/// The sequence is traversed inside out, i.e.
14101410
/// starting with the innermost struct_element_addr
14111411
/// Move into utils.
1412+
///
1413+
/// FIXME: this utility does not make sense as an API. How can the caller
1414+
/// guarantee that the only uses of `I` are struct_element_addr and
1415+
/// tuple_element_addr?
14121416
void swift::replaceLoadSequence(SILInstruction *I,
14131417
SILValue Value,
14141418
SILBuilder &B) {
@@ -1434,6 +1438,18 @@ void swift::replaceLoadSequence(SILInstruction *I,
14341438
return;
14351439
}
14361440

1441+
if (auto *BA = dyn_cast<BeginAccessInst>(I)) {
1442+
for (auto Use : BA->getUses()) {
1443+
replaceLoadSequence(Use->getUser(), Value, B);
1444+
}
1445+
return;
1446+
}
1447+
1448+
// Incidental uses of an addres are meaningless with regard to the loaded
1449+
// value.
1450+
if (isIncidentalUse(I) || isa<BeginUnpairedAccessInst>(I))
1451+
return;
1452+
14371453
llvm_unreachable("Unknown instruction sequence for reading from a global");
14381454
}
14391455

test/SILGen/access_marker_gen.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,5 +150,6 @@ func testDispatchedClassInstanceProperty(d: D) {
150150
// CHECK: bb0([[D:%.*]] : @guaranteed $D
151151
// CHECK: [[METHOD:%.*]] = class_method [[D]] : $D, #D.x!materializeForSet.1
152152
// CHECK: apply [[METHOD]]({{.*}}, [[D]])
153+
// CHECK: begin_access [modify] [unsafe]
153154
// CHECK-NOT: begin_access
154155

test/SILGen/accessors.swift

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,22 +58,24 @@ func test0(_ ref: A) {
5858
// CHECK-NEXT: [[OPT_CALLBACK:%.*]] = tuple_extract [[T2]] {{.*}}, 1
5959
// CHECK-NEXT: [[T4:%.*]] = pointer_to_address [[T3]]
6060
// CHECK-NEXT: [[ADDR:%.*]] = mark_dependence [[T4]] : $*OrdinarySub on [[ARG]] : $A
61+
// CHECK-NEXT: [[ACCESS:%.*]] = begin_access [modify] [unsafe] [[ADDR]] : $*OrdinarySub
6162
// CHECK-NEXT: // function_ref accessors.OrdinarySub.subscript.setter : (Swift.Int) -> Swift.Int
6263
// CHECK-NEXT: [[SETTER:%.*]] = function_ref @$S9accessors11OrdinarySubVyS2icis
63-
// CHECK-NEXT: apply [[SETTER]]([[VALUE]], [[INDEX0]], [[ADDR]])
64+
// CHECK-NEXT: apply [[SETTER]]([[VALUE]], [[INDEX0]], [[ACCESS]])
6465
// CHECK-NEXT: switch_enum [[OPT_CALLBACK]] : $Optional<Builtin.RawPointer>, case #Optional.some!enumelt.1: [[WRITEBACK:bb[0-9]+]], case #Optional.none!enumelt: [[CONT:bb[0-9]+]]
6566

6667
// CHECK: [[WRITEBACK]]([[CALLBACK_ADDR:%.*]] : @trivial $Builtin.RawPointer):
6768
// CHECK-NEXT: [[CALLBACK:%.*]] = pointer_to_thin_function [[CALLBACK_ADDR]] : $Builtin.RawPointer to $@convention(method) (Builtin.RawPointer, @inout Builtin.UnsafeValueBuffer, @in_guaranteed A, @thick A.Type) -> ()
6869
// CHECK-NEXT: [[TEMP2:%.*]] = alloc_stack $A
6970
// CHECK-NEXT: store_borrow [[ARG]] to [[TEMP2]] : $*A
7071
// CHECK-NEXT: [[T0:%.*]] = metatype $@thick A.Type
71-
// CHECK-NEXT: [[T1:%.*]] = address_to_pointer [[ADDR]] : $*OrdinarySub to $Builtin.RawPointer
72+
// CHECK-NEXT: [[T1:%.*]] = address_to_pointer [[ACCESS]] : $*OrdinarySub to $Builtin.RawPointer
7273
// CHECK-NEXT: apply [[CALLBACK]]([[T1]], [[STORAGE]], [[TEMP2]], [[T0]])
7374
// CHECK-NEXT: dealloc_stack [[TEMP2]]
7475
// CHECK-NEXT: br [[CONT]]
7576

7677
// CHECK: [[CONT]]:
78+
// CHECK-NEXT: end_access [[ACCESS]] : $*OrdinarySub
7779
// CHECK-NEXT: dealloc_stack [[BUFFER]]
7880
// CHECK-NEXT: dealloc_stack [[STORAGE]]
7981
// CHECK-NEXT: dealloc_stack [[TEMP]]
@@ -114,23 +116,25 @@ func test1(_ ref: B) {
114116
// CHECK-NEXT: [[OPT_CALLBACK:%.*]] = tuple_extract [[T2]] {{.*}}, 1
115117
// CHECK-NEXT: [[T4:%.*]] = pointer_to_address [[T3]]
116118
// CHECK-NEXT: [[ADDR:%.*]] = mark_dependence [[T4]] : $*MutatingSub on [[ARG]] : $B
119+
// CHECK-NEXT: [[ACCESS:%.*]] = begin_access [modify] [unsafe] [[ADDR]] : $*MutatingSub
117120
// CHECK-NEXT: // function_ref accessors.MutatingSub.subscript.getter : (Swift.Int) -> Swift.Int
118121
// CHECK-NEXT: [[T0:%.*]] = function_ref @$S9accessors11MutatingSubVyS2icig : $@convention(method) (Int, @inout MutatingSub) -> Int
119-
// CHECK-NEXT: [[VALUE:%.*]] = apply [[T0]]([[INDEX1]], [[ADDR]])
122+
// CHECK-NEXT: [[VALUE:%.*]] = apply [[T0]]([[INDEX1]], [[ACCESS]])
120123
// CHECK-NEXT: switch_enum [[OPT_CALLBACK]] : $Optional<Builtin.RawPointer>, case #Optional.some!enumelt.1: [[WRITEBACK:bb[0-9]+]], case #Optional.none!enumelt: [[CONT:bb[0-9]+]]
121124
//
122125
// CHECK: [[WRITEBACK]]([[CALLBACK_ADDR:%.*]] : @trivial $Builtin.RawPointer):
123126
// CHECK-NEXT: [[CALLBACK:%.*]] = pointer_to_thin_function [[CALLBACK_ADDR]] : $Builtin.RawPointer to $@convention(method) (Builtin.RawPointer, @inout Builtin.UnsafeValueBuffer, @in_guaranteed B, @thick B.Type) -> ()
124127
// CHECK-NEXT: [[TEMP2:%.*]] = alloc_stack $B
125128
// CHECK-NEXT: store_borrow [[ARG]] to [[TEMP2]] : $*B
126129
// CHECK-NEXT: [[T0:%.*]] = metatype $@thick B.Type
127-
// CHECK-NEXT: [[T1:%.*]] = address_to_pointer [[ADDR]] : $*MutatingSub to $Builtin.RawPointer
130+
// CHECK-NEXT: [[T1:%.*]] = address_to_pointer [[ACCESS]] : $*MutatingSub to $Builtin.RawPointer
128131
// CHECK-NEXT: apply [[CALLBACK]]([[T1]], [[STORAGE]], [[TEMP2]], [[T0]])
129132
// CHECK-NEXT: dealloc_stack [[TEMP2]]
130133
// CHECK-NEXT: br [[CONT]]
131134
//
132135
// CHECK: [[CONT]]:
133136
// Formal access to LHS.
137+
// CHECK-NEXT: end_access [[ACCESS]] : $*MutatingSub
134138
// CHECK-NEXT: [[STORAGE2:%.*]] = alloc_stack $Builtin.UnsafeValueBuffer
135139
// CHECK-NEXT: [[BUFFER2:%.*]] = alloc_stack $MutatingSub
136140
// CHECK-NEXT: [[T0:%.*]] = address_to_pointer [[BUFFER2]]
@@ -140,22 +144,24 @@ func test1(_ ref: B) {
140144
// CHECK-NEXT: [[OPT_CALLBACK:%.*]] = tuple_extract [[T2]] {{.*}}, 1
141145
// CHECK-NEXT: [[T4:%.*]] = pointer_to_address [[T3]]
142146
// CHECK-NEXT: [[ADDR:%.*]] = mark_dependence [[T4]] : $*MutatingSub on [[ARG]] : $B
147+
// CHECK-NEXT: [[ACCESS:%.*]] = begin_access [modify] [unsafe] [[ADDR]] : $*MutatingSub
143148
// CHECK-NEXT: // function_ref accessors.MutatingSub.subscript.setter : (Swift.Int) -> Swift.Int
144149
// CHECK-NEXT: [[SETTER:%.*]] = function_ref @$S9accessors11MutatingSubVyS2icis : $@convention(method) (Int, Int, @inout MutatingSub) -> ()
145-
// CHECK-NEXT: apply [[SETTER]]([[VALUE]], [[INDEX0]], [[ADDR]])
150+
// CHECK-NEXT: apply [[SETTER]]([[VALUE]], [[INDEX0]], [[ACCESS]])
146151
// CHECK-NEXT: switch_enum [[OPT_CALLBACK]] : $Optional<Builtin.RawPointer>, case #Optional.some!enumelt.1: [[WRITEBACK:bb[0-9]+]], case #Optional.none!enumelt: [[CONT:bb[0-9]+]]
147152
//
148153
// CHECK: [[WRITEBACK]]([[CALLBACK_ADDR:%.*]] : @trivial $Builtin.RawPointer):
149154
// CHECK-NEXT: [[CALLBACK:%.*]] = pointer_to_thin_function [[CALLBACK_ADDR]] : $Builtin.RawPointer to $@convention(method) (Builtin.RawPointer, @inout Builtin.UnsafeValueBuffer, @in_guaranteed B, @thick B.Type) -> ()
150155
// CHECK-NEXT: [[TEMP2:%.*]] = alloc_stack $B
151156
// CHECK-NEXT: store_borrow [[ARG]] to [[TEMP2]] : $*B
152157
// CHECK-NEXT: [[T0:%.*]] = metatype $@thick B.Type
153-
// CHECK-NEXT: [[T1:%.*]] = address_to_pointer [[ADDR]] : $*MutatingSub to $Builtin.RawPointer
158+
// CHECK-NEXT: [[T1:%.*]] = address_to_pointer [[ACCESS]] : $*MutatingSub to $Builtin.RawPointer
154159
// CHECK-NEXT: apply [[CALLBACK]]([[T1]], [[STORAGE2]], [[TEMP2]], [[T0]])
155160
// CHECK-NEXT: dealloc_stack [[TEMP2]]
156161
// CHECK-NEXT: br [[CONT]]
157162
//
158163
// CHECK: [[CONT]]:
164+
// CHECK-NEXT: end_access [[ACCESS]] : $*MutatingSub
159165
// CHECK-NEXT: dealloc_stack [[BUFFER2]]
160166
// CHECK-NEXT: dealloc_stack [[STORAGE2]]
161167
// CHECK-NEXT: dealloc_stack [[BUFFER]]

0 commit comments

Comments
 (0)