[Flang][FIR] `fir.array_coor` on non-pointer `intent(in)` assumed-shape arguments uses dynamic box stride instead of compile-time element size
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
For a non-pointer, non-allocatable `intent(in)` assumed-shape dummy argument, flang generates `fir.array_coor` with a box-typed memref (`!fir.box>`). In `XArrayCoorOpConversion` in `CodeGen.cpp`, `baseIsBoxed` is true, so CodeGen loads the byte stride from the descriptor on every element access. For scalar element access (`ext(i)` where `i` is a loop index), the stride is loop-invariant and — if the array is non-pointer and non-allocatable — statically known to be `sizeof(T)`. By keeping the stride load opaque inside `fir.array_coor` until CodeGen, it prevents LLVM from hoisting the stride load or optimizing it away entirely. GFortran eliminates this overhead via IPA-SRA, creating a `.isra.0` clone with flat pointer arguments.
## Reproducer
flang version 24.0.0
```
flang-new -O3 -march=z17 -c test_assumed_shape.f90
```
```fortran
module params
implicit none
integer, parameter :: nswbands = 19
integer, parameter :: pcols = 16
integer, parameter :: pver = 26
end module params
module rad_cnst
implicit none
contains
subroutine get_aer_props(ext, ssa, asm)
real(8), pointer, intent(out) :: ext(:)
real(8), pointer, intent(out) :: ssa(:)
real(8), pointer, intent(out) :: asm(:)
real(8), target, save :: ext_d(19)
real(8), target, save :: ssa_d(19)
real(8), target, save :: asm_d(19)
ext => ext_d
ssa => ssa_d
asm => asm_d
end subroutine
end module rad_cnst
module compute_mod
use params
implicit none
private
public :: aer_rad_props_sw
contains
subroutine get_nonhygro_rad_props(ncol, mass, ext, ssa, asm, tau, tau_w, tau_w_g, tau_w_f)
integer, intent(in) :: ncol
real(8), intent(in) :: mass(pcols, pver)
real(8), intent(in) :: ext(:)
real(8), intent(in) :: ssa(:)
real(8), intent(in) :: asm(:)
real(8), intent(out) :: tau (pcols, pver, nswbands)
real(8), intent(out) :: tau_w (pcols, pver, nswbands)
real(8), intent(out) :: tau_w_g(pcols, pver, nswbands)
real(8), intent(out) :: tau_w_f(pcols, pver, nswbands)
integer :: iswband
real(8) :: ext1, ssa1, asm1
do iswband = 1, nswbands
ext1 = ext(iswband)
ssa1 = ssa(iswband)
asm1 = asm(iswband)
tau (1:ncol, 1:pver, iswband) = mass(1:ncol, 1:pver) * ext1
tau_w (1:ncol, 1:pver, iswband) = mass(1:ncol, 1:pver) * ext1 * ssa1
tau_w_g(1:ncol, 1:pver, iswband) = mass(1:ncol, 1:pver) * ext1 * ssa1 * asm1
tau_w_f(1:ncol, 1:pver, iswband) = mass(1:ncol, 1:pver) * ext1 * ssa1 * asm1 * asm1
enddo
end subroutine get_nonhygro_rad_props
subroutine aer_rad_props_sw(ncol, numaerosols, mass, tau, tau_w, tau_w_g, tau_w_f)
use rad_cnst, only: get_aer_props
integer, intent(in) :: ncol
integer, intent(in) :: numaerosols
real(8), intent(in) :: mass(pcols, pver)
real(8), intent(out) :: tau (pcols, pver, nswbands)
real(8), intent(out) :: tau_w (pcols, pver, nswbands)
real(8), intent(out) :: tau_w_g(pcols, pver, nswbands)
real(8), intent(out) :: tau_w_f(pcols, pver, nswbands)
real(8), pointer :: n_ext(:), n_ssa(:), n_asm(:)
real(8) :: ta(pcols,pver,nswbands), tw(pcols,pver,nswbands)
real(8) :: twg(pcols,pver,nswbands), twf(pcols,pver,nswbands)
integer :: iaerosol
tau = 0.0d0; tau_w = 0.0d0; tau_w_g = 0.0d0; tau_w_f = 0.0d0
do iaerosol = 1, numaerosols
call get_aer_props(n_ext, n_ssa, n_asm)
call get_nonhygro_rad_props(ncol, mass, n_ext, n_ssa, n_asm, ta, tw, twg, twf)
tau(1:ncol,1:pver,:) = tau(1:ncol,1:pver,:) + ta(1:ncol,:,:)
tau_w(1:ncol,1:pver,:) = tau_w(1:ncol,1:pver,:) + tw(1:ncol,:,:)
enddo
end subroutine aer_rad_props_sw
end module compute_mod
```
## FIR (`-mmlir --mlir-print-ir-before=loop-versioning`)
`nswbands = 19` is a compile-time constant. Inside the `iswband = 1, nswbands` loop in `get_nonhygro_rad_props`:
```mlir
%29 = fir.array_coor %10 %28 : (!fir.box>, i64) -> !fir.ref
%33 = fir.array_coor %18 %32 : (!fir.box>, i64) -> !fir.ref
%37 = fir.array_coor %7 %36 : (!fir.box>, i64) -> !fir.ref
```
`%10`, `%18`, `%7` are the `fir.declare` results for `ext`, `ssa`, `asm` — all `!fir.box>`. In `XArrayCoorOpConversion::doRewrite` (`CodeGen.cpp`), `baseIsBoxed = mlir::isa(coor.getMemref().getType())` is true for all three, so CodeGen calls `getStrideFromBox` on every access instead of using `sizeof(f64)`.
## Assembly (`-O3 -march=z17`)
The generated assembly shows that stride and offset resolution for `ext`, `ssa`, `asm` is dynamic within the `iswband` loop body (`.LBB1_40`), based on descriptor fields spilled to the stack.
```asm
.LBB1_39:
la %r1, 1(%r1) ; ++iswband
cgije %r1, 20, .LBB1_73 ; exit if iswband == 20
.LBB1_40:
chsi 164(%r15), 0 ; LoopVersioning contiguity check
jle .LBB1_39
lg %r1, 184(%r15) ; iswband
lg %r5, 168(%r15) ; ext byte stride (from descriptor, spilled to stack)
aghi %r1, -1
msgrkc %r5, %r1, %r5 ; ext offset = (iswband-1) * stride
ld %f0, 0(%r5,%r11) ; ext(iswband)
lg %r5, 176(%r15) ; ssa byte stride (from descriptor, spilled to stack)
msgrkc %r5, %r1, %r5 ; ssa offset = (iswband-1) * stride
...
msgc %r1, 216(%r15) ; asm offset = (iswband-1) * stride
```
GFortran creates an IPA-SRA clone (`.isra.0`) with flat pointer arguments. The induction variable advances by a compile-time static element size (`sizeof(f64) = 8`).
```asm
ag %r4, 200(%r15) ; advance ext pointer by element size
ag %r5, 208(%r15) ; advance ssa pointer
ag %r8, 216(%r15) ; advance asm pointer
brctg %r3, .L126 ; decrement counter, branch if != 0
.L126:
j .L29 ; next iswband iteration
.L29:
vleg %v20, 0(%r11), 0 ; ext(iswband) — direct load
vleg %v22, 0(%r12), 0 ; ssa(iswband)
vleg %v17, 0(%r1), 0 ; asm(iswband)
```
Contributor guide
Research direction
Start in CodeGen.cpp at XArrayCoorOpConversion::doRewrite and inspect how boxed memrefs reach getStrideFromBox. Reproduce with the provided Fortran program using flang-new -O3 -march=z17, then inspect FIR and assembly. Done means non-pointer, non-allocatable intent(in) assumed-shape scalar accesses use the compile-time element size without repeated descriptor stride loads, while existing behavior remains correct.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- fortran
- Domain
- compilers, performance
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100