[flang-commits] [flang] [flang][cuda] Add extra constructor option to CUFAddConstructor (PR #225181)
via flang-commits
flang-commits at lists.llvm.org
Mon Sep 21 13:03:08 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-fir-hlfir
Author: Valentin Clement (バレンタイン クレメン) (clementval)
<details>
<summary>Changes</summary>
Add an option to add extra constructors to the `llvm.mlir.global_ctors`. The option is taking a pair of string and a boolean. The boolean is telling if the constructor needs to be added always or only when `PROGRAM` statement is present in the translation unit. This is used to set up sentinels in different compilation mode.
---
Full diff: https://github.com/llvm/llvm-project/pull/225181.diff
4 Files Affected:
- (modified) flang/include/flang/Optimizer/Transforms/Passes.h (+1)
- (modified) flang/include/flang/Optimizer/Transforms/Passes.td (+6-1)
- (modified) flang/lib/Optimizer/Transforms/CUDA/CUFAddConstructor.cpp (+53-7)
- (added) flang/test/Fir/CUDA/cuda-constructor-extra.fir (+36)
``````````diff
diff --git a/flang/include/flang/Optimizer/Transforms/Passes.h b/flang/include/flang/Optimizer/Transforms/Passes.h
index 83aef1e8fdb55..5271d17514ddc 100644
--- a/flang/include/flang/Optimizer/Transforms/Passes.h
+++ b/flang/include/flang/Optimizer/Transforms/Passes.h
@@ -14,6 +14,7 @@
#include "mlir/Pass/Pass.h"
#include "mlir/Pass/PassRegistry.h"
#include <memory>
+#include <utility>
namespace mlir {
class IRMapping;
diff --git a/flang/include/flang/Optimizer/Transforms/Passes.td b/flang/include/flang/Optimizer/Transforms/Passes.td
index 653f490715707..0bb2aacc61d75 100644
--- a/flang/include/flang/Optimizer/Transforms/Passes.td
+++ b/flang/include/flang/Optimizer/Transforms/Passes.td
@@ -634,7 +634,12 @@ def CUFAddConstructor : Pass<"cuf-add-constructor", "mlir::ModuleOp"> {
Option<"emitCudaCompiled", "emit-cuda-compiled", "bool",
/*default=*/"false",
"Declare an undefined cuda_compiled symbol so linking without the "
- "CUDA Fortran runtime fails with a clear diagnostic.">
+ "CUDA Fortran runtime fails with a clear diagnostic.">,
+ ListOption<"extraConstructors", "extra-constructors",
+ "std::pair<std::string, bool>",
+ "Constructors to add to llvm.mlir.global_ctors. Entries use "
+ "<name>:<if-main>, where if-main is true to add the function "
+ "only when the module contains the Fortran program entry.">
];
}
diff --git a/flang/lib/Optimizer/Transforms/CUDA/CUFAddConstructor.cpp b/flang/lib/Optimizer/Transforms/CUDA/CUFAddConstructor.cpp
index 4f6613bc8017f..b00f05b9fd3a8 100644
--- a/flang/lib/Optimizer/Transforms/CUDA/CUFAddConstructor.cpp
+++ b/flang/lib/Optimizer/Transforms/CUDA/CUFAddConstructor.cpp
@@ -28,6 +28,46 @@
#include "mlir/IR/Value.h"
#include "mlir/Pass/Pass.h"
#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/CommandLine.h"
+
+#include <string>
+#include <utility>
+
+namespace llvm::cl {
+template <>
+class parser<std::pair<std::string, bool>>
+ : public basic_parser<std::pair<std::string, bool>> {
+public:
+ parser(Option &option) : basic_parser(option) {}
+
+ bool parse(Option &option, StringRef argName, StringRef arg,
+ std::pair<std::string, bool> &value) {
+ auto [name, flagStr] = arg.rsplit(':');
+ if (name.empty() || flagStr.empty())
+ return option.error("expected <name>:<bool>", argName);
+
+ bool ifMain = false;
+ if (flagStr.equals_insensitive("true") || flagStr == "1")
+ ifMain = true;
+ else if (flagStr.equals_insensitive("false") || flagStr == "0")
+ ifMain = false;
+ else
+ return option.error("invalid boolean in extra constructor mapping",
+ argName);
+
+ value = {name.str(), ifMain};
+ return false;
+ }
+
+ StringRef getValueName() const override { return "name:bool"; }
+
+ static void print(raw_ostream &os,
+ const std::pair<std::string, bool> &value) {
+ os << value.first << ':' << (value.second ? "true" : "false");
+ }
+};
+} // namespace llvm::cl
namespace fir {
#define GEN_PASS_DEF_CUFADDCONSTRUCTOR
@@ -321,10 +361,10 @@ struct CUFAddConstructor
// Create the constructor function that call CUFRegisterAllocator.
builder.setInsertionPointToEnd(mod.getBody());
mlir::LLVM::GlobalOp cudaCompiledGlobal;
- // Only the program unit needs the link-time CUDA Fortran runtime check.
- bool emitCudaCompiledMarker =
- emitCudaCompiled &&
+ bool hasProgramEntry =
symTab.lookup<mlir::func::FuncOp>(fir::NameUniquer::doProgramEntry());
+ // Only the program unit needs the link-time CUDA Fortran runtime check.
+ bool emitCudaCompiledMarker = emitCudaCompiled && hasProgramEntry;
if (emitCudaCompiledMarker) {
// Undefined sentinel: objects compiled as CUDA Fortran reference this
// symbol so linking without the CUDA Fortran runtime produces
@@ -497,12 +537,18 @@ struct CUFAddConstructor
// created and adds new functions.
builder.setInsertionPointToEnd(mod.getBody());
llvm::SmallVector<mlir::Attribute> funcs;
- funcs.push_back(
- mlir::FlatSymbolRefAttr::get(mod.getContext(), func.getSymName()));
llvm::SmallVector<int> priorities;
llvm::SmallVector<mlir::Attribute> data;
- priorities.push_back(priority);
- data.push_back(mlir::LLVM::ZeroAttr::get(mod.getContext()));
+ auto addCtor = [&](llvm::StringRef name) {
+ funcs.push_back(mlir::FlatSymbolRefAttr::get(mod.getContext(), name));
+ priorities.push_back(priority);
+ data.push_back(mlir::LLVM::ZeroAttr::get(mod.getContext()));
+ };
+ addCtor(func.getSymName());
+ for (const auto &[funcName, ifMain] : extraConstructors) {
+ if (!ifMain || hasProgramEntry)
+ addCtor(funcName);
+ }
mlir::LLVM::GlobalCtorsOp::create(
builder, mod.getLoc(), builder.getArrayAttr(funcs),
builder.getI32ArrayAttr(priorities), builder.getArrayAttr(data));
diff --git a/flang/test/Fir/CUDA/cuda-constructor-extra.fir b/flang/test/Fir/CUDA/cuda-constructor-extra.fir
new file mode 100644
index 0000000000000..47c0d5b041ee0
--- /dev/null
+++ b/flang/test/Fir/CUDA/cuda-constructor-extra.fir
@@ -0,0 +1,36 @@
+// RUN: fir-opt --split-input-file --cuf-add-constructor="extra-constructors=foo:false,bar:true" %s | FileCheck %s
+
+// extra-constructors is a list of <name>:<if-main> pairs. if-main=false
+// always appends the function to llvm.mlir.global_ctors; if-main=true
+// appends it only when the module contains the Fortran program entry
+// (_QQmain). Each extra ctor reuses the same priority and #llvm.zero
+// data as the CUDA Fortran constructor.
+
+module attributes {dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<!llvm.ptr, dense<64> : vector<4xi64>>, #dlti.dl_entry<i8, dense<8> : vector<2xi64>>, #dlti.dl_entry<i64, dense<64> : vector<2xi64>>, #dlti.dl_entry<"dlti.endianness", "little">>, fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", llvm.data_layout = "e-m:e-i64:64-i128:128-n8:16:32:64-S128"} {
+ llvm.func @foo() {
+ llvm.return
+ }
+ llvm.func @bar() {
+ llvm.return
+ }
+}
+
+// CHECK: llvm.func internal @__cudaFortranConstructor()
+// CHECK: llvm.mlir.global_ctors ctors = [@__cudaFortranConstructor, @foo], priorities = [0 : i32, 0 : i32], data = [#llvm.zero, #llvm.zero]
+
+// -----
+
+module attributes {dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<!llvm.ptr, dense<64> : vector<4xi64>>, #dlti.dl_entry<i8, dense<8> : vector<2xi64>>, #dlti.dl_entry<i64, dense<64> : vector<2xi64>>, #dlti.dl_entry<"dlti.endianness", "little">>, fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", llvm.data_layout = "e-m:e-i64:64-i128:128-n8:16:32:64-S128"} {
+ func.func @_QQmain() {
+ return
+ }
+ llvm.func @foo() {
+ llvm.return
+ }
+ llvm.func @bar() {
+ llvm.return
+ }
+}
+
+// CHECK: llvm.func internal @__cudaFortranConstructor()
+// CHECK: llvm.mlir.global_ctors ctors = [@__cudaFortranConstructor, @foo, @bar], priorities = [0 : i32, 0 : i32, 0 : i32], data = [#llvm.zero, #llvm.zero, #llvm.zero]
``````````
</details>
https://github.com/llvm/llvm-project/pull/225181
More information about the flang-commits
mailing list