Lowering memref<?xi8> to LLVM when a bare pointer conversion flag is set

Hello all,

I am writing a function for debugging purpose in MLIR that takes a message string as argument and prints it.

In C, its signature is something like this:

void print(const char *c);

What I’d like to do is to insert calls to this function in MLIR. I created its declaration in MLIR as follows:

func @print(%c: memref<?xi8>)

However, -convert-func-to-llvm does not lower it into a function with i8* pointer when a bare pointer conversion flag is set.

I found that it is LLVMTypeConverter::convertMemRefToBarePtr (llvm-project/TypeConverter.cpp at main · llvm/llvm-project · GitHub) that gives up when a dynamically shaped memref is given.
Is there a reason why it is unsupported?

Thanks!

Indeed, the bare pointer calling convention only allows statically known shapes: LLVM IR Target - MLIR
In this convention, you cannot pass size, stride and offset info to the lowered function because the memref is lowered to a single “bare” pointer, and nothing else. The size/stride/offset information in the memref must be known statically, so that it can be lowered to llvm.mlir.constant instances in the function body, both in the caller and callee. So, as far as I understand, this is a fundamental limitation of the bare pointer calling convention.

Have you considered using the emit-c-wrappers flag? Also, if this is only for debugging, maybe you can use the execution engine, and you can find some helpers in and around this file that can give you some inspiration: llvm-project/CRunnerUtils.h at main · llvm/llvm-project · GitHub

1 Like

Thank you for the link!
I found that the link is talking about unranked memrefs, but not dynamically sized memrefs - they should be different, am I understanding right?
For memref<?xi32> type, its rank is fixed to 1 (memref with rank 2 must be memref<?x?xi32>), and the stride still must be 1 because it does not have a custom layout. Hence, I think it is still fine to lower it to a bare pointer.

I haven’t tried it because the generated IR has vector operations for performance and not sure emit-c-wrapper can successfully translate it.
The C runner indeed looks interesting to me! :slight_smile:

The link points to a documentation subsection “Bare Pointer Calling Convention for Ranked MemRef”. It clearly states that the bare pointer calling convention supports only “memref types with all dimensions statically known.”

No, it is not. We must have a possibility to query the size of this dimension, i.e. be able to implement memref.dim. It is impossible if the size is not stored anywhere nor known statically. (And before somebody asks, I am certainly aware of the possibility to have null-terminated arrays and I am strongly against it in memref.)

1 Like

I don’t think convert-func-to-llvm cares if you have vector operations in the function body IR. It only cares about arguments and results of the function. In fact the following works fine when lowered with
mlir-opt discourse.mlir --convert-vector-to-llvm -convert-memref-to-llvm -convert-func-to-llvm=emit-c-wrappers -reconcile-unrealized-casts:

func @vec(%v : vector<3xi8>, %m : memref<?xi8>) -> memref<?xi8> {
  %0 = vector.extract %v[0]: vector<3xi8>
  %c0 = arith.constant 0 : index
  memref.store %0, %m[%c0] : memref<?xi8>
  return %m : memref<?xi8>
}
1 Like

Note that 2-D vectors have a different ABI and would connect differently to external C

1 Like

Thank all, I certiainly misunderstood the initial reply. My concerns are clearly addressed.
It’s bad that I can only choose one answer as a solution. :slight_smile: