[Mlir-commits] [mlir] [mlir][func] Refactor FuncToLLVM discardable attributes algorithm (PR #188232)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Tue Mar 24 05:07:58 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir

Author: Hocky Yudhiono (hockyy)

<details>
<summary>Changes</summary>



---
Full diff: https://github.com/llvm/llvm-project/pull/188232.diff


5 Files Affected:

- (modified) mlir/include/mlir/Dialect/Func/IR/FuncOps.td (+1) 
- (modified) mlir/lib/Conversion/FuncToLLVM/FuncToLLVM.cpp (+108-38) 
- (modified) mlir/lib/Dialect/Async/Transforms/AsyncToAsyncRuntime.cpp (+2-2) 
- (modified) mlir/lib/Dialect/Func/IR/FuncOps.cpp (+10) 
- (modified) mlir/test/Conversion/FuncToLLVM/convert-funcs.mlir (+8-2) 


``````````diff
diff --git a/mlir/include/mlir/Dialect/Func/IR/FuncOps.td b/mlir/include/mlir/Dialect/Func/IR/FuncOps.td
index a31b860276099..342d20bbb5a4d 100644
--- a/mlir/include/mlir/Dialect/Func/IR/FuncOps.td
+++ b/mlir/include/mlir/Dialect/Func/IR/FuncOps.td
@@ -357,6 +357,7 @@ def FuncOp : Func_Op<"func", [
     bool isDeclaration() { return isExternal(); }
   }];
   let hasCustomAssemblyFormat = 1;
+  let hasVerifier = 1;
   let hasRegionVerifier = 1;
 }
 
diff --git a/mlir/lib/Conversion/FuncToLLVM/FuncToLLVM.cpp b/mlir/lib/Conversion/FuncToLLVM/FuncToLLVM.cpp
index 2220f61ed8a07..13b7ecbf800d3 100644
--- a/mlir/lib/Conversion/FuncToLLVM/FuncToLLVM.cpp
+++ b/mlir/lib/Conversion/FuncToLLVM/FuncToLLVM.cpp
@@ -35,6 +35,7 @@
 #include "mlir/Transforms/Passes.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/IR/Type.h"
+#include "llvm/Support/DebugLog.h"
 #include "llvm/Support/FormatVariadic.h"
 #include <optional>
 
@@ -47,7 +48,15 @@ namespace mlir {
 using namespace mlir;
 
 #define PASS_NAME "convert-func-to-llvm"
-
+#define DEBUG_TYPE PASS_NAME
+
+// Attribute names on `func.func` consumed by this conversion.
+// - `llvm.linkage` is a discardable attribute on `func.func` that lowers to
+//   inherent `linkage` on `llvm.func`.
+// - `llvm.bareptr` selects bare-pointer calling convention behavior during
+//   function and call lowering.
+// - `llvm.emit_c_interface` is read only by this conversion (C wrapper); it is
+//   not an LLVM function attribute and must not be forwarded to `llvm.func`.
 static constexpr StringRef varargsAttrName = "func.varargs";
 static constexpr StringRef linkageAttrName = "llvm.linkage";
 static constexpr StringRef barePtrAttrName = "llvm.bareptr";
@@ -59,19 +68,98 @@ static bool shouldUseBarePtrCallConv(Operation *op,
          typeConverter->getOptions().useBarePtrCallConv;
 }
 
+static bool isDiscardableAttr(StringRef name) {
+  return name == linkageAttrName || name == varargsAttrName ||
+         name == LLVM::LLVMDialect::getReadnoneAttrName();
+}
+
 /// Only retain those attributes that are not constructed by
 /// `LLVMFuncOp::build`.
 static void filterFuncAttributes(FunctionOpInterface func,
                                  SmallVectorImpl<NamedAttribute> &result) {
   for (const NamedAttribute &attr : func->getDiscardableAttrs()) {
-    if (attr.getName() == linkageAttrName ||
-        attr.getName() == varargsAttrName ||
-        attr.getName() == LLVM::LLVMDialect::getReadnoneAttrName())
+    if (isDiscardableAttr(attr.getName().strref()))
       continue;
     result.push_back(attr);
   }
 }
 
+/// Add custom lowered funcOp to llvm.func attributes here.
+struct LoweredFuncAttrs {
+  LLVM::Linkage linkage = LLVM::Linkage::External;
+  bool hasReadnone = false;
+  SmallVector<NamedAttribute, 4> attrs;
+};
+
+static LogicalResult lowerLinkageAttr(FunctionOpInterface func,
+                                      Attribute attrValue,
+                                      LoweredFuncAttrs &lowered) {
+  auto linkageAttr = dyn_cast<mlir::LLVM::LinkageAttr>(attrValue);
+  if (!linkageAttr) {
+    func->emitError() << "Contains " << linkageAttrName
+                      << " attribute not of type LLVM::LinkageAttr";
+    return failure();
+  }
+  lowered.linkage = linkageAttr.getLinkage();
+  return success();
+}
+
+static LogicalResult lowerReadnoneAttr(FunctionOpInterface func,
+                                       Attribute attrValue,
+                                       LoweredFuncAttrs &lowered) {
+  StringRef readnoneAttrName = LLVM::LLVMDialect::getReadnoneAttrName();
+  if (!isa<UnitAttr>(attrValue)) {
+    func->emitError() << "Contains " << readnoneAttrName
+                      << " attribute not of type UnitAttr";
+    return failure();
+  }
+  lowered.hasReadnone = true;
+  return success();
+}
+
+/// Lower discardable function attributes on `func.func` to attributes expected
+/// by `llvm.func`.
+static FailureOr<LoweredFuncAttrs>
+lowerFuncAttributes(FunctionOpInterface func) {
+  MLIRContext *ctx = func->getContext();
+  LoweredFuncAttrs lowered;
+  llvm::SmallDenseSet<StringRef> odsAttrNames(
+      LLVM::LLVMFuncOp::getAttributeNames().begin(),
+      LLVM::LLVMFuncOp::getAttributeNames().end());
+
+  // Obtain specific attributes and add them to the lowered attributes.
+  for (const NamedAttribute &attr : func->getDiscardableAttrs()) {
+    StringRef attrName = attr.getName().strref();
+    if (attrName == linkageAttrName) {
+      if (failed(lowerLinkageAttr(func, attr.getValue(), lowered)))
+        return failure();
+      continue;
+    }
+
+    if (attrName == LLVM::LLVMDialect::getReadnoneAttrName()) {
+      if (failed(lowerReadnoneAttr(func, attr.getValue(), lowered)))
+        return failure();
+      continue;
+    }
+
+    if (odsAttrNames.contains(attrName)) {
+      continue;
+    }
+
+    // Map llvm.<name> -> inherent <name> when <name> is an LLVMFuncOp ODS attr.
+    StringRef inherent = attrName;
+    LDBG() << "inherent: " << inherent;
+    if (inherent.consume_front("llvm.") && odsAttrNames.contains(inherent)) {
+      lowered.attrs.emplace_back(StringAttr::get(ctx, inherent),
+                                 attr.getValue());
+    } else {
+      lowered.attrs.push_back(attr);
+    }
+  }
+
+  return lowered;
+}
+
 /// Propagate argument/results attributes.
 static void propagateArgResAttrs(OpBuilder &builder, bool resultStructType,
                                  FunctionOpInterface funcOp,
@@ -288,6 +376,7 @@ static void restoreByValRefArgumentType(
   }
 }
 
+/// TODO: Refactor this function to be more modular and easier to understand.
 FailureOr<LLVM::LLVMFuncOp> mlir::convertFuncOpToLLVMFuncOp(
     FunctionOpInterface funcOp, ConversionPatternRewriter &rewriter,
     const LLVMTypeConverter &converter, SymbolTableCollection *symbolTables) {
@@ -320,35 +409,10 @@ FailureOr<LLVM::LLVMFuncOp> mlir::convertFuncOpToLLVMFuncOp(
         return funcOp.emitError("C interface for variadic functions is not "
                                 "supported yet.");
 
-  // 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 (!attr) {
-      funcOp->emitError() << "Contains " << linkageAttrName
-                          << " attribute not of type LLVM::LinkageAttr";
-      return rewriter.notifyMatchFailure(
-          funcOp, "Contains linkage attribute not of type LLVM::LinkageAttr");
-    }
-    linkage = attr.getLinkage();
-  }
-
-  // Check for invalid attributes.
-  StringRef readnoneAttrName = LLVM::LLVMDialect::getReadnoneAttrName();
-  if (funcOp->hasAttr(readnoneAttrName)) {
-    auto attr = funcOp->getAttrOfType<UnitAttr>(readnoneAttrName);
-    if (!attr) {
-      funcOp->emitError() << "Contains " << readnoneAttrName
-                          << " attribute not of type UnitAttr";
-      return rewriter.notifyMatchFailure(
-          funcOp, "Contains readnone attribute not of type UnitAttr");
-    }
-  }
-
-  SmallVector<NamedAttribute, 4> attributes;
-  filterFuncAttributes(funcOp, attributes);
+  FailureOr<LoweredFuncAttrs> loweredAttrs = lowerFuncAttributes(funcOp);
+  if (failed(loweredAttrs))
+    return rewriter.notifyMatchFailure(funcOp,
+                                       "failed to lower func attributes");
 
   Operation *symbolTableOp = funcOp->getParentWithTrait<OpTrait::SymbolTable>();
 
@@ -357,10 +421,16 @@ FailureOr<LLVM::LLVMFuncOp> mlir::convertFuncOpToLLVMFuncOp(
     symbolTable.remove(funcOp);
   }
 
-  auto newFuncOp = LLVM::LLVMFuncOp::create(
-      rewriter, funcOp.getLoc(), funcOp.getName(), llvmType, linkage,
-      /*dsoLocal=*/false, /*cconv=*/LLVM::CConv::C, /*comdat=*/nullptr,
-      attributes);
+  /// TODO: Improve create function API
+  for (auto attr : loweredAttrs->attrs) {
+    LDBG() << "attr: " << attr.getName() << " value: " << attr.getValue();
+  }
+  LDBG() << "loweredAttrs->linkage: " << loweredAttrs->linkage;
+  auto newFuncOp =
+      LLVM::LLVMFuncOp::create(rewriter, funcOp.getLoc(), funcOp.getName(),
+                               llvmType, loweredAttrs->linkage,
+                               /*dsoLocal=*/false, /*cconv=*/LLVM::CConv::C,
+                               /*comdat=*/nullptr, loweredAttrs->attrs);
 
   if (symbolTables && symbolTableOp) {
     auto ip = rewriter.getInsertionPoint();
@@ -372,7 +442,7 @@ FailureOr<LLVM::LLVMFuncOp> mlir::convertFuncOpToLLVMFuncOp(
       .setVisibility(funcOp.getVisibility());
 
   // Create a memory effect attribute corresponding to readnone.
-  if (funcOp->hasAttr(readnoneAttrName)) {
+  if (loweredAttrs->hasReadnone) {
     auto memoryAttr = LLVM::MemoryEffectsAttr::get(
         rewriter.getContext(), {/*other=*/LLVM::ModRefInfo::NoModRef,
                                 /*argMem=*/LLVM::ModRefInfo::NoModRef,
diff --git a/mlir/lib/Dialect/Async/Transforms/AsyncToAsyncRuntime.cpp b/mlir/lib/Dialect/Async/Transforms/AsyncToAsyncRuntime.cpp
index 112d69ce87f7f..0c5bcfe631c6c 100644
--- a/mlir/lib/Dialect/Async/Transforms/AsyncToAsyncRuntime.cpp
+++ b/mlir/lib/Dialect/Async/Transforms/AsyncToAsyncRuntime.cpp
@@ -242,8 +242,8 @@ 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(
-                                   StringAttr::get(ctx, "presplitcoroutine")));
+  func->setAttr("llvm.passthrough", builder.getArrayAttr(StringAttr::get(
+                                        ctx, "presplitcoroutine")));
 
   CoroMachinery machinery;
   machinery.func = func;
diff --git a/mlir/lib/Dialect/Func/IR/FuncOps.cpp b/mlir/lib/Dialect/Func/IR/FuncOps.cpp
index d803e99154499..a16816a74ae6f 100644
--- a/mlir/lib/Dialect/Func/IR/FuncOps.cpp
+++ b/mlir/lib/Dialect/Func/IR/FuncOps.cpp
@@ -280,6 +280,16 @@ FuncOp FuncOp::clone() {
   return clone(mapper);
 }
 
+/// TODO: Should we add some exception for attributes that are not
+/// dialect-prefixed?
+LogicalResult FuncOp::verify() {
+  for (const NamedAttribute &attr : (*this)->getDiscardableAttrs()) {
+    if (attr.getNameDialect())
+      continue;
+  }
+  return success();
+}
+
 //===----------------------------------------------------------------------===//
 // ReturnOp
 //===----------------------------------------------------------------------===//
diff --git a/mlir/test/Conversion/FuncToLLVM/convert-funcs.mlir b/mlir/test/Conversion/FuncToLLVM/convert-funcs.mlir
index 4cb31f8f92661..4e8f54fda82dc 100644
--- a/mlir/test/Conversion/FuncToLLVM/convert-funcs.mlir
+++ b/mlir/test/Conversion/FuncToLLVM/convert-funcs.mlir
@@ -63,12 +63,18 @@ 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" }
+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"]>
 func.func private @target_features() attributes {
-  "target_features" = #llvm.target_features<["+sme", "+sve"]>
+  "llvm.target_features" = #llvm.target_features<["+sme", "+sve"]>
+}
+
+// CHECK-LABEL: llvm.func @passthrough_attr()
+// CHECK-SAME: passthrough = ["presplitcoroutine"]
+func.func private @passthrough_attr() attributes {
+  "llvm.passthrough" = ["presplitcoroutine"]
 }
 
 // -----

``````````

</details>


https://github.com/llvm/llvm-project/pull/188232


More information about the Mlir-commits mailing list