[llvm] [mlir] [mlir][EmitC] Add func, call and return operations and conversions (PR #79612)

via llvm-commits llvm-commits at lists.llvm.org
Fri Jan 26 07:58:31 PST 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir-emitc

@llvm/pr-subscribers-mlir

Author: Marius Brehler (marbre)

<details>
<summary>Changes</summary>

This adds a `func`, `call` and `return` operation to the EmitC dialect,
closely related to the corresponding operations of the Func dialect. In
contrast to the operations of the Func dialect, the EmitC operations do
not support multiple results. The `emitc.func` op features a
`specifiers` argument that for example allows, with corresponding
support in the emitter, to emit `inline static` functions.

Furthermore, this adds patterns and a pass to convert the Func dialect to EmitC.
A `func.func` op that is `private` is converted to `emitc.func` with a
`"static"` specifier.

---

Patch is 40.77 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/79612.diff


16 Files Affected:

- (added) mlir/include/mlir/Conversion/FuncToEmitC/FuncToEmitC.h (+25) 
- (modified) mlir/include/mlir/Conversion/Passes.h (+1) 
- (modified) mlir/include/mlir/Conversion/Passes.td (+9) 
- (modified) mlir/include/mlir/Dialect/EmitC/IR/EmitC.h (+1) 
- (modified) mlir/include/mlir/Dialect/EmitC/IR/EmitC.td (+186) 
- (modified) mlir/lib/Conversion/CMakeLists.txt (+1) 
- (added) mlir/lib/Conversion/FuncToEmitC/CMakeLists.txt (+15) 
- (added) mlir/lib/Conversion/FuncToEmitC/FuncToEmitC.cpp (+137) 
- (modified) mlir/lib/Dialect/EmitC/IR/CMakeLists.txt (+2) 
- (modified) mlir/lib/Dialect/EmitC/IR/EmitC.cpp (+182) 
- (modified) mlir/lib/Target/Cpp/TranslateToCpp.cpp (+113-29) 
- (added) mlir/test/Conversion/FuncToEmitC/func-to-emitc.mlir (+55) 
- (modified) mlir/test/Dialect/EmitC/invalid_ops.mlir (+32) 
- (modified) mlir/test/Dialect/EmitC/ops.mlir (+15) 
- (added) mlir/test/Target/Cpp/func.mlir (+39) 
- (modified) utils/bazel/llvm-project-overlay/mlir/BUILD.bazel (+31) 


``````````diff
diff --git a/mlir/include/mlir/Conversion/FuncToEmitC/FuncToEmitC.h b/mlir/include/mlir/Conversion/FuncToEmitC/FuncToEmitC.h
new file mode 100644
index 000000000000000..368c604b8356be7
--- /dev/null
+++ b/mlir/include/mlir/Conversion/FuncToEmitC/FuncToEmitC.h
@@ -0,0 +1,25 @@
+//===- FuncToEmitC.h - Func to EmitC dialect conversion ---------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_CONVERSION_FUNCTOEMITC_FUNCTOEMITC_H
+#define MLIR_CONVERSION_FUNCTOEMITC_FUNCTOEMITC_H
+
+#include <memory>
+
+namespace mlir {
+class ModuleOp;
+class Pass;
+
+#define GEN_PASS_DECL_FUNCTOEMITC
+#include "mlir/Conversion/Passes.h.inc"
+
+std::unique_ptr<Pass> createConvertFuncToEmitC();
+
+} // namespace mlir
+
+#endif // MLIR_CONVERSION_FUNCTOEMITC_FUNCTOEMITC_H
diff --git a/mlir/include/mlir/Conversion/Passes.h b/mlir/include/mlir/Conversion/Passes.h
index a25fd17ea923fb5..401f5d3106180d4 100644
--- a/mlir/include/mlir/Conversion/Passes.h
+++ b/mlir/include/mlir/Conversion/Passes.h
@@ -28,6 +28,7 @@
 #include "mlir/Conversion/ControlFlowToSPIRV/ControlFlowToSPIRV.h"
 #include "mlir/Conversion/ControlFlowToSPIRV/ControlFlowToSPIRVPass.h"
 #include "mlir/Conversion/ConvertToLLVM/ToLLVMPass.h"
+#include "mlir/Conversion/FuncToEmitC/FuncToEmitC.h"
 #include "mlir/Conversion/FuncToLLVM/ConvertFuncToLLVMPass.h"
 #include "mlir/Conversion/FuncToSPIRV/FuncToSPIRVPass.h"
 #include "mlir/Conversion/GPUCommon/GPUCommonPass.h"
diff --git a/mlir/include/mlir/Conversion/Passes.td b/mlir/include/mlir/Conversion/Passes.td
index 6193aeb545bc6b1..e4f6012ca24fa84 100644
--- a/mlir/include/mlir/Conversion/Passes.td
+++ b/mlir/include/mlir/Conversion/Passes.td
@@ -344,6 +344,15 @@ def ConvertControlFlowToSPIRV : Pass<"convert-cf-to-spirv"> {
   ];
 }
 
+//===----------------------------------------------------------------------===//
+// FuncToEmitC
+//===----------------------------------------------------------------------===//
+
+def ConvertFuncToEmitC : Pass<"convert-func-to-emitc", "ModuleOp"> {
+  let summary = "Convert Func dialect to EmitC dialect";
+  let dependentDialects = ["emitc::EmitCDialect"];
+}
+
 //===----------------------------------------------------------------------===//
 // FuncToLLVM
 //===----------------------------------------------------------------------===//
diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h
index 4dff26e23c42850..3d38744527d5997 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h
@@ -20,6 +20,7 @@
 #include "mlir/IR/Dialect.h"
 #include "mlir/Interfaces/CastInterfaces.h"
 #include "mlir/Interfaces/ControlFlowInterfaces.h"
+#include "mlir/Interfaces/FunctionInterfaces.h"
 #include "mlir/Interfaces/SideEffectInterfaces.h"
 
 #include "mlir/Dialect/EmitC/IR/EmitCDialect.h.inc"
diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index b8f8f1e2d818d51..df0418a2ac372ac 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -16,8 +16,10 @@
 include "mlir/Dialect/EmitC/IR/EmitCAttributes.td"
 include "mlir/Dialect/EmitC/IR/EmitCTypes.td"
 
+include "mlir/Interfaces/CallInterfaces.td"
 include "mlir/Interfaces/CastInterfaces.td"
 include "mlir/Interfaces/ControlFlowInterfaces.td"
+include "mlir/Interfaces/FunctionInterfaces.td"
 include "mlir/Interfaces/SideEffectInterfaces.td"
 include "mlir/IR/RegionKindInterface.td"
 
@@ -386,6 +388,190 @@ def EmitC_ForOp : EmitC_Op<"for",
   let hasRegionVerifier = 1;
 }
 
+def EmitC_CallOp : EmitC_Op<"call",
+    [CallOpInterface,
+     DeclareOpInterfaceMethods<SymbolUserOpInterface>]> {
+  let summary = "call operation";
+  let description = [{
+    The `emitc.call` operation represents a direct call to an `emitc.func`
+    that is within the same symbol scope as the call. The operands and result type
+    of the call must match the specified function type. The callee is encoded as a
+    symbol reference attribute named "callee".
+
+    Example:
+
+    ```mlir
+    %2 = emitc.call @my_add(%0, %1) : (f32, f32) -> f32
+    ```
+  }];
+  let arguments = (ins FlatSymbolRefAttr:$callee, Variadic<AnyType>:$operands);
+  let results = (outs Variadic<AnyType>);
+
+  let builders = [
+    OpBuilder<(ins "FuncOp":$callee, CArg<"ValueRange", "{}">:$operands), [{
+      $_state.addOperands(operands);
+      $_state.addAttribute("callee", SymbolRefAttr::get(callee));
+      $_state.addTypes(callee.getFunctionType().getResults());
+    }]>,
+    OpBuilder<(ins "SymbolRefAttr":$callee, "TypeRange":$results,
+      CArg<"ValueRange", "{}">:$operands), [{
+      $_state.addOperands(operands);
+      $_state.addAttribute("callee", callee);
+      $_state.addTypes(results);
+    }]>,
+    OpBuilder<(ins "StringAttr":$callee, "TypeRange":$results,
+      CArg<"ValueRange", "{}">:$operands), [{
+      build($_builder, $_state, SymbolRefAttr::get(callee), results, operands);
+    }]>,
+    OpBuilder<(ins "StringRef":$callee, "TypeRange":$results,
+      CArg<"ValueRange", "{}">:$operands), [{
+      build($_builder, $_state, StringAttr::get($_builder.getContext(), callee),
+            results, operands);
+    }]>];
+
+  let extraClassDeclaration = [{
+    FunctionType getCalleeType();
+
+    /// Get the argument operands to the called function.
+    operand_range getArgOperands() {
+      return {arg_operand_begin(), arg_operand_end()};
+    }
+
+    MutableOperandRange getArgOperandsMutable() {
+      return getOperandsMutable();
+    }
+
+    operand_iterator arg_operand_begin() { return operand_begin(); }
+    operand_iterator arg_operand_end() { return operand_end(); }
+
+    /// Return the callee of this operation.
+    CallInterfaceCallable getCallableForCallee() {
+      return (*this)->getAttrOfType<SymbolRefAttr>("callee");
+    }
+
+    /// Set the callee for this operation.
+    void setCalleeFromCallable(CallInterfaceCallable callee) {
+      (*this)->setAttr("callee", callee.get<SymbolRefAttr>());
+    }
+  }];
+
+  let assemblyFormat = [{
+    $callee `(` $operands `)` attr-dict `:` functional-type($operands, results)
+  }];
+}
+
+def EmitC_FuncOp : EmitC_Op<"func", [
+  AutomaticAllocationScope,
+  FunctionOpInterface, IsolatedFromAbove
+]> {
+  let summary = "An operation with a name containing a single `SSACFG` region";
+  let description = [{
+    Operations within the function cannot implicitly capture values defined
+    outside of the function, i.e. Functions are `IsolatedFromAbove`. All
+    external references must use function arguments or attributes that establish
+    a symbolic connection (e.g. symbols referenced by name via a string
+    attribute like SymbolRefAttr). While the MLIR textual form provides a nice
+    inline syntax for function arguments, they are internally represented as
+    “block arguments” to the first block in the region.
+
+    Only dialect attribute names may be specified in the attribute dictionaries
+    for function arguments, results, or the function itself.
+
+    Example:
+
+    ```mlir
+    // A function with no results:
+    emitc.func @foo(%arg0 : i32) {
+      emitc.call_opaque "bar" (%arg0) : (i32) -> ()
+      emitc.return
+    }
+
+    // A function with its argument as single result:
+    emitc.func @foo(%arg0 : i32) -> i32 {
+      emitc.return %arg0 : i32
+    }
+
+    // A function with specifiers attribute:
+    emitc.func @example_specifiers_fn_attr() -> i32
+                attributes {specifiers = ["static","inline"]} {
+      %0 = emitc.call_opaque "foo" (): () -> i32
+      emitc.return %0 : i32
+    }
+
+    ```
+  }];
+  let arguments = (ins SymbolNameAttr:$sym_name,
+                       TypeAttrOf<FunctionType>:$function_type,
+                       OptionalAttr<StrArrayAttr>:$specifiers,
+                       OptionalAttr<DictArrayAttr>:$arg_attrs,
+                       OptionalAttr<DictArrayAttr>:$res_attrs);
+  let regions = (region AnyRegion:$body);
+
+  let builders = [OpBuilder<(ins
+    "StringRef":$name, "FunctionType":$type,
+    CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs,
+    CArg<"ArrayRef<DictionaryAttr>", "{}">:$argAttrs)
+  >];
+  let extraClassDeclaration = [{
+    /// Create a deep copy of this function and all of its blocks, remapping any
+    /// operands that use values outside of the function using the map that is
+    /// provided (leaving them alone if no entry is present). If the mapper
+    /// contains entries for function arguments, these arguments are not
+    /// included in the new function. Replaces references to cloned sub-values
+    /// with the corresponding value that is copied, and adds those mappings to
+    /// the mapper.
+    FuncOp clone(IRMapping &mapper);
+    FuncOp clone();
+
+    /// Clone the internal blocks and attributes from this function into dest.
+    /// Any cloned blocks are appended to the back of dest. This function
+    /// asserts that the attributes of the current function and dest are
+    /// compatible.
+    void cloneInto(FuncOp dest, IRMapping &mapper);
+
+    //===------------------------------------------------------------------===//
+    // FunctionOpInterface Methods
+    //===------------------------------------------------------------------===//
+
+    /// Returns the region on the current operation that is callable. This may
+    /// return null in the case of an external callable object, e.g. an external
+    /// function.
+    ::mlir::Region *getCallableRegion() { return isExternal() ? nullptr : &getBody(); }
+
+    /// Returns the argument types of this function.
+    ArrayRef<Type> getArgumentTypes() { return getFunctionType().getInputs(); }
+
+    /// Returns the result types of this function.
+    ArrayRef<Type> getResultTypes() { return getFunctionType().getResults(); }
+  }];
+  let hasCustomAssemblyFormat = 1;
+  let hasVerifier = 1;
+}
+
+def EmitC_ReturnOp : EmitC_Op<"return", [Pure, HasParent<"FuncOp">,
+                                ReturnLike, Terminator]> {
+  let summary = "Function return operation";
+  let description = [{
+    The `emitc.return` operation represents a return operation within a function.
+    The operation takes zero or exactly one operand and produces no results.
+    The operand number and type must match the signature of the function
+    that contains the operation.
+
+    Example:
+
+    ```mlir
+    emitc.func @foo() : (i32) {
+      ...
+      emitc.return %0 : i32
+    }
+    ```
+  }];
+  let arguments = (ins Optional<AnyType>:$operand);
+
+  let assemblyFormat = "attr-dict ($operand^ `:` type($operand))?";
+  let hasVerifier = 1;
+}
+
 def EmitC_IncludeOp
     : EmitC_Op<"include", [HasParent<"ModuleOp">]> {
   let summary = "Include operation";
diff --git a/mlir/lib/Conversion/CMakeLists.txt b/mlir/lib/Conversion/CMakeLists.txt
index c3a2481975040c9..bc89f8621a29c15 100644
--- a/mlir/lib/Conversion/CMakeLists.txt
+++ b/mlir/lib/Conversion/CMakeLists.txt
@@ -17,6 +17,7 @@ add_subdirectory(ControlFlowToLLVM)
 add_subdirectory(ControlFlowToSCF)
 add_subdirectory(ControlFlowToSPIRV)
 add_subdirectory(ConvertToLLVM)
+add_subdirectory(FuncToEmitC)
 add_subdirectory(FuncToLLVM)
 add_subdirectory(FuncToSPIRV)
 add_subdirectory(GPUCommon)
diff --git a/mlir/lib/Conversion/FuncToEmitC/CMakeLists.txt b/mlir/lib/Conversion/FuncToEmitC/CMakeLists.txt
new file mode 100644
index 000000000000000..c3c1f7abebf94db
--- /dev/null
+++ b/mlir/lib/Conversion/FuncToEmitC/CMakeLists.txt
@@ -0,0 +1,15 @@
+add_mlir_conversion_library(MLIRFuncToEmitC
+  FuncToEmitC.cpp
+
+  ADDITIONAL_HEADER_DIRS
+  ${MLIR_MAIN_INCLUDE_DIR}/mlir/Conversion/FuncToEmitC
+
+  DEPENDS
+  MLIRConversionPassIncGen
+
+  LINK_LIBS PUBLIC
+  MLIREmitCDialect
+  MLIRFuncDialect
+  MLIRPass
+  MLIRTransformUtils
+  )
diff --git a/mlir/lib/Conversion/FuncToEmitC/FuncToEmitC.cpp b/mlir/lib/Conversion/FuncToEmitC/FuncToEmitC.cpp
new file mode 100644
index 000000000000000..502619bfbd2db8b
--- /dev/null
+++ b/mlir/lib/Conversion/FuncToEmitC/FuncToEmitC.cpp
@@ -0,0 +1,137 @@
+//===- FuncToEmitC.cpp - Func to EmitC dialect conversion -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements patterns and a pass to convert the Func dialect to the
+// EmitC dialect.
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Conversion/FuncToEmitC/FuncToEmitC.h"
+
+#include "mlir/Dialect/EmitC/IR/EmitC.h"
+#include "mlir/Dialect/Func/IR/FuncOps.h"
+#include "mlir/Pass/Pass.h"
+#include "mlir/Transforms/DialectConversion.h"
+
+namespace mlir {
+#define GEN_PASS_DEF_CONVERTFUNCTOEMITC
+#include "mlir/Conversion/Passes.h.inc"
+} // namespace mlir
+
+using namespace mlir;
+
+namespace {
+class CallOpConversion final : public OpConversionPattern<func::CallOp> {
+public:
+  using OpConversionPattern<func::CallOp>::OpConversionPattern;
+
+  LogicalResult
+  matchAndRewrite(func::CallOp callOp, OpAdaptor adaptor,
+                  ConversionPatternRewriter &rewriter) const override {
+    // multiple results func was not converted to spirv.func
+    if (callOp.getNumResults() > 1)
+      return rewriter.notifyMatchFailure(
+          callOp, "Only functions with zero or one result can be converted");
+
+    rewriter.replaceOpWithNewOp<emitc::CallOp>(
+        callOp,
+        callOp.getNumResults() ? callOp.getResult(0).getType() : nullptr,
+        adaptor.getOperands(), callOp->getAttrs());
+
+    return success();
+  }
+};
+
+class FuncOpConversion final : public OpConversionPattern<func::FuncOp> {
+public:
+  using OpConversionPattern<func::FuncOp>::OpConversionPattern;
+
+  LogicalResult
+  matchAndRewrite(func::FuncOp funcOp, OpAdaptor adaptor,
+                  ConversionPatternRewriter &rewriter) const override {
+
+    if (funcOp.getFunctionType().getNumResults() > 1)
+      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());
+
+    // Copy over all attributes other than the function name and type.
+    for (const auto &namedAttr : funcOp->getAttrs()) {
+      if (namedAttr.getName() != funcOp.getFunctionTypeAttrName() &&
+          namedAttr.getName() != SymbolTable::getSymbolAttrName())
+        newFuncOp->setAttr(namedAttr.getName(), namedAttr.getValue());
+    }
+
+    // Create add `static` to specifiers if `func.func` is private.
+    if (funcOp.isPrivate()) {
+      StringAttr specifier = rewriter.getStringAttr("static");
+      ArrayAttr specifiers = rewriter.getArrayAttr(specifier);
+      newFuncOp.setSpecifiersAttr(specifiers);
+    }
+
+    rewriter.inlineRegionBefore(funcOp.getBody(), newFuncOp.getBody(),
+                                newFuncOp.end());
+    rewriter.eraseOp(funcOp);
+
+    return success();
+  }
+};
+
+class ReturnOpConversion final : public OpConversionPattern<func::ReturnOp> {
+public:
+  using OpConversionPattern<func::ReturnOp>::OpConversionPattern;
+
+  LogicalResult
+  matchAndRewrite(func::ReturnOp returnOp, OpAdaptor adaptor,
+                  ConversionPatternRewriter &rewriter) const override {
+    if (returnOp.getNumOperands() > 1)
+      return rewriter.notifyMatchFailure(
+          returnOp, "Only zero or one operand is supported");
+
+    rewriter.replaceOpWithNewOp<emitc::ReturnOp>(
+        returnOp,
+        returnOp.getNumOperands() ? adaptor.getOperands()[0] : nullptr);
+    return success();
+  }
+};
+
+struct ConvertFuncToEmitC
+    : public impl::ConvertFuncToEmitCBase<ConvertFuncToEmitC> {
+  void runOnOperation() override;
+};
+} // namespace
+
+void populateFuncToEmitcPatterns(MLIRContext *ctx,
+                                 RewritePatternSet &patterns) {
+  patterns.add<CallOpConversion>(ctx);
+  patterns.add<FuncOpConversion>(ctx);
+  patterns.add<ReturnOpConversion>(ctx);
+}
+
+void ConvertFuncToEmitC::runOnOperation() {
+  ConversionTarget target(getContext());
+
+  target.addLegalDialect<emitc::EmitCDialect>();
+  target.addIllegalOp<func::CallOp>();
+  target.addIllegalOp<func::FuncOp>();
+  target.addIllegalOp<func::ReturnOp>();
+
+  RewritePatternSet patterns(&getContext());
+  populateFuncToEmitcPatterns(&getContext(), patterns);
+
+  if (failed(
+          applyPartialConversion(getOperation(), target, std::move(patterns))))
+    signalPassFailure();
+}
diff --git a/mlir/lib/Dialect/EmitC/IR/CMakeLists.txt b/mlir/lib/Dialect/EmitC/IR/CMakeLists.txt
index 4665c41a62e80b8..4cc54201d2745d6 100644
--- a/mlir/lib/Dialect/EmitC/IR/CMakeLists.txt
+++ b/mlir/lib/Dialect/EmitC/IR/CMakeLists.txt
@@ -9,8 +9,10 @@ add_mlir_dialect_library(MLIREmitCDialect
   MLIREmitCAttributesIncGen
 
   LINK_LIBS PUBLIC
+  MLIRCallInterfaces
   MLIRCastInterfaces
   MLIRControlFlowInterfaces
+  MLIRFunctionInterfaces
   MLIRIR
   MLIRSideEffectInterfaces
   )
diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
index 5f502f1f7a1714d..ef67764e732d224 100644
--- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
+++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
@@ -8,7 +8,10 @@
 
 #include "mlir/Dialect/EmitC/IR/EmitC.h"
 #include "mlir/IR/Builders.h"
+#include "mlir/IR/BuiltinAttributes.h"
 #include "mlir/IR/DialectImplementation.h"
+#include "mlir/IR/IRMapping.h"
+#include "mlir/Interfaces/FunctionImplementation.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/TypeSwitch.h"
 
@@ -346,6 +349,185 @@ LogicalResult ForOp::verifyRegions() {
 
   return success();
 }
+//===----------------------------------------------------------------------===//
+// CallOp
+//===----------------------------------------------------------------------===//
+
+LogicalResult CallOp::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";
+
+  // Verify that the operand and result types match the callee.
+  auto fnType = fn.getFunctionType();
+  if (fnType.getNumInputs() != getNumOperands())
+    return emitOpError("incorrect number of operands for callee");
+
+  for (unsigned i = 0, e = fnType.getNumInputs(); i != e; ++i)
+    if (getOperand(i).getType() != fnType.getInput(i))
+      return emitOpError("operand type mismatch: expected operand type ")
+             << fnType.getInput(i) << ", but provided "
+             << getOperand(i).getType() << " for operand number " << i;
+
+  if (fnType.getNumResults() != getNumResults())
+    return emitOpError("incorrect number of results for callee");
+
+  for (unsigned i = 0, e = fnType.getNumResults(); i != e; ++i)
+    if (getResult(i).getType() != fnType.getResult(i)) {
+      auto diag = emitOpError("result type mismatch at index ") << i;
+      diag.attachNote() << "      op result types: " << getResultTypes();
+      diag.attachNote() << "function result types: " << fnType.getResults();
+      return diag;
+    }
+
+  return success();
+}
+
+FunctionType CallOp::getCalleeType() {
+  return FunctionType::get(getContext(), getOperandTypes(), getResultTypes());
+}
+
+//===----------------------------------------------------------------------===//
+// FuncOp
+//===----------------------------------------------------------------------===//
+
+void FuncOp::build(OpBuilder &builder, OperationState &state, Strin...
[truncated]

``````````

</details>


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


More information about the llvm-commits mailing list