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) () -> () {

test/SILOptimizer/allocbox_to_stack_noncopyable.sil

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ bb0(%0 : @closureCapture @guaranteed ${ var NonTrivialStruct }):
2828
// CHECK-NEXT: [[STACK2:%.*]] = alloc_stack $NonTrivialStruct
2929
// CHECK-NEXT: [[MARKED2:%.*]] = mark_unresolved_non_copyable_value [consumable_and_assignable] [[STACK2]]
3030
// CHECK-NEXT: store [[LOADED_VALUE]] to [init] [[MARKED2]]
31-
// CHECK-NEXT: // function_ref
32-
// CHECK-NEXT: [[FUNC:%.*]] = function_ref @$s26host_markmustcheck_closureTf0s_n : $@convention(thin) (@inout_aliasable NonTrivialStruct) -> ()
31+
// CHECK: [[FUNC:%.*]] = function_ref @$s26host_markmustcheck_closureTf0s_n : $@convention(thin) (@inout_aliasable NonTrivialStruct) -> ()
3332
// CHECK-NEXT: [[PA:%.*]] = partial_apply [callee_guaranteed] [[FUNC]]([[MARKED2]]) : $@convention(thin) (@inout_aliasable NonTrivialStruct) -> ()
3433
// CHECK-NEXT: apply [[PA]]()
3534
// CHECK-NEXT: destroy_value [[PA]]

test/SILOptimizer/allocbox_to_stack_ownership.sil

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all %s -allocbox-to-stack -enable-copy-propagation=requested-passes-only | %FileCheck %s
1+
// RUN: %target-sil-opt -sil-print-types %s -allocbox-to-stack | %FileCheck %s --check-prefix=CHECK --check-prefix=OPT
2+
// RUN: %target-sil-opt -sil-print-types %s -mandatory-allocbox-to-stack | %FileCheck %s --check-prefix=CHECK --check-prefix=MANDATORY
23

34
sil_stage raw
45

@@ -123,10 +124,9 @@ bb1(%0 : $Int):
123124

124125
sil [ossa] @callee : $@convention(thin) (@inout Int) -> ()
125126

126-
// CHECK-LABEL: sil [ossa] @inout_nocapture
127-
sil [ossa] @inout_nocapture : $@convention(thin) () -> Int {
127+
// CHECK-LABEL: sil [ossa] @escaping_pointer
128+
sil [ossa] @escaping_pointer : $@convention(thin) () -> Int {
128129
bb0:
129-
// CHECK: alloc_stack
130130
%1 = alloc_box ${ var Int }
131131
%1a = project_box %1 : ${ var Int }, 0
132132
%6 = function_ref @callee : $@convention(thin) (@inout Int) -> ()
@@ -394,8 +394,8 @@ bb0(%0 : $Int):
394394
}
395395

396396
// CHECK-LABEL: sil private [transparent] [ossa] @$s6struct8useStack1tySi_tFSiycfU_Tf0s_n
397-
// CHECK-LABEL: sil private [transparent] [_semantics "sil.optimizer.delete_if_unused"] [ossa] @$s6struct8useStack1tySi_tFSiycfU_
398-
// struct.(useStack (t : Swift.Int) -> ()).(closure #1)
397+
// OPT-LABEL: sil private [transparent] [ossa] @$s6struct8useStack1tySi_tFSiycfU_ :
398+
// MANDATORY-NOT: sil private [transparent] [ossa] @$s6struct8useStack1tySi_tFSiycfU_ :
399399
sil private [transparent] [ossa] @$s6struct8useStack1tySi_tFSiycfU_ : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int>) -> Int {
400400
bb0(%0 : @owned $<τ_0_0> { var τ_0_0 } <Int>):
401401
%1 = project_box %0 : $<τ_0_0> { var τ_0_0 } <Int>, 0
@@ -751,7 +751,8 @@ bb0(%0 : $Int, %1 : $*S<T>):
751751
return %9 : $Bool
752752
}
753753

754-
// CHECK-LABEL: sil shared [_semantics "sil.optimizer.delete_if_unused"] [ossa] @closure1
754+
// OPT-LABEL: sil shared [ossa] @closure1 :
755+
// MANDATORY-NOT: sil shared [ossa] @closure1 :
755756
sil shared [ossa] @closure1 : $@convention(thin) <T where T : Count> (Int, @owned <τ_0_0 : Count> { var S<τ_0_0> } <T>) -> Bool {
756757
// CHECK: bb0
757758
bb0(%0 : $Int, %1 : @owned $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>):
@@ -773,9 +774,9 @@ bb0(%0 : $Int, %1 : @owned $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>)
773774
// CHECK: return
774775
// CHECK-NOT: bb1
775776

776-
// CHECK-LABEL: sil shared [_semantics "sil.optimizer.delete_if_unused"] [ossa] @closure2
777+
// OPT-LABEL: sil shared [ossa] @closure2 :
778+
// MANDATORY-NOT: sil shared [ossa] @closure2 :
777779
sil shared [ossa] @closure2 : $@convention(thin) <T where T : Count> (Int, @owned <τ_0_0 : Count> { var S<τ_0_0> } <T>) -> Bool {
778-
// CHECK: bb0
779780
bb0(%0 : $Int, %1 : @owned $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>):
780781
%2 = project_box %1 : $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>, 0
781782
%3 = function_ref @binary : $@convention(thin) (Int, Int) -> Bool
@@ -791,7 +792,6 @@ bb0(%0 : $Int, %1 : @owned $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>)
791792
destroy_addr %4 : $*S<T>
792793
dealloc_stack %4 : $*S<T>
793794
destroy_value %1 : $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>
794-
// CHECK: return
795795
return %8 : $Bool
796796
}
797797

@@ -1126,22 +1126,17 @@ bb2:
11261126
return %r : $()
11271127
}
11281128

1129-
// Test that
1130-
// begin_access [read], copy_addr, end_access, destroy_addr
1131-
// is folded into
1132-
// begin_access [deinit], copy_addr [take], end_access
1133-
//
11341129
// CHECK-LABEL: sil [ossa] @deinit_access : $@convention(thin) <T> (@in T) -> (@out T, @out T) {
11351130
// CHECK: bb0(%0 : $*T, %1 : $*T, %2 : $*T):
11361131
// CHECK: [[STK:%.*]] = alloc_stack $T, var, name "x"
11371132
// CHECK: copy_addr %2 to [init] [[STK]] : $*T
11381133
// CHECK: [[READ:%.*]] = begin_access [read] [static] [[STK]] : $*T
11391134
// CHECK: copy_addr [[READ]] to [init] %0 : $*T
11401135
// CHECK: end_access [[READ]] : $*T
1141-
// CHECK: [[READ:%.*]] = begin_access [deinit] [static] [[STK]] : $*T
1142-
// CHECK: copy_addr [take] [[READ]] to [init] %1 : $*T
1136+
// CHECK: [[READ:%.*]] = begin_access [read] [static] [[STK]] : $*T
1137+
// CHECK: copy_addr [[READ]] to [init] %1 : $*T
11431138
// CHECK: end_access [[READ]] : $*T
1144-
// CHECK-NOT: destroy_addr
1139+
// CHECK: destroy_addr [[STK]]
11451140
// CHECK: dealloc_stack [[STK]] : $*T
11461141
// CHECK: destroy_addr %2 : $*T
11471142
// CHECK: return %{{.*}} : $()

test/SILOptimizer/allocboxtostack_escape.swift

Lines changed: 0 additions & 25 deletions
This file was deleted.

test/SILOptimizer/allocboxtostack_localapply.sil

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ bb0(%0 : ${ var Int64 }):
257257
}
258258

259259
// CHECK-LABEL: sil [noinline] @$testrecur :
260-
// CHECK: alloc_box ${ var Int64 }, var, name "x"
260+
// CHECK: alloc_stack $Int64, var, name "x"
261261
// CHECK-LABEL: } // end sil function '$testrecur'
262262
sil [noinline] @$testrecur : $@convention(thin) () -> () {
263263
bb0:
@@ -275,6 +275,7 @@ bb0:
275275
return %10 : $()
276276
}
277277

278+
// CHECK-LABEL: sil private [noinline] @$s13$testrecurbarTf0s_n : $@convention(thin) (@inout_aliasable Int64) -> () {
278279
sil private [noinline] @$testrecurbar : $@convention(thin) (@guaranteed { var Int64 }) -> () {
279280
bb0(%0 : ${ var Int64 }):
280281
%1 = project_box %0 : ${ var Int64 }, 0
@@ -292,6 +293,7 @@ bb0(%0 : ${ var Int64 }):
292293
return %13 : $()
293294
}
294295

296+
// CHECK-LABEL: sil private [noinline] @$s13$testrecurbasTf0s_n : $@convention(thin) (@inout_aliasable Int64) -> () {
295297
sil private [noinline] @$testrecurbas : $@convention(thin) (@guaranteed { var Int64 }) -> () {
296298
bb0(%0 : ${ var Int64 }):
297299
%1 = project_box %0 : ${ var Int64 }, 0

test/SILOptimizer/allocboxtostack_localapply.swift

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public func testboxescapes() -> (() -> ()) {
103103
}
104104

105105
// CHECK-LABEL: sil [noinline] @$s26allocboxtostack_localapply9testrecurSiyF :
106-
// CHECK: alloc_box ${ var Int }, var, name "x"
106+
// CHECK: alloc_stack [var_decl] $Int, var, name "x"
107107
// CHECK-LABEL: } // end sil function '$s26allocboxtostack_localapply9testrecurSiyF'
108108
@inline(never)
109109
public func testrecur() -> Int {
@@ -146,14 +146,9 @@ public func testdfs1() -> Int {
146146
return common()
147147
}
148148

149-
// Test to make sure we don't optimize the case when we have an inner common function call for multiple boxes.
150-
// We don't optimize this case now, because we don't have additional logic to correctly construct AppliesToSpecialize
151-
// Order of function calls constructed in PromotedOperands: bar innercommon local1 bas innercommon local2
152-
// AppliesToSpecialize should have the order: bar bas innercommon local1 local2
153-
// Since we don't maintain any tree like data structure with more info on the call tree, this is not possible to construct today
154149
// CHECK-LABEL: sil [noinline] @$s26allocboxtostack_localapply8testdfs2SiyF :
155-
// CHECK: alloc_box ${ var Int }, var, name "x"
156-
// CHECK: alloc_box ${ var Int }, var, name "y"
150+
// CHECK: alloc_stack [var_decl] $Int, var, name "x"
151+
// CHECK: alloc_stack [var_decl] $Int, var, name "y"
157152
// CHECK-LABEL:} // end sil function '$s26allocboxtostack_localapply8testdfs2SiyF'
158153
@inline(never)
159154
public func testdfs2() -> Int {
@@ -182,3 +177,20 @@ public func testdfs2() -> Int {
182177
return local1() + local2()
183178
}
184179

180+
// CHECK-LABEL: sil @$s26allocboxtostack_localapply15call2localfuncsSiyF :
181+
// CHECK-NOT: alloc_box
182+
// CHECK-LABEL:} // end sil function '$s26allocboxtostack_localapply15call2localfuncsSiyF'
183+
public func call2localfuncs() -> Int {
184+
var a1 = 1
185+
186+
@inline(never)
187+
func innerFunction() {
188+
a1 += 1
189+
}
190+
191+
innerFunction()
192+
innerFunction()
193+
194+
return a1
195+
}
196+

0 commit comments

Comments
 (0)