[RFC] Flang lowering for OpenMP metadirective

Static Resolution

When all context selectors are compile-time evaluable (e.g., implementation={vendor(llvm)}, device={kind(host)}, device={isa("neon")}, construct={parallel}), the best-matching variant is selected at lowering time and only its directive is emitted.

Fortran input:

subroutine static_vendor()
  !$omp metadirective &
  !$omp & when(implementation={vendor(llvm)}: barrier) &
  !$omp & otherwise(nothing)
end subroutine

MLIR output — the metadirective is gone, only the winning variant remains:

func.func @_QPstatic_vendor() {
  %0 = fir.dummy_scope : !fir.dscope
  omp.barrier
  return
}

Since vendor(llvm) is always true inside Flang, barrier is selected at compile time. The metadirective leaves no trace in the IR.

Dynamic Resolution

When a when clause whose expression is not a compile-time constant, the metadirective is lowered to an fir.if/else chain that evaluates the condition at runtime.

Fortran input:

subroutine dynamic_user(n)
  integer, intent(in) :: n
  !$omp metadirective &
  !$omp & when(user={condition(n > 1000)}: barrier) &
  !$omp & otherwise(nothing)
end subroutine

MLIR output — an fir.if selects the variant at runtime:

func.func @_QPdynamic_user(%arg0: !fir.ref<i32> {fir.bindc_name = "n"}) {
  %0 = fir.dummy_scope : !fir.dscope
  %1:2 = hlfir.declare %arg0 ...
  %2 = fir.load %1#0 : !fir.ref<i32>
  %c1000_i32 = arith.constant 1000 : i32
  %3 = arith.cmpi sgt, %2, %c1000_i32 : i32
  fir.if %3 {
    omp.barrier
  } else {
    // otherwise(nothing) — empty
  }
  return
}

With multiple dynamic when clauses, the fir.if chains nest: each dynamic condition is tested in priority order, with the static fallback (or otherwise) in the innermost else.

Mixed Static + Dynamic

When a when clause with a dynamic user={condition}], the static traits are evaluated first. Only statically-applicable variants enter the dynamic selection:

subroutine mixed_case(n)
  integer, intent(in) :: n
  !$omp metadirective &
  !$omp & when(implementation={vendor(llvm)}, user={condition(n > 100)}: barrier) &
  !$omp & when(implementation={vendor(llvm)}: flush) &
  !$omp & otherwise(nothing)
end subroutine

Both when clauses pass the static vendor(llvm) check. The first has a dynamic condition, so it becomes a runtime branch; the second is fully static and becomes the fallback:

func.func @_QPmixed_case(%arg0: !fir.ref<i32> {fir.bindc_name = "n"}) {
  %0 = fir.dummy_scope : !fir.dscope
  %1:2 = hlfir.declare %arg0 ...
  %2 = fir.load %1#0 : !fir.ref<i32>
  %c100_i32 = arith.constant 100 : i32
  %3 = arith.cmpi sgt, %2, %c100_i32 : i32
  fir.if %3 {
    omp.barrier
  } else {
    omp.flush
  }
  return
}
1 Like

The current implementation status is that we parse an arbitrary METADIRECTIVE as a standalone directive. Any code that follows it is not associated with the directive in any way.

We do semantic checks for the METADIRECTIVE as a whole, but there are no semantic checks for any individual contained directive variants. There is also no support for BEGIN/END METDIRECTIVE, but I’m planning to add parser support for it soon.

We have talked briefly a while back in some telecom about doing semantic checks on individual variants, and what seemed somewhat reasonable back then was to perform the initial semantic checks for METADIRECTIVE as a whole, then “expand” the metadirective into a series of checks (as you described above) in the AST, then run a second round of semantic checks on the expanded code. One challenge with this approach may be that AST nodes are not very amenable to cloning, so perhaps a different approach will be necessary.

Are you planning to work on the lowering part, or also on semantic checks?

Thank you for the information and context you shared. I was initially planning to work on the lowering part for Flang, but I’m also open to helping with semantic checks.

Here’s my pr for the lowering work: [flang][OpenMP] Support lowering of metadirective (part 1) by chichunchen · Pull Request #193664 · llvm/llvm-project · GitHub.

PR is up for review:

[flang][OpenMP] Frontend support for BEGIN/END METADIRECTIVE by kparzysz · Pull Request #194402 · llvm/llvm-project

The basic metadirective lowering PR now incorporate begin/end metadirective: [flang][OpenMP] Support lowering of metadirective (part 1) by chichunchen · Pull Request #193664 · llvm/llvm-project · GitHub

Proposed “dynamic” and “mixed static + dynamic” is ready for review now: [flang][OpenMP] Support lowering of metadirective (part 2) by chichunchen · Pull Request #194424 · llvm/llvm-project · GitHub

Following PR will be the support for “loop” and full support of “construct”.