diff --git a/llvm/docs/SourceLevelDebugging.rst b/llvm/docs/SourceLevelDebugging.rst index 12b5e3e549df7c..6bf38193acfefc 100644 --- a/llvm/docs/SourceLevelDebugging.rst +++ b/llvm/docs/SourceLevelDebugging.rst @@ -308,6 +308,28 @@ directly, not its address. Note that the value operand of this intrinsic may be indirect (i.e, a pointer to the source variable), provided that interpreting the complex expression derives the direct value. + +``#dbg_declare_value`` +^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: llvm + + #dbg_declare_value([Value|MDNode], DILocalVariable, DIExpression, DILocation) + +This record provides information about a local element (e.g., variable). The +first argument is used to compute the value of the variable throughout the +entire function. The second argument is a +:ref:`local variable ` containing a description of the +variable. The third argument is a :ref:`complex expression `. The +foruth argument is a :ref:`source location `. A +``#dbg_declare_value`` record describes describes the *value* of a source +variable directly, not its address. The difference between a ``#dbg_value`` and +a ``#dbg_declare_value`` is that, just like a ``#dbg_declare``, a frontend +should generate exactly one ``#dbg_declare_value`` record. The idea is to have +``#dbg_declare`` guarantees but be able to describe a value rather than the +address of a value. + + ``#dbg_assign`` ^^^^^^^^^^^^^^^ .. toctree:: diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h index b0c5beae631ce6..aa3d1d92548d79 100644 --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -688,6 +688,8 @@ enum FunctionCodes { FUNC_CODE_DEBUG_RECORD_VALUE_SIMPLE = 64, // [DILocation, DILocalVariable, DIExpression, Value] FUNC_CODE_DEBUG_RECORD_LABEL = 65, // [DILocation, DILabel] + FUNC_CODE_DEBUG_RECORD_DECLARE_VALUE = + 66, // [DILocation, DILocalVariable, DIExpression, ValueAsMetadata] }; enum UseListCodes { diff --git a/llvm/include/llvm/IR/DIBuilder.h b/llvm/include/llvm/IR/DIBuilder.h index 4228ec9c3ef7a7..49d9e686aeffac 100644 --- a/llvm/include/llvm/IR/DIBuilder.h +++ b/llvm/include/llvm/IR/DIBuilder.h @@ -1156,6 +1156,18 @@ namespace llvm { DIExpression *Expr, const DILocation *DL, InsertPosition InsertPt); + /// Insert a new llvm.dbg.declare_value intrinsic call. + /// \param Storage llvm::Value of the variable + /// \param VarInfo Variable's debug info descriptor. + /// \param Expr A complex location expression. + /// \param DL Debug info location. + /// \param InsertPt Location for the new intrinsic. + LLVM_ABI DbgInstPtr insertDeclareValue(llvm::Value *Storage, + DILocalVariable *VarInfo, + DIExpression *Expr, + const DILocation *DL, + InsertPosition InsertPt); + /// Insert a new llvm.dbg.label intrinsic call. /// \param LabelInfo Label's debug info descriptor. /// \param DL Debug info location. diff --git a/llvm/include/llvm/IR/DebugInfo.h b/llvm/include/llvm/IR/DebugInfo.h index 862293c9666a71..922658927ad6a6 100644 --- a/llvm/include/llvm/IR/DebugInfo.h +++ b/llvm/include/llvm/IR/DebugInfo.h @@ -44,6 +44,8 @@ class Module; LLVM_ABI TinyPtrVector findDVRDeclares(Value *V); /// As above, for DVRValues. LLVM_ABI TinyPtrVector findDVRValues(Value *V); +/// As above, for DVRDeclareValues. +LLVM_ABI TinyPtrVector findDVRDeclareValues(Value *V); /// Finds the debug info records describing a value. LLVM_ABI void diff --git a/llvm/include/llvm/IR/DebugProgramInstruction.h b/llvm/include/llvm/IR/DebugProgramInstruction.h index 66f44fe34d3f68..20f11b4fb8bac9 100644 --- a/llvm/include/llvm/IR/DebugProgramInstruction.h +++ b/llvm/include/llvm/IR/DebugProgramInstruction.h @@ -282,6 +282,7 @@ class DbgVariableRecord : public DbgRecord, protected DebugValueUser { Declare, Value, Assign, + DeclareValue, End, ///< Marks the end of the concrete types. Any, ///< To indicate all LocationTypes in searches. @@ -364,6 +365,13 @@ class DbgVariableRecord : public DbgRecord, protected DebugValueUser { createDVRDeclare(Value *Address, DILocalVariable *DV, DIExpression *Expr, const DILocation *DI, DbgVariableRecord &InsertBefore); + LLVM_ABI static DbgVariableRecord * + createDVRDeclareValue(Value *Address, DILocalVariable *DV, DIExpression *Expr, + const DILocation *DI); + LLVM_ABI static DbgVariableRecord * + createDVRDeclareValue(Value *Address, DILocalVariable *DV, DIExpression *Expr, + const DILocation *DI, DbgVariableRecord &InsertBefore); + /// Iterator for ValueAsMetadata that internally uses direct pointer iteration /// over either a ValueAsMetadata* or a ValueAsMetadata**, dereferencing to the /// ValueAsMetadata . @@ -414,6 +422,7 @@ class DbgVariableRecord : public DbgRecord, protected DebugValueUser { bool isDbgDeclare() const { return Type == LocationType::Declare; } bool isDbgValue() const { return Type == LocationType::Value; } + bool isDbgDeclareValue() const { return Type == LocationType::DeclareValue; } /// Get the locations corresponding to the variable referenced by the debug /// info intrinsic. Depending on the intrinsic, this could be the @@ -439,12 +448,16 @@ class DbgVariableRecord : public DbgRecord, protected DebugValueUser { bool hasValidLocation() const { return getVariableLocationOp(0) != nullptr; } /// Does this describe the address of a local variable. True for dbg.addr - /// and dbg.declare, but not dbg.value, which describes its value. + /// and dbg.declare, but not dbg.value or dbg.declare_value, which describes + /// its value. bool isAddressOfVariable() const { return Type == LocationType::Declare; } /// Determine if this describes the value of a local variable. It is false for - /// dbg.declare, but true for dbg.value, which describes its value. - bool isValueOfVariable() const { return Type == LocationType::Value; } + /// dbg.declare, but true for dbg.value and dbg.declare_value, which describes + /// its value. + bool isValueOfVariable() const { + return Type == LocationType::Value || Type == LocationType::DeclareValue; + } LocationType getType() const { return Type; } diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp index ebca344ae7b931..ef1515ca104944 100644 --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -1007,6 +1007,7 @@ lltok::Kind LLLexer::LexIdentifier() { DBGRECORDTYPEKEYWORD(declare); DBGRECORDTYPEKEYWORD(assign); DBGRECORDTYPEKEYWORD(label); + DBGRECORDTYPEKEYWORD(declare_value); #undef DBGRECORDTYPEKEYWORD if (Keyword.starts_with("DIFlag")) { diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp index 799234a0b491d3..61d5c2c81df2e1 100644 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -7155,7 +7155,8 @@ bool LLParser::parseDebugRecord(DbgRecord *&DR, PerFunctionState &PFS) { .Case("declare", RecordKind::ValueKind) .Case("value", RecordKind::ValueKind) .Case("assign", RecordKind::ValueKind) - .Case("label", RecordKind::LabelKind); + .Case("label", RecordKind::LabelKind) + .Case("declare_value", RecordKind::ValueKind); // Parsing labels is trivial; parse here and early exit, otherwise go into the // full DbgVariableRecord processing stage. @@ -7180,7 +7181,8 @@ bool LLParser::parseDebugRecord(DbgRecord *&DR, PerFunctionState &PFS) { LocType ValueType = StringSwitch(Lex.getStrVal()) .Case("declare", LocType::Declare) .Case("value", LocType::Value) - .Case("assign", LocType::Assign); + .Case("assign", LocType::Assign) + .Case("declare_value", LocType::DeclareValue); Lex.Lex(); if (parseToken(lltok::lparen, "Expected '(' here")) diff --git a/llvm/lib/Bitcode/Reader/BitcodeAnalyzer.cpp b/llvm/lib/Bitcode/Reader/BitcodeAnalyzer.cpp index fe9e0ddca70914..911ec7501eb8b5 100644 --- a/llvm/lib/Bitcode/Reader/BitcodeAnalyzer.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeAnalyzer.cpp @@ -272,6 +272,7 @@ GetCodeName(unsigned CodeID, unsigned BlockID, STRINGIFY_CODE(FUNC_CODE, INST_CALLBR) STRINGIFY_CODE(FUNC_CODE, BLOCKADDR_USERS) STRINGIFY_CODE(FUNC_CODE, DEBUG_RECORD_DECLARE) + STRINGIFY_CODE(FUNC_CODE, DEBUG_RECORD_DECLARE_VALUE) STRINGIFY_CODE(FUNC_CODE, DEBUG_RECORD_VALUE) STRINGIFY_CODE(FUNC_CODE, DEBUG_RECORD_ASSIGN) STRINGIFY_CODE(FUNC_CODE, DEBUG_RECORD_VALUE_SIMPLE) diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp index 8930d64de5e37b..c2a0fb4f1ebfe7 100644 --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -6655,6 +6655,7 @@ Error BitcodeReader::parseFunctionBody(Function *F) { case bitc::FUNC_CODE_DEBUG_RECORD_VALUE_SIMPLE: case bitc::FUNC_CODE_DEBUG_RECORD_VALUE: case bitc::FUNC_CODE_DEBUG_RECORD_DECLARE: + case bitc::FUNC_CODE_DEBUG_RECORD_DECLARE_VALUE: case bitc::FUNC_CODE_DEBUG_RECORD_ASSIGN: { // DbgVariableRecords are placed after the Instructions that they are // attached to. @@ -6671,6 +6672,8 @@ Error BitcodeReader::parseFunctionBody(Function *F) { // ..., Value // dbg_declare (FUNC_CODE_DEBUG_RECORD_DECLARE) // ..., LocationMetadata + // dbg_declare_value (FUNC_CODE_DEBUG_RECORD_DECLARE_VALUE) + // ..., LocationMetadata // dbg_assign (FUNC_CODE_DEBUG_RECORD_ASSIGN) // ..., LocationMetadata, DIAssignID, DIExpression, LocationMetadata unsigned Slot = 0; @@ -6712,6 +6715,11 @@ Error BitcodeReader::parseFunctionBody(Function *F) { DVR = new DbgVariableRecord(RawLocation, Var, Expr, DIL, DbgVariableRecord::LocationType::Declare); break; + case bitc::FUNC_CODE_DEBUG_RECORD_DECLARE_VALUE: + DVR = new DbgVariableRecord( + RawLocation, Var, Expr, DIL, + DbgVariableRecord::LocationType::DeclareValue); + break; case bitc::FUNC_CODE_DEBUG_RECORD_ASSIGN: { DIAssignID *ID = cast(getFnMetadataByID(Record[Slot++])); DIExpression *AddrExpr = diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp index 76494c792ac7b5..1d0461478b90cd 100644 --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -3844,6 +3844,9 @@ void ModuleBitcodeWriter::writeFunction( } else if (DVR.isDbgDeclare()) { Vals.push_back(VE.getMetadataID(DVR.getRawLocation())); Stream.EmitRecord(bitc::FUNC_CODE_DEBUG_RECORD_DECLARE, Vals); + } else if (DVR.isDbgDeclareValue()) { + Vals.push_back(VE.getMetadataID(DVR.getRawLocation())); + Stream.EmitRecord(bitc::FUNC_CODE_DEBUG_RECORD_DECLARE_VALUE, Vals); } else { assert(DVR.isDbgAssign() && "Unexpected DbgRecord kind"); Vals.push_back(VE.getMetadataID(DVR.getRawLocation())); diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp index 94a1aa30873779..eebabfd772982b 100644 --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -4864,6 +4864,9 @@ void AssemblyWriter::printDbgVariableRecord(const DbgVariableRecord &DVR) { case DbgVariableRecord::LocationType::Declare: Out << "declare"; break; + case DbgVariableRecord::LocationType::DeclareValue: + Out << "declare_value"; + break; case DbgVariableRecord::LocationType::Assign: Out << "assign"; break; diff --git a/llvm/lib/IR/DIBuilder.cpp b/llvm/lib/IR/DIBuilder.cpp index ca11ecf2f473e1..b01860dba74a94 100644 --- a/llvm/lib/IR/DIBuilder.cpp +++ b/llvm/lib/IR/DIBuilder.cpp @@ -1147,6 +1147,24 @@ DbgInstPtr DIBuilder::insertDeclare(Value *Storage, DILocalVariable *VarInfo, return DVR; } +DbgInstPtr DIBuilder::insertDeclareValue(Value *Storage, + DILocalVariable *VarInfo, + DIExpression *Expr, + const DILocation *DL, + InsertPosition InsertPt) { + assert(VarInfo && + "empty or invalid DILocalVariable* passed to dbg.declare_value"); + assert(DL && "Expected debug loc"); + assert(DL->getScope()->getSubprogram() == + VarInfo->getScope()->getSubprogram() && + "Expected matching subprograms"); + + DbgVariableRecord *DVR = + DbgVariableRecord::createDVRDeclareValue(Storage, VarInfo, Expr, DL); + insertDbgVariableRecord(DVR, InsertPt); + return DVR; +} + void DIBuilder::insertDbgVariableRecord(DbgVariableRecord *DVR, InsertPosition InsertPt) { assert(InsertPt.isValid()); diff --git a/llvm/lib/IR/DebugInfo.cpp b/llvm/lib/IR/DebugInfo.cpp index 0f9d064857dc48..1859bc468cac91 100644 --- a/llvm/lib/IR/DebugInfo.cpp +++ b/llvm/lib/IR/DebugInfo.cpp @@ -62,6 +62,23 @@ TinyPtrVector llvm::findDVRDeclares(Value *V) { return Declares; } +TinyPtrVector llvm::findDVRDeclareValues(Value *V) { + // This function is hot. Check whether the value has any metadata to avoid a + // DenseMap lookup. This check is a bitfield datamember lookup. + if (!V->isUsedByMetadata()) + return {}; + auto *L = ValueAsMetadata::getIfExists(V); + if (!L) + return {}; + + TinyPtrVector DEclareValues; + for (DbgVariableRecord *DVR : L->getAllDbgVariableRecordUsers()) + if (DVR->getType() == DbgVariableRecord::LocationType::DeclareValue) + DEclareValues.push_back(DVR); + + return DEclareValues; +} + TinyPtrVector llvm::findDVRValues(Value *V) { // This function is hot. Check whether the value has any metadata to avoid a // DenseMap lookup. This check is a bitfield datamember lookup. diff --git a/llvm/lib/IR/DebugProgramInstruction.cpp b/llvm/lib/IR/DebugProgramInstruction.cpp index 6b1fd3907dc411..9efa3c71c0bb88 100644 --- a/llvm/lib/IR/DebugProgramInstruction.cpp +++ b/llvm/lib/IR/DebugProgramInstruction.cpp @@ -211,6 +211,22 @@ DbgVariableRecord::createDVRDeclare(Value *Address, DILocalVariable *DV, return NewDVRDeclare; } +DbgVariableRecord * +DbgVariableRecord::createDVRDeclareValue(Value *Address, DILocalVariable *DV, + DIExpression *Expr, + const DILocation *DI) { + return new DbgVariableRecord(ValueAsMetadata::get(Address), DV, Expr, DI, + LocationType::DeclareValue); +} + +DbgVariableRecord *DbgVariableRecord::createDVRDeclareValue( + Value *Address, DILocalVariable *DV, DIExpression *Expr, + const DILocation *DI, DbgVariableRecord &InsertBefore) { + auto *NewDVRCoro = createDVRDeclareValue(Address, DV, Expr, DI); + NewDVRCoro->insertBefore(&InsertBefore); + return NewDVRCoro; +} + DbgVariableRecord *DbgVariableRecord::createDVRAssign( Value *Val, DILocalVariable *Variable, DIExpression *Expression, DIAssignID *AssignID, Value *Address, DIExpression *AddressExpression, @@ -427,6 +443,10 @@ DbgVariableRecord::createDebugIntrinsic(Module *M, case DbgVariableRecord::LocationType::End: case DbgVariableRecord::LocationType::Any: llvm_unreachable("Invalid LocationType"); + break; + case DbgVariableRecord::LocationType::DeclareValue: + llvm_unreachable( + "#dbg_declare_value should never be converted to an intrinsic"); } // Create the intrinsic from this DbgVariableRecord's information, optionally diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index 92e7b7530b0387..7cc1980d24c331 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -190,6 +190,9 @@ struct llvm::VerifierSupport { case DbgVariableRecord::LocationType::Declare: *OS << "declare"; break; + case DbgVariableRecord::LocationType::DeclareValue: + *OS << "declare_value"; + break; case DbgVariableRecord::LocationType::Assign: *OS << "assign"; break; @@ -7083,6 +7086,7 @@ void Verifier::visit(DbgVariableRecord &DVR) { CheckDI(DVR.getType() == DbgVariableRecord::LocationType::Value || DVR.getType() == DbgVariableRecord::LocationType::Declare || + DVR.getType() == DbgVariableRecord::LocationType::DeclareValue || DVR.getType() == DbgVariableRecord::LocationType::Assign, "invalid #dbg record type", &DVR, DVR.getType(), BB, F); diff --git a/llvm/test/Assembler/dbg_declare_value.ll b/llvm/test/Assembler/dbg_declare_value.ll new file mode 100644 index 00000000000000..cacbfc6b0e6b00 --- /dev/null +++ b/llvm/test/Assembler/dbg_declare_value.ll @@ -0,0 +1,37 @@ +; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis | FileCheck %s + +; CHECK: #dbg_declare_value(double %{{[0-9]+}}, !{{[0-9]+}}, !DIExpression(), !{{[0-9]+}}) + +; ModuleID = '/tmp/test.c' +source_filename = "/tmp/test.c" +target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32" +target triple = "arm64-apple-macosx26.0.0" + +; Function Attrs: noinline nounwind optnone ssp uwtable(sync) +define void @foo(double noundef %0) #0 !dbg !9 { + #dbg_declare_value(double %0, !15, !DIExpression(), !16) + ret void, !dbg !21 +} + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6, !7} +!llvm.ident = !{!8} + +!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 22.0.0git (git@github.com:rastogishubham/llvm-project.git bacf99969b2f3e6db4cfcf536cce8b01ffd20aa0)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/") +!1 = !DIFile(filename: "/tmp/test.c", directory: "/Users/srastogi/Development/llvm-project-2/build_ninja", checksumkind: CSK_MD5, checksum: "fa15cf45ed4f9d805aab17eb7856a442") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 8, !"PIC Level", i32 2} +!6 = !{i32 7, !"uwtable", i32 1} +!7 = !{i32 7, !"frame-pointer", i32 4} +!8 = !{!"clang version 22.0.0git (git@github.com:rastogishubham/llvm-project.git bacf99969b2f3e6db4cfcf536cce8b01ffd20aa0)"} +!9 = distinct !DISubprogram(name: "foo", scope: !10, file: !10, line: 1, type: !11, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !14) +!10 = !DIFile(filename: "/tmp/test.c", directory: "", checksumkind: CSK_MD5, checksum: "fa15cf45ed4f9d805aab17eb7856a442") +!11 = !DISubroutineType(types: !12) +!12 = !{null, !13} +!13 = !DIBasicType(name: "double", size: 64, encoding: DW_ATE_float) +!14 = !{} +!15 = !DILocalVariable(name: "x", arg: 1, scope: !9, file: !10, line: 1, type: !13) +!16 = !DILocation(line: 1, column: 17, scope: !9) +!21 = !DILocation(line: 3, column: 1, scope: !9)