[DebugInfo] Add DIArgList MD to store multple values in DbgVariableIntrinsics

This patch adds a new metadata node, DIArgList, which contains a list of SSA
values. This node is in many ways similar in function to the existing
ValueAsMetadata node, with the difference being that it tracks a list instead of
a single value. Internally, it uses ValueAsMetadata to track the individual
values, but there is also a reasonable amount of DIArgList-specific
value-tracking logic on top of that. Similar to ValueAsMetadata, it is a special
case in parsing and printing due to the fact that it requires a function state
(as it may reference function-local values).

This patch should not result in any immediate functional change; it allows for
DIArgLists to be parsed and printed, but debug variable intrinsics do not yet
recognize them as a valid argument (outside of parsing).

Differential Revision: https://reviews.llvm.org/D88175
This commit is contained in:
gbtozers 2020-09-30 16:29:53 +01:00 committed by Stephen Tozer
parent adc35b689f
commit 65600cb2a7
20 changed files with 371 additions and 15 deletions

View File

@ -5407,6 +5407,22 @@ valid debug intrinsic.
!4 = !DIExpression(DW_OP_constu, 2, DW_OP_swap, DW_OP_xderef)
!5 = !DIExpression(DW_OP_constu, 42, DW_OP_stack_value)
DIArgList
""""""""""""
``DIArgList`` nodes hold a list of constant or SSA value references. These are
used in :ref:`debug intrinsics<dbg_intrinsics>` (currently only in
``llvm.dbg.value``) in combination with a ``DIExpression`` that uses the
``DW_OP_LLVM_arg`` operator. Because a DIArgList may refer to local values
within a function, it must only be used as a function argument, must always be
inlined, and cannot appear in named metadata.
.. code-block:: text
llvm.dbg.value(metadata !DIArgList(i32 %a, i32 %b),
metadata !16,
metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus))
DIFlags
"""""""""""""""

View File

@ -161,7 +161,8 @@ enum {
LLVMDIMacroFileMetadataKind,
LLVMDICommonBlockMetadataKind,
LLVMDIStringTypeMetadataKind,
LLVMDIGenericSubrangeMetadataKind
LLVMDIGenericSubrangeMetadataKind,
LLVMDIArgListMetadataKind
};
typedef unsigned LLVMMetadataKind;

View File

@ -342,8 +342,9 @@ enum MetadataCodes {
METADATA_STRING_TYPE = 41, // [distinct, name, size, align,...]
// Codes 42 and 43 are reserved for support for Fortran array specific debug
// info.
METADATA_COMMON_BLOCK = 44, // [distinct, scope, name, variable,...]
METADATA_GENERIC_SUBRANGE = 45 // [distinct, count, lo, up, stride]
METADATA_COMMON_BLOCK = 44, // [distinct, scope, name, variable,...]
METADATA_GENERIC_SUBRANGE = 45, // [distinct, count, lo, up, stride]
METADATA_ARG_LIST = 46 // [n x [type num, value num]]
};
// The constants block (CONSTANTS_BLOCK_ID) describes emission for each

View File

@ -3510,6 +3510,52 @@ public:
}
};
/// List of ValueAsMetadata, to be used as an argument to a dbg.value
/// intrinsic.
class DIArgList : public MDNode {
friend class LLVMContextImpl;
friend class MDNode;
using iterator = SmallVectorImpl<ValueAsMetadata *>::iterator;
SmallVector<ValueAsMetadata *, 4> Args;
DIArgList(LLVMContext &C, StorageType Storage,
ArrayRef<ValueAsMetadata *> Args)
: MDNode(C, DIArgListKind, Storage, None),
Args(Args.begin(), Args.end()) {
track();
}
~DIArgList() { untrack(); }
static DIArgList *getImpl(LLVMContext &Context,
ArrayRef<ValueAsMetadata *> Args,
StorageType Storage, bool ShouldCreate = true);
TempDIArgList cloneImpl() const {
return getTemporary(getContext(), getArgs());
}
void track();
void untrack();
void dropAllReferences();
public:
DEFINE_MDNODE_GET(DIArgList, (ArrayRef<ValueAsMetadata *> Args), (Args))
TempDIArgList clone() const { return cloneImpl(); }
ArrayRef<ValueAsMetadata *> getArgs() const { return Args; }
iterator args_begin() { return Args.begin(); }
iterator args_end() { return Args.end(); }
static bool classof(const Metadata *MD) {
return MD->getMetadataID() == DIArgListKind;
}
void handleChangedOperand(void *Ref, Metadata *New);
};
/// Identifies a unique instance of a variable.
///
/// Storage for identifying a potentially inlined instance of a variable,

View File

@ -114,6 +114,7 @@ HANDLE_SPECIALIZED_MDNODE_BRANCH(DIMacroNode)
HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DIMacro)
HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DIMacroFile)
HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DICommonBlock)
HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DIArgList)
HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DIStringType)
HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DIGenericSubrange)

View File

@ -299,6 +299,9 @@ public:
/// Replace all uses of this with \c MD, which is allowed to be null.
void replaceAllUsesWith(Metadata *MD);
/// Returns the list of all DIArgList users of this.
SmallVector<Metadata *, 4> getAllArgListUsers();
/// Resolve all uses of this.
///
/// Resolve all uses of this, turning off RAUW permanently. If \c
@ -378,6 +381,10 @@ public:
Type *getType() const { return V->getType(); }
LLVMContext &getContext() const { return V->getContext(); }
SmallVector<Metadata *, 4> getAllArgListUsers() {
return ReplaceableMetadataImpl::getAllArgListUsers();
}
static void handleDeletion(Value *V);
static void handleRAUW(Value *From, Value *To);

View File

@ -776,6 +776,11 @@ bool LLParser::parseNamedMetadata() {
Lex.getStrVal() == "DIExpression") {
if (parseDIExpression(N, /*IsDistinct=*/false))
return true;
// DIArgLists should only appear inline in a function, as they may
// contain LocalAsMetadata arguments which require a function context.
} else if (Lex.getKind() == lltok::MetadataVar &&
Lex.getStrVal() == "DIArgList") {
return tokError("found DIArgList outside of function");
} else if (parseToken(lltok::exclaim, "Expected '!' here") ||
parseMDNodeID(N)) {
return true;
@ -5297,6 +5302,36 @@ bool LLParser::parseDIExpression(MDNode *&Result, bool IsDistinct) {
return false;
}
bool LLParser::parseDIArgList(MDNode *&Result, bool IsDistinct) {
return parseDIArgList(Result, IsDistinct, nullptr);
}
/// ParseDIArgList:
/// ::= !DIArgList(i32 7, i64 %0)
bool LLParser::parseDIArgList(MDNode *&Result, bool IsDistinct,
PerFunctionState *PFS) {
assert(PFS && "Expected valid function state");
assert(Lex.getKind() == lltok::MetadataVar && "Expected metadata type name");
Lex.Lex();
if (parseToken(lltok::lparen, "expected '(' here"))
return true;
SmallVector<ValueAsMetadata *, 4> Args;
if (Lex.getKind() != lltok::rparen)
do {
Metadata *MD;
if (parseValueAsMetadata(MD, "expected value-as-metadata operand", PFS))
return true;
Args.push_back(dyn_cast<ValueAsMetadata>(MD));
} while (EatIfPresent(lltok::comma));
if (parseToken(lltok::rparen, "expected ')' here"))
return true;
Result = GET_OR_DISTINCT(DIArgList, (Context, Args));
return false;
}
/// parseDIGlobalVariableExpression:
/// ::= !DIGlobalVariableExpression(var: !0, expr: !1)
bool LLParser::parseDIGlobalVariableExpression(MDNode *&Result,
@ -5407,8 +5442,14 @@ bool LLParser::parseValueAsMetadata(Metadata *&MD, const Twine &TypeMsg,
bool LLParser::parseMetadata(Metadata *&MD, PerFunctionState *PFS) {
if (Lex.getKind() == lltok::MetadataVar) {
MDNode *N;
if (parseSpecializedMDNode(N))
// DIArgLists are a special case, as they are a list of ValueAsMetadata and
// so parsing this requires a Function State.
if (Lex.getStrVal() == "DIArgList") {
if (parseDIArgList(N, false, PFS))
return true;
} else if (parseSpecializedMDNode(N)) {
return true;
}
MD = N;
return false;
}

View File

@ -536,6 +536,8 @@ namespace llvm {
#define HANDLE_SPECIALIZED_MDNODE_LEAF(CLASS) \
bool parse##CLASS(MDNode *&Result, bool IsDistinct);
#include "llvm/IR/Metadata.def"
bool parseDIArgList(MDNode *&Result, bool IsDistinct,
PerFunctionState *PFS);
// Function Parsing.
struct ArgInfo {

View File

@ -362,6 +362,7 @@ static Optional<const char *> GetCodeName(unsigned CodeID, unsigned BlockID,
STRINGIFY_CODE(METADATA, GLOBAL_VAR_EXPR)
STRINGIFY_CODE(METADATA, INDEX_OFFSET)
STRINGIFY_CODE(METADATA, INDEX)
STRINGIFY_CODE(METADATA, ARG_LIST)
}
case bitc::METADATA_KIND_BLOCK_ID:
switch (CodeID) {

View File

@ -2076,6 +2076,16 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata(
return Err;
break;
}
case bitc::METADATA_ARG_LIST: {
SmallVector<ValueAsMetadata *, 4> Elts;
Elts.reserve(Record.size());
for (uint64_t Elt : Record)
Elts.push_back(dyn_cast_or_null<ValueAsMetadata>(getMDOrNull(Elt)));
MetadataList.assignValue(DIArgList::get(Context, Elts), NextMetadataNo);
NextMetadataNo++;
break;
}
}
return Error::success();
#undef GET_OR_DISTINCT

View File

@ -335,6 +335,8 @@ private:
unsigned Abbrev);
void writeDIMacroFile(const DIMacroFile *N, SmallVectorImpl<uint64_t> &Record,
unsigned Abbrev);
void writeDIArgList(const DIArgList *N, SmallVectorImpl<uint64_t> &Record,
unsigned Abbrev);
void writeDIModule(const DIModule *N, SmallVectorImpl<uint64_t> &Record,
unsigned Abbrev);
void writeDITemplateTypeParameter(const DITemplateTypeParameter *N,
@ -1867,6 +1869,17 @@ void ModuleBitcodeWriter::writeDIMacroFile(const DIMacroFile *N,
Record.clear();
}
void ModuleBitcodeWriter::writeDIArgList(const DIArgList *N,
SmallVectorImpl<uint64_t> &Record,
unsigned Abbrev) {
Record.reserve(N->getArgs().size());
for (ValueAsMetadata *MD : N->getArgs())
Record.push_back(VE.getMetadataOrNullID(MD));
Stream.EmitRecord(bitc::METADATA_ARG_LIST, Record, Abbrev);
Record.clear();
}
void ModuleBitcodeWriter::writeDIModule(const DIModule *N,
SmallVectorImpl<uint64_t> &Record,
unsigned Abbrev) {

View File

@ -449,7 +449,8 @@ ValueEnumerator::ValueEnumerator(const Module &M,
}
// Local metadata is enumerated during function-incorporation.
if (isa<LocalAsMetadata>(MD->getMetadata()))
if (isa<LocalAsMetadata>(MD->getMetadata()) ||
isa<DIArgList>(MD->getMetadata()))
continue;
EnumerateMetadata(&F, MD->getMetadata());
@ -1031,14 +1032,26 @@ void ValueEnumerator::incorporateFunction(const Function &F) {
FirstInstID = Values.size();
SmallVector<LocalAsMetadata *, 8> FnLocalMDVector;
SmallVector<DIArgList *, 8> ArgListMDVector;
// Add all of the instructions.
for (const BasicBlock &BB : F) {
for (const Instruction &I : BB) {
for (const Use &OI : I.operands()) {
if (auto *MD = dyn_cast<MetadataAsValue>(&OI))
if (auto *Local = dyn_cast<LocalAsMetadata>(MD->getMetadata()))
if (auto *MD = dyn_cast<MetadataAsValue>(&OI)) {
if (auto *Local = dyn_cast<LocalAsMetadata>(MD->getMetadata())) {
// Enumerate metadata after the instructions they might refer to.
FnLocalMDVector.push_back(Local);
} else if (auto *ArgList = dyn_cast<DIArgList>(MD->getMetadata())) {
ArgListMDVector.push_back(ArgList);
for (ValueAsMetadata *VMD : ArgList->getArgs()) {
if (auto *Local = dyn_cast<LocalAsMetadata>(VMD)) {
// Enumerate metadata after the instructions they might refer
// to.
FnLocalMDVector.push_back(Local);
}
}
}
}
}
if (!I.getType()->isVoidTy())
@ -1054,6 +1067,10 @@ void ValueEnumerator::incorporateFunction(const Function &F) {
"Missing value for metadata operand");
EnumerateFunctionLocalMetadata(F, FnLocalMDVector[i]);
}
// DIArgList entries must come after function-local metadata, as it is not
// possible to forward-reference them.
for (const DIArgList *ArgList : ArgListMDVector)
EnumerateMetadata(&F, ArgList);
}
void ValueEnumerator::purgeFunction() {

View File

@ -1239,8 +1239,9 @@ void SlotTracker::CreateFunctionSlot(const Value *V) {
void SlotTracker::CreateMetadataSlot(const MDNode *N) {
assert(N && "Can't insert a null Value into SlotTracker!");
// Don't make slots for DIExpressions. We just print them inline everywhere.
if (isa<DIExpression>(N))
// Don't make slots for DIExpressions or DIArgLists. We just print them inline
// everywhere.
if (isa<DIExpression>(N) || isa<DIArgList>(N))
return;
unsigned DestSlot = mdnNext;
@ -2351,6 +2352,21 @@ static void writeDIExpression(raw_ostream &Out, const DIExpression *N,
Out << ")";
}
static void writeDIArgList(raw_ostream &Out, const DIArgList *N,
TypePrinting *TypePrinter, SlotTracker *Machine,
const Module *Context, bool FromValue = false) {
assert(FromValue &&
"Unexpected DIArgList metadata outside of value argument");
Out << "!DIArgList(";
FieldSeparator FS;
MDFieldPrinter Printer(Out, TypePrinter, Machine, Context);
for (Metadata *Arg : N->getArgs()) {
Out << FS;
WriteAsOperandInternal(Out, Arg, TypePrinter, Machine, Context, true);
}
Out << ")";
}
static void writeDIGlobalVariableExpression(raw_ostream &Out,
const DIGlobalVariableExpression *N,
TypePrinting *TypePrinter,
@ -2496,12 +2512,16 @@ static void WriteAsOperandInternal(raw_ostream &Out, const Metadata *MD,
TypePrinting *TypePrinter,
SlotTracker *Machine, const Module *Context,
bool FromValue) {
// Write DIExpressions inline when used as a value. Improves readability of
// debug info intrinsics.
// Write DIExpressions and DIArgLists inline when used as a value. Improves
// readability of debug info intrinsics.
if (const DIExpression *Expr = dyn_cast<DIExpression>(MD)) {
writeDIExpression(Out, Expr, TypePrinter, Machine, Context);
return;
}
if (const DIArgList *ArgList = dyn_cast<DIArgList>(MD)) {
writeDIArgList(Out, ArgList, TypePrinter, Machine, Context, FromValue);
return;
}
if (const MDNode *N = dyn_cast<MDNode>(MD)) {
std::unique_ptr<SlotTracker> MachineStorage;
@ -3427,6 +3447,8 @@ void AssemblyWriter::printNamedMDNode(const NamedMDNode *NMD) {
// Write DIExpressions inline.
// FIXME: Ban DIExpressions in NamedMDNodes, they will serve no purpose.
MDNode *Op = NMD->getOperand(i);
assert(!isa<DIArgList>(Op) &&
"DIArgLists should not appear in NamedMDNodes");
if (auto *Expr = dyn_cast<DIExpression>(Op)) {
writeDIExpression(Out, Expr, nullptr, nullptr, nullptr);
continue;
@ -4697,7 +4719,7 @@ static void printMetadataImpl(raw_ostream &ROS, const Metadata &MD,
/* FromValue */ true);
auto *N = dyn_cast<MDNode>(&MD);
if (OnlyAsOperand || !N || isa<DIExpression>(MD))
if (OnlyAsOperand || !N || isa<DIExpression>(MD) || isa<DIArgList>(MD))
return;
OS << " = ";

View File

@ -1515,3 +1515,42 @@ DIMacroFile *DIMacroFile::getImpl(LLVMContext &Context, unsigned MIType,
Metadata *Ops[] = { File, Elements };
DEFINE_GETIMPL_STORE(DIMacroFile, (MIType, Line), Ops);
}
DIArgList *DIArgList::getImpl(LLVMContext &Context,
ArrayRef<ValueAsMetadata *> Args,
StorageType Storage, bool ShouldCreate) {
DEFINE_GETIMPL_LOOKUP(DIArgList, (Args));
DEFINE_GETIMPL_STORE_NO_OPS(DIArgList, (Args));
}
void DIArgList::handleChangedOperand(void *Ref, Metadata *New) {
ValueAsMetadata **OldVMPtr = static_cast<ValueAsMetadata **>(Ref);
assert((!New || isa<ValueAsMetadata>(New)) &&
"DIArgList must be passed a ValueAsMetadata");
untrack();
ValueAsMetadata *NewVM = cast_or_null<ValueAsMetadata>(New);
for (ValueAsMetadata *&VM : Args) {
if (&VM == OldVMPtr) {
if (NewVM)
VM = NewVM;
else
VM = ValueAsMetadata::get(UndefValue::get(VM->getValue()->getType()));
}
}
track();
}
void DIArgList::track() {
for (ValueAsMetadata *&VAM : Args)
if (VAM)
MetadataTracking::track(&VAM, *VAM, *this);
}
void DIArgList::untrack() {
for (ValueAsMetadata *&VAM : Args)
if (VAM)
MetadataTracking::untrack(&VAM, *VAM);
}
void DIArgList::dropAllReferences() {
untrack();
Args.clear();
MDNode::dropAllReferences();
}

View File

@ -1220,6 +1220,19 @@ template <> struct MDNodeKeyImpl<DIMacroFile> {
}
};
template <> struct MDNodeKeyImpl<DIArgList> {
ArrayRef<ValueAsMetadata *> Args;
MDNodeKeyImpl(ArrayRef<ValueAsMetadata *> Args) : Args(Args) {}
MDNodeKeyImpl(const DIArgList *N) : Args(N->getArgs()) {}
bool isKeyOf(const DIArgList *RHS) const { return Args == RHS->getArgs(); }
unsigned getHashValue() const {
return hash_combine_range(Args.begin(), Args.end());
}
};
/// DenseMapInfo for MDNode subclasses.
template <class NodeTy> struct MDNodeInfo {
using KeyTy = MDNodeKeyImpl<NodeTy>;

View File

@ -195,6 +195,19 @@ bool MetadataTracking::isReplaceable(const Metadata &MD) {
return ReplaceableMetadataImpl::isReplaceable(MD);
}
SmallVector<Metadata *, 4> ReplaceableMetadataImpl::getAllArgListUsers() {
SmallVector<Metadata *, 4> MDUsers;
for (auto Pair : UseMap) {
OwnerTy Owner = Pair.second.first;
if (!Owner.is<Metadata *>())
continue;
Metadata *OwnerMD = Owner.get<Metadata *>();
if (OwnerMD->getMetadataID() == Metadata::DIArgListKind)
MDUsers.push_back(OwnerMD);
}
return MDUsers;
}
void ReplaceableMetadataImpl::addRef(void *Ref, OwnerTy Owner) {
bool WasInserted =
UseMap.insert(std::make_pair(Ref, std::make_pair(Owner, NextIndex)))

View File

@ -1301,6 +1301,13 @@ void Verifier::visitDIMacroFile(const DIMacroFile &N) {
}
}
void Verifier::visitDIArgList(const DIArgList &N) {
AssertDI(!N.getNumOperands(),
"DIArgList should have no operands other than a list of "
"ValueAsMetadata",
&N);
}
void Verifier::visitDIModule(const DIModule &N) {
AssertDI(N.getTag() == dwarf::DW_TAG_module, "invalid tag", &N);
AssertDI(!N.getName().empty(), "anonymous module", &N);
@ -5365,9 +5372,9 @@ void Verifier::visitConstrainedFPIntrinsic(ConstrainedFPIntrinsic &FPI) {
void Verifier::visitDbgIntrinsic(StringRef Kind, DbgVariableIntrinsic &DII) {
auto *MD = cast<MetadataAsValue>(DII.getArgOperand(0))->getMetadata();
AssertDI(isa<ValueAsMetadata>(MD) ||
(isa<MDNode>(MD) && !cast<MDNode>(MD)->getNumOperands()),
"invalid llvm.dbg." + Kind + " intrinsic address/value", &DII, MD);
AssertDI(isa<ValueAsMetadata>(MD) || isa<DIArgList>(MD) ||
(isa<MDNode>(MD) && !cast<MDNode>(MD)->getNumOperands()),
"invalid llvm.dbg." + Kind + " intrinsic address/value", &DII, MD);
AssertDI(isa<DILocalVariable>(DII.getRawVariable()),
"invalid llvm.dbg." + Kind + " intrinsic variable", &DII,
DII.getRawVariable());

View File

@ -390,6 +390,26 @@ Value *Mapper::mapValue(const Value *V) {
: MetadataAsValue::get(V->getContext(),
MDTuple::get(V->getContext(), None));
}
if (auto *AL = dyn_cast<DIArgList>(MD)) {
SmallVector<ValueAsMetadata *, 4> MappedArgs;
for (auto *VAM : AL->getArgs()) {
// Map both Local and Constant VAMs here; they will both ultimately
// be mapped via mapValue (apart from constants when we have no
// module level changes, which have an identity mapping).
if ((Flags & RF_NoModuleLevelChanges) && isa<ConstantAsMetadata>(VAM)) {
MappedArgs.push_back(VAM);
} else if (Value *LV = mapValue(VAM->getValue())) {
MappedArgs.push_back(
LV == VAM->getValue() ? VAM : ValueAsMetadata::get(LV));
} else {
// If we cannot map the value, set the argument as undef.
MappedArgs.push_back(ValueAsMetadata::get(
UndefValue::get(VAM->getValue()->getType())));
}
}
return MetadataAsValue::get(V->getContext(),
DIArgList::get(V->getContext(), MappedArgs));
}
// If this is a module-level metadata and we know that nothing at the module
// level is changing, then use an identity mapping.

View File

@ -0,0 +1,50 @@
; RUN: opt -verify < %s | opt -verify -S | FileCheck %s
; Simple IR-BC-IR round-trip test for a @llvm.dbg.value that uses !DIArgList
; and DW_OP_LLVM_arg.
source_filename = ".\\debug_value_list.cpp"
target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-windows-msvc19.16.27034"
; CHECK-COUNT-3: llvm.dbg.value(
; CHECK-SAME: metadata !DIArgList(i32 %a, i32 %b)
; CHECK-SAME: metadata !16,
; CHECK-SAME: metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus)
define dso_local i32 @"?foo@@YAHHH@Z"(i32 %a, i32 %b) local_unnamed_addr !dbg !8 {
entry:
call void @llvm.dbg.value(metadata !DIArgList(i32 %b), metadata !14, metadata !DIExpression(DW_OP_LLVM_arg, 0)), !dbg !17
call void @llvm.dbg.value(metadata !DIArgList(i32 %a), metadata !15, metadata !DIExpression(DW_OP_LLVM_arg, 0)), !dbg !17
call void @llvm.dbg.value(
metadata !DIArgList(i32 %a, i32 %b),
metadata !16,
metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus)), !dbg !17
%mul = mul nsw i32 %b, %a, !dbg !18
ret i32 %mul, !dbg !18
}
declare void @llvm.dbg.value(metadata, metadata, metadata)
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!3, !4, !5, !6}
!llvm.ident = !{!7}
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None)
!1 = !DIFile(filename: "debug_value_list.cpp", directory: "/tmp")
!2 = !{}
!3 = !{i32 2, !"CodeView", i32 1}
!4 = !{i32 2, !"Debug Info Version", i32 3}
!5 = !{i32 1, !"wchar_size", i32 2}
!6 = !{i32 7, !"PIC Level", i32 2}
!7 = !{!"clang version 11.0.0"}
!8 = distinct !DISubprogram(name: "foo", linkageName: "?foo@@YAHHH@Z", scope: !9, file: !9, line: 1, type: !10, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !13)
!9 = !DIFile(filename: ".\\debug_value_list.cpp", directory: "/tmp")
!10 = !DISubroutineType(types: !11)
!11 = !{!12, !12, !12}
!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!13 = !{!14, !15, !16}
!14 = !DILocalVariable(name: "b", arg: 2, scope: !8, file: !9, line: 1, type: !12)
!15 = !DILocalVariable(name: "a", arg: 1, scope: !8, file: !9, line: 1, type: !12)
!16 = !DILocalVariable(name: "c", scope: !8, file: !9, line: 2, type: !12)
!17 = !DILocation(line: 0, scope: !8)
!18 = !DILocation(line: 3, scope: !8)

View File

@ -3056,6 +3056,42 @@ TEST_F(ValueAsMetadataTest, CollidingDoubleUpdates) {
Temp->replaceAllUsesWith(nullptr);
}
typedef MetadataTest DIArgListTest;
TEST_F(DIArgListTest, get) {
SmallVector<ValueAsMetadata *, 2> VMs;
VMs.push_back(
ConstantAsMetadata::get(ConstantInt::get(Context, APInt(8, 0))));
VMs.push_back(
ConstantAsMetadata::get(ConstantInt::get(Context, APInt(2, 0))));
DIArgList *DV0 = DIArgList::get(Context, VMs);
DIArgList *DV1 = DIArgList::get(Context, VMs);
EXPECT_EQ(DV0, DV1);
}
TEST_F(DIArgListTest, UpdatesOnRAUW) {
Type *Ty = Type::getInt1PtrTy(Context);
ConstantAsMetadata *CI =
ConstantAsMetadata::get(ConstantInt::get(Context, APInt(8, 0)));
std::unique_ptr<GlobalVariable> GV0(
new GlobalVariable(Ty, false, GlobalValue::ExternalLinkage));
auto *MD0 = ValueAsMetadata::get(GV0.get());
SmallVector<ValueAsMetadata *, 2> VMs;
VMs.push_back(CI);
VMs.push_back(MD0);
auto *AL = DIArgList::get(Context, VMs);
EXPECT_EQ(AL->getArgs()[0], CI);
EXPECT_EQ(AL->getArgs()[1], MD0);
std::unique_ptr<GlobalVariable> GV1(
new GlobalVariable(Ty, false, GlobalValue::ExternalLinkage));
auto *MD1 = ValueAsMetadata::get(GV1.get());
GV0->replaceAllUsesWith(GV1.get());
EXPECT_EQ(AL->getArgs()[0], CI);
EXPECT_EQ(AL->getArgs()[1], MD1);
}
typedef MetadataTest TrackingMDRefTest;
TEST_F(TrackingMDRefTest, UpdatesOnRAUW) {