Skip to content

Commit 2ad0c1a

Browse files
authored
Merge pull request swiftlang#71998 from gottesmm/pr-ca981c22b983cdfc472ae9c1d737c76dae955aed
[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.
2 parents 7685371 + 5e623d7 commit 2ad0c1a

File tree

13 files changed

+188
-99
lines changed

13 files changed

+188
-99
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)