Kajetan Puchalski d3d96e2057
[flang][OpenMP] Add -f[no]-openmp-simd (#150269)
Both clang and gfortran support the -fopenmp-simd flag, which enables
OpenMP support only for simd constructs, while disabling the rest of
OpenMP.

Implement the appropriate parse tree rewriting to remove non-SIMD OpenMP
constructs at the parsing stage.

Add a new SimdOnly flang OpenMP IR pass which rewrites generated OpenMP
FIR to handle untangling composite simd constructs, and clean up OpenMP
operations leftover after the parse tree rewriting stage.
With this approach, the two parts of the logic required to make the flag
work can be self-contained within the parse tree rewriter and the MLIR
pass, respectively. It does not need to be implemented within the core
lowering logic itself.

The flag is expected to have no effect if -fopenmp is passed explicitly,
and is only expected to remove OpenMP constructs, not things like OpenMP
library functions calls. This matches the behaviour of other compilers.

---------

Signed-off-by: Kajetan Puchalski <kajetan.puchalski@arm.com>
2025-08-14 14:20:15 +01:00

197 lines
6.0 KiB
MLIR

// RUN: fir-opt --split-input-file --verify-diagnostics --omp-simd-only %s | FileCheck %s
// Check that simd operations are not removed and rewritten, but all the other OpenMP ops are.
// Tests the logic in flang/lib/Optimizer/OpenMP/SimdOnly.cpp
// CHECK: omp.private
// CHECK-LABEL: func.func @simd
omp.private {type = private} @_QFEi_private_i32 : i32
func.func @simd(%arg0: i32, %arg1: !fir.ref<i32>, %arg2: !fir.ref<i32>) {
%c1_i32 = arith.constant 1 : i32
%c100000_i32 = arith.constant 100000 : i32
// CHECK: omp.simd private
omp.simd private(@_QFEi_private_i32 %arg2 -> %arg3 : !fir.ref<i32>) {
// CHECK: omp.loop_nest
omp.loop_nest (%arg4) : i32 = (%c1_i32) to (%c100000_i32) inclusive step (%c1_i32) {
// CHECK: fir.store
fir.store %arg0 to %arg1 : !fir.ref<i32>
// CHECK: omp.yield
omp.yield
}
}
return
}
// -----
// CHECK-LABEL: func.func @simd_composite
func.func @simd_composite(%arg0: i32, %arg1: !fir.ref<i32>) {
%c1_i32 = arith.constant 1 : i32
%c100000_i32 = arith.constant 100000 : i32
// CHECK-NOT: omp.parallel
omp.parallel {
// CHECK-NOT: omp.wsloop
omp.wsloop {
// CHECK: omp.simd
omp.simd {
// CHECK: omp.loop_nest
omp.loop_nest (%arg3) : i32 = (%c1_i32) to (%c100000_i32) inclusive step (%c1_i32) {
// CHECK: fir.store
fir.store %arg0 to %arg1 : !fir.ref<i32>
// CHECK: omp.yield
omp.yield
}
// CHECK-NOT: {omp.composite}
} {omp.composite}
} {omp.composite}
omp.terminator
}
return
}
// -----
// CHECK-NOT: omp.private
// CHECK-LABEL: func.func @parallel
omp.private {type = private} @_QFEi_private_i32 : i32
func.func @parallel(%arg0: i32, %arg1: !fir.ref<i32>) {
%c1 = arith.constant 1 : index
%c1_i32 = arith.constant 1 : i32
%c100000_i32 = arith.constant 100000 : i32
// CHECK-NOT: omp.parallel
omp.parallel private(@_QFEi_private_i32 %arg1 -> %arg3 : !fir.ref<i32>) {
// CHECK: fir.convert
%15 = fir.convert %c1_i32 : (i32) -> index
// CHECK: fir.convert
%16 = fir.convert %c100000_i32 : (i32) -> index
// CHECK: fir.do_loop
%18:2 = fir.do_loop %arg4 = %15 to %16 step %c1 iter_args(%arg2 = %arg0) -> (index, i32) {
// CHECK: fir.store
fir.store %arg0 to %arg1 : !fir.ref<i32>
fir.result %arg4, %arg2 : index, i32
}
// CHECK-NOT: omp.terminator
omp.terminator
}
return
}
// -----
// CHECK-LABEL: func.func @target_map(
// CHECK-SAME: %[[ARG_0:.*]]: i32, %[[ARG_1:.*]]: !fir.ref<i32>
func.func @target_map(%arg5: i32, %arg6: !fir.ref<i32>) {
// CHECK-NOT: omp.map.info
%3 = omp.map.info var_ptr(%arg6 : !fir.ref<i32>, i32) map_clauses(implicit) capture(ByCopy) -> !fir.ref<i32>
// CHECK-NOT: omp.target
omp.target map_entries(%3 -> %arg0 : !fir.ref<i32>) {
// CHECK: arith.constant
%c1_i32 = arith.constant 1 : i32
// CHECK: fir.store %c1_i32 to %[[ARG_1]]
fir.store %c1_i32 to %arg0 : !fir.ref<i32>
// CHECK-NOT: omp.terminator
omp.terminator
}
return
}
// -----
// CHECK-LABEL: func.func @teams
func.func @teams(%arg0: i32, %arg1: !fir.ref<i32>) {
// CHECK-NOT: omp.teams
omp.teams {
// CHECK: fir.store
fir.store %arg0 to %arg1 : !fir.ref<i32>
// CHECK-NOT: omp.terminator
omp.terminator
}
return
}
// -----
// CHECK-LABEL: func.func @distribute_simd
func.func @distribute_simd(%arg0: i32, %arg1: !fir.ref<i32>) {
%c1_i32 = arith.constant 1 : i32
%c100000_i32 = arith.constant 100000 : i32
// CHECK-NOT: omp.distribute
omp.distribute {
// CHECK: omp.simd
omp.simd {
// CHECK: omp.loop_nest
omp.loop_nest (%arg3) : i32 = (%c1_i32) to (%c100000_i32) inclusive step (%c1_i32) {
// CHECK: fir.store
fir.store %arg0 to %arg1 : !fir.ref<i32>
// CHECK: omp.yield
omp.yield
}
// CHECK-NOT: {omp.composite}
} {omp.composite}
// CHECK-NOT: {omp.composite}
} {omp.composite}
return
}
// -----
// CHECK-LABEL: func.func @threadprivate(
// CHECK-SAME: %[[ARG_0:.*]]: i32, %[[ARG_1:.*]]: !fir.ref<i32>
func.func @threadprivate(%arg0: i32, %arg1: !fir.ref<i32>) {
// CHECK-NOT: omp.threadprivate
%1 = omp.threadprivate %arg1 : !fir.ref<i32> -> !fir.ref<i32>
// CHECK: fir.store %[[ARG_0]] to %[[ARG_1]]
fir.store %arg0 to %1 : !fir.ref<i32>
return
}
// -----
// CHECK-LABEL: func.func @multi_block(
// CHECK-SAME: %[[ARG_0:.*]]: i32, %[[ARG_1:.*]]: !fir.ref<i32>, %[[ARG_3:.*]]: i1
func.func @multi_block(%funcArg0: i32, %funcArg1: !fir.ref<i32>, %6: i1) {
%false = arith.constant false
%c0_i32 = arith.constant 0 : i32
// CHECK-NOT: omp.parallel
omp.parallel {
// CHECK: cf.cond_br %[[ARG_3]], ^[[BB1:.*]], ^[[BB2:.*]]
cf.cond_br %6, ^bb1, ^bb2
// CHECK: ^[[BB1]]
^bb1: // pred: ^bb0
// CHECK: fir.call
fir.call @_FortranAStopStatement(%c0_i32, %false, %false) fastmath<contract> : (i32, i1, i1) -> ()
// CHECK-NOT: omp.terminator
omp.terminator
// CHECK: ^[[BB2]]
^bb2: // pred: ^bb0
// CHECK: fir.store
fir.store %funcArg0 to %funcArg1 : !fir.ref<i32>
// CHECK-NOT: omp.terminator
omp.terminator
}
return
}
// -----
// CHECK-LABEL: func.func @map_info(
// CHECK-SAME: %[[ARG_0:.*]]: i32, %[[ARG_1:.*]]: !fir.ref<i32>
func.func @map_info(%funcArg0: i32, %funcArg1: !fir.ref<i32>) {
%c1 = arith.constant 1 : index
// CHECK-NOT: omp.map.bounds
%1 = omp.map.bounds lower_bound(%c1 : index) upper_bound(%c1 : index) extent(%c1 : index) stride(%c1 : index) start_idx(%c1 : index)
// CHECK-NOT: omp.map.info
%13 = omp.map.info var_ptr(%funcArg1 : !fir.ref<i32>, i32) map_clauses(to) capture(ByRef) bounds(%1) -> !fir.ref<i32>
// CHECK-NOT: omp.target
omp.target map_entries(%13 -> %arg3 : !fir.ref<i32>) {
%c1_i32 = arith.constant 1 : i32
// CHECK: fir.store %c1_i32 to %[[ARG_1]]
fir.store %c1_i32 to %arg3 : !fir.ref<i32>
// CHECK-NOT: omp.terminator
omp.terminator
}
// CHECK-NOT: omp.map.info
%18 = omp.map.info var_ptr(%funcArg1 : !fir.ref<i32>, i32) map_clauses(from) capture(ByRef) bounds(%1) -> !fir.ref<i32>
return
}