swift icon indicating copy to clipboard operation
swift copied to clipboard

Ensure we are using mapped SIL type for switch_enum case and not the original lowered one

Open asl opened this issue 1 year ago • 6 comments

Fixes #73018

asl avatar May 01 '24 22:05 asl

@swift-ci please test

asl avatar May 01 '24 22:05 asl

Tagging @fibrechannelscsi

asl avatar May 01 '24 22:05 asl

@swift-ci please test

asl avatar May 01 '24 23:05 asl

@swift-ci please test

asl avatar May 02 '24 17:05 asl

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.

aschwaighofer avatar May 03 '24 16:05 aschwaighofer

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

asl avatar May 03 '24 20:05 asl

@swift-ci please test

asl avatar May 08 '24 21:05 asl