Open asl opened 8 months ago
@asl are you actively working on this? Otherwise I can take a look at it.
@asl are you actively working on this? Otherwise I can take a look at it.
Yes, as I mentioned today, I am working on this. Closure optimizations are much more important :)
So, this case is different and I'm afraid (see below) we are having lots of issues with same-type requirements in derivatives.
What happens is:
// reverse-mode derivative of f<A>(_:)
sil hidden [ossa] @$s4main1fyxxAA1PRzlFAA1DVRszlTJrSpSr : $@convention(thin) (@in_guaranteed D) -> (@out D, @owned @callee_guaranteed (@in_guaranteed D.TangentVector) -> @out D.TangentVector) {
// f<A>(_:)
sil hidden [ossa] @$s4main1fyxxAA1PRzlF : $@convention(thin) <T where T : P> (@in_guaranteed T) -> @out T {
// %0 "$return_value" // user: %10
// %1 "x" // users: %10, %5, %2
bb0(%0 : $*T, %1 : $*T):
debug_value %1 : $*T, let, name "x", argno 1, expr op_deref // id: %2
%3 = alloc_stack $any P // users: %9, %8, %7, %4
%4 = init_existential_addr %3 : $*any P, $T // user: %5
copy_addr %1 to [init] %4 : $*T // id: %5
// function_ref a(_:)
%6 = function_ref @$s4main1ayyAA1P_pF : $@convention(thin) (@in_guaranteed any P) -> () // user: %7
%7 = apply %6(%3) : $@convention(thin) (@in_guaranteed any P) -> ()
destroy_addr %3 : $*any P // id: %8
dealloc_stack %3 : $*any P // id: %9
copy_addr %1 to [init] %0 : $*T // id: %10
%11 = tuple () // user: %12
return %11 : $() // id: %12
} // end sil function '$s4main1fyxxAA1PRzlF'
The issue happens here:
%4 = init_existential_addr %3 : $*any P, $T // user: %5
copy_addr %1 to [init] %4 : $*T // id: %5
in VJP cloner there is no information that T == D
and therefore %4
is cloned as-is. So, copy_addr
tries to produce a copy from %1
which has type $*D
to $*T
causing SIL verifier mismatch.
Here is similar, but a bit easier case adopted from the existing testcase (SILGen/differentiability_witness_generic_signature.swift):
struct AllConcrete<T>: Differentiable {}
extension AllConcrete {
// Original generic signature: `<T>`
// Derivative generic signature: `<T where T == Float>`
// Witness generic signature: `<T where T == Float>`
@_silgen_name("allconcrete_where_gensig_constrained")
@differentiable(reverse where T == Float)
func whereClauseGenericSignatureConstrained() -> AllConcrete {
var a = self
return a
}
}
Note that the comment in the testcase is wrong, the derivative generic signature is null these days.
VJP here is:
// allconcrete_where_gensig_constrainedSfRszlTJrSpSr
sil hidden [ossa] @allconcrete_where_gensig_constrainedSfRszlTJrSpSr : $@convention(method) (AllConcrete<Float>) -> (AllConcrete<Float>, @owned @callee_guaranteed (AllConcrete<Float>.TangentVector) -> AllConcrete<Float>.TangentVector) {
// %0 // users: %3, %1
bb0(%0 : $AllConcrete<Float>):
debug_value %0 : $AllConcrete<Float>, let, name "self", argno 1 // id: %1
%2 = alloc_stack [var_decl] $AllConcrete<T>, var, name "a" // users: %7, %4, %3
store %0 to [trivial] %2 : $*AllConcrete<T> // id: %3
%4 = begin_access [read] [static] %2 : $*AllConcrete<T> // users: %6, %5
%5 = load [trivial] %4 : $*AllConcrete<T> // user: %10
end_access %4 : $*AllConcrete<T> // id: %6
dealloc_stack %2 : $*AllConcrete<T> // id: %7
// function_ref allconcrete_where_gensig_constrainedSfRszlTJpSpSr
%8 = function_ref @allconcrete_where_gensig_constrainedSfRszlTJpSpSr : $@convention(thin) (AllConcrete<Float>.TangentVector) -> AllConcrete<Float>.TangentVector // user: %9
%9 = partial_apply [callee_guaranteed] %8() : $@convention(thin) (AllConcrete<Float>.TangentVector) -> AllConcrete<Float>.TangentVector // user: %10
%10 = tuple (%5 : $AllConcrete<T>, %9 : $@callee_guaranteed (AllConcrete<Float>.TangentVector) -> AllConcrete<Float>.TangentVector) // user: %11
return %10 : $(AllConcrete<T>, @callee_guaranteed (AllConcrete<Float>.TangentVector) -> AllConcrete<Float>.TangentVector) // id: %11
} // end sil function 'allconcrete_where_gensig_constrainedSfRszlTJrSpSr'
and SIL verifier complains (and indeed, there is no way to resolve T
above):
SIL verification failed: Operand is of an ArchetypeType that does not exist in the Caller's generic param list.: isArchetypeValidInFunction(A, F)
Verifying instruction:
-> %2 = alloc_stack [var_decl] $AllConcrete<T>, var, name "a" // users: %7, %4, %3
store %0 to [trivial] %2 : $*AllConcrete<T> // id: %3
%4 = begin_access [read] [static] %2 : $*AllConcrete<T> // users: %6, %5
dealloc_stack %2 : $*AllConcrete<T> // id: %7
So, the question is: why same-type requirements are dropped from VJP's generic signature? Tagging @rxwei for maybe some past knowledge.
My memory is rusty on this one, but I do recall autodiff having issues with same-type constraints all along. @dan-zheng knows best.
Description
Factored out second testcase from https://github.com/apple/swift/issues/72363
Reproduction
Stack dump
Expected behavior
The compilation should succeed, or the compiler should produce an error message indicating why the compilation cannot succeed.
Environment
Swift version 6.0-dev (LLVM db7d0a8bc4512db, Swift a62e62d046ba949) Target: arm64-apple-macosx13.0
Additional information
No response