Add new llvm.dbg.declare_value intrinsic. (#168132)

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.

More information here:
https://discourse.llvm.org/t/rfc-introduce-new-llvm-dbg-coroframe-entry-intrinsic/88269

This is the first patch as part of a stack of patches, with the one
succeeding it being: https://github.com/llvm/llvm-project/pull/168134
This commit is contained in:
Shubham Sandeep Rastogi 2025-11-22 00:49:35 -08:00 committed by GitHub
parent ad7a5d4e05
commit 20ebc7ea82
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 170 additions and 5 deletions

View File

@ -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 <dilocalvariable>` containing a description of the
variable. The third argument is a :ref:`complex expression <diexpression>`. The
foruth 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::

View File

@ -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 {

View File

@ -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.

View File

@ -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

View File

@ -282,6 +282,7 @@ public:
Declare,
Value,
Assign,
DeclareValue,
End, ///< Marks the end of the concrete types.
Any, ///< To indicate all LocationTypes in searches.
@ -364,6 +365,13 @@ public:
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 @@ public:
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 @@ public:
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; }

View File

@ -1007,6 +1007,7 @@ lltok::Kind LLLexer::LexIdentifier() {
DBGRECORDTYPEKEYWORD(declare);
DBGRECORDTYPEKEYWORD(assign);
DBGRECORDTYPEKEYWORD(label);
DBGRECORDTYPEKEYWORD(declare_value);
#undef DBGRECORDTYPEKEYWORD
if (Keyword.starts_with("DIFlag")) {

View File

@ -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"))

View File

@ -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)

View File

@ -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 =

View File

@ -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()));

View File

@ -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;

View File

@ -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());

View File

@ -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.

View File

@ -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

View File

@ -190,6 +190,9 @@ private:
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);

View File

@ -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)