diff --git a/flang/lib/Lower/OpenMP.cpp b/flang/lib/Lower/OpenMP.cpp --- a/flang/lib/Lower/OpenMP.cpp +++ b/flang/lib/Lower/OpenMP.cpp @@ -507,6 +507,19 @@ return omp::ClauseProcBindKindAttr::get(firOpBuilder.getContext(), pbKind); } +static mlir::Value +getIfClauseOperand(Fortran::lower::AbstractConverter &converter, + Fortran::lower::StatementContext &stmtCtx, + const Fortran::parser::OmpClause::If *ifClause) { + fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); + mlir::Location currentLocation = converter.getCurrentLocation(); + auto &expr = std::get(ifClause->v.t); + mlir::Value ifVal = fir::getBase( + converter.genExprValue(*Fortran::semantics::GetExpr(expr), stmtCtx)); + return firOpBuilder.createConvert(currentLocation, firOpBuilder.getI1Type(), + ifVal); +} + /* When parallel is used in a combined construct, then use this function to * create the parallel operation. It handles the parallel specific clauses * and leaves the rest for handling at the inner operations. @@ -532,11 +545,7 @@ for (const Fortran::parser::OmpClause &clause : opClauseList.v) { if (const auto &ifClause = std::get_if(&clause.u)) { - auto &expr = std::get(ifClause->v.t); - mlir::Value ifVal = fir::getBase( - converter.genExprValue(*Fortran::semantics::GetExpr(expr), stmtCtx)); - ifClauseOperand = firOpBuilder.createConvert( - currentLocation, firOpBuilder.getI1Type(), ifVal); + ifClauseOperand = getIfClauseOperand(converter, stmtCtx, ifClause); } else if (const auto &numThreadsClause = std::get_if( &clause.u)) { @@ -585,11 +594,7 @@ for (const auto &clause : opClauseList.v) { if (const auto &ifClause = std::get_if(&clause.u)) { - auto &expr = std::get(ifClause->v.t); - mlir::Value ifVal = fir::getBase( - converter.genExprValue(*Fortran::semantics::GetExpr(expr), stmtCtx)); - ifClauseOperand = firOpBuilder.createConvert( - currentLocation, firOpBuilder.getI1Type(), ifVal); + ifClauseOperand = getIfClauseOperand(converter, stmtCtx, ifClause); } else if (const auto &numThreadsClause = std::get_if( &clause.u)) { @@ -760,9 +765,10 @@ mlir::Location currentLocation = converter.getCurrentLocation(); llvm::SmallVector lowerBound, upperBound, step, linearVars, linearStepVars, reductionVars; - mlir::Value scheduleChunkClauseOperand; + mlir::Value scheduleChunkClauseOperand, ifClauseOperand; mlir::Attribute scheduleClauseOperand, noWaitClauseOperand, orderedClauseOperand, orderClauseOperand; + Fortran::lower::StatementContext stmtCtx; const auto &loopOpClauseList = std::get( std::get(loopConstruct.t).t); @@ -823,11 +829,13 @@ std::get>( scheduleClause->v.t)) { if (const auto *expr = Fortran::semantics::GetExpr(*chunkExpr)) { - Fortran::lower::StatementContext stmtCtx; scheduleChunkClauseOperand = fir::getBase(converter.genExprValue(*expr, stmtCtx)); } } + } else if (const auto &ifClause = + std::get_if(&clause.u)) { + ifClauseOperand = getIfClauseOperand(converter, stmtCtx, ifClause); } } @@ -848,7 +856,8 @@ if (llvm::omp::OMPD_simd == ompDirective) { TypeRange resultType; auto SimdLoopOp = firOpBuilder.create( - currentLocation, resultType, lowerBound, upperBound, step); + currentLocation, resultType, lowerBound, upperBound, step, + ifClauseOperand, /*inclusive=*/firOpBuilder.getUnitAttr()); createBodyOfOp(SimdLoopOp, converter, currentLocation, eval, &loopOpClauseList, iv); return; diff --git a/flang/test/Fir/convert-to-llvm-openmp-and-fir.fir b/flang/test/Fir/convert-to-llvm-openmp-and-fir.fir --- a/flang/test/Fir/convert-to-llvm-openmp-and-fir.fir +++ b/flang/test/Fir/convert-to-llvm-openmp-and-fir.fir @@ -181,7 +181,7 @@ omp.parallel { %1 = fir.alloca i32 {adapt.valuebyref, pinned} %2 = fir.load %arg0 : !fir.ref - omp.simdloop (%arg2) : i32 = (%c1_i32) to (%2) step (%c1_i32) { + omp.simdloop for (%arg2) : i32 = (%c1_i32) to (%2) step (%c1_i32) { fir.store %arg2 to %1 : !fir.ref %3 = fir.load %1 : !fir.ref %4 = fir.convert %3 : (i32) -> i64 diff --git a/flang/test/Lower/OpenMP/simd.f90 b/flang/test/Lower/OpenMP/simd.f90 --- a/flang/test/Lower/OpenMP/simd.f90 +++ b/flang/test/Lower/OpenMP/simd.f90 @@ -9,7 +9,7 @@ ! CHECK: %[[LB:.*]] = arith.constant 1 : i32 ! CHECK-NEXT: %[[UB:.*]] = arith.constant 9 : i32 ! CHECK-NEXT: %[[STEP:.*]] = arith.constant 1 : i32 - ! CHECK-NEXT: omp.simdloop (%[[I:.*]]) : i32 = (%[[LB]]) to (%[[UB]]) step (%[[STEP]]) { + ! CHECK-NEXT: omp.simdloop for (%[[I:.*]]) : i32 = (%[[LB]]) to (%[[UB]]) inclusive step (%[[STEP]]) { do i=1, 9 ! CHECK: fir.store %[[I]] to %[[LOCAL:.*]] : !fir.ref ! CHECK: %[[LD:.*]] = fir.load %[[LOCAL]] : !fir.ref @@ -18,3 +18,21 @@ end do !$OMP END SIMD end subroutine + +!CHECK-LABEL: func @_QPsimdloop_with_if_clause +subroutine simdloop_with_if_clause(n, threshold) +integer :: i, n, threshold + !$OMP SIMD IF( n .GE. threshold ) + ! CHECK: %[[LB:.*]] = arith.constant 1 : i32 + ! CHECK: %[[UB:.*]] = fir.load %arg0 + ! CHECK: %[[STEP:.*]] = arith.constant 1 : i32 + ! CHECK: %[[COND:.*]] = arith.cmpi sge + ! CHECK: omp.simdloop if(%[[COND:.*]]) 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 diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td --- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td +++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td @@ -420,13 +420,18 @@ transformed into a SIMD loop (that is, multiple iterations of the loop can be executed concurrently using SIMD instructions).. The lower and upper bounds specify a half-open range: the range includes the lower bound but - does not include the upper bound. + does not include the upper bound. If the `inclusive` attribute is specified + then the upper bound is also included. The body region can contain any number of blocks. The region is terminated by "omp.yield" instruction without operands. + + When an if clause is present and evaluates to false, the preferred number of + iterations to be executed concurrently is one, regardless of whether + a simdlen clause is speciļ¬ed. ``` - omp.simdloop (%i1, %i2) : index = (%c0, %c0) to (%c10, %c10) - step (%c1, %c1) { + omp.simdloop + for (%i1, %i2) : index = (%c0, %c0) to (%c10, %c10) step (%c1, %c1) { // block operations omp.yield } @@ -436,9 +441,17 @@ // TODO: Add other clauses let arguments = (ins Variadic:$lowerBound, Variadic:$upperBound, - Variadic:$step); + Variadic:$step, + Optional:$if_expr, + UnitAttr:$inclusive + ); let regions = (region AnyRegion:$region); + let assemblyFormat = [{ + oilist(`if` `(` $if_expr `)` + ) `for` custom($region, $lowerBound, $upperBound, $step, + type($step), $inclusive) attr-dict + }]; let extraClassDeclaration = [{ /// Returns the number of loops in the simd loop nest. diff --git a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp --- a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp +++ b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp @@ -570,62 +570,6 @@ p.printRegion(region, /*printEntryBlockArgs=*/false); } -//===----------------------------------------------------------------------===// -// SimdLoopOp -//===----------------------------------------------------------------------===// -/// Parses an OpenMP Simd construct [2.9.3.1] -/// -/// simdloop ::= `omp.simdloop` loop-control clause-list -/// loop-control ::= `(` ssa-id-list `)` `:` type `=` loop-bounds -/// loop-bounds := `(` ssa-id-list `)` to `(` ssa-id-list `)` steps -/// steps := `step` `(`ssa-id-list`)` -/// clause-list ::= clause clause-list | empty -/// clause ::= TODO -ParseResult SimdLoopOp::parse(OpAsmParser &parser, OperationState &result) { - // Parse an opening `(` followed by induction variables followed by `)` - SmallVector ivs; - Type loopVarType; - SmallVector lower, upper, steps; - if (parser.parseArgumentList(ivs, OpAsmParser::Delimiter::Paren) || - parser.parseColonType(loopVarType) || - // Parse loop bounds. - parser.parseEqual() || - parser.parseOperandList(lower, ivs.size(), - OpAsmParser::Delimiter::Paren) || - parser.resolveOperands(lower, loopVarType, result.operands) || - parser.parseKeyword("to") || - parser.parseOperandList(upper, ivs.size(), - OpAsmParser::Delimiter::Paren) || - parser.resolveOperands(upper, loopVarType, result.operands) || - // Parse step values. - parser.parseKeyword("step") || - parser.parseOperandList(steps, ivs.size(), - OpAsmParser::Delimiter::Paren) || - parser.resolveOperands(steps, loopVarType, result.operands)) - return failure(); - - int numIVs = static_cast(ivs.size()); - SmallVector segments{numIVs, numIVs, numIVs}; - // TODO: Add parseClauses() when we support clauses - result.addAttribute("operand_segment_sizes", - parser.getBuilder().getI32VectorAttr(segments)); - - // Now parse the body. - Region *body = result.addRegion(); - for (auto &iv : ivs) - iv.type = loopVarType; - return parser.parseRegion(*body, ivs); -} - -void SimdLoopOp::print(OpAsmPrinter &p) { - auto args = getRegion().front().getArguments(); - p << " (" << args << ") : " << args[0].getType() << " = (" << lowerBound() - << ") to (" << upperBound() << ") "; - p << "step (" << step() << ") "; - - p.printRegion(region(), /*printEntryBlockArgs=*/false); -} - //===----------------------------------------------------------------------===// // Verifier for Simd construct [2.9.3.1] //===----------------------------------------------------------------------===// diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp --- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp @@ -912,6 +912,11 @@ SmallVector loopInfos; SmallVector bodyInsertPoints; LogicalResult bodyGenStatus = success(); + + // TODO: The code generation for if clause is not supported yet. + if (loop.if_expr()) + return failure(); + auto bodyGen = [&](llvm::OpenMPIRBuilder::InsertPointTy ip, llvm::Value *iv) { // Make sure further conversions know about the induction variable. moduleTranslation.mapValue( diff --git a/mlir/test/Dialect/OpenMP/invalid.mlir b/mlir/test/Dialect/OpenMP/invalid.mlir --- a/mlir/test/Dialect/OpenMP/invalid.mlir +++ b/mlir/test/Dialect/OpenMP/invalid.mlir @@ -197,7 +197,7 @@ "omp.simdloop" (%lb, %ub, %step) ({ ^bb0(%iv: index): omp.yield - }) {operand_segment_sizes = dense<[1,1,1]> : vector<3xi32>} : + }) {operand_segment_sizes = dense<[1,1,1,0]> : vector<4xi32>} : (index, index, i32) -> () return diff --git a/mlir/test/Dialect/OpenMP/ops.mlir b/mlir/test/Dialect/OpenMP/ops.mlir --- a/mlir/test/Dialect/OpenMP/ops.mlir +++ b/mlir/test/Dialect/OpenMP/ops.mlir @@ -329,21 +329,29 @@ // CHECK-LABEL: omp_simdloop func.func @omp_simdloop(%lb : index, %ub : index, %step : index) -> () { - // CHECK: omp.simdloop (%{{.*}}) : index = (%{{.*}}) to (%{{.*}}) step (%{{.*}}) + // CHECK: omp.simdloop for (%{{.*}}) : index = (%{{.*}}) to (%{{.*}}) step (%{{.*}}) "omp.simdloop" (%lb, %ub, %step) ({ ^bb0(%iv: index): omp.yield - }) {operand_segment_sizes = dense<[1,1,1]> : vector<3xi32>} : + }) {operand_segment_sizes = dense<[1,1,1,0]> : vector<4xi32>} : (index, index, index) -> () return } - // CHECK-LABEL: omp_simdloop_pretty func.func @omp_simdloop_pretty(%lb : index, %ub : index, %step : index) -> () { - // CHECK: omp.simdloop (%{{.*}}) : index = (%{{.*}}) to (%{{.*}}) step (%{{.*}}) - omp.simdloop (%iv) : index = (%lb) to (%ub) step (%step) { + // CHECK: omp.simdloop for (%{{.*}}) : index = (%{{.*}}) to (%{{.*}}) step (%{{.*}}) + omp.simdloop for (%iv) : index = (%lb) to (%ub) step (%step) { + omp.yield + } + return +} + +// CHECK-LABEL: omp_simdloop_pretty_if +func.func @omp_simdloop_pretty_if(%lb : index, %ub : index, %step : index, %if_cond : i1) -> () { + // CHECK: omp.simdloop if(%{{.*}}) for (%{{.*}}) : index = (%{{.*}}) to (%{{.*}}) step (%{{.*}}) + omp.simdloop if(%if_cond) for (%iv): index = (%lb) to (%ub) step (%step) { omp.yield } return @@ -351,8 +359,8 @@ // 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 (%{{.*}}, %{{.*}}) : index = (%{{.*}}, %{{.*}}) to (%{{.*}}, %{{.*}}) step (%{{.*}}, %{{.*}}) - omp.simdloop (%iv1, %iv2) : index = (%lb1, %lb2) to (%ub1, %ub2) step (%step1, %step2) { + // CHECK: omp.simdloop for (%{{.*}}, %{{.*}}) : index = (%{{.*}}, %{{.*}}) to (%{{.*}}, %{{.*}}) step (%{{.*}}, %{{.*}}) + omp.simdloop for (%iv1, %iv2) : index = (%lb1, %lb2) to (%ub1, %ub2) step (%step1, %step2) { omp.yield } return diff --git a/mlir/test/Target/LLVMIR/openmp-llvm.mlir b/mlir/test/Target/LLVMIR/openmp-llvm.mlir --- a/mlir/test/Target/LLVMIR/openmp-llvm.mlir +++ b/mlir/test/Target/LLVMIR/openmp-llvm.mlir @@ -697,7 +697,7 @@ %4 = llvm.getelementptr %arg0[%iv] : (!llvm.ptr, i64) -> !llvm.ptr llvm.store %3, %4 : !llvm.ptr omp.yield - }) {operand_segment_sizes = dense<[1,1,1]> : vector<3xi32>} : + }) {operand_segment_sizes = dense<[1,1,1,0]> : vector<4xi32>} : (i64, i64, i64) -> () llvm.return @@ -709,7 +709,7 @@ // CHECK-LABEL: @simdloop_simple_multiple llvm.func @simdloop_simple_multiple(%lb1 : i64, %ub1 : i64, %step1 : i64, %lb2 : i64, %ub2 : i64, %step2 : i64, %arg0: !llvm.ptr, %arg1: !llvm.ptr) { - omp.simdloop (%iv1, %iv2) : i64 = (%lb1, %lb2) to (%ub1, %ub2) step (%step1, %step2) { + omp.simdloop for (%iv1, %iv2) : i64 = (%lb1, %lb2) to (%ub1, %ub2) step (%step1, %step2) { %3 = llvm.mlir.constant(2.000000e+00 : f32) : f32 // The form of the emitted IR is controlled by OpenMPIRBuilder and // tested there. Just check that the right metadata is added.