blob: 7b0fbdf74817342a7d785059c561b11d4ba4d872 [file]
// RUN: fir-opt %s --fir-to-memref --allow-unregistered-dialect | FileCheck %s
// Tests for fir.slice with a path component (projected component slice).
// A projected slice changes the element type of the boxed view, e.g.
// z%re projects complex<f32> -> f32. The layout (strides / base address)
// must come from the projected box descriptor, NOT from reconstructing the
// triplets, because memref.reinterpret_cast requires the same element type
// on both sides and the triplet strides are in storage-element units
// (complex<f32>) while the MemRef strides must be in projected-element units
// (f32).
//
// Derived from:
// complex, target :: z(4) = 0.
// real, pointer :: r(:)
// r => z%re
// r = r + z(4:1:-1)%re
// ----------------------------------------------------------------------------
// Forward projected slice: z(1:4:1)%re
// The slice path %c0 projects complex<f32> -> f32 (real part).
// Expected lowering:
// - fir.box_addr on the projected box (!fir.box<!fir.array<4xf32>>)
// - fir.convert to memref<4xf32> (NOT to memref<4xcomplex<f32>>)
// - index = i - 1 (1-based, no triplet arithmetic)
// - strides from fir.box_dims / fir.box_elesize on the projected box
// ----------------------------------------------------------------------------
// CHECK-LABEL: func.func @projected_slice_fwd
// CHECK: [[C1:%.*]] = arith.constant 1 : index
// CHECK: [[C4:%.*]] = arith.constant 4 : index
// CHECK: [[C0:%.*]] = arith.constant 0 : index
// CHECK: [[SHAPE:%.*]] = fir.shape [[C4]] : (index) -> !fir.shape<1>
// CHECK: [[SLICE:%.*]] = fir.slice [[C1]], [[C4]], [[C1]] path [[C0]] : (index, index, index, index) -> !fir.slice<1>
// CHECK: [[EMBOX:%.*]] = fir.embox %arg0([[SHAPE]]) {{\[}}[[SLICE]]{{\]}} : (!fir.ref<!fir.array<4xcomplex<f32>>>, !fir.shape<1>, !fir.slice<1>) -> !fir.box<!fir.array<4xf32>>
// CHECK: fir.do_loop [[I:%.*]] = [[C1]] to [[C4]] step [[C1]] unordered {
// Projected box_addr gives f32 pointer, not complex<f32>.
// CHECK: [[BOXADDR:%.*]] = fir.box_addr [[EMBOX]] : (!fir.box<!fir.array<4xf32>>) -> !fir.ref<!fir.array<4xf32>>
// CHECK: [[CONVERT:%.*]] = fir.convert [[BOXADDR]] : (!fir.ref<!fir.array<4xf32>>) -> memref<4xf32>
// Index: i-1 (1-based). The lowering emits: delta=i-1, scaled=delta*1,
// offset=1-1=0, finalIdx=scaled+offset. The addi result is what feeds the load.
// CHECK: [[C1_0:%.*]] = arith.constant 1 : index
// CHECK: [[DELTA:%.*]] = arith.subi [[I]], [[C1_0]] : index
// CHECK: [[SCALED:%.*]] = arith.muli [[DELTA]], [[C1_0]] : index
// CHECK: [[OFFSET:%.*]] = arith.subi [[C1_0]], [[C1_0]] : index
// CHECK: [[IDX:%.*]] = arith.addi [[SCALED]], [[OFFSET]] : index
// Layout: extent and stride come from the projected box descriptor.
// CHECK: [[ELE:%.*]] = fir.box_elesize [[EMBOX]] : (!fir.box<!fir.array<4xf32>>) -> index
// CHECK: [[C0_0:%.*]] = arith.constant 0 : index
// CHECK: [[DIMS:%.*]]:3 = fir.box_dims [[EMBOX]], [[C0_0]] : (!fir.box<!fir.array<4xf32>>, index) -> (index, index, index)
// CHECK: [[STRIDE:%.*]] = arith.divsi [[DIMS]]#2, [[ELE]] : index
// CHECK: [[C0_1:%.*]] = arith.constant 0 : index
// CHECK: [[VIEW:%.*]] = memref.reinterpret_cast [[CONVERT]] to offset: {{\[}}[[C0_1]]{{\]}}, sizes: {{\[}}[[DIMS]]#1{{\]}}, strides: {{\[}}[[STRIDE]]{{\]}} : memref<4xf32> to memref<?xf32, strided<[?], offset: ?>>
// CHECK: memref.load [[VIEW]]{{\[}}[[IDX]]{{\]}} : memref<?xf32, strided<[?], offset: ?>>
func.func @projected_slice_fwd(%arg0: !fir.ref<!fir.array<4xcomplex<f32>>>) {
%c1 = arith.constant 1 : index
%c4 = arith.constant 4 : index
%c0 = arith.constant 0 : index
%shape = fir.shape %c4 : (index) -> !fir.shape<1>
%slice = fir.slice %c1, %c4, %c1 path %c0 : (index, index, index, index) -> !fir.slice<1>
%embox = fir.embox %arg0(%shape) [%slice] : (!fir.ref<!fir.array<4xcomplex<f32>>>, !fir.shape<1>, !fir.slice<1>) -> !fir.box<!fir.array<4xf32>>
fir.do_loop %i = %c1 to %c4 step %c1 unordered {
%coor = fir.array_coor %embox %i : (!fir.box<!fir.array<4xf32>>, index) -> !fir.ref<f32>
%val = fir.load %coor : !fir.ref<f32>
}
return
}
// ----------------------------------------------------------------------------
// Derived-type component projection: a%x where a : TYPE{x:f64, y:complex<f64>}
//
// This is NOT a complex projection — the storage element is the derived type T,
// not complex<T>. FIRToMemRef cannot safely compute element-unit strides via
// divsi(byte_stride, elesize) because sizeof(T)/sizeof(component) may not be an
// integer (e.g. sizeof(T)=24, sizeof(complex<f64>)=16 -> 1.5, truncated to 1).
//
// The pass must leave fir.array_coor and fir.store/fir.load unconverted;
// downstream FIR-to-LLVM lowering handles them correctly via the descriptor.
//
// CHECK-LABEL: func.func @derived_component_not_projected
// The fir.array_coor must survive (not be erased).
// CHECK: fir.array_coor
// The store must remain as fir.store, not memref.store.
// CHECK: fir.store
// CHECK-NOT: memref.store
// ----------------------------------------------------------------------------
func.func @derived_component_not_projected(
%arg0: !fir.ref<!fir.array<4x!fir.type<T{x:f64,y:complex<f64>}>>>) {
%c1 = arith.constant 1 : index
%c4 = arith.constant 4 : index
%cst = arith.constant 9.9e+01 : f64
%field = fir.field_index x, !fir.type<T{x:f64,y:complex<f64>}>
%shape = fir.shape %c4 : (index) -> !fir.shape<1>
%slice = fir.slice %c1, %c4, %c1 path %field : (index, index, index, !fir.field) -> !fir.slice<1>
%embox = fir.embox %arg0(%shape) [%slice] : (!fir.ref<!fir.array<4x!fir.type<T{x:f64,y:complex<f64>}>>>, !fir.shape<1>, !fir.slice<1>) -> !fir.box<!fir.array<4xf64>>
fir.do_loop %i = %c1 to %c4 step %c1 unordered {
%coor = fir.array_coor %embox %i : (!fir.box<!fir.array<4xf64>>, index) -> !fir.ref<f64>
fir.store %cst to %coor : !fir.ref<f64>
}
return
}