[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