[Mlir-commits] [mlir] [mlir][func] Account for named attributes without a dialect prefix in `FuncToLLVM` lowering pass (PR #182987)
Ayokunle Amodu
llvmlistbot at llvm.org
Sat Feb 28 16:08:59 PST 2026
https://github.com/ayokunle321 updated https://github.com/llvm/llvm-project/pull/182987
>From ce8192fba10e63c8faab9aa5650be67ad42e0969 Mon Sep 17 00:00:00 2001
From: Ayokunle Amodu <ayokunle321 at gmail.com>
Date: Mon, 23 Feb 2026 19:34:28 -0700
Subject: [PATCH 1/4] fix handling of attribute names
---
mlir/lib/Conversion/FuncToLLVM/FuncToLLVM.cpp | 29 ++++++++++++++-----
1 file changed, 22 insertions(+), 7 deletions(-)
diff --git a/mlir/lib/Conversion/FuncToLLVM/FuncToLLVM.cpp b/mlir/lib/Conversion/FuncToLLVM/FuncToLLVM.cpp
index 2220f61ed8a07..d0b33bae6d0a5 100644
--- a/mlir/lib/Conversion/FuncToLLVM/FuncToLLVM.cpp
+++ b/mlir/lib/Conversion/FuncToLLVM/FuncToLLVM.cpp
@@ -52,10 +52,15 @@ static constexpr StringRef varargsAttrName = "func.varargs";
static constexpr StringRef linkageAttrName = "llvm.linkage";
static constexpr StringRef barePtrAttrName = "llvm.bareptr";
+static constexpr StringRef varargsAttrNameNoPrefix = "varargs";
+static constexpr StringRef linkageAttrNameNoPrefix = "linkage";
+static constexpr StringRef barePtrAttrNameNoPrefix = "bareptr";
+
/// Return `true` if the `op` should use bare pointer calling convention.
static bool shouldUseBarePtrCallConv(Operation *op,
const LLVMTypeConverter *typeConverter) {
- return (op && op->hasAttr(barePtrAttrName)) ||
+ return (op && (op->hasAttr(barePtrAttrName) ||
+ op->hasAttr(barePtrAttrNameNoPrefix))) ||
typeConverter->getOptions().useBarePtrCallConv;
}
@@ -65,7 +70,9 @@ static void filterFuncAttributes(FunctionOpInterface func,
SmallVectorImpl<NamedAttribute> &result) {
for (const NamedAttribute &attr : func->getDiscardableAttrs()) {
if (attr.getName() == linkageAttrName ||
+ attr.getName() == linkageAttrNameNoPrefix ||
attr.getName() == varargsAttrName ||
+ attr.getName() == varargsAttrNameNoPrefix ||
attr.getName() == LLVM::LLVMDialect::getReadnoneAttrName())
continue;
result.push_back(attr);
@@ -299,7 +306,10 @@ FailureOr<LLVM::LLVMFuncOp> mlir::convertFuncOpToLLVMFuncOp(
// Convert the original function arguments. They are converted using the
// LLVMTypeConverter provided to this legalization pattern.
- auto varargsAttr = funcOp->getAttrOfType<BoolAttr>(varargsAttrName);
+ auto varargsAttr =
+ funcOp->getAttrOfType<BoolAttr>(varargsAttrName)
+ ? funcOp->getAttrOfType<BoolAttr>(varargsAttrName)
+ : funcOp->getAttrOfType<BoolAttr>(varargsAttrNameNoPrefix);
// Gather `llvm.byval` and `llvm.byref` arguments whose type convertion was
// overriden with an LLVM pointer type for later processing.
SmallVector<std::optional<NamedAttribute>> byValRefNonPtrAttrs;
@@ -323,9 +333,12 @@ FailureOr<LLVM::LLVMFuncOp> mlir::convertFuncOpToLLVMFuncOp(
// Create an LLVM function, use external linkage by default until MLIR
// functions have linkage.
LLVM::Linkage linkage = LLVM::Linkage::External;
- if (funcOp->hasAttr(linkageAttrName)) {
- auto attr =
- dyn_cast<mlir::LLVM::LinkageAttr>(funcOp->getAttr(linkageAttrName));
+ if (funcOp->hasAttr(linkageAttrName) ||
+ funcOp->hasAttr(linkageAttrNameNoPrefix)) {
+ auto attr = dyn_cast_or_null<mlir::LLVM::LinkageAttr>(
+ funcOp->getAttr(linkageAttrName)
+ ? funcOp->getAttr(linkageAttrName)
+ : funcOp->getAttr(linkageAttrNameNoPrefix));
if (!attr) {
funcOp->emitError() << "Contains " << linkageAttrName
<< " attribute not of type LLVM::LinkageAttr";
@@ -651,13 +664,15 @@ class CallOpLowering : public CallOpInterfaceLowering<func::CallOp> {
Operation *callee =
symbolTables->lookupNearestSymbolFrom(callOp, callOp.getCalleeAttr());
useBarePtrCallConv =
- callee != nullptr && callee->hasAttr(barePtrAttrName);
+ callee != nullptr && (callee->hasAttr(barePtrAttrName) ||
+ callee->hasAttr(barePtrAttrNameNoPrefix));
} else {
// Warning: This is a linear lookup.
Operation *callee =
SymbolTable::lookupNearestSymbolFrom(callOp, callOp.getCalleeAttr());
useBarePtrCallConv =
- callee != nullptr && callee->hasAttr(barePtrAttrName);
+ callee != nullptr && (callee->hasAttr(barePtrAttrName) ||
+ callee->hasAttr(barePtrAttrNameNoPrefix));
}
return matchAndRewriteImpl(callOp, adaptor, rewriter, useBarePtrCallConv);
}
>From 2f46eb55d01ab062cfaa77441cb000a768a75a8e Mon Sep 17 00:00:00 2001
From: Ayokunle Amodu <ayokunle321 at gmail.com>
Date: Wed, 25 Feb 2026 08:52:25 -0700
Subject: [PATCH 2/4] replace hardcoded attribute names in filter
---
mlir/lib/Conversion/FuncToLLVM/FuncToLLVM.cpp | 36 ++++++-------------
1 file changed, 11 insertions(+), 25 deletions(-)
diff --git a/mlir/lib/Conversion/FuncToLLVM/FuncToLLVM.cpp b/mlir/lib/Conversion/FuncToLLVM/FuncToLLVM.cpp
index d0b33bae6d0a5..f2377f480a3bc 100644
--- a/mlir/lib/Conversion/FuncToLLVM/FuncToLLVM.cpp
+++ b/mlir/lib/Conversion/FuncToLLVM/FuncToLLVM.cpp
@@ -52,15 +52,10 @@ static constexpr StringRef varargsAttrName = "func.varargs";
static constexpr StringRef linkageAttrName = "llvm.linkage";
static constexpr StringRef barePtrAttrName = "llvm.bareptr";
-static constexpr StringRef varargsAttrNameNoPrefix = "varargs";
-static constexpr StringRef linkageAttrNameNoPrefix = "linkage";
-static constexpr StringRef barePtrAttrNameNoPrefix = "bareptr";
-
/// Return `true` if the `op` should use bare pointer calling convention.
static bool shouldUseBarePtrCallConv(Operation *op,
const LLVMTypeConverter *typeConverter) {
- return (op && (op->hasAttr(barePtrAttrName) ||
- op->hasAttr(barePtrAttrNameNoPrefix))) ||
+ return (op && op->hasAttr(barePtrAttrName)) ||
typeConverter->getOptions().useBarePtrCallConv;
}
@@ -68,12 +63,11 @@ static bool shouldUseBarePtrCallConv(Operation *op,
/// `LLVMFuncOp::build`.
static void filterFuncAttributes(FunctionOpInterface func,
SmallVectorImpl<NamedAttribute> &result) {
+ llvm::SmallDenseSet<StringRef> odsAttrNames(
+ LLVM::LLVMFuncOp::getAttributeNames().begin(),
+ LLVM::LLVMFuncOp::getAttributeNames().end());
for (const NamedAttribute &attr : func->getDiscardableAttrs()) {
- if (attr.getName() == linkageAttrName ||
- attr.getName() == linkageAttrNameNoPrefix ||
- attr.getName() == varargsAttrName ||
- attr.getName() == varargsAttrNameNoPrefix ||
- attr.getName() == LLVM::LLVMDialect::getReadnoneAttrName())
+ if (odsAttrNames.contains(attr.getName().strref()))
continue;
result.push_back(attr);
}
@@ -306,10 +300,7 @@ FailureOr<LLVM::LLVMFuncOp> mlir::convertFuncOpToLLVMFuncOp(
// Convert the original function arguments. They are converted using the
// LLVMTypeConverter provided to this legalization pattern.
- auto varargsAttr =
- funcOp->getAttrOfType<BoolAttr>(varargsAttrName)
- ? funcOp->getAttrOfType<BoolAttr>(varargsAttrName)
- : funcOp->getAttrOfType<BoolAttr>(varargsAttrNameNoPrefix);
+ auto varargsAttr = funcOp->getAttrOfType<BoolAttr>(varargsAttrName);
// Gather `llvm.byval` and `llvm.byref` arguments whose type convertion was
// overriden with an LLVM pointer type for later processing.
SmallVector<std::optional<NamedAttribute>> byValRefNonPtrAttrs;
@@ -333,12 +324,9 @@ FailureOr<LLVM::LLVMFuncOp> mlir::convertFuncOpToLLVMFuncOp(
// Create an LLVM function, use external linkage by default until MLIR
// functions have linkage.
LLVM::Linkage linkage = LLVM::Linkage::External;
- if (funcOp->hasAttr(linkageAttrName) ||
- funcOp->hasAttr(linkageAttrNameNoPrefix)) {
- auto attr = dyn_cast_or_null<mlir::LLVM::LinkageAttr>(
- funcOp->getAttr(linkageAttrName)
- ? funcOp->getAttr(linkageAttrName)
- : funcOp->getAttr(linkageAttrNameNoPrefix));
+ if (funcOp->hasAttr(linkageAttrName)) {
+ auto attr =
+ dyn_cast<mlir::LLVM::LinkageAttr>(funcOp->getAttr(linkageAttrName));
if (!attr) {
funcOp->emitError() << "Contains " << linkageAttrName
<< " attribute not of type LLVM::LinkageAttr";
@@ -664,15 +652,13 @@ class CallOpLowering : public CallOpInterfaceLowering<func::CallOp> {
Operation *callee =
symbolTables->lookupNearestSymbolFrom(callOp, callOp.getCalleeAttr());
useBarePtrCallConv =
- callee != nullptr && (callee->hasAttr(barePtrAttrName) ||
- callee->hasAttr(barePtrAttrNameNoPrefix));
+ callee != nullptr && callee->hasAttr(barePtrAttrName);
} else {
// Warning: This is a linear lookup.
Operation *callee =
SymbolTable::lookupNearestSymbolFrom(callOp, callOp.getCalleeAttr());
useBarePtrCallConv =
- callee != nullptr && (callee->hasAttr(barePtrAttrName) ||
- callee->hasAttr(barePtrAttrNameNoPrefix));
+ callee != nullptr && callee->hasAttr(barePtrAttrName);
}
return matchAndRewriteImpl(callOp, adaptor, rewriter, useBarePtrCallConv);
}
>From 4cec1170d2905fa2f8b6b6707cc392fcce6c0409 Mon Sep 17 00:00:00 2001
From: Ayokunle Amodu <ayokunle321 at gmail.com>
Date: Fri, 27 Feb 2026 07:58:53 -0700
Subject: [PATCH 3/4] update tests
---
mlir/test/Conversion/FuncToLLVM/convert-funcs.mlir | 8 ++++----
.../FuncToLLVM/emit-c-wrappers-for-external-callers.mlir | 9 ---------
.../emit-c-wrappers-for-external-functions.mlir | 6 ------
3 files changed, 4 insertions(+), 19 deletions(-)
diff --git a/mlir/test/Conversion/FuncToLLVM/convert-funcs.mlir b/mlir/test/Conversion/FuncToLLVM/convert-funcs.mlir
index bd281620c2918..ff2d9e05c8d50 100644
--- a/mlir/test/Conversion/FuncToLLVM/convert-funcs.mlir
+++ b/mlir/test/Conversion/FuncToLLVM/convert-funcs.mlir
@@ -62,13 +62,13 @@ func.func @variadic_func(%arg0: i32) attributes { "func.varargs" = true } {
}
// CHECK-LABEL: llvm.func @target_cpu()
-// CHECK-SAME: target_cpu = "gfx90a"
-func.func private @target_cpu() attributes { "target_cpu" = "gfx90a" }
+// CHECK-SAME: llvm.target_cpu = "gfx90a"
+func.func private @target_cpu() attributes { "llvm.target_cpu" = "gfx90a" }
// CHECK-LABEL: llvm.func @target_features()
-// CHECK-SAME: target_features = #llvm.target_features<["+sme", "+sve"]>
+// CHECK-SAME: llvm.target_features = #llvm.target_features<["+sme", "+sve"]>
func.func private @target_features() attributes {
- "target_features" = #llvm.target_features<["+sme", "+sve"]>
+ "llvm.target_features" = #llvm.target_features<["+sme", "+sve"]>
}
// -----
diff --git a/mlir/test/Conversion/FuncToLLVM/emit-c-wrappers-for-external-callers.mlir b/mlir/test/Conversion/FuncToLLVM/emit-c-wrappers-for-external-callers.mlir
index 826ca9540ae56..441659f534221 100644
--- a/mlir/test/Conversion/FuncToLLVM/emit-c-wrappers-for-external-callers.mlir
+++ b/mlir/test/Conversion/FuncToLLVM/emit-c-wrappers-for-external-callers.mlir
@@ -87,12 +87,3 @@ func.func @multiple_arg_attr_multiple_res_attr(%arg0: memref<f32> {test.argZero
%2 = arith.constant 2 : i32
return %0, %1, %2 : f32, memref<i32>, i32
}
-
-// CHECK: llvm.func @drop_linkage_attr() -> (!llvm.struct{{.*}} {test.returnOne})
-// CHECK-LABEL: llvm.func @_mlir_ciface_drop_linkage_attr
-// CHECK-SAME: !llvm.ptr
-// CHECK-NOT: llvm.linkage
-func.func @drop_linkage_attr() -> (memref<f32> {test.returnOne}) attributes { llvm.linkage = #llvm.linkage<external> } {
- %0 = memref.alloc() : memref<f32>
- return %0 : memref<f32>
-}
diff --git a/mlir/test/Conversion/FuncToLLVM/emit-c-wrappers-for-external-functions.mlir b/mlir/test/Conversion/FuncToLLVM/emit-c-wrappers-for-external-functions.mlir
index 28c2638c7be51..7127dae9b2d0c 100644
--- a/mlir/test/Conversion/FuncToLLVM/emit-c-wrappers-for-external-functions.mlir
+++ b/mlir/test/Conversion/FuncToLLVM/emit-c-wrappers-for-external-functions.mlir
@@ -57,9 +57,3 @@ func.func private @one_arg_attr_one_res_attr_with_value_return(%arg0: memref<f32
// CHECK-SAME: f32
// CHECK-SAME: i32 {test.argTwo = 2 : i64})
func.func private @multiple_arg_attr_multiple_res_attr(%arg0: memref<f32> {test.argZero = 0}, %arg1: f32, %arg2: i32 {test.argTwo = 2}) -> (f32, memref<i32> {test.returnOne = 1}, i32 {test.returnTwo = 2})
-
-// CHECK: llvm.func private @drop_linkage_attr() -> (!llvm.struct{{.*}} {test.returnOne})
-// CHECK-LABEL: llvm.func @_mlir_ciface_drop_linkage_attr
-// CHECK-SAME: !llvm.ptr
-// CHECK-NOT: llvm.linkage
-func.func private @drop_linkage_attr() -> (memref<f32> {test.returnOne}) attributes { llvm.linkage = #llvm.linkage<weak> }
>From 1396fd75e64383a307ce6d6559fd94ebbe728e43 Mon Sep 17 00:00:00 2001
From: Ayokunle Amodu <ayokunle321 at gmail.com>
Date: Sat, 28 Feb 2026 17:08:43 -0700
Subject: [PATCH 4/4] updates tests and linkage attr handling
---
mlir/lib/Conversion/FuncToLLVM/FuncToLLVM.cpp | 5 +++--
.../lib/Dialect/Async/Transforms/AsyncToAsyncRuntime.cpp | 2 +-
.../FuncToLLVM/emit-c-wrappers-for-external-callers.mlir | 9 +++++++++
.../emit-c-wrappers-for-external-functions.mlir | 6 ++++++
4 files changed, 19 insertions(+), 3 deletions(-)
diff --git a/mlir/lib/Conversion/FuncToLLVM/FuncToLLVM.cpp b/mlir/lib/Conversion/FuncToLLVM/FuncToLLVM.cpp
index f2377f480a3bc..77a5db66ffb9c 100644
--- a/mlir/lib/Conversion/FuncToLLVM/FuncToLLVM.cpp
+++ b/mlir/lib/Conversion/FuncToLLVM/FuncToLLVM.cpp
@@ -59,8 +59,8 @@ static bool shouldUseBarePtrCallConv(Operation *op,
typeConverter->getOptions().useBarePtrCallConv;
}
-/// Only retain those attributes that are not constructed by
-/// `LLVMFuncOp::build`.
+/// Filter out ODS-named attributes to prevent duplicates when
+/// LLVMFuncOp::build constructs the attribute dictionary.
static void filterFuncAttributes(FunctionOpInterface func,
SmallVectorImpl<NamedAttribute> &result) {
llvm::SmallDenseSet<StringRef> odsAttrNames(
@@ -334,6 +334,7 @@ FailureOr<LLVM::LLVMFuncOp> mlir::convertFuncOpToLLVMFuncOp(
funcOp, "Contains linkage attribute not of type LLVM::LinkageAttr");
}
linkage = attr.getLinkage();
+ funcOp->removeAttr(linkageAttrName);
}
// Check for invalid attributes.
diff --git a/mlir/lib/Dialect/Async/Transforms/AsyncToAsyncRuntime.cpp b/mlir/lib/Dialect/Async/Transforms/AsyncToAsyncRuntime.cpp
index 112d69ce87f7f..40cc15e31fd6d 100644
--- a/mlir/lib/Dialect/Async/Transforms/AsyncToAsyncRuntime.cpp
+++ b/mlir/lib/Dialect/Async/Transforms/AsyncToAsyncRuntime.cpp
@@ -242,7 +242,7 @@ static CoroMachinery setupCoroMachinery(func::FuncOp func) {
// The switch-resumed API based coroutine should be marked with
// presplitcoroutine attribute to mark the function as a coroutine.
- func->setAttr("passthrough", builder.getArrayAttr(
+ func->setAttr("llvm.passthrough", builder.getArrayAttr(
StringAttr::get(ctx, "presplitcoroutine")));
CoroMachinery machinery;
diff --git a/mlir/test/Conversion/FuncToLLVM/emit-c-wrappers-for-external-callers.mlir b/mlir/test/Conversion/FuncToLLVM/emit-c-wrappers-for-external-callers.mlir
index 441659f534221..826ca9540ae56 100644
--- a/mlir/test/Conversion/FuncToLLVM/emit-c-wrappers-for-external-callers.mlir
+++ b/mlir/test/Conversion/FuncToLLVM/emit-c-wrappers-for-external-callers.mlir
@@ -87,3 +87,12 @@ func.func @multiple_arg_attr_multiple_res_attr(%arg0: memref<f32> {test.argZero
%2 = arith.constant 2 : i32
return %0, %1, %2 : f32, memref<i32>, i32
}
+
+// CHECK: llvm.func @drop_linkage_attr() -> (!llvm.struct{{.*}} {test.returnOne})
+// CHECK-LABEL: llvm.func @_mlir_ciface_drop_linkage_attr
+// CHECK-SAME: !llvm.ptr
+// CHECK-NOT: llvm.linkage
+func.func @drop_linkage_attr() -> (memref<f32> {test.returnOne}) attributes { llvm.linkage = #llvm.linkage<external> } {
+ %0 = memref.alloc() : memref<f32>
+ return %0 : memref<f32>
+}
diff --git a/mlir/test/Conversion/FuncToLLVM/emit-c-wrappers-for-external-functions.mlir b/mlir/test/Conversion/FuncToLLVM/emit-c-wrappers-for-external-functions.mlir
index 7127dae9b2d0c..f100379390b34 100644
--- a/mlir/test/Conversion/FuncToLLVM/emit-c-wrappers-for-external-functions.mlir
+++ b/mlir/test/Conversion/FuncToLLVM/emit-c-wrappers-for-external-functions.mlir
@@ -57,3 +57,9 @@ func.func private @one_arg_attr_one_res_attr_with_value_return(%arg0: memref<f32
// CHECK-SAME: f32
// CHECK-SAME: i32 {test.argTwo = 2 : i64})
func.func private @multiple_arg_attr_multiple_res_attr(%arg0: memref<f32> {test.argZero = 0}, %arg1: f32, %arg2: i32 {test.argTwo = 2}) -> (f32, memref<i32> {test.returnOne = 1}, i32 {test.returnTwo = 2})
+
+// CHECK: llvm.func private @drop_linkage_attr() -> (!llvm.struct{{.*}} {test.returnOne})
+// CHECK-LABEL: llvm.func @_mlir_ciface_drop_linkage_attr
+// CHECK-SAME: !llvm.ptr
+// CHECK-NOT: llvm.linkage
+func.func private @drop_linkage_attr() -> (memref<f32> {test.returnOne}) attributes { llvm.linkage = #llvm.linkage<weak> }
\ No newline at end of file
More information about the Mlir-commits
mailing list