Files
swift-mirror/test/IRGen/loadable_by_address_borrow.sil
Joe Groff 6a258bf835 LoadableByAddress: Looser placement of stack allocations for local borrowees.
The previous implementation did not work in all CFG configurations, and would
take some effort to do so. We don't have an immediate need to support OSSA
form with this pass, so we don't need to precisely place `store_borrow` and
`end_borrow` scopes, and `alloc_stack`/`dealloc_stack` can be placed with
less precision so long as they balance, and type-dependent stack allocations
are dominated by the def for the type.
2026-05-11 20:56:10 -07:00

239 lines
9.3 KiB
Plaintext

// RUN: %target-sil-opt -enable-sil-verify-all %s -loadable-address | %FileCheck %s
import Builtin
import Swift
public struct BigArc { var a, b, c, d, e: AnyObject }
public struct BiggerArc { var bigArc: BigArc }
sil @makeBigArc : $@convention(thin) () -> BigArc
sil @takeBorrowBigArc : $@convention(thin) (@guaranteed Ref<BigArc>) -> ()
// In test1, `%1` is a `BigArc` that will already become indirected because
// it is the result of a function call that will become an indirect return.
// `make_borrow` of the value becomes `make_borrow_addr` of the value's
// lowered memory address.
// CHECK-LABEL: sil @test1
// CHECK: apply {{%.*}}([[OUT:%[0-9]+]])
// CHECK: make_addr_borrow [[OUT]]
sil @test1 : $@convention(thin) () -> () {
bb0:
%0 = function_ref @makeBigArc : $@convention(thin) () -> BigArc
%1 = apply %0() : $@convention(thin) () -> BigArc
%2 = make_borrow %1
%3 = struct $Ref<BigArc> (%2)
%4 = function_ref @takeBorrowBigArc : $@convention(thin) (@guaranteed Ref<BigArc>) -> ()
%5 = apply %4(%3) : $@convention(thin) (@guaranteed Ref<BigArc>) -> ()
%6 = tuple ()
return %6
}
// In test2, `%4` is a `BigArc` that is `load`-ed from a memory location.
// `make_borrow` of the load becomes `make_borrow_addr` of the loaded address.
// CHECK-LABEL: sil @test2
// CHECK: apply {{%.*}}([[OUT:%[0-9]+]])
// CHECK: copy_addr [take] [[OUT]] to [init] [[LOCAL:%[0-9]+]]
// CHECK: make_addr_borrow [[LOCAL]]
sil @test2 : $@convention(thin) () -> () {
bb0:
%0 = alloc_stack $BigArc
%1 = function_ref @makeBigArc : $@convention(thin) () -> BigArc
%2 = apply %1() : $@convention(thin) () -> BigArc
store %2 to %0
%4 = load %0
%5 = make_borrow %4
%6 = struct $Ref<BigArc> (%5)
%7 = function_ref @takeBorrowBigArc : $@convention(thin) (@guaranteed Ref<BigArc>) -> ()
%8 = apply %7(%6) : $@convention(thin) (@guaranteed Ref<BigArc>) -> ()
destroy_addr %0
dealloc_stack %0
%11 = tuple ()
return %11
}
// In test3, `%5` is a `BigArc` that is formed locally and doesn't directly
// pass through any function boundaries. `make_borrow` of the local value
// becomes `make_addr_borrow` of a temporary allocation.
// CHECK-LABEL: sil @test3
// CHECK: [[BUF:%.*]] = alloc_stack
// CHECK: [[BIGARC:%.*]] = struct $BigArc
// CHECK: store [[BIGARC]] to [[BUF]]
// CHECK: [[B_BORROW:%.*]] = make_addr_borrow [[BUF]]
// CHECK: [[S_BORROW:%.*]] = struct $Ref<BigArc> ([[B_BORROW]])
// CHECK: apply {{.*}}([[S_BORROW]])
// CHECK: release_value [[BIGARC]]
// CHECK: dealloc_stack [[BUF]]
sil @test3 : $@convention(thin) (@owned AnyObject) -> () {
bb0(%0 : $AnyObject):
retain_value %0
retain_value %0
retain_value %0
retain_value %0
%5 = struct $BigArc(%0, %0, %0, %0, %0)
%6 = make_borrow %5
%7 = struct $Ref<BigArc> (%6)
%8 = function_ref @takeBorrowBigArc : $@convention(thin) (@guaranteed Ref<BigArc>) -> ()
%9 = apply %8(%7) : $@convention(thin) (@guaranteed Ref<BigArc>) -> ()
release_value %5
%11 = tuple ()
return %11
}
// In test4, `%1` is a `BigArc` that is projected from a `BiggerArc` that
// is passed in by value and will be turned into an indirect parameter by
// the pass.
// CHECK-LABEL: sil @test4
// CHECK: bb0([[PARAM:%.*]] : $*BiggerArc):
// CHECK: [[BIGARC_ADDR:%.*]] = struct_element_addr [[PARAM]]
// CHECK: [[BORROW:%.*]] = make_addr_borrow [[BIGARC_ADDR]]
sil @test4 : $@convention(thin) (@guaranteed BiggerArc) -> () {
bb0(%0 : $BiggerArc):
%1 = struct_extract %0, #BiggerArc.bigArc
%2 = make_borrow %1
%3 = struct $Ref<BigArc> (%2)
%4 = function_ref @takeBorrowBigArc : $@convention(thin) (@guaranteed Ref<BigArc>) -> ()
%5 = apply %4(%3) : $@convention(thin) (@guaranteed Ref<BigArc>) -> ()
%6 = tuple ()
return %6
}
// In test5, `%7` is a `BigArc` that is projected from a `BiggerArc` that
// is formed locally.
// CHECK-LABEL: sil @test5
// CHECK: [[BUF:%.*]] = alloc_stack
// CHECK: [[BIGARC:%.*]] = struct_extract {{%.*}}, #BiggerArc.bigArc
// CHECK: store [[BIGARC]] to [[BUF]]
// CHECK: [[B_BORROW:%.*]] = make_addr_borrow [[BUF]]
// CHECK: [[S_BORROW:%.*]] = struct $Ref<BigArc> ([[B_BORROW]])
// CHECK: apply {{.*}}([[S_BORROW]])
// CHECK: release_value [[BIGARC]]
// CHECK: dealloc_stack [[BUF]]
sil @test5 : $@convention(thin) (@owned AnyObject) -> () {
bb0(%0 : $AnyObject):
retain_value %0
retain_value %0
retain_value %0
retain_value %0
%5 = struct $BigArc(%0, %0, %0, %0, %0)
%6 = struct $BiggerArc(%5)
%7 = struct_extract %6, #BiggerArc.bigArc
%8 = make_borrow %7
%9 = struct $Ref<BigArc> (%8)
%10 = function_ref @takeBorrowBigArc : $@convention(thin) (@guaranteed Ref<BigArc>) -> ()
%11 = apply %10(%9) : $@convention(thin) (@guaranteed Ref<BigArc>) -> ()
release_value %7
%13 = tuple ()
return %13
}
public struct OneArcGen<T> { var a: AnyObject }
public struct BigArcGen<T> { var a, b, c, d, e: OneArcGen<T> }
sil @takeBorrowBigArcGen : $@convention(thin) <T> (@guaranteed Ref<BigArcGen<T>>) -> ()
protocol P {
static func makeBigArc() -> OneArcGen<Self>
}
// In test6, `%x` is a `BigArc<T>` that is formed dependent on an opened
// existential which does not pass through any function boundaries.
// `make_borrow` of the local value becomes `make_addr_borrow` of a temporary
// allocation.
// CHECK-LABEL: sil @test6
// CHECK: open_existential
// CHECK: [[BUF:%.*]] = alloc_stack
// CHECK: [[BIGARC:%.*]] = struct $BigArc
// CHECK: store [[BIGARC]] to [[BUF]]
// CHECK: [[B_BORROW:%.*]] = make_addr_borrow [[BUF]]
// CHECK: [[S_BORROW:%.*]] = struct $Ref<BigArcGen<{{.*}}>> ([[B_BORROW]])
// CHECK: apply {{.*}}([[S_BORROW]])
// CHECK: release_value [[BIGARC]]
// CHECK: dealloc_stack [[BUF]]
sil @test6 : $@convention(thin) (@thick any P.Type) -> () {
bb0(%0 : $@thick any P.Type):
%1 = open_existential_metatype %0 to $@thick (@opened("00000000-0000-0000-0000-000000000000", any P) Self).Type
%2 = witness_method $@opened("00000000-0000-0000-0000-000000000000", any P) Self, #P.makeBigArc : <Self: P> (Self.Type) -> () -> OneArcGen<Self> : $@convention(witness_method : P) <Self: P> (@thick Self.Type) -> @owned OneArcGen<Self>
%3 = apply %2<@opened("00000000-0000-0000-0000-000000000000", any P) Self>(%1) : $@convention(witness_method : P) <Self: P> (@thick Self.Type) -> @owned OneArcGen<Self>
retain_value %3
retain_value %3
retain_value %3
retain_value %3
%8 = struct $BigArcGen<@opened("00000000-0000-0000-0000-000000000000", any P) Self>(%3, %3, %3, %3, %3)
%9 = make_borrow %8
%10 = struct $Ref<BigArcGen<@opened("00000000-0000-0000-0000-000000000000", any P) Self>> (%9)
%11 = function_ref @takeBorrowBigArcGen : $@convention(thin) <T> (@guaranteed Ref<BigArcGen<T>>) -> ()
%12 = apply %11<@opened("00000000-0000-0000-0000-000000000000", any P) Self>(%10) : $@convention(thin) <T> (@guaranteed Ref<BigArcGen<T>>) -> ()
release_value %8
%14 = tuple ()
return %14
}
// In test7, `%x` is a `BigArc<T>` that is formed dependent on an opened
// existential which does not pass through any function boundaries, and which
// is only used on one branch of a divergent control flow.
// `make_borrow` of the local value becomes `make_addr_borrow` of a temporary
// allocation.
// CHECK-LABEL: sil @test7
// CHECK: cond_br {{.*}}, [[YES:bb[0-9]+]], [[NO:bb[0-9]+]]
// CHECK: [[YES]]:
// CHECK: open_existential
// CHECK: [[BUF:%.*]] = alloc_stack
// CHECK: br [[YES2:bb[0-9]+]]
// CHECK: [[YES2]]:
// CHECK: [[BIGARC:%.*]] = struct $BigArcGen
// CHECK: store [[BIGARC]] to [[BUF]]
// CHECK: [[B_BORROW:%.*]] = make_addr_borrow [[BUF]]
// CHECK: [[S_BORROW:%.*]] = struct $Ref<BigArcGen<{{.*}}>> ([[B_BORROW]])
// CHECK: apply {{.*}}([[S_BORROW]])
// CHECK: release_value [[BIGARC]]
// CHECK: dealloc_stack [[BUF]]
// CHECK: br [[CONT:bb[0-9]+]]
// CHECK: [[NO]]:
// CHECK: br [[CONT]]
sil @test7 : $@convention(thin) (@thick any P.Type, Builtin.Int1) -> () {
bb0(%0 : $@thick any P.Type, %1 : $Builtin.Int1):
cond_br %1, bb1, bb3
bb1:
%3 = open_existential_metatype %0 to $@thick (@opened("00000001-0000-0000-0000-000000010000", any P) Self).Type
%4 = witness_method $@opened("00000001-0000-0000-0000-000000010000", any P) Self, #P.makeBigArc : <Self: P> (Self.Type) -> () -> OneArcGen<Self> : $@convention(witness_method : P) <Self: P> (@thick Self.Type) -> @owned OneArcGen<Self>
%5 = apply %4<@opened("00000001-0000-0000-0000-000000010000", any P) Self>(%3) : $@convention(witness_method : P) <Self: P> (@thick Self.Type) -> @owned OneArcGen<Self>
br bb2
bb2:
retain_value %5
retain_value %5
retain_value %5
retain_value %5
%10 = struct $BigArcGen<@opened("00000001-0000-0000-0000-000000010000", any P) Self>(%5, %5, %5, %5, %5)
%11 = make_borrow %10
%12 = struct $Ref<BigArcGen<@opened("00000001-0000-0000-0000-000000010000", any P) Self>> (%11)
%13 = function_ref @takeBorrowBigArcGen : $@convention(thin) <T> (@guaranteed Ref<BigArcGen<T>>) -> ()
%14 = apply %13<@opened("00000001-0000-0000-0000-000000010000", any P) Self>(%12) : $@convention(thin) <T> (@guaranteed Ref<BigArcGen<T>>) -> ()
release_value %10
br bb4
bb3:
br bb4
bb4:
%16 = tuple ()
return %16
}