blob: c69991ee48084eee51e072e026a5953bacd2a696 [file] [log] [blame]
// RUN: %target-sil-opt -enable-sil-verify-all %s -jumpthread-simplify-cfg | %FileCheck %s
sil_stage canonical
import Builtin
import Swift
import SwiftShims
// CHECK-LABEL: sil @test_jump_threading
// CHECK: bb5(%{{[0-9]+}} : $Builtin.Int64):
// CHECK-NEXT: br bb1
sil @test_jump_threading : $@convention(thin) (Builtin.Int1) -> () {
bb0(%0 : $Builtin.Int1):
cond_br %0, bb2, bb3
// Blocks are handled from last to first. Block bb1 is placed here so that its argument
// is not optimized before jump threading is done in bb2 and bb3.
bb1(%i4 : $Builtin.Int64):
%f3 = function_ref @get_condition : $@convention(thin) (Builtin.Int64) -> Builtin.Int1
%c1 = apply %f3(%i3) : $@convention(thin) (Builtin.Int64) -> Builtin.Int1
%i5 = integer_literal $Builtin.Int64, 27
cond_br %c1, bb1(%i5 : $Builtin.Int64), bb5
bb2:
%f1 = function_ref @get_int1 : $@convention(thin) () -> Builtin.Int64
%i1 = apply %f1() : $@convention(thin) () -> Builtin.Int64
br bb4(%i1 : $Builtin.Int64)
bb3:
%f2 = function_ref @get_int1 : $@convention(thin) () -> Builtin.Int64
%i2 = apply %f2() : $@convention(thin) () -> Builtin.Int64
br bb4(%i2 : $Builtin.Int64)
// Jump threading must not be done for this block because the argument %i3 is also
// used in bb1.
bb4(%i3 : $Builtin.Int64):
br bb1(%i3 : $Builtin.Int64)
bb5:
%r1 = tuple ()
return %r1 : $()
}
sil @get_int1 : $@convention(thin) () -> Builtin.Int64
sil @get_int2 : $@convention(thin) () -> Builtin.Int64
sil @get_condition : $@convention(thin) (Builtin.Int64) -> Builtin.Int1
public final class AA {
}
public final class BB {
@_hasStorage internal weak final var n: BB!
@_hasStorage internal final var o: AA!
}
// Test that SimplifyCFG does not hang when compiling an infinite loop with switch_enum.
// CHECK-LABEL: test_inifite_loop
sil hidden @test_inifite_loop : $@convention(method) (@owned BB) -> () {
bb0(%0 : $BB):
%31 = enum $Optional<BB>, #Optional.some!enumelt, %0 : $BB
br bb4(%31 : $Optional<BB>)
bb4(%36 : $Optional<BB>):
switch_enum %36 : $Optional<BB>, case #Optional.some!enumelt: bb6, default bb5
bb5:
br bb7
bb6:
%39 = unchecked_enum_data %36 : $Optional<BB>, #Optional.some!enumelt
%40 = ref_element_addr %39 : $BB, #BB.o
%41 = load %40 : $*Optional<AA>
release_value %41 : $Optional<AA>
br bb7
bb7:
switch_enum %36 : $Optional<BB>, case #Optional.none!enumelt: bb8, case #Optional.some!enumelt: bb9
bb8:
br bb4(%36 : $Optional<BB>)
bb9:
%48 = unchecked_enum_data %36 : $Optional<BB>, #Optional.some!enumelt
%49 = ref_element_addr %48 : $BB, #BB.n
%50 = load_weak %49 : $*@sil_weak Optional<BB>
release_value %36 : $Optional<BB>
switch_enum %50 : $Optional<BB>, case #Optional.some!enumelt: bb11, case #Optional.none!enumelt: bb10
bb10:
br bb4(%50 : $Optional<BB>)
bb11:
%54 = unchecked_enum_data %50 : $Optional<BB>, #Optional.some!enumelt
%55 = ref_to_raw_pointer %54 : $BB to $Builtin.RawPointer
%56 = ref_to_raw_pointer %0 : $BB to $Builtin.RawPointer
%57 = builtin "cmp_eq_RawPointer"(%55 : $Builtin.RawPointer, %56 : $Builtin.RawPointer) : $Builtin.Int1
cond_br %57, bb13, bb12
bb12:
br bb4(%50 : $Optional<BB>)
bb13:
release_value %50 : $Optional<BB>
strong_release %0 : $BB
%65 = tuple ()
return %65 : $()
}
sil @some_function : $@convention(thin) (AA) -> Optional<AA>
// Another test for checking that SimplifyCFG does not hang.
// CHECK-LABEL: test_other_infinite_loop
sil hidden @test_other_infinite_loop : $@convention(method) (@owned AA) -> () {
bb0(%5 : $AA):
strong_retain %5 : $AA
%6 = enum $Optional<AA>, #Optional.some!enumelt, %5 : $AA
br bb1(%6 : $Optional<AA>)
bb1(%8 : $Optional<AA>):
retain_value %8 : $Optional<AA>
switch_enum %8 : $Optional<AA>, case #Optional.some!enumelt: bb3, default bb2
bb2:
release_value %8 : $Optional<AA>
br bb6
bb3:
cond_br undef, bb4, bb5
bb4:
%85 = tuple ()
return %85 : $()
bb5:
br bb6
bb6:
switch_enum %8 : $Optional<AA>, case #Optional.none!enumelt: bb7, default bb8
bb7:
br bb9(%8 : $Optional<AA>)
bb8:
%23 = unchecked_enum_data %8 : $Optional<AA>, #Optional.some!enumelt
strong_retain %23 : $AA
%25 = function_ref @some_function : $@convention(thin) (AA) -> Optional<AA>
%26 = apply %25(%23) : $@convention(thin) (AA) -> Optional<AA>
strong_release %23 : $AA
br bb9(%26 : $Optional<AA>)
bb9(%29 : $Optional<AA>):
release_value %8 : $Optional<AA>
br bb1(%29 : $Optional<AA>)
}
// -----------------------------------------------------------------------------
// Test jump-threading through a non-pure address producer.
//
// BB3 cannot (currently) be cloned because the block cloner does not
// know how to sink address producers unless they are pure address
// projections. init_existential_addr is not a pure projection. It's
// address is transitively used outside bb3 via %17 =
// tuple_element_addr. Test that cloning is inhibited. If cloning did
// happen, then it would either need to sink init_existential_addr, or
// SSA would be incorrectly updated.
enum FakeOptional<T> {
case some(T)
case none
}
struct S {
}
struct T {
let s: S
}
class C {
func method() -> Any
func f() -> FakeOptional<T>
}
// Make BB3 is not jump-threaded. And init_existential_addr is not cloned
//
// CHECK-LABEL: sil hidden @nonPureAddressProducer : $@convention(method) (@guaranteed C) -> @out Any {
// CHECK: bb0(%0 : $*Any, %1 : $C):
// CHECK: switch_enum %{{.*}} : $FakeOptional<T>, case #FakeOptional.some!enumelt: bb1, case #FakeOptional.none!enumelt: bb2
// CHECK: bb3(%{{.*}} : $FakeOptional<Int64>):
// CHECK: init_existential_addr %0 : $*Any, $(FakeOptional<Int64>, FakeOptional<S>)
// CHECK: switch_enum %{{.*}} : $FakeOptional<T>, case #FakeOptional.some!enumelt: bb4, case #FakeOptional.none!enumelt: bb6
// CHECK-LABEL: } // end sil function 'nonPureAddressProducer'
sil hidden @nonPureAddressProducer : $@convention(method) (@guaranteed C) -> @out Any {
bb0(%0 : $*Any, %1 : $C):
%3 = class_method %1 : $C, #C.f : (C) -> () -> FakeOptional<T>, $@convention(method) (@guaranteed C) -> FakeOptional<T>
%4 = apply %3(%1) : $@convention(method) (@guaranteed C) -> FakeOptional<T>
switch_enum %4 : $FakeOptional<T>, case #FakeOptional.some!enumelt: bb1, case #FakeOptional.none!enumelt: bb2
bb1:
%7 = integer_literal $Builtin.Int64, 1
%8 = struct $Int64 (%7 : $Builtin.Int64)
%9 = enum $FakeOptional<Int64>, #FakeOptional.some!enumelt, %8 : $Int64
br bb3(%9 : $FakeOptional<Int64>)
bb2:
%11 = enum $FakeOptional<Int64>, #FakeOptional.none!enumelt
br bb3(%11 : $FakeOptional<Int64>)
bb3(%13 : $FakeOptional<Int64>):
%15 = init_existential_addr %0 : $*Any, $(FakeOptional<Int64>, FakeOptional<S>)
%16 = tuple_element_addr %15 : $*(FakeOptional<Int64>, FakeOptional<S>), 0
%17 = tuple_element_addr %15 : $*(FakeOptional<Int64>, FakeOptional<S>), 1
store %13 to %16 : $*FakeOptional<Int64>
switch_enum %4 : $FakeOptional<T>, case #FakeOptional.some!enumelt: bb4, case #FakeOptional.none!enumelt: bb6
bb4(%20 : $T):
%21 = struct_extract %20 : $T, #T.s
%22 = enum $FakeOptional<S>, #FakeOptional.some!enumelt, %21 : $S
store %22 to %17 : $*FakeOptional<S>
br bb5
bb5:
%25 = tuple ()
return %25 : $()
bb6:
%27 = enum $FakeOptional<S>, #FakeOptional.none!enumelt
store %27 to %17 : $*FakeOptional<S>
br bb5
}