diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp --- a/flang/lib/Lower/Bridge.cpp +++ b/flang/lib/Lower/Bridge.cpp @@ -62,6 +62,43 @@ llvm::cl::desc("force the body of a loop to execute at least once")); namespace { +/// Information for generating a structured or unstructured increment loop. +struct IncrementLoopInfo { + template + explicit IncrementLoopInfo(Fortran::semantics::Symbol &sym, const T &lower, + const T &upper, const std::optional &step, + bool isUnordered = false) + : loopVariableSym{sym}, lowerExpr{Fortran::semantics::GetExpr(lower)}, + upperExpr{Fortran::semantics::GetExpr(upper)}, + stepExpr{Fortran::semantics::GetExpr(step)}, isUnordered{isUnordered} {} + + IncrementLoopInfo(IncrementLoopInfo &&) = default; + IncrementLoopInfo &operator=(IncrementLoopInfo &&x) { return x; } + + // TODO: change when unstructured loops are also supported + bool isStructured() const { return true; } + + mlir::Type getLoopVariableType() const { + assert(loopVariable && "must be set"); + return fir::unwrapRefType(loopVariable.getType()); + } + + // Data members common to both structured and unstructured loops. + const Fortran::semantics::Symbol &loopVariableSym; + const Fortran::lower::SomeExpr *lowerExpr; + const Fortran::lower::SomeExpr *upperExpr; + const Fortran::lower::SomeExpr *stepExpr; + bool isUnordered; // do concurrent, forall + mlir::Value loopVariable = nullptr; + mlir::Value stepValue = nullptr; // possible uses in multiple blocks + + // Data members for structured loops. + fir::DoLoopOp doLoop = nullptr; + + // Data members for unstructured loops. + // TODO: +}; + /// Helper class to generate the runtime type info global data. This data /// is required to describe the derived type to the runtime so that it can /// operate over it. It must be ensured this data will be generated for every @@ -122,6 +159,7 @@ llvm::SmallSetVector seen; }; +using IncrementLoopNestInfo = llvm::SmallVector; } // namespace //===----------------------------------------------------------------------===// @@ -573,6 +611,17 @@ [&sb](auto &) { return sb.toExtendedValue(); }); } + /// Generate the address of loop variable \p sym. + mlir::Value genLoopVariableAddress(mlir::Location loc, + const Fortran::semantics::Symbol &sym) { + Fortran::lower::SymbolBox entry = lookupSymbol(sym); + (void)entry; + assert(entry && "loop control variable must already be in map"); + Fortran::lower::StatementContext stmtCtx; + return fir::getBase( + genExprAddr(Fortran::evaluate::AsGenericExpr(sym).value(), stmtCtx)); + } + static bool isNumericScalarCategory(Fortran::common::TypeCategory cat) { return cat == Fortran::common::TypeCategory::Integer || cat == Fortran::common::TypeCategory::Real || @@ -907,7 +956,106 @@ /// - structured and unstructured increment loops /// - structured and unstructured concurrent loops void genFIR(const Fortran::parser::DoConstruct &doConstruct) { - TODO(toLocation(), "DoConstruct lowering"); + setCurrentPositionAt(doConstruct); + // Collect loop nest information. + // Generate begin loop code directly for infinite and while loops. + Fortran::lower::pft::Evaluation &eval = getEval(); + Fortran::lower::pft::Evaluation &doStmtEval = + eval.getFirstNestedEvaluation(); + auto *doStmt = doStmtEval.getIf(); + const auto &loopControl = + std::get>(doStmt->t); + IncrementLoopNestInfo incrementLoopNestInfo; + if (const auto *bounds = std::get_if( + &loopControl->u)) { + // Non-concurrent increment loop. + incrementLoopNestInfo.emplace_back(*bounds->name.thing.symbol, + bounds->lower, bounds->upper, + bounds->step); + // TODO: unstructured loop + } else { + TODO(toLocation(), "infinite/unstructured loop/concurrent loop"); + } + + // Increment loop begin code. (TODO: Infinite/while code was already + // generated.) + genFIRIncrementLoopBegin(incrementLoopNestInfo); + + // Loop body code - NonLabelDoStmt and EndDoStmt code is generated here. + // Their genFIR calls are nops except for block management in some cases. + for (Fortran::lower::pft::Evaluation &e : eval.getNestedEvaluations()) + genFIR(e, /*unstructuredContext=*/false); + + // Loop end code. (TODO: infinite/while loop) + genFIRIncrementLoopEnd(incrementLoopNestInfo); + } + + /// Generate FIR to begin a structured or unstructured increment loop nest. + void genFIRIncrementLoopBegin(IncrementLoopNestInfo &incrementLoopNestInfo) { + assert(!incrementLoopNestInfo.empty() && "empty loop nest"); + mlir::Location loc = toLocation(); + auto genControlValue = [&](const Fortran::lower::SomeExpr *expr, + const IncrementLoopInfo &info) { + mlir::Type controlType = info.isStructured() ? builder->getIndexType() + : info.getLoopVariableType(); + Fortran::lower::StatementContext stmtCtx; + if (expr) + return builder->createConvert(loc, controlType, + createFIRExpr(loc, expr, stmtCtx)); + return builder->createIntegerConstant(loc, controlType, 1); // step + }; + for (IncrementLoopInfo &info : incrementLoopNestInfo) { + info.loopVariable = genLoopVariableAddress(loc, info.loopVariableSym); + mlir::Value lowerValue = genControlValue(info.lowerExpr, info); + mlir::Value upperValue = genControlValue(info.upperExpr, info); + info.stepValue = genControlValue(info.stepExpr, info); + + // Structured loop - generate fir.do_loop. + if (info.isStructured()) { + info.doLoop = builder->create( + loc, lowerValue, upperValue, info.stepValue, info.isUnordered, + /*finalCountValue=*/!info.isUnordered); + builder->setInsertionPointToStart(info.doLoop.getBody()); + // Update the loop variable value, as it may have non-index references. + mlir::Value value = builder->createConvert( + loc, info.getLoopVariableType(), info.doLoop.getInductionVar()); + builder->create(loc, value, info.loopVariable); + // TODO: Mask expr + // TODO: handle Locality Spec + continue; + } + // TODO: Unstructured loop handling + } + } + + /// Generate FIR to end a structured or unstructured increment loop nest. + void genFIRIncrementLoopEnd(IncrementLoopNestInfo &incrementLoopNestInfo) { + assert(!incrementLoopNestInfo.empty() && "empty loop nest"); + mlir::Location loc = toLocation(); + for (auto it = incrementLoopNestInfo.rbegin(), + rend = incrementLoopNestInfo.rend(); + it != rend; ++it) { + IncrementLoopInfo &info = *it; + if (info.isStructured()) { + // End fir.do_loop. + if (!info.isUnordered) { + builder->setInsertionPointToEnd(info.doLoop.getBody()); + mlir::Value result = builder->create( + loc, info.doLoop.getInductionVar(), info.doLoop.getStep()); + builder->create(loc, result); + } + builder->setInsertionPointAfter(info.doLoop); + if (info.isUnordered) + continue; + // The loop control variable may be used after loop execution. + mlir::Value lcv = builder->createConvert( + loc, info.getLoopVariableType(), info.doLoop.getResult(0)); + builder->create(loc, lcv, info.loopVariable); + continue; + } + + // TODO: Unstructured loop + } } /// Generate structured or unstructured FIR for an IF construct. @@ -2066,10 +2214,6 @@ genFIRBranch(getEval().controlSuccessor->block); } - void genFIR(const Fortran::parser::EndDoStmt &) { - TODO(toLocation(), "EndDoStmt lowering"); - } - // Nop statements - No code, or code is generated at the construct level. void genFIR(const Fortran::parser::AssociateStmt &) {} // nop void genFIR(const Fortran::parser::CaseStmt &) {} // nop @@ -2077,6 +2221,7 @@ void genFIR(const Fortran::parser::ElseIfStmt &) {} // nop void genFIR(const Fortran::parser::ElseStmt &) {} // nop void genFIR(const Fortran::parser::EndAssociateStmt &) {} // nop + void genFIR(const Fortran::parser::EndDoStmt &) {} // nop void genFIR(const Fortran::parser::EndFunctionStmt &) {} // nop void genFIR(const Fortran::parser::EndIfStmt &) {} // nop void genFIR(const Fortran::parser::EndMpSubprogramStmt &) {} // nop @@ -2085,10 +2230,7 @@ void genFIR(const Fortran::parser::EntryStmt &) {} // nop void genFIR(const Fortran::parser::IfStmt &) {} // nop void genFIR(const Fortran::parser::IfThenStmt &) {} // nop - - void genFIR(const Fortran::parser::NonLabelDoStmt &) { - TODO(toLocation(), "NonLabelDoStmt lowering"); - } + void genFIR(const Fortran::parser::NonLabelDoStmt &) {} // nop void genFIR(const Fortran::parser::OmpEndLoopDirective &) { TODO(toLocation(), "OmpEndLoopDirective lowering"); diff --git a/flang/test/Lower/do_loop.f90 b/flang/test/Lower/do_loop.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Lower/do_loop.f90 @@ -0,0 +1,209 @@ +! RUN: bbc -emit-fir -o - %s | FileCheck %s +! RUN: %flang_fc1 -emit-fir -o - %s | FileCheck %s + +! Simple tests for structured ordered loops with loop-control. +! Tests the structure of the loop, storage to index variable and return and +! storage of the final value of the index variable. + +! Test a simple loop with the final value of the index variable read outside the loop +! CHECK-LABEL: simple_loop +subroutine simple_loop + ! CHECK: %[[I_REF:.*]] = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFsimple_loopEi"} + integer :: i + + ! CHECK: %[[C1:.*]] = arith.constant 1 : i32 + ! CHECK: %[[C1_CVT:.*]] = fir.convert %c1_i32 : (i32) -> index + ! CHECK: %[[C5:.*]] = arith.constant 5 : i32 + ! CHECK: %[[C5_CVT:.*]] = fir.convert %c5_i32 : (i32) -> index + ! CHECK: %[[C1:.*]] = arith.constant 1 : index + ! CHECK: %[[LI_RES:.*]] = fir.do_loop %[[LI:.*]] = %[[C1_CVT]] to %[[C5_CVT]] step %[[C1]] -> index { + do i=1,5 + ! CHECK: %[[LI_CVT:.*]] = fir.convert %[[LI]] : (index) -> i32 + ! CHECK: fir.store %[[LI_CVT]] to %[[I_REF]] : !fir.ref + ! CHECK: %[[LI_NEXT:.*]] = arith.addi %[[LI]], %[[C1]] : index + ! CHECK: fir.result %[[LI_NEXT]] : index + ! CHECK: } + end do + ! CHECK: %[[LI_RES_CVT:.*]] = fir.convert %[[LI_RES]] : (index) -> i32 + ! CHECK: fir.store %[[LI_RES_CVT]] to %[[I_REF]] : !fir.ref + ! CHECK: %[[I:.*]] = fir.load %[[I_REF]] : !fir.ref + ! CHECK: %{{.*}} = fir.call @_FortranAioOutputInteger32(%{{.*}}, %[[I]]) : (!fir.ref, i32) -> i1 + print *, i +end subroutine + +! Test a 2-nested loop with a body composed of a reduction. Values are read from a 2d array. +! CHECK-LABEL: nested_loop +subroutine nested_loop + ! CHECK: %[[ARR_REF:.*]] = fir.alloca !fir.array<5x5xi32> {bindc_name = "arr", uniq_name = "_QFnested_loopEarr"} + ! CHECK: %[[ASUM_REF:.*]] = fir.alloca i32 {bindc_name = "asum", uniq_name = "_QFnested_loopEasum"} + ! CHECK: %[[I_REF:.*]] = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFnested_loopEi"} + ! CHECK: %[[J_REF:.*]] = fir.alloca i32 {bindc_name = "j", uniq_name = "_QFnested_loopEj"} + integer :: asum, arr(5,5) + integer :: i, j + asum = 0 + ! CHECK: %[[S_I:.*]] = arith.constant 1 : i32 + ! CHECK: %[[S_I_CVT:.*]] = fir.convert %[[S_I]] : (i32) -> index + ! CHECK: %[[E_I:.*]] = arith.constant 5 : i32 + ! CHECK: %[[E_I_CVT:.*]] = fir.convert %[[E_I]] : (i32) -> index + ! CHECK: %[[ST_I:.*]] = arith.constant 1 : index + ! CHECK: %[[I_RES:.*]] = fir.do_loop %[[LI:.*]] = %[[S_I_CVT]] to %[[E_I_CVT]] step %[[ST_I]] -> index { + do i=1,5 + ! CHECK: %[[LI_CVT:.*]] = fir.convert %[[LI]] : (index) -> i32 + ! CHECK: fir.store %[[LI_CVT]] to %[[I_REF]] : !fir.ref + ! CHECK: %[[S_J:.*]] = arith.constant 1 : i32 + ! CHECK: %[[S_J_CVT:.*]] = fir.convert %[[S_J]] : (i32) -> index + ! CHECK: %[[E_J:.*]] = arith.constant 5 : i32 + ! CHECK: %[[E_J_CVT:.*]] = fir.convert %[[E_J]] : (i32) -> index + ! CHECK: %[[ST_J:.*]] = arith.constant 1 : index + ! CHECK: %[[J_RES:.*]] = fir.do_loop %[[LJ:.*]] = %[[S_J_CVT]] to %[[E_J_CVT]] step %[[ST_J]] -> index { + do j=1,5 + ! CHECK: %[[LJ_CVT:.*]] = fir.convert %[[LJ]] : (index) -> i32 + ! CHECK: fir.store %[[LJ_CVT]] to %[[J_REF]] : !fir.ref + ! CHECK: %[[ASUM:.*]] = fir.load %[[ASUM_REF]] : !fir.ref + ! CHECK: %[[I:.*]] = fir.load %[[I_REF]] : !fir.ref + ! CHECK: %[[I_CVT:.*]] = fir.convert %[[I]] : (i32) -> i64 + ! CHECK: %[[C1_I:.*]] = arith.constant 1 : i64 + ! CHECK: %[[I_INDX:.*]] = arith.subi %[[I_CVT]], %[[C1_I]] : i64 + ! CHECK: %[[J:.*]] = fir.load %[[J_REF]] : !fir.ref + ! CHECK: %[[J_CVT:.*]] = fir.convert %[[J]] : (i32) -> i64 + ! CHECK: %[[C1_J:.*]] = arith.constant 1 : i64 + ! CHECK: %[[J_INDX:.*]] = arith.subi %[[J_CVT]], %[[C1_J]] : i64 + ! CHECK: %[[ARR_IJ_REF:.*]] = fir.coordinate_of %[[ARR_REF]], %[[I_INDX]], %[[J_INDX]] : (!fir.ref>, i64, i64) -> !fir.ref + ! CHECK: %[[ARR_VAL:.*]] = fir.load %[[ARR_IJ_REF]] : !fir.ref + ! CHECK: %[[ASUM_NEW:.*]] = arith.addi %[[ASUM]], %[[ARR_VAL]] : i32 + ! CHECK: fir.store %[[ASUM_NEW]] to %[[ASUM_REF]] : !fir.ref + asum = asum + arr(i,j) + ! CHECK: %[[LJ_NEXT:.*]] = arith.addi %[[LJ]], %[[ST_J]] : index + ! CHECK: fir.result %[[LJ_NEXT]] : index + ! CHECK: } + end do + ! CHECK: %[[J_RES_CVT:.*]] = fir.convert %[[J_RES]] : (index) -> i32 + ! CHECK: fir.store %[[J_RES_CVT]] to %[[J_REF]] : !fir.ref + ! CHECK: %[[LI_NEXT:.*]] = arith.addi %[[LI]], %[[ST_I]] : index + ! CHECK: fir.result %[[LI_NEXT]] : index + ! CHECK: } + end do + ! CHECK: %[[I_RES_CVT:.*]] = fir.convert %[[I_RES]] : (index) -> i32 + ! CHECK: fir.store %[[I_RES_CVT]] to %[[I_REF]] : !fir.ref +end subroutine + +! Test a downcounting loop +! CHECK-LABEL: down_counting_loop +subroutine down_counting_loop() + integer :: i + ! CHECK: %[[I_REF:.*]] = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFdown_counting_loopEi"} + + ! CHECK: %[[C5:.*]] = arith.constant 5 : i32 + ! CHECK: %[[C5_CVT:.*]] = fir.convert %[[C5]] : (i32) -> index + ! CHECK: %[[C1:.*]] = arith.constant 1 : i32 + ! CHECK: %[[C1_CVT:.*]] = fir.convert %[[C1]] : (i32) -> index + ! CHECK: %[[CMINUS1:.*]] = arith.constant -1 : i32 + ! CHECK: %[[CMINUS1_STEP_CVT:.*]] = fir.convert %[[CMINUS1]] : (i32) -> index + ! CHECK: %[[I_RES:.*]] = fir.do_loop %[[LI:.*]] = %[[C5_CVT]] to %[[C1_CVT]] step %[[CMINUS1_STEP_CVT]] -> index { + do i=5,1,-1 + ! CHECK: %[[LI_CVT:.*]] = fir.convert %[[LI]] : (index) -> i32 + ! CHECK: fir.store %[[LI_CVT]] to %[[I_REF]] : !fir.ref + ! CHECK: %[[LI_NEXT:.*]] = arith.addi %[[LI]], %[[CMINUS1_STEP_CVT]] : index + ! CHECK: fir.result %[[LI_NEXT]] : index + ! CHECK: } + end do + ! CHECK: %[[I_RES_CVT:.*]] = fir.convert %[[I_RES]] : (index) -> i32 + ! CHECK: fir.store %[[I_RES_CVT]] to %[[I_REF]] : !fir.ref +end subroutine + +! Test a general loop with a variable step +! CHECK-LABEL: general_loop +! CHECK-SAME: (%[[S_REF:.*]]: !fir.ref {fir.bindc_name = "s"}, %[[E_REF:.*]]: !fir.ref {fir.bindc_name = "e"}, %[[ST_REF:.*]]: !fir.ref {fir.bindc_name = "st"}) { +subroutine general_loop(s,e,st) + integer :: s, e, st + ! CHECK: %[[S:.*]] = fir.load %[[S_REF]] : !fir.ref + ! CHECK: %[[S_CVT:.*]] = fir.convert %[[S]] : (i32) -> index + ! CHECK: %[[E:.*]] = fir.load %[[E_REF]] : !fir.ref + ! CHECK: %[[E_CVT:.*]] = fir.convert %[[E]] : (i32) -> index + ! CHECK: %[[ST:.*]] = fir.load %[[ST_REF]] : !fir.ref + ! CHECK: %[[ST_CVT:.*]] = fir.convert %[[ST]] : (i32) -> index + ! CHECK: %[[I_RES:.*]] = fir.do_loop %[[LI:.*]] = %[[S_CVT]] to %[[E_CVT]] step %[[ST_CVT]] -> index { + do i=s,e,st + ! CHECK: %[[LI_CVT:.*]] = fir.convert %[[LI]] : (index) -> i32 + ! CHECK: fir.store %[[LI_CVT]] to %[[I_REF]] : !fir.ref + ! CHECK: %[[LI_NEXT:.*]] = arith.addi %[[LI]], %[[ST_CVT]] : index + ! CHECK: fir.result %[[LI_NEXT]] : index + ! CHECK: } + end do + ! CHECK: %[[I_RES_CVT:.*]] = fir.convert %[[I_RES]] : (index) -> i32 + ! CHECK: fir.store %[[I_RES_CVT]] to %[[I_REF]] : !fir.ref +end subroutine + +! Test usage of pointer variables as index, start, end and step variables +! CHECK-LABEL: loop_with_pointer_variables +! CHECK-SAME: (%[[S_REF:.*]]: !fir.ref {fir.bindc_name = "s", fir.target}, %[[E_REF:.*]]: !fir.ref {fir.bindc_name = "e", fir.target}, %[[ST_REF:.*]]: !fir.ref {fir.bindc_name = "st", fir.target}) { +subroutine loop_with_pointer_variables(s,e,st) +! CHECK: %[[E_PTR_REF:.*]] = fir.alloca !fir.ptr {uniq_name = "_QFloop_with_pointer_variablesEeptr.addr"} +! CHECK: %[[I_REF:.*]] = fir.alloca i32 {bindc_name = "i", fir.target, uniq_name = "_QFloop_with_pointer_variablesEi"} +! CHECK: %[[I_PTR_REF:.*]] = fir.alloca !fir.ptr {uniq_name = "_QFloop_with_pointer_variablesEiptr.addr"} +! CHECK: %[[S_PTR_REF:.*]] = fir.alloca !fir.ptr {uniq_name = "_QFloop_with_pointer_variablesEsptr.addr"} +! CHECK: %[[ST_PTR_REF:.*]] = fir.alloca !fir.ptr {uniq_name = "_QFloop_with_pointer_variablesEstptr.addr"} + integer, target :: i + integer, target :: s, e, st + integer, pointer :: iptr, sptr, eptr, stptr + +! CHECK: %[[I_PTR:.*]] = fir.convert %[[I_REF]] : (!fir.ref) -> !fir.ptr +! CHECK: fir.store %[[I_PTR]] to %[[I_PTR_REF]] : !fir.ref> +! CHECK: %[[S_PTR:.*]] = fir.convert %[[S_REF]] : (!fir.ref) -> !fir.ptr +! CHECK: fir.store %[[S_PTR]] to %[[S_PTR_REF]] : !fir.ref> +! CHECK: %[[E_PTR:.*]] = fir.convert %[[E_REF]] : (!fir.ref) -> !fir.ptr +! CHECK: fir.store %[[E_PTR]] to %[[E_PTR_REF]] : !fir.ref> +! CHECK: %[[ST_PTR:.*]] = fir.convert %[[ST_REF]] : (!fir.ref) -> !fir.ptr +! CHECK: fir.store %[[ST_PTR]] to %[[ST_PTR_REF]] : !fir.ref> + iptr => i + sptr => s + eptr => e + stptr => st + +! CHECK: %[[I_PTR:.*]] = fir.load %[[I_PTR_REF]] : !fir.ref> +! CHECK: %[[S_PTR:.*]] = fir.load %[[S_PTR_REF]] : !fir.ref> +! CHECK: %[[S:.*]] = fir.load %[[S_PTR]] : !fir.ptr +! CHECK: %[[S_CVT:.*]] = fir.convert %[[S]] : (i32) -> index +! CHECK: %[[E_PTR:.*]] = fir.load %[[E_PTR_REF]] : !fir.ref> +! CHECK: %[[E:.*]] = fir.load %[[E_PTR]] : !fir.ptr +! CHECK: %[[E_CVT:.*]] = fir.convert %[[E]] : (i32) -> index +! CHECK: %[[ST_PTR:.*]] = fir.load %[[ST_PTR_REF]] : !fir.ref> +! CHECK: %[[ST:.*]] = fir.load %[[ST_PTR]] : !fir.ptr +! CHECK: %[[ST_CVT:.*]] = fir.convert %[[ST]] : (i32) -> index +! CHECK: %[[I_RES:.*]] = fir.do_loop %[[LI:.*]] = %[[S_CVT]] to %[[E_CVT]] step %[[ST_CVT]] -> index { + do iptr=sptr,eptr,stptr +! CHECK: %[[LI_CVT:.*]] = fir.convert %[[LI]] : (index) -> i32 +! CHECK: fir.store %[[LI_CVT]] to %[[I_PTR]] : !fir.ptr +! CHECK: %[[LI_NEXT:.*]] = arith.addi %[[LI]], %[[ST_CVT]] : index +! CHECK: fir.result %[[LI_NEXT]] : index + end do +! CHECK: } +! CHECK: %[[I_RES_CVT:.*]] = fir.convert %[[I_RES]] : (index) -> i32 +! CHECK: fir.store %[[I_RES_CVT:.*]] to %[[I_PTR]] : !fir.ptr +end subroutine + +! Test usage of non-default integer kind for loop control and loop index variable +! CHECK-LABEL: loop_with_non_default_integer +! CHECK-SAME: (%[[S_REF:.*]]: !fir.ref {fir.bindc_name = "s"}, %[[E_REF:.*]]: !fir.ref {fir.bindc_name = "e"}, %[[ST_REF:.*]]: !fir.ref {fir.bindc_name = "st"}) { +subroutine loop_with_non_default_integer(s,e,st) + ! CHECK: %[[I_REF:.*]] = fir.alloca i64 {bindc_name = "i", uniq_name = "_QFloop_with_non_default_integerEi"} + integer(kind=8):: i + ! CHECK: %[[S:.*]] = fir.load %[[S_REF]] : !fir.ref + ! CHECK: %[[S_CVT:.*]] = fir.convert %[[S]] : (i64) -> index + ! CHECK: %[[E:.*]] = fir.load %[[E_REF]] : !fir.ref + ! CHECK: %[[E_CVT:.*]] = fir.convert %[[E]] : (i64) -> index + ! CHECK: %[[ST:.*]] = fir.load %[[ST_REF]] : !fir.ref + ! CHECK: %[[ST_CVT:.*]] = fir.convert %[[ST]] : (i64) -> index + integer(kind=8) :: s, e, st + + ! CHECK: %[[I_RES:.*]] = fir.do_loop %[[LI:.*]] = %[[S_CVT]] to %[[E_CVT]] step %[[ST_CVT]] -> index { + do i=s,e,st + ! CHECK: %[[LI_CVT:.*]] = fir.convert %[[LI]] : (index) -> i64 + ! CHECK: fir.store %[[LI_CVT]] to %[[I_REF]] : !fir.ref + ! CHECK: %[[LI_NEXT:.*]] = arith.addi %[[LI]], %[[ST_CVT]] : index + ! CHECK: fir.result %[[LI_NEXT]] : index + end do + ! CHECK: } + ! CHECK: %[[I_RES_CVT:.*]] = fir.convert %[[I_RES]] : (index) -> i64 + ! CHECK: fir.store %[[I_RES_CVT]] to %[[I_REF]] : !fir.ref +end subroutine