From 5983b8b6d33291ec14a55706670536a92b123989 Mon Sep 17 00:00:00 2001 From: Slava Zakharin Date: Thu, 29 Jun 2023 09:41:36 -0700 Subject: [PATCH] [flang][hlfir] Lower ordered elemental subroutine calls. This patch sets `unordered` `fir.do_loop` attribute during lowering of elemental subroutine calls to HLFIR, when it is safe to do so. Proper handling of `hlfir.elemental` will be done in a separate patch. Reviewed By: jeanPerier, tblah Differential Revision: https://reviews.llvm.org/D154031 --- flang/include/flang/Optimizer/Builder/HLFIRTools.h | 9 ++-- flang/lib/Lower/ConvertCall.cpp | 7 +-- flang/lib/Optimizer/Builder/HLFIRTools.cpp | 5 +- flang/test/HLFIR/extents-of-shape-of.f90 | 8 ++-- flang/test/Lower/HLFIR/calls-optional.f90 | 6 +-- .../Lower/HLFIR/elemental-user-procedure-ref.f90 | 54 +++++++++++++++++++++- 6 files changed, 72 insertions(+), 17 deletions(-) diff --git a/flang/include/flang/Optimizer/Builder/HLFIRTools.h b/flang/include/flang/Optimizer/Builder/HLFIRTools.h index 7bf1289..ddea8e5d 100644 --- a/flang/include/flang/Optimizer/Builder/HLFIRTools.h +++ b/flang/include/flang/Optimizer/Builder/HLFIRTools.h @@ -385,11 +385,14 @@ struct LoopNest { }; /// Generate a fir.do_loop nest looping from 1 to extents[i]. +/// \p isUnordered specifies whether the loops in the loop nest +/// are unordered. LoopNest genLoopNest(mlir::Location loc, fir::FirOpBuilder &builder, - mlir::ValueRange extents); + mlir::ValueRange extents, bool isUnordered = false); inline LoopNest genLoopNest(mlir::Location loc, fir::FirOpBuilder &builder, - mlir::Value shape) { - return genLoopNest(loc, builder, getIndexExtents(loc, builder, shape)); + mlir::Value shape, bool isUnordered = false) { + return genLoopNest(loc, builder, getIndexExtents(loc, builder, shape), + isUnordered); } /// Inline the body of an hlfir.elemental at the current insertion point diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp index d96c89b..9bfe93f 100644 --- a/flang/lib/Lower/ConvertCall.cpp +++ b/flang/lib/Lower/ConvertCall.cpp @@ -1593,13 +1593,12 @@ public: } assert(shape && "elemental array calls must have at least one array arguments"); - if (mustBeOrdered) - TODO(loc, "ordered elemental calls in HLFIR"); // Push a new local scope so that any temps made inside the elemental // iterations are cleaned up inside the iterations. if (!callContext.resultType) { // Subroutine case. Generate call inside loop nest. - hlfir::LoopNest loopNest = hlfir::genLoopNest(loc, builder, shape); + hlfir::LoopNest loopNest = + hlfir::genLoopNest(loc, builder, shape, !mustBeOrdered); mlir::ValueRange oneBasedIndices = loopNest.oneBasedIndices; auto insPt = builder.saveInsertionPoint(); builder.setInsertionPointToStart(loopNest.innerLoop.getBody()); @@ -1613,6 +1612,8 @@ public: return std::nullopt; } // Function case: generate call inside hlfir.elemental + if (mustBeOrdered) + TODO(loc, "ordered elemental calls in HLFIR"); mlir::Type elementType = hlfir::getFortranElementType(*callContext.resultType); // Get result length parameters. diff --git a/flang/lib/Optimizer/Builder/HLFIRTools.cpp b/flang/lib/Optimizer/Builder/HLFIRTools.cpp index 30f5ba5..a905f83 100644 --- a/flang/lib/Optimizer/Builder/HLFIRTools.cpp +++ b/flang/lib/Optimizer/Builder/HLFIRTools.cpp @@ -807,7 +807,7 @@ mlir::Value hlfir::inlineElementalOp( hlfir::LoopNest hlfir::genLoopNest(mlir::Location loc, fir::FirOpBuilder &builder, - mlir::ValueRange extents) { + mlir::ValueRange extents, bool isUnordered) { hlfir::LoopNest loopNest; assert(!extents.empty() && "must have at least one extent"); auto insPt = builder.saveInsertionPoint(); @@ -818,7 +818,8 @@ hlfir::LoopNest hlfir::genLoopNest(mlir::Location loc, unsigned dim = extents.size() - 1; for (auto extent : llvm::reverse(extents)) { auto ub = builder.createConvert(loc, indexType, extent); - loopNest.innerLoop = builder.create(loc, one, ub, one); + loopNest.innerLoop = + builder.create(loc, one, ub, one, isUnordered); builder.setInsertionPointToStart(loopNest.innerLoop.getBody()); // Reverse the indices so they are in column-major order. loopNest.oneBasedIndices[dim--] = loopNest.innerLoop.getInductionVar(); diff --git a/flang/test/HLFIR/extents-of-shape-of.f90 b/flang/test/HLFIR/extents-of-shape-of.f90 index e9310ca..fda9e37 100644 --- a/flang/test/HLFIR/extents-of-shape-of.f90 +++ b/flang/test/HLFIR/extents-of-shape-of.f90 @@ -20,8 +20,8 @@ end subroutine ! CHECK-HLFIR-NEXT: %[[EXT0:.*]] = arith.constant 2 : index ! CHECK-HLFIR-NEXT: %[[EXT1:.*]] = hlfir.get_extent %[[SHAPE]] {dim = 1 : index} : (!fir.shape<2>) -> index ! CHECK-HLFIR-NEXT: %[[C1:.*]] = arith.constant 1 : index -! CHECK-HLFIR-NEXT: fir.do_loop %[[ARG2:.*]] = %[[C1]] to %[[EXT1]] step %[[C1]] { -! CHECK-HLFIR-NEXT: fir.do_loop %[[ARG3:.*]] = %[[C1]] to %[[EXT0]] step %[[C1]] { +! CHECK-HLFIR-NEXT: fir.do_loop %[[ARG2:.*]] = %[[C1]] to %[[EXT1]] step %[[C1]] unordered { +! CHECK-HLFIR-NEXT: fir.do_loop %[[ARG3:.*]] = %[[C1]] to %[[EXT0]] step %[[C1]] unordered { ! CHECK-HLFIR-NEXT: %[[ELE:.*]] = hlfir.apply %[[MUL]], %[[ARG3]], %[[ARG2]] : (!hlfir.expr<2x?xf32>, index, index) -> f32 ! CHECK-HLFIR-NEXT: %[[ASSOC:.*]]:3 = hlfir.associate %[[ELE]] {uniq_name = "adapt.valuebyref"} : (f32) -> (!fir.ref, !fir.ref, i1) ! CHECK-HLFIR-NEXT: fir.call @@ -41,8 +41,8 @@ end subroutine ! CHECK-FIR: %[[SHAPE:.*]] = fir.shape %[[DIMS0]]#1, %[[DIMS1]]#1 ! CHECK-FIR-NEXT: %[[C2:.*]] = arith.constant 2 : index ! CHECK-FIR-NEXT: %[[C1_1:.*]] = arith.constant 1 : index -! CHECK-FIR-NEXT: fir.do_loop %[[ARG2:.*]] = %[[C1_1]] to %[[DIMS1]]#1 step %[[C1_1]] { -! CHECK-FIR-NEXT: fir.do_loop %[[ARG3:.*]] = %[[C1_1]] to %[[C2]] step %[[C1_1]] { +! CHECK-FIR-NEXT: fir.do_loop %[[ARG2:.*]] = %[[C1_1]] to %[[DIMS1]]#1 step %[[C1_1]] unordered { +! CHECK-FIR-NEXT: fir.do_loop %[[ARG3:.*]] = %[[C1_1]] to %[[C2]] step %[[C1_1]] unordered { ! ... ! CHECK-ALL: return diff --git a/flang/test/Lower/HLFIR/calls-optional.f90 b/flang/test/Lower/HLFIR/calls-optional.f90 index b25ccbd..c1cb717 100644 --- a/flang/test/Lower/HLFIR/calls-optional.f90 +++ b/flang/test/Lower/HLFIR/calls-optional.f90 @@ -77,7 +77,7 @@ end subroutine ! CHECK: %[[VAL_10:.*]]:3 = fir.box_dims %[[VAL_2]]#0, %[[VAL_9]] : (!fir.box>, index) -> (index, index, index) ! CHECK: %[[VAL_11:.*]] = fir.load %[[VAL_3]]#0 : !fir.ref>>> ! CHECK: %[[VAL_12:.*]] = arith.constant 1 : index -! CHECK: fir.do_loop %[[VAL_13:.*]] = %[[VAL_12]] to %[[VAL_10]]#1 step %[[VAL_12]] { +! CHECK: fir.do_loop %[[VAL_13:.*]] = %[[VAL_12]] to %[[VAL_10]]#1 step %[[VAL_12]] unordered { ! CHECK: %[[VAL_14:.*]] = hlfir.designate %[[VAL_2]]#0 (%[[VAL_13]]) : (!fir.box>, index) -> !fir.ref ! CHECK: %[[VAL_15:.*]] = fir.if %[[VAL_8]] -> (!fir.ref) { ! CHECK: %[[VAL_16:.*]] = arith.constant 0 : index @@ -109,7 +109,7 @@ end subroutine ! CHECK: %[[VAL_2:.*]] = arith.constant 0 : index ! CHECK: %[[VAL_3:.*]]:3 = fir.box_dims %[[VAL_1]]#0, %[[VAL_2]] : (!fir.box>, index) -> (index, index, index) ! CHECK: %[[VAL_4:.*]] = arith.constant 1 : index -! CHECK: fir.do_loop %[[VAL_5:.*]] = %[[VAL_4]] to %[[VAL_3]]#1 step %[[VAL_4]] { +! CHECK: fir.do_loop %[[VAL_5:.*]] = %[[VAL_4]] to %[[VAL_3]]#1 step %[[VAL_4]] unordered { ! CHECK: %[[VAL_6:.*]] = hlfir.designate %[[VAL_1]]#0 (%[[VAL_5]]) : (!fir.box>, index) -> !fir.ref ! CHECK: fir.call @_QPelem_takes_one_optional(%[[VAL_6]]) {{.*}} : (!fir.ref) -> () ! CHECK: } @@ -131,7 +131,7 @@ end subroutine ! CHECK: %[[VAL_5:.*]] = arith.constant 0 : index ! CHECK: %[[VAL_6:.*]]:3 = fir.box_dims %[[VAL_2]]#0, %[[VAL_5]] : (!fir.box>, index) -> (index, index, index) ! CHECK: %[[VAL_7:.*]] = arith.constant 1 : index -! CHECK: fir.do_loop %[[VAL_8:.*]] = %[[VAL_7]] to %[[VAL_6]]#1 step %[[VAL_7]] { +! CHECK: fir.do_loop %[[VAL_8:.*]] = %[[VAL_7]] to %[[VAL_6]]#1 step %[[VAL_7]] unordered { ! CHECK: %[[VAL_9:.*]] = hlfir.designate %[[VAL_2]]#0 (%[[VAL_8]]) : (!fir.box>, index) -> !fir.ref ! CHECK: %[[VAL_10:.*]] = fir.embox %[[VAL_9]] : (!fir.ref) -> !fir.box ! CHECK: %[[VAL_11:.*]] = fir.rebox %[[VAL_10]] : (!fir.box) -> !fir.class diff --git a/flang/test/Lower/HLFIR/elemental-user-procedure-ref.f90 b/flang/test/Lower/HLFIR/elemental-user-procedure-ref.f90 index b22cedf..cd0425f 100644 --- a/flang/test/Lower/HLFIR/elemental-user-procedure-ref.f90 +++ b/flang/test/Lower/HLFIR/elemental-user-procedure-ref.f90 @@ -90,9 +90,59 @@ end subroutine ! CHECK: %[[VAL_4:.*]] = arith.constant 20 : index ! CHECK: %[[VAL_6:.*]]:2 = hlfir.declare %[[VAL_1:.*]](%[[VAL_5:[^)]*]]) {{.*}}y ! CHECK: %[[VAL_7:.*]] = arith.constant 1 : index -! CHECK: fir.do_loop %[[VAL_8:.*]] = %[[VAL_7]] to %[[VAL_4]] step %[[VAL_7]] { -! CHECK: fir.do_loop %[[VAL_9:.*]] = %[[VAL_7]] to %[[VAL_3]] step %[[VAL_7]] { +! CHECK: fir.do_loop %[[VAL_8:.*]] = %[[VAL_7]] to %[[VAL_4]] step %[[VAL_7]] unordered { +! CHECK: fir.do_loop %[[VAL_9:.*]] = %[[VAL_7]] to %[[VAL_3]] step %[[VAL_7]] unordered { ! CHECK: %[[VAL_10:.*]] = hlfir.designate %[[VAL_6]]#0 (%[[VAL_9]], %[[VAL_8]]) : (!fir.ref>, index, index) -> !fir.ref ! CHECK: fir.call @_QPelem_sub(%[[VAL_2]]#1, %[[VAL_10]]) fastmath : (!fir.ref, !fir.ref) -> () ! CHECK: } ! CHECK: } + +subroutine impure_elemental(x) + real :: x(10, 20) + interface + impure elemental subroutine impure_elem(a) + real, intent(in) :: a + end subroutine + end interface + call impure_elem(x) +end subroutine +! CHECK-LABEL: func.func @_QPimpure_elemental( +! CHECK-SAME: %[[VAL_0:.*]]: !fir.ref> {fir.bindc_name = "x"}) { +! CHECK: %[[VAL_1:.*]] = arith.constant 10 : index +! CHECK: %[[VAL_2:.*]] = arith.constant 20 : index +! CHECK: %[[VAL_3:.*]] = fir.shape %[[VAL_1]], %[[VAL_2]] : (index, index) -> !fir.shape<2> +! CHECK: %[[VAL_4:.*]]:2 = hlfir.declare %[[VAL_0]](%[[VAL_3]]) {uniq_name = "_QFimpure_elementalEx"} : (!fir.ref>, !fir.shape<2>) -> (!fir.ref>, !fir.ref>) +! CHECK: %[[VAL_5:.*]] = arith.constant 1 : index +! CHECK: fir.do_loop %[[VAL_6:.*]] = %[[VAL_5]] to %[[VAL_2]] step %[[VAL_5]] { +! CHECK: fir.do_loop %[[VAL_7:.*]] = %[[VAL_5]] to %[[VAL_1]] step %[[VAL_5]] { +! CHECK: %[[VAL_8:.*]] = hlfir.designate %[[VAL_4]]#0 (%[[VAL_7]], %[[VAL_6]]) : (!fir.ref>, index, index) -> !fir.ref +! CHECK: fir.call @_QPimpure_elem(%[[VAL_8]]) fastmath : (!fir.ref) -> () +! CHECK: } +! CHECK: } +! CHECK: return +! CHECK: } + +subroutine ordered_elemental(x) + real :: x(10, 20) + interface + elemental subroutine ordered_elem(a) + real, intent(inout) :: a + end subroutine + end interface + call ordered_elem(x) +end subroutine +! CHECK-LABEL: func.func @_QPordered_elemental( +! CHECK-SAME: %[[VAL_0:.*]]: !fir.ref> {fir.bindc_name = "x"}) { +! CHECK: %[[VAL_1:.*]] = arith.constant 10 : index +! CHECK: %[[VAL_2:.*]] = arith.constant 20 : index +! CHECK: %[[VAL_3:.*]] = fir.shape %[[VAL_1]], %[[VAL_2]] : (index, index) -> !fir.shape<2> +! CHECK: %[[VAL_4:.*]]:2 = hlfir.declare %[[VAL_0]](%[[VAL_3]]) {uniq_name = "_QFordered_elementalEx"} : (!fir.ref>, !fir.shape<2>) -> (!fir.ref>, !fir.ref>) +! CHECK: %[[VAL_5:.*]] = arith.constant 1 : index +! CHECK: fir.do_loop %[[VAL_6:.*]] = %[[VAL_5]] to %[[VAL_2]] step %[[VAL_5]] { +! CHECK: fir.do_loop %[[VAL_7:.*]] = %[[VAL_5]] to %[[VAL_1]] step %[[VAL_5]] { +! CHECK: %[[VAL_8:.*]] = hlfir.designate %[[VAL_4]]#0 (%[[VAL_7]], %[[VAL_6]]) : (!fir.ref>, index, index) -> !fir.ref +! CHECK: fir.call @_QPordered_elem(%[[VAL_8]]) fastmath : (!fir.ref) -> () +! CHECK: } +! CHECK: } +! CHECK: return +! CHECK: } -- 2.7.4