Using C-compatible memref wrappers

Hi all!

We are figuring out how to interact between mlir code and C code when using memrefs, so we tried to implement the C-compatible wrapper emission. However, we get some unexpected results from the example below:

The example starts in the main loop in C. It creates a 1D memref of size 10, and calls an MLIR function @retrieve_dim. The mlir function extracts the size of the memref using memref.dim, and calls the C function @print_index, to print out the retrieved dimension.

I would expect to get a final result of 10, but instead the memref.dim result is a pointer to the array where the size is stored. Is this expected behaviour or is there something wrong with the implementation of the memref wrapper?

The output of the example code is this:

value of i: 268565428
value of pointer i: 10

This is the MLIR code:

module {
  func.func public @retrieve_dim(%arg0: memref<?xi32>) -> () {
    %c0 = arith.constant 0 : index
    %dim = memref.dim %arg0, %c0 : memref<?xi32>
    func.call @print_index(%dim) : (index) -> ()
    return
  }
  func.func private @print_index(index)
}

This is the C code:

#include "stdint.h"
#include <snrt.h>

struct OneDMemrefI32 {
  int32_t *data; // allocated pointer: Pointer to data buffer as allocated,
                 // only used for deallocating the memref
  int32_t *aligned_data; // aligned pointer: Pointer to properly aligned data
                         // that memref indexes
  uint32_t *offset;
  uint32_t *shape[1];
  uint32_t *stride[1];
};

typedef struct OneDMemrefI32 OneDMemrefI32_t;

// Kernel provided via external definition
void _mlir_ciface_retrieve_dim(OneDMemrefI32_t* a);

void _mlir_ciface_print_index(int32_t i) {
  printf("value of i: %i\n", i);
  printf("value of pointer i: %i\n", *((int32_t* )i));
}

int main() {
  
  int32_t N = 10;
  int32_t data[N];
  uint32_t constant_zero = 0;
  uint32_t constant_size[1] = { N };
  uint32_t constant_stride[1] = { sizeof(int32_t) };


  OneDMemrefI32_t memrefA = {
      .data = data,
      .aligned_data = data,
      .offset = &constant_zero,
      .shape = constant_size,
      .stride = constant_stride,
  };

  _mlir_ciface_retrieve_dim(&memrefA);
}
1 Like

Offsets, sizes and strides are stored as integer values, not pointers to values. I also assume you configured the lowering to use 32-bit integers for index and pointer size because the default width is 64 bit.

You can check out the default implementation of the memref structure, which is used in integration tests, here:

3 Likes

Ah, I see. Thanks for your answer!
Setting the values directly in the struct does indeed solve the problem.

I got confused by the intptr_t type in the docs. This is thus not a pointer type, but an integer type large enough to store pointers.

Thanks!

1 Like

intptr_t is a standard C++ typedef for an integer type capable of holding any address (Fixed width integer types (since C++11) - cppreference.com), much like size_t for sizes.

2 Likes

Please let us know if you run into any other issues and/or get to place where you could show & tell at an ODM.

3 Likes

Great, thank you! The ODM meeting would be very interesting! However, it is still very early MLIR days for us, so for now there is not much to show. I think it would be more useful to do this at a later stage in our project, in a couple of months.

Our team (MICAS - KU Leuven) is more hardware-focused, and we’re designing a heterogeneous accelerator cluster platform. We’ve now started to design a compiler for this platform using MLIR, to implement our high-level scheduling algorithms.

We will definetly get back to you if we have something interesting to showcase!

I know it has grown into a being a conference-style “show-and-tell” venue, but ODM has been intended to be an open design meeting. It is perfectly fine, and even encouraged, to come to ODM with problems and questions rather than solutions and answers. It can be a group design exercise with input from experts! So don’t hesitate to schedule a slot earlier!

2 Likes