[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