mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Type annotations for instruction operands are omitted, e.g. ``` %3 = struct $S(%1, %2) ``` Operand types are redundant anyway and were only used for sanity checking in the SIL parser. But: operand types _are_ printed if the definition of the operand value was not printed yet. This happens: * if the block with the definition appears after the block where the operand's instruction is located * if a block or instruction is printed in isolation, e.g. in a debugger The old behavior can be restored with `-Xllvm -sil-print-types`. This option is added to many existing test files which check for operand types in their check-lines.
3283 lines
96 KiB
Plaintext
3283 lines
96 KiB
Plaintext
// RUN: %target-sil-opt -sil-print-types -enable-objc-interop -enforce-exclusivity=none -enable-sil-verify-all %s -jumpthread-simplify-cfg | %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
|
|
|
|
///////////////////////
|
|
// Type Declarations //
|
|
///////////////////////
|
|
|
|
class foo {
|
|
var a: Int
|
|
deinit
|
|
init()
|
|
}
|
|
|
|
sil @use_foo : $@convention(thin) (@owned foo) -> ()
|
|
|
|
|
|
///////////
|
|
// Tests //
|
|
///////////
|
|
|
|
// CHECK-LABEL: sil @test_dead_block
|
|
// CHECK-NEXT: bb0:
|
|
// CHECK-NEXT: unreachable
|
|
// CHECK-NEXT: }
|
|
sil @test_dead_block : $() -> () {
|
|
bb0:
|
|
unreachable
|
|
|
|
bb1:
|
|
%4 = integer_literal $Builtin.Int64, 1
|
|
br bb2
|
|
|
|
bb2:
|
|
%5 = struct $Int64 (%4 : $Builtin.Int64)
|
|
unreachable
|
|
}
|
|
|
|
// CHECK-LABEL: @release_in_arcinert_termination_block
|
|
// CHECK: bb0
|
|
// CHECK: unreachable
|
|
// CHECK: }
|
|
sil @release_in_arcinert_termination_block : $(@owned foo) -> () {
|
|
bb0(%0 : $foo):
|
|
br bb1
|
|
|
|
bb1:
|
|
strong_release %0 : $foo
|
|
unreachable
|
|
}
|
|
|
|
// CHECK-LABEL: @release_in_nonarcinert_termination_block
|
|
// CHECK: bb0
|
|
// CHECK: strong_release
|
|
// CHECK: apply
|
|
// CHECK: unreachable
|
|
sil @release_in_nonarcinert_termination_block : $(@owned foo) -> () {
|
|
bb0(%0 : $foo):
|
|
br bb1
|
|
|
|
bb1:
|
|
strong_release %0 : $foo
|
|
%1 = function_ref @use_foo : $@convention(thin) (@owned foo) -> ()
|
|
apply %1(%0) : $@convention(thin) (@owned foo) -> ()
|
|
unreachable
|
|
}
|
|
|
|
// CHECK-LABEL: @test_single_pred_block
|
|
// CHECK: bb3([[ARG:%[0-9]+]] : $Builtin.Int64):
|
|
// CHECK: struct $Int64
|
|
// CHECK-NEXT: return
|
|
sil @test_single_pred_block : $@convention(thin) (Builtin.Int1) -> Int64 {
|
|
bb0(%0 : $Builtin.Int1):
|
|
cond_br %0, bb1, bb3
|
|
|
|
bb1:
|
|
%4 = integer_literal $Builtin.Int64, 1
|
|
br bb2(%4 : $Builtin.Int64)
|
|
|
|
bb3:
|
|
%9 = integer_literal $Builtin.Int64, 2
|
|
br bb2(%9 : $Builtin.Int64)
|
|
|
|
bb2(%6 : $Builtin.Int64):
|
|
%7 = struct $Int64 (%6 : $Builtin.Int64)
|
|
br bb4(%7 : $Int64)
|
|
|
|
bb4(%8 : $Int64):
|
|
return %8 : $Int64
|
|
}
|
|
|
|
// CHECK-LABEL: sil @canonicalize_not_branch
|
|
// CHECK: bb0([[ARG:%.*]] : $Builtin.Int1):
|
|
// CHECK: cond_br [[ARG]], bb2, bb1
|
|
// CHECK: bb1:
|
|
// CHECK: integer_literal {{.*}} 2
|
|
// CHECK: br
|
|
// CHECK: bb2:
|
|
// CHECK: integer_literal {{.*}} 3
|
|
// CHECK: br
|
|
|
|
sil @canonicalize_not_branch : $@convention(thin) (Builtin.Int1) -> (Builtin.Int32) {
|
|
bb0(%0 : $Builtin.Int1):
|
|
%1 = integer_literal $Builtin.Int1, -1
|
|
%2 = builtin "xor_Int1"(%0 : $Builtin.Int1, %1 : $Builtin.Int1) : $Builtin.Int1
|
|
cond_br %2, bb1, bb2
|
|
|
|
bb1:
|
|
%4 = integer_literal $Builtin.Int32, 2
|
|
br bb3(%4 : $Builtin.Int32)
|
|
|
|
bb2:
|
|
%5 = integer_literal $Builtin.Int32, 3
|
|
br bb3(%5 : $Builtin.Int32)
|
|
|
|
bb3(%6 : $Builtin.Int32):
|
|
return %6 : $Builtin.Int32
|
|
}
|
|
|
|
// CHECK-LABEL: sil @canonicalize_not_branch_expect
|
|
// CHECK: bb0([[ARG:%.*]] : $Builtin.Int1):
|
|
// CHECK: [[ZERO:%.*]] = integer_literal $Builtin.Int1, 0
|
|
// CHECK: [[EXPECT:%.*]] = builtin "int_expect_Int1"([[ARG]] : $Builtin.Int1, [[ZERO]]
|
|
// CHECK: cond_br [[EXPECT]], bb2, bb1
|
|
// CHECK: bb1:
|
|
// CHECK: integer_literal {{.*}} 2
|
|
// CHECK: br
|
|
// CHECK: bb2:
|
|
// CHECK: integer_literal {{.*}} 3
|
|
// CHECK: br
|
|
|
|
sil @canonicalize_not_branch_expect : $@convention(thin) (Builtin.Int1) -> (Builtin.Int32) {
|
|
bb0(%0 : $Builtin.Int1):
|
|
%1 = integer_literal $Builtin.Int1, -1
|
|
%2 = builtin "xor_Int1"(%0 : $Builtin.Int1, %1 : $Builtin.Int1) : $Builtin.Int1
|
|
%3 = builtin "int_expect_Int1"(%2 : $Builtin.Int1, %1 : $Builtin.Int1) : $Builtin.Int1
|
|
cond_br %3, bb1, bb2
|
|
|
|
bb1:
|
|
%4 = integer_literal $Builtin.Int32, 2
|
|
br bb3(%4 : $Builtin.Int32)
|
|
|
|
bb2:
|
|
%5 = integer_literal $Builtin.Int32, 3
|
|
br bb3(%5 : $Builtin.Int32)
|
|
|
|
bb3(%6 : $Builtin.Int32):
|
|
return %6 : $Builtin.Int32
|
|
}
|
|
|
|
enum BoolLike { case true_, false_ }
|
|
|
|
// func testThread(a : BoolLike) -> Int32 {
|
|
// if a { return 42 } else { return 17 } }
|
|
//
|
|
/// CHECK-LABEL: sil @testThread
|
|
sil @testThread : $@convention(thin) (@in BoolLike) -> Int64 {
|
|
bb0(%0 : $*BoolLike):
|
|
// CHECK: switch_enum_addr %0 : $*BoolLike, case #BoolLike.true_!enumelt: bb1, case #BoolLike.false_!enumelt: bb2
|
|
switch_enum_addr %0 : $*BoolLike, case #BoolLike.true_!enumelt: bb1, case #BoolLike.false_!enumelt: bb3
|
|
|
|
bb1:
|
|
%4 = integer_literal $Builtin.Int1, -1
|
|
br bb2(%4 : $Builtin.Int1)
|
|
|
|
bb2(%6 : $Builtin.Int1):
|
|
br bb4
|
|
|
|
bb3:
|
|
%8 = integer_literal $Builtin.Int1, 0
|
|
br bb2(%8 : $Builtin.Int1)
|
|
|
|
bb4:
|
|
cond_br %6, bb5, bb6
|
|
|
|
bb5:
|
|
%11 = metatype $@thin Int64.Type
|
|
%12 = integer_literal $Builtin.Int64, 42
|
|
%13 = struct $Int64 (%12 : $Builtin.Int64)
|
|
br bb7(%13 : $Int64)
|
|
|
|
bb6:
|
|
%15 = metatype $@thin Int64.Type
|
|
%16 = integer_literal $Builtin.Int64, 17
|
|
%17 = struct $Int64 (%16 : $Builtin.Int64)
|
|
br bb7(%17 : $Int64)
|
|
|
|
bb7(%19 : $Int64):
|
|
return %19 : $Int64
|
|
}
|
|
|
|
// func testThread2(a : Int32) -> Int32 {
|
|
// enum b = (a ? _true : _false)
|
|
// if b == _true { return 42 } else { return 17 }
|
|
//
|
|
|
|
/// CHECK-LABEL: sil @testThread2
|
|
/// CHECK: bb0([[COND:%.*]] : {{.*}}):
|
|
/// CHECK: cond_br [[COND]], bb1, bb2
|
|
/// CHECK: bb1:
|
|
/// CHECK: integer_literal $Builtin.Int64, 42
|
|
/// CHeCK: br bb3
|
|
/// CHECK: bb2:
|
|
/// CHECK: integer_literal $Builtin.Int64, 17
|
|
/// CHECK: br bb3
|
|
/// CHECK: bb3
|
|
/// CHECK: return
|
|
|
|
sil @testThread2 : $@convention(thin) (Builtin.Int1) -> Int64 {
|
|
bb0(%0 : $Builtin.Int1):
|
|
%t = integer_literal $Builtin.Int1, 1
|
|
%f = integer_literal $Builtin.Int1, 0
|
|
cond_br %0, bb1, bb2
|
|
|
|
bb1:
|
|
%4 = enum $BoolLike, #BoolLike.true_!enumelt
|
|
br bb3(%4 : $BoolLike)
|
|
|
|
bb2:
|
|
%8 = enum $BoolLike, #BoolLike.false_!enumelt
|
|
br bb3(%8 : $BoolLike)
|
|
|
|
bb3(%6 : $BoolLike):
|
|
%100 = select_enum %6 : $BoolLike, case #BoolLike.true_!enumelt: %t, case #BoolLike.false_!enumelt: %f : $Builtin.Int1
|
|
br bb4
|
|
|
|
bb4:
|
|
cond_br %100, bb5, bb6
|
|
|
|
bb5:
|
|
%11 = metatype $@thin Int64.Type
|
|
%12 = integer_literal $Builtin.Int64, 42
|
|
%13 = struct $Int64 (%12 : $Builtin.Int64)
|
|
br bb7(%13 : $Int64)
|
|
|
|
bb6:
|
|
%15 = metatype $@thin Int64.Type
|
|
%16 = integer_literal $Builtin.Int64, 17
|
|
%17 = struct $Int64 (%16 : $Builtin.Int64)
|
|
br bb7(%17 : $Int64)
|
|
|
|
bb7(%19 : $Int64):
|
|
return %19 : $Int64
|
|
}
|
|
|
|
// func testThread3(a : Int32) -> Int32 {
|
|
// (enum b, val) = (a ? (_true, 16) : (_false, 17))
|
|
// if b == true { return 42 } else { return v } }
|
|
//
|
|
|
|
|
|
/// CHECK-LABEL: sil @testThread3
|
|
/// CHECK: bb0([[COND:%.*]] : {{.*}}):
|
|
/// CHECK: cond_br [[COND]], bb1, bb2
|
|
/// CHECK: bb1:
|
|
/// CHECK: integer_literal $Builtin.Int64, 42
|
|
/// CHeCK: br bb3
|
|
/// CHECK: bb2:
|
|
/// CHECK: integer_literal $Builtin.Int64, 17
|
|
/// CHECK: br bb3
|
|
/// CHECK: bb3
|
|
/// CHECK: return
|
|
|
|
sil @testThread3 : $@convention(thin) (Builtin.Int1) -> Int64 {
|
|
bb0(%0 : $Builtin.Int1):
|
|
%t = integer_literal $Builtin.Int1, 1
|
|
%f = integer_literal $Builtin.Int1, 0
|
|
cond_br %0, bb1, bb2
|
|
|
|
bb1:
|
|
%4 = enum $BoolLike, #BoolLike.true_!enumelt
|
|
%40 = integer_literal $Builtin.Int64, 16
|
|
br bb3(%4 : $BoolLike, %40 : $Builtin.Int64)
|
|
|
|
bb2:
|
|
%8 = enum $BoolLike, #BoolLike.false_!enumelt
|
|
%80 = integer_literal $Builtin.Int64, 17
|
|
br bb3(%8 : $BoolLike, %80 : $Builtin.Int64)
|
|
|
|
bb3(%6 : $BoolLike, %60 : $Builtin.Int64):
|
|
%100 = select_enum %6 : $BoolLike, case #BoolLike.true_!enumelt: %t, case #BoolLike.false_!enumelt: %f : $Builtin.Int1
|
|
br bb4
|
|
|
|
bb4:
|
|
cond_br %100, bb5, bb6
|
|
|
|
bb5:
|
|
%11 = metatype $@thin Int64.Type
|
|
%12 = integer_literal $Builtin.Int64, 42
|
|
%13 = struct $Int64 (%12 : $Builtin.Int64)
|
|
br bb7(%13 : $Int64)
|
|
|
|
bb6:
|
|
%15 = metatype $@thin Int64.Type
|
|
%17 = struct $Int64 (%60 : $Builtin.Int64)
|
|
br bb7(%17 : $Int64)
|
|
|
|
bb7(%19 : $Int64):
|
|
return %19 : $Int64
|
|
}
|
|
|
|
|
|
|
|
/// CHECK-LABEL: sil @testCondBrFold
|
|
/// CHECK: bb0(
|
|
/// CHECK-NEXT: return %1 : $Int64
|
|
sil @testCondBrFold : $@convention(thin) (Int64, Int64) -> Int64 {
|
|
bb0(%0 : $Int64, %1 : $Int64):
|
|
%8 = integer_literal $Builtin.Int1, 0
|
|
cond_br %8, bb1, bb2
|
|
bb1:
|
|
unreachable
|
|
bb2:
|
|
return %1 : $Int64
|
|
}
|
|
|
|
/// CHECK-LABEL: sil @testSwitchEnumFold
|
|
/// CHECK: bb0(
|
|
/// CHECK-NEXT: return %0 : $Int64
|
|
sil @testSwitchEnumFold : $@convention(thin) (Int64) -> Int64 {
|
|
bb0(%0 : $Int64):
|
|
%1 = enum $BoolLike, #BoolLike.true_!enumelt
|
|
switch_enum %1 : $BoolLike, case #BoolLike.true_!enumelt: bb2, case #BoolLike.false_!enumelt: bb1
|
|
bb1:
|
|
unreachable
|
|
bb2:
|
|
return %0 : $Int64
|
|
}
|
|
|
|
// CHECK-LABEL: @elim_trampoline
|
|
// CHECK: bb0
|
|
// CHECK: cond_br {{.*}}, bb1, bb2
|
|
// CHECK:bb1:
|
|
// CHECK: br bb3(%1
|
|
// CHECK:bb2:
|
|
// CHECK: br bb3(%2
|
|
// CHECK:bb3({{.*}}):
|
|
// CHECK: return
|
|
sil @elim_trampoline : $@convention(thin) (Builtin.Int1, Int64, Int64) -> Int64 {
|
|
bb0(%0 : $Builtin.Int1, %1 : $Int64, %2 : $Int64):
|
|
cond_br %0, bb1(%1 : $Int64), bb2(%2 : $Int64)
|
|
|
|
bb1(%3 : $Int64):
|
|
br bb3(%3 : $Int64)
|
|
|
|
bb2(%4 : $Int64):
|
|
br bb3(%4 : $Int64)
|
|
|
|
bb3(%5 : $Int64):
|
|
return %5 : $Int64
|
|
}
|
|
|
|
// CHECK-LABEL: @elim_trampoline2
|
|
// CHECK-NOT: cond_br %0, bb1(%1 : $Int64), bb1(%1 : $Int64)
|
|
// CHECK: return
|
|
sil @elim_trampoline2 : $@convention(thin) (Builtin.Int1, Int64) -> Int64 {
|
|
bb0(%0 : $Builtin.Int1, %1 : $Int64):
|
|
cond_br %0, bb1(%1 : $Int64), bb2(%1 : $Int64)
|
|
|
|
bb1(%2 : $Int64):
|
|
br bb3(%2 : $Int64)
|
|
|
|
bb2(%3 : $Int64):
|
|
br bb3(%3 : $Int64)
|
|
|
|
bb3(%4 : $Int64):
|
|
return %4 : $Int64
|
|
}
|
|
|
|
// CHECK-LABEL: @elim_trampoline_debug
|
|
// CHECK-NOT: cond_br %0, bb1(%1 : $Int64), bb1(%1 : $Int64)
|
|
// CHECK: return
|
|
sil @elim_trampoline_debug : $@convention(thin) (Builtin.Int1, Int64) -> Int64 {
|
|
bb0(%0 : $Builtin.Int1, %1 : $Int64):
|
|
cond_br %0, bb1(%1 : $Int64), bb2(%1 : $Int64)
|
|
|
|
bb1(%2 : $Int64):
|
|
debug_value %0 : $Builtin.Int1
|
|
br bb3(%2 : $Int64)
|
|
|
|
bb2(%3 : $Int64):
|
|
br bb3(%3 : $Int64)
|
|
|
|
bb3(%4 : $Int64):
|
|
return %4 : $Int64
|
|
}
|
|
|
|
// CHECK-LABEL: @elim_trampoline3
|
|
// CHECK: cond_br %0, bb1, bb2
|
|
// CHECK:bb1:
|
|
// CHECK: br bb3(%1
|
|
// CHECK:bb2:
|
|
// CHECK: br bb3(%2
|
|
// CHECK:bb3({{.*}}):
|
|
// CHECK: return
|
|
sil @elim_trampoline3 : $@convention(thin) (Builtin.Int1, Int64, Int64) -> Int64 {
|
|
bb0(%0 : $Builtin.Int1, %1 : $Int64, %2 : $Int64):
|
|
cond_br %0, bb1(%1 : $Int64), bb2(%2 : $Int64)
|
|
|
|
bb1(%3 : $Int64):
|
|
br bb3(%3 : $Int64)
|
|
|
|
bb2(%4 : $Int64):
|
|
br bb3(%4 : $Int64)
|
|
|
|
bb3(%5 : $Int64):
|
|
br bb4(%5 : $Int64)
|
|
|
|
bb4(%6 : $Int64):
|
|
return %6 : $Int64
|
|
}
|
|
|
|
// Make sure the cond_br is not simplified as it would create
|
|
// a critical edge.
|
|
// CHECK-LABEL: @elim_trampoline4
|
|
// CHECK: cond_br %0, bb2, bb1
|
|
// CHECK: bb1:
|
|
// CHECK: br bb3
|
|
// CHECK: bb2:
|
|
// CHECK-NEXT: br bb3
|
|
// CHECK: bb3
|
|
// CHECK: return
|
|
// CHECK: }
|
|
|
|
sil @external_f : $@convention(thin) () -> ()
|
|
|
|
sil @elim_trampoline4 : $@convention(thin) (Builtin.Int1, Int64, Int64) -> Int64 {
|
|
bb0(%0 : $Builtin.Int1, %1 : $Int64, %2 : $Int64):
|
|
cond_br %0, bb1, bb2
|
|
|
|
bb1:
|
|
br bb3(%1 : $Int64)
|
|
|
|
bb2:
|
|
br bb4(%2 : $Int64)
|
|
|
|
bb4(%4 : $Int64):
|
|
%55 = function_ref @external_f : $@convention(thin) () -> ()
|
|
apply %55() : $@convention(thin) () -> ()
|
|
br bb5(%4: $Int64)
|
|
|
|
bb3(%5 : $Int64):
|
|
br bb5(%5 : $Int64)
|
|
|
|
bb5(%6 : $Int64):
|
|
return %6 : $Int64
|
|
}
|
|
|
|
// Eliminate the trampoline from a cond_br at bb5->bb3->bb4/
|
|
// This becomes a cond_br at bb8->bb5
|
|
// CHECK-LABEL: @elim_trampoline5
|
|
// CHECK: cond_br
|
|
// CHECK: bb5:
|
|
// CHECK: cond_br
|
|
// CHECK: bb8{{.*}}:
|
|
// CHECK: cond_br {{.*}}, bb5, bb9
|
|
// CHECK: bb9:
|
|
// CHECK-NEXT: br bb8
|
|
// CHECK: }
|
|
sil @elim_trampoline5 : $@convention(thin) (Int32) -> () {
|
|
bb0(%0 : $Int32):
|
|
%1 = integer_literal $Builtin.Int32, 0
|
|
%2 = struct_extract %0 : $Int32, #Int32._value
|
|
%3 = builtin "cmp_eq_Int32"(%1 : $Builtin.Int32, %2 : $Builtin.Int32) : $Builtin.Int1
|
|
cond_br %3, bb1, bb2(%1 : $Builtin.Int32)
|
|
|
|
bb1:
|
|
%5 = tuple ()
|
|
return %5 : $()
|
|
|
|
bb2(%7 : $Builtin.Int32):
|
|
%8 = integer_literal $Builtin.Int32, 1
|
|
%9 = integer_literal $Builtin.Int1, 0
|
|
%10 = integer_literal $Builtin.Int32, 2
|
|
br bb5(%1 : $Builtin.Int32)
|
|
|
|
bb3:
|
|
br bb4(%8 : $Builtin.Int32)
|
|
|
|
bb4(%13 : $Builtin.Int32):
|
|
%14 = builtin "cmp_eq_Int32"(%13 : $Builtin.Int32, %2 : $Builtin.Int32) : $Builtin.Int1
|
|
cond_br %14, bb1, bb2(%13 : $Builtin.Int32)
|
|
|
|
bb5(%16 : $Builtin.Int32):
|
|
%17 = builtin "sadd_with_overflow_Int32"(%16 : $Builtin.Int32, %8 : $Builtin.Int32, %9 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
|
|
%18 = tuple_extract %17 : $(Builtin.Int32, Builtin.Int1), 0
|
|
%19 = builtin "cmp_eq_Int32"(%18 : $Builtin.Int32, %10 : $Builtin.Int32) : $Builtin.Int1
|
|
cond_br %19, bb3, bb5(%18 : $Builtin.Int32)
|
|
}
|
|
|
|
// CHECK-LABEL: @elim_trampoline_loop
|
|
// Make sure we are not crashing on this one.
|
|
// CHECK: return
|
|
sil @elim_trampoline_loop : $@convention(thin) (Builtin.Int1, Int64, Int64) -> Int64 {
|
|
bb0(%0 : $Builtin.Int1, %1 : $Int64, %2 : $Int64):
|
|
cond_br %0, bb1(%1 : $Int64), bb2(%2 : $Int64)
|
|
|
|
bb1(%3 : $Int64):
|
|
br bb3(%3 : $Int64)
|
|
|
|
bb2(%4 : $Int64):
|
|
br bb2(%4 : $Int64)
|
|
|
|
bb3(%5 : $Int64):
|
|
br bb4(%5 : $Int64)
|
|
|
|
bb4(%6 : $Int64):
|
|
return %6 : $Int64
|
|
}
|
|
|
|
// CHECK-LABEL: @elim_common_arg
|
|
// CHECK: bb3:
|
|
// CHECK-NEXT: return %1
|
|
sil @elim_common_arg : $@convention(thin) (Builtin.Int1, Int64) -> Int64 {
|
|
bb0(%0 : $Builtin.Int1, %1 : $Int64):
|
|
%f1 = function_ref @external_f : $@convention(thin) () -> ()
|
|
cond_br %0, bb1, bb2
|
|
|
|
bb1:
|
|
apply %f1() : $@convention(thin) () -> ()
|
|
br bb3(%1 : $Int64)
|
|
|
|
bb2:
|
|
apply %f1() : $@convention(thin) () -> ()
|
|
br bb3(%1 : $Int64)
|
|
|
|
bb3(%a1 : $Int64):
|
|
return %a1 : $Int64
|
|
}
|
|
|
|
// CHECK-LABEL: @elim_diamonds
|
|
// CHECK: bb0
|
|
// CHECK-NEXT: return %1
|
|
sil @elim_diamonds : $@convention(thin) (Builtin.Int1, Int64, Int64) -> Int64 {
|
|
bb0(%0 : $Builtin.Int1, %1 : $Int64, %2 : $Int64):
|
|
cond_br %0, bb1(%1 : $Int64), bb2(%1 : $Int64)
|
|
|
|
bb1(%3 : $Int64):
|
|
br bb3(%3 : $Int64)
|
|
|
|
bb2(%4 : $Int64):
|
|
br bb3(%4 : $Int64)
|
|
|
|
bb3(%5 : $Int64):
|
|
cond_br %0, bb4(%5 : $Int64), bb5(%5 : $Int64)
|
|
|
|
bb4(%6 : $Int64):
|
|
br bb6(%6 : $Int64)
|
|
|
|
bb5(%7 : $Int64):
|
|
br bb6(%7 : $Int64)
|
|
|
|
bb6(%8 : $Int64):
|
|
return %8 : $Int64
|
|
}
|
|
|
|
// CHECK-LABEL: @infinite_loop
|
|
// CHECK: bb0
|
|
// CHECK-NEXT: br bb1
|
|
// CHECK: bb1
|
|
// CHECK-NEXT: br bb1
|
|
sil @infinite_loop : $@convention(thin) () -> () {
|
|
bb0:
|
|
br bb1
|
|
bb1:
|
|
br bb1
|
|
}
|
|
|
|
import Builtin
|
|
import Swift
|
|
|
|
// CHECK-LABEL: @dead_loop
|
|
// CHECK-NOT: br bb
|
|
sil @dead_loop : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = integer_literal $Builtin.Int1, 0
|
|
%2 = integer_literal $Builtin.Int1, -1
|
|
cond_br %0, bb1, bb3
|
|
|
|
bb1:
|
|
%5 = integer_literal $Builtin.Int32, 0
|
|
%6 = struct $Int32 (%5 : $Builtin.Int32)
|
|
br bb2(%5 : $Builtin.Int32)
|
|
|
|
bb2(%8 : $Builtin.Int32):
|
|
%9 = integer_literal $Builtin.Int32, 1
|
|
%11 = builtin "sadd_with_overflow_Int32"(%8 : $Builtin.Int32, %9 : $Builtin.Int32, %2 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
|
|
%12 = tuple_extract %11 : $(Builtin.Int32, Builtin.Int1), 0
|
|
%13 = tuple_extract %11 : $(Builtin.Int32, Builtin.Int1), 1
|
|
cond_fail %13 : $Builtin.Int1
|
|
%15 = struct $Int32 (%12 : $Builtin.Int32)
|
|
br bb2(%12 : $Builtin.Int32)
|
|
|
|
bb3:
|
|
%17 = tuple ()
|
|
return %17 : $()
|
|
}
|
|
|
|
// We should be able to compile this down to returning the parameter
|
|
// but we're not quite there yet.
|
|
// CHECK-LABEL: @nop
|
|
sil @nop : $@convention(thin) (Bool) -> Bool {
|
|
bb0(%0 : $Bool):
|
|
%1 = struct_extract %0 : $Bool, #Bool._value
|
|
// CHECK: cond_br %1, bb2, bb1
|
|
cond_br %1, bb1, bb2
|
|
|
|
// CHECK: bb1:
|
|
// CHECK: br bb3
|
|
// CHECK: bb2:
|
|
// CHECK: br bb3
|
|
bb1:
|
|
%3 = integer_literal $Builtin.Int1, 0
|
|
%4 = struct $Bool (%3 : $Builtin.Int1)
|
|
br bb3(%4 : $Bool)
|
|
|
|
bb2:
|
|
%6 = integer_literal $Builtin.Int1, -1
|
|
%7 = struct $Bool (%6 : $Builtin.Int1)
|
|
br bb3(%7 : $Bool)
|
|
|
|
// CHECK: bb3
|
|
bb3(%9 : $Bool):
|
|
// CHECK-NOT: struct_extract
|
|
%10 = struct_extract %9 : $Bool, #Bool._value
|
|
// CHECK: return
|
|
cond_br %10, bb4, bb5
|
|
|
|
// CHECK-NOT: bb4
|
|
bb4:
|
|
%12 = integer_literal $Builtin.Int1, 0
|
|
%13 = struct $Bool (%12 : $Builtin.Int1)
|
|
br bb6(%13 : $Bool)
|
|
|
|
// CHECK-NOT: bb5
|
|
bb5:
|
|
%15 = integer_literal $Builtin.Int1, -1
|
|
%16 = struct $Bool (%15 : $Builtin.Int1)
|
|
br bb6(%16 : $Bool)
|
|
|
|
bb6(%18 : $Bool):
|
|
return %18 : $Bool
|
|
}
|
|
|
|
class C {
|
|
final var value: Int32
|
|
init(v: Int32)
|
|
}
|
|
|
|
// CHECK-LABEL: @redundant_switchenum
|
|
sil @redundant_switchenum : $@convention(thin) (@owned Optional<C>) -> Int32 {
|
|
bb0(%0 : $Optional<C>):
|
|
switch_enum %0 : $Optional<C>, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2
|
|
|
|
// CHECK: bb1:
|
|
bb1:
|
|
%9 = integer_literal $Builtin.Int1, -1
|
|
%10 = struct $Bool (%9 : $Builtin.Int1)
|
|
// CHECK: br [[DEST:[a-zA-Z0-9]+]]
|
|
br bb3(%10 : $Bool)
|
|
|
|
// CHECK: bb2:
|
|
bb2:
|
|
%17 = integer_literal $Builtin.Int1, 0
|
|
%18 = struct $Bool (%17 : $Builtin.Int1)
|
|
// CHECK: br [[DEST]]
|
|
br bb3(%18 : $Bool)
|
|
|
|
// CHECK: [[DEST]]({{.*}}):
|
|
bb3(%12 : $Bool):
|
|
%15 = struct_extract %12 : $Bool, #Bool._value
|
|
// CHECK-NOT: cond_br
|
|
// CHECK: return
|
|
cond_br %15, bb4, bb7
|
|
|
|
// CHECK-NOT: bb4:
|
|
bb4:
|
|
%21 = alloc_stack $Optional<C>
|
|
store %0 to %21 : $*Optional<C>
|
|
// CHECK-NOT: switch_enum
|
|
switch_enum %0 : $Optional<C>, case #Optional.some!enumelt: bb5, case #Optional.none!enumelt: bb6
|
|
|
|
bb5:
|
|
%25 = unchecked_take_enum_data_addr %21 : $*Optional<C>, #Optional.some!enumelt
|
|
%26 = load %25 : $*C
|
|
dealloc_stack %21 : $*Optional<C>
|
|
%29 = ref_element_addr %26 : $C, #C.value
|
|
%30 = load %29 : $*Int32
|
|
br bb8(%30 : $Int32)
|
|
|
|
bb6:
|
|
%34 = builtin "int_trap"() : $()
|
|
unreachable
|
|
|
|
bb7:
|
|
%36 = integer_literal $Builtin.Int32, 0
|
|
%37 = struct $Int32 (%36 : $Builtin.Int32)
|
|
br bb8(%37 : $Int32)
|
|
|
|
bb8(%39 : $Int32):
|
|
release_value %0 : $Optional<C>
|
|
return %39 : $Int32
|
|
}
|
|
|
|
enum A {
|
|
case B, C, D
|
|
}
|
|
|
|
// CHECK-LABEL: cannot_optimize_switch_enum
|
|
sil @cannot_optimize_switch_enum : $@convention(thin) (A) -> () {
|
|
// CHECK: bb0
|
|
bb0(%0 : $A):
|
|
// CHECK: %1 = function_ref
|
|
// CHECK-NEXT: switch_enum %0 : $A, case #A.B!enumelt: bb1, default [[BB:bb[0-9a-zA-Z]+]]
|
|
%f1 = function_ref @external_f : $@convention(thin) () -> ()
|
|
switch_enum %0 : $A, case #A.B!enumelt: bb1, default bb2
|
|
|
|
bb1:
|
|
apply %f1() : $@convention(thin) () -> ()
|
|
br bb5
|
|
|
|
// CHECK: [[BB]]
|
|
bb2:
|
|
// CHECK-NEXT: switch_enum %0
|
|
switch_enum %0 : $A, case #A.C!enumelt: bb3, default bb4
|
|
|
|
bb3:
|
|
apply %f1() : $@convention(thin) () -> ()
|
|
br bb5
|
|
|
|
bb4:
|
|
apply %f1() : $@convention(thin) () -> ()
|
|
br bb5
|
|
|
|
bb5:
|
|
%6 = tuple ()
|
|
return %6 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @simplify_switch_enum1
|
|
// CHECK: bb0:
|
|
// CHECK-NOT: bb[0-9]
|
|
// CHECK: apply
|
|
// CHECK-NEXT: return
|
|
sil @simplify_switch_enum1 : $@convention(thin) () -> Int32 {
|
|
bb0:
|
|
%10 = integer_literal $Builtin.Int32, 2
|
|
%11 = struct $Int32 (%10 : $Builtin.Int32)
|
|
%20 = integer_literal $Builtin.Int32, 3
|
|
%21 = struct $Int32 (%20 : $Builtin.Int32)
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
%12 = enum $Optional<Int32>, #Optional.some!enumelt, %11 : $Int32
|
|
br bb3(%12 : $Optional<Int32>)
|
|
|
|
bb2:
|
|
%22 = enum $Optional<Int32>, #Optional.some!enumelt, %21 : $Int32
|
|
br bb3(%22 : $Optional<Int32>)
|
|
|
|
bb3(%30 : $Optional<Int32>):
|
|
%u = function_ref @unknown : $@convention(thin) () -> ()
|
|
apply %u() : $@convention(thin) () -> ()
|
|
switch_enum %30 : $Optional<Int32>, case #Optional.none!enumelt: bb4, case #Optional.some!enumelt: bb5
|
|
|
|
bb4:
|
|
br bb6(%11 : $Int32)
|
|
|
|
bb5:
|
|
br bb6(%21 : $Int32)
|
|
|
|
bb6(%r : $Int32):
|
|
return %r : $Int32
|
|
}
|
|
|
|
// CHECK-LABEL: sil @simplify_switch_enum2
|
|
// CHECK: bb3([[A:%[0-9]+]] : $Optional<Int32>):
|
|
// CHECK: apply
|
|
// CHECK: [[R:%[0-9]+]] = unchecked_enum_data [[A]] : $Optional<Int32>, #Optional.some!enumelt
|
|
// CHECK: return [[R]]
|
|
sil @simplify_switch_enum2 : $@convention(thin) (Optional<Int32>) -> Int32 {
|
|
bb0(%0 : $Optional<Int32>):
|
|
%10 = integer_literal $Builtin.Int32, 2
|
|
%11 = struct $Int32 (%10 : $Builtin.Int32)
|
|
%20 = integer_literal $Builtin.Int32, 3
|
|
%21 = struct $Int32 (%20 : $Builtin.Int32)
|
|
switch_enum %0 : $Optional<Int32>, case #Optional.none!enumelt: bb1, case #Optional.some!enumelt: bb2
|
|
|
|
bb1:
|
|
%12 = enum $Optional<Int32>, #Optional.some!enumelt, %11 : $Int32
|
|
br bb3(%12 : $Optional<Int32>)
|
|
|
|
bb2:
|
|
br bb3(%0 : $Optional<Int32>)
|
|
|
|
bb3(%30 : $Optional<Int32>):
|
|
%u = function_ref @unknown : $@convention(thin) () -> ()
|
|
apply %u() : $@convention(thin) () -> ()
|
|
switch_enum %30 : $Optional<Int32>, case #Optional.none!enumelt: bb4, case #Optional.some!enumelt: bb5
|
|
|
|
bb4:
|
|
br bb6(%21 : $Int32)
|
|
|
|
bb5(%p : $Int32):
|
|
br bb6(%p : $Int32)
|
|
|
|
bb6(%r : $Int32):
|
|
return %r : $Int32
|
|
}
|
|
|
|
// CHECK-LABEL: sil @identical_switch_enum_dests : $@convention(thin) (Optional<Int32>) -> () {
|
|
// CHECK: bb0(%0 : $Optional<Int32>):
|
|
// CHECK-NEXT: tuple
|
|
// CHECK-NEXT: return
|
|
sil @identical_switch_enum_dests : $@convention(thin) (Optional<Int32>) -> () {
|
|
bb0(%0 : $Optional<Int32>):
|
|
switch_enum %0 : $Optional<Int32>, case #Optional.none!enumelt: bb1, case #Optional.some!enumelt: bb2
|
|
|
|
bb1:
|
|
br bb3
|
|
|
|
bb2:
|
|
br bb3
|
|
|
|
bb3:
|
|
%r = tuple()
|
|
return %r : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @identical_switch_enum_addr_dests : $@convention(thin) (@in Optional<Int32>) -> () {
|
|
// CHECK: bb0(%0 : $*Optional<Int32>):
|
|
// CHECK-NEXT: tuple
|
|
// CHECK-NEXT: return
|
|
sil @identical_switch_enum_addr_dests : $@convention(thin) (@in Optional<Int32>) -> () {
|
|
bb0(%0 : $*Optional<Int32>):
|
|
switch_enum_addr %0 : $*Optional<Int32>, case #Optional.none!enumelt: bb1, case #Optional.some!enumelt: bb2
|
|
|
|
bb1:
|
|
br bb3
|
|
|
|
bb2:
|
|
br bb3
|
|
|
|
bb3:
|
|
%r = tuple()
|
|
return %r : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @identical_switch_value_dests : $@convention(thin) (Builtin.Int32) -> () {
|
|
// CHECK: bb0(%0 : $Builtin.Int32):
|
|
// CHECK-NEXT: integer_literal
|
|
// CHECK-NEXT: integer_literal
|
|
// CHECK-NEXT: tuple
|
|
// CHECK-NEXT: return
|
|
sil @identical_switch_value_dests : $@convention(thin) (Builtin.Int32) -> () {
|
|
bb0(%0 : $Builtin.Int32):
|
|
%1 = integer_literal $Builtin.Int32, 24
|
|
%2 = integer_literal $Builtin.Int32, 25
|
|
switch_value %0 : $Builtin.Int32, case %1: bb1, case %2: bb2
|
|
|
|
bb1:
|
|
br bb3
|
|
|
|
bb2:
|
|
br bb3
|
|
|
|
bb3:
|
|
%r = tuple()
|
|
return %r : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @fold_switch_value : $@convention(thin) () -> Int32 {
|
|
// CHECK: bb0:
|
|
// CHECK-NOT: bb1
|
|
// CHECK: integer_literal $Builtin.Int32, 100
|
|
// CHECK-NEXT: struct
|
|
// CHECK-NEXT: return
|
|
sil @fold_switch_value : $@convention(thin) () -> Int32 {
|
|
bb0:
|
|
%1 = integer_literal $Builtin.Int32, 24
|
|
%2 = integer_literal $Builtin.Int32, 25
|
|
switch_value %1 : $Builtin.Int32, case %1: bb1, case %2: bb2
|
|
|
|
bb1:
|
|
%3 = integer_literal $Builtin.Int32, 100
|
|
br bb3(%3 : $Builtin.Int32)
|
|
|
|
bb2:
|
|
%4 = integer_literal $Builtin.Int32, 200
|
|
br bb3(%4 : $Builtin.Int32)
|
|
|
|
bb3(%5 : $Builtin.Int32):
|
|
%r = struct $Int32 (%5 : $Builtin.Int32)
|
|
return %r : $Int32
|
|
}
|
|
|
|
|
|
// CHECK-LABEL: @dominator_based_simplify_condbr
|
|
// CHECK: integer_literal $Builtin.Int64, 1
|
|
// CHECK-NOT: integer_literal $Builtin.Int64, 2
|
|
// CHECK-NOT: integer_literal $Builtin.Int64, 3
|
|
// CHECK: integer_literal $Builtin.Int64, 4
|
|
// CHECK: return
|
|
sil @dominator_based_simplify_condbr : $@convention(thin) (Builtin.Int1) -> Int64 {
|
|
bb0(%0 : $Builtin.Int1):
|
|
%l1 = integer_literal $Builtin.Int1, -1
|
|
cond_br %0, bb1, bb4
|
|
|
|
bb1:
|
|
cond_br %0, bb2, bb3
|
|
|
|
bb2:
|
|
%1 = integer_literal $Builtin.Int64, 1
|
|
br bb7(%1 : $Builtin.Int64)
|
|
|
|
bb3:
|
|
%2 = integer_literal $Builtin.Int64, 2
|
|
br bb7(%2 : $Builtin.Int64)
|
|
|
|
bb4:
|
|
// expect-intrinsics should be transparent for checking the condition.
|
|
%x1 = builtin "int_expect_Int1"(%0 : $Builtin.Int1, %l1 : $Builtin.Int1) : $Builtin.Int1
|
|
cond_br %x1, bb5, bb6
|
|
|
|
bb5:
|
|
%3 = integer_literal $Builtin.Int64, 3
|
|
br bb7(%3 : $Builtin.Int64)
|
|
|
|
bb6:
|
|
%4 = integer_literal $Builtin.Int64, 4
|
|
br bb7(%4 : $Builtin.Int64)
|
|
|
|
bb7(%6 : $Builtin.Int64):
|
|
%7 = struct $Int64 (%6 : $Builtin.Int64)
|
|
return %7 : $Int64
|
|
}
|
|
|
|
// CHECK-LABEL: @dominator_based_simplify_condbr_with_inverts
|
|
// CHECK-NOT: integer_literal $Builtin.Int64, 1
|
|
// CHECK: [[I2:%[0-9]+]] = integer_literal $Builtin.Int64, 2
|
|
// CHECK: br bb3([[I2]] : $Builtin.Int64)
|
|
// CHECK-NOT: integer_literal $Builtin.Int64, 3
|
|
// CHECK: [[I4:%[0-9]+]] = integer_literal $Builtin.Int64, 4
|
|
// CHECK: br bb3([[I4]] : $Builtin.Int64)
|
|
// CHECK: bb3([[R:%[0-9]+]] : $Builtin.Int64):
|
|
// CHECK-NEXT: return [[R]]
|
|
sil @dominator_based_simplify_condbr_with_inverts : $@convention(thin) (Builtin.Int1) -> Builtin.Int64 {
|
|
bb0(%0 : $Builtin.Int1):
|
|
%l1 = integer_literal $Builtin.Int1, -1
|
|
%x1 = builtin "xor_Int1"(%0 : $Builtin.Int1, %l1 : $Builtin.Int1) : $Builtin.Int1
|
|
cond_br %x1, bb1, bb4
|
|
|
|
bb1:
|
|
cond_br %0, bb2, bb3
|
|
|
|
bb2:
|
|
%1 = integer_literal $Builtin.Int64, 1
|
|
br bb7(%1 : $Builtin.Int64)
|
|
|
|
bb3:
|
|
%2 = integer_literal $Builtin.Int64, 2
|
|
br bb7(%2 : $Builtin.Int64)
|
|
|
|
bb4:
|
|
// expect-intrinsics should be transparent for checking the condition.
|
|
%x2 = builtin "int_expect_Int1"(%0 : $Builtin.Int1, %l1 : $Builtin.Int1) : $Builtin.Int1
|
|
%x3 = builtin "xor_Int1"(%0 : $Builtin.Int1, %l1 : $Builtin.Int1) : $Builtin.Int1
|
|
cond_br %x3, bb5, bb6
|
|
|
|
bb5:
|
|
%3 = integer_literal $Builtin.Int64, 3
|
|
br bb7(%3 : $Builtin.Int64)
|
|
|
|
bb6:
|
|
%4 = integer_literal $Builtin.Int64, 4
|
|
br bb7(%4 : $Builtin.Int64)
|
|
|
|
bb7(%6 : $Builtin.Int64):
|
|
return %6 : $Builtin.Int64
|
|
}
|
|
|
|
// CHECK-LABEL: @switch_enum_dominates_switch_enum_arg
|
|
// CHECK: bb0(%0 : $Optional<Builtin.Int32>):
|
|
// CHECK-NEXT: switch_enum %0 {{.*}} case #Optional.some!enumelt: bb2
|
|
// CHECK: bb2:
|
|
// CHECK-NEXT: [[D:%[0-9]+]] = unchecked_enum_data %0
|
|
// CHECK-NEXT: br bb3([[D]] : $Builtin.Int32)
|
|
// CHECK: bb3([[R:%[0-9]+]] : $Builtin.Int32):
|
|
// CHECK-NEXT: return [[R]]
|
|
sil @switch_enum_dominates_switch_enum_arg : $@convention(thin) (Optional<Builtin.Int32>) -> Builtin.Int32 {
|
|
bb0(%0 : $Optional<Builtin.Int32>):
|
|
switch_enum %0 : $Optional<Builtin.Int32>, case #Optional.none!enumelt: bb1, case #Optional.some!enumelt: bb2
|
|
|
|
bb1:
|
|
%i1 = integer_literal $Builtin.Int32, 1
|
|
br bb5(%i1 : $Builtin.Int32)
|
|
|
|
bb2:
|
|
switch_enum %0 : $Optional<Builtin.Int32>, case #Optional.none!enumelt: bb3, case #Optional.some!enumelt: bb4
|
|
|
|
bb3:
|
|
%i2 = integer_literal $Builtin.Int32, 2
|
|
br bb5(%i2 : $Builtin.Int32)
|
|
|
|
bb4(%e : $Builtin.Int32):
|
|
br bb5(%e : $Builtin.Int32)
|
|
|
|
bb5(%r : $Builtin.Int32):
|
|
return %r : $Builtin.Int32
|
|
}
|
|
|
|
// CHECK-LABEL: @switch_enum_dominates_switch_enum_arg_reuse
|
|
// CHECK: bb0(%0 : $Optional<Builtin.Int32>):
|
|
// CHECK-NEXT: switch_enum %0 {{.*}} case #Optional.some!enumelt: bb2
|
|
// CHECK: bb2({{.*}} : $Builtin.Int32):
|
|
// CHECK-NEXT: [[A:%[0-9]+]] = unchecked_enum_data %0
|
|
// CHECK-NEXT: br bb3([[A]] : $Builtin.Int32)
|
|
// CHECK: bb3([[R:%[0-9]+]] : $Builtin.Int32):
|
|
// CHECK-NEXT: return [[R]]
|
|
sil @switch_enum_dominates_switch_enum_arg_reuse : $@convention(thin) (Optional<Builtin.Int32>) -> Builtin.Int32 {
|
|
bb0(%0 : $Optional<Builtin.Int32>):
|
|
switch_enum %0 : $Optional<Builtin.Int32>, case #Optional.none!enumelt: bb1, case #Optional.some!enumelt: bb2
|
|
|
|
bb1:
|
|
%i1 = integer_literal $Builtin.Int32, 1
|
|
br bb5(%i1 : $Builtin.Int32)
|
|
|
|
bb2(%d : $Builtin.Int32):
|
|
switch_enum %0 : $Optional<Builtin.Int32>, case #Optional.none!enumelt: bb3, case #Optional.some!enumelt: bb4
|
|
|
|
bb3:
|
|
%i2 = integer_literal $Builtin.Int32, 2
|
|
br bb5(%i2 : $Builtin.Int32)
|
|
|
|
bb4(%e : $Builtin.Int32):
|
|
br bb5(%e : $Builtin.Int32)
|
|
|
|
bb5(%r : $Builtin.Int32):
|
|
return %r : $Builtin.Int32
|
|
}
|
|
|
|
// CHECK-LABEL: sil @simplify_loop_header
|
|
// CHECK: bb1(
|
|
// CHECK: cond_br {{.*}}, bb3, bb2
|
|
// CHECK: bb2:
|
|
// CHECK: br bb1(
|
|
// CHECK: bb3:
|
|
// CHECK: return
|
|
|
|
sil @simplify_loop_header : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = integer_literal $Builtin.Int32, 0
|
|
%1 = integer_literal $Builtin.Int32, 1000
|
|
br bb1(%0 : $Builtin.Int32)
|
|
|
|
bb1(%3 : $Builtin.Int32):
|
|
%4 = struct $Int32 (%3 : $Builtin.Int32)
|
|
%6 = builtin "cmp_eq_Int32"(%3 : $Builtin.Int32, %1 : $Builtin.Int32) : $Builtin.Int1
|
|
cond_br %6, bb2, bb3
|
|
|
|
bb2:
|
|
%8 = enum $Optional<Int32>, #Optional.none!enumelt
|
|
br bb4(%3 : $Builtin.Int32, %8 : $Optional<Int32>)
|
|
|
|
bb3:
|
|
%10 = integer_literal $Builtin.Int32, 1
|
|
%12 = integer_literal $Builtin.Int1, -1
|
|
%13 = builtin "sadd_with_overflow_Int32"(%3 : $Builtin.Int32, %10 : $Builtin.Int32, %12 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
|
|
%14 = tuple_extract %13 : $(Builtin.Int32, Builtin.Int1), 0
|
|
%15 = tuple_extract %13 : $(Builtin.Int32, Builtin.Int1), 1
|
|
cond_fail %15 : $Builtin.Int1
|
|
%17 = enum $Optional<Int32>, #Optional.some!enumelt, %4 : $Int32
|
|
br bb4(%14 : $Builtin.Int32, %17 : $Optional<Int32>)
|
|
|
|
bb4(%19 : $Builtin.Int32, %20 : $Optional<Int32>):
|
|
switch_enum %20 : $Optional<Int32>, case #Optional.some!enumelt: bb5, case #Optional.none!enumelt: bb6
|
|
|
|
bb5:
|
|
// This could be a use like we generate for a loop with an induction
|
|
// variable use like in:
|
|
// for i in 1..10 { a[i] = i }
|
|
%9 = unchecked_enum_data %20 : $Optional<Int32>, #Optional.some!enumelt
|
|
br bb1(%19 : $Builtin.Int32)
|
|
|
|
bb6:
|
|
%23 = tuple ()
|
|
return %23 : $()
|
|
}
|
|
|
|
class Base {
|
|
@inline(never) func inner()
|
|
func middle()
|
|
func outer()
|
|
}
|
|
|
|
class Derived : Base {
|
|
override func inner()
|
|
@inline(never) final override func middle()
|
|
}
|
|
|
|
class Final : Derived {
|
|
}
|
|
|
|
sil @_TFC3ccb4Base5innerfS0_FT_T_ : $@convention(method) (@guaranteed Base) -> ()
|
|
sil @_TFC3ccb4Base6middlefS0_FT_T_ : $@convention(method) (@guaranteed Base) -> ()
|
|
|
|
// CHECK-LABEL: sil @redundant_checked_cast_br
|
|
sil @redundant_checked_cast_br : $@convention(method) (@guaranteed Base) -> () {
|
|
bb0(%0 : $Base):
|
|
// CHECK: [[METHOD:%.*]] = class_method %0 : $Base, #Base.middle : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
|
|
%1 = class_method %0 : $Base, #Base.middle : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
|
|
// CHECK: checked_cast_br [exact] Base in %0 : $Base to Base, [[SUCCESS:bb[0-9]+]], [[FAIL:bb[0-9]+]]
|
|
checked_cast_br [exact] Base in %0 : $Base to Base, bb2, bb7
|
|
|
|
// CHECK: bb1
|
|
bb1:
|
|
%3 = tuple ()
|
|
return %3 : $()
|
|
|
|
bb2(%5 : $Base):
|
|
// CHECK: [[SUCCESS]]
|
|
%7 = class_method %0 : $Base, #Base.inner : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
|
|
// CHECK-NOT: checked_cast_br
|
|
checked_cast_br [exact] Base in %0 : $Base to Base, bb3, bb5
|
|
// CHECK: [[INNER:%.*]] = function_ref @_TFC3ccb4Base5innerfS0_FT_T_ : $@convention(method) (@guaranteed Base) -> ()
|
|
// CHECK: apply [[INNER]]
|
|
// CHECK: br bb1
|
|
|
|
bb3(%9 : $Base):
|
|
// CHECK: [[FAIL]]
|
|
// CHECK-NOT: function-ref
|
|
// CHECK: apply [[METHOD]]
|
|
|
|
%10 = function_ref @_TFC3ccb4Base5innerfS0_FT_T_ : $@convention(method) (@guaranteed Base) -> ()
|
|
%11 = apply %10(%0) : $@convention(method) (@guaranteed Base) -> ()
|
|
br bb4
|
|
|
|
bb4:
|
|
%13 = tuple ()
|
|
br bb6(%13 : $())
|
|
|
|
bb5:
|
|
%15 = apply %7(%0) : $@convention(method) (@guaranteed Base) -> ()
|
|
br bb4
|
|
|
|
bb6(%17 : $()):
|
|
br bb1
|
|
|
|
bb7:
|
|
%19 = apply %1(%0) : $@convention(method) (@guaranteed Base) -> ()
|
|
br bb1
|
|
}
|
|
|
|
|
|
// CHECK-LABEL: sil @not_redundant_checked_cast_br : $@convention(method) (@guaranteed Base) -> () {
|
|
sil @not_redundant_checked_cast_br : $@convention(method) (@guaranteed Base) -> () {
|
|
bb0(%0 : $Base):
|
|
// CHECK: [[METHOD:%.*]] = class_method %0 : $Base, #Base.middle : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
|
|
%1 = class_method %0 : $Base, #Base.middle : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
|
|
// CHECK: checked_cast_br [exact] Base in %0 : $Base to Base, [[SUCCESS:bb[0-9]+]], [[FAIL:bb[0-9]+]]
|
|
checked_cast_br [exact] Base in %0 : $Base to Base, bb2, bb7
|
|
|
|
// CHECK: bb1:
|
|
// CHECK: tuple ()
|
|
// CHECK: return
|
|
|
|
bb1:
|
|
%3 = tuple ()
|
|
return %3 : $()
|
|
|
|
bb2(%5 : $Base):
|
|
// CHECK: [[SUCCESS]]
|
|
// CHECK: [[METHOD2:%.*]] = class_method %0 : $Base, #Base.inner : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
|
|
%7 = class_method %0 : $Base, #Base.inner : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
|
|
%8 = apply %7(%0) : $@convention(method) (@guaranteed Base) -> ()
|
|
br bb4
|
|
|
|
bb3(%9 : $Derived):
|
|
%10 = function_ref @_TFC3ccb4Base5innerfS0_FT_T_ : $@convention(method) (@guaranteed Base) -> ()
|
|
%11 = apply %10(%0) : $@convention(method) (@guaranteed Base) -> ()
|
|
br bb4
|
|
|
|
bb4:
|
|
%13 = tuple ()
|
|
br bb6(%13 : $())
|
|
|
|
bb5:
|
|
%14 = class_method %0 : $Base, #Base.inner : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
|
|
%15 = apply %14(%0) : $@convention(method) (@guaranteed Base) -> ()
|
|
br bb4
|
|
|
|
bb6(%17 : $()):
|
|
br bb1
|
|
|
|
bb7:
|
|
// CHECK: checked_cast_br [exact] Base in %0 : $Base to Derived
|
|
checked_cast_br [exact] Base in %0 : $Base to Derived, bb3, bb5
|
|
}
|
|
|
|
// CHECK-LABEL: sil @failing_checked_cast_br
|
|
sil @failing_checked_cast_br : $@convention(method) (@guaranteed Base) -> () {
|
|
bb0(%0 : $Base):
|
|
// CHECK: [[METHOD:%.*]] = class_method %0 : $Base, #Base.middle : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
|
|
%1 = class_method %0 : $Base, #Base.middle : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
|
|
// CHECK: checked_cast_br [exact] Base in %0 : $Base to Base, [[SUCCESS:bb[0-9]+]], [[FAIL:bb[0-9]+]]
|
|
checked_cast_br [exact] Base in %0 : $Base to Base, bb2, bb7
|
|
|
|
// CHECK-LABEL: bb1
|
|
bb1:
|
|
%3 = tuple ()
|
|
return %3 : $()
|
|
|
|
bb2(%5 : $Base):
|
|
// CHECK: [[SUCCESS]]
|
|
// CHECK: [[METHOD2:%.*]] = class_method %0 : $Base, #Base.inner : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
|
|
%7 = class_method %0 : $Base, #Base.inner : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
|
|
// CHECK-NOT: checked_cast_br [exact] Base in %0 : $Base to Derived
|
|
// CHECK: apply [[METHOD2]]
|
|
// Check that checked_cast_br [exact] was replaced by a branch to the failure BB of the checked_cast_br.
|
|
// This is because bb2 is reached via the success branch of the checked_cast_br [exact] from bb0.
|
|
// It means that the exact dynamic type of %0 is $Base. Thus it cannot be $Derived.
|
|
// CHECK: br bb1
|
|
checked_cast_br [exact] Base in %5 : $Base to Derived, bb3, bb5
|
|
|
|
bb3(%9 : $Derived):
|
|
%10 = function_ref @_TFC3ccb4Base5innerfS0_FT_T_ : $@convention(method) (@guaranteed Base) -> ()
|
|
%11 = apply %10(%0) : $@convention(method) (@guaranteed Base) -> ()
|
|
br bb4
|
|
|
|
bb4:
|
|
%13 = tuple ()
|
|
br bb6(%13 : $())
|
|
|
|
bb5:
|
|
%15 = apply %7(%0) : $@convention(method) (@guaranteed Base) -> ()
|
|
br bb4
|
|
|
|
bb6(%17 : $()):
|
|
br bb1
|
|
|
|
bb7:
|
|
%19 = apply %1(%0) : $@convention(method) (@guaranteed Base) -> ()
|
|
br bb1
|
|
}
|
|
|
|
sil @unknown2 : $@convention(thin) () -> ()
|
|
|
|
// CHECK-LABEL: no_checked_cast_br_threading_with_alloc_ref_stack
|
|
// CHECK: checked_cast_br
|
|
// CHECK: apply
|
|
// CHECK: apply
|
|
// CHECK: checked_cast_br
|
|
// CHECK: apply
|
|
// CHECK: apply
|
|
// CHECK: return
|
|
sil @no_checked_cast_br_threading_with_alloc_ref_stack : $@convention(method) (@guaranteed Base) -> () {
|
|
bb0(%0 : $Base):
|
|
%fu = function_ref @unknown : $@convention(thin) () -> ()
|
|
%fu2 = function_ref @unknown2 : $@convention(thin) () -> ()
|
|
checked_cast_br [exact] Base in %0 : $Base to Base, bb1, bb2
|
|
|
|
bb1(%1 : $Base):
|
|
apply %fu() : $@convention(thin) () -> ()
|
|
br bb3
|
|
|
|
bb2:
|
|
apply %fu2() : $@convention(thin) () -> ()
|
|
br bb3
|
|
|
|
bb3:
|
|
%a = alloc_ref [stack] $Base
|
|
checked_cast_br [exact] Base in %0 : $Base to Base, bb4, bb5
|
|
|
|
bb4(%2 : $Base):
|
|
apply %fu() : $@convention(thin) () -> ()
|
|
br bb6
|
|
|
|
bb5:
|
|
apply %fu2() : $@convention(thin) () -> ()
|
|
br bb6
|
|
|
|
bb6:
|
|
dealloc_stack_ref %a : $Base
|
|
%r = tuple()
|
|
return %r : $()
|
|
}
|
|
|
|
|
|
// CHECK-LABEL: sil @jumpthread_switch_enum
|
|
// CHECK-NOT: switch_enum
|
|
// CHECK: return
|
|
|
|
sil @jumpthread_switch_enum : $@convention(thin) (Int32) -> Int32 {
|
|
bb0(%0 : $Int32):
|
|
%1 = integer_literal $Builtin.Int32, 0
|
|
%2 = integer_literal $Builtin.Int32, 1
|
|
%3 = struct_extract %0 : $Int32, #Int32._value
|
|
%5 = integer_literal $Builtin.Int1, -1
|
|
%6 = builtin "sadd_with_overflow_Int32"(%3 : $Builtin.Int32, %2 : $Builtin.Int32, %5 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
|
|
%7 = tuple_extract %6 : $(Builtin.Int32, Builtin.Int1), 0
|
|
%8 = tuple_extract %6 : $(Builtin.Int32, Builtin.Int1), 1
|
|
cond_fail %8 : $Builtin.Int1
|
|
%11 = builtin "cmp_eq_Int32"(%2 : $Builtin.Int32, %7 : $Builtin.Int32) : $Builtin.Int1
|
|
cond_br %11, bb8(%1 : $Builtin.Int32), bb2
|
|
|
|
bb2:
|
|
%14 = integer_literal $Builtin.Int32, 2
|
|
%15 = struct $Int32 (%2 : $Builtin.Int32)
|
|
%16 = enum $Optional<Int32>, #Optional.some!enumelt, %15 : $Int32
|
|
br bb3(%1 : $Builtin.Int32, %14 : $Builtin.Int32, %16 : $Optional<Int32>)
|
|
|
|
bb3(%18 : $Builtin.Int32, %19 : $Builtin.Int32, %20 : $Optional<Int32>):
|
|
switch_enum %20 : $Optional<Int32>, case #Optional.some!enumelt: bb4, case #Optional.none!enumelt: bb5
|
|
|
|
bb4:
|
|
%22 = unchecked_enum_data %20 : $Optional<Int32>, #Optional.some!enumelt
|
|
%23 = struct_extract %22 : $Int32, #Int32._value
|
|
%24 = builtin "sadd_with_overflow_Int32"(%18 : $Builtin.Int32, %23 : $Builtin.Int32, %5 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
|
|
%25 = tuple_extract %24 : $(Builtin.Int32, Builtin.Int1), 0
|
|
%26 = tuple_extract %24 : $(Builtin.Int32, Builtin.Int1), 1
|
|
cond_fail %26 : $Builtin.Int1
|
|
%28 = struct $Int32 (%19 : $Builtin.Int32)
|
|
%29 = builtin "cmp_eq_Int32"(%19 : $Builtin.Int32, %7 : $Builtin.Int32) : $Builtin.Int1
|
|
cond_br %29, bb6, bb7
|
|
|
|
bb5:
|
|
cond_fail %5 : $Builtin.Int1
|
|
unreachable
|
|
|
|
bb6:
|
|
br bb8(%25 : $Builtin.Int32)
|
|
|
|
bb7:
|
|
%34 = builtin "sadd_with_overflow_Int32"(%19 : $Builtin.Int32, %2 : $Builtin.Int32, %5 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
|
|
%35 = tuple_extract %34 : $(Builtin.Int32, Builtin.Int1), 0
|
|
%36 = tuple_extract %34 : $(Builtin.Int32, Builtin.Int1), 1
|
|
cond_fail %36 : $Builtin.Int1
|
|
%38 = enum $Optional<Int32>, #Optional.some!enumelt, %28 : $Int32
|
|
br bb3(%25 : $Builtin.Int32, %35 : $Builtin.Int32, %38 : $Optional<Int32>)
|
|
|
|
bb8(%40 : $Builtin.Int32):
|
|
%41 = struct $Int32 (%40 : $Builtin.Int32)
|
|
return %41 : $Int32
|
|
}
|
|
|
|
// CHECK-LABEL: sil @jumpthread_switch_enum2
|
|
// CHECK-NOT: switch_enum
|
|
// CHECK: return
|
|
|
|
sil @jumpthread_switch_enum2 : $@convention(thin) (Int32) -> Int32 {
|
|
bb0(%0 : $Int32):
|
|
%1 = integer_literal $Builtin.Int32, 0
|
|
%3 = enum $Optional<Int32>, #Optional.some!enumelt, %0 : $Int32
|
|
%4 = enum $Optional<Int32>, #Optional.none!enumelt
|
|
br bb1(%1 : $Builtin.Int32, %3 : $Optional<Int32>)
|
|
|
|
bb1(%6 : $Builtin.Int32, %7 : $Optional<Int32>):
|
|
switch_enum %7 : $Optional<Int32>, case #Optional.some!enumelt: bb2, case #Optional.none!enumelt: bb3
|
|
|
|
bb2:
|
|
%9 = unchecked_enum_data %7 : $Optional<Int32>, #Optional.some!enumelt
|
|
%10 = struct_extract %9 : $Int32, #Int32._value
|
|
%11 = integer_literal $Builtin.Int1, 0
|
|
%12 = builtin "sadd_with_overflow_Int32"(%6 : $Builtin.Int32, %10 : $Builtin.Int32, %11 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
|
|
%13 = tuple_extract %12 : $(Builtin.Int32, Builtin.Int1), 0
|
|
br bb1(%13 : $Builtin.Int32, %4 : $Optional<Int32>)
|
|
|
|
bb3:
|
|
%17 = struct $Int32 (%6 : $Builtin.Int32)
|
|
return %17 : $Int32
|
|
}
|
|
|
|
// CHECK-LABEL: sil @jumpthread_switch_enum3
|
|
// CHECK: [[ONE:%.*]] = integer_literal $Builtin.Int32, 2
|
|
// CHECK: [[ONEVAL:%.*]] = struct $Int32 ([[ONE]] : $Builtin.Int32)
|
|
// CHECK: [[THREE:%.*]] = integer_literal $Builtin.Int32, 3
|
|
// CHECK: [[THREEVAL:%.*]] = struct $Int32 ([[THREE]] : $Builtin.Int32)
|
|
// CHECK: switch_enum %0 : $Optional<Int32>, case #Optional.none!enumelt: bb2, case #Optional.some!enumelt: bb1
|
|
// CHECK: bb1:
|
|
// CHECK-NEXT: br bb5([[ONEVAL]]
|
|
// CHECK: bb2:
|
|
// CHECK-NEXT: switch_enum undef : $Optional<Int32>, case #Optional.none!enumelt: bb3, case #Optional.some!enumelt: bb4
|
|
// CHECK: bb3:
|
|
// CHECK-NEXT: br bb5([[THREEVAL]]
|
|
// CHECK: bb4:
|
|
// CHECK-NEXT: br bb5([[ONEVAL]]
|
|
// CHECK: bb5
|
|
// CHECK-NEXT: return
|
|
sil @jumpthread_switch_enum3 : $@convention(thin) (Optional<Int32>) -> Int32 {
|
|
bb0(%0 : $Optional<Int32>):
|
|
%10 = integer_literal $Builtin.Int32, 2
|
|
%11 = struct $Int32 (%10 : $Builtin.Int32)
|
|
%20 = integer_literal $Builtin.Int32, 3
|
|
%21 = struct $Int32 (%20 : $Builtin.Int32)
|
|
switch_enum %0 : $Optional<Int32>, case #Optional.none!enumelt: bb1, case #Optional.some!enumelt: bb2
|
|
|
|
bb1:
|
|
br bb3(undef : $Optional<Int32>)
|
|
|
|
bb2:
|
|
br bb3(%0 : $Optional<Int32>)
|
|
|
|
bb3(%30 : $Optional<Int32>):
|
|
switch_enum %30 : $Optional<Int32>, case #Optional.none!enumelt: bb4, case #Optional.some!enumelt: bb5
|
|
|
|
bb4:
|
|
br bb6(%21 : $Int32)
|
|
|
|
bb5:
|
|
br bb6(%11 : $Int32)
|
|
|
|
bb6(%r : $Int32):
|
|
return %r : $Int32
|
|
}
|
|
|
|
// CHECK-LABEL: sil @jumpthread_switch_enum4
|
|
// CHECK: bb0:
|
|
// CHECK-NEXT: cond_br undef, bb1, bb2
|
|
// CHECK: bb1:
|
|
// CHECK: enum $Optional<Int32>, #Optional.none!enumelt
|
|
// CHECK: br bb3
|
|
// CHECK: bb2:
|
|
// CHECK: integer_literal $Builtin.Int32, 0
|
|
// CHECK: enum $Optional<Int32>, #Optional.some!enumelt
|
|
// CHECK: br bb3
|
|
// CHECK: bb3
|
|
// CHECK: integer_literal {{.*}}, 27
|
|
// CHECK: integer_literal {{.*}}, 28
|
|
// CHECK: return
|
|
sil @jumpthread_switch_enum4 : $@convention(thin) () -> Builtin.Int32 {
|
|
bb0:
|
|
%c0 = builtin "assert_configuration"() : $Builtin.Int32
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
%4 = enum $Optional<Int32>, #Optional.none!enumelt
|
|
cond_br undef, bb3(%4 : $Optional<Int32>), bb4(%4 : $Optional<Int32>, %c0 : $Builtin.Int32)
|
|
|
|
bb2:
|
|
%6 = integer_literal $Builtin.Int32, 0
|
|
%7 = struct $Int32 (%6 : $Builtin.Int32)
|
|
%8 = enum $Optional<Int32>, #Optional.some!enumelt, %7 : $Int32
|
|
br bb3(%8 : $Optional<Int32>)
|
|
|
|
|
|
bb3(%10 : $Optional<Int32>):
|
|
// Some instruction which is not "trivial"
|
|
%c1 = builtin "assert_configuration"() : $Builtin.Int32
|
|
br bb4(%10 : $Optional<Int32>, %c1 : $Builtin.Int32)
|
|
|
|
bb4(%13 : $Optional<Int32>, %carg1 : $Builtin.Int32):
|
|
switch_enum %13 : $Optional<Int32>, case #Optional.some!enumelt: bb5, case #Optional.none!enumelt: bb6
|
|
|
|
bb5:
|
|
%r1 = integer_literal $Builtin.Int32, 27
|
|
%c2 = builtin "assert_configuration"() : $Builtin.Int32
|
|
br bb7(%r1 : $Builtin.Int32, %c2 : $Builtin.Int32)
|
|
|
|
bb6:
|
|
%r2 = integer_literal $Builtin.Int32, 28
|
|
%c3 = builtin "assert_configuration"() : $Builtin.Int32
|
|
br bb7(%r2 : $Builtin.Int32, %c3 : $Builtin.Int32)
|
|
|
|
bb7(%r : $Builtin.Int32, %carg2 : $Builtin.Int32):
|
|
return %r : $Builtin.Int32
|
|
}
|
|
|
|
// CHECK-LABEL: sil @jumpthread_switch_enum5
|
|
// CHECK: bb0:
|
|
// CHECK: br bb1
|
|
// CHECK: bb1:
|
|
// CHECK-NEXT: cond_br undef, bb2, bb3
|
|
// CHECK: bb2:
|
|
// CHECK: br bb1
|
|
// CHECK: bb3:
|
|
// CHECK-NEXT: tuple
|
|
// CHECK-NEXT: return
|
|
sil @jumpthread_switch_enum5 : $@convention(thin) () -> () {
|
|
bb0:
|
|
%6 = integer_literal $Builtin.Int32, 0
|
|
%7 = struct $Int32 (%6 : $Builtin.Int32)
|
|
%8 = enum $Optional<Int32>, #Optional.some!enumelt, %7 : $Int32
|
|
br bb1(%8 : $Optional<Int32>)
|
|
|
|
bb1(%13 : $Optional<Int32>):
|
|
switch_enum %13 : $Optional<Int32>, case #Optional.some!enumelt: bb2, case #Optional.none!enumelt: bb3
|
|
|
|
bb2:
|
|
br bb4
|
|
|
|
bb3:
|
|
%55 = function_ref @external_f : $@convention(thin) () -> ()
|
|
apply %55() : $@convention(thin) () -> ()
|
|
br bb4
|
|
|
|
bb4:
|
|
cond_br undef, bb1(%13 : $Optional<Int32>), bb5
|
|
|
|
bb5:
|
|
%r = tuple ()
|
|
return %r : $()
|
|
}
|
|
|
|
/// Don't jumpthread blocks that contain objc method instructions. We don't
|
|
/// support building phis with objc method values.
|
|
|
|
class Bar {
|
|
init()
|
|
@objc func foo()
|
|
}
|
|
|
|
|
|
// CHECK-LABEL: @dont_jumpthread_switch_enum
|
|
// CHECK: objc_method
|
|
// CHECK: switch_enum
|
|
// CHECK: return
|
|
|
|
sil @dont_jumpthread_switch_enum : $@convention(thin) (Int32) -> Int32 {
|
|
bb0(%0 : $Int32):
|
|
%100 = alloc_ref $Bar
|
|
%1 = integer_literal $Builtin.Int32, 0
|
|
%2 = integer_literal $Builtin.Int32, 1
|
|
%3 = struct_extract %0 : $Int32, #Int32._value
|
|
%5 = integer_literal $Builtin.Int1, -1
|
|
%6 = builtin "sadd_with_overflow_Int32"(%3 : $Builtin.Int32, %2 : $Builtin.Int32, %5 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
|
|
%7 = tuple_extract %6 : $(Builtin.Int32, Builtin.Int1), 0
|
|
%8 = tuple_extract %6 : $(Builtin.Int32, Builtin.Int1), 1
|
|
cond_fail %8 : $Builtin.Int1
|
|
%11 = builtin "cmp_eq_Int32"(%2 : $Builtin.Int32, %7 : $Builtin.Int32) : $Builtin.Int1
|
|
cond_br %11, bb8(%1 : $Builtin.Int32), bb2
|
|
|
|
bb2:
|
|
%14 = integer_literal $Builtin.Int32, 2
|
|
%15 = struct $Int32 (%2 : $Builtin.Int32)
|
|
%16 = enum $Optional<Int32>, #Optional.some!enumelt, %15 : $Int32
|
|
br bb3(%1 : $Builtin.Int32, %14 : $Builtin.Int32, %16 : $Optional<Int32>)
|
|
|
|
bb3(%18 : $Builtin.Int32, %19 : $Builtin.Int32, %20 : $Optional<Int32>):
|
|
%101 = objc_method %100 : $Bar, #Bar.foo!foreign : (Bar) -> () -> (), $@convention(objc_method) (Bar) -> ()
|
|
switch_enum %20 : $Optional<Int32>, case #Optional.some!enumelt: bb4, case #Optional.none!enumelt: bb5
|
|
|
|
bb4:
|
|
%102 = apply %101(%100) : $@convention(objc_method) (Bar) -> ()
|
|
%22 = unchecked_enum_data %20 : $Optional<Int32>, #Optional.some!enumelt
|
|
%23 = struct_extract %22 : $Int32, #Int32._value
|
|
%24 = builtin "sadd_with_overflow_Int32"(%18 : $Builtin.Int32, %23 : $Builtin.Int32, %5 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
|
|
%25 = tuple_extract %24 : $(Builtin.Int32, Builtin.Int1), 0
|
|
%26 = tuple_extract %24 : $(Builtin.Int32, Builtin.Int1), 1
|
|
cond_fail %26 : $Builtin.Int1
|
|
%28 = struct $Int32 (%19 : $Builtin.Int32)
|
|
%29 = builtin "cmp_eq_Int32"(%19 : $Builtin.Int32, %7 : $Builtin.Int32) : $Builtin.Int1
|
|
cond_br %29, bb6, bb7
|
|
|
|
bb5:
|
|
cond_fail %5 : $Builtin.Int1
|
|
unreachable
|
|
|
|
bb6:
|
|
br bb8(%25 : $Builtin.Int32)
|
|
|
|
bb7:
|
|
%34 = builtin "sadd_with_overflow_Int32"(%19 : $Builtin.Int32, %2 : $Builtin.Int32, %5 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
|
|
%35 = tuple_extract %34 : $(Builtin.Int32, Builtin.Int1), 0
|
|
%36 = tuple_extract %34 : $(Builtin.Int32, Builtin.Int1), 1
|
|
cond_fail %36 : $Builtin.Int1
|
|
%38 = enum $Optional<Int32>, #Optional.none!enumelt
|
|
br bb3(%25 : $Builtin.Int32, %35 : $Builtin.Int32, %38 : $Optional<Int32>)
|
|
|
|
bb8(%40 : $Builtin.Int32):
|
|
%41 = struct $Int32 (%40 : $Builtin.Int32)
|
|
return %41 : $Int32
|
|
}
|
|
|
|
enum OneCase {
|
|
case First
|
|
}
|
|
|
|
enum TwoCase {
|
|
case First
|
|
case Second
|
|
}
|
|
|
|
enum ThreeCase {
|
|
case First
|
|
case Second
|
|
case Third
|
|
}
|
|
|
|
sil @unknown : $@convention(thin) () -> ()
|
|
sil @int1_user : $@convention(thin) (Builtin.Int1) -> ()
|
|
|
|
// CHECK-LABEL: sil @select_enum_case_canonicalization : $@convention(thin) (OneCase, TwoCase, TwoCase, ThreeCase, ThreeCase, ThreeCase) -> () {
|
|
// CHECK: bb0([[ONE_1:%.*]] : $OneCase, [[TWO_1:%.*]] : $TwoCase, [[TWO_2:%.*]] : $TwoCase, [[THREE_1:%.*]] : $ThreeCase, [[THREE_2:%.*]] : $ThreeCase, [[THREE_3:%.*]] : $ThreeCase):
|
|
// CHECK: [[TAG1:%.*]] = select_enum [[ONE_1]] : $OneCase, case #OneCase.First!enumelt: [[TRUE:%[0-9]+]]
|
|
// CHECK: [[TAG2:%.*]] = select_enum [[TWO_1]] : $TwoCase, case #TwoCase.First!enumelt: [[TRUE:%[0-9]+]]
|
|
// CHECK: [[TAG3:%.*]] = select_enum [[TWO_2]] : $TwoCase, case #TwoCase.First!enumelt: [[TRUE:%[0-9]+]]
|
|
// CHECK: [[TAG3_OLD:%.*]] = select_enum [[TWO_2]] : $TwoCase, case #TwoCase.Second!enumelt: [[TRUE:%[0-9]+]]
|
|
// CHECK: [[TAG4:%.*]] = select_enum [[THREE_1]] : $ThreeCase, case #ThreeCase.First!enumelt: [[TRUE:%[0-9]+]]
|
|
// CHECK: [[TAG5:%.*]] = select_enum [[THREE_2]] : $ThreeCase, case #ThreeCase.Second!enumelt: [[TRUE:%[0-9]+]]
|
|
// CHECK: [[TAG6:%.*]] = select_enum [[THREE_3]] : $ThreeCase, case #ThreeCase.Third!enumelt: [[TRUE:%[0-9]+]]
|
|
// CHECK: cond_br [[TAG1]], bb1, bb6
|
|
// CHECK: cond_br [[TAG2]], bb2, bb3
|
|
// CHECK: [[INT1_USER_FUN:%.*]] = function_ref @int1_user : $@convention(thin) (Builtin.Int1) -> ()
|
|
// CHECK: apply [[INT1_USER_FUN]]([[TAG3_OLD]])
|
|
// CHECK: cond_br [[TAG3]], bb5, bb4
|
|
// CHECK: cond_br [[TAG4]], bb7, bb8
|
|
// CHECK: cond_br [[TAG5]], bb9, bb10
|
|
// CHECK: cond_br [[TAG6]], bb11, bb12
|
|
sil @select_enum_case_canonicalization : $@convention(thin) (OneCase, TwoCase, TwoCase, ThreeCase, ThreeCase, ThreeCase) -> () {
|
|
bb0(%0 : $OneCase, %1 : $TwoCase, %2 : $TwoCase, %3 : $ThreeCase, %4 : $ThreeCase, %5 : $ThreeCase):
|
|
%t = integer_literal $Builtin.Int1, 1
|
|
%f = integer_literal $Builtin.Int1, 0
|
|
%6 = select_enum %0 : $OneCase, case #OneCase.First!enumelt: %t, default %f : $Builtin.Int1
|
|
%7 = select_enum %1 : $TwoCase, case #TwoCase.First!enumelt: %t, default %f : $Builtin.Int1
|
|
%8 = select_enum %2 : $TwoCase, case #TwoCase.Second!enumelt: %t, default %f : $Builtin.Int1
|
|
%9 = select_enum %3 : $ThreeCase, case #ThreeCase.First!enumelt: %t, default %f : $Builtin.Int1
|
|
%10 = select_enum %4 : $ThreeCase, case #ThreeCase.Second!enumelt: %t, default %f : $Builtin.Int1
|
|
%11 = select_enum %5 : $ThreeCase, case #ThreeCase.Third!enumelt: %t, default %f : $Builtin.Int1
|
|
%12 = function_ref @unknown : $@convention(thin) () -> ()
|
|
cond_br %6, bb1a, bb1b
|
|
|
|
bb1a:
|
|
apply %12() : $@convention(thin) () -> ()
|
|
cond_br %7, bb2a, bb3a
|
|
|
|
bb2a:
|
|
apply %12() : $@convention(thin) () -> ()
|
|
%13 = function_ref @int1_user : $@convention(thin) (Builtin.Int1) -> ()
|
|
apply %13(%8) : $@convention(thin) (Builtin.Int1) -> ()
|
|
cond_br %8, bb4a, bb5a
|
|
|
|
bb3a:
|
|
apply %12() : $@convention(thin) () -> ()
|
|
br exit
|
|
|
|
bb4a:
|
|
apply %12() : $@convention(thin) () -> ()
|
|
br exit
|
|
|
|
bb5a:
|
|
apply %12() : $@convention(thin) () -> ()
|
|
br exit
|
|
|
|
bb1b:
|
|
apply %12() : $@convention(thin) () -> ()
|
|
cond_br %9, bb2b, bb3b
|
|
|
|
bb2b:
|
|
apply %12() : $@convention(thin) () -> ()
|
|
cond_br %10, bb4b, bb5b
|
|
|
|
bb3b:
|
|
apply %12() : $@convention(thin) () -> ()
|
|
cond_br %11, bb6b, bb7b
|
|
|
|
bb4b:
|
|
apply %12() : $@convention(thin) () -> ()
|
|
br exit
|
|
|
|
bb5b:
|
|
apply %12() : $@convention(thin) () -> ()
|
|
br exit
|
|
|
|
bb6b:
|
|
apply %12() : $@convention(thin) () -> ()
|
|
br exit
|
|
|
|
bb7b:
|
|
apply %12() : $@convention(thin) () -> ()
|
|
br exit
|
|
|
|
exit:
|
|
apply %12() : $@convention(thin) () -> ()
|
|
%9999 = tuple()
|
|
return %9999 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @dont_crash_jump_threading_single_case_switch_enums
|
|
// CHECK-NOT: switch_enum
|
|
// CHECK: } // end sil function
|
|
sil @dont_crash_jump_threading_single_case_switch_enums : $@convention(thin) () -> () {
|
|
bb0:
|
|
br bb1(undef : $OneCase)
|
|
|
|
bb1(%19 : $OneCase):
|
|
switch_enum %19 : $OneCase, case #OneCase.First!enumelt: bb2
|
|
|
|
bb2:
|
|
switch_enum %19 : $OneCase, case #OneCase.First!enumelt: bb3
|
|
|
|
bb3:
|
|
%48 = enum $OneCase, #OneCase.First!enumelt
|
|
cond_br undef, bb6, bb7
|
|
|
|
bb6:
|
|
%72 = tuple ()
|
|
return %72 : $()
|
|
|
|
bb7:
|
|
br bb1(%48 : $OneCase)
|
|
}
|
|
|
|
|
|
class B {}
|
|
class E : B {}
|
|
|
|
// CHECK-LABEL: sil @checked_cast_anyobject_metatypeinst_to_class
|
|
// CHECK: bb0
|
|
// CHECK-NOT: checked_cast
|
|
// CHECK-NOT: bb1
|
|
// CHECK: [[RET:%.*]] = tuple ()
|
|
// CHECK: return [[RET]] : $()
|
|
|
|
sil @checked_cast_anyobject_metatypeinst_to_class : $@convention(thin)() -> () {
|
|
bb0:
|
|
%0 = metatype $@thick AnyObject.Protocol
|
|
checked_cast_br AnyObject.Protocol in %0 : $@thick AnyObject.Protocol to B.Type, bb1, bb2
|
|
|
|
bb1(%3 : $@thick B.Type):
|
|
br bb3
|
|
bb2:
|
|
br bb3
|
|
|
|
bb3:
|
|
%2 = tuple()
|
|
return %2 : $()
|
|
}
|
|
|
|
|
|
// CHECK-LABEL: sil @remove_cond_fail_trueblock
|
|
// CHECK: bb0([[COND:%.*]] :
|
|
// CHECK-NOT: bb
|
|
// CHECK: cond_fail [[COND]]
|
|
// CHECK-NOT: bb
|
|
// CHECK: return
|
|
sil @remove_cond_fail_trueblock : $@convention(thin)(Builtin.Int1) -> () {
|
|
bb0(%0 : $Builtin.Int1):
|
|
cond_br %0, bb1, bb2
|
|
bb1:
|
|
%1 = integer_literal $Builtin.Int1, -1
|
|
cond_fail %1 : $Builtin.Int1
|
|
unreachable
|
|
bb2:
|
|
%2 = tuple()
|
|
return %2 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @remove_cond_fail_falseblock
|
|
// CHECK: bb0([[COND:%.*]] :
|
|
// CHECK-NOT: bb
|
|
// CHECK: [[TRUE:%.*]] = integer_literal $Builtin.Int1, -1
|
|
// CHECK-NOT: bb
|
|
// CHECK: [[NOTCOND:%.*]] = builtin "xor_Int1"([[COND]]{{.*}}, [[TRUE]]
|
|
// CHECK: cond_fail [[NOTCOND]]
|
|
// CHECK-NOT: bb
|
|
// CHECK: return
|
|
sil @remove_cond_fail_falseblock : $@convention(thin)(Builtin.Int1) -> () {
|
|
bb0(%0 : $Builtin.Int1):
|
|
cond_br %0, bb2, bb1
|
|
|
|
bb1:
|
|
%1 = integer_literal $Builtin.Int1, -1
|
|
cond_fail %1 : $Builtin.Int1
|
|
unreachable
|
|
|
|
bb2:
|
|
%2 = tuple()
|
|
return %2 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @dont_remove_cond_fail_wrong_const
|
|
// CHECK: bb0(%0 : $Builtin.Int1):
|
|
// CHECK-NEXT: cond_br %0, bb2, bb1
|
|
sil @dont_remove_cond_fail_wrong_const : $@convention(thin) (Builtin.Int1) -> () {
|
|
bb0(%0 : $Builtin.Int1):
|
|
cond_br %0, bb1, bb2
|
|
|
|
bb1:
|
|
%i1 = integer_literal $Builtin.Int1, 0
|
|
cond_fail %i1 : $Builtin.Int1
|
|
br bb2
|
|
|
|
bb2:
|
|
%r = tuple ()
|
|
return %r : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @remove_cond_fail_same_cond_in_true
|
|
// CHECK: bb0([[COND:%[0-9]*]]
|
|
// CHECK-NOT: bb
|
|
// CHECK: cond_fail [[COND]]
|
|
// CHECK: bb1:
|
|
// CHECK: return
|
|
sil @remove_cond_fail_same_cond_in_true : $@convention(thin)(Builtin.Int1, Builtin.Int1) -> () {
|
|
bb0(%0 : $Builtin.Int1, %1 : $Builtin.Int1):
|
|
cond_br %0, bb1, bb2
|
|
bb1:
|
|
cond_fail %0 : $Builtin.Int1
|
|
unreachable
|
|
bb2:
|
|
// Make bb1 not dominated from bb0 to prevent that dominator based
|
|
// simplification does the same thing.
|
|
%55 = function_ref @external_f : $@convention(thin) () -> ()
|
|
apply %55() : $@convention(thin) () -> ()
|
|
cond_br %1, bb1, bb3
|
|
bb3:
|
|
%2 = tuple()
|
|
return %2 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @remove_cond_fail_same_cond_in_false
|
|
// CHECK: bb0([[COND:%[0-9]*]]
|
|
// CHECK-NOT: bb
|
|
// CHECK: [[INV:%[0-9]*]] = builtin "xor_Int1"([[COND]]
|
|
// CHECK-NOT: bb
|
|
// CHECK: cond_fail [[INV]]
|
|
// CHECK: bb1:
|
|
// CHECK: return
|
|
sil @remove_cond_fail_same_cond_in_false : $@convention(thin)(Builtin.Int1, Builtin.Int1) -> () {
|
|
bb0(%0 : $Builtin.Int1, %1 : $Builtin.Int1):
|
|
cond_br %0, bb2, bb1
|
|
bb1:
|
|
%i1 = integer_literal $Builtin.Int1, -1
|
|
%i2 = builtin "xor_Int1"(%0 : $Builtin.Int1, %i1 : $Builtin.Int1) : $Builtin.Int1
|
|
cond_fail %i2 : $Builtin.Int1
|
|
unreachable
|
|
bb2:
|
|
// Make bb1 not dominated from bb0 to prevent that dominator based
|
|
// simplification does the same thing.
|
|
%55 = function_ref @external_f : $@convention(thin) () -> ()
|
|
apply %55() : $@convention(thin) () -> ()
|
|
cond_br %1, bb1, bb3
|
|
bb3:
|
|
%2 = tuple()
|
|
return %2 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @remove_cond_fail_same_cond_in_false2
|
|
// CHECK: bb0([[COND:%[0-9]*]]
|
|
// CHECK-NOT: bb
|
|
// CHECK: cond_fail [[COND]]
|
|
// CHECK: bb1:
|
|
// CHECK: return
|
|
sil @remove_cond_fail_same_cond_in_false2 : $@convention(thin)(Builtin.Int1, Builtin.Int1) -> () {
|
|
bb0(%0 : $Builtin.Int1, %1 : $Builtin.Int1):
|
|
%i1 = integer_literal $Builtin.Int1, -1
|
|
%i2 = builtin "xor_Int1"(%0 : $Builtin.Int1, %i1 : $Builtin.Int1) : $Builtin.Int1
|
|
cond_br %i2, bb2, bb1
|
|
bb1:
|
|
cond_fail %0 : $Builtin.Int1
|
|
unreachable
|
|
bb2:
|
|
// Make bb1 not dominated from bb0 to prevent that dominator based
|
|
// simplification does the same thing.
|
|
%55 = function_ref @external_f : $@convention(thin) () -> ()
|
|
apply %55() : $@convention(thin) () -> ()
|
|
cond_br %1, bb1, bb3
|
|
bb3:
|
|
%2 = tuple()
|
|
return %2 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @dont_remove_cond_fail_same_cond_in_false
|
|
// CHECK: bb0([[COND:%[0-9]*]]
|
|
// CHECK-NEXT: cond_br
|
|
// CHECK: bb2:
|
|
// CHECK-NEXT: cond_fail [[COND]]
|
|
// CHECK: return
|
|
sil @dont_remove_cond_fail_same_cond_in_false : $@convention(thin)(Builtin.Int1, Builtin.Int1) -> () {
|
|
bb0(%0 : $Builtin.Int1, %1 : $Builtin.Int1):
|
|
cond_br %0, bb2, bb1
|
|
bb1:
|
|
cond_fail %0 : $Builtin.Int1
|
|
unreachable
|
|
bb2:
|
|
// Make bb1 not dominated from bb0.
|
|
%55 = function_ref @external_f : $@convention(thin) () -> ()
|
|
apply %55() : $@convention(thin) () -> ()
|
|
cond_br %1, bb1, bb3
|
|
bb3:
|
|
%2 = tuple()
|
|
return %2 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @move_cond_fail
|
|
// CHECK: bb1:
|
|
// CHECK-NEXT: apply
|
|
// CHECK-NEXT: [[X:%[0-9]*]] = integer_literal $Builtin.Int1, -1
|
|
// CHECK-NEXT: cond_fail [[X]]
|
|
// CHECK-NEXT: br bb3
|
|
// CHECK: bb2:
|
|
// CHECK-NEXT: apply
|
|
// CHECK-NEXT: cond_fail %1
|
|
// CHECK-NEXT: br bb3
|
|
// CHECK: bb3:
|
|
// CHECK-NOT: cond_fail
|
|
// CHECK: return
|
|
sil @move_cond_fail : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> () {
|
|
bb0(%0 : $Builtin.Int1, %1 : $Builtin.Int1):
|
|
%f1 = function_ref @external_f : $@convention(thin) () -> ()
|
|
cond_br %0, bb2, bb1
|
|
|
|
bb1:
|
|
apply %f1() : $@convention(thin) () -> () // prevent other CFG optimizations
|
|
%i1 = integer_literal $Builtin.Int1, -1
|
|
br bb3(%i1 : $Builtin.Int1)
|
|
|
|
bb2:
|
|
apply %f1() : $@convention(thin) () -> () // prevent other CFG optimizations
|
|
br bb3(%1 : $Builtin.Int1)
|
|
|
|
bb3(%a3 : $Builtin.Int1):
|
|
cond_fail %a3 : $Builtin.Int1
|
|
%r = tuple ()
|
|
return %r : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @move_cond_fail_inverted
|
|
// CHECK: bb1:
|
|
// CHECK-NEXT: apply
|
|
// CHECK-NEXT: [[X:%[0-9]*]] = integer_literal $Builtin.Int1, -1
|
|
// CHECK-NEXT: [[Y:%[0-9]*]] = builtin "xor_Int1"(%2 : $Builtin.Int1, [[X]] : $Builtin.Int1)
|
|
// CHECK-NEXT: cond_fail [[Y]]
|
|
// CHECK-NEXT: br bb3
|
|
// CHECK: bb2:
|
|
// CHECK-NEXT: apply
|
|
// CHECK-NEXT: [[R:%[0-9]*]] = integer_literal $Builtin.Int1, -1
|
|
// CHECK-NEXT: [[S:%[0-9]*]] = builtin "xor_Int1"(%1 : $Builtin.Int1, [[R]] : $Builtin.Int1)
|
|
// CHECK-NEXT: cond_fail [[S]]
|
|
// CHECK-NEXT: br bb3
|
|
// CHECK: bb3({{.*}}):
|
|
// CHECK-NOT: cond_fail
|
|
// CHECK: return
|
|
sil @move_cond_fail_inverted : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> () {
|
|
bb0(%0 : $Builtin.Int1, %1 : $Builtin.Int1):
|
|
%2 = integer_literal $Builtin.Int1, -1
|
|
%f1 = function_ref @external_f : $@convention(thin) () -> ()
|
|
cond_br %0, bb2, bb1
|
|
|
|
bb1:
|
|
apply %f1() : $@convention(thin) () -> () // prevent other CFG optimizations
|
|
br bb3(%2 : $Builtin.Int1)
|
|
|
|
bb2:
|
|
apply %f1() : $@convention(thin) () -> () // prevent other CFG optimizations
|
|
br bb3(%1 : $Builtin.Int1)
|
|
|
|
bb3(%a3 : $Builtin.Int1):
|
|
%v1 = builtin "xor_Int1"(%a3 : $Builtin.Int1, %2 : $Builtin.Int1) : $Builtin.Int1
|
|
cond_fail %v1 : $Builtin.Int1
|
|
%r = tuple ()
|
|
return %r : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @dont_move_cond_fail_no_const
|
|
// CHECK: bb1:
|
|
// CHECK-NEXT: apply
|
|
// CHECK-NEXT: br bb3
|
|
// CHECK: bb2:
|
|
// CHECK-NEXT: apply
|
|
// CHECK-NEXT: br bb3
|
|
// CHECK: bb3({{.*}}):
|
|
// CHECK-NEXT: cond_fail
|
|
sil @dont_move_cond_fail_no_const : $@convention(thin) (Builtin.Int1, Builtin.Int1, Builtin.Int1) -> () {
|
|
bb0(%0 : $Builtin.Int1, %1 : $Builtin.Int1, %2 : $Builtin.Int1):
|
|
%f1 = function_ref @external_f : $@convention(thin) () -> ()
|
|
cond_br %0, bb2, bb1
|
|
|
|
bb1:
|
|
apply %f1() : $@convention(thin) () -> () // prevent other CFG optimizations
|
|
br bb3(%1 : $Builtin.Int1)
|
|
|
|
bb2:
|
|
apply %f1() : $@convention(thin) () -> () // prevent other CFG optimizations
|
|
br bb3(%2 : $Builtin.Int1)
|
|
|
|
bb3(%a3 : $Builtin.Int1):
|
|
cond_fail %a3 : $Builtin.Int1
|
|
%r = tuple ()
|
|
return %r : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @dont_move_cond_fail_no_postdom
|
|
// CHECK: bb1:
|
|
// CHECK-NEXT: apply
|
|
// CHECK-NEXT: integer_literal
|
|
// CHECK-NEXT: br bb5
|
|
// CHECK: bb2:
|
|
// CHECK-NEXT: apply
|
|
// CHECK-NEXT: cond_br
|
|
// CHECK: bb3:
|
|
// CHECK: br bb6
|
|
// CHECK: bb5({{.*}}):
|
|
// CHECK-NEXT: apply
|
|
// CHECK-NEXT: cond_fail
|
|
sil @dont_move_cond_fail_no_postdom : $@convention(thin) (Builtin.Int1, Builtin.Int1, Builtin.Int1) -> () {
|
|
bb0(%0 : $Builtin.Int1, %1 : $Builtin.Int1, %2 : $Builtin.Int1):
|
|
%f1 = function_ref @external_f : $@convention(thin) () -> ()
|
|
cond_br %0, bb2, bb1
|
|
|
|
bb1:
|
|
apply %f1() : $@convention(thin) () -> () // prevent other CFG optimizations
|
|
%i1 = integer_literal $Builtin.Int1, -1
|
|
br bb3(%i1 : $Builtin.Int1)
|
|
|
|
bb2:
|
|
apply %f1() : $@convention(thin) () -> () // prevent other CFG optimizations
|
|
cond_br %2, bb3(%1 : $Builtin.Int1), bb4
|
|
|
|
bb3(%a3 : $Builtin.Int1):
|
|
apply %f1() : $@convention(thin) () -> () // prevent other CFG optimizations
|
|
cond_fail %a3 : $Builtin.Int1
|
|
br bb4
|
|
|
|
bb4:
|
|
%r = tuple ()
|
|
return %r : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @dont_move_cond_fail_multiple_uses
|
|
// CHECK: bb1:
|
|
// CHECK-NEXT: apply
|
|
// CHECK-NEXT: integer_literal
|
|
// CHECK-NEXT: br bb3
|
|
// CHECK: bb2:
|
|
// CHECK-NEXT: apply
|
|
// CHECK-NEXT: br bb3
|
|
// CHECK: bb3({{.*}}):
|
|
// CHECK-NEXT: cond_fail
|
|
sil @dont_move_cond_fail_multiple_uses : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1 {
|
|
bb0(%0 : $Builtin.Int1, %1 : $Builtin.Int1):
|
|
%f1 = function_ref @external_f : $@convention(thin) () -> ()
|
|
cond_br %0, bb2, bb1
|
|
|
|
bb1:
|
|
apply %f1() : $@convention(thin) () -> () // prevent other CFG optimizations
|
|
%i1 = integer_literal $Builtin.Int1, -1
|
|
br bb3(%i1 : $Builtin.Int1)
|
|
|
|
bb2:
|
|
apply %f1() : $@convention(thin) () -> () // prevent other CFG optimizations
|
|
br bb3(%1 : $Builtin.Int1)
|
|
|
|
bb3(%a3 : $Builtin.Int1):
|
|
cond_fail %a3 : $Builtin.Int1
|
|
return %a3 : $Builtin.Int1
|
|
}
|
|
|
|
// CHECK-LABEL: sil @dont_move_cond_fail_multiple_uses2
|
|
// CHECK: bb1:
|
|
// CHECK-NEXT: apply
|
|
// CHECK-NEXT: br bb3
|
|
// CHECK: bb2:
|
|
// CHECK-NEXT: apply
|
|
// CHECK-NEXT: br bb3
|
|
// CHECK: bb3({{.*}}):
|
|
// CHECK-NEXT: builtin "xor_Int1"
|
|
// CHECK-NEXT: cond_fail
|
|
// CHECK-NEXT: return
|
|
sil @dont_move_cond_fail_multiple_uses2 : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1 {
|
|
bb0(%0 : $Builtin.Int1, %1 : $Builtin.Int1):
|
|
%f1 = function_ref @external_f : $@convention(thin) () -> ()
|
|
%i1 = integer_literal $Builtin.Int1, -1
|
|
cond_br %0, bb2, bb1
|
|
|
|
bb1:
|
|
apply %f1() : $@convention(thin) () -> () // prevent other CFG optimizations
|
|
br bb3(%i1 : $Builtin.Int1)
|
|
|
|
bb2:
|
|
apply %f1() : $@convention(thin) () -> () // prevent other CFG optimizations
|
|
br bb3(%1 : $Builtin.Int1)
|
|
|
|
bb3(%a3 : $Builtin.Int1):
|
|
%v1 = builtin "xor_Int1"(%a3 : $Builtin.Int1, %i1 : $Builtin.Int1) : $Builtin.Int1
|
|
cond_fail %v1 : $Builtin.Int1
|
|
return %v1 : $Builtin.Int1
|
|
}
|
|
|
|
|
|
// CHECK-LABEL: sil @successful_checked_cast_br_on_alloc_ref
|
|
// CHECK: bb0
|
|
// CHECK-NEXT: alloc_ref
|
|
// CHECK-NOT: checked_cast_br
|
|
// CHECK-NOT: bb1
|
|
// CHECK: integer_literal $Builtin.Int32, 1
|
|
// CHECK: strong_release
|
|
// CHECK: return
|
|
sil @successful_checked_cast_br_on_alloc_ref : $() -> Builtin.Int32 {
|
|
bb0:
|
|
%1 = alloc_ref $B
|
|
checked_cast_br [exact] B in %1 : $B to B, bb1, bb2
|
|
|
|
bb1(%2 : $B):
|
|
%3 = integer_literal $Builtin.Int32, 1
|
|
br bb3 (%3 : $Builtin.Int32)
|
|
|
|
bb2:
|
|
%5 = integer_literal $Builtin.Int32, 2
|
|
br bb3 (%5 : $Builtin.Int32)
|
|
|
|
bb3 (%10: $Builtin.Int32):
|
|
strong_release %1 : $B
|
|
return %10 : $Builtin.Int32
|
|
}
|
|
|
|
// CHECK-LABEL: sil @failing_checked_cast_br_on_alloc_ref
|
|
// CHECK: bb0
|
|
// CHECK-NEXT: alloc_ref
|
|
// CHECK-NOT: checked_cast_br
|
|
// CHECK-NOT: bb1
|
|
// CHECK: integer_literal $Builtin.Int32, 2
|
|
// CHECK: strong_release
|
|
// CHECK: return
|
|
sil @failing_checked_cast_br_on_alloc_ref : $() -> Builtin.Int32 {
|
|
bb0:
|
|
%1 = alloc_ref $E
|
|
%2 = upcast %1 : $E to $B
|
|
checked_cast_br [exact] B in %2 : $B to B, bb1, bb2
|
|
|
|
bb1(%3 : $B):
|
|
%4 = integer_literal $Builtin.Int32, 1
|
|
br bb3 (%4 : $Builtin.Int32)
|
|
|
|
bb2:
|
|
%5 = integer_literal $Builtin.Int32, 2
|
|
br bb3 (%5 : $Builtin.Int32)
|
|
|
|
bb3 (%10: $Builtin.Int32):
|
|
strong_release %1 : $E
|
|
return %10 : $Builtin.Int32
|
|
}
|
|
|
|
@objc protocol ObjcProto { func foo() }
|
|
|
|
// CHECK-LABEL: sil @thread_objc_method_call_succ_block
|
|
// CHECK: bb0
|
|
// CHECK: cond_br {{.*}}, bb1, bb3
|
|
// CHECK: bb1
|
|
// CHECK: objc_method
|
|
// CHECK: apply
|
|
// CHECK: strong_release
|
|
// CHECK: cond_br {{.*}}, bb2, bb6
|
|
// CHECK: bb3
|
|
// CHECK: strong_release
|
|
// CHECK: cond_br {{.*}}, bb5, bb4
|
|
// CHECK: bb7
|
|
// CHECK: cond_fail
|
|
// CHECK: br bb8
|
|
// CHECK: bb8:
|
|
// CHECK: strong_release
|
|
// CHECK: return
|
|
|
|
sil @thread_objc_method_call_succ_block : $@convention(thin) <T where T : ObjcProto> (Builtin.Int1, @owned T, Builtin.Int1) -> () {
|
|
bb0(%0: $Builtin.Int1, %1 : $T, %2 : $Builtin.Int1):
|
|
strong_retain %1 : $T
|
|
cond_br %0, bb1 , bb2
|
|
|
|
bb1:
|
|
%3 = objc_method %1 : $T, #ObjcProto.foo!foreign, $@convention(objc_method) <τ_0_0 where τ_0_0 : ObjcProto> (τ_0_0) -> ()
|
|
%4 = apply %3<T>(%1) : $@convention(objc_method) <τ_0_0 where τ_0_0 : ObjcProto> (τ_0_0) -> ()
|
|
br bb2
|
|
|
|
bb2:
|
|
strong_release %1 : $T
|
|
cond_br %2, bb3, bb4
|
|
|
|
bb3:
|
|
cond_fail %0 : $Builtin.Int1
|
|
br bb4
|
|
|
|
bb4:
|
|
strong_release %1 : $T
|
|
%41 = tuple ()
|
|
return %41 : $()
|
|
}
|
|
|
|
sil @f_use : $@convention(thin) (Builtin.Int32) -> ()
|
|
|
|
// CHECK-LABEL: sil @switch_enum_jumpthreading_bug
|
|
// CHECK: bb1:
|
|
// CHECK: [[INVADD:%.*]] = builtin "sadd
|
|
// CHECK: [[EXT:%.*]] = tuple_extract [[INVADD]]
|
|
// CHECK: switch_enum {{.*}} case #Optional.some!enumelt: bb3
|
|
|
|
// CHECK: bb3{{.*}}
|
|
// CHECK: br bb5(%2 : $Builtin.Int32, [[EXT]]
|
|
|
|
// CHECK: bb5([[CUR:%.*]] : $Builtin.Int32, [[NEXT:%.*]] : $Builtin.Int32
|
|
// CHECK: [[F:%.*]] = function_ref @f
|
|
// CHECK: apply [[F]]([[CUR]])
|
|
// CHECK: cond_br {{.*}}, bb7, bb6
|
|
|
|
// CHECK: bb7:
|
|
// CHECK: [[VARADD:%.*]] = builtin "sadd_with_overflow_Int32"([[NEXT]] : $Builtin.Int32
|
|
// CHECK: [[NEXT2:%.*]] = tuple_extract [[VARADD]]
|
|
// CHECK: br bb5([[NEXT]] : $Builtin.Int32, [[NEXT2]]
|
|
|
|
|
|
sil @switch_enum_jumpthreading_bug : $@convention(thin) (Optional<Builtin.Int32>, Builtin.Int1, Builtin.Int32, Builtin.Int1) -> Builtin.Int32 {
|
|
bb0(%0 : $Optional<Builtin.Int32>, %1 : $Builtin.Int1, %2: $Builtin.Int32, %3 : $Builtin.Int1):
|
|
cond_br %1, bb2, bb10
|
|
|
|
bb2:
|
|
br bb3(%2 : $Builtin.Int32, %0 : $Optional<Builtin.Int32>)
|
|
|
|
bb3(%10 : $Builtin.Int32, %7 : $Optional<Builtin.Int32>):
|
|
%4 = integer_literal $Builtin.Int32, 1
|
|
%5 = integer_literal $Builtin.Int1, -1
|
|
%6 = builtin "sadd_with_overflow_Int32"(%10 : $Builtin.Int32, %4 : $Builtin.Int32, %5 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
|
|
%16 = tuple_extract %6 : $(Builtin.Int32, Builtin.Int1), 0
|
|
switch_enum %7 : $Optional<Builtin.Int32>, case #Optional.some!enumelt: bb5, case #Optional.none!enumelt: bb4
|
|
|
|
bb4:
|
|
cond_fail %5 : $Builtin.Int1
|
|
unreachable
|
|
|
|
bb5(%9 : $Builtin.Int32):
|
|
%f = function_ref @f_use : $@convention(thin) (Builtin.Int32) -> ()
|
|
%a = apply %f(%10) : $@convention(thin) (Builtin.Int32) -> ()
|
|
cond_br %3, bb6, bb10
|
|
|
|
bb6:
|
|
%8 = enum $Optional<Builtin.Int32>, #Optional.some!enumelt, %9 : $Builtin.Int32
|
|
br bb3(%16 : $Builtin.Int32, %8 : $Optional<Builtin.Int32>)
|
|
|
|
bb10:
|
|
br bb11(%2: $Builtin.Int32)
|
|
|
|
bb11(%100 : $Builtin.Int32):
|
|
return %100 : $Builtin.Int32
|
|
}
|
|
|
|
sil @a : $@convention(thin) () -> ()
|
|
sil @b : $@convention(thin) () -> ()
|
|
sil @c : $@convention(thin) () -> ()
|
|
sil @d : $@convention(thin) () -> ()
|
|
|
|
// CHECK-LABEL: sil @jump_thread_diamond
|
|
// CHECK: bb1:
|
|
// CHECK: [[A:%.*]] = function_ref @a
|
|
// CHECK: apply [[A]]
|
|
// CHECK: [[C:%.*]] = function_ref @c
|
|
// CHECK: apply [[C]]
|
|
// CHECK: br bb3
|
|
// CHECK: bb2:
|
|
// CHECK: [[B:%.*]] = function_ref @b
|
|
// CHECK: apply [[B]]
|
|
// CHECK: [[D:%.*]] = function_ref @d
|
|
// CHECK: apply [[D]]
|
|
// CHECK: br bb3
|
|
|
|
// CHECK: return
|
|
sil @jump_thread_diamond : $@convention(thin) (Builtin.Int1) -> () {
|
|
bb0(%0 : $Builtin.Int1):
|
|
%1 = integer_literal $Builtin.Int1, -1
|
|
%2 = builtin "int_expect_Int1"(%0 : $Builtin.Int1, %1 : $Builtin.Int1) : $Builtin.Int1
|
|
%8 = builtin "int_expect_Int1"(%0 : $Builtin.Int1, %1 : $Builtin.Int1) : $Builtin.Int1
|
|
%9 = builtin "int_expect_Int1"(%0 : $Builtin.Int1, %1 : $Builtin.Int1) : $Builtin.Int1
|
|
cond_br %8, bb1, bb2
|
|
|
|
bb1:
|
|
%10 = function_ref @a : $@convention(thin) () -> ()
|
|
%11 = apply %10() : $@convention(thin) () -> ()
|
|
br bb3
|
|
|
|
bb2:
|
|
%13 = function_ref @b : $@convention(thin) () -> ()
|
|
%14 = apply %13() : $@convention(thin) () -> ()
|
|
br bb3
|
|
|
|
bb3:
|
|
%19 = builtin "int_expect_Int1"(%0 : $Builtin.Int1, %1 : $Builtin.Int1) : $Builtin.Int1
|
|
%20 = builtin "int_expect_Int1"(%0 : $Builtin.Int1, %1 : $Builtin.Int1) : $Builtin.Int1
|
|
%21 = builtin "int_expect_Int1"(%0 : $Builtin.Int1, %1 : $Builtin.Int1) : $Builtin.Int1
|
|
cond_br %21, bb4, bb5
|
|
|
|
bb4:
|
|
%23 = function_ref @c : $@convention(thin) () -> ()
|
|
%24 = apply %23() : $@convention(thin) () -> ()
|
|
br bb6
|
|
|
|
bb5:
|
|
%26 = function_ref @d : $@convention(thin) () -> ()
|
|
%27 = apply %26() : $@convention(thin) () -> ()
|
|
br bb6
|
|
|
|
bb6:
|
|
%29 = tuple ()
|
|
return %29 : $()
|
|
}
|
|
|
|
enum AnEnum {
|
|
case B(Builtin.Int32), C(Builtin.Int16)
|
|
}
|
|
|
|
sil @f : $@convention(thin) (Builtin.Int32) -> ()
|
|
sil @f2 : $@convention(thin) (Builtin.Int16) -> ()
|
|
|
|
// CHECK-LABEL: sil @jump_thread_switch_enum
|
|
// CHECK: bb0([[ARG:%.*]] : $AnEnum):
|
|
// CHECK: [[F:%.*]] = function_ref @f : $@convention(thin) (Builtin.Int32) -> ()
|
|
// CHECK: [[F2:%.*]] = function_ref @f2 : $@convention(thin) (Builtin.Int16) -> ()
|
|
// CHECK: switch_enum [[ARG]] : $AnEnum, case #AnEnum.B!enumelt: bb2, case #AnEnum.C!enumelt: bb1
|
|
|
|
// CHECK: bb1([[ARG3:%.*]] : $Builtin.Int16):
|
|
// CHECK: apply [[F2]]([[ARG3]])
|
|
// CHECK: [[UED2:%.*]] = unchecked_enum_data [[ARG]] : $AnEnum, #AnEnum.C!enumelt
|
|
// CHECK: apply [[F2]]([[UED2]])
|
|
// CHECK: br bb3
|
|
|
|
// CHECK: bb2([[ARG2:%.*]] : $Builtin.Int32):
|
|
// CHECK: apply [[F]]([[ARG2]])
|
|
// CHECK: [[UED:%.*]] = unchecked_enum_data [[ARG]] : $AnEnum, #AnEnum.B!enumelt
|
|
// CHECK: apply [[F]]([[UED]])
|
|
// CHECK: br bb3
|
|
|
|
sil @jump_thread_switch_enum : $@convention(thin) (AnEnum) -> () {
|
|
bb0(%0 : $AnEnum):
|
|
|
|
%1 = function_ref @f : $@convention(thin) (Builtin.Int32) -> ()
|
|
|
|
%2 = function_ref @f2 : $@convention(thin) (Builtin.Int16) -> ()
|
|
switch_enum %0 : $AnEnum, case #AnEnum.B!enumelt: bb1, case #AnEnum.C!enumelt: bb3
|
|
|
|
bb1(%4 : $Builtin.Int32):
|
|
br bb2
|
|
|
|
bb2:
|
|
%6 = apply %1(%4) : $@convention(thin) (Builtin.Int32) -> ()
|
|
br bb5
|
|
|
|
bb3(%8 : $Builtin.Int16):
|
|
br bb4
|
|
|
|
bb4:
|
|
%10 = apply %2(%8) : $@convention(thin) (Builtin.Int16) -> ()
|
|
br bb5
|
|
|
|
bb5:
|
|
switch_enum %0 : $AnEnum, case #AnEnum.C!enumelt: bb6, case #AnEnum.B!enumelt: bb8
|
|
|
|
bb6(%13 : $Builtin.Int16):
|
|
br bb7
|
|
|
|
bb7:
|
|
%15 = apply %2(%13) : $@convention(thin) (Builtin.Int16) -> ()
|
|
br bb10
|
|
|
|
bb8(%17 : $Builtin.Int32):
|
|
br bb9
|
|
|
|
bb9:
|
|
%19 = apply %1(%17) : $@convention(thin) (Builtin.Int32) -> ()
|
|
br bb10
|
|
|
|
bb10:
|
|
%21 = tuple ()
|
|
return %21 : $()
|
|
}
|
|
|
|
sil @fB : $@convention(thin) () -> ()
|
|
sil @fC : $@convention(thin) () -> ()
|
|
|
|
|
|
// Make sure that we correctly thread such that we end up calling @fB on the
|
|
// AnEnum.B path.
|
|
|
|
// CHECK-LABEL: sil @dont_jump_thread_switch_enum_to_cond_br
|
|
// CHECK: [[BFUN:%.*]] = function_ref @fB : $@convention(thin) () -> ()
|
|
// CHECK: [[FALSE:%.*]] = integer_literal $Builtin.Int1, 0
|
|
// CHECK: switch_enum [[ENUM:%.*]] : $AnEnum, case #AnEnum.B!enumelt: bb2
|
|
// CHECK: bb2:
|
|
// CHECK: [[F:%.*]] = select_enum [[ENUM]] : $AnEnum, case #AnEnum.B!enumelt: [[FALSE]]
|
|
// CHECK: cond_br [[F]], bb3, bb4
|
|
// CHECK: bb4:
|
|
// CHECK-NOT: br
|
|
// CHECK: apply [[BFUN]]
|
|
// CHECK: br
|
|
|
|
sil @dont_jump_thread_switch_enum_to_cond_br : $@convention(thin) (AnEnum) -> () {
|
|
bb0(%0 : $AnEnum):
|
|
%1 = function_ref @fB : $@convention(thin) () -> ()
|
|
%2 = function_ref @fC : $@convention(thin) () -> ()
|
|
%t = integer_literal $Builtin.Int1, 1
|
|
%f = integer_literal $Builtin.Int1, 0
|
|
switch_enum %0 : $AnEnum, case #AnEnum.B!enumelt: bb4, case #AnEnum.C!enumelt: bb5
|
|
|
|
bb4:
|
|
br bb1
|
|
|
|
bb5:
|
|
%8 = select_enum %0 : $AnEnum, case #AnEnum.B!enumelt: %f, case #AnEnum.C!enumelt: %t : $Builtin.Int1
|
|
cond_br %8, bb10, bb1
|
|
|
|
bb1:
|
|
%3 = select_enum %0 : $AnEnum, case #AnEnum.B!enumelt: %f, case #AnEnum.C!enumelt: %t : $Builtin.Int1
|
|
cond_br %3, bb2, bb3
|
|
|
|
bb2:
|
|
%6 = apply %2() : $@convention(thin) () -> ()
|
|
br bb10
|
|
|
|
bb3:
|
|
%7 = apply %1() : $@convention(thin) () -> ()
|
|
br bb10
|
|
|
|
|
|
bb10:
|
|
%21 = tuple ()
|
|
return %21 : $()
|
|
}
|
|
|
|
sil @rethrow_function : $@convention(thin) (@owned @callee_owned (Int) -> (Int, @error Error)) -> (Int, @error Error)
|
|
sil @non_throwing_closure : $@convention(thin) (Int) -> Int
|
|
|
|
// CHECK-LABEL: sil @replace_try_apply_with_apply
|
|
// CHECK: [[R:%[0-9]+]] = apply [nothrow] %1(%{{[0-9]+}}) : $@convention(thin) (@owned @callee_owned (Int) -> (Int, @error any Error)) -> (Int, @error any Error)
|
|
// CHECK-NEXT: dealloc_stack
|
|
// CHECK-NEXT: return [[R]] : $Int
|
|
sil @replace_try_apply_with_apply : $@convention(thin) () -> Int {
|
|
bb0:
|
|
%as = alloc_stack $Builtin.Int32
|
|
%0 = function_ref @rethrow_function : $@convention(thin) (@owned @callee_owned (Int) -> (Int, @error Error)) -> (Int, @error Error)
|
|
%1 = function_ref @non_throwing_closure : $@convention(thin) (Int) -> Int
|
|
%2 = thin_to_thick_function %1 : $@convention(thin) (Int) -> Int to $@callee_owned (Int) -> Int
|
|
%3 = convert_function %2 : $@callee_owned (Int) -> Int to $@callee_owned (Int) -> (Int, @error Error)
|
|
try_apply %0(%3) : $@convention(thin) (@owned @callee_owned (Int) -> (Int, @error Error)) -> (Int, @error Error), normal bb1, error bb2
|
|
|
|
bb1(%5 : $Int):
|
|
dealloc_stack %as: $*Builtin.Int32
|
|
return %5 : $Int
|
|
|
|
bb2(%8 : $Error):
|
|
dealloc_stack %as: $*Builtin.Int32
|
|
unreachable
|
|
}
|
|
|
|
public class EE {
|
|
init()
|
|
}
|
|
|
|
public class BB {
|
|
init()
|
|
}
|
|
|
|
public class CC : BB {
|
|
@inline(never) init(e: EE)
|
|
override init()
|
|
}
|
|
|
|
public protocol PP {
|
|
var prop1: BB? { get }
|
|
}
|
|
|
|
public class DD : PP {
|
|
public var prop1: BB? { get }
|
|
init()
|
|
}
|
|
|
|
|
|
// CHECK-LABEL: sil @replace_try_apply_with_apply_cast_return_type : $@convention(method) (@guaranteed DD) -> @owned Optional<BB>
|
|
// CHECK: bb0
|
|
// CHECK: apply %{{.*}}
|
|
// CHECK: convert_function
|
|
// CHECK-NOT: try_apply
|
|
// CHECK: apply
|
|
// Check that return value is properly casted
|
|
// CHECK-NEXT: enum $Optional<CC>, #Optional.some!enumelt, %{{.*}} : $CC
|
|
// CHECK-NEXT: upcast %{{.*}} : $Optional<CC> to $Optional<BB>
|
|
// CHECK-NEXT: return
|
|
sil @replace_try_apply_with_apply_cast_return_type: $@convention(method) (@guaranteed DD) -> @owned Optional<BB> {
|
|
bb0(%0 : $DD):
|
|
%1 = alloc_ref $EE
|
|
debug_value %1 : $EE
|
|
%3 = function_ref @initCC : $@convention(thin) (@thick CC.Type) -> @owned @callee_owned (@owned EE) -> @owned CC
|
|
%4 = metatype $@thick CC.Type
|
|
%5 = apply %3(%4) : $@convention(thin) (@thick CC.Type) -> @owned @callee_owned (@owned EE) -> @owned CC
|
|
%6 = convert_function %5 : $@callee_owned (@owned EE) -> @owned CC to $@callee_owned (@owned EE) -> (@owned Optional<BB>, @error Error)
|
|
try_apply %6(%1) : $@callee_owned (@owned EE) -> (@owned Optional<BB>, @error Error), normal bb1, error bb2
|
|
|
|
bb1(%8 : $Optional<BB>):
|
|
return %8 : $Optional<BB>
|
|
|
|
bb2(%10 : $Error):
|
|
unreachable
|
|
}
|
|
|
|
// Check that we don't crash on this, because we perform casting
|
|
// if the argument types of the converted function types do not match.
|
|
// CHECK-LABEL: try_apply_with_apply_of_cast_argument
|
|
// CHECK-NOT: try_apply {{%[0-9]+}}
|
|
// CHECK: convert_function
|
|
// CHECK: upcast
|
|
// CHECK: apply
|
|
// CHECK-NOT: try_apply
|
|
// CHECK: return
|
|
sil @try_apply_with_apply_of_cast_argument: $@convention(method) (@owned CC) -> @owned BB {
|
|
bb0(%0 : $CC):
|
|
%3 = function_ref @takeBB : $@convention(thin) (@owned BB) -> @owned BB
|
|
%6 = convert_function %3 : $@convention(thin) (@owned BB) -> @owned BB to $@convention(thin) (@owned CC) -> (@owned BB, @error Error)
|
|
try_apply %6(%0) : $@convention(thin) (@owned CC) -> (@owned BB, @error Error), normal bb1, error bb2
|
|
|
|
bb1(%8 : $BB):
|
|
return %8 : $BB
|
|
|
|
bb2(%10 : $Error):
|
|
// Prevent that the conversion is done because the error block is empty and unreachable.
|
|
%12 = function_ref @unknown : $@convention(thin) () -> ()
|
|
apply %12() : $@convention(thin) () -> ()
|
|
unreachable
|
|
}
|
|
|
|
sil [noinline] @initCC : $@convention(thin) (@thick CC.Type) -> @owned @callee_owned (@owned EE) -> @owned CC
|
|
|
|
sil [noinline] @takeBB : $@convention(thin) (@owned BB) -> @owned BB
|
|
|
|
// Check that we don't crash on this.
|
|
// The compiler should be able to cast between the labeled and unlabeled return tuple types.
|
|
// CHECK-LABEL: @try_apply_with_convert_function_returning_casted_unlabeled_tuple
|
|
// CHECK: [[T0:%.*]] = apply {{%[0-9]+}}
|
|
// CHECK: return [[T0]]
|
|
sil @try_apply_with_convert_function_returning_casted_unlabeled_tuple: $@convention(thin) () -> (Int32, Int32) {
|
|
bb0:
|
|
%3 = function_ref @returnLabeledTuple : $@convention(thin) () -> (Int32, Int32)
|
|
%6 = convert_function %3 : $@convention(thin) () -> (Int32, Int32) to $@convention(thin) () -> (Int32, Int32, @error Error)
|
|
try_apply %6() : $@convention(thin) () -> (Int32, Int32, @error Error), normal bb1, error bb2
|
|
|
|
bb1(%8 : $(Int32, Int32)):
|
|
return %8 : $(Int32, Int32)
|
|
|
|
bb2(%10 : $Error):
|
|
// Prevent that the conversion is done because the error block is empty and unreachable.
|
|
%12 = function_ref @unknown : $@convention(thin) () -> ()
|
|
apply %12() : $@convention(thin) () -> ()
|
|
unreachable
|
|
}
|
|
|
|
// Check that we don't crash on this.
|
|
// The compiler should be able to cast between the labeled and unlabeled return tuple types.
|
|
// CHECK-LABEL: @try_apply_with_convert_function_returning_casted_labeled_tuple
|
|
// CHECK: apply {{%[0-9]+}}
|
|
// Proper tuple is created by deconstructing the old one and creating a new one using its elements.
|
|
// CHECK: tuple_extract
|
|
// CHECK: tuple_extract
|
|
// CHECK: tuple
|
|
// CHECK: return
|
|
sil @try_apply_with_convert_function_returning_casted_labeled_tuple: $@convention(thin) () -> (Int32, Int32) {
|
|
bb0:
|
|
%3 = function_ref @returnUnlabeledTuple : $@convention(thin) () -> (Int32, Int32)
|
|
%6 = convert_function %3 : $@convention(thin) () -> (Int32, Int32) to $@convention(thin) () -> (Int32, Int32, @error Error)
|
|
try_apply %6() : $@convention(thin) () -> (Int32, Int32, @error Error), normal bb1, error bb2
|
|
|
|
bb1(%8 : $(Int32, Int32)):
|
|
return %8 : $(Int32, Int32)
|
|
|
|
bb2(%10 : $Error):
|
|
// Prevent that the conversion is done because the error block is empty and unreachable.
|
|
%12 = function_ref @unknown : $@convention(thin) () -> ()
|
|
apply %12() : $@convention(thin) () -> ()
|
|
unreachable
|
|
}
|
|
|
|
sil [noinline] @returnLabeledTuple: $@convention(thin) () -> (Int32, Int32)
|
|
sil [noinline] @returnUnlabeledTuple : $@convention(thin) () -> (Int32, Int32)
|
|
|
|
public class AAA {
|
|
}
|
|
|
|
public class BBB : AAA {
|
|
}
|
|
|
|
@inline(never) func returnUnlabeledTuple(b: BBB) -> (BBB, BBB)
|
|
|
|
func testit(f: (BBB) throws -> (AAA, AAA), _ b: BBB) throws -> (AAA, AAA)
|
|
|
|
func callit(b: BBB) throws -> (AAA, AAA)
|
|
|
|
sil [noinline] @returnUnlabeledTupleOfClasses : $@convention(thin) (@owned BBB) -> @owned (BBB, BBB) {
|
|
bb0(%0 : $BBB):
|
|
debug_value %0 : $BBB
|
|
strong_retain %0 : $BBB
|
|
%3 = tuple (%0 : $BBB, %0 : $BBB)
|
|
return %3 : $(BBB, BBB)
|
|
}
|
|
|
|
sil @testFunctorReturningUnlabeledTuple : $@convention(thin) (@owned @callee_owned (@owned BBB) -> (@owned (AAA, AAA), @error Error), @owned BBB) -> (@owned (AAA, AAA), @error Error) {
|
|
bb0(%0 : $@callee_owned (@owned BBB) -> (@owned (AAA, AAA), @error Error), %1 : $BBB):
|
|
debug_value %0 : $@callee_owned (@owned BBB) -> (@owned (AAA, AAA), @error Error)
|
|
debug_value %1 : $BBB
|
|
strong_retain %0 : $@callee_owned (@owned BBB) -> (@owned (AAA, AAA), @error Error)
|
|
strong_retain %1 : $BBB
|
|
try_apply %0(%1) : $@callee_owned (@owned BBB) -> (@owned (AAA, AAA), @error Error), normal bb1, error bb2
|
|
|
|
bb1(%7 : $(AAA, AAA)):
|
|
%8 = tuple_extract %7 : $(AAA, AAA), 0
|
|
%9 = tuple_extract %7 : $(AAA, AAA), 1
|
|
%10 = tuple (%8 : $AAA, %9 : $AAA)
|
|
strong_release %1 : $BBB
|
|
strong_release %0 : $@callee_owned (@owned BBB) -> (@owned (AAA, AAA), @error Error)
|
|
return %10 : $(AAA, AAA)
|
|
|
|
bb2(%14 : $Error):
|
|
strong_release %1 : $BBB
|
|
strong_release %0 : $@callee_owned (@owned BBB) -> (@owned (AAA, AAA), @error Error)
|
|
throw %14 : $Error
|
|
}
|
|
|
|
|
|
// Check that we don't crash on this. Currently we just do not optimize try_apply if
|
|
// we cannot cast the actual return type into expected return type.
|
|
// TODO: Change the checks when we support more complex casts of return types.
|
|
// CHECK-LABEL: @testCallingFunctionWithFunctorReturningUnlabeledTuple
|
|
// CHECK: try_apply {{%[0-9]+}}
|
|
// CHECK: return
|
|
sil @testCallingFunctionWithFunctorReturningUnlabeledTuple : $@convention(thin) (@owned BBB) -> (@owned (AAA, AAA), @error Error) {
|
|
bb0(%0 : $BBB):
|
|
debug_value %0 : $BBB
|
|
|
|
%2 = function_ref @testFunctorReturningUnlabeledTuple : $@convention(thin) (@owned @callee_owned (@owned BBB) -> (@owned (AAA, AAA), @error Error), @owned BBB) -> (@owned (AAA, AAA), @error Error)
|
|
|
|
%3 = function_ref @returnUnlabeledTupleOfClasses : $@convention(thin) (@owned BBB) -> @owned (BBB, BBB)
|
|
%4 = thin_to_thick_function %3 : $@convention(thin) (@owned BBB) -> @owned (BBB, BBB) to $@callee_owned (@owned BBB) -> @owned (BBB, BBB)
|
|
%5 = convert_function %4 : $@callee_owned (@owned BBB) -> @owned (BBB, BBB) to $@callee_owned (@owned BBB) -> (@owned (AAA, AAA), @error Error)
|
|
strong_retain %0 : $BBB
|
|
try_apply %2(%5, %0) : $@convention(thin) (@owned @callee_owned (@owned BBB) -> (@owned (AAA, AAA), @error Error), @owned BBB) -> (@owned (AAA, AAA), @error Error), normal bb1, error bb2
|
|
|
|
bb1(%8 : $(AAA, AAA)):
|
|
%9 = tuple_extract %8 : $(AAA, AAA), 0
|
|
%10 = tuple_extract %8 : $(AAA, AAA), 1
|
|
%11 = tuple (%9 : $AAA, %10 : $AAA)
|
|
strong_release %0 : $BBB
|
|
return %11 : $(AAA, AAA)
|
|
|
|
bb2(%14 : $Error):
|
|
strong_release %0 : $BBB
|
|
throw %14 : $Error
|
|
}
|
|
|
|
struct UP<T> {
|
|
|
|
}
|
|
|
|
struct UBP<A> {
|
|
}
|
|
|
|
struct CAB<A> {
|
|
|
|
}
|
|
|
|
sil @CABIdentityGetter : $@convention(thin) <τ_0_0> (UBP<τ_0_0>) -> UP<()>
|
|
sil @CABwithUnsafeBufferPointer : $@convention(method) <τ_0_0><τ_1_0> (@owned @callee_owned (UBP<τ_0_0>) -> (@out τ_1_0, @error Error), @guaranteed CAB<τ_0_0>) -> (@out τ_1_0, @error Error)
|
|
sil @thunk_helper : $@convention(thin) <τ_0_0> (UBP<τ_0_0>, @owned @callee_owned (UBP<τ_0_0>) -> (UP<()>, @error Error)) -> (@out UP<()>, @error Error)
|
|
|
|
// CHECK-LABEL: sil @check_parameters_casting_with_generics
|
|
// CHECK-NOT: try_apply
|
|
// CHECK: apply [nothrow] %{{.*}}<Element, UP<()>>
|
|
// CHECK: return
|
|
sil @check_parameters_casting_with_generics : $@convention(method) <Element> (@guaranteed CAB<Element>) -> UP<()> {
|
|
bb0(%0 : $CAB<Element>):
|
|
// function_ref Swift._ContiguousArrayBuffer.withUnsafeBufferPointer <A><B> (CAB<A>)((Swift.UBP<A>) throws -> B) throws -> B
|
|
%2 = function_ref @CABwithUnsafeBufferPointer : $@convention(method) <τ_0_0><τ_1_0> (@owned @callee_owned (UBP<τ_0_0>) -> (@out τ_1_0, @error Error), @guaranteed CAB<τ_0_0>) -> (@out τ_1_0, @error Error)
|
|
// function_ref Swift._ContiguousArrayBuffer.(identity.getter : UP<()>).(closure #1)
|
|
%3 = function_ref @CABIdentityGetter : $@convention(thin) <τ_0_0> (UBP<τ_0_0>) -> UP<()>
|
|
%4 = partial_apply %3<Element>() : $@convention(thin) <τ_0_0> (UBP<τ_0_0>) -> UP<()>
|
|
%5 = convert_function %4 : $@callee_owned (UBP<Element>) -> UP<()> to $@callee_owned (UBP<Element>) -> (UP<()>, @error Error)
|
|
// function_ref reabstraction thunk helper <A> from @callee_owned (@unowned UBP<A>) -> (@unowned UP<()>, @error @owned Swift.Error) to @callee_owned (@unowned UBP<A>) -> (@out UP<()>, @error @owned Swift.Error)
|
|
%6 = function_ref @thunk_helper : $@convention(thin) <τ_0_0> (UBP<τ_0_0>, @owned @callee_owned (UBP<τ_0_0>) -> (UP<()>, @error Error)) -> (@out UP<()>, @error Error)
|
|
%7 = partial_apply %6<Element>(%5) : $@convention(thin) <τ_0_0> (UBP<τ_0_0>, @owned @callee_owned (UBP<τ_0_0>) -> (UP<()>, @error Error)) -> (@out UP<()>, @error Error)
|
|
%8 = alloc_stack $UP<()>
|
|
%9 = unchecked_addr_cast %8 : $*UP<()> to $*UP<()>
|
|
%10 = convert_function %7 : $@callee_owned (UBP<Element>) -> (@out UP<()>, @error Error) to $@callee_owned (UBP<Element>) -> (@out UP<()>, @error Error)
|
|
try_apply %2<Element, UP<()>>(%8, %7, %0) : $@convention(method) <τ_0_0><τ_1_0> (@owned @callee_owned (UBP<τ_0_0>) -> (@out τ_1_0, @error Error), @guaranteed CAB<τ_0_0>) -> (@out τ_1_0, @error Error), normal bb1, error bb2
|
|
|
|
bb1(%12 : $()):
|
|
%13 = load %8 : $*UP<()>
|
|
dealloc_stack %8 : $*UP<()>
|
|
return %13 : $UP<()>
|
|
|
|
bb2(%16 : $Error):
|
|
unreachable
|
|
}
|
|
|
|
// CHECK-LABEL: sil @unpack_enum_arg
|
|
// CHECK: bb1:
|
|
// CHECK: br bb3(%0 : $Int)
|
|
// CHECK: bb2:
|
|
// CHECK: br bb3(%1 : $Int)
|
|
// CHECK: bb3([[A:%[0-9]+]] : $Int):
|
|
// CHECK: return [[A]]
|
|
sil @unpack_enum_arg : $@convention(thin) (Int, Int) -> Int {
|
|
bb0(%0 : $Int, %1 : $Int):
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
%2 = enum $Optional<Int>, #Optional.some!enumelt, %0 : $Int
|
|
br bb3(%2 : $Optional<Int>)
|
|
|
|
bb2:
|
|
%3 = enum $Optional<Int>, #Optional.some!enumelt, %1 : $Int
|
|
br bb3(%3 : $Optional<Int>)
|
|
|
|
bb3(%4 : $Optional<Int>):
|
|
%5 = unchecked_enum_data %4 : $Optional<Int>, #Optional.some!enumelt
|
|
return %5 : $Int
|
|
}
|
|
|
|
|
|
// CHECK-LABEL: sil @dont_crash_on_enum_payload_is_enum
|
|
// CHECK: bb0(%0 : $TwoCase):
|
|
// CHECK: switch_enum %0
|
|
// CHECK: bb1:
|
|
// CHECK: return
|
|
sil @dont_crash_on_enum_payload_is_enum : $@convention(thin) (TwoCase) -> () {
|
|
bb0(%0 : $TwoCase):
|
|
%x = enum $Optional<TwoCase>, #Optional.some!enumelt, %0 : $TwoCase
|
|
br bb1(%x : $Optional<TwoCase>)
|
|
|
|
bb1(%10 : $Optional<TwoCase>):
|
|
switch_enum %10 : $Optional<TwoCase>, case #Optional.some!enumelt: bb2, case #Optional.none!enumelt: bb5
|
|
|
|
bb2(%1 : $TwoCase):
|
|
// Simplification of that switch_enum crashed the compiler because
|
|
// SILArgument::getIncomingValues returns %10 ($Optional<TwoCase>) as the
|
|
// incoming value for %1 ($TwoCase).
|
|
// rdar://problem/26251856
|
|
switch_enum %1 : $TwoCase, case #TwoCase.First!enumelt: bb3, case #TwoCase.Second!enumelt: bb4
|
|
|
|
bb3:
|
|
%f1 = function_ref @unknown : $@convention(thin) () -> ()
|
|
%n1 = apply %f1() : $@convention(thin) () -> ()
|
|
br bb6
|
|
|
|
bb4:
|
|
br bb6
|
|
|
|
bb5:
|
|
br bb6
|
|
|
|
bb6:
|
|
%6 = tuple ()
|
|
return %6 : $()
|
|
}
|
|
|
|
enum TestEnum {
|
|
case int64(Builtin.Int64)
|
|
case string (Base)
|
|
case none
|
|
}
|
|
|
|
enum MyError : Error {
|
|
case a
|
|
case b
|
|
case c(TestEnum)
|
|
}
|
|
|
|
enum MyError2 : Error {
|
|
case o(Optional<MyError>)
|
|
}
|
|
|
|
sil @foo : $@convention(thin) (Builtin.Int64) -> Builtin.Int8
|
|
sil @foo2 : $@convention(thin) (Builtin.Int32) -> Builtin.Int8
|
|
|
|
sil @dont_thread_throw_block : $@convention(thin) (@guaranteed TestEnum) -> (Builtin.Int8, @error Error) {
|
|
bb0(%0 : $TestEnum):
|
|
switch_enum %0 : $TestEnum, case #TestEnum.int64!enumelt: bb1, default bb4
|
|
|
|
bb1(%5 : $Builtin.Int64):
|
|
%7 = function_ref @foo : $@convention(thin) (Builtin.Int64) -> Builtin.Int8
|
|
%9 = apply %7(%5) : $@convention(thin) (Builtin.Int64) -> Builtin.Int8
|
|
br bb6(%9 : $Builtin.Int8)
|
|
|
|
bb2(%11 : $Builtin.Int32):
|
|
%13 = function_ref @foo2 : $@convention(thin) (Builtin.Int32) -> Builtin.Int8
|
|
%15 = apply %13(%11) : $@convention(thin) (Builtin.Int32) -> Builtin.Int8
|
|
br bb6(%15 : $Builtin.Int8)
|
|
|
|
bb4:
|
|
debug_value %0 : $TestEnum
|
|
%22 = alloc_existential_box $Error, $MyError2
|
|
%23 = project_existential_box $MyError2 in %22 : $Error
|
|
switch_enum %0 : $TestEnum, case #TestEnum.none!enumelt: bbnone, case #TestEnum.int64!enumelt: bb7, case #TestEnum.string!enumelt: bb10
|
|
|
|
bbnone:
|
|
%tn = enum $TestEnum, #TestEnum.none!enumelt
|
|
%en = enum $MyError, #MyError.c!enumelt, %tn : $TestEnum
|
|
br bb5(%en : $MyError)
|
|
|
|
bb7(%50 : $Builtin.Int64):
|
|
%t = enum $TestEnum, #TestEnum.int64!enumelt, %50 : $Builtin.Int64
|
|
%e1 = enum $MyError, #MyError.c!enumelt, %t : $TestEnum
|
|
br bb5(%e1 : $MyError)
|
|
|
|
bb10(%53 : $Base):
|
|
%t4 = enum $TestEnum, #TestEnum.string!enumelt, %53 : $Base
|
|
%e4 = enum $MyError, #MyError.c!enumelt, %t4 : $TestEnum
|
|
br bb5(%e4 : $MyError)
|
|
|
|
bb5(%e : $MyError):
|
|
%89 = enum $Optional<MyError>, #Optional.some!enumelt, %e : $MyError
|
|
%e5 = enum $MyError2, #MyError2.o!enumelt, %89 : $Optional<MyError>
|
|
store %e5 to %23 : $*MyError2
|
|
throw %22 : $Error
|
|
|
|
bb6(%44 : $Builtin.Int8):
|
|
return %44 : $Builtin.Int8
|
|
}
|
|
|
|
// CHECK-LABEL: jump_thread_retain_release
|
|
// CHECK: cond_br
|
|
// CHECK: cond_br %3, bb4, bb3
|
|
|
|
// CHECK: bb3:
|
|
// CHECK: strong_retain %2 : $foo
|
|
// CHECK: strong_release %2 : $foo
|
|
// CHECK: br bb5(%2 : $foo)
|
|
|
|
// CHECK: bb4:
|
|
// CHECK: strong_retain %1 : $foo
|
|
// CHECK: strong_release %1 : $foo
|
|
// CHECK: br bb5(%1 : $foo)
|
|
|
|
sil @jump_thread_retain_release : $@convention(thin) (Builtin.Int1, foo, foo, Builtin.Int1) -> () {
|
|
bb0(%0 : $Builtin.Int1, %1 : $foo, %2 : $foo, %3 : $Builtin.Int1):
|
|
cond_br %0, bb2, bb1
|
|
|
|
bb1:
|
|
br bb6(%2 : $foo)
|
|
|
|
bb2:
|
|
cond_br %3, bb3, bb4
|
|
|
|
bb3:
|
|
strong_retain %1 : $foo
|
|
br bb5(%1 : $foo)
|
|
|
|
bb4:
|
|
strong_retain %2 : $foo
|
|
br bb5(%2 : $foo)
|
|
|
|
bb5(%11 : $foo):
|
|
strong_release %11 : $foo
|
|
br bb6(%11 : $foo)
|
|
|
|
bb6(%14 : $foo):
|
|
strong_release %14 : $foo
|
|
%16 = tuple ()
|
|
return %16 : $()
|
|
}
|
|
|
|
protocol Q {}
|
|
|
|
class IsQ : Q {}
|
|
|
|
// checked_cast_br take_* should be replaced by a destroy in case it ever
|
|
// converts a managed object to an unmanaged value. Currently this doesn't
|
|
// happen at the language level because bridge (NSNumber->Int) casts aren't
|
|
// represented with checked_cast_br.
|
|
// ---
|
|
// CHECK-LABEL: sil @test_dead_checked_cast_br : $@convention(thin) (@in IsQ) -> () {
|
|
// CHECK: bb0(%0 : $*IsQ):
|
|
// CHECK: [[Q:%.*]] = alloc_stack $any Q
|
|
// CHECK: [[LD:%.*]] = load %0 : $*IsQ
|
|
// CHECK: strong_release [[LD]] : $IsQ
|
|
// CHECK: dealloc_stack [[Q]] : $*any Q
|
|
// CHECK: [[R:%.*]] = tuple ()
|
|
// CHECK: return [[R]] : $()
|
|
// CHECK-LABEL: } // end sil function 'test_dead_checked_cast_br'
|
|
sil @test_dead_checked_cast_br : $@convention(thin) (@in IsQ) -> () {
|
|
bb0(%0 : $*IsQ):
|
|
%p = alloc_stack $Q
|
|
checked_cast_addr_br take_always IsQ in %0 : $*IsQ to Q in %p : $*Q, bb1, bb3
|
|
|
|
bb1:
|
|
%m1 = integer_literal $Builtin.Int1, -1
|
|
br bb2(%m1 : $Builtin.Int1)
|
|
|
|
bb2(%5 : $Builtin.Int1):
|
|
// To avoid violating ownership, Q needs to be destroyed here. However, that
|
|
// would create a use of the checked_cast, defeating the test. In theory, Q
|
|
// could be some unmanaged type with no destroy, but we don't have a way to
|
|
// express that in the type system, and bridged casts don't yet go through
|
|
// this optimization path.
|
|
dealloc_stack %p : $*Q
|
|
%r = tuple ()
|
|
return %r : $()
|
|
|
|
bb3:
|
|
%z = integer_literal $Builtin.Int1, 0
|
|
br bb2(%z : $Builtin.Int1)
|
|
}
|
|
|
|
// CHECK-LABEL: sil @trivial_checked_cast_addr_br : $@convention(thin) (@guaranteed Int, @guaranteed Int) -> @owned Int
|
|
// CHECK: [[SRC:%.*]] = alloc_stack $Int
|
|
// CHECK: store %0 to [[SRC]]
|
|
// CHECK: [[DST:%.*]] = alloc_stack $Int
|
|
// CHECK: [[LD1:%.*]] = load [[SRC]]
|
|
// CHECK: store [[LD1]] to [[DST]]
|
|
// CHECK: [[LD2:%.*]] = load [[DST]]
|
|
// CHECK: return [[LD2]]
|
|
// CHECK: } // end sil function 'trivial_checked_cast_addr_br'
|
|
sil @trivial_checked_cast_addr_br : $@convention(thin) (@guaranteed Int, @guaranteed Int) -> @owned Int {
|
|
bb0(%0 : $Int, %1 : $Int):
|
|
%6 = alloc_stack $Int
|
|
store %0 to %6 : $*Int
|
|
%8 = alloc_stack $Int
|
|
checked_cast_addr_br copy_on_success Int in %6 : $*Int to Int in %8 : $*Int, bb1, bb2
|
|
|
|
bb1:
|
|
%10 = load %8 : $*Int
|
|
dealloc_stack %8 : $*Int
|
|
br bb3(%10 : $Int)
|
|
|
|
bb2:
|
|
retain_value %1: $Int
|
|
dealloc_stack %8 : $*Int
|
|
br bb3(%1 : $Int)
|
|
|
|
bb3(%11 : $Int):
|
|
dealloc_stack %6 : $*Int
|
|
return %11 : $Int
|
|
}
|
|
|
|
// CHECK-LABEL: sil @non_trivial_checked_cast_addr_br : $@convention(thin) (@guaranteed String, @guaranteed String) -> @owned String
|
|
// CHECK: [[SRC:%.*]] = alloc_stack $String
|
|
// CHECK: store %0 to [[SRC]]
|
|
// CHECK: [[DST:%.*]] = alloc_stack $String
|
|
// CHECK: [[TEMP:%.*]] = alloc_stack $String
|
|
// CHECK: copy_addr [[SRC]] to [init] [[TEMP]]
|
|
// CHECK: [[LD1:%.*]] = load [[TEMP]]
|
|
// CHECK: store [[LD1]] to [[DST]]
|
|
// CHECK: [[LD2:%.*]] = load [[DST]]
|
|
// CHECK: return [[LD2]]
|
|
// CHECK: } // end sil function 'non_trivial_checked_cast_addr_br'
|
|
sil @non_trivial_checked_cast_addr_br : $@convention(thin) (@guaranteed String, @guaranteed String) -> @owned String {
|
|
bb0(%0 : $String, %1 : $String):
|
|
%6 = alloc_stack $String
|
|
store %0 to %6 : $*String
|
|
%8 = alloc_stack $String
|
|
checked_cast_addr_br copy_on_success String in %6 : $*String to String in %8 : $*String, bb1, bb2
|
|
|
|
bb1:
|
|
%10 = load %8 : $*String
|
|
dealloc_stack %8 : $*String
|
|
br bb3(%10 : $String)
|
|
|
|
bb2:
|
|
retain_value %1: $String
|
|
dealloc_stack %8 : $*String
|
|
br bb3(%1 : $String)
|
|
|
|
bb3(%11 : $String):
|
|
dealloc_stack %6 : $*String
|
|
return %11 : $String
|
|
}
|
|
|
|
// CHECK-LABEL: sil @dont_hang
|
|
// CHECK: bb6:
|
|
// CHECK: integer_literal $Builtin.Int64, 1
|
|
// CHECK-NEXT: br bb6
|
|
// CHECK-NEXT: }
|
|
sil @dont_hang : $@convention(thin) () -> () {
|
|
bb0:
|
|
cond_br undef, bb1, bb4
|
|
|
|
bb1:
|
|
%0 = integer_literal $Builtin.Int64, 1
|
|
cond_br undef, bb2, bb6
|
|
|
|
bb2:
|
|
br bb3
|
|
|
|
bb3:
|
|
br bb5
|
|
|
|
bb4:
|
|
%1 = integer_literal $Builtin.Int64, 1
|
|
br bb3
|
|
|
|
bb5:
|
|
br bb2
|
|
|
|
bb6:
|
|
%2 = integer_literal $Builtin.Int64, 1
|
|
br bb7
|
|
|
|
bb7:
|
|
br bb5
|
|
}
|
|
|
|
// CHECK-LABEL: sil @test_constant_folding
|
|
// CHECK: [[R:%[0-9]+]] = integer_literal $Builtin.Int32, 30
|
|
// CHECK: return [[R]] : $Builtin.Int32
|
|
// CHECK-NEXT: }
|
|
sil @test_constant_folding : $@convention(thin) () -> Builtin.Int32 {
|
|
bb0:
|
|
%0 = integer_literal $Builtin.Int1, 0
|
|
%20 = integer_literal $Builtin.Int32, 20
|
|
%30 = integer_literal $Builtin.Int32, 30
|
|
cond_br %0, bb1, bb2
|
|
bb1:
|
|
br bb3(%20 : $Builtin.Int32)
|
|
bb2:
|
|
br bb3(%30 : $Builtin.Int32)
|
|
|
|
bb3(%2 : $Builtin.Int32):
|
|
%3 = builtin "cmp_slt_Int32"(%2 : $Builtin.Int32, %30 : $Builtin.Int32) : $Builtin.Int1
|
|
cond_br %3, bb4, bb5
|
|
bb4:
|
|
br bb6(%20 : $Builtin.Int32)
|
|
bb5:
|
|
br bb6(%30 : $Builtin.Int32)
|
|
|
|
bb6(%4 : $Builtin.Int32):
|
|
%5 = builtin "cmp_slt_Int32"(%4 : $Builtin.Int32, %30 : $Builtin.Int32) : $Builtin.Int1
|
|
cond_br %5, bb7, bb8
|
|
bb7:
|
|
br bb9(%20 : $Builtin.Int32)
|
|
bb8:
|
|
br bb9(%30 : $Builtin.Int32)
|
|
|
|
bb9(%6 : $Builtin.Int32):
|
|
return %6 : $Builtin.Int32
|
|
}
|
|
|
|
sil @adder : $@convention(thin) (Builtin.Int32, Builtin.Int32) -> Builtin.Int32
|
|
|
|
// CHECK-LABEL: sil @test_noescape
|
|
// CHECK: [[FN:%.*]] = function_ref @adder
|
|
// CHECK: [[PA:%.*]] = partial_apply [callee_guaranteed] [[FN]](%0)
|
|
// CHECK-NOT: try_apply
|
|
// CHECK: apply [[PA]](%1)
|
|
// CHECK: return
|
|
sil @test_noescape : $@convention(thin) (Builtin.Int32, Builtin.Int32) -> Builtin.Int32 {
|
|
bb0(%0 : $Builtin.Int32, %1 : $Builtin.Int32):
|
|
%f = function_ref @adder : $@convention(thin) (Builtin.Int32, Builtin.Int32) -> Builtin.Int32
|
|
%pa = partial_apply [callee_guaranteed] %f(%0) : $@convention(thin) (Builtin.Int32, Builtin.Int32) -> Builtin.Int32
|
|
%conv = convert_function %pa : $@callee_guaranteed (Builtin.Int32) -> (Builtin.Int32) to $@callee_guaranteed (Builtin.Int32) -> (Builtin.Int32, @error Error)
|
|
%ne = convert_escape_to_noescape %conv : $@callee_guaranteed (Builtin.Int32) -> (Builtin.Int32, @error Error) to $@noescape @callee_guaranteed (Builtin.Int32) -> (Builtin.Int32, @error Error)
|
|
try_apply %ne(%1) : $@noescape @callee_guaranteed (Builtin.Int32) -> (Builtin.Int32, @error Error), normal bb1, error bb2
|
|
|
|
bb1(%r : $Builtin.Int32):
|
|
br bb3(%r : $Builtin.Int32)
|
|
|
|
bb2(%e : $Error):
|
|
%r1 = integer_literal $Builtin.Int32, 0
|
|
br bb3(%r1 : $Builtin.Int32)
|
|
|
|
bb3(%res : $Builtin.Int32):
|
|
release_value %pa : $@callee_guaranteed (Builtin.Int32) -> (Builtin.Int32)
|
|
return %res : $Builtin.Int32
|
|
}
|
|
|
|
struct TestStr {
|
|
let a: Int32
|
|
let c: Int32
|
|
}
|
|
|
|
enum TestEnm {
|
|
case X
|
|
case Y(TestStr)
|
|
}
|
|
|
|
// CHECK-LABEL: sil @dont_crash
|
|
// CHECK: bb0(%0 : $TestEnm, %1 : $Int32):
|
|
// CHECK-NEXT: %2 = tuple ()
|
|
// CHECK-NEXT: return %2 : $()
|
|
sil @dont_crash : $@convention(method) (TestEnm, Int32) -> () {
|
|
bb0(%2 : $TestEnm, %3 : $Int32):
|
|
%98 = integer_literal $Builtin.Int1, -1
|
|
cond_br %98, bb2, bb3
|
|
|
|
bb2:
|
|
%18 = tuple()
|
|
return %18 : $()
|
|
|
|
bb3:
|
|
br bb8(%2 : $TestEnm)
|
|
|
|
bb8(%47 : $TestEnm):
|
|
%49 = unchecked_enum_data %47 : $TestEnm, #TestEnm.Y!enumelt
|
|
%57 = struct_extract %49 : $TestStr, #TestStr.c
|
|
cond_br undef, bb9, bb11
|
|
|
|
bb9:
|
|
br bb10
|
|
|
|
bb10:
|
|
%64 = struct $TestStr (%3 : $Int32, %57 : $Int32)
|
|
%65 = enum $TestEnm, #TestEnm.Y!enumelt, %64 : $TestStr
|
|
cond_br undef, bb2, bb16
|
|
|
|
bb11:
|
|
br bb10
|
|
|
|
|
|
bb16:
|
|
br bb8(%65 : $TestEnm)
|
|
}
|
|
|
|
sil @print : $@convention(thin) (@guaranteed String) -> ()
|
|
sil @yield_string : $@yield_once @convention(thin) () -> @yields @guaranteed String
|
|
|
|
sil @dont_clone_begin_apply : $(Builtin.Int1, @guaranteed String) -> () {
|
|
bb0(%condition : $Builtin.Int1, %arg : $String):
|
|
%print = function_ref @print : $@convention(thin) (@guaranteed String) -> ()
|
|
cond_br %condition, bb1, bb2
|
|
bb1:
|
|
apply %print(%arg) : $@convention(thin) (@guaranteed String) -> ()
|
|
br bb3
|
|
bb2:
|
|
br bb3
|
|
bb3:
|
|
%yield_string = function_ref @yield_string : $@yield_once @convention(thin) () -> @yields @guaranteed String
|
|
(%yield, %token) = begin_apply %yield_string() : $@yield_once @convention(thin) () -> @yields @guaranteed String
|
|
cond_br %condition, bb4, bb5
|
|
bb4:
|
|
apply %print(%yield) : $@convention(thin) (@guaranteed String) -> ()
|
|
br bb6
|
|
bb5:
|
|
br bb6
|
|
bb6:
|
|
end_apply %token as $()
|
|
%rv = tuple ()
|
|
return %rv : $()
|
|
}
|
|
|
|
class X {
|
|
@objc func f() { }
|
|
}
|
|
|
|
sil @external_g : $@convention(thin) () -> ()
|
|
|
|
// Don't tail duplicate dynamic_method_br. IRGen cannot handle phi nodes of
|
|
// objc_methods.
|
|
// CHECK-LABEL: sil @dont_tail_duplicate_dynamic_method_br
|
|
// CHECK: dynamic_method_br
|
|
// CHECK-NOT: dynamic_method_br
|
|
// CHECK: return
|
|
sil @dont_tail_duplicate_dynamic_method_br : $@convention(thin) (@owned Builtin.AnyObject, Builtin.Int1) -> () {
|
|
bb0(%x : $Builtin.AnyObject, %b : $Builtin.Int1):
|
|
cond_br %b, bb1, bb2
|
|
|
|
bb1:
|
|
%f = function_ref @external_f : $@convention(thin) () -> ()
|
|
apply %f() : $@convention(thin) () -> ()
|
|
strong_retain %x : $Builtin.AnyObject
|
|
br bb3(%x : $Builtin.AnyObject)
|
|
|
|
bb2:
|
|
%g = function_ref @external_g : $@convention(thin) () -> ()
|
|
apply %g() : $@convention(thin) () -> ()
|
|
strong_retain %x : $Builtin.AnyObject
|
|
br bb3(%x : $Builtin.AnyObject)
|
|
|
|
bb3(%y: $Builtin.AnyObject):
|
|
strong_release %y : $Builtin.AnyObject
|
|
dynamic_method_br %x : $Builtin.AnyObject, #X.f!foreign, bb4, bb5
|
|
|
|
bb4(%m : $@convention(objc_method) (Builtin.AnyObject) -> ()):
|
|
br bb6
|
|
|
|
bb5:
|
|
br bb6
|
|
|
|
bb6:
|
|
%r = tuple()
|
|
return %r : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @dont_crash_in_cast_jump_threading
|
|
// CHECK: checked_cast_br
|
|
// CHECK: } // end sil function 'dont_crash_in_cast_jump_threading'
|
|
sil @dont_crash_in_cast_jump_threading : $@convention(thin) (Optional<Base>) -> () {
|
|
bb0(%0 : $Optional<Base>):
|
|
br bb1
|
|
|
|
bb1:
|
|
switch_enum %0 : $Optional<Base>, case #Optional.some!enumelt: bb2, case #Optional.none!enumelt: bb21
|
|
|
|
|
|
bb2(%18 : $Base):
|
|
checked_cast_br Base in %18 : $Base to Derived, bb3, bb4
|
|
|
|
bb3(%21 : $Derived):
|
|
%22 = integer_literal $Builtin.Int1, -1
|
|
br bb5(%22 : $Builtin.Int1)
|
|
|
|
bb4:
|
|
%24 = integer_literal $Builtin.Int1, 0
|
|
br bb5(%24 : $Builtin.Int1)
|
|
|
|
|
|
bb5(%26 : $Builtin.Int1):
|
|
cond_br %26, bb6, bb19
|
|
|
|
bb6:
|
|
checked_cast_br Base in %18 : $Base to Derived, bb7, bb8
|
|
|
|
|
|
bb7(%29 : $Derived):
|
|
%30 = enum $Optional<Derived>, #Optional.some!enumelt, %29 : $Derived
|
|
br bb9(%30 : $Optional<Derived>)
|
|
|
|
bb8:
|
|
%32 = enum $Optional<Derived>, #Optional.none!enumelt
|
|
br bb9(%32 : $Optional<Derived>)
|
|
|
|
|
|
bb9(%34 : $Optional<Derived>):
|
|
switch_enum %34 : $Optional<Derived>, case #Optional.some!enumelt: bb10, case #Optional.none!enumelt: bb18
|
|
|
|
|
|
bb10(%36 : $Derived):
|
|
br bb11
|
|
|
|
bb11:
|
|
checked_cast_br Base in %18 : $Base to Derived, bb12, bb13
|
|
|
|
|
|
bb12(%43 : $Derived):
|
|
%44 = enum $Optional<Derived>, #Optional.some!enumelt, %43 : $Derived
|
|
br bb14(%44 : $Optional<Derived>)
|
|
|
|
bb13:
|
|
%46 = enum $Optional<Derived>, #Optional.none!enumelt
|
|
br bb14(%46 : $Optional<Derived>)
|
|
|
|
|
|
bb14(%48 : $Optional<Derived>):
|
|
switch_enum %48 : $Optional<Derived>, case #Optional.some!enumelt: bb15, case #Optional.none!enumelt: bb17
|
|
|
|
|
|
bb15(%50 : $Derived):
|
|
br bb16
|
|
|
|
bb16:
|
|
br bb20
|
|
|
|
bb17:
|
|
br bb16
|
|
|
|
bb18:
|
|
br bb11
|
|
|
|
bb19:
|
|
br bb20
|
|
|
|
bb20:
|
|
br bb1
|
|
|
|
bb21:
|
|
%75 = tuple ()
|
|
return %75 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @simplify_switch_enum_unreachable :
|
|
// CHECK-NOT: switch_enum
|
|
// CHECK: } // end sil function 'simplify_switch_enum_unreachable'
|
|
sil @simplify_switch_enum_unreachable : $@convention(thin) (@owned Optional<String>) -> () {
|
|
bb0(%0 : $Optional<String>):
|
|
switch_enum %0 : $Optional<String>, case #Optional.some!enumelt: bb5, case #Optional.none!enumelt: bb4
|
|
|
|
bb4:
|
|
unreachable
|
|
|
|
bb5:
|
|
%1 = unchecked_enum_data %0 : $Optional<String>, #Optional.some!enumelt
|
|
release_value %1 : $String
|
|
br bb6
|
|
|
|
bb6:
|
|
%17 = tuple ()
|
|
return %17 : $()
|
|
}
|
|
|