Skip to content

Commit 01b6438

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. The current blocker is IROutliner has its own diy cloning, which doesn't use ValueMapper and it does make direct use of constant use lists. 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 b0025b6 commit 01b6438

24 files changed

+255
-106
lines changed

llvm/include/llvm/IR/Use.h

Lines changed: 6 additions & 17 deletions
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

Lines changed: 97 additions & 24 deletions
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,53 @@ 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 == nullptr && "should not have uselist remnant");
951+
assert(Next == nullptr && "should not have uselist remnant");
952+
--Count;
953+
}
954+
955+
void Use::removeFromList(Use *&List) {
956+
*Prev = Next;
957+
if (Next)
958+
Next->Prev = Prev;
959+
}
960+
892961
void Use::set(Value *V) {
893-
if (Val) removeFromList();
962+
if (Val) {
963+
Val->removeUse(*this);
964+
}
894965
Val = V;
895-
if (V) V->addUse(*this);
966+
if (V) {
967+
V->addUse(*this);
968+
}
896969
}
897970

898971
Value *Use::operator=(Value *RHS) {
@@ -906,7 +979,7 @@ const Use &Use::operator=(const Use &RHS) {
906979
}
907980

908981
template <class Compare> void Value::sortUseList(Compare Cmp) {
909-
if (!UseList || !UseList->Next)
982+
if (!hasUseList() || !Uses.List || !Uses.List->Next)
910983
// No need to sort 0 or 1 uses.
911984
return;
912985

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

921994
// Collect the first use, turning it into a single-item list.
922-
Use *Next = UseList->Next;
923-
UseList->Next = nullptr;
995+
Use *Next = Uses.List->Next;
996+
Uses.List->Next = nullptr;
924997
unsigned NumSlots = 1;
925-
Slots[0] = UseList;
998+
Slots[0] = Uses.List;
926999

9271000
// Collect all but the last use.
9281001
while (Next->Next) {
@@ -958,15 +1031,15 @@ template <class Compare> void Value::sortUseList(Compare Cmp) {
9581031
// Merge all the lists together.
9591032
assert(Next && "Expected one more Use");
9601033
assert(!Next->Next && "Expected only one Use");
961-
UseList = Next;
1034+
Uses.List = Next;
9621035
for (unsigned I = 0; I < NumSlots; ++I)
9631036
if (Slots[I])
964-
// Since the uses in Slots[I] originally preceded those in UseList, send
1037+
// Since the uses in Slots[I] originally preceded those in Uses.List, send
9651038
// Slots[I] in as the left parameter to maintain a stable sort.
966-
UseList = mergeUseLists(Slots[I], UseList, Cmp);
1039+
Uses.List = mergeUseLists(Slots[I], Uses.List, Cmp);
9671040

9681041
// Fix the Prev pointers.
969-
for (Use *I = UseList, **Prev = &UseList; I; I = I->Next) {
1042+
for (Use *I = Uses.List, **Prev = &Uses.List; I; I = I->Next) {
9701043
I->Prev = Prev;
9711044
Prev = &I->Next;
9721045
}

llvm/lib/Analysis/TypeMetadataUtils.cpp

Lines changed: 3 additions & 0 deletions
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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8863,6 +8863,8 @@ bool LLParser::parseMDNodeVector(SmallVectorImpl<Metadata *> &Elts) {
88638863
//===----------------------------------------------------------------------===//
88648864
bool LLParser::sortUseListOrder(Value *V, ArrayRef<unsigned> Indexes,
88658865
SMLoc Loc) {
8866+
if (isa<ConstantData>(V))
8867+
return false;
88668868
if (V->use_empty())
88678869
return error(Loc, "value has no uses");
88688870

llvm/lib/Bitcode/Reader/BitcodeReader.cpp

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

llvm/lib/Bitcode/Writer/ValueEnumerator.cpp

Lines changed: 3 additions & 0 deletions
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

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

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

llvm/lib/CodeGen/CodeGenPrepare.cpp

Lines changed: 3 additions & 0 deletions
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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 7 additions & 2 deletions
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.
@@ -251,7 +255,8 @@ static UseListOrderMap predictUseListOrder(const Module *M) {
251255
UseListOrderMap ULOM;
252256
for (const auto &Pair : OM) {
253257
const Value *V = Pair.first;
254-
if (V->use_empty() || std::next(V->use_begin()) == V->use_end())
258+
if (!V->hasUseList() || V->use_empty() ||
259+
std::next(V->use_begin()) == V->use_end())
255260
continue;
256261

257262
std::vector<unsigned> Shuffle =

llvm/lib/IR/Instruction.cpp

Lines changed: 3 additions & 1 deletion
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

Lines changed: 6 additions & 2 deletions
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)