Skip to content

Commit 3236bc2

Browse files
committed
[region-isolation] Refactor out the stubify dead function if no longer used functionality from move only checker into its own pass and put it before region based isolation.
I am doing this since region based isolation hit the same issue that the move checker did. So it makes sense to refactor the functionality into its own pass and move it into a helper pass that runs before both. It is very conservative and only stubifies functions that the specialization passes explicitly mark as this being ok to be done to.
1 parent e51c3bd commit 3236bc2

File tree

13 files changed

+192
-66
lines changed

13 files changed

+192
-66
lines changed

include/swift/AST/SemanticAttrs.def

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,15 +137,25 @@ SEMANTICS_ATTR(NO_PERFORMANCE_ANALYSIS, "no_performance_analysis")
137137
// errors that may cause the user to think there is a bug in the compiler.
138138
SEMANTICS_ATTR(NO_MOVEONLY_DIAGNOSTICS, "sil.optimizer.moveonly.diagnostic.ignore")
139139

140-
// Tell the move-only checker to delete this function body instead of performing
140+
// Tell the compiler that it can delete this function body instead of performing
141141
// diagnostics if the function is not used at the time of checking.
142+
//
143+
// This is currently used by allocbox to stack and capture promotion.
144+
//
145+
// MoveOnlyChecker and TransferNonSendable both potentially delete functions.
146+
//
147+
// DISCUSSION: We want to make sure that in cases where we may emit diagnostics
148+
// twice, we just delete the original function. When running alloc box to stack
149+
// and capture promotion, we may not have eliminated all of the relevant uses so
150+
// we may not know to eliminate them.
151+
//
142152
// When allocbox to stack specializes a function, we do not want to emit move
143-
// errors twice, once on the specialized and once on the original
144-
// function. The semantics of the closure with regards to move-only values may
145-
// also be dependent on stack promotion. Therefore, we mark the original with
146-
// this attribute, so that after it's promoted, the original function gets deleted
153+
// errors twice, once on the specialized and once on the original function. The
154+
// semantics of the closure with regards to move-only values may also be
155+
// dependent on stack promotion. Therefore, we mark the original with this
156+
// attribute, so that after it's promoted, the original function gets deleted
147157
// instead of raising spurious diagnostics.
148-
SEMANTICS_ATTR(MOVEONLY_DELETE_IF_UNUSED, "sil.optimizer.moveonly.delete_if_unused")
158+
SEMANTICS_ATTR(DELETE_IF_UNUSED, "sil.optimizer.delete_if_unused")
149159

150160
// Force the use of the frame pointer for the specified function
151161
SEMANTICS_ATTR(USE_FRAME_POINTER, "use_frame_pointer")

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,8 @@ PASS(MoveOnlyBorrowToDestructureTransform,
497497
"any move only errors with respect to non-borrow+projection uses")
498498
PASS(ReferenceBindingTransform, "sil-reference-binding-transform",
499499
"Check/transform reference bindings")
500+
PASS(DiagnosticDeadFunctionElimination, "sil-diagnostic-dead-function-elim",
501+
"Eliminate dead functions from early specialization optimizations before we run later diagnostics")
500502
PASS(PruneVTables, "prune-vtables",
501503
"Mark class methods that do not require vtable dispatch")
502504
PASS_RANGE(AllPasses, AADumper, PruneVTables)

lib/SILOptimizer/Mandatory/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,5 @@ target_sources(swiftSILOptimizer PRIVATE
4949
OSLogOptimization.cpp
5050
MoveOnlyWrappedTypeEliminator.cpp
5151
RegionAnalysisInvalidationTransform.cpp
52+
DiagnosticDeadFunctionElimination.cpp
5253
OwnershipModelEliminator.cpp)

lib/SILOptimizer/Mandatory/CapturePromotion.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646

4747
#include "swift/AST/DiagnosticsSIL.h"
4848
#include "swift/AST/GenericEnvironment.h"
49+
#include "swift/AST/SemanticAttrs.h"
4950
#include "swift/Basic/FrozenMultiMap.h"
5051
#include "swift/SIL/OwnershipUtils.h"
5152
#include "swift/SIL/SILCloner.h"
@@ -1473,6 +1474,10 @@ processPartialApplyInst(SILOptFunctionBuilder &funcBuilder,
14731474
funcBuilder, pai, fri, promotableIndices, f->getResilienceExpansion());
14741475
worklist.push_back(clonedFn);
14751476

1477+
// Mark the original partial apply function as deletable if it doesn't have
1478+
// uses later.
1479+
fri->getReferencedFunction()->addSemanticsAttr(semantics::DELETE_IF_UNUSED);
1480+
14761481
// Initialize a SILBuilder and create a function_ref referencing the cloned
14771482
// closure.
14781483
SILBuilderWithScope builder(pai);
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
//===--- DiagnosticDeadFunctionElimination.cpp ----------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
///
13+
/// Delete functions that early diagnostic specialization passes mark as being
14+
/// able to be DCE-ed if there are no further uses. This prevents later
15+
/// diagnostic passes from emitting diagnostics both on the original function
16+
/// and the diagnostic function.
17+
///
18+
//===----------------------------------------------------------------------===//
19+
20+
#define DEBUG_TYPE "sil-diagnostic-dead-function-eliminator"
21+
22+
#include "swift/AST/SemanticAttrs.h"
23+
#include "swift/SIL/SILBuilder.h"
24+
#include "swift/SILOptimizer/PassManager/Passes.h"
25+
#include "swift/SILOptimizer/PassManager/Transforms.h"
26+
#include "llvm/Support/Debug.h"
27+
28+
using namespace swift;
29+
30+
//===----------------------------------------------------------------------===//
31+
// MARK: Top Level Entrypoint
32+
//===----------------------------------------------------------------------===//
33+
34+
namespace {
35+
36+
struct DiagnosticDeadFunctionEliminator : SILFunctionTransform {
37+
void run() override {
38+
auto *fn = getFunction();
39+
40+
// If an earlier pass asked us to eliminate the function body if it's
41+
// unused, and the function is in fact unused, do that now.
42+
if (!fn->hasSemanticsAttr(semantics::DELETE_IF_UNUSED) ||
43+
fn->getRefCount() != 0 ||
44+
isPossiblyUsedExternally(fn->getLinkage(),
45+
fn->getModule().isWholeModule())) {
46+
return;
47+
}
48+
49+
LLVM_DEBUG(llvm::dbgs()
50+
<< "===> Stubbifying unused function " << fn->getName()
51+
<< "'s body that was marked for deletion\n");
52+
// Remove all non-entry blocks.
53+
auto entryBB = fn->begin();
54+
auto nextBB = std::next(entryBB);
55+
56+
while (nextBB != fn->end()) {
57+
auto thisBB = nextBB;
58+
++nextBB;
59+
thisBB->eraseFromParent();
60+
}
61+
62+
// Rewrite the entry block to only contain an unreachable.
63+
auto loc = entryBB->begin()->getLoc();
64+
entryBB->eraseAllInstructions(fn->getModule());
65+
{
66+
SILBuilder b(&*entryBB);
67+
b.createUnreachable(loc);
68+
}
69+
70+
// If the function has shared linkage, reduce this version to private
71+
// linkage, because we don't want the deleted-body form to win in any
72+
// ODR shootouts.
73+
if (fn->getLinkage() == SILLinkage::Shared) {
74+
fn->setLinkage(SILLinkage::Private);
75+
fn->setSerialized(IsNotSerialized);
76+
}
77+
78+
invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody);
79+
}
80+
};
81+
82+
} // namespace
83+
84+
SILTransform *swift::createDiagnosticDeadFunctionElimination() {
85+
return new DiagnosticDeadFunctionEliminator();
86+
}

lib/SILOptimizer/Mandatory/MoveOnlyChecker.cpp

Lines changed: 0 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -222,55 +222,6 @@ class MoveOnlyCheckerPass : public SILFunctionTransform {
222222
assert(fn->getModule().getStage() == SILStage::Raw &&
223223
"Should only run on Raw SIL");
224224

225-
// If an earlier pass asked us to eliminate the function body if it's
226-
// unused, and the function is in fact unused, do that now.
227-
if (fn->hasSemanticsAttr(semantics::MOVEONLY_DELETE_IF_UNUSED)) {
228-
if (fn->getRefCount() == 0
229-
&& !isPossiblyUsedExternally(fn->getLinkage(),
230-
fn->getModule().isWholeModule())) {
231-
LLVM_DEBUG(llvm::dbgs() << "===> Deleting unused function " << fn->getName() << "'s body that was marked for deletion\n");
232-
// Remove all non-entry blocks.
233-
auto entryBB = fn->begin();
234-
auto nextBB = std::next(entryBB);
235-
236-
while (nextBB != fn->end()) {
237-
auto thisBB = nextBB;
238-
++nextBB;
239-
thisBB->eraseFromParent();
240-
}
241-
242-
// Rewrite the entry block to only contain an unreachable.
243-
auto loc = entryBB->begin()->getLoc();
244-
entryBB->eraseAllInstructions(fn->getModule());
245-
{
246-
SILBuilder b(&*entryBB);
247-
b.createUnreachable(loc);
248-
}
249-
250-
// If the function has shared linkage, reduce this version to private
251-
// linkage, because we don't want the deleted-body form to win in any
252-
// ODR shootouts.
253-
if (fn->getLinkage() == SILLinkage::Shared) {
254-
fn->setLinkage(SILLinkage::Private);
255-
}
256-
257-
invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody);
258-
return;
259-
}
260-
// If the function wasn't unused, let it continue into diagnostics.
261-
// This would come up if a closure function somehow was used in different
262-
// functions with different escape analysis results. This shouldn't really
263-
// be possible, and we should try harder to make it impossible, but if
264-
// it does happen now, the least bad thing to do is to proceed with
265-
// move checking. This will either succeed and make sure the original
266-
// function contains valid SIL, or raise errors relating to the use
267-
// of the captures in an escaping way, which is the right thing to do
268-
// if they are in fact escapable.
269-
LLVM_DEBUG(llvm::dbgs() << "===> Function " << fn->getName()
270-
<< " was marked to be deleted, but has uses. Continuing with move checking\n");
271-
272-
}
273-
274225
// If an earlier pass told use to not emit diagnostics for this function,
275226
// clean up any copies, invalidate the analysis, and return early.
276227
if (fn->hasSemanticsAttr(semantics::NO_MOVEONLY_DIAGNOSTICS)) {

lib/SILOptimizer/PassManager/PassPipeline.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,10 @@ static void addMandatoryDiagnosticOptPipeline(SILPassPipelinePlan &P) {
139139

140140
P.addAddressLowering();
141141

142+
// Before we run later semantic optimizations, eliminate simple functions that
143+
// we specialized to ensure that we do not emit diagnostics twice.
144+
P.addDiagnosticDeadFunctionElimination();
145+
142146
P.addFlowIsolation();
143147

144148
//===---

lib/SILOptimizer/Transforms/AllocBoxToStack.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1057,7 +1057,7 @@ specializeApplySite(SILOptFunctionBuilder &FuncBuilder, ApplySite Apply,
10571057

10581058
// Set the moveonly delete-if-unused flag so we do not emit an error on the
10591059
// original once we promote all its current uses.
1060-
F->addSemanticsAttr(semantics::MOVEONLY_DELETE_IF_UNUSED);
1060+
F->addSemanticsAttr(semantics::DELETE_IF_UNUSED);
10611061

10621062
// If any of our promoted callee arg indices were originally noncopyable let
10631063
// boxes, convert them from having escaping to having non-escaping

test/SILOptimizer/allocbox_to_stack.sil

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ bb0(%0 : $Int):
398398
}
399399

400400
// CHECK-LABEL: sil private @$s6struct8useStack1tySi_tFSiycfU_Tf0s_n
401-
// CHECK-LABEL: sil private [_semantics "sil.optimizer.moveonly.delete_if_unused"] @$s6struct8useStack1tySi_tFSiycfU_
401+
// CHECK-LABEL: sil private [_semantics "sil.optimizer.delete_if_unused"] @$s6struct8useStack1tySi_tFSiycfU_
402402
// struct.(useStack (t : Swift.Int) -> ()).(closure #1)
403403
sil private @$s6struct8useStack1tySi_tFSiycfU_ : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int>) -> Int {
404404
bb0(%0 : $<τ_0_0> { var τ_0_0 } <Int>):
@@ -651,7 +651,7 @@ bb0(%0 : $Int, %1 : $*S<T>):
651651
return %9 : $Bool
652652
}
653653

654-
// CHECK-LABEL: sil shared [_semantics "sil.optimizer.moveonly.delete_if_unused"] @closure1
654+
// CHECK-LABEL: sil shared [_semantics "sil.optimizer.delete_if_unused"] @closure1
655655
sil shared @closure1 : $@convention(thin) <T where T : Count> (Int, @owned <τ_0_0 : Count> { var S<τ_0_0> } <T>) -> Bool {
656656
// CHECK: bb0
657657
bb0(%0 : $Int, %1 : $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>):
@@ -673,7 +673,7 @@ bb0(%0 : $Int, %1 : $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>):
673673
// CHECK: return
674674
// CHECK-NOT: bb1
675675

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

test/SILOptimizer/allocbox_to_stack_ownership.sil

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ 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.moveonly.delete_if_unused"] [ossa] @$s6struct8useStack1tySi_tFSiycfU_
397+
// CHECK-LABEL: sil private [transparent] [_semantics "sil.optimizer.delete_if_unused"] [ossa] @$s6struct8useStack1tySi_tFSiycfU_
398398
// struct.(useStack (t : Swift.Int) -> ()).(closure #1)
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>):
@@ -751,7 +751,7 @@ bb0(%0 : $Int, %1 : $*S<T>):
751751
return %9 : $Bool
752752
}
753753

754-
// CHECK-LABEL: sil shared [_semantics "sil.optimizer.moveonly.delete_if_unused"] [ossa] @closure1
754+
// CHECK-LABEL: sil shared [_semantics "sil.optimizer.delete_if_unused"] [ossa] @closure1
755755
sil shared [ossa] @closure1 : $@convention(thin) <T where T : Count> (Int, @owned <τ_0_0 : Count> { var S<τ_0_0> } <T>) -> Bool {
756756
// CHECK: bb0
757757
bb0(%0 : $Int, %1 : @owned $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>):
@@ -773,7 +773,7 @@ bb0(%0 : $Int, %1 : @owned $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>)
773773
// CHECK: return
774774
// CHECK-NOT: bb1
775775

776-
// CHECK-LABEL: sil shared [_semantics "sil.optimizer.moveonly.delete_if_unused"] [ossa] @closure2
776+
// CHECK-LABEL: sil shared [_semantics "sil.optimizer.delete_if_unused"] [ossa] @closure2
777777
sil shared [ossa] @closure2 : $@convention(thin) <T where T : Count> (Int, @owned <τ_0_0 : Count> { var S<τ_0_0> } <T>) -> Bool {
778778
// CHECK: bb0
779779
bb0(%0 : $Int, %1 : @owned $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>):

0 commit comments

Comments
 (0)