[Mlir-commits] [mlir] [mlir] Add support for DIGlobalVariable and DIGlobalVariableExpression (PR #73367)

Justin Wilson llvmlistbot at llvm.org
Mon Nov 27 20:44:00 PST 2023


https://github.com/waj334 updated https://github.com/llvm/llvm-project/pull/73367

>From 83e52e0857562675f8b41198670b66d6d16327a8 Mon Sep 17 00:00:00 2001
From: "Justin A. Wilson" <justin.wilson at omibyte.io>
Date: Fri, 24 Nov 2023 14:22:34 -0600
Subject: [PATCH] [mlir] Add support for DIGlobalVariable,
 DIGlobalVariableExpression and DIExpression

Introduces DIGlobalVariableAttr, DIGlobalVariableExpressionAttr and DIExpressionAttr so that ModuleTranslation can emit the required metadata needed for debug information about global variable. The translator implementation for debug metadata needed to be refactored in order to allow translation of nodes based on MDNode (DIGlobalVariableExpressionAttr and DIExpression) in addition to DINode-based nodes.

A DIGlobalVariableExpressionAttr can now be passed to the GlobalOp operation directly and ModuleTranslation will create the respective DIGlobalVariable and DIGlobalVariableExpression nodes. The compile unit that DIGlobalVariable is expected to be configured with will be updated with the created DIGlobalVariableExpression.
---
 .../mlir/Dialect/LLVMIR/LLVMAttrDefs.td       |  61 +++++++++-
 mlir/include/mlir/Dialect/LLVMIR/LLVMAttrs.h  |   3 +
 .../mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td   |   6 +-
 mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td   |   4 +-
 .../mlir/Target/LLVMIR/ModuleTranslation.h    |   8 ++
 mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp      | 111 +++++++++++++++++-
 mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp    |   7 +-
 mlir/lib/Target/LLVMIR/DebugImporter.cpp      |  45 ++++++-
 mlir/lib/Target/LLVMIR/DebugImporter.h        |   8 ++
 mlir/lib/Target/LLVMIR/DebugTranslation.cpp   |  41 ++++++-
 mlir/lib/Target/LLVMIR/DebugTranslation.h     |   8 ++
 mlir/lib/Target/LLVMIR/ModuleImport.cpp       |   3 +-
 mlir/lib/Target/LLVMIR/ModuleTranslation.cpp  |  27 +++++
 mlir/test/Dialect/LLVMIR/debuginfo.mlir       |   4 +-
 mlir/test/Dialect/LLVMIR/global.mlir          |   8 ++
 mlir/test/Target/LLVMIR/Import/debug-info.ll  |   6 +-
 mlir/test/Target/LLVMIR/llvmir-debug.mlir     |  10 +-
 17 files changed, 332 insertions(+), 28 deletions(-)

diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
index 9e35bf1ba977725..5c39f586e04fe79 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
@@ -11,6 +11,7 @@
 
 include "mlir/Dialect/LLVMIR/LLVMDialect.td"
 include "mlir/IR/AttrTypeBase.td"
+include "mlir/IR/CommonAttrConstraints.td"
 
 // All of the attributes will extend this class.
 class LLVM_Attr<string name, string attrMnemonic,
@@ -261,15 +262,32 @@ def LLVM_DITagParameter : LLVM_DIParameter<
   "tag", /*default=*/"", "Tag"
 >;
 
+def LLVM_DIExpressionOpcodeParameter : LLVM_DIParameter<
+  "operation encoding", /*default=*/"", "OperationEncoding"
+>;
+
 //===----------------------------------------------------------------------===//
 // DIExpressionAttr
 //===----------------------------------------------------------------------===//
 
-// TODO: Implement custom printer/parser for elements so that operators are
-// dumped in textual form.
-def LLVM_DIExpressionAttr : ArrayOfAttr<LLVM_Dialect, "DIExpression",
-                                        "di_expr", "uint64_t"> {
-  let assemblyFormat = "`<` `[` (`]` `>`) : ($value^ `]` `>`)?";
+def LLVM_DIExpressionElemAttr : LLVM_Attr<"DIExpressionElem", "di_expression_elem"> {
+    let parameters = (ins
+        LLVM_DIExpressionOpcodeParameter:$opcode,
+        OptionalArrayRefParameter<"uint64_t">:$arguments);
+    let assemblyFormat = "`` $opcode ( `(` custom<ExpressionArg>(ref($opcode), $arguments)^ `)` ) : (``)?";
+}
+
+def LLVM_DIExpressionAttr : LLVM_Attr<"DIExpression", "di_expression",
+                                          /*traits=*/[], "DINodeAttr"> {
+  let parameters = (ins
+    OptionalArrayRefParameter<"DIExpressionElemAttr">:$operations
+  );
+  let builders = [
+    AttrBuilder<(ins)>
+  ];
+  let constBuilderCall =
+            "::mlir::LLVM::DIExpressionAttr::get($_builder.getContext(), $0)";
+  let assemblyFormat = "`<` ( `[` $operations^ `]` ) : (``)? `>`";
 }
 
 //===----------------------------------------------------------------------===//
@@ -447,6 +465,39 @@ def LLVM_DILocalVariableAttr : LLVM_Attr<"DILocalVariable", "di_local_variable",
   let assemblyFormat = "`<` struct(params) `>`";
 }
 
+//===----------------------------------------------------------------------===//
+// DIGlobalVariableExpressionAttr
+//===----------------------------------------------------------------------===//
+
+def LLVM_DIGlobalVariableExpressionAttr : LLVM_Attr<"DIGlobalVariableExpression", "di_global_variable_expression",
+                                          /*traits=*/[], "DINodeAttr"> {
+  let parameters = (ins
+    "DIGlobalVariableAttr":$var,
+    OptionalParameter<"DIExpressionAttr">:$expr
+  );
+  let assemblyFormat = "`<` struct(params) `>`";
+  let constBuilderCall = "$0";
+}
+
+//===----------------------------------------------------------------------===//
+// DIGlobalVariableAttr
+//===----------------------------------------------------------------------===//
+
+def LLVM_DIGlobalVariable : LLVM_Attr<"DIGlobalVariable", "di_global_variable",
+                                      /*traits=*/[], "DINodeAttr"> {
+  let parameters = (ins
+    "DIScopeAttr":$scope,
+    "StringAttr":$name,
+    OptionalParameter<"StringAttr">:$linkageName,
+    "DIFileAttr":$file,
+    "unsigned":$line,
+    "DITypeAttr":$type,
+    OptionalParameter<"bool">:$isLocalToUnit,
+    OptionalParameter<"bool">:$isDefined,
+    OptionalParameter<"unsigned">:$alignInBits);
+  let assemblyFormat = "`<` struct(params) `>`";
+}
+
 //===----------------------------------------------------------------------===//
 // DISubprogramAttr
 //===----------------------------------------------------------------------===//
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrs.h b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrs.h
index c370bfa2b733d65..dd88c3fd89167db 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrs.h
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrs.h
@@ -16,6 +16,9 @@
 
 #include "mlir/Dialect/LLVMIR/LLVMTypes.h"
 #include "mlir/IR/OpImplementation.h"
+#include "mlir/TableGen/Constraint.h"
+#include "llvm/BinaryFormat/Dwarf.h"
+#include "llvm/TableGen/Record.h"
 #include <optional>
 
 #include "mlir/Dialect/LLVMIR/LLVMOpsEnums.h.inc"
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td
index fc088eacfc49744..f8862045f12b84c 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td
@@ -434,7 +434,7 @@ class LLVM_DbgIntrOp<string name, string argName, list<Trait> traits = []>
         llvm::MetadataAsValue::get(ctx,
             llvm::ValueAsMetadata::get(moduleTranslation.lookupValue(opInst.getOperand(0)))),
         llvm::MetadataAsValue::get(ctx, moduleTranslation.translateDebugInfo($varInfo)),
-        llvm::MetadataAsValue::get(ctx, llvm::DIExpression::get(ctx, $locationExpr)),
+        llvm::MetadataAsValue::get(ctx, moduleTranslation.translateExpression($locationExpr)),
       });
   }];
   let mlirBuilder = [{
@@ -455,7 +455,7 @@ def LLVM_DbgDeclareOp : LLVM_DbgIntrOp<"dbg.declare", "addr",
   let arguments = (ins
     LLVM_AnyPointer:$addr,
     LLVM_DILocalVariableAttr:$varInfo,
-    DefaultValuedAttr<LLVM_DIExpressionAttr, "std::nullopt">:$locationExpr
+    DefaultValuedAttr<LLVM_DIExpressionAttr, "{}">:$locationExpr
   );
 }
 
@@ -465,7 +465,7 @@ def LLVM_DbgValueOp : LLVM_DbgIntrOp<"dbg.value", "value",
   let arguments = (ins
     LLVM_Type:$value,
     LLVM_DILocalVariableAttr:$varInfo,
-    DefaultValuedAttr<LLVM_DIExpressionAttr, "std::nullopt">:$locationExpr
+    DefaultValuedAttr<LLVM_DIExpressionAttr, "{}">:$locationExpr
   );
 }
 
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index 88f4f81735372b9..09226340330d230 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -1087,6 +1087,7 @@ def LLVM_GlobalOp : LLVM_Op<"mlir.global",
     OptionalAttr<UnnamedAddr>:$unnamed_addr,
     OptionalAttr<StrAttr>:$section,
     OptionalAttr<SymbolRefAttr>:$comdat,
+    DefaultValuedAttr<LLVM_DIGlobalVariableExpressionAttr, "::mlir::LLVM::DIGlobalVariableExpressionAttr()">:$dbg_expr,
     DefaultValuedAttr<Visibility, "mlir::LLVM::Visibility::Default">:$visibility_
   );
   let summary = "LLVM dialect global.";
@@ -1196,7 +1197,8 @@ def LLVM_GlobalOp : LLVM_Op<"mlir.global",
       CArg<"bool", "false">:$dsoLocal,
       CArg<"bool", "false">:$thread_local_,
       CArg<"SymbolRefAttr", "{}">:$comdat,
-      CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>
+      CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs,
+      CArg<"DIGlobalVariableExpressionAttr", "{}">:$dbgExpr)>
   ];
 
   let extraClassDeclaration = [{
diff --git a/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h b/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h
index 4820e826d0ca357..961592999601329 100644
--- a/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h
+++ b/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h
@@ -46,6 +46,7 @@ class LoopAnnotationTranslation;
 
 class AliasScopeAttr;
 class AliasScopeDomainAttr;
+class MDNodeAttr;
 class DINodeAttr;
 class LLVMFuncOp;
 class ComdatSelectorOp;
@@ -191,6 +192,13 @@ class ModuleTranslation {
   /// Translates the given location.
   llvm::DILocation *translateLoc(Location loc, llvm::DILocalScope *scope);
 
+  /// Translates the given LLVM DWARF expression metadata
+  llvm::DIExpression *translateExpression(LLVM::DIExpressionAttr attr);
+
+  /// Translates the given LLVM global variable expression metadata
+  llvm::DIGlobalVariableExpression *
+  translateGlobalVariableExpression(LLVM::DIGlobalVariableExpressionAttr attr);
+
   /// Translates the given LLVM debug info metadata.
   llvm::Metadata *translateDebugInfo(LLVM::DINodeAttr attr);
 
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp
index 3d45ab8fac4d705..9b52d09ce729404 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp
@@ -11,17 +11,31 @@
 //===----------------------------------------------------------------------===//
 
 #include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
+
+#include "../../../../../llvm/include/llvm/DebugInfo/DWARF/DWARFFormValue.h"
 #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
 #include "mlir/IR/Builders.h"
 #include "mlir/IR/DialectImplementation.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/TypeSwitch.h"
 #include "llvm/BinaryFormat/Dwarf.h"
+#include "llvm/IR/DebugInfoMetadata.h"
 #include <optional>
 
 using namespace mlir;
 using namespace mlir::LLVM;
 
+/// Parse DWARF expression arguments with respect to the DWARF operation
+/// opcode. Some DWARF expression operations have a specific number of operands
+/// and may appear in a textual form.
+static LogicalResult parseExpressionArg(AsmParser &parser, uint64_t opcode,
+                                        SmallVector<uint64_t> &args);
+
+/// Print DWARF expression arguments with respect to the specific DWARF
+/// operation. Some operands are printed in their textual form.
+static LogicalResult printExpressionArg(AsmPrinter &printer, uint64_t opcode,
+                                        ArrayRef<uint64_t> args);
+
 #include "mlir/Dialect/LLVMIR/LLVMOpsEnums.cpp.inc"
 #define GET_ATTRDEF_CLASSES
 #include "mlir/Dialect/LLVMIR/LLVMOpsAttrDefs.cpp.inc"
@@ -34,6 +48,7 @@ void LLVMDialect::registerAttributes() {
   addAttributes<
 #define GET_ATTRDEF_LIST
 #include "mlir/Dialect/LLVMIR/LLVMOpsAttrDefs.cpp.inc"
+
       >();
 }
 
@@ -43,8 +58,9 @@ void LLVMDialect::registerAttributes() {
 
 bool DINodeAttr::classof(Attribute attr) {
   return llvm::isa<DIBasicTypeAttr, DICompileUnitAttr, DICompositeTypeAttr,
-                   DIDerivedTypeAttr, DIFileAttr, DILabelAttr,
-                   DILexicalBlockAttr, DILexicalBlockFileAttr,
+                   DIDerivedTypeAttr, DIExpressionAttr, DIFileAttr,
+                   DIGlobalVariableAttr, DIGlobalVariableExpressionAttr,
+                   DILabelAttr, DILexicalBlockAttr, DILexicalBlockFileAttr,
                    DILocalVariableAttr, DIModuleAttr, DINamespaceAttr,
                    DINullTypeAttr, DISubprogramAttr, DISubrangeAttr,
                    DISubroutineTypeAttr>(attr);
@@ -109,3 +125,94 @@ bool MemoryEffectsAttr::isReadWrite() {
     return false;
   return true;
 }
+
+//===----------------------------------------------------------------------===//
+// DIExpression
+//===----------------------------------------------------------------------===//
+
+DIExpressionAttr DIExpressionAttr::get(MLIRContext *context) {
+  return get(context, ArrayRef<DIExpressionElemAttr>({}));
+}
+
+/// Parse DWARF expression arguments with respect to the DWARF operation
+/// opcode. Some DWARF expression operations have a specific number of operands
+/// and may appear in a textual form.
+LogicalResult parseExpressionArg(AsmParser &parser, uint64_t opcode,
+                                 SmallVector<uint64_t> &args) {
+  // The number of operands must be known for each specific operations
+  // in order to parse operations in their respective textual forms.
+  const llvm::DIExpression::ExprOperand opInfo(&opcode);
+  size_t numOperands = opInfo.getNumArgs();
+
+  for (size_t i = 0; i < numOperands; ++i) {
+    uint64_t operand = 0;
+
+    // Parse an operand.
+    if (i > 0 && opcode == llvm::dwarf::DW_OP_LLVM_convert) {
+      // Attempt to parse an keyword.
+      StringRef keyword;
+      if (succeeded(parser.parseOptionalKeyword(&keyword))) {
+        operand = llvm::dwarf::getAttributeEncoding(keyword);
+        if (operand == 0) {
+          // The keyword is invalid.
+          return parser.emitError(parser.getCurrentLocation())
+                 << "encountered unknown attribute encoding \"" << keyword
+                 << "\"";
+        }
+      }
+    }
+
+    // operand should be non-zero if a keyword was parsed. Otherwise, the
+    // operand MUST be an integer.
+    if (operand == 0) {
+      // Parse the next operand as an integer
+      if (parser.parseInteger(operand)) {
+        return parser.emitError(parser.getCurrentLocation())
+               << "expected operand #" << i;
+      }
+    }
+
+    // NOTE: A few operations are variadic.
+    if (i == 0 && (opcode == llvm::dwarf::DW_OP_LLVM_entry_value ||
+                   opcode == llvm::dwarf::DW_OP_LLVM_arg)) {
+      // This operand represents the number of follwing operands. Add to the
+      // current number of operands.
+      numOperands += operand;
+    }
+    args.push_back(operand);
+
+    if (i + 1 != numOperands) {
+      if (parser.parseComma()) {
+        return parser.emitError(parser.getCurrentLocation())
+               << "expected `,` following operand #" << i;
+      }
+    }
+  }
+  return success();
+}
+
+/// Print DWARF expression arguments with respect to the specific DWARF
+/// operation. Some operands are printed in their textual form.
+LogicalResult printExpressionArg(AsmPrinter &printer, uint64_t opcode,
+                                 ArrayRef<uint64_t> args) {
+  for (size_t i = 0; i < args.size(); i++) {
+    const auto &operand = args[i];
+    if (i > 0 && opcode == llvm::dwarf::DW_OP_LLVM_convert) {
+      if (auto keyword = llvm::dwarf::AttributeEncodingString(operand);
+          !keyword.empty()) {
+        printer << keyword;
+      } else {
+        // Fallback to printing the operand as an integer.
+        printer << operand;
+      }
+    } else {
+      // All operands are expected to be printed as integers.
+      printer << operand;
+    }
+
+    if (i + 1 != args.size()) {
+      printer << ", ";
+    }
+  }
+  return success();
+}
\ No newline at end of file
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index f6c8f388732c3da..2ae1d2ad8249e2f 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -1781,7 +1781,8 @@ void GlobalOp::build(OpBuilder &builder, OperationState &result, Type type,
                      bool isConstant, Linkage linkage, StringRef name,
                      Attribute value, uint64_t alignment, unsigned addrSpace,
                      bool dsoLocal, bool threadLocal, SymbolRefAttr comdat,
-                     ArrayRef<NamedAttribute> attrs) {
+                     ArrayRef<NamedAttribute> attrs,
+                     DIGlobalVariableExpressionAttr dbgExpr) {
   result.addAttribute(getSymNameAttrName(result.name),
                       builder.getStringAttr(name));
   result.addAttribute(getGlobalTypeAttrName(result.name), TypeAttr::get(type));
@@ -1812,6 +1813,10 @@ void GlobalOp::build(OpBuilder &builder, OperationState &result, Type type,
     result.addAttribute(getAddrSpaceAttrName(result.name),
                         builder.getI32IntegerAttr(addrSpace));
   result.attributes.append(attrs.begin(), attrs.end());
+
+  if (dbgExpr)
+    result.addAttribute(getDbgExprAttrName(result.name), dbgExpr);
+
   result.addRegion();
 }
 
diff --git a/mlir/lib/Target/LLVMIR/DebugImporter.cpp b/mlir/lib/Target/LLVMIR/DebugImporter.cpp
index 36a03ec71c0c6c6..36f5c79ec7aab27 100644
--- a/mlir/lib/Target/LLVMIR/DebugImporter.cpp
+++ b/mlir/lib/Target/LLVMIR/DebugImporter.cpp
@@ -61,7 +61,11 @@ DICompositeTypeAttr DebugImporter::translateImpl(llvm::DICompositeType *node) {
   SmallVector<DINodeAttr> elements;
   for (llvm::DINode *element : node->getElements()) {
     assert(element && "expected a non-null element type");
-    elements.push_back(translate(element));
+    const auto translatedElement = translate(element);
+    if (translatedElement) {
+      // TODO: Should this fail if the returned attribute is invalid?
+      elements.push_back(cast<DINodeAttr>(translatedElement));
+    }
   }
   // Drop the elements parameter if a cyclic dependency is detected. We
   // currently cannot model these cycles and thus drop the parameter if
@@ -116,6 +120,16 @@ DebugImporter::translateImpl(llvm::DILexicalBlockFile *node) {
                                      node->getDiscriminator());
 }
 
+DIGlobalVariableAttr
+DebugImporter::translateImpl(llvm::DIGlobalVariable *node) {
+  return DIGlobalVariableAttr::get(
+      context, translate(node->getScope()),
+      StringAttr::get(context, node->getName()),
+      StringAttr::get(context, node->getLinkageName()),
+      translate(node->getFile()), node->getLine(), translate(node->getType()),
+      node->isLocalToUnit(), node->isDefinition(), node->getAlignInBits());
+}
+
 DILocalVariableAttr DebugImporter::translateImpl(llvm::DILocalVariable *node) {
   return DILocalVariableAttr::get(context, translate(node->getScope()),
                                   getStringAttrOrNull(node->getRawName()),
@@ -220,7 +234,7 @@ DINodeAttr DebugImporter::translate(llvm::DINode *node) {
   auto guard = llvm::make_scope_exit([&]() { translationStack.pop_back(); });
 
   // Convert the debug metadata if possible.
-  auto translateNode = [this](llvm::DINode *node) -> DINodeAttr {
+  auto translateNode = [this](llvm::MDNode *node) -> DINodeAttr {
     if (auto *casted = dyn_cast<llvm::DIBasicType>(node))
       return translateImpl(casted);
     if (auto *casted = dyn_cast<llvm::DICompileUnit>(node))
@@ -231,6 +245,8 @@ DINodeAttr DebugImporter::translate(llvm::DINode *node) {
       return translateImpl(casted);
     if (auto *casted = dyn_cast<llvm::DIFile>(node))
       return translateImpl(casted);
+    if (auto *casted = dyn_cast<llvm::DIGlobalVariable>(node))
+      return translateImpl(casted);
     if (auto *casted = dyn_cast<llvm::DILabel>(node))
       return translateImpl(casted);
     if (auto *casted = dyn_cast<llvm::DILexicalBlock>(node))
@@ -281,8 +297,31 @@ Location DebugImporter::translateLoc(llvm::DILocation *loc) {
   return result;
 }
 
+DIExpressionAttr DebugImporter::translateExpression(llvm::DIExpression *node) {
+  SmallVector<DIExpressionElemAttr> ops;
+
+  // Begin processing the operations
+  for (const auto &op : node->expr_ops()) {
+    SmallVector<uint64_t> operands;
+    operands.reserve(op.getNumArgs());
+    for (const auto &i : llvm::seq(op.getNumArgs())) {
+      operands.push_back(op.getArg(i));
+    }
+    const auto attr = DIExpressionElemAttr::get(context, op.getOp(), operands);
+    ops.push_back(attr);
+  }
+  return DIExpressionAttr::get(context, ops);
+}
+
+DIGlobalVariableExpressionAttr DebugImporter::translateGlobalVariableExpression(
+    llvm::DIGlobalVariableExpression *node) {
+  return DIGlobalVariableExpressionAttr::get(
+      context, translate(node->getVariable()),
+      translateExpression(node->getExpression()));
+}
+
 StringAttr DebugImporter::getStringAttrOrNull(llvm::MDString *stringNode) {
   if (!stringNode)
     return StringAttr();
   return StringAttr::get(context, stringNode->getString());
-}
+}
\ No newline at end of file
diff --git a/mlir/lib/Target/LLVMIR/DebugImporter.h b/mlir/lib/Target/LLVMIR/DebugImporter.h
index 5c07ec82d0191bc..42d90975201df9b 100644
--- a/mlir/lib/Target/LLVMIR/DebugImporter.h
+++ b/mlir/lib/Target/LLVMIR/DebugImporter.h
@@ -35,6 +35,13 @@ class DebugImporter {
   /// Translates the given LLVM debug location to an MLIR location.
   Location translateLoc(llvm::DILocation *loc);
 
+  /// Translate the LLVM DWARF expression metadata to MLIR.
+  DIExpressionAttr translateExpression(llvm::DIExpression *node);
+
+  /// Translate the LLVM DWARF global variable expression metadata to MLIR.
+  DIGlobalVariableExpressionAttr
+  translateGlobalVariableExpression(llvm::DIGlobalVariableExpression *node);
+
   /// Translates the debug information for the given function into a Location.
   /// Returns UnknownLoc if `func` has no debug information attached to it.
   Location translateFuncLocation(llvm::Function *func);
@@ -61,6 +68,7 @@ class DebugImporter {
   DILabelAttr translateImpl(llvm::DILabel *node);
   DILexicalBlockAttr translateImpl(llvm::DILexicalBlock *node);
   DILexicalBlockFileAttr translateImpl(llvm::DILexicalBlockFile *node);
+  DIGlobalVariableAttr translateImpl(llvm::DIGlobalVariable *node);
   DILocalVariableAttr translateImpl(llvm::DILocalVariable *node);
   DIModuleAttr translateImpl(llvm::DIModule *node);
   DINamespaceAttr translateImpl(llvm::DINamespace *node);
diff --git a/mlir/lib/Target/LLVMIR/DebugTranslation.cpp b/mlir/lib/Target/LLVMIR/DebugTranslation.cpp
index a15ffb40065a08b..015a39b3fc7e3c9 100644
--- a/mlir/lib/Target/LLVMIR/DebugTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/DebugTranslation.cpp
@@ -189,6 +189,15 @@ DebugTranslation::translateImpl(DILocalVariableAttr attr) {
       /*Annotations=*/nullptr);
 }
 
+llvm::DIGlobalVariable *
+DebugTranslation::translateImpl(DIGlobalVariableAttr attr) {
+  return llvm::DIGlobalVariable::getDistinct(
+      llvmCtx, translate(attr.getScope()), getMDStringOrNull(attr.getName()),
+      getMDStringOrNull(attr.getLinkageName()), translate(attr.getFile()),
+      attr.getLine(), translate(attr.getType()), attr.getIsLocalToUnit(),
+      attr.getIsDefined(), nullptr, nullptr, attr.getAlignInBits(), nullptr);
+}
+
 llvm::DIScope *DebugTranslation::translateImpl(DIScopeAttr attr) {
   return cast<llvm::DIScope>(translate(DINodeAttr(attr)));
 }
@@ -260,10 +269,11 @@ llvm::DINode *DebugTranslation::translate(DINodeAttr attr) {
   llvm::DINode *node =
       TypeSwitch<DINodeAttr, llvm::DINode *>(attr)
           .Case<DIBasicTypeAttr, DICompileUnitAttr, DICompositeTypeAttr,
-                DIDerivedTypeAttr, DIFileAttr, DILabelAttr, DILexicalBlockAttr,
-                DILexicalBlockFileAttr, DILocalVariableAttr, DIModuleAttr,
-                DINamespaceAttr, DINullTypeAttr, DISubprogramAttr,
-                DISubrangeAttr, DISubroutineTypeAttr>(
+                DIDerivedTypeAttr, DIFileAttr, DIGlobalVariableAttr,
+                DILabelAttr, DILexicalBlockAttr, DILexicalBlockFileAttr,
+                DILocalVariableAttr, DIModuleAttr, DINamespaceAttr,
+                DINullTypeAttr, DISubprogramAttr, DISubrangeAttr,
+                DISubroutineTypeAttr>(
               [&](auto attr) { return translateImpl(attr); });
   attrToNode.insert({attr, node});
   return node;
@@ -281,6 +291,29 @@ llvm::DILocation *DebugTranslation::translateLoc(Location loc,
   return translateLoc(loc, scope, /*inlinedAt=*/nullptr);
 }
 
+// Translate the given DWARF expression metadata to to LLVM.
+llvm::DIExpression *
+DebugTranslation::translateExpression(LLVM::DIExpressionAttr attr) {
+  if (attr) {
+    SmallVector<uint64_t, 1> ops;
+    // Append operations their operands to the list
+    for (const auto &op : attr.getOperations()) {
+      ops.push_back(op.getOpcode());
+      append_range(ops, op.getArguments());
+    }
+    return llvm::DIExpression::get(llvmCtx, ops);
+  }
+  return llvm::DIExpression::get(llvmCtx, {});
+}
+
+/// Translate the given global DWARF variable expression to LLVM.
+llvm::DIGlobalVariableExpression *
+DebugTranslation::translateGlobalVariableExpression(
+    LLVM::DIGlobalVariableExpressionAttr attr) {
+  return llvm::DIGlobalVariableExpression::get(
+      llvmCtx, translate(attr.getVar()), translateExpression(attr.getExpr()));
+}
+
 /// Translate the given location to an llvm DebugLoc.
 llvm::DILocation *DebugTranslation::translateLoc(Location loc,
                                                  llvm::DILocalScope *scope,
diff --git a/mlir/lib/Target/LLVMIR/DebugTranslation.h b/mlir/lib/Target/LLVMIR/DebugTranslation.h
index d22c3d80dafd5f9..2e5cddd4be23054 100644
--- a/mlir/lib/Target/LLVMIR/DebugTranslation.h
+++ b/mlir/lib/Target/LLVMIR/DebugTranslation.h
@@ -37,6 +37,13 @@ class DebugTranslation {
   /// Translate the given location to an llvm debug location.
   llvm::DILocation *translateLoc(Location loc, llvm::DILocalScope *scope);
 
+  /// Translate the given DWARF expression metadata to to LLVM.
+  llvm::DIExpression *translateExpression(LLVM::DIExpressionAttr attr);
+
+  /// Translate the given DWARF global variable expression to LLVM.
+  llvm::DIGlobalVariableExpression *
+  translateGlobalVariableExpression(LLVM::DIGlobalVariableExpressionAttr attr);
+
   /// Translate the debug information for the given function.
   void translate(LLVMFuncOp func, llvm::Function &llvmFunc);
 
@@ -72,6 +79,7 @@ class DebugTranslation {
   llvm::DILexicalBlockFile *translateImpl(DILexicalBlockFileAttr attr);
   llvm::DILocalScope *translateImpl(DILocalScopeAttr attr);
   llvm::DILocalVariable *translateImpl(DILocalVariableAttr attr);
+  llvm::DIGlobalVariable *translateImpl(DIGlobalVariableAttr attr);
   llvm::DIModule *translateImpl(DIModuleAttr attr);
   llvm::DINamespace *translateImpl(DINamespaceAttr attr);
   llvm::DIScope *translateImpl(DIScopeAttr attr);
diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
index 9cdc1f45d38fa59..d805e6083b8f384 100644
--- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
@@ -129,6 +129,7 @@ static LogicalResult convertInstructionImpl(OpBuilder &odsBuilder,
 
   // Convert all instructions that provide an MLIR builder.
 #include "mlir/Dialect/LLVMIR/LLVMOpFromLLVMIRConversions.inc"
+
   return failure();
 }
 
@@ -1890,7 +1891,7 @@ ModuleImport::processDebugIntrinsic(llvm::DbgVariableIntrinsic *dbgIntr,
   DILocalVariableAttr localVariableAttr =
       matchLocalVariableAttr(dbgIntr->getArgOperand(1));
   auto locationExprAttr =
-      DIExpressionAttr::get(context, dbgIntr->getExpression()->getElements());
+      debugImporter->translateExpression(dbgIntr->getExpression());
   Operation *op =
       llvm::TypeSwitch<llvm::DbgVariableIntrinsic *, Operation *>(dbgIntr)
           .Case([&](llvm::DbgDeclareInst *) {
diff --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
index 322843e65627603..76100f92d36aab8 100644
--- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
@@ -782,6 +782,22 @@ LogicalResult ModuleTranslation::convertGlobals() {
     var->setVisibility(convertVisibilityToLLVM(op.getVisibility_()));
 
     globalsMapping.try_emplace(op, var);
+
+    // Add debug information if present
+    if (op.getDbgExpr()) {
+      llvm::DIGlobalVariableExpression *globalExpr =
+          debugTranslation->translateGlobalVariableExpression(op.getDbgExpr());
+      const llvm::DIGlobalVariable *globalVar = globalExpr->getVariable();
+      var->addDebugInfo(globalExpr);
+
+      // Get the compile unit (scope) of the the global variable
+      if (auto *const compileUnit =
+              dyn_cast<llvm::DICompileUnit>(globalVar->getScope())) {
+        // Update the compile unit with this incoming global variable
+        compileUnit->replaceGlobalVariables(
+            llvm::MDTuple::get(getLLVMContext(), {globalExpr}));
+      }
+    }
   }
 
   // Convert global variable bodies. This is done after all global variables
@@ -1329,6 +1345,17 @@ llvm::DILocation *ModuleTranslation::translateLoc(Location loc,
   return debugTranslation->translateLoc(loc, scope);
 }
 
+llvm::DIExpression *
+ModuleTranslation::translateExpression(LLVM::DIExpressionAttr attr) {
+  return debugTranslation->translateExpression(attr);
+}
+
+llvm::DIGlobalVariableExpression *
+ModuleTranslation::translateGlobalVariableExpression(
+    LLVM::DIGlobalVariableExpressionAttr attr) {
+  return debugTranslation->translateGlobalVariableExpression(attr);
+}
+
 llvm::Metadata *ModuleTranslation::translateDebugInfo(LLVM::DINodeAttr attr) {
   return debugTranslation->translate(attr);
 }
diff --git a/mlir/test/Dialect/LLVMIR/debuginfo.mlir b/mlir/test/Dialect/LLVMIR/debuginfo.mlir
index cdfcb7f9e32e5c4..53c38b479703107 100644
--- a/mlir/test/Dialect/LLVMIR/debuginfo.mlir
+++ b/mlir/test/Dialect/LLVMIR/debuginfo.mlir
@@ -162,8 +162,8 @@ llvm.func @addr(%arg: i64) {
 
 // CHECK: llvm.func @value(%[[ARG1:.*]]: i32, %[[ARG2:.*]]: i32)
 llvm.func @value(%arg1: i32, %arg2: i32) {
-  // CHECK: llvm.intr.dbg.value #[[VAR1]] #llvm.di_expr<[2, 4096, 0, 4]> = %[[ARG1]]
-  llvm.intr.dbg.value #var1 #llvm.di_expr<[2, 4096, 0, 4]> = %arg1 : i32
+  // CHECK: llvm.intr.dbg.value #[[VAR1]] #llvm.di_expression<[DW_OP_LLVM_fragment(16, 8), DW_OP_plus_uconst(2), DW_OP_deref]> = %[[ARG1]]
+  llvm.intr.dbg.value #var1 #llvm.di_expression<[DW_OP_LLVM_fragment(16, 8), DW_OP_plus_uconst(2), DW_OP_deref]> = %arg1 : i32
   // CHECK: llvm.intr.dbg.value #[[VAR2]] = %[[ARG2]]
   llvm.intr.dbg.value #var2 = %arg2 : i32
   // CHECK: llvm.intr.dbg.label #[[LABEL1]]
diff --git a/mlir/test/Dialect/LLVMIR/global.mlir b/mlir/test/Dialect/LLVMIR/global.mlir
index 81178b2ef901f4f..d1a9d9c97d3685e 100644
--- a/mlir/test/Dialect/LLVMIR/global.mlir
+++ b/mlir/test/Dialect/LLVMIR/global.mlir
@@ -258,3 +258,11 @@ llvm.mlir.global @target_ext_init() : !llvm.target<"spirv.Image", i32, 0> {
 
 // expected-error @+1 {{global with target extension type can only be initialized with zero-initializer}}
 llvm.mlir.global @target_fail(0 : i64) : !llvm.target<"spirv.Image", i32, 0>
+
+// -----
+
+// CHECK: llvm.mlir.global external @global_with_expr() {addr_space = 0 : i32, dbg_expr = #llvm.di_global_variable_expression<var = <scope = #di_compile_unit, name = "global_with_expr", linkageName = "global_with_expr", file = #di_file, line = 370, type = #di_basic_type, isLocalToUnit = true, isDefined = true, alignInBits = 8>, expr = <>>} : i64
+#di_file = #llvm.di_file<"not" in "existence">
+#di_compile_unit = #llvm.di_compile_unit<sourceLanguage = DW_LANG_C, file = #di_file, producer = "MLIR", isOptimized = true, emissionKind = Full>
+#di_basic_type = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "uint64_t", sizeInBits = 64, encoding = DW_ATE_unsigned>
+llvm.mlir.global external @global_with_expr() {addr_space = 0 : i32, dbg_expr = #llvm.di_global_variable_expression<var = <scope = #di_compile_unit, name = "global_with_expr", linkageName = "global_with_expr", file = #di_file, line = 370, type = #di_basic_type, isLocalToUnit = true, isDefined = true, alignInBits = 8>, expr = <>>} : i64
\ No newline at end of file
diff --git a/mlir/test/Target/LLVMIR/Import/debug-info.ll b/mlir/test/Target/LLVMIR/Import/debug-info.ll
index 51445f25dc400f0..c8f40cfeabdb759 100644
--- a/mlir/test/Target/LLVMIR/Import/debug-info.ll
+++ b/mlir/test/Target/LLVMIR/Import/debug-info.ll
@@ -257,8 +257,10 @@ source_filename = "debug-info.ll"
 ; CHECK-SAME:  %[[ARG1:[a-zA-Z0-9]+]]
 define void @intrinsic(i64 %0, ptr %1) {
   ; CHECK: llvm.intr.dbg.declare #[[$VAR1]] = %[[ARG1]] : !llvm.ptr loc(#[[LOC1:.+]])
-  ; CHECK: llvm.intr.dbg.value #[[$VAR0]] #llvm.di_expr<[4096, 0, 8]> = %[[ARG0]] : i64 loc(#[[LOC0:.+]])
-  call void @llvm.dbg.value(metadata i64 %0, metadata !5, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 8)), !dbg !7
+  ; CHECK: llvm.intr.dbg.value #[[$VAR0]] #llvm.di_expression<[DW_OP_deref, DW_OP_constu(3), DW_OP_plus, DW_OP_LLVM_convert(4, DW_ATE_signed)]> = %[[ARG0]] : i64 loc(#[[LOC0:.+]])
+  ; CHECK: llvm.intr.dbg.value #[[$VAR0]] #llvm.di_expression<[DW_OP_deref, DW_OP_constu(3), DW_OP_plus, DW_OP_LLVM_fragment(3, 7)]> = %[[ARG0]] : i64 loc(#[[LOC0:.+]])
+  call void @llvm.dbg.value(metadata i64 %0, metadata !5, metadata !DIExpression(DW_OP_deref, DW_OP_constu, 3, DW_OP_plus, DW_OP_LLVM_fragment, 3, 7)), !dbg !7
+  call void @llvm.dbg.value(metadata i64 %0, metadata !5, metadata !DIExpression(DW_OP_deref, DW_OP_constu, 3, DW_OP_plus, DW_OP_LLVM_convert, 4, DW_ATE_signed)), !dbg !7
   call void @llvm.dbg.declare(metadata ptr %1, metadata !6, metadata !DIExpression()), !dbg !9
   ; CHECK: llvm.intr.dbg.label #[[$LABEL]] loc(#[[LOC1:.+]])
   call void @llvm.dbg.label(metadata !10), !dbg !9
diff --git a/mlir/test/Target/LLVMIR/llvmir-debug.mlir b/mlir/test/Target/LLVMIR/llvmir-debug.mlir
index e088374ec0eb8e1..b68c18720376b38 100644
--- a/mlir/test/Target/LLVMIR/llvmir-debug.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir-debug.mlir
@@ -91,11 +91,13 @@ llvm.func @func_with_debug(%arg: i64) {
   %allocCount = llvm.mlir.constant(1 : i32) : i32
   %alloc = llvm.alloca %allocCount x i64 : (i32) -> !llvm.ptr
 
-  // CHECK: call void @llvm.dbg.value(metadata i64 %[[ARG]], metadata ![[VAR_LOC:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_fragment, 8, 8))
-  // CHECK: call void @llvm.dbg.declare(metadata ptr %[[ALLOC]], metadata ![[ADDR_LOC:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 8))
+  // CHECK: call void @llvm.dbg.value(metadata i64 %[[ARG]], metadata ![[VAR_LOC:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 1))
+  llvm.intr.dbg.value #variable #llvm.di_expression<[DW_OP_LLVM_fragment(0, 1)]> = %arg : i64
+
+  // CHECK: call void @llvm.dbg.declare(metadata ptr %[[ALLOC]], metadata ![[ADDR_LOC:[0-9]+]], metadata !DIExpression(DW_OP_deref, DW_OP_LLVM_convert, 4, DW_ATE_signed))
+  llvm.intr.dbg.declare #variableAddr #llvm.di_expression<[DW_OP_deref, DW_OP_LLVM_convert(4, DW_ATE_signed)]> = %alloc : !llvm.ptr
+
   // CHECK: call void @llvm.dbg.value(metadata i64 %[[ARG]], metadata ![[NO_NAME_VAR:[0-9]+]], metadata !DIExpression())
-  llvm.intr.dbg.value #variable #llvm.di_expr<[4096, 8, 8]> = %arg : i64
-  llvm.intr.dbg.declare #variableAddr #llvm.di_expr<[4096, 0, 8]> = %alloc : !llvm.ptr
   llvm.intr.dbg.value #noNameVariable = %arg : i64
 
   // CHECK: call void @func_no_debug(), !dbg ![[CALLSITE_LOC:[0-9]+]]



More information about the Mlir-commits mailing list