Skip to content

Commit b1738c6

Browse files
committed
[move-only] Implement MoveOnlyDeinitInsertion that converts destroy_value of known deinit move only types to call the deinit directly.
NOTE: If one does not define a deinit on a move only type, if a destroy_value lasts until IRGen time we will assert since I haven't implemented support in IRGen for the destroy value witness for move only types. That being said, just creating a deinit by adding to such a type: ``` deinit {} ``` is pretty low overhead for the experiments we want to use this for.
1 parent 527d5b3 commit b1738c6

File tree

6 files changed

+430
-9
lines changed

6 files changed

+430
-9
lines changed

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,8 @@ PASS(PartialApplySimplification, "partial-apply-simplification",
474474
"Transform partial_apply instructions into explicit closure box constructions")
475475
PASS(MovedAsyncVarDebugInfoPropagator, "sil-moved-async-var-dbginfo-propagator",
476476
"Propagate debug info from moved async vars after coroutine funclet boundaries")
477+
PASS(MoveOnlyDeinitInsertion, "sil-move-only-deinit-insertion",
478+
"After running move only checking, convert last destroy_values to deinit calls")
477479
PASS(PruneVTables, "prune-vtables",
478480
"Mark class methods that do not require vtable dispatch")
479481
PASS_RANGE(AllPasses, AADumper, PruneVTables)

lib/SILOptimizer/Mandatory/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ target_sources(swiftSILOptimizer PRIVATE
2626
MoveKillsCopyableValuesChecker.cpp
2727
MoveOnlyObjectChecker.cpp
2828
MoveOnlyAddressChecker.cpp
29+
MoveOnlyDeinitInsertion.cpp
2930
NestedSemanticFunctionCheck.cpp
3031
OptimizeHopToExecutor.cpp
3132
PerformanceDiagnostics.cpp
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
//===--- MoveOnlyDeinitInsertion.cpp --------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 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+
/// \file
14+
///
15+
/// This pass runs after move only checking has occured and transforms last
16+
/// destroy_value of move only types into a call to the move only types deinit.
17+
///
18+
//===----------------------------------------------------------------------===//
19+
20+
#define DEBUG_TYPE "sil-move-only-checker"
21+
22+
#include "swift/AST/DiagnosticEngine.h"
23+
#include "swift/AST/DiagnosticsSIL.h"
24+
#include "swift/AST/SubstitutionMap.h"
25+
#include "swift/Basic/Defer.h"
26+
#include "swift/Basic/FrozenMultiMap.h"
27+
#include "swift/SIL/ApplySite.h"
28+
#include "swift/SIL/BasicBlockBits.h"
29+
#include "swift/SIL/BasicBlockData.h"
30+
#include "swift/SIL/BasicBlockDatastructures.h"
31+
#include "swift/SIL/BasicBlockUtils.h"
32+
#include "swift/SIL/Consumption.h"
33+
#include "swift/SIL/DebugUtils.h"
34+
#include "swift/SIL/InstructionUtils.h"
35+
#include "swift/SIL/MemAccessUtils.h"
36+
#include "swift/SIL/OwnershipUtils.h"
37+
#include "swift/SIL/PostOrder.h"
38+
#include "swift/SIL/PrunedLiveness.h"
39+
#include "swift/SIL/SILArgument.h"
40+
#include "swift/SIL/SILArgumentConvention.h"
41+
#include "swift/SIL/SILBasicBlock.h"
42+
#include "swift/SIL/SILBuilder.h"
43+
#include "swift/SIL/SILFunction.h"
44+
#include "swift/SIL/SILInstruction.h"
45+
#include "swift/SIL/SILMoveOnlyDeinit.h"
46+
#include "swift/SIL/SILUndef.h"
47+
#include "swift/SIL/SILValue.h"
48+
#include "swift/SILOptimizer/Analysis/Analysis.h"
49+
#include "swift/SILOptimizer/PassManager/Passes.h"
50+
#include "swift/SILOptimizer/PassManager/Transforms.h"
51+
52+
using namespace swift;
53+
54+
static bool performTransform(SILFunction &fn) {
55+
bool changed = false;
56+
57+
auto &mod = fn.getModule();
58+
for (auto &block : fn) {
59+
for (auto ii = block.begin(), ie = block.end(); ii != ie;) {
60+
auto *inst = &*ii;
61+
++ii;
62+
63+
if (auto *dvi = dyn_cast<DestroyValueInst>(inst)) {
64+
auto destroyType = dvi->getOperand()->getType();
65+
if (destroyType.isMoveOnlyType()) {
66+
LLVM_DEBUG(llvm::dbgs() << "Handling: " << *dvi);
67+
auto *nom = destroyType.getNominalOrBoundGenericNominal();
68+
assert(nom);
69+
auto *deinitFunc = mod.lookUpMoveOnlyDeinitFunction(nom);
70+
if (!deinitFunc) {
71+
LLVM_DEBUG(llvm::dbgs()
72+
<< "Failed to find deinit func for type! Skipping!\n");
73+
continue;
74+
}
75+
76+
auto astType = destroyType.getASTType();
77+
auto subMap =
78+
astType->getContextSubstitutionMap(nom->getModuleContext(), nom);
79+
SILBuilderWithScope builder(dvi);
80+
auto *funcRef = builder.createFunctionRef(dvi->getLoc(), deinitFunc);
81+
builder.createApply(dvi->getLoc(), funcRef, subMap,
82+
dvi->getOperand());
83+
dvi->eraseFromParent();
84+
changed = true;
85+
continue;
86+
}
87+
}
88+
89+
if (auto *dai = dyn_cast<DestroyAddrInst>(inst)) {
90+
auto destroyType = dai->getOperand()->getType();
91+
if (destroyType.isLoadable(fn) && destroyType.isMoveOnlyType()) {
92+
LLVM_DEBUG(llvm::dbgs() << "Handling: " << *dai);
93+
auto *nom = destroyType.getNominalOrBoundGenericNominal();
94+
assert(nom);
95+
auto *deinitFunc = mod.lookUpMoveOnlyDeinitFunction(nom);
96+
if (!deinitFunc) {
97+
LLVM_DEBUG(llvm::dbgs()
98+
<< "Failed to find deinit func for type! Skipping!\n");
99+
continue;
100+
}
101+
102+
SILBuilderWithScope builder(dai);
103+
auto *funcRef = builder.createFunctionRef(dai->getLoc(), deinitFunc);
104+
auto subMap = destroyType.getASTType()->getContextSubstitutionMap(
105+
nom->getModuleContext(), nom);
106+
auto loadedValue = builder.emitLoadValueOperation(
107+
dai->getLoc(), dai->getOperand(), LoadOwnershipQualifier::Take);
108+
builder.createApply(dai->getLoc(), funcRef, subMap, loadedValue);
109+
dai->eraseFromParent();
110+
changed = true;
111+
continue;
112+
}
113+
}
114+
}
115+
}
116+
117+
return changed;
118+
}
119+
120+
//===----------------------------------------------------------------------===//
121+
// Top Level Entrypoint
122+
//===----------------------------------------------------------------------===//
123+
124+
namespace {
125+
126+
class SILMoveOnlyDeinitInsertionPass : public SILFunctionTransform {
127+
void run() override {
128+
auto *fn = getFunction();
129+
130+
// Don't rerun diagnostics on deserialized functions.
131+
if (getFunction()->wasDeserializedCanonical())
132+
return;
133+
134+
assert(fn->getModule().getStage() == SILStage::Raw &&
135+
"Should only run on Raw SIL");
136+
LLVM_DEBUG(llvm::dbgs() << "===> MoveOnly Deinit Insertion. Visiting: "
137+
<< fn->getName() << '\n');
138+
if (performTransform(*fn)) {
139+
invalidateAnalysis(SILAnalysis::InvalidationKind::CallsAndInstructions);
140+
}
141+
}
142+
};
143+
144+
} // anonymous namespace
145+
146+
SILTransform *swift::createMoveOnlyDeinitInsertion() {
147+
return new SILMoveOnlyDeinitInsertionPass();
148+
}

lib/SILOptimizer/PassManager/PassPipeline.cpp

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -133,15 +133,18 @@ static void addMandatoryDiagnosticOptPipeline(SILPassPipelinePlan &P) {
133133
// Begin Ownership Optimizations
134134
//
135135

136-
P.addMoveOnlyAddressChecker(); // Check noImplicitCopy and move only types for
137-
// addresses.
138-
P.addMoveKillsCopyableAddressesChecker(); // Check _move for addresses.
139-
P.addMoveOnlyObjectChecker(); // Check noImplicitCopy and move only
140-
// types for objects
141-
P.addMoveKillsCopyableValuesChecker(); // No uses after _move of copyable
142-
// value.
143-
P.addTrivialMoveOnlyTypeEliminator(); // Lower move only wrapped trivial
144-
// types.
136+
// Check noImplicitCopy and move only types for addresses.
137+
P.addMoveOnlyAddressChecker();
138+
// Check noImplicitCopy and move only types for objects
139+
P.addMoveOnlyObjectChecker();
140+
// Convert last destroy_value to deinits.
141+
P.addMoveOnlyDeinitInsertion();
142+
// Lower move only wrapped trivial types.
143+
P.addTrivialMoveOnlyTypeEliminator();
144+
// Check no uses after _move of a value in an address.
145+
P.addMoveKillsCopyableAddressesChecker();
146+
// No uses after _move of copyable value.
147+
P.addMoveKillsCopyableValuesChecker();
145148

146149
//
147150
// End Ownership Optimizations
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-move-only) | %FileCheck %s
2+
3+
// REQUIRES: executable_test
4+
5+
@_moveOnly
6+
public struct BufferView<T> {
7+
var ptr: UnsafeMutableBufferPointer<T>
8+
9+
deinit {}
10+
}
11+
12+
extension BufferView : Sequence {
13+
public typealias Iterator = UnsafeMutableBufferPointer<T>.Iterator
14+
public typealias Element = UnsafeMutableBufferPointer<T>.Element
15+
16+
public func makeIterator() -> Self.Iterator {
17+
return ptr.makeIterator()
18+
}
19+
}
20+
21+
extension Array {
22+
public mutating func withBufferView<U>(_ f: (BufferView<Element>) -> U) -> U {
23+
return withUnsafeMutableBufferPointer {
24+
return f(BufferView(ptr: $0))
25+
}
26+
}
27+
}
28+
29+
func testBufferView(_ x: __owned [Int]) {
30+
var y = _move x
31+
// CHECK: 1
32+
// CHECK: 2
33+
// CHECK: 3
34+
y.withBufferView {
35+
for x in $0 {
36+
print(x)
37+
}
38+
}
39+
}
40+
41+
func main() {
42+
testBufferView([1,2,3])
43+
}
44+
45+
main()

0 commit comments

Comments
 (0)