Files
swift-mirror/test/SILOptimizer/looprotate_ossa.sil
Erik Eckstein 5dc80f62dc LoopRotate: don't rotate loops where the header block branches to a dead-end block.
Incomplete liveranges in the dead-end exit block can cause a missing adjacent phi-argument for a re-borrow if there is a borrow-scope is in the loop.
But even when we have complete lifetimes, it's probably not worth rotating a loop where the header block branches to a dead-end block.

Fixes a SIL verifier crash
2025-01-21 19:08:08 +01:00

753 lines
26 KiB
Plaintext

// RUN: %target-sil-opt -sil-print-types -enable-objc-interop -enforce-exclusivity=none -enable-sil-verify-all -loop-rotate %s | %FileCheck %s
// Declare this SIL to be canonical because some tests break raw SIL
// conventions. e.g. address-type block args. -enforce-exclusivity=none is also
// required to allow address-type block args in canonical SIL.
sil_stage canonical
import Builtin
import Swift
protocol P {
func boo() -> Int64
}
class Bar {
func boo() -> Int64
func foo()
@objc func foo_objc()
}
sil [ossa] @getBar : $@convention(thin) () -> @owned Bar
sil [ossa] @_TFC4main3Bar3foofS0_FT_T_ : $@convention(method) (@guaranteed Bar) -> ()
sil [ossa] @_TFC4main3Bar3boofS0_FT_T_ : $@convention(method) (@guaranteed Bar) -> Int64
sil [ossa] @_TFC4main3Bar3foo_objcfS0_FT_T_ : $@convention(objc_method) (Bar) -> ()
sil_vtable Bar {
#Bar.boo: @_TFC4main3Bar3boofS0_FT_T_
#Bar.foo: @_TFC4main3Bar3foofS0_FT_T_
#Bar.foo_objc: @_TFC4main3Bar3foofS0_FT_T_
}
// CHECK-LABEL: sil [ossa] @looprotate :
// CHECK: bb0(%0 : $Int32, %1 : @owned $Bar):
// CHECK: class_method
// CHECK: apply
// CHECK: cond_br {{.*}}, bb2, bb1
// CHECK: bb1:
// CHECK-NEXT: br bb3({{.*}} : $Builtin.Int32, {{.*}} : $Builtin.Int32, {{.*}}: $Int32)
// CHECK: bb3({{.*}}: $Builtin.Int32, {{.*}} : $Builtin.Int32, {{.*}} : $Int32):
// CHECK: class_method
// CHECK: apply
// CHECK: cond_br {{%.*}}, bb5, bb4
// CHECK: bb5:
// CHECK-NEXT: br bb6({{.*}} : $Builtin.Int32, {{.*}} : $Bar, {{.*}} : $<τ_0_0> { var τ_0_0 } <Bool>)
// CHECK: bb6({{.*}} : $Builtin.Int32, {{.*}} : @owned $Bar, {{.*}} : @owned $<τ_0_0> { var τ_0_0 } <Bool>):
// CHECK: [[STRUCT:%.*]] = struct $Int32 ({{%.*}} : $Builtin.Int32)
// CHECK: return [[STRUCT]] : $Int32
// CHECK: } // end sil function 'looprotate'
sil [ossa] @looprotate : $@convention(thin) (Int32, @owned Bar) -> Int32 {
bb0(%0 : $Int32, %25: @owned $Bar):
%1 = struct_extract %0 : $Int32, #Int32._value
%2 = integer_literal $Builtin.Int32, 0
%30 = alloc_box $<τ_0_0> { var τ_0_0 } <Bool>
%30a = project_box %30 : $<τ_0_0> { var τ_0_0 } <Bool>, 0
br bb1(%1 : $Builtin.Int32, %2 : $Builtin.Int32, %25: $Bar, %30 : $<τ_0_0> { var τ_0_0 } <Bool>)
bb1(%4 : $Builtin.Int32, %5 : $Builtin.Int32, %26: @owned $Bar, %31 : @owned $<τ_0_0> { var τ_0_0 } <Bool>):
%24 = class_method %26 : $Bar, #Bar.foo : (Bar) -> () -> (), $@convention(method) (@guaranteed Bar) -> () // user: %6
%27 = apply %24(%26) : $@convention(method) (@guaranteed Bar) -> ()
%6 = struct $Int32 (%5 : $Builtin.Int32)
%8 = builtin "cmp_eq_Word"(%5 : $Builtin.Int32, %1 : $Builtin.Int32) : $Builtin.Int1
cond_br %8, bb3, bb2
bb2:
%10 = integer_literal $Builtin.Int32, 1
%12 = integer_literal $Builtin.Int1, -1
%13 = builtin "sadd_with_overflow_Word"(%5 : $Builtin.Int32, %10 : $Builtin.Int32, %12 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
%14 = tuple_extract %13 : $(Builtin.Int32, Builtin.Int1), 0
%15 = enum $Optional<Int32>, #Optional.some!enumelt, %6 : $Int32
%16 = unchecked_enum_data %15 : $Optional<Int32>, #Optional.some!enumelt
%17 = struct_extract %16 : $Int32, #Int32._value
%19 = integer_literal $Builtin.Int1, -1
%20 = builtin "sadd_with_overflow_Word"(%4 : $Builtin.Int32, %17 : $Builtin.Int32, %19 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
%21 = tuple_extract %20 : $(Builtin.Int32, Builtin.Int1), 0
br bb1(%21 : $Builtin.Int32, %14 : $Builtin.Int32, %26: $Bar, %31 : $<τ_0_0> { var τ_0_0 } <Bool>)
bb3:
destroy_value %26 : $Bar
destroy_value %31 : $<τ_0_0> { var τ_0_0 } <Bool>
%23 = struct $Int32 (%4 : $Builtin.Int32)
return %23 : $Int32
}
// CHECK-LABEL: sil [ossa] @dont_rotate_multi_exit_loop :
// CHECK: bb1({{.*}}):
// CHECK: cond_br %{{.*}}, bb4, bb2
// CHECK: bb2:
// CHECK: cond_br undef, bb3, bb5
// CHECK: bb6:
// CHECK: return
// CHECK-NOT: bb7
// CHECK: } // end sil function 'dont_rotate_multi_exit_loop'
sil [ossa] @dont_rotate_multi_exit_loop : $@convention(thin) (Int32, @owned Bar) -> Int32 {
bb0(%0 : $Int32, %25: @owned $Bar):
%1 = struct_extract %0 : $Int32, #Int32._value
%2 = integer_literal $Builtin.Int32, 0
%30 = alloc_box $<τ_0_0> { var τ_0_0 } <Bool>
%30a = project_box %30 : $<τ_0_0> { var τ_0_0 } <Bool>, 0
br bb1(%1 : $Builtin.Int32, %2 : $Builtin.Int32, %25: $Bar, %30 : $<τ_0_0> { var τ_0_0 } <Bool>)
bb1(%4 : $Builtin.Int32, %5 : $Builtin.Int32, %26: @owned $Bar, %31 : @owned $<τ_0_0> { var τ_0_0 } <Bool>):
%24 = class_method %26 : $Bar, #Bar.foo : (Bar) -> () -> (), $@convention(method) (@guaranteed Bar) -> () // user: %6
%27 = apply %24(%26) : $@convention(method) (@guaranteed Bar) -> ()
%6 = struct $Int32 (%5 : $Builtin.Int32)
%8 = builtin "cmp_eq_Word"(%5 : $Builtin.Int32, %1 : $Builtin.Int32) : $Builtin.Int1
cond_br %8, bb4, bb2
bb2:
%10 = integer_literal $Builtin.Int32, 1
%12 = integer_literal $Builtin.Int1, -1
%13 = builtin "sadd_with_overflow_Word"(%5 : $Builtin.Int32, %10 : $Builtin.Int32, %12 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
%14 = tuple_extract %13 : $(Builtin.Int32, Builtin.Int1), 0
%15 = enum $Optional<Int32>, #Optional.some!enumelt, %6 : $Int32
%16 = unchecked_enum_data %15 : $Optional<Int32>, #Optional.some!enumelt
%17 = struct_extract %16 : $Int32, #Int32._value
%19 = integer_literal $Builtin.Int1, -1
%20 = builtin "sadd_with_overflow_Word"(%4 : $Builtin.Int32, %17 : $Builtin.Int32, %19 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
%21 = tuple_extract %20 : $(Builtin.Int32, Builtin.Int1), 0
cond_br undef, bb3, bb5
bb3:
br bb1(%21 : $Builtin.Int32, %14 : $Builtin.Int32, %26: $Bar, %31 : $<τ_0_0> { var τ_0_0 } <Bool>)
bb4:
br bb6
bb5:
br bb6
bb6:
destroy_value %26 : $Bar
destroy_value %31 : $<τ_0_0> { var τ_0_0 } <Bool>
%23 = struct $Int32 (%4 : $Builtin.Int32)
return %23 : $Int32
}
// CHECK-LABEL: sil [ossa] @dont_duplicate_large_block :
// CHECK: bb0(%0 : $Int32, %1 : @owned $Bar):
// CHECK-NOT: class_method
// CHECK-NOT: apply
// CHECK: br bb1
// CHECK: bb1({{.*}}):
// CHECK: class_method
// CHECK: apply
// CHECK: cond_br
// CHECK: bb2:
// CHECK: br bb1
// CHECK: bb3:
// CHECK: return
// CHECK: } // end sil function 'dont_duplicate_large_block'
sil [ossa] @dont_duplicate_large_block : $@convention(thin) (Int32, @owned Bar) -> Int32 {
bb0(%0 : $Int32, %25: @owned $Bar):
%1 = struct_extract %0 : $Int32, #Int32._value
%2 = integer_literal $Builtin.Int32, 0
%30 = alloc_box $<τ_0_0> { var τ_0_0 } <Bool>
%30a = project_box %30 : $<τ_0_0> { var τ_0_0 } <Bool>, 0
br bb1(%1 : $Builtin.Int32, %2 : $Builtin.Int32, %25: $Bar, %30 : $<τ_0_0> { var τ_0_0 } <Bool>)
bb1(%4 : $Builtin.Int32, %5 : $Builtin.Int32, %26: @owned $Bar, %31 : @owned $<τ_0_0> { var τ_0_0 } <Bool>):
%24 = class_method %26 : $Bar, #Bar.foo : (Bar) -> () -> (), $@convention(method) (@guaranteed Bar) -> () // user: %6
%27 = apply %24(%26) : $@convention(method) (@guaranteed Bar) -> ()
%28 = apply %24(%26) : $@convention(method) (@guaranteed Bar) -> ()
%29 = apply %24(%26) : $@convention(method) (@guaranteed Bar) -> ()
%33 = apply %24(%26) : $@convention(method) (@guaranteed Bar) -> ()
%34 = apply %24(%26) : $@convention(method) (@guaranteed Bar) -> ()
%35 = apply %24(%26) : $@convention(method) (@guaranteed Bar) -> ()
%36 = apply %24(%26) : $@convention(method) (@guaranteed Bar) -> ()
%37 = apply %24(%26) : $@convention(method) (@guaranteed Bar) -> ()
%38 = apply %24(%26) : $@convention(method) (@guaranteed Bar) -> ()
%39 = apply %24(%26) : $@convention(method) (@guaranteed Bar) -> ()
%40 = apply %24(%26) : $@convention(method) (@guaranteed Bar) -> ()
%41 = apply %24(%26) : $@convention(method) (@guaranteed Bar) -> ()
%42 = apply %24(%26) : $@convention(method) (@guaranteed Bar) -> ()
%43 = apply %24(%26) : $@convention(method) (@guaranteed Bar) -> ()
%44 = apply %24(%26) : $@convention(method) (@guaranteed Bar) -> ()
%45 = apply %24(%26) : $@convention(method) (@guaranteed Bar) -> ()
%46 = apply %24(%26) : $@convention(method) (@guaranteed Bar) -> ()
%47 = apply %24(%26) : $@convention(method) (@guaranteed Bar) -> ()
%48 = apply %24(%26) : $@convention(method) (@guaranteed Bar) -> ()
%49 = apply %24(%26) : $@convention(method) (@guaranteed Bar) -> ()
%50 = apply %24(%26) : $@convention(method) (@guaranteed Bar) -> ()
%51 = apply %24(%26) : $@convention(method) (@guaranteed Bar) -> ()
%52 = apply %24(%26) : $@convention(method) (@guaranteed Bar) -> ()
%53 = apply %24(%26) : $@convention(method) (@guaranteed Bar) -> ()
%54 = apply %24(%26) : $@convention(method) (@guaranteed Bar) -> ()
%6 = struct $Int32 (%5 : $Builtin.Int32)
%8 = builtin "cmp_eq_Word"(%5 : $Builtin.Int32, %1 : $Builtin.Int32) : $Builtin.Int1
cond_br %8, bb3, bb2
bb2:
%10 = integer_literal $Builtin.Int32, 1
%12 = integer_literal $Builtin.Int1, -1
%13 = builtin "sadd_with_overflow_Word"(%5 : $Builtin.Int32, %10 : $Builtin.Int32, %12 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
%14 = tuple_extract %13 : $(Builtin.Int32, Builtin.Int1), 0
%15 = enum $Optional<Int32>, #Optional.some!enumelt, %6 : $Int32
%16 = unchecked_enum_data %15 : $Optional<Int32>, #Optional.some!enumelt
%17 = struct_extract %16 : $Int32, #Int32._value
%19 = integer_literal $Builtin.Int1, -1
%20 = builtin "sadd_with_overflow_Word"(%4 : $Builtin.Int32, %17 : $Builtin.Int32, %19 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
%21 = tuple_extract %20 : $(Builtin.Int32, Builtin.Int1), 0
br bb1(%21 : $Builtin.Int32, %14 : $Builtin.Int32, %26: $Bar, %31 : $<τ_0_0> { var τ_0_0 } <Bool>)
bb3:
destroy_value %26 : $Bar
destroy_value %31 : $<τ_0_0> { var τ_0_0 } <Bool>
%23 = struct $Int32 (%4 : $Builtin.Int32)
return %23 : $Int32
}
// This example illustrates the problem with using ValueUseIterators.
// As part of updating SSA form we will introduce a phi node argument to the
// branch to bb2. This means we change "cond_br %8, bb3(%4 : $Builtin.Int32),
// bb2" invalidating any outstanding use iterator pointing to the use of "%4" in
// said branch.
// CHECK-LABEL: sil [ossa] @looprotate2 :
// CHECK: return
// CHECK-LABEL: } // end sil function 'looprotate2'
sil [ossa] @looprotate2 : $@convention(thin) (Int32) -> Int32 {
bb0(%0 : $Int32):
%1 = struct_extract %0 : $Int32, #Int32._value
%2 = integer_literal $Builtin.Int32, 0
br bb1(%1 : $Builtin.Int32, %2 : $Builtin.Int32)
bb1(%4 : $Builtin.Int32, %5 : $Builtin.Int32):
%6 = struct $Int32 (%5 : $Builtin.Int32)
%8 = builtin "cmp_eq_Word"(%5 : $Builtin.Int32, %1 : $Builtin.Int32) : $Builtin.Int1
cond_br %8, bb3, bb2
bb2:
%10 = integer_literal $Builtin.Int32, 1
%12 = integer_literal $Builtin.Int1, -1
%13 = builtin "sadd_with_overflow_Word"(%5 : $Builtin.Int32, %10 : $Builtin.Int32, %12 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
%14 = tuple_extract %13 : $(Builtin.Int32, Builtin.Int1), 0
%15 = enum $Optional<Int32>, #Optional.some!enumelt, %6 : $Int32
%16 = unchecked_enum_data %15 : $Optional<Int32>, #Optional.some!enumelt
%17 = struct_extract %16 : $Int32, #Int32._value
%19 = integer_literal $Builtin.Int1, -1
%20 = builtin "sadd_with_overflow_Word"(%4 : $Builtin.Int32, %17 : $Builtin.Int32, %19 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
%21 = tuple_extract %20 : $(Builtin.Int32, Builtin.Int1), 0
br bb1(%21 : $Builtin.Int32, %14 : $Builtin.Int32)
bb3:
%24 = struct $Int32 (%4 : $Builtin.Int32)
return %24 : $Int32
}
// The following function used to crash the compiler, because loop rotate
// could not properly handle the reference from witness_method to the
// archetype opened by open_existential_addr
// CHECK-LABEL: sil [ossa] @looprotate_with_opened_archetype :
// CHECK: return
// CHECK-LABEL: } // end sil function 'looprotate_with_opened_archetype'
sil [ossa] @looprotate_with_opened_archetype : $@convention(thin) (Int32, @in_guaranteed P) -> Int32 {
bb0(%0 : $Int32, %25: $*P):
%1 = struct_extract %0 : $Int32, #Int32._value
%2 = integer_literal $Builtin.Int32, 0
%30 = alloc_box $<τ_0_0> { var τ_0_0 } <Bool>
%30a = project_box %30 : $<τ_0_0> { var τ_0_0 } <Bool>, 0
%40 = open_existential_addr immutable_access %25 : $*P to $*@opened("C22498FA-CABF-11E5-B9A9-685B35C48C83", P) Self
br bb1(%1 : $Builtin.Int32, %2 : $Builtin.Int32, %30 : $<τ_0_0> { var τ_0_0 } <Bool>)
bb1(%4 : $Builtin.Int32, %5 : $Builtin.Int32, %31 : @owned $<τ_0_0> { var τ_0_0 } <Bool>):
%111 = witness_method $@opened("C22498FA-CABF-11E5-B9A9-685B35C48C83", P) Self, #P.boo, %40 : $*@opened("C22498FA-CABF-11E5-B9A9-685B35C48C83", P) Self : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64
%122 = apply %111<@opened("C22498FA-CABF-11E5-B9A9-685B35C48C83", P) Self>(%40) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64
%6 = struct $Int32 (%5 : $Builtin.Int32)
%8 = builtin "cmp_eq_Word"(%5 : $Builtin.Int32, %1 : $Builtin.Int32) : $Builtin.Int1
cond_br %8, bb3, bb2
bb2:
%10 = integer_literal $Builtin.Int32, 1
%12 = integer_literal $Builtin.Int1, -1
%13 = builtin "sadd_with_overflow_Word"(%5 : $Builtin.Int32, %10 : $Builtin.Int32, %12 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
%14 = tuple_extract %13 : $(Builtin.Int32, Builtin.Int1), 0
%15 = enum $Optional<Int32>, #Optional.some!enumelt, %6 : $Int32
%16 = unchecked_enum_data %15 : $Optional<Int32>, #Optional.some!enumelt
%17 = struct_extract %16 : $Int32, #Int32._value
%19 = integer_literal $Builtin.Int1, -1
%20 = builtin "sadd_with_overflow_Word"(%4 : $Builtin.Int32, %17 : $Builtin.Int32, %19 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
%21 = tuple_extract %20 : $(Builtin.Int32, Builtin.Int1), 0
br bb1(%21 : $Builtin.Int32, %14 : $Builtin.Int32, %31 : $<τ_0_0> { var τ_0_0 } <Bool>)
bb3:
destroy_value %31 : $<τ_0_0> { var τ_0_0 } <Bool>
%23 = struct $Int32 (%4 : $Builtin.Int32)
return %23 : $Int32
}
// Don't assert on this loop. bb2 is bb1 loop's new header after rotation but
// also bb2 loop's header.
// CHECK-LABEL: sil [ossa] @testnested :
// CHECK: return
// CHECK-LABEL: } // end sil function 'testnested'
sil [ossa] @testnested : $@convention(thin) () -> () {
bb0:
br bb1(undef : $Builtin.Int32, undef : $Builtin.Int32)
bb1(%1 : $Builtin.Int32, %2 : $Builtin.Int32):
%3 = integer_literal $Builtin.Int1, 0
cond_br %3, bb1a, bb10
bb1a:
br bb2(%1 : $Builtin.Int32, %2 : $Builtin.Int32)
bb2(%5 : $Builtin.Int32, %6 : $Builtin.Int32):
%7 = integer_literal $Builtin.Int1, -1
cond_br %7, bb3, bb2a
bb2a:
br bb1(%5 : $Builtin.Int32, %6 : $Builtin.Int32)
bb3:
%9 = integer_literal $Builtin.Int32, 0
%10 = integer_literal $Builtin.Int32, 1
br bb4(%9 : $Builtin.Int32)
bb4(%12 : $Builtin.Int32):
%14 = builtin "cmp_eq_Word"(%12 : $Builtin.Int32, %10 : $Builtin.Int32) : $Builtin.Int1
cond_br %14, bb5, bb9
bb5:
%16 = enum $Optional<Int32>, #Optional.none!enumelt
br bb6(%12 : $Builtin.Int32, %16 : $Optional<Int32>)
bb6(%18 : $Builtin.Int32, %19 : $Optional<Int32>):
switch_enum %19 : $Optional<Int32>, case #Optional.some!enumelt: bb7, case #Optional.none!enumelt: bb8
bb7(%arg : $Int32):
%21 = unchecked_enum_data %19 : $Optional<Int32>, #Optional.some!enumelt
br bb4(%18 : $Builtin.Int32)
bb8:
br bb2(%18 : $Builtin.Int32, %10 : $Builtin.Int32)
bb9:
%24 = struct $Int32 (%12 : $Builtin.Int32)
%25 = integer_literal $Builtin.Int32, 1
%27 = integer_literal $Builtin.Int1, -1
%28 = builtin "sadd_with_overflow_Word"(%12 : $Builtin.Int32, %25 : $Builtin.Int32, %27 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
%29 = tuple_extract %28 : $(Builtin.Int32, Builtin.Int1), 0
%30 = tuple_extract %28 : $(Builtin.Int32, Builtin.Int1), 1
cond_fail %30 : $Builtin.Int1
%32 = enum $Optional<Int32>, #Optional.some!enumelt, %24 : $Int32
br bb6(%29 : $Builtin.Int32, %32 : $Optional<Int32>)
bb10:
%34 = tuple ()
return %34 : $()
}
// CHECK-LABEL: sil [ossa] @dont_looprotate_objc :
// CHECK: bb0{{.*}}:
// CHECK: br bb1
// CHECK: bb1{{.*}}:
// CHECK: cond_br {{.*}}, bb3, bb2
// CHECK: bb2:
// CHECK: br bb1
// CHECK: bb3:
// CHECK: return
// CHECK-LABEL: } // end sil function 'dont_looprotate_objc'
sil [ossa] @dont_looprotate_objc : $@convention(thin) (Int32, @owned Bar) -> Int32 {
bb0(%0 : $Int32, %25: @owned $Bar):
%100 = alloc_ref $Bar
%1 = struct_extract %0 : $Int32, #Int32._value
%2 = integer_literal $Builtin.Int32, 0
br bb1(%1 : $Builtin.Int32, %2 : $Builtin.Int32, %25: $Bar)
bb1(%4 : $Builtin.Int32, %5 : $Builtin.Int32, %26: @owned $Bar):
%101 = objc_method %100 : $Bar, #Bar.foo!foreign : (Bar) -> () -> (), $@convention(objc_method) (Bar) -> ()
%6 = struct $Int32 (%5 : $Builtin.Int32)
%8 = builtin "cmp_eq_Word"(%5 : $Builtin.Int32, %1 : $Builtin.Int32) : $Builtin.Int1
cond_br %8, bb3, bb2
bb2:
%102 = apply %101(%100) : $@convention(objc_method) (Bar) -> ()
%10 = integer_literal $Builtin.Int32, 1
%12 = integer_literal $Builtin.Int1, -1
%13 = builtin "sadd_with_overflow_Word"(%5 : $Builtin.Int32, %10 : $Builtin.Int32, %12 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
%14 = tuple_extract %13 : $(Builtin.Int32, Builtin.Int1), 0
%15 = enum $Optional<Int32>, #Optional.some!enumelt, %6 : $Int32
%16 = unchecked_enum_data %15 : $Optional<Int32>, #Optional.some!enumelt
%17 = struct_extract %16 : $Int32, #Int32._value
%19 = integer_literal $Builtin.Int1, -1
%20 = builtin "sadd_with_overflow_Word"(%4 : $Builtin.Int32, %17 : $Builtin.Int32, %19 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
%21 = tuple_extract %20 : $(Builtin.Int32, Builtin.Int1), 0
br bb1(%21 : $Builtin.Int32, %14 : $Builtin.Int32, %26: $Bar)
bb3:
destroy_value %100 : $Bar
destroy_value %26 : $Bar
%23 = struct $Int32 (%4 : $Builtin.Int32)
return %23 : $Int32
}
sil [ossa] @readInt : $@convention(thin) (Int32) -> () {
bb0(%a0 : $Int32):
%t1 = tuple ()
return %t1 : $()
}
// Test LoopRotate insertBackedgeBlock.
//
// CHECK-LABEL: sil [ossa] @insert_backedge_block :
// PreLoop Check
// CHECK: bb0(%
// CHECK: cond_br
// Loop Header
// CHECK: bb1:
// CHECK-NEXT: br bb3
// CHECK: bb3:
// CHECK: cond_br %{{.*}}, bb4, bb8
// CHECK: bb4:
// CHECK: apply
// CHECK: br bb5(
// CHECK: bb5(%[[ARG:[0-9]*]]
// CHECK-NEXT: cond_br %{{.*}}, bb7, bb6
// CHECK: bb8:
// CHECK: apply
// CHECK: br bb5(%
// CHECK: bb9
// CHECK: return
// CHECK-LABEL: } // end sil function 'insert_backedge_block'
sil [ossa] @insert_backedge_block : $@convention(thin) (@owned @callee_owned () -> Builtin.Int32) -> () {
bb0(%0 : @owned $@callee_owned () -> Builtin.Int32):
%copy0 = copy_value %0 : $@callee_owned () -> Builtin.Int32
%r0 = apply %copy0() : $@callee_owned () -> Builtin.Int32
br bb1(%r0: $Builtin.Int32)
bb1(%a1 : $Builtin.Int32):
%z1 = integer_literal $Builtin.Int1, 0
cond_br %z1, bb5, bb2
bb2:
%z2 = integer_literal $Builtin.Int1, 0
cond_br %z2, bb3, bb4
bb3:
%copy1 = copy_value %0 : $@callee_owned () -> Builtin.Int32
%r1 = apply %copy1() : $@callee_owned () -> Builtin.Int32
br bb1(%r1: $Builtin.Int32)
bb4:
%copy2 = copy_value %0 : $@callee_owned () -> Builtin.Int32
%r2 = apply %copy2() : $@callee_owned () -> Builtin.Int32
br bb1(%r2: $Builtin.Int32)
bb5:
destroy_value %0 : $@callee_owned () -> Builtin.Int32
%r3 = tuple ()
return %r3 : $()
}
// CHECK-LABEL: sil [ossa] @dont_rotate_single_basic_block_loop :
// CHECK: bb0({{.*}}):
// CHECK: br bb1
// CHECK: bb1:
// CHECK: cond_br {{.*}}, bb2, bb3
// CHECK: bb2:
// CHECK: br bb1
// CHECK: bb3:
// CHECK: return
// CHECK-LABEL: } // end sil function 'dont_rotate_single_basic_block_loop'
sil [ossa] @dont_rotate_single_basic_block_loop : $@convention(thin) (Builtin.Int1) -> () {
bb0(%0 : $Builtin.Int1):
br bb1
bb1:
cond_br %0, bb1a, bb2
bb1a:
%t = tuple(%0 : $Builtin.Int1, %0 : $Builtin.Int1)
br bb1
bb2:
%1 = tuple ()
return %1 : $()
}
// Make sure that we do not create address phis.
//
// CHECK-LABEL: sil [ossa] @addressPhiFixUp : $@convention(thin) (Builtin.RawPointer) -> Builtin.RawPointer {
// CHECK: bb1:
// CHECK: cond_br {{%.*}}, bb3, bb2
//
// CHECK: bb2:
// CHECK-NEXT: br bb7
//
// CHECK: bb3:
// CHECK-NEXT: br bb4
//
// CHECK: bb4([[ARG:%.*]] :
// CHECK: [[CAST_BACK:%.*]] = pointer_to_address [[ARG]] : $Builtin.RawPointer to [strict] $*UInt8
// CHECK: [[GEP:%.*]] = index_addr [[CAST_BACK]] :
// CHECK: cond_br {{%.*}}, bb6, bb5
//
// CHECK: bb5:
// CHECK-NEXT: br bb7
//
// CHECK: bb6:
// CHECK-NEXT: br bb4
//
// CHECK: bb7([[RESULT:%.*]] :
// CHECK-NEXT: return [[RESULT]]
// CHECK: } // end sil function 'addressPhiFixUp'
sil [ossa] @addressPhiFixUp : $@convention(thin) (Builtin.RawPointer) -> Builtin.RawPointer {
bb0(%0 : $Builtin.RawPointer):
br bb1
bb1:
br bb2(%0 : $Builtin.RawPointer)
bb2(%1 : $Builtin.RawPointer):
%2 = pointer_to_address %1 : $Builtin.RawPointer to [strict] $*UInt8
%3 = load [trivial] %2 : $*UInt8
%4 = destructure_struct %3 : $UInt8
%5 = integer_literal $Builtin.Int64, 0
%6 = builtin "zextOrBitCast_Int8_Int64"(%4 : $Builtin.Int8) : $Builtin.Int64
%7 = builtin "cmp_eq_Int64"(%6 : $Builtin.Int64, %5 : $Builtin.Int64) : $Builtin.Int1
cond_br %7, bb3, bb4
bb3:
%8 = integer_literal $Builtin.Word, 1
%9 = index_addr %2 : $*UInt8, %8 : $Builtin.Word
%10 = address_to_pointer %9 : $*UInt8 to $Builtin.RawPointer
br bb2(%10 : $Builtin.RawPointer)
bb4:
return %1 : $Builtin.RawPointer
}
// CHECK-LABEL: sil [ossa] @looprotate_copy_move_borrow :
// CHECK: bb0(%0 : $Int32, %1 : @guaranteed $Bar):
// CHECK: copy_value
// CHECK: move_value
// CHECK: begin_borrow
// CHECK: cond_br {{.*}}, bb2, bb1
// CHECK: copy_value
// CHECK: move_value
// CHECK: begin_borrow
// CHECK-LABEL: } // end sil function 'looprotate_copy_move_borrow'
sil [ossa] @looprotate_copy_move_borrow : $@convention(thin) (Int32, @guaranteed Bar) -> Int32 {
bb0(%0 : $Int32, %1 : @guaranteed $Bar):
%2 = struct_extract %0 : $Int32, #Int32._value
%3 = integer_literal $Builtin.Int32, 0
br bb1(%2 : $Builtin.Int32, %3 : $Builtin.Int32)
bb1(%5 : $Builtin.Int32, %6 : $Builtin.Int32):
%c = copy_value %1 : $Bar
%m = move_value %c : $Bar
%7 = begin_borrow %m : $Bar
%8 = class_method %7 : $Bar, #Bar.foo : (Bar) -> () -> (), $@convention(method) (@guaranteed Bar) -> ()
%9 = apply %8(%7) : $@convention(method) (@guaranteed Bar) -> ()
%10 = struct $Int32 (%6 : $Builtin.Int32)
%11 = builtin "cmp_eq_Word"(%6 : $Builtin.Int32, %2 : $Builtin.Int32) : $Builtin.Int1
cond_br %11, bb3, bb2
bb2:
end_borrow %7 : $Bar
destroy_value %m : $Bar
%14 = integer_literal $Builtin.Int32, 1
%15 = integer_literal $Builtin.Int1, -1
%16 = builtin "sadd_with_overflow_Word"(%6 : $Builtin.Int32, %14 : $Builtin.Int32, %15 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
%17 = tuple_extract %16 : $(Builtin.Int32, Builtin.Int1), 0
%18 = enum $Optional<Int32>, #Optional.some!enumelt, %10 : $Int32
%19 = unchecked_enum_data %18 : $Optional<Int32>, #Optional.some!enumelt
%20 = struct_extract %19 : $Int32, #Int32._value
%21 = integer_literal $Builtin.Int1, -1
%22 = builtin "sadd_with_overflow_Word"(%5 : $Builtin.Int32, %20 : $Builtin.Int32, %21 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
%23 = tuple_extract %22 : $(Builtin.Int32, Builtin.Int1), 0
br bb1(%23 : $Builtin.Int32, %17 : $Builtin.Int32)
bb3:
end_borrow %7 : $Bar
destroy_value %m : $Bar
%26 = struct $Int32 (%5 : $Builtin.Int32)
return %26 : $Int32
}
// Make sure that no unnecessary phis are inserted which would cause an ownership error.
// CHECK-LABEL: sil [ossa] @test_ssa_updater_owned :
// CHECK: begin_borrow
// CHECK-NOT: bb({{.*@owned.*}}):
// CHECK: end_borrow
// CHECK-LABEL: } // end sil function 'test_ssa_updater_owned'
sil [ossa] @test_ssa_updater_owned : $@convention(thin) (@owned String, @guaranteed String) -> () {
bb0(%0 : @owned $String, %1 : @guaranteed $String):
%2 = integer_literal $Builtin.Int64, 0
br bb1(%0, %2)
bb1(%4 : @owned $String, %5 : $Builtin.Int64):
cond_br undef, bb14, bb2
bb2:
%7 = integer_literal $Builtin.Int64, 1
%8 = begin_borrow %4
cond_br undef, bb4, bb3
bb3:
br bb5
bb4:
cond_br undef, bb7, bb8
bb5:
cond_br undef, bb10, bb6
bb6:
br bb9
bb7:
br bb9
bb8:
br bb12
bb9:
br bb11
bb10:
br bb11
bb11:
end_borrow %8
destroy_value %4
br bb13
bb12:
end_borrow %8
destroy_value [dead_end] %4
unreachable
bb13:
%24 = copy_value %1
br bb1(%24, %7)
bb14:
destroy_value %4
%27 = tuple ()
return %27
}
// Make sure that no unnecessary phis are inserted which would cause an ownership error.
// CHECK-LABEL: sil [ossa] @test_ssa_updater_guaranteed :
// CHECK: begin_borrow
// CHECK: begin_borrow
// CHECK-NOT: bb({{.*@reborrow.*}}):
// CHECK: end_borrow
// CHECK-LABEL: } // end sil function 'test_ssa_updater_guaranteed'
sil [ossa] @test_ssa_updater_guaranteed : $@convention(thin) (@owned String) -> () {
bb0(%0 : @owned $String):
%2 = integer_literal $Builtin.Int64, 0
%3 = begin_borrow %0
br bb1(%3, %2)
bb1(%4 : @reborrow $String, %5 : $Builtin.Int64):
%6 = borrowed %4 from (%0)
cond_br undef, bb14, bb2
bb2:
%7 = integer_literal $Builtin.Int64, 1
%8 = begin_borrow %6
cond_br undef, bb4, bb3
bb3:
br bb5
bb4:
cond_br undef, bb7, bb8
bb5:
cond_br undef, bb10, bb6
bb6:
br bb9
bb7:
br bb9
bb8:
br bb12
bb9:
br bb11
bb10:
br bb11
bb11:
end_borrow %8
end_borrow %6
br bb13
bb12:
end_borrow %8
end_borrow %6
destroy_value [dead_end] %0
unreachable
bb13:
%24 = begin_borrow %0
br bb1(%24, %7)
bb14:
end_borrow %6
destroy_value %0
%27 = tuple ()
return %27
}
// Incomplete liveranges in dead-end exit blocks can cause a missing phi in the rotated loop.
// CHECK-LABEL: sil [ossa] @incomplete_liverange_in_dead_end_exit :
// CHECK: bb1:
// CHECK: apply
// CHECK: bb2:
// CHECK-LABEL: } // end sil function 'incomplete_liverange_in_dead_end_exit'
sil [ossa] @incomplete_liverange_in_dead_end_exit : $@convention(thin) () -> () {
bb0:
br bb1
bb1:
%1 = function_ref @getBar : $@convention(thin) () -> @owned Bar
%2 = apply %1() : $@convention(thin) () -> @owned Bar
%3 = begin_borrow %2
cond_br undef, bb3, bb2
bb2:
br bb4
bb3:
end_borrow %3
unreachable
bb4:
end_borrow %3
destroy_value %2
cond_br undef, bb6, bb5
bb5:
br bb1
bb6:
%12 = tuple ()
return %12
}