Skip to content

Commit 4ad2bc9

Browse files
committed
[field-sensitive-pruned-liveness] Some small changes.
Specifically: 1. I added to the documentation at the top of the file that our representation allows for partial init/reinit of structs/tuples from parts. 2. I renamed SubElementNumber to SubElementOffset. This I think fits the actual use case better and makes it clearer what one is working with (the offset inside a type of a subelement of the type). 3. I added some small helpers to TypeSubElementCount and SubElementOffset for adding/subtracting from them. 4. I added the ability to iterate over just consuming/nonconsuming users in FieldSensitivePrunedLiveness. Just a useful little helper.
1 parent e2b5c03 commit 4ad2bc9

File tree

2 files changed

+146
-40
lines changed

2 files changed

+146
-40
lines changed

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> &)>>;

lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -88,16 +88,16 @@ TypeSubElementCount::TypeSubElementCount(SILType type, SILModule &mod,
8888
// MARK: SubElementNumber
8989
//===----------------------------------------------------------------------===//
9090

91-
Optional<SubElementNumber>
92-
SubElementNumber::computeForAddress(SILValue projectionDerivedFromRoot,
91+
Optional<SubElementOffset>
92+
SubElementOffset::computeForAddress(SILValue projectionDerivedFromRoot,
9393
SILValue rootAddress) {
94-
unsigned finalSubElementNumber = 0;
94+
unsigned finalSubElementOffset = 0;
9595
SILModule &mod = *rootAddress->getModule();
9696

9797
while (1) {
9898
// If we got to the root, we're done.
9999
if (rootAddress == projectionDerivedFromRoot)
100-
return {SubElementNumber(finalSubElementNumber)};
100+
return {SubElementOffset(finalSubElementOffset)};
101101

102102
if (auto *pbi = dyn_cast<ProjectBoxInst>(projectionDerivedFromRoot)) {
103103
projectionDerivedFromRoot = pbi->getOperand();
@@ -115,7 +115,7 @@ SubElementNumber::computeForAddress(SILValue projectionDerivedFromRoot,
115115

116116
// Keep track of what subelement is being referenced.
117117
for (unsigned i : range(teai->getFieldIndex())) {
118-
finalSubElementNumber += TypeSubElementCount(
118+
finalSubElementOffset += TypeSubElementCount(
119119
tupleType.getTupleElementType(i), mod,
120120
TypeExpansionContext(*rootAddress->getFunction()));
121121
}
@@ -133,7 +133,7 @@ SubElementNumber::computeForAddress(SILValue projectionDerivedFromRoot,
133133
if (fieldDecl == seai->getField())
134134
break;
135135
auto context = TypeExpansionContext(*rootAddress->getFunction());
136-
finalSubElementNumber += TypeSubElementCount(
136+
finalSubElementOffset += TypeSubElementCount(
137137
type.getFieldType(fieldDecl, mod, context), mod, context);
138138
}
139139

@@ -175,38 +175,75 @@ SubElementNumber::computeForAddress(SILValue projectionDerivedFromRoot,
175175
}
176176
}
177177

178-
Optional<SubElementNumber>
179-
SubElementNumber::computeForValue(SILValue projectionDerivedFromRoot,
178+
Optional<SubElementOffset>
179+
SubElementOffset::computeForValue(SILValue projectionDerivedFromRoot,
180180
SILValue rootAddress) {
181-
unsigned finalSubElementNumber = 0;
181+
unsigned finalSubElementOffset = 0;
182182
SILModule &mod = *rootAddress->getModule();
183183

184184
while (1) {
185185
// If we got to the root, we're done.
186186
if (rootAddress == projectionDerivedFromRoot)
187-
return {SubElementNumber(finalSubElementNumber)};
187+
return {SubElementOffset(finalSubElementOffset)};
188188

189189
// Look through these single operand instructions.
190190
if (isa<BeginBorrowInst>(projectionDerivedFromRoot) ||
191191
isa<CopyValueInst>(projectionDerivedFromRoot)) {
192192
projectionDerivedFromRoot =
193193
cast<SingleValueInstruction>(projectionDerivedFromRoot)
194194
->getOperand(0);
195+
continue;
195196
}
196197

197198
if (auto *teai = dyn_cast<TupleExtractInst>(projectionDerivedFromRoot)) {
198199
SILType tupleType = teai->getOperand()->getType();
199200

200201
// Keep track of what subelement is being referenced.
201202
for (unsigned i : range(teai->getFieldIndex())) {
202-
finalSubElementNumber += TypeSubElementCount(
203+
finalSubElementOffset += TypeSubElementCount(
203204
tupleType.getTupleElementType(i), mod,
204205
TypeExpansionContext(*rootAddress->getFunction()));
205206
}
206207
projectionDerivedFromRoot = teai->getOperand();
207208
continue;
208209
}
209210

211+
if (auto *mvir = dyn_cast<MultipleValueInstructionResult>(
212+
projectionDerivedFromRoot)) {
213+
if (auto *dsi = dyn_cast<DestructureStructInst>(mvir->getParent())) {
214+
SILType type = dsi->getOperand()->getType();
215+
216+
// Keep track of what subelement is being referenced.
217+
unsigned resultIndex = mvir->getIndex();
218+
StructDecl *structDecl = dsi->getStructDecl();
219+
for (auto pair : llvm::enumerate(structDecl->getStoredProperties())) {
220+
if (pair.index() == resultIndex)
221+
break;
222+
auto context = TypeExpansionContext(*rootAddress->getFunction());
223+
finalSubElementOffset += TypeSubElementCount(
224+
type.getFieldType(pair.value(), mod, context), mod, context);
225+
}
226+
227+
projectionDerivedFromRoot = dsi->getOperand();
228+
continue;
229+
}
230+
231+
if (auto *dti = dyn_cast<DestructureTupleInst>(mvir->getParent())) {
232+
SILType type = dti->getOperand()->getType();
233+
234+
// Keep track of what subelement is being referenced.
235+
unsigned resultIndex = mvir->getIndex();
236+
for (unsigned i : range(resultIndex)) {
237+
auto context = TypeExpansionContext(*rootAddress->getFunction());
238+
finalSubElementOffset +=
239+
TypeSubElementCount(type.getTupleElementType(i), mod, context);
240+
}
241+
242+
projectionDerivedFromRoot = dti->getOperand();
243+
continue;
244+
}
245+
}
246+
210247
if (auto *seai = dyn_cast<StructExtractInst>(projectionDerivedFromRoot)) {
211248
SILType type = seai->getOperand()->getType();
212249

@@ -216,7 +253,7 @@ SubElementNumber::computeForValue(SILValue projectionDerivedFromRoot,
216253
if (fieldDecl == seai->getField())
217254
break;
218255
auto context = TypeExpansionContext(*rootAddress->getFunction());
219-
finalSubElementNumber += TypeSubElementCount(
256+
finalSubElementOffset += TypeSubElementCount(
220257
type.getFieldType(fieldDecl, mod, context), mod, context);
221258
}
222259

0 commit comments

Comments
 (0)