mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
I also removed the -verify-sil-ownership flag in favor of a disable flag -disable-sil-ownership-verifier. I used this on only two tests that still need work to get them to pass with ownership, but whose problems are well understood, small corner cases. I am going to fix them in follow on commits. I detail them below: 1. SILOptimizer/definite_init_inout_super_init.swift. This is a test case where DI is supposed to error. The only problem is that we crash before we error since the code emitting by SILGen to trigger this error does not pass ownership invariants. I have spoken with JoeG about this and he suggested that I fix this earlier in the compiler. Since we do not run the ownership verifier without asserts enabled, this should not affect compiler users. Given that it has triggered DI errors previously I think it is safe to disable ownership here. 2. PrintAsObjC/extensions.swift. In this case, the signature generated by type lowering for one of the thunks here uses an unsafe +0 return value instead of doing an autorelease return. The ownership checker rightly flags this leak. This is going to require either an AST level change or a change to TypeLowering. I think it is safe to turn this off since it is such a corner case that it was found by a test that has nothing to do with it. rdar://43398898
205 lines
7.1 KiB
Plaintext
205 lines
7.1 KiB
Plaintext
// RUN: %target-sil-opt -copy-propagation -enable-sil-opaque-values -enable-sil-verify-all %s | %FileCheck %s
|
|
|
|
sil_stage canonical
|
|
|
|
import Builtin
|
|
import Swift
|
|
|
|
// Once Mem2Reg supports ownership, it will leave behind extra copies as
|
|
// seen in the SIL test below for simple assignment:
|
|
// public func testVarAssign<T>(_ t: T) -> T {
|
|
// var u = t
|
|
// return u
|
|
// }
|
|
// CopyPropagation should leave behind a single copy and no destroys.
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @testVarAssign : $@convention(thin) <T> (@in_guaranteed T) -> @out T {
|
|
// CHECK: bb0(%0 : @guaranteed $T):
|
|
// CHECK-NOT: destroy
|
|
// CHECK: [[CPY:%.*]] = copy_value %0 : $T
|
|
// CHECK-NOT: destroy
|
|
// CHECK: return [[CPY]] : $T
|
|
// CHECK-LABEL: } // end sil function 'testVarAssign'
|
|
sil [ossa] @testVarAssign : $@convention(thin) <T> (@in_guaranteed T) -> @out T {
|
|
bb0(%0 : @guaranteed $T):
|
|
%1 = copy_value %0 : $T
|
|
%2 = copy_value %1 : $T
|
|
destroy_value %1 : $T
|
|
return %2 : $T
|
|
}
|
|
|
|
// CHECK: sil [ossa] @multiReturnValue : $@convention(thin) <T> (@in_guaranteed T) -> (@out T, @out T) {
|
|
// CHECK: bb0(%0 : @guaranteed $T):
|
|
// CHECK-NOT: destroy
|
|
// CHECK: [[CPY1:%.*]] = copy_value %0 : $T
|
|
// CHECK-NOT: destroy
|
|
// CHECK: [[CPY2:%.*]] = copy_value %0 : $T
|
|
// CHECK-NOT: destroy
|
|
// CHECK: [[R:%.*]] = tuple ([[CPY1]] : $T, [[CPY2]] : $T)
|
|
// CHECK-NOT: destroy
|
|
// CHECK: return [[R]] : $(T, T)
|
|
// CHECK-LABEL: } // end sil function 'multiReturnValue'
|
|
sil [ossa] @multiReturnValue : $@convention(thin) <T> (@in_guaranteed T) -> (@out T, @out T) {
|
|
bb0(%0 : @guaranteed $T):
|
|
%1 = copy_value %0 : $T
|
|
%2 = copy_value %1 : $T
|
|
%3 = copy_value %1 : $T
|
|
%4 = tuple (%2 : $T, %3 : $T)
|
|
destroy_value %1 : $T
|
|
return %4 : $(T, T)
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @multiCallResult : $@convention(thin) <T> (@in_guaranteed T) -> @out T {
|
|
// CHECK: bb0(%0 : @guaranteed $T):
|
|
// CHECK-NEXT: // function_ref multiReturnValue
|
|
// CHECK-NEXT: [[F:%.*]] = function_ref @multiReturnValue : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> (@out τ_0_0, @out τ_0_0)
|
|
// CHECK-NEXT: [[CALL:%.*]] = apply [[F]]<T>(%0) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> (@out τ_0_0, @out τ_0_0)
|
|
// CHECK-NEXT: ([[D1:%.*]], [[D2:%.*]]) = destructure_tuple [[CALL]] : $(T, T)
|
|
// CHECK-NEXT: destroy_value [[D2]] : $T
|
|
// CHECK-NEXT: return [[D1]] : $T
|
|
// CHECK-LABEL: } // end sil function 'multiCallResult'
|
|
sil [ossa] @multiCallResult : $@convention(thin) <T> (@in_guaranteed T) -> @out T {
|
|
bb0(%0 : @guaranteed $T):
|
|
%1 = copy_value %0 : $T
|
|
%2 = function_ref @multiReturnValue : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> (@out τ_0_0, @out τ_0_0)
|
|
%3 = apply %2<T>(%1) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> (@out τ_0_0, @out τ_0_0)
|
|
(%4, %5) = destructure_tuple %3 : $(T, T)
|
|
%6 = copy_value %4 : $T
|
|
%7 = copy_value %5 : $T
|
|
destroy_value %1 : $T
|
|
destroy_value %4 : $T
|
|
destroy_value %5 : $T
|
|
destroy_value %7 : $T
|
|
return %6 : $T
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @testPhi : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T, Bool) -> @out T {
|
|
// CHECK: bb0(%0 : @guaranteed $T, %1 : @guaranteed $T, %2 : $Bool):
|
|
// CHECK-NEXT: %3 = struct_extract %2 : $Bool, #Bool._value
|
|
// CHECK-NEXT: cond_br %3, bb1, bb2
|
|
//
|
|
// CHECK: bb1:
|
|
// CHECK-NEXT: %5 = copy_value %0 : $T
|
|
// CHECK-NEXT: br bb3(%5 : $T)
|
|
//
|
|
// CHECK: bb2:
|
|
// CHECK-NEXT: %7 = copy_value %1 : $T
|
|
// CHECK-NEXT: br bb3(%7 : $T)
|
|
//
|
|
// CHECK: bb3(%9 : @owned $T):
|
|
// CHECK-NEXT: return %9 : $T
|
|
// CHECK-LABEL: } // end sil function 'testPhi'
|
|
sil [ossa] @testPhi : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T, Bool) -> @out T {
|
|
bb0(%0 : @guaranteed $T, %1 : @guaranteed $T, %2 : $Bool):
|
|
%3 = copy_value %0 : $T
|
|
%4 = copy_value %1 : $T
|
|
%5 = struct_extract %2 : $Bool, #Bool._value
|
|
cond_br %5, bb1, bb2
|
|
|
|
bb1:
|
|
%7 = copy_value %3 : $T
|
|
br bb3(%7 : $T)
|
|
|
|
bb2:
|
|
%9 = copy_value %4 : $T
|
|
br bb3(%9 : $T)
|
|
|
|
bb3(%11 : @owned $T):
|
|
destroy_value %4 : $T
|
|
destroy_value %3 : $T
|
|
return %11 : $T
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @testConsume : $@convention(thin) <T> (@in T, @inout T) -> () {
|
|
// CHECK: bb0(%0 : @owned $T, %1 : $*T):
|
|
//
|
|
// The original copy_value is deleted.
|
|
// CHECK-NEXT: debug_value %0 : $T
|
|
//
|
|
// A new copy_value is inserted before the consuming store.
|
|
// CHECK-NEXT: %3 = copy_value %0 : $T
|
|
// CHECK-NEXT: store %3 to [init] %1 : $*T
|
|
//
|
|
// The non-consuming use now uses the original value.
|
|
// CHECK-NEXT: debug_value %0 : $T
|
|
//
|
|
// A new destroy is inserted after the last use.
|
|
// CHECK-NEXT: destroy_value %0 : $T
|
|
// CHECK-NEXT: debug_value_addr %1 : $*T
|
|
//
|
|
// The original destroy is deleted.
|
|
// CHECK-NEXT: %8 = tuple ()
|
|
// CHECK-NEXT: return %8 : $()
|
|
// CHECK-LABEL: // end sil function 'testConsume'
|
|
sil [ossa] @testConsume : $@convention(thin) <T> (@in T, @inout T) -> () {
|
|
bb0(%arg : @owned $T, %addr : $*T):
|
|
%copy = copy_value %arg : $T
|
|
debug_value %copy : $T
|
|
store %copy to [init] %addr : $*T
|
|
debug_value %arg : $T
|
|
debug_value_addr %addr : $*T
|
|
destroy_value %arg : $T
|
|
%v = tuple ()
|
|
return %v : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @testDestroyEdge : $@convention(thin) <T> (@in T, @inout T, Builtin.Int1) -> () {
|
|
// CHECK: bb0(%0 : @owned $T, %1 : $*T, %2 : $Builtin.Int1):
|
|
// CHECK-NEXT: cond_br %2, bb2, bb1
|
|
//
|
|
// CHECK: bb1:
|
|
//
|
|
// The critical edge is split.
|
|
// CHECK-NEXT: destroy_value %0 : $T
|
|
// CHECK-NEXT: br bb3
|
|
//
|
|
// CHECK: bb2:
|
|
// The original copy is deleted.
|
|
// CHECK-NEXT: debug_value %0 : $T
|
|
// CHECK-NEXT: destroy_value %0 : $T
|
|
// CHECK-NEXT: br bb3
|
|
//
|
|
// CHECK: bb3:
|
|
// The original destroy is deleted.
|
|
// CHECK-NEXT: %9 = tuple ()
|
|
// CHECK-NEXT: return %9 : $()
|
|
// CHECK-LABEL: } // end sil function 'testDestroyEdge'
|
|
sil [ossa] @testDestroyEdge : $@convention(thin) <T> (@in T, @inout T, Builtin.Int1) -> () {
|
|
bb0(%arg : @owned $T, %addr : $*T, %z : $Builtin.Int1):
|
|
cond_br %z, bb1, bb2
|
|
|
|
bb1:
|
|
debug_value %arg : $T
|
|
%copy = copy_value %arg : $T
|
|
destroy_value %copy : $T
|
|
br bb2
|
|
|
|
bb2:
|
|
destroy_value %arg : $T
|
|
%10 = tuple ()
|
|
return %10 : $()
|
|
}
|
|
|
|
sil [ossa] @takeGuaranteedAndOwnedArg : $@convention(thin) <T> (@in_guaranteed T, @in T) -> ()
|
|
|
|
// Test the same user instruction with both @guaranteed and @owned operands taking the same copied value.
|
|
// We need to keep the value alive to the end of the instruction.
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @testGuaranteedAndOwnedArg : $@convention(thin) <T> (@in T) -> () {
|
|
// CHECK: bb0(%0 : @owned $T):
|
|
// CHECK-NEXT: // function_ref takeGuaranteedAndOwnedArg
|
|
// CHECK-NEXT: function_ref @takeGuaranteedAndOwnedArg : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0, @in τ_0_0) -> ()
|
|
// CHECK-NEXT: [[CPY:%.*]] = copy_value %0 : $T
|
|
// CHECK-NEXT: apply %{{.*}}<T>(%0, [[CPY]]) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0, @in τ_0_0) -> ()
|
|
// CHECK-NEXT: destroy_value %0 : $T
|
|
// CHECK-NEXT: return %{{.*}} : $()
|
|
// CHECK-LABEL: } // end sil function 'testGuaranteedAndOwnedArg'
|
|
sil [ossa] @testGuaranteedAndOwnedArg : $@convention(thin) <T> (@in T) -> () {
|
|
bb(%0 : @owned $T):
|
|
%copy = copy_value %0 : $T
|
|
%f = function_ref @takeGuaranteedAndOwnedArg : $@convention(thin) <T> (@in_guaranteed T, @in T) -> ()
|
|
%call = apply %f<T>(%0, %copy) : $@convention(thin) <T> (@in_guaranteed T, @in T) -> ()
|
|
destroy_value %0 : $T
|
|
return %call : $()
|
|
}
|