mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
This is a follow-up of https://github.com/swiftlang/swift/pull/84905, which handles non-copyable enums with a deinit correctly. Also, for copyable enums it's more efficient to use `end_lifetime` than `destroy_value`, because we already know that the enum contains a trivial case. Therefore no destroy operation is needed.
2070 lines
54 KiB
Plaintext
2070 lines
54 KiB
Plaintext
// RUN: %target-sil-opt -sil-print-types -enable-objc-interop -enable-experimental-feature MoveOnlyEnumDeinits -enable-sil-verify-all %s -simplify-cfg | %FileCheck %s
|
|
|
|
// REQUIRES: swift_feature_MoveOnlyEnumDeinits
|
|
|
|
// OSSA form of tests from simplify_cfg.sil and simplify_cfg_simple.sil.
|
|
//
|
|
// Test miscellaneous SimplifyCFG optimization from simplifyBlocks.
|
|
//
|
|
// Does not include:
|
|
// - tryJumpThreading.
|
|
// - dominatorBasedSimplify
|
|
// - CheckedCastBranchJumpThreading
|
|
|
|
// 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 Klass {
|
|
var a: Int
|
|
deinit
|
|
init()
|
|
func foo() -> Int?
|
|
}
|
|
|
|
class B {}
|
|
class E : B {}
|
|
|
|
sil [ossa] @consume_klass : $@convention(thin) (@owned Klass) -> ()
|
|
sil [ossa] @use_klass : $@convention(thin) (@guaranteed Klass) -> ()
|
|
sil [ossa] @get_klass : $@convention(thin) () -> @owned Klass
|
|
|
|
struct KlassWrapper {
|
|
var k: Klass
|
|
}
|
|
|
|
struct OptionalKlassWrapper {
|
|
@_hasStorage var k: Optional<Klass>
|
|
}
|
|
|
|
internal enum CompareResult {
|
|
case equal
|
|
case less
|
|
}
|
|
|
|
struct FakeBool {
|
|
@_hasStorage var value: Builtin.Int1 { get set }
|
|
init(value: Builtin.Int1)
|
|
}
|
|
|
|
sil [ossa] @dummy : $@convention(thin) () -> FakeBool
|
|
|
|
struct NonTrivial {
|
|
var object: AnyObject
|
|
}
|
|
|
|
struct S {
|
|
var a: Int
|
|
var b: NonTrivial
|
|
}
|
|
|
|
enum NCE: ~Copyable {
|
|
case a
|
|
case b(AnyObject)
|
|
deinit
|
|
}
|
|
|
|
///////////
|
|
// Tests //
|
|
///////////
|
|
|
|
// CHECK-LABEL: sil @simple_test : $@convention(thin) () -> () {
|
|
// CHECK: bb0:
|
|
// CHECK-NEXT: tuple
|
|
// CHECK-NEXT: return
|
|
// CHECK: } // end sil function 'simple_test'
|
|
sil @simple_test : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = integer_literal $Builtin.Int1, -1
|
|
cond_br %0, bb1, bb2
|
|
|
|
bb1:
|
|
br bb3
|
|
|
|
bb2:
|
|
br bb3
|
|
|
|
bb3:
|
|
%9999 = tuple ()
|
|
return %9999 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @test_dead_block :
|
|
// CHECK-NEXT: bb0:
|
|
// CHECK-NEXT: unreachable
|
|
// CHECK-NEXT: }
|
|
sil [ossa] @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 [ossa] @release_in_arcinert_termination_block : $(@owned Klass) -> () {
|
|
bb0(%0 : @owned $Klass):
|
|
br bb1
|
|
|
|
bb1:
|
|
destroy_value %0 : $Klass
|
|
unreachable
|
|
}
|
|
|
|
// CHECK-LABEL: @release_in_nonarcinert_termination_block :
|
|
// CHECK: bb0
|
|
// CHECK: destroy_value
|
|
// CHECK: apply
|
|
// CHECK: unreachable
|
|
// CHECK: } // end sil function 'release_in_nonarcinert_termination_block'
|
|
sil [ossa] @release_in_nonarcinert_termination_block : $(@owned Klass) -> () {
|
|
bb0(%0 : @owned $Klass):
|
|
%1 = copy_value %0 : $Klass
|
|
br bb1
|
|
|
|
bb1:
|
|
destroy_value %0 : $Klass
|
|
%2 = function_ref @consume_klass : $@convention(thin) (@owned Klass) -> ()
|
|
apply %2(%1) : $@convention(thin) (@owned Klass) -> ()
|
|
unreachable
|
|
}
|
|
|
|
// CHECK-LABEL: @test_single_pred_block :
|
|
// CHECK: bb3([[ARG:%[0-9]+]] : $Builtin.Int64):
|
|
// CHECK: struct $Int64
|
|
// CHECK-NEXT: return
|
|
// CHECK: } // end sil function 'test_single_pred_block'
|
|
sil [ossa] @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: @test_single_pred_block_nontrivial :
|
|
// CHECK: bb3([[ARG:%[0-9]+]] : @owned $Klass):
|
|
// CHECK: [[WRAPPED:%.*]] = struct $KlassWrapper ([[ARG]] :
|
|
// CHECK-NEXT: return [[WRAPPED]]
|
|
// CHECK: } // end sil function 'test_single_pred_block_nontrivial'
|
|
sil [ossa] @test_single_pred_block_nontrivial : $@convention(thin) (Builtin.Int1) -> @owned KlassWrapper {
|
|
bb0(%0 : $Builtin.Int1):
|
|
%f = function_ref @get_klass : $@convention(thin) () -> @owned Klass
|
|
cond_br %0, bb1, bb3
|
|
|
|
bb1:
|
|
%4a = apply %f() : $@convention(thin) () -> @owned Klass
|
|
br bb2(%4a : $Klass)
|
|
|
|
bb3:
|
|
%4b = apply %f() : $@convention(thin) () -> @owned Klass
|
|
br bb2(%4b : $Klass)
|
|
|
|
bb2(%5 : @owned $Klass):
|
|
%6 = struct $KlassWrapper (%5 : $Klass)
|
|
br bb4(%6 : $KlassWrapper)
|
|
|
|
bb4(%9 : @owned $KlassWrapper):
|
|
return %9 : $KlassWrapper
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @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
|
|
// CHECK: } // end sil function 'canonicalize_not_branch'
|
|
sil [ossa] @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 [ossa] @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
|
|
// CHECK: } // end sil function 'canonicalize_not_branch_expect'
|
|
sil [ossa] @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_ }
|
|
// BoolLike with a payload of state on the true_ side.
|
|
|
|
// func testThread(a : BoolLike) -> Int32 {
|
|
// if a { return 42 } else { return 17 } }
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @testThread :
|
|
// CHECK: switch_enum_addr %0 : $*BoolLike, case #BoolLike.true_!enumelt: bb1, case #BoolLike.false_!enumelt: bb2
|
|
// CHECK: } // end sil function 'testThread'
|
|
sil [ossa] @testThread : $@convention(thin) (@in BoolLike) -> Int64 {
|
|
bb0(%0 : $*BoolLike):
|
|
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
|
|
}
|
|
|
|
/// CHECK-LABEL: sil [ossa] @testCondBrFold
|
|
/// CHECK: bb0(
|
|
/// CHECK-NEXT: return %1 : $Int64
|
|
sil [ossa] @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 [ossa] @testSwitchEnumFold
|
|
/// CHECK: bb0(
|
|
/// CHECK-NEXT: return %0 : $Int64
|
|
sil [ossa] @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 [ossa] @elim_trampoline : $@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 bb3(%2 : $Int64)
|
|
|
|
bb3(%5 : $Int64):
|
|
return %5 : $Int64
|
|
}
|
|
|
|
// CHECK-LABEL: @elim_trampoline2
|
|
// CHECK-NOT: cond_br
|
|
// CHECK: return
|
|
sil [ossa] @elim_trampoline2 : $@convention(thin) (Builtin.Int1, Int64) -> Int64 {
|
|
bb0(%0 : $Builtin.Int1, %1 : $Int64):
|
|
cond_br %0, bb1, bb2
|
|
|
|
bb1:
|
|
br bb3(%1 : $Int64)
|
|
|
|
bb2:
|
|
br bb3(%1 : $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 [ossa] @elim_trampoline_debug : $@convention(thin) (Builtin.Int1, Int64) -> Int64 {
|
|
bb0(%0 : $Builtin.Int1, %1 : $Int64):
|
|
cond_br %0, bb1, bb2
|
|
|
|
bb1:
|
|
debug_value %0 : $Builtin.Int1
|
|
br bb3(%1 : $Int64)
|
|
|
|
bb2:
|
|
br bb3(%1 : $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 [ossa] @elim_trampoline3 : $@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 bb3(%2 : $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 [ossa] @external_f : $@convention(thin) () -> ()
|
|
|
|
sil [ossa] @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
|
|
}
|
|
|
|
// Make sure that a conditional branch to a trampoline without parameters
|
|
// gets eliminated by branching to a target of this trampoline.
|
|
// CHECK-LABEL: @elim_trampoline5
|
|
// CHECK: cond_br %{{.*}}, bb1, bb3
|
|
// CHECK: bb3:
|
|
// CHECK-NEXT: br bb4
|
|
// CHECK: bb4:
|
|
// CHECK: br bb8({{.*}})
|
|
// CHECK: bb5:
|
|
// CHECK-NEXT: builtin
|
|
// CHECK-NEXT: cond_br
|
|
// CHECK: bb8{{.*}}:
|
|
// Last cond_br should branch directly to the target of a trampoline
|
|
// instead of the original bb3
|
|
// CHECK: cond_br {{.*}}, bb5, bb9
|
|
// CHECK: }
|
|
sil [ossa] @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, bb2a
|
|
|
|
bb1:
|
|
br bbReturn
|
|
|
|
bbReturn:
|
|
%5 = tuple ()
|
|
return %5 : $()
|
|
|
|
bb2a:
|
|
br bb2(%1 : $Builtin.Int32)
|
|
|
|
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, bb4a, bb2b
|
|
|
|
bb2b:
|
|
br bb2(%13 : $Builtin.Int32)
|
|
|
|
bb4a:
|
|
br bbReturn
|
|
|
|
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, bb6
|
|
|
|
bb6:
|
|
br bb5(%18 : $Builtin.Int32)
|
|
}
|
|
|
|
// CHECK-LABEL: @elim_trampoline_loop
|
|
// Make sure we are not crashing on this one.
|
|
// CHECK: return
|
|
sil [ossa] @elim_trampoline_loop : $@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 bb2a(%2 : $Int64)
|
|
|
|
bb2a(%4a : $Int64):
|
|
br bb2a(%4a : $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 [ossa] @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 [ossa] @elim_diamonds : $@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 bb3(%1 : $Int64)
|
|
|
|
bb3(%5 : $Int64):
|
|
cond_br %0, bb4, bb5
|
|
|
|
bb4:
|
|
br bb6(%5 : $Int64)
|
|
|
|
bb5:
|
|
br bb6(%5 : $Int64)
|
|
|
|
bb6(%8 : $Int64):
|
|
return %8 : $Int64
|
|
}
|
|
|
|
// CHECK-LABEL: @infinite_loop
|
|
// CHECK: bb0
|
|
// CHECK-NEXT: br bb1
|
|
// CHECK: bb1
|
|
// CHECK-NEXT: br bb1
|
|
sil [ossa] @infinite_loop : $@convention(thin) () -> () {
|
|
bb0:
|
|
br bb1
|
|
bb1:
|
|
br bb1
|
|
}
|
|
|
|
// CHECK-LABEL: @dead_loop
|
|
// CHECK-NOT: br bb
|
|
sil [ossa] @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 : $()
|
|
}
|
|
|
|
class C {
|
|
final var value: Int32
|
|
init(v: Int32)
|
|
}
|
|
|
|
|
|
enum A {
|
|
case B, C, D
|
|
}
|
|
|
|
// CHECK-LABEL: cannot_optimize_switch_enum
|
|
sil [ossa] @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(%defaultArg : $A):
|
|
// CHECK-NEXT: switch_enum %0
|
|
switch_enum %0 : $A, case #A.C!enumelt: bb3, default bb4
|
|
|
|
bb3:
|
|
apply %f1() : $@convention(thin) () -> ()
|
|
br bb5
|
|
|
|
bb4(%defaultArg2 : $A):
|
|
apply %f1() : $@convention(thin) () -> ()
|
|
br bb5
|
|
|
|
bb5:
|
|
%6 = tuple ()
|
|
return %6 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @simplify_switch_enum1
|
|
// CHECK: bb0:
|
|
// CHECK-NOT: bb[0-9]
|
|
// CHECK: apply
|
|
// CHECK-NEXT: return
|
|
sil [ossa] @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(%payload : $Int32):
|
|
br bb6(%21 : $Int32)
|
|
|
|
bb6(%r : $Int32):
|
|
return %r : $Int32
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @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 [ossa] @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(%0payload : $Int32):
|
|
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 [ossa] @identical_switch_enum_addr_dests : $@convention(thin) (@in Optional<Int32>) -> () {
|
|
// CHECK: bb0(%0 : $*Optional<Int32>):
|
|
// CHECK-NEXT: tuple
|
|
// CHECK-NEXT: return
|
|
sil [ossa] @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 [ossa] @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 [ossa] @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 [ossa] @fold_switch_value : $@convention(thin) () -> Int32 {
|
|
// CHECK: bb0:
|
|
// CHECK-NOT: bb1
|
|
// CHECK: integer_literal $Builtin.Int32, 100
|
|
// CHECK-NEXT: struct
|
|
// CHECK-NEXT: return
|
|
sil [ossa] @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
|
|
}
|
|
|
|
enum OneCase {
|
|
case First
|
|
}
|
|
|
|
enum TwoCase {
|
|
case First
|
|
case Second
|
|
}
|
|
|
|
enum ThreeCase {
|
|
case First
|
|
case Second
|
|
case Third
|
|
}
|
|
|
|
sil [ossa] @unknown : $@convention(thin) () -> ()
|
|
sil [ossa] @int1_user : $@convention(thin) (Builtin.Int1) -> ()
|
|
|
|
// CHECK-LABEL: sil [ossa] @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 [ossa] @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 : $()
|
|
}
|
|
|
|
enum IntEnum : Int32 {
|
|
case E0
|
|
case E1
|
|
case E2
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @checked_cast_anyobject_metatypeinst_to_class
|
|
// CHECK: bb0
|
|
// CHECK-NOT: checked_cast
|
|
// CHECK-NOT: bb1
|
|
// CHECK: [[RET:%.*]] = tuple ()
|
|
// CHECK: return [[RET]] : $()
|
|
|
|
sil [ossa] @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(%default : $@thick AnyObject.Protocol):
|
|
br bb3
|
|
|
|
bb3:
|
|
%2 = tuple()
|
|
return %2 : $()
|
|
}
|
|
|
|
|
|
// CHECK-LABEL: sil [ossa] @remove_cond_fail_trueblock
|
|
// CHECK: bb0([[COND:%.*]] :
|
|
// CHECK-NOT: bb
|
|
// CHECK: cond_fail [[COND]]
|
|
// CHECK-NOT: bb
|
|
// CHECK: return
|
|
sil [ossa] @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 [ossa] @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 [ossa] @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 [ossa] @dont_remove_cond_fail_wrong_const
|
|
// CHECK: bb0(%0 : $Builtin.Int1):
|
|
// CHECK-NEXT: cond_br %0, bb1, bb2
|
|
sil [ossa] @dont_remove_cond_fail_wrong_const : $@convention(thin) (Builtin.Int1) -> () {
|
|
bb0(%0 : $Builtin.Int1):
|
|
cond_br %0, bb1, bb1a
|
|
|
|
bb1:
|
|
%i1 = integer_literal $Builtin.Int1, 0
|
|
cond_fail %i1 : $Builtin.Int1
|
|
br bb2
|
|
|
|
bb1a:
|
|
br bb2
|
|
|
|
bb2:
|
|
%r = tuple ()
|
|
return %r : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @remove_cond_fail_same_cond_in_true
|
|
// CHECK: bb0([[COND:%[0-9]*]]
|
|
// CHECK-NOT: bb
|
|
// CHECK: cond_fail [[COND]]
|
|
// CHECK: bb1:
|
|
// CHECK: return
|
|
sil [ossa] @remove_cond_fail_same_cond_in_true : $@convention(thin)(Builtin.Int1, Builtin.Int1) -> () {
|
|
bb0(%0 : $Builtin.Int1, %1 : $Builtin.Int1):
|
|
cond_br %0, bb1b, bb2
|
|
|
|
bb1b:
|
|
br 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, bb1a, bb3
|
|
|
|
bb1a:
|
|
br bb1
|
|
|
|
bb3:
|
|
%2 = tuple()
|
|
return %2 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @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 [ossa] @remove_cond_fail_same_cond_in_false : $@convention(thin)(Builtin.Int1, Builtin.Int1) -> () {
|
|
bb0(%0 : $Builtin.Int1, %1 : $Builtin.Int1):
|
|
cond_br %0, bb2, bb1b
|
|
|
|
bb1b:
|
|
br 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, bb1a, bb3
|
|
|
|
bb1a:
|
|
br bb1
|
|
|
|
bb3:
|
|
%2 = tuple()
|
|
return %2 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @remove_cond_fail_same_cond_in_false2
|
|
// CHECK: bb0([[COND:%[0-9]*]]
|
|
// CHECK-NOT: bb
|
|
// CHECK: cond_fail [[COND]]
|
|
// CHECK: bb1:
|
|
// CHECK: return
|
|
sil [ossa] @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, bb1a
|
|
|
|
bb1a:
|
|
br 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, bb1b, bb3
|
|
|
|
bb1b:
|
|
br bb1
|
|
|
|
bb3:
|
|
%2 = tuple()
|
|
return %2 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @dont_remove_cond_fail_same_cond_in_false
|
|
// CHECK: bb0([[COND:%[0-9]*]]
|
|
// CHECK-NEXT: cond_br
|
|
// CHECK: bb1:
|
|
// CHECK: br bb2
|
|
// CHECK: bb2:
|
|
// CHECK-NEXT: cond_fail [[COND]]
|
|
// CHECK: return
|
|
sil [ossa] @dont_remove_cond_fail_same_cond_in_false : $@convention(thin)(Builtin.Int1, Builtin.Int1) -> () {
|
|
bb0(%0 : $Builtin.Int1, %1 : $Builtin.Int1):
|
|
cond_br %0, bb3, bb1
|
|
|
|
bb1:
|
|
br bb2
|
|
|
|
bb2:
|
|
cond_fail %0 : $Builtin.Int1
|
|
unreachable
|
|
|
|
bb3:
|
|
// Make bb1 not dominated from bb0.
|
|
%55 = function_ref @external_f : $@convention(thin) () -> ()
|
|
apply %55() : $@convention(thin) () -> ()
|
|
cond_br %1, bb4, bb5
|
|
|
|
bb4:
|
|
br bb2
|
|
|
|
bb5:
|
|
%2 = tuple()
|
|
return %2 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @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 [ossa] @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 [ossa] @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 [ossa] @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 [ossa] @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 [ossa] @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 [ossa] @dont_move_cond_fail_no_postdom
|
|
// CHECK: bb1:
|
|
// CHECK-NEXT: apply
|
|
// CHECK-NEXT: integer_literal
|
|
// CHECK-NEXT: br bb4
|
|
// CHECK: bb2:
|
|
// CHECK-NEXT: apply
|
|
// CHECK-NEXT: cond_br
|
|
// CHECK: bb3:
|
|
// CHECK: br bb4
|
|
// CHECK: bb4({{.*}}):
|
|
// CHECK-NEXT: apply
|
|
// CHECK-NEXT: cond_fail
|
|
sil [ossa] @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 bb3b(%i1 : $Builtin.Int1)
|
|
|
|
bb3b(%1b : $Builtin.Int1):
|
|
br bb3(%1b : $Builtin.Int1)
|
|
|
|
bb2:
|
|
apply %f1() : $@convention(thin) () -> () // prevent other CFG optimizations
|
|
cond_br %2, bb3a, bb4a
|
|
|
|
bb3a:
|
|
br bb3(%1 : $Builtin.Int1)
|
|
|
|
bb3(%a3 : $Builtin.Int1):
|
|
apply %f1() : $@convention(thin) () -> () // prevent other CFG optimizations
|
|
cond_fail %a3 : $Builtin.Int1
|
|
br bb4
|
|
|
|
bb4a:
|
|
br bb4
|
|
|
|
bb4:
|
|
%r = tuple ()
|
|
return %r : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @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 [ossa] @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 [ossa] @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 [ossa] @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 [ossa] @successful_checked_cast_br_on_alloc_ref
|
|
// CHECK: bb0
|
|
// CHECK-NEXT: alloc_ref
|
|
// CHECK-NOT: checked_cast_br
|
|
// CHECK-NOT: bb1
|
|
// CHECK: destroy_value
|
|
// CHECK: integer_literal $Builtin.Int32, 1
|
|
// CHECK: return
|
|
sil [ossa] @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 : @owned $B):
|
|
destroy_value %2 : $B
|
|
%3 = integer_literal $Builtin.Int32, 1
|
|
br bb3 (%3 : $Builtin.Int32)
|
|
|
|
bb2(%2a : @owned $B):
|
|
destroy_value %2a : $B
|
|
%5 = integer_literal $Builtin.Int32, 2
|
|
br bb3 (%5 : $Builtin.Int32)
|
|
|
|
bb3(%10 : $Builtin.Int32):
|
|
return %10 : $Builtin.Int32
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @failing_checked_cast_br_on_alloc_ref
|
|
// CHECK: bb0
|
|
// CHECK-NEXT: alloc_ref
|
|
// CHECK-NOT: checked_cast_br
|
|
// CHECK-NOT: bb1
|
|
// CHECK: destroy_value
|
|
// CHECK: integer_literal $Builtin.Int32, 2
|
|
// CHECK: return
|
|
sil [ossa] @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 : @owned $B):
|
|
destroy_value %3 : $B
|
|
%4 = integer_literal $Builtin.Int32, 1
|
|
br bb3 (%4 : $Builtin.Int32)
|
|
|
|
bb2(%3a : @owned $B):
|
|
destroy_value %3a : $B
|
|
%5 = integer_literal $Builtin.Int32, 2
|
|
br bb3 (%5 : $Builtin.Int32)
|
|
|
|
bb3(%10 : $Builtin.Int32):
|
|
return %10 : $Builtin.Int32
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @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 [ossa] @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 [ossa] @dont_crash_on_enum_payload_is_enum
|
|
// CHECK: bb0(%0 : $TwoCase):
|
|
// CHECK: switch_enum %0
|
|
// CHECK: bb1:
|
|
// CHECK: return
|
|
sil [ossa] @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 : $()
|
|
}
|
|
|
|
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 {
|
|
}
|
|
|
|
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 [ossa] @foo : $@convention(thin) (Builtin.Int64) -> Builtin.Int8
|
|
sil [ossa] @foo2 : $@convention(thin) (Builtin.Int32) -> Builtin.Int8
|
|
|
|
sil [ossa] @dont_thread_throw_block : $@convention(thin) (@guaranteed TestEnum) -> (Builtin.Int8, @error Error) {
|
|
bb0(%0 : @guaranteed $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(%default : @guaranteed $TestEnum):
|
|
%22 = alloc_existential_box $Error, $MyError2
|
|
%22a = begin_borrow %22 : $Error
|
|
%23 = project_existential_box $MyError2 in %22a : $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 : @guaranteed $Base):
|
|
%t4 = enum $TestEnum, #TestEnum.string!enumelt, %53 : $Base
|
|
%e4 = enum $MyError, #MyError.c!enumelt, %t4 : $TestEnum
|
|
%e4a = copy_value %e4 : $MyError
|
|
br bb5(%e4a : $MyError)
|
|
|
|
bb5(%e : @owned $MyError):
|
|
%89 = enum $Optional<MyError>, #Optional.some!enumelt, %e : $MyError
|
|
%e5 = enum $MyError2, #MyError2.o!enumelt, %89 : $Optional<MyError>
|
|
store %e5 to [init] %23 : $*MyError2
|
|
end_borrow %22a : $Error
|
|
throw %22 : $Error
|
|
|
|
bb6(%44 : $Builtin.Int8):
|
|
return %44 : $Builtin.Int8
|
|
}
|
|
|
|
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 [ossa] @test_dead_checked_cast_br : $@convention(thin) (@in IsQ) -> () {
|
|
// CHECK: bb0(%0 : $*IsQ):
|
|
// CHECK: [[Q:%.*]] = alloc_stack $any Q
|
|
// CHECK: [[LD:%.*]] = load [take] %0 : $*IsQ
|
|
// CHECK: destroy_value [[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 [ossa] @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
|
|
destroy_addr %p : $*Q
|
|
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 [ossa] @dont_hang
|
|
// CHECK: bb6:
|
|
// CHECK: integer_literal $Builtin.Int64, 1
|
|
// CHECK-NEXT: br bb6
|
|
// CHECK-NEXT: }
|
|
sil [ossa] @dont_hang : $@convention(thin) () -> () {
|
|
bb0:
|
|
cond_br undef, bb1, bb4
|
|
|
|
bb1:
|
|
%0 = integer_literal $Builtin.Int64, 1
|
|
cond_br undef, bb2a, bb6a
|
|
|
|
bb2a:
|
|
br bb2
|
|
|
|
bb2:
|
|
br bb3
|
|
|
|
bb3:
|
|
br bb5
|
|
|
|
bb4:
|
|
%1 = integer_literal $Builtin.Int64, 1
|
|
br bb3
|
|
|
|
bb5:
|
|
br bb2
|
|
|
|
bb6a:
|
|
br bb6
|
|
|
|
bb6:
|
|
%2 = integer_literal $Builtin.Int64, 1
|
|
br bb7
|
|
|
|
bb7:
|
|
br bb5
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @test_constant_folding
|
|
// CHECK: [[R:%[0-9]+]] = integer_literal $Builtin.Int32, 30
|
|
// CHECK: return [[R]] : $Builtin.Int32
|
|
// CHECK-NEXT: }
|
|
sil [ossa] @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
|
|
}
|
|
|
|
struct TestStr {
|
|
let a: Int32
|
|
let c: Int32
|
|
}
|
|
|
|
enum TestEnm {
|
|
case X
|
|
case Y(TestStr)
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @dont_crash
|
|
// CHECK: bb0(%0 : $TestEnm, %1 : $Int32):
|
|
// CHECK-NEXT: %2 = tuple ()
|
|
// CHECK-NEXT: return %2 : $()
|
|
sil [ossa] @dont_crash : $@convention(method) (TestEnm, Int32) -> () {
|
|
bb0(%2 : $TestEnm, %3 : $Int32):
|
|
%98 = integer_literal $Builtin.Int1, -1
|
|
cond_br %98, bb2a, bb3
|
|
|
|
bb2a:
|
|
br bb2
|
|
|
|
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, bb2b, bb16
|
|
|
|
bb2b:
|
|
br bb2
|
|
|
|
bb11:
|
|
br bb10
|
|
|
|
|
|
bb16:
|
|
br bb8(%65 : $TestEnm)
|
|
}
|
|
|
|
sil [ossa] @print : $@convention(thin) (@guaranteed NonTrivial) -> ()
|
|
sil [ossa] @yield_string : $@yield_once @convention(thin) () -> @yields @guaranteed NonTrivial
|
|
|
|
sil [ossa] @dont_clone_begin_apply : $(Builtin.Int1, @guaranteed NonTrivial) -> () {
|
|
bb0(%condition : $Builtin.Int1, %arg : @guaranteed $NonTrivial):
|
|
%print = function_ref @print : $@convention(thin) (@guaranteed NonTrivial) -> ()
|
|
cond_br %condition, bb1, bb2
|
|
bb1:
|
|
apply %print(%arg) : $@convention(thin) (@guaranteed NonTrivial) -> ()
|
|
br bb3
|
|
bb2:
|
|
br bb3
|
|
bb3:
|
|
%yield_string = function_ref @yield_string : $@yield_once @convention(thin) () -> @yields @guaranteed NonTrivial
|
|
(%yield, %token) = begin_apply %yield_string() : $@yield_once @convention(thin) () -> @yields @guaranteed NonTrivial
|
|
cond_br %condition, bb4, bb5
|
|
bb4:
|
|
apply %print(%yield) : $@convention(thin) (@guaranteed NonTrivial) -> ()
|
|
br bb6
|
|
bb5:
|
|
br bb6
|
|
bb6:
|
|
end_apply %token as $()
|
|
%rv = tuple ()
|
|
return %rv : $()
|
|
}
|
|
|
|
class X {
|
|
@objc func f() { }
|
|
}
|
|
|
|
sil [ossa] @external_g : $@convention(thin) () -> ()
|
|
|
|
// Don't tail duplicate dynamic_method_br. IRGen cannot handle phi nodes of
|
|
// objc_methods.
|
|
// CHECK-LABEL: sil [ossa] @dont_tail_duplicate_dynamic_method_br
|
|
// CHECK: dynamic_method_br
|
|
// CHECK-NOT: dynamic_method_br
|
|
// CHECK: return
|
|
sil [ossa] @dont_tail_duplicate_dynamic_method_br : $@convention(thin) (@owned Builtin.AnyObject, Builtin.Int1) -> () {
|
|
bb0(%x : @owned $Builtin.AnyObject, %b : $Builtin.Int1):
|
|
cond_br %b, bb1, bb2
|
|
|
|
bb1:
|
|
%f = function_ref @external_f : $@convention(thin) () -> ()
|
|
apply %f() : $@convention(thin) () -> ()
|
|
%x2 = copy_value %x : $Builtin.AnyObject
|
|
br bb3(%x2 : $Builtin.AnyObject)
|
|
|
|
bb2:
|
|
%g = function_ref @external_g : $@convention(thin) () -> ()
|
|
apply %g() : $@convention(thin) () -> ()
|
|
%x3 = copy_value %x : $Builtin.AnyObject
|
|
br bb3(%x3 : $Builtin.AnyObject)
|
|
|
|
bb3(%y : @owned $Builtin.AnyObject):
|
|
destroy_value %x : $Builtin.AnyObject
|
|
dynamic_method_br %y : $Builtin.AnyObject, #X.f!foreign, bb4, bb5
|
|
|
|
bb4(%m : $@convention(objc_method) (Builtin.AnyObject) -> ()):
|
|
br bb6
|
|
|
|
bb5:
|
|
br bb6
|
|
|
|
bb6:
|
|
destroy_value %y : $Builtin.AnyObject
|
|
%r = tuple()
|
|
return %r : $()
|
|
}
|
|
|
|
// OSSA-only test.
|
|
//
|
|
// Test that simplifySwitchEnumBlock does not assert on a default
|
|
// switch case with an argument.
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @testSwitchEnumDefaultArg : $@convention(thin) (CompareResult) -> FakeBool {
|
|
// CHECK: bb0(%0 : $CompareResult):
|
|
// CHECK-NOT: switch
|
|
// CHECK-NOT: br
|
|
// CHECK: [[CALL:%.*]] = apply
|
|
// CHECK: return [[CALL]] : $FakeBool
|
|
// CHECK-LABEL: } // end sil function 'testSwitchEnumDefaultArg'
|
|
sil [ossa] @testSwitchEnumDefaultArg : $@convention(thin) (CompareResult) -> FakeBool {
|
|
bb0(%0 : $CompareResult):
|
|
%1 = enum $CompareResult, #CompareResult.equal!enumelt
|
|
switch_enum %1 : $CompareResult, case #CompareResult.less!enumelt: bb1, default bb2
|
|
|
|
bb1:
|
|
%3 = integer_literal $Builtin.Int1, -1
|
|
%4 = struct $FakeBool(%3 : $Builtin.Int1)
|
|
br bb3(%3 : $Builtin.Int1)
|
|
|
|
bb2:
|
|
%7 = integer_literal $Builtin.Int1, 0
|
|
%8 = struct $FakeBool(%7 : $Builtin.Int1)
|
|
br bb3(%7 : $Builtin.Int1)
|
|
|
|
bb3(%10 : $Builtin.Int1):
|
|
cond_br %10, bb4, bb5
|
|
|
|
bb4:
|
|
%11 = struct $FakeBool(%10 : $Builtin.Int1)
|
|
br bb6(%11 : $FakeBool)
|
|
|
|
bb5:
|
|
%f = function_ref @dummy : $@convention(thin) () -> FakeBool
|
|
%call = apply %f() : $@convention(thin) () -> FakeBool
|
|
br bb6(%call : $FakeBool)
|
|
|
|
bb6(%14 : $FakeBool):
|
|
return %14 : $FakeBool
|
|
}
|
|
|
|
struct Err: Error {
|
|
var i: Int
|
|
}
|
|
|
|
sil @callee1 : $@convention(thin) (Int) -> @out Int
|
|
sil @callee2 : $@convention(thin) (Int) -> (@out Int, @error_indirect Err)
|
|
|
|
// CHECK-LABEL: sil [ossa] @try_apply_to_apply_with_indirect_error1 :
|
|
// CHECK: [[OUT:%.*]] = alloc_stack $Int
|
|
// CHECK: apply {{%[0-9]+}}([[OUT]], %0)
|
|
// CHECK: } // end sil function 'try_apply_to_apply_with_indirect_error1'
|
|
sil [ossa] @try_apply_to_apply_with_indirect_error1 : $@convention(method) (Int) -> () {
|
|
bb0(%0 : $Int):
|
|
%1 = function_ref @callee1 : $@convention(thin) (Int) -> @out Int
|
|
%2 = thin_to_thick_function %1 : $@convention(thin) (Int) -> @out Int to $@noescape @callee_guaranteed (Int) -> @out Int
|
|
%3 = convert_function %2 : $@noescape @callee_guaranteed (Int) -> @out Int to $@noescape @callee_guaranteed (Int) -> (@out Int, @error_indirect Err), forwarding: @owned
|
|
%4 = alloc_stack $Int
|
|
%5 = alloc_stack $Err
|
|
try_apply %3(%4, %5, %0) : $@noescape @callee_guaranteed (Int) -> (@out Int, @error_indirect Err), normal bb10, error bb11
|
|
|
|
bb10(%7 : $()):
|
|
dealloc_stack %5 : $*Err
|
|
dealloc_stack %4 : $*Int
|
|
destroy_value %3 : $@noescape @callee_guaranteed (Int) -> (@out Int, @error_indirect Err)
|
|
%r = tuple ()
|
|
return %r : $()
|
|
|
|
bb11:
|
|
dealloc_stack %5 : $*Err
|
|
dealloc_stack %4 : $*Int
|
|
destroy_value %3 : $@noescape @callee_guaranteed (Int) -> (@out Int, @error_indirect Err)
|
|
unreachable
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @try_apply_to_apply_with_indirect_error2 :
|
|
// CHECK: [[OUT:%.*]] = alloc_stack $Int
|
|
// CHECK: [[ERR:%.*]] = alloc_stack $Err
|
|
// CHECK: apply [nothrow] {{%[0-9]+}}([[OUT]], [[ERR]], %0)
|
|
// CHECK: } // end sil function 'try_apply_to_apply_with_indirect_error2'
|
|
sil [ossa] @try_apply_to_apply_with_indirect_error2 : $@convention(method) (Int) -> () {
|
|
bb0(%0 : $Int):
|
|
%1 = function_ref @callee2 : $@convention(thin) (Int) -> (@out Int, @error_indirect Err)
|
|
%4 = alloc_stack $Int
|
|
%5 = alloc_stack $Err
|
|
try_apply %1(%4, %5, %0) : $@convention(thin) (Int) -> (@out Int, @error_indirect Err), normal bb10, error bb11
|
|
|
|
bb10(%7 : $()):
|
|
dealloc_stack %5 : $*Err
|
|
dealloc_stack %4 : $*Int
|
|
%r = tuple ()
|
|
return %r : $()
|
|
|
|
bb11:
|
|
unreachable
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @jump_threading_creates_phi :
|
|
// CHECK: = borrowed %{{[0-9]+}} : $Klass from (%1 : $OptionalKlassWrapper)
|
|
// CHECK: } // end sil function 'jump_threading_creates_phi'
|
|
sil [ossa] @jump_threading_creates_phi: $@convention(thin) (Optional<A>, @guaranteed OptionalKlassWrapper) -> () {
|
|
bb0(%0 : $Optional<A>, %1 : @guaranteed $OptionalKlassWrapper):
|
|
%2 = alloc_stack [lexical] $Optional<A>
|
|
store %0 to [trivial] %2 : $*Optional<A>
|
|
switch_enum_addr %2 : $*Optional<A>, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2
|
|
|
|
bb1:
|
|
%5 = unchecked_take_enum_data_addr %2 : $*Optional<A>, #Optional.some!enumelt
|
|
%6 = load [trivial] %5 : $*A
|
|
br bb3(%6 : $A)
|
|
|
|
bb2:
|
|
%8 = enum $A, #A.B!enumelt
|
|
br bb3(%8 : $A)
|
|
|
|
bb3(%10 : $A):
|
|
dealloc_stack %2 : $*Optional<A>
|
|
%12 = enum $Optional<A>, #Optional.some!enumelt, %10 : $A
|
|
%13 = struct_extract %1 : $OptionalKlassWrapper, #OptionalKlassWrapper.k
|
|
switch_enum %13 : $Optional<Klass>, case #Optional.some!enumelt: bb4, case #Optional.none!enumelt: bb5
|
|
|
|
bb4(%15 : @guaranteed $Klass):
|
|
%16 = class_method %15 : $Klass, #Klass.foo : (Klass) -> () -> Int?, $@convention(method) (@guaranteed Klass) -> Optional<Int>
|
|
%17 = apply %16(%15) : $@convention(method) (@guaranteed Klass) -> Optional<Int>
|
|
br bb6(%17 : $Optional<Int>)
|
|
|
|
bb5:
|
|
%19 = enum $Optional<Int>, #Optional.none!enumelt
|
|
br bb6(%19 : $Optional<Int>)
|
|
|
|
bb6(%21 : $Optional<Int>):
|
|
%22 = tuple ()
|
|
return %22 : $()
|
|
}
|
|
|
|
|
|
// CHECK-LABEL: sil [ossa] @simplify_switch_enum3 :
|
|
// CHECK-NOT: switch_enum
|
|
// CHECK: destroy_value %{{[0-9]+}} : $Optional<String>
|
|
// CHECK: } // end sil function 'simplify_switch_enum3'
|
|
sil [ossa] @simplify_switch_enum3 : $@convention(thin) () -> () {
|
|
bb0:
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
%1 = enum $Optional<String>, #Optional.none!enumelt
|
|
br bb3(%1 : $Optional<String>)
|
|
|
|
bb2:
|
|
%4 = enum $Optional<String>, #Optional.none!enumelt
|
|
br bb3(%4 : $Optional<String>)
|
|
|
|
bb3(%10 : @owned $Optional<String>):
|
|
debug_value %10 : $Optional<String>
|
|
switch_enum %10 : $Optional<String>, case #Optional.some!enumelt: bb5, case #Optional.none!enumelt: bb4
|
|
|
|
bb4:
|
|
br bb6
|
|
|
|
bb5(%14 : @owned $String):
|
|
destroy_value %14 : $String
|
|
br bb6
|
|
|
|
bb6:
|
|
%17 = tuple ()
|
|
return %17 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @simplify_switch_enum_unreachable :
|
|
// CHECK-NOT: switch_enum
|
|
// CHECK: } // end sil function 'simplify_switch_enum_unreachable'
|
|
sil [ossa] @simplify_switch_enum_unreachable : $@convention(thin) (A) -> () {
|
|
bb0(%0 : $A):
|
|
switch_enum %0 : $A, case #A.B!enumelt: bb1, default bb2
|
|
|
|
bb1:
|
|
unreachable
|
|
|
|
bb2(%defaultArg : $A):
|
|
apply undef(%defaultArg) : $@convention(thin) (A) -> ()
|
|
br bb3
|
|
|
|
bb3:
|
|
%t = tuple ()
|
|
return %t : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @insert_compensating_endlifetime_in_switch_enum_destination_block :
|
|
// CHECK: bb0(%0 : @owned $Optional<AnyObject>):
|
|
// CHECK-NEXT: end_lifetime %0
|
|
// CHECK-NEXT: tuple
|
|
// CHECK: } // end sil function 'insert_compensating_endlifetime_in_switch_enum_destination_block'
|
|
sil [ossa] @insert_compensating_endlifetime_in_switch_enum_destination_block : $@convention(thin) (@owned Optional<AnyObject>) -> () {
|
|
bb0(%0 : @owned $Optional<AnyObject>):
|
|
switch_enum %0, case #Optional.none!enumelt: bb1, case #Optional.some!enumelt: bb2
|
|
|
|
bb1:
|
|
%15 = tuple ()
|
|
return %15
|
|
|
|
bb2(%4 : @owned $AnyObject):
|
|
destroy_value %4
|
|
unreachable
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @insert_compensating_endlifetime_in_switch_enum_destination_block2 :
|
|
// CHECK: bb0(%0 : @owned $NCE):
|
|
// CHECK-NEXT: %1 = drop_deinit %0
|
|
// CHECK-NEXT: end_lifetime %1
|
|
// CHECK-NEXT: tuple
|
|
// CHECK: } // end sil function 'insert_compensating_endlifetime_in_switch_enum_destination_block2'
|
|
sil [ossa] @insert_compensating_endlifetime_in_switch_enum_destination_block2 : $@convention(thin) (@owned NCE) -> () {
|
|
bb0(%0 : @owned $NCE):
|
|
%1 = drop_deinit %0
|
|
switch_enum %1, case #NCE.a!enumelt: bb1, case #NCE.b!enumelt: bb2
|
|
|
|
bb1:
|
|
%15 = tuple ()
|
|
return %15
|
|
|
|
bb2(%4 : @owned $AnyObject):
|
|
destroy_value %4
|
|
unreachable
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @replace_phi_arg_with_borrowed_from_use :
|
|
// CHECK: bb3([[R:%.*]] : @reborrow $B):
|
|
// CHECK: bb6([[G:%.*]] : @guaranteed $E):
|
|
// CHECK-NEXT: [[X:%.*]] = borrowed [[G]] : $E from ([[R]] : $B)
|
|
// CHECK-NEXT: fix_lifetime [[X]]
|
|
// CHECK: } // end sil function 'replace_phi_arg_with_borrowed_from_use'
|
|
sil [ossa] @replace_phi_arg_with_borrowed_from_use : $@convention(thin) (@in_guaranteed B) -> () {
|
|
bb0(%0 : $*B):
|
|
cond_br undef, bb1, bb2
|
|
bb1:
|
|
%4 = load_borrow %0
|
|
br bb3(%4)
|
|
bb2:
|
|
%6 = load_borrow %0
|
|
br bb3(%6)
|
|
bb3(%8 : @reborrow $B):
|
|
%9 = borrowed %8 from ()
|
|
cond_br undef, bb4, bb5
|
|
bb4:
|
|
%11 = unchecked_ref_cast %9 to $E
|
|
br bb6(%11, %9)
|
|
bb5:
|
|
%13 = unchecked_ref_cast %9 to $E
|
|
br bb6(%13, %9)
|
|
bb6(%15: @guaranteed $E, %16 : @reborrow $B):
|
|
%17 = borrowed %15 from (%16)
|
|
%18 = borrowed %16 from ()
|
|
fix_lifetime %17
|
|
end_borrow %18
|
|
%r = tuple()
|
|
return %r
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @dont_crash_when_merging_blocks_with_borrowed_from :
|
|
// CHECK: } // end sil function 'dont_crash_when_merging_blocks_with_borrowed_from'
|
|
sil [ossa] @dont_crash_when_merging_blocks_with_borrowed_from : $@convention(thin) (@owned Optional<S>) -> () {
|
|
bb0(%0 : @owned $Optional<S>):
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
%2 = integer_literal $Builtin.Int1, -1
|
|
br bb3(%2)
|
|
|
|
bb2:
|
|
%4 = integer_literal $Builtin.Int1, 0
|
|
br bb3(%4)
|
|
|
|
bb3(%6 : $Builtin.Int1):
|
|
%7 = begin_borrow %0
|
|
cond_br %6, bb4, bb5
|
|
|
|
bb4:
|
|
switch_enum %7, case #Optional.some!enumelt: bb6, case #Optional.none!enumelt: bb7
|
|
|
|
bb5:
|
|
br bb9
|
|
|
|
bb6(%11 : @guaranteed $S):
|
|
(%12, %13) = destructure_struct %11
|
|
%14 = enum $Optional<NonTrivial>, #Optional.some!enumelt, %13
|
|
br bb8(%14)
|
|
|
|
bb7:
|
|
%16 = enum $Optional<NonTrivial>, #Optional.none!enumelt
|
|
br bb8(%16)
|
|
|
|
bb8(%18 : @guaranteed $Optional<NonTrivial>):
|
|
%19 = borrowed %18 from (%7)
|
|
br bb9
|
|
|
|
bb9:
|
|
end_borrow %7
|
|
destroy_value %0
|
|
%23 = tuple ()
|
|
return %23
|
|
}
|
|
|