[Mlir-commits] [mlir] [mlir][emitc] Add a `declare_func` operation (PR #80297)

Marius Brehler llvmlistbot at llvm.org
Sun Feb 4 01:23:20 PST 2024


https://github.com/marbre updated https://github.com/llvm/llvm-project/pull/80297

>From 8bb74a066f812e5e47ba7014c6f5f2f3891fd656 Mon Sep 17 00:00:00 2001
From: Marius Brehler <marius.brehler at iml.fraunhofer.de>
Date: Thu, 1 Feb 2024 14:40:45 +0000
Subject: [PATCH 1/3] [mlir][emitc] Add a `declare_func` operation

This adds the `emitc.declare_func` operation that allows to emit the
declaration of an `emitc.func` at a specific location.
---
 mlir/include/mlir/Dialect/EmitC/IR/EmitC.td | 42 +++++++++++++++++++
 mlir/lib/Dialect/EmitC/IR/EmitC.cpp         | 18 +++++++++
 mlir/lib/Target/Cpp/TranslateToCpp.cpp      | 45 ++++++++++++++++++---
 mlir/test/Dialect/EmitC/invalid_ops.mlir    | 10 +++++
 mlir/test/Dialect/EmitC/ops.mlir            |  2 +
 mlir/test/Target/Cpp/declare_func.mlir      | 16 ++++++++
 6 files changed, 127 insertions(+), 6 deletions(-)
 create mode 100644 mlir/test/Target/Cpp/declare_func.mlir

diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index 5c8c3c9ce7bb3..fe004e441e9f9 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -460,6 +460,48 @@ def EmitC_CallOp : EmitC_Op<"call",
   }];
 }
 
+def EmitC_DeclareFuncOp : EmitC_Op<"declare_func", [
+  DeclareOpInterfaceMethods<SymbolUserOpInterface>
+]> {
+  let summary = "An operation to declare a function";
+  let description = [{
+    The `declare_func` operation allows to insert a function declaration for an
+    `emitc.func` at a specific position. The operation only requires the `callee`
+    of the `emitc.func` to be specified as an attribute.
+
+    Example:
+
+    ```mlir
+    emitc.declare_func @bar
+    emitc.func @foo(%arg0: i32) -> i32 {
+      %0 = emitc.call @bar(%arg0) : (i32) -> (i32)
+      emitc.return %0 : i32
+    }
+
+    emitc.func @bar(%arg0: i32) -> i32 {
+      emitc.return %arg0 : i32
+    }
+    ```
+
+    ```c++
+    // Code emitted for the operations above.
+    int32_t bar(int32_t v1);
+    int32_t foo(int32_t v1) {
+      int32_t v2 = bar(v1);
+      return v2;
+    }
+
+    int32_t bar(int32_t v1) {
+      return v1;
+    }
+    ```
+  }];
+  let arguments = (ins FlatSymbolRefAttr:$callee);
+  let assemblyFormat = [{
+    $callee attr-dict
+  }];
+}
+
 def EmitC_FuncOp : EmitC_Op<"func", [
   AutomaticAllocationScope,
   FunctionOpInterface, IsolatedFromAbove
diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
index df489c6d90fb1..67bcb7079672e 100644
--- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
+++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
@@ -393,6 +393,24 @@ FunctionType CallOp::getCalleeType() {
   return FunctionType::get(getContext(), getOperandTypes(), getResultTypes());
 }
 
+//===----------------------------------------------------------------------===//
+// DeclareFuncOp
+//===----------------------------------------------------------------------===//
+
+LogicalResult
+DeclareFuncOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
+  // Check that the callee attribute was specified.
+  auto fnAttr = (*this)->getAttrOfType<FlatSymbolRefAttr>("callee");
+  if (!fnAttr)
+    return emitOpError("requires a 'callee' symbol reference attribute");
+  FuncOp fn = symbolTable.lookupNearestSymbolFrom<FuncOp>(*this, fnAttr);
+  if (!fn)
+    return emitOpError() << "'" << fnAttr.getValue()
+                         << "' does not reference a valid function";
+
+  return success();
+}
+
 //===----------------------------------------------------------------------===//
 // FuncOp
 //===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
index c0c6105409f8d..28aef02e72566 100644
--- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp
+++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
@@ -14,6 +14,7 @@
 #include "mlir/IR/BuiltinTypes.h"
 #include "mlir/IR/Dialect.h"
 #include "mlir/IR/Operation.h"
+#include "mlir/IR/SymbolTable.h"
 #include "mlir/Support/IndentedOstream.h"
 #include "mlir/Support/LLVM.h"
 #include "mlir/Target/Cpp/CppEmitter.h"
@@ -847,8 +848,9 @@ static LogicalResult printFunctionBody(CppEmitter &emitter,
       // needs to be printed after the closing brace.
       // When generating code for an emitc.for and emitc.verbatim op, printing a
       // trailing semicolon is handled within the printOperation function.
-      bool trailingSemicolon = !isa<cf::CondBranchOp, emitc::ForOp, emitc::IfOp,
-                                    emitc::LiteralOp, emitc::VerbatimOp>(op);
+      bool trailingSemicolon =
+          !isa<cf::CondBranchOp, emitc::DeclareFuncOp, emitc::ForOp,
+               emitc::IfOp, emitc::LiteralOp, emitc::VerbatimOp>(op);
 
       if (failed(emitter.emitOperation(
               op, /*trailingSemicolon=*/trailingSemicolon)))
@@ -923,6 +925,37 @@ static LogicalResult printOperation(CppEmitter &emitter,
   return success();
 }
 
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    DeclareFuncOp declareFuncOp) {
+  CppEmitter::Scope scope(emitter);
+  raw_indented_ostream &os = emitter.ostream();
+
+  auto functionOp = SymbolTable::lookupNearestSymbolFrom<emitc::FuncOp>(
+      declareFuncOp, declareFuncOp.getCalleeAttr());
+
+  if (!functionOp)
+    return failure();
+
+  if (functionOp.getSpecifiers()) {
+    for (Attribute specifier : functionOp.getSpecifiersAttr()) {
+      os << cast<StringAttr>(specifier).str() << " ";
+    }
+  }
+
+  if (failed(emitter.emitTypes(functionOp.getLoc(),
+                               functionOp.getFunctionType().getResults())))
+    return failure();
+  os << " " << functionOp.getName();
+
+  os << "(";
+  Operation *operation = functionOp.getOperation();
+  if (failed(printFunctionArgs(emitter, operation, functionOp.getArguments())))
+    return failure();
+  os << ");";
+
+  return success();
+}
+
 CppEmitter::CppEmitter(raw_ostream &os, bool declareVariablesAtTop)
     : os(os), declareVariablesAtTop(declareVariablesAtTop) {
   valueInScopeCount.push(0);
@@ -1236,10 +1269,10 @@ LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) {
           // EmitC ops.
           .Case<emitc::AddOp, emitc::ApplyOp, emitc::AssignOp, emitc::CallOp,
                 emitc::CallOpaqueOp, emitc::CastOp, emitc::CmpOp,
-                emitc::ConstantOp, emitc::DivOp, emitc::ExpressionOp,
-                emitc::ForOp, emitc::FuncOp, emitc::IfOp, emitc::IncludeOp,
-                emitc::MulOp, emitc::RemOp, emitc::ReturnOp, emitc::SubOp,
-                emitc::VariableOp, emitc::VerbatimOp>(
+                emitc::ConstantOp, emitc::DeclareFuncOp, emitc::DivOp,
+                emitc::ExpressionOp, emitc::ForOp, emitc::FuncOp, emitc::IfOp,
+                emitc::IncludeOp, emitc::MulOp, emitc::RemOp, emitc::ReturnOp,
+                emitc::SubOp, emitc::VariableOp, emitc::VerbatimOp>(
               [&](auto op) { return printOperation(*this, op); })
           // Func ops.
           .Case<func::CallOp, func::ConstantOp, func::FuncOp, func::ReturnOp>(
diff --git a/mlir/test/Dialect/EmitC/invalid_ops.mlir b/mlir/test/Dialect/EmitC/invalid_ops.mlir
index 707f9a5b23b0b..b95fafa379ff7 100644
--- a/mlir/test/Dialect/EmitC/invalid_ops.mlir
+++ b/mlir/test/Dialect/EmitC/invalid_ops.mlir
@@ -326,3 +326,13 @@ emitc.func @func_variadic(...)
 
 // expected-error at +1 {{'emitc.func' op does not support empty function bodies}}
 emitc.func private @empty()
+
+// -----
+
+// expected-error at +1 {{'emitc.declare_func' op 'bar' does not reference a valid function}}
+emitc.declare_func @bar
+
+// -----
+
+// expected-error at +1 {{'emitc.declare_func' op requires attribute 'callee'}}
+"emitc.declare_func"()  : () -> ()
diff --git a/mlir/test/Dialect/EmitC/ops.mlir b/mlir/test/Dialect/EmitC/ops.mlir
index b41333faa4d4e..81ad5a2b46334 100644
--- a/mlir/test/Dialect/EmitC/ops.mlir
+++ b/mlir/test/Dialect/EmitC/ops.mlir
@@ -15,6 +15,8 @@ func.func @f(%arg0: i32, %f: !emitc.opaque<"int32_t">) {
   return
 }
 
+emitc.declare_func @func
+
 emitc.func @func(%arg0 : i32) {
   emitc.call_opaque "foo"(%arg0) : (i32) -> ()
   emitc.return
diff --git a/mlir/test/Target/Cpp/declare_func.mlir b/mlir/test/Target/Cpp/declare_func.mlir
new file mode 100644
index 0000000000000..72c087a3388e2
--- /dev/null
+++ b/mlir/test/Target/Cpp/declare_func.mlir
@@ -0,0 +1,16 @@
+// RUN: mlir-translate -mlir-to-cpp %s | FileCheck %s
+
+// CHECK: int32_t bar(int32_t [[V1:[^ ]*]]);
+emitc.declare_func @bar
+// CHECK: int32_t bar(int32_t [[V1:[^ ]*]]) {
+emitc.func @bar(%arg0: i32) -> i32 {
+    emitc.return %arg0 : i32
+}
+
+
+// CHECK: static inline int32_t foo(int32_t [[V1:[^ ]*]]);
+emitc.declare_func @foo
+// CHECK: static inline int32_t foo(int32_t [[V1:[^ ]*]]) {
+emitc.func @foo(%arg0: i32) -> i32 attributes {specifiers = ["static","inline"]} {
+    emitc.return %arg0 : i32
+}

>From 79d9cab638db1e27af417e3781c6dbab8a639ab7 Mon Sep 17 00:00:00 2001
From: Marius Brehler <marius.brehler at iml.fraunhofer.de>
Date: Sat, 3 Feb 2024 15:48:15 +0000
Subject: [PATCH 2/3] Rename `callee` to `sym_name` and revise checking if
 specified

---
 mlir/include/mlir/Dialect/EmitC/IR/EmitC.td | 4 ++--
 mlir/lib/Dialect/EmitC/IR/EmitC.cpp         | 6 +++---
 mlir/lib/Target/Cpp/TranslateToCpp.cpp      | 2 +-
 mlir/test/Dialect/EmitC/invalid_ops.mlir    | 2 +-
 4 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index fe004e441e9f9..0e71a9545cc36 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -496,9 +496,9 @@ def EmitC_DeclareFuncOp : EmitC_Op<"declare_func", [
     }
     ```
   }];
-  let arguments = (ins FlatSymbolRefAttr:$callee);
+  let arguments = (ins FlatSymbolRefAttr:$sym_name);
   let assemblyFormat = [{
-    $callee attr-dict
+    $sym_name attr-dict
   }];
 }
 
diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
index 67bcb7079672e..30389de22b201 100644
--- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
+++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
@@ -399,10 +399,10 @@ FunctionType CallOp::getCalleeType() {
 
 LogicalResult
 DeclareFuncOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
-  // Check that the callee attribute was specified.
-  auto fnAttr = (*this)->getAttrOfType<FlatSymbolRefAttr>("callee");
+  // Check that the sym_name attribute was specified.
+  auto fnAttr = getSymNameAttr();
   if (!fnAttr)
-    return emitOpError("requires a 'callee' symbol reference attribute");
+    return emitOpError("requires a 'sym_name' symbol reference attribute");
   FuncOp fn = symbolTable.lookupNearestSymbolFrom<FuncOp>(*this, fnAttr);
   if (!fn)
     return emitOpError() << "'" << fnAttr.getValue()
diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
index 28aef02e72566..2c6efd2440b36 100644
--- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp
+++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
@@ -931,7 +931,7 @@ static LogicalResult printOperation(CppEmitter &emitter,
   raw_indented_ostream &os = emitter.ostream();
 
   auto functionOp = SymbolTable::lookupNearestSymbolFrom<emitc::FuncOp>(
-      declareFuncOp, declareFuncOp.getCalleeAttr());
+      declareFuncOp, declareFuncOp.getSymNameAttr());
 
   if (!functionOp)
     return failure();
diff --git a/mlir/test/Dialect/EmitC/invalid_ops.mlir b/mlir/test/Dialect/EmitC/invalid_ops.mlir
index b95fafa379ff7..ed71a490619ff 100644
--- a/mlir/test/Dialect/EmitC/invalid_ops.mlir
+++ b/mlir/test/Dialect/EmitC/invalid_ops.mlir
@@ -334,5 +334,5 @@ emitc.declare_func @bar
 
 // -----
 
-// expected-error at +1 {{'emitc.declare_func' op requires attribute 'callee'}}
+// expected-error at +1 {{'emitc.declare_func' op requires attribute 'sym_name'}}
 "emitc.declare_func"()  : () -> ()

>From 589be752626ad7d2627c073343cad8e79c563ca9 Mon Sep 17 00:00:00 2001
From: Marius Brehler <marius.brehler at iml.fraunhofer.de>
Date: Sun, 4 Feb 2024 09:22:38 +0000
Subject: [PATCH 3/3] Restrict declaration of static functions

---
 mlir/include/mlir/Dialect/EmitC/IR/EmitC.td |  1 +
 mlir/lib/Dialect/EmitC/IR/EmitC.cpp         | 12 ++++++++++++
 mlir/test/Dialect/EmitC/invalid_ops.mlir    | 12 ++++++++++++
 3 files changed, 25 insertions(+)

diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index 0e71a9545cc36..e64a3f713cef0 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -500,6 +500,7 @@ def EmitC_DeclareFuncOp : EmitC_Op<"declare_func", [
   let assemblyFormat = [{
     $sym_name attr-dict
   }];
+  let hasVerifier = 1;
 }
 
 def EmitC_FuncOp : EmitC_Op<"func", [
diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
index 30389de22b201..5a88439956a54 100644
--- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
+++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
@@ -411,6 +411,18 @@ DeclareFuncOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
   return success();
 }
 
+LogicalResult DeclareFuncOp::verify() {
+  auto fnAttr = (*this)->getAttrOfType<FlatSymbolRefAttr>("sym_name");
+  auto funcOp =
+      SymbolTable::lookupNearestSymbolFrom<emitc::FuncOp>((*this), fnAttr);
+
+  if (!isa<ModuleOp>((*this)->getParentOp()) && funcOp.isPrivate())
+    return emitOpError(
+        "parent needs to be ModuleOp if declaring a static function");
+
+  return success();
+}
+
 //===----------------------------------------------------------------------===//
 // FuncOp
 //===----------------------------------------------------------------------===//
diff --git a/mlir/test/Dialect/EmitC/invalid_ops.mlir b/mlir/test/Dialect/EmitC/invalid_ops.mlir
index ed71a490619ff..669f793a06feb 100644
--- a/mlir/test/Dialect/EmitC/invalid_ops.mlir
+++ b/mlir/test/Dialect/EmitC/invalid_ops.mlir
@@ -336,3 +336,15 @@ emitc.declare_func @bar
 
 // expected-error at +1 {{'emitc.declare_func' op requires attribute 'sym_name'}}
 "emitc.declare_func"()  : () -> ()
+
+// -----
+
+emitc.func private @foo(%arg0: i32) attributes {specifiers = ["static"]} {
+    emitc.return
+}
+
+emitc.func @bar() {
+    // expected-error at +1 {{'emitc.declare_func' op parent needs to be ModuleOp if declaring static functions}}
+    emitc.declare_func @foo
+    emitc.return
+}



More information about the Mlir-commits mailing list