Need help on code generation semantics for memref

Hello,

Following previous exchanges, I try to better understand the semantics of memref, and in particular the way code generation is done from it.

I have written a very small mlir example that accesses a fixed-size, single-dimension memref:

func @access5(%in : memref<100xi32>) -> i32 {
  %c = constant 5 : index 
  %res = load %in[%c] : memref<100xi32>
  return %res : i32
}

From it, mlir-opt --convert-std-to-llvm will produce the following function, which is unexpectedly complex:

module attributes {llvm.data_layout = “”} {
llvm.func @access5(%arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.i64, %arg3: !llvm.i64, %arg4: !llvm.i64) → !llvm.i32 {
%0 = llvm.mlir.undef : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
%1 = llvm.insertvalue %arg0, %0[0] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
%2 = llvm.insertvalue %arg1, %1[1] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
%3 = llvm.insertvalue %arg2, %2[2] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
%4 = llvm.insertvalue %arg3, %3[3, 0] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
%5 = llvm.insertvalue %arg4, %4[4, 0] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
%6 = llvm.mlir.constant(5 : index) : !llvm.i64
%7 = llvm.extractvalue %5[1] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
%8 = llvm.mlir.constant(0 : index) : !llvm.i64
%9 = llvm.mlir.constant(1 : index) : !llvm.i64
%10 = llvm.mul %6, %9 : !llvm.i64
%11 = llvm.add %8, %10 : !llvm.i64
%12 = llvm.getelementptr %7[%11] : (!llvm.ptr, !llvm.i64) → !llvm.ptr
%13 = llvm.load %12 : !llvm.ptr
llvm.return %13 : !llvm.i32
}
}

The generated function has 5 arguments, of which 2 are pointers. Also, there are lots of operations, where what I expected was a simple table access. Why? And where can I find the documentation on this? I have the impression that the code assumes the access is done through an affine map, but there is also code that I don’t understand at all, such as the use of undef, and that of the inservalue operations…

Best,
Dumitru

This is explained in the conversion to LLVM dialect documentation - https://mlir.llvm.org/docs/ConversionToLLVMDialect/#memref-types.

For your other questions, there are no assumptions about affine accesses, but the conversion only supports the “strided” form of the memref. I am not aware of other forms being used in-tree. Undef/insertvalue is the common way to populate a struct in LLVM IR. Memref is way more complex than a simple table that would produce a simple table access.

1 Like

Thanks! This explains my previous misunderstandings concerning memref. :slight_smile:

That documentation is quite old so please do let me know if you spot any issues or inconsistencies with what actually happens. Also, documentation patches are most certainly welcome!