[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