From 80dc5aa537264f409d83097ce16b7e3abe6b84c1 Mon Sep 17 00:00:00 2001 From: "Chi-Chun, Chen" Date: Wed, 25 Mar 2026 10:17:30 -0500 Subject: [PATCH] [flang][mlir][OpenMP] Add linear modifier (val, ref, uval) (#187142) Add support for OpenMP linear modifiers `val`, `ref`, and `uval` as defined in OpenMP 5.2 (5.4.6). --- flang/lib/Lower/OpenMP/ClauseProcessor.cpp | 63 +++++++-- flang/lib/Lower/OpenMP/ClauseProcessor.h | 3 +- flang/lib/Lower/OpenMP/OpenMP.cpp | 30 +++- .../Lower/OpenMP/composite_simd_linear.f90 | 21 ++- flang/test/Lower/OpenMP/declare-simd.f90 | 57 +++++++- .../OpenMP/distribute-parallel-do-simd.f90 | 24 ++-- flang/test/Lower/OpenMP/linear_modifier.f90 | 54 ++++++++ flang/test/Lower/OpenMP/simd-linear.f90 | 10 +- flang/test/Lower/OpenMP/wsloop-linear.f90 | 12 +- .../mlir/Dialect/OpenMP/OpenMPClauses.td | 10 +- .../mlir/Dialect/OpenMP/OpenMPEnums.td | 16 +++ mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp | 130 ++++++++++++++---- mlir/test/Dialect/OpenMP/invalid.mlir | 75 ++++++++++ mlir/test/Dialect/OpenMP/ops.mlir | 82 +++++++++++ 14 files changed, 521 insertions(+), 66 deletions(-) create mode 100644 flang/test/Lower/OpenMP/linear_modifier.f90 diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp index 8e50c38ce68c..75df4163c8a5 100644 --- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp +++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp @@ -1488,16 +1488,32 @@ bool ClauseProcessor::processIsDevicePtr( return clauseFound; } -bool ClauseProcessor::processLinear(mlir::omp::LinearClauseOps &result) const { +bool ClauseProcessor::processLinear(mlir::omp::LinearClauseOps &result, + bool isDeclareSimd) const { lower::StatementContext stmtCtx; + std::vector typeAttrs; + std::vector linearModAttrs; return findRepeatableClause< omp::clause::Linear>([&](const omp::clause::Linear &clause, const parser::CharBlock &) { auto &objects = std::get(clause.t); - static std::vector typeAttrs; - if (!result.linearVars.size()) - typeAttrs.clear(); + std::optional explicitLinearMod; + if (auto &linearModifier = + std::get>( + clause.t)) { + switch (*linearModifier) { + case omp::clause::Linear::LinearModifier::Val: + explicitLinearMod = mlir::omp::LinearModifier::val; + break; + case omp::clause::Linear::LinearModifier::Ref: + explicitLinearMod = mlir::omp::LinearModifier::ref; + break; + case omp::clause::Linear::LinearModifier::Uval: + explicitLinearMod = mlir::omp::LinearModifier::uval; + break; + } + } for (const omp::Object &object : objects) { semantics::Symbol *sym = object.sym(); @@ -1512,10 +1528,6 @@ bool ClauseProcessor::processLinear(mlir::omp::LinearClauseOps &result) const { mlir::Value operand = fir::getBase(converter.genExprValue(toEvExpr(*mod), stmtCtx)); result.linearStepVars.append(objects.size(), operand); - } else if (std::get>( - clause.t)) { - mlir::Location currentLocation = converter.getCurrentLocation(); - TODO(currentLocation, "Linear modifiers not yet implemented"); } else { // If nothing is present, add the default step of 1. fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); @@ -1525,9 +1537,44 @@ bool ClauseProcessor::processLinear(mlir::omp::LinearClauseOps &result) const { firOpBuilder.createIntegerConstant(currentLocation, integerTy, 1); result.linearStepVars.append(objects.size(), operand); } + + // Determine the linear modifier: + // 1. Use explicit modifier if provided. + // 2. For OpenMP >= 5.2 (Section 5.4.6: "the default linear-modifier + // is val"): + // - declare simd: "ref" for POINTER or non-VALUE dummy args, + // "val" otherwise. + // - do/simd: always "val". + // 3. Otherwise, leave unset (UnitAttr placeholder). + auto getDeclareSimdDefaultMod = [](const semantics::Symbol &sym) { + const auto &ultimate = sym.GetUltimate(); + if (semantics::IsPointer(ultimate)) + return mlir::omp::LinearModifier::ref; + if (const auto *obj = + ultimate.detailsIf()) + if (obj->isDummy() && !semantics::IsValue(ultimate)) + return mlir::omp::LinearModifier::ref; + return mlir::omp::LinearModifier::val; + }; + + std::optional linearMod; + if (explicitLinearMod) + linearMod = *explicitLinearMod; + else if (semaCtx.langOptions().OpenMPVersion >= 52) + linearMod = isDeclareSimd ? getDeclareSimdDefaultMod(*sym) + : mlir::omp::LinearModifier::val; + + if (linearMod) + linearModAttrs.push_back(mlir::omp::LinearModifierAttr::get( + &converter.getMLIRContext(), *linearMod)); + else + linearModAttrs.push_back( + mlir::UnitAttr::get(&converter.getMLIRContext())); } result.linearVarTypes = mlir::ArrayAttr::get(&converter.getMLIRContext(), typeAttrs); + result.linearModifiers = + mlir::ArrayAttr::get(&converter.getMLIRContext(), linearModAttrs); }); } diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.h b/flang/lib/Lower/OpenMP/ClauseProcessor.h index 52e69c179687..6c6056aac77e 100644 --- a/flang/lib/Lower/OpenMP/ClauseProcessor.h +++ b/flang/lib/Lower/OpenMP/ClauseProcessor.h @@ -143,7 +143,8 @@ public: bool processIsDevicePtr( lower::StatementContext &stmtCtx, mlir::omp::IsDevicePtrClauseOps &result, llvm::SmallVectorImpl &isDeviceSyms) const; - bool processLinear(mlir::omp::LinearClauseOps &result) const; + bool processLinear(mlir::omp::LinearClauseOps &result, + bool isDeclareSimd = false) const; bool processLink(llvm::SmallVectorImpl &result) const; diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp index 77aced25f9f5..4d135019ea70 100644 --- a/flang/lib/Lower/OpenMP/OpenMP.cpp +++ b/flang/lib/Lower/OpenMP/OpenMP.cpp @@ -1653,6 +1653,7 @@ static void genSimdClauses( // linear semantics on IV. Process the same here. static void genSimdImplicitLinear(lower::AbstractConverter &converter, + semantics::SemanticsContext &semaCtx, mlir::omp::SimdOperands &clauseOps, mlir::omp::LoopNestOperands loopNestClauseOps, llvm::SmallVector iv) { @@ -1671,11 +1672,15 @@ genSimdImplicitLinear(lower::AbstractConverter &converter, } std::vector typeAttrs; + std::vector linearModAttrs; // If attributes from explicit `linear(...)` clause are present, // carry them forward. if (clauseOps.linearVarTypes && !clauseOps.linearVarTypes.empty()) typeAttrs.assign(clauseOps.linearVarTypes.begin(), clauseOps.linearVarTypes.end()); + if (clauseOps.linearModifiers && !clauseOps.linearModifiers.empty()) + linearModAttrs.assign(clauseOps.linearModifiers.begin(), + clauseOps.linearModifiers.end()); for (auto [loopVar, loopStep] : llvm::zip(iv, loopNestClauseOps.loopSteps)) { const mlir::Value variable = converter.getSymbolAddress(*loopVar); @@ -1696,13 +1701,22 @@ genSimdImplicitLinear(lower::AbstractConverter &converter, Fortran::semantics::IsAllocatableOrPointer(loopVar->GetUltimate()))) { mlir::Type ty = converter.genType(*loopVar); typeAttrs.push_back(mlir::TypeAttr::get(ty)); + if (semaCtx.langOptions().OpenMPVersion >= 52) + linearModAttrs.push_back(mlir::omp::LinearModifierAttr::get( + &converter.getMLIRContext(), mlir::omp::LinearModifier::val)); + else + linearModAttrs.push_back( + mlir::UnitAttr::get(&converter.getMLIRContext())); clauseOps.linearVars.push_back(variable); clauseOps.linearStepVars.push_back(loopStep); } } - if (!typeAttrs.empty()) + if (!typeAttrs.empty()) { clauseOps.linearVarTypes = mlir::ArrayAttr::get(&converter.getMLIRContext(), typeAttrs); + clauseOps.linearModifiers = + mlir::ArrayAttr::get(&converter.getMLIRContext(), linearModAttrs); + } } static void genSingleClauses(lower::AbstractConverter &converter, @@ -3233,7 +3247,8 @@ genStandaloneSimd(lower::AbstractConverter &converter, lower::SymMap &symTable, llvm::SmallVector iv; genLoopNestClauses(converter, semaCtx, eval, item->clauses, loc, loopNestClauseOps, iv); - genSimdImplicitLinear(converter, simdClauseOps, loopNestClauseOps, iv); + genSimdImplicitLinear(converter, semaCtx, simdClauseOps, loopNestClauseOps, + iv); EntryBlockArgs simdArgs; simdArgs.priv.syms = dsp.getDelayedPrivSymbols(); @@ -3412,7 +3427,8 @@ static mlir::omp::DistributeOp genCompositeDistributeParallelDoSimd( llvm::SmallVector iv; genLoopNestClauses(converter, semaCtx, eval, simdItem->clauses, loc, loopNestClauseOps, iv); - genSimdImplicitLinear(converter, simdClauseOps, loopNestClauseOps, iv); + genSimdImplicitLinear(converter, semaCtx, simdClauseOps, loopNestClauseOps, + iv); // Operation creation. EntryBlockArgs distributeArgs; @@ -3484,7 +3500,8 @@ static mlir::omp::DistributeOp genCompositeDistributeSimd( llvm::SmallVector iv; genLoopNestClauses(converter, semaCtx, eval, simdItem->clauses, loc, loopNestClauseOps, iv); - genSimdImplicitLinear(converter, simdClauseOps, loopNestClauseOps, iv); + genSimdImplicitLinear(converter, semaCtx, simdClauseOps, loopNestClauseOps, + iv); // Operation creation. EntryBlockArgs distributeArgs; @@ -3547,7 +3564,8 @@ static mlir::omp::WsloopOp genCompositeDoSimd( llvm::SmallVector iv; genLoopNestClauses(converter, semaCtx, eval, simdItem->clauses, loc, loopNestClauseOps, iv); - genSimdImplicitLinear(converter, simdClauseOps, loopNestClauseOps, iv); + genSimdImplicitLinear(converter, semaCtx, simdClauseOps, loopNestClauseOps, + iv); // Operation creation. EntryBlockArgs wsloopArgs; @@ -4032,7 +4050,7 @@ genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable, ClauseProcessor cp(converter, semaCtx, clauses); cp.processAligned(clauseOps); cp.processInbranch(clauseOps); - cp.processLinear(clauseOps); + cp.processLinear(clauseOps, /*isDeclareSimd=*/true); cp.processNotinbranch(clauseOps); cp.processSimdlen(clauseOps); cp.processUniform(clauseOps); diff --git a/flang/test/Lower/OpenMP/composite_simd_linear.f90 b/flang/test/Lower/OpenMP/composite_simd_linear.f90 index 469b341c4665..ff290fc3dc4a 100644 --- a/flang/test/Lower/OpenMP/composite_simd_linear.f90 +++ b/flang/test/Lower/OpenMP/composite_simd_linear.f90 @@ -1,4 +1,5 @@ -! RUN: %flang_fc1 -fopenmp -emit-hlfir %s -o - 2>&1 | FileCheck %s +! RUN: %flang_fc1 -fopenmp -emit-hlfir %s -o - 2>&1 | FileCheck %s --check-prefixes=CHECK,DEFAULT +! RUN: %flang_fc1 -fopenmp -fopenmp-version=52 -emit-hlfir %s -o - 2>&1 | FileCheck %s --check-prefixes=CHECK,OPENMP52 subroutine do_simd @@ -8,7 +9,8 @@ subroutine do_simd !CHECK: %{{.*}} = arith.constant 1 : i32 !CHECK: %[[IV_STEP:.*]] = arith.constant 1 : i32 !CHECK: omp.wsloop { -!CHECK: omp.simd linear(%[[X]]#0 : !fir.ref = %[[CONST]] : i32, %[[I]]#0 : !fir.ref = %[[IV_STEP]] : i32) { +!DEFAULT: omp.simd linear(%[[X]]#0 : !fir.ref = %[[CONST]] : i32, %[[I]]#0 : !fir.ref = %[[IV_STEP]] : i32) { +!OPENMP52: omp.simd linear(val(%[[X]]#0 : !fir.ref = %[[CONST]] : i32), val(%[[I]]#0 : !fir.ref = %[[IV_STEP]] : i32)) { !CHECK: } !CHECK: } {linear_var_types = [i32, i32], omp.composite} !CHECK: } {omp.composite} @@ -24,7 +26,8 @@ subroutine distribute_simd !CHECK: %[[I:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFdistribute_simdEi"} : (!fir.ref) -> (!fir.ref, !fir.ref) !CHECK: omp.teams { !CHECK: omp.distribute { -!CHECK: omp.simd linear(%[[I]]#0 : !fir.ref = %c1_i32 : i32) { +!DEFAULT: omp.simd linear({{.*}}) { +!OPENMP52: omp.simd linear(val({{.*}})) { !CHECK: } {linear_var_types = [i32], omp.composite} !CHECK: } {omp.composite} integer :: i @@ -45,7 +48,8 @@ subroutine distribute_parallel_do !CHECK: %[[CONST]] = arith.constant 1 : i32 !CHECK: omp.distribute { !CHECK: omp.wsloop { -!CHECK: omp.simd linear(%[[I]]#0 : !fir.ref = %[[CONST]] : i32) { +!DEFAULT: omp.simd linear(%[[I]]#0 : !fir.ref = %[[CONST]] : i32) { +!OPENMP52: omp.simd linear(val(%[[I]]#0 : !fir.ref = %[[CONST]] : i32)) { !$omp teams !$omp distribute parallel do simd linear(i:1) do i = 1, N @@ -63,7 +67,8 @@ subroutine parallel_do !CHECK: %{{.*}} = arith.constant 1 : i32 !CHECK: %[[IV_STEP:.*]] = arith.constant 1 : i32 !CHECK: omp.wsloop { -!CHECK: omp.simd linear(%[[X]]#0 : !fir.ref = %[[LINEAR_STEP]] : i32, %[[I]]#0 : !fir.ref = %[[IV_STEP]] : i32) { +!DEFAULT: omp.simd linear(%[[X]]#0 : !fir.ref = %[[LINEAR_STEP]] : i32, %[[I]]#0 : !fir.ref = %[[IV_STEP]] : i32) { +!OPENMP52: omp.simd linear(val(%[[X]]#0 : !fir.ref = %[[LINEAR_STEP]] : i32), val(%[[I]]#0 : !fir.ref = %[[IV_STEP]] : i32)) { integer :: x !$omp parallel do simd linear(x:2) do i = 1, N @@ -80,7 +85,8 @@ subroutine teams_distribute !CHECK: {{.*}} = arith.constant 1 : i32 !CHECK: %[[IV_STEP:.*]] = arith.constant 1 : i32 !CHECK: omp.distribute { -!CHECK: omp.simd linear(%[[X]]#0 : !fir.ref = %[[LINEAR_STEP]] : i32, %[[I]]#0 : !fir.ref = %[[IV_STEP]] : i32) { +!DEFAULT: omp.simd linear(%[[X]]#0 : !fir.ref = %[[LINEAR_STEP]] : i32, %[[I]]#0 : !fir.ref = %[[IV_STEP]] : i32) { +!OPENMP52: omp.simd linear(val(%[[X]]#0 : !fir.ref = %[[LINEAR_STEP]] : i32), val(%[[I]]#0 : !fir.ref = %[[IV_STEP]] : i32)) { integer :: x !$omp teams distribute simd linear(x) do i = 1, N @@ -99,7 +105,8 @@ subroutine teams_distribute_parallel_do !CHECK: %[[IV_STEP:.*]] = arith.constant 1 : i32 !CHECK: omp.distribute { !CHECK: omp.wsloop { -!CHECK: omp.simd linear(%[[X]]#0 : !fir.ref = %c1_i32 : i32, %[[I]]#0 : !fir.ref = %c1_i32_1 : i32) { +!DEFAULT: omp.simd linear(%[[X]]#0 : !fir.ref = %c1_i32 : i32, %[[I]]#0 : !fir.ref = %c1_i32_1 : i32) { +!OPENMP52: omp.simd linear(val(%[[X]]#0 : !fir.ref = %c1_i32 : i32), val(%[[I]]#0 : !fir.ref = %c1_i32_1 : i32)) { integer :: x !$omp teams distribute parallel do simd linear(x) do i = 1, N diff --git a/flang/test/Lower/OpenMP/declare-simd.f90 b/flang/test/Lower/OpenMP/declare-simd.f90 index 5b578b436817..b2c4592ad4e8 100644 --- a/flang/test/Lower/OpenMP/declare-simd.f90 +++ b/flang/test/Lower/OpenMP/declare-simd.f90 @@ -57,7 +57,7 @@ end subroutine declare_simd_linear ! CHECK: %[[SCOPE:.*]] = fir.dummy_scope : !fir.dscope ! CHECK: %[[I:.*]]:2 = hlfir.declare %{{.*}} dummy_scope %[[SCOPE]] arg 4 {{.*}} : (!fir.ref, !fir.dscope) -> (!fir.ref, !fir.ref) ! CHECK: %[[C1:.*]] = arith.constant 1 : i32 -! CHECK: omp.declare_simd linear(%[[I]]#0 : !fir.ref = %[[C1]] : i32) {linear_var_types = [i32]}{{$}} +! CHECK: omp.declare_simd linear(ref(%[[I]]#0 : !fir.ref = %[[C1]] : i32)) {linear_var_types = [i32]}{{$}} ! CHECK: return subroutine declare_simd_simdlen(x, y, n, i) @@ -157,9 +157,62 @@ end subroutine declare_simd_combined ! CHECK-SAME: aligned(%[[X_DECL]]#0 : !fir.ref>>> -> 64 : i64, ! CHECK-SAME: %[[Y_DECL]]#0 : !fir.ref>>> -> 64 : i64) ! CHECK-SAME: inbranch -! CHECK-SAME: linear(%[[I_DECL]]#0 : !fir.ref = %[[C1]] : i32) +! CHECK-SAME: linear(ref(%[[I_DECL]]#0 : !fir.ref = %[[C1]] : i32)) ! CHECK-SAME: simdlen(8) ! CHECK-SAME: uniform(%[[X_DECL]]#0 : !fir.ref>>>, ! CHECK-SAME: %[[Y_DECL]]#0 : !fir.ref>>>) ! CHECK-SAME: {linear_var_types = [i32]}{{$}} ! CHECK: return + +subroutine declare_simd_linear_val(a, b) +#ifdef OMP_60 +!$omp declare_simd linear(a : val, step(2)) linear(b : val) +#else +!$omp declare simd linear(a : val, step(2)) linear(b : val) +#endif + integer, intent(in) :: a, b +end subroutine declare_simd_linear_val + +! CHECK-LABEL: func.func @_QPdeclare_simd_linear_val( +! CHECK: %[[SCOPE:.*]] = fir.dummy_scope : !fir.dscope +! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}} dummy_scope %[[SCOPE]] arg 1 {{.*}} : (!fir.ref, !fir.dscope) -> (!fir.ref, !fir.ref) +! CHECK: %[[B:.*]]:2 = hlfir.declare %{{.*}} dummy_scope %[[SCOPE]] arg 2 {{.*}} : (!fir.ref, !fir.dscope) -> (!fir.ref, !fir.ref) +! CHECK: %[[C2:.*]] = arith.constant 2 : i32 +! CHECK: %[[C1:.*]] = arith.constant 1 : i32 +! CHECK: omp.declare_simd linear(val(%[[A]]#0 : !fir.ref = %[[C2]] : i32), val(%[[B]]#0 : !fir.ref = %[[C1]] : i32)) +! CHECK-SAME: {linear_var_types = [i32, i32]}{{$}} +! CHECK: return + +subroutine declare_simd_linear_ref(x) +#ifdef OMP_60 +!$omp declare_simd linear(x : ref, step(4)) +#else +!$omp declare simd linear(x : ref, step(4)) +#endif + integer, allocatable, intent(inout) :: x +end subroutine declare_simd_linear_ref + +! CHECK-LABEL: func.func @_QPdeclare_simd_linear_ref( +! CHECK: %[[SCOPE:.*]] = fir.dummy_scope : !fir.dscope +! CHECK: %[[X:.*]]:2 = hlfir.declare %{{.*}} dummy_scope %[[SCOPE]] arg 1 {{.*}} : (!fir.ref>>, !fir.dscope) -> (!fir.ref>>, !fir.ref>>) +! CHECK: %[[C4:.*]] = arith.constant 4 : i32 +! CHECK: omp.declare_simd linear(ref(%[[X]]#0 : !fir.ref>> = %[[C4]] : i32)) +! CHECK-SAME: {linear_var_types = [!fir.box>]}{{$}} +! CHECK: return + +subroutine declare_simd_linear_uval(y) +#ifdef OMP_60 +!$omp declare_simd linear(y : uval) +#else +!$omp declare simd linear(y : uval) +#endif + integer, intent(in) :: y +end subroutine declare_simd_linear_uval + +! CHECK-LABEL: func.func @_QPdeclare_simd_linear_uval( +! CHECK: %[[SCOPE:.*]] = fir.dummy_scope : !fir.dscope +! CHECK: %[[Y:.*]]:2 = hlfir.declare %{{.*}} dummy_scope %[[SCOPE]] arg 1 {{.*}} : (!fir.ref, !fir.dscope) -> (!fir.ref, !fir.ref) +! CHECK: %[[C1:.*]] = arith.constant 1 : i32 +! CHECK: omp.declare_simd linear(uval(%[[Y]]#0 : !fir.ref = %[[C1]] : i32)) +! CHECK-SAME: {linear_var_types = [i32]}{{$}} +! CHECK: return diff --git a/flang/test/Lower/OpenMP/distribute-parallel-do-simd.f90 b/flang/test/Lower/OpenMP/distribute-parallel-do-simd.f90 index 7f91177cc331..2543e9470452 100644 --- a/flang/test/Lower/OpenMP/distribute-parallel-do-simd.f90 +++ b/flang/test/Lower/OpenMP/distribute-parallel-do-simd.f90 @@ -1,8 +1,10 @@ ! This test checks lowering of OpenMP DISTRIBUTE PARALLEL DO SIMD composite ! constructs. -! RUN: bbc -fopenmp -emit-hlfir %s -o - | FileCheck %s -! RUN: %flang_fc1 -fopenmp -emit-hlfir %s -o - | FileCheck %s +! RUN: bbc -fopenmp -emit-hlfir %s -o - | FileCheck %s --check-prefixes=CHECK,DEFAULT +! RUN: %flang_fc1 -fopenmp -emit-hlfir %s -o - | FileCheck %s --check-prefixes=CHECK,DEFAULT +! RUN: bbc -fopenmp -fopenmp-version=52 -emit-hlfir %s -o - | FileCheck %s --check-prefixes=CHECK,OPENMP52 +! RUN: %flang_fc1 -fopenmp -fopenmp-version=52 -emit-hlfir %s -o - | FileCheck %s --check-prefixes=CHECK,OPENMP52 ! CHECK-LABEL: func.func @_QPdistribute_parallel_do_simd_num_threads( subroutine distribute_parallel_do_simd_num_threads() @@ -11,7 +13,8 @@ subroutine distribute_parallel_do_simd_num_threads() ! CHECK: omp.parallel num_threads({{.*}}) { ! CHECK: omp.distribute { ! CHECK-NEXT: omp.wsloop { - ! CHECK-NEXT: omp.simd linear({{.*}}) { + ! DEFAULT-NEXT: omp.simd linear({{.*}}) { + ! OPENMP52-NEXT: omp.simd linear(val({{.*}})) { ! CHECK-NEXT: omp.loop_nest !$omp distribute parallel do simd num_threads(10) do index_ = 1, 10 @@ -28,7 +31,8 @@ subroutine distribute_parallel_do_simd_dist_schedule() ! CHECK: omp.parallel { ! CHECK: omp.distribute dist_schedule_static dist_schedule_chunk_size({{.*}}) { ! CHECK-NEXT: omp.wsloop { - ! CHECK-NEXT: omp.simd linear({{.*}}) { + ! DEFAULT-NEXT: omp.simd linear({{.*}}) { + ! OPENMP52-NEXT: omp.simd linear(val({{.*}})) { ! CHECK-NEXT: omp.loop_nest !$omp distribute parallel do simd dist_schedule(static, 4) do index_ = 1, 10 @@ -45,7 +49,8 @@ subroutine distribute_parallel_do_simd_schedule() ! CHECK: omp.parallel { ! CHECK: omp.distribute { ! CHECK-NEXT: omp.wsloop schedule(static = {{.*}}) { - ! CHECK-NEXT: omp.simd linear({{.*}}) { + ! DEFAULT-NEXT: omp.simd linear({{.*}}) { + ! OPENMP52-NEXT: omp.simd linear(val({{.*}})) { ! CHECK-NEXT: omp.loop_nest !$omp distribute parallel do simd schedule(static, 4) do index_ = 1, 10 @@ -62,7 +67,8 @@ subroutine distribute_parallel_do_simd_simdlen() ! CHECK: omp.parallel { ! CHECK: omp.distribute { ! CHECK-NEXT: omp.wsloop { - ! CHECK-NEXT: omp.simd linear({{.*}}) simdlen(4) { + ! DEFAULT-NEXT: omp.simd linear({{.*}}) simdlen(4) { + ! OPENMP52-NEXT: omp.simd linear(val({{.*}})) simdlen(4) { ! CHECK-NEXT: omp.loop_nest !$omp distribute parallel do simd simdlen(4) do index_ = 1, 10 @@ -86,8 +92,10 @@ subroutine distribute_parallel_do_simd_private() ! CHECK: omp.parallel { ! CHECK: omp.distribute { ! CHECK-NEXT: omp.wsloop { - ! CHECK-NEXT: omp.simd linear(%{{.*}}) private(@{{.*}} %[[X]]#0 -> %[[X_ARG:[^:]+]] - ! CHECK-SAME: : !fir.ref) { + ! DEFAULT-NEXT: omp.simd linear(%{{.*}}) private(@{{.*}} %[[X]]#0 -> %[[X_ARG:[^:]+]] + ! DEFAULT-SAME: : !fir.ref) { + ! OPENMP52-NEXT: omp.simd linear(val(%{{.*}})) private(@{{.*}} %[[X]]#0 -> %[[X_ARG:[^:]+]] + ! OPENMP52-SAME: : !fir.ref) { ! CHECK-NEXT: omp.loop_nest ! CHECK: %[[X_PRIV:.*]]:2 = hlfir.declare %[[X_ARG]] !$omp distribute parallel do simd private(x) diff --git a/flang/test/Lower/OpenMP/linear_modifier.f90 b/flang/test/Lower/OpenMP/linear_modifier.f90 new file mode 100644 index 000000000000..624fa170cc1b --- /dev/null +++ b/flang/test/Lower/OpenMP/linear_modifier.f90 @@ -0,0 +1,54 @@ +! This test checks lowering of OpenMP linear clause with implicit modifiers. + +! RUN: %flang_fc1 -fopenmp -fopenmp-version=52 -emit-hlfir %s -o - 2>&1 | FileCheck %s --check-prefixes=CHECK,OPENMP52 +! RUN: %flang_fc1 -fopenmp -fopenmp-version=45 -emit-hlfir %s -o - 2>&1 | FileCheck %s --check-prefixes=CHECK,OPENMP45 + +!CHECK: %[[X_alloca:.*]] = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFsimple_linearEx"} +!CHECK: %[[X:.*]]:2 = hlfir.declare %[[X_alloca]] {uniq_name = "_QFsimple_linearEx"} : (!fir.ref) -> (!fir.ref, !fir.ref) +!CHECK: %[[const:.*]] = arith.constant 1 : i32 +subroutine simple_linear + implicit none + integer :: x, y, i + !OPENMP52: omp.wsloop linear(val(%[[X]]#0 : !fir.ref = %[[const]] : i32)) {{.*}} + !OPENMP45: omp.wsloop linear(%[[X]]#0 : !fir.ref = %[[const]] : i32) {{.*}} + !$omp do linear(x) + do i = 1, 10 + y = x + 2 + end do + !$omp end do + !CHECK: } {linear_var_types = [i32]} +end subroutine + +subroutine linear_step +!CHECK: %[[X_alloca:.*]] = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFlinear_stepEx"} +!CHECK: %[[X:.*]]:2 = hlfir.declare %[[X_alloca]] {uniq_name = "_QFlinear_stepEx"} : (!fir.ref) -> (!fir.ref, !fir.ref) + implicit none + integer :: x, y, i + !CHECK: %[[const:.*]] = arith.constant 4 : i32 + !OPENMP52: omp.wsloop linear(val(%[[X]]#0 : !fir.ref = %[[const]] : i32)) {{.*}} + !OPENMP45: omp.wsloop linear(%[[X]]#0 : !fir.ref = %[[const]] : i32) {{.*}} + !$omp do linear(x:4) + do i = 1, 10 + y = x + 2 + end do + !$omp end do + !CHECK: } {linear_var_types = [i32]} +end subroutine + +subroutine do_simd_linear +!CHECK: %[[I:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFdo_simd_linearEi"} : (!fir.ref) -> (!fir.ref, !fir.ref) +!CHECK: %[[X:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFdo_simd_linearEx"} : (!fir.ref) -> (!fir.ref, !fir.ref) +!CHECK: %[[CONST:.*]] = arith.constant 1 : i32 +!CHECK: %{{.*}} = arith.constant 1 : i32 +!CHECK: %[[IV_STEP:.*]] = arith.constant 1 : i32 +!CHECK: omp.wsloop { +!OPENMP52: omp.simd linear(val(%[[X]]#0 : !fir.ref = %[[CONST]] : i32), val(%[[I]]#0 : !fir.ref = %[[IV_STEP]] : i32)) { +!OPENMP45: omp.simd linear(%[[X]]#0 : !fir.ref = %[[CONST]] : i32, %[[I]]#0 : !fir.ref = %[[IV_STEP]] : i32) { + integer :: x + !$omp do simd linear(x:1) + do i = 1, 10 + end do + !$omp end do simd +!CHECK: } {linear_var_types = [i32, i32], omp.composite} +!CHECK: } {omp.composite} +end subroutine do_simd_linear diff --git a/flang/test/Lower/OpenMP/simd-linear.f90 b/flang/test/Lower/OpenMP/simd-linear.f90 index b478cafb4fa1..db5425e153a6 100644 --- a/flang/test/Lower/OpenMP/simd-linear.f90 +++ b/flang/test/Lower/OpenMP/simd-linear.f90 @@ -16,7 +16,7 @@ subroutine simple_linear implicit none integer :: x, y, i - !CHECK: omp.simd linear(%[[X]]#0 : !fir.ref = %[[const]] : i32) {{.*}} + !CHECK: omp.simd linear(val(%[[X]]#0 : !fir.ref = %[[const]] : i32)) {{.*}} !IMPLICIT: omp.simd linear(%[[X]]#0 : !fir.ref = %[[const]] : i32, %[[I]]#0 : !fir.ref = %{{.*}} : i32) {{.*}} !$omp simd linear(x) @@ -39,7 +39,7 @@ subroutine linear_step implicit none integer :: x, y, i !CHECK: %[[const:.*]] = arith.constant 4 : i32 - !CHECK: omp.simd linear(%[[X]]#0 : !fir.ref = %[[const]] : i32) {{.*}} + !CHECK: omp.simd linear(val(%[[X]]#0 : !fir.ref = %[[const]] : i32)) {{.*}} !IMPLICIT: omp.simd linear(%[[X]]#0 : !fir.ref = %[[const]] : i32, %[[I]]#0 : !fir.ref = %{{.*}} : i32) {{.*}} !$omp simd linear(x:4) @@ -71,7 +71,7 @@ subroutine linear_expr !IMPLICIT: %[[const:.*]] = arith.constant 4 : i32 !IMPLICIT: %[[LINEAR_EXPR:.*]] = arith.addi %[[LOAD_A]], %[[const]] : i32 - !CHECK: omp.simd linear(%[[X]]#0 : !fir.ref = %[[LINEAR_EXPR]] : i32) {{.*}} + !CHECK: omp.simd linear(val(%[[X]]#0 : !fir.ref = %[[LINEAR_EXPR]] : i32)) {{.*}} !IMPLICIT: omp.simd linear(%[[X]]#0 : !fir.ref = %[[LINEAR_EXPR]] : i32, %[[I]]#0 : !fir.ref = {{.*}} : i32) {{.*}} !$omp simd linear(x:a+4) @@ -96,11 +96,11 @@ subroutine non_i32_type !IMPLICIT: %[[STEP_VAR_CONST:.*]] = arith.constant 1 : i32 integer(kind=8)::j=0 - !CHECK: omp.simd linear(%[[J_DECLARE]]#0 : !fir.ref = %[[CONST]] : i64) {{.*}} + !CHECK: omp.simd linear(val(%[[J_DECLARE]]#0 : !fir.ref = %[[CONST]] : i64)) {{.*}} !IMPLICIT: omp.simd linear(%[[J_DECLARE]]#0 : !fir.ref = %[[CONST]] : i64, %[[I_DECLARE]]#0 : !fir.ref = %[[STEP_VAR_CONST]] : i32) !$omp simd linear(j:1_8) do i = 1,10 - end do + end do !$omp end simd !CHECK: } {linear_var_types = [i64]} !IMPLICIT: } {linear_var_types = [i64, i32]} diff --git a/flang/test/Lower/OpenMP/wsloop-linear.f90 b/flang/test/Lower/OpenMP/wsloop-linear.f90 index 67588e24c4c5..78aede0cbfa1 100644 --- a/flang/test/Lower/OpenMP/wsloop-linear.f90 +++ b/flang/test/Lower/OpenMP/wsloop-linear.f90 @@ -1,7 +1,8 @@ ! This test checks lowering of OpenMP DO Directive (Worksharing) ! with linear clause -! RUN: %flang_fc1 -fopenmp -emit-hlfir %s -o - 2>&1 | FileCheck %s +! RUN: %flang_fc1 -fopenmp -emit-hlfir %s -o - 2>&1 | FileCheck %s --check-prefixes=CHECK,DEFAULT +! RUN: %flang_fc1 -fopenmp -fopenmp-version=52 -emit-hlfir %s -o - 2>&1 | FileCheck %s --check-prefixes=CHECK,OPENMP52 !CHECK: %[[X_alloca:.*]] = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFsimple_linearEx"} !CHECK: %[[X:.*]]:2 = hlfir.declare %[[X_alloca]] {uniq_name = "_QFsimple_linearEx"} : (!fir.ref) -> (!fir.ref, !fir.ref) @@ -9,7 +10,8 @@ subroutine simple_linear implicit none integer :: x, y, i - !CHECK: omp.wsloop linear(%[[X]]#0 : !fir.ref = %[[const]] : i32) {{.*}} + !DEFAULT: omp.wsloop linear(%[[X]]#0 : !fir.ref = %[[const]] : i32) {{.*}} + !OPENMP52: omp.wsloop linear(val(%[[X]]#0 : !fir.ref = %[[const]] : i32)) {{.*}} !$omp do linear(x) !CHECK: %[[LOAD:.*]] = fir.load %[[X]]#0 : !fir.ref !CHECK: %[[const:.*]] = arith.constant 2 : i32 @@ -28,7 +30,8 @@ subroutine linear_step implicit none integer :: x, y, i !CHECK: %[[const:.*]] = arith.constant 4 : i32 - !CHECK: omp.wsloop linear(%[[X]]#0 : !fir.ref = %[[const]] : i32) {{.*}} + !DEFAULT: omp.wsloop linear(%[[X]]#0 : !fir.ref = %[[const]] : i32) {{.*}} + !OPENMP52: omp.wsloop linear(val(%[[X]]#0 : !fir.ref = %[[const]] : i32)) {{.*}} !$omp do linear(x:4) !CHECK: %[[LOAD:.*]] = fir.load %[[X]]#0 : !fir.ref !CHECK: %[[const:.*]] = arith.constant 2 : i32 @@ -50,7 +53,8 @@ subroutine linear_expr !CHECK: %[[LOAD_A:.*]] = fir.load %[[A]]#0 : !fir.ref !CHECK: %[[const:.*]] = arith.constant 4 : i32 !CHECK: %[[LINEAR_EXPR:.*]] = arith.addi %[[LOAD_A]], %[[const]] : i32 - !CHECK: omp.wsloop linear(%[[X]]#0 : !fir.ref = %[[LINEAR_EXPR]] : i32) {{.*}} + !DEFAULT: omp.wsloop linear(%[[X]]#0 : !fir.ref = %[[LINEAR_EXPR]] : i32) {{.*}} + !OPENMP52: omp.wsloop linear(val(%[[X]]#0 : !fir.ref = %[[LINEAR_EXPR]] : i32)) {{.*}} !$omp do linear(x:a+4) do i = 1, 10 y = x + 2 diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPClauses.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPClauses.td index 23c2fbdfd736..b5a047dc613f 100644 --- a/mlir/include/mlir/Dialect/OpenMP/OpenMPClauses.td +++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPClauses.td @@ -773,12 +773,14 @@ class OpenMP_LinearClauseSkip< extraClassDeclaration> { let arguments = (ins Variadic:$linear_vars, Variadic:$linear_step_vars, - OptionalAttr:$linear_var_types); + OptionalAttr:$linear_var_types, + OptionalAttr:$linear_modifiers); let optAssemblyFormat = [{ `linear` `(` custom($linear_vars, type($linear_vars), - $linear_step_vars, type($linear_step_vars)) `)` + $linear_step_vars, type($linear_step_vars), + $linear_modifiers) `)` }]; let description = [{ @@ -786,6 +788,10 @@ class OpenMP_LinearClauseSkip< associated linear operand. Note that the `linear_vars` and `linear_step_vars` variadic lists should contain the same number of elements. + + The `linear_modifiers` attribute optionally specifies a per-variable + linear-modifier: `val`, `ref`, or `uval`. When omitted, the default + modifier is determined by the language semantics. }]; } diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPEnums.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPEnums.td index 707850cbb47b..06c5e0b89ff0 100644 --- a/mlir/include/mlir/Dialect/OpenMP/OpenMPEnums.td +++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPEnums.td @@ -237,6 +237,22 @@ def OrderModifier def OrderModifierAttr : EnumAttr; +//===----------------------------------------------------------------------===// +// linear_modifier enum. +//===----------------------------------------------------------------------===// + +def LinearModifierVal : I32EnumAttrCase<"val", 0>; +def LinearModifierRef : I32EnumAttrCase<"ref", 1>; +def LinearModifierUval : I32EnumAttrCase<"uval", 2>; + +def LinearModifier : OpenMP_I32EnumAttr<"LinearModifier", "linear modifier", + [LinearModifierVal, LinearModifierRef, + LinearModifierUval]>; + +def LinearModifierAttr : OpenMP_EnumAttr { + let assemblyFormat = "`(` $value `)`"; +} + //===----------------------------------------------------------------------===// // reduction_modifier enum. //===----------------------------------------------------------------------===// diff --git a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp index 7cab929d583c..04418ee39be5 100644 --- a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp +++ b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp @@ -420,43 +420,114 @@ static void printClauseAttr(OpAsmPrinter &p, Operation *op, ClauseAttr attr) { /// linear ::= `linear` `(` linear-list `)` /// linear-list := linear-val | linear-val linear-list /// linear-val := ssa-id-and-type `=` ssa-id-and-type +/// | `val` `(` ssa-id-and-type `=` ssa-id-and-type `)` +/// | `ref` `(` ssa-id-and-type `=` ssa-id-and-type `)` +/// | `uval` `(` ssa-id-and-type `=` ssa-id-and-type `)` static ParseResult parseLinearClause( OpAsmParser &parser, SmallVectorImpl &linearVars, SmallVectorImpl &linearTypes, SmallVectorImpl &linearStepVars, - SmallVectorImpl &linearStepTypes) { - return parser.parseCommaSeparatedList([&]() { + SmallVectorImpl &linearStepTypes, ArrayAttr &linearModifiers) { + SmallVector modifiers; + auto result = parser.parseCommaSeparatedList([&]() { OpAsmParser::UnresolvedOperand var; Type type, stepType; OpAsmParser::UnresolvedOperand stepVar; + + std::optional linearModifier; + if (succeeded(parser.parseOptionalKeyword("val"))) { + linearModifier = omp::LinearModifier::val; + } else if (succeeded(parser.parseOptionalKeyword("ref"))) { + linearModifier = omp::LinearModifier::ref; + } else if (succeeded(parser.parseOptionalKeyword("uval"))) { + linearModifier = omp::LinearModifier::uval; + } + + bool hasLinearModifierParens = linearModifier.has_value(); + if (hasLinearModifierParens && parser.parseLParen()) + return failure(); + if (parser.parseOperand(var) || parser.parseColonType(type) || parser.parseEqual() || parser.parseOperand(stepVar) || parser.parseColonType(stepType)) return failure(); + if (hasLinearModifierParens && parser.parseRParen()) + return failure(); + linearVars.push_back(var); linearTypes.push_back(type); linearStepVars.push_back(stepVar); linearStepTypes.push_back(stepType); + if (linearModifier) { + modifiers.push_back( + omp::LinearModifierAttr::get(parser.getContext(), *linearModifier)); + } else { + modifiers.push_back(UnitAttr::get(parser.getContext())); + } return success(); }); + if (failed(result)) + return failure(); + linearModifiers = ArrayAttr::get(parser.getContext(), modifiers); + return success(); } /// Print Linear Clause static void printLinearClause(OpAsmPrinter &p, Operation *op, ValueRange linearVars, TypeRange linearTypes, - ValueRange linearStepVars, - TypeRange stepVarTypes) { + ValueRange linearStepVars, TypeRange stepVarTypes, + ArrayAttr linearModifiers) { size_t linearVarsSize = linearVars.size(); for (unsigned i = 0; i < linearVarsSize; ++i) { - std::string separator = i == linearVarsSize - 1 ? "" : ", "; + if (i != 0) + p << ", "; + // Print modifier keyword wrapper if present. + Attribute modAttr = linearModifiers ? linearModifiers[i] : nullptr; + auto mod = modAttr ? dyn_cast(modAttr) : nullptr; + if (mod) { + p << omp::stringifyLinearModifier(mod.getValue()) << "("; + } p << linearVars[i] << " : " << linearTypes[i]; p << " = " << linearStepVars[i] << " : " << stepVarTypes[i]; - p << separator; + if (mod) + p << ")"; } } +//===----------------------------------------------------------------------===// +// Verifier for Linear modifier +//===----------------------------------------------------------------------===// + +/// OpenMP 5.2, Section 5.4.6: "A linear-modifier may be specified as ref or +/// uval only on a declare simd directive." +/// Also verifies that modifier count matches variable count. +static LogicalResult +verifyLinearModifiers(Operation *op, std::optional linearModifiers, + OperandRange linearVars, bool isDeclareSimd = false) { + if (!linearModifiers) + return success(); + if (linearModifiers->size() != linearVars.size()) + return op->emitOpError() + << "expected as many linear modifiers as linear variables"; + if (!isDeclareSimd) { + for (Attribute attr : *linearModifiers) { + if (!attr) + continue; + auto modAttr = dyn_cast(attr); + if (!modAttr) + continue; + omp::LinearModifier mod = modAttr.getValue(); + if (mod == omp::LinearModifier::ref || mod == omp::LinearModifier::uval) + return op->emitOpError() + << "linear modifier '" << omp::stringifyLinearModifier(mod) + << "' may only be specified on a declare simd directive"; + } + } + return success(); +} + //===----------------------------------------------------------------------===// // Verifier for Nontemporal Clause //===----------------------------------------------------------------------===// @@ -2897,7 +2968,7 @@ void WsloopOp::build(OpBuilder &builder, OperationState &state, ArrayRef attributes) { build(builder, state, /*allocate_vars=*/{}, /*allocator_vars=*/{}, /*linear_vars=*/ValueRange(), /*linear_step_vars=*/ValueRange(), - /*linear_var_types*/ nullptr, + /*linear_var_types*/ nullptr, /*linear_modifiers=*/nullptr, /*nowait=*/false, /*order=*/nullptr, /*order_mod=*/nullptr, /*ordered=*/nullptr, /*private_vars=*/{}, /*private_syms=*/nullptr, /*private_needs_barrier=*/false, @@ -2916,16 +2987,19 @@ void WsloopOp::build(OpBuilder &builder, OperationState &state, WsloopOp::build( builder, state, /*allocate_vars=*/{}, /*allocator_vars=*/{}, clauses.linearVars, - clauses.linearStepVars, clauses.linearVarTypes, clauses.nowait, - clauses.order, clauses.orderMod, clauses.ordered, clauses.privateVars, - makeArrayAttr(ctx, clauses.privateSyms), clauses.privateNeedsBarrier, - clauses.reductionMod, clauses.reductionVars, + clauses.linearStepVars, clauses.linearVarTypes, clauses.linearModifiers, + clauses.nowait, clauses.order, clauses.orderMod, clauses.ordered, + clauses.privateVars, makeArrayAttr(ctx, clauses.privateSyms), + clauses.privateNeedsBarrier, clauses.reductionMod, clauses.reductionVars, makeDenseBoolArrayAttr(ctx, clauses.reductionByref), makeArrayAttr(ctx, clauses.reductionSyms), clauses.scheduleKind, clauses.scheduleChunk, clauses.scheduleMod, clauses.scheduleSimd); } LogicalResult WsloopOp::verify() { + if (failed( + verifyLinearModifiers(*this, getLinearModifiers(), getLinearVars()))) + return failure(); if (getLinearVars().size() && getLinearVarTypes().value().size() != getLinearVars().size()) return emitError() << "Ill-formed type attributes for linear variables"; @@ -2965,16 +3039,17 @@ LogicalResult WsloopOp::verifyRegions() { void SimdOp::build(OpBuilder &builder, OperationState &state, const SimdOperands &clauses) { MLIRContext *ctx = builder.getContext(); - SimdOp::build( - builder, state, clauses.alignedVars, - makeArrayAttr(ctx, clauses.alignments), clauses.ifExpr, - clauses.linearVars, clauses.linearStepVars, clauses.linearVarTypes, - clauses.nontemporalVars, clauses.order, clauses.orderMod, - clauses.privateVars, makeArrayAttr(ctx, clauses.privateSyms), - clauses.privateNeedsBarrier, clauses.reductionMod, clauses.reductionVars, - makeDenseBoolArrayAttr(ctx, clauses.reductionByref), - makeArrayAttr(ctx, clauses.reductionSyms), clauses.safelen, - clauses.simdlen); + SimdOp::build(builder, state, clauses.alignedVars, + makeArrayAttr(ctx, clauses.alignments), clauses.ifExpr, + clauses.linearVars, clauses.linearStepVars, + clauses.linearVarTypes, clauses.linearModifiers, + clauses.nontemporalVars, clauses.order, clauses.orderMod, + clauses.privateVars, makeArrayAttr(ctx, clauses.privateSyms), + clauses.privateNeedsBarrier, clauses.reductionMod, + clauses.reductionVars, + makeDenseBoolArrayAttr(ctx, clauses.reductionByref), + makeArrayAttr(ctx, clauses.reductionSyms), clauses.safelen, + clauses.simdlen); } LogicalResult SimdOp::verify() { @@ -2990,6 +3065,10 @@ LogicalResult SimdOp::verify() { if (verifyNontemporalClause(*this, getNontemporalVars()).failed()) return failure(); + if (failed( + verifyLinearModifiers(*this, getLinearModifiers(), getLinearVars()))) + return failure(); + bool isCompositeChildLeaf = llvm::dyn_cast_if_present((*this)->getParentOp()); @@ -4628,6 +4707,10 @@ LogicalResult DeclareSimdOp::verify() { if (getInbranch() && getNotinbranch()) return emitOpError("cannot have both 'inbranch' and 'notinbranch'"); + if (failed(verifyLinearModifiers(*this, getLinearModifiers(), getLinearVars(), + /*isDeclareSimd=*/true))) + return failure(); + return verifyAlignedClause(*this, getAlignments(), getAlignedVars()); } @@ -4637,8 +4720,9 @@ void DeclareSimdOp::build(OpBuilder &odsBuilder, OperationState &odsState, DeclareSimdOp::build(odsBuilder, odsState, clauses.alignedVars, makeArrayAttr(ctx, clauses.alignments), clauses.inbranch, clauses.linearVars, clauses.linearStepVars, - clauses.linearVarTypes, clauses.notinbranch, - clauses.simdlen, clauses.uniformVars); + clauses.linearVarTypes, clauses.linearModifiers, + clauses.notinbranch, clauses.simdlen, + clauses.uniformVars); } //===----------------------------------------------------------------------===// diff --git a/mlir/test/Dialect/OpenMP/invalid.mlir b/mlir/test/Dialect/OpenMP/invalid.mlir index cbe18b9b882d..4879ea754bf7 100644 --- a/mlir/test/Dialect/OpenMP/invalid.mlir +++ b/mlir/test/Dialect/OpenMP/invalid.mlir @@ -3170,6 +3170,81 @@ func.func @omp_declare_simd_branch() -> () { // ----- +func.func @omp_wsloop_linear_ref(%lb : index, %ub : index, %step : index, + %data_var : memref, %linear_var : i32) { + // expected-error @+1 {{'omp.wsloop' op linear modifier 'ref' may only be specified on a declare simd directive}} + omp.wsloop linear(ref(%data_var : memref = %linear_var : i32)) { + omp.loop_nest (%iv) : index = (%lb) to (%ub) step (%step) { + omp.yield + } + } {linear_var_types = [i32]} + return +} + +// ----- + +func.func @omp_wsloop_linear_uval(%lb : index, %ub : index, %step : index, + %data_var : memref, %linear_var : i32) { + // expected-error @+1 {{'omp.wsloop' op linear modifier 'uval' may only be specified on a declare simd directive}} + omp.wsloop linear(uval(%data_var : memref = %linear_var : i32)) { + omp.loop_nest (%iv) : index = (%lb) to (%ub) step (%step) { + omp.yield + } + } {linear_var_types = [i32]} + return +} + +// ----- + +func.func @omp_simd_linear_ref(%lb : index, %ub : index, %step : index, + %data_var : memref, %linear_var : i32) { + // expected-error @+1 {{'omp.simd' op linear modifier 'ref' may only be specified on a declare simd directive}} + omp.simd linear(ref(%data_var : memref = %linear_var : i32)) { + omp.loop_nest (%iv) : index = (%lb) to (%ub) step (%step) { + omp.yield + } + } {linear_var_types = [i32]} + return +} + +// ----- + +func.func @omp_wsloop_linear_modifiers_mismatch(%lb : index, %ub : index, %step : index, + %data_var : memref, %linear_var : i32) { + // expected-error @below {{'omp.wsloop' op expected as many linear modifiers as linear variables}} + "omp.wsloop"(%data_var, %linear_var) ({ + omp.loop_nest (%iv) : index = (%lb) to (%ub) step (%step) { + omp.yield + } + }) {linear_modifiers = [#omp, #omp], + operandSegmentSizes = array} : (memref, i32) -> () + return +} + +// ----- + +func.func @omp_simd_linear_modifiers_mismatch(%lb : index, %ub : index, %step : index, + %data_var : memref, %linear_var : i32) { + // expected-error @below {{'omp.simd' op expected as many linear modifiers as linear variables}} + "omp.simd"(%data_var, %linear_var) ({ + omp.loop_nest (%iv) : index = (%lb) to (%ub) step (%step) { + omp.yield + } + }) {linear_modifiers = [#omp, #omp], + operandSegmentSizes = array} : (memref, i32) -> () + return +} + +// ----- + +func.func @omp_declare_simd_linear_modifiers_mismatch(%iv : i32, %step : i32) { + // expected-error @below {{'omp.declare_simd' op expected as many linear modifiers as linear variables}} + "omp.declare_simd"(%iv, %step) <{linear_modifiers = [#omp, #omp], operandSegmentSizes = array}> : (i32, i32) -> () + return +} + +// ----- + func.func @iterator_bad_result_type(%lb : index, %ub : index, %st : index) { // expected-error@+1 {{result #0 must be OpenMP iterator-produced list handle, but got 'index'}} %0 = omp.iterator(%i: index) = (%lb to %ub step %st) { diff --git a/mlir/test/Dialect/OpenMP/ops.mlir b/mlir/test/Dialect/OpenMP/ops.mlir index ba329cc67bb1..d924d479eba9 100644 --- a/mlir/test/Dialect/OpenMP/ops.mlir +++ b/mlir/test/Dialect/OpenMP/ops.mlir @@ -524,6 +524,15 @@ func.func @omp_wsloop_pretty(%lb : index, %ub : index, %step : index, %data_var } } { linear_var_types = [i32] } + // CHECK: omp.wsloop linear(val(%{{.*}} : memref = %{{.*}} : i32), val(%{{.*}} : memref = %{{.*}} : i32)) schedule(static) { + // CHECK-NEXT: omp.loop_nest + omp.wsloop schedule(static) linear(val(%data_var : memref = %linear_var : i32), + val(%data_var : memref = %linear_var : i32)) { + omp.loop_nest (%iv) : index = (%lb) to (%ub) step (%step) { + omp.yield + } + } { linear_var_types = [i32, i32] } + // CHECK: omp.wsloop linear(%{{.*}} : memref = %{{.*}} : i32) ordered(2) schedule(static = %{{.*}} : i32) { // CHECK-NEXT: omp.loop_nest omp.wsloop ordered(2) linear(%data_var : memref = %linear_var : i32) schedule(static = %chunk_var : i32) { @@ -744,6 +753,32 @@ func.func @omp_simd_pretty_order(%lb : index, %ub : index, %step : index) -> () return } +// CHECK-LABEL: omp_simd_linear_val +func.func @omp_simd_linear_val(%lb : index, %ub : index, %step : index, + %data_var : memref, %linear_var : i32) -> () { + // CHECK: omp.simd linear(val(%{{.*}} : memref = %{{.*}} : i32)) + omp.simd linear(val(%data_var : memref = %linear_var : i32)) { + omp.loop_nest (%iv) : index = (%lb) to (%ub) step (%step) { + omp.yield + } + } {linear_var_types = [i32]} + return +} + +// CHECK-LABEL: omp_simd_linear_val_multiple +func.func @omp_simd_linear_val_multiple(%lb : index, %ub : index, %step : index, + %d1 : memref, %d2 : memref, + %s1 : i32, %s2 : i32) -> () { + // CHECK: omp.simd linear(val(%{{.*}} : memref = %{{.*}} : i32), val(%{{.*}} : memref = %{{.*}} : i32)) + omp.simd linear(val(%d1 : memref = %s1 : i32), + val(%d2 : memref = %s2 : i32)) { + omp.loop_nest (%iv) : index = (%lb) to (%ub) step (%step) { + omp.yield + } + } {linear_var_types = [i32, i32]} + return +} + // CHECK-LABEL: omp_simd_pretty_simdlen func.func @omp_simd_pretty_simdlen(%lb : index, %ub : index, %step : index) -> () { // CHECK: omp.simd simdlen(2) @@ -3452,6 +3487,53 @@ func.func @omp_declare_simd_linear(%a: f64, %b: f64, %iv: i32, %step: i32) -> () return } +// CHECK-LABEL: func.func @omp_declare_simd_linear_val +func.func @omp_declare_simd_linear_val(%iv: i32, %step: i32) -> () { + // CHECK: omp.declare_simd + // CHECK-SAME: linear(val(%{{.*}} : i32 = %{{.*}} : i32)) + omp.declare_simd linear(val(%iv : i32 = %step : i32)) + return +} + +// CHECK-LABEL: func.func @omp_declare_simd_linear_ref +func.func @omp_declare_simd_linear_ref(%p: memref, %step: i32) -> () { + // CHECK: omp.declare_simd + // CHECK-SAME: linear(ref(%{{.*}} : memref = %{{.*}} : i32)) + omp.declare_simd linear(ref(%p : memref = %step : i32)) + return +} + +// CHECK-LABEL: func.func @omp_declare_simd_linear_uval +func.func @omp_declare_simd_linear_uval(%p: memref, %step: i32) -> () { + // CHECK: omp.declare_simd + // CHECK-SAME: linear(uval(%{{.*}} : memref = %{{.*}} : i32)) + omp.declare_simd linear(uval(%p : memref = %step : i32)) + return +} + +// CHECK-LABEL: func.func @omp_declare_simd_linear_mixed_modifiers +func.func @omp_declare_simd_linear_mixed_modifiers(%a: i32, %b: memref, + %c: memref, + %s1: i32, %s2: i32, %s3: i32) -> () { + // CHECK: omp.declare_simd + // CHECK-SAME: linear(val(%{{.*}} : i32 = %{{.*}} : i32), ref(%{{.*}} : memref = %{{.*}} : i32), uval(%{{.*}} : memref = %{{.*}} : i32)) + omp.declare_simd linear(val(%a : i32 = %s1 : i32), + ref(%b : memref = %s2 : i32), + uval(%c : memref = %s3 : i32)) + return +} + +// CHECK-LABEL: func.func @omp_declare_simd_linear_mixed_with_non_modifiers +func.func @omp_declare_simd_linear_mixed_with_non_modifiers( + %a: i32, %b: memref, %c: i32, %s1: i32, %s2: i32, %s3: i32) -> () { + // CHECK: omp.declare_simd + // CHECK-SAME: linear(val(%{{.*}} : i32 = %{{.*}} : i32), %{{.*}} : memref = %{{.*}} : i32, ref(%{{.*}} : i32 = %{{.*}} : i32)) + omp.declare_simd linear(val(%a : i32 = %s1 : i32), + %b : memref = %s2 : i32, + ref(%c : i32 = %s3 : i32)) + return +} + // CHECK-LABEL: func.func @omp_declare_simd_uniform func.func @omp_declare_simd_uniform(%a: f64, %b: f64, %p0: memref, %p1: memref) -> () {