Skip to content

Commit 2887187

Browse files
committed
[move-function-addr] When inserting compensating destroy_addr, insert debug_value undef before the destroy_addr.
Otherwise in async contexts we may propagate forward an original non-invalidated debug_value created a use of the invalidated memory. This then causes the MemoryLifetimeVerifier to trigger. I also discovered via this test case that we were not ignoring end_access as a liveness use when visiting non-closure uses. With this change we ignore it now. That being said, the reason why the end_access has not been an issue previously is that due to the hacky way that the move builtin lowers, we actually create the move outside of the relevant access scope (which even worse is a read!). I am going to fix that in a different PR though.
1 parent 2e54880 commit 2887187

File tree

2 files changed

+104
-0
lines changed

2 files changed

+104
-0
lines changed

lib/SILOptimizer/Mandatory/MoveKillsCopyableAddressesChecker.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1363,6 +1363,10 @@ bool GatherLexicalLifetimeUseVisitor::visitUse(Operand *op,
13631363
if (isa<DeallocStackInst>(op->getUser()))
13641364
return true;
13651365

1366+
// Ignore end_access.
1367+
if (isa<EndAccessInst>(op->getUser()))
1368+
return true;
1369+
13661370
LLVM_DEBUG(llvm::dbgs() << "Found liveness use: " << *op->getUser());
13671371
useState.livenessUses.insert(op->getUser());
13681372

@@ -1507,6 +1511,24 @@ bool DataflowState::cleanupAllDestroyAddr(
15071511
SILBuilderWithScope builder(iter);
15081512
auto *dvi = builder.createDestroyAddr(
15091513
RegularLocation::getAutoGeneratedLocation(), address);
1514+
// Create a debug_value undef if we have debug info to stop the async dbg
1515+
// info propagation from creating debug info for an already destroyed
1516+
// value. We use a separate builder since we need to control the debug
1517+
// scope/location to get llvm to do the right thing.
1518+
if (addressDebugInst) {
1519+
if (auto varInfo = addressDebugInst.getVarInfo()) {
1520+
// We need to always insert /after/ the reinit since the value will
1521+
// not be defined before the value.
1522+
SILBuilderWithScope dbgValueInsertBuilder(dvi);
1523+
dbgValueInsertBuilder.setCurrentDebugScope(
1524+
addressDebugInst->getDebugScope());
1525+
dbgValueInsertBuilder.createDebugValue(
1526+
addressDebugInst.inst->getLoc(),
1527+
SILUndef::get(address->getType(), dvi->getModule()), *varInfo,
1528+
false,
1529+
/*was moved*/ true);
1530+
}
1531+
}
15101532
useState.destroys.insert(dvi);
15111533
continue;
15121534
}

test/SILOptimizer/move_function_kills_addresses_dbginfo.sil

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,28 @@
55
sil_stage raw
66

77
import Builtin
8+
import Swift
9+
10+
//////////////////
11+
// Declarations //
12+
//////////////////
13+
14+
public protocol P {
15+
static var value: P { get }
16+
func doSomething()
17+
}
18+
19+
sil @forceSplit : $@convention(thin) @async () -> ()
20+
sil @genericUse : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> ()
21+
22+
enum Optional<T> {
23+
case some(T)
24+
case none
25+
}
26+
27+
///////////
28+
// Tests //
29+
///////////
830

931
// Make sure that when we process a move_addr on an alloc_stack/debug_value that
1032
// we properly put the [moved] marker on them.
@@ -71,3 +93,63 @@ bb3:
7193
%9999 = tuple()
7294
return %9999 : $()
7395
}
96+
97+
// Make sure that we insert a debug_value undef after the destroy_addr that we
98+
// place in block bb2.
99+
// CHECK-LABEL: sil [ossa] @dbg_undef_in_inserted_destroy_addr : $@convention(thin) @async <T where T : P> (@inout T) -> () {
100+
// CHECK: bb2:
101+
// CHECK-NEXT: // function_ref forceSplit
102+
// CHECK-NEXT: function_ref @forceSplit
103+
// CHECK-NEXT: apply
104+
// CHECK-NEXT: debug_value [moved] undef
105+
// CHECK-NEXT: destroy_addr
106+
// CHECK-NEXT: hop_to_executor
107+
// CHECK-NEXT: br bb3
108+
sil [ossa] @dbg_undef_in_inserted_destroy_addr : $@convention(thin) @async <T where T : P> (@inout T) -> () {
109+
bb0(%0 : $*T):
110+
debug_value %0 : $*T, var, name "msg", argno 1, expr op_deref
111+
%2 = enum $Optional<Builtin.Executor>, #Optional.none!enumelt
112+
hop_to_executor %2 : $Optional<Builtin.Executor>
113+
%4 = function_ref @forceSplit : $@convention(thin) @async () -> ()
114+
%5 = apply %4() : $@convention(thin) @async () -> ()
115+
hop_to_executor %2 : $Optional<Builtin.Executor>
116+
cond_br undef, bb1, bb2
117+
118+
bb1:
119+
%11 = alloc_stack $T
120+
%12 = begin_access [modify] [static] %0 : $*T
121+
mark_unresolved_move_addr %12 to %11 : $*T
122+
end_access %12 : $*T
123+
%21 = function_ref @genericUse : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> ()
124+
%22 = apply %21<T>(%11) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> ()
125+
destroy_addr %11 : $*T
126+
dealloc_stack %11 : $*T
127+
%25 = function_ref @forceSplit : $@convention(thin) @async () -> ()
128+
%26 = apply %25() : $@convention(thin) @async () -> ()
129+
hop_to_executor %2 : $Optional<Builtin.Executor>
130+
br bb3
131+
132+
bb2:
133+
%29 = function_ref @forceSplit : $@convention(thin) @async () -> ()
134+
%30 = apply %29() : $@convention(thin) @async () -> ()
135+
hop_to_executor %2 : $Optional<Builtin.Executor>
136+
br bb3
137+
138+
bb3:
139+
%33 = alloc_stack $P
140+
%34 = metatype $@thick T.Type
141+
%35 = witness_method $T, #P.value!getter : <Self where Self : P> (Self.Type) -> () -> P : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@thick τ_0_0.Type) -> @out P
142+
%36 = apply %35<T>(%33, %34) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@thick τ_0_0.Type) -> @out P
143+
%37 = alloc_stack $T
144+
unconditional_checked_cast_addr P in %33 : $*P to T in %37 : $*T
145+
%39 = begin_access [modify] [static] %0 : $*T
146+
copy_addr [take] %37 to %39 : $*T
147+
end_access %39 : $*T
148+
dealloc_stack %37 : $*T
149+
dealloc_stack %33 : $*P
150+
%44 = function_ref @forceSplit : $@convention(thin) @async () -> ()
151+
%45 = apply %44() : $@convention(thin) @async () -> ()
152+
hop_to_executor %2 : $Optional<Builtin.Executor>
153+
%47 = tuple ()
154+
return %47 : $()
155+
}

0 commit comments

Comments
 (0)