Skip to content

Conversation

abidh
Copy link
Contributor

@abidh abidh commented Aug 22, 2025

Currently the DbgDeclareOP/DbgValueOP/DbgLabelOp are first converted to llvm debug intrinsics which are later translated to debug records by a call of convertToNewDbgValues. This is not only inefficient but also makes the code that works on intermediate IR unnecessarily complicated. The debug intrinsics are also being phased out. This PR converts these Ops directly to debug records.

The conversion is relatively simple but there is a bit of code repetition due to how the APIs in the DIBuilders are named. There are few cast<> which I would like to do without but could not see a good way around them. Any suggestions welcome here. Also noticed that DISubprogramAttr is inherited from DIScopeAttr while in llvm, the DISubprogram inherits from DILocalScope. I am going to fix this separately and then we could use FusedLocWith<LLVM::DILocalScopeAttr> and cast to DILocalScope will be much safer.

As the output remains the same, the existing tests cover this change. I also ran the GDB tests with flang and there was no regression.

Currently the DbgIntrOp are first converted to llvm intrinsics
which are laterr translated to debug records by a call of
convertToNewDbgValues. This is not only inefficient but also makes
the code that works on intermediate IR unnecessarily complicated. This
PR converts the DbgIntrOp directly to debug records.

The conversion is relatively simple but there is a bit of code
repetition due to how the APIs in the DIBuilders are named. Also noticed
that DISubprogramAttr is inherited from DIScopeAttr while in llvm, the
DISubprogram inherits from DILocalScope. I am going to fix this
separately and then we could use FusedLocWith<LLVM::DILocalScopeAttr>
and cast to <DILocalScope> will be much safer.
@llvmbot
Copy link
Member

llvmbot commented Aug 22, 2025

@llvm/pr-subscribers-mlir

Author: Abid Qadeer (abidh)

Changes

Currently the DbgDeclareOP/DbgValueOP/DbgLabelOp are first converted to llvm debug intrinsics which are later translated to debug records by a call of convertToNewDbgValues. This is not only inefficient but also makes the code that works on intermediate IR unnecessarily complicated. The debug intrinsics are also being phased out. This PR converts these Ops directly to debug records.

The conversion is relatively simple but there is a bit of code repetition due to how the APIs in the DIBuilders are named. There are few cast&lt;&gt; which I would like to do without but could not see a good way around them. Any suggestions welcome here. Also noticed that DISubprogramAttr is inherited from DIScopeAttr while in llvm, the DISubprogram inherits from DILocalScope. I am going to fix this separately and then we could use FusedLocWith&lt;LLVM::DILocalScopeAttr&gt; and cast to DILocalScope will be much safer.

As the output remains the same, the existing tests cover this change. I also ran the GDB tests with flang and there was no regression.


Full diff: https://github.com/llvm/llvm-project/pull/154926.diff

3 Files Affected:

  • (modified) mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td (+59-22)
  • (modified) mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp (+1)
  • (modified) mlir/lib/Target/LLVMIR/ModuleTranslation.cpp (-5)
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td
index 5e2461b4508e4..f964eb079a725 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td
@@ -673,22 +673,6 @@ def LLVM_CoroPromiseOp : LLVM_IntrOp<"coro.promise", [], [], [], 1> {
 
 class LLVM_DbgIntrOp<string name, string argName, list<Trait> traits = []>
     : LLVM_IntrOp<name, [], [], traits, 0> {
-  let llvmBuilder = [{
-    // Debug intrinsics without debug locations are invalid.
-    if(!builder.getCurrentDebugLocation())
-      return success();
-    llvm::Module *module = builder.GetInsertBlock()->getModule();
-    llvm::LLVMContext &ctx = module->getContext();
-    llvm::Function *fn =
-      llvm::Intrinsic::getOrInsertDeclaration(module, llvm::Intrinsic::}]
-       # !subst(".", "_", name) # [{);
-    builder.CreateCall(fn, {
-        llvm::MetadataAsValue::get(ctx,
-            llvm::ValueAsMetadata::get(moduleTranslation.lookupValue(opInst.getOperand(0)))),
-        llvm::MetadataAsValue::get(ctx, moduleTranslation.translateDebugInfo($varInfo)),
-        llvm::MetadataAsValue::get(ctx, moduleTranslation.translateExpression($locationExpr)),
-      });
-  }];
   let mlirBuilder = [{
     // Add debug intrindic to the list of intrinsics that need to be converted once the
     // full function was converted.
@@ -711,6 +695,28 @@ def LLVM_DbgDeclareOp : LLVM_DbgIntrOp<"dbg.declare", "addr", [
     LLVM_DILocalVariableAttr:$varInfo,
     DefaultValuedAttr<LLVM_DIExpressionAttr, "{}">:$locationExpr
   );
+  let llvmBuilder = [{
+    // Debug intrinsics without debug locations are invalid.
+    if(!builder.getCurrentDebugLocation())
+      return success();
+    llvm::Function *parentFn = builder.GetInsertBlock()->getParent();
+    llvm::DILocalScope *scope;
+    if (auto scopeLoc =
+            opInst.getLoc()->findInstanceOf<FusedLocWith<LLVM::DIScopeAttr>>())
+      scope = llvm::cast<llvm::DILocalScope>(
+          moduleTranslation.translateDebugInfo(scopeLoc.getMetadata()));
+    else
+      scope = parentFn->getSubprogram();
+
+    llvm::Module *module = builder.GetInsertBlock()->getModule();
+    llvm::DIBuilder DB(*module);
+    DB.insertDeclare(moduleTranslation.lookupValue(opInst.getOperand(0)),
+                     llvm::cast<llvm::DILocalVariable>(
+                         moduleTranslation.translateDebugInfo($varInfo)),
+                     moduleTranslation.translateExpression($locationExpr),
+                     moduleTranslation.translateLoc(opInst.getLoc(), scope),
+                     builder.GetInsertPoint());
+  }];
 }
 
 def LLVM_DbgValueOp : LLVM_DbgIntrOp<"dbg.value", "value",
@@ -721,6 +727,29 @@ def LLVM_DbgValueOp : LLVM_DbgIntrOp<"dbg.value", "value",
     LLVM_DILocalVariableAttr:$varInfo,
     DefaultValuedAttr<LLVM_DIExpressionAttr, "{}">:$locationExpr
   );
+  let llvmBuilder = [{
+    // Debug intrinsics without debug locations are invalid.
+    if(!builder.getCurrentDebugLocation())
+      return success();
+    llvm::Function *parentFn = builder.GetInsertBlock()->getParent();
+    llvm::DILocalScope *scope;
+    if (auto scopeLoc =
+            opInst.getLoc()->findInstanceOf<FusedLocWith<LLVM::DIScopeAttr>>())
+      scope = llvm::cast<llvm::DILocalScope>(
+          moduleTranslation.translateDebugInfo(scopeLoc.getMetadata()));
+    else
+      scope = parentFn->getSubprogram();
+
+    llvm::Module *module = builder.GetInsertBlock()->getModule();
+    llvm::DIBuilder DB(*module);
+    DB.insertDbgValueIntrinsic(
+        moduleTranslation.lookupValue(opInst.getOperand(0)),
+        llvm::cast<llvm::DILocalVariable>(
+            moduleTranslation.translateDebugInfo($varInfo)),
+        moduleTranslation.translateExpression($locationExpr),
+        moduleTranslation.translateLoc(opInst.getLoc(), scope),
+        builder.GetInsertPoint());
+  }];
 }
 
 def LLVM_DbgLabelOp : LLVM_IntrOp<"dbg.label", [], [], [], 0> {
@@ -730,13 +759,21 @@ def LLVM_DbgLabelOp : LLVM_IntrOp<"dbg.label", [], [], [], 0> {
     // Debug intrinsics without debug locations are invalid.
     if(!builder.getCurrentDebugLocation())
       return success();
+    llvm::Function *parentFn = builder.GetInsertBlock()->getParent();
+    llvm::DILocalScope *scope;
+    if (auto scopeLoc =
+            opInst.getLoc()->findInstanceOf<FusedLocWith<LLVM::DIScopeAttr>>())
+      scope = llvm::cast<llvm::DILocalScope>(
+          moduleTranslation.translateDebugInfo(scopeLoc.getMetadata()));
+    else
+      scope = parentFn->getSubprogram();
+
     llvm::Module *module = builder.GetInsertBlock()->getModule();
-    llvm::LLVMContext &ctx = module->getContext();
-    llvm::Function *fn =
-      llvm::Intrinsic::getOrInsertDeclaration(module, llvm::Intrinsic::dbg_label);
-    builder.CreateCall(fn, {
-        llvm::MetadataAsValue::get(ctx, moduleTranslation.translateDebugInfo($label))
-      });
+    llvm::DIBuilder DB(*module);
+    DB.insertLabel(
+        llvm::cast<llvm::DILabel>(moduleTranslation.translateDebugInfo($label)),
+        moduleTranslation.translateLoc(opInst.getLoc(), scope),
+        builder.GetInsertPoint());
   }];
   let mlirBuilder = [{
     DILabelAttr labelAttr = $_label_attr($label);
diff --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
index 0f675a0a5df5d..611d8d1960e0e 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
@@ -18,6 +18,7 @@
 #include "mlir/Target/LLVMIR/ModuleTranslation.h"
 
 #include "llvm/ADT/TypeSwitch.h"
+#include "llvm/IR/DIBuilder.h"
 #include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/InlineAsm.h"
 #include "llvm/IR/Instructions.h"
diff --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
index b3a06e2be7fe6..d9aae529d19b6 100644
--- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
@@ -2407,11 +2407,6 @@ mlir::translateModuleToLLVMIR(Operation *module, llvm::LLVMContext &llvmContext,
   if (failed(translator.convertUnresolvedBlockAddress()))
     return nullptr;
 
-  // Once we've finished constructing elements in the module, we should convert
-  // it to use the debug info format desired by LLVM.
-  // See https://llvm.org/docs/RemoveDIsDebugInfo.html
-  translator.llvmModule->convertToNewDbgValues();
-
   // Add the necessary debug info module flags, if they were not encoded in MLIR
   // beforehand.
   translator.debugTranslation->addModuleFlagsIfNotPresent();

@llvmbot
Copy link
Member

llvmbot commented Aug 22, 2025

@llvm/pr-subscribers-mlir-llvm

Author: Abid Qadeer (abidh)

Changes

Currently the DbgDeclareOP/DbgValueOP/DbgLabelOp are first converted to llvm debug intrinsics which are later translated to debug records by a call of convertToNewDbgValues. This is not only inefficient but also makes the code that works on intermediate IR unnecessarily complicated. The debug intrinsics are also being phased out. This PR converts these Ops directly to debug records.

The conversion is relatively simple but there is a bit of code repetition due to how the APIs in the DIBuilders are named. There are few cast&lt;&gt; which I would like to do without but could not see a good way around them. Any suggestions welcome here. Also noticed that DISubprogramAttr is inherited from DIScopeAttr while in llvm, the DISubprogram inherits from DILocalScope. I am going to fix this separately and then we could use FusedLocWith&lt;LLVM::DILocalScopeAttr&gt; and cast to DILocalScope will be much safer.

As the output remains the same, the existing tests cover this change. I also ran the GDB tests with flang and there was no regression.


Full diff: https://github.com/llvm/llvm-project/pull/154926.diff

3 Files Affected:

  • (modified) mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td (+59-22)
  • (modified) mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp (+1)
  • (modified) mlir/lib/Target/LLVMIR/ModuleTranslation.cpp (-5)
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td
index 5e2461b4508e4..f964eb079a725 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td
@@ -673,22 +673,6 @@ def LLVM_CoroPromiseOp : LLVM_IntrOp<"coro.promise", [], [], [], 1> {
 
 class LLVM_DbgIntrOp<string name, string argName, list<Trait> traits = []>
     : LLVM_IntrOp<name, [], [], traits, 0> {
-  let llvmBuilder = [{
-    // Debug intrinsics without debug locations are invalid.
-    if(!builder.getCurrentDebugLocation())
-      return success();
-    llvm::Module *module = builder.GetInsertBlock()->getModule();
-    llvm::LLVMContext &ctx = module->getContext();
-    llvm::Function *fn =
-      llvm::Intrinsic::getOrInsertDeclaration(module, llvm::Intrinsic::}]
-       # !subst(".", "_", name) # [{);
-    builder.CreateCall(fn, {
-        llvm::MetadataAsValue::get(ctx,
-            llvm::ValueAsMetadata::get(moduleTranslation.lookupValue(opInst.getOperand(0)))),
-        llvm::MetadataAsValue::get(ctx, moduleTranslation.translateDebugInfo($varInfo)),
-        llvm::MetadataAsValue::get(ctx, moduleTranslation.translateExpression($locationExpr)),
-      });
-  }];
   let mlirBuilder = [{
     // Add debug intrindic to the list of intrinsics that need to be converted once the
     // full function was converted.
@@ -711,6 +695,28 @@ def LLVM_DbgDeclareOp : LLVM_DbgIntrOp<"dbg.declare", "addr", [
     LLVM_DILocalVariableAttr:$varInfo,
     DefaultValuedAttr<LLVM_DIExpressionAttr, "{}">:$locationExpr
   );
+  let llvmBuilder = [{
+    // Debug intrinsics without debug locations are invalid.
+    if(!builder.getCurrentDebugLocation())
+      return success();
+    llvm::Function *parentFn = builder.GetInsertBlock()->getParent();
+    llvm::DILocalScope *scope;
+    if (auto scopeLoc =
+            opInst.getLoc()->findInstanceOf<FusedLocWith<LLVM::DIScopeAttr>>())
+      scope = llvm::cast<llvm::DILocalScope>(
+          moduleTranslation.translateDebugInfo(scopeLoc.getMetadata()));
+    else
+      scope = parentFn->getSubprogram();
+
+    llvm::Module *module = builder.GetInsertBlock()->getModule();
+    llvm::DIBuilder DB(*module);
+    DB.insertDeclare(moduleTranslation.lookupValue(opInst.getOperand(0)),
+                     llvm::cast<llvm::DILocalVariable>(
+                         moduleTranslation.translateDebugInfo($varInfo)),
+                     moduleTranslation.translateExpression($locationExpr),
+                     moduleTranslation.translateLoc(opInst.getLoc(), scope),
+                     builder.GetInsertPoint());
+  }];
 }
 
 def LLVM_DbgValueOp : LLVM_DbgIntrOp<"dbg.value", "value",
@@ -721,6 +727,29 @@ def LLVM_DbgValueOp : LLVM_DbgIntrOp<"dbg.value", "value",
     LLVM_DILocalVariableAttr:$varInfo,
     DefaultValuedAttr<LLVM_DIExpressionAttr, "{}">:$locationExpr
   );
+  let llvmBuilder = [{
+    // Debug intrinsics without debug locations are invalid.
+    if(!builder.getCurrentDebugLocation())
+      return success();
+    llvm::Function *parentFn = builder.GetInsertBlock()->getParent();
+    llvm::DILocalScope *scope;
+    if (auto scopeLoc =
+            opInst.getLoc()->findInstanceOf<FusedLocWith<LLVM::DIScopeAttr>>())
+      scope = llvm::cast<llvm::DILocalScope>(
+          moduleTranslation.translateDebugInfo(scopeLoc.getMetadata()));
+    else
+      scope = parentFn->getSubprogram();
+
+    llvm::Module *module = builder.GetInsertBlock()->getModule();
+    llvm::DIBuilder DB(*module);
+    DB.insertDbgValueIntrinsic(
+        moduleTranslation.lookupValue(opInst.getOperand(0)),
+        llvm::cast<llvm::DILocalVariable>(
+            moduleTranslation.translateDebugInfo($varInfo)),
+        moduleTranslation.translateExpression($locationExpr),
+        moduleTranslation.translateLoc(opInst.getLoc(), scope),
+        builder.GetInsertPoint());
+  }];
 }
 
 def LLVM_DbgLabelOp : LLVM_IntrOp<"dbg.label", [], [], [], 0> {
@@ -730,13 +759,21 @@ def LLVM_DbgLabelOp : LLVM_IntrOp<"dbg.label", [], [], [], 0> {
     // Debug intrinsics without debug locations are invalid.
     if(!builder.getCurrentDebugLocation())
       return success();
+    llvm::Function *parentFn = builder.GetInsertBlock()->getParent();
+    llvm::DILocalScope *scope;
+    if (auto scopeLoc =
+            opInst.getLoc()->findInstanceOf<FusedLocWith<LLVM::DIScopeAttr>>())
+      scope = llvm::cast<llvm::DILocalScope>(
+          moduleTranslation.translateDebugInfo(scopeLoc.getMetadata()));
+    else
+      scope = parentFn->getSubprogram();
+
     llvm::Module *module = builder.GetInsertBlock()->getModule();
-    llvm::LLVMContext &ctx = module->getContext();
-    llvm::Function *fn =
-      llvm::Intrinsic::getOrInsertDeclaration(module, llvm::Intrinsic::dbg_label);
-    builder.CreateCall(fn, {
-        llvm::MetadataAsValue::get(ctx, moduleTranslation.translateDebugInfo($label))
-      });
+    llvm::DIBuilder DB(*module);
+    DB.insertLabel(
+        llvm::cast<llvm::DILabel>(moduleTranslation.translateDebugInfo($label)),
+        moduleTranslation.translateLoc(opInst.getLoc(), scope),
+        builder.GetInsertPoint());
   }];
   let mlirBuilder = [{
     DILabelAttr labelAttr = $_label_attr($label);
diff --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
index 0f675a0a5df5d..611d8d1960e0e 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
@@ -18,6 +18,7 @@
 #include "mlir/Target/LLVMIR/ModuleTranslation.h"
 
 #include "llvm/ADT/TypeSwitch.h"
+#include "llvm/IR/DIBuilder.h"
 #include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/InlineAsm.h"
 #include "llvm/IR/Instructions.h"
diff --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
index b3a06e2be7fe6..d9aae529d19b6 100644
--- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
@@ -2407,11 +2407,6 @@ mlir::translateModuleToLLVMIR(Operation *module, llvm::LLVMContext &llvmContext,
   if (failed(translator.convertUnresolvedBlockAddress()))
     return nullptr;
 
-  // Once we've finished constructing elements in the module, we should convert
-  // it to use the debug info format desired by LLVM.
-  // See https://llvm.org/docs/RemoveDIsDebugInfo.html
-  translator.llvmModule->convertToNewDbgValues();
-
   // Add the necessary debug info module flags, if they were not encoded in MLIR
   // beforehand.
   translator.debugTranslation->addModuleFlagsIfNotPresent();

@abidh abidh requested a review from andykaylor August 22, 2025 11:05
Copy link
Contributor

@SLTozer SLTozer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't speak to the MLIR logic, but the DIBuilder usage LGTM (also looks like we missed a spot, leaving the debug value creation function as insertDbgValueIntrinsic!) - thanks for updating this!

@@ -711,6 +695,28 @@ def LLVM_DbgDeclareOp : LLVM_DbgIntrOp<"dbg.declare", "addr", [
LLVM_DILocalVariableAttr:$varInfo,
DefaultValuedAttr<LLVM_DIExpressionAttr, "{}">:$locationExpr
);
let llvmBuilder = [{
// Debug intrinsics without debug locations are invalid.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May as well update the terminology while passing by.

Suggested change
// Debug intrinsics without debug locations are invalid.
// Debug records without debug locations are invalid.

Copy link
Contributor

@gysit gysit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change makes sense!

Let's try to factor out some helpers to reduce the code duplication.

The casts are probably unavoidable. If there a place where the cast could fail we may want to use dyn_cast and return failure to have a better error handling. However, it sounds like refactoring the inheritance to match LLVM will fix the critical case?

Comment on lines 735 to 741
llvm::DILocalScope *scope;
if (auto scopeLoc =
opInst.getLoc()->findInstanceOf<FusedLocWith<LLVM::DIScopeAttr>>())
scope = llvm::cast<llvm::DILocalScope>(
moduleTranslation.translateDebugInfo(scopeLoc.getMetadata()));
else
scope = parentFn->getSubprogram();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it is not possible anymore to put all the builder code in the debug intrinsic base class there is quite a bit of code duplication. Can you factor out some of this code into a helper function(s) placed in LLVMDialect.cpp? One thing that seems doable is to extract a helper for the scope computation but there may be other options as well. I would place it somewhere to the other intrinsic functionality that has been factored out (e.g. here

void LLVM::masked_compressstore::build(OpBuilder &builder,
).

That should reduce code duplication and the amount of C++ embedded in tablegen.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your suggestion. I have moved the code in a separate function. I initially used the location that you suggested for the new function but it would have required to add its declaration in LLVMDialect.h and then at least a forward declaration of ModuleTranslation there. It seemed like too much churn for a local helper. I have instead added in LLVMToLLVMIRTranslation.cpp.

I also changed the code to use dyn_cast.

scope = parentFn->getSubprogram();

llvm::Module *module = builder.GetInsertBlock()->getModule();
llvm::DIBuilder DB(*module);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
llvm::DIBuilder DB(*module);
llvm::DIBuilder debugInfoBuilder(*module);

nit: local variables should start with a lower case letter. Let's maybe use debugInfoBuilder or similar?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Contributor

@andykaylor andykaylor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a test for the new behavior?

let llvmBuilder = [{
// Debug intrinsics without debug locations are invalid.
if(!builder.getCurrentDebugLocation())
return success();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be reasonable to report an error here? If this is "invalid" as the comment says, silently ignoring it doesn't seem like the right behavior.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with this comment. I can change this in a separate PR.

@@ -673,22 +673,6 @@ def LLVM_CoroPromiseOp : LLVM_IntrOp<"coro.promise", [], [], [], 1> {

class LLVM_DbgIntrOp<string name, string argName, list<Trait> traits = []>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
class LLVM_DbgIntrOp<string name, string argName, list<Trait> traits = []>
class LLVM_DbgRecordOp<string name, string argName, list<Trait> traits = []>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure about this. The mlir representation remains the same. It is just that mlir InrtOp now translates to dbg records instead of debug intrinsics in llvm.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I would stick to LLVM_DbgIntrOp since they are effectively still intrinsics in MLIR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In what sense are they intrinsics in MLIR? I didn't think MLIR had a concept of intrinsics beyond having a way to represent calls to LLVM IR intrinsics.

This is obviously a minor point and I don't have a problem with the change going in either way. I just want to make sure I'm not missing something.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see. It's still deriving from LLVM_IntrOp<> and the name will still be "llvm.intr.dbg.*". That feels like less of a minor point to me. I think there's more going on here than I realized, such as the call to moduleImport.addDebugIntrinsic in the mlirBuilder which is apparently processing this as a CallInst (while importing LLVM IR?).

Will something have to be done later to import debug records?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are correct that MLIR does not have the concept of an intrinsic. However, LLVM dialect still models intrinsics, using operations, and it doesn't have the equivalent of debug records. Ideally, a debug record would not be an MLIR operation, but some sort of Location attribute attached to an operation. Unfortunately, MLIR Locations cannot depend on values which would be necessary for debug records if I understand them correctly.

TD;LR we will need to come up with a modeling for debug records. Ideally, this would be done with MLIR's Location infrastructure, but I fear we will have to stick to operations.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, there's definitely a difficulty mapping debug records to MLIR constructs. I'm fine with this implementation if it's just seen as a temporary step along the way to a better handling of debug records, though this artificial link to intrinsic calls makes me uneasy.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I agree that this has to change. We just miss volunteers to implement this. There is a ticket for this (#129121)

1. Refactor some code into a separate function to avoid duplication.
2. Some name and comments update.
@abidh
Copy link
Contributor Author

abidh commented Aug 26, 2025

Can you add a test for the new behavior?

As I wrote in the PR description, the output remains the same. It is just that intermediate use of llvm intrinsics have been removed. So all the existing tests check this functionality.

Copy link
Contributor

@gysit gysit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing the comments.

LGTM from my end modulo last nit.

Co-authored-by: Tobias Gysi <tobias.gysi@nextsilicon.com>
@@ -673,22 +673,6 @@ def LLVM_CoroPromiseOp : LLVM_IntrOp<"coro.promise", [], [], [], 1> {

class LLVM_DbgIntrOp<string name, string argName, list<Trait> traits = []>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, there's definitely a difficulty mapping debug records to MLIR constructs. I'm fine with this implementation if it's just seen as a temporary step along the way to a better handling of debug records, though this artificial link to intrinsic calls makes me uneasy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants