Skip to content

Commit 6714a72

Browse files
committed
Optimizer: re-implement and improve the AllocBoxToStack pass
This pass replaces `alloc_box` with `alloc_stack` if the box is not escaping. The original implementation had some limitations. It could not handle cases of local functions which are called multiple times or even recursively, e.g. ``` public func foo() -> Int { var i = 1 func localFunction() { i += 1 } localFunction() localFunction() return i } ``` The new implementation (done in Swift) fixes this problem with a new algorithm. It's not only more powerful, but also simpler: the new pass has less than half lines of code than the old pass. The pass is invoked in the mandatory pipeline and later in the optimizer pipeline. The new implementation provides a module-pass for the mandatory pipeline (whereas the "regular" pass is a function pass). This is required because the mandatory pass needs to remove originals of specialized closures, which cannot be done from a function-pass. In the old implementation this was done with a hack by adding a semantic attribute and deleting the function later in the pipeline. I still kept the sources of the old pass for being able to bootstrap the compiler without a host compiler. rdar://142756547
1 parent fa56ba2 commit 6714a72

18 files changed

+558
-96
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/AllocBoxToStack.swift

Lines changed: 467 additions & 0 deletions
Large diffs are not rendered by default.

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# See http://swift.org/CONTRIBUTORS.txt for Swift project authors
88

99
swift_compiler_sources(Optimizer
10+
AllocBoxToStack.swift
1011
AssumeSingleThreaded.swift
1112
AsyncDemotion.swift
1213
BooleanLiteralFolding.swift

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,15 @@ private func registerForSILCombine<InstType: SILCombineSimplifiable>(
6363

6464
private func registerSwiftPasses() {
6565
// Module passes
66+
registerPass(mandatoryAllocBoxToStack, { mandatoryAllocBoxToStack.run($0) })
6667
registerPass(mandatoryPerformanceOptimizations, { mandatoryPerformanceOptimizations.run($0) })
6768
registerPass(diagnoseUnknownConstValues, { diagnoseUnknownConstValues.run($0)})
6869
registerPass(readOnlyGlobalVariablesPass, { readOnlyGlobalVariablesPass.run($0) })
6970
registerPass(stackProtection, { stackProtection.run($0) })
7071
registerPass(embeddedSwiftDiagnostics, { embeddedSwiftDiagnostics.run($0) })
7172

7273
// Function passes
74+
registerPass(allocBoxToStack, { allocBoxToStack.run($0) })
7375
registerPass(asyncDemotion, { asyncDemotion.run($0) })
7476
registerPass(booleanLiteralFolding, { booleanLiteralFolding.run($0) })
7577
registerPass(letPropertyLowering, { letPropertyLowering.run($0) })

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@
5959
#endif
6060

6161

62+
PASS(AllocBoxToStack, "allocbox-to-stack",
63+
"stack promotion of box objects")
6264
PASS(CopyToBorrowOptimization, "copy-to-borrow-optimization",
6365
"Convert load [copy] instructions to load_borrow and remove copies of borrowed values")
6466
PASS(AliasInfoDumper, "dump-alias-info",
@@ -153,6 +155,8 @@ PASS(ExperimentalSwiftBasedClosureSpecialization, "experimental-swift-based-clos
153155
PASS(AutodiffClosureSpecialization, "autodiff-closure-specialization",
154156
"Autodiff specific closure-specialization pass")
155157

158+
MODULE_PASS(MandatoryAllocBoxToStack, "mandatory-allocbox-to-stack",
159+
"Mandatory stack promotion of box objects")
156160
MODULE_PASS(AsyncDemotion, "async-demotion",
157161
"Convert async functions to be synchronous")
158162
MODULE_PASS(RunUnitTests, "run-unit-tests",
@@ -214,7 +218,7 @@ LEGACY_PASS(AccessMarkerElimination, "access-marker-elim",
214218
"Access Marker Elimination.")
215219
LEGACY_PASS(AddressLowering, "address-lowering",
216220
"SIL Address Lowering")
217-
LEGACY_PASS(AllocBoxToStack, "allocbox-to-stack",
221+
LEGACY_PASS(LegacyAllocBoxToStack, "legacy-allocbox-to-stack",
218222
"Stack Promotion of Box Objects")
219223
LEGACY_PASS(ArrayCountPropagation, "array-count-propagation",
220224
"Array Count Propagation")

lib/SILOptimizer/PassManager/PassPipeline.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,15 +117,18 @@ static void addMandatoryDiagnosticOptPipeline(SILPassPipelinePlan &P) {
117117
// This guarantees that stack-promotable boxes have [static] enforcement.
118118
P.addAccessEnforcementSelection();
119119

120-
P.addAllocBoxToStack();
120+
#ifdef SWIFT_ENABLE_SWIFT_IN_SWIFT
121+
P.addMandatoryAllocBoxToStack();
122+
#else
123+
P.addLegacyAllocBoxToStack();
124+
#endif
121125
P.addNoReturnFolding();
122126
P.addBooleanLiteralFolding();
123127
addDefiniteInitialization(P);
124128

125129
P.addAddressLowering();
126130

127-
// Before we run later semantic optimizations, eliminate simple functions that
128-
// we specialized to ensure that we do not emit diagnostics twice.
131+
// TODO: remove this once CapturePromotion deletes specialized functions itself.
129132
P.addDiagnosticDeadFunctionElimination();
130133

131134
P.addFlowIsolation();

lib/SILOptimizer/Transforms/AllocBoxToStack.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1315,6 +1315,6 @@ class AllocBoxToStack : public SILFunctionTransform {
13151315
};
13161316
} // end anonymous namespace
13171317

1318-
SILTransform *swift::createAllocBoxToStack() {
1318+
SILTransform *swift::createLegacyAllocBoxToStack() {
13191319
return new AllocBoxToStack();
13201320
}

test/AutoDiff/SILOptimizer/activity_analysis.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -798,7 +798,7 @@ func testActiveOptional(_ x: Float) -> Float {
798798
// CHECK: [NONE] // function_ref _diagnoseUnexpectedNilOptional(_filenameStart:_filenameLength:_filenameIsASCII:_line:_isImplicitUnwrap:)
799799
// CHECK: [NONE] %24 = apply %23(%17, %18, %19, %20, %22) : $@convention(thin) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, Builtin.Word, Builtin.Int1) -> ()
800800
// CHECK: bb2:
801-
// CHECK: [ACTIVE] %26 = argument of bb2 : $Float
801+
// CHECK: [ACTIVE] {{%[0-9]+}} = argument of bb2 : $Float
802802

803803
enum DirectEnum: Differentiable & AdditiveArithmetic {
804804
case case0

test/IRGen/generic_tuples.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// Make sure that optimization passes don't choke on storage types for generic tuples
44
// RUN: %target-swift-frontend -module-name generic_tuples -emit-ir -O %s
55

6-
// REQUIRES: CPU=x86_64
6+
// REQUIRES: PTRSIZE=64
77

88
// CHECK-DAG: [[OPAQUE:%swift.opaque]] = type opaque
99
// CHECK-DAG: [[TUPLE_TYPE:%swift.tuple_type]] = type { %swift.type, i64, ptr, [0 x %swift.tuple_element_type] }
@@ -26,9 +26,10 @@ func dup<T>(_ x: T) -> (T, T) { var x = x; return (x,x) }
2626
// Copy 'x' into the first result.
2727
// CHECK-NEXT: call ptr [[WITNESS]](ptr noalias %0, ptr noalias [[X_ALLOCA]], ptr %T)
2828
// Copy 'x' into the second element.
29-
// CHECK-NEXT: [[WITNESS_ADDR:%.*]] = getelementptr inbounds ptr, ptr [[VWT]], i32 4
30-
// CHECK-NEXT: [[WITNESS:%.*]] = load ptr, ptr [[WITNESS_ADDR]], align 8
3129
// CHECK-NEXT: call ptr [[WITNESS]](ptr noalias %1, ptr noalias [[X_ALLOCA]], ptr %T)
30+
// CHECK-NEXT: [[WITNESS_ADDR:%.*]] = getelementptr inbounds ptr, ptr [[VWT]], i32 1
31+
// CHECK-NEXT: [[DESTROYWITNESS:%.*]] = load ptr, ptr [[WITNESS_ADDR]], align 8
32+
// CHECK-NEXT: call void [[DESTROYWITNESS]](ptr noalias [[X_ALLOCA]],
3233

3334
struct S {}
3435

test/SILOptimizer/allocbox_to_stack.sil

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,9 @@ bb1(%0 : $Int):
125125

126126
sil @callee : $@convention(thin) (@inout Int) -> ()
127127

128-
// CHECK-LABEL: sil @inout_nocapture
129-
sil @inout_nocapture : $@convention(thin) () -> Int {
128+
// CHECK-LABEL: sil @escaping_pointer
129+
sil @escaping_pointer : $@convention(thin) () -> Int {
130130
bb0:
131-
// CHECK: alloc_stack
132131
%1 = alloc_box ${ var Int }
133132
%1a = project_box %1 : ${ var Int }, 0
134133
%6 = function_ref @callee : $@convention(thin) (@inout Int) -> ()
@@ -398,7 +397,7 @@ bb0(%0 : $Int):
398397
}
399398

400399
// CHECK-LABEL: sil private @$s6struct8useStack1tySi_tFSiycfU_Tf0s_n
401-
// CHECK-LABEL: sil private [_semantics "sil.optimizer.delete_if_unused"] @$s6struct8useStack1tySi_tFSiycfU_
400+
// CHECK-LABEL: sil private @$s6struct8useStack1tySi_tFSiycfU_
402401
// struct.(useStack (t : Swift.Int) -> ()).(closure #1)
403402
sil private @$s6struct8useStack1tySi_tFSiycfU_ : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int>) -> Int {
404403
bb0(%0 : $<τ_0_0> { var τ_0_0 } <Int>):
@@ -651,7 +650,7 @@ bb0(%0 : $Int, %1 : $*S<T>):
651650
return %9 : $Bool
652651
}
653652

654-
// CHECK-LABEL: sil shared [_semantics "sil.optimizer.delete_if_unused"] @closure1
653+
// CHECK-LABEL: sil shared @closure1
655654
sil shared @closure1 : $@convention(thin) <T where T : Count> (Int, @owned <τ_0_0 : Count> { var S<τ_0_0> } <T>) -> Bool {
656655
// CHECK: bb0
657656
bb0(%0 : $Int, %1 : $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>):
@@ -673,7 +672,7 @@ bb0(%0 : $Int, %1 : $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>):
673672
// CHECK: return
674673
// CHECK-NOT: bb1
675674

676-
// CHECK-LABEL: sil shared [_semantics "sil.optimizer.delete_if_unused"] @closure2
675+
// CHECK-LABEL: sil shared @closure2
677676
sil shared @closure2 : $@convention(thin) <T where T : Count> (Int, @owned <τ_0_0 : Count> { var S<τ_0_0> } <T>) -> Bool {
678677
// CHECK: bb0
679678
bb0(%0 : $Int, %1 : $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>):

test/SILOptimizer/allocbox_to_stack_lifetime.sil

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ bb0(%0 : $Int):
5454
// CHECK: [[STACK:%[^,]+]] = alloc_stack
5555
// CHECK: cond_br undef, [[DIE:bb[0-9]+]]
5656
// CHECK: [[DIE]]:
57+
// CHECK-NEXT: dealloc_stack [[STACK]]
5758
// CHECK-NEXT: unreachable
5859
// CHECK-LABEL: } // end sil function 'keep_dead_end'
5960
sil [ossa] @keep_dead_end : $@convention(thin) () -> () {

0 commit comments

Comments
 (0)