MLIR : Define Operation and Access C++ API

Hi,
We are mimicing teh standalone dialect which has one operation defined in ODS format. We just changed the name of standalone to custom.
The CustomOps.td file is :

#ifndef CUSTOM_OPS
#define CUSTOM_OPS
include "Custom/CustomTypes.td"
include "mlir/Interfaces/InferTypeOpInterface.td"
include "mlir/Interfaces/SideEffectInterfaces.td"
def Custom_FooOp : Custom_Op<"foo", [Pure,
                                             SameOperandsAndResultType]> {
    let summary = "Illustrates how to define an operation.";
    let description = [{
        The `custom.foo` operation illustrates how to define a new
        operation in a dialect. It uses an operation trait to declare that it
        has no side effects.
        This operation takes an integer argument and returns an integer.
        Example:
        ```mlir
        %0 = arith.constant 2 : i32
        // Apply the foo operation to %0
        %1 = custom.foo %0 : i32
        ```
    }];
    let arguments = (ins I32:$input);
    let results = (outs I32:$res);
    let assemblyFormat = [{
        $input attr-dict `:` type($input)
    }];
}
#endif // CUSTOM_OPS

we created a test file to access various c++ APIs :

#include "mlir/IR/Builders.h"
#include "mlir/IR/OwningOpRef.h"

#include <mlir/Dialect/Arith/IR/Arith.h>
#include </home/sudip/Research/opensource/llvm-project/mlir/examples/custom/include/Custom/CustomDialect.h>
#include <stdio.h>
#include "mlir/CAPI/IR.h"
#include "mlir/InitAllDialects.h"
#include <iostream>
#include "llvm/Support/raw_ostream.h"
#include "mlir/Pass/AnalysisManager.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinOps.h"

          
using namespace mlir;
using namespace arith;
using namespace custom;
/* RUN: casair-cppapi-ir-test 2>&1 | FileCheck %s
*/
int main(){

    MLIRContext context;
    context.getOrLoadDialect<arith::ArithDialect>();
    context.getOrLoadDialect<func::FuncDialect>();
    context.getOrLoadDialect<custom::CustomDialect>();
    auto loc = UnknownLoc::get(&context);

    OwningOpRef<ModuleOp> module(ModuleOp::create(loc));
    OpBuilder builder(&context);
    llvm::raw_ostream& os = llvm::outs();
    
    auto i64Type = IntegerType::get(&context, 64);
    auto constOp = builder.create<arith::ConstantOp>(loc, i64Type, builder.getI64IntegerAttr(2));
    module->push_back(constOp);

    auto fooOp = builder.create<custom::FooOp>(loc, builder.getI32IntegerAttr(2));
    // CHECK: %[[C:.*]] = arith.constant 2 : i64
    module->print(os);
}

The error received :

sudip@sudip:~/Research/opensource/llvm-project/mlir/examples/custom/build$ cmake --build .
[10/11] Building CXX object test/CPPAPI/CMakeFiles/custom-cppapi-ir-test.dir/Test.cpp.o
FAILED: test/CPPAPI/CMakeFiles/custom-cppapi-ir-test.dir/Test.cpp.o 
/usr/bin/c++ -DGTEST_HAS_RTTI=0 -D_DEBUG -D_GLIBCXX_ASSERTIONS -D_LIBCPP_ENABLE_ASSERTIONS -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I/home/sudip/Research/opensource/llvm-project/llvm/include -I/home/sudip/Research/opensource/llvm-project/build/include -I/home/sudip/Research/opensource/llvm-project/mlir/include -I/home/sudip/Research/opensource/llvm-project/build/tools/mlir/include -I/home/sudip/Research/opensource/llvm-project/mlir/examples/custom/include -I/home/sudip/Research/opensource/llvm-project/mlir/examples/custom/build/include -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror=date-time -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wno-missing-field-initializers -Wimplicit-fallthrough -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wdelete-non-virtual-dtor -Wsuggest-override -Wno-comment -Wno-misleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections   -D_DEBUG -D_GLIBCXX_ASSERTIONS -D_LIBCPP_ENABLE_ASSERTIONS -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS  -fno-exceptions -funwind-tables -fno-rtti -fno-exceptions -funwind-tables -fno-rtti -UNDEBUG -MD -MT test/CPPAPI/CMakeFiles/custom-cppapi-ir-test.dir/Test.cpp.o -MF test/CPPAPI/CMakeFiles/custom-cppapi-ir-test.dir/Test.cpp.o.d -o test/CPPAPI/CMakeFiles/custom-cppapi-ir-test.dir/Test.cpp.o -c /home/sudip/Research/opensource/llvm-project/mlir/examples/custom/test/CPPAPI/Test.cpp
/home/sudip/Research/opensource/llvm-project/mlir/examples/custom/test/CPPAPI/Test.cpp: In function ‘int main()’:
/home/sudip/Research/opensource/llvm-project/mlir/examples/custom/test/CPPAPI/Test.cpp:47:41: error: ‘FooOp’ is not a member of ‘mlir::custom’
   47 |     auto fooOp = builder.create<custom::FooOp>(loc, builder.getI32IntegerAttr(2));
      |                                         ^~~~~
/home/sudip/Research/opensource/llvm-project/mlir/examples/custom/test/CPPAPI/Test.cpp:47:47: error: no matching function for call to ‘mlir::OpBuilder::create<<expression error> >(mlir::UnknownLoc&, mlir::IntegerAttr)’
   47 |     auto fooOp = builder.create<custom::FooOp>(loc, builder.getI32IntegerAttr(2));
      |                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /home/sudip/Research/opensource/llvm-project/mlir/examples/custom/test/CPPAPI/Test.cpp:1:
/home/sudip/Research/opensource/llvm-project/mlir/include/mlir/IR/Builders.h:487:8: note: candidate: ‘template<class OpTy, class ... Args> OpTy mlir::OpBuilder::create(mlir::Location, Args&& ...)’
  487 |   OpTy create(Location location, Args &&...args) {
      |        ^~~~~~
/home/sudip/Research/opensource/llvm-project/mlir/include/mlir/IR/Builders.h:487:8: note:   template argument deduction/substitution failed:
/home/sudip/Research/opensource/llvm-project/mlir/examples/custom/test/CPPAPI/Test.cpp:47:47: error: template argument 1 is invalid
   47 |     auto fooOp = builder.create<custom::FooOp>(loc, builder.getI32IntegerAttr(2));
      |                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ninja: build stopped: subcommand failed.

The reason for creating a C++ file accessing C++ APIs is to understand the flow of the project and how to use various dialects defined in mlir for our purpose.
Please help around the above error.

What’s in /home/sudip/Research/opensource/llvm-project/mlir/examples/custom/include/Custom/CustomDialect.h? There should be an include to the ODS-generated header files with all the ops. Another thing is the dialect definition in ODS: it needs to define the custom namespace.

CustomDialect.td (it has the namespace defined) :

#ifndef CUSTOM_DIALECT
#define CUSTOM_DIALECT
​
include "mlir/IR/OpBase.td"
​
//===----------------------------------------------------------------------===//
// Custom dialect definition.
//===----------------------------------------------------------------------===//
​
def Custom_Dialect : Dialect {
    let name = "custom";
    let summary = "A Custom out-of-tree MLIR dialect.";
    let description = [{
        This dialect is an example of an out-of-tree MLIR dialect designed to
        illustrate the basic setup required to develop MLIR-based tools without
        working inside of the LLVM source tree.
    }];
    let cppNamespace = "::mlir::custom";
​
    let useDefaultTypePrinterParser = 1;
    let extraClassDeclaration = [{
        void registerTypes();
    }];
}
​
//===----------------------------------------------------------------------===//
// Base Custom operation definition.
//===----------------------------------------------------------------------===//
​
class Custom_Op<string mnemonic, list<Trait> traits = []> :
        Op<Custom_Dialect, mnemonic, traits>;
​
#endif // CUSTOM_DIALECT

CustomDialect.h (there is an include to CustomOpsDialect.h.inc):

#ifndef CUSTOM_CUSTOMDIALECT_H
#define CUSTOM_CUSTOMDIALECT_H
​
#include "mlir/IR/Dialect.h"
​
#include "Custom/CustomOpsDialect.h.inc"
​
#endif // CUSTOM_CUSTOMDIALECT_H

If I look at the toy examples, the Dialect.h file looks like:

/// Include the auto-generated header file containing the declaration of the toy
/// dialect.
#include "toy/Dialect.h.inc"

/// Include the auto-generated header file containing the declarations of the
/// toy operations.
#define GET_OP_CLASSES
#include "toy/Ops.h.inc"

Where do you include the file that declares the ops?

We have added it CustomOps.h and it looks like below -

#ifndef CUSTOM_CUSTOMOPS_H
#define CUSTOM_CUSTOMOPS_H

#include "mlir/IR/BuiltinTypes.h"
#include "mlir/IR/Dialect.h"
#include "mlir/IR/OpDefinition.h"
#include "mlir/Interfaces/InferTypeOpInterface.h"
#include "mlir/Interfaces/SideEffectInterfaces.h"

#define GET_OP_CLASSES
#include "Custom/CustomOps.h.inc"

#endif 

Added “include <CustomOps.h>” in CustomDialce.h :-


#ifndef CUSTOM_CUSTOMDIALECT_H
#define CUSTOM_CUSTOMDIALECT_H

#include "mlir/IR/Dialect.h"

#include Custom/CustomOpsDialect.h.inc"
#include "Custom/CustomOps.h"

#endif 

FooOp is being recognised. Thank you !

Now using the FooOp operation, we are making the type matching error with the paramters for the build function.
The build function in CustomOps.h.inc has FooOp::build functions for the parameters “Type and Value”.

static void build(::mlir::OpBuilder &odsBuilder, ::mlir::OperationState &odsState, ::mlir::Type res, ::mlir::Value input);
  static void build(::mlir::OpBuilder &odsBuilder, ::mlir::OperationState &odsState, ::mlir::Value input);
  static void build(::mlir::OpBuilder &odsBuilder, ::mlir::OperationState &odsState, ::mlir::TypeRange resultTypes, ::mlir::Value input);
  static void build(::mlir::OpBuilder &, ::mlir::OperationState &odsState, ::mlir::TypeRange resultTypes, ::mlir::ValueRange operands, ::llvm::ArrayRef<::mlir::NamedAttribute> attributes = {});
  static void build(::mlir::OpBuilder &odsBuilder, ::mlir::OperationState &odsState, ::mlir::ValueRange operands, ::llvm::ArrayRef<::mlir::NamedAttribute> attributes = {});
  ::mlir::LogicalResult verifyInvariantsImpl();

The way we are accessing is :

    auto addOp = builder.create<custom::FooOp>(loc, IntegerType::get(&context, 32), mlir::Value::getDefiningOp());

The error is :

 error: no matching function for call to ‘mlir::custom::FooOp::build(mlir::OpBuilder&, mlir::OperationState&, mlir::IntegerType)’
  490 |     OpTy::build(*this, state, std::forward<Args>(args)...);

You seem to be passing a Type and an Operation * (.getDefiningOp()) instead of the Value itself.