mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Handling address_to_pointer as a plain inout missed some mutations and lead to miscompiles. We now treat address_to_pointer as escaping address. Fixes SR-3554
419 lines
12 KiB
Plaintext
419 lines
12 KiB
Plaintext
// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all %s -predictable-memopt | %FileCheck %s
|
|
|
|
import Builtin
|
|
import Swift
|
|
|
|
|
|
// CHECK-LABEL: sil @simple_reg_promotion
|
|
sil @simple_reg_promotion : $@convention(thin) (Int) -> Int {
|
|
bb0(%0 : $Int): // CHECK: bb0(%0 : $Int):
|
|
%1 = alloc_box $<τ_0_0> { var τ_0_0 } <Int>
|
|
%1a = project_box %1 : $<τ_0_0> { var τ_0_0 } <Int>, 0
|
|
store %0 to %1a : $*Int
|
|
%3 = alloc_box $<τ_0_0> { var τ_0_0 } <Int>
|
|
%3a = project_box %3 : $<τ_0_0> { var τ_0_0 } <Int>, 0
|
|
%4 = load %1a : $*Int
|
|
store %4 to %3a : $*Int
|
|
%6 = load %3a : $*Int
|
|
strong_release %3 : $<τ_0_0> { var τ_0_0 } <Int>
|
|
strong_release %1 : $<τ_0_0> { var τ_0_0 } <Int>
|
|
return %6 : $Int
|
|
|
|
// CHECK-NEXT: return %0 : $Int
|
|
}
|
|
|
|
|
|
// CHECK-LABEL: sil @tuple_reg_promotion
|
|
sil @tuple_reg_promotion : $@convention(thin) (Int) -> Int {
|
|
bb0(%0 : $Int): // CHECK: bb0(%0 : $Int):
|
|
%1 = alloc_box $<τ_0_0> { var τ_0_0 } <(Int, Int)>
|
|
%1a = project_box %1 : $<τ_0_0> { var τ_0_0 } <(Int, Int)>, 0
|
|
|
|
%a = tuple_element_addr %1a : $*(Int, Int), 0
|
|
%b = tuple_element_addr %1a : $*(Int, Int), 1
|
|
store %0 to %a : $*Int
|
|
store %0 to %b : $*Int
|
|
|
|
%c = load %1a : $*(Int, Int)
|
|
%d = tuple_extract %c : $(Int, Int), 0
|
|
|
|
strong_release %1 : $<τ_0_0> { var τ_0_0 } <(Int, Int)>
|
|
|
|
return %d : $Int
|
|
|
|
// Verify that promotion has promoted the tuple load away, and we know that
|
|
// %0 is being returned through scalar instructions in SSA form.
|
|
// CHECK-NEXT: [[TUPLE:%[0-9]+]] = tuple ({{.*}} : $Int, {{.*}} : $Int)
|
|
// CHECK-NEXT: [[TUPLE_ELT:%[0-9]+]] = tuple_extract [[TUPLE]] : $(Int, Int), 0
|
|
|
|
// CHECK-NEXT: return [[TUPLE_ELT]] : $Int
|
|
}
|
|
|
|
|
|
sil @takes_Int_inout : $@convention(thin) (@inout Int) -> ()
|
|
|
|
|
|
|
|
// Verify that load promotion works properly with inout arguments.
|
|
//
|
|
// func used_by_inout(a : Int) -> (Int, Int) {
|
|
// var t = a
|
|
// takes_Int_inout(&a)
|
|
// return (t, a)
|
|
//}
|
|
// CHECK-LABEL: sil @used_by_inout
|
|
sil @used_by_inout : $@convention(thin) (Int) -> (Int, Int) {
|
|
bb0(%0 : $Int):
|
|
// This alloc_stack can't be removed since it is used by an inout call.
|
|
// CHECK: %1 = alloc_box $<τ_0_0> { var τ_0_0 } <Int>
|
|
%1 = alloc_box $<τ_0_0> { var τ_0_0 } <Int>
|
|
%1a = project_box %1 : $<τ_0_0> { var τ_0_0 } <Int>, 0
|
|
%2 = store %0 to %1a : $*Int
|
|
|
|
// This load should be eliminated.
|
|
%3 = load %1a : $*Int
|
|
%5 = function_ref @takes_Int_inout : $@convention(thin) (@inout Int) -> ()
|
|
%6 = apply %5(%1a) : $@convention(thin) (@inout Int) -> ()
|
|
|
|
// This load is needed in case the callee modifies the allocation.
|
|
// CHECK: [[RES:%[0-9]+]] = load
|
|
%7 = load %1a : $*Int
|
|
|
|
// This should use the incoming argument to the function.
|
|
// CHECK: tuple ({{.*}} : $Int, {{.*}} : $Int)
|
|
%8 = tuple (%3 : $Int, %7 : $Int)
|
|
strong_release %1 : $<τ_0_0> { var τ_0_0 } <Int>
|
|
%11 = return %8 : $(Int, Int)
|
|
}
|
|
|
|
|
|
struct AddressOnlyStruct {
|
|
var a : Any
|
|
var b : Int
|
|
}
|
|
|
|
/// returns_generic_struct - This returns a struct by reference.
|
|
sil @returns_generic_struct : $@convention(thin) () -> @out AddressOnlyStruct
|
|
|
|
|
|
|
|
sil @takes_closure : $@convention(thin) (@callee_owned () -> ()) -> ()
|
|
sil @closure0 : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int>) -> ()
|
|
|
|
|
|
// CHECK-LABEL: sil @closure_test2
|
|
sil @closure_test2 : $@convention(thin) (Int) -> Int {
|
|
bb0(%1 : $Int):
|
|
%0 = alloc_box $<τ_0_0> { var τ_0_0 } <Int>
|
|
%0a = project_box %0 : $<τ_0_0> { var τ_0_0 } <Int>, 0
|
|
store %1 to %0a : $*Int // CHECK: store
|
|
|
|
%5 = function_ref @takes_closure : $@convention(thin) (@callee_owned () -> ()) -> ()
|
|
%6 = function_ref @closure0 : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int>) -> ()
|
|
strong_retain %0 : $<τ_0_0> { var τ_0_0 } <Int>
|
|
%8 = partial_apply %6(%0) : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int>) -> ()
|
|
%9 = apply %5(%8) : $@convention(thin) (@callee_owned () -> ()) -> ()
|
|
strong_release %0 : $<τ_0_0> { var τ_0_0 } <Int>
|
|
|
|
store %1 to %0a : $*Int // CHECK: store
|
|
|
|
// In an escape region, we should not promote loads.
|
|
%r = load %0a : $*Int // CHECK: load
|
|
return %r : $Int
|
|
}
|
|
|
|
|
|
|
|
class SomeClass {}
|
|
|
|
sil @getSomeClass : $@convention(thin) () -> @owned SomeClass
|
|
|
|
|
|
// CHECK-LABEL: sil @assign_test_trivial
|
|
sil @assign_test_trivial : $@convention(thin) (Int) -> Int {
|
|
bb0(%0 : $Int):
|
|
%1 = alloc_box $<τ_0_0> { var τ_0_0 } <Int>
|
|
%1a = project_box %1 : $<τ_0_0> { var τ_0_0 } <Int>, 0
|
|
|
|
store %0 to %1a : $*Int
|
|
store %0 to %1a : $*Int
|
|
store %0 to %1a : $*Int
|
|
|
|
%2 = load %1a : $*Int
|
|
strong_release %1 : $<τ_0_0> { var τ_0_0 } <Int>
|
|
|
|
// Verify that the load got forwarded from an assign.
|
|
return %2 : $Int // CHECK: return %0 : $Int
|
|
}
|
|
|
|
|
|
struct ContainsNativeObject {
|
|
var x : Int
|
|
var y : Builtin.NativeObject
|
|
}
|
|
|
|
var int_global : Int
|
|
|
|
|
|
// CHECK-LABEL: sil @promote_alloc_stack
|
|
sil @promote_alloc_stack : $@convention(thin) (Int32) -> Builtin.Int32 {
|
|
bb0(%0 : $Int32):
|
|
%5 = integer_literal $Builtin.Int32, 1
|
|
// CHECK: [[IL:%[0-9]+]] = integer_literal
|
|
|
|
%18 = struct $Int32 (%5 : $Builtin.Int32)
|
|
%22 = alloc_stack $Int32
|
|
|
|
// CHECK-NOT: alloc_stack
|
|
|
|
store %18 to %22 : $*Int32
|
|
%24 = struct_element_addr %22 : $*Int32, #Int32._value
|
|
%25 = load %24 : $*Builtin.Int32
|
|
dealloc_stack %22 : $*Int32
|
|
// CHECK-NEXT: return [[IL]]
|
|
return %25 : $Builtin.Int32
|
|
}
|
|
|
|
// CHECK-LABEL: sil @copy_addr_to_load
|
|
sil @copy_addr_to_load : $@convention(thin) (Int) -> Int {
|
|
bb0(%0 : $Int): // CHECK: bb0(%0 : $Int):
|
|
%1 = alloc_stack $Int
|
|
store %0 to %1 : $*Int
|
|
%2 = alloc_stack $Int
|
|
|
|
copy_addr %1 to [initialization] %2 : $*Int
|
|
|
|
%3 = load %2 : $*Int
|
|
|
|
dealloc_stack %2 : $*Int
|
|
dealloc_stack %1 : $*Int
|
|
// CHECK-NEXT: return %0
|
|
return %3 : $Int
|
|
}
|
|
|
|
// rdar://15170149
|
|
// CHECK-LABEL: sil @store_to_copyaddr
|
|
sil @store_to_copyaddr : $(Bool) -> Bool {
|
|
bb0(%0 : $Bool): // CHECK: bb0(%0 :
|
|
%1 = alloc_stack $Bool
|
|
store %0 to %1 : $*Bool
|
|
%3 = alloc_stack $Bool
|
|
copy_addr %1 to [initialization] %3 : $*Bool
|
|
%5 = load %3 : $*Bool
|
|
copy_addr %3 to %1 : $*Bool
|
|
%12 = load %1 : $*Bool
|
|
dealloc_stack %3 : $*Bool
|
|
dealloc_stack %1 : $*Bool
|
|
return %12 : $Bool // CHECK-NEXT: return %0
|
|
}
|
|
|
|
// CHECK-LABEL: sil @cross_block_load_promotion
|
|
sil @cross_block_load_promotion : $@convention(thin) (Int) -> Int {
|
|
bb0(%0 : $Int):
|
|
%1 = alloc_stack $Int
|
|
store %0 to %1 : $*Int
|
|
%11 = integer_literal $Builtin.Int1, 1
|
|
cond_br %11, bb1, bb2
|
|
|
|
bb1:
|
|
br bb5
|
|
|
|
bb2:
|
|
br bb5
|
|
|
|
bb5:
|
|
%15 = load %1 : $*Int
|
|
dealloc_stack %1 : $*Int
|
|
return %15 : $Int
|
|
|
|
// CHECK: return %0 : $Int
|
|
}
|
|
|
|
struct XYStruct { var x, y : Int }
|
|
sil @init_xy_struct : $@convention(thin) () -> XYStruct
|
|
|
|
|
|
// CHECK-LABEL: sil @cross_block_load_promotion_struct
|
|
sil @cross_block_load_promotion_struct : $@convention(thin) (Int, Int) -> Int {
|
|
bb0(%0 : $Int, %2 : $Int):
|
|
%1 = alloc_stack $XYStruct
|
|
|
|
%7 = function_ref @init_xy_struct : $@convention(thin) () -> XYStruct
|
|
%9 = apply %7() : $@convention(thin) () -> XYStruct
|
|
store %9 to %1 : $*XYStruct
|
|
|
|
%11 = struct_element_addr %1 : $*XYStruct, #XYStruct.y
|
|
store %0 to %11 : $*Int
|
|
|
|
%12 = integer_literal $Builtin.Int1, 1 // user: %3
|
|
cond_br %12, bb1, bb2
|
|
|
|
bb1: // Preds: bb3
|
|
%13 = struct_element_addr %1 : $*XYStruct, #XYStruct.x
|
|
store %2 to %13 : $*Int
|
|
br bb5
|
|
|
|
bb2: // Preds: bb0
|
|
br bb5
|
|
|
|
bb5: // Preds: bb4
|
|
%15 = load %11 : $*Int
|
|
dealloc_stack %1 : $*XYStruct
|
|
return %15 : $Int
|
|
|
|
// CHECK: return %0 : $Int
|
|
}
|
|
|
|
// CHECK-LABEL: sil @cross_block_load_promotion_struct2
|
|
sil @cross_block_load_promotion_struct2 : $@convention(thin) (Int, Int) -> Int {
|
|
bb0(%0 : $Int, %2 : $Int):
|
|
%1 = alloc_stack $XYStruct
|
|
|
|
%7 = function_ref @init_xy_struct : $@convention(thin) () -> XYStruct
|
|
%9 = apply %7() : $@convention(thin) () -> XYStruct
|
|
store %9 to %1 : $*XYStruct
|
|
|
|
%11 = struct_element_addr %1 : $*XYStruct, #XYStruct.x
|
|
store %0 to %11 : $*Int
|
|
|
|
%12 = integer_literal $Builtin.Int1, 1 // user: %3
|
|
cond_br %12, bb1, bb2
|
|
|
|
bb1: // Preds: bb3
|
|
%13 = struct_element_addr %1 : $*XYStruct, #XYStruct.x
|
|
store %0 to %13 : $*Int
|
|
br bb5
|
|
|
|
bb2: // Preds: bb0
|
|
br bb5
|
|
|
|
bb5: // Preds: bb4
|
|
%15 = load %11 : $*Int
|
|
dealloc_stack %1 : $*XYStruct
|
|
return %15 : $Int
|
|
|
|
// CHECK: return %0 : $Int
|
|
}
|
|
|
|
|
|
// CHECK-LABEL: sil @destroy_addr
|
|
sil @destroy_addr : $@convention(method) (@owned SomeClass) -> @owned SomeClass {
|
|
bb0(%0 : $SomeClass):
|
|
%1 = alloc_stack $SomeClass
|
|
%2 = tuple ()
|
|
store %0 to %1 : $*SomeClass
|
|
%7 = load %1 : $*SomeClass
|
|
strong_retain %7 : $SomeClass
|
|
strong_release %7 : $SomeClass
|
|
%12 = load %1 : $*SomeClass // users: %16, %13
|
|
strong_retain %12 : $SomeClass // id: %13
|
|
destroy_addr %1 : $*SomeClass // id: %14
|
|
dealloc_stack %1 : $*SomeClass // id: %15
|
|
return %12 : $SomeClass // id: %16
|
|
}
|
|
|
|
|
|
protocol P {}
|
|
class C : P {}
|
|
|
|
sil @use : $@convention(thin) (@in P) -> ()
|
|
|
|
// rdar://15492647
|
|
// CHECK-LABEL: sil @destroy_addr_removed
|
|
sil @destroy_addr_removed : $@convention(thin) () -> () {
|
|
bb0:
|
|
%3 = alloc_stack $SomeClass
|
|
%f = function_ref @getSomeClass : $@convention(thin) () -> @owned SomeClass
|
|
%9 = apply %f() : $@convention(thin) () -> @owned SomeClass
|
|
// CHECK: [[CVAL:%[0-9]+]] = apply
|
|
|
|
assign %9 to %3 : $*SomeClass
|
|
destroy_addr %3 : $*SomeClass
|
|
dealloc_stack %3 : $*SomeClass
|
|
%15 = tuple ()
|
|
return %15 : $()
|
|
// CHECK-NEXT: strong_release [[CVAL]]
|
|
}
|
|
|
|
// <rdar://problem/17755462> Predictable memory opts removes refcount operation
|
|
// CHECK-LABEL: sil @dead_allocation_1
|
|
sil @dead_allocation_1 : $@convention(thin) (Optional<AnyObject>) -> () {
|
|
bb0(%0 : $Optional<AnyObject>):
|
|
// CHECK: retain_value %0
|
|
%1 = alloc_stack $Optional<AnyObject>
|
|
%2 = alloc_stack $Optional<AnyObject>
|
|
store %0 to %2 : $*Optional<AnyObject>
|
|
// CHECK-NOT: copy_addr
|
|
copy_addr %2 to [initialization] %1 : $*Optional<AnyObject>
|
|
dealloc_stack %2 : $*Optional<AnyObject>
|
|
dealloc_stack %1 : $*Optional<AnyObject>
|
|
%3 = tuple ()
|
|
return %3 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @dead_allocation_2
|
|
sil @dead_allocation_2 : $@convention(thin) (Optional<AnyObject>) -> () {
|
|
bb0(%0 : $Optional<AnyObject>):
|
|
// CHECK: retain_value %0
|
|
// CHECK-NOT: alloc_stack
|
|
%1 = alloc_stack $Optional<AnyObject>
|
|
%2 = alloc_stack $Optional<AnyObject>
|
|
store %0 to %1 : $*Optional<AnyObject>
|
|
// CHECK-NOT: copy_addr
|
|
copy_addr %1 to [initialization] %2 : $*Optional<AnyObject>
|
|
dealloc_stack %2 : $*Optional<AnyObject>
|
|
dealloc_stack %1 : $*Optional<AnyObject>
|
|
%3 = tuple ()
|
|
return %3 : $()
|
|
}
|
|
|
|
enum IndirectCase {
|
|
indirect case X(Int)
|
|
}
|
|
|
|
// CHECK-LABEL: sil @indirect_enum_box
|
|
sil @indirect_enum_box : $@convention(thin) (Int) -> IndirectCase {
|
|
// CHECK: bb0([[X:%.*]] : $Int):
|
|
entry(%x : $Int):
|
|
// CHECK: [[BOX:%.*]] = alloc_box $<τ_0_0> { var τ_0_0 } <Int>
|
|
%b = alloc_box $<τ_0_0> { var τ_0_0 } <Int>
|
|
// CHECK: [[PB:%.*]] = project_box [[BOX]]
|
|
%ba = project_box %b : $<τ_0_0> { var τ_0_0 } <Int>, 0
|
|
// CHECK: store [[X]] to [[PB]]
|
|
store %x to %ba : $*Int
|
|
// CHECK: [[E:%.*]] = enum $IndirectCase, #IndirectCase.X!enumelt.1, [[BOX]] : $<τ_0_0> { var τ_0_0 } <Int>
|
|
%e = enum $IndirectCase, #IndirectCase.X!enumelt.1, %b : $<τ_0_0> { var τ_0_0 } <Int>
|
|
// CHECK: return [[E]]
|
|
return %e : $IndirectCase
|
|
}
|
|
|
|
sil @write_to_bool : $@convention(c) (UnsafeMutablePointer<Bool>) -> Int32
|
|
|
|
// CHECK-LABEL: sil @escaping_address
|
|
sil @escaping_address : $@convention(thin) () -> Bool {
|
|
bb0:
|
|
// CHECK: [[A:%[0-9]+]] = alloc_stack
|
|
%a = alloc_stack $Bool
|
|
%f = function_ref @write_to_bool : $@convention(c) (UnsafeMutablePointer<Bool>) -> Int32
|
|
%a2p = address_to_pointer %a : $*Bool to $Builtin.RawPointer
|
|
%ump = struct $UnsafeMutablePointer<Bool> (%a2p : $Builtin.RawPointer)
|
|
|
|
%0 = integer_literal $Builtin.Int1, 0
|
|
%b0 = struct $Bool (%0 : $Builtin.Int1)
|
|
// CHECK: [[BV:%[0-9]+]] = struct_element_addr [[A]]
|
|
%bv = struct_element_addr %a : $*Bool, #Bool._value
|
|
store %b0 to %a : $*Bool
|
|
|
|
// CHECK: apply
|
|
%ap = apply %f(%ump) : $@convention(c) (UnsafeMutablePointer<Bool>) -> Int32
|
|
|
|
// CHECK: [[L:%[0-9]+]] = load [[BV]]
|
|
%l = load %bv : $*Builtin.Int1
|
|
// CHECK: [[R:%[0-9]+]] = struct $Bool ([[L]]
|
|
%r = struct $Bool (%l : $Builtin.Int1)
|
|
dealloc_stack %a : $*Bool
|
|
// CHECK: return [[R]]
|
|
return %r : $Bool
|
|
}
|
|
|