[flang-commits] [flang] 198cabe - [flang][cuda] Exclude device variables from host debug info (#222241)

via flang-commits flang-commits at lists.llvm.org
Mon Sep 14 12:01:35 PDT 2026


Author: jiel-nv
Date: 2026-09-14T12:01:29-07:00
New Revision: 198cabe1180311b0ec9c1969632e9647c316729c

URL: https://github.com/llvm/llvm-project/commit/198cabe1180311b0ec9c1969632e9647c316729c
DIFF: https://github.com/llvm/llvm-project/commit/198cabe1180311b0ec9c1969632e9647c316729c.diff

LOG: [flang][cuda] Exclude device variables from host debug info (#222241)

Added: 
    flang/test/Transforms/debug-cuf-device-variable.fir

Modified: 
    flang/include/flang/Optimizer/Dialect/FIRCG/CGOps.h
    flang/include/flang/Optimizer/Dialect/FIRCG/CGOps.td
    flang/lib/Optimizer/CodeGen/PreCGRewrite.cpp
    flang/lib/Optimizer/Dialect/FIRCG/CMakeLists.txt
    flang/lib/Optimizer/Transforms/AddDebugInfo.cpp
    flang/test/Fir/declare-codegen.fir

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Optimizer/Dialect/FIRCG/CGOps.h b/flang/include/flang/Optimizer/Dialect/FIRCG/CGOps.h
index e2795d8a136b8..67c14e944ec56 100644
--- a/flang/include/flang/Optimizer/Dialect/FIRCG/CGOps.h
+++ b/flang/include/flang/Optimizer/Dialect/FIRCG/CGOps.h
@@ -13,6 +13,7 @@
 #ifndef OPTIMIZER_DIALECT_FIRCG_CGOPS_H
 #define OPTIMIZER_DIALECT_FIRCG_CGOPS_H
 
+#include "flang/Optimizer/Dialect/CUF/Attributes/CUFAttr.h"
 #include "flang/Optimizer/Dialect/FIRType.h"
 #include "mlir/Dialect/Func/IR/FuncOps.h"
 

diff  --git a/flang/include/flang/Optimizer/Dialect/FIRCG/CGOps.td b/flang/include/flang/Optimizer/Dialect/FIRCG/CGOps.td
index f3c72eaf44958..5a7c85e7883fa 100644
--- a/flang/include/flang/Optimizer/Dialect/FIRCG/CGOps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIRCG/CGOps.td
@@ -15,6 +15,7 @@
 #define FORTRAN_DIALECT_FIRCG_OPS
 
 include "mlir/IR/SymbolInterfaces.td"
+include "flang/Optimizer/Dialect/CUF/Attributes/CUFAttr.td"
 include "flang/Optimizer/Dialect/FIRTypes.td"
 include "flang/Optimizer/Dialect/FIRAttr.td"
 include "mlir/IR/BuiltinAttributes.td"
@@ -236,6 +237,7 @@ def fircg_XDeclareOp : fircg_Op<"ext_declare", [AttrSizedOperandSegments]> {
       Optional<AnyReferenceLike>:$storage,
       DefaultValuedAttr<UI64Attr, "0">:$storage_offset,
       Builtin_StringAttr:$uniq_name,
+      OptionalAttr<cuf_DataAttributeAttr>:$data_attr,
       OptionalAttr<UI32Attr>:$dummy_arg_no);
   let results = (outs AnyRefOrBox);
 

diff  --git a/flang/lib/Optimizer/CodeGen/PreCGRewrite.cpp b/flang/lib/Optimizer/CodeGen/PreCGRewrite.cpp
index ee3b8362b5ef6..f4ae6841afc12 100644
--- a/flang/lib/Optimizer/CodeGen/PreCGRewrite.cpp
+++ b/flang/lib/Optimizer/CodeGen/PreCGRewrite.cpp
@@ -388,12 +388,12 @@ class DeclareOpConversion : public mlir::OpRewritePattern<fir::DeclareOp> {
     mlir::IntegerAttr dummyArgNoAttr;
     if (auto attr = declareOp->getAttrOfType<mlir::IntegerAttr>("dummy_arg_no"))
       dummyArgNoAttr = attr;
-    // FIXME: Add FortranAttrs and CudaAttrs
+    // FIXME: Add FortranAttrs
     auto xDeclOp = fir::cg::XDeclareOp::create(
         rewriter, loc, declareOp.getType(), declareOp.getMemref(), shapeOpers,
         shiftOpers, declareOp.getTypeparams(), declareOp.getDummyScope(),
         declareOp.getStorage(), declareOp.getStorageOffset(),
-        declareOp.getUniqName(), dummyArgNoAttr);
+        declareOp.getUniqName(), declareOp.getDataAttrAttr(), dummyArgNoAttr);
     LLVM_DEBUG(llvm::dbgs()
                << "rewriting " << declareOp << " to " << xDeclOp << '\n');
     rewriter.replaceOp(declareOp, xDeclOp.getOperation()->getResults());

diff  --git a/flang/lib/Optimizer/Dialect/FIRCG/CMakeLists.txt b/flang/lib/Optimizer/Dialect/FIRCG/CMakeLists.txt
index cd5865fc8766a..24b62f3b23b36 100644
--- a/flang/lib/Optimizer/Dialect/FIRCG/CMakeLists.txt
+++ b/flang/lib/Optimizer/Dialect/FIRCG/CMakeLists.txt
@@ -3,6 +3,7 @@ add_flang_library(FIRCodeGenDialect
 
   DEPENDS
   CGOpsIncGen
+  CUFAttrsIncGen
 
   LINK_LIBS
   FIRDialect

diff  --git a/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp b/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp
index c911827b72a7c..045caafa3e5f9 100644
--- a/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp
+++ b/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "flang/Optimizer/Builder/FIRBuilder.h"
+#include "flang/Optimizer/Dialect/CUF/Attributes/CUFAttr.h"
 #include "flang/Optimizer/Dialect/FIRCG/CGOps.h"
 #include "flang/Optimizer/Dialect/FIRDialect.h"
 #include "flang/Optimizer/Dialect/FIROps.h"
@@ -199,6 +200,46 @@ static bool isModuleLevelName(const fir::NameUniquer::DeconstructedName &name) {
   return name.procs.empty() && !name.modules.empty();
 }
 
+// Check if the storage of a variable with the given CUDA Fortran data
+// attribute can be addressed by the host.
+static bool isHostAddressable(cuf::DataAttributeAttr dataAttr) {
+  if (!dataAttr)
+    return true;
+  switch (dataAttr.getValue()) {
+  case cuf::DataAttribute::Constant:
+  case cuf::DataAttribute::Device:
+  case cuf::DataAttribute::Shared:
+    return false;
+  case cuf::DataAttribute::Managed:
+  case cuf::DataAttribute::Pinned:
+  case cuf::DataAttribute::Unified:
+    return true;
+  }
+  llvm_unreachable("unknown CUDA Fortran data attribute");
+}
+
+// Check if the operation belongs to a procedure that is compiled for the
+// device only, whose debug info is generated separately.
+static bool isInDeviceOnlyProcedure(mlir::Operation *op) {
+  auto funcOp = op->getParentOfType<mlir::func::FuncOp>();
+  if (!funcOp)
+    return false;
+  auto procAttr =
+      funcOp->getAttrOfType<cuf::ProcAttributeAttr>(cuf::getProcAttrName());
+  if (!procAttr)
+    return false;
+  switch (procAttr.getValue()) {
+  case cuf::ProcAttribute::Host:
+  case cuf::ProcAttribute::HostDevice:
+    return false;
+  case cuf::ProcAttribute::Device:
+  case cuf::ProcAttribute::Global:
+  case cuf::ProcAttribute::GridGlobal:
+    return true;
+  }
+  llvm_unreachable("unknown CUDA Fortran procedure attribute");
+}
+
 // Check if a global represents a data object declared in a module.
 static bool isModuleDataObject(fir::GlobalOp globalOp) {
   std::pair result = fir::NameUniquer::deconstruct(globalOp.getSymName());
@@ -337,6 +378,11 @@ void AddDebugInfoPass::handleLocalVariable(Op declOp, llvm::StringRef name,
                                            mlir::Value dummyScope,
                                            mlir::Type typeToConvert,
                                            fir::cg::XDeclareOp typeGenDeclOp) {
+  // Exclude variables that the host cannot address.
+  if (!isInDeviceOnlyProcedure(declOp) &&
+      !isHostAddressable(declOp.getDataAttrAttr()))
+    return;
+
   mlir::MLIRContext *context = &getContext();
   mlir::OpBuilder builder(context);
 

diff  --git a/flang/test/Fir/declare-codegen.fir b/flang/test/Fir/declare-codegen.fir
index 9413525a5dbd3..934fb47d3bde3 100644
--- a/flang/test/Fir/declare-codegen.fir
+++ b/flang/test/Fir/declare-codegen.fir
@@ -72,3 +72,12 @@ fir.global @common_block : !fir.array<8xi8>
 // DECL: %[[STORAGE:.*]] = fir.address_of(@common_block) : !fir.ref<!fir.array<8xi8>>
 // DECL: fircg.ext_declare {{.*}} storage(%[[STORAGE]][0]) {uniq_name = "_QFEx"}
 // DECL: fircg.ext_declare {{.*}} storage(%[[STORAGE]][4]) {uniq_name = "_QFEy"}
+
+// Test that the CUDA Fortran data attribute is preserved during conversion
+func.func @test_data_attr(%arg0: !fir.ref<i32>) {
+  %0 = fir.declare %arg0 {data_attr = #cuf.cuda<device>, uniq_name = "_QFEd"} : (!fir.ref<i32>) -> !fir.ref<i32>
+  return
+}
+
+// DECL-LABEL: func.func @test_data_attr(
+// DECL: fircg.ext_declare {{.*}} {data_attr = #cuf.cuda<device>, uniq_name = "_QFEd"}

diff  --git a/flang/test/Transforms/debug-cuf-device-variable.fir b/flang/test/Transforms/debug-cuf-device-variable.fir
new file mode 100644
index 0000000000000..282dc24169c04
--- /dev/null
+++ b/flang/test/Transforms/debug-cuf-device-variable.fir
@@ -0,0 +1,100 @@
+// RUN: fir-opt --add-debug-info --mlir-print-debuginfo --mlir-print-local-scope %s | FileCheck %s
+
+module {
+  func.func @_QPhost(%arg0: !fir.ref<i32> {fir.bindc_name = "a"}, %arg1: !fir.ref<i32> {fir.bindc_name = "a_d", cuf.data_attr = #cuf.cuda<device>}) {
+    %0 = fir.undefined !fir.dscope
+    %1 = fircg.ext_declare %arg0 dummy_scope %0 arg 1 {uniq_name = "_QFhostEa"} : (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32> loc(#loc1)
+    %2 = fircg.ext_declare %arg1 dummy_scope %0 arg 2 {data_attr = #cuf.cuda<device>, uniq_name = "_QFhostEa_d"} : (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32> loc(#loc2)
+    return
+  } loc(#loc3)
+
+  func.func @_QPhostlocals() {
+    %0 = fir.alloca i32 {uniq_name = "_QFhostlocalsEd"}
+    %1 = fircg.ext_declare %0 {data_attr = #cuf.cuda<device>, uniq_name = "_QFhostlocalsEd"} : (!fir.ref<i32>) -> !fir.ref<i32> loc(#loc4)
+    %2 = fir.alloca i32 {uniq_name = "_QFhostlocalsEc"}
+    %3 = fircg.ext_declare %2 {data_attr = #cuf.cuda<constant>, uniq_name = "_QFhostlocalsEc"} : (!fir.ref<i32>) -> !fir.ref<i32> loc(#loc5)
+    %4 = fir.alloca i32 {uniq_name = "_QFhostlocalsEm"}
+    %5 = fircg.ext_declare %4 {data_attr = #cuf.cuda<managed>, uniq_name = "_QFhostlocalsEm"} : (!fir.ref<i32>) -> !fir.ref<i32> loc(#loc6)
+    %6 = fir.alloca i32 {uniq_name = "_QFhostlocalsEp"}
+    %7 = fircg.ext_declare %6 {data_attr = #cuf.cuda<pinned>, uniq_name = "_QFhostlocalsEp"} : (!fir.ref<i32>) -> !fir.ref<i32> loc(#loc7)
+    %8 = fir.alloca i32 {uniq_name = "_QFhostlocalsEu"}
+    %9 = fircg.ext_declare %8 {data_attr = #cuf.cuda<unified>, uniq_name = "_QFhostlocalsEu"} : (!fir.ref<i32>) -> !fir.ref<i32> loc(#loc8)
+    return
+  } loc(#loc9)
+
+  func.func @_QPkernel(%arg0: !fir.ref<i32> {fir.bindc_name = "k_d", cuf.data_attr = #cuf.cuda<device>}) attributes {cuf.proc_attr = #cuf.cuda_proc<global>} {
+    %0 = fir.undefined !fir.dscope
+    %1 = fircg.ext_declare %arg0 dummy_scope %0 arg 1 {data_attr = #cuf.cuda<device>, uniq_name = "_QFkernelEk_d"} : (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32> loc(#loc10)
+    %2 = fir.alloca i32 {uniq_name = "_QFkernelEs"}
+    %3 = fircg.ext_declare %2 {data_attr = #cuf.cuda<shared>, uniq_name = "_QFkernelEs"} : (!fir.ref<i32>) -> !fir.ref<i32> loc(#loc11)
+    return
+  } loc(#loc12)
+
+  func.func @_QPhostdevice(%arg0: !fir.ref<i32> {fir.bindc_name = "x"}) attributes {cuf.proc_attr = #cuf.cuda_proc<host_device>} {
+    %0 = fir.undefined !fir.dscope
+    %1 = fircg.ext_declare %arg0 dummy_scope %0 arg 1 {uniq_name = "_QFhostdeviceEx"} : (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32> loc(#loc13)
+    %2 = fir.alloca i32 {uniq_name = "_QFhostdeviceEd"}
+    %3 = fircg.ext_declare %2 {data_attr = #cuf.cuda<device>, uniq_name = "_QFhostdeviceEd"} : (!fir.ref<i32>) -> !fir.ref<i32> loc(#loc14)
+    return
+  } loc(#loc15)
+
+  func.func @_QPsave() {
+    %0 = fir.address_of(@_QFsaveEs_d) : !fir.ref<!fir.box<!fir.heap<i32>>>
+    %1 = fircg.ext_declare %0 {data_attr = #cuf.cuda<device>, uniq_name = "_QFsaveEs_d"} : (!fir.ref<!fir.box<!fir.heap<i32>>>) -> !fir.ref<!fir.box<!fir.heap<i32>>> loc(#loc16)
+    return
+  } loc(#loc17)
+
+  fir.global internal @_QFsaveEs_d {data_attr = #cuf.cuda<device>} : !fir.box<!fir.heap<i32>> {
+    %0 = fir.zero_bits !fir.heap<i32>
+    %1 = fir.embox %0 {allocator_idx = 2 : i32} : (!fir.heap<i32>) -> !fir.box<!fir.heap<i32>>
+    fir.has_value %1 : !fir.box<!fir.heap<i32>>
+  } loc(#loc18)
+}
+
+#loc1 = loc("test.cuf":3:1)
+#loc2 = loc("test.cuf":4:1)
+#loc3 = loc("test.cuf":2:1)
+#loc4 = loc("test.cuf":9:1)
+#loc5 = loc("test.cuf":10:1)
+#loc6 = loc("test.cuf":11:1)
+#loc7 = loc("test.cuf":12:1)
+#loc8 = loc("test.cuf":13:1)
+#loc9 = loc("test.cuf":8:1)
+#loc10 = loc("test.cuf":18:1)
+#loc11 = loc("test.cuf":19:1)
+#loc12 = loc("test.cuf":17:1)
+#loc13 = loc("test.cuf":24:1)
+#loc14 = loc("test.cuf":25:1)
+#loc15 = loc("test.cuf":23:1)
+#loc16 = loc("test.cuf":30:1)
+#loc17 = loc("test.cuf":29:1)
+#loc18 = loc("test.cuf":30:1)
+
+// A variable that gets debug info ends up with a fused loc carrying
+// #llvm.di_local_variable, while a filtered one keeps its plain source loc.
+
+// CHECK-LABEL: func.func @_QPhost
+// CHECK: ext_declare{{.*}}uniq_name = "_QFhostEa"{{.*}}loc(fused<#llvm.di_local_variable<
+// CHECK: ext_declare{{.*}}uniq_name = "_QFhostEa_d"{{.*}}loc("test.cuf":4:1)
+
+// CHECK-LABEL: func.func @_QPhostlocals
+// CHECK: ext_declare{{.*}}uniq_name = "_QFhostlocalsEd"{{.*}}loc("test.cuf":9:1)
+// CHECK: ext_declare{{.*}}uniq_name = "_QFhostlocalsEc"{{.*}}loc("test.cuf":10:1)
+// CHECK: ext_declare{{.*}}uniq_name = "_QFhostlocalsEm"{{.*}}loc(fused<#llvm.di_local_variable<
+// CHECK: ext_declare{{.*}}uniq_name = "_QFhostlocalsEp"{{.*}}loc(fused<#llvm.di_local_variable<
+// CHECK: ext_declare{{.*}}uniq_name = "_QFhostlocalsEu"{{.*}}loc(fused<#llvm.di_local_variable<
+
+// CHECK-LABEL: func.func @_QPkernel
+// CHECK: ext_declare{{.*}}uniq_name = "_QFkernelEk_d"{{.*}}loc(fused<#llvm.di_local_variable<
+// CHECK: ext_declare{{.*}}uniq_name = "_QFkernelEs"{{.*}}loc(fused<#llvm.di_local_variable<
+
+// CHECK-LABEL: func.func @_QPhostdevice
+// CHECK: ext_declare{{.*}}uniq_name = "_QFhostdeviceEx"{{.*}}loc(fused<#llvm.di_local_variable<
+// CHECK: ext_declare{{.*}}uniq_name = "_QFhostdeviceEd"{{.*}}loc("test.cuf":25:1)
+
+// A device variable with SAVE is lowered to a global, leave it unfiltered.
+
+// CHECK-LABEL: func.func @_QPsave
+// CHECK: ext_declare{{.*}}uniq_name = "_QFsaveEs_d"{{.*}}loc("test.cuf":30:1)
+// CHECK: fir.global internal @_QFsaveEs_d
+// CHECK: } loc(fused<[#llvm.di_global_variable_expression<


        


More information about the flang-commits mailing list