[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-func
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