Files
swift-mirror/test/SILOptimizer/loop_unroll_ossa.sil
Erik Eckstein 0f0aa0c17b Optimizer: require that there are no unreachable blocks and infinite loops in OSSA
These two new invariants eliminate corner cases which caused bugs if optimization didn't handle them.
Also, it will significantly simplify lifetime completion.

The implementation basically consists of these changes:
* add a flag in SILFunction which tells optimization if they need to take care of infinite loops
* add a utility to break infinite loops
* let all optimizations remove unreachable blocks and break infinite loops if necessary
* add verification to check the new SIL invariants

The new `breakIfniniteLoops` utility breaks infinite loops in the control flow by inserting an "artificial" loop exit to a new dead-end block with an `unreachable`.
It inserts a `cond_br` with a `builtin "infinite_loop_true_condition"`:
```
bb0:
  br bb1
bb1:
  br bb1              // back-end branch
```
->
```
bb0:
  br bb1
bb1:
  %1 = builtin "infinite_loop_true_condition"() // always true, but the compiler doesn't know
  cond_br %1, bb2, bb3
bb2:                  // new back-end block
  br bb1
bb3:                  // new dead-end block
  unreachable
```
2026-01-22 17:41:23 +01:00

797 lines
26 KiB
Plaintext

// RUN: %target-sil-opt -enable-sil-verify-all -loop-unroll %s | %FileCheck %s
sil_stage canonical
import Builtin
import Swift
struct UInt {
var value: Builtin.Word
}
struct Int {
var value: Builtin.Word
}
struct Bool {
var value: Builtin.Int1
}
struct Int64 {
var value: Builtin.Int64
}
struct UInt64 {
var value: Builtin.Int64
}
class Klass {
var id: Int64
}
struct WrapperStruct {
var val: Klass
}
sil [_semantics "array.check_subscript"] @checkbounds : $@convention(method) (Int64, Bool, @guaranteed Array<Klass>) -> _DependenceToken
sil [_semantics "array.get_element"] @getElement : $@convention(method) (Int64, Bool, _DependenceToken, @guaranteed Array<Klass>) -> @owned Klass
sil @use_inguaranteed : $@convention(thin) (@in_guaranteed Klass) -> ()
sil @get_wrapper_struct : $@convention(thin) (@inout WrapperStruct) -> ()
// CHECK-LABEL: sil [ossa] @loop_unroll_1 :
// CHECK: bb0
// CHECK: br bb1
// CHECK: bb1
// CHECK: builtin "sadd_with_overflow_Int64
// CHECK: cond_br {{.*}}, bb2, bb3
// CHECK: bb2
// CHECK: br bb6
// CHECK: bb3
// CHECK: br bb4
// CHECK: bb4
// CHECK: builtin "sadd_with_overflow_Int64
// CHECK: br bb5
// CHECK: bb5
// CHECK: br bb6
// CHECK: bb6:
// CHECK-LABEL: } // end sil function 'loop_unroll_1'
sil [ossa] @loop_unroll_1 : $@convention(thin) () -> () {
bb0:
%0 = integer_literal $Builtin.Int64, 0
%1 = integer_literal $Builtin.Int64, 1
%2 = integer_literal $Builtin.Int64, 2
%3 = integer_literal $Builtin.Int1, 1
br bb1(%0 : $Builtin.Int64)
bb1(%4 : $Builtin.Int64):
%5 = builtin "sadd_with_overflow_Int64"(%4 : $Builtin.Int64, %1 : $Builtin.Int64, %3 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%6 = tuple_extract %5 : $(Builtin.Int64, Builtin.Int1), 0
%7 = builtin "cmp_eq_Int64"(%6 : $Builtin.Int64, %2 : $Builtin.Int64) : $Builtin.Int1
cond_br %7, bb3, bb2
bb2:
br bb1(%6 : $Builtin.Int64)
bb3:
%9 = tuple()
return %9 : $()
}
// CHECK-LABEL: sil [ossa] @loop_unroll_2 :
// CHECK: bb0:
// CHECK: br bb1
// CHECK: bb1
// CHECK: = builtin "sadd_with_overflow_Int64
// CHECK: cond_br {{.*}}, bb2, bb3
// CHECK: bb2:
// CHECK: br bb6
// CHECK: bb3:
// CHECK: br bb4
// CHECK: bb4
// CHECK: = builtin "sadd_with_overflow_Int64
// CHECK: br bb5
// CHECK: bb5:
// CHECK: br bb6
// CHECK: bb6:
// CHECK-LABEL: } // end sil function 'loop_unroll_2'
sil [ossa] @loop_unroll_2 : $@convention(thin) () -> () {
bb0:
%0 = integer_literal $Builtin.Int64, 0
%1 = integer_literal $Builtin.Int64, 1
%2 = integer_literal $Builtin.Int64, 2
%3 = integer_literal $Builtin.Int1, 1
br bb1(%0 : $Builtin.Int64)
bb1(%4 : $Builtin.Int64):
%5 = builtin "sadd_with_overflow_Int64"(%4 : $Builtin.Int64, %1 : $Builtin.Int64, %3 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%6 = tuple_extract %5 : $(Builtin.Int64, Builtin.Int1), 0
%7 = builtin "cmp_eq_Int64"(%6 : $Builtin.Int64, %2 : $Builtin.Int64) : $Builtin.Int1
cond_br %7, bb3, bb2
bb2:
br bb1(%6 : $Builtin.Int64)
bb3:
%8 = tuple()
return %8 : $()
}
// CHECK-LABEL: sil [ossa] @loop_unroll_3 :
// CHECK: bb0:
// CHECK: br bb1
// CHECK: bb1
// CHECK: = builtin "sadd_with_overflow_Int64
// CHECK: cond_br {{.*}}, bb2, bb3
// CHECK: bb2:
// CHECK: br bb6
// CHECK: bb3:
// CHECK: br bb4
// CHECK: bb4
// CHECK: = builtin "sadd_with_overflow_Int64
// CHECK: br bb5
// CHECK: bb5:
// CHECK: br bb6
// CHECK: bb6:
// CHECK-LABEL: } // end sil function 'loop_unroll_3'
sil [ossa] @loop_unroll_3 : $@convention(thin) () -> () {
bb0:
%0 = integer_literal $Builtin.Int64, 0
%1 = integer_literal $Builtin.Int64, 1
%2 = integer_literal $Builtin.Int64, 2
%3 = integer_literal $Builtin.Int1, 1
br bb1(%0 : $Builtin.Int64)
bb1(%4 : $Builtin.Int64):
%5 = builtin "sadd_with_overflow_Int64"(%4 : $Builtin.Int64, %1 : $Builtin.Int64, %3 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%6 = tuple_extract %5 : $(Builtin.Int64, Builtin.Int1), 0
%7 = builtin "cmp_sge_Int64"(%6 : $Builtin.Int64, %2 : $Builtin.Int64) : $Builtin.Int1
cond_br %7, bb3, bb2
bb2:
br bb1(%6 : $Builtin.Int64)
bb3:
%8 = tuple()
return %8 : $()
}
// CHECK-LABEL: sil [ossa] @loop_unroll_4 :
// CHECK: bb0:
// CHECK: br bb1
// CHECK: bb1
// CHECK: = builtin "sadd_with_overflow_Int64
// CHECK: cond_br {{.*}}, bb2, bb3
// CHECK: bb2:
// CHECK: br bb6
// CHECK: bb3:
// CHECK: br bb4
// CHECK: bb4
// CHECK: = builtin "sadd_with_overflow_Int64
// CHECK: br bb5
// CHECK: bb5:
// CHECK: br bb6
// CHECK: bb6:
// CHECK-LABEL: } // end sil function 'loop_unroll_4'
sil [ossa] @loop_unroll_4 : $@convention(thin) () -> () {
bb0:
%0 = integer_literal $Builtin.Int64, 0
%1 = integer_literal $Builtin.Int64, 1
%2 = integer_literal $Builtin.Int64, 1
%3 = integer_literal $Builtin.Int1, 1
br bb1(%0 : $Builtin.Int64)
bb1(%4 : $Builtin.Int64):
%5 = builtin "sadd_with_overflow_Int64"(%4 : $Builtin.Int64, %1 : $Builtin.Int64, %3 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%6 = tuple_extract %5 : $(Builtin.Int64, Builtin.Int1), 0
%7 = builtin "cmp_sgt_Int64"(%6 : $Builtin.Int64, %2 : $Builtin.Int64) : $Builtin.Int1
cond_br %7, bb3, bb2
bb2:
br bb1(%6 : $Builtin.Int64)
bb3:
%8 = tuple()
return %8 : $()
}
// CHECK-LABEL: sil [ossa] @loop_unroll_5 :
// CHECK: bb2:
// CHECK-NEXT: br bb6
// CHECK: bb5:
// CHECK-NEXT: br bb6
// CHECK: bb6:
// CHECK-NEXT: tuple ()
// CHECK-NEXT: return
// CHECK-LABEL: } // end sil function 'loop_unroll_5'
sil [ossa] @loop_unroll_5 : $@convention(thin) () -> () {
bb0:
%0 = integer_literal $Builtin.Int64, 0
%1 = integer_literal $Builtin.Int64, 1
%2 = integer_literal $Builtin.Int64, 2
%3 = integer_literal $Builtin.Int1, 1
br bb1(%0 : $Builtin.Int64)
bb1(%4 : $Builtin.Int64):
%5 = builtin "sadd_with_overflow_Int64"(%4 : $Builtin.Int64, %1 : $Builtin.Int64, %3 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%6 = tuple_extract %5 : $(Builtin.Int64, Builtin.Int1), 0
%7 = builtin "cmp_slt_Int64"(%6 : $Builtin.Int64, %2 : $Builtin.Int64) : $Builtin.Int1
cond_br %7, bb2, bb3
bb2:
br bb1(%6 : $Builtin.Int64)
bb3:
%8 = tuple()
return %8 : $()
}
// CHECK-LABEL: sil [ossa] @loop_unroll_6 :
// CHECK: bb2:
// CHECK-NEXT: br bb6
// CHECK: bb5:
// CHECK-NEXT: br bb6
// CHECK: bb6:
// CHECK-NEXT: tuple ()
// CHECK-NEXT: return
// CHECK-NEXT: } // end sil function 'loop_unroll_6'
sil [ossa] @loop_unroll_6 : $@convention(thin) () -> () {
bb0:
%0 = integer_literal $Builtin.Int64, 0
%1 = integer_literal $Builtin.Int64, 1
%2 = integer_literal $Builtin.Int64, 1
%3 = integer_literal $Builtin.Int1, 1
br bb1(%0 : $Builtin.Int64)
bb1(%4 : $Builtin.Int64):
%5 = builtin "sadd_with_overflow_Int64"(%4 : $Builtin.Int64, %1 : $Builtin.Int64, %3 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%6 = tuple_extract %5 : $(Builtin.Int64, Builtin.Int1), 0
%7 = builtin "cmp_sle_Int64"(%6 : $Builtin.Int64, %2 : $Builtin.Int64) : $Builtin.Int1
cond_br %7, bb2, bb3
bb2:
br bb1(%6 : $Builtin.Int64)
bb3:
%8 = tuple()
return %8 : $()
}
// CHECK-LABEL: sil [ossa] @loop_unroll_7 :
// CHECK: bb2:
// CHECK-NEXT: br bb6
// CHECK: bb5:
// CHECK-NEXT: br bb6
// CHECK: bb6:
// CHECK-NEXT: tuple ()
// CHECK-NEXT: return
// CHECK-NEXT: } // end sil function 'loop_unroll_7'
sil [ossa] @loop_unroll_7 : $@convention(thin) () -> () {
bb0:
%0 = integer_literal $Builtin.Int64, 0
%1 = integer_literal $Builtin.Int64, 1
%2 = integer_literal $Builtin.Int64, 2
%3 = integer_literal $Builtin.Int1, 1
br bb1(%0 : $Builtin.Int64)
bb1(%4 : $Builtin.Int64):
%5 = builtin "sadd_with_overflow_Int64"(%4 : $Builtin.Int64, %1 : $Builtin.Int64, %3 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%6 = tuple_extract %5 : $(Builtin.Int64, Builtin.Int1), 0
%7 = builtin "cmp_ne_Int64"(%6 : $Builtin.Int64, %2 : $Builtin.Int64) : $Builtin.Int1
cond_br %7, bb2, bb3
bb2:
br bb1(%6 : $Builtin.Int64)
bb3:
%8 = tuple()
return %8 : $()
}
// CHECK-LABEL: sil [ossa] @unroll_with_exit_block_arg_1 :
// CHECK: bb1({{.*}}):
// CHECK: cond_br {{.*}}, bb4, bb2
// CHECK: bb2:
// CHECK: br bb3({{.*}})
// CHECK: bb3({{.*}}):
// CHECK: br bb10({{.*}})
// CHECK: bb4:
// CHECK: br bb7({{.*}})
// CHECK: bb7({{.*}}):
// CHECK: cond_br {{.*}}, bb8, bb9
// CHECK: bb9:
// CHECK: br bb10({{.*}})
// CHECK: bb10({{.*}}):
// CHECK: return
// CHECK-LABEL: } // end sil function 'unroll_with_exit_block_arg_1'
sil [ossa] @unroll_with_exit_block_arg_1 : $@convention(thin) () -> () {
bb0:
%27 = integer_literal $Builtin.Int64, 1
%28 = integer_literal $Builtin.Int64, 4
%56 = integer_literal $Builtin.Int1, -1
br bb4(%27 : $Builtin.Int64, %28 : $Builtin.Int64)
bb4(%58 : $Builtin.Int64, %59 : $Builtin.Int64):
%60 = builtin "sadd_with_overflow_Int64"(%58 : $Builtin.Int64, %27 : $Builtin.Int64, %56 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%61 = tuple_extract %60 : $(Builtin.Int64, Builtin.Int1), 0
%64 = builtin "smul_with_overflow_Int64"(%59 : $Builtin.Int64, %28 : $Builtin.Int64, %56 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%65 = tuple_extract %64 : $(Builtin.Int64, Builtin.Int1), 0
%70 = builtin "cmp_slt_Int64"(%61 : $Builtin.Int64, %28 : $Builtin.Int64) : $Builtin.Int1
cond_br %70, bb5, bb6
bb5:
br bb4(%61 : $Builtin.Int64, %65 : $Builtin.Int64)
bb6:
br bb7(%61 : $Builtin.Int64)
bb7(%72 : $Builtin.Int64):
%401 = tuple ()
return %401 : $()
}
// CHECK-LABEL: sil [ossa] @unroll_with_exit_block_arg_2 :
// CHECK: bb1({{.*}}):
// CHECK: cond_br {{.*}}, bb4, bb2
// CHECK: bb2:
// CHECK: br bb3{{.*}}
// CHECK: bb3({{.*}}):
// CHECK: br bb10{{.*}}
// CHECK: bb4:
// CHECK: br bb7{{.*}}
// CHECK: bb7({{.*}}):
// CHECK: cond_br {{.*}}, bb8, bb9
// CHECK: bb9:
// CHECK: br bb10({{.*}})
// CHECK: bb10({{.*}}):
// CHECK-LABEL: } // end sil function 'unroll_with_exit_block_arg_2'
sil [ossa] @unroll_with_exit_block_arg_2 : $@convention(thin) () -> () {
bb0:
%27 = integer_literal $Builtin.Int64, 1
%28 = integer_literal $Builtin.Int64, 4
%56 = integer_literal $Builtin.Int1, -1
br bb4(%27 : $Builtin.Int64, %28 : $Builtin.Int64)
bb4(%58 : $Builtin.Int64, %59 : $Builtin.Int64):
%60 = builtin "sadd_with_overflow_Int64"(%58 : $Builtin.Int64, %27 : $Builtin.Int64, %56 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%61 = tuple_extract %60 : $(Builtin.Int64, Builtin.Int1), 0
%64 = builtin "smul_with_overflow_Int64"(%59 : $Builtin.Int64, %28 : $Builtin.Int64, %56 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%65 = tuple_extract %64 : $(Builtin.Int64, Builtin.Int1), 0
%70 = builtin "cmp_slt_Int64"(%61 : $Builtin.Int64, %28 : $Builtin.Int64) : $Builtin.Int1
cond_br %70, bb5, bb6
bb5:
br bb4(%61 : $Builtin.Int64, %65 : $Builtin.Int64)
bb6:
br bb7(%61 : $Builtin.Int64)
bb7(%72 : $Builtin.Int64):
%401 = tuple ()
return %401 : $()
}
class B {}
// CHECK-LABEL: sil [ossa] @unroll_with_stack_allocation :
// CHECK: = alloc_ref [stack]
// CHECK: dealloc_stack_ref
// CHECK: = alloc_ref [stack]
// CHECK: dealloc_stack_ref
// CHECK-LABEL: } // end sil function 'unroll_with_stack_allocation'
sil [ossa] @unroll_with_stack_allocation : $@convention(thin) () -> () {
bb0:
%0 = integer_literal $Builtin.Int64, 0
%1 = integer_literal $Builtin.Int64, 1
%2 = integer_literal $Builtin.Int64, 2
%3 = integer_literal $Builtin.Int1, 1
br bb1(%0 : $Builtin.Int64)
bb1(%4 : $Builtin.Int64):
%a = alloc_ref [stack] $B
br bb2
bb2:
%5 = builtin "sadd_with_overflow_Int64"(%4 : $Builtin.Int64, %1 : $Builtin.Int64, %3 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%6 = tuple_extract %5 : $(Builtin.Int64, Builtin.Int1), 0
%7 = builtin "cmp_eq_Int64"(%6 : $Builtin.Int64, %2 : $Builtin.Int64) : $Builtin.Int1
dealloc_ref %a : $B
dealloc_stack_ref %a : $B
cond_br %7, bb4, bb3
bb3:
br bb1(%6 : $Builtin.Int64)
bb4:
%8 = tuple()
return %8 : $()
}
// CHECK-LABEL: sil [ossa] @dont_copy_stack_allocation_with_dealloc_outside_loop :
// CHECK: = alloc_ref [stack]
// CHECK: bb2:
// CHECK: dealloc_stack_ref
// CHECK: bb3:
// CHECK: dealloc_stack_ref
// CHECK-NEXT: tuple
// CHECK-LABEL: } // end sil function 'dont_copy_stack_allocation_with_dealloc_outside_loop'
sil [ossa] @dont_copy_stack_allocation_with_dealloc_outside_loop : $@convention(thin) () -> () {
bb0:
%0 = integer_literal $Builtin.Int64, 0
%1 = integer_literal $Builtin.Int64, 1
%2 = integer_literal $Builtin.Int64, 2
%3 = integer_literal $Builtin.Int1, 1
br bb1(%0 : $Builtin.Int64)
bb1(%4 : $Builtin.Int64):
%a = alloc_ref [stack] $B
%5 = builtin "sadd_with_overflow_Int64"(%4 : $Builtin.Int64, %1 : $Builtin.Int64, %3 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%6 = tuple_extract %5 : $(Builtin.Int64, Builtin.Int1), 0
%7 = builtin "cmp_eq_Int64"(%6 : $Builtin.Int64, %2 : $Builtin.Int64) : $Builtin.Int1
cond_br %7, bb3, bb2
bb2:
dealloc_ref %a : $B
dealloc_stack_ref %a : $B
br bb1(%6 : $Builtin.Int64)
bb3:
dealloc_ref %a : $B
dealloc_stack_ref %a : $B
%8 = tuple()
return %8 : $()
}
sil [ossa] @big_func: $@convention(thin) () -> Builtin.Int64 {
bb0:
%x0 = integer_literal $Builtin.Int64, 1
%overflow_check = integer_literal $Builtin.Int1, 0
%sum1 = builtin "sadd_with_overflow_Int64"(%x0 : $Builtin.Int64, %x0 : $Builtin.Int64, %overflow_check : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%x1 = tuple_extract %sum1 : $(Builtin.Int64, Builtin.Int1), 0
br bb1
bb1:
%sum2 = builtin "sadd_with_overflow_Int64"(%x1 : $Builtin.Int64, %x1 : $Builtin.Int64, %overflow_check : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%x2 = tuple_extract %sum2 : $(Builtin.Int64, Builtin.Int1), 0
br bb2
bb2:
%sum3 = builtin "sadd_with_overflow_Int64"(%x2 : $Builtin.Int64, %x2 : $Builtin.Int64, %overflow_check : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%x3 = tuple_extract %sum3 : $(Builtin.Int64, Builtin.Int1), 0
br bb3
bb3:
%sum4 = builtin "sadd_with_overflow_Int64"(%x3 : $Builtin.Int64, %x3 : $Builtin.Int64, %overflow_check : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%x4 = tuple_extract %sum4 : $(Builtin.Int64, Builtin.Int1), 0
br bb4
bb4:
%sum5 = builtin "sadd_with_overflow_Int64"(%x4 : $Builtin.Int64, %x4 : $Builtin.Int64, %overflow_check : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%x5 = tuple_extract %sum5 : $(Builtin.Int64, Builtin.Int1), 0
br bb5
bb5:
%sum6 = builtin "sadd_with_overflow_Int64"(%x5 : $Builtin.Int64, %x5 : $Builtin.Int64, %overflow_check : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%x6 = tuple_extract %sum6 : $(Builtin.Int64, Builtin.Int1), 0
br bb6
bb6:
return %x6 : $Builtin.Int64
}
// Check that the compiler does not unroll loops containing calls
// of big inlinable functions.
//
// CHECK-LABEL: sil [ossa] @unroll_with_apply :
// CHECK: apply
// CHECK-LABEL: } // end sil function 'unroll_with_apply'
sil [ossa] @unroll_with_apply : $@convention(thin) () -> () {
bb0:
%0 = integer_literal $Builtin.Int64, 0
%1 = integer_literal $Builtin.Int64, 1
%2 = integer_literal $Builtin.Int64, 20
%3 = integer_literal $Builtin.Int1, 1
%f = function_ref @big_func: $@convention(thin) () -> Builtin.Int64
br bb1(%0 : $Builtin.Int64)
bb1(%4 : $Builtin.Int64):
%r = apply %f() : $@convention(thin) () -> Builtin.Int64
br bb2
bb2:
%5 = builtin "sadd_with_overflow_Int64"(%4 : $Builtin.Int64, %1 : $Builtin.Int64, %3 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%6 = tuple_extract %5 : $(Builtin.Int64, Builtin.Int1), 0
%7 = builtin "cmp_eq_Int64"(%6 : $Builtin.Int64, %2 : $Builtin.Int64) : $Builtin.Int1
cond_br %7, bb4, bb3
bb3:
br bb1(%6 : $Builtin.Int64)
bb4:
%8 = tuple()
return %8 : $()
}
// CHECK-LABEL: sil [ossa] @sumKlassId :
// CHECK: [[FUNC:%.*]] = function_ref @getElement :
// CHECK: apply [[FUNC]]
// CHECK: apply [[FUNC]]
// CHECK: apply [[FUNC]]
// CHECK: apply [[FUNC]]
// CHECK-LABEL: } // end sil function 'sumKlassId'
sil [ossa] @sumKlassId : $@convention(thin) (@guaranteed Array<Klass>) -> Int64 {
bb0(%0 : @guaranteed $Array<Klass>):
%1 = integer_literal $Builtin.Int1, -1
%2 = struct $Bool (%1 : $Builtin.Int1)
%3 = integer_literal $Builtin.Int64, 0
%4 = integer_literal $Builtin.Int64, 4
%5 = integer_literal $Builtin.Int1, -1
%6 = integer_literal $Builtin.Int64, 1
%7 = function_ref @getElement : $@convention(method) (Int64, Bool, _DependenceToken, @guaranteed Array<Klass>) -> @owned Klass
%15 = struct $_DependenceToken ()
br bb1(%3 : $Builtin.Int64, %3 : $Builtin.Int64)
bb1(%17 : $Builtin.Int64, %18 : $Builtin.Int64):
%19 = struct $Int64 (%18 : $Builtin.Int64)
%20 = builtin "sadd_with_overflow_Int64"(%18 : $Builtin.Int64, %6 : $Builtin.Int64, %5 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%21 = tuple_extract %20 : $(Builtin.Int64, Builtin.Int1), 0
%22 = apply %7(%19, %2, %15, %0) : $@convention(method) (Int64, Bool, _DependenceToken, @guaranteed Array<Klass>) -> @owned Klass
%borrow = begin_borrow %22 : $Klass
%23 = ref_element_addr %borrow : $Klass, #Klass.id
%24 = load [trivial] %23 : $*Int64
end_borrow %borrow : $Klass
destroy_value %22 : $Klass
%26 = struct_extract %24 : $Int64, #Int64.value
%27 = builtin "sadd_with_overflow_Int64"(%17 : $Builtin.Int64, %26 : $Builtin.Int64, %5 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%28 = tuple_extract %27 : $(Builtin.Int64, Builtin.Int1), 0
%31 = builtin "cmp_eq_Int64"(%21 : $Builtin.Int64, %4 : $Builtin.Int64) : $Builtin.Int1
cond_br %31, bb3, bb2
bb2:
br bb1(%28 : $Builtin.Int64, %21 : $Builtin.Int64)
bb3:
%34 = struct $Int64 (%28 : $Builtin.Int64)
return %34 : $Int64
}
sil [ossa] @collectKlass : $@convention(thin) (@owned Klass) -> ()
// CHECK-LABEL: sil [ossa] @testWithCopy :
// CHECK: [[FUNC:%.*]] = function_ref @getElement :
// CHECK: apply [[FUNC]]
// CHECK: apply [[FUNC]]
// CHECK: apply [[FUNC]]
// CHECK: apply [[FUNC]]
// CHECK-LABEL: } // end sil function 'testWithCopy'
sil [ossa] @testWithCopy : $@convention(thin) (@guaranteed Array<Klass>) -> () {
bb0(%0 : @guaranteed $Array<Klass>):
%1 = integer_literal $Builtin.Int1, -1
%2 = struct $Bool (%1 : $Builtin.Int1)
%3 = integer_literal $Builtin.Int64, 0
%4 = integer_literal $Builtin.Int64, 4
%5 = integer_literal $Builtin.Int1, -1
%6 = integer_literal $Builtin.Int64, 1
%7 = function_ref @getElement : $@convention(method) (Int64, Bool, _DependenceToken, @guaranteed Array<Klass>) -> @owned Klass
%8 = function_ref @collectKlass : $@convention(thin) (@owned Klass) -> ()
%15 = struct $_DependenceToken ()
br bb1(%3 : $Builtin.Int64)
bb1(%18 : $Builtin.Int64):
%19 = struct $Int64 (%18 : $Builtin.Int64)
%20 = builtin "sadd_with_overflow_Int64"(%18 : $Builtin.Int64, %6 : $Builtin.Int64, %5 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%21 = tuple_extract %20 : $(Builtin.Int64, Builtin.Int1), 0
%22 = apply %7(%19, %2, %15, %0) : $@convention(method) (Int64, Bool, _DependenceToken, @guaranteed Array<Klass>) -> @owned Klass
%copy = copy_value %22 : $Klass
%res = apply %8(%copy) : $@convention(thin) (@owned Klass) -> ()
destroy_value %22 : $Klass
%31 = builtin "cmp_eq_Int64"(%21 : $Builtin.Int64, %4 : $Builtin.Int64) : $Builtin.Int1
cond_br %31, bb3, bb2
bb2:
br bb1(%21 : $Builtin.Int64)
bb3:
%tup = tuple ()
return %tup : $()
}
// CHECK-LABEL: sil [ossa] @testWithEarlyExit :
// CHECK: [[FUNC:%.*]] = function_ref @getElement :
// CHECK: apply [[FUNC]]
// CHECK-NOT: apply [[FUNC]]
// CHECK-LABEL: } // end sil function 'testWithEarlyExit'
sil [ossa] @testWithEarlyExit : $@convention(thin) (@guaranteed Array<Klass>) -> () {
bb0(%0 : @guaranteed $Array<Klass>):
%1 = integer_literal $Builtin.Int1, -1
%2 = struct $Bool (%1 : $Builtin.Int1)
%3 = integer_literal $Builtin.Int64, 0
%4 = integer_literal $Builtin.Int64, 4
%5 = integer_literal $Builtin.Int1, -1
%6 = integer_literal $Builtin.Int64, 1
%7 = function_ref @getElement : $@convention(method) (Int64, Bool, _DependenceToken, @guaranteed Array<Klass>) -> @owned Klass
%8 = function_ref @collectKlass : $@convention(thin) (@owned Klass) -> ()
%15 = struct $_DependenceToken ()
br bb1(%3 : $Builtin.Int64)
bb1(%18 : $Builtin.Int64):
cond_br undef, bb6, bb2
bb2:
br bb3
bb3:
%19 = struct $Int64 (%18 : $Builtin.Int64)
%20 = builtin "sadd_with_overflow_Int64"(%18 : $Builtin.Int64, %6 : $Builtin.Int64, %5 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%21 = tuple_extract %20 : $(Builtin.Int64, Builtin.Int1), 0
%22 = apply %7(%19, %2, %15, %0) : $@convention(method) (Int64, Bool, _DependenceToken, @guaranteed Array<Klass>) -> @owned Klass
%copy = copy_value %22 : $Klass
%res = apply %8(%copy) : $@convention(thin) (@owned Klass) -> ()
destroy_value %22 : $Klass
%31 = builtin "cmp_eq_Int64"(%21 : $Builtin.Int64, %4 : $Builtin.Int64) : $Builtin.Int1
cond_br %31, bb4, bb3a
bb3a:
br bb5
bb4:
br bb1(%21 : $Builtin.Int64)
bb6:
br bb5
bb5:
%tup = tuple ()
return %tup : $()
}
// CHECK-LABEL: sil [ossa] @testWithNonTrivialBBArg1 :
// CHECK: [[FUNC:%.*]] = function_ref @getElement :
// CHECK: apply [[FUNC]]
// CHECK: apply [[FUNC]]
// CHECK: apply [[FUNC]]
// CHECK: apply [[FUNC]]
// CHECK-LABEL: } // end sil function 'testWithNonTrivialBBArg1'
sil [ossa] @testWithNonTrivialBBArg1 : $@convention(thin) (@owned Array<Klass>) -> () {
bb0(%0 : @owned $Array<Klass>):
%1 = integer_literal $Builtin.Int1, -1
%2 = struct $Bool (%1)
%3 = integer_literal $Builtin.Int64, 0
%4 = integer_literal $Builtin.Int64, 4
%5 = integer_literal $Builtin.Int1, -1
%6 = integer_literal $Builtin.Int64, 1
%7 = function_ref @getElement : $@convention(method) (Int64, Bool, _DependenceToken, @guaranteed Array<Klass>) -> @owned Klass
%8 = function_ref @collectKlass : $@convention(thin) (@owned Klass) -> ()
%9 = struct $_DependenceToken ()
br bb1
bb1:
%11 = begin_borrow %0
br bb2(%11, %3)
bb2(%13 : @reborrow $Array<Klass>, %14 : $Builtin.Int64):
%15 = borrowed %13 from (%0)
%16 = struct $Int64 (%14)
%17 = builtin "sadd_with_overflow_Int64"(%14, %6, %5) : $(Builtin.Int64, Builtin.Int1)
%18 = tuple_extract %17, 0
%19 = apply %7(%16, %2, %9, %15) : $@convention(method) (Int64, Bool, _DependenceToken, @guaranteed Array<Klass>) -> @owned Klass
destroy_value %19
%21 = builtin "cmp_eq_Int64"(%18, %4) : $Builtin.Int1
end_borrow %15
cond_br %21, bb4, bb3
bb3:
%24 = begin_borrow %0
br bb2(%24, %18)
bb4:
destroy_value %0
%27 = tuple ()
return %27
}
// CHECK-LABEL: sil [ossa] @$testWithNonTrivialBBArg2 :
// CHECK: [[FUNC:%.*]] = function_ref @getElement :
// CHECK: apply [[FUNC]]
// CHECK: apply [[FUNC]]
// CHECK: apply [[FUNC]]
// CHECK: apply [[FUNC]]
// CHECK-LABEL: } // end sil function '$testWithNonTrivialBBArg2'
sil [ossa] @$testWithNonTrivialBBArg2 : $@convention(thin) (@guaranteed Array<Klass>) -> () {
bb0(%0 : @guaranteed $Array<Klass>):
%1 = integer_literal $Builtin.Int1, -1
%2 = struct $Bool (%1 : $Builtin.Int1)
%3 = integer_literal $Builtin.Int64, 0
%4 = integer_literal $Builtin.Int64, 4
%5 = integer_literal $Builtin.Int1, -1
%6 = integer_literal $Builtin.Int64, 1
%7 = function_ref @getElement : $@convention(method) (Int64, Bool, _DependenceToken, @guaranteed Array<Klass>) -> @owned Klass
%8 = function_ref @collectKlass : $@convention(thin) (@owned Klass) -> ()
%15 = struct $_DependenceToken ()
br bb1
bb1:
%copy1 = copy_value %0 : $Array<Klass>
br bb2(%copy1 : $Array<Klass>, %3 : $Builtin.Int64)
bb2(%newArr : @owned $Array<Klass>, %18 : $Builtin.Int64):
%19 = struct $Int64 (%18 : $Builtin.Int64)
%20 = builtin "sadd_with_overflow_Int64"(%18 : $Builtin.Int64, %6 : $Builtin.Int64, %5 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%21 = tuple_extract %20 : $(Builtin.Int64, Builtin.Int1), 0
%22 = apply %7(%19, %2, %15, %newArr) : $@convention(method) (Int64, Bool, _DependenceToken, @guaranteed Array<Klass>) -> @owned Klass
destroy_value %22 : $Klass
%31 = builtin "cmp_eq_Int64"(%21 : $Builtin.Int64, %4 : $Builtin.Int64) : $Builtin.Int1
cond_br %31, bb3, bb2a
bb2a:
%copy2 = copy_value %newArr : $Array<Klass>
destroy_value %newArr : $Array<Klass>
br bb2(%copy2 : $Array<Klass>, %21 : $Builtin.Int64)
bb3:
destroy_value %newArr : $Array<Klass>
%tup = tuple ()
return %tup : $()
}
// Ensure we don't trigger presence of address phis verification
sil [ossa] @unroll_with_addr_proj1 : $@convention(thin) (@in_guaranteed WrapperStruct) -> () {
bb0(%0 : $*WrapperStruct):
%1 = integer_literal $Builtin.Int64, 1
%2 = integer_literal $Builtin.Int64, 4
%3 = integer_literal $Builtin.Int1, -1
br bb1(%1 : $Builtin.Int64, %2 : $Builtin.Int64)
bb1(%5 : $Builtin.Int64, %6 : $Builtin.Int64):
%7 = builtin "sadd_with_overflow_Int64"(%5 : $Builtin.Int64, %1 : $Builtin.Int64, %3 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%8 = tuple_extract %7 : $(Builtin.Int64, Builtin.Int1), 0
%9 = builtin "smul_with_overflow_Int64"(%6 : $Builtin.Int64, %2 : $Builtin.Int64, %3 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%10 = tuple_extract %9 : $(Builtin.Int64, Builtin.Int1), 0
%11 = builtin "cmp_slt_Int64"(%8 : $Builtin.Int64, %2 : $Builtin.Int64) : $Builtin.Int1
%12 = struct_element_addr %0 : $*WrapperStruct, #WrapperStruct.val
cond_br %11, bb2, bb3
bb2:
br bb1(%8 : $Builtin.Int64, %10 : $Builtin.Int64)
bb3:
%15 = function_ref @use_inguaranteed : $@convention(thin) (@in_guaranteed Klass) -> ()
%16 = apply %15(%12) : $@convention(thin) (@in_guaranteed Klass) -> ()
%17 = tuple ()
return %17 : $()
}
// Ensure we don't trigger presence of address phis verification
sil [ossa] @unroll_with_addr_proj2 : $@convention(thin) (@in_guaranteed WrapperStruct) -> () {
bb0(%0 : $*WrapperStruct):
%1 = alloc_stack $WrapperStruct
%2 = integer_literal $Builtin.Int64, 1
%3 = integer_literal $Builtin.Int64, 4
%4 = integer_literal $Builtin.Int1, -1
%5 = load [copy] %0 : $*WrapperStruct
store %5 to [init] %1 : $*WrapperStruct
br bb1(%2 : $Builtin.Int64, %3 : $Builtin.Int64)
bb1(%8 : $Builtin.Int64, %9 : $Builtin.Int64):
%10 = builtin "sadd_with_overflow_Int64"(%8 : $Builtin.Int64, %2 : $Builtin.Int64, %4 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%11 = tuple_extract %10 : $(Builtin.Int64, Builtin.Int1), 0
%12 = builtin "smul_with_overflow_Int64"(%9 : $Builtin.Int64, %3 : $Builtin.Int64, %4 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%13 = tuple_extract %12 : $(Builtin.Int64, Builtin.Int1), 0
%14 = builtin "cmp_slt_Int64"(%11 : $Builtin.Int64, %3 : $Builtin.Int64) : $Builtin.Int1
%15 = function_ref @get_wrapper_struct : $@convention(thin) (@inout WrapperStruct) -> ()
%16 = apply %15(%1) : $@convention(thin) (@inout WrapperStruct) -> ()
%17 = struct_element_addr %1 : $*WrapperStruct, #WrapperStruct.val
cond_br %14, bb2, bb3
bb2:
br bb1(%11 : $Builtin.Int64, %13 : $Builtin.Int64)
bb3:
%20 = function_ref @use_inguaranteed : $@convention(thin) (@in_guaranteed Klass) -> ()
%21 = apply %20(%17) : $@convention(thin) (@in_guaranteed Klass) -> ()
destroy_addr %1 : $*WrapperStruct
dealloc_stack %1 : $*WrapperStruct
%24 = tuple ()
return %24 : $()
}