diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -5317,6 +5317,22 @@ !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` (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 """"""""""""""" diff --git a/llvm/include/llvm-c/DebugInfo.h b/llvm/include/llvm-c/DebugInfo.h --- a/llvm/include/llvm-c/DebugInfo.h +++ b/llvm/include/llvm-c/DebugInfo.h @@ -161,7 +161,8 @@ LLVMDIMacroFileMetadataKind, LLVMDICommonBlockMetadataKind, LLVMDIStringTypeMetadataKind, - LLVMDIGenericSubrangeMetadataKind + LLVMDIGenericSubrangeMetadataKind, + LLVMDIArgListMetadataKind }; typedef unsigned LLVMMetadataKind; diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -341,8 +341,9 @@ 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 diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h --- a/llvm/include/llvm/IR/DebugInfoMetadata.h +++ b/llvm/include/llvm/IR/DebugInfoMetadata.h @@ -3511,6 +3511,47 @@ } }; +/// List of ValueAsMetadata, to be used as an argument to a dbg.value +/// intrinsic. +class DIArgList : public MDNode { + friend class LLVMContextImpl; + friend class MDNode; + + std::vector Args; + + DIArgList(LLVMContext &C, StorageType Storage, + ArrayRef Args) + : MDNode(C, DIArgListKind, Storage, None), Args(Args) { + track(); + } + ~DIArgList() { untrack(); } + + static DIArgList *getImpl(LLVMContext &Context, + ArrayRef 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 Args), (Args)) + + TempDIArgList clone() const { return cloneImpl(); } + + ArrayRef getArgs() const { return Args; } + + 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, diff --git a/llvm/include/llvm/IR/Metadata.h b/llvm/include/llvm/IR/Metadata.h --- a/llvm/include/llvm/IR/Metadata.h +++ b/llvm/include/llvm/IR/Metadata.h @@ -301,6 +301,9 @@ /// 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 getAllArgListUsers(); + /// Resolve all uses of this. /// /// Resolve all uses of this, turning off RAUW permanently. If \c @@ -380,6 +383,10 @@ Type *getType() const { return V->getType(); } LLVMContext &getContext() const { return V->getContext(); } + SmallVector getAllArgListUsers() { + return ReplaceableMetadataImpl::getAllArgListUsers(); + } + static void handleDeletion(Value *V); static void handleRAUW(Value *From, Value *To); diff --git a/llvm/include/llvm/IR/Metadata.def b/llvm/include/llvm/IR/Metadata.def --- a/llvm/include/llvm/IR/Metadata.def +++ b/llvm/include/llvm/IR/Metadata.def @@ -114,6 +114,7 @@ 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) diff --git a/llvm/lib/AsmParser/LLParser.h b/llvm/lib/AsmParser/LLParser.h --- a/llvm/lib/AsmParser/LLParser.h +++ b/llvm/lib/AsmParser/LLParser.h @@ -536,6 +536,8 @@ #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 { diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -776,6 +776,11 @@ 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; @@ -5289,6 +5294,36 @@ 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 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(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, @@ -5399,8 +5434,14 @@ 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; } diff --git a/llvm/lib/Bitcode/Reader/BitcodeAnalyzer.cpp b/llvm/lib/Bitcode/Reader/BitcodeAnalyzer.cpp --- a/llvm/lib/Bitcode/Reader/BitcodeAnalyzer.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeAnalyzer.cpp @@ -361,6 +361,7 @@ 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) { diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp --- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp +++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp @@ -2075,6 +2075,16 @@ return Err; break; } + case bitc::METADATA_ARG_LIST: { + SmallVector Elts; + Elts.reserve(Record.size()); + for (uint64_t Elt : Record) + Elts.push_back(dyn_cast_or_null(getMDOrNull(Elt))); + + MetadataList.assignValue(DIArgList::get(Context, Elts), NextMetadataNo); + NextMetadataNo++; + break; + } } return Error::success(); #undef GET_OR_DISTINCT diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -335,6 +335,8 @@ unsigned Abbrev); void writeDIMacroFile(const DIMacroFile *N, SmallVectorImpl &Record, unsigned Abbrev); + void writeDIArgList(const DIArgList *N, SmallVectorImpl &Record, + unsigned Abbrev); void writeDIModule(const DIModule *N, SmallVectorImpl &Record, unsigned Abbrev); void writeDITemplateTypeParameter(const DITemplateTypeParameter *N, @@ -1857,6 +1859,17 @@ Record.clear(); } +void ModuleBitcodeWriter::writeDIArgList(const DIArgList *N, + SmallVectorImpl &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 &Record, unsigned Abbrev) { diff --git a/llvm/lib/Bitcode/Writer/ValueEnumerator.cpp b/llvm/lib/Bitcode/Writer/ValueEnumerator.cpp --- a/llvm/lib/Bitcode/Writer/ValueEnumerator.cpp +++ b/llvm/lib/Bitcode/Writer/ValueEnumerator.cpp @@ -449,7 +449,8 @@ } // Local metadata is enumerated during function-incorporation. - if (isa(MD->getMetadata())) + if (isa(MD->getMetadata()) || + isa(MD->getMetadata())) continue; EnumerateMetadata(&F, MD->getMetadata()); @@ -1031,14 +1032,25 @@ FirstInstID = Values.size(); SmallVector FnLocalMDVector; + SmallVector 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(&OI)) - if (auto *Local = dyn_cast(MD->getMetadata())) + if (auto *Local = dyn_cast(MD->getMetadata())) { // Enumerate metadata after the instructions they might refer to. FnLocalMDVector.push_back(Local); + } else if (auto *ArgList = dyn_cast(MD->getMetadata())) { + ArgListMDVector.push_back(ArgList); + for (ValueAsMetadata *VMD : ArgList->getArgs()) { + if (auto *Local = dyn_cast(VMD)) { + // Enumerate metadata after the instructions they might refer + // to. + FnLocalMDVector.push_back(Local); + } + } + } } if (!I.getType()->isVoidTy()) @@ -1054,6 +1066,10 @@ "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() { diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -1239,8 +1239,9 @@ 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(N)) + // Don't make slots for DIExpressions or DIArgLists. We just print them inline + // everywhere. + if (isa(N) || isa(N)) return; unsigned DestSlot = mdnNext; @@ -2351,6 +2352,21 @@ 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 @@ 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(MD)) { writeDIExpression(Out, Expr, TypePrinter, Machine, Context); return; } + if (const DIArgList *ArgList = dyn_cast(MD)) { + writeDIArgList(Out, ArgList, TypePrinter, Machine, Context, FromValue); + return; + } if (const MDNode *N = dyn_cast(MD)) { std::unique_ptr MachineStorage; @@ -3414,6 +3434,8 @@ // Write DIExpressions inline. // FIXME: Ban DIExpressions in NamedMDNodes, they will serve no purpose. MDNode *Op = NMD->getOperand(i); + assert(!isa(Op) && + "DIArgLists should not appear in NamedMDNodes"); if (auto *Expr = dyn_cast(Op)) { writeDIExpression(Out, Expr, nullptr, nullptr, nullptr); continue; @@ -4684,7 +4706,7 @@ /* FromValue */ true); auto *N = dyn_cast(&MD); - if (OnlyAsOperand || !N || isa(MD)) + if (OnlyAsOperand || !N || isa(MD) || isa(MD)) return; OS << " = "; diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp --- a/llvm/lib/IR/DebugInfoMetadata.cpp +++ b/llvm/lib/IR/DebugInfoMetadata.cpp @@ -1506,3 +1506,42 @@ Metadata *Ops[] = { File, Elements }; DEFINE_GETIMPL_STORE(DIMacroFile, (MIType, Line), Ops); } + +DIArgList *DIArgList::getImpl(LLVMContext &Context, + ArrayRef 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(Ref); + assert((!New || isa(New)) && + "DIArgList must be passed a ValueAsMetadata"); + untrack(); + ValueAsMetadata *NewVM = cast_or_null(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(); +} diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h --- a/llvm/lib/IR/LLVMContextImpl.h +++ b/llvm/lib/IR/LLVMContextImpl.h @@ -1217,6 +1217,19 @@ } }; +template <> struct MDNodeKeyImpl { + ArrayRef Args; + + MDNodeKeyImpl(ArrayRef 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 struct MDNodeInfo { using KeyTy = MDNodeKeyImpl; diff --git a/llvm/lib/IR/Metadata.cpp b/llvm/lib/IR/Metadata.cpp --- a/llvm/lib/IR/Metadata.cpp +++ b/llvm/lib/IR/Metadata.cpp @@ -195,6 +195,19 @@ return ReplaceableMetadataImpl::isReplaceable(MD); } +SmallVector ReplaceableMetadataImpl::getAllArgListUsers() { + SmallVector MDUsers; + for (auto Pair : UseMap) { + OwnerTy Owner = Pair.second.first; + if (!Owner.is()) + continue; + Metadata *OwnerMD = Owner.get(); + 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))) diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -1293,6 +1293,13 @@ } } +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); @@ -5301,9 +5308,9 @@ void Verifier::visitDbgIntrinsic(StringRef Kind, DbgVariableIntrinsic &DII) { auto *MD = cast(DII.getArgOperand(0))->getMetadata(); - AssertDI(isa(MD) || - (isa(MD) && !cast(MD)->getNumOperands()), - "invalid llvm.dbg." + Kind + " intrinsic address/value", &DII, MD); + AssertDI(isa(MD) || isa(MD) || + (isa(MD) && !cast(MD)->getNumOperands()), + "invalid llvm.dbg." + Kind + " intrinsic address/value", &DII, MD); AssertDI(isa(DII.getRawVariable()), "invalid llvm.dbg." + Kind + " intrinsic variable", &DII, DII.getRawVariable()); diff --git a/llvm/lib/Transforms/Utils/ValueMapper.cpp b/llvm/lib/Transforms/Utils/ValueMapper.cpp --- a/llvm/lib/Transforms/Utils/ValueMapper.cpp +++ b/llvm/lib/Transforms/Utils/ValueMapper.cpp @@ -392,6 +392,26 @@ : MetadataAsValue::get(V->getContext(), MDTuple::get(V->getContext(), None)); } + if (auto *AL = dyn_cast(MD)) { + SmallVector 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(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. diff --git a/llvm/test/DebugInfo/Generic/debug_value_list.ll b/llvm/test/DebugInfo/Generic/debug_value_list.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/debug_value_list.ll @@ -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) diff --git a/llvm/unittests/IR/MetadataTest.cpp b/llvm/unittests/IR/MetadataTest.cpp --- a/llvm/unittests/IR/MetadataTest.cpp +++ b/llvm/unittests/IR/MetadataTest.cpp @@ -3052,6 +3052,42 @@ Temp->replaceAllUsesWith(nullptr); } +typedef MetadataTest DIArgListTest; + +TEST_F(DIArgListTest, get) { + SmallVector 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 GV0( + new GlobalVariable(Ty, false, GlobalValue::ExternalLinkage)); + auto *MD0 = ValueAsMetadata::get(GV0.get()); + + SmallVector 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 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) {