Files
swift-mirror/test/SILOptimizer/looprotate.sil
Arnold Schwaighofer 876cea81ae SIL: Add an allowed access kind to the opened value of an open_existential_addr instruction
Once we move to a copy-on-write implementation of existential value buffers we
can no longer consume or destroy values of an opened existential unless the
buffer is uniquely owned.

Therefore we need to track the allowed operation on opened values.

Add qualifiers "mutable_access" and "immutable_access" to open_existential_addr
instructions to indicate the allowed access to the opened value.

Once we move to a copy-on-write implementation, an "open_existential_addr
mutable_access" instruction will ensure unique ownership of the value buffer.
2017-02-15 14:23:12 -08:00

386 lines
15 KiB
Plaintext

// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all -loop-rotate %s | %FileCheck %s
sil_stage canonical
import Builtin
import Swift
// CHECK-LABEL: looprotate
// CHECK: bb0(%0 : $Int32, %1 : $Bar):
// CHECK: class_method
// CHECK: cond_br {{.*}}, bb3({{.*}} : $Builtin.Int32), bb1
// CHECK: bb1:
// CHECK: br bb2({{.*}} : $Builtin.Int32, {{.*}} : $Builtin.Int32, {{.*}}: $Int32)
// CHECK: bb2({{.*}}: $Builtin.Int32, {{.*}} : $Builtin.Int32, {{.*}} : $Int32):
// CHECK: class_method
// CHECK: cond_br {{%.*}}, bb3({{.*}} : $Builtin.Int32), bb2({{.*}} : $Builtin.Int32, {{.*}} : $Builtin.Int32, {{.*}} : $Int32)
// CHECK: bb3({{.*}} : $Builtin.Int32):
// CHECK: [[STRUCT:%.*]] = struct $Int32 ({{%.*}} : $Builtin.Int32)
// CHECK: return [[STRUCT]] : $Int32
protocol P {
func boo() -> Int64
}
class Bar {
func boo() -> Int64
func foo()
@objc func foo_objc()
}
sil @_TFC4main3Bar3foofS0_FT_T_ : $@convention(method) (@guaranteed Bar) -> ()
sil @_TFC4main3Bar3boofS0_FT_T_ : $@convention(method) (@guaranteed Bar) -> Int64
sil @_TFC4main3Bar3foo_objcfS0_FT_T_ : $@convention(objc_method) (Bar) -> ()
sil_vtable Bar {
#Bar.boo!1: _TFC4main3Bar3boofS0_FT_T_
#Bar.foo!1: _TFC4main3Bar3foofS0_FT_T_
#Bar.foo_objc!1: _TFC4main3Bar3foofS0_FT_T_
}
sil @looprotate : $@convention(thin) (Int32, @owned Bar) -> Int32 {
bb0(%0 : $Int32, %25: $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>, %30a : $*Bool)
bb1(%4 : $Builtin.Int32, %5 : $Builtin.Int32, %26: $Bar, %31 : $<τ_0_0> { var τ_0_0 } <Bool>, %32 : $*Bool):
%24 = class_method %26 : $Bar, #Bar.foo!1 : (Bar) -> () -> (), $@convention(method) (@guaranteed Bar) -> () // user: %6
%27 = apply %24(%25) : $@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.1, %6 : $Int32
%16 = unchecked_enum_data %15 : $Optional<Int32>, #Optional.some!enumelt.1
%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>, %32 : $*Bool)
bb3:
%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: looprotate2
// CHECK: return
sil @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(%4 : $Builtin.Int32), 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.1, %6 : $Int32
%16 = unchecked_enum_data %15 : $Optional<Int32>, #Optional.some!enumelt.1
%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 (%23 : $Builtin.Int32) :
%24 = struct $Int32 (%23 : $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 @looprotate_with_opened_archetype
// CHECK: return
sil @looprotate_with_opened_archetype : $@convention(thin) (Int32, @in 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
br bb1(%1 : $Builtin.Int32, %2 : $Builtin.Int32, %25: $*P, %30 : $<τ_0_0> { var τ_0_0 } <Bool>, %30a : $*Bool)
bb1(%4 : $Builtin.Int32, %5 : $Builtin.Int32, %26: $*P, %31 : $<τ_0_0> { var τ_0_0 } <Bool>, %32 : $*Bool):
%111 = witness_method $@opened("C22498FA-CABF-11E5-B9A9-685B35C48C83") P, #P.boo!1, %40 : $*@opened("C22498FA-CABF-11E5-B9A9-685B35C48C83") P : $@convention(witness_method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64
%122 = apply %111<@opened("C22498FA-CABF-11E5-B9A9-685B35C48C83") P>(%40) : $@convention(witness_method) <τ_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.1, %6 : $Int32
%16 = unchecked_enum_data %15 : $Optional<Int32>, #Optional.some!enumelt.1
%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: $*P, %31 : $<τ_0_0> { var τ_0_0 } <Bool>, %32 : $*Bool)
bb3:
%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: @testnested
// CHECK: return
sil @testnested : $@convention(thin) () -> () {
bb0:
br bb1(undef : $Builtin.Int32, undef : $Builtin.Int32) // id: %0
bb1(%1 : $Builtin.Int32, %2 : $Builtin.Int32): // Preds: bb0 bb2
%3 = integer_literal $Builtin.Int1, 0 // user: %4
cond_br %3, bb2(%1 : $Builtin.Int32, %2 : $Builtin.Int32), bb10 // id: %4
bb2(%5 : $Builtin.Int32, %6 : $Builtin.Int32): // Preds: bb1 bb8
%7 = integer_literal $Builtin.Int1, -1 // user: %8
cond_br %7, bb3, bb1(%5 : $Builtin.Int32, %6 : $Builtin.Int32) // id: %8
bb3: // Preds: bb2
%9 = integer_literal $Builtin.Int32, 0 // user: %11
%10 = integer_literal $Builtin.Int32, 1 // users: %14, %23
br bb4(%9 : $Builtin.Int32) // id: %11
bb4(%12 : $Builtin.Int32): // Preds: bb3 bb7
%14 = builtin "cmp_eq_Word"(%12 : $Builtin.Int32, %10 : $Builtin.Int32) : $Builtin.Int1 // user: %15
cond_br %14, bb5, bb9 // id: %15
bb5: // Preds: bb4
%16 = enum $Optional<Int32>, #Optional.none!enumelt // user: %17
br bb6(%12 : $Builtin.Int32, %16 : $Optional<Int32>) // id: %17
bb6(%18 : $Builtin.Int32, %19 : $Optional<Int32>): // Preds: bb5 bb9
switch_enum %19 : $Optional<Int32>, case #Optional.some!enumelt.1: bb7, case #Optional.none!enumelt: bb8 // id: %20
bb7: // Preds: bb6
%21 = unchecked_enum_data %19 : $Optional<Int32>, #Optional.some!enumelt.1
br bb4(%18 : $Builtin.Int32) // id: %22
bb8: // Preds: bb6
br bb2(%18 : $Builtin.Int32, %10 : $Builtin.Int32) // id: %23
bb9: // Preds: bb4
%24 = struct $Int32 (%12 : $Builtin.Int32) // user: %32
%25 = integer_literal $Builtin.Int32, 1 // user: %28
%27 = integer_literal $Builtin.Int1, -1 // user: %28
%28 = builtin "sadd_with_overflow_Word"(%12 : $Builtin.Int32, %25 : $Builtin.Int32, %27 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1) // users: %29, %30
%29 = tuple_extract %28 : $(Builtin.Int32, Builtin.Int1), 0 // user: %33
%30 = tuple_extract %28 : $(Builtin.Int32, Builtin.Int1), 1 // user: %31
cond_fail %30 : $Builtin.Int1 // id: %31
%32 = enum $Optional<Int32>, #Optional.some!enumelt.1, %24 : $Int32 // user: %33
br bb6(%29 : $Builtin.Int32, %32 : $Optional<Int32>) // id: %33
bb10: // Preds: bb1
%34 = tuple () // user: %35
return %34 : $() // id: %35
}
// CHECK-LABEL: dont_looprotate_objc
// CHECK: bb0{{.*}}:
// CHECK: br bb1
// CHECK: bb1{{.*}}:
// CHECK: cond_br {{.*}}, bb3, bb2
// CHECK: bb2:
// CHECK: br bb1
// CHECK: bb3:
// CHECK: return
sil @dont_looprotate_objc : $@convention(thin) (Int32, @owned Bar) -> Int32 {
bb0(%0 : $Int32, %25: $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: $Bar):
%101 = class_method [volatile] %100 : $Bar, #Bar.foo!1.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.1, %6 : $Int32
%16 = unchecked_enum_data %15 : $Optional<Int32>, #Optional.some!enumelt.1
%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:
%23 = struct $Int32 (%4 : $Builtin.Int32)
return %23 : $Int32
}
sil @readInt : $@convention(thin) (Int32) -> () {
bb0(%a0 : $Int32):
%t1 = tuple ()
return %t1 : $()
}
// Test the SSA Updater utility: replaceBBArgWithCast().
//
// CHECK-LABEL: struct_arg
// CHECK: bb0:
// CHECK: cond_br
// CHECK: bb1:
// CHECK: br bb2
// CHECK: bb2({{%.*}} : $Builtin.Int32, [[WORD:%.*]] : $Builtin.Int32
// CHECK: [[STRUCT:%[0-9]+]] = struct $Int32 ([[WORD]]
// CHECK: apply {{.*}}([[STRUCT]])
// CHECK: cond_br
// CHECK: bb3({{.*}}):
// CHECK: return
sil @struct_arg : $@convention(thin) () -> () {
bb0:
%c0 = integer_literal $Builtin.Int32, 0
%c100 = integer_literal $Builtin.Int32, 100
br bb1(%c0 : $Builtin.Int32, %c0 : $Builtin.Int32)
bb1(%a1 : $Builtin.Int32, %a0 : $Builtin.Int32):
%s0 = struct $Int32 (%a0 : $Builtin.Int32)
%z1 = builtin "cmp_eq_Word"(%a0 : $Builtin.Int32, %c100 : $Builtin.Int32) : $Builtin.Int1
cond_br %z1, bb2, bb3
bb3:
%c1 = integer_literal $Builtin.Int32, 1
%z2 = integer_literal $Builtin.Int1, 0
%d1 = builtin "sadd_with_overflow_Word"(%a0 : $Builtin.Int32, %c1 : $Builtin.Int32, %z2 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
%t1 = tuple_extract %d1 : $(Builtin.Int32, Builtin.Int1), 0
%d2 = builtin "sadd_with_overflow_Word"(%a1 : $Builtin.Int32, %c1 : $Builtin.Int32, %z2 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
%t2 = tuple_extract %d2 : $(Builtin.Int32, Builtin.Int1), 0
%f1 = function_ref @readInt : $@convention(thin) (Int32) -> ()
%l1 = apply %f1(%s0) : $@convention(thin) (Int32) -> ()
br bb1(%t2 : $Builtin.Int32, %t1 : $Builtin.Int32)
bb2:
%14 = struct $Int32 (%a1 : $Builtin.Int32)
%t3 = tuple ()
return %t3 : $()
}
// Test LoopRotate insertBackedgeBlock.
//
// CHECK-LABEL: insert_backedge_block
// PreLoop Check
// CHECK: bb0(%
// CHECK: cond_br
// Loop Header
// CHECK: bb1:
// CHECK-NEXT: br bb2
// CHECK: bb2:
// CHECK: cond_br %{{.*}}, bb3, bb5
// CHECK: bb3:
// CHECK: apply
// CHECK: br bb4(
// CHECK: bb4(%[[ARG:[0-9]*]]
// CHECK-NEXT: cond_br %{{.*}}, bb6, bb2
// CHECK: bb5:
// CHECK: apply
// CHECK: br bb4(%
// CHECK: bb6
// CHECK: return
sil @insert_backedge_block : $@convention(thin) (@owned @callee_owned () -> Builtin.Int32) -> () {
bb0(%0 : $@callee_owned () -> Builtin.Int32):
%r0 = apply %0() : $@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:
%r1 = apply %0() : $@callee_owned () -> Builtin.Int32
br bb1(%r1: $Builtin.Int32)
bb4:
%r2 = apply %0() : $@callee_owned () -> Builtin.Int32
br bb1(%r2: $Builtin.Int32)
bb5:
%r3 = tuple ()
return %r3 : $()
}
// CHECK-LABEL: sil @dont_rotate_single_basic_block_loop
// CHECK: bb0({{.*}}):
// CHECK: br bb1
// CHECK: bb1:
// CHECK: cond_br {{.*}}, bb1, bb2
// CHECK: bb2:
// CHECK: return
sil @dont_rotate_single_basic_block_loop : $@convention(thin) (Builtin.Int1) -> () {
bb0(%0 : $Builtin.Int1):
br bb1
bb1:
cond_br %0, bb1, bb2
bb2:
%1 = tuple ()
return %1 : $()
}
// CHECK-LABEL: sil @dont_rotate_single_basic_block_loop_split_backedge
// CHECK: bb0({{.*}}):
// CHECK: br bb1
// CHECK: bb1:
// CHECK: cond_br {{.*}}, bb2, bb3
// CHECK: bb2:
// CHECK: br bb1
// CHECK: bb3:
// CHECK: return
sil @dont_rotate_single_basic_block_loop_split_backedge : $@convention(thin) (Builtin.Int1) -> () {
bb0(%0 : $Builtin.Int1):
br bb1
bb1:
cond_br %0, bb2, bb3
bb2:
br bb1
bb3:
%1 = tuple ()
return %1 : $()
}