Ensure we are using mapped SIL type for switch_enum case and not the original lowered one
Fixes #73018
@swift-ci please test
Tagging @fibrechannelscsi
@swift-ci please test
@swift-ci please test
A reproducer could look something like:
import Builtin
import Swift
struct X {
var x1 : Int
var x2 : Int
var x3 : Int
var x4: Int
var x5: Int
var x6: Int
var x7: Int
var x8: Int
var x9: Int
var x10: Int
var x11: Int
var x12: Int
var x13: Int
var x14: Int
var x15: Int
var x16: Int
}
enum large_enum {
case bb0(( () -> X, () -> X, () -> X, () -> X, () -> X, () -> X, () -> X, () -> X, () -> X, () -> X, () -> X, () -> X, () -> X, () -> X, () -> X, () -> X))
}
sil @test1 : $@convention(thin) (@guaranteed large_enum) -> () {
bb0(%arg : $large_enum):
%loc = alloc_stack $(@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X,
@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X,
@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X,
@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X,
@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X,
@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X,
@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X,
@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X)
switch_enum %arg : $large_enum, case #large_enum.bb0!enumelt: bb1
bb1(%p : $((@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X,
@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X,
@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X,
@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X,
@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X,
@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X,
@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X,
@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X))):
store %p to %loc : $*(@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X,
@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X,
@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X,
@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X,
@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X,
@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X,
@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X,
@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X)
dealloc_stack %loc : $*(@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X,
@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X,
@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X,
@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X,
@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X,
@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X,
@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X,
@callee_guaranteed () -> @out X, @callee_guaranteed () -> @out X)
%t = tuple ()
return %t : $()
}
Unfortunately, if you run swift-macosx-arm64/bin/swiftc -Xllvm -sil-print-after=loadable-address r.sil 2>&1 | less on this, a bunch of passes and the rest of LoadableByAddress runs prior to runPeepholesAndReg2Mem.
Maybe, this can be massage somehow to get to the crash by disabling the SIL passes that run prior (-Xllvm -sil-disable-pass=) and adding an option to disable what LoadableByAddress does prior to running runPeepholesAndReg2Mem.
After few hours of experiments, I think I ended with smaller reproducer. Note that it needs to be used with disabled SIL verifier (-Xllvm -verify-continue-on-failure) due to exactly same issue as fixed in the PR (results are lowered as @out, but we'd need them @owned, but there is no way to specify this on Swift level):
import Builtin
import Swift
typealias X = Int
typealias LargeX = (() -> X, () -> X, () -> X, () -> X, () -> X, () -> X, () -> X, () -> X, () -> X)
enum enum1 {
case bb0(LargeX)
}
enum enum2 {
case bb0(LargeX)
}
enum large_enum {
case bb1((enum1, X))
case bb2((enum2, X))
}
sil @test1 : $@convention(thin) (@guaranteed large_enum) -> () {
bb0(%arg : $large_enum):
%loc = alloc_stack $(@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X)
switch_enum %arg : $large_enum, case #large_enum.bb1!enumelt: bb1, case #large_enum.bb2!enumelt: bb2
bb1(%e1 : $(enum1, X)):
%e11 = tuple_extract %e1 : $(enum1, X), 0
switch_enum %e11 : $enum1, case #enum1.bb0!enumelt: bb11
bb11(%p1 : $((@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X))):
br bb3(%p1 : $((@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X)))
bb2(%e2 : $(enum2, X)):
%e22 = tuple_extract %e2 : $(enum2, X), 0
switch_enum %e22 : $enum2, case #enum2.bb0!enumelt: bb22
bb22(%p2 : $((@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X))):
br bb3(%p2 : $((@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X)))
bb3(%p3 : $((@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X))):
store %p3 to %loc : $*(@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X)
dealloc_stack %loc : $*(@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X, @callee_guaranteed () -> @owned X,
@callee_guaranteed () -> @owned X)
%t = tuple ()
return %t : $()
}
Are we ok with this? @slavapestov @aschwaighofer
@swift-ci please test