Skip to content

Commit 51a3bd9

Browse files
authored
IR: Remove reference counts from ConstantData (#137314)
This is a follow up change to eliminating uselists for ConstantData. In the previous revision, ConstantData had a replacement reference count instead of a uselist. This reference count was misleading, and not useful in the same way as it would be for another value. The references may not have even been in the current module, since these are shared throughout the LLVMContext. This doesn't space leak any more than we previously did; nothing was attempting to garbage collect unused constants. Previously the use_empty, and hasNUses type of APIs were supported through the reference count. These now behave as if the uses are always empty. Ideally it would be illegal to inspect these, but this forces API complexity into quite a few places. It may be doable to make it illegal to check these counts, but I would like there to be a targeted fuzzing effort to make sure every transform properly deals with a constant in every operand position. All tests pass if I turn the hasNUses* and getNumUses queries into assertions, only hasOneUse in particular appears to hit in some set of contexts. I've added unit tests to ensure logical consistency between these cases
1 parent 87f312a commit 51a3bd9

File tree

9 files changed

+103
-105
lines changed

9 files changed

+103
-105
lines changed

llvm/docs/ReleaseNotes.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ Makes programs 10x faster by doing Special New Thing.
5656
Changes to the LLVM IR
5757
----------------------
5858

59-
* It is no longer permitted to inspect the uses of ConstantData
59+
* It is no longer permitted to inspect the uses of ConstantData. Use
60+
count APIs will behave as if they have no uses (i.e. use_empty() is
61+
always true).
6062

6163
* The `nocapture` attribute has been replaced by `captures(none)`.
6264
* The constant expression variants of the following instructions have been

llvm/include/llvm/IR/Constants.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ template <class ConstantClass> struct ConstantAggrKeyType;
5151
/// Since they can be in use by unrelated modules (and are never based on
5252
/// GlobalValues), it never makes sense to RAUW them.
5353
///
54-
/// These do not have use lists. It is illegal to inspect the uses.
54+
/// These do not have use lists. It is illegal to inspect the uses. These behave
55+
/// as if they have no uses (i.e. use_empty() is always true).
5556
class ConstantData : public Constant {
5657
constexpr static IntrusiveOperandsAllocMarker AllocMarker{0};
5758

llvm/include/llvm/IR/Use.h

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
namespace llvm {
2424

2525
template <typename> struct simplify_type;
26-
class ConstantData;
2726
class User;
2827
class Value;
2928

@@ -43,7 +42,7 @@ class Use {
4342

4443
private:
4544
/// Destructor - Only for zap()
46-
~Use();
45+
~Use() { removeFromList(); }
4746

4847
/// Constructor
4948
Use(User *Parent) : Parent(Parent) {}
@@ -85,10 +84,25 @@ class Use {
8584
Use **Prev = nullptr;
8685
User *Parent = nullptr;
8786

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

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

llvm/include/llvm/IR/Value.h

Lines changed: 20 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,7 @@ class Value {
116116

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

124121
friend class ValueAsMetadata; // Allow access to IsUsedByMD.
125122
friend class ValueHandleBase; // Allow access to HasValueHandle.
@@ -347,23 +344,21 @@ class Value {
347344

348345
bool use_empty() const {
349346
assertModuleIsMaterialized();
350-
return hasUseList() ? Uses.List == nullptr : Uses.Count == 0;
347+
return UseList == nullptr;
351348
}
352349

353-
bool materialized_use_empty() const {
354-
return hasUseList() ? Uses.List == nullptr : !Uses.Count;
355-
}
350+
bool materialized_use_empty() const { return UseList == nullptr; }
356351

357352
using use_iterator = use_iterator_impl<Use>;
358353
using const_use_iterator = use_iterator_impl<const Use>;
359354

360355
use_iterator materialized_use_begin() {
361356
assert(hasUseList());
362-
return use_iterator(Uses.List);
357+
return use_iterator(UseList);
363358
}
364359
const_use_iterator materialized_use_begin() const {
365360
assert(hasUseList());
366-
return const_use_iterator(Uses.List);
361+
return const_use_iterator(UseList);
367362
}
368363
use_iterator use_begin() {
369364
assertModuleIsMaterialized();
@@ -397,11 +392,11 @@ class Value {
397392

398393
user_iterator materialized_user_begin() {
399394
assert(hasUseList());
400-
return user_iterator(Uses.List);
395+
return user_iterator(UseList);
401396
}
402397
const_user_iterator materialized_user_begin() const {
403398
assert(hasUseList());
404-
return const_user_iterator(Uses.List);
399+
return const_user_iterator(UseList);
405400
}
406401
user_iterator user_begin() {
407402
assertModuleIsMaterialized();
@@ -440,11 +435,7 @@ class Value {
440435
///
441436
/// This is specialized because it is a common request and does not require
442437
/// traversing the whole use list.
443-
bool hasOneUse() const {
444-
if (!hasUseList())
445-
return Uses.Count == 1;
446-
return hasSingleElement(uses());
447-
}
438+
bool hasOneUse() const { return UseList && hasSingleElement(uses()); }
448439

449440
/// Return true if this Value has exactly N uses.
450441
bool hasNUses(unsigned N) const;
@@ -518,17 +509,8 @@ class Value {
518509

519510
/// This method should only be used by the Use class.
520511
void addUse(Use &U) {
521-
if (hasUseList())
522-
U.addToList(Uses.List);
523-
else
524-
U.addToList(Uses.Count);
525-
}
526-
527-
void removeUse(Use &U) {
528-
if (hasUseList())
529-
U.removeFromList(Uses.List);
530-
else
531-
U.removeFromList(Uses.Count);
512+
if (UseList || hasUseList())
513+
U.addToList(&UseList);
532514
}
533515

534516
/// Concrete subclass of this.
@@ -870,8 +852,7 @@ class Value {
870852
///
871853
/// \return the first element in the list.
872854
///
873-
/// \note Completely ignores \a Use::PrevOrCount (doesn't read, doesn't
874-
/// update).
855+
/// \note Completely ignores \a Use::Prev (doesn't read, doesn't update).
875856
template <class Compare>
876857
static Use *mergeUseLists(Use *L, Use *R, Compare Cmp) {
877858
Use *Merged;
@@ -917,47 +898,8 @@ inline raw_ostream &operator<<(raw_ostream &OS, const Value &V) {
917898
return OS;
918899
}
919900

920-
inline Use::~Use() {
921-
if (Val)
922-
Val->removeUse(*this);
923-
}
924-
925-
void Use::addToList(unsigned &Count) {
926-
assert(isa<ConstantData>(Val) && "Only ConstantData is ref-counted");
927-
++Count;
928-
929-
// We don't have a uselist - clear the remnant if we are replacing a
930-
// non-constant value.
931-
Prev = nullptr;
932-
Next = nullptr;
933-
}
934-
935-
void Use::addToList(Use *&List) {
936-
assert(!isa<ConstantData>(Val) && "ConstantData has no use-list");
937-
938-
Next = List;
939-
if (Next)
940-
Next->Prev = &Next;
941-
Prev = &List;
942-
List = this;
943-
}
944-
945-
void Use::removeFromList(unsigned &Count) {
946-
assert(isa<ConstantData>(Val));
947-
assert(Count > 0 && "reference count underflow");
948-
assert(!Prev && !Next && "should not have uselist remnant");
949-
--Count;
950-
}
951-
952-
void Use::removeFromList(Use *&List) {
953-
*Prev = Next;
954-
if (Next)
955-
Next->Prev = Prev;
956-
}
957-
958901
void Use::set(Value *V) {
959-
if (Val)
960-
Val->removeUse(*this);
902+
removeFromList();
961903
Val = V;
962904
if (V)
963905
V->addUse(*this);
@@ -974,7 +916,7 @@ const Use &Use::operator=(const Use &RHS) {
974916
}
975917

976918
template <class Compare> void Value::sortUseList(Compare Cmp) {
977-
if (!hasUseList() || !Uses.List || !Uses.List->Next)
919+
if (!UseList || !UseList->Next)
978920
// No need to sort 0 or 1 uses.
979921
return;
980922

@@ -987,10 +929,10 @@ template <class Compare> void Value::sortUseList(Compare Cmp) {
987929
Use *Slots[MaxSlots];
988930

989931
// Collect the first use, turning it into a single-item list.
990-
Use *Next = Uses.List->Next;
991-
Uses.List->Next = nullptr;
932+
Use *Next = UseList->Next;
933+
UseList->Next = nullptr;
992934
unsigned NumSlots = 1;
993-
Slots[0] = Uses.List;
935+
Slots[0] = UseList;
994936

995937
// Collect all but the last use.
996938
while (Next->Next) {
@@ -1026,15 +968,15 @@ template <class Compare> void Value::sortUseList(Compare Cmp) {
1026968
// Merge all the lists together.
1027969
assert(Next && "Expected one more Use");
1028970
assert(!Next->Next && "Expected only one Use");
1029-
Uses.List = Next;
971+
UseList = Next;
1030972
for (unsigned I = 0; I < NumSlots; ++I)
1031973
if (Slots[I])
1032-
// Since the uses in Slots[I] originally preceded those in Uses.List, send
974+
// Since the uses in Slots[I] originally preceded those in UseList, send
1033975
// Slots[I] in as the left parameter to maintain a stable sort.
1034-
Uses.List = mergeUseLists(Slots[I], Uses.List, Cmp);
976+
UseList = mergeUseLists(Slots[I], UseList, Cmp);
1035977

1036978
// Fix the Prev pointers.
1037-
for (Use *I = Uses.List, **Prev = &Uses.List; I; I = I->Next) {
979+
for (Use *I = UseList, **Prev = &UseList; I; I = I->Next) {
1038980
I->Prev = Prev;
1039981
Prev = &I->Next;
1040982
}

llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp

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

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

llvm/lib/IR/AsmWriter.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,8 +279,7 @@ static UseListOrderMap predictUseListOrder(const Module *M) {
279279
UseListOrderMap ULOM;
280280
for (const auto &Pair : OM) {
281281
const Value *V = Pair.first;
282-
if (!V->hasUseList() || V->use_empty() ||
283-
std::next(V->use_begin()) == V->use_end())
282+
if (V->use_empty() || std::next(V->use_begin()) == V->use_end())
284283
continue;
285284

286285
std::vector<unsigned> Shuffle =

llvm/lib/IR/Instruction.cpp

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

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

381379
void Instruction::setHasNoUnsignedWrap(bool b) {

llvm/lib/IR/Value.cpp

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -148,14 +148,18 @@ void Value::destroyValueName() {
148148
}
149149

150150
bool Value::hasNUses(unsigned N) const {
151-
if (!hasUseList())
152-
return Uses.Count == N;
151+
if (!UseList)
152+
return N == 0;
153+
154+
// TODO: Disallow for ConstantData and remove !UseList check?
153155
return hasNItems(use_begin(), use_end(), N);
154156
}
155157

156158
bool Value::hasNUsesOrMore(unsigned N) const {
157-
if (!hasUseList())
158-
return Uses.Count >= N;
159+
// TODO: Disallow for ConstantData and remove !UseList check?
160+
if (!UseList)
161+
return N == 0;
162+
159163
return hasNItemsOrMore(use_begin(), use_end(), N);
160164
}
161165

@@ -259,9 +263,9 @@ bool Value::isUsedInBasicBlock(const BasicBlock *BB) const {
259263
}
260264

261265
unsigned Value::getNumUses() const {
262-
if (!hasUseList())
263-
return Uses.Count;
264-
266+
// TODO: Disallow for ConstantData and remove !UseList check?
267+
if (!UseList)
268+
return 0;
265269
return (unsigned)std::distance(use_begin(), use_end());
266270
}
267271

@@ -522,7 +526,7 @@ void Value::doRAUW(Value *New, ReplaceMetadataUses ReplaceMetaUses) {
522526
ValueAsMetadata::handleRAUW(this, New);
523527

524528
while (!materialized_use_empty()) {
525-
Use &U = *Uses.List;
529+
Use &U = *UseList;
526530
// Must handle Constants specially, we cannot call replaceUsesOfWith on a
527531
// constant because they are uniqued.
528532
if (auto *C = dyn_cast<Constant>(U.getUser())) {
@@ -1102,12 +1106,12 @@ const Value *Value::DoPHITranslation(const BasicBlock *CurBB,
11021106
LLVMContext &Value::getContext() const { return VTy->getContext(); }
11031107

11041108
void Value::reverseUseList() {
1105-
if (!Uses.List || !Uses.List->Next || !hasUseList())
1109+
if (!UseList || !UseList->Next)
11061110
// No need to reverse 0 or 1 uses.
11071111
return;
11081112

1109-
Use *Head = Uses.List;
1110-
Use *Current = Uses.List->Next;
1113+
Use *Head = UseList;
1114+
Use *Current = UseList->Next;
11111115
Head->Next = nullptr;
11121116
while (Current) {
11131117
Use *Next = Current->Next;
@@ -1116,8 +1120,8 @@ void Value::reverseUseList() {
11161120
Head = Current;
11171121
Current = Next;
11181122
}
1119-
Uses.List = Head;
1120-
Head->Prev = &Uses.List;
1123+
UseList = Head;
1124+
Head->Prev = &UseList;
11211125
}
11221126

11231127
bool Value::isSwiftError() const {

0 commit comments

Comments
 (0)