Files
swift-mirror/test/SILOptimizer/allocstack_hoisting.sil
Erik Eckstein 1a01f870e1 AllocStackHoisting: fix a quadratic complexity bug when merging stack locations.
Use consecutive instruction numbers to check for overlapping instead of iterating over the instruction list.

This fixes a compile time problem which shows up if there are many stack locations in a large basic block.

rdar://113207176
2023-08-25 10:49:21 +02:00

323 lines
7.8 KiB
Plaintext

// RUN: %target-sil-opt -enable-sil-verify-all %s -alloc-stack-hoisting | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-MERGING %s
// RUN: %target-sil-opt -enable-sil-verify-all %s -alloc-stack-hoisting -sil-merge-stack-slots=false | %FileCheck %s
sil_stage canonical
import Builtin
import Swift
import SwiftShims
protocol P {
}
struct Generic<T> {
var x : T
}
struct FixedSize {
var x : Builtin.Int8
}
// CHECK-LABEL: sil @hoist_generic
// CHECK: bb0({{.*}}):
// CHECK: [[AS:%.*]] = alloc_stack $T
// CHECK: bb1:
// CHECK-NOT: alloc_stack
// CHECK-NOT: dealloc_stack
// CHECK: bb2
// CHECK: bb3:
// CHECK: dealloc_stack [[AS]]
// CHECK: return
sil @hoist_generic : $@convention(thin) <T> (@in T, Builtin.Int1) -> () {
bb0(%0 : $*T, %1: $Builtin.Int1):
cond_br %1, bb1, bb2
bb1:
%2 = alloc_stack $T
copy_addr [take] %0 to [init] %2 : $*T
destroy_addr %2 : $*T
dealloc_stack %2 : $*T
br bb3
bb2:
destroy_addr %0 : $*T
br bb3
bb3:
%3 = tuple ()
return %3 : $()
}
sil @throwing_fun : $@convention(thin) () -> @error any Error
// CHECK-LABEL: sil @hoist_generic
// CHECK: bb0({{.*}}):
// CHECK: [[AS:%.*]] = alloc_stack $T
// CHECK: bb1:
// CHECK-NOT: alloc_stack
// CHECK-NOT: dealloc_stack
// CHECK: bb2
// CHECK: bb3:
// CHECK: try_apply
// CHECK: bb4({{.*}}:
// CHECK: dealloc_stack [[AS]]
// CHECK: return
// CHECK: bb5({{.*}}):
// CHECK: dealloc_stack [[AS]]
// CHECK: throw
sil @hoist_generic_throwing : $@convention(thin) <T> (@in T, Builtin.Int1) -> @error any Error {
bb0(%0 : $*T, %1: $Builtin.Int1):
cond_br %1, bb1, bb2
bb1:
%2 = alloc_stack $T
copy_addr [take] %0 to [init] %2 : $*T
destroy_addr %2 : $*T
dealloc_stack %2 : $*T
br bb3
bb2:
destroy_addr %0 : $*T
br bb3
bb3:
%3 = function_ref @throwing_fun : $@convention(thin) () -> @error any Error
try_apply %3() : $@convention(thin) () -> @error any Error, normal bb4, error bb5
bb4(%6: $()):
%4 = tuple ()
return %4 : $()
bb5(%5: $Error):
throw %5: $Error
}
// CHECK-LABEL: sil @hoist_generic
// CHECK: bb0({{.*}}):
// CHECK: [[AS:%.*]] = alloc_stack $Generic<T>
// CHECK: bb1:
// CHECK-NOT: alloc_stack
// CHECK-NOT: dealloc_stack
// CHECK: bb2
// CHECK: bb3:
// CHECK: dealloc_stack [[AS]]
// CHECK: return
sil @hoist_generic_type : $@convention(thin) <T> (@in Generic<T>, Builtin.Int1) -> () {
bb0(%0 : $*Generic<T>, %1: $Builtin.Int1):
cond_br %1, bb1, bb2
bb1:
%2 = alloc_stack $Generic<T>
copy_addr [take] %0 to [init] %2 : $*Generic<T>
destroy_addr %2 : $*Generic<T>
dealloc_stack %2 : $*Generic<T>
br bb3
bb2:
destroy_addr %0 : $*Generic<T>
br bb3
bb3:
%3 = tuple ()
return %3 : $()
}
// CHECK-LABEL: sil @hoist_generic_nesting
// CHECK: bb0({{.*}}):
// CHECK: [[AS:%.*]] = alloc_stack $T
// CHECK: [[AS2:%.*]] = alloc_stack $T
// CHECK: [[FIXED:%.*]] = alloc_stack $FixedSize
// CHECK: bb1:
// CHECK-NOT: alloc_stack
// CHECK-NOT: dealloc_stack
// CHECK: bb2
// CHECK: bb3:
// CHECK: dealloc_stack [[FIXED]]
// CHECK: dealloc_stack [[AS2]]
// CHECK: dealloc_stack [[AS]]
// CHECK: return
sil @hoist_generic_nesting : $@convention(thin) <T> (@in T, Builtin.Int1) -> () {
bb0(%0 : $*T, %1: $Builtin.Int1):
%2 = alloc_stack $FixedSize
cond_br %1, bb1, bb2
bb1:
%3 = alloc_stack $T
%4 = alloc_stack $T
copy_addr [take] %0 to [init] %3 : $*T
destroy_addr %3 : $*T
dealloc_stack %4: $*T
dealloc_stack %3 : $*T
br bb3
bb2:
destroy_addr %0 : $*T
br bb3
bb3:
dealloc_stack %2: $*FixedSize
%5 = tuple ()
return %5 : $()
}
// CHECK-LABEL: sil @dont_hoist_opened_generic
// CHECK: bb0({{.*}}):
// CHECK-NOT: alloc_stack
// CHECK: bb1:
// CHECK: alloc_stack
// CHECK: bb2:
// CHECK: bb3:
// CHECK-NOT: dealloc_stack
// CHECK: return
sil @dont_hoist_opened_generic : $@convention(thin) (@in P, Builtin.Int1) -> () {
bb0(%0 : $*P, %1: $Builtin.Int1):
cond_br %1, bb1, bb2
bb1:
%2 = open_existential_addr mutable_access %0 : $*P to $*@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60", P) Self
%3 = alloc_stack $@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60", P) Self
copy_addr [take] %2 to [init] %3 : $*@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60", P) Self
destroy_addr %3 : $*@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60", P) Self
dealloc_stack %3 : $*@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60", P) Self
br bb3
bb2:
destroy_addr %0 : $*P
br bb3
bb3:
%4 = tuple ()
return %4 : $()
}
// CHECK-LABEL: sil @dont_hoist_protocol
// CHECK: bb0({{.*}}):
// CHECK-NOT: alloc_stack
// CHECK: bb1:
// CHECK: alloc_stack
// CHECK: bb2:
// CHECK: bb3:
// CHECK-NOT: dealloc_stack
// CHECK: return
sil @dont_hoist_protocol : $@convention(thin) (@in P, Builtin.Int1) -> () {
bb0(%0 : $*P, %1: $Builtin.Int1):
cond_br %1, bb1, bb2
bb1:
%2 = alloc_stack $P
copy_addr [take] %0 to [init] %2 : $*P
destroy_addr %2 : $*P
dealloc_stack %2 : $*P
br bb3
bb2:
destroy_addr %0 : $*P
br bb3
bb3:
%3 = tuple ()
return %3 : $()
}
// CHECK-LABEL: sil @dont_crash_on_unreachable
// CHECK: bb0({{.*}}):
// CHECK: alloc_stack
// CHECK: bb1:
// CHECK-NOT: alloc_stack
// CHECK: bb2:
// CHECK: bb3:
// CHECK: unreachable
sil @dont_crash_on_unreachable : $@convention(thin) <T> (@in T, Builtin.Int1) -> Never {
bb0(%0 : $*T, %1: $Builtin.Int1):
cond_br %1, bb1, bb2
bb1:
%2 = alloc_stack $T
copy_addr [take] %0 to [init] %2 : $*T
destroy_addr %2 : $*T
br bb3
bb2:
destroy_addr %0 : $*T
br bb3
bb3:
%3 = builtin "int_trap"() : $()
unreachable
}
// CHECK-LABEL: sil @dont_hoist_with_availability_checks
// CHECK-NOT: alloc_stack
// CHECK: cond_br
// CHECK: bb1:
// CHECK: alloc_stack
// CHECK: } // end sil function 'dont_hoist_with_availability_checks'
sil @dont_hoist_with_availability_checks : $@convention(thin) <T> (@in T, Builtin.Int1) -> () {
bb0(%0 : $*T, %1: $Builtin.Int1):
%5 = integer_literal $Builtin.Int32, 15
%7 = builtin "targetOSVersionAtLeast"(%5 : $Builtin.Int32, %5 : $Builtin.Int32, %5 : $Builtin.Int32) : $Builtin.Int32
%8 = builtin "cmp_eq_Int32"(%7 : $Builtin.Int32, %5 : $Builtin.Int32) : $Builtin.Int1
cond_br %8, bb1, bb2
bb1:
%2 = alloc_stack $T
copy_addr [take] %0 to [init] %2 : $*T
destroy_addr %2 : $*T
dealloc_stack %2 : $*T
br bb3
bb2:
destroy_addr %0 : $*T
br bb3
bb3:
%3 = tuple ()
return %3 : $()
}
// CHECK-LABEL: sil @dont_hoist_with_has_symbol_checks
// CHECK-NOT: alloc_stack
// CHECK: cond_br
// CHECK: bb1:
// CHECK: alloc_stack
// CHECK: } // end sil function 'dont_hoist_with_has_symbol_checks'
sil @dont_hoist_with_has_symbol_checks : $@convention(thin) <T> (@in T, Builtin.Int1) -> () {
bb0(%0 : $*T, %1: $Builtin.Int1):
%5 = has_symbol #FixedSize.init
cond_br %5, bb1, bb2
bb1:
%2 = alloc_stack $T
copy_addr [take] %0 to [init] %2 : $*T
destroy_addr %2 : $*T
dealloc_stack %2 : $*T
br bb3
bb2:
destroy_addr %0 : $*T
br bb3
bb3:
%3 = tuple ()
return %3 : $()
}
// CHECK-MERGING-LABEL: sil @merging
// CHECK-MERGING: %1 = alloc_stack $T
// CHECK-MERGING-NEXT: debug_value %1
// CHECK-MERGING-NEXT: debug_value %1
// CHECK-MERGING: dealloc_stack %1
// CHECK-MERGING-NOT: alloc_stack
// CHECK: } // end sil function 'merging'
sil @merging : $@convention(thin) <T> (@in T) -> () {
bb0(%0 : $*T):
%1 = alloc_stack $T
debug_value %1 : $*T, name "x"
dealloc_stack %1 : $*T
%2 = alloc_stack $T
debug_value %2 : $*T, name "y"
dealloc_stack %2 : $*T
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil @dont_merge
// CHECK: %1 = alloc_stack $T
// CHECK-NEXT: %2 = alloc_stack $T
// CHECK-NEXT: debug_value %2
// CHECK-NEXT: debug_value %1
// CHECK: dealloc_stack %2
// CHECK-NEXT: dealloc_stack %1
// CHECK: } // end sil function 'dont_merge'
sil @dont_merge : $@convention(thin) <T> (@in T) -> () {
bb0(%0 : $*T):
%1 = alloc_stack $T
debug_value %1 : $*T, name "x"
%3 = alloc_stack $T
debug_value %3 : $*T, name "y"
dealloc_stack %3 : $*T
dealloc_stack %1 : $*T
%r = tuple ()
return %r : $()
}