[Mlir-commits] [mlir] [mlir][openacc] Add extra-constructors option to declare ctor conversion (PR #225194)

Valentin Clement バレンタイン クレメン llvmlistbot at llvm.org
Mon Sep 21 14:00:46 PDT 2026


https://github.com/clementval created https://github.com/llvm/llvm-project/pull/225194

Allow acc-declare-ctor-dtor-conversion to declare extra runtime functions
and call them from a generated constructor. Entries are <name>:<if-main>
pairs; if-main functions are emitted only when program-entry-name is
present in the module.

The extra functions stay external declarations. This is used to generate
sentinel in different compilation mode. 

>From 07615c13d2f10f21d052d53ab6bd08963dfbb198 Mon Sep 17 00:00:00 2001
From: Valentin Clement <clementval at gmail.com>
Date: Mon, 21 Sep 2026 13:59:15 -0700
Subject: [PATCH] [mlir][openacc] Add extra-constructors option to declare ctor
 conversion

---
 .../mlir/Dialect/OpenACC/Transforms/Passes.h  |  1 +
 .../mlir/Dialect/OpenACC/Transforms/Passes.td | 13 ++-
 .../ACCDeclareCtorDtorConversion.cpp          | 87 +++++++++++++++++++
 .../OpenACC/acc-declare-ctor-dtor-extra.mlir  | 61 +++++++++++++
 4 files changed, 161 insertions(+), 1 deletion(-)
 create mode 100644 mlir/test/Dialect/OpenACC/acc-declare-ctor-dtor-extra.mlir

diff --git a/mlir/include/mlir/Dialect/OpenACC/Transforms/Passes.h b/mlir/include/mlir/Dialect/OpenACC/Transforms/Passes.h
index 4751da91c6090..8c2f8e935a402 100644
--- a/mlir/include/mlir/Dialect/OpenACC/Transforms/Passes.h
+++ b/mlir/include/mlir/Dialect/OpenACC/Transforms/Passes.h
@@ -15,6 +15,7 @@
 #include "mlir/Dialect/SCF/IR/SCF.h"
 #include "mlir/Pass/Pass.h"
 #include "llvm/ADT/SetVector.h"
+#include <utility>
 
 namespace mlir {
 
diff --git a/mlir/include/mlir/Dialect/OpenACC/Transforms/Passes.td b/mlir/include/mlir/Dialect/OpenACC/Transforms/Passes.td
index 58e68f55c5010..609b3a7f38edb 100644
--- a/mlir/include/mlir/Dialect/OpenACC/Transforms/Passes.td
+++ b/mlir/include/mlir/Dialect/OpenACC/Transforms/Passes.td
@@ -180,7 +180,18 @@ def ACCDeclareCtorDtorConversion
     Option<"priority", "priority", "int32_t", /*default=*/"102",
            "Priority for ACC declare global constructors and destructors">,
     Option<"generateDtors", "generate-dtors", "bool", /*default=*/"true",
-           "Generate LLVM destructor functions and register them globally">
+           "Generate LLVM destructor functions and register them globally">,
+    ListOption<"extraConstructors", "extra-constructors",
+               "std::pair<std::string, bool>",
+               "Extra functions to declare and call from an OpenACC "
+               "constructor. Entries use <name>:<if-main>, where if-main is "
+               "true to call the function only when the module contains "
+               "program-entry-name.">,
+    Option<"programEntryName", "program-entry-name", "std::string",
+           /*default=*/"",
+           "Symbol name of the program entry point. Extra constructors with "
+           "if-main=true are emitted only when this symbol is present. Empty "
+           "means if-main constructors are never emitted.">
   ];
   let dependentDialects = ["mlir::LLVM::LLVMDialect"];
 }
diff --git a/mlir/lib/Dialect/OpenACC/Transforms/ACCDeclareCtorDtorConversion.cpp b/mlir/lib/Dialect/OpenACC/Transforms/ACCDeclareCtorDtorConversion.cpp
index c44f87f94bdcb..6f90f60269f0c 100644
--- a/mlir/lib/Dialect/OpenACC/Transforms/ACCDeclareCtorDtorConversion.cpp
+++ b/mlir/lib/Dialect/OpenACC/Transforms/ACCDeclareCtorDtorConversion.cpp
@@ -17,6 +17,46 @@
 #include "mlir/IR/Builders.h"
 #include "mlir/IR/IRMapping.h"
 #include "mlir/Pass/Pass.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 mlir {
 namespace acc {
@@ -117,6 +157,36 @@ static LLVM::LLVMFuncOp createLLVMFunctionFromRegion(StringRef symName,
   return newFunc;
 }
 
+static constexpr llvm::StringRef kExtraCtorName{"__openaccExtraConstructor"};
+
+/// Declare extra runtime functions and call them from a defined llvm.func
+/// registered in llvm.mlir.global_ctors. The extra functions themselves stay
+/// declarations: llvm.mlir.global_ctors requires a function with a body.
+static LLVM::LLVMFuncOp
+createExtraConstructorCaller(ModuleOp mod, OpBuilder &builder,
+                             ArrayRef<std::string> extraNames) {
+  auto llvmVoidTy = LLVM::LLVMVoidType::get(mod.getContext());
+  auto funcTy = LLVM::LLVMFunctionType::get(llvmVoidTy, {}, /*isVarArg=*/false);
+
+  builder.setInsertionPointToEnd(mod.getBody());
+  for (const std::string &name : extraNames) {
+    if (mod.lookupSymbol<LLVM::LLVMFuncOp>(name))
+      continue;
+    auto decl = LLVM::LLVMFuncOp::create(builder, mod.getLoc(), name, funcTy);
+    decl.setVisibility(SymbolTable::Visibility::Private);
+  }
+
+  auto wrapper = LLVM::LLVMFuncOp::create(builder, mod.getLoc(), kExtraCtorName,
+                                          funcTy, LLVM::Linkage::Internal);
+  Block *entry = wrapper.addEntryBlock(builder);
+  builder.setInsertionPointToStart(entry);
+  for (const std::string &name : extraNames)
+    LLVM::CallOp::create(builder, mod.getLoc(), funcTy,
+                         FlatSymbolRefAttr::get(mod.getContext(), name));
+  LLVM::ReturnOp::create(builder, mod.getLoc(), ValueRange{});
+  return wrapper;
+}
+
 struct ACCDeclareCtorDtorConversion
     : public acc::impl::ACCDeclareCtorDtorConversionBase<
           ACCDeclareCtorDtorConversion> {
@@ -167,6 +237,23 @@ struct ACCDeclareCtorDtorConversion
       worklist.push_back(op.getOperation());
     });
 
+    bool hasProgramEntry =
+        !programEntryName.empty() &&
+        static_cast<bool>(mod.lookupSymbol(programEntryName));
+    SmallVector<std::string, 4> extraNames;
+    for (const auto &[funcName, ifMain] : extraConstructors) {
+      if (!ifMain || hasProgramEntry)
+        extraNames.push_back(funcName);
+    }
+    if (!extraNames.empty()) {
+      LLVM::LLVMFuncOp extraCtor =
+          createExtraConstructorCaller(mod, builder, extraNames);
+      allCtors.push_back(
+          FlatSymbolRefAttr::get(mod.getContext(), extraCtor.getSymName()));
+      ctorPriorities.push_back(priority);
+      ctorData.push_back(LLVM::ZeroAttr::get(builder.getContext()));
+    }
+
     if (allCtors.size() > existingCtorCount)
       replaceGlobalCtors(mod, builder, allCtors, ctorPriorities, ctorData,
                          globalCtorsOps);
diff --git a/mlir/test/Dialect/OpenACC/acc-declare-ctor-dtor-extra.mlir b/mlir/test/Dialect/OpenACC/acc-declare-ctor-dtor-extra.mlir
new file mode 100644
index 0000000000000..41e177eaa9dbd
--- /dev/null
+++ b/mlir/test/Dialect/OpenACC/acc-declare-ctor-dtor-extra.mlir
@@ -0,0 +1,61 @@
+// RUN: mlir-opt %s --pass-pipeline='builtin.module(acc-declare-ctor-dtor-conversion{extra-constructors=foo:false,bar:true program-entry-name=main})' -split-input-file | FileCheck %s
+
+// extra-constructors is a list of <name>:<if-main> pairs. The functions are
+// declared and called from __openaccExtraConstructor; llvm.mlir.global_ctors
+// cannot reference a declaration. if-main=false always calls the function;
+// if-main=true calls it only when the module contains program-entry-name.
+
+// CHECK: llvm.func @foo() attributes {sym_visibility = "private"}
+// CHECK: llvm.func internal @__openaccExtraConstructor() {
+// CHECK:   llvm.call @foo() : () -> ()
+// CHECK-NOT: llvm.call @bar()
+// CHECK:   llvm.return
+// CHECK: }
+// CHECK: llvm.mlir.global_ctors ctors = [@__openaccExtraConstructor], priorities = [102 : i32], data = [#llvm.zero]
+// CHECK-NOT: llvm.func @bar()
+
+module {
+}
+
+// -----
+
+// CHECK: llvm.func @foo() attributes {sym_visibility = "private"}
+// CHECK: llvm.func @bar() attributes {sym_visibility = "private"}
+// CHECK: llvm.func internal @__openaccExtraConstructor() {
+// CHECK:   llvm.call @foo() : () -> ()
+// CHECK:   llvm.call @bar() : () -> ()
+// CHECK:   llvm.return
+// CHECK: }
+// CHECK: llvm.mlir.global_ctors ctors = [@__openaccExtraConstructor], priorities = [102 : i32], data = [#llvm.zero]
+
+module {
+  llvm.func @main() {
+    llvm.return
+  }
+}
+
+// -----
+
+// An existing declaration is reused. The extra constructor is merged into
+// llvm.mlir.global_ctors next to converted acc.global_ctor functions.
+
+// CHECK: llvm.func @foo()
+// CHECK-NOT: llvm.func @foo()
+// CHECK: llvm.func internal @arr_acc_ctor()
+// CHECK: llvm.func internal @__openaccExtraConstructor() {
+// CHECK:   llvm.call @foo() : () -> ()
+// CHECK:   llvm.return
+// CHECK: }
+// CHECK: llvm.mlir.global_ctors ctors = [@arr_acc_ctor, @__openaccExtraConstructor], priorities = [102 : i32, 102 : i32], data = [#llvm.zero, #llvm.zero]
+
+llvm.mlir.global external @arr() {acc.declare = #acc.declare<dataClause = acc_create>} : i32 {
+  %0 = llvm.mlir.constant(0 : i32) : i32
+  llvm.return %0 : i32
+}
+llvm.func @foo()
+acc.global_ctor @arr_acc_ctor {
+  %0 = llvm.mlir.addressof @arr {acc.declare = #acc.declare<dataClause = acc_create>} : !llvm.ptr
+  %1 = acc.create varPtr(%0 : !llvm.ptr) varType(i32) -> !llvm.ptr
+  acc.declare_enter dataOperands(%1 : !llvm.ptr)
+  acc.terminator
+}



More information about the Mlir-commits mailing list