How to pass a pointer from my c++ code to mlir?

Hi, I have a question about pointer in mlir: How to I pass a pointer from my c++ code to mlir ?

Suppose I have a class A in my c++code, and a function that take void* ptr as parameter which is actually an A*. I want to call the function from mlir.

I have defined a PtrType in my dialect to wrap the pointer of A, and want to call the function from llvm.

class A {
    int64_t a;
};

extern "C" {
    int64_t my_func(void * p);
}

struct PtrTypeStorage: public TypeStorage {
  void* ptr = nullptr;
   ....
};

class PtrType: public mlir::Type::TypeBase<PtrType, mlir::Type, detail::PtrTypeStorage> {
 public:
   using Base::Base;

   static PtrType get(mlir::MLIRContext* ctx);
   size_t getNumElementTypes() { return 1; }
   void setPtr(void* ptr);

   static llvm::StringRef name;
};

And I have an op in dialect which just return a PtrType:

def GetPtrOp : Toy_Op<"get_ptr"> {
  let summary = "get_ptr";

  let arguments = (ins);
  let results = (outs Toy_PtrType:$output);

  let assemblyFormat = "`(` `)` attr-dict `to` type($output)";

  let builders = [
    OpBuilder<(ins)>
  ];
}

I tried the following in lowering to llvm, but it does’t work

struct GetPtrOpLowering : public OpConversionPattern<mlir::toy::GetPtrOp> {
  using OpConversionPattern<mlir::toy::GetPtrOp>::OpConversionPattern;

  LogicalResult matchAndRewrite(mlir::toy::GetPtrOp op,
                                OpAdaptor adaptor,
                                ConversionPatternRewriter& rewriter) const override {
    ModuleOp parentModule = op.getOperation()->getParentOfType<ModuleOp>();
    auto* context = parentModule.getContext();

    auto loc = op.getOperation()->getLoc();

    auto ptrType = LLVM::LLVMPointerType::get(parentModule.getContext());
    Value one = rewriter.create<LLVM::ConstantOp>(loc, rewriter.getI64Type(), rewriter.getIndexAttr(1));
    Value allocated = rewriter.create<LLVM::AllocaOp>(loc, ptrType, PtrType::get(context), one);
    mlir::Value res = rewriter.create<LLVM::LoadOp>(loc, PtrType::get(context), allocated);

    rewriter.eraseOp(op);

    return mlir::success();
  }
};

When I run the code var a = get_ptr(), the error says:

loc("codegen.toy":15:15): error: 'llvm.load' op result #0 must be LLVM type with size, but got '!toy.PtrType'

So I have the following questions:

  1. How do I convert the PtrType in my dialect to LLVM::LLVMPointerType ?
  2. According the document of Type, every Type is just a wrapper of an actual TypeStorage. So What is the actual storage LLVM::LLVMPointerType ? Can I just get the actual storage and just set the pointer value ? I grep all code under llvm-project, and found that LLVM::LLVMPointerType::get(context) seems to be the only way to construct a LLVM::LLVMPointerType.
  3. If I use MemrefType, still I need to provide a type, which also need to be converted to llvm type at last, back to the first question: how to convert void* pointer to LLVM::LLVMPointerType?
  4. My goal is to call the function my_func with some object in my c++ code, Is my idea correct ? Are there any other idea to achieve this?

This constructs invalid IR. Operations from the LLVM dialect accept only process values of LLVM dialect types or compatible builtin types. This is intentional. You cannot make LLVM dialect process your custom types.

This is the consequence of the above.

This depends on the semantics of your type. If they are identical to the LLVM pointer type, you have to replace all operations that operate on your type with corresponding operations that operate on the LLVM pointer type. You should also update block signatures. Check out the documentation: Dialect Conversion - MLIR

These seem to stem from a fundamental misunderstanding. The internal storage of types in a compiler has nothing to do with what that type can store when the compiled program runs. It is impossible to “set a value” of something in your program by modifying the code of the compiler and vice versa (disregarding exotic JIT scenarios).

That being said, you can define a function in the IR that takes an argument of !llvm.ptr type. That function will be callable with any C pointer under all ABIs I’m aware of.

Hi, thanks for the reply. It seems that I confused with the compiler code and the running program. The pointer I need is just a void * pointer, I will handle it in my c++ function later. So LLVM pointer should be enough. So I think I should define a function that takes an argument of !llvm.ptr type.

The Toy dialect has a GenericCallOp, whose arguments are Variadic<Toy_Type>. Can I add LLVM_AnyPointer to Toy_Type to support ptr type ? Or are there any other things need to do ? The type used directly in Toy, such as F64Tensor, seems to be slightly different from LLVM_AnyPointer. LLVM_AnyPointer is defined in another dialect llvm, while F64Tensor is defined in `CommonTypeConstraints.td’.

I tried to add LLVM_AnyPointer directly in the td file (actually the toy/Ops.td file
in toy tutorial Ch7), but encountered an error.

include "mlir/Dialect/LLVMIR/LLVMOpBase.td"

def Toy_Type : AnyTypeOf<[F64Tensor, I64, Toy_StructType, LLVM_AnyPointer]>;

The erros says

when more than 1 dialect is present, one must be selected via '-dialect'

Seems that use more than 1 dialects need to set some flags. I tried pass -dialect toy to ninja, but it didnt work. How can I fix this error? Thanks for any suggestions.

This will let the operation accept the type. But I suspect the lowering of that operation to the LLVM dialect may still need to be updated.

This is a flag for mlir-tblgen that gets called by this cmake rule: llvm-project/mlir/examples/toy/Ch7/include/toy/CMakeLists.txt at main · llvm/llvm-project · GitHub if I’m not mistaken.

Hi, thanks for the reply. I finally succeed to build after add -dialect=toy in the CmakeLists.txt:

mlir_tablegen(Ops.h.inc -gen-op-decls -dialect=toy)
mlir_tablegen(Ops.cpp.inc -gen-op-defs -dialect=toy)

I’ll try to pass a pointer from my c++ code to the function defined in IR.