Skip to content

Commit a630ea3

Browse files
author
Wolfgang Pieb
committed
Reland [Metadata] Add a resize capability to MDNodes and add a push_back interface to MDNode
Fixed a bug with double destruction of operands and corrected a test issue. Note that this patch leads to a slight increase in compile time (I measured about .3%) and a slight increase in memory usage. The increased memory usage should be offset once resizing is used to a larger extent. Reviewed By: dexonsmith Differential Revision: https://reviews.llvm.org/D125998
1 parent 14d3021 commit a630ea3

File tree

3 files changed

+301
-25
lines changed

3 files changed

+301
-25
lines changed

llvm/include/llvm/IR/Metadata.h

Lines changed: 97 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -934,38 +934,101 @@ struct TempMDNodeDeleter {
934934
/// If an unresolved node is part of a cycle, \a resolveCycles() needs
935935
/// to be called on some member of the cycle once all temporary nodes have been
936936
/// replaced.
937+
///
938+
/// MDNodes can be large or small, as well as resizable or non-resizable.
939+
/// Large MDNodes' operands are allocated in a separate storage vector,
940+
/// whereas small MDNodes' operands are co-allocated. Distinct and temporary
941+
/// MDnodes are resizable, but only MDTuples support this capability.
942+
///
943+
/// Clients can add operands to resizable MDNodes using push_back().
937944
class MDNode : public Metadata {
938945
friend class ReplaceableMetadataImpl;
939946
friend class LLVMContextImpl;
940947
friend class DIArgList;
941948

942-
/// The header that is coallocated with an MDNode, along with the operands.
943-
/// It is located immediately before the main body of the node. The operands
944-
/// are in turn located immediately before the header.
949+
/// The header that is coallocated with an MDNode along with its "small"
950+
/// operands. It is located immediately before the main body of the node.
951+
/// The operands are in turn located immediately before the header.
952+
/// For resizable MDNodes, the space for the storage vector is also allocated
953+
/// immediately before the header, overlapping with the operands.
945954
struct Header {
946-
unsigned NumOperands;
955+
bool IsResizable : 1;
956+
bool IsLarge : 1;
957+
size_t SmallSize : 4;
958+
size_t SmallNumOps : 4;
959+
size_t : sizeof(size_t) * CHAR_BIT - 10;
960+
947961
unsigned NumUnresolved = 0;
962+
using LargeStorageVector = SmallVector<MDOperand, 0>;
963+
964+
static constexpr size_t NumOpsFitInVector =
965+
sizeof(LargeStorageVector) / sizeof(MDOperand);
966+
static_assert(
967+
NumOpsFitInVector * sizeof(MDOperand) == sizeof(LargeStorageVector),
968+
"sizeof(LargeStorageVector) must be a multiple of sizeof(MDOperand)");
969+
970+
static constexpr size_t MaxSmallSize = 15;
948971

949972
static constexpr size_t getOpSize(unsigned NumOps) {
950973
return sizeof(MDOperand) * NumOps;
951974
}
952-
static constexpr size_t getAllocSize(unsigned NumOps) {
953-
return getOpSize(NumOps) + sizeof(Header);
975+
/// Returns the number of operands the node has space for based on its
976+
/// allocation characteristics.
977+
static size_t getSmallSize(size_t NumOps, bool IsResizable, bool IsLarge) {
978+
return IsLarge ? NumOpsFitInVector
979+
: std::max(NumOps, NumOpsFitInVector * IsResizable);
980+
}
981+
/// Returns the number of bytes allocated for operands and header.
982+
static size_t getAllocSize(StorageType Storage, size_t NumOps) {
983+
return getOpSize(
984+
getSmallSize(NumOps, isResizable(Storage), isLarge(NumOps))) +
985+
sizeof(Header);
986+
}
987+
988+
/// Only temporary and distinct nodes are resizable.
989+
static bool isResizable(StorageType Storage) { return Storage != Uniqued; }
990+
static bool isLarge(size_t NumOps) { return NumOps > MaxSmallSize; }
991+
992+
size_t getAllocSize() const {
993+
return getOpSize(SmallSize) + sizeof(Header);
954994
}
955995
void *getAllocation() {
956996
return reinterpret_cast<char *>(this + 1) -
957-
alignTo(getAllocSize(NumOperands), alignof(uint64_t));
997+
alignTo(getAllocSize(), alignof(uint64_t));
998+
}
999+
1000+
void *getLargePtr() const;
1001+
void *getSmallPtr();
1002+
1003+
LargeStorageVector &getLarge() {
1004+
assert(IsLarge);
1005+
return *reinterpret_cast<LargeStorageVector *>(getLargePtr());
9581006
}
9591007

960-
explicit Header(unsigned NumOperands);
1008+
const LargeStorageVector &getLarge() const {
1009+
assert(IsLarge);
1010+
return *reinterpret_cast<const LargeStorageVector *>(getLargePtr());
1011+
}
1012+
1013+
void resizeSmall(size_t NumOps);
1014+
void resizeSmallToLarge(size_t NumOps);
1015+
void resize(size_t NumOps);
1016+
1017+
explicit Header(size_t NumOps, StorageType Storage);
9611018
~Header();
1019+
9621020
MutableArrayRef<MDOperand> operands() {
1021+
if (IsLarge)
1022+
return getLarge();
9631023
return makeMutableArrayRef(
964-
reinterpret_cast<MDOperand *>(this) - NumOperands, NumOperands);
1024+
reinterpret_cast<MDOperand *>(this) - SmallSize, SmallNumOps);
9651025
}
1026+
9661027
ArrayRef<MDOperand> operands() const {
967-
return makeArrayRef(
968-
reinterpret_cast<const MDOperand *>(this) - NumOperands, NumOperands);
1028+
if (IsLarge)
1029+
return getLarge();
1030+
return makeArrayRef(reinterpret_cast<const MDOperand *>(this) - SmallSize,
1031+
SmallNumOps);
9691032
}
9701033
};
9711034

@@ -982,7 +1045,7 @@ class MDNode : public Metadata {
9821045
ArrayRef<Metadata *> Ops1, ArrayRef<Metadata *> Ops2 = None);
9831046
~MDNode() = default;
9841047

985-
void *operator new(size_t Size, unsigned NumOps, StorageType Storage);
1048+
void *operator new(size_t Size, size_t NumOps, StorageType Storage);
9861049
void operator delete(void *Mem);
9871050

9881051
/// Required by std, but never called.
@@ -1146,6 +1209,17 @@ class MDNode : public Metadata {
11461209
static T *storeImpl(T *N, StorageType Storage, StoreT &Store);
11471210
template <class T> static T *storeImpl(T *N, StorageType Storage);
11481211

1212+
/// Resize the node to hold \a NumOps operands.
1213+
///
1214+
/// \pre \a isTemporary() or \a isDistinct()
1215+
/// \pre MetadataID == MDTupleKind
1216+
void resize(size_t NumOps) {
1217+
assert(!isUniqued() && "Resizing is not supported for uniqued nodes");
1218+
assert(getMetadataID() == MDTupleKind &&
1219+
"Resizing is not supported for this node kind");
1220+
getHeader().resize(NumOps);
1221+
}
1222+
11491223
private:
11501224
void handleChangedOperand(void *Ref, Metadata *New);
11511225

@@ -1207,7 +1281,7 @@ class MDNode : public Metadata {
12071281
}
12081282

12091283
/// Return number of MDNode operands.
1210-
unsigned getNumOperands() const { return getHeader().NumOperands; }
1284+
unsigned getNumOperands() const { return getHeader().operands().size(); }
12111285

12121286
/// Methods for support type inquiry through isa, cast, and dyn_cast:
12131287
static bool classof(const Metadata *MD) {
@@ -1292,6 +1366,16 @@ class MDTuple : public MDNode {
12921366
/// Return a (temporary) clone of this.
12931367
TempMDTuple clone() const { return cloneImpl(); }
12941368

1369+
/// Append an element to the tuple. This will resize the node.
1370+
void push_back(Metadata *MD) {
1371+
size_t NumOps = getNumOperands();
1372+
resize(NumOps + 1);
1373+
setOperand(NumOps, MD);
1374+
}
1375+
1376+
/// Shrink the operands by 1.
1377+
void pop_back() { resize(getNumOperands() - 1); }
1378+
12951379
static bool classof(const Metadata *MD) {
12961380
return MD->getMetadataID() == MDTupleKind;
12971381
}

llvm/lib/IR/Metadata.cpp

Lines changed: 80 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -521,13 +521,13 @@ StringRef MDString::getString() const {
521521
"Alignment is insufficient after objects prepended to " #CLASS);
522522
#include "llvm/IR/Metadata.def"
523523

524-
void *MDNode::operator new(size_t Size, unsigned NumOps,
525-
StorageType /* Storage */) {
524+
void *MDNode::operator new(size_t Size, size_t NumOps, StorageType Storage) {
526525
// uint64_t is the most aligned type we need support (ensured by static_assert
527526
// above)
528-
size_t AllocSize = alignTo(Header::getAllocSize(NumOps), alignof(uint64_t));
527+
size_t AllocSize =
528+
alignTo(Header::getAllocSize(Storage, NumOps), alignof(uint64_t));
529529
char *Mem = reinterpret_cast<char *>(::operator new(AllocSize + Size));
530-
Header *H = new (Mem + AllocSize - sizeof(Header)) Header(NumOps);
530+
Header *H = new (Mem + AllocSize - sizeof(Header)) Header(NumOps, Storage);
531531
return reinterpret_cast<void *>(H + 1);
532532
}
533533

@@ -566,17 +566,85 @@ TempMDNode MDNode::clone() const {
566566
}
567567
}
568568

569-
MDNode::Header::Header(unsigned NumOps) {
570-
NumOperands = NumOps;
571-
MDOperand *O = reinterpret_cast<MDOperand *>(this);
572-
for (MDOperand *E = O - NumOps; O != E; --O)
573-
(void)new (O - 1) MDOperand();
569+
MDNode::Header::Header(size_t NumOps, StorageType Storage) {
570+
IsLarge = isLarge(NumOps);
571+
IsResizable = isResizable(Storage);
572+
SmallSize = getSmallSize(NumOps, IsResizable, IsLarge);
573+
if (IsLarge) {
574+
SmallNumOps = 0;
575+
new (getLargePtr()) LargeStorageVector();
576+
getLarge().resize(NumOps);
577+
return;
578+
}
579+
SmallNumOps = NumOps;
580+
MDOperand *O = reinterpret_cast<MDOperand *>(this) - SmallSize;
581+
for (MDOperand *E = O + SmallSize; O != E;)
582+
(void)new (O++) MDOperand();
574583
}
575584

576585
MDNode::Header::~Header() {
577-
MDOperand *O = reinterpret_cast<MDOperand *>(this) - NumOperands;
578-
for (MDOperand *E = O + NumOperands; O != E; ++O)
579-
(void)O->~MDOperand();
586+
if (IsLarge) {
587+
getLarge().~LargeStorageVector();
588+
return;
589+
}
590+
MDOperand *O = reinterpret_cast<MDOperand *>(this);
591+
for (MDOperand *E = O - SmallSize; O != E; --O)
592+
(void)(O - 1)->~MDOperand();
593+
}
594+
595+
void *MDNode::Header::getLargePtr() const {
596+
static_assert(alignof(LargeStorageVector) <= alignof(Header),
597+
"LargeStorageVector too strongly aligned");
598+
return reinterpret_cast<char *>(const_cast<Header *>(this)) -
599+
sizeof(LargeStorageVector);
600+
}
601+
602+
void *MDNode::Header::getSmallPtr() {
603+
static_assert(alignof(MDOperand) <= alignof(Header),
604+
"MDOperand too strongly aligned");
605+
return reinterpret_cast<char *>(const_cast<Header *>(this)) -
606+
sizeof(MDOperand) * SmallSize;
607+
}
608+
609+
void MDNode::Header::resize(size_t NumOps) {
610+
assert(IsResizable && "Node is not resizable");
611+
if (operands().size() == NumOps)
612+
return;
613+
614+
if (IsLarge)
615+
getLarge().resize(NumOps);
616+
else if (NumOps <= SmallSize)
617+
resizeSmall(NumOps);
618+
else
619+
resizeSmallToLarge(NumOps);
620+
}
621+
622+
void MDNode::Header::resizeSmall(size_t NumOps) {
623+
assert(!IsLarge && "Expected a small MDNode");
624+
assert(NumOps <= SmallSize && "NumOps too large for small resize");
625+
626+
MutableArrayRef<MDOperand> ExistingOps = operands();
627+
assert(NumOps != ExistingOps.size() && "Expected a different size");
628+
629+
int NumNew = (int)NumOps - (int)ExistingOps.size();
630+
MDOperand *O = ExistingOps.end();
631+
for (int I = 0, E = NumNew; I < E; ++I)
632+
(O++)->reset();
633+
for (int I = 0, E = NumNew; I > E; --I)
634+
(--O)->reset();
635+
SmallNumOps = NumOps;
636+
assert(O == operands().end() && "Operands not (un)initialized until the end");
637+
}
638+
639+
void MDNode::Header::resizeSmallToLarge(size_t NumOps) {
640+
assert(!IsLarge && "Expected a small MDNode");
641+
assert(NumOps > SmallSize && "Expected NumOps to be larger than allocation");
642+
LargeStorageVector NewOps;
643+
NewOps.resize(NumOps);
644+
llvm::move(operands(), NewOps.begin());
645+
resizeSmall(0);
646+
new (getLargePtr()) LargeStorageVector(std::move(NewOps));
647+
IsLarge = true;
580648
}
581649

582650
static bool isOperandUnresolved(Metadata *Op) {

0 commit comments

Comments
 (0)