[flang-commits] [flang] [mlir] [openacc] Implicitly map external constant globals (PR #210408)

via flang-commits flang-commits at lists.llvm.org
Fri Jul 17 13:49:55 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir-openacc

Author: Valentin Clement (バレンタイン クレメン) (clementval)

<details>
<summary>Changes</summary>

Hoist initializer-less constant globals out of OpenACC compute regions so implicit data mapping handles them. This avoids unresolved device symbols for PARAMETER arrays defined in separately compiled modules, while retaining implicit declare for initialized constants. Add regression coverage and a global initializer query to the OpenACC interface.

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


6 Files Affected:

- (modified) flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCOpsInterfaces.h (+1) 
- (modified) flang/lib/Optimizer/OpenACC/Support/FIROpenACCOpsInterfaces.cpp (+5) 
- (modified) mlir/include/mlir/Dialect/OpenACC/OpenACCOpsInterfaces.td (+4) 
- (modified) mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp (+5) 
- (modified) mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitDeclare.cpp (+20-12) 
- (modified) mlir/test/Dialect/OpenACC/acc-implicit-declare.mlir (+24) 


``````````diff
diff --git a/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCOpsInterfaces.h b/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCOpsInterfaces.h
index 31e1593e11325..f5307c6b4f046 100644
--- a/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCOpsInterfaces.h
+++ b/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCOpsInterfaces.h
@@ -68,6 +68,7 @@ struct GlobalVariableModel
     : public mlir::acc::GlobalVariableOpInterface::ExternalModel<
           GlobalVariableModel, fir::GlobalOp> {
   bool isConstant(mlir::Operation *op) const;
+  bool hasInitializer(mlir::Operation *op) const;
   mlir::Region *getInitRegion(mlir::Operation *op) const;
   bool isDeviceData(mlir::Operation *op) const;
 };
diff --git a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCOpsInterfaces.cpp b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCOpsInterfaces.cpp
index 8e4b55fe5f4a8..3fdfb0302fb74 100644
--- a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCOpsInterfaces.cpp
+++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCOpsInterfaces.cpp
@@ -121,6 +121,11 @@ bool GlobalVariableModel::isConstant(mlir::Operation *op) const {
   return globalOp.getConstant().has_value();
 }
 
+bool GlobalVariableModel::hasInitializer(mlir::Operation *op) const {
+  auto globalOp = mlir::cast<fir::GlobalOp>(op);
+  return globalOp.getInitVal().has_value() || globalOp.hasInitializationBody();
+}
+
 mlir::Region *GlobalVariableModel::getInitRegion(mlir::Operation *op) const {
   auto globalOp = mlir::cast<fir::GlobalOp>(op);
   return globalOp.hasInitializationBody() ? &globalOp.getRegion() : nullptr;
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCOpsInterfaces.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCOpsInterfaces.td
index fa44fe7a49158..eddd21a7e917b 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCOpsInterfaces.td
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCOpsInterfaces.td
@@ -73,6 +73,10 @@ def GlobalVariableOpInterface : OpInterface<"GlobalVariableOpInterface"> {
       "isConstant", (ins), [{
         return false;
       }]>,
+    InterfaceMethod<"Check if the global variable has an initializer", "bool",
+      "hasInitializer", (ins), [{
+        return false;
+      }]>,
     InterfaceMethod<"Get the initialization region (returns nullptr if none)",
       "::mlir::Region*", "getInitRegion", (ins)>,
     InterfaceMethod<"Check if the global variable is device data",
diff --git a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
index 0205afc1dfedb..cdf34234f06df 100644
--- a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
+++ b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
@@ -394,6 +394,11 @@ struct MemrefGlobalVariableModel
     return globalOp.getConstant();
   }
 
+  bool hasInitializer(Operation *op) const {
+    auto globalOp = cast<memref::GlobalOp>(op);
+    return globalOp.getInitialValue().has_value();
+  }
+
   Region *getInitRegion(Operation *op) const {
     // GlobalOp uses attributes for initialization, not regions
     return nullptr;
diff --git a/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitDeclare.cpp b/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitDeclare.cpp
index 3b8bf86fee11c..4d77353c20f56 100644
--- a/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitDeclare.cpp
+++ b/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitDeclare.cpp
@@ -45,10 +45,11 @@
 //
 // The pass performs two main optimizations:
 //
-// 1. Hoisting: For non-constant globals referenced in compute regions, the
-//    pass hoists the address-of operation out of the region when possible,
-//    allowing them to be implicitly mapped through normal data clause
-//    mechanisms rather than requiring declare marking.
+// 1. Hoisting: For non-constant globals and constant globals without a local
+//    initializer referenced in compute regions, the pass hoists the address-of
+//    operation out of the region when possible, allowing them to be implicitly
+//    mapped through normal data clause mechanisms rather than requiring
+//    declare marking.
 //
 // 2. Declaration: For globals that must be available on the device (constants,
 //    globals in routines, globals in recipe operations), the pass adds the
@@ -214,20 +215,27 @@ static bool isGlobalUseCandidateForHoisting(Operation *globalOp,
   if (accSupport.isValidSymbolUse(user, symbol))
     return false;
 
-  bool isConstant = false;
+  bool isInitializedConstant = false;
   bool isFunction = false;
 
   if (auto globalVarOp = dyn_cast<acc::GlobalVariableOpInterface>(globalOp))
-    isConstant = globalVarOp.isConstant();
+    isInitializedConstant =
+        globalVarOp.isConstant() && globalVarOp.hasInitializer();
 
   if (isa<FunctionOpInterface>(globalOp))
     isFunction = true;
 
-  // Constants should be kept in device code to ensure they are duplicated.
+  // Initialized constants should be kept in device code so their definitions
+  // can be duplicated in the device image. An initializer-less constant is an
+  // external declaration, so keeping its address-of in device code would
+  // create a device symbol that a separately compiled defining translation
+  // unit may not provide. Hoist it so normal implicit mapping passes its host
+  // definition to the compute region instead.
+  //
   // Function references should be kept in device code to ensure their device
   // addresses are computed. Everything else should be hoisted since we already
-  // proved they are not valid symbols in GPU region.
-  return !isConstant && !isFunction;
+  // proved it is not a valid symbol in the GPU region.
+  return !isInitializedConstant && !isFunction;
 }
 
 /// Checks whether it is valid to use acc.declare marking on the global.
@@ -259,9 +267,9 @@ static bool hasRelevantRecipeUse(RecipeOpT &recipeOp, ModuleOp &mod) {
   return use.getUser() != recipeOp.getOperation();
 }
 
-// Hoists addr_of operations for non-constant globals out of OpenACC regions.
-// This way - they are implicitly mapped instead of being considered for
-// implicit declare.
+// Hoists addr_of operations for globals that cannot be defined in this
+// translation unit out of OpenACC regions. This way they are implicitly mapped
+// instead of being considered for implicit declare.
 template <typename AccConstructT>
 static void hoistNonConstantDirectUses(AccConstructT accOp,
                                        acc::OpenACCSupport &accSupport) {
diff --git a/mlir/test/Dialect/OpenACC/acc-implicit-declare.mlir b/mlir/test/Dialect/OpenACC/acc-implicit-declare.mlir
index b1c9aa9016dc3..b8ab5c89cd7a5 100644
--- a/mlir/test/Dialect/OpenACC/acc-implicit-declare.mlir
+++ b/mlir/test/Dialect/OpenACC/acc-implicit-declare.mlir
@@ -43,6 +43,30 @@ func.func @test_constant_in_serial() {
 
 // -----
 
+// Test that an external constant global is hoisted for implicit mapping. Its
+// definition may be in a separately compiled translation unit that does not
+// contain an OpenACC construct and therefore does not emit a device definition.
+
+memref.global constant @gexternalconstant : memref<4xf32>
+
+func.func @test_external_constant_in_serial() {
+  acc.serial {
+    %c0 = arith.constant 0 : index
+    %addr = memref.get_global @gexternalconstant : memref<4xf32>
+    %load = memref.load %addr[%c0] : memref<4xf32>
+    acc.yield
+  }
+  return
+}
+
+// CHECK: memref.global constant @gexternalconstant : memref<4xf32>
+// CHECK-NOT: acc.declare
+// CHECK-LABEL: func.func @test_external_constant_in_serial
+// CHECK: memref.get_global @gexternalconstant
+// CHECK-NEXT: acc.serial
+
+// -----
+
 // Test globals referenced in acc routine functions
 
 memref.global @gscalar_routine : memref<f32> = dense<0.0>

``````````

</details>


https://github.com/llvm/llvm-project/pull/210408


More information about the flang-commits mailing list