[Mlir-commits] [mlir] 1c6d2ad - [OpenMP][Flang][MLIR] Introduce omp.declare_simd op and emit from Flang (#175604)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Fri Jan 16 08:51:32 PST 2026
Author: Chi-Chun, Chen
Date: 2026-01-16T10:51:27-06:00
New Revision: 1c6d2add766288e2d0d2b089a2b66f7b0f285141
URL: https://github.com/llvm/llvm-project/commit/1c6d2add766288e2d0d2b089a2b66f7b0f285141
DIFF: https://github.com/llvm/llvm-project/commit/1c6d2add766288e2d0d2b089a2b66f7b0f285141.diff
LOG: [OpenMP][Flang][MLIR] Introduce omp.declare_simd op and emit from Flang (#175604)
Changes:
- Adds a new `omp.declare_simd` operation to the OpenMP MLIR dialect
- Lowers Fortran `!$omp declare simd` into `omp.declare_simd` inside the
enclosing function body
mlir to LLVMIR translation and uniform clause will be added in follow-up
PRs.
Added:
flang/test/Lower/OpenMP/Todo/declare-simd-uniform.f90
flang/test/Lower/OpenMP/declare-simd.f90
Modified:
flang/lib/Lower/OpenMP/OpenMP.cpp
mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
mlir/test/Dialect/OpenMP/invalid.mlir
mlir/test/Dialect/OpenMP/ops.mlir
Removed:
################################################################################
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 989e370870f33..0764693f748a5 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -3839,8 +3839,18 @@ static void
genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,
const parser::OpenMPDeclareSimdConstruct &declareSimdConstruct) {
- if (!semaCtx.langOptions().OpenMPSimd)
- TODO(converter.getCurrentLocation(), "OpenMPDeclareSimdConstruct");
+ mlir::Location loc = converter.getCurrentLocation();
+ const parser::OmpDirectiveSpecification &beginSpec = declareSimdConstruct.v;
+ List<Clause> clauses = makeClauses(beginSpec.Clauses(), semaCtx);
+
+ mlir::omp::DeclareSimdOperands clauseOps;
+ ClauseProcessor cp(converter, semaCtx, clauses);
+ cp.processAligned(clauseOps);
+ cp.processLinear(clauseOps);
+ cp.processSimdlen(clauseOps);
+ cp.processTODO<clause::Uniform>(loc, llvm::omp::Directive::OMPD_declare_simd);
+
+ mlir::omp::DeclareSimdOp::create(converter.getFirOpBuilder(), loc, clauseOps);
}
static void genOpenMPDeclareMapperImpl(
diff --git a/flang/test/Lower/OpenMP/Todo/declare-simd-uniform.f90 b/flang/test/Lower/OpenMP/Todo/declare-simd-uniform.f90
new file mode 100644
index 0000000000000..1ca5dd34de2d2
--- /dev/null
+++ b/flang/test/Lower/OpenMP/Todo/declare-simd-uniform.f90
@@ -0,0 +1,9 @@
+! RUN: %not_todo_cmd %flang_fc1 -emit-fir -fopenmp -o - %s 2>&1 | FileCheck %s
+
+! CHECK: not yet implemented: Unhandled clause UNIFORM in DECLARE SIMD construct
+subroutine declare_simd_uniform(x, y)
+ real(8), pointer, intent(inout) :: x(:)
+ real(8), pointer, intent(in) :: y(:)
+ !$omp declare simd uniform(x, y)
+end subroutine declare_simd_uniform
+
diff --git a/flang/test/Lower/OpenMP/declare-simd.f90 b/flang/test/Lower/OpenMP/declare-simd.f90
new file mode 100644
index 0000000000000..240d0f0d58305
--- /dev/null
+++ b/flang/test/Lower/OpenMP/declare-simd.f90
@@ -0,0 +1,108 @@
+! This test checks the lowering of the OpenMP `declare simd` directive with
+!
diff erent clauses (e.g. aligned, linear, simdlen). It also verifies that
+! the `omp.declare_simd` operation is emitted after the function prologue,
+! since the directive requires access to function arguments.
+
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 %s -o - | FileCheck %s
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=60 %s -o - | FileCheck %s
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=60 -cpp -DOMP_60 %s -o - | FileCheck %s
+
+subroutine declare_simd_no_clause()
+#ifdef OMP_60
+!$omp declare_simd
+#else
+!$omp declare simd
+#endif
+end subroutine declare_simd_no_clause
+
+! CHECK-LABEL: func.func @_QPdeclare_simd_no_clause()
+! CHECK: omp.declare_simd{{$}}
+! CHECK: return
+
+subroutine declare_simd_aligned(x, y, n, i)
+#ifdef OMP_60
+!$omp declare_simd aligned(x, y : 64)
+#else
+!$omp declare simd aligned(x, y : 64)
+#endif
+ real(8), pointer, intent(inout) :: x(:)
+ real(8), pointer, intent(in) :: y(:)
+ integer, intent(in) :: n, i
+ if (i <= n) x(i) = x(i) + y(i)
+end subroutine declare_simd_aligned
+
+! CHECK-LABEL: func.func @_QPdeclare_simd_aligned(
+! CHECK: %[[SCOPE_A:.*]] = fir.dummy_scope : !fir.dscope
+! CHECK: %[[I_A:.*]]:2 = hlfir.declare %{{.*}} dummy_scope %[[SCOPE_A]] arg 4 {{.*}} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK: %[[N_A:.*]]:2 = hlfir.declare %{{.*}} dummy_scope %[[SCOPE_A]] arg 3 {{.*}} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK: %[[X_A:.*]]:2 = hlfir.declare %{{.*}} dummy_scope %[[SCOPE_A]] arg 1 {{.*pointer.*}} : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>, !fir.dscope) -> (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>, !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>)
+! CHECK: %[[Y_A:.*]]:2 = hlfir.declare %{{.*}} dummy_scope %[[SCOPE_A]] arg 2 {{.*pointer.*}} : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>, !fir.dscope) -> (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>, !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>)
+! CHECK: omp.declare_simd aligned(%[[X_A]]#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>> -> 64 : i64,
+! CHECK-SAME: %[[Y_A]]#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>> -> 64 : i64){{$}}
+! CHECK: return
+
+subroutine declare_simd_linear(x, y, n, i)
+#ifdef OMP_60
+!$omp declare_simd linear(i)
+#else
+!$omp declare simd linear(i)
+#endif
+ real(8), pointer, intent(inout) :: x(:)
+ real(8), pointer, intent(in) :: y(:)
+ integer, intent(in) :: n, i
+ if (i <= n) x(i) = x(i) + y(i)
+end subroutine declare_simd_linear
+
+! CHECK-LABEL: func.func @_QPdeclare_simd_linear(
+! CHECK: %[[SCOPE:.*]] = fir.dummy_scope : !fir.dscope
+! CHECK: %[[I:.*]]:2 = hlfir.declare %{{.*}} dummy_scope %[[SCOPE]] arg 4 {{.*}} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK: %[[C1:.*]] = arith.constant 1 : i32
+! CHECK: omp.declare_simd linear(%[[I]]#0 = %[[C1]] : !fir.ref<i32>) {linear_var_types = [i32]}{{$}}
+! CHECK: return
+
+subroutine declare_simd_simdlen(x, y, n, i)
+#ifdef OMP_60
+!$omp declare_simd simdlen(8)
+#else
+!$omp declare simd simdlen(8)
+#endif
+end subroutine declare_simd_simdlen
+
+! CHECK-LABEL: func.func @_QPdeclare_simd_simdlen(
+! CHECK: %[[SCOPE_S:.*]] = fir.dummy_scope : !fir.dscope
+! CHECK: omp.declare_simd{{.*}}simdlen(8){{$}}
+! CHECK-NEXT: return
+
+subroutine declare_simd_combined(x, y, n, i)
+#ifdef OMP_60
+!$omp declare_simd aligned(x, y : 64) linear(i) simdlen(8)
+#else
+!$omp declare simd aligned(x, y : 64) linear(i) simdlen(8)
+#endif
+ real(8), pointer, intent(inout) :: x(:)
+ real(8), pointer, intent(in) :: y(:)
+ integer, intent(in) :: n, i
+
+ if (i <= n) then
+ x(i) = x(i) + y(i)
+ end if
+end subroutine declare_simd_combined
+
+! CHECK-LABEL: func.func @_QPdeclare_simd_combined(
+! CHECK-SAME: %{{.*}}: !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>{{.*}}fir.bindc_name = "x"
+! CHECK-SAME: %{{.*}}: !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>{{.*}}fir.bindc_name = "y"
+! CHECK-SAME: %{{.*}}: !fir.ref<i32>{{.*}}fir.bindc_name = "n"
+! CHECK-SAME: %{{.*}}: !fir.ref<i32>{{.*}}fir.bindc_name = "i"
+! CHECK: %[[SCOPE:.*]] = fir.dummy_scope : !fir.dscope
+! CHECK: %[[I_DECL:.*]]:2 = hlfir.declare %{{.*}} dummy_scope %[[SCOPE]] arg 4 {{.*}} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK: %[[N_DECL:.*]]:2 = hlfir.declare %{{.*}} dummy_scope %[[SCOPE]] arg 3 {{.*}} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK: %[[X_DECL:.*]]:2 = hlfir.declare %{{.*}} dummy_scope %[[SCOPE]] arg 1 {{.*}} : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>, !fir.dscope) -> (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>, !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>)
+! CHECK: %[[Y_DECL:.*]]:2 = hlfir.declare %{{.*}} dummy_scope %[[SCOPE]] arg 2 {{.*}} : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>, !fir.dscope) -> (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>, !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>)
+! CHECK: %[[C1:.*]] = arith.constant 1 : i32
+
+! CHECK: omp.declare_simd
+! CHECK-SAME: aligned(%[[X_DECL]]#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>> -> 64 : i64,
+! CHECK-SAME: %[[Y_DECL]]#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>> -> 64 : i64)
+! CHECK-SAME: linear(%[[I_DECL]]#0 = %[[C1]] : !fir.ref<i32>)
+! CHECK-SAME: simdlen(8)
+! CHECK-SAME: {linear_var_types = [i32]}
diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
index 1fcd7b3c23e10..d4e8cecda2601 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
@@ -2241,4 +2241,35 @@ def WorkdistributeOp : OpenMP_Op<"workdistribute"> {
let assemblyFormat = "$region attr-dict";
}
+//===----------------------------------------------------------------------===//
+// declare simd Construct
+//===----------------------------------------------------------------------===//
+
+def DeclareSimdOp
+ : OpenMP_Op<"declare_simd", traits = [AttrSizedOperandSegments],
+ clauses = [OpenMP_AlignedClause, OpenMP_LinearClause,
+ OpenMP_SimdlenClause]> {
+ let summary = "declare simd directive";
+ let description = [{
+ "omp.declare_simd" models the OpenMP `declare simd` directive.
+
+ This is a declarative operation (no region) intended to appear inside
+ a function body. It attaches clauses of declare simd to the enclosing
+ function.
+
+ Example:
+ ```mlir
+ func.func @add(%a: memref<16xi32>) {
+ omp.declare_simd simdlen(8) aligned(%a : memref<16xi32> -> 64 : i64)
+ ...
+ }
+ ```
+ }] # clausesDescription;
+
+ let builders = [OpBuilder<(
+ ins CArg<"const DeclareSimdOperands &">:$clauses)>];
+
+ let hasVerifier = 1;
+}
+
#endif // OPENMP_OPS
diff --git a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
index 1af525ba92bd0..cf25fc53e0238 100644
--- a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
+++ b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
@@ -4465,6 +4465,29 @@ LogicalResult WorkdistributeOp::verify() {
return success();
}
+//===----------------------------------------------------------------------===//
+// Declare simd [7.7]
+//===----------------------------------------------------------------------===//
+
+LogicalResult DeclareSimdOp::verify() {
+ // Must be nested inside a function-like op
+ auto func =
+ dyn_cast_if_present<mlir::FunctionOpInterface>((*this)->getParentOp());
+ if (!func)
+ return emitOpError() << "must be nested inside a function";
+
+ return verifyAlignedClause(*this, getAlignments(), getAlignedVars());
+}
+
+void DeclareSimdOp::build(OpBuilder &odsBuilder, OperationState &odsState,
+ const DeclareSimdOperands &clauses) {
+ MLIRContext *ctx = odsBuilder.getContext();
+ DeclareSimdOp::build(odsBuilder, odsState, clauses.alignedVars,
+ makeArrayAttr(ctx, clauses.alignments),
+ clauses.linearVars, clauses.linearStepVars,
+ clauses.linearVarTypes, clauses.simdlen);
+}
+
#define GET_ATTRDEF_CLASSES
#include "mlir/Dialect/OpenMP/OpenMPOpsAttributes.cpp.inc"
diff --git a/mlir/test/Dialect/OpenMP/invalid.mlir b/mlir/test/Dialect/OpenMP/invalid.mlir
index af24d969064ab..1350c5e2ee8d2 100644
--- a/mlir/test/Dialect/OpenMP/invalid.mlir
+++ b/mlir/test/Dialect/OpenMP/invalid.mlir
@@ -3139,3 +3139,7 @@ func.func @invalid_workdistribute() -> () {
}
return
}
+
+// -----
+// expected-error @+1 {{'omp.declare_simd' op must be nested inside a function}}
+omp.declare_simd
diff --git a/mlir/test/Dialect/OpenMP/ops.mlir b/mlir/test/Dialect/OpenMP/ops.mlir
index 816df56ecc5a5..902d962138fc9 100644
--- a/mlir/test/Dialect/OpenMP/ops.mlir
+++ b/mlir/test/Dialect/OpenMP/ops.mlir
@@ -3368,3 +3368,65 @@ func.func @omp_target_map_clause_type_test(%arg0 : memref<?xi32>) -> () {
return
}
+
+// CHECK-LABEL: func.func @omp_declare_simd
+func.func @omp_declare_simd() -> () {
+ // CHECK: omp.declare_simd
+ omp.declare_simd
+ return
+}
+
+// CHECK-LABEL: func.func @omp_declare_simd_simdlen
+func.func @omp_declare_simd_simdlen() -> () {
+ // CHECK: omp.declare_simd
+ // CHECK-SAME: simdlen(8)
+ omp.declare_simd simdlen(8)
+ return
+}
+
+// CHECK-LABEL: func.func @omp_declare_simd_aligned
+func.func @omp_declare_simd_aligned(%a: f64, %b: f64,
+ %p0: memref<i32>, %p1: memref<i32>) -> () {
+ // CHECK: omp.declare_simd
+ // CHECK-SAME: aligned(
+ // CHECK-SAME: %{{.*}} : memref<i32> -> 32 : i64,
+ // CHECK-SAME: %{{.*}} : memref<i32> -> 128 : i64)
+ omp.declare_simd aligned(%p0 : memref<i32> -> 32 : i64,
+ %p1 : memref<i32> -> 128 : i64)
+
+ return
+}
+
+// CHECK-LABEL: func.func @omp_declare_simd_aligned_list_generic
+func.func @omp_declare_simd_aligned_list_generic(%arg0: f64, %arg1: f64,
+ %arg2: memref<i32>, %arg3: memref<i32>) -> () {
+ // CHECK: omp.declare_simd
+ // CHECK-SAME: aligned(%{{.*}} : memref<i32> -> 32 : i64, %{{.*}} : memref<i32> -> 128 : i64)
+ omp.declare_simd aligned(%arg2 : memref<i32> -> 32 : i64, %arg3 : memref<i32> -> 128 : i64)
+ return
+}
+
+// CHECK-LABEL: func.func @omp_declare_simd_linear
+func.func @omp_declare_simd_linear(%a: f64, %b: f64, %iv: i32, %step: i32) -> () {
+ // CHECK: omp.declare_simd
+ // CHECK-SAME: linear(%{{.*}} = %{{.*}} : i32)
+ omp.declare_simd linear(%iv = %step : i32)
+ return
+}
+
+// CHECK-LABEL: func.func @omp_declare_simd_all_clauses
+func.func @omp_declare_simd_all_clauses(%a: f64, %b: f64,
+ %p0: memref<i32>, %p1: memref<i32>,
+ %iv: i32, %step: i32) -> () {
+ // CHECK: omp.declare_simd
+ // CHECK-SAME: aligned(
+ // CHECK-SAME: %{{.*}} : memref<i32> -> 32 : i64,
+ // CHECK-SAME: %{{.*}} : memref<i32> -> 128 : i64)
+ // CHECK-SAME: linear(%{{.*}} = %{{.*}} : i32)
+ // CHECK-SAME: simdlen(8)
+ omp.declare_simd simdlen(8)
+ aligned(%p0 : memref<i32> -> 32 : i64,
+ %p1 : memref<i32> -> 128 : i64)
+ linear(%iv = %step : i32)
+ return
+}
More information about the Mlir-commits
mailing list