66 changes: 59 additions & 7 deletions llvm/lib/Bitcode/Writer/ValueEnumerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,42 @@ class ValueEnumerator {
ComdatSetType Comdats;

std::vector<const Metadata *> MDs;
typedef DenseMap<const Metadata *, unsigned> MetadataMapType;
std::vector<const Metadata *> FunctionMDs;

/// Index of information about a piece of metadata.
struct MDIndex {
unsigned F = 0; ///< The ID of the function for this metadata, if any.
unsigned ID = 0; ///< The implicit ID of this metadata in bitcode.

MDIndex() = default;
explicit MDIndex(unsigned F) : F(F) {}

/// Check if this has a function tag, and it's different from NewF.
bool hasDifferentFunction(unsigned NewF) const { return F && F != NewF; }

/// Fetch the MD this references out of the given metadata array.
const Metadata *get(ArrayRef<const Metadata *> MDs) const {
assert(ID && "Expected non-zero ID");
assert(ID <= MDs.size() && "Expected valid ID");
return MDs[ID - 1];
}
};
typedef DenseMap<const Metadata *, MDIndex> MetadataMapType;
MetadataMapType MetadataMap;

/// Range of metadata IDs, as a half-open range.
struct MDRange {
unsigned First = 0;
unsigned Last = 0;

/// Number of strings in the prefix of the metadata range.
unsigned NumStrings = 0;

MDRange() = default;
explicit MDRange(unsigned First) : First(First) {}
};
SmallDenseMap<unsigned, MDRange, 1> FunctionMDInfo;

bool ShouldPreserveUseListOrder;

typedef DenseMap<AttributeSet, unsigned> AttributeGroupMapType;
Expand Down Expand Up @@ -116,7 +150,7 @@ class ValueEnumerator {
return ID - 1;
}
unsigned getMetadataOrNullID(const Metadata *MD) const {
return MetadataMap.lookup(MD);
return MetadataMap.lookup(MD).ID;
}
unsigned numMDs() const { return MDs.size(); }

Expand Down Expand Up @@ -196,13 +230,31 @@ class ValueEnumerator {
private:
void OptimizeConstants(unsigned CstStart, unsigned CstEnd);

// Reorder the reachable metadata. This is not just an optimization, but is
// mandatory for emitting MDString correctly.
/// Reorder the reachable metadata.
///
/// This is not just an optimization, but is mandatory for emitting MDString
/// correctly.
void organizeMetadata();

void EnumerateMDNodeOperands(const MDNode *N);
void EnumerateMetadata(const Metadata *MD);
void EnumerateFunctionLocalMetadata(const LocalAsMetadata *Local);
/// Drop the function tag from the transitive operands of the given node.
void dropFunctionFromOps(const MDNode &N);

/// Incorporate the function metadata.
///
/// This should be called before enumerating LocalAsMetadata for the
/// function.
void incorporateFunctionMetadata(const Function &F);

bool insertMetadata(unsigned F, const Metadata *MD);

unsigned getMetadataFunctionID(const Function *F) const;
void EnumerateMDNodeOperands(const Function *F, const MDNode *N);
void EnumerateMDNodeOperands(unsigned F, const MDNode *N);
void EnumerateMetadata(const Function *F, const Metadata *MD);
void EnumerateMetadata(unsigned F, const Metadata *MD);
void EnumerateFunctionLocalMetadata(const Function &F,
const LocalAsMetadata *Local);
void EnumerateFunctionLocalMetadata(unsigned F, const LocalAsMetadata *Local);
void EnumerateNamedMDNode(const NamedMDNode *NMD);
void EnumerateValue(const Value *V);
void EnumerateType(Type *T);
Expand Down
75 changes: 75 additions & 0 deletions llvm/test/Bitcode/metadata-function-blocks.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
; RUN: llvm-as < %s | llvm-bcanalyzer -dump | FileCheck %s
; Test that metadata only used by a single function is serialized in that
; function instead of in the global pool.
;
; In order to make the bitcode records easy to follow, nodes in this testcase
; are named after the ids they are given in the bitcode. Nodes local to a
; function have offsets of 100 or 200 (depending on the function) so that they
; remain unique within this textual IR.

; Check for strings in the global pool.
; CHECK: <METADATA_BLOCK
; CHECK-NEXT: <STRINGS
; CHECK-SAME: /> num-strings = 3 {
; CHECK-NEXT: 'named'
; CHECK-NEXT: 'named and foo'
; CHECK-NEXT: 'foo and bar'
; CHECK-NEXT: }

; Each node gets a new number. Bottom-up traversal of nodes.
!named = !{!6}

; CHECK-NEXT: <NODE op0=1/>
!4 = !{!"named"}

; CHECK-NEXT: <NODE op0=2/>
!5 = !{!"named and foo"}

; CHECK-NEXT: <NODE op0=1 op1=4 op2=5/>
!6 = !{!"named", !4, !5}

; CHECK-NEXT: <NODE op0=3/>
!7 = !{!"foo and bar"}

; CHECK-NOT: <NODE
; CHECK: </METADATA_BLOCK

; Look at metadata local to @foo, starting with strings.
; CHECK: <FUNCTION_BLOCK
; CHECK: <METADATA_BLOCK
; CHECK-NEXT: <STRINGS
; CHECK-SAME: /> num-strings = 1 {
; CHECK-NEXT: 'foo'
; CHECK-NEXT: }

; Function-local nodes start at 9 (strings at 8).
; CHECK-NEXT: <NODE op0=8/>
!109 = !{!"foo"}

; CHECK-NEXT: <NODE op0=8 op1=3 op2=9 op3=7 op4=5/>
!110 = !{!"foo", !"foo and bar", !109, !7, !5}

; CHECK-NEXT: </METADATA_BLOCK
define void @foo() !foo !110 {
unreachable
}

; Look at metadata local to @bar, starting with strings.
; CHECK: <FUNCTION_BLOCK
; CHECK: <METADATA_BLOCK
; CHECK-NEXT: <STRINGS
; CHECK-SAME: /> num-strings = 1 {
; CHECK-NEXT: 'bar'
; CHECK-NEXT: }

; Function-local nodes start at 9 (strings at 8).
; CHECK-NEXT: <NODE op0=8/>
!209 = !{!"bar"}

; CHECK-NEXT: <NODE op0=8 op1=3 op2=9 op3=7/>
!210 = !{!"bar", !"foo and bar", !209, !7}

; CHECK-NEXT: </METADATA_BLOCK
define void @bar() {
unreachable, !bar !210
}