blob: 0a5cb672333ede67bf89e54d591aee7cbd49ae63 [file] [edit]
// 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 logical element type of the boxed view, e.g.
// z%re maps complex<f32> -> f32 in the box type.
//
// FIRToMemRef only lowers array_coor through this path when storage is
// complex<T> and the slice path is constant 0 (%re) or 1 (%im). It then
// fir.convert's storage to memref<...x2xT>, appends that component index,
// and memref.reinterpret_cast's a strided memref<...xT> using fir.box_dims
// byte strides divided by sizeof(T) (not sizeof(complex<T>) from box_elesize).
// Derived-type component projections (e.g. a%x) are left for FIR codegen.
//
// Derived from:
// complex, target :: z(4) = 0.
// real, pointer :: r(:)
// r => z%re
// r = r + z(4:1:-1)%re
// ----------------------------------------------------------------------------
// Forward projected slice load: z(1:4:1)%re
// The fir.convert appears inside the loop body (insertion point tracks the
// array_coor inside the loop). elemIdx = (i - 1) * step + (lb - 1) = i - 1
// for step=1, lb=1. Indices are reversed (col-major → row-major) but for 1D
// that is a no-op.
// ----------------------------------------------------------------------------
// CHECK-LABEL: func.func @projected_slice_fwd
// CHECK: fir.do_loop [[I:%.*]] =
// CHECK: [[MEMREF:%.*]] = fir.convert %arg0 : (!fir.ref<!fir.array<4xcomplex<f32>>>) -> memref<4xcomplex<f32>>
// CHECK: [[IDX:%.*]] = arith.addi
// CHECK: [[COMP:%[0-9]+]] = fir.convert [[MEMREF]] : (memref<4xcomplex<f32>>) -> memref<4x2xf32>
// Reinterpret applies the shape-derived layout onto the scalar view:
// sizes = [shape extent, 2 (re/im pair)]
// strides = [2 (one complex == two scalar slots), 1]
// CHECK-NOT: fir.box_dims
// CHECK: [[VIEW:%.*]] = memref.reinterpret_cast [[COMP]] to offset: [%c0{{.*}}], sizes: [%c4{{.*}}, %c2{{.*}}], strides: [%c2{{.*}}, %c1{{.*}}] : memref<4x2xf32> to memref<?x?xf32, strided<
// CHECK: [[LOAD:%[0-9]+]] = memref.load [[VIEW]][[[IDX]], %c0{{.*}}] : memref<?x?xf32, strided<
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
}
// ----------------------------------------------------------------------------
// Backward projected slice load: z(4:1:-1)%re
// step = -1, lb = 4 → elemIdx = (i - 1) * (-1) + (4 - 1) = 3 - (i-1)
// ----------------------------------------------------------------------------
// CHECK-LABEL: func.func @projected_slice_bwd
// CHECK: fir.do_loop [[I:%.*]] =
// CHECK: [[MEMREF:%.*]] = fir.convert %arg0 : (!fir.ref<!fir.array<4xcomplex<f32>>>) -> memref<4xcomplex<f32>>
// CHECK: [[IDX:%.*]] = arith.addi
// CHECK: [[COMP:%[0-9]+]] = fir.convert [[MEMREF]] : (memref<4xcomplex<f32>>) -> memref<4x2xf32>
// Same reinterpret as forward; slice triple only changes [[IDX]], not strides.
// CHECK-NOT: fir.box_dims
// CHECK: [[VIEW:%.*]] = memref.reinterpret_cast [[COMP]] to offset: [%c0{{.*}}], sizes: [%c4{{.*}}, %c2{{.*}}], strides: [%c2{{.*}}, %c1{{.*}}] : memref<4x2xf32> to memref<?x?xf32, strided<
// CHECK: [[LOAD:%[0-9]+]] = memref.load [[VIEW]][[[IDX]], %c0{{.*}}] : memref<?x?xf32, strided<
func.func @projected_slice_bwd(%arg0: !fir.ref<!fir.array<4xcomplex<f32>>>) {
%c1 = arith.constant 1 : index
%c4 = arith.constant 4 : index
%cm1 = arith.constant -1 : index
%c0 = arith.constant 0 : index
%shape = fir.shape %c4 : (index) -> !fir.shape<1>
%slice = fir.slice %c4, %c1, %cm1 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
}
// ----------------------------------------------------------------------------
// Imaginary component store: z(1:4:1)%im = val
// Direct scalar store — no read-modify-write, no complex.create.
// ----------------------------------------------------------------------------
// CHECK-LABEL: func.func @projected_slice_store_im
// CHECK: fir.do_loop [[I:%.*]] =
// CHECK: [[MEMREF:%.*]] = fir.convert %arg0 : (!fir.ref<!fir.array<4xcomplex<f32>>>) -> memref<4xcomplex<f32>>
// CHECK: [[IDX:%.*]] = arith.addi
// CHECK: [[COMP:%[0-9]+]] = fir.convert [[MEMREF]] : (memref<4xcomplex<f32>>) -> memref<4x2xf32>
// Same layout as %re; store uses component index 1 for imaginary.
// CHECK-NOT: fir.box_dims
// CHECK: [[VIEW:%.*]] = memref.reinterpret_cast [[COMP]] to offset: [%c0{{.*}}], sizes: [%c4{{.*}}, %c2{{.*}}], strides: [%c2{{.*}}, %c1{{.*}}] : memref<4x2xf32> to memref<?x?xf32, strided<
// CHECK: memref.store %arg1, [[VIEW]][[[IDX]], %c1{{.*}}] : memref<?x?xf32, strided<
func.func @projected_slice_store_im(%arg0: !fir.ref<!fir.array<4xcomplex<f32>>>,
%arg1: f32) {
%c1 = arith.constant 1 : index
%c4 = arith.constant 4 : index
%c1_im = arith.constant 1 : index // imaginary component index
%shape = fir.shape %c4 : (index) -> !fir.shape<1>
%slice = fir.slice %c1, %c4, %c1 path %c1_im : (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>
fir.store %arg1 to %coor : !fir.ref<f32>
}
return
}
// ----------------------------------------------------------------------------
// 2-D boxed projected slice load: z(1:2:1, 1:3:1)%re
// Storage: !fir.array<2x3xcomplex<f32>>
//
// convertMemrefType reverses Fortran column-major extents to MLIR row-major:
// !fir.ref<!fir.array<2x3xcomplex<f32>>> → memref<3x2xcomplex<f32>>
// Reinterpret adds the component dimension:
// memref<3x2xcomplex<f32>> → memref<3x2x2xf32>
//
// Per-dimension element index (0-based, column-major):
// elemIdx_i = (i-1)*1 + (1-1) = i-1 (Fortran dim 1, size 2)
// elemIdx_j = (j-1)*1 + (1-1) = j-1 (Fortran dim 2, size 3)
//
// After reversing for MLIR row-major access:
// memref.load [elemIdx_j, elemIdx_i, 0]
// ----------------------------------------------------------------------------
// CHECK-LABEL: func.func @projected_slice_2d
// CHECK: fir.do_loop [[I:%.*]] =
// CHECK: fir.do_loop [[J:%.*]] =
// CHECK: [[MEMREF:%.*]] = fir.convert %arg0 : (!fir.ref<!fir.array<2x3xcomplex<f32>>>) -> memref<3x2xcomplex<f32>>
// CHECK: [[IDX_I:%.*]] = arith.addi
// CHECK: [[IDX_J:%.*]] = arith.addi
// CHECK: [[COMP:%[0-9]+]] = fir.convert [[MEMREF]] : (memref<3x2xcomplex<f32>>) -> memref<3x2x2xf32>
// 2-D shapeVec path: outer stride = inner_extent * 2 (pair slots), inner
// stride = 2, pair stride = 1. Row-major memref indices are [j, i, 0] after
// Fortran dim reversal.
// CHECK-NOT: fir.box_dims
// CHECK: [[STR0:%.*]] = arith.muli %c2{{.*}}, %c2{{.*}} : index
// CHECK: [[VIEW:%.*]] = memref.reinterpret_cast [[COMP]] to offset: [%c0{{.*}}], sizes: [%c3{{.*}}, %c2{{.*}}, %c2{{.*}}], strides: [[[STR0]], %c2{{.*}}, %c1{{.*}}] : memref<3x2x2xf32> to memref<?x?x?xf32, strided<
// CHECK: [[LOAD:%[0-9]+]] = memref.load [[VIEW]][[[IDX_J]], [[IDX_I]], %c0{{.*}}] : memref<?x?x?xf32, strided<
func.func @projected_slice_2d(%arg0: !fir.ref<!fir.array<2x3xcomplex<f32>>>) {
%c1 = arith.constant 1 : index
%c2 = arith.constant 2 : index
%c3 = arith.constant 3 : index
%c0 = arith.constant 0 : index
%shape = fir.shape %c2, %c3 : (index, index) -> !fir.shape<2>
%slice = fir.slice %c1, %c2, %c1, %c1, %c3, %c1 path %c0 : (index, index, index, index, index, index, index) -> !fir.slice<2>
%embox = fir.embox %arg0(%shape) [%slice] : (!fir.ref<!fir.array<2x3xcomplex<f32>>>, !fir.shape<2>, !fir.slice<2>) -> !fir.box<!fir.array<2x3xf32>>
fir.do_loop %i = %c1 to %c2 step %c1 unordered {
fir.do_loop %j = %c1 to %c3 step %c1 unordered {
%coor = fir.array_coor %embox %i, %j : (!fir.box<!fir.array<2x3xf32>>, index, index) -> !fir.ref<f32>
%val = fir.load %coor : !fir.ref<f32>
}
}
return
}
// ----------------------------------------------------------------------------
// Projected slice on a complex descriptor (no embox): array_coor %box[%slice]
// path %c0 for %re. Strides must be scaled by sizeof(f32), not
// sizeof(complex<f32>) from fir.box_elesize on the complex box.
// ----------------------------------------------------------------------------
// CHECK-LABEL: func.func @projected_slice_complex_box
// CHECK: fir.do_loop
// CHECK: fir.do_loop
// CHECK: [[CB_BOX:%[0-9]+]] = fir.box_addr %arg0 : (!fir.box<!fir.array<?x?xcomplex<f32>>>) -> !fir.ref<!fir.array<?x?xcomplex<f32>>>
// CHECK: [[CB_REF:%[0-9]+]] = fir.convert [[CB_BOX]] : (!fir.ref<!fir.array<?x?xcomplex<f32>>>) -> memref<?x?xcomplex<f32>>
// CHECK: [[CB_I:%[0-9]+]] = arith.addi
// CHECK: [[CB_J:%[0-9]+]] = arith.addi
// CHECK: [[CB_COMP:%[0-9]+]] = fir.convert [[CB_REF]] : (memref<?x?xcomplex<f32>>) -> memref<?x?x2xf32>
// CHECK: %[[CB_C_RE:.*]] = arith.constant 0 : index
// CHECK: %[[CB_C_SZF32:.*]] = arith.constant 4 : index
// CHECK: %[[CB_C_DIM1:.*]] = arith.constant 1 : index
// CHECK: [[CB_BD0:%[0-9]+]]:3 = fir.box_dims %arg0, %[[CB_C_DIM1]] : (!fir.box<!fir.array<?x?xcomplex<f32>>>, index) -> (index, index, index)
// CHECK: [[CB_STR0:%[0-9]+]] = arith.divsi [[CB_BD0]]#2, %[[CB_C_SZF32]] : index
// CHECK: %[[CB_C_DIM0:.*]] = arith.constant 0 : index
// CHECK: [[CB_BD1:%[0-9]+]]:3 = fir.box_dims %arg0, %[[CB_C_DIM0]] : (!fir.box<!fir.array<?x?xcomplex<f32>>>, index) -> (index, index, index)
// CHECK: [[CB_STR1:%[0-9]+]] = arith.divsi [[CB_BD1]]#2, %[[CB_C_SZF32]] : index
// CHECK-NOT: fir.box_elesize %arg0
// Complex box (not embox): strides from fir.box_dims on %arg0; divisor sizeof(f32).
// CHECK: %[[CB_C_PAIR:.*]] = arith.constant 2 : index
// CHECK: %[[CB_C_COMP_STRIDE:.*]] = arith.constant 1 : index
// CHECK: %[[CB_C_OFF:.*]] = arith.constant 0 : index
// CHECK: [[CB_VIEW:%.*]] = memref.reinterpret_cast [[CB_COMP]] to offset: [%[[CB_C_OFF]]], sizes: [[[CB_BD0]]#1, [[CB_BD1]]#1, %[[CB_C_PAIR]]], strides: [[[CB_STR0]], [[CB_STR1]], %[[CB_C_COMP_STRIDE]]] : memref<?x?x2xf32> to memref<?x?x?xf32, strided<
// Row-major memref indices are [j, i, 0] (see projected_slice_2d).
// CHECK: [[CB_LOAD:%[0-9]+]] = memref.load [[CB_VIEW]][[[CB_J]], [[CB_I]], %[[CB_C_RE]]] : memref<?x?x?xf32, strided<
func.func @projected_slice_complex_box(%arg0: !fir.box<!fir.array<?x?xcomplex<f32>>>) {
%c0 = arith.constant 0 : index
%c1 = arith.constant 1 : index
%dim0:3 = fir.box_dims %arg0, %c0 : (!fir.box<!fir.array<?x?xcomplex<f32>>>, index) -> (index, index, index)
%dim1:3 = fir.box_dims %arg0, %c1 : (!fir.box<!fir.array<?x?xcomplex<f32>>>, index) -> (index, index, index)
%shape = fir.shape %dim0#1, %dim1#1 : (index, index) -> !fir.shape<2>
%slice = fir.slice %c1, %dim0#1, %c1, %c1, %dim1#1, %c1 path %c0 : (index, index, index, index, index, index, index) -> !fir.slice<2>
fir.do_loop %i = %c1 to %dim0#1 step %c1 unordered {
fir.do_loop %j = %c1 to %dim1#1 step %c1 unordered {
%coor = fir.array_coor %arg0 [%slice] %i, %j : (!fir.box<!fir.array<?x?xcomplex<f32>>>, !fir.slice<2>, index, 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 handle this; downstream
// FIR-to-LLVM lowering handles it 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
}
// ----------------------------------------------------------------------------
// ref + fir.shape + projected %re/%im (0093_0105): shapeVec path, no box_dims.
// ----------------------------------------------------------------------------
// CHECK-LABEL: func.func @projected_slice_ref_shapevec_stride_store
// CHECK: fir.do_loop
// CHECK: [[COMP:%[0-9]+]] = fir.convert %{{.*}} : (memref<5xcomplex<f32>>) -> memref<5x2xf32>
// CHECK-NOT: fir.box_dims %arg0
// CHECK: memref.reinterpret_cast [[COMP]]{{.*}}strides: [%c2{{.*}}, %c1{{.*}}] : memref<5x2xf32> to memref<?x?xf32, strided<
// CHECK-NEXT: memref.store
func.func @projected_slice_ref_shapevec_stride_store(
%arg0: !fir.ref<!fir.array<5xcomplex<f32>>>, %arg1: f32) {
%c1 = arith.constant 1 : index
%c5 = arith.constant 5 : index
%c0 = arith.constant 0 : index
%shape = fir.shape %c5 : (index) -> !fir.shape<1>
%slice = fir.slice %c1, %c5, %c1 path %c0 : (index, index, index, index) -> !fir.slice<1>
fir.do_loop %i = %c1 to %c5 step %c1 unordered {
%coor = fir.array_coor %arg0 (%shape) [%slice] %i
: (!fir.ref<!fir.array<5xcomplex<f32>>>, !fir.shape<1>, !fir.slice<1>, index) -> !fir.ref<f32>
fir.store %arg1 to %coor : !fir.ref<f32>
}
return
}
// CHECK-LABEL: func.func @projected_slice_ref_shapevec_stride_load_im
// CHECK: fir.do_loop
// CHECK: [[COMP:%[0-9]+]] = fir.convert %{{.*}} : (memref<4xcomplex<f64>>) -> memref<4x2xf64>
// CHECK-NOT: fir.box_dims
// CHECK: memref.reinterpret_cast [[COMP]]{{.*}}strides: [%c2{{.*}}, %c1{{.*}}] : memref<4x2xf64> to memref<?x?xf64, strided<
// CHECK-NEXT: memref.load
func.func @projected_slice_ref_shapevec_stride_load_im(
%arg0: !fir.ref<!fir.array<4xcomplex<f64>>>) {
%c1 = arith.constant 1 : index
%c4 = arith.constant 4 : index
%c1_im = arith.constant 1 : index
%shape = fir.shape %c4 : (index) -> !fir.shape<1>
%slice = fir.slice %c1, %c4, %c1 path %c1_im : (index, index, index, index) -> !fir.slice<1>
fir.do_loop %i = %c1 to %c4 step %c1 unordered {
%coor = fir.array_coor %arg0 (%shape) [%slice] %i
: (!fir.ref<!fir.array<4xcomplex<f64>>>, !fir.shape<1>, !fir.slice<1>, index) -> !fir.ref<f64>
%val = fir.load %coor : !fir.ref<f64>
}
return
}