Skip to content

Commit f5969c6

Browse files
committed
[sil] Refactor FieldSensitivePrunedLiveness along the lines of PrunedLiveness.
To do this I removed isWithinBoundary and friends from FieldSensitiveAddressPrunedLiveness and moved them and defs into a new class called FieldSensitiveAddressPrunedLiveRange. The former is now akin to PrunedLiveness and the later is akin to MultiDefPrunedLiveRange. Since we are dealing with addresses which are inherently not in SSA, I eliminated the extra CRTP class that PrunedLiveness used to handle both SSA/MultiDef code. I also ripped out FieldSensitiveAddressPrunedLivenessBoundary since as I was experimenting with it, I realized that I fundamentally needed a different API than what the non field sensitive case supports since I have to handle TypeTreeSpans.
1 parent cfe49a0 commit f5969c6

File tree

5 files changed

+771
-178
lines changed

5 files changed

+771
-178
lines changed

include/swift/SIL/FieldSensitivePrunedLiveness.h

Lines changed: 209 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#ifndef SWIFT_SIL_FIELDSENSITIVEPRUNTEDLIVENESS_H
1414
#define SWIFT_SIL_FIELDSENSITIVEPRUNTEDLIVENESS_H
1515

16+
#include "swift/AST/TypeExpansionContext.h"
1617
#include "swift/SIL/PrunedLiveness.h"
1718

1819
namespace swift {
@@ -149,7 +150,10 @@ namespace swift {
149150
/// the original enum and its payload since that would be a verifier caught
150151
/// leak.
151152
struct SubElementNumber {
152-
unsigned number;
153+
/// Our internal sub element representation number. We force 32 bits so that
154+
/// our type tree span is always pointer width. This is convenient for storing
155+
/// it in other data structures.
156+
uint32_t number;
153157

154158
SubElementNumber(unsigned number) : number(number) {}
155159

@@ -168,9 +172,22 @@ struct SubElementNumber {
168172
/// \returns None if we didn't know how to compute sub-element for this
169173
/// projection.
170174
static Optional<SubElementNumber> compute(SILValue projectionFromRoot,
171-
SILValue root);
175+
SILValue root) {
176+
assert(projectionFromRoot->getType().getCategory() ==
177+
root->getType().getCategory() &&
178+
"projectionFromRoot and root must both be objects or address.");
179+
if (root->getType().isObject())
180+
return computeForValue(projectionFromRoot, root);
181+
return computeForAddress(projectionFromRoot, root);
182+
}
172183

173184
operator unsigned() const { return number; }
185+
186+
private:
187+
static Optional<SubElementNumber>
188+
computeForAddress(SILValue projectionFromRoot, SILValue rootAddress);
189+
static Optional<SubElementNumber> computeForValue(SILValue projectionFromRoot,
190+
SILValue rootValue);
174191
};
175192

176193
/// Given a type T, this is the number of leaf field types in T's type tree. A
@@ -196,16 +213,24 @@ struct TypeSubElementCount {
196213
TypeSubElementCount(SILType type, SILModule &mod,
197214
TypeExpansionContext context);
198215

216+
/// Helper method that invokes the SILModule &mod entry point.
217+
TypeSubElementCount(SILType type, SILFunction *fn)
218+
: TypeSubElementCount(type, fn->getModule(),
219+
TypeExpansionContext(*fn)) {}
220+
199221
TypeSubElementCount(SILValue value)
200222
: TypeSubElementCount(value->getType(), *value->getModule(),
201223
TypeExpansionContext(*value->getFunction())) {}
202224

203225
operator unsigned() const { return number; }
204226
};
205227

228+
class FieldSensitivePrunedLiveness;
229+
206230
/// A span of leaf elements in the sub-element break down of the linearization
207231
/// of the type tree of a type T.
208232
struct TypeTreeLeafTypeRange {
233+
friend FieldSensitivePrunedLiveness;
209234
SubElementNumber startEltOffset;
210235
SubElementNumber endEltOffset;
211236

@@ -218,6 +243,10 @@ struct TypeTreeLeafTypeRange {
218243
TypeTreeLeafTypeRange(SILValue rootAddress)
219244
: startEltOffset(0), endEltOffset(TypeSubElementCount(rootAddress)) {}
220245

246+
/// The leaf type range for the entire type tree.
247+
TypeTreeLeafTypeRange(SILType rootType, SILFunction *fn)
248+
: startEltOffset(0), endEltOffset(TypeSubElementCount(rootType, fn)) {}
249+
221250
/// The leaf type sub-range of the type tree of \p rootAddress, consisting of
222251
/// \p projectedAddress and all of \p projectedAddress's descendent fields in
223252
/// the type tree.
@@ -234,6 +263,32 @@ struct TypeTreeLeafTypeRange {
234263
*startEltOffset + TypeSubElementCount(projectedAddress)}};
235264
}
236265

266+
/// Given a type \p rootType and a set of needed elements specified by the bit
267+
/// vector \p neededElements, place into \p foundContiguousTypeRanges a set of
268+
/// TypeTreeLeafTypeRanges that are associated with the bit vectors
269+
/// elements. As a constraint, we ensure that if \p neededElements has bits
270+
/// set that are part of subsequent fields of a type that is only partially
271+
/// needed, the two fields are represented as separate ranges. This ensures
272+
/// that it is easy to use this API to correspond to independent operations
273+
/// for the fields.
274+
static void convertNeededElementsToContiguousTypeRanges(
275+
SILFunction *fn,
276+
SILType rootType, SmallBitVector &neededElements,
277+
SmallVectorImpl<TypeTreeLeafTypeRange> &foundContiguousTypeRanges);
278+
279+
static void constructProjectionsForNeededElements(
280+
SILValue rootValue, SILInstruction *insertPt, SmallBitVector &neededElements,
281+
SmallVectorImpl<SILValue> &resultingProjections);
282+
283+
bool operator==(const TypeTreeLeafTypeRange &other) const {
284+
return startEltOffset == other.startEltOffset &&
285+
endEltOffset == other.endEltOffset;
286+
}
287+
288+
bool operator!=(const TypeTreeLeafTypeRange &other) const {
289+
return !(*this == other);
290+
}
291+
237292
/// Is the given leaf type specified by \p singleLeafElementNumber apart of
238293
/// our \p range of leaf type values in the our larger type.
239294
bool contains(SubElementNumber singleLeafElementNumber) const {
@@ -256,6 +311,21 @@ struct TypeTreeLeafTypeRange {
256311
// Othrwise, see if endEltOffset - 1 is within the range.
257312
return startEltOffset <= rangeLastElt && rangeLastElt < endEltOffset;
258313
}
314+
315+
IntRange<unsigned> getRange() const {
316+
return range(startEltOffset, endEltOffset);
317+
}
318+
319+
bool empty() const { return startEltOffset == endEltOffset; }
320+
321+
unsigned size() const { return endEltOffset - startEltOffset; }
322+
323+
void getPerFieldTypeRange(SILType type, SILFunction *fn, llvm::function_ref<void (SILType, TypeTreeLeafTypeRange)> callback);
324+
325+
/// Construct per field projections if the projection range has any bits in
326+
/// common with filterBitVector.
327+
void constructFilteredProjections(SILValue value, SILInstruction *insertPt, SmallBitVector &filterBitVector,
328+
llvm::function_ref<void (SILValue, TypeTreeLeafTypeRange)> callback);
259329
};
260330

261331
/// This is exactly like pruned liveness except that instead of tracking a
@@ -265,7 +335,7 @@ struct TypeTreeLeafTypeRange {
265335
/// DISCUSSION: One can view a type T as a tree with recursively each field F of
266336
/// the type T being a child of T in the tree. We say recursively since the tree
267337
/// unfolds for F and its children as well.
268-
class FieldSensitiveAddressPrunedLiveness {
338+
class FieldSensitivePrunedLiveness {
269339
PrunedLiveBlocks liveBlocks;
270340

271341
struct InterestingUser {
@@ -295,14 +365,14 @@ class FieldSensitiveAddressPrunedLiveness {
295365
llvm::SmallMapVector<SILInstruction *, InterestingUser, 8> users;
296366

297367
/// The root address of our type tree.
298-
SILValue rootAddress;
368+
SILValue rootValue;
299369

300370
public:
301-
FieldSensitiveAddressPrunedLiveness(
371+
FieldSensitivePrunedLiveness(
302372
SILFunction *fn, SILValue rootValue,
303373
SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr)
304374
: liveBlocks(TypeSubElementCount(rootValue), discoveredBlocks),
305-
rootAddress(rootValue) {}
375+
rootValue(rootValue) {}
306376

307377
bool empty() const {
308378
assert(!liveBlocks.empty() || users.empty());
@@ -314,10 +384,15 @@ class FieldSensitiveAddressPrunedLiveness {
314384
users.clear();
315385
}
316386

317-
SILValue getRootAddress() const { return rootAddress; }
387+
SILValue getRootValue() const { return rootValue; }
388+
SILType getRootType() const { return rootValue->getType(); }
318389

319390
unsigned numLiveBlocks() const { return liveBlocks.numLiveBlocks(); }
320391

392+
TypeTreeLeafTypeRange getTopLevelSpan() const {
393+
return TypeTreeLeafTypeRange(0, getNumSubElements());
394+
}
395+
321396
/// If the constructor was provided with a vector to populate, then this
322397
/// returns the list of all live blocks with no duplicates.
323398
ArrayRef<SILBasicBlock *> getDiscoveredBlocks() const {
@@ -396,49 +471,142 @@ class FieldSensitiveAddressPrunedLiveness {
396471
}
397472

398473
unsigned getNumSubElements() const { return liveBlocks.getNumBitsToTrack(); }
399-
400-
/// Return true if \p inst occurs before the liveness boundary. Used when the
401-
/// client already knows that inst occurs after the start of liveness.
402-
void isWithinBoundary(SILInstruction *inst, SmallBitVector &outVector) const;
403474
};
404475

405-
/// Record the last use points and CFG edges that form the boundary of
406-
/// FieldSensitiveAddressPrunedLiveness. It does this on a per type tree leaf
407-
/// node basis.
408-
struct FieldSensitiveAddressPrunedLivenessBoundary {
409-
/// The list of last users and an associated SILValue that is the address that
410-
/// is being used. The address can be used to determine the start sub element
411-
/// number of the user in the type tree and the end sub element number.
476+
template <typename LivenessWithDefs>
477+
class FieldSensitivePrunedLiveRange : public FieldSensitivePrunedLiveness {
478+
const LivenessWithDefs &asImpl() const {
479+
return reinterpret_cast<const LivenessWithDefs &>(*this);
480+
}
481+
482+
public:
483+
FieldSensitivePrunedLiveRange(
484+
SILFunction *fn, SILValue rootValue,
485+
SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr)
486+
: FieldSensitivePrunedLiveness(fn, rootValue, discoveredBlocks) {}
487+
488+
/// Check if \p inst occurs in between the definition of a def and the
489+
/// liveness boundary for bits in \p span.
412490
///
413-
/// TODO (MG): If we don't eventually need to store the SILValue here (I am
414-
/// not sure yet...), just store a tuple with the start/end sub element
415-
/// number.
416-
SmallVector<std::tuple<SILInstruction *, TypeTreeLeafTypeRange>, 8> lastUsers;
491+
/// NOTE: It is assumed that \p inst is correctly described by span.
492+
bool isWithinBoundary(SILInstruction *inst, TypeTreeLeafTypeRange span) const __attribute__((optnone));
493+
};
494+
495+
/// Single defined liveness.
496+
///
497+
// An SSA def results in pruned liveness with a contiguous liverange.
498+
///
499+
/// An unreachable self-loop might result in a "gap" between the last use above
500+
/// the def in the same block.
501+
///
502+
/// For SSA live ranges, a single "def" block dominates all uses. If no def
503+
/// block is provided, liveness is computed as if defined by a function
504+
/// argument. If the client does not provide a single, dominating def block,
505+
/// then the client must at least ensure that no uses precede the first
506+
/// definition in a def block. Since this analysis does not remember the
507+
/// positions of defs, it assumes that, within a block, uses follow
508+
/// defs. Breaking this assumption will result in a "hole" in the live range in
509+
/// which the def block's predecessors incorrectly remain dead. This situation
510+
/// could be handled by adding an updateForUseBeforeFirstDef() API.
511+
class FieldSensitiveSSAPrunedLiveRange
512+
: public FieldSensitivePrunedLiveRange<FieldSensitiveSSAPrunedLiveRange> {
513+
std::pair<SILValue, Optional<TypeTreeLeafTypeRange>> def = {{}, {}};
514+
515+
/// None for arguments.
516+
std::pair<SILInstruction *, Optional<TypeTreeLeafTypeRange>> defInst = {
517+
nullptr, None};
518+
519+
public:
520+
FieldSensitiveSSAPrunedLiveRange(
521+
SILFunction *fn, SILValue rootValue,
522+
SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr)
523+
: FieldSensitivePrunedLiveRange(fn, rootValue, discoveredBlocks) {}
417524

418-
/// Blocks where the value was live out but had a successor that was dead.
419-
SmallVector<SILBasicBlock *, 8> boundaryEdges;
525+
std::pair<SILValue, Optional<TypeTreeLeafTypeRange>> getDef() const {
526+
return def;
527+
}
420528

421529
void clear() {
422-
lastUsers.clear();
423-
boundaryEdges.clear();
530+
def = {{}, {}};
531+
defInst = {{}, {}};
532+
FieldSensitivePrunedLiveRange::clear();
424533
}
425534

426-
/// Compute the boundary from the blocks discovered during liveness analysis.
427-
///
428-
/// Precondition: \p liveness.getDiscoveredBlocks() is a valid list of all
429-
/// live blocks with no duplicates.
430-
///
431-
/// The computed boundary will completely post-dominate, including dead end
432-
/// paths. The client should query DeadEndBlocks to ignore those dead end
433-
/// paths.
434-
void compute(const FieldSensitiveAddressPrunedLiveness &liveness);
535+
void initializeDef(SILValue def, TypeTreeLeafTypeRange span) {
536+
assert(!this->def.first && !this->def.second && "reinitialization");
435537

436-
private:
437-
void
438-
findLastUserInBlock(SILBasicBlock *bb,
439-
FieldSensitiveAddressPrunedLivenessBoundary &boundary,
440-
const FieldSensitiveAddressPrunedLiveness &liveness,
441-
unsigned subElementNumber);
538+
this->def = {def, span};
539+
defInst = {def->getDefiningInstruction(), span};
540+
initializeDefBlock(def->getParentBlock(), span);
541+
}
542+
543+
bool isInitialized() const { return bool(def.first) && bool(def.second); }
544+
545+
bool isDef(SILInstruction *inst, unsigned bit) const {
546+
return inst == defInst.first && defInst.second->contains(bit);
547+
}
548+
549+
bool isDefBlock(SILBasicBlock *block, unsigned bit) const {
550+
return def.first->getParentBlock() == block && def.second->contains(bit);
551+
}
552+
};
553+
554+
/// MultiDefPrunedLiveness is computed incrementally by calling updateForUse.
555+
///
556+
/// Defs should be initialized before calling updatingForUse on any def
557+
/// that reaches the use.
558+
class FieldSensitiveMultiDefPrunedLiveRange
559+
: public FieldSensitivePrunedLiveRange<
560+
FieldSensitiveMultiDefPrunedLiveRange> {
561+
// TODO: See if we can make this more efficient.
562+
llvm::SmallMapVector<SILNode *, TypeTreeLeafTypeRange, 8> defs;
563+
llvm::SmallMapVector<SILBasicBlock *, TypeTreeLeafTypeRange, 8> defBlocks;
564+
565+
public:
566+
FieldSensitiveMultiDefPrunedLiveRange(
567+
SILFunction *fn, SILValue rootValue,
568+
SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr)
569+
: FieldSensitivePrunedLiveRange(fn, rootValue, discoveredBlocks) {}
570+
571+
void clear() { llvm_unreachable("multi-def liveness cannot be reused"); }
572+
573+
void initializeDef(SILValue def, TypeTreeLeafTypeRange span) {
574+
defs.insert({def, span});
575+
auto *block = def->getParentBlock();
576+
defBlocks.insert({block, span});
577+
initializeDefBlock(block, span);
578+
}
579+
580+
void initializeDef(SILInstruction *def, TypeTreeLeafTypeRange span) {
581+
defs.insert({cast<SILNode>(def), span});
582+
auto *block = def->getParent();
583+
defBlocks.insert({block, span});
584+
initializeDefBlock(block, span);
585+
}
586+
587+
bool isInitialized() const { return !defs.empty(); }
588+
589+
/// Return true if this block is a def block for this specific bit.
590+
bool isDefBlock(SILBasicBlock *block, unsigned bit) const {
591+
auto iter = defBlocks.find(block);
592+
if (iter == defBlocks.end())
593+
return false;
594+
return iter->second.contains(bit);
595+
}
596+
597+
bool isDef(SILInstruction *inst, unsigned bit) const {
598+
auto iter = defs.find(cast<SILNode>(inst));
599+
if (iter == defs.end())
600+
return false;
601+
return iter->second.contains(bit);
602+
}
603+
604+
bool isDef(SILValue value, unsigned bit) const {
605+
auto iter = defs.find(cast<SILNode>(value));
606+
if (iter == defs.end())
607+
return false;
608+
return iter->second.contains(bit);
609+
}
442610
};
443611

444612
} // namespace swift

include/swift/SIL/SILType.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,8 @@ class SILType {
495495
SILType getFieldType(VarDecl *field, SILModule &M,
496496
TypeExpansionContext context) const;
497497

498+
SILType getFieldType(VarDecl *field, SILFunction *fn) const;
499+
498500
/// Given that this is an enum type, return the lowered type of the
499501
/// data for the given element. Applies substitutions as necessary.
500502
/// The result will have the same value category as the base type.

lib/SIL/IR/SILType.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,10 @@ SILType SILType::getFieldType(VarDecl *field, SILModule &M,
320320
return getFieldType(field, M.Types, context);
321321
}
322322

323+
SILType SILType::getFieldType(VarDecl *field, SILFunction *fn) const {
324+
return getFieldType(field, fn->getModule(), fn->getTypeExpansionContext());
325+
}
326+
323327
SILType SILType::getEnumElementType(EnumElementDecl *elt, TypeConverter &TC,
324328
TypeExpansionContext context) const {
325329
assert(elt->getDeclContext() == getEnumOrBoundGenericEnum());

0 commit comments

Comments
 (0)