Skip to content

Commit 8917692

Browse files
authored
Merge pull request swiftlang#63213 from gottesmm/rdar103271138
[move-only] Implement borrow+struct_extract to restructure transform
2 parents 4e50e29 + 27ecd84 commit 8917692

22 files changed

+3007
-194
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,7 +743,13 @@ ERROR(sil_moveonlychecker_value_consumed_in_a_loop, none,
743743
"'%0' consumed by a use in a loop", (StringRef))
744744
ERROR(sil_moveonlychecker_exclusivity_violation, none,
745745
"'%0' has consuming use that cannot be eliminated due to a tight exclusivity scope", (StringRef))
746+
ERROR(sil_moveonlychecker_moveonly_field_consumed, none,
747+
"'%0' has a move only field that was consumed before later uses", (StringRef))
746748

749+
NOTE(sil_moveonlychecker_moveonly_field_consumed_here, none,
750+
"move only field consumed here", ())
751+
NOTE(sil_moveonlychecker_boundary_use, none,
752+
"boundary use here", ())
747753
NOTE(sil_moveonlychecker_consuming_use_here, none,
748754
"consuming use", ())
749755
NOTE(sil_moveonlychecker_consuming_closure_use_here, none,

include/swift/Basic/FrozenMultiMap.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,11 @@ class FrozenMultiMap {
122122

123123
bool isFrozen() const { return frozen; }
124124

125-
/// Set this map into its frozen state when we
125+
/// Set this map into its frozen state. This stable sorts our internal array
126+
/// to create our map like context.
127+
///
128+
/// After this, one can only use map like operations and non-mutable vector
129+
/// operations instead of full mutable/non-mutable vector operations.
126130
void setFrozen() {
127131
std::stable_sort(storage.begin(), storage.end(),
128132
[&](const std::pair<Key, Optional<Value>> &lhs,
@@ -134,6 +138,13 @@ class FrozenMultiMap {
134138
frozen = true;
135139
}
136140

141+
/// Unfreeze the map, so one can go back to using mutable vector
142+
/// operations. After one calls this until one freezes the map again, one
143+
/// cannot use map operations.
144+
///
145+
/// This allows one to incrementally update the map.
146+
void unfreeze() { frozen = false; }
147+
137148
/// Reset the frozen multimap in an unfrozen state with its storage cleared.
138149
void reset() {
139150
storage.clear();

include/swift/SIL/FieldSensitivePrunedLiveness.h

Lines changed: 97 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
#include "swift/AST/TypeExpansionContext.h"
1717
#include "swift/Basic/Debug.h"
1818
#include "swift/Basic/FrozenMultiMap.h"
19+
#include "swift/Basic/STLExtras.h"
1920
#include "swift/SIL/PrunedLiveness.h"
21+
#include "llvm/ADT/STLExtras.h"
2022
#include "llvm/ADT/SmallBitVector.h"
2123
#include "llvm/Support/raw_ostream.h"
2224

@@ -59,6 +61,13 @@ namespace swift {
5961
/// [0, 1, 1, 0, 0]
6062
/// ```
6163
///
64+
/// NOTE: Our representation allows for partial initialization/reinitialization
65+
/// since we do not include a bit for each level of struct/tuple. The effect of
66+
/// this is that we cannot distinguish a single field type from its child
67+
/// field. This makes this type not suited for projection operations. This is a
68+
/// trade-off that was made to make it easy to support partial
69+
/// initialization/reinitialization of liveness.
70+
///
6271
/// Linearized Representation of Enums
6372
/// ----------------------------------
6473
///
@@ -141,28 +150,36 @@ namespace swift {
141150
/// we would only use a single bit in our linearized representation, just for
142151
/// the discriminator value.
143152
///
144-
/// Enums and Partial Initialization
145-
/// --------------------------------
153+
/// Enums and Partial Initialization/Deinitialization
154+
/// -------------------------------------------------
146155
///
147156
/// One property of our representation of structs and tuples is that a code
148-
/// generator can reinitialize a struct/tuple completely just by re-initializing
149-
/// each of its sub-types individually. This is not possible for enums in our
150-
/// representation since if one just took the leaf nodes for the payload, one
151-
/// would not update the bit for the enum case itself and any additional spare
152-
/// bits. Luckily for us, this is actually impossible to do in SIL since it is
153-
/// impossible to dynamically change the payload of an enum without destroying
154-
/// the original enum and its payload since that would be a verifier caught
155-
/// leak.
156-
struct SubElementNumber {
157+
/// generator can init/reinit/deinit a struct/tuple completely just by
158+
/// performing the relevant operation on each of its sub-types
159+
/// individually. This is not possible for enums in our representation since if
160+
/// one just took the leaf nodes for the payload, one would not update the bit
161+
/// for the enum case itself and any additional spare bits. Luckily for us, this
162+
/// is safe to assume since we only use this in misc address contexts and move
163+
/// only object contexts in Raw SIL which have the following invariants:
164+
///
165+
/// 1. SIL addresses of enum type cannot dynamically change the payload of an
166+
/// enum without destroying the original enum completely. So such an address
167+
/// can never be reinitialized by storing into the payload of an enum.
168+
///
169+
/// 2. It is illegal in SIL to unchecked_enum_data a move only type in Raw
170+
/// SIL. One must instead use a switch_enum which creates a new value for the
171+
/// destructured enum. We when writing such verifiers consider the switch to
172+
/// produce an entire new value rather than a derived forwarding value.
173+
struct SubElementOffset {
157174
/// Our internal sub element representation number. We force 32 bits so that
158175
/// our type tree span is always pointer width. This is convenient for storing
159176
/// it in other data structures.
160177
uint32_t number;
161178

162-
SubElementNumber(unsigned number) : number(number) {}
179+
SubElementOffset(unsigned number) : number(number) {}
163180

164181
/// Given an arbitrary projection \p projectionFromRoot from the \p
165-
/// rootAddress, compute the sub element number for that \p SILValue. The sub
182+
/// rootValue, compute the sub element number for that \p SILValue. The sub
166183
/// element number of a type T is always the index of its first leaf node
167184
/// descendent in the type tree.
168185
///
@@ -175,7 +192,7 @@ struct SubElementNumber {
175192
///
176193
/// \returns None if we didn't know how to compute sub-element for this
177194
/// projection.
178-
static Optional<SubElementNumber> compute(SILValue projectionFromRoot,
195+
static Optional<SubElementOffset> compute(SILValue projectionFromRoot,
179196
SILValue root) {
180197
assert(projectionFromRoot->getType().getCategory() ==
181198
root->getType().getCategory() &&
@@ -187,10 +204,15 @@ struct SubElementNumber {
187204

188205
operator unsigned() const { return number; }
189206

207+
SubElementOffset &operator+=(unsigned other) {
208+
number += other;
209+
return *this;
210+
}
211+
190212
private:
191-
static Optional<SubElementNumber>
213+
static Optional<SubElementOffset>
192214
computeForAddress(SILValue projectionFromRoot, SILValue rootAddress);
193-
static Optional<SubElementNumber> computeForValue(SILValue projectionFromRoot,
215+
static Optional<SubElementOffset> computeForValue(SILValue projectionFromRoot,
194216
SILValue rootValue);
195217
};
196218

@@ -226,6 +248,11 @@ struct TypeSubElementCount {
226248
TypeExpansionContext(*value->getFunction())) {}
227249

228250
operator unsigned() const { return number; }
251+
252+
TypeSubElementCount operator-=(unsigned other) {
253+
*this = TypeSubElementCount(unsigned(*this) - other);
254+
return *this;
255+
}
229256
};
230257

231258
class FieldSensitivePrunedLiveness;
@@ -234,18 +261,18 @@ class FieldSensitivePrunedLiveness;
234261
/// of the type tree of a type T.
235262
struct TypeTreeLeafTypeRange {
236263
friend FieldSensitivePrunedLiveness;
237-
SubElementNumber startEltOffset;
238-
SubElementNumber endEltOffset;
239264

240-
public:
265+
SubElementOffset startEltOffset;
266+
SubElementOffset endEltOffset;
267+
241268
TypeTreeLeafTypeRange() : startEltOffset(0), endEltOffset(0) {}
242269

243-
TypeTreeLeafTypeRange(SubElementNumber start, SubElementNumber end)
270+
TypeTreeLeafTypeRange(SubElementOffset start, SubElementOffset end)
244271
: startEltOffset(start), endEltOffset(end) {}
245272

246273
/// The leaf type range for the entire type tree.
247-
TypeTreeLeafTypeRange(SILValue rootAddress)
248-
: startEltOffset(0), endEltOffset(TypeSubElementCount(rootAddress)) {}
274+
TypeTreeLeafTypeRange(SILValue rootValue)
275+
: startEltOffset(0), endEltOffset(TypeSubElementCount(rootValue)) {}
249276

250277
/// The leaf type range for the entire type tree.
251278
TypeTreeLeafTypeRange(SILType rootType, SILFunction *fn)
@@ -257,14 +284,13 @@ struct TypeTreeLeafTypeRange {
257284
///
258285
/// \returns None if we are unable to understand the path in between \p
259286
/// projectedAddress and \p rootAddress.
260-
static Optional<TypeTreeLeafTypeRange> get(SILValue projectedAddress,
261-
SILValue rootAddress) {
262-
auto startEltOffset =
263-
SubElementNumber::compute(projectedAddress, rootAddress);
287+
static Optional<TypeTreeLeafTypeRange> get(SILValue projectedValue,
288+
SILValue rootValue) {
289+
auto startEltOffset = SubElementOffset::compute(projectedValue, rootValue);
264290
if (!startEltOffset)
265291
return None;
266292
return {{*startEltOffset,
267-
*startEltOffset + TypeSubElementCount(projectedAddress)}};
293+
*startEltOffset + TypeSubElementCount(projectedValue)}};
268294
}
269295

270296
/// Given a type \p rootType and a set of needed elements specified by the bit
@@ -311,7 +337,7 @@ struct TypeTreeLeafTypeRange {
311337

312338
/// Is the given leaf type specified by \p singleLeafElementNumber apart of
313339
/// our \p range of leaf type values in the our larger type.
314-
bool contains(SubElementNumber singleLeafElementNumber) const {
340+
bool contains(SubElementOffset singleLeafElementNumber) const {
315341
return startEltOffset <= singleLeafElementNumber &&
316342
singleLeafElementNumber < endEltOffset;
317343
}
@@ -343,6 +369,12 @@ struct TypeTreeLeafTypeRange {
343369
void dump() const { print(llvm::dbgs()); }
344370
};
345371

372+
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
373+
const TypeTreeLeafTypeRange &value) {
374+
value.print(os);
375+
return os;
376+
}
377+
346378
/// This is exactly like pruned liveness except that instead of tracking a
347379
/// single bit of liveness, it tracks multiple bits of liveness for leaf type
348380
/// tree nodes of an allocation one is calculating pruned liveness for.
@@ -353,10 +385,12 @@ struct TypeTreeLeafTypeRange {
353385
class FieldSensitivePrunedLiveness {
354386
PrunedLiveBlocks liveBlocks;
355387

388+
public:
356389
struct InterestingUser {
357390
TypeTreeLeafTypeRange subEltSpan;
358391
bool isConsuming;
359392

393+
InterestingUser() : subEltSpan(), isConsuming(false) {}
360394
InterestingUser(TypeTreeLeafTypeRange subEltSpan, bool isConsuming)
361395
: subEltSpan(subEltSpan), isConsuming(isConsuming) {}
362396

@@ -366,6 +400,7 @@ class FieldSensitivePrunedLiveness {
366400
}
367401
};
368402

403+
private:
369404
/// Map all "interesting" user instructions in this def's live range to a pair
370405
/// consisting of the SILValue that it uses and a flag indicating whether they
371406
/// must end the lifetime.
@@ -420,6 +455,40 @@ class FieldSensitivePrunedLiveness {
420455
return llvm::make_range(users.begin(), users.end());
421456
}
422457

458+
using LifetimeEndingUserRange = OptionalTransformRange<
459+
UserRange,
460+
function_ref<Optional<std::pair<SILInstruction *, TypeTreeLeafTypeRange>>(
461+
const std::pair<SILInstruction *, InterestingUser> &)>>;
462+
LifetimeEndingUserRange getAllLifetimeEndingUses() const {
463+
function_ref<Optional<std::pair<SILInstruction *, TypeTreeLeafTypeRange>>(
464+
const std::pair<SILInstruction *, InterestingUser> &)>
465+
op;
466+
op = [](const std::pair<SILInstruction *, InterestingUser> &pair)
467+
-> Optional<std::pair<SILInstruction *, TypeTreeLeafTypeRange>> {
468+
if (pair.second.isConsuming)
469+
return {{pair.first, pair.second.subEltSpan}};
470+
return None;
471+
};
472+
return LifetimeEndingUserRange(getAllUsers(), op);
473+
}
474+
475+
using NonLifetimeEndingUserRange = OptionalTransformRange<
476+
UserRange,
477+
function_ref<Optional<std::pair<SILInstruction *, TypeTreeLeafTypeRange>>(
478+
const std::pair<SILInstruction *, InterestingUser> &)>>;
479+
NonLifetimeEndingUserRange getAllNonLifetimeEndingUses() const {
480+
function_ref<Optional<std::pair<SILInstruction *, TypeTreeLeafTypeRange>>(
481+
const std::pair<SILInstruction *, InterestingUser> &)>
482+
op;
483+
op = [](const std::pair<SILInstruction *, InterestingUser> &pair)
484+
-> Optional<std::pair<SILInstruction *, TypeTreeLeafTypeRange>> {
485+
if (!pair.second.isConsuming)
486+
return {{pair.first, pair.second.subEltSpan}};
487+
return None;
488+
};
489+
return NonLifetimeEndingUserRange(getAllUsers(), op);
490+
}
491+
423492
using UserBlockRange = TransformRange<
424493
UserRange, function_ref<SILBasicBlock *(
425494
const std::pair<SILInstruction *, InterestingUser> &)>>;

include/swift/SIL/SILInstruction.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10307,6 +10307,11 @@ class DestructureStructInst final
1030710307
static DestructureStructInst *
1030810308
create(const SILFunction &F, SILDebugLocation Loc, SILValue Operand,
1030910309
ValueOwnershipKind forwardingOwnershipKind);
10310+
10311+
StructDecl *getStructDecl() const {
10312+
return getOperand()->getType().getStructOrBoundGenericStruct();
10313+
}
10314+
1031010315
static bool classof(SILNodePointer node) {
1031110316
return node->getKind() == SILNodeKind::DestructureStructInst;
1031210317
}

include/swift/SIL/SILLocation.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,6 @@ class SILLocation {
267267

268268
SILLocation(ExtendedASTNodeLoc *ext, LocationKind K)
269269
: storage(ext), kindAndFlags(K, ExtendedASTNodeKind) {
270-
assert(ext && !ext->forDebugging.isNull());
271270
}
272271

273272
SILLocation(SourceLoc L, LocationKind K)
@@ -470,7 +469,8 @@ class RegularLocation : public SILLocation {
470469
RegularLocation(Decl *D) : SILLocation(ASTNodeTy(D), RegularKind) {}
471470
RegularLocation(Pattern *P) : SILLocation(ASTNodeTy(P), RegularKind) {}
472471
RegularLocation(Stmt *S, Pattern *P, SILModule &Module);
473-
RegularLocation(SILLocation ForDebuggingOnly, SILModule &Module);
472+
RegularLocation(SILLocation ForDebuggingOrDiagnosticsOnly, SILModule &Module,
473+
bool isForDebugOnly = true);
474474
RegularLocation(SourceLoc L) : SILLocation(L, RegularKind) {}
475475
RegularLocation(FilenameAndLocation *filePos)
476476
: SILLocation(filePos, RegularKind) {}
@@ -510,6 +510,15 @@ class RegularLocation : public SILLocation {
510510
return getAutoGeneratedLocation(L);
511511
}
512512

513+
/// Returns a location that uses L for diagnostics but is otherwise an auto
514+
/// generated location.
515+
static RegularLocation getDiagnosticsOnlyLocation(SILLocation L,
516+
SILModule &M) {
517+
if (L.isASTNode())
518+
return RegularLocation(L, M, false /*is for debug only*/);
519+
return getAutoGeneratedLocation(L);
520+
}
521+
513522
static bool isKind(const SILLocation& L) {
514523
return L.getKind() == RegularKind;
515524
}
@@ -518,6 +527,8 @@ class RegularLocation : public SILLocation {
518527
RegularLocation() : SILLocation(RegularKind) {}
519528
static SILLocation::ExtendedASTNodeLoc *
520529
getDebugOnlyExtendedASTNodeLoc(SILLocation L, SILModule &Module);
530+
static SILLocation::ExtendedASTNodeLoc *
531+
getDiagnosticOnlyExtendedASTNodeLoc(SILLocation L, SILModule &Module);
521532
};
522533

523534
/// Used to represent a return instruction in user code.

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,9 @@ PASS(MovedAsyncVarDebugInfoPropagator, "sil-moved-async-var-dbginfo-propagator",
460460
"Propagate debug info from moved async vars after coroutine funclet boundaries")
461461
PASS(MoveOnlyDeinitInsertion, "sil-move-only-deinit-insertion",
462462
"After running move only checking, convert last destroy_values to deinit calls")
463+
PASS(MoveOnlyBorrowToDestructureTransform, "sil-move-only-borrow-to-destructure",
464+
"Utility pass that is used to test the borrow to destructure transform "
465+
"independently of the move only object/address checkers")
463466
PASS(PruneVTables, "prune-vtables",
464467
"Mark class methods that do not require vtable dispatch")
465468
PASS_RANGE(AllPasses, AADumper, PruneVTables)

lib/SIL/IR/SILLocation.cpp

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,25 @@ RegularLocation::getDebugOnlyExtendedASTNodeLoc(SILLocation L,
235235
return new (Module) ExtendedASTNodeLoc((Decl *)nullptr, P);
236236
}
237237

238-
RegularLocation::RegularLocation(SILLocation ForDebuggingOnly,
239-
SILModule &Module)
240-
: SILLocation(getDebugOnlyExtendedASTNodeLoc(ForDebuggingOnly, Module),
238+
SILLocation::ExtendedASTNodeLoc *
239+
RegularLocation::getDiagnosticOnlyExtendedASTNodeLoc(SILLocation L,
240+
SILModule &Module) {
241+
if (auto D = L.getAsASTNode<Decl>())
242+
return new (Module) ExtendedASTNodeLoc(D, (Decl *)nullptr);
243+
if (auto E = L.getAsASTNode<Expr>())
244+
return new (Module) ExtendedASTNodeLoc(E, (Decl *)nullptr);
245+
if (auto S = L.getAsASTNode<Stmt>())
246+
return new (Module) ExtendedASTNodeLoc(S, (Decl *)nullptr);
247+
auto P = L.getAsASTNode<Pattern>();
248+
return new (Module) ExtendedASTNodeLoc(P, (Decl *)nullptr);
249+
}
250+
251+
RegularLocation::RegularLocation(SILLocation ForDebuggingOrDiagnosticsOnly,
252+
SILModule &Module, bool isForDebugOnly)
253+
: SILLocation(isForDebugOnly ? getDebugOnlyExtendedASTNodeLoc(
254+
ForDebuggingOrDiagnosticsOnly, Module)
255+
: getDiagnosticOnlyExtendedASTNodeLoc(
256+
ForDebuggingOrDiagnosticsOnly, Module),
241257
RegularKind) {
242258
markAutoGenerated();
243259
}

0 commit comments

Comments
 (0)