Skip to content

Commit 6536c5b

Browse files
authored
Merge pull request swiftlang#40040 from gottesmm/pr-270a55632e2b31495bf1b801e01cc69e742ddf6b
[moveOnly] Add checker that validates that if a let copyable value has _move applied to it, the let doesn't have any later uses.
2 parents b6643b2 + 4aa4ac3 commit 6536c5b

File tree

20 files changed

+812
-17
lines changed

20 files changed

+812
-17
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,8 @@ ERROR(sil_keypath_index_operand_type_conflict,none,
633633
ERROR(sil_keypath_no_use_of_operand_in_pattern,none,
634634
"operand %0 is not referenced by any component in the pattern",
635635
(unsigned))
636+
ERROR(sil_movevalue_invalid_optional_attribute,none,
637+
"Optional attribute '[%0]' can not be applied to move_value", (StringRef))
636638

637639
// SIL Basic Blocks
638640
ERROR(expected_sil_block_name,none,

include/swift/AST/DiagnosticsSIL.def

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -733,5 +733,15 @@ ERROR(sil_moveonlychecker_value_consumed_more_than_once, none,
733733
NOTE(sil_moveonlychecker_consuming_use_here, none,
734734
"consuming use", ())
735735

736+
// move kills copyable values checker diagnostics
737+
ERROR(sil_movekillscopyablevalue_value_consumed_more_than_once, none,
738+
"'%0' used after being moved", (StringRef))
739+
NOTE(sil_movekillscopyablevalue_move_here, none,
740+
"move here", ())
741+
NOTE(sil_movekillscopyablevalue_use_here, none,
742+
"use here", ())
743+
NOTE(sil_movekillscopyablevalue_value_consumed_in_loop, none,
744+
"cyclic move here. move will occur multiple times in the loop", ())
745+
736746
#define UNDEFINE_DIAGNOSTIC_MACROS
737747
#include "DefineDiagnosticMacros.h"

include/swift/SIL/PrunedLiveness.h

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
#define SWIFT_SILOPTIMIZER_UTILS_PRUNEDLIVENESS_H
9292

9393
#include "swift/SIL/SILBasicBlock.h"
94+
#include "llvm/ADT/MapVector.h"
9495

9596
namespace swift {
9697

@@ -221,11 +222,19 @@ class PrunedLiveness {
221222
// they may be the last use in the block.
222223
//
223224
// Non-lifetime-ending within a LiveOut block are uninteresting.
224-
llvm::SmallDenseMap<SILInstruction *, bool, 8> users;
225+
llvm::SmallMapVector<SILInstruction *, bool, 8> users;
226+
227+
/// A side array that stores any non lifetime ending uses we find in live out
228+
/// blocks. This is used to enable our callers to emit errors on non-lifetime
229+
/// ending uses that extend liveness into a loop body.
230+
SmallSetVector<SILInstruction *, 8> *nonLifetimeEndingUsesInLiveOut;
225231

226232
public:
227-
PrunedLiveness(SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr)
228-
: liveBlocks(discoveredBlocks) {}
233+
PrunedLiveness(SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr,
234+
SmallSetVector<SILInstruction *, 8>
235+
*nonLifetimeEndingUsesInLiveOut = nullptr)
236+
: liveBlocks(discoveredBlocks),
237+
nonLifetimeEndingUsesInLiveOut(nonLifetimeEndingUsesInLiveOut) {}
229238

230239
bool empty() const {
231240
assert(!liveBlocks.empty() || users.empty());
@@ -235,6 +244,8 @@ class PrunedLiveness {
235244
void clear() {
236245
liveBlocks.clear();
237246
users.clear();
247+
if (nonLifetimeEndingUsesInLiveOut)
248+
nonLifetimeEndingUsesInLiveOut->clear();
238249
}
239250

240251
unsigned numLiveBlocks() const { return liveBlocks.numLiveBlocks(); }
@@ -245,6 +256,47 @@ class PrunedLiveness {
245256
return liveBlocks.getDiscoveredBlocks();
246257
}
247258

259+
using NonLifetimeEndingUsesInLiveOutRange =
260+
iterator_range<SILInstruction *const *>;
261+
262+
NonLifetimeEndingUsesInLiveOutRange
263+
getNonLifetimeEndingUsesInLiveOut() const {
264+
assert(nonLifetimeEndingUsesInLiveOut &&
265+
"Called without passing in nonLifetimeEndingUsesInLiveOut to "
266+
"constructor?!");
267+
return llvm::make_range(nonLifetimeEndingUsesInLiveOut->begin(),
268+
nonLifetimeEndingUsesInLiveOut->end());
269+
}
270+
271+
using NonLifetimeEndingUsesInLiveOutBlocksRange =
272+
TransformRange<NonLifetimeEndingUsesInLiveOutRange,
273+
function_ref<SILBasicBlock *(const SILInstruction *&)>>;
274+
NonLifetimeEndingUsesInLiveOutBlocksRange
275+
getNonLifetimeEndingUsesInLiveOutBlocks() const {
276+
function_ref<SILBasicBlock *(const SILInstruction *&)> op;
277+
op = [](const SILInstruction *&ptr) -> SILBasicBlock * {
278+
return ptr->getParent();
279+
};
280+
return NonLifetimeEndingUsesInLiveOutBlocksRange(
281+
getNonLifetimeEndingUsesInLiveOut(), op);
282+
}
283+
284+
using UserRange = iterator_range<const std::pair<SILInstruction *, bool> *>;
285+
UserRange getAllUsers() const {
286+
return llvm::make_range(users.begin(), users.end());
287+
}
288+
289+
using UserBlockRange = TransformRange<
290+
UserRange,
291+
function_ref<SILBasicBlock *(const std::pair<SILInstruction *, bool> &)>>;
292+
UserBlockRange getAllUserBlocks() const {
293+
function_ref<SILBasicBlock *(const std::pair<SILInstruction *, bool> &)> op;
294+
op = [](const std::pair<SILInstruction *, bool> &pair) -> SILBasicBlock * {
295+
return pair.first->getParent();
296+
};
297+
return UserBlockRange(getAllUsers(), op);
298+
}
299+
248300
void initializeDefBlock(SILBasicBlock *defBB) {
249301
liveBlocks.initializeDefBlock(defBB);
250302
}

include/swift/SIL/SILCloner.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1731,9 +1731,10 @@ void SILCloner<ImplClass>::visitExplicitCopyValueInst(
17311731
template <typename ImplClass>
17321732
void SILCloner<ImplClass>::visitMoveValueInst(MoveValueInst *Inst) {
17331733
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
1734-
recordClonedInstruction(
1735-
Inst, getBuilder().createMoveValue(getOpLocation(Inst->getLoc()),
1736-
getOpValue(Inst->getOperand())));
1734+
auto *MVI = getBuilder().createMoveValue(getOpLocation(Inst->getLoc()),
1735+
getOpValue(Inst->getOperand()));
1736+
MVI->setAllowsDiagnostics(Inst->getAllowDiagnostics());
1737+
recordClonedInstruction(Inst, MVI);
17371738
}
17381739

17391740
template <typename ImplClass>

include/swift/SIL/SILInstruction.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7337,8 +7337,17 @@ class MoveValueInst
73377337
SingleValueInstruction> {
73387338
friend class SILBuilder;
73397339

7340+
/// If set to true, we should emit the kill diagnostic for this move_value. If
7341+
/// set to false, we shouldn't emit such a diagnostic. This is a short term
7342+
/// addition until we get MoveOnly wrapper types into the SIL type system.
7343+
bool allowDiagnostics = false;
7344+
73407345
MoveValueInst(SILDebugLocation DebugLoc, SILValue operand)
73417346
: UnaryInstructionBase(DebugLoc, operand, operand->getType()) {}
7347+
7348+
public:
7349+
bool getAllowDiagnostics() const { return allowDiagnostics; }
7350+
void setAllowsDiagnostics(bool newValue) { allowDiagnostics = newValue; }
73427351
};
73437352

73447353
/// Given an object reference, return true iff it is non-nil and refers

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,9 @@ PASS(AssemblyVisionRemarkGenerator, "assembly-vision-remark-generator",
422422
"Emit assembly vision remarks that provide source level guidance of where runtime calls ended up")
423423
PASS(MoveOnlyChecker, "sil-move-only-checker",
424424
"Pass that enforces move only invariants on raw SIL")
425+
PASS(MoveKillsCopyableValuesChecker, "sil-move-kills-copyable-values-checker",
426+
"Pass that checks that any copyable (non-move only) value that is passed "
427+
"to _move do not have any uses later than the _move")
425428
PASS(PruneVTables, "prune-vtables",
426429
"Mark class methods that do not require vtable dispatch")
427430
PASS_RANGE(AllPasses, AADumper, PruneVTables)

lib/SIL/IR/SILPrinter.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1884,6 +1884,8 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
18841884
}
18851885

18861886
void visitMoveValueInst(MoveValueInst *I) {
1887+
if (I->getAllowDiagnostics())
1888+
*this << "[allows_diagnostics] ";
18871889
*this << getIDAndType(I->getOperand());
18881890
}
18891891

lib/SIL/Parser/ParseSIL.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3156,7 +3156,6 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B,
31563156
UNARY_INSTRUCTION(DestroyAddr)
31573157
UNARY_INSTRUCTION(CopyValue)
31583158
UNARY_INSTRUCTION(ExplicitCopyValue)
3159-
UNARY_INSTRUCTION(MoveValue)
31603159
UNARY_INSTRUCTION(EndBorrow)
31613160
UNARY_INSTRUCTION(DestructureStruct)
31623161
UNARY_INSTRUCTION(DestructureTuple)
@@ -3266,6 +3265,28 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B,
32663265
break;
32673266
}
32683267

3268+
case SILInstructionKind::MoveValueInst: {
3269+
bool allowsDiagnostics = false;
3270+
StringRef AttrName;
3271+
if (parseSILOptional(AttrName, *this)) {
3272+
if (!AttrName.equals("allows_diagnostics")) {
3273+
auto diag = diag::sil_movevalue_invalid_optional_attribute;
3274+
P.diagnose(InstLoc.getSourceLoc(), diag, AttrName);
3275+
return true;
3276+
}
3277+
allowsDiagnostics = true;
3278+
}
3279+
3280+
if (parseTypedValueRef(Val, B))
3281+
return true;
3282+
if (parseSILDebugLocation(InstLoc, B))
3283+
return true;
3284+
auto *MVI = B.createMoveValue(InstLoc, Val);
3285+
MVI->setAllowsDiagnostics(allowsDiagnostics);
3286+
ResultVal = MVI;
3287+
break;
3288+
}
3289+
32693290
case SILInstructionKind::LoadInst: {
32703291
Optional<LoadOwnershipQualifier> Qualifier;
32713292
SourceLoc AddrLoc;

lib/SIL/Utils/PrunedLiveness.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ void PrunedLiveness::updateForUse(SILInstruction *user, bool lifetimeEnding) {
8080
// Record all uses of blocks on the liveness boundary. For blocks marked
8181
// LiveWithin, the boundary is considered to be the last use in the block.
8282
if (!lifetimeEnding && useBlockLive == PrunedLiveBlocks::LiveOut) {
83+
if (nonLifetimeEndingUsesInLiveOut)
84+
nonLifetimeEndingUsesInLiveOut->insert(user);
8385
return;
8486
}
8587
// Note that a user may use the current value from multiple operands. If any
@@ -93,7 +95,7 @@ void PrunedLiveness::updateForUse(SILInstruction *user, bool lifetimeEnding) {
9395
//
9496
// This call is not considered the end of %val's lifetime. The @owned
9597
// argument must be copied.
96-
auto iterAndSuccess = users.try_emplace(user, lifetimeEnding);
98+
auto iterAndSuccess = users.insert({user, lifetimeEnding});
9799
if (!iterAndSuccess.second)
98100
iterAndSuccess.first->second &= lifetimeEnding;
99101
}

lib/SILOptimizer/Mandatory/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ target_sources(swiftSILOptimizer PRIVATE
1818
LowerHopToActor.cpp
1919
MandatoryInlining.cpp
2020
MoveOnlyChecker.cpp
21+
MoveKillsCopyableValuesChecker.cpp
2122
NestedSemanticFunctionCheck.cpp
2223
OptimizeHopToExecutor.cpp
2324
PerformanceDiagnostics.cpp

0 commit comments

Comments
 (0)