From 85d7927c08b5563f050d56d2bf88a6c152489c41 Mon Sep 17 00:00:00 2001 From: "Chi-Chun, Chen" Date: Tue, 31 Mar 2026 11:11:36 -0500 Subject: [PATCH] [Flang][OpenMP] Support iterator modifier in depend clause (#189412) Lower the iterator modifier on depend clause to omp.iterator. Iterated depend objects emit `!omp.iterated` by using `getDataOperandBaseAddr` to generate base address and `genIteratorCoordinate` to get the addr+offset. The non-iterated objects in depend clause still use existing lowering path. This patch is part of feature work for #188061. Assisted with copilot. --- flang/lib/Lower/OpenMP/ClauseProcessor.cpp | 77 ++- .../test/Lower/OpenMP/Todo/depend-clause.f90 | 10 - flang/test/Lower/OpenMP/depend-iterator.f90 | 439 ++++++++++++++++++ 3 files changed, 502 insertions(+), 24 deletions(-) delete mode 100644 flang/test/Lower/OpenMP/Todo/depend-clause.f90 create mode 100644 flang/test/Lower/OpenMP/depend-iterator.f90 diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp index 75df4163c8a5..7ae27daac5d6 100644 --- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp +++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp @@ -1289,16 +1289,14 @@ bool ClauseProcessor::processDepend(lower::SymMap &symMap, auto depType = std::get(clause.t); auto &objects = std::get(clause.t); fir::FirOpBuilder &builder = converter.getFirOpBuilder(); + mlir::Location clauseLocation = converter.getCurrentLocation(); - if (std::get>(clause.t)) { - TODO(converter.getCurrentLocation(), - "Support for iterator modifiers is not implemented yet"); - } mlir::omp::ClauseTaskDependAttr dependTypeOperand = genDependKindAttr(converter, depType); - result.dependKinds.append(objects.size(), dependTypeOperand); - for (const omp::Object &object : objects) { + auto genDependVar = + [&](const omp::Object &object, lower::SymMap &localSymMap, + lower::StatementContext &localStmtCtx) -> mlir::Value { assert(object.ref() && "Expecting designator"); mlir::Value dependVar; SomeExpr expr = *object.ref(); @@ -1311,18 +1309,18 @@ bool ClauseProcessor::processDepend(lower::SymMap &symMap, // don't need an accurate length for the array section because the // OpenMP standard forbids overlapping array sections. dependVar = genVectorSubscriptedDesignatorFirstElementAddress( - converter.getCurrentLocation(), converter, expr, symMap, stmtCtx); + clauseLocation, converter, expr, localSymMap, localStmtCtx); } else { // Ordinary array section e.g. A(1:512:2) hlfir::EntityWithAttributes entity = convertExprToHLFIR( - converter.getCurrentLocation(), converter, expr, symMap, stmtCtx); + clauseLocation, converter, expr, localSymMap, localStmtCtx); dependVar = entity.getBase(); } } else if (evaluate::isStructureComponent(expr) || evaluate::ExtractComplexPart(expr)) { SomeExpr expr = *object.ref(); hlfir::EntityWithAttributes entity = convertExprToHLFIR( - converter.getCurrentLocation(), converter, expr, symMap, stmtCtx); + clauseLocation, converter, expr, localSymMap, localStmtCtx); dependVar = entity.getBase(); } else { semantics::Symbol *sym = object.sym(); @@ -1335,8 +1333,7 @@ bool ClauseProcessor::processDepend(lower::SymMap &symMap, // allocations so this is not a reliable way to identify the dependency. if (auto ref = mlir::dyn_cast(dependVar.getType())) if (fir::isa_box_type(ref.getElementType())) - dependVar = fir::LoadOp::create( - builder, converter.getCurrentLocation(), dependVar); + dependVar = fir::LoadOp::create(builder, clauseLocation, dependVar); // The openmp dialect doesn't know what to do with boxes (and it would // break layering to teach it about them). The dependency variable can be @@ -1345,10 +1342,62 @@ bool ClauseProcessor::processDepend(lower::SymMap &symMap, // Getting the address of the box data is okay because all the runtime // ultimately cares about is the base address of the array. if (fir::isa_box_type(dependVar.getType())) - dependVar = fir::BoxAddrOp::create( - builder, converter.getCurrentLocation(), dependVar); + dependVar = fir::BoxAddrOp::create(builder, clauseLocation, dependVar); - result.dependVars.push_back(dependVar); + return dependVar; + }; + + auto &iteratorModifier = + std::get>(clause.t); + + llvm::SmallVector iteratorRanges; + llvm::SmallPtrSet ivSyms; + collectIteratorIVs(clause, converter, stmtCtx, iteratorRanges, ivSyms); + + mlir::Type ptrTy = + mlir::LLVM::LLVMPointerType::get(&converter.getMLIRContext()); + mlir::Type iterTy = + mlir::omp::IteratedType::get(&converter.getMLIRContext(), ptrTy); + + for (const omp::Object &object : objects) { + if (iteratorModifier.has_value() && + hasIteratorIVReference(object, ivSyms)) { + mlir::Value iterHandle = buildIteratorOp( + converter, clauseLocation, iterTy, iteratorRanges, + [&](fir::FirOpBuilder &builder, mlir::Location loc, + llvm::ArrayRef /*ivs*/) -> mlir::Value { + lower::StatementContext iterStmtCtx; + if (std::optional> loweredIndices = + getIteratorElementIndices(converter, object, iterStmtCtx, + loc)) { + const Fortran::semantics::Symbol *sym = object.sym(); + assert(sym && "expected symbol for iterator object"); + // We currently cannot reuse genDependVar here because + // buildIteratorOp maps iterator IV symbols to bare scalar + // values (e.g. i32), but genDependVar uses convertExprToHLFIR + // which expects memory-backed references. Instead, manually get + // the base address and compute the element coordinate from the + // FIR-level lowered indices. + fir::factory::AddrAndBoundsInfo info = + Fortran::lower::getDataOperandBaseAddr( + converter, builder, *sym, loc, + /*unwrapFirBox=*/false); + hlfir::Entity entity{info.addr}; + mlir::Value iteratedAddr = genIteratorCoordinate( + converter, entity, *loweredIndices, loc); + // Convert to !llvm.ptr for the omp.yield + return fir::ConvertOp::create(builder, loc, ptrTy, + iteratedAddr); + } + + TODO(loc, "object type not supported by iterator modifier"); + }); + result.dependIterated.push_back(iterHandle); + result.dependIteratedKinds.push_back(dependTypeOperand); + } else { + result.dependVars.push_back(genDependVar(object, symMap, stmtCtx)); + result.dependKinds.push_back(dependTypeOperand); + } } }; diff --git a/flang/test/Lower/OpenMP/Todo/depend-clause.f90 b/flang/test/Lower/OpenMP/Todo/depend-clause.f90 deleted file mode 100644 index 74525888c91d..000000000000 --- a/flang/test/Lower/OpenMP/Todo/depend-clause.f90 +++ /dev/null @@ -1,10 +0,0 @@ -!RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %s 2>&1 | FileCheck %s -!RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %s 2>&1 | FileCheck %s - -!CHECK: Support for iterator modifiers is not implemented yet -subroutine f00(x) - integer :: x(10) - !$omp task depend(iterator(i = 1:10), in: x(i)) - x = 0 - !$omp end task -end diff --git a/flang/test/Lower/OpenMP/depend-iterator.f90 b/flang/test/Lower/OpenMP/depend-iterator.f90 new file mode 100644 index 000000000000..6f94fa399870 --- /dev/null +++ b/flang/test/Lower/OpenMP/depend-iterator.f90 @@ -0,0 +1,439 @@ +! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %s | FileCheck %s + +! Tests for the iterator modifier on the depend clause across all directives +! that Flang currently supports: task, target, target enter data, target exit data, +! and target update. +! TODO: We need to add iterator test for taskwait, depobj, interop once they are +! supported. + +!=============================================================================== +! task +!=============================================================================== + +subroutine task_depend_iterator_simple() + integer, parameter :: n = 16 + integer :: a(n) + integer :: i + + !$omp task depend(iterator(i = 1:n), in: a(i)) + !$omp end task +end subroutine + +! CHECK-LABEL: func.func @_QPtask_depend_iterator_simple() +! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtask_depend_iterator_simpleEa"} +! CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}) { +! CHECK: %[[IV_I32:.*]] = fir.convert %[[IV]] : (index) -> i32 +! CHECK: %[[IV_I64:.*]] = fir.convert %[[IV_I32]] : (i32) -> i64 +! CHECK: %[[SHAPE:.*]] = fir.shape %c16 : (index) -> !fir.shape<1> +! CHECK: %[[COOR:.*]] = fir.array_coor %[[A]]#0(%[[SHAPE]]) %[[IV_I64]] : (!fir.ref>, !fir.shape<1>, i64) -> !fir.ref +! CHECK: %[[PTR:.*]] = fir.convert %[[COOR]] : (!fir.ref) -> !llvm.ptr +! CHECK: omp.yield(%[[PTR]] : !llvm.ptr) +! CHECK: } -> !omp.iterated +! CHECK: omp.task depend(taskdependin -> %[[IT]] : !omp.iterated) { +! CHECK: omp.terminator +! CHECK: } + +subroutine task_depend_iterator_2d() + integer, parameter :: n = 4, m = 6 + integer :: a(n, m) + integer :: i, j + + !$omp task depend(iterator(i = 1:n, j = 1:m), inout: a(i, j)) + !$omp end task +end subroutine + +! CHECK-LABEL: func.func @_QPtask_depend_iterator_2d() +! CHECK: %[[IT:.*]] = omp.iterator(%[[IV0:.*]]: index, %[[IV1:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}, {{.*}} to {{.*}} step {{.*}}) { +! CHECK: %[[IV0_I32:.*]] = fir.convert %[[IV0]] : (index) -> i32 +! CHECK: %[[IV1_I32:.*]] = fir.convert %[[IV1]] : (index) -> i32 +! CHECK: %[[IV0_I64:.*]] = fir.convert %[[IV0_I32]] : (i32) -> i64 +! CHECK: %[[IV1_I64:.*]] = fir.convert %[[IV1_I32]] : (i32) -> i64 +! CHECK: %[[SHAPE:.*]] = fir.shape %c4, %c6 : (index, index) -> !fir.shape<2> +! CHECK: %[[COOR:.*]] = fir.array_coor %{{.*}}(%[[SHAPE]]) %[[IV0_I64]], %[[IV1_I64]] : (!fir.ref>, !fir.shape<2>, i64, i64) -> !fir.ref +! CHECK: %[[PTR:.*]] = fir.convert %[[COOR]] : (!fir.ref) -> !llvm.ptr +! CHECK: omp.yield(%[[PTR]] : !llvm.ptr) +! CHECK: } -> !omp.iterated +! CHECK: omp.task depend(taskdependinout -> %[[IT]] : !omp.iterated) { + +subroutine task_depend_iterator_mixed() + integer, parameter :: n = 16 + integer :: a(n), x + integer :: i + + !$omp task depend(iterator(i = 1:n), in: a(i)) depend(out: x) + !$omp end task +end subroutine + +! CHECK-LABEL: func.func @_QPtask_depend_iterator_mixed() +! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtask_depend_iterator_mixedEa"} +! CHECK: %[[X:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtask_depend_iterator_mixedEx"} +! CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}) { +! CHECK: omp.yield(%{{.*}} : !llvm.ptr) +! CHECK: } -> !omp.iterated +! CHECK: omp.task depend(taskdependout -> %[[X]]#0 : !fir.ref, taskdependin -> %[[IT]] : !omp.iterated) { + +subroutine task_depend_iterator_step() + integer, parameter :: n = 16 + integer :: a(n) + integer :: i + + !$omp task depend(iterator(i = 1:n:2), in: a(i)) + !$omp end task +end subroutine + +! CHECK-LABEL: func.func @_QPtask_depend_iterator_step() +! CHECK: %[[C1_I32:.*]] = arith.constant 1 : i32 +! CHECK: %[[C16_I32:.*]] = arith.constant 16 : i32 +! CHECK: %[[LB:.*]] = fir.convert %[[C1_I32]] : (i32) -> index +! CHECK: %[[UB:.*]] = fir.convert %[[C16_I32]] : (i32) -> index +! CHECK: %[[C2_I32:.*]] = arith.constant 2 : i32 +! CHECK: %[[STEP:.*]] = fir.convert %[[C2_I32]] : (i32) -> index +! CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: index) = (%[[LB]] to %[[UB]] step %[[STEP]]) { +! CHECK: omp.yield(%{{.*}} : !llvm.ptr) +! CHECK: } -> !omp.iterated +! CHECK: omp.task depend(taskdependin -> %[[IT]] : !omp.iterated) { + +subroutine task_depend_iterator_multi_obj() + integer, parameter :: n = 16 + integer :: a(n), b(n) + integer :: i + + !$omp task depend(iterator(i = 1:n), inout: a(i), b(i)) + !$omp end task +end subroutine + +! CHECK-LABEL: func.func @_QPtask_depend_iterator_multi_obj() +! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtask_depend_iterator_multi_objEa"} +! CHECK: %[[B:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtask_depend_iterator_multi_objEb"} +! CHECK: %[[IT1:.*]] = omp.iterator(%[[IV1:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}) { +! CHECK: %[[COOR1:.*]] = fir.array_coor %[[A]]#0(%{{.*}}) %{{.*}} : (!fir.ref>, !fir.shape<1>, i64) -> !fir.ref +! CHECK: %[[PTR1:.*]] = fir.convert %[[COOR1]] : (!fir.ref) -> !llvm.ptr +! CHECK: omp.yield(%[[PTR1]] : !llvm.ptr) +! CHECK: } -> !omp.iterated +! CHECK: %[[IT2:.*]] = omp.iterator(%[[IV2:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}) { +! CHECK: %[[COOR2:.*]] = fir.array_coor %[[B]]#0(%{{.*}}) %{{.*}} : (!fir.ref>, !fir.shape<1>, i64) -> !fir.ref +! CHECK: %[[PTR2:.*]] = fir.convert %[[COOR2]] : (!fir.ref) -> !llvm.ptr +! CHECK: omp.yield(%[[PTR2]] : !llvm.ptr) +! CHECK: } -> !omp.iterated +! CHECK: omp.task depend(taskdependinout -> %[[IT1]] : !omp.iterated, taskdependinout -> %[[IT2]] : !omp.iterated) { + +! Expression-based subscript using multiple iterator variables: a((i-1)*m+j) +! maps a 2D logical iteration space onto a 1D array. +subroutine task_depend_iterator_expr_subscript() + integer, parameter :: m = 4 + integer, parameter :: n = m * m + integer :: a(n) + integer :: i, j + + !$omp task depend(iterator(i = 1:m, j = 1:m), out: a((i-1)*m+j)) + !$omp end task +end subroutine + +! CHECK-LABEL: func.func @_QPtask_depend_iterator_expr_subscript() +! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtask_depend_iterator_expr_subscriptEa"} +! CHECK: %[[IT:.*]] = omp.iterator(%[[IV0:.*]]: index, %[[IV1:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}, {{.*}} to {{.*}} step {{.*}}) { +! CHECK: %[[IV0_I32:.*]] = fir.convert %[[IV0]] : (index) -> i32 +! CHECK: %[[IV1_I32:.*]] = fir.convert %[[IV1]] : (index) -> i32 +! CHECK: %[[C1_I32:.*]] = arith.constant 1 : i32 +! CHECK: %[[SUB:.*]] = arith.subi %[[IV0_I32]], %[[C1_I32]] : i32 +! CHECK: %[[NOREASSOC:.*]] = fir.no_reassoc %[[SUB]] : i32 +! CHECK: %[[MUL:.*]] = arith.muli %{{.*}}, %[[NOREASSOC]] : i32 +! CHECK: %[[ADD:.*]] = arith.addi %[[MUL]], %[[IV1_I32]] : i32 +! CHECK: %[[IDX:.*]] = fir.convert %[[ADD]] : (i32) -> i64 +! CHECK: %[[COOR:.*]] = fir.array_coor %[[A]]#0(%{{.*}}) %[[IDX]] : (!fir.ref>, !fir.shape<1>, i64) -> !fir.ref +! CHECK: %[[PTR:.*]] = fir.convert %[[COOR]] : (!fir.ref) -> !llvm.ptr +! CHECK: omp.yield(%[[PTR]] : !llvm.ptr) +! CHECK: } -> !omp.iterated +! CHECK: omp.task depend(taskdependout -> %[[IT]] : !omp.iterated) { + +! Multiple depend clauses each with their own iterator on the same task. +subroutine task_depend_multi_iter_clauses() + integer, parameter :: n = 8 + integer :: a(n), b(n) + integer :: i, j + + !$omp task depend(iterator(i = 1:n), in: a(i)) & + !$omp& depend(iterator(j = 1:n), out: b(j)) + !$omp end task +end subroutine + +! CHECK-LABEL: func.func @_QPtask_depend_multi_iter_clauses() +! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtask_depend_multi_iter_clausesEa"} +! CHECK: %[[B:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtask_depend_multi_iter_clausesEb"} +! CHECK: %[[IT1:.*]] = omp.iterator(%[[IV1:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}) { +! CHECK: %[[COOR1:.*]] = fir.array_coor %[[A]]#0(%{{.*}}) %{{.*}} : (!fir.ref>, !fir.shape<1>, i64) -> !fir.ref +! CHECK: %[[PTR1:.*]] = fir.convert %[[COOR1]] : (!fir.ref) -> !llvm.ptr +! CHECK: omp.yield(%[[PTR1]] : !llvm.ptr) +! CHECK: } -> !omp.iterated +! CHECK: %[[IT2:.*]] = omp.iterator(%[[IV2:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}) { +! CHECK: %[[COOR2:.*]] = fir.array_coor %[[B]]#0(%{{.*}}) %{{.*}} : (!fir.ref>, !fir.shape<1>, i64) -> !fir.ref +! CHECK: %[[PTR2:.*]] = fir.convert %[[COOR2]] : (!fir.ref) -> !llvm.ptr +! CHECK: omp.yield(%[[PTR2]] : !llvm.ptr) +! CHECK: } -> !omp.iterated +! CHECK: omp.task depend(taskdependin -> %[[IT1]] : !omp.iterated, taskdependout -> %[[IT2]] : !omp.iterated) { + +subroutine task_depend_iterator_negative_step() + integer, parameter :: n = 16 + integer :: a(n) + integer :: i + + !$omp task depend(iterator(i = n:1:-1), in: a(i)) + !$omp end task +end subroutine + +! CHECK-LABEL: func.func @_QPtask_depend_iterator_negative_step() +! CHECK: %[[C16_I32:.*]] = arith.constant 16 : i32 +! CHECK: %[[C1_I32:.*]] = arith.constant 1 : i32 +! CHECK: %[[LB:.*]] = fir.convert %[[C16_I32]] : (i32) -> index +! CHECK: %[[UB:.*]] = fir.convert %[[C1_I32]] : (i32) -> index +! CHECK: %[[CM1_I32:.*]] = arith.constant -1 : i32 +! CHECK: %[[STEP:.*]] = fir.convert %[[CM1_I32]] : (i32) -> index +! CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: index) = (%[[LB]] to %[[UB]] step %[[STEP]]) { +! CHECK: omp.yield(%{{.*}} : !llvm.ptr) +! CHECK: } -> !omp.iterated +! CHECK: omp.task depend(taskdependin -> %[[IT]] : !omp.iterated) { + +! Mixed iterated and non-iterated objects in the same depend clause: +! a(1) does not reference the iterator IV, so it is lowered as a regular +! (non-iterated) depend var, while a(i) produces an omp.iterator. +subroutine task_depend_iterator_mixed_within_clause() + integer, parameter :: n = 16 + integer :: a(n) + integer :: i + + !$omp task depend(iterator(i = 2:n:2), in: a(1), a(i)) + !$omp end task +end subroutine + +! CHECK-LABEL: func.func @_QPtask_depend_iterator_mixed_within_clause() +! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtask_depend_iterator_mixed_within_clauseEa"} +! CHECK: %[[A1:.*]] = hlfir.designate %[[A]]#0 (%{{.*}}) : (!fir.ref>, index) -> !fir.ref +! CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}) { +! CHECK: %[[COOR:.*]] = fir.array_coor %[[A]]#0(%{{.*}}) %{{.*}} : (!fir.ref>, !fir.shape<1>, i64) -> !fir.ref +! CHECK: %[[PTR:.*]] = fir.convert %[[COOR]] : (!fir.ref) -> !llvm.ptr +! CHECK: omp.yield(%[[PTR]] : !llvm.ptr) +! CHECK: } -> !omp.iterated +! CHECK: omp.task depend(taskdependin -> %[[A1]] : !fir.ref, taskdependin -> %[[IT]] : !omp.iterated) { + +!=============================================================================== +! target +!=============================================================================== + +subroutine target_depend_iterator() + integer, parameter :: n = 16 + integer :: a(n) + integer :: i + + !$omp target depend(iterator(i = 1:n), in: a(i)) map(tofrom: a) + a(1) = 10 + !$omp end target +end subroutine + +! CHECK-LABEL: func.func @_QPtarget_depend_iterator() +! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_depend_iteratorEa"} +! CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}) { +! CHECK: %[[IV_I32:.*]] = fir.convert %[[IV]] : (index) -> i32 +! CHECK: %[[IV_I64:.*]] = fir.convert %[[IV_I32]] : (i32) -> i64 +! CHECK: %[[SHAPE:.*]] = fir.shape %c16 : (index) -> !fir.shape<1> +! CHECK: %[[COOR:.*]] = fir.array_coor %[[A]]#0(%[[SHAPE]]) %[[IV_I64]] : (!fir.ref>, !fir.shape<1>, i64) -> !fir.ref +! CHECK: %[[PTR:.*]] = fir.convert %[[COOR]] : (!fir.ref) -> !llvm.ptr +! CHECK: omp.yield(%[[PTR]] : !llvm.ptr) +! CHECK: } -> !omp.iterated +! CHECK: %[[MAP:.*]] = omp.map.info var_ptr(%[[A]]#1 : {{.*}}) map_clauses(tofrom) capture(ByRef) bounds({{.*}}) -> !fir.ref> {name = "a"} +! CHECK: omp.target depend(taskdependin -> %[[IT]] : !omp.iterated) map_entries(%[[MAP]] -> %{{.*}} : !fir.ref>) { +! CHECK: omp.terminator +! CHECK: } + +subroutine target_depend_iterator_multi() + integer, parameter :: n = 8 + integer :: a(n), b(n), c(n) + integer :: i, j + + !$omp target depend(iterator(i = 1:n), inout: a(i), b(i)) & + !$omp& depend(iterator(j = 1:n:2), in: c(j)) & + !$omp& map(tofrom: a, b) + a(1) = b(1) + !$omp end target +end subroutine + +! CHECK-LABEL: func.func @_QPtarget_depend_iterator_multi() +! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_depend_iterator_multiEa"} +! CHECK: %[[B:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_depend_iterator_multiEb"} +! CHECK: %[[C:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_depend_iterator_multiEc"} +! CHECK: %[[IT1:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}} to {{.*}} step {{.*}}) { +! CHECK: %[[COOR1:.*]] = fir.array_coor %[[A]]#0(%{{.*}}) %{{.*}} : (!fir.ref>, !fir.shape<1>, i64) -> !fir.ref +! CHECK: omp.yield(%{{.*}} : !llvm.ptr) +! CHECK: } -> !omp.iterated +! CHECK: %[[IT2:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}} to {{.*}} step {{.*}}) { +! CHECK: %[[COOR2:.*]] = fir.array_coor %[[B]]#0(%{{.*}}) %{{.*}} : (!fir.ref>, !fir.shape<1>, i64) -> !fir.ref +! CHECK: omp.yield(%{{.*}} : !llvm.ptr) +! CHECK: } -> !omp.iterated +! CHECK: %[[IT3:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}} to {{.*}} step {{.*}}) { +! CHECK: %[[COOR3:.*]] = fir.array_coor %[[C]]#0(%{{.*}}) %{{.*}} : (!fir.ref>, !fir.shape<1>, i64) -> !fir.ref +! CHECK: omp.yield(%{{.*}} : !llvm.ptr) +! CHECK: } -> !omp.iterated +! CHECK: %[[MAP_A:.*]] = omp.map.info var_ptr(%[[A]]#1 : {{.*}}) map_clauses(tofrom) capture(ByRef) bounds({{.*}}) -> !fir.ref> {name = "a"} +! CHECK: %[[MAP_B:.*]] = omp.map.info var_ptr(%[[B]]#1 : {{.*}}) map_clauses(tofrom) capture(ByRef) bounds({{.*}}) -> !fir.ref> {name = "b"} +! CHECK: %[[MAP_C:.*]] = omp.map.info var_ptr(%[[C]]#1 : {{.*}}) map_clauses(implicit, tofrom) capture(ByRef) bounds({{.*}}) -> !fir.ref> {name = "c"} +! CHECK: omp.target depend(taskdependinout -> %[[IT1]] : !omp.iterated, taskdependinout -> %[[IT2]] : !omp.iterated, taskdependin -> %[[IT3]] : !omp.iterated) map_entries(%[[MAP_A]] -> %{{.*}}, %[[MAP_B]] -> %{{.*}}, %[[MAP_C]] -> %{{.*}} : !fir.ref>, !fir.ref>, !fir.ref>) { + +!=============================================================================== +! target enter data +!=============================================================================== + +subroutine target_enter_data_depend_iterator() + integer, parameter :: n = 16 + integer :: a(n) + integer :: i + + !$omp target enter data map(to: a) depend(iterator(i = 1:n), in: a(i)) +end subroutine + +! CHECK-LABEL: func.func @_QPtarget_enter_data_depend_iterator() +! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_enter_data_depend_iteratorEa"} +! CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}) { +! CHECK: %[[IV_I32:.*]] = fir.convert %[[IV]] : (index) -> i32 +! CHECK: %[[IV_I64:.*]] = fir.convert %[[IV_I32]] : (i32) -> i64 +! CHECK: %[[SHAPE:.*]] = fir.shape %c16 : (index) -> !fir.shape<1> +! CHECK: %[[COOR:.*]] = fir.array_coor %[[A]]#0(%[[SHAPE]]) %[[IV_I64]] : (!fir.ref>, !fir.shape<1>, i64) -> !fir.ref +! CHECK: %[[PTR:.*]] = fir.convert %[[COOR]] : (!fir.ref) -> !llvm.ptr +! CHECK: omp.yield(%[[PTR]] : !llvm.ptr) +! CHECK: } -> !omp.iterated +! CHECK: %[[MAP:.*]] = omp.map.info var_ptr(%[[A]]#1 : {{.*}}) map_clauses(to) capture(ByRef) bounds({{.*}}) -> !fir.ref> {name = "a"} +! CHECK: omp.target_enter_data depend(taskdependin -> %[[IT]] : !omp.iterated) map_entries(%[[MAP]] : !fir.ref>) + +subroutine target_enter_data_depend_iterator_expr() + integer, parameter :: m = 4 + integer, parameter :: n = m * m + integer :: a(n) + integer :: i, j + + !$omp target enter data map(to: a) & + !$omp& depend(iterator(i = 1:m, j = 1:m), inout: a(1), a((i-1)*m+j)) +end subroutine + +! CHECK-LABEL: func.func @_QPtarget_enter_data_depend_iterator_expr() +! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_enter_data_depend_iterator_exprEa"} +! CHECK: %[[A1:.*]] = hlfir.designate %[[A]]#0 (%{{.*}}) : (!fir.ref>, index) -> !fir.ref +! CHECK: %[[IT:.*]] = omp.iterator(%[[IV0:.*]]: index, %[[IV1:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}, {{.*}} to {{.*}} step {{.*}}) { +! CHECK: %[[IV0_I32:.*]] = fir.convert %[[IV0]] : (index) -> i32 +! CHECK: %[[IV1_I32:.*]] = fir.convert %[[IV1]] : (index) -> i32 +! CHECK: %[[SUB:.*]] = arith.subi %[[IV0_I32]], %{{.*}} : i32 +! CHECK: %[[NOREASSOC:.*]] = fir.no_reassoc %[[SUB]] : i32 +! CHECK: %[[MUL:.*]] = arith.muli %{{.*}}, %[[NOREASSOC]] : i32 +! CHECK: %[[ADD:.*]] = arith.addi %[[MUL]], %[[IV1_I32]] : i32 +! CHECK: %[[IDX:.*]] = fir.convert %[[ADD]] : (i32) -> i64 +! CHECK: %[[COOR:.*]] = fir.array_coor %[[A]]#0(%{{.*}}) %[[IDX]] : (!fir.ref>, !fir.shape<1>, i64) -> !fir.ref +! CHECK: omp.yield(%{{.*}} : !llvm.ptr) +! CHECK: } -> !omp.iterated +! CHECK: %[[MAP:.*]] = omp.map.info var_ptr(%[[A]]#1 : {{.*}}) map_clauses(to) capture(ByRef) bounds({{.*}}) -> !fir.ref> {name = "a"} +! CHECK: omp.target_enter_data depend(taskdependinout -> %[[A1]] : !fir.ref, taskdependinout -> %[[IT]] : !omp.iterated) map_entries(%[[MAP]] : !fir.ref>) + +!=============================================================================== +! target exit data +!=============================================================================== + +subroutine target_exit_data_depend_iterator() + integer, parameter :: n = 16 + integer :: a(n) + integer :: i + + !$omp target exit data map(from: a) depend(iterator(i = 1:n), out: a(i)) +end subroutine + +! CHECK-LABEL: func.func @_QPtarget_exit_data_depend_iterator() +! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_exit_data_depend_iteratorEa"} +! CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}) { +! CHECK: %[[IV_I32:.*]] = fir.convert %[[IV]] : (index) -> i32 +! CHECK: %[[IV_I64:.*]] = fir.convert %[[IV_I32]] : (i32) -> i64 +! CHECK: %[[SHAPE:.*]] = fir.shape %c16 : (index) -> !fir.shape<1> +! CHECK: %[[COOR:.*]] = fir.array_coor %[[A]]#0(%[[SHAPE]]) %[[IV_I64]] : (!fir.ref>, !fir.shape<1>, i64) -> !fir.ref +! CHECK: %[[PTR:.*]] = fir.convert %[[COOR]] : (!fir.ref) -> !llvm.ptr +! CHECK: omp.yield(%[[PTR]] : !llvm.ptr) +! CHECK: } -> !omp.iterated +! CHECK: %[[MAP:.*]] = omp.map.info var_ptr(%[[A]]#1 : {{.*}}) map_clauses(from) capture(ByRef) bounds({{.*}}) -> !fir.ref> {name = "a"} +! CHECK: omp.target_exit_data depend(taskdependout -> %[[IT]] : !omp.iterated) map_entries(%[[MAP]] : !fir.ref>) + +subroutine target_exit_data_depend_iterator_multi() + integer, parameter :: n = 16 + integer :: a(n), b(n) + integer :: i + + !$omp target exit data map(from: a, b) & + !$omp& depend(iterator(i = n:1:-1), out: a(i), b(i)) +end subroutine + +! CHECK-LABEL: func.func @_QPtarget_exit_data_depend_iterator_multi() +! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_exit_data_depend_iterator_multiEa"} +! CHECK: %[[B:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_exit_data_depend_iterator_multiEb"} +! CHECK: %[[C16_I32:.*]] = arith.constant 16 : i32 +! CHECK: %[[C1_I32:.*]] = arith.constant 1 : i32 +! CHECK: %[[LB:.*]] = fir.convert %[[C16_I32]] : (i32) -> index +! CHECK: %[[UB:.*]] = fir.convert %[[C1_I32]] : (i32) -> index +! CHECK: %[[CM1_I32:.*]] = arith.constant -1 : i32 +! CHECK: %[[STEP:.*]] = fir.convert %[[CM1_I32]] : (i32) -> index +! CHECK: %[[IT1:.*]] = omp.iterator(%{{.*}}: index) = (%[[LB]] to %[[UB]] step %[[STEP]]) { +! CHECK: %[[COOR1:.*]] = fir.array_coor %[[A]]#0(%{{.*}}) %{{.*}} : (!fir.ref>, !fir.shape<1>, i64) -> !fir.ref +! CHECK: omp.yield(%{{.*}} : !llvm.ptr) +! CHECK: } -> !omp.iterated +! CHECK: %[[IT2:.*]] = omp.iterator(%{{.*}}: index) = (%[[LB]] to %[[UB]] step %[[STEP]]) { +! CHECK: %[[COOR2:.*]] = fir.array_coor %[[B]]#0(%{{.*}}) %{{.*}} : (!fir.ref>, !fir.shape<1>, i64) -> !fir.ref +! CHECK: omp.yield(%{{.*}} : !llvm.ptr) +! CHECK: } -> !omp.iterated +! CHECK: %[[MAP_A:.*]] = omp.map.info var_ptr(%[[A]]#1 : {{.*}}) map_clauses(from) capture(ByRef) bounds({{.*}}) -> !fir.ref> {name = "a"} +! CHECK: %[[MAP_B:.*]] = omp.map.info var_ptr(%[[B]]#1 : {{.*}}) map_clauses(from) capture(ByRef) bounds({{.*}}) -> !fir.ref> {name = "b"} +! CHECK: omp.target_exit_data depend(taskdependout -> %[[IT1]] : !omp.iterated, taskdependout -> %[[IT2]] : !omp.iterated) map_entries(%[[MAP_A]], %[[MAP_B]] : !fir.ref>, !fir.ref>) + +!=============================================================================== +! target update +!=============================================================================== + +subroutine target_update_depend_iterator() + integer, parameter :: n = 16 + integer :: a(n) + integer :: i + + !$omp target update to(a) depend(iterator(i = 1:n), in: a(i)) +end subroutine + +! CHECK-LABEL: func.func @_QPtarget_update_depend_iterator() +! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_update_depend_iteratorEa"} +! CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}) { +! CHECK: %[[IV_I32:.*]] = fir.convert %[[IV]] : (index) -> i32 +! CHECK: %[[IV_I64:.*]] = fir.convert %[[IV_I32]] : (i32) -> i64 +! CHECK: %[[SHAPE:.*]] = fir.shape %c16 : (index) -> !fir.shape<1> +! CHECK: %[[COOR:.*]] = fir.array_coor %[[A]]#0(%[[SHAPE]]) %[[IV_I64]] : (!fir.ref>, !fir.shape<1>, i64) -> !fir.ref +! CHECK: %[[PTR:.*]] = fir.convert %[[COOR]] : (!fir.ref) -> !llvm.ptr +! CHECK: omp.yield(%[[PTR]] : !llvm.ptr) +! CHECK: } -> !omp.iterated +! CHECK: %[[MAP:.*]] = omp.map.info var_ptr(%[[A]]#1 : {{.*}}) map_clauses(to) capture(ByRef) bounds({{.*}}) -> !fir.ref> {name = "a"} +! CHECK: omp.target_update depend(taskdependin -> %[[IT]] : !omp.iterated) map_entries(%[[MAP]] : !fir.ref>) + +! Two separate depend(iterator) clauses with different IVs and depend kinds, +! plus a non-iterated depend. +subroutine target_update_depend_iterator_multi() + integer, parameter :: n = 8 + integer :: a(n), b(n), x + integer :: i, j + + !$omp target update to(a) from(b) & + !$omp& depend(iterator(i = 1:n), in: a(i)) & + !$omp& depend(iterator(j = 1:n:2), out: b(j)) & + !$omp& depend(inout: x) +end subroutine + +! CHECK-LABEL: func.func @_QPtarget_update_depend_iterator_multi() +! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_update_depend_iterator_multiEa"} +! CHECK: %[[B:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_update_depend_iterator_multiEb"} +! CHECK: %[[X:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtarget_update_depend_iterator_multiEx"} +! CHECK: %[[IT1:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}} to {{.*}} step {{.*}}) { +! CHECK: %[[COOR1:.*]] = fir.array_coor %[[A]]#0(%{{.*}}) %{{.*}} : (!fir.ref>, !fir.shape<1>, i64) -> !fir.ref +! CHECK: omp.yield(%{{.*}} : !llvm.ptr) +! CHECK: } -> !omp.iterated +! CHECK: %[[IT2:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}} to {{.*}} step {{.*}}) { +! CHECK: %[[COOR2:.*]] = fir.array_coor %[[B]]#0(%{{.*}}) %{{.*}} : (!fir.ref>, !fir.shape<1>, i64) -> !fir.ref +! CHECK: omp.yield(%{{.*}} : !llvm.ptr) +! CHECK: } -> !omp.iterated +! CHECK: %[[MAP_A:.*]] = omp.map.info var_ptr(%[[A]]#1 : {{.*}}) map_clauses(to) capture(ByRef) bounds({{.*}}) -> !fir.ref> {name = "a"} +! CHECK: %[[MAP_B:.*]] = omp.map.info var_ptr(%[[B]]#1 : {{.*}}) map_clauses(from) capture(ByRef) bounds({{.*}}) -> !fir.ref> {name = "b"} +! CHECK: omp.target_update depend(taskdependinout -> %[[X]]#0 : !fir.ref, taskdependin -> %[[IT1]] : !omp.iterated, taskdependout -> %[[IT2]] : !omp.iterated) map_entries(%[[MAP_A]], %[[MAP_B]] : !fir.ref>, !fir.ref>)