[llvm] Add new llvm.dbg.declare_value intrinsic. (PR #168132)
Shubham Sandeep Rastogi via llvm-commits
llvm-commits at lists.llvm.org
Tue Nov 18 15:58:21 PST 2025
https://github.com/rastogishubham updated https://github.com/llvm/llvm-project/pull/168132
>From a1b7e1335cb99b1876193b09999ff94b64591d4b Mon Sep 17 00:00:00 2001
From: Shubham Sandeep Rastogi <srastogi22 at apple.com>
Date: Fri, 19 Sep 2025 13:06:08 -0700
Subject: [PATCH] Add new llvm.dbg.declare_value intrinsic.
For swift async code, we need to use a debug intrinsic that behaves like
an llvm.dbg.declare but can take any location type rather than just a
pointer or integer.
To solve this, a new debug instrinsic called llvm.dbg.declare_value
has been created, which behaves exactly like an llvm.dbg.declare but
can take non pointer and integer location types.
---
llvm/docs/SourceLevelDebugging.rst | 21 +++++++++++++++++++
llvm/include/llvm/Bitcode/LLVMBitCodes.h | 2 ++
llvm/include/llvm/IR/DIBuilder.h | 12 +++++++++++
llvm/include/llvm/IR/DebugInfo.h | 2 ++
.../include/llvm/IR/DebugProgramInstruction.h | 19 ++++++++++++++---
llvm/lib/AsmParser/LLLexer.cpp | 1 +
llvm/lib/AsmParser/LLParser.cpp | 6 ++++--
llvm/lib/Bitcode/Reader/BitcodeAnalyzer.cpp | 1 +
llvm/lib/Bitcode/Reader/BitcodeReader.cpp | 8 +++++++
llvm/lib/Bitcode/Writer/BitcodeWriter.cpp | 3 +++
llvm/lib/IR/AsmWriter.cpp | 3 +++
llvm/lib/IR/DIBuilder.cpp | 18 ++++++++++++++++
llvm/lib/IR/DebugInfo.cpp | 17 +++++++++++++++
llvm/lib/IR/DebugProgramInstruction.cpp | 20 ++++++++++++++++++
llvm/lib/IR/Verifier.cpp | 4 ++++
llvm/test/Assembler/dbg_declare_value.ll | 19 +++++++++++++++++
16 files changed, 151 insertions(+), 5 deletions(-)
create mode 100644 llvm/test/Assembler/dbg_declare_value.ll
diff --git a/llvm/docs/SourceLevelDebugging.rst b/llvm/docs/SourceLevelDebugging.rst
index 12b5e3e549df7..0b1dc9bb1e7fe 100644
--- a/llvm/docs/SourceLevelDebugging.rst
+++ b/llvm/docs/SourceLevelDebugging.rst
@@ -308,6 +308,27 @@ 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 is the new value, and in the function entry block. The
+second argument is a :ref:`local variable <dilocalvariable>` containing a
+description of the variable. The third argument is a :ref:`complex expression
+<diexpression>`. The fourth argument is a :ref:`source location <dilocation>`.
+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 b0c5beae631ce..aa3d1d92548d7 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 4228ec9c3ef7a..49d9e686aeffa 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 862293c9666a7..922658927ad6a 100644
--- a/llvm/include/llvm/IR/DebugInfo.h
+++ b/llvm/include/llvm/IR/DebugInfo.h
@@ -44,6 +44,8 @@ class Module;
LLVM_ABI TinyPtrVector<DbgVariableRecord *> findDVRDeclares(Value *V);
/// As above, for DVRValues.
LLVM_ABI TinyPtrVector<DbgVariableRecord *> findDVRValues(Value *V);
+/// As above, for DVRDeclareValues.
+LLVM_ABI TinyPtrVector<DbgVariableRecord *> 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 66f44fe34d3f6..20f11b4fb8bac 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 ebca344ae7b93..ef1515ca10494 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 799234a0b491d..61d5c2c81df2e 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<LocType>(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 fe9e0ddca7091..911ec7501eb8b 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 8930d64de5e37..c2a0fb4f1ebfe 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<DIAssignID>(getFnMetadataByID(Record[Slot++]));
DIExpression *AddrExpr =
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index 76494c792ac7b..1d0461478b90c 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 94a1aa3087377..eebabfd772982 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 ca11ecf2f473e..b01860dba74a9 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 0f9d064857dc4..1859bc468cac9 100644
--- a/llvm/lib/IR/DebugInfo.cpp
+++ b/llvm/lib/IR/DebugInfo.cpp
@@ -62,6 +62,23 @@ TinyPtrVector<DbgVariableRecord *> llvm::findDVRDeclares(Value *V) {
return Declares;
}
+TinyPtrVector<DbgVariableRecord *> 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<DbgVariableRecord *> DEclareValues;
+ for (DbgVariableRecord *DVR : L->getAllDbgVariableRecordUsers())
+ if (DVR->getType() == DbgVariableRecord::LocationType::DeclareValue)
+ DEclareValues.push_back(DVR);
+
+ return DEclareValues;
+}
+
TinyPtrVector<DbgVariableRecord *> 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 6b1fd3907dc41..9efa3c71c0bb8 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 fa18c3cd0f404..678b06a48e9d4 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;
@@ -7058,6 +7061,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 0000000000000..1d5e4f7fe87a5
--- /dev/null
+++ b/llvm/test/Assembler/dbg_declare_value.ll
@@ -0,0 +1,19 @@
+; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis
+
+; CHECK: #dbg_declare_value(double %0, !{{[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
+}
+
+!9 = distinct !DISubprogram(name: "foo")
+!15 = !DILocalVariable(name: "x", arg: 1, scope: !9)
+!16 = !DILocation(line: 1, column: 17, scope: !9)
+!21 = !DILocation(line: 3, column: 1, scope: !9)
\ No newline at end of file
More information about the llvm-commits
mailing list