mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Fix CheckedCastBrJumpThreading when we have dominating checked_cast_br on failure path only
745 lines
26 KiB
Plaintext
745 lines
26 KiB
Plaintext
// RUN: %target-sil-opt -enable-sil-verify-all %s -jumpthread-simplify-cfg -enable-ossa-simplify-cfg -enable-ossa-rewriteterminator | %FileCheck %s
|
|
// RUN: %target-sil-opt -enable-sil-verify-all %s -jumpthread-simplify-cfg -enable-ossa-simplify-cfg -enable-ossa-rewriteterminator -debug-only=sil-simplify-cfg 2>&1 | %FileCheck %s --check-prefix=CHECK-TRACE
|
|
//
|
|
// REQUIRES: asserts
|
|
|
|
// FIXME: which of these tests actually require -jumpthread-simplify-cfg instead of -simplify-cfg?
|
|
|
|
// FIXME_rauw: revisit these fixme's after the OwnershipRAUW utility is rewritten in terms of OwnershipLiveness.
|
|
|
|
sil_stage canonical
|
|
|
|
import Builtin
|
|
import Swift
|
|
|
|
class Klass {
|
|
var a: Int
|
|
deinit
|
|
init()
|
|
}
|
|
|
|
protocol OtherKlass : AnyObject {}
|
|
|
|
sil [ossa] @consume_klass : $@convention(thin) (@owned Klass) -> ()
|
|
sil [ossa] @get_klass : $@convention(thin) () -> @owned Klass
|
|
|
|
sil [ossa] @unknown : $@convention(thin) () -> ()
|
|
|
|
struct KlassWrapper {
|
|
var k: Klass
|
|
}
|
|
|
|
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 [ossa] @_TFC3ccb4Base5innerfS0_FT_T_ : $@convention(method) (@guaranteed Base) -> ()
|
|
sil [ossa] @_TFC3ccb4Base6middlefS0_FT_T_ : $@convention(method) (@guaranteed Base) -> ()
|
|
|
|
sil @get_base : $@convention(thin) () -> @owned Base
|
|
|
|
// CHECK-LABEL: sil [ossa] @redundant_checked_cast_br
|
|
sil [ossa] @redundant_checked_cast_br : $@convention(method) (@guaranteed Base) -> () {
|
|
bb0(%0 : @guaranteed $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] %0 : $Base to Base, [[SUCCESS:bb[0-9]+]], [[FAIL:bb[0-9]+]]
|
|
checked_cast_br [exact] %0 : $Base to Base, bb2, bb7
|
|
|
|
// CHECK: bb1
|
|
bb1:
|
|
%3 = tuple ()
|
|
return %3 : $()
|
|
|
|
bb2(%5 : @guaranteed $Base):
|
|
// CHECK: [[SUCCESS]]
|
|
%7 = class_method %0 : $Base, #Base.inner : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
|
|
// CHECK-NOT: checked_cast_br
|
|
checked_cast_br [exact] %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 : @guaranteed $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(%defaultBB2 : @guaranteed $Base):
|
|
%15 = apply %7(%0) : $@convention(method) (@guaranteed Base) -> ()
|
|
br bb4
|
|
|
|
bb6(%17 : $()):
|
|
br bb1
|
|
|
|
bb7(%defaultBB0 : @guaranteed $Base):
|
|
%19 = apply %1(%0) : $@convention(method) (@guaranteed Base) -> ()
|
|
br bb1
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @redundant_checked_cast_br_owned
|
|
sil [ossa] @redundant_checked_cast_br_owned : $@convention(method) (@guaranteed Base) -> () {
|
|
// CHECK: [[COPY:%.*]] = copy_value %0
|
|
// CHECK: [[METHOD:%.*]] = class_method %0 : $Base, #Base.middle : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
|
|
// CHECK: checked_cast_br [exact] [[COPY]] : $Base to Base, [[SUCCESS:bb[0-9]+]], [[FAIL:bb[0-9]+]]
|
|
bb0(%0 : @guaranteed $Base):
|
|
%copy = copy_value %0 : $Base
|
|
%1 = class_method %0 : $Base, #Base.middle : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
|
|
checked_cast_br [exact] %copy : $Base to Base, bb2, bb7
|
|
|
|
// CHECK: bb1
|
|
bb1:
|
|
%3 = tuple ()
|
|
return %3 : $()
|
|
|
|
// CHECK: [[SUCCESS]]([[SUCCESSARG:%.*]] : @owned $Base)
|
|
// CHECK-NOT: checked_cast_br
|
|
//
|
|
// FIXME_rauw: RAUW should not create this copy!
|
|
// CHECK: [[SUCCESSCOPY:%.*]] = copy_value [[SUCCESSARG]] : $Base
|
|
// CHECK: [[INNER:%.*]] = function_ref @_TFC3ccb4Base5innerfS0_FT_T_ : $@convention(method) (@guaranteed Base) -> ()
|
|
// CHECK: apply [[INNER]]([[SUCCESSCOPY]])
|
|
// CHECK: br bb1
|
|
bb2(%5 : @owned $Base):
|
|
%7 = class_method %0 : $Base, #Base.inner : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
|
|
checked_cast_br [exact] %5 : $Base to Base, bb3, bb5
|
|
|
|
// CHECK: [[FAIL]]([[FAILARG:%.*]] : @owned $Base)
|
|
// CHECK-NOT: function-ref
|
|
// CHECK: apply [[METHOD]]([[FAILARG]])
|
|
bb3(%9 : @owned $Base):
|
|
%10 = function_ref @_TFC3ccb4Base5innerfS0_FT_T_ : $@convention(method) (@guaranteed Base) -> ()
|
|
%11 = apply %10(%9) : $@convention(method) (@guaranteed Base) -> ()
|
|
destroy_value %9 : $Base
|
|
br bb4
|
|
|
|
bb4:
|
|
%13 = tuple ()
|
|
br bb6(%13 : $())
|
|
|
|
bb5(%defaultBB2 : @owned $Base):
|
|
%15 = apply %7(%defaultBB2) : $@convention(method) (@guaranteed Base) -> ()
|
|
destroy_value %defaultBB2 : $Base
|
|
br bb4
|
|
|
|
bb6(%17 : $()):
|
|
br bb1
|
|
|
|
bb7(%defaultBB0 : @owned $Base):
|
|
%19 = apply %1(%defaultBB0) : $@convention(method) (@guaranteed Base) -> ()
|
|
destroy_value %defaultBB0 : $Base
|
|
br bb1
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @not_redundant_checked_cast_br : $@convention(method) (@guaranteed Base) -> () {
|
|
sil [ossa] @not_redundant_checked_cast_br : $@convention(method) (@guaranteed Base) -> () {
|
|
bb0(%0 : @guaranteed $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] %0 : $Base to Base, [[SUCCESS:bb[0-9]+]], [[FAIL:bb[0-9]+]]
|
|
checked_cast_br [exact] %0 : $Base to Base, bb2, bb7
|
|
|
|
// CHECK: bb1:
|
|
// CHECK: tuple ()
|
|
// CHECK: return
|
|
|
|
bb1:
|
|
%3 = tuple ()
|
|
return %3 : $()
|
|
|
|
bb2(%5 : @guaranteed $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 : @guaranteed $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(%9a : @guaranteed $Base):
|
|
%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(%10a : @guaranteed $Base):
|
|
// CHECK: checked_cast_br [exact] %0 : $Base to Derived
|
|
checked_cast_br [exact] %0 : $Base to Derived, bb3, bb5
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @failing_checked_cast_br
|
|
sil [ossa] @failing_checked_cast_br : $@convention(method) (@guaranteed Base) -> () {
|
|
bb0(%0 : @guaranteed $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] %0 : $Base to Base, [[SUCCESS:bb[0-9]+]], [[FAIL:bb[0-9]+]]
|
|
checked_cast_br [exact] %0 : $Base to Base, bb2, bb7
|
|
|
|
// CHECK-LABEL: bb1
|
|
bb1:
|
|
%3 = tuple ()
|
|
return %3 : $()
|
|
|
|
bb2(%5 : @guaranteed $Base):
|
|
// CHECK: [[SUCCESS]]([[SUCCESSARG:%.*]] : @guaranteed $Base)
|
|
// 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] %0 : $Base to Derived
|
|
// CHECK: apply [[METHOD2]]([[SUCCESSARG]])
|
|
// 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] %5 : $Base to Derived, bb3, bb5
|
|
|
|
bb3(%9 : @guaranteed $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(%9o : @guaranteed $Base):
|
|
%15 = apply %7(%9o) : $@convention(method) (@guaranteed Base) -> ()
|
|
br bb4
|
|
|
|
bb6(%17 : $()):
|
|
br bb1
|
|
|
|
bb7(%anotherDefaultPayload : @guaranteed $Base):
|
|
%19 = apply %1(%0) : $@convention(method) (@guaranteed Base) -> ()
|
|
br bb1
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @failing_checked_cast_br_owned
|
|
sil [ossa] @failing_checked_cast_br_owned : $@convention(method) (@guaranteed Base) -> () {
|
|
// CHECK: [[COPY:%.*]] = copy_value %0
|
|
// CHECK: [[METHOD:%.*]] = class_method %0 : $Base, #Base.middle : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
|
|
// CHECK: checked_cast_br [exact] [[COPY]] : $Base to Base, [[SUCCESS:bb[0-9]+]], [[FAIL:bb[0-9]+]]
|
|
bb0(%0 : @guaranteed $Base):
|
|
%copy = copy_value %0 : $Base
|
|
%1 = class_method %0 : $Base, #Base.middle : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
|
|
checked_cast_br [exact] %copy : $Base to Base, bb2, bb7
|
|
|
|
// CHECK-LABEL: bb1
|
|
bb1:
|
|
%3 = tuple ()
|
|
return %3 : $()
|
|
|
|
// CHECK: [[SUCCESS]]([[SUCCESSARG:%.*]] : @owned $Base)
|
|
// CHECK: [[METHOD2:%.*]] = class_method %0 : $Base, #Base.inner : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
|
|
// CHECK-NOT: checked_cast_br [exact] %0 : $Base to Derived
|
|
// CHECK: apply [[METHOD2]]([[SUCCESSARG]])
|
|
// 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
|
|
bb2(%5 : @owned $Base):
|
|
%7 = class_method %0 : $Base, #Base.inner : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
|
|
checked_cast_br [exact] %5 : $Base to Derived, bb3, bb5
|
|
|
|
bb3(%9 : @owned $Derived):
|
|
%upcast = upcast %9 : $Derived to $Base
|
|
%10 = function_ref @_TFC3ccb4Base5innerfS0_FT_T_ : $@convention(method) (@guaranteed Base) -> ()
|
|
%11 = apply %10(%upcast) : $@convention(method) (@guaranteed Base) -> ()
|
|
destroy_value %upcast : $Base
|
|
br bb4
|
|
|
|
bb4:
|
|
%13 = tuple ()
|
|
br bb6(%13 : $())
|
|
|
|
bb5(%9o : @owned $Base):
|
|
%15 = apply %7(%9o) : $@convention(method) (@guaranteed Base) -> ()
|
|
destroy_value %9o : $Base
|
|
br bb4
|
|
|
|
bb6(%17 : $()):
|
|
br bb1
|
|
|
|
bb7(%anotherDefaultPayload : @owned $Base):
|
|
%19 = apply %1(%0) : $@convention(method) (@guaranteed Base) -> ()
|
|
destroy_value %anotherDefaultPayload : $Base
|
|
br bb1
|
|
}
|
|
|
|
sil [ossa] @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 [ossa] @no_checked_cast_br_threading_with_alloc_ref_stack : $@convention(method) (@guaranteed Base) -> () {
|
|
bb0(%0 : @guaranteed $Base):
|
|
%fu = function_ref @unknown : $@convention(thin) () -> ()
|
|
%fu2 = function_ref @unknown2 : $@convention(thin) () -> ()
|
|
checked_cast_br [exact] %0 : $Base to Base, bb1, bb2
|
|
|
|
bb1(%1 : @guaranteed $Base):
|
|
apply %fu() : $@convention(thin) () -> ()
|
|
br bb3
|
|
|
|
bb2(%1a : @guaranteed $Base):
|
|
apply %fu2() : $@convention(thin) () -> ()
|
|
br bb3
|
|
|
|
bb3:
|
|
%a = alloc_ref [stack] $Base
|
|
checked_cast_br [exact] %0 : $Base to Base, bb4, bb5
|
|
|
|
bb4(%2 : @guaranteed $Base):
|
|
apply %fu() : $@convention(thin) () -> ()
|
|
br bb6
|
|
|
|
bb5(%2a : @guaranteed $Base):
|
|
apply %fu2() : $@convention(thin) () -> ()
|
|
br bb6
|
|
|
|
bb6:
|
|
dealloc_ref %a : $Base
|
|
dealloc_stack_ref %a : $Base
|
|
%r = tuple()
|
|
return %r : $()
|
|
}
|
|
|
|
// Test a redundant checked_cast_br that has success, failure paths, and unknown paths.
|
|
//
|
|
// TODO: this is currently a bailout because the CFG transform can't handle it:
|
|
// (!SuccessPreds.empty() && !FailurePreds.empty() && numUnknownPreds > 0)
|
|
sil [ossa] @redundant_checked_cast_br_joined_success_fail_unknown : $@convention(method) (@guaranteed Base) -> () {
|
|
bb0(%0 : @guaranteed $Base):
|
|
%middle = class_method %0 : $Base, #Base.middle : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
|
|
checked_cast_br [exact] %0 : $Base to Base, bb1, bb4
|
|
|
|
bb1(%successBB0 : @guaranteed $Base):
|
|
cond_br undef, bb2, bb3
|
|
|
|
bb2:
|
|
%successBB0call2 = apply %middle(%successBB0) : $@convention(method) (@guaranteed Base) -> ()
|
|
br bb8(%successBB0 : $Base)
|
|
|
|
bb3:
|
|
%successBB0call3 = apply %middle(%successBB0) : $@convention(method) (@guaranteed Base) -> ()
|
|
br bb7(%successBB0 : $Base)
|
|
|
|
bb4(%failBB0 : @guaranteed $Base):
|
|
cond_br undef, bb5, bb6
|
|
|
|
bb5:
|
|
%failBB0call5 = apply %middle(%failBB0) : $@convention(method) (@guaranteed Base) -> ()
|
|
br bb7(%failBB0 : $Base)
|
|
|
|
bb6:
|
|
%failBB0call6 = apply %middle(%failBB0) : $@convention(method) (@guaranteed Base) -> ()
|
|
br bb8(%failBB0 : $Base)
|
|
|
|
bb7(%unknown : @guaranteed $Base):
|
|
%unknownCall = apply %middle(%unknown) : $@convention(method) (@guaranteed Base) -> ()
|
|
br bb8(%unknown : $Base)
|
|
|
|
bb8(%joined : @guaranteed $Base):
|
|
%joinedCall = apply %middle(%joined) : $@convention(method) (@guaranteed Base) -> ()
|
|
checked_cast_br [exact] %joined : $Base to Base, bb9, bb10
|
|
|
|
bb9(%successBB7 : @guaranteed $Base):
|
|
%successBB7call8 = apply %middle(%successBB7) : $@convention(method) (@guaranteed Base) -> ()
|
|
br bb11
|
|
|
|
bb10(%failBB7 : @guaranteed $Base):
|
|
%failBB7call8 = apply %middle(%failBB7) : $@convention(method) (@guaranteed Base) -> ()
|
|
br bb11
|
|
|
|
bb11:
|
|
%20 = tuple ()
|
|
return %20 : $()
|
|
|
|
}
|
|
|
|
// Verify that checked-cast jump-threading kicks in and generates verifiable SIL.
|
|
//
|
|
// CHECK-TRACE-LABEL: ### Run SimplifyCFG on $testCheckCastJumpThread
|
|
// CHECK-TRACE: Condition is the same if reached over {{.*}} parent @$testCheckCastJumpThread : $@convention(thin) (@guaranteed Klass) -> @owned any OtherKlass }
|
|
// CHECK-TRACE-NEXT: // %{{.*}} user:
|
|
// CHECK-TRACE-NEXT: bb1(%{{.*}} : @owned $any OtherKlass):
|
|
// CHECK-TRACE-NEXT: destroy_value
|
|
// CHECK-TRACE-NEXT: br bb5(%{{.*}} : $Klass)
|
|
sil shared [ossa] @$testCheckCastJumpThread : $@convention(thin) (@guaranteed Klass) -> @owned OtherKlass {
|
|
bb0(%0 : @guaranteed $Klass):
|
|
%1 = function_ref @get_klass : $@convention(thin) () -> @owned Klass
|
|
%2 = integer_literal $Builtin.Int64, 1
|
|
%3 = apply %1() : $@convention(thin) () -> @owned Klass
|
|
%4 = copy_value %3 : $Klass
|
|
checked_cast_br %3 : $Klass to OtherKlass, bb1, bb2
|
|
|
|
bb1(%5 : @owned $OtherKlass):
|
|
destroy_value %5 : $OtherKlass
|
|
%6 = integer_literal $Builtin.Int1, -1
|
|
br bb3(%6 : $Builtin.Int1)
|
|
|
|
bb2(%7 : @owned $Klass):
|
|
destroy_value %7 : $Klass
|
|
%8 = integer_literal $Builtin.Int1, 0
|
|
br bb3(%8 : $Builtin.Int1)
|
|
|
|
bb3(%10 : $Builtin.Int1):
|
|
cond_br %10, bb5, bb6
|
|
|
|
bb4:
|
|
unreachable
|
|
|
|
bb5:
|
|
br bb7(%4 : $Klass)
|
|
|
|
bb6:
|
|
destroy_value %4 : $Klass
|
|
br bb10(%2 : $Builtin.Int64)
|
|
|
|
bb7(%16 : @owned $Klass):
|
|
checked_cast_br %16 : $Klass to OtherKlass, bb9, bb8
|
|
|
|
bb8(%18 : @owned $Klass):
|
|
destroy_value %18 : $Klass
|
|
br bb4
|
|
|
|
bb9(%20 : @owned $OtherKlass):
|
|
return %20 : $OtherKlass
|
|
|
|
bb10(%22 : $Builtin.Int64):
|
|
%23 = apply %1() : $@convention(thin) () -> @owned Klass
|
|
%24 = copy_value %23 : $Klass
|
|
checked_cast_br %23 : $Klass to OtherKlass, bb11, bb12
|
|
|
|
bb11(%25 : @owned $OtherKlass):
|
|
destroy_value %25 : $OtherKlass
|
|
%26 = integer_literal $Builtin.Int1, -1
|
|
br bb13(%26 : $Builtin.Int1)
|
|
|
|
bb12(%27 : @owned $Klass):
|
|
destroy_value %27 : $Klass
|
|
%28 = integer_literal $Builtin.Int1, 0
|
|
br bb13(%28 : $Builtin.Int1)
|
|
|
|
bb13(%30 : $Builtin.Int1):
|
|
cond_br %30, bb14, bb15
|
|
|
|
bb14:
|
|
br bb7(%24 : $Klass)
|
|
|
|
bb15:
|
|
destroy_value %24 : $Klass
|
|
cond_br undef, bb16, bb17
|
|
|
|
bb16:
|
|
br bb4
|
|
|
|
bb17:
|
|
br bb10(undef : $Builtin.Int64)
|
|
}
|
|
|
|
// =============================================================================
|
|
// Test OSSA lifetime fixup after removing redundant checked_cast_br
|
|
|
|
// Replace an owned result with a guaranteed value on the success path.
|
|
// Introduce a copy for the new lifetime.
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @redundant_checked_cast_br_rauw_guaranteed_to_owned_success : $@convention(method) (@owned Base) -> () {
|
|
// CHECK: bb0(%0 : @owned $Base):
|
|
// CHECK: [[BORROW:%.*]] = begin_borrow %0 : $Base
|
|
// CHECK: checked_cast_br [exact] [[BORROW]] : $Base to Base, bb1, bb2
|
|
// CHECK: bb1([[EXACT:%.*]] : @guaranteed $Base):
|
|
// CHECK-NEXT: [[NEWCP:%.*]] = copy_value [[EXACT]] : $Base
|
|
// CHECK-NEXT: [[OLDCP:%.*]] = copy_value [[EXACT]] : $Base
|
|
// CHECK-NEXT: end_borrow
|
|
// CHECK: destroy_value [[OLDCP]] : $Base
|
|
// CHECK: apply %{{.*}}([[NEWCP]]) : $@convention(method) (@guaranteed Base) -> ()
|
|
// CHECK: destroy_value [[NEWCP]] : $Base
|
|
// CHECK: br bb3
|
|
// CHECK: bb2([[INEXACT:%.*]] : @guaranteed $Base):
|
|
// CHECK: apply %{{.*}}([[INEXACT]]) : $@convention(method) (@guaranteed Base) -> ()
|
|
// CHECK: end_borrow %1 : $Base
|
|
// CHECK: br bb3
|
|
// CHECK: bb3:
|
|
// CHECK: destroy_value %0 : $Base
|
|
// CHECK-LABEL: } // end sil function 'redundant_checked_cast_br_rauw_guaranteed_to_owned_success'
|
|
sil [ossa] @redundant_checked_cast_br_rauw_guaranteed_to_owned_success : $@convention(method) (@owned Base) -> () {
|
|
bb0(%0 : @owned $Base):
|
|
%borrow = begin_borrow %0 : $Base
|
|
%1 = class_method %0 : $Base, #Base.middle : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
|
|
checked_cast_br [exact] %borrow : $Base to Base, bb1, bb6
|
|
|
|
bb1(%5 : @guaranteed $Base):
|
|
%6 = copy_value %5 : $Base
|
|
end_borrow %borrow : $Base
|
|
%7 = class_method %0 : $Base, #Base.inner : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
|
|
checked_cast_br [exact] %6 : $Base to Base, bb2, bb4
|
|
|
|
bb2(%9 : @owned $Base):
|
|
%10 = function_ref @_TFC3ccb4Base5innerfS0_FT_T_ : $@convention(method) (@guaranteed Base) -> ()
|
|
%11 = apply %10(%9) : $@convention(method) (@guaranteed Base) -> ()
|
|
destroy_value %9 : $Base
|
|
br bb3
|
|
|
|
bb3:
|
|
%13 = tuple ()
|
|
br bb5(%13 : $())
|
|
|
|
bb4(%defaultBB2 : @owned $Base):
|
|
%15 = apply %7(%defaultBB2) : $@convention(method) (@guaranteed Base) -> ()
|
|
destroy_value %defaultBB2 : $Base
|
|
br bb3
|
|
|
|
bb5(%17 : $()):
|
|
br bb7
|
|
|
|
bb6(%defaultBB0 : @guaranteed $Base):
|
|
%19 = apply %1(%defaultBB0) : $@convention(method) (@guaranteed Base) -> ()
|
|
end_borrow %borrow : $Base
|
|
br bb7
|
|
|
|
bb7:
|
|
destroy_value %0 : $Base
|
|
%3 = tuple ()
|
|
return %3 : $()
|
|
}
|
|
|
|
// Replace an owned result with a guaranteed value on the merged path.
|
|
// Introduce a copy for the new lifetime.
|
|
// Simplify down to a single diamond.
|
|
//
|
|
// TODO: Simplify some of the obvisouly dead copy/destroy/borrows
|
|
// on-the-fly after each simplify-cfg. Possible using CanonicalizeOSSALifetime.
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @redundant_checked_cast_br_rauw_guaranteed_to_owned_merge : $@convention(method) (@owned Base) -> () {
|
|
// CHECK: bb0(%0 : @owned $Base):
|
|
// CHECK: [[BORROW:%.*]] = begin_borrow %0 : $Base
|
|
// CHECK: checked_cast_br [exact] [[BORROW]] : $Base to Base, bb1, bb3
|
|
// CHECK: bb1([[ARG1:%.*]] : @guaranteed $Base):
|
|
// CHECK: [[NEWCOPY1:%.*]] = copy_value [[ARG1]] : $Base
|
|
// CHECK: apply %{{.*}}([[ARG1]]) : $@convention(method) (@guaranteed Base) -> ()
|
|
// CHECK: [[OLDCOPY:%.*]] = copy_value [[BORROW]] : $Base
|
|
// CHECK: end_borrow [[BORROW]] : $Base
|
|
// CHECK: destroy_value [[OLDCOPY]] : $Base
|
|
// CHECK: apply %{{.*}}([[NEWCOPY1]]) : $@convention(method) (@guaranteed Base) -> ()
|
|
// CHECK: destroy_value [[NEWCOPY1]] : $Base
|
|
// CHECK: br bb2
|
|
// CHECK: bb2:
|
|
// CHECK: destroy_value %0 : $Base
|
|
// CHECK: return
|
|
// CHECK: bb3([[ARG3:%.*]] : @guaranteed $Base):
|
|
// CHECK: apply %1([[ARG3]]) : $@convention(method) (@guaranteed Base) -> ()
|
|
// CHECK: [[NEWCOPY3:%.*]] = copy_value [[BORROW]] : $Base
|
|
// CHECK: end_borrow [[BORROW]] : $Base
|
|
// CHECK: apply %{{.*}}([[NEWCOPY3]]) : $@convention(method) (@guaranteed Base) -> ()
|
|
// CHECK: destroy_value [[NEWCOPY3]] : $Base
|
|
// CHECK: br bb2
|
|
// CHECK-LABEL: } // end sil function 'redundant_checked_cast_br_rauw_guaranteed_to_owned_merge'
|
|
sil [ossa] @redundant_checked_cast_br_rauw_guaranteed_to_owned_merge : $@convention(method) (@owned Base) -> () {
|
|
bb0(%0 : @owned $Base):
|
|
%m = class_method %0 : $Base, #Base.inner : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
|
|
%f = function_ref @_TFC3ccb4Base5innerfS0_FT_T_ : $@convention(method) (@guaranteed Base) -> ()
|
|
%1 = begin_borrow %0 : $Base
|
|
checked_cast_br [exact] %1 : $Base to Base, bb1, bb2
|
|
|
|
bb1(%4 : @guaranteed $Base):
|
|
%5 = apply %f(%4) : $@convention(method) (@guaranteed Base) -> ()
|
|
br bb3
|
|
|
|
bb2(%7 : @guaranteed $Base):
|
|
%8 = apply %m(%7) : $@convention(method) (@guaranteed Base) -> ()
|
|
br bb3
|
|
|
|
bb3:
|
|
%11 = copy_value %1 : $Base
|
|
end_borrow %1 : $Base
|
|
checked_cast_br [exact] %11 : $Base to Base, bb4, bb5
|
|
|
|
bb4(%14 : @owned $Base):
|
|
%15 = apply %f(%14) : $@convention(method) (@guaranteed Base) -> ()
|
|
destroy_value %14 : $Base
|
|
br bb6
|
|
|
|
bb5(%20 : @owned $Base):
|
|
%21 = apply %m(%20) : $@convention(method) (@guaranteed Base) -> ()
|
|
destroy_value %20 : $Base
|
|
br bb6
|
|
|
|
bb6:
|
|
destroy_value %0 : $Base
|
|
%27 = tuple ()
|
|
return %27 : $()
|
|
}
|
|
|
|
// Replace an owned result with a guaranteed value on the merged path.
|
|
//
|
|
// The merged path has an unknown predecessor, where the merged value
|
|
// is produced by an apply.
|
|
//
|
|
// Clone the success path, introducing a copy on it.
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @redundant_checked_cast_br_rauw_guaranteed_to_owned_mergecopy : $@convention(method) (@owned Base) -> () {
|
|
// CHECK: bb0(%0 : @owned $Base):
|
|
// CHECK: [[BORROW:%.*]] = begin_borrow %0 : $Base
|
|
// CHECK: checked_cast_br [exact] [[BORROW]] : $Base to Base, bb1, bb2
|
|
// CHECK: bb1([[ARG1:%.*]] : @guaranteed $Base):
|
|
// CHECK: [[CP1:%.*]] = copy_value [[ARG1]] : $Base
|
|
// CHECK: apply %2([[ARG1]]) : $@convention(method) (@guaranteed Base) -> ()
|
|
// CHECK: end_borrow [[BORROW]] : $Base
|
|
// CHECK: br bb4([[CP1]] : $Base)
|
|
// CHECK: bb2([[ARG2:%.*]] : @guaranteed $Base):
|
|
// CHECK: apply %{{.*}}([[ARG2]]) : $@convention(method) (@guaranteed Base) -> ()
|
|
// CHECK: [[RESULT:%.*]] = apply %{{.*}}() : $@convention(thin) () -> @owned Base
|
|
// CHECK: end_borrow [[BORROW]] : $Base
|
|
// CHECK: checked_cast_br [exact] %13 : $Base to Base, bb3, bb5
|
|
// CHECK: bb3([[ARG3:%.*]] : @owned $Base):
|
|
// CHECK: br bb4(%16 : $Base)
|
|
// CHECK: bb4([[ARG4:%.*]] : @owned $Base):
|
|
// CHECK: apply %{{.*}}([[ARG4]]) : $@convention(method) (@guaranteed Base) -> ()
|
|
// CHECK: destroy_value [[ARG4]] : $Base
|
|
// CHECK: br bb6
|
|
// CHECK: bb5([[ARG5:%.*]] : @owned $Base):
|
|
// CHECK: apply %{{.*}}([[ARG5]]) : $@convention(method) (@guaranteed Base) -> ()
|
|
// CHECK: destroy_value [[ARG5]] : $Base
|
|
// CHECK: br bb6
|
|
// CHECK: bb6:
|
|
// CHECK: destroy_value %0 : $Base
|
|
// CHECK: return
|
|
// CHECK-LABEL: } // end sil function 'redundant_checked_cast_br_rauw_guaranteed_to_owned_mergecopy'
|
|
sil [ossa] @redundant_checked_cast_br_rauw_guaranteed_to_owned_mergecopy : $@convention(method) (@owned Base) -> () {
|
|
bb0(%0 : @owned $Base):
|
|
%1 = class_method %0 : $Base, #Base.inner : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
|
|
|
|
%2 = function_ref @_TFC3ccb4Base5innerfS0_FT_T_ : $@convention(method) (@guaranteed Base) -> ()
|
|
%f = function_ref @get_base : $@convention(thin) () -> @owned Base
|
|
%3 = begin_borrow %0 : $Base
|
|
checked_cast_br [exact] %3 : $Base to Base, bb1, bb2
|
|
|
|
bb1(%6 : @guaranteed $Base):
|
|
%7 = apply %2(%6) : $@convention(method) (@guaranteed Base) -> ()
|
|
%8 = copy_value %6 : $Base
|
|
br bb3(%8 : $Base)
|
|
|
|
bb2(%9 : @guaranteed $Base):
|
|
%10 = apply %1(%9) : $@convention(method) (@guaranteed Base) -> ()
|
|
%11 = apply %f() : $@convention(thin) () -> @owned Base
|
|
br bb3(%11 : $Base)
|
|
|
|
bb3(%12 : @owned $Base):
|
|
end_borrow %3 : $Base
|
|
checked_cast_br [exact] %12 : $Base to Base, bb6, bb7
|
|
|
|
bb6(%16 : @owned $Base):
|
|
%17 = apply %2(%16) : $@convention(method) (@guaranteed Base) -> ()
|
|
destroy_value %16 : $Base
|
|
br bb8
|
|
|
|
bb7(%20 : @owned $Base):
|
|
%21 = apply %1(%20) : $@convention(method) (@guaranteed Base) -> ()
|
|
destroy_value %20 : $Base
|
|
br bb8
|
|
|
|
bb8:
|
|
destroy_value %0 : $Base
|
|
%25 = tuple ()
|
|
return %25 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @redundant_checked_cast_failure_path :
|
|
// CHECK: checked_cast_br
|
|
// CHECK-NOT: checked_cast_br
|
|
// CHECK-LABEL: } // end sil function 'redundant_checked_cast_failure_path'
|
|
sil [ossa] @redundant_checked_cast_failure_path : $@convention(method) (@owned Base) -> () {
|
|
bb0(%0 : @owned $Base):
|
|
%borrow = begin_borrow %0 : $Base
|
|
checked_cast_br %borrow : $Base to Derived, bb1, bb2
|
|
|
|
bb1(%succ1 : @guaranteed $Derived):
|
|
br bbexit
|
|
|
|
bb2(%5 : @guaranteed $Base):
|
|
checked_cast_br %5 : $Base to Derived, bb3, bb4
|
|
|
|
bb3(%9 : @guaranteed $Derived):
|
|
br bbexit
|
|
|
|
bb4(%10 : @guaranteed $Base):
|
|
%m = class_method %10 : $Base, #Base.inner : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
|
|
apply %m(%10) : $@convention(method) (@guaranteed Base) -> ()
|
|
br bbexit
|
|
|
|
bbexit:
|
|
end_borrow %borrow : $Base
|
|
destroy_value %0 : $Base
|
|
%t = tuple ()
|
|
return %t : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @redundant_checked_cast_failure_path_not_idom :
|
|
// CHECK: checked_cast_br
|
|
// CHECK-NOT: checked_cast_br
|
|
// CHECK-LABEL: } // end sil function 'redundant_checked_cast_failure_path_not_idom'
|
|
sil [ossa] @redundant_checked_cast_failure_path_not_idom : $@convention(method) (@owned Base) -> () {
|
|
bb0(%0 : @owned $Base):
|
|
%borrow = begin_borrow %0 : $Base
|
|
checked_cast_br %borrow : $Base to Derived, bb1, bb2
|
|
|
|
bb1(%succ1 : @guaranteed $Derived):
|
|
br bbexit
|
|
|
|
bb2(%5 : @guaranteed $Base):
|
|
br bb3(%5 : $Base)
|
|
|
|
bb3(%6 : @guaranteed $Base):
|
|
checked_cast_br %6 : $Base to Derived, bb4, bb5
|
|
|
|
bb4(%9 : @guaranteed $Derived):
|
|
br bbexit
|
|
|
|
bb5(%10 : @guaranteed $Base):
|
|
%m = class_method %10 : $Base, #Base.inner : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
|
|
apply %m(%10) : $@convention(method) (@guaranteed Base) -> ()
|
|
br bbexit
|
|
|
|
bbexit:
|
|
end_borrow %borrow : $Base
|
|
destroy_value %0 : $Base
|
|
%t = tuple ()
|
|
return %t : $()
|
|
}
|
|
|
|
//!!!TODO: test replacing a guaranteed value with an ownedvalue
|
|
// test borrowOverValue->borrowCopyOverScope path
|
|
|
|
//!!!TODO: test prepareUnowned paths
|