[Mlir-commits] [mlir] [mlir][EmitC] Add support for external functions (PR #80547)

Marius Brehler llvmlistbot at llvm.org
Sat Feb 3 07:37:37 PST 2024


https://github.com/marbre created https://github.com/llvm/llvm-project/pull/80547

This adds a conversion from an externaly defined `func.func`, a `func.func` without function body, to an `emitc.func` with an `extern` specifier.

>From 5a6975ce1c38a49743eb239a2e7f66425b9f4e8c Mon Sep 17 00:00:00 2001
From: Marius Brehler <marius.brehler at iml.fraunhofer.de>
Date: Sat, 3 Feb 2024 15:22:11 +0000
Subject: [PATCH] [mlir][EmitC] Add support for external functions

This adds a conversion from an externaly defined `func.func`, a
`func.func` without function body, to an `emitc.func` with an `extern`
specifier.
---
 mlir/include/mlir/Dialect/EmitC/IR/EmitC.td   |  3 +++
 .../Conversion/FuncToEmitC/FuncToEmitC.cpp    | 19 +++++++++-------
 mlir/lib/Dialect/EmitC/IR/EmitC.cpp           |  3 ---
 mlir/lib/Target/Cpp/TranslateToCpp.cpp        | 22 +++++++++++++++++++
 .../Conversion/FuncToEmitC/func-to-emitc.mlir |  5 +++++
 mlir/test/Dialect/EmitC/invalid_ops.mlir      |  5 -----
 mlir/test/Dialect/EmitC/ops.mlir              |  2 ++
 mlir/test/Target/Cpp/func.mlir                |  3 +++
 8 files changed, 46 insertions(+), 16 deletions(-)

diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index 5c8c3c9ce7bb3..6871948d14cfc 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -498,6 +498,9 @@ def EmitC_FuncOp : EmitC_Op<"func", [
       emitc.return %0 : i32
     }
 
+    // An external function definition:
+    emitc.func private @extern_func(i32)
+                        attributes {specifiers = ["extern"]}
     ```
   }];
   let arguments = (ins SymbolNameAttr:$sym_name,
diff --git a/mlir/lib/Conversion/FuncToEmitC/FuncToEmitC.cpp b/mlir/lib/Conversion/FuncToEmitC/FuncToEmitC.cpp
index ac3d8297953f3..658b94613d12b 100644
--- a/mlir/lib/Conversion/FuncToEmitC/FuncToEmitC.cpp
+++ b/mlir/lib/Conversion/FuncToEmitC/FuncToEmitC.cpp
@@ -57,10 +57,6 @@ class FuncOpConversion final : public OpConversionPattern<func::FuncOp> {
       return rewriter.notifyMatchFailure(
           funcOp, "only functions with zero or one result can be converted");
 
-    if (funcOp.isDeclaration())
-      return rewriter.notifyMatchFailure(funcOp,
-                                         "declarations cannot be converted");
-
     // Create the converted `emitc.func` op.
     emitc::FuncOp newFuncOp = rewriter.create<emitc::FuncOp>(
         funcOp.getLoc(), funcOp.getName(), funcOp.getFunctionType());
@@ -72,14 +68,21 @@ class FuncOpConversion final : public OpConversionPattern<func::FuncOp> {
         newFuncOp->setAttr(namedAttr.getName(), namedAttr.getValue());
     }
 
-    // Add `static` to specifiers if `func.func` is private.
-    if (funcOp.isPrivate()) {
+    // Add `extern` to specifiers if `func.func` is declaration only.
+    if (funcOp.isDeclaration()) {
+      ArrayAttr specifiers = rewriter.getStrArrayAttr({"extern"});
+      newFuncOp.setSpecifiersAttr(specifiers);
+    }
+
+    // Add `static` to specifiers if `func.func` is private but not a declaration.
+    if (funcOp.isPrivate() && !funcOp.isDeclaration()) {
       ArrayAttr specifiers = rewriter.getStrArrayAttr({"static"});
       newFuncOp.setSpecifiersAttr(specifiers);
     }
 
-    rewriter.inlineRegionBefore(funcOp.getBody(), newFuncOp.getBody(),
-                                newFuncOp.end());
+    if (!funcOp.isDeclaration())
+      rewriter.inlineRegionBefore(funcOp.getBody(), newFuncOp.getBody(),
+                                  newFuncOp.end());
     rewriter.eraseOp(funcOp);
 
     return success();
diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
index df489c6d90fb1..f384fcbefcfdb 100644
--- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
+++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
@@ -437,9 +437,6 @@ LogicalResult FuncOp::verify() {
     return emitOpError("requires zero or exactly one result, but has ")
            << getNumResults();
 
-  if (isExternal())
-    return emitOpError("does not support empty function bodies");
-
   return success();
 }
 
diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
index c0c6105409f8d..0abbce5335278 100644
--- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp
+++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
@@ -771,6 +771,22 @@ static LogicalResult printOperation(CppEmitter &emitter, ModuleOp moduleOp) {
   return success();
 }
 
+static LogicalResult printFunctionArgs(CppEmitter &emitter,
+                                       Operation *functionOp,
+                                       ArrayRef<Type> arguments) {
+  raw_indented_ostream &os = emitter.ostream();
+
+  if (failed(interleaveCommaWithError(
+          arguments, os, [&](Type arg) -> LogicalResult {
+            if (failed(emitter.emitType(functionOp->getLoc(), arg)))
+              return failure();
+            return success();
+          })))
+    return failure();
+
+  return success();
+}
+
 static LogicalResult printFunctionArgs(CppEmitter &emitter,
                                        Operation *functionOp,
                                        Region::BlockArgListType arguments) {
@@ -913,6 +929,12 @@ static LogicalResult printOperation(CppEmitter &emitter,
 
   os << "(";
   Operation *operation = functionOp.getOperation();
+  if (functionOp.isExternal()) {
+    if (failed(printFunctionArgs(emitter, operation, functionOp.getArgumentTypes())))
+      return failure();
+    os << ");";
+    return success();
+  }
   if (failed(printFunctionArgs(emitter, operation, functionOp.getArguments())))
     return failure();
   os << ") {\n";
diff --git a/mlir/test/Conversion/FuncToEmitC/func-to-emitc.mlir b/mlir/test/Conversion/FuncToEmitC/func-to-emitc.mlir
index a1c8af2587aa0..5c96cf1ce0d34 100644
--- a/mlir/test/Conversion/FuncToEmitC/func-to-emitc.mlir
+++ b/mlir/test/Conversion/FuncToEmitC/func-to-emitc.mlir
@@ -53,3 +53,8 @@ func.func @call(%arg0: i32) -> i32 {
   %0 = call @return_i32(%arg0) : (i32) -> (i32)
   return %0 : i32
 }
+
+// -----
+
+// CHECK-LABEL: emitc.func private @return_i32(i32) -> i32 attributes {specifiers = ["extern"]}
+func.func private @return_i32(%arg0: i32) -> i32
diff --git a/mlir/test/Dialect/EmitC/invalid_ops.mlir b/mlir/test/Dialect/EmitC/invalid_ops.mlir
index 707f9a5b23b0b..6d2471b4d2b48 100644
--- a/mlir/test/Dialect/EmitC/invalid_ops.mlir
+++ b/mlir/test/Dialect/EmitC/invalid_ops.mlir
@@ -321,8 +321,3 @@ func.func @return_inside_func.func(%0: i32) -> (i32) {
 
 // expected-error at +1 {{expected non-function type}}
 emitc.func @func_variadic(...)
-
-// -----
-
-// expected-error at +1 {{'emitc.func' op does not support empty function bodies}}
-emitc.func private @empty()
diff --git a/mlir/test/Dialect/EmitC/ops.mlir b/mlir/test/Dialect/EmitC/ops.mlir
index b41333faa4d4e..e03c3d58c3e84 100644
--- a/mlir/test/Dialect/EmitC/ops.mlir
+++ b/mlir/test/Dialect/EmitC/ops.mlir
@@ -30,6 +30,8 @@ emitc.func @call() -> i32 {
   emitc.return %0 : i32
 }
 
+emitc.func private @extern(i32) attributes {specifiers = ["extern"]}
+
 func.func @cast(%arg0: i32) {
   %1 = emitc.cast %arg0: i32 to f32
   return
diff --git a/mlir/test/Target/Cpp/func.mlir b/mlir/test/Target/Cpp/func.mlir
index d2e14a9e5a7ae..a639cae6f623c 100644
--- a/mlir/test/Target/Cpp/func.mlir
+++ b/mlir/test/Target/Cpp/func.mlir
@@ -37,3 +37,6 @@ emitc.func @emitc_call() -> i32 {
 // CPP-DECLTOP-NEXT: int32_t [[V0:[^ ]*]];
 // CPP-DECLTOP-NEXT: [[V0:[^ ]*]] = return_i32();
 // CPP-DECLTOP-NEXT: return [[V0:[^ ]*]];
+
+emitc.func private @extern_func(i32) attributes {specifiers = ["extern"]}
+// CPP-DEFAULT: extern void extern_func(int32_t);



More information about the Mlir-commits mailing list