Skip to content

Commit aa6347c

Browse files
authored
Merge pull request swiftlang#24153 from atrick/fix-let-exclusivity
Add a SILGenCleanup pass and CanonicalizeInstruction utility.
2 parents 27c6586 + 52237aa commit aa6347c

24 files changed

+913
-213
lines changed

include/swift/SIL/DebugUtils.h

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,18 @@ inline SILInstruction *getSingleNonDebugUser(SILValue V) {
171171
/// Precondition: The instruction may only have debug instructions as uses.
172172
/// If the iterator \p InstIter references any deleted instruction, it is
173173
/// incremented.
174-
inline void eraseFromParentWithDebugInsts(SILInstruction *I,
175-
SILBasicBlock::iterator &InstIter) {
174+
///
175+
/// \p callBack will be invoked before each instruction is deleted. \p callBack
176+
/// is not responsible for deleting the instruction because this utility
177+
/// unconditionally deletes the \p I and its debug users.
178+
///
179+
/// Returns an iterator to the next non-deleted instruction after \p I.
180+
inline SILBasicBlock::iterator eraseFromParentWithDebugInsts(
181+
SILInstruction *I, llvm::function_ref<void(SILInstruction *)> callBack =
182+
[](SILInstruction *) {}) {
183+
184+
auto nextII = std::next(I->getIterator());
185+
176186
auto results = I->getResults();
177187

178188
bool foundAny;
@@ -183,26 +193,16 @@ inline void eraseFromParentWithDebugInsts(SILInstruction *I,
183193
foundAny = true;
184194
auto *User = result->use_begin()->getUser();
185195
assert(User->isDebugInstruction());
186-
if (InstIter == User->getIterator())
187-
InstIter++;
188-
196+
if (nextII == User->getIterator())
197+
nextII++;
198+
callBack(User);
189199
User->eraseFromParent();
190200
}
191201
}
192202
} while (foundAny);
193203

194-
if (InstIter == I->getIterator())
195-
++InstIter;
196-
197204
I->eraseFromParent();
198-
}
199-
200-
/// Erases the instruction \p I from it's parent block and deletes it, including
201-
/// all debug instructions which use \p I.
202-
/// Precondition: The instruction may only have debug instructions as uses.
203-
inline void eraseFromParentWithDebugInsts(SILInstruction *I) {
204-
SILBasicBlock::iterator nullIter;
205-
eraseFromParentWithDebugInsts(I, nullIter);
205+
return nextII;
206206
}
207207

208208
/// Return true if the def-use graph rooted at \p V contains any non-debug,

include/swift/SIL/SILInstruction.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3252,9 +3252,27 @@ class BeginBorrowInst
32523252
SingleValueInstruction> {
32533253
friend class SILBuilder;
32543254

3255+
/// Predicate used to filter EndBorrowRange.
3256+
struct UseToEndBorrow;
3257+
32553258
BeginBorrowInst(SILDebugLocation DebugLoc, SILValue LValue)
32563259
: UnaryInstructionBase(DebugLoc, LValue,
32573260
LValue->getType().getObjectType()) {}
3261+
3262+
public:
3263+
using EndBorrowRange =
3264+
OptionalTransformRange<use_range, UseToEndBorrow, use_iterator>;
3265+
3266+
/// Return a range over all EndBorrow instructions for this BeginBorrow.
3267+
EndBorrowRange getEndBorrows() const;
3268+
3269+
/// Return the single use of this BeginBorrowInst, not including any
3270+
/// EndBorrowInst uses, or return nullptr if the borrow is dead or has
3271+
/// multiple uses.
3272+
///
3273+
/// Useful for matching common SILGen patterns that emit one borrow per use,
3274+
/// and simplifying pass logic.
3275+
Operand *getSingleNonEndingUse() const;
32583276
};
32593277

32603278
/// Represents a store of a borrowed value into an address. Returns the borrowed
@@ -3346,6 +3364,20 @@ class EndBorrowInst
33463364
}
33473365
};
33483366

3367+
struct BeginBorrowInst::UseToEndBorrow {
3368+
Optional<EndBorrowInst *> operator()(Operand *use) const {
3369+
if (auto borrow = dyn_cast<EndBorrowInst>(use->getUser())) {
3370+
return borrow;
3371+
} else {
3372+
return None;
3373+
}
3374+
}
3375+
};
3376+
3377+
inline auto BeginBorrowInst::getEndBorrows() const -> EndBorrowRange {
3378+
return EndBorrowRange(getUses(), UseToEndBorrow());
3379+
}
3380+
33493381
/// Different kinds of access.
33503382
enum class SILAccessKind : uint8_t {
33513383
/// An access which takes uninitialized memory and initializes it.

include/swift/SILOptimizer/Analysis/SimplifyInstruction.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ SILValue simplifyInstruction(SILInstruction *I);
3838
///
3939
/// If it is nonnull, eraseNotify will be called before each instruction is
4040
/// deleted.
41-
void replaceAllSimplifiedUsesAndErase(
41+
SILBasicBlock::iterator replaceAllSimplifiedUsesAndErase(
4242
SILInstruction *I, SILValue result,
4343
std::function<void(SILInstruction *)> eraseNotify = nullptr);
4444

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,8 @@ PASS(SideEffectsDumper, "side-effects-dump",
267267
"Print Side-Effect Information for all Functions")
268268
PASS(IRGenPrepare, "irgen-prepare",
269269
"Cleanup SIL in preparation for IRGen")
270+
PASS(SILGenCleanup, "silgen-cleanup",
271+
"Cleanup SIL in preparation for diagnostics")
270272
PASS(SILCombine, "sil-combine",
271273
"Combine SIL Instructions via Peephole Optimization")
272274
PASS(SILDebugInfoGenerator, "sil-debuginfo-gen",
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
//===-- CanonicalizeInstruction.h - canonical SIL peepholes -----*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2019 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+
/// SSA-peephole transformations that yield a more canonical SIL representation.
14+
///
15+
/// Unlike simplifyInstruction, these transformations may effect any
16+
/// instruction, not only single-values, and may arbitrarily generate new SIL
17+
/// instructions.
18+
///
19+
/// Unlike SILCombine, these peepholes must work on 'raw' SIL form and should be
20+
/// limited to those necessary to aid in diagnostics and other mandatory
21+
/// pipelin/e passes. Optimization may only be done to the extent that it
22+
/// neither interferes with diagnostics nor increases compile time.
23+
///
24+
//===----------------------------------------------------------------------===//
25+
26+
#ifndef SWIFT_SILOPTIMIZER_UTILS_CANONICALIZEINSTRUCTION_H
27+
#define SWIFT_SILOPTIMIZER_UTILS_CANONICALIZEINSTRUCTION_H
28+
29+
#include "swift/SIL/SILBasicBlock.h"
30+
#include "swift/SIL/SILInstruction.h"
31+
#include "llvm/Support/Debug.h"
32+
33+
namespace swift {
34+
35+
/// Abstract base class. Implements all canonicalization transforms. Extended by
36+
/// passes to be notified of each SIL modification.
37+
struct CanonicalizeInstruction {
38+
// May be overriden by passes.
39+
static constexpr const char *defaultDebugType = "sil-canonicalize";
40+
const char *debugType = defaultDebugType;
41+
42+
CanonicalizeInstruction(const char *passDebugType) {
43+
#ifndef NDEBUG
44+
if (llvm::DebugFlag && !llvm::isCurrentDebugType(debugType))
45+
debugType = passDebugType;
46+
#endif
47+
}
48+
49+
virtual ~CanonicalizeInstruction();
50+
51+
/// Rewrite this instruction, based on its operands and uses, into a more
52+
/// canonical representation.
53+
///
54+
/// Return an iterator to the next instruction or to the end of the block.
55+
/// The returned iterator will follow any newly added or to-be-deleted
56+
/// instructions, regardless of whether the pass immediately deletes the
57+
/// instructions or simply records them for later deletion.
58+
///
59+
/// To (re)visit new instructions, override notifyNewInstruction().
60+
///
61+
/// To determine if any transformation at all occurred, override
62+
/// notifyNewInstruction(), killInstruction(), and notifyNewUsers().
63+
///
64+
/// Warning: If the \p inst argument is killed and the client immediately
65+
/// erases \p inst, then it may be an invalid pointer upon return.
66+
SILBasicBlock::iterator canonicalize(SILInstruction *inst);
67+
68+
/// Record a newly generated instruction.
69+
virtual void notifyNewInstruction(SILInstruction *inst) = 0;
70+
71+
/// Kill an instruction that no longer has uses, or whose side effect is now
72+
/// represented by a different instruction. The client can defer erasing the
73+
/// instruction but must eventually erase all killed instructions to restore
74+
/// valid SIL.
75+
///
76+
/// This callback should not mutate any other instructions. It may only delete
77+
/// the given argument. It will be called separately for each end-of-scope and
78+
/// debug use before being called on the instruction they use.
79+
virtual void killInstruction(SILInstruction *inst) = 0;
80+
81+
/// Record a SIL value that has acquired new users.
82+
virtual void notifyHasNewUsers(SILValue value) = 0;
83+
};
84+
85+
} // end namespace swift
86+
87+
#endif // SWIFT_SILOPTIMIZER_UTILS_CANONICALIZEINSTRUCTION_H

include/swift/SILOptimizer/Utils/Local.h

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -64,31 +64,14 @@ recursivelyDeleteTriviallyDeadInstructions(
6464
ArrayRef<SILInstruction*> I, bool Force = false,
6565
llvm::function_ref<void(SILInstruction *)> C = [](SILInstruction *){});
6666

67-
/// For each of the given instructions, if they are dead delete them
68-
/// along with their dead operands.
69-
///
70-
/// \param I The ArrayRef of instructions to be deleted.
71-
/// \param InstIter is updated to the next valid instruction if it points to any
72-
/// deleted instruction, including debug values.
73-
/// \param Force If Force is set, don't check if the top level instructions
74-
/// are considered dead - delete them regardless.
75-
/// \param C a callback called whenever an instruction is deleted.
76-
void recursivelyDeleteTriviallyDeadInstructions(
77-
ArrayRef<SILInstruction *> I, SILBasicBlock::iterator &InstIter,
78-
bool Force = false,
79-
llvm::function_ref<void(SILInstruction *)> C = [](SILInstruction *) {});
80-
8167
/// If the given instruction is dead, delete it along with its dead
8268
/// operands.
8369
///
8470
/// \param I The instruction to be deleted.
8571
/// \param Force If Force is set, don't check if the top level instruction is
8672
/// considered dead - delete it regardless.
8773
/// \param C a callback called whenever an instruction is deleted.
88-
///
89-
/// Returns a valid instruction iterator to the next nondeleted instruction
90-
/// after `I`.
91-
SILBasicBlock::iterator recursivelyDeleteTriviallyDeadInstructions(
74+
void recursivelyDeleteTriviallyDeadInstructions(
9275
SILInstruction *I, bool Force = false,
9376
llvm::function_ref<void(SILInstruction *)> C = [](SILInstruction *) {});
9477

lib/SIL/SILInstruction.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,20 @@ void SILInstruction::replaceAllUsesPairwiseWith(
279279
}
280280
}
281281

282+
Operand *BeginBorrowInst::getSingleNonEndingUse() const {
283+
Operand *singleUse = nullptr;
284+
for (auto *use : getUses()) {
285+
if (isa<EndBorrowInst>(use->getUser()))
286+
continue;
287+
288+
if (singleUse)
289+
return nullptr;
290+
291+
singleUse = use;
292+
}
293+
return singleUse;
294+
}
295+
282296
namespace {
283297
class InstructionDestroyer
284298
: public SILInstructionVisitor<InstructionDestroyer> {

lib/SILOptimizer/Analysis/SimplifyInstruction.cpp

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,22 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
99
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
1010
//
1111
//===----------------------------------------------------------------------===//
12+
///
13+
/// An SSA-peephole analysis. Given a single-value instruction, find an existing
14+
/// equivalent but less costly or more canonical SIL value.
15+
///
16+
/// This analysis must handle 'raw' SIL form. It should be possible to perform
17+
/// the substitution discovered by the analysis without interfering with
18+
/// subsequent diagnostic passes.
19+
///
20+
//===----------------------------------------------------------------------===//
1221

1322
#define DEBUG_TYPE "sil-simplify"
1423
#include "swift/SILOptimizer/Analysis/SimplifyInstruction.h"
@@ -665,29 +674,40 @@ SILValue swift::simplifyInstruction(SILInstruction *I) {
665674
/// Replace an instruction with a simplified result, including any debug uses,
666675
/// and erase the instruction. If the instruction initiates a scope, do not
667676
/// replace the end of its scope; it will be deleted along with its parent.
668-
void swift::replaceAllSimplifiedUsesAndErase(
677+
///
678+
/// This is a simple transform based on the above analysis.
679+
///
680+
/// Return an iterator to the next (nondeleted) instruction.
681+
SILBasicBlock::iterator swift::replaceAllSimplifiedUsesAndErase(
669682
SILInstruction *I, SILValue result,
670-
std::function<void(SILInstruction *)> eraseNotify) {
683+
std::function<void(SILInstruction *)> eraseHandler) {
671684

672685
auto *SVI = cast<SingleValueInstruction>(I);
673686
assert(SVI != result && "Cannot RAUW a value with itself");
687+
SILBasicBlock::iterator nextii = std::next(I->getIterator());
674688

675689
// Only SingleValueInstructions are currently simplified.
676690
while (!SVI->use_empty()) {
677691
Operand *use = *SVI->use_begin();
678692
SILInstruction *user = use->getUser();
679693
// Erase the end of scope marker.
680694
if (isEndOfScopeMarker(user)) {
681-
if (eraseNotify)
682-
eraseNotify(user);
683-
user->eraseFromParent();
695+
if (&*nextii == user)
696+
++nextii;
697+
if (eraseHandler)
698+
eraseHandler(user);
699+
else
700+
user->eraseFromParent();
684701
continue;
685702
}
686703
use->set(result);
687704
}
688-
I->eraseFromParent();
689-
if (eraseNotify)
690-
eraseNotify(I);
705+
if (eraseHandler)
706+
eraseHandler(I);
707+
else
708+
I->eraseFromParent();
709+
710+
return nextii;
691711
}
692712

693713
/// Simplify invocations of builtin operations that may overflow.

lib/SILOptimizer/Mandatory/CMakeLists.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ silopt_register_sources(
22
AccessEnforcementSelection.cpp
33
AccessMarkerElimination.cpp
44
AddressLowering.cpp
5+
ClosureLifetimeFixup.cpp
56
ConstantPropagation.cpp
67
DefiniteInitialization.cpp
78
DIMemoryUseCollector.cpp
@@ -15,8 +16,8 @@ silopt_register_sources(
1516
MandatoryInlining.cpp
1617
PredictableMemOpt.cpp
1718
PMOMemoryUseCollector.cpp
18-
SemanticARCOpts.cpp
19-
ClosureLifetimeFixup.cpp
2019
RawSILInstLowering.cpp
20+
SemanticARCOpts.cpp
21+
SILGenCleanup.cpp
2122
YieldOnceCheck.cpp
2223
)

lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -861,8 +861,7 @@ void LifetimeChecker::handleLoadForTypeOfSelfUse(const DIMemoryUse &Use) {
861861
valueMetatype->getLoc(), metatypeArgument,
862862
valueMetatype->getType());
863863
}
864-
replaceAllSimplifiedUsesAndErase(valueMetatype, metatypeArgument,
865-
[](SILInstruction*) { });
864+
replaceAllSimplifiedUsesAndErase(valueMetatype, metatypeArgument);
866865
}
867866
}
868867

0 commit comments

Comments
 (0)