Skip to content

Commit 65c21b6

Browse files
committed
[move-only] Begin implementing support for concrete move only types.
This is just the very beginning... I still need to implement more parts of SILGen for this. But all great things start small. I am going to iterate on top of this and just wanted to get some initial parts of the work in as I go.
1 parent afd8acc commit 65c21b6

File tree

10 files changed

+380
-13
lines changed

10 files changed

+380
-13
lines changed

include/swift/SIL/SILInstruction.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7587,8 +7587,8 @@ class MarkMustCheckInst
75877587
OwnershipForwardingMixin(SILInstructionKind::MarkMustCheckInst,
75887588
operand->getOwnershipKind()),
75897589
kind(checkKind) {
7590-
assert(operand->getType().isMoveOnlyWrapped() &&
7591-
"mark_must_check can only take a move only wrapped value");
7590+
assert(operand->getType().isMoveOnly() &&
7591+
"mark_must_check can only take a move only typed value");
75927592
}
75937593

75947594
public:

lib/SIL/IR/SILType.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -925,8 +925,10 @@ SILType::getSingletonAggregateFieldType(SILModule &M,
925925
return SILType();
926926
}
927927

928+
// TODO: Create isPureMoveOnly.
928929
bool SILType::isMoveOnly() const {
929930
if (auto *nom = getNominalOrBoundGenericNominal())
930-
return nom->isMoveOnly();
931+
if (nom->isMoveOnly())
932+
return true;
931933
return isMoveOnlyWrapped();
932934
}

lib/SILGen/SILGenConstructor.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "swift/AST/PropertyWrappers.h"
2727
#include "swift/Basic/Defer.h"
2828
#include "swift/SIL/SILArgument.h"
29+
#include "swift/SIL/SILInstruction.h"
2930
#include "swift/SIL/SILUndef.h"
3031
#include "swift/SIL/TypeLowering.h"
3132

@@ -386,6 +387,7 @@ void SILGenFunction::emitValueConstructor(ConstructorDecl *ctor) {
386387

387388
// Allocate the local variable for 'self'.
388389
emitLocalVariableWithCleanup(selfDecl, MUIKind)->finishInitialization(*this);
390+
389391
SILValue selfLV = VarLocs[selfDecl].value;
390392

391393
// Emit the prolog.
@@ -831,6 +833,11 @@ void SILGenFunction::emitClassConstructorInitializer(ConstructorDecl *ctor) {
831833
StoreOwnershipQualifier::Init);
832834
} else {
833835
selfArg = B.createMarkUninitialized(selfDecl, selfArg, MUKind);
836+
if (selfArg.getType().isMoveOnly()) {
837+
assert(selfArg.getOwnershipKind() == OwnershipKind::Owned);
838+
selfArg = B.createMarkMustCheckInst(
839+
selfDecl, selfArg, MarkMustCheckInst::CheckKind::NoImplicitCopy);
840+
}
834841
VarLocs[selfDecl] = VarLoc::get(selfArg.getValue());
835842
}
836843
}

lib/SILGen/SILGenDecl.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -572,15 +572,21 @@ class LetValueInitialization : public Initialization {
572572
// why we want this is even if we are only performing a borrow for our
573573
// lexical lifetime, we want to ensure that our defs see this
574574
// initialization as consuming this value.
575-
if (value->getType().isMoveOnlyWrapped() &&
576-
value->getOwnershipKind() == OwnershipKind::Owned) {
575+
if (value->getOwnershipKind() == OwnershipKind::Owned) {
577576
assert(wasPlusOne);
578577
// NOTE: If our type is trivial when not wrapped in a
579578
// SILMoveOnlyWrappedType, this will return a trivial value. We rely
580579
// on the checker to determine if this is an acceptable use of the
581580
// value.
582-
value = SGF.B.createOwnedMoveOnlyWrapperToCopyableValue(PrologueLoc,
583-
value);
581+
if (value->getType().isMoveOnly()) {
582+
if (value->getType().isMoveOnlyWrapped()) {
583+
value = SGF.B.createOwnedMoveOnlyWrapperToCopyableValue(
584+
PrologueLoc, value);
585+
} else {
586+
// Change this to be lexical and get rid of borrow scope?
587+
value = SGF.B.createMoveValue(PrologueLoc, value);
588+
}
589+
}
584590
}
585591

586592
// If we still have a non-trivial thing, emit code that will need to

lib/SILGen/SILGenPattern.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2835,9 +2835,13 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
28352835
auto subject = ([&]() -> ConsumableManagedValue {
28362836
// If we have a move only value, ensure plus one and convert it. Switches
28372837
// always consume move only values.
2838-
if (subjectMV.getType().isMoveOnlyWrapped()) {
2839-
subjectMV = B.createOwnedMoveOnlyWrapperToCopyableValue(
2840-
S, subjectMV.ensurePlusOne(*this, S));
2838+
if (subjectMV.getType().isMoveOnly()) {
2839+
if (subjectMV.getType().isMoveOnlyWrapped()) {
2840+
subjectMV = B.createOwnedMoveOnlyWrapperToCopyableValue(
2841+
S, subjectMV.ensurePlusOne(*this, S));
2842+
} else {
2843+
subjectMV = B.createMoveValue(S, subjectMV.ensurePlusOne(*this, S));
2844+
}
28412845
}
28422846

28432847
// If we have a plus one value...

lib/SILGen/SILGenProlog.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,20 @@ struct ArgumentInitHelper {
314314
loc, value, MarkMustCheckInst::CheckKind::NoImplicitCopy);
315315
SGF.emitManagedRValueWithCleanup(value);
316316
}
317+
} else if (value->getType().isMoveOnly()) {
318+
if (value->getOwnershipKind() == OwnershipKind::Owned) {
319+
value = SGF.B.createMoveValue(loc, argrv.forward(SGF),
320+
/*isLexical*/ true);
321+
value = SGF.B.createMarkMustCheckInst(
322+
loc, value, MarkMustCheckInst::CheckKind::NoImplicitCopy);
323+
SGF.emitManagedRValueWithCleanup(value);
324+
} else {
325+
assert(value->getOwnershipKind() == OwnershipKind::Guaranteed);
326+
value = SGF.B.createCopyValue(loc, value);
327+
value = SGF.B.createMarkMustCheckInst(
328+
loc, value, MarkMustCheckInst::CheckKind::NoCopy);
329+
SGF.emitManagedRValueWithCleanup(value);
330+
}
317331
} else {
318332
if (value->getOwnershipKind() == OwnershipKind::Owned) {
319333
value = SILValue(

lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#define DEBUG_TYPE "definite-init"
14+
1415
#include "DIMemoryUseCollector.h"
1516
#include "swift/AST/Expr.h"
1617
#include "swift/SIL/ApplySite.h"
1718
#include "swift/SIL/InstructionUtils.h"
1819
#include "swift/SIL/SILArgument.h"
1920
#include "swift/SIL/SILBuilder.h"
21+
#include "swift/SIL/SILInstruction.h"
2022
#include "swift/SILOptimizer/Utils/DistributedActor.h"
2123
#include "llvm/ADT/StringExtras.h"
2224
#include "llvm/Support/Debug.h"
@@ -1211,6 +1213,7 @@ void ElementUseCollector::collectClassSelfUses(SILValue ClassPointer) {
12111213
Uses.append(beginAccess->getUses().begin(), beginAccess->getUses().end());
12121214
continue;
12131215
}
1216+
12141217
if (isa<EndAccessInst>(User))
12151218
continue;
12161219

@@ -1489,9 +1492,9 @@ void ElementUseCollector::collectClassSelfUses(
14891492

14901493
// Look through begin_borrow, upcast, unchecked_ref_cast
14911494
// and copy_value.
1492-
if (isa<BeginBorrowInst>(User) || isa<BeginAccessInst>(User)
1493-
|| isa<UpcastInst>(User) || isa<UncheckedRefCastInst>(User)
1494-
|| isa<CopyValueInst>(User)) {
1495+
if (isa<BeginBorrowInst>(User) || isa<BeginAccessInst>(User) ||
1496+
isa<UpcastInst>(User) || isa<UncheckedRefCastInst>(User) ||
1497+
isa<CopyValueInst>(User) || isa<MarkMustCheckInst>(User)) {
14951498
auto value = cast<SingleValueInstruction>(User);
14961499
std::copy(value->use_begin(), value->use_end(),
14971500
std::back_inserter(Worklist));

lib/SILOptimizer/Mandatory/MoveOnlyChecker.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,46 @@ bool MoveOnlyChecker::searchForCandidateMarkMustChecks() {
543543
if (!mmci || !mmci->hasMoveCheckerKind())
544544
continue;
545545

546+
// Handle guaranteed/owned move only typed arguments.
547+
//
548+
// We are pattern matching against these patterns:
549+
//
550+
// bb0(%0 : @guaranteed $T):
551+
// %1 = copy_value %0
552+
// %2 = mark_must_check [no_copy] %1
553+
// bb0(%0 : @owned $T):
554+
// %1 = copy_value %0
555+
// %2 = mark_must_check [no_copy] %1
556+
if (mmci->getOperand()->getType().isMoveOnly() &&
557+
!mmci->getOperand()->getType().isMoveOnlyWrapped()) {
558+
if (auto *cvi = dyn_cast<CopyValueInst>(mmci->getOperand())) {
559+
if (auto *arg = dyn_cast<SILFunctionArgument>(cvi->getOperand())) {
560+
if (arg->getOwnershipKind() == OwnershipKind::Guaranteed) {
561+
moveIntroducersToProcess.insert(mmci);
562+
continue;
563+
}
564+
}
565+
}
566+
567+
if (auto *mvi = dyn_cast<MoveValueInst>(mmci->getOperand())) {
568+
if (mvi->isLexical()) {
569+
if (auto *arg = dyn_cast<SILFunctionArgument>(mvi->getOperand())) {
570+
if (arg->getOwnershipKind() == OwnershipKind::Owned) {
571+
moveIntroducersToProcess.insert(mmci);
572+
continue;
573+
}
574+
}
575+
}
576+
}
577+
578+
if (auto *arg = dyn_cast<SILFunctionArgument>(mmci->getOperand())) {
579+
if (arg->getOwnershipKind() == OwnershipKind::Owned) {
580+
moveIntroducersToProcess.insert(mmci);
581+
continue;
582+
}
583+
}
584+
}
585+
546586
// Handle guaranteed arguments.
547587
//
548588
// We are pattern matching this pattern:

test/SILGen/moveonly.swift

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
// RUN: %target-swift-emit-silgen -enable-experimental-move-only %s | %FileCheck %s
2+
3+
//////////////////
4+
// Declarations //
5+
//////////////////
6+
7+
@_moveOnly
8+
public class Klass {
9+
var intField: Int
10+
var klsField: Klass?
11+
12+
// CHECK-LABEL: sil hidden [ossa] @$s8moveonly5KlassCACycfc : $@convention(method) (@owned Klass) -> @owned Klass {
13+
// CHECK: bb0([[ARG:%.*]] : @owned $Klass):
14+
// CHECK: [[MARK_UNINIT:%.*]] = mark_uninitialized [rootself] [[ARG]]
15+
// CHECK: [[MARK_NO_IMP_COPY:%.*]] = mark_must_check [no_implicit_copy] [[MARK_UNINIT]]
16+
// CHECK: } // end sil function '$s8moveonly5KlassCACycfc'
17+
init() {
18+
klsField = Klass()
19+
intField = 5
20+
}
21+
}
22+
23+
public func nonConsumingUseKlass(_ k: Klass) {}
24+
25+
@_moveOnly
26+
public struct NonTrivialStruct {
27+
var k = Klass()
28+
}
29+
30+
public func nonConsumingUseNonTrivialStruct(_ s: NonTrivialStruct) {}
31+
32+
@_moveOnly
33+
public enum NonTrivialEnum {
34+
case first
35+
case second(Klass)
36+
case third(NonTrivialStruct)
37+
}
38+
39+
public func nonConsumingUseNonTrivialEnum(_ e : NonTrivialEnum) {}
40+
41+
///////////
42+
// Tests //
43+
///////////
44+
45+
//===---
46+
// Function Arguments
47+
//
48+
49+
// CHECK-LABEL: sil [ossa] @$s8moveonly8useKlassyyAA0C0CF : $@convention(thin) (@guaranteed Klass) -> () {
50+
// CHECK: bb0([[ARG:%.*]] : @guaranteed $Klass):
51+
// CHECK: [[OWNED_ARG:%.*]] = copy_value [[ARG]]
52+
// CHECK: [[MARKED_OWNED_ARG:%.*]] = mark_must_check [no_copy] [[OWNED_ARG]]
53+
// CHECK: } // end sil function '$s8moveonly8useKlassyyAA0C0CF'
54+
public func useKlass(_ k: Klass) {
55+
nonConsumingUseKlass(k)
56+
let k2 = k
57+
nonConsumingUseKlass(k)
58+
let _ = k2
59+
}
60+
61+
// CHECK-LABEL: sil [ossa] @$s8moveonly15useKlassConsumeyyAA0C0CnF : $@convention(thin) (@owned Klass) -> () {
62+
// CHECK: bb0([[ARG:%.*]] : @owned $Klass):
63+
// TODO: Is this move_value [lexical] necessary?
64+
// CHECK: [[LEXICAL_MOVE:%.*]] = move_value [lexical] [[ARG]]
65+
// CHECK: mark_must_check [no_implicit_copy] [[LEXICAL_MOVE]]
66+
// CHECK: } // end sil function '$s8moveonly15useKlassConsumeyyAA0C0CnF'
67+
public func useKlassConsume(_ k: __owned Klass) {
68+
nonConsumingUseKlass(k)
69+
let k2 = k
70+
// NOTE: We should mark the next line as a lifetime extending use.
71+
nonConsumingUseKlass(k)
72+
let _ = k2
73+
}
74+
75+
// CHECK-LABEL: sil [ossa] @$s8moveonly19useNonTrivialStructyyAA0cdE0VF : $@convention(thin) (@guaranteed NonTrivialStruct) -> () {
76+
// CHECK: bb0([[ARG:%.*]] : @guaranteed $NonTrivialStruct):
77+
// CHECK: [[COPIED_ARG:%.*]] = copy_value [[ARG]]
78+
// CHECK: mark_must_check [no_copy] [[COPIED_ARG]]
79+
// CHECK: } // end sil function '$s8moveonly19useNonTrivialStructyyAA0cdE0VF'
80+
public func useNonTrivialStruct(_ s: NonTrivialStruct) {
81+
nonConsumingUseNonTrivialStruct(s)
82+
let s2 = s
83+
let k = s.k
84+
let _ = k
85+
nonConsumingUseNonTrivialStruct(s)
86+
let _ = s2
87+
}
88+
89+
// CHECK-LABEL: sil [ossa] @$s8moveonly24useNonTrivialOwnedStructyyAA0cdF0VnF : $@convention(thin) (@owned NonTrivialStruct) -> () {
90+
// CHECK: bb0([[ARG:%.*]] : @owned $NonTrivialStruct):
91+
// CHECK: [[MOVED_ARG:%.*]] = move_value [lexical] [[ARG]]
92+
// CHECK: mark_must_check [no_implicit_copy] [[MOVED_ARG]]
93+
// CHECK: } // end sil function '$s8moveonly24useNonTrivialOwnedStructyyAA0cdF0VnF'
94+
public func useNonTrivialOwnedStruct(_ s: __owned NonTrivialStruct) {
95+
nonConsumingUseNonTrivialStruct(s)
96+
let s2 = s
97+
let k = s.k
98+
let _ = k
99+
nonConsumingUseNonTrivialStruct(s)
100+
let _ = s2
101+
}
102+
103+
// CHECK-LABEL: sil [ossa] @$s8moveonly17useNonTrivialEnumyyAA0cdE0OF : $@convention(thin) (@guaranteed NonTrivialEnum) -> () {
104+
// CHECK: bb0([[ARG:%.*]] : @guaranteed $NonTrivialEnum):
105+
// CHECK: [[COPIED_ARG:%.*]] = copy_value [[ARG]]
106+
// CHECK: mark_must_check [no_copy] [[COPIED_ARG]]
107+
// CHECK: } // end sil function '$s8moveonly17useNonTrivialEnumyyAA0cdE0OF'
108+
public func useNonTrivialEnum(_ s: NonTrivialEnum) {
109+
nonConsumingUseNonTrivialEnum(s)
110+
let s2 = s
111+
switch s {
112+
case _:
113+
break
114+
}
115+
nonConsumingUseNonTrivialEnum(s)
116+
let _ = s2
117+
}
118+
119+
// CHECK-LABEL: sil [ossa] @$s8moveonly22useNonTrivialOwnedEnumyyAA0cdF0OnF : $@convention(thin) (@owned NonTrivialEnum) -> () {
120+
// CHECK: bb0([[ARG:%.*]] : @owned $NonTrivialEnum):
121+
// CHECK: [[MOVED_ARG:%.*]] = move_value [lexical] [[ARG]]
122+
// CHECK: mark_must_check [no_implicit_copy] [[MOVED_ARG]]
123+
// CHECK: } // end sil function '$s8moveonly22useNonTrivialOwnedEnumyyAA0cdF0OnF'
124+
public func useNonTrivialOwnedEnum(_ s: __owned NonTrivialEnum) {
125+
nonConsumingUseNonTrivialEnum(s)
126+
let s2 = s
127+
switch s {
128+
case _:
129+
break
130+
}
131+
nonConsumingUseNonTrivialEnum(s)
132+
let _ = s2
133+
}
134+
135+
//===---
136+
// Self in Init
137+
//
138+
139+
//===---
140+
// Self in Methods
141+
//
142+
143+
extension Klass {
144+
// CHECK-LABEL: sil hidden [ossa] @$s8moveonly5KlassC13testNoUseSelfyyF : $@convention(method) (@guaranteed Klass) -> () {
145+
// CHECK: bb0([[ARG:%.*]] : @guaranteed $Klass):
146+
// CHECK: [[COPIED_ARG:%.*]] = copy_value [[ARG]]
147+
// CHECK: mark_must_check [no_copy] [[COPIED_ARG]]
148+
// CHECK: } // end sil function '$s8moveonly5KlassC13testNoUseSelfyyF'
149+
func testNoUseSelf() {
150+
let x = self
151+
let _ = x
152+
}
153+
}
154+
155+
extension NonTrivialStruct {
156+
// CHECK-LABEL: sil hidden [ossa] @$s8moveonly16NonTrivialStructV13testNoUseSelfyyF : $@convention(method) (@guaranteed NonTrivialStruct) -> () {
157+
// CHECK: bb0([[ARG:%.*]] : @guaranteed $NonTrivialStruct):
158+
// CHECK: [[COPIED_ARG:%.*]] = copy_value [[ARG]]
159+
// CHECK: mark_must_check [no_copy] [[COPIED_ARG]]
160+
// CHECK: } // end sil function '$s8moveonly16NonTrivialStructV13testNoUseSelfyyF'
161+
func testNoUseSelf() {
162+
let x = self
163+
let _ = x
164+
}
165+
}
166+
167+
extension NonTrivialEnum {
168+
// CHECK-LABEL: sil hidden [ossa] @$s8moveonly14NonTrivialEnumO13testNoUseSelfyyF : $@convention(method) (@guaranteed NonTrivialEnum) -> () {
169+
// CHECK: bb0([[ARG:%.*]] : @guaranteed $NonTrivialEnum):
170+
// CHECK: [[COPIED_ARG:%.*]] = copy_value [[ARG]]
171+
// CHECK: mark_must_check [no_copy] [[COPIED_ARG]]
172+
// CHECK: } // end sil function '$s8moveonly14NonTrivialEnumO13testNoUseSelfyyF'
173+
func testNoUseSelf() {
174+
let x = self
175+
let _ = x
176+
}
177+
}

0 commit comments

Comments
 (0)