Lowering custom dialect

Hi all,

I am very new to MLIR and exploring the standalone project of MLIR. I have added two custom operations in StandaloneOps.td file. The added operations looks like below -

def Standalone_AddIOp : Standalone_Op<"addi", [Commutative]> {
  let summary = "integer addition operation";
  let description = [{
    The `addi` operation takes two operands and returns one result, each of
    these is required to be the same type. This type may be an integer scalar
    type, a vector whose element type is integer, or a tensor of integers. It
    has no standard attributes.

    Example:

    ```mlir
    // Scalar addition.
    %a = arith.addi %b, %c : i32
    ```
  }];

  let arguments = (ins I32:$lhs, I32:$rhs);
  let results = (outs I32:$res);

   let assemblyFormat = "$lhs `,` $rhs attr-dict `:` type($res)";
}

And the print operation -

def Standalone_PrintOp : Standalone_Op<"print"> {
  let summary = "print operation";
  let description = [{
    The "print" builtin operation prints a given input tensor, and produces
    no results.
  }];

  // The print operation takes an input tensor to print.
  let arguments = (ins I32:$input);

  let assemblyFormat = "$input attr-dict `:` type($input)";
}

Then I modified the standalone-capi-test.c as follow -

MlirModule module = mlirModuleCreateParse(
      ctx, mlirStringRefCreateFromCString( "func.func @main() {"
                                          "%0 = arith.constant 2 : i32\n"
                                          "%1 = arith.constant 3 : i32\n"
                                          "%2 = standalone.addi %0, %1 : i32 \n" 
                                          "standalone.print %2 : i32 \n"
                                          "return }")); 

After this changes I can generate MLIR code from this which looks like -

module {
  func.func @main() {
    %c2_i32 = arith.constant 2 : i32
    %c3_i32 = arith.constant 3 : i32
    %0 = standalone.addi %c2_i32, %c3_i32 : i32
    standalone.print %0 : i32
    return
  }
}

However, I am not able to lower the code to llvm.
I am trying like this -

./bin/standalone-opt ./bin/test.mlir -convert-scf-to-cf -convert-func-to-llvm -convert-cf-to-llvm -convert-memref-to-llvm -reconcile-unrealized-casts -convert-vector-to-llvm -allow-unregistered-dialect -mlir-print-debuginfo > test.llvm

This works fine and then trying to generate ll code from the llvm using the below command

./bin/standalone-translate -mlir-to-llvmir test.llvm -debug > test.ll

However, this gives me error -

test.llvm:5:10: error: Dialect `standalone' not found for custom op 'standalone.addi' 
    %2 = standalone.addi %0, %1 : i32 loc(#loc4)
         ^
test.llvm:5:10: note: Registered dialects: acc, amx, arm_neon, arm_sve, builtin, dlti, func, gpu, llvm, nvvm, omp, rocdl, x86vector ; for more info on dialect registration see https://mlir.llvm.org/getting_started/Faq/#registered-loaded-dependent-whats-up-with-dialects-management

Would you please help me to resolve this issue? Thanks in advance.

Regards
Sudip

To begin with, try to never use -allow-unregistered-dialect, this can only be a source of problems.

Have you looked into what is in the output file? If you have still some “standalone” operations then you’re missing the implementation of some lowering, did you add them for your ops? Also I don’t see anything on the command line that looks like --convert-standalone-to-XXX?

Thank you, Mehdi for your quick reply. The llvm code looks like this -

module attributes {llvm.data_layout = ""} {
  llvm.func @main() {
    %0 = llvm.mlir.constant(2 : i32) : i32 loc(#loc2)
    %1 = llvm.mlir.constant(3 : i32) : i32 loc(#loc3)
    %2 = standalone.addi %0, %1 : i32 loc(#loc4)
    standalone.print %2 : i32 loc(#loc5)
    llvm.return loc(#loc6)
  } loc(#loc1)
} loc(#loc)
#loc = loc("./bin/test.mlir":1:1)
#loc1 = loc("./bin/test.mlir":2:3)
#loc2 = loc("./bin/test.mlir":3:15)
#loc3 = loc("./bin/test.mlir":4:15)
#loc4 = loc("./bin/test.mlir":5:10)
#loc5 = loc("./bin/test.mlir":6:5)
#loc6 = loc("./bin/test.mlir":7:5)

I saw this contains standalone. Would you please point how to lower standalone? I tried --convert-standalone-to-std but that didn’t work.

Thanks,
Sudip

If the standalone example does not contain any such pass, you need to implement it yourself. The Toy tutorial explains how lowering work and provides example about how to do so.

sure. Let me check.

I have added cpp file for lowering the standalone dialect to standard dialect. However, I am getting following issue -

llvm-project/mlir/examples/standalone/lib/Standalone/LowerToStd.cpp:53:52: error: ‘AddIOp’ is not a member of ‘mlir::standalone’; did you mean ‘mlir::arith::AddIOp’?
   53 | using AddOpLowering = BinaryOpLowering<standalone::AddIOp, arith::AddIOp>;

My add operation looks like -

def Standalone_AddIOp : Standalone_Op<"addi", [Commutative]> {
  let summary = "integer addition operation";
  let description = [{
    The `addi` operation takes two operands and returns one result, each of
    these is required to be the same type. 
  }];

  let arguments = (ins I32:$lhs, I32:$rhs);
  let results = (outs I32:$res);

   let assemblyFormat = "$lhs `,` $rhs attr-dict `:` type($res)";
}

Lower to standard cpp looks like -

template <class OpClass, class StdOpClass>
struct BinaryOpLowering : public OpConversionPattern<OpClass> {
public:
  using OpConversionPattern<OpClass>::OpConversionPattern;
  LogicalResult
  matchAndRewrite(OpClass binOp,
                  typename OpConversionPattern<OpClass>::OpAdaptor adaptor,
                  ConversionPatternRewriter &rewriter) const override {
   
      rewriter.template replaceOpWithNewOp<StdOpClass>(
          binOp, adaptor.left(), adaptor.right());
      return success();

  }
};

using AddOpLowering = BinaryOpLowering<standalone::AddIOp, arith::AddIOp>;
} // namespace

I could not understand why the standalone::AddIOp is not recognized. While generating MLIR code it worked properly.

Regards,
Sudip

It worked when I add #include "Standalone/StandaloneOps.h" in LowerToStd.cpp

1 Like

We are parsing this mlir code to convert to llvm dialect and then to llvmir.

  func.func @main() {
  %1 = arith.constant 2 : i64
  %2 = arith.constant 3 : i64
  %0 = casair.addi %1, %2 : i64 
  vector.print %0 : i64
  return
 }

using ./bin/standalone-tool ../resources/dummy.mlir --emit=llvmir converted to LLVM Dialect :

module attributes {llvm.data_layout = ""} {
  llvm.func @printNewline()
  llvm.func @printI64(i64)
  llvm.func @main() {
    %0 = llvm.mlir.constant(5 : i64) : i64
    llvm.call @printI64(%0) : (i64) -> ()
    llvm.call @printNewline() : () -> ()
    llvm.return
  }
}

using /bin/standalone-opt --convert-arith-to-llvm --convert-func-to-llvm='use-opaque-pointers=1' --convert-vector-to-llvm='use-opaque-pointers=1' ../resources/dummyArit.mlir translated llvm dialect to LLVMIR :

; ModuleID = 'LLVMDialectModule'
source_filename = "LLVMDialectModule"
declare ptr @malloc(i64)
declare void @free(ptr)
declare void @printNewline()
declare void @printI64(i64)
define void @main() {
  call void @printI64(i64 5)
  call void @printNewline()
  ret void
}
!llvm.module.flags = !{!0}
!0 = !{i32 2, !"Debug Info Version", i32 3}

Running this llvmir using clang++ dummy.ll or lli dummy.ll gives the following error :

warning: overriding the module target triple with x86_64-unknown-linux-gnu [-Woverride-module]
1 warning generated.
/usr/bin/ld: /tmp/a-b09adb.o: in function `main':
LLVMDialectModule:(.text+0x7): undefined reference to `printI64'
/usr/bin/ld: LLVMDialectModule:(.text+0xc): undefined reference to `printNewline'
clang++: error: linker command failed with exit code 1 (use -v to see invocation)

You emitted IR that is calling a function printI64(int64_t), but you’re trying to link an executable without providing the implementation for this function.
You may find one possible implementation in the CRunnerUtils.cpp (you should find this yourself with git grep printI64 -- mlir/ from the top of the repo)

It worked. Thank you so much.

Thank you very much for the direction. We got the output. :smiley: