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