Closed nate-chandler closed 3 weeks ago
So, apparently the following function type is considered as non-refcounted:
$@differentiable(reverse) @noescape @callee_guaranteed @substituted <τ_0_0> (@inout τ_0_0) -> () for <Double>
Still, we're doing retains / release on values of this type.
So, things are interesting. Here is the code at some point of sil-combine:
sil shared @$s4main17valueWithPullback2at2ofyt0B0_y13TangentVectorQzzc8pullbacktx_yxzYjrXEt16_Differentiation14DifferentiableRzlFSd_Tg5 : $@convention(thin) (Double, @guaranteed @differentiable(reverse) @noescape @callee_guaranteed @substituted <τ_0_0> (@inout τ_0_0) -> () for <Double>) -> @owned @callee_guaranteed @substituted <τ_0_0> (@inout τ_0_0) -> () for <Double> {
// %0 "x" // users: %15, %2
// %1 "f" // users: %10, %8, %5, %3
bb0(%0 : $Double, %1 : $@differentiable(reverse) @noescape @callee_guaranteed @substituted <τ_0_0> (@inout τ_0_0) -> () for <Double>):
debug_value %0 : $Double, let, name "x", argno 1 // id: %2
debug_value %1 : $@differentiable(reverse) @noescape @callee_guaranteed @substituted <τ_0_0> (@inout τ_0_0) -> () for <Double>, let, name "f", argno 2 // id: %3
// function_ref specialized nonInoutWrappingFunction #1 <A>(_:) in valueWithPullback<A>(at:of:)
%4 = function_ref @$s4main17valueWithPullback2at2ofyt0B0_y13TangentVectorQzzc8pullbacktx_yxzYjrXEt16_Differentiation14DifferentiableRzlF24nonInoutWrappingFunctionL_yxxAiJRzlFSd_TG5 : $@convention(thin) (@in_guaranteed Double, @guaranteed @differentiable(reverse) @noescape @callee_guaranteed @substituted <τ_0_0> (@inout τ_0_0) -> () for <Double>) -> @out Double // user: %5
%5 = partial_apply [callee_guaranteed] %4(%1) : $@convention(thin) (@in_guaranteed Double, @guaranteed @differentiable(reverse) @noescape @callee_guaranteed @substituted <τ_0_0> (@inout τ_0_0) -> () for <Double>) -> @out Double // user: %6
%6 = convert_function %5 : $@callee_guaranteed (@in_guaranteed Double) -> @out Double to $@callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <Double, Double> // users: %22, %11
// function_ref specialized forward-mode derivative of nonInoutWrappingFunction #1 <A>(_:) in valueWithPullback<A>(at:of:)
%7 = function_ref @$s4main17valueWithPullback2at2ofyt0B0_y13TangentVectorQzzc8pullbacktx_yxzYjrXEt16_Differentiation14DifferentiableRzlF24nonInoutWrappingFunctionL_yxxAiJRzlFAiJRzlTJfSUpSrSd_TG5 : $@convention(thin) (@in_guaranteed Double, @guaranteed @differentiable(reverse) @noescape @callee_guaranteed @substituted <τ_0_0> (@inout τ_0_0) -> () for <Double>) -> (@out Double, @owned @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <Double, Double>) // user: %8
%8 = partial_apply [callee_guaranteed] %7(%1) : $@convention(thin) (@in_guaranteed Double, @guaranteed @differentiable(reverse) @noescape @callee_guaranteed @substituted <τ_0_0> (@inout τ_0_0) -> () for <Double>) -> (@out Double, @owned @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <Double, Double>) // users: %12, %23
// function_ref specialized reverse-mode derivative of nonInoutWrappingFunction #1 <A>(_:) in valueWithPullback<A>(at:of:)
%9 = function_ref @$s4main17valueWithPullback2at2ofyt0B0_y13TangentVectorQzzc8pullbacktx_yxzYjrXEt16_Differentiation14DifferentiableRzlF24nonInoutWrappingFunctionL_yxxAiJRzlFAiJRzlTJrSUpSrSd_TG5 : $@convention(thin) (@in_guaranteed Double, @guaranteed @differentiable(reverse) @noescape @callee_guaranteed @substituted <τ_0_0> (@inout τ_0_0) -> () for <Double>) -> (@out Double, @owned @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <Double, Double>) // user: %10
%10 = partial_apply [callee_guaranteed] %9(%1) : $@convention(thin) (@in_guaranteed Double, @guaranteed @differentiable(reverse) @noescape @callee_guaranteed @substituted <τ_0_0> (@inout τ_0_0) -> () for <Double>) -> (@out Double, @owned @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <Double, Double>) // users: %13, %24
%11 = convert_escape_to_noescape %6 : $@callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <Double, Double> to $@noescape @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <Double, Double> // user: %16
%12 = convert_escape_to_noescape %8 : $@callee_guaranteed (@in_guaranteed Double) -> (@out Double, @owned @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <Double, Double>) to $@noescape @callee_guaranteed (@in_guaranteed Double) -> (@out Double, @owned @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <Double, Double>) // user: %17
%13 = convert_escape_to_noescape %10 : $@callee_guaranteed (@in_guaranteed Double) -> (@out Double, @owned @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <Double, Double>) to $@noescape @callee_guaranteed (@in_guaranteed Double) -> (@out Double, @owned @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <Double, Double>) // user: %19
%14 = alloc_stack $Double // users: %21, %19, %15
store %0 to %14 : $*Double // id: %15
%16 = convert_function %11 : $@noescape @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <Double, Double> to $@noescape @callee_guaranteed (@in_guaranteed Double) -> @out Double
%17 = convert_function %12 : $@noescape @callee_guaranteed (@in_guaranteed Double) -> (@out Double, @owned @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <Double, Double>) to $@noescape @callee_guaranteed (@in_guaranteed Double) -> (@out Double, @owned @callee_guaranteed (@in_guaranteed Double) -> @out Double)
%18 = alloc_stack $Double // users: %20, %19
%19 = apply %13(%18, %14) : $@noescape @callee_guaranteed (@in_guaranteed Double) -> (@out Double, @owned @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <Double, Double>) // users: %27, %25
dealloc_stack %18 : $*Double // id: %20
dealloc_stack %14 : $*Double // id: %21
strong_release %6 : $@callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <Double, Double> // id: %22
strong_release %8 : $@callee_guaranteed (@in_guaranteed Double) -> (@out Double, @owned @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <Double, Double>) // id: %23
strong_release %10 : $@callee_guaranteed (@in_guaranteed Double) -> (@out Double, @owned @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <Double, Double>) // id: %24
debug_value %19 : $@callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <Double, Double>, let, name "nonInoutPullback" // id: %25
// function_ref specialized closure #1 in valueWithPullback<A>(at:of:)
%26 = function_ref @$s4main17valueWithPullback2at2ofyt0B0_y13TangentVectorQzzc8pullbacktx_yxzYjrXEt16_Differentiation14DifferentiableRzlFyAGzcfU_Sd_Tg5 : $@convention(thin) (@inout Double, @guaranteed @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <Double, Double>) -> () // user: %27
%27 = partial_apply [callee_guaranteed] %26(%19) : $@convention(thin) (@inout Double, @guaranteed @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <Double, Double>) -> () // user: %28
%28 = convert_function %27 : $@callee_guaranteed (@inout Double) -> () to $@callee_guaranteed @substituted <τ_0_0> (@inout τ_0_0) -> () for <Double> // user: %29
return %28 : $@callee_guaranteed @substituted <τ_0_0> (@inout τ_0_0) -> () for <Double> // id: %29
} // end sil function '$s4main17valueWithPullback2at2ofyt0B0_y13TangentVectorQzzc8pullbacktx_yxzYjrXEt16_Differentiation14DifferentiableRzlFSd_Tg5'
Now, notice that %5 = partial_apply [callee_guaranteed] %4(%1) : $@convention(thin) (@in_guaranteed Double, @guaranteed @differentiable(reverse) @noescape @callee_guaranteed @substituted <τ_0_0> (@inout τ_0_0) -> () for <Double>) -> @out Double // user: %6
has essentially only refcounting users. We capture %1
into closure %5
and then release this closure in the end.
Then sil-combine goes via tryDeleteDeadClosure
that in turn calls keepArgsOfPartialApplyAlive
. Here in the end we're calling swift::emitDestroyOperation
on the argument of a partial_apply
.
Here strong_release
is emitted if type is know to have reference semantics (i.e. hasReferenceSemantics
returns true). SIL verifier checks is argument is reference counted. (i.e. isReferenceCounted
returns true). differentiable functions are modelled as non-trivial aggregates, so they are not reference-counted scalars. Still hasReferenceSemantics
returns true for them as they are function types at AST level.
To me it seems that SIL verifier is correct and swift::emitDestroyOperation
is not. But maybe I'm missing something? It seems very strange that such issue was not found previously. But maybe it's just because differentiable functions are so special?
Tagging @rxwei
Description
A run of the test after applying the following diff
results in
Reproduction
Enable sil-verify-all in the test via, for example
Expected behavior
Valid SIL out of SILCombine
Environment
Recent main. Specifically
6fdaea514fd621f62a56afb4ff1ce39f8bf77b31
.Additional information
No response