[flang-commits] [flang] [llvm] [mlir] [flang][mlir][OpenMP] Allow integer host_eval uses in effect-free ops (PR #223475)
Caroline Newcombe via flang-commits
flang-commits at lists.llvm.org
Wed Sep 16 14:03:31 PDT 2026
cenewcombe wrote:
Here’s the MLIR produced from the first subroutine in collapse-target-intervening.f90 in a host-only compilation (annotated by AI). The six `host_eval` operands are the collapsed loop's `(lb1, lb2, ub1, ub2, step1, step2)`, so `%arg4`, `%arg6` and `%arg8` are the inner lower bound, upper bound and step. `%8` and `%9` are where the host evaluates `n` and `m`.
All six `host_eval` block arguments (`%arg3`–`%arg8`) are used inside the region by `omp.loop_nest` as its bounds — the use `TargetOp::verifyRegions()` requires on host compilations. The guard at `%24`–`%28` additionally uses the inner three (`%arg4, %arg6, %arg8`) to compute `lb + ((ub-lb)/step)*step` and compare it against the inner IV, detecting the last inner iteration. The `fir.if` body then recomputes that value plus one step to get the terminal value of j, and runs the intervening `x = x + j`.
<details><summary>Details</summary>
```
func.func @_QPrepro(%arg0: !fir.ref<i32> {fir.bindc_name = "n", fir.read_only}, %arg1: !fir.ref<i32> {fir.bindc_name = "m", fir.read_only}, %arg2: !fir.ref<i32> {fir.bindc_name = "x"}) {
%0 = fir.dummy_scope : !fir.dscope
%1 = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFreproEi"}
%2:2 = hlfir.declare %1 {uniq_name = "_QFreproEi"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
%3 = fir.alloca i32 {bindc_name = "j", uniq_name = "_QFreproEj"}
%4:2 = hlfir.declare %3 {uniq_name = "_QFreproEj"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
%5:2 = hlfir.declare %arg1 dummy_scope %0 arg 2 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFreproEm"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
%6:2 = hlfir.declare %arg0 dummy_scope %0 arg 1 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFreproEn"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
%7:2 = hlfir.declare %arg2 dummy_scope %0 arg 3 {fortran_attrs = #fir.var_attrs<intent_inout>, uniq_name = "_QFreproEx"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
// The six loop bounds, evaluated here on the host.
%c1_i32 = arith.constant 1 : i32 // outer lb
%8 = fir.load %6#0 : !fir.ref<i32> // outer ub = n
%c1_i32_0 = arith.constant 1 : i32 // outer step
%c1_i32_1 = arith.constant 1 : i32 // inner lb
%9 = fir.load %5#0 : !fir.ref<i32> // inner ub = m
%c1_i32_2 = arith.constant 1 : i32 // inner step
%10 = omp.map.info var_ptr(%7#1 : !fir.ref<i32>, i32) map_clauses(tofrom) capture(ByRef) name("x") -> !fir.ref<i32>
%11 = omp.map.info var_ptr(%2#1 : !fir.ref<i32>, i32) map_clauses(implicit) capture(ByCopy) name("i") -> !fir.ref<i32>
%12 = omp.map.info var_ptr(%6#1 : !fir.ref<i32>, i32) map_clauses(implicit) capture(ByCopy) name("n") -> !fir.ref<i32>
%13 = omp.map.info var_ptr(%4#1 : !fir.ref<i32>, i32) map_clauses(implicit) capture(ByCopy) name("j") -> !fir.ref<i32>
%14 = omp.map.info var_ptr(%5#1 : !fir.ref<i32>, i32) map_clauses(implicit) capture(ByCopy) name("m") -> !fir.ref<i32>
// host_eval order is (lb1, lb2, ub1, ub2, step1, step2), so the inner
// bounds arrive as %arg4 (lb), %arg6 (ub = m) and %arg8 (step).
omp.target kernel_type(spmd) host_eval(%c1_i32 -> %arg3, %c1_i32_1 -> %arg4, %8 -> %arg5, %9 -> %arg6, %c1_i32_0 -> %arg7, %c1_i32_2 -> %arg8 : i32, i32, i32, i32, i32, i32) map_entries(%10 -> %arg9, %11 -> %arg10, %12 -> %arg11, %13 -> %arg12, %14 -> %arg13 : !fir.ref<i32>, !fir.ref<i32>, !fir.ref<i32>, !fir.ref<i32>, !fir.ref<i32>) {
%15:2 = hlfir.declare %arg9 {fortran_attrs = #fir.var_attrs<intent_inout>, uniq_name = "_QFreproEx"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
%16:2 = hlfir.declare %arg10 {uniq_name = "_QFreproEi"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
%17:2 = hlfir.declare %arg11 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFreproEn"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
%18:2 = hlfir.declare %arg12 {uniq_name = "_QFreproEj"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
// Mapped copy of m, available in-region: this is what reading the bound
// from the map/firstprivate binding would load from.
%19:2 = hlfir.declare %arg13 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFreproEm"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
omp.teams {
omp.parallel private(@_QFreproEi_private_i32 %16#0 -> %arg14, @_QFreproEj_private_i32 %18#0 -> %arg15 : !fir.ref<i32>, !fir.ref<i32>) {
%20:2 = hlfir.declare %arg14 {uniq_name = "_QFreproEi"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
%21:2 = hlfir.declare %arg15 {uniq_name = "_QFreproEj"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
omp.distribute {
omp.wsloop {
// First in-region use of the host_eval values: all six are the
// loop_nest bounds, which TargetOp::verifyRegions() requires.
omp.loop_nest (%arg16, %arg17) : i32 = (%arg3, %arg4) to (%arg5, %arg6) inclusive step (%arg7, %arg8) collapse(2) {
hlfir.assign %arg16 to %20#0 : i32, !fir.ref<i32>
hlfir.assign %arg17 to %21#0 : i32, !fir.ref<i32>
// x = x + 1
%22 = fir.load %15#0 : !fir.ref<i32>
%c1_i32_3 = arith.constant 1 : i32
%23 = arith.addi %22, %c1_i32_3 : i32
hlfir.assign %23 to %15#0 : i32, !fir.ref<i32>
// Second use: guard the intervening code on the last inner
// iteration, lb + ((ub-lb)/step)*step. Needs all three inner
// bounds, and this is the arithmetic the verifier rejects.
%24 = arith.subi %arg6, %arg4 : i32
%25 = arith.divsi %24, %arg8 : i32
%26 = arith.muli %25, %arg8 : i32
%27 = arith.addi %arg4, %26 : i32
%28 = arith.cmpi eq, %arg17, %27 : i32
fir.if %28 {
// Fortran terminal value of j: the above plus one step.
%29 = arith.subi %arg6, %arg4 : i32
%30 = arith.divsi %29, %arg8 : i32
%31 = arith.muli %30, %arg8 : i32
%32 = arith.addi %arg4, %31 : i32
%33 = arith.addi %32, %arg8 : i32
hlfir.assign %33 to %21#0 : i32, !fir.ref<i32>
// Intervening statement: x = x + j
%34 = fir.load %15#0 : !fir.ref<i32>
%35 = fir.load %21#0 : !fir.ref<i32>
%36 = arith.addi %34, %35 : i32
hlfir.assign %36 to %15#0 : i32, !fir.ref<i32>
}
omp.yield
}
} {omp.composite}
} {omp.composite}
omp.terminator
} {omp.composite}
omp.terminator
} {omp.combined}
omp.terminator
} {omp.combined}
return
}
```
</details>
https://github.com/llvm/llvm-project/pull/223475
More information about the flang-commits
mailing list