[flang-commits] [flang] [flang] Inline max/minval according to -ffp-maxmin-behavior. (PR #185148)
via flang-commits
flang-commits at lists.llvm.org
Fri Mar 6 19:37:10 PST 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-fir-hlfir
Author: Slava Zakharin (vzakhari)
<details>
<summary>Changes</summary>
This patch takes into account the option setting when inlining
max/minval intrinsics. It is not an NFC change for Flang, because:
* Inlining for integer types now uses arith.max/minsi operations.
* We do not mark the reduction loops as `unordered`
under `reassoc` FMF. I think this was not quite correct.
Otherwise, the default Legacy setting should produce the same
MLIR as before.
---
Patch is 73.95 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/185148.diff
7 Files Affected:
- (modified) flang/include/flang/Optimizer/HLFIR/Passes.h (+1)
- (modified) flang/include/flang/Optimizer/HLFIR/Passes.td (+16-1)
- (modified) flang/lib/Optimizer/HLFIR/Transforms/SimplifyHLFIRIntrinsics.cpp (+161-35)
- (modified) flang/lib/Optimizer/Passes/Pipelines.cpp (+6-4)
- (added) flang/test/HLFIR/simplify-hlfir-intrinsics-maxmin.fir (+557)
- (modified) flang/test/HLFIR/simplify-hlfir-intrinsics-maxval.fir (+7-7)
- (modified) flang/test/HLFIR/simplify-hlfir-intrinsics-minval.fir (+7-7)
``````````diff
diff --git a/flang/include/flang/Optimizer/HLFIR/Passes.h b/flang/include/flang/Optimizer/HLFIR/Passes.h
index 83388d0527e19..2ae1ecd51391e 100644
--- a/flang/include/flang/Optimizer/HLFIR/Passes.h
+++ b/flang/include/flang/Optimizer/HLFIR/Passes.h
@@ -13,6 +13,7 @@
#ifndef FORTRAN_OPTIMIZER_HLFIR_PASSES_H
#define FORTRAN_OPTIMIZER_HLFIR_PASSES_H
+#include "flang/Support/FPMaxminBehavior.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/Pass/Pass.h"
#include "mlir/Pass/PassRegistry.h"
diff --git a/flang/include/flang/Optimizer/HLFIR/Passes.td b/flang/include/flang/Optimizer/HLFIR/Passes.td
index bfff458f7a6c5..dbce6c3c4ebc9 100644
--- a/flang/include/flang/Optimizer/HLFIR/Passes.td
+++ b/flang/include/flang/Optimizer/HLFIR/Passes.td
@@ -58,7 +58,22 @@ def SimplifyHLFIRIntrinsics : Pass<"simplify-hlfir-intrinsics"> {
"argument of hlfir.eval_in_mem, which may block "
"some existing MLIR transformations (e.g. CSE) "
"that otherwise would have been possible across "
- "the hlfir.matmul.">];
+ "the hlfir.matmul.">,
+ Option<"fpMaxminBehavior", "fp-maxmin-behavior",
+ "Fortran::common::FPMaxminBehavior",
+ /*default=*/"Fortran::common::FPMaxminBehavior::Legacy",
+ "This option defines the FP max/min behavior.",
+ [{::llvm::cl::values(
+ clEnumValN(Fortran::common::FPMaxminBehavior::Legacy,
+ "legacy", "cmp+select"),
+ clEnumValN(Fortran::common::FPMaxminBehavior::Portable,
+ "portable", "same as legacy, except that "
+ "arith.max/minnumf may be used when nnan "
+ "and nsz fast math flags are enabled"),
+ clEnumValN(Fortran::common::FPMaxminBehavior::Extremum,
+ "extremum", "arith.max/minimumf"),
+ clEnumValN(Fortran::common::FPMaxminBehavior::ExtremeNum,
+ "extremenum", "arith.max/minnumf"))}]>];
}
def ExpressionSimplification : Pass<"hlfir-expression-simplification"> {
diff --git a/flang/lib/Optimizer/HLFIR/Transforms/SimplifyHLFIRIntrinsics.cpp b/flang/lib/Optimizer/HLFIR/Transforms/SimplifyHLFIRIntrinsics.cpp
index cc396520289d2..f47353dc30f64 100644
--- a/flang/lib/Optimizer/HLFIR/Transforms/SimplifyHLFIRIntrinsics.cpp
+++ b/flang/lib/Optimizer/HLFIR/Transforms/SimplifyHLFIRIntrinsics.cpp
@@ -15,6 +15,7 @@
#include "flang/Optimizer/Builder/FIRBuilder.h"
#include "flang/Optimizer/Builder/HLFIRTools.h"
#include "flang/Optimizer/Builder/IntrinsicCall.h"
+#include "flang/Optimizer/Builder/Todo.h"
#include "flang/Optimizer/Dialect/FIRDialect.h"
#include "flang/Optimizer/HLFIR/HLFIRDialect.h"
#include "flang/Optimizer/HLFIR/HLFIROps.h"
@@ -476,8 +477,10 @@ class MinMaxlocAsElementalConverter : public ReductionAsElementalConverter {
using Base = ReductionAsElementalConverter;
public:
- MinMaxlocAsElementalConverter(T op, mlir::PatternRewriter &rewriter)
- : Base{op.getOperation(), rewriter} {}
+ MinMaxlocAsElementalConverter(
+ T op, mlir::PatternRewriter &rewriter,
+ Fortran::common::FPMaxminBehavior fpMaxminBehavior)
+ : Base{op.getOperation(), rewriter}, fpMaxminBehavior{fpMaxminBehavior} {}
private:
virtual mlir::Value getSource() const final { return getOp().getArray(); }
@@ -558,6 +561,12 @@ class MinMaxlocAsElementalConverter : public ReductionAsElementalConverter {
// value to +/-LARGEST; the coordinates are guaranteed to be updated
// properly for non-empty input without NaNs.
bool useIsFirst() const { return getMask() && honorNans(); }
+
+ // Specifies the behavior of max/min idiom.
+ // TODO: for consistency, maxloc/minloc should probably take
+ // this control into account, though, we need to define what
+ // this means exactly.
+ [[maybe_unused]] Fortran::common::FPMaxminBehavior fpMaxminBehavior;
};
template <typename T>
@@ -762,8 +771,10 @@ class MinMaxvalAsElementalConverter
using Base = NumericReductionAsElementalConverterBase<T>;
public:
- MinMaxvalAsElementalConverter(T op, mlir::PatternRewriter &rewriter)
- : Base{op, rewriter} {}
+ MinMaxvalAsElementalConverter(
+ T op, mlir::PatternRewriter &rewriter,
+ Fortran::common::FPMaxminBehavior fpMaxminBehavior)
+ : Base{op, rewriter}, fpMaxminBehavior{fpMaxminBehavior} {}
private:
virtual mlir::LogicalResult isConvertible() const final {
@@ -771,6 +782,12 @@ class MinMaxvalAsElementalConverter
return this->rewriter.notifyMatchFailure(
this->getOp(),
"CHARACTER type is not supported for MINVAL/MAXVAL inlining");
+ if (auto intType =
+ mlir::dyn_cast<mlir::IntegerType>(this->getSourceElementType()))
+ if (intType.isUnsigned())
+ return this->rewriter.notifyMatchFailure(
+ this->getOp(),
+ "UNSIGNED type is not supported for MINVAL/MAXVAL inlining");
return mlir::success();
}
@@ -789,16 +806,21 @@ class MinMaxvalAsElementalConverter
hlfir::Entity elementValue =
hlfir::loadElementAt(loc, builder, array, oneBasedIndices);
mlir::Value currentMinMax = getCurrentMinMax(currentValue);
- mlir::Value cmp =
- genMinMaxComparison<isMax>(loc, builder, elementValue, currentMinMax);
- if (useIsFirst())
- cmp = mlir::arith::OrIOp::create(builder, loc, cmp,
- getIsFirst(currentValue));
- mlir::Value newMinMax = mlir::arith::SelectOp::create(
- builder, loc, cmp, elementValue, currentMinMax);
- result.push_back(newMinMax);
- if (useIsFirst())
- result.push_back(builder.createBool(loc, false));
+ if (isUnordered()) {
+ result.push_back(
+ reduceOneElementUnordered(loc, builder, elementValue, currentMinMax));
+ } else {
+ mlir::Value cmp =
+ genMinMaxComparison<isMax>(loc, builder, elementValue, currentMinMax);
+ if (useIsFirst())
+ cmp = mlir::arith::OrIOp::create(builder, loc, cmp,
+ getIsFirst(currentValue));
+ mlir::Value newMinMax = mlir::arith::SelectOp::create(
+ builder, loc, cmp, elementValue, currentMinMax);
+ result.push_back(newMinMax);
+ if (useIsFirst())
+ result.push_back(builder.createBool(loc, false));
+ }
return result;
}
@@ -830,8 +852,8 @@ class MinMaxvalAsElementalConverter
// Return true iff the input can contain NaNs, and they should be
// honored, such that all-NaNs input must produce NaN result.
bool honorNans() const {
- return !static_cast<bool>(this->getFastMath() &
- mlir::arith::FastMathFlags::nnan);
+ return !mlir::arith::bitEnumContainsAny(this->getFastMath(),
+ mlir::arith::FastMathFlags::nnan);
}
// Return true iff we have to use the loop-carried IsFirst predicate.
@@ -839,9 +861,82 @@ class MinMaxvalAsElementalConverter
// the first elements of the input.
// If NaNs are not honored, we can initialize the starting MIN/MAX
// value to +/-LARGEST.
- bool useIsFirst() const { return this->getMask() && honorNans(); }
+ bool useIsFirst() const {
+ return this->getMask() && honorNans() && !isUnordered();
+ }
+
+ // Return true iff the max/min reduction can be unordered.
+ // This is always true for integer type.
+ // For FP type, this is only true for Extremum and ExtremeNum modes,
+ // and for Portble mode when nnan and nsz are present.
+ // We used to mark the reduction loop unordered when reassoc
+ // FMF was present - it is unclear if it should indeed behave
+ // this way.
+ virtual bool isUnordered() const override {
+ if (mlir::isa<mlir::IntegerType>(this->getSourceElementType()))
+ return true;
+
+ return fpMaxminBehavior == Fortran::common::FPMaxminBehavior::Extremum ||
+ fpMaxminBehavior == Fortran::common::FPMaxminBehavior::ExtremeNum ||
+ (fpMaxminBehavior == Fortran::common::FPMaxminBehavior::Portable &&
+ mlir::arith::bitEnumContainsAll(
+ this->getFastMath(), mlir::arith::FastMathFlags::nnan |
+ mlir::arith::FastMathFlags::nsz));
+ }
+
+ // Generate arith.max/minsi, arith.max/minimumf or arith.max/minnumf to reduce
+ // a single element.
+ mlir::Value reduceOneElementUnordered(mlir::Location loc,
+ fir::FirOpBuilder &builder,
+ mlir::Value elementValue,
+ mlir::Value currentMinMax) {
+ assert(!useIsFirst() &&
+ "unordered max/min reduction must not use first predicate");
+
+ if (mlir::isa<fir::CharacterType>(this->getSourceElementType()))
+ TODO(loc, "max/minval with CHARACTER type");
+
+ if (auto intType =
+ mlir::dyn_cast<mlir::IntegerType>(this->getSourceElementType())) {
+ if (intType.isUnsigned())
+ TODO(loc, "max/minval with UNSIGNED type");
+
+ if constexpr (isMax)
+ return mlir::arith::MaxSIOp::create(builder, loc, elementValue,
+ currentMinMax);
+ else
+ return mlir::arith::MinSIOp::create(builder, loc, elementValue,
+ currentMinMax);
+ }
+
+ if (fpMaxminBehavior == Fortran::common::FPMaxminBehavior::Extremum) {
+ if constexpr (isMax)
+ return mlir::arith::MaximumFOp::create(builder, loc, elementValue,
+ currentMinMax);
+ else
+ return mlir::arith::MinimumFOp::create(builder, loc, elementValue,
+ currentMinMax);
+ }
+
+ if (fpMaxminBehavior == Fortran::common::FPMaxminBehavior::ExtremeNum ||
+ (fpMaxminBehavior == Fortran::common::FPMaxminBehavior::Portable &&
+ mlir::arith::bitEnumContainsAll(
+ this->getFastMath(), mlir::arith::FastMathFlags::nnan |
+ mlir::arith::FastMathFlags::nsz))) {
+ if constexpr (isMax)
+ return mlir::arith::MaxNumFOp::create(builder, loc, elementValue,
+ currentMinMax);
+ else
+ return mlir::arith::MinNumFOp::create(builder, loc, elementValue,
+ currentMinMax);
+ }
+
+ llvm_unreachable("unhandled unordered max/min reduction");
+ }
std::size_t getNumReductions() const { return useIsFirst() ? 2 : 1; }
+
+ Fortran::common::FPMaxminBehavior fpMaxminBehavior;
};
template <typename T>
@@ -854,7 +949,8 @@ MinMaxvalAsElementalConverter<T>::genReductionInitValues(
mlir::Location loc = this->loc;
fir::IfOp ifOp;
- if (!useIsFirst() && honorNans()) {
+ // Unordered max/min reductions use +/-LARGEST always.
+ if (!useIsFirst() && honorNans() && !isUnordered()) {
// Check if we can load the value of the first element in the array
// or its section (for partial reduction).
assert(!this->getMask() &&
@@ -1292,15 +1388,7 @@ class ReductionConversion : public mlir::OpRewritePattern<Op> {
llvm::LogicalResult
matchAndRewrite(Op op, mlir::PatternRewriter &rewriter) const override {
- if constexpr (std::is_same_v<Op, hlfir::MaxlocOp> ||
- std::is_same_v<Op, hlfir::MinlocOp>) {
- MinMaxlocAsElementalConverter<Op> converter(op, rewriter);
- return converter.convert();
- } else if constexpr (std::is_same_v<Op, hlfir::MaxvalOp> ||
- std::is_same_v<Op, hlfir::MinvalOp>) {
- MinMaxvalAsElementalConverter<Op> converter(op, rewriter);
- return converter.convert();
- } else if constexpr (std::is_same_v<Op, hlfir::CountOp>) {
+ if constexpr (std::is_same_v<Op, hlfir::CountOp>) {
CountAsElementalConverter converter(op, rewriter);
return converter.convert();
} else if constexpr (std::is_same_v<Op, hlfir::AllOp> ||
@@ -1318,6 +1406,40 @@ class ReductionConversion : public mlir::OpRewritePattern<Op> {
}
};
+/// Convert an operation that is a partial or total max/min reduction
+/// over an array of values into a reduction loop[-nest]
+/// optionally wrapped into hlfir.elemental.
+template <typename Op>
+class ExtremumReductionConversion : public mlir::OpRewritePattern<Op> {
+public:
+ using mlir::OpRewritePattern<Op>::OpRewritePattern;
+
+ ExtremumReductionConversion(
+ mlir::MLIRContext *ctx,
+ Fortran::common::FPMaxminBehavior fpMaxminBehavior =
+ Fortran::common::FPMaxminBehavior::Legacy)
+ : mlir::OpRewritePattern<Op>{ctx}, fpMaxminBehavior{fpMaxminBehavior} {}
+
+ llvm::LogicalResult
+ matchAndRewrite(Op op, mlir::PatternRewriter &rewriter) const override {
+ if constexpr (std::is_same_v<Op, hlfir::MaxlocOp> ||
+ std::is_same_v<Op, hlfir::MinlocOp>) {
+ MinMaxlocAsElementalConverter<Op> converter(op, rewriter,
+ fpMaxminBehavior);
+ return converter.convert();
+ } else if constexpr (std::is_same_v<Op, hlfir::MaxvalOp> ||
+ std::is_same_v<Op, hlfir::MinvalOp>) {
+ MinMaxvalAsElementalConverter<Op> converter(op, rewriter,
+ fpMaxminBehavior);
+ return converter.convert();
+ }
+ return rewriter.notifyMatchFailure(op, "unexpected reduction operation");
+ }
+
+private:
+ Fortran::common::FPMaxminBehavior fpMaxminBehavior;
+};
+
template <typename Op>
class ArrayShiftConversion : public mlir::OpRewritePattern<Op> {
public:
@@ -1869,10 +1991,10 @@ class ArrayShiftConversion : public mlir::OpRewritePattern<Op> {
// For CSHIFT, shiftVal is the normalized shift value that satisfies
// (SH >= 0 && SH < SIZE(ARRAY,DIM)).
//
- auto genDimensionShift = [&](mlir::Location loc, fir::FirOpBuilder &builder,
- mlir::Value shiftVal, mlir::Value boundary,
- bool exposeContiguity,
- mlir::ValueRange oneBasedIndices)
+ auto genDimensionShift =
+ [&](mlir::Location loc, fir::FirOpBuilder &builder,
+ mlir::Value shiftVal, [[maybe_unused]] mlir::Value boundary,
+ bool exposeContiguity, mlir::ValueRange oneBasedIndices)
-> llvm::SmallVector<mlir::Value, 0> {
// Create a vector of indices (s(1), ..., s(dim-1), nullptr, s(dim+1),
// ..., s(n)) so that we can update the dimVal index as needed.
@@ -3210,10 +3332,14 @@ class SimplifyHLFIRIntrinsics
patterns.insert<ReductionConversion<hlfir::CountOp>>(context);
patterns.insert<ReductionConversion<hlfir::AnyOp>>(context);
patterns.insert<ReductionConversion<hlfir::AllOp>>(context);
- patterns.insert<ReductionConversion<hlfir::MaxlocOp>>(context);
- patterns.insert<ReductionConversion<hlfir::MinlocOp>>(context);
- patterns.insert<ReductionConversion<hlfir::MaxvalOp>>(context);
- patterns.insert<ReductionConversion<hlfir::MinvalOp>>(context);
+ patterns.insert<ExtremumReductionConversion<hlfir::MaxlocOp>>(
+ context, this->fpMaxminBehavior);
+ patterns.insert<ExtremumReductionConversion<hlfir::MinlocOp>>(
+ context, this->fpMaxminBehavior);
+ patterns.insert<ExtremumReductionConversion<hlfir::MaxvalOp>>(
+ context, this->fpMaxminBehavior);
+ patterns.insert<ExtremumReductionConversion<hlfir::MinvalOp>>(
+ context, this->fpMaxminBehavior);
// If forceMatmulAsElemental is false, then hlfir.matmul inlining
// will introduce hlfir.eval_in_mem operation with new memory side
diff --git a/flang/lib/Optimizer/Passes/Pipelines.cpp b/flang/lib/Optimizer/Passes/Pipelines.cpp
index b6a34e0e5aad5..658077ea2f548 100644
--- a/flang/lib/Optimizer/Passes/Pipelines.cpp
+++ b/flang/lib/Optimizer/Passes/Pipelines.cpp
@@ -267,8 +267,10 @@ void createHLFIRToFIRPassPipeline(mlir::PassManager &pm,
}
if (optLevel.isOptimizingForSpeed()) {
addCanonicalizerPassWithoutRegionSimplification(pm);
- addNestedPassToAllTopLevelOperations<PassConstructor>(
- pm, hlfir::createSimplifyHLFIRIntrinsics);
+ addNestedPassToAllTopLevelOperations(pm, [&]() {
+ return hlfir::createSimplifyHLFIRIntrinsics(
+ {/*allowNewSideEffects=*/false, config.fpMaxminBehavior});
+ });
}
addNestedPassToAllTopLevelOperations<PassConstructor>(
pm, hlfir::createInlineElementals);
@@ -277,9 +279,9 @@ void createHLFIRToFIRPassPipeline(mlir::PassManager &pm,
pm.addPass(mlir::createCSEPass());
// Run SimplifyHLFIRIntrinsics pass late after CSE,
// and allow introducing operations with new side effects.
- addNestedPassToAllTopLevelOperations<PassConstructor>(pm, []() {
+ addNestedPassToAllTopLevelOperations(pm, [&]() {
return hlfir::createSimplifyHLFIRIntrinsics(
- {/*allowNewSideEffects=*/true});
+ {/*allowNewSideEffects=*/true, config.fpMaxminBehavior});
});
addNestedPassToAllTopLevelOperations<PassConstructor>(
pm, hlfir::createPropagateFortranVariableAttributes);
diff --git a/flang/test/HLFIR/simplify-hlfir-intrinsics-maxmin.fir b/flang/test/HLFIR/simplify-hlfir-intrinsics-maxmin.fir
new file mode 100644
index 0000000000000..96ffb61c7605d
--- /dev/null
+++ b/flang/test/HLFIR/simplify-hlfir-intrinsics-maxmin.fir
@@ -0,0 +1,557 @@
+// RUN: fir-opt %s --simplify-hlfir-intrinsics=fp-maxmin-behavior=portable --split-input-file | FileCheck %s --check-prefixes=ALL,PORTABLE
+// RUN: fir-opt %s --simplify-hlfir-intrinsics=fp-maxmin-behavior=extremum --split-input-file | FileCheck %s --check-prefixes=ALL,UNORDERED,EXTREMUM
+// RUN: fir-opt %s --simplify-hlfir-intrinsics=fp-maxmin-behavior=extremenum --split-input-file | FileCheck %s --check-prefixes=ALL,UNORDERED,EXTREMENUM
+
+func.func @_QPtest_unmasked_max(%arg0: !fir.ref<f32> {fir.bindc_name = "x"}, %arg1: !fir.ref<!fir.array<100xf32>> {fir.bindc_name = "y"}) {
+ %c100 = arith.constant 100 : index
+ %0 = fir.dummy_scope : !fir.dscope
+ %1:2 = hlfir.declare %arg0 dummy_scope %0 arg 1 {uniq_name = "_QFtest_unmasked_maxEx"} : (!fir.ref<f32>, !fir.dscope) -> (!fir.ref<f32>, !fir.ref<f32>)
+ %2 = fir.shape %c100 : (index) -> !fir.shape<1>
+ %3:2 = hlfir.declare %arg1(%2) dummy_scope %0 arg 2 {uniq_name = "_QFtest_unmasked_maxEy"} : (!fir.ref<!fir.array<100xf32>>, !fir.shape<1>, !fir.dscope) -> (!fir.ref<!fir.array<100xf32>>, !fir.ref<!fir.array<100xf32>>)
+ %4 = hlfir.maxval %3#0 {fastmath = #arith.fastmath<contract>} : (!fir.ref<!fir.array<100xf32>>) -> f32
+ hlfir.assign %4 to %1#0 : f32, !fir.ref<f32>
+ return
+}
+
+// ALL-LABEL: func.func @_QPtest_unmasked_max(
+// PORTABLE: %[[CONSTANT_0:.*]] = arith.constant true
+// PORTABLE: %[[CONSTANT_1:.*]] = arith.constant -3.40282347E+38 : f32
+// PORTABLE: %[[CONSTANT_2:.*]] = arith.constant 1 : index
+// PORTABLE: %[[CONSTANT_3:.*]] = arith.constant 100 : index
+// PORTABLE: %[[IF_0:.*]] = fir.if %[[CONSTANT_0]] -> (f32) {
+// PORTABLE: } else {
+// PORTABLE: fir.result %[[CONSTANT_1]] : f32
+// PORTABLE: }
+// PORTABLE: %[[DO_LOOP_0:.*]] = fir.do_loop %[[VAL_0:.*]] = %[[CONSTANT_2]] to %[[CONSTANT_3]] step %[[CONSTANT_2]] iter_args(%[[VAL_1:.*]] = %[[IF_0]]) -> (f32) {
+// PORTABLE: %[[CMPF_0:.*]] = arith.cmpf ogt, %[[LOAD_1:.*]], %[[VAL_1]] fastmath<contract> : f32
+// PORTABLE: %[[CMPF_1:.*]] = arith.cmpf une, %[[VAL_1]], %[[VAL_1]] fastmath<contract> : f32
+// PORTABLE: %[[CMPF_2:.*]] = arith.cmpf oeq, %[[LOAD_1]], %[[LOAD_1]] fastmath<contract> : f32
+// PORTABLE: %[[ANDI_0:.*]] = arith.andi %[[CMPF_1]], %[[CMPF_2]] : i1
+// PORTABLE: %[[ORI_0:.*]] = arith.ori %[[CMPF_0]], %[[ANDI_0]] : i1
+// PORTABLE: %[[SELECT_0:.*]] = arith.select %[[ORI_0]], %[[LOAD_1]], %[[VAL_1]] : f32
+
+// UNORDERED: %[[CONSTANT_0:.*]] = arith.constant 1 : index
+// UNORDERED: %[[CONSTANT_1:.*]] = arith.constant -3.40282347E+38 : f32
+// UNORDERED: %[[CONSTANT_2:.*]] = arith.constant 100 : index
+// UNORDERED: %[[DO_LOOP_0:.*]] = fir.do_lo...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/185148
More information about the flang-commits
mailing list