Files
swift-mirror/test/SILOptimizer/simplify_cfg_jump_thread_crash.sil
Andrew Trick b1fbc2b389 Rewrite SimplifyCFG's trampoline removal.
Avoid introducing new critical edges. Passes will end up resplitting
them, forcing SimplifyCFG to continually rerun. Also, we want to allow
SIL utilities to assume no critical edges, and avoid the need for
several passes to internally split edges and modify the CFG for no
reason.

Also, handle arbitrary block arguments which may be trivial and
unrelated to the real optimizations that trampoline removal exposes,
such as "unwrapping" enumeration-type arguments.

The previous code was an example of how to write an unstable
optimization. It could be defeated by other code in the function that
isn't directly related to the SSA graph being optimized. In general,
when an optimization can be defeated by unrelated code in the
function, that leads to instability which can be very hard to track
down (I spent multiple full days on this one). In this case, we have
enum-type branch args which need to be simplified by unwrapping
them. But, the existence of a trivial and entirely unrelated block
argument would defeat the optimization.
2020-10-29 15:25:20 -07:00

233 lines
7.2 KiB
Plaintext

// 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
}