From 316714ce0ea992d738371bedafa1bee6c6aae9ba Mon Sep 17 00:00:00 2001 From: yanming Date: Tue, 18 Nov 2025 18:15:16 +0800 Subject: [PATCH 1/4] [flang][fir] Convert `fir.do_loop` with the unordered attribute to `scf.parallel`. --- flang/lib/Optimizer/Transforms/FIRToSCF.cpp | 41 +++++++++++++-------- flang/test/Fir/FirToSCF/do-loop.fir | 32 ++++++++++++++++ 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/flang/lib/Optimizer/Transforms/FIRToSCF.cpp b/flang/lib/Optimizer/Transforms/FIRToSCF.cpp index 70d6ebbcb039c..291147886a75e 100644 --- a/flang/lib/Optimizer/Transforms/FIRToSCF.cpp +++ b/flang/lib/Optimizer/Transforms/FIRToSCF.cpp @@ -30,6 +30,7 @@ struct DoLoopConversion : public mlir::OpRewritePattern { mlir::PatternRewriter &rewriter) const override { mlir::Location loc = doLoopOp.getLoc(); bool hasFinalValue = doLoopOp.getFinalValue().has_value(); + bool isUnordered = doLoopOp.getUnordered().has_value(); // Get loop values from the DoLoopOp mlir::Value low = doLoopOp.getLowerBound(); @@ -53,37 +54,47 @@ struct DoLoopConversion : public mlir::OpRewritePattern { mlir::arith::DivSIOp::create(rewriter, loc, distance, step); auto zero = mlir::arith::ConstantIndexOp::create(rewriter, loc, 0); auto one = mlir::arith::ConstantIndexOp::create(rewriter, loc, 1); - auto scfForOp = - mlir::scf::ForOp::create(rewriter, loc, zero, tripCount, one, iterArgs); + // Create the scf.for or scf.parallel operation + mlir::Operation *scfLoopOp = nullptr; + if (isUnordered) { + scfLoopOp = mlir::scf::ParallelOp::create(rewriter, loc, {zero}, + {tripCount}, {one}, iterArgs); + } else { + scfLoopOp = mlir::scf::ForOp::create(rewriter, loc, zero, tripCount, one, + iterArgs); + } + + // Move the body of the fir.do_loop to the scf.for or scf.parallel auto &loopOps = doLoopOp.getBody()->getOperations(); auto resultOp = mlir::cast(doLoopOp.getBody()->getTerminator()); auto results = resultOp.getOperands(); - mlir::Block *loweredBody = scfForOp.getBody(); + auto scfLoopLikeOp = mlir::cast(scfLoopOp); + mlir::Block &scfLoopBody = scfLoopLikeOp.getLoopRegions().front()->front(); - loweredBody->getOperations().splice(loweredBody->begin(), loopOps, - loopOps.begin(), - std::prev(loopOps.end())); + scfLoopBody.getOperations().splice(scfLoopBody.begin(), loopOps, + loopOps.begin(), + std::prev(loopOps.end())); - rewriter.setInsertionPointToStart(loweredBody); + rewriter.setInsertionPointToStart(&scfLoopBody); mlir::Value iv = mlir::arith::MulIOp::create( - rewriter, loc, scfForOp.getInductionVar(), step); + rewriter, loc, scfLoopLikeOp.getSingleInductionVar().value(), step); iv = mlir::arith::AddIOp::create(rewriter, loc, low, iv); if (!results.empty()) { - rewriter.setInsertionPointToEnd(loweredBody); + rewriter.setInsertionPointToEnd(&scfLoopBody); mlir::scf::YieldOp::create(rewriter, resultOp->getLoc(), results); } doLoopOp.getInductionVar().replaceAllUsesWith(iv); - rewriter.replaceAllUsesWith(doLoopOp.getRegionIterArgs(), - hasFinalValue - ? scfForOp.getRegionIterArgs().drop_front() - : scfForOp.getRegionIterArgs()); + rewriter.replaceAllUsesWith( + doLoopOp.getRegionIterArgs(), + hasFinalValue ? scfLoopLikeOp.getRegionIterArgs().drop_front() + : scfLoopLikeOp.getRegionIterArgs()); // Copy all the attributes from the old to new op. - scfForOp->setAttrs(doLoopOp->getAttrs()); - rewriter.replaceOp(doLoopOp, scfForOp); + scfLoopOp->setAttrs(doLoopOp->getAttrs()); + rewriter.replaceOp(doLoopOp, scfLoopOp); return mlir::success(); } }; diff --git a/flang/test/Fir/FirToSCF/do-loop.fir b/flang/test/Fir/FirToSCF/do-loop.fir index 812497c8d0c74..bb245bf06ac3e 100644 --- a/flang/test/Fir/FirToSCF/do-loop.fir +++ b/flang/test/Fir/FirToSCF/do-loop.fir @@ -146,6 +146,38 @@ func.func @loop_with_final_value(%arg0: !fir.ref>, %arg1: !f return } +// CHECK-LABEL: func.func @loop_with_unordered_attr( +// CHECK-SAME: %[[ARG0:.*]]: !fir.ref>) { +// CHECK: %[[CONSTANT_0:.*]] = arith.constant 1 : index +// CHECK: %[[CONSTANT_1:.*]] = arith.constant 100 : index +// CHECK: %[[SHAPE_0:.*]] = fir.shape %[[CONSTANT_1]] : (index) -> !fir.shape<1> +// CHECK: %[[CONSTANT_2:.*]] = arith.constant 1 : i32 +// CHECK: %[[SUBI_0:.*]] = arith.subi %[[CONSTANT_1]], %[[CONSTANT_0]] : index +// CHECK: %[[ADDI_0:.*]] = arith.addi %[[SUBI_0]], %[[CONSTANT_0]] : index +// CHECK: %[[DIVSI_0:.*]] = arith.divsi %[[ADDI_0]], %[[CONSTANT_0]] : index +// CHECK: %[[CONSTANT_3:.*]] = arith.constant 0 : index +// CHECK: %[[CONSTANT_4:.*]] = arith.constant 1 : index +// CHECK: scf.parallel (%[[VAL_0:.*]]) = (%[[CONSTANT_3]]) to (%[[DIVSI_0]]) step (%[[CONSTANT_4]]) { +// CHECK: %[[MULI_0:.*]] = arith.muli %[[VAL_0]], %[[CONSTANT_0]] : index +// CHECK: %[[ADDI_1:.*]] = arith.addi %[[CONSTANT_0]], %[[MULI_0]] : index +// CHECK: %[[ARRAY_COOR_0:.*]] = fir.array_coor %[[ARG0]](%[[SHAPE_0]]) %[[ADDI_1]] : (!fir.ref>, !fir.shape<1>, index) -> !fir.ref +// CHECK: fir.store %[[CONSTANT_2]] to %[[ARRAY_COOR_0]] : !fir.ref +// CHECK: scf.reduce +// CHECK: } {unordered} +// CHECK: return +// CHECK: } +func.func @loop_with_unordered_attr(%arg0: !fir.ref>) { + %c1 = arith.constant 1 : index + %c100 = arith.constant 100 : index + %0 = fir.shape %c100 : (index) -> !fir.shape<1> + %c1_i32 = arith.constant 1 : i32 + fir.do_loop %arg1 = %c1 to %c100 step %c1 unordered { + %1 = fir.array_coor %arg0(%0) %arg1 : (!fir.ref>, !fir.shape<1>, index) -> !fir.ref + fir.store %c1_i32 to %1 : !fir.ref + } + return +} + // CHECK-LABEL: func.func @loop_with_attribute( // CHECK-SAME: %[[ARG0:.*]]: !fir.ref>, // CHECK-SAME: %[[ARG1:.*]]: !fir.ref) { From 45a330f3fdebd42310e7b5cde31f63bcae14c20d Mon Sep 17 00:00:00 2001 From: yanming Date: Tue, 18 Nov 2025 18:31:57 +0800 Subject: [PATCH 2/4] Only keep attributes that are valid for scf loop op. --- flang/lib/Optimizer/Transforms/FIRToSCF.cpp | 6 +++-- flang/test/Fir/FirToSCF/do-loop.fir | 25 +++++++++++++++++---- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/flang/lib/Optimizer/Transforms/FIRToSCF.cpp b/flang/lib/Optimizer/Transforms/FIRToSCF.cpp index 291147886a75e..662bdbf28a6dc 100644 --- a/flang/lib/Optimizer/Transforms/FIRToSCF.cpp +++ b/flang/lib/Optimizer/Transforms/FIRToSCF.cpp @@ -92,8 +92,10 @@ struct DoLoopConversion : public mlir::OpRewritePattern { hasFinalValue ? scfLoopLikeOp.getRegionIterArgs().drop_front() : scfLoopLikeOp.getRegionIterArgs()); - // Copy all the attributes from the old to new op. - scfLoopOp->setAttrs(doLoopOp->getAttrs()); + // Copy loop annotations from the fir.do_loop to scf loop op. + if (auto ann = doLoopOp.getLoopAnnotation()) + scfLoopOp->setAttr("loop_annotation", *ann); + rewriter.replaceOp(doLoopOp, scfLoopOp); return mlir::success(); } diff --git a/flang/test/Fir/FirToSCF/do-loop.fir b/flang/test/Fir/FirToSCF/do-loop.fir index bb245bf06ac3e..aa8526febeefc 100644 --- a/flang/test/Fir/FirToSCF/do-loop.fir +++ b/flang/test/Fir/FirToSCF/do-loop.fir @@ -1,4 +1,4 @@ -// RUN: fir-opt %s --fir-to-scf | FileCheck %s +// RUN: fir-opt %s --fir-to-scf --split-input-file | FileCheck %s // CHECK-LABEL: func.func @simple_loop( // CHECK-SAME: %[[ARG0:.*]]: !fir.ref>) { @@ -31,6 +31,8 @@ func.func @simple_loop(%arg0: !fir.ref>) { return } +// ----- + // CHECK-LABEL: func.func @loop_with_negtive_step( // CHECK-SAME: %[[ARG0:.*]]: !fir.ref>) { // CHECK: %[[VAL_0:.*]] = arith.constant 100 : index @@ -64,6 +66,8 @@ func.func @loop_with_negtive_step(%arg0: !fir.ref>) { return } +// ----- + // CHECK-LABEL: func.func @loop_with_results( // CHECK-SAME: %[[ARG0:.*]]: !fir.ref>, // CHECK-SAME: %[[ARG1:.*]]: !fir.ref) { @@ -102,6 +106,8 @@ func.func @loop_with_results(%arg0: !fir.ref>, %arg1: !fir.r return } +// ----- + // CHECK-LABEL: func.func @loop_with_final_value( // CHECK-SAME: %[[ARG0:.*]]: !fir.ref>, // CHECK-SAME: %[[ARG1:.*]]: !fir.ref) { @@ -146,6 +152,8 @@ func.func @loop_with_final_value(%arg0: !fir.ref>, %arg1: !f return } +// ----- + // CHECK-LABEL: func.func @loop_with_unordered_attr( // CHECK-SAME: %[[ARG0:.*]]: !fir.ref>) { // CHECK: %[[CONSTANT_0:.*]] = arith.constant 1 : index @@ -163,7 +171,7 @@ func.func @loop_with_final_value(%arg0: !fir.ref>, %arg1: !f // CHECK: %[[ARRAY_COOR_0:.*]] = fir.array_coor %[[ARG0]](%[[SHAPE_0]]) %[[ADDI_1]] : (!fir.ref>, !fir.shape<1>, index) -> !fir.ref // CHECK: fir.store %[[CONSTANT_2]] to %[[ARRAY_COOR_0]] : !fir.ref // CHECK: scf.reduce -// CHECK: } {unordered} +// CHECK: } // CHECK: return // CHECK: } func.func @loop_with_unordered_attr(%arg0: !fir.ref>) { @@ -178,6 +186,10 @@ func.func @loop_with_unordered_attr(%arg0: !fir.ref>) { return } +// ----- + +// CHECK: #[[$ATTR_0:.+]] = #llvm.loop_vectorize +// CHECK: #[[$ATTR_1:.+]] = #llvm.loop_annotation // CHECK-LABEL: func.func @loop_with_attribute( // CHECK-SAME: %[[ARG0:.*]]: !fir.ref>, // CHECK-SAME: %[[ARG1:.*]]: !fir.ref) { @@ -199,16 +211,19 @@ func.func @loop_with_unordered_attr(%arg0: !fir.ref>) { // CHECK: %[[VAL_15:.*]] = fir.load %[[VAL_3]] : !fir.ref // CHECK: %[[VAL_16:.*]] = arith.addi %[[VAL_15]], %[[VAL_14]] : i32 // CHECK: fir.store %[[VAL_16]] to %[[VAL_3]] : !fir.ref -// CHECK: } {operandSegmentSizes = array, reduceAttrs = [#fir.reduce_attr]} +// CHECK: } {loop_annotation = #[[$ATTR_1]]} // CHECK: return // CHECK: } + +#loop_vectorize = #llvm.loop_vectorize +#loop_annotation = #llvm.loop_annotation func.func @loop_with_attribute(%arg0: !fir.ref>, %arg1: !fir.ref) { %c1 = arith.constant 1 : index %c0_i32 = arith.constant 0 : i32 %c100 = arith.constant 100 : index %0 = fir.alloca i32 %1 = fir.shape %c100 : (index) -> !fir.shape<1> - fir.do_loop %arg2 = %c1 to %c100 step %c1 reduce(#fir.reduce_attr -> %0 : !fir.ref) { + fir.do_loop %arg2 = %c1 to %c100 step %c1 attributes {loopAnnotation = #loop_annotation} { %2 = fir.array_coor %arg0(%1) %arg2 : (!fir.ref>, !fir.shape<1>, index) -> !fir.ref %3 = fir.load %2 : !fir.ref %4 = fir.load %0 : !fir.ref @@ -219,6 +234,8 @@ func.func @loop_with_attribute(%arg0: !fir.ref>, %arg1: !fir return } +// ----- + // CHECK-LABEL: func.func @nested_loop( // CHECK-SAME: %[[ARG0:.*]]: !fir.ref>) { // CHECK: %[[VAL_0:.*]] = arith.constant 1 : index From 1446f96a2a5f96a529643e150186a332accfbe34 Mon Sep 17 00:00:00 2001 From: yanming Date: Fri, 21 Nov 2025 13:14:52 +0800 Subject: [PATCH 3/4] Add an option to control this conversion. --- .../flang/Optimizer/Transforms/Passes.h | 3 +++ .../flang/Optimizer/Transforms/Passes.td | 4 ++++ flang/lib/Optimizer/Transforms/FIRToSCF.cpp | 20 ++++++++++++++++--- flang/test/Fir/FirToSCF/do-loop.fir | 8 +++++--- 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/flang/include/flang/Optimizer/Transforms/Passes.h b/flang/include/flang/Optimizer/Transforms/Passes.h index 6f5dff4687cbb..f83a1559fa016 100644 --- a/flang/include/flang/Optimizer/Transforms/Passes.h +++ b/flang/include/flang/Optimizer/Transforms/Passes.h @@ -53,6 +53,9 @@ std::unique_ptr createVScaleAttrPass(); std::unique_ptr createVScaleAttrPass(std::pair vscaleAttr); +void populateFIRToSCFRewrites(mlir::RewritePatternSet &patterns, + bool parallelUnordered = false); + void populateCfgConversionRewrites(mlir::RewritePatternSet &patterns, bool forceLoopToExecuteOnce = false, bool setNSW = true); diff --git a/flang/include/flang/Optimizer/Transforms/Passes.td b/flang/include/flang/Optimizer/Transforms/Passes.td index bb2509b1747d5..701fb5dbd8f8e 100644 --- a/flang/include/flang/Optimizer/Transforms/Passes.td +++ b/flang/include/flang/Optimizer/Transforms/Passes.td @@ -85,6 +85,10 @@ def FIRToSCFPass : Pass<"fir-to-scf"> { let dependentDialects = [ "fir::FIROpsDialect", "mlir::scf::SCFDialect" ]; + let options = [Option<"parallelUnordered", "parallel-unordered", "bool", + /*default=*/"false", + "Whether to convert `fir.do_loop` with the unordered " + "attribute to `scf.parallel`. Defaults to false.">]; } def AnnotateConstantOperands : Pass<"annotate-constant"> { diff --git a/flang/lib/Optimizer/Transforms/FIRToSCF.cpp b/flang/lib/Optimizer/Transforms/FIRToSCF.cpp index 662bdbf28a6dc..e72ee333101f5 100644 --- a/flang/lib/Optimizer/Transforms/FIRToSCF.cpp +++ b/flang/lib/Optimizer/Transforms/FIRToSCF.cpp @@ -25,6 +25,12 @@ class FIRToSCFPass : public fir::impl::FIRToSCFPassBase { struct DoLoopConversion : public mlir::OpRewritePattern { using OpRewritePattern::OpRewritePattern; + DoLoopConversion(mlir::MLIRContext *context, + bool parallelUnorderedLoop = false, + mlir::PatternBenefit benefit = 1) + : OpRewritePattern(context, benefit), + parallelUnorderedLoop(parallelUnorderedLoop) {} + mlir::LogicalResult matchAndRewrite(fir::DoLoopOp doLoopOp, mlir::PatternRewriter &rewriter) const override { @@ -57,7 +63,7 @@ struct DoLoopConversion : public mlir::OpRewritePattern { // Create the scf.for or scf.parallel operation mlir::Operation *scfLoopOp = nullptr; - if (isUnordered) { + if (isUnordered && parallelUnorderedLoop) { scfLoopOp = mlir::scf::ParallelOp::create(rewriter, loc, {zero}, {tripCount}, {one}, iterArgs); } else { @@ -99,6 +105,9 @@ struct DoLoopConversion : public mlir::OpRewritePattern { rewriter.replaceOp(doLoopOp, scfLoopOp); return mlir::success(); } + +private: + bool parallelUnorderedLoop; }; struct IterWhileConversion : public mlir::OpRewritePattern { @@ -210,10 +219,15 @@ struct IfConversion : public mlir::OpRewritePattern { }; } // namespace +void fir::populateFIRToSCFRewrites(mlir::RewritePatternSet &patterns, + bool parallelUnordered) { + patterns.add(patterns.getContext()); + patterns.add(patterns.getContext(), parallelUnordered); +} + void FIRToSCFPass::runOnOperation() { mlir::RewritePatternSet patterns(&getContext()); - patterns.add( - patterns.getContext()); + fir::populateFIRToSCFRewrites(patterns, parallelUnordered); walkAndApplyPatterns(getOperation(), std::move(patterns)); } diff --git a/flang/test/Fir/FirToSCF/do-loop.fir b/flang/test/Fir/FirToSCF/do-loop.fir index aa8526febeefc..8862a4c2969e8 100644 --- a/flang/test/Fir/FirToSCF/do-loop.fir +++ b/flang/test/Fir/FirToSCF/do-loop.fir @@ -1,4 +1,5 @@ -// RUN: fir-opt %s --fir-to-scf --split-input-file | FileCheck %s +// RUN: fir-opt %s --fir-to-scf --split-input-file | FileCheck %s --check-prefixes=CHECK,NO-PARALLEL +// RUN: fir-opt %s --fir-to-scf='parallel-unordered' --split-input-file | FileCheck %s --check-prefixes=CHECK,PARALLEL // CHECK-LABEL: func.func @simple_loop( // CHECK-SAME: %[[ARG0:.*]]: !fir.ref>) { @@ -165,12 +166,13 @@ func.func @loop_with_final_value(%arg0: !fir.ref>, %arg1: !f // CHECK: %[[DIVSI_0:.*]] = arith.divsi %[[ADDI_0]], %[[CONSTANT_0]] : index // CHECK: %[[CONSTANT_3:.*]] = arith.constant 0 : index // CHECK: %[[CONSTANT_4:.*]] = arith.constant 1 : index -// CHECK: scf.parallel (%[[VAL_0:.*]]) = (%[[CONSTANT_3]]) to (%[[DIVSI_0]]) step (%[[CONSTANT_4]]) { +// PARALLEL: scf.parallel (%[[VAL_0:.*]]) = (%[[CONSTANT_3]]) to (%[[DIVSI_0]]) step (%[[CONSTANT_4]]) { +// NO-PARALLEL: scf.for %[[VAL_0:.*]] = %[[CONSTANT_3]] to %[[DIVSI_0]] step %[[CONSTANT_4]] { // CHECK: %[[MULI_0:.*]] = arith.muli %[[VAL_0]], %[[CONSTANT_0]] : index // CHECK: %[[ADDI_1:.*]] = arith.addi %[[CONSTANT_0]], %[[MULI_0]] : index // CHECK: %[[ARRAY_COOR_0:.*]] = fir.array_coor %[[ARG0]](%[[SHAPE_0]]) %[[ADDI_1]] : (!fir.ref>, !fir.shape<1>, index) -> !fir.ref // CHECK: fir.store %[[CONSTANT_2]] to %[[ARRAY_COOR_0]] : !fir.ref -// CHECK: scf.reduce +// PARALLEL: scf.reduce // CHECK: } // CHECK: return // CHECK: } From 4cb2853562409d31682051059a26a526d7370adc Mon Sep 17 00:00:00 2001 From: Ming Yan Date: Tue, 25 Nov 2025 22:24:58 +0800 Subject: [PATCH 4/4] [NFC] Update comments. --- flang/include/flang/Optimizer/Transforms/Passes.td | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flang/include/flang/Optimizer/Transforms/Passes.td b/flang/include/flang/Optimizer/Transforms/Passes.td index 701fb5dbd8f8e..0f613584c6e17 100644 --- a/flang/include/flang/Optimizer/Transforms/Passes.td +++ b/flang/include/flang/Optimizer/Transforms/Passes.td @@ -87,8 +87,8 @@ def FIRToSCFPass : Pass<"fir-to-scf"> { ]; let options = [Option<"parallelUnordered", "parallel-unordered", "bool", /*default=*/"false", - "Whether to convert `fir.do_loop` with the unordered " - "attribute to `scf.parallel`. Defaults to false.">]; + "Allow converting a fir.do_loop with the `unordered` " + "attribute to scf.parallel (experimental).">]; } def AnnotateConstantOperands : Pass<"annotate-constant"> {