Files
swift-mirror/test/SILOptimizer/looprotate_ossa.sil
Erik Eckstein 18063707b5 Optimizer: enable complete OSSA lifetimes throughout the pass pipeline
This new OSSA invariant simplifies many optimizations because they don't have to take care of the corner case of incomplete lifetimes in dead-end blocks.

The implementation basically consists of these changes:
* add the lifetime completion utility
* add a flag in SILFunction which tells optimization that they need to run the lifetime completion utility
* let all optimizations complete lifetimes if necessary
* enable the ownership verifier to check complete lifetimes
2026-01-22 17:41:48 +01:00

754 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
destroy_value [dead_end] %2
unreachable
bb4:
end_borrow %3
destroy_value %2
cond_br undef, bb6, bb5
bb5:
br bb1
bb6:
%12 = tuple ()
return %12
}