Problem
MLIR’s mem2reg only recognizes loads and stores whose memref operand is exactly the slot pointer. Any “transparent view” op sitting between the slot and its load/store — an op that takes a slot pointer and returns another pointer aliasing the same memory, possibly at a different element type — currently blocks promotion. This pattern is quite common: memref.assume_alignment after memref.alloca, llvm.bitcast between llvm.alloca and llvm.load, fir.declare tying a Fortran variable to its storage, fir.convert casting between FIR pointer types.
Proposed API
Two new methods on PromotableOpInterface:
// Describes this op as a transparent view of a memory slot.
::std::optional<::mlir::PromotableSlotView> getPromotableSlotView();
// Bridges a value between the underlying slot's elemType and the view's
// elemType (the framework calls this in both directions for type-changing
// chains; default impl handles the identity case).
::mlir::Value convertSlotValue(::mlir::Value value, ::mlir::Type targetType,
::mlir::OpBuilder &builder);
mem2reg uses these new getPromotableSlotView APIs to traverse the use-def chain, and convertSlotValue to convert the value around load/stores.
The mem2reg pass changes are minimal, the reachingDef value is still tracked with the root element type, and no new data structures are added to keep track of the slot views.
Motivation examples
memref.assume_alignment is currently blocking mem2reg in the following simple example:
%a = memref.alloca() : memref<i32>
%aa = memref.assume_alignment %a, 4 : memref<i32> // blocks promotion today
memref.store %v, %aa[] : memref<i32>
%r = memref.load %aa[] : memref<i32>
fir.declare is used to mark what source variable the memory access are made through (for both debug and aliasing purposes). It currently has some mem2reg support which involves walking back the fir.load/store operands, but this workaround is limits loads/stores from being in different blocks. The proposal will allow removing the workaround and limitation.
%a = fir.alloca i32
%d = fir.declare %a {uniq_name = "x"} : (!fir.ref<i32>) -> !fir.ref<i32>
fir.store %v to %d : !fir.ref<i32>
%r = fir.load %d : !fir.ref<i32>
fir.convert (typed pointer cast). The framework will chain convertSlotValue calls so loads/stores see values at the view’s element type while the reaching definition is tracked at the root. This use case is relevant for Fortran EQUIVALENCE (union):
%a = fir.alloca i32
%p = fir.convert %a : (!fir.ref<i32>) -> !fir.ref<f32>
fir.store %f to %p : !fir.ref<f32> // mem2reg will materializes f32→i32
%r = fir.load %p : !fir.ref<f32> // and i32→f32 around store/load
The implementation for this RFC is for review in PR196924.