ioana ghiban 2754e35f73
[mlir][EmitC] Support pointer-based memrefs in load/store lowering (#186828)
## Problem  
  
In the MemRef → EmitC conversion, `memref.load` and `memref.store`
assume that the converted memref operand is an `emitc.array`, as defined
by the type conversion in `populateMemRefToEmitCTypeConversion`.
  
However, `memref.alloc` is lowered to a `malloc` call returning
`emitc.ptr`. When such values are used by `memref.load` or
`memref.store`, the conversion framework inserts a bridging
`builtin.unrealized_conversion_cast` from `emitc.ptr` to `emitc.array`.
  
These casts have no EmitC representation and therefore remain in the IR
after conversion, preventing valid C/C++ emission.

## Solution  
  
Extend the `memref.load` and `memref.store` conversions to handle
pointer-backed buffers.
  
If the memref operand is defined by an `UnrealizedConversionCastOp`
whose input is an `emitc.ptr`, the cast is stripped and the underlying
pointer operand is used directly. Since pointer subscripting in EmitC is
one-dimensional, the multi-dimensional memref indices are converted to a
row-major linear index (matching the default memref layout) using the
original `MemRefType` shape before emitting `emitc.subscript`.
  
The existing array-based lowering path remains unchanged.  
  
This patch intentionally does ***not*** modify the MemRef → EmitC type
conversion rule (`memref → emitc.array`). Instead, the mismatch
introduced by `memref.alloc` returning a pointer is handled locally in
the `LoadOp` and `StoreOp` conversions.

## Example 1: Single-dimensional store  
  
### Input  
```mlir
func.func @alloc_store(%arg0: i32, %i: index) {
  %alloc = memref.alloc() : memref<999xi32>
  memref.store %arg0, %alloc[%i] : memref<999xi32>
  return
}
```
### Current lowering  
```mlir
// AllocOp conversion unchanged -> excluded for brevity
%5 = builtin.unrealized_conversion_cast %4 : !emitc.ptr<i32> to !emitc.array<999xi32>
%6 = subscript %5[%arg1]
assign %arg0 to %6 : <i32>
```
  
The `unrealized_conversion_cast` remains in the IR.  
### Lowering after this patch  
```mlir
%5 = subscript %4[%arg1] : (!emitc.ptr<i32>, !emitc.size_t) -> !emitc.lvalue<i32>
assign %arg0 : i32 to %5 : <i32>
```

The cast is eliminated and pointer subscripting is used directly.  
  
## Example 2: Multi-dimensional store  
  
### Input  
```mlir
func.func @memref_alloc_store(%v : f32, %i : index, %j : index) {
  %alloc = memref.alloc() : memref<4x8xf32>
  memref.store %v, %alloc[%i, %j] : memref<4x8xf32>
  return
}
```
### Current lowering  
```mlir
// AllocOp conversion unchanged -> excluded for brevity
%5 = builtin.unrealized_conversion_cast %4 : !emitc.ptr<f32> to !emitc.array<4x8xf32>
%6 = subscript %5[%arg1, %arg2] : (!emitc.array<4x8xf32>, !emitc.size_t, !emitc.size_t) -> !emitc.lvalue<f32>
assign %arg0 : f32 to %6 : <f32>
```
### Lowering after this patch  
```mlir
%5 = "emitc.constant"() <{value = 8 : index}> : () -> !emitc.size_t
%6 = mul %arg1, %5 : (!emitc.size_t, !emitc.size_t) -> !emitc.size_t
%7 = add %6, %arg2 : (!emitc.size_t, !emitc.size_t) -> !emitc.size_t
%8 = subscript %4[%7] : (!emitc.ptr<f32>, !emitc.size_t) -> !emitc.lvalue<f32>
assign %arg0 : f32 to %8 : <f32>
```

The multi-dimensional indices are converted into a linear row-major
index before pointer subscripting.


Assisted-by: ChatGPT (refine implementation + tests). I reviewed all
code and tests before submission.
2026-03-19 15:31:00 +00:00
..