Index: flang/lib/Lower/OpenMP.cpp =================================================================== --- flang/lib/Lower/OpenMP.cpp +++ flang/lib/Lower/OpenMP.cpp @@ -1019,7 +1019,7 @@ mlir::Value scheduleChunkClauseOperand, ifClauseOperand; mlir::Attribute scheduleClauseOperand, noWaitClauseOperand, orderedClauseOperand, orderClauseOperand; - mlir::IntegerAttr simdlenClauseOperand; + mlir::IntegerAttr simdlenClauseOperand, safelenClauseOperand; SmallVector reductionDeclSymbols; Fortran::lower::StatementContext stmtCtx; const auto &loopOpClauseList = std::get( @@ -1144,6 +1144,13 @@ const std::optional simdlenVal = Fortran::evaluate::ToInt64(*expr); simdlenClauseOperand = firOpBuilder.getI64IntegerAttr(*simdlenVal); + } else if (const auto &safelenClause = + std::get_if( + &clause.u)) { + const auto *expr = Fortran::semantics::GetExpr(safelenClause->v); + const std::optional safelenVal = + Fortran::evaluate::ToInt64(*expr); + safelenClauseOperand = firOpBuilder.getI64IntegerAttr(*safelenVal); } } @@ -1165,7 +1172,7 @@ TypeRange resultType; auto SimdLoopOp = firOpBuilder.create( currentLocation, resultType, lowerBound, upperBound, step, - ifClauseOperand, simdlenClauseOperand, + ifClauseOperand, simdlenClauseOperand, safelenClauseOperand, /*inclusive=*/firOpBuilder.getUnitAttr()); createBodyOfOp(SimdLoopOp, converter, currentLocation, eval, &loopOpClauseList, iv); Index: flang/test/Lower/OpenMP/simd.f90 =================================================================== --- flang/test/Lower/OpenMP/simd.f90 +++ flang/test/Lower/OpenMP/simd.f90 @@ -90,6 +90,58 @@ !$OMP END SIMD end subroutine +!CHECK-LABEL: func @_QPsimdloop_with_safelen_clause +subroutine simdloop_with_safelen_clause(n, threshold) +integer :: i, n, threshold + !$OMP SIMD SAFELEN(2) + ! CHECK: %[[LB:.*]] = arith.constant 1 : i32 + ! CHECK: %[[UB:.*]] = fir.load %arg0 + ! CHECK: %[[STEP:.*]] = arith.constant 1 : i32 + ! CHECK: omp.simdloop safelen(2) for (%[[I:.*]]) : i32 = (%[[LB]]) to (%[[UB]]) inclusive step (%[[STEP]]) { + do i = 1, n + ! CHECK: fir.store %[[I]] to %[[LOCAL:.*]] : !fir.ref + ! CHECK: %[[LD:.*]] = fir.load %[[LOCAL]] : !fir.ref + ! CHECK: fir.call @_FortranAioOutputInteger32({{.*}}, %[[LD]]) : (!fir.ref, i32) -> i1 + print*, i + end do + !$OMP END SIMD +end subroutine + +!CHECK-LABEL: func @_QPsimdloop_with_safelen_clause_from_expr_from_param +subroutine simdloop_with_safelen_clause_from_expr_from_param(n, threshold) +integer :: i, n, threshold +integer, parameter :: safelen = 2; + !$OMP SIMD SAFELEN(safelen*2 + 2) + ! CHECK: %[[LB:.*]] = arith.constant 1 : i32 + ! CHECK: %[[UB:.*]] = fir.load %arg0 + ! CHECK: %[[STEP:.*]] = arith.constant 1 : i32 + ! CHECK: omp.simdloop safelen(6) for (%[[I:.*]]) : i32 = (%[[LB]]) to (%[[UB]]) inclusive step (%[[STEP]]) { + do i = 1, n + ! CHECK: fir.store %[[I]] to %[[LOCAL:.*]] : !fir.ref + ! CHECK: %[[LD:.*]] = fir.load %[[LOCAL]] : !fir.ref + ! CHECK: fir.call @_FortranAioOutputInteger32({{.*}}, %[[LD]]) : (!fir.ref, i32) -> i1 + print*, i + end do + !$OMP END SIMD +end subroutine + +!CHECK-LABEL: func @_QPsimdloop_with_simdlen_safelen_clause +subroutine simdloop_with_simdlen_safelen_clause(n, threshold) +integer :: i, n, threshold + !$OMP SIMD SIMDLEN(1) SAFELEN(2) + ! CHECK: %[[LB:.*]] = arith.constant 1 : i32 + ! CHECK: %[[UB:.*]] = fir.load %arg0 + ! CHECK: %[[STEP:.*]] = arith.constant 1 : i32 + ! CHECK: omp.simdloop simdlen(1) safelen(2) for (%[[I:.*]]) : i32 = (%[[LB]]) to (%[[UB]]) inclusive step (%[[STEP]]) { + do i = 1, n + ! CHECK: fir.store %[[I]] to %[[LOCAL:.*]] : !fir.ref + ! CHECK: %[[LD:.*]] = fir.load %[[LOCAL]] : !fir.ref + ! CHECK: fir.call @_FortranAioOutputInteger32({{.*}}, %[[LD]]) : (!fir.ref, i32) -> i1 + print*, i + end do + !$OMP END SIMD +end subroutine + !CHECK-LABEL: func @_QPsimdloop_with_collapse_clause subroutine simdloop_with_collapse_clause(n) integer :: i, j, n Index: mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td =================================================================== --- mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td +++ mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td @@ -421,6 +421,10 @@ When a simdlen clause is present, the preferred number of iterations to be executed concurrently is the value provided to the simdlen clause. + + The safelen clause specifies that no two concurrent iterations within a + SIMD chunk can have a distance in the logical iteration space that is + greater than or equal to the value given in the clause. ``` omp.simdloop for (%i1, %i2) : index = (%c0, %c0) to (%c10, %c10) step (%c1, %c1) { @@ -436,6 +440,7 @@ Variadic:$step, Optional:$if_expr, ConfinedAttr, [IntPositive]>:$simdlen, + ConfinedAttr, [IntPositive]>:$safelen, UnitAttr:$inclusive ); @@ -443,6 +448,7 @@ let assemblyFormat = [{ oilist(`if` `(` $if_expr `)` |`simdlen` `(` $simdlen `)` + |`safelen` `(` $safelen `)` ) `for` custom($region, $lowerBound, $upperBound, $step, type($step), $inclusive) attr-dict }]; Index: mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp =================================================================== --- mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp +++ mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp @@ -578,6 +578,13 @@ if (this->lowerBound().empty()) { return emitOpError() << "empty lowerbound for simd loop operation"; } + if (this->simdlen().has_value() && this->safelen().has_value() && + this->simdlen().value() > this->safelen().value()) { + return emitOpError() + << "simdlen clause and safelen clause are both present, but the " + "simdlen value is not less than or equal to safelen value"; + } + return success(); } Index: mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp =================================================================== --- mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp +++ mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp @@ -970,10 +970,14 @@ if (llvm::Optional simdlenVar = loop.simdlen()) simdlen = builder.getInt64(simdlenVar.value()); + llvm::ConstantInt *safelen = nullptr; + if (llvm::Optional safelenVar = loop.safelen()) + safelen = builder.getInt64(safelenVar.value()); + ompBuilder->applySimd( loopInfo, loop.if_expr() ? moduleTranslation.lookupValue(loop.if_expr()) : nullptr, - simdlen, nullptr); + simdlen, safelen); builder.restoreIP(afterIP); return success(); Index: mlir/test/Dialect/OpenMP/invalid.mlir =================================================================== --- mlir/test/Dialect/OpenMP/invalid.mlir +++ mlir/test/Dialect/OpenMP/invalid.mlir @@ -215,6 +215,26 @@ // ----- +func.func @omp_simdloop_pretty_safelen(%lb : index, %ub : index, %step : index) -> () { + // expected-error @below {{op attribute 'safelen' failed to satisfy constraint: 64-bit signless integer attribute whose value is positive}} + omp.simdloop safelen(0) for (%iv): index = (%lb) to (%ub) step (%step) { + omp.yield + } + return +} + +// ----- + +func.func @omp_simdloop_pretty_simdlen_safelen(%lb : index, %ub : index, %step : index) -> () { + // expected-error @below {{'omp.simdloop' op simdlen clause and safelen clause are both present, but the simdlen value is not less than or equal to safelen value}} + omp.simdloop simdlen(2) safelen(1) for (%iv): index = (%lb) to (%ub) step (%step) { + omp.yield + } + return +} + +// ----- + // expected-error @below {{op expects initializer region with one argument of the reduction type}} omp.reduction.declare @add_f32 : f64 init { Index: mlir/test/Dialect/OpenMP/ops.mlir =================================================================== --- mlir/test/Dialect/OpenMP/ops.mlir +++ mlir/test/Dialect/OpenMP/ops.mlir @@ -366,6 +366,15 @@ return } +// CHECK-LABEL: omp_simdloop_pretty_safelen +func.func @omp_simdloop_pretty_safelen(%lb : index, %ub : index, %step : index) -> () { + // CHECK: omp.simdloop safelen(2) for (%{{.*}}) : index = (%{{.*}}) to (%{{.*}}) step (%{{.*}}) + omp.simdloop safelen(2) for (%iv): index = (%lb) to (%ub) step (%step) { + omp.yield + } + return +} + // CHECK-LABEL: omp_simdloop_pretty_multiple func.func @omp_simdloop_pretty_multiple(%lb1 : index, %ub1 : index, %step1 : index, %lb2 : index, %ub2 : index, %step2 : index) -> () { // CHECK: omp.simdloop for (%{{.*}}, %{{.*}}) : index = (%{{.*}}, %{{.*}}) to (%{{.*}}, %{{.*}}) step (%{{.*}}, %{{.*}}) Index: mlir/test/Target/LLVMIR/openmp-llvm.mlir =================================================================== --- mlir/test/Target/LLVMIR/openmp-llvm.mlir +++ mlir/test/Target/LLVMIR/openmp-llvm.mlir @@ -765,6 +765,40 @@ // ----- +// CHECK-LABEL: @simdloop_simple_multiple_safelen +llvm.func @simdloop_simple_multiple_safelen(%lb1 : i64, %ub1 : i64, %step1 : i64, %lb2 : i64, %ub2 : i64, %step2 : i64, %arg0: !llvm.ptr, %arg1: !llvm.ptr) { + omp.simdloop safelen(2) for (%iv1, %iv2) : i64 = (%lb1, %lb2) to (%ub1, %ub2) step (%step1, %step2) { + %3 = llvm.mlir.constant(2.000000e+00 : f32) : f32 + %4 = llvm.getelementptr %arg0[%iv1] : (!llvm.ptr, i64) -> !llvm.ptr + %5 = llvm.getelementptr %arg1[%iv2] : (!llvm.ptr, i64) -> !llvm.ptr + llvm.store %3, %4 : !llvm.ptr + llvm.store %3, %5 : !llvm.ptr + omp.yield + } + llvm.return +} +// CHECK: llvm.loop.vectorize.enable +// CHECK-NEXT: llvm.loop.vectorize.width{{.*}}i64 2 + +// ----- + +// CHECK-LABEL: @simdloop_simple_multiple_simdlen_safelen +llvm.func @simdloop_simple_multiple_simdlen_safelen(%lb1 : i64, %ub1 : i64, %step1 : i64, %lb2 : i64, %ub2 : i64, %step2 : i64, %arg0: !llvm.ptr, %arg1: !llvm.ptr) { + omp.simdloop simdlen(1) safelen(2) for (%iv1, %iv2) : i64 = (%lb1, %lb2) to (%ub1, %ub2) step (%step1, %step2) { + %3 = llvm.mlir.constant(2.000000e+00 : f32) : f32 + %4 = llvm.getelementptr %arg0[%iv1] : (!llvm.ptr, i64) -> !llvm.ptr + %5 = llvm.getelementptr %arg1[%iv2] : (!llvm.ptr, i64) -> !llvm.ptr + llvm.store %3, %4 : !llvm.ptr + llvm.store %3, %5 : !llvm.ptr + omp.yield + } + llvm.return +} +// CHECK: llvm.loop.vectorize.enable +// CHECK-NEXT: llvm.loop.vectorize.width{{.*}}i64 1 + +// ----- + // CHECK-LABEL: @simdloop_if llvm.func @simdloop_if(%arg0: !llvm.ptr {fir.bindc_name = "n"}, %arg1: !llvm.ptr {fir.bindc_name = "threshold"}) { %0 = llvm.mlir.constant(1 : i64) : i64