Skip to content

Commit ee561ee

Browse files
committed
IR: Remove reference counts from ConstantData
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 622d957 commit ee561ee

File tree

9 files changed

+100
-107
lines changed

9 files changed

+100
-107
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: 3 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,8 @@ 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+
inline void addToList(Use **List);
88+
inline void removeFromList();
9289
};
9390

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

llvm/include/llvm/IR/Value.h

Lines changed: 40 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);
@@ -973,8 +915,28 @@ const Use &Use::operator=(const Use &RHS) {
973915
return *this;
974916
}
975917

918+
void Use::addToList(Use **List) {
919+
Next = *List;
920+
if (Next)
921+
Next->Prev = &Next;
922+
Prev = List;
923+
*Prev = this;
924+
}
925+
926+
void Use::removeFromList() {
927+
if (Prev) {
928+
*Prev = Next;
929+
if (Next) {
930+
Next->Prev = Prev;
931+
Next = nullptr;
932+
}
933+
934+
Prev = nullptr;
935+
}
936+
}
937+
976938
template <class Compare> void Value::sortUseList(Compare Cmp) {
977-
if (!hasUseList() || !Uses.List || !Uses.List->Next)
939+
if (!UseList || !UseList->Next)
978940
// No need to sort 0 or 1 uses.
979941
return;
980942

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

989951
// Collect the first use, turning it into a single-item list.
990-
Use *Next = Uses.List->Next;
991-
Uses.List->Next = nullptr;
952+
Use *Next = UseList->Next;
953+
UseList->Next = nullptr;
992954
unsigned NumSlots = 1;
993-
Slots[0] = Uses.List;
955+
Slots[0] = UseList;
994956

995957
// Collect all but the last use.
996958
while (Next->Next) {
@@ -1026,15 +988,15 @@ template <class Compare> void Value::sortUseList(Compare Cmp) {
1026988
// Merge all the lists together.
1027989
assert(Next && "Expected one more Use");
1028990
assert(!Next->Next && "Expected only one Use");
1029-
Uses.List = Next;
991+
UseList = Next;
1030992
for (unsigned I = 0; I < NumSlots; ++I)
1031993
if (Slots[I])
1032-
// Since the uses in Slots[I] originally preceded those in Uses.List, send
994+
// Since the uses in Slots[I] originally preceded those in UseList, send
1033995
// Slots[I] in as the left parameter to maintain a stable sort.
1034-
Uses.List = mergeUseLists(Slots[I], Uses.List, Cmp);
996+
UseList = mergeUseLists(Slots[I], UseList, Cmp);
1035997

1036998
// Fix the Prev pointers.
1037-
for (Use *I = Uses.List, **Prev = &Uses.List; I; I = I->Next) {
999+
for (Use *I = UseList, **Prev = &UseList; I; I = I->Next) {
10381000
I->Prev = Prev;
10391001
Prev = &I->Next;
10401002
}

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: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -148,15 +148,13 @@ void Value::destroyValueName() {
148148
}
149149

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

156155
bool Value::hasNUsesOrMore(unsigned N) const {
157-
if (!hasUseList())
158-
return Uses.Count >= N;
159-
return hasNItemsOrMore(use_begin(), use_end(), N);
156+
// TODO: Disallow for ConstantData and remove !UseList check?
157+
return UseList && hasNItemsOrMore(use_begin(), use_end(), N);
160158
}
161159

162160
bool Value::hasOneUser() const {
@@ -259,9 +257,9 @@ bool Value::isUsedInBasicBlock(const BasicBlock *BB) const {
259257
}
260258

261259
unsigned Value::getNumUses() const {
262-
if (!hasUseList())
263-
return Uses.Count;
264-
260+
// TODO: Disallow for ConstantData and remove !UseList check?
261+
if (!UseList)
262+
return 0;
265263
return (unsigned)std::distance(use_begin(), use_end());
266264
}
267265

@@ -522,7 +520,7 @@ void Value::doRAUW(Value *New, ReplaceMetadataUses ReplaceMetaUses) {
522520
ValueAsMetadata::handleRAUW(this, New);
523521

524522
while (!materialized_use_empty()) {
525-
Use &U = *Uses.List;
523+
Use &U = *UseList;
526524
// Must handle Constants specially, we cannot call replaceUsesOfWith on a
527525
// constant because they are uniqued.
528526
if (auto *C = dyn_cast<Constant>(U.getUser())) {
@@ -1102,12 +1100,12 @@ const Value *Value::DoPHITranslation(const BasicBlock *CurBB,
11021100
LLVMContext &Value::getContext() const { return VTy->getContext(); }
11031101

11041102
void Value::reverseUseList() {
1105-
if (!Uses.List || !Uses.List->Next || !hasUseList())
1103+
if (!UseList || !UseList->Next)
11061104
// No need to reverse 0 or 1 uses.
11071105
return;
11081106

1109-
Use *Head = Uses.List;
1110-
Use *Current = Uses.List->Next;
1107+
Use *Head = UseList;
1108+
Use *Current = UseList->Next;
11111109
Head->Next = nullptr;
11121110
while (Current) {
11131111
Use *Next = Current->Next;
@@ -1116,8 +1114,8 @@ void Value::reverseUseList() {
11161114
Head = Current;
11171115
Current = Next;
11181116
}
1119-
Uses.List = Head;
1120-
Head->Prev = &Uses.List;
1117+
UseList = Head;
1118+
Head->Prev = &UseList;
11211119
}
11221120

11231121
bool Value::isSwiftError() const {

0 commit comments

Comments
 (0)