Skip to content

Commit 21970c5

Browse files
committed
IR: Remove uselist for constantdata
This is a resurrected version of the patch attached to this RFC: https://discourse.llvm.org/t/rfc-constantdata-should-not-have-use-lists/42606 In this adaptation, there are a few differences. In the original patch, the Use's use list was replaced with an unsigned* to the reference count in the value. This version leaves them as null and leaves the ref counting only in Value. Remove use-lists from instances of ConstantData (which are shared across modules and have no operands). To continue supporting most of the use-list API, store a ref-count in place of the use-list; this is for API like Value::use_empty and Value::hasNUses. Operations that actually need the use-list -- like Value::use_begin -- will assert. This change has three benefits: 1. The compiler output cannot in any way depend on the use-list order of instances of ConstantData. 2. There's no use-list traffic when adding and removing simple constants from operand lists (although there is ref-count traffic; YMMV). 3. It's cheaper to serialize use-lists (since we're no longer serializing the use-list order of things like i32 0). The downside is that you can't look at all the users of ConstantData, but traversals of users of i32 0 are already ill-advised. Possible follow-ups: - Track if an instance of a ConstantVector/ConstantArray/etc. is known to have all ConstantData arguments, and drop the use-lists to ref-counts in those cases. Callers need to check Value::hasUseList before iterating through the use-list. - Remove even the ref-counts. I'm not sure they have any benefit besides minimizing the scope of this commit, and maintaining the counts is not free. Fixes #58629
1 parent 36377c3 commit 21970c5

24 files changed

+242
-104
lines changed

llvm/include/llvm/IR/Use.h

+6-17
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
namespace llvm {
2424

2525
template <typename> struct simplify_type;
26+
class ConstantData;
2627
class User;
2728
class Value;
2829

@@ -42,10 +43,7 @@ class Use {
4243

4344
private:
4445
/// Destructor - Only for zap()
45-
~Use() {
46-
if (Val)
47-
removeFromList();
48-
}
46+
~Use();
4947

5048
/// Constructor
5149
Use(User *Parent) : Parent(Parent) {}
@@ -87,19 +85,10 @@ class Use {
8785
Use **Prev = nullptr;
8886
User *Parent = nullptr;
8987

90-
void addToList(Use **List) {
91-
Next = *List;
92-
if (Next)
93-
Next->Prev = &Next;
94-
Prev = List;
95-
*Prev = this;
96-
}
97-
98-
void removeFromList() {
99-
*Prev = Next;
100-
if (Next)
101-
Next->Prev = Prev;
102-
}
88+
inline void addToList(unsigned &Count);
89+
inline void addToList(Use *&List);
90+
inline void removeFromList(unsigned &Count);
91+
inline void removeFromList(Use *&List);
10392
};
10493

10594
/// Allow clients to treat uses just like values when using

llvm/include/llvm/IR/Value.h

+94-24
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,10 @@ class Value {
116116

117117
private:
118118
Type *VTy;
119-
Use *UseList;
119+
union {
120+
Use *List = nullptr;
121+
unsigned Count;
122+
} Uses;
120123

121124
friend class ValueAsMetadata; // Allow access to IsUsedByMD.
122125
friend class ValueHandleBase; // Allow access to HasValueHandle.
@@ -341,21 +344,28 @@ class Value {
341344
#endif
342345
}
343346

347+
/// Check if this Value has a use-list.
348+
bool hasUseList() const { return !isa<ConstantData>(this); }
349+
344350
bool use_empty() const {
345351
assertModuleIsMaterialized();
346-
return UseList == nullptr;
352+
return hasUseList() ? Uses.List == nullptr : Uses.Count == 0;
347353
}
348354

349355
bool materialized_use_empty() const {
350-
return UseList == nullptr;
356+
return hasUseList() ? Uses.List == nullptr : !Uses.Count;
351357
}
352358

353359
using use_iterator = use_iterator_impl<Use>;
354360
using const_use_iterator = use_iterator_impl<const Use>;
355361

356-
use_iterator materialized_use_begin() { return use_iterator(UseList); }
362+
use_iterator materialized_use_begin() {
363+
assert(hasUseList());
364+
return use_iterator(Uses.List);
365+
}
357366
const_use_iterator materialized_use_begin() const {
358-
return const_use_iterator(UseList);
367+
assert(hasUseList());
368+
return const_use_iterator(Uses.List);
359369
}
360370
use_iterator use_begin() {
361371
assertModuleIsMaterialized();
@@ -382,17 +392,18 @@ class Value {
382392
return materialized_uses();
383393
}
384394

385-
bool user_empty() const {
386-
assertModuleIsMaterialized();
387-
return UseList == nullptr;
388-
}
395+
bool user_empty() const { return use_empty(); }
389396

390397
using user_iterator = user_iterator_impl<User>;
391398
using const_user_iterator = user_iterator_impl<const User>;
392399

393-
user_iterator materialized_user_begin() { return user_iterator(UseList); }
400+
user_iterator materialized_user_begin() {
401+
assert(hasUseList());
402+
return user_iterator(Uses.List);
403+
}
394404
const_user_iterator materialized_user_begin() const {
395-
return const_user_iterator(UseList);
405+
assert(hasUseList());
406+
return const_user_iterator(Uses.List);
396407
}
397408
user_iterator user_begin() {
398409
assertModuleIsMaterialized();
@@ -431,7 +442,11 @@ class Value {
431442
///
432443
/// This is specialized because it is a common request and does not require
433444
/// traversing the whole use list.
434-
bool hasOneUse() const { return hasSingleElement(uses()); }
445+
bool hasOneUse() const {
446+
if (!hasUseList())
447+
return Uses.Count == 1;
448+
return hasSingleElement(uses());
449+
}
435450

436451
/// Return true if this Value has exactly N uses.
437452
bool hasNUses(unsigned N) const;
@@ -493,6 +508,8 @@ class Value {
493508
static void dropDroppableUse(Use &U);
494509

495510
/// Check if this value is used in the specified basic block.
511+
///
512+
/// Not supported for ConstantData.
496513
bool isUsedInBasicBlock(const BasicBlock *BB) const;
497514

498515
/// This method computes the number of uses of this Value.
@@ -502,7 +519,19 @@ class Value {
502519
unsigned getNumUses() const;
503520

504521
/// This method should only be used by the Use class.
505-
void addUse(Use &U) { U.addToList(&UseList); }
522+
void addUse(Use &U) {
523+
if (hasUseList())
524+
U.addToList(Uses.List);
525+
else
526+
U.addToList(Uses.Count);
527+
}
528+
529+
void removeUse(Use &U) {
530+
if (hasUseList())
531+
U.removeFromList(Uses.List);
532+
else
533+
U.removeFromList(Uses.Count);
534+
}
506535

507536
/// Concrete subclass of this.
508537
///
@@ -843,7 +872,8 @@ class Value {
843872
///
844873
/// \return the first element in the list.
845874
///
846-
/// \note Completely ignores \a Use::Prev (doesn't read, doesn't update).
875+
/// \note Completely ignores \a Use::PrevOrCount (doesn't read, doesn't
876+
/// update).
847877
template <class Compare>
848878
static Use *mergeUseLists(Use *L, Use *R, Compare Cmp) {
849879
Use *Merged;
@@ -889,10 +919,50 @@ inline raw_ostream &operator<<(raw_ostream &OS, const Value &V) {
889919
return OS;
890920
}
891921

922+
inline Use::~Use() {
923+
if (Val)
924+
Val->removeUse(*this);
925+
}
926+
927+
void Use::addToList(unsigned &Count) {
928+
assert(isa<ConstantData>(Val) && "Only ConstantData is ref-counted");
929+
++Count;
930+
931+
// We don't have a uselist - clear the remnant if we are replacing a
932+
// non-constant value.
933+
Prev = nullptr;
934+
Next = nullptr;
935+
}
936+
937+
void Use::addToList(Use *&List) {
938+
assert(!isa<ConstantData>(Val) && "ConstantData has no use-list");
939+
940+
Next = List;
941+
if (Next)
942+
Next->Prev = &Next;
943+
Prev = &List;
944+
List = this;
945+
}
946+
947+
void Use::removeFromList(unsigned &Count) {
948+
assert(isa<ConstantData>(Val));
949+
assert(Count > 0 && "reference count underflow");
950+
assert(!Prev && !Next && "should not have uselist remnant");
951+
--Count;
952+
}
953+
954+
void Use::removeFromList(Use *&List) {
955+
*Prev = Next;
956+
if (Next)
957+
Next->Prev = Prev;
958+
}
959+
892960
void Use::set(Value *V) {
893-
if (Val) removeFromList();
961+
if (Val)
962+
Val->removeUse(*this);
894963
Val = V;
895-
if (V) V->addUse(*this);
964+
if (V)
965+
V->addUse(*this);
896966
}
897967

898968
Value *Use::operator=(Value *RHS) {
@@ -906,7 +976,7 @@ const Use &Use::operator=(const Use &RHS) {
906976
}
907977

908978
template <class Compare> void Value::sortUseList(Compare Cmp) {
909-
if (!UseList || !UseList->Next)
979+
if (!hasUseList() || !Uses.List || !Uses.List->Next)
910980
// No need to sort 0 or 1 uses.
911981
return;
912982

@@ -919,10 +989,10 @@ template <class Compare> void Value::sortUseList(Compare Cmp) {
919989
Use *Slots[MaxSlots];
920990

921991
// Collect the first use, turning it into a single-item list.
922-
Use *Next = UseList->Next;
923-
UseList->Next = nullptr;
992+
Use *Next = Uses.List->Next;
993+
Uses.List->Next = nullptr;
924994
unsigned NumSlots = 1;
925-
Slots[0] = UseList;
995+
Slots[0] = Uses.List;
926996

927997
// Collect all but the last use.
928998
while (Next->Next) {
@@ -958,15 +1028,15 @@ template <class Compare> void Value::sortUseList(Compare Cmp) {
9581028
// Merge all the lists together.
9591029
assert(Next && "Expected one more Use");
9601030
assert(!Next->Next && "Expected only one Use");
961-
UseList = Next;
1031+
Uses.List = Next;
9621032
for (unsigned I = 0; I < NumSlots; ++I)
9631033
if (Slots[I])
964-
// Since the uses in Slots[I] originally preceded those in UseList, send
1034+
// Since the uses in Slots[I] originally preceded those in Uses.List, send
9651035
// Slots[I] in as the left parameter to maintain a stable sort.
966-
UseList = mergeUseLists(Slots[I], UseList, Cmp);
1036+
Uses.List = mergeUseLists(Slots[I], Uses.List, Cmp);
9671037

9681038
// Fix the Prev pointers.
969-
for (Use *I = UseList, **Prev = &UseList; I; I = I->Next) {
1039+
for (Use *I = Uses.List, **Prev = &Uses.List; I; I = I->Next) {
9701040
I->Prev = Prev;
9711041
Prev = &I->Next;
9721042
}

llvm/lib/Analysis/TypeMetadataUtils.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ findCallsAtConstantOffset(SmallVectorImpl<DevirtCallSite> &DevirtCalls,
5454
static void findLoadCallsAtConstantOffset(
5555
const Module *M, SmallVectorImpl<DevirtCallSite> &DevirtCalls, Value *VPtr,
5656
int64_t Offset, const CallInst *CI, DominatorTree &DT) {
57+
if (!VPtr->hasUseList())
58+
return;
59+
5760
for (const Use &U : VPtr->uses()) {
5861
Value *User = U.getUser();
5962
if (isa<BitCastInst>(User)) {

llvm/lib/AsmParser/LLParser.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -8857,6 +8857,8 @@ bool LLParser::parseMDNodeVector(SmallVectorImpl<Metadata *> &Elts) {
88578857
//===----------------------------------------------------------------------===//
88588858
bool LLParser::sortUseListOrder(Value *V, ArrayRef<unsigned> Indexes,
88598859
SMLoc Loc) {
8860+
if (isa<ConstantData>(V))
8861+
return false;
88608862
if (V->use_empty())
88618863
return error(Loc, "value has no uses");
88628864

llvm/lib/Bitcode/Reader/BitcodeReader.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -3856,6 +3856,10 @@ Error BitcodeReader::parseUseLists() {
38563856
V = FunctionBBs[ID];
38573857
} else
38583858
V = ValueList[ID];
3859+
3860+
if (isa<ConstantData>(V))
3861+
break;
3862+
38593863
unsigned NumUses = 0;
38603864
SmallDenseMap<const Use *, unsigned, 16> Order;
38613865
for (const Use &U : V->materialized_uses()) {

llvm/lib/Bitcode/Writer/ValueEnumerator.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,9 @@ static void predictValueUseListOrderImpl(const Value *V, const Function *F,
230230

231231
static void predictValueUseListOrder(const Value *V, const Function *F,
232232
OrderMap &OM, UseListOrderStack &Stack) {
233+
if (isa<ConstantData>(V))
234+
return;
235+
233236
auto &IDPair = OM[V];
234237
assert(IDPair.first && "Unmapped value");
235238
if (IDPair.second)

llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -3953,7 +3953,7 @@ static void emitGlobalConstantImpl(const DataLayout &DL, const Constant *CV,
39533953
// Globals with sub-elements such as combinations of arrays and structs
39543954
// are handled recursively by emitGlobalConstantImpl. Keep track of the
39553955
// constant symbol base and the current position with BaseCV and Offset.
3956-
if (!BaseCV && CV->hasOneUse())
3956+
if (!isa<ConstantData>(CV) && !BaseCV && CV->hasOneUse())
39573957
BaseCV = dyn_cast<Constant>(CV->user_back());
39583958

39593959
if (isa<ConstantAggregateZero>(CV)) {

llvm/lib/CodeGen/CodeGenPrepare.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -8547,6 +8547,9 @@ static bool optimizeBranch(BranchInst *Branch, const TargetLowering &TLI,
85478547
return false;
85488548

85498549
Value *X = Cmp->getOperand(0);
8550+
if (!X->hasUseList())
8551+
return false;
8552+
85508553
APInt CmpC = cast<ConstantInt>(Cmp->getOperand(1))->getValue();
85518554

85528555
for (auto *U : X->users()) {

llvm/lib/CodeGen/ComplexDeinterleavingPass.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -1034,6 +1034,9 @@ ComplexDeinterleavingGraph::identifyPartialReduction(Value *R, Value *I) {
10341034
if (!isa<VectorType>(R->getType()) || !isa<VectorType>(I->getType()))
10351035
return nullptr;
10361036

1037+
if (!R->hasUseList() || !I->hasUseList())
1038+
return nullptr;
1039+
10371040
auto CommonUser =
10381041
findCommonBetweenCollections<Value *>(R->users(), I->users());
10391042
if (!CommonUser)

llvm/lib/IR/AsmWriter.cpp

+7-2
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,15 @@ static void orderValue(const Value *V, OrderMap &OM) {
125125
if (OM.lookup(V))
126126
return;
127127

128-
if (const Constant *C = dyn_cast<Constant>(V))
128+
if (const Constant *C = dyn_cast<Constant>(V)) {
129+
if (isa<ConstantData>(C))
130+
return;
131+
129132
if (C->getNumOperands() && !isa<GlobalValue>(C))
130133
for (const Value *Op : C->operands())
131134
if (!isa<BasicBlock>(Op) && !isa<GlobalValue>(Op))
132135
orderValue(Op, OM);
136+
}
133137

134138
// Note: we cannot cache this lookup above, since inserting into the map
135139
// changes the map's size, and thus affects the other IDs.
@@ -275,7 +279,8 @@ static UseListOrderMap predictUseListOrder(const Module *M) {
275279
UseListOrderMap ULOM;
276280
for (const auto &Pair : OM) {
277281
const Value *V = Pair.first;
278-
if (V->use_empty() || std::next(V->use_begin()) == V->use_end())
282+
if (!V->hasUseList() || V->use_empty() ||
283+
std::next(V->use_begin()) == V->use_end())
279284
continue;
280285

281286
std::vector<unsigned> Shuffle =

llvm/lib/IR/Instruction.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,9 @@ std::optional<BasicBlock::iterator> Instruction::getInsertionPointAfterDef() {
373373
}
374374

375375
bool Instruction::isOnlyUserOfAnyOperand() {
376-
return any_of(operands(), [](Value *V) { return V->hasOneUser(); });
376+
return any_of(operands(), [](const Value *V) {
377+
return V->hasUseList() && V->hasOneUser();
378+
});
377379
}
378380

379381
void Instruction::setHasNoUnsignedWrap(bool b) {

llvm/lib/IR/Use.cpp

+6-2
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,15 @@ void Use::swap(Use &RHS) {
1919
std::swap(Next, RHS.Next);
2020
std::swap(Prev, RHS.Prev);
2121

22-
*Prev = this;
22+
if (Prev)
23+
*Prev = this;
24+
2325
if (Next)
2426
Next->Prev = &Next;
2527

26-
*RHS.Prev = &RHS;
28+
if (RHS.Prev)
29+
*RHS.Prev = &RHS;
30+
2731
if (RHS.Next)
2832
RHS.Next->Prev = &RHS.Next;
2933
}

0 commit comments

Comments
 (0)