[flang-commits] [flang] [mlir] [mlir][LLVMIR] Avoid duplicate retained local variables (PR #209911)
via flang-commits
flang-commits at lists.llvm.org
Thu Jul 16 01:14:58 PDT 2026
https://github.com/qyingwu updated https://github.com/llvm/llvm-project/pull/209911
>From 818ce27934a2b483f0d0864243721b7492a55ddd Mon Sep 17 00:00:00 2001
From: qyingwu <qiyingwu at utexas.edu>
Date: Wed, 15 Jul 2026 14:58:04 -0700
Subject: [PATCH] [mlir][LLVMIR] Avoid duplicate retained local variables
Use recursive DILocalVariableAttr for retained locals that also appear in debug intrinsics. This avoids importing the same LLVM metadata node twice.
---
.../lib/Optimizer/Transforms/AddDebugInfo.cpp | 5 +-
.../Transforms/DebugTypeGenerator.cpp | 2 +-
.../mlir/Dialect/LLVMIR/LLVMAttrDefs.td | 26 ++++++++--
.../Dialect/LLVMIR/LLVMDialectBytecode.td | 4 +-
mlir/lib/CAPI/Dialect/LLVM.cpp | 7 +--
mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp | 19 ++++++++
mlir/lib/Target/LLVMIR/DebugImporter.cpp | 42 +++++++++++++----
mlir/lib/Target/LLVMIR/DebugImporter.h | 13 +++++
mlir/lib/Target/LLVMIR/DebugTranslation.cpp | 47 ++++++++++++++++++-
mlir/lib/Target/LLVMIR/DebugTranslation.h | 10 ++++
mlir/lib/Target/LLVMIR/ModuleImport.cpp | 33 +++++++++++++
mlir/test/Target/LLVMIR/Import/debug-info.ll | 33 +++++++++++++
mlir/test/Target/LLVMIR/llvmir-debug.mlir | 40 ++++++++++++++++
13 files changed, 260 insertions(+), 21 deletions(-)
diff --git a/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp b/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp
index 7c0b32e48832e..6004e5509e6e6 100644
--- a/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp
+++ b/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp
@@ -363,9 +363,8 @@ void AddDebugInfoPass::handleLocalVariable(Op declOp, llvm::StringRef name,
}
auto localVarAttr = mlir::LLVM::DILocalVariableAttr::get(
- context, scopeAttr, mlir::StringAttr::get(context, name), fileAttr,
- getLineFromLoc(declOp.getLoc()), argNo, /* alignInBits*/ 0, tyAttr,
- mlir::LLVM::DIFlags::Zero);
+ scopeAttr, name, fileAttr, getLineFromLoc(declOp.getLoc()), argNo,
+ /*alignInBits=*/0, tyAttr, mlir::LLVM::DIFlags::Zero);
declOp->setLoc(builder.getFusedLoc({declOp->getLoc()}, localVarAttr));
}
diff --git a/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp b/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp
index d45aee26470a7..b3f3186288685 100644
--- a/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp
+++ b/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp
@@ -114,7 +114,7 @@ mlir::LLVM::DILocalVariableAttr DebugTypeGenerator::generateArtificialVariable(
}
mlir::LLVM::DITypeAttr Ty = convertType(type, fileAttr, scope, declOp);
auto lvAttr = mlir::LLVM::DILocalVariableAttr::get(
- context, scope, name, fileAttr, /*line=*/0, /*argNo=*/0,
+ scope, name.getValue(), fileAttr, /*line=*/0, /*argNo=*/0,
/*alignInBits=*/0, Ty, mlir::LLVM::DIFlags::Artificial);
mlir::LLVM::DbgValueOp::create(builder, declOp.getLoc(), val, lvAttr,
nullptr);
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
index 6e2d4628c6173..fb17f1efb7c94 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
@@ -696,9 +696,14 @@ def LLVM_DILexicalBlockFile : LLVM_Attr<"DILexicalBlockFile", "di_lexical_block_
//===----------------------------------------------------------------------===//
def LLVM_DILocalVariableAttr : LLVM_Attr<"DILocalVariable", "di_local_variable",
- /*traits=*/[], "DINodeAttr"> {
+ [LLVM_DIRecursiveTypeAttrInterface],
+ "DINodeAttr"> {
let parameters = (ins
- "DIScopeAttr":$scope,
+ // DIRecursiveTypeAttrInterface specific parameters.
+ OptionalParameter<"DistinctAttr">:$recId,
+ OptionalParameter<"bool">:$isRecSelf,
+ // DILocalVariableAttr specific parameters.
+ OptionalParameter<"DIScopeAttr">:$scope,
OptionalParameter<"StringAttr">:$name,
OptionalParameter<"DIFileAttr">:$file,
OptionalParameter<"unsigned">:$line,
@@ -714,11 +719,24 @@ def LLVM_DILocalVariableAttr : LLVM_Attr<"DILocalVariable", "di_local_variable",
"DITypeAttr":$type, "DIFlags":$flags
), [{
MLIRContext *ctx = scope.getContext();
- return $_get(ctx, scope, StringAttr::get(ctx, name), file, line,
- arg, alignInBits, type, flags);
+ return $_get(ctx, /*recId=*/nullptr, /*isRecSelf=*/false, scope,
+ StringAttr::get(ctx, name), file, line, arg, alignInBits,
+ type, flags);
}]>
];
let assemblyFormat = "`<` struct(params) `>`";
+ let extraClassDeclaration = [{
+ /// Requirements of DIRecursiveTypeAttrInterface.
+ /// @{
+
+ /// Get a copy of this attr but with the recursive ID set to `recId`.
+ DIRecursiveTypeAttrInterface withRecId(DistinctAttr recId);
+
+ /// Build a rec-self instance using the provided `recId`.
+ static DIRecursiveTypeAttrInterface getRecSelf(DistinctAttr recId);
+
+ /// @}
+ }];
// Generate mnemonic alias for the attribute.
let genMnemonicAlias = 1;
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMDialectBytecode.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMDialectBytecode.td
index b25a8c8a9829e..ef7df3c204841 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMDialectBytecode.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMDialectBytecode.td
@@ -99,7 +99,9 @@ def DIFileAttr : DialectAttribute<(attr
//===----------------------------------------------------------------------===//
def DILocalVariableAttr : DialectAttribute<(attr
- Attr<"DIScopeAttr">:$scope,
+ OptionalAttribute<"DistinctAttr">:$recId,
+ Bool:$isRecSelf,
+ OptionalAttribute<"DIScopeAttr">:$scope,
OptionalAttribute<"StringAttr">:$name,
OptionalAttribute<"DIFileAttr">:$file,
VarInt:$line,
diff --git a/mlir/lib/CAPI/Dialect/LLVM.cpp b/mlir/lib/CAPI/Dialect/LLVM.cpp
index 8082de122e74e..d59fb3c86d903 100644
--- a/mlir/lib/CAPI/Dialect/LLVM.cpp
+++ b/mlir/lib/CAPI/Dialect/LLVM.cpp
@@ -414,9 +414,10 @@ MlirAttribute mlirLLVMDILocalVariableAttrGet(
MlirAttribute diFile, unsigned int line, unsigned int arg,
unsigned int alignInBits, MlirAttribute diType, int64_t flags) {
return wrap(DILocalVariableAttr::get(
- unwrap(ctx), cast<DIScopeAttr>(unwrap(scope)),
- cast<StringAttr>(unwrap(name)), cast<DIFileAttr>(unwrap(diFile)), line,
- arg, alignInBits, cast<DITypeAttr>(unwrap(diType)), DIFlags(flags)));
+ unwrap(ctx), /*recId=*/DistinctAttr{}, /*isRecSelf=*/false,
+ cast<DIScopeAttr>(unwrap(scope)), cast<StringAttr>(unwrap(name)),
+ cast<DIFileAttr>(unwrap(diFile)), line, arg, alignInBits,
+ cast<DITypeAttr>(unwrap(diType)), DIFlags(flags)));
}
MlirStringRef mlirLLVMDILocalVariableAttrGetName(void) {
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp
index 266faeca4553b..7917ac8b1ff10 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp
@@ -325,6 +325,25 @@ void printExpressionArg(AsmPrinter &printer, uint64_t opcode,
});
}
+//===----------------------------------------------------------------------===//
+// DILocalVariableAttr
+//===----------------------------------------------------------------------===//
+
+DIRecursiveTypeAttrInterface
+DILocalVariableAttr::withRecId(DistinctAttr recId) {
+ return DILocalVariableAttr::get(
+ getContext(), recId, getIsRecSelf(), getScope(), getName(), getFile(),
+ getLine(), getArg(), getAlignInBits(), getType(), getFlags());
+}
+
+DIRecursiveTypeAttrInterface
+DILocalVariableAttr::getRecSelf(DistinctAttr recId) {
+ return DILocalVariableAttr::get(
+ recId.getContext(), recId, /*isRecSelf=*/true, /*scope=*/{},
+ /*name=*/{}, /*file=*/{}, /*line=*/0, /*arg=*/0, /*alignInBits=*/0,
+ /*type=*/{}, DIFlags::Zero);
+}
+
//===----------------------------------------------------------------------===//
// DICompositeTypeAttr
//===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Target/LLVMIR/DebugImporter.cpp b/mlir/lib/Target/LLVMIR/DebugImporter.cpp
index 85df880d2712a..f8ccbc5bb6637 100644
--- a/mlir/lib/Target/LLVMIR/DebugImporter.cpp
+++ b/mlir/lib/Target/LLVMIR/DebugImporter.cpp
@@ -17,6 +17,7 @@
#include "llvm/IR/Constants.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/Metadata.h"
+#include "llvm/Support/SaveAndRestore.h"
using namespace mlir;
using namespace mlir::LLVM;
@@ -193,13 +194,21 @@ DebugImporter::translateImpl(llvm::DIGlobalVariable *node) {
DILocalVariableAttr DebugImporter::translateImpl(llvm::DILocalVariable *node) {
// Return nullptr if the scope or type is a cyclic dependency.
- DIScopeAttr scope = translate(node->getScope());
+ DIScopeAttr scope;
+ {
+ bool preferLocalVariableCycleBreakForNode =
+ static_cast<bool>(nodeToRecId.lookup(node));
+ llvm::SaveAndRestore preferLocalVariableCycleBreakGuard(
+ preferLocalVariableCycleBreak, preferLocalVariableCycleBreakForNode);
+ scope = translate(node->getScope());
+ }
if (node->getScope() && !scope)
return nullptr;
return DILocalVariableAttr::get(
- context, scope, getStringAttrOrNull(node->getRawName()),
- translate(node->getFile()), node->getLine(), node->getArg(),
- node->getAlignInBits(), translate(node->getType()),
+ context, /*recId=*/DistinctAttr{}, /*isRecSelf=*/false, scope,
+ getStringAttrOrNull(node->getRawName()), translate(node->getFile()),
+ node->getLine(), node->getArg(), node->getAlignInBits(),
+ translate(node->getType()),
symbolizeDIFlags(node->getFlags()).value_or(DIFlags::Zero));
}
@@ -261,6 +270,11 @@ DISubprogramAttr DebugImporter::translateImpl(llvm::DISubprogram *node) {
// Convert the retained nodes but drop all of them if one of them is invalid.
SmallVector<Attribute> retainedNodes;
auto add = [this, &retainedNodes](llvm::DINode *retainedNode) {
+ if (auto *local = dyn_cast<llvm::DILocalVariable>(retainedNode);
+ local && nodeToRecId.contains(local)) {
+ retainedNodes.push_back(*createRecSelf(local));
+ return;
+ }
retainedNodes.push_back(translate(retainedNode));
};
auto addGVE = [](llvm::DIGlobalVariableExpression *GVE) {
@@ -446,6 +460,9 @@ DINodeAttr DebugImporter::translate(llvm::DINode *node) {
DistinctAttr recId = nodeToRecId.lookup(node);
auto recType = cast<DIRecursiveTypeAttrInterface>(attr);
attr = cast<DINodeAttr>(recType.withRecId(recId));
+ } else if (DistinctAttr recId = nodeToRecId.lookup(node)) {
+ auto recType = cast<DIRecursiveTypeAttrInterface>(attr);
+ attr = cast<DINodeAttr>(recType.withRecId(recId));
}
cacheEntry.resolve(attr);
return attr;
@@ -454,22 +471,31 @@ DINodeAttr DebugImporter::translate(llvm::DINode *node) {
return nullptr;
}
+void DebugImporter::markLocalVariableAsRecursive(llvm::DILocalVariable *node) {
+ if (!nodeToRecId.contains(node))
+ nodeToRecId[node] = DistinctAttr::create(UnitAttr::get(context));
+}
+
/// Get the `getRecSelf` constructor for the translated node if it participates
-/// in CyclicReplacerCache cycle breaking (recursive composite types,
-/// subprograms, or compile units).
-static function_ref<DIRecursiveTypeAttrInterface(DistinctAttr)>
-getRecSelfConstructor(llvm::DINode *node) {
+/// in CyclicReplacerCache cycle breaking.
+function_ref<DIRecursiveTypeAttrInterface(DistinctAttr)>
+DebugImporter::getRecSelfConstructor(llvm::DINode *node) {
using CtorType = function_ref<DIRecursiveTypeAttrInterface(DistinctAttr)>;
return TypeSwitch<llvm::DINode *, CtorType>(node)
.Case([&](llvm::DICompositeType *) {
return CtorType(DICompositeTypeAttr::getRecSelf);
})
.Case([&](llvm::DISubprogram *) {
+ if (preferLocalVariableCycleBreak)
+ return CtorType();
return CtorType(DISubprogramAttr::getRecSelf);
})
.Case([&](llvm::DICompileUnit *) {
return CtorType(DICompileUnitAttr::getRecSelf);
})
+ .Case([&](llvm::DILocalVariable *) {
+ return CtorType(DILocalVariableAttr::getRecSelf);
+ })
.Default(CtorType());
}
diff --git a/mlir/lib/Target/LLVMIR/DebugImporter.h b/mlir/lib/Target/LLVMIR/DebugImporter.h
index b224ad3376be7..40bf0b113e28f 100644
--- a/mlir/lib/Target/LLVMIR/DebugImporter.h
+++ b/mlir/lib/Target/LLVMIR/DebugImporter.h
@@ -47,6 +47,9 @@ class DebugImporter {
/// Returns UnknownLoc if `func` has no debug information attached to it.
Location translateFuncLocation(llvm::Function *func);
+ /// Marks the local variable as requiring a recursive representation.
+ void markLocalVariableAsRecursive(llvm::DILocalVariable *node);
+
/// Translates the given LLVM debug metadata to MLIR.
DINodeAttr translate(llvm::DINode *node);
@@ -92,6 +95,11 @@ class DebugImporter {
/// for it, or create a new one if not.
DistinctAttr getOrCreateDistinctID(llvm::DINode *node);
+ /// Get the `getRecSelf` constructor for the translated node if it
+ /// participates in CyclicReplacerCache cycle breaking.
+ function_ref<DIRecursiveTypeAttrInterface(DistinctAttr)>
+ getRecSelfConstructor(llvm::DINode *node);
+
std::optional<DINodeAttr> createRecSelf(llvm::DINode *node);
/// A mapping between distinct LLVM debug metadata nodes and the corresponding
@@ -103,6 +111,11 @@ class DebugImporter {
/// same ID and be deduplicated.
DenseMap<llvm::DINode *, DistinctAttr> nodeToRecId;
+ /// True while translating the scope of a DILocalVariable. This lets a
+ /// local-variable/subprogram cycle be broken at the local variable instead of
+ /// at the subprogram.
+ bool preferLocalVariableCycleBreak = false;
+
CyclicReplacerCache<llvm::DINode *, DINodeAttr> cache;
MLIRContext *context;
diff --git a/mlir/lib/Target/LLVMIR/DebugTranslation.cpp b/mlir/lib/Target/LLVMIR/DebugTranslation.cpp
index 442f5b6b137e6..473935fcf38d3 100644
--- a/mlir/lib/Target/LLVMIR/DebugTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/DebugTranslation.cpp
@@ -16,6 +16,7 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
+#include "llvm/Support/SaveAndRestore.h"
using namespace mlir;
using namespace mlir::LLVM;
@@ -329,6 +330,20 @@ DebugTranslation::translateImpl(DILocalVariableAttr attr) {
/*Annotations=*/nullptr);
}
+llvm::TempDILocalVariable
+DebugTranslation::translateTemporaryImpl(DILocalVariableAttr attr) {
+ llvm::DILocalScope *scope =
+ attr.getScope() ? cast<llvm::DILocalScope>(translate(attr.getScope()))
+ : localVariablePlaceholderScope;
+ assert(scope && "expected local variable placeholder scope");
+ return llvm::DILocalVariable::getTemporary(
+ llvmCtx, scope, /*Name=*/"", /*File=*/nullptr, attr.getLine(),
+ /*Type=*/static_cast<llvm::DIType *>(nullptr), attr.getArg(),
+ static_cast<llvm::DINode::DIFlags>(attr.getFlags()),
+ attr.getAlignInBits(),
+ /*Annotations=*/nullptr);
+}
+
llvm::DIGlobalVariable *
DebugTranslation::translateImpl(DIGlobalVariableAttr attr) {
return llvm::DIGlobalVariable::getDistinct(
@@ -345,6 +360,24 @@ DebugTranslation::translateRecursive(DIRecursiveTypeAttrInterface attr) {
iter != recursiveNodeMap.end()) {
return iter->second;
}
+
+ if (auto localVariable = dyn_cast<DILocalVariableAttr>(attr)) {
+ if (localVariable.getIsRecSelf()) {
+ auto iter =
+ forwardLocalVariablePlaceholders
+ .try_emplace(recursiveId, translateTemporaryImpl(localVariable))
+ .first;
+ return iter->second.get();
+ }
+ if (auto iter = forwardLocalVariablePlaceholders.find(recursiveId);
+ iter != forwardLocalVariablePlaceholders.end()) {
+ llvm::DILocalVariable *concrete = translateImpl(localVariable);
+ iter->second->replaceAllUsesWith(concrete);
+ forwardLocalVariablePlaceholders.erase(iter);
+ return concrete;
+ }
+ }
+
assert(!attr.getIsRecSelf() && "unbound DI recursive self reference");
auto setRecursivePlaceholder = [&](llvm::DINode *placeholder) {
@@ -377,6 +410,13 @@ DebugTranslation::translateRecursive(DIRecursiveTypeAttrInterface attr) {
auto *concrete = translateImpl(attr);
temporary->replaceAllUsesWith(concrete);
return concrete;
+ })
+ .Case([&](DILocalVariableAttr attr) {
+ auto temporary = translateTemporaryImpl(attr);
+ setRecursivePlaceholder(temporary.get());
+ auto *concrete = translateImpl(attr);
+ temporary->replaceAllUsesWith(concrete);
+ return concrete;
});
assert(recursiveNodeMap.back().first == recursiveId &&
@@ -417,10 +457,15 @@ llvm::DISubprogram *DebugTranslation::translateImpl(DISubprogramAttr attr) {
/*ThisAdjustment=*/0, llvm::DINode::FlagZero,
static_cast<llvm::DISubprogram::DISPFlags>(attr.getSubprogramFlags()),
compileUnit, /*TemplateParams=*/nullptr, /*Declaration=*/nullptr,
- getRetainedNodesOrNull(attr.getRetainedNodes()), nullptr,
+ /*RetainedNodes=*/nullptr, /*ThrownTypes=*/nullptr,
getMDTupleOrNull(attr.getAnnotations()));
if (attr.getId())
distinctAttrToNode.try_emplace(attr.getId(), node);
+ if (!attr.getRetainedNodes().empty()) {
+ llvm::SaveAndRestore guard(localVariablePlaceholderScope,
+ cast<llvm::DILocalScope>(node));
+ node->replaceRetainedNodes(getRetainedNodesOrNull(attr.getRetainedNodes()));
+ }
return node;
}
diff --git a/mlir/lib/Target/LLVMIR/DebugTranslation.h b/mlir/lib/Target/LLVMIR/DebugTranslation.h
index c1fdbd89087da..2db7f401f576d 100644
--- a/mlir/lib/Target/LLVMIR/DebugTranslation.h
+++ b/mlir/lib/Target/LLVMIR/DebugTranslation.h
@@ -105,6 +105,7 @@ class DebugTranslation {
llvm::TempDICompositeType translateTemporaryImpl(DICompositeTypeAttr attr);
llvm::TempDISubprogram translateTemporaryImpl(DISubprogramAttr attr);
llvm::TempDICompileUnit translateTemporaryImpl(DICompileUnitAttr attr);
+ llvm::TempDILocalVariable translateTemporaryImpl(DILocalVariableAttr attr);
/// Constructs a string metadata node from the string attribute. Returns
/// nullptr if `stringAttr` is null or contains and empty string.
@@ -135,6 +136,15 @@ class DebugTranslation {
/// A mapping between recursive ID and the translated DINode.
llvm::MapVector<DistinctAttr, llvm::DINode *> recursiveNodeMap;
+ /// A mapping between recursive IDs and local-variable placeholders created
+ /// from forward self references in retainedNodes.
+ DenseMap<DistinctAttr, llvm::TempDILocalVariable>
+ forwardLocalVariablePlaceholders;
+
+ /// Scope to use for forward local-variable placeholders while translating a
+ /// DISubprogram's retainedNodes.
+ llvm::DILocalScope *localVariablePlaceholderScope = nullptr;
+
/// A mapping between a distinct ID and the translated LLVM metadata node.
/// This helps identify attrs that should translate into the same LLVM debug
/// node.
diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
index bb55678f91005..fc4ad29d2d747 100644
--- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
@@ -35,6 +35,7 @@
#include "llvm/ADT/TypeSwitch.h"
#include "llvm/IR/Comdat.h"
#include "llvm/IR/Constants.h"
+#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/DebugProgramInstruction.h"
#include "llvm/IR/InlineAsm.h"
#include "llvm/IR/InstIterator.h"
@@ -246,6 +247,38 @@ ModuleImport::ModuleImport(ModuleOp mlirModule,
emitExpensiveWarnings(emitExpensiveWarnings),
preferUnregisteredIntrinsics(preferUnregisteredIntrinsics) {
builder.setInsertionPointToStart(mlirModule.getBody());
+
+ // Variables that are both retained and used by a debug intrinsic need to
+ // break cycles at the variable itself to avoid creating duplicate attrs with
+ // different subprogram scopes.
+ llvm::SmallPtrSet<llvm::DILocalVariable *, 8> retainedLocalVariables;
+ for (llvm::Function &func : *this->llvmModule) {
+ llvm::DISubprogram *subprogram = func.getSubprogram();
+ if (!subprogram)
+ continue;
+ auto addLocalVariable = [&](llvm::DILocalVariable *localVariable) {
+ retainedLocalVariables.insert(localVariable);
+ };
+ auto skip = [](auto *) {};
+ subprogram->forEachRetainedNode(addLocalVariable, skip, skip, skip, skip);
+ }
+
+ auto markIfRetained = [&](llvm::DILocalVariable *localVariable) {
+ if (localVariable && retainedLocalVariables.contains(localVariable))
+ debugImporter->markLocalVariableAsRecursive(localVariable);
+ };
+ for (llvm::Function &func : *this->llvmModule) {
+ for (llvm::Instruction &inst : llvm::instructions(func)) {
+ if (auto *dbgIntrinsic = dyn_cast<llvm::DbgVariableIntrinsic>(&inst))
+ markIfRetained(dbgIntrinsic->getVariable());
+ if (!inst.DebugMarker)
+ continue;
+ for (llvm::DbgRecord &dbgRecord : inst.DebugMarker->getDbgRecordRange())
+ if (auto *dbgVariableRecord =
+ dyn_cast<llvm::DbgVariableRecord>(&dbgRecord))
+ markIfRetained(dbgVariableRecord->getVariable());
+ }
+ }
}
ComdatOp ModuleImport::getGlobalComdatOp() {
diff --git a/mlir/test/Target/LLVMIR/Import/debug-info.ll b/mlir/test/Target/LLVMIR/Import/debug-info.ll
index 28c25809002cd..38b250f00b958 100644
--- a/mlir/test/Target/LLVMIR/Import/debug-info.ll
+++ b/mlir/test/Target/LLVMIR/Import/debug-info.ll
@@ -1046,3 +1046,36 @@ define void @fn_with_static_local() !dbg !3 {
!12 = !DILocalVariable(name: "unused_local", scope: !3, file: !4, line: 122, type: !9)
!13 = !DISubroutineType(types: !14)
!14 = !{null}
+
+; // -----
+
+; Test that a DILocalVariable listed in DISubprogram's retainedNodes and used by
+; a debug intrinsic does not get duplicated after import.
+
+; CHECK-DAG: #[[LVAR_SELF:.+]] = #llvm.di_local_variable<recId = [[REC_ID:distinct.+<>]], isRecSelf = true>
+; CHECK-DAG: #[[SP:.+]] = #llvm.di_subprogram<{{.*}}name = "fn_with_retained_local"{{.*}}retainedNodes = [#[[LVAR_SELF]]]>
+; CHECK-DAG: #[[LVAR:.+]] = #llvm.di_local_variable<recId = [[REC_ID]], scope = #[[SP]], name = "n"{{.*}}>
+; CHECK-DAG-COUNT-2: {{#llvm\.di_local_variable<}}
+; CHECK: llvm.intr.dbg.value #[[LVAR]] = %{{.*}} : i32
+
+define void @fn_with_retained_local(i32 %n) !dbg !3 {
+entry:
+ call void @llvm.dbg.value(metadata i32 %n, metadata !12, metadata !DIExpression()), !dbg !15
+ ret void
+}
+
+declare void @llvm.dbg.value(metadata, metadata, metadata)
+
+!llvm.dbg.cu = !{!7}
+!llvm.module.flags = !{!10}
+
+!3 = distinct !DISubprogram(name: "fn_with_retained_local", scope: !4, file: !4, line: 1, type: !13, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !7, retainedNodes: !11)
+!4 = !DIFile(filename: "test.c", directory: "")
+!7 = distinct !DICompileUnit(language: DW_LANG_C, file: !4)
+!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!10 = !{i32 2, !"Debug Info Version", i32 3}
+!11 = !{!12}
+!12 = !DILocalVariable(name: "n", arg: 1, scope: !3, file: !4, line: 1, type: !9)
+!13 = !DISubroutineType(types: !14)
+!14 = !{null, !9}
+!15 = !DILocation(line: 1, column: 1, scope: !3)
diff --git a/mlir/test/Target/LLVMIR/llvmir-debug.mlir b/mlir/test/Target/LLVMIR/llvmir-debug.mlir
index 72dadb2602c52..77eade68ae9e3 100644
--- a/mlir/test/Target/LLVMIR/llvmir-debug.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir-debug.mlir
@@ -421,6 +421,46 @@ llvm.func @imp_fn() {
// -----
+// Test that a recursive local variable retained by a subprogram is exported
+// without duplicating the DILocalVariable metadata node.
+
+#file = #llvm.di_file<"test.c" in "">
+#cu = #llvm.di_compile_unit<
+ id = distinct[0]<>, sourceLanguage = DW_LANG_C, file = #file,
+ isOptimized = false, emissionKind = Full
+>
+#void = #llvm.di_null_type
+#void_type = #llvm.di_subroutine_type<types = #void>
+#int = #llvm.di_basic_type<
+ tag = DW_TAG_base_type, name = "int",
+ sizeInBits = 32, encoding = DW_ATE_signed
+>
+#local_self = #llvm.di_local_variable<recId = distinct[1]<>, isRecSelf = true>
+#sp = #llvm.di_subprogram<
+ id = distinct[2]<>, compileUnit = #cu, scope = #file,
+ name = "fn_with_retained_local", file = #file,
+ subprogramFlags = Definition, type = #void_type,
+ retainedNodes = [#local_self]
+>
+#local = #llvm.di_local_variable<
+ recId = distinct[1]<>, scope = #sp, name = "n", file = #file,
+ line = 1, arg = 1, type = #int
+>
+
+// CHECK-LABEL: define void @fn_with_retained_local(
+// CHECK-SAME: i32 %[[ARG:.*]]) !dbg ![[SP:[0-9]+]]
+llvm.func @fn_with_retained_local(%arg0: i32) {
+ // RECORDS: #dbg_value(i32 %[[ARG]], ![[LOCAL:[0-9]+]], !DIExpression(), !{{.*}})
+ llvm.intr.dbg.value #local = %arg0 : i32 loc(fused<#sp>["test.c":1:1])
+ llvm.return
+} loc(fused<#sp>["test.c":1:1])
+
+// CHECK-DAG: ![[SP]] = distinct !DISubprogram(name: "fn_with_retained_local"{{.*}}retainedNodes: ![[NODES:[0-9]+]])
+// CHECK-DAG: ![[LOCAL]] = !DILocalVariable(name: "n", arg: 1, scope: ![[SP]], file: !{{.*}}, line: 1, type: !{{.*}})
+// CHECK-DAG: ![[NODES]] = !{![[LOCAL]]}
+
+// -----
+
// Nameless and scopeless global constant.
// CHECK-LABEL: @.str.1 = external constant [10 x i8]
More information about the flang-commits
mailing list