[flang-commits] [flang] [flang][hlfir] Optimize MINLOC/MAXLOC with equality mask (PR #211722)
via flang-commits
flang-commits at lists.llvm.org
Thu Jul 23 22:15:47 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-fir-hlfir
Author: anoopkg6
<details>
<summary>Changes</summary>
When the mask is an element-wise integer equality comparison against a loop-invariant scalar (e.g. MASK = A == val), tracking the minimum/maximum value throughout the reduction loop is redundant — every unmasked element is identical to val, so the first match is the result regardless of its value.
This is the second part of the optimization enabled by #<!-- -->186916, which relaxed InlineElementals to inline the mask hlfir.elemental at its hlfir.apply site inside the generated reduction loop.
The [coords, minval, isFirst] accumulator is replaced with [coords, isFirst], turning the loop into a short-circuiting find-first locator. The arith.andi %mask_val, %isFirst guard ensures subsequent masked elements are skipped once the first match is recorded.
---
Patch is 49.86 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/211722.diff
4 Files Affected:
- (modified) flang/include/flang/Optimizer/Builder/HLFIRTools.h (+6)
- (modified) flang/lib/Optimizer/Builder/HLFIRTools.cpp (+129)
- (modified) flang/lib/Optimizer/HLFIR/Transforms/SimplifyHLFIRIntrinsics.cpp (+78)
- (added) flang/test/HLFIR/simplify-hlfir-intrinsics-minmaxloc-equality-mask.fir (+759)
``````````diff
diff --git a/flang/include/flang/Optimizer/Builder/HLFIRTools.h b/flang/include/flang/Optimizer/Builder/HLFIRTools.h
index ce99a37052621..de3e115544533 100644
--- a/flang/include/flang/Optimizer/Builder/HLFIRTools.h
+++ b/flang/include/flang/Optimizer/Builder/HLFIRTools.h
@@ -619,6 +619,12 @@ bool isSimplyContiguous(mlir::Value base, bool checkWhole = true);
/// that is subject to the WHERE mask control.
bool isInsideHlfirWhereMaskedExpression(mlir::Region ®ion);
+/// Return true if \p mask is an element-wise equality comparison of
+/// \p searchArray against an invariant scalar (e.g. MASK = A == val).
+/// On success, \p targetVal is set to the invariant scalar operand.
+std::optional<mlir::Value> getEqualityMaskTarget(mlir::Value mask,
+ mlir::Value searchArray);
+
} // namespace hlfir
#endif // FORTRAN_OPTIMIZER_BUILDER_HLFIRTOOLS_H
diff --git a/flang/lib/Optimizer/Builder/HLFIRTools.cpp b/flang/lib/Optimizer/Builder/HLFIRTools.cpp
index f35ed4dd59e12..43b9c530836dc 100644
--- a/flang/lib/Optimizer/Builder/HLFIRTools.cpp
+++ b/flang/lib/Optimizer/Builder/HLFIRTools.cpp
@@ -1829,3 +1829,132 @@ bool hlfir::isInsideHlfirWhereMaskedExpression(mlir::Region ®ion) {
// all other expressions nested under the where must be evaluated masked.
return !whereOp.getMaskRegion().isAncestor(®ion);
}
+
+static bool isElementalScalarEqualityMask(mlir::Value mask,
+ hlfir::ElementalOp &elementalOut,
+ mlir::Value &targetVal,
+ mlir::Value &arraySide) {
+ if (!mask)
+ return false;
+
+ mlir::Value currentMask = mask;
+ while (auto def = currentMask.getDefiningOp()) {
+ if (!mlir::isa<hlfir::AsExprOp, fir::ConvertOp, hlfir::DeclareOp,
+ hlfir::CopyInOp>(def))
+ break;
+ currentMask = def->getOperand(0);
+ }
+ // The mask must be produced by an hlfir.elemental.
+ auto elemental = currentMask.getDefiningOp<hlfir::ElementalOp>();
+ if (!elemental)
+ return false;
+
+ mlir::Block &body = elemental.getRegion().front();
+ auto yieldOp = mlir::cast<hlfir::YieldElementOp>(body.getTerminator());
+ mlir::Value val = yieldOp.getElementValue();
+ while (auto conv = val.getDefiningOp<fir::ConvertOp>())
+ val = conv.getOperand();
+
+ // Optimizing only integer equality (arith.cmpi eq).
+ auto cmpOp = val.getDefiningOp<mlir::arith::CmpIOp>();
+ if (!cmpOp || cmpOp.getPredicate() != mlir::arith::CmpIPredicate::eq)
+ return false;
+
+ // Loop-invariance check for structures inside the elemental body.
+ std::function<bool(mlir::Value)> isInvariant = [&](mlir::Value v) -> bool {
+ if (auto arg = mlir::dyn_cast<mlir::BlockArgument>(v))
+ return arg.getOwner()->getParent() != &elemental.getRegion();
+ mlir::Operation *def = v.getDefiningOp();
+ if (!def)
+ return true;
+ if (!elemental.getRegion().isAncestor(def->getParentRegion()))
+ return true;
+ if (!mlir::isa<hlfir::DesignateOp, hlfir::DeclareOp, hlfir::ApplyOp,
+ fir::LoadOp, fir::ConvertOp, hlfir::AsExprOp>(def))
+ return false;
+ return llvm::all_of(def->getOperands(), isInvariant);
+ };
+
+ mlir::Value lhs = cmpOp.getLhs(), rhs = cmpOp.getRhs();
+ bool lhsInv = isInvariant(lhs), rhsInv = isInvariant(rhs);
+ // The optimization is valid only if exactly one side is invariant (the
+ // target) and the other side is variant (the array element).
+ if (lhsInv == rhsInv)
+ return false;
+
+ elementalOut = elemental;
+ targetVal = lhsInv ? lhs : rhs;
+ arraySide = lhsInv ? rhs : lhs;
+ return true;
+}
+
+std::optional<mlir::Value>
+hlfir::getEqualityMaskTarget(mlir::Value mask, mlir::Value searchArray) {
+ hlfir::ElementalOp elemental;
+ mlir::Value targetVal, arraySide;
+ if (!isElementalScalarEqualityMask(mask, elemental, targetVal, arraySide))
+ return std::nullopt;
+
+ // Find the DesignateOp or ApplyOp that element-accesses the array.
+ mlir::Operation *accessOp = nullptr;
+ mlir::Value v = arraySide;
+ while (v) {
+ mlir::Operation *def = v.getDefiningOp();
+ if (!def)
+ break;
+ if (mlir::isa<hlfir::DesignateOp, hlfir::ApplyOp>(def)) {
+ accessOp = def;
+ break;
+ }
+ if (auto decl = mlir::dyn_cast<hlfir::DeclareOp>(def))
+ v = decl.getMemref();
+ else if (mlir::isa<fir::ConvertOp, hlfir::AsExprOp>(def))
+ v = def->getOperand(0);
+ else if (auto load = mlir::dyn_cast<fir::LoadOp>(def))
+ v = load.getMemref();
+ else
+ break;
+ }
+ if (!accessOp)
+ return std::nullopt;
+
+ mlir::Value accessedArray;
+ mlir::ValueRange accessIndices;
+ if (auto desig = mlir::dyn_cast<hlfir::DesignateOp>(accessOp)) {
+ for (bool isTriplet : desig.getIsTriplet())
+ if (isTriplet)
+ return std::nullopt;
+ accessedArray = desig.getMemref();
+ accessIndices = desig.getIndices();
+ } else {
+ auto apply = mlir::cast<hlfir::ApplyOp>(accessOp);
+ accessedArray = apply.getExpr();
+ accessIndices = apply.getIndices();
+ }
+
+ mlir::Value canonSearch = searchArray;
+ while (canonSearch) {
+ mlir::Operation *def = canonSearch.getDefiningOp();
+ if (!def)
+ break;
+ if (auto decl = mlir::dyn_cast<hlfir::DeclareOp>(def))
+ canonSearch = decl.getMemref();
+ else if (mlir::isa<fir::ConvertOp, hlfir::AsExprOp>(def))
+ canonSearch = def->getOperand(0);
+ else
+ break;
+ }
+
+ if (accessedArray != canonSearch)
+ return std::nullopt;
+
+ // Check the access indices map 1:1 to the elemental loop indices.
+ mlir::Block::BlockArgListType bodyArgs = elemental.getIndices();
+ if (accessIndices.size() != bodyArgs.size())
+ return std::nullopt;
+ for (auto [idx, arg] : llvm::zip(accessIndices, bodyArgs))
+ if (idx != arg)
+ return std::nullopt;
+
+ return targetVal;
+}
diff --git a/flang/lib/Optimizer/HLFIR/Transforms/SimplifyHLFIRIntrinsics.cpp b/flang/lib/Optimizer/HLFIR/Transforms/SimplifyHLFIRIntrinsics.cpp
index 716737bb80ff4..25b87225fb01e 100644
--- a/flang/lib/Optimizer/HLFIR/Transforms/SimplifyHLFIRIntrinsics.cpp
+++ b/flang/lib/Optimizer/HLFIR/Transforms/SimplifyHLFIRIntrinsics.cpp
@@ -266,6 +266,11 @@ class ReductionAsElementalConverter {
return hlfir::Entity{reductionResults[0]};
}
+ /// Overridden by MinMaxlocAsElementalConverter to use the cached result.
+ virtual std::optional<mlir::Value> getEqualityMaskTargetForMask() const {
+ return std::nullopt;
+ }
+
/// Return mlir::success(), if the operation can be converted.
/// The default implementation always returns mlir::success().
/// The derived type may override the default implementation
@@ -529,8 +534,24 @@ class MinMaxlocAsElementalConverter : public ReductionAsElementalConverter {
return isTotalReduction() ? getSourceRank() : 1;
}
+ std::optional<mlir::Value> getEqualityMaskTarget() const {
+ if (!cachedEqualityTarget.has_value())
+ cachedEqualityTarget = hlfir::getEqualityMaskTarget(
+ this->getMask(), mlir::cast<T>(this->op).getArray());
+ return *cachedEqualityTarget;
+ }
+
+ std::optional<mlir::Value> getEqualityMaskTargetForMask() const final {
+ return getEqualityMaskTarget();
+ }
+
void
checkReductions(const llvm::SmallVectorImpl<mlir::Value> &reductions) const {
+ if (getEqualityMaskTarget()) {
+ assert(reductions.size() == getNumCoors() + 1 &&
+ "invalid number of reductions for equality mask MINLOC/MAXLOC");
+ return;
+ }
if (!useIsFirst())
assert(reductions.size() == getNumCoors() + 1 &&
"invalid number of reductions for MINLOC/MAXLOC");
@@ -542,6 +563,8 @@ class MinMaxlocAsElementalConverter : public ReductionAsElementalConverter {
mlir::Value
getCurrentMinMax(const llvm::SmallVectorImpl<mlir::Value> &reductions) const {
checkReductions(reductions);
+ assert(!getEqualityMaskTarget() &&
+ "equality mask reductions have no minmax slot");
return reductions[getNumCoors()];
}
@@ -572,6 +595,7 @@ class MinMaxlocAsElementalConverter : public ReductionAsElementalConverter {
// this control into account, though, we need to define what
// this means exactly.
[[maybe_unused]] Fortran::common::FPMaxminBehavior fpMaxminBehavior;
+ mutable std::optional<std::optional<mlir::Value>> cachedEqualityTarget;
};
template <typename T>
@@ -579,6 +603,16 @@ llvm::SmallVector<mlir::Value>
MinMaxlocAsElementalConverter<T>::genReductionInitValues(
mlir::ValueRange oneBasedIndices,
const llvm::SmallVectorImpl<mlir::Value> &extents) {
+ // Equality-mask path only track coordinates and firstHit flag.
+ if (auto targetVal = getEqualityMaskTarget()) {
+ unsigned rank = getNumCoors();
+ mlir::Type resElemTy = getResultElementType();
+ mlir::Value zeroVal = builder.createIntegerConstant(loc, resElemTy, 0);
+ llvm::SmallVector<mlir::Value> result(rank, zeroVal);
+ result.push_back(builder.createBool(loc, true));
+ return result;
+ }
+
fir::IfOp ifOp;
if (!useIsFirst() && honorNans()) {
// Check if we can load the value of the first element in the array
@@ -641,6 +675,23 @@ MinMaxlocAsElementalConverter<T>::reduceOneElement(
const llvm::SmallVectorImpl<mlir::Value> ¤tValue, hlfir::Entity array,
mlir::ValueRange oneBasedIndices) {
checkReductions(currentValue);
+
+ if (getEqualityMaskTarget()) {
+ int64_t dim = 1;
+ if (!isTotalReduction()) {
+ auto dimVal = getConstDim();
+ assert(mlir::succeeded(dimVal) &&
+ "partial MINLOC/MAXLOC reduction with invalid DIM");
+ dim = *dimVal;
+ }
+ llvm::SmallVector<mlir::Value> newValues;
+ for (unsigned i = 0; i < getNumCoors(); ++i)
+ newValues.push_back(builder.createConvert(loc, currentValue[i].getType(),
+ oneBasedIndices[i + dim - 1]));
+ newValues.push_back(builder.createBool(loc, false));
+ return newValues;
+ }
+
hlfir::Entity elementValue =
hlfir::loadElementAt(loc, builder, array, oneBasedIndices);
mlir::Value cmp = genMinMaxComparison<isMax>(loc, builder, elementValue,
@@ -690,6 +741,30 @@ MinMaxlocAsElementalConverter<T>::reduceOneElement(
template <typename T>
hlfir::Entity MinMaxlocAsElementalConverter<T>::genFinalResult(
const llvm::SmallVectorImpl<mlir::Value> &reductionResults) {
+ // Drop the firstHit bool and pack coordinates into the result array.
+ if (getEqualityMaskTarget()) {
+ if (getResultRank() == 0 || !isTotalReduction()) {
+ assert(getNumCoors() == 1 &&
+ "unexpected coordinate count for scalar result");
+ return hlfir::Entity{reductionResults[0]};
+ }
+
+ unsigned rank = getNumCoors();
+ mlir::Type indexType = builder.getIndexType();
+ mlir::Value tempArray = builder.createTemporary(
+ loc, fir::SequenceType::get(rank, getResultElementType()));
+ for (unsigned i = 0; i < rank; ++i) {
+ mlir::Value coor = reductionResults[i];
+ mlir::Value idx = builder.createIntegerConstant(loc, indexType, i + 1);
+ mlir::Value resultElement =
+ hlfir::getElementAt(loc, builder, hlfir::Entity{tempArray}, {idx});
+ hlfir::AssignOp::create(builder, loc, coor, resultElement);
+ }
+ mlir::Value tempExpr = hlfir::AsExprOp::create(
+ builder, loc, tempArray, builder.createBool(loc, false));
+ return hlfir::Entity{tempExpr};
+ }
+
// Identification of the final result of MINLOC/MAXLOC:
// * If DIM is absent, the result is rank-one array.
// * If DIM is present:
@@ -1220,6 +1295,9 @@ mlir::LogicalResult ReductionAsElementalConverter::convert() {
}
mlir::Value isUnmasked = fir::ConvertOp::create(
builder, loc, builder.getI1Type(), maskValue);
+ if (getEqualityMaskTargetForMask())
+ isUnmasked = mlir::arith::AndIOp::create(builder, loc, isUnmasked,
+ reductionValues.back());
ifOp = fir::IfOp::create(builder, loc, reductionTypes, isUnmasked,
/*withElseRegion=*/true);
// In the 'else' block return the current reduction value.
diff --git a/flang/test/HLFIR/simplify-hlfir-intrinsics-minmaxloc-equality-mask.fir b/flang/test/HLFIR/simplify-hlfir-intrinsics-minmaxloc-equality-mask.fir
new file mode 100644
index 0000000000000..d09f583808efc
--- /dev/null
+++ b/flang/test/HLFIR/simplify-hlfir-intrinsics-minmaxloc-equality-mask.fir
@@ -0,0 +1,759 @@
+// RUN: fir-opt %s --simplify-hlfir-intrinsics | FileCheck %s
+
+func.func @test_minloc_1d_eq_mask(%input: !fir.box<!fir.array<?xi32>>,
+ %val: i32) -> !hlfir.expr<1xi32> {
+ %c0 = arith.constant 0 : index
+ %dims:3 = fir.box_dims %input, %c0 : (!fir.box<!fir.array<?xi32>>, index) -> (index, index, index)
+ %shape = fir.shape %dims#1 : (index) -> !fir.shape<1>
+ %mask = hlfir.elemental %shape unordered : (!fir.shape<1>) -> !hlfir.expr<?x!fir.logical<4>> {
+ ^bb0(%i: index):
+ %elem = hlfir.designate %input (%i) : (!fir.box<!fir.array<?xi32>>, index) -> !fir.ref<i32>
+ %ld = fir.load %elem : !fir.ref<i32>
+ %cmp = arith.cmpi eq, %ld, %val : i32
+ %conv = fir.convert %cmp : (i1) -> !fir.logical<4>
+ hlfir.yield_element %conv : !fir.logical<4>
+ }
+ %res = hlfir.minloc %input mask %mask {fastmath = #arith.fastmath<contract>} : (!fir.box<!fir.array<?xi32>>, !hlfir.expr<?x!fir.logical<4>>) -> !hlfir.expr<1xi32>
+ hlfir.destroy %mask : !hlfir.expr<?x!fir.logical<4>>
+ return %res : !hlfir.expr<1xi32>
+}
+// CHECK-LABEL: func.func @test_minloc_1d_eq_mask(
+// CHECK-NOT: arith.constant 2147483647
+// CHECK: fir.do_loop {{.*}} iter_args(%{{.*}} = %{{.*}}, %{{.*}} = %true) -> (i32, i1)
+// CHECK: hlfir.apply
+// CHECK: arith.andi
+// CHECK: fir.if {{.*}} -> (i32, i1) {
+// CHECK: fir.result %{{.*}}, %false : i32, i1
+// CHECK-NOT: arith.cmpi slt
+// CHECK-NOT: arith.select {{.*}} : i32
+
+func.func @test_minloc_2d_eq_mask(%input: !fir.box<!fir.array<?x?xi32>>,
+ %val: i32) -> !hlfir.expr<2xi32> {
+ %c0 = arith.constant 0 : index
+ %c1 = arith.constant 1 : index
+ %d0:3 = fir.box_dims %input, %c0 : (!fir.box<!fir.array<?x?xi32>>, index) -> (index, index, index)
+ %d1:3 = fir.box_dims %input, %c1 : (!fir.box<!fir.array<?x?xi32>>, index) -> (index, index, index)
+ %shape = fir.shape %d0#1, %d1#1 : (index, index) -> !fir.shape<2>
+ %mask = hlfir.elemental %shape unordered : (!fir.shape<2>) -> !hlfir.expr<?x?x!fir.logical<4>> {
+ ^bb0(%i: index, %j: index):
+ %elem = hlfir.designate %input (%i, %j) : (!fir.box<!fir.array<?x?xi32>>, index, index) -> !fir.ref<i32>
+ %ld = fir.load %elem : !fir.ref<i32>
+ %cmp = arith.cmpi eq, %ld, %val : i32
+ %conv = fir.convert %cmp : (i1) -> !fir.logical<4>
+ hlfir.yield_element %conv : !fir.logical<4>
+ }
+ %res = hlfir.minloc %input mask %mask {fastmath = #arith.fastmath<contract>} : (!fir.box<!fir.array<?x?xi32>>, !hlfir.expr<?x?x!fir.logical<4>>) -> !hlfir.expr<2xi32>
+ hlfir.destroy %mask : !hlfir.expr<?x?x!fir.logical<4>>
+ return %res : !hlfir.expr<2xi32>
+}
+// CHECK-LABEL: func.func @test_minloc_2d_eq_mask(
+// CHECK-NOT: arith.constant 2147483647
+// CHECK: fir.do_loop {{.*}} iter_args(%{{.*}} = %{{.*}}, %{{.*}} = %{{.*}}, %{{.*}} = %true) -> (i32, i32, i1)
+// CHECK: fir.do_loop {{.*}} iter_args(%{{.*}}, %{{.*}}, %{{.*}}) -> (i32, i32, i1)
+// CHECK: hlfir.apply
+// CHECK: arith.andi
+// CHECK: fir.if {{.*}} -> (i32, i32, i1) {
+// CHECK: fir.result %{{.*}}, %{{.*}}, %false : i32, i32, i1
+// CHECK-NOT: arith.cmpi slt
+// CHECK-NOT: arith.select {{.*}} : i32
+
+func.func @test_maxloc_1d_eq_mask(%input: !fir.box<!fir.array<?xi32>>,
+ %val: i32) -> !hlfir.expr<1xi32> {
+ %c0 = arith.constant 0 : index
+ %dims:3 = fir.box_dims %input, %c0 : (!fir.box<!fir.array<?xi32>>, index) -> (index, index, index)
+ %shape = fir.shape %dims#1 : (index) -> !fir.shape<1>
+ %mask = hlfir.elemental %shape unordered : (!fir.shape<1>) -> !hlfir.expr<?x!fir.logical<4>> {
+ ^bb0(%i: index):
+ %elem = hlfir.designate %input (%i) : (!fir.box<!fir.array<?xi32>>, index) -> !fir.ref<i32>
+ %ld = fir.load %elem : !fir.ref<i32>
+ %cmp = arith.cmpi eq, %ld, %val : i32
+ %conv = fir.convert %cmp : (i1) -> !fir.logical<4>
+ hlfir.yield_element %conv : !fir.logical<4>
+ }
+ %res = hlfir.maxloc %input mask %mask {fastmath = #arith.fastmath<contract>} : (!fir.box<!fir.array<?xi32>>, !hlfir.expr<?x!fir.logical<4>>) -> !hlfir.expr<1xi32>
+ hlfir.destroy %mask : !hlfir.expr<?x!fir.logical<4>>
+ return %res : !hlfir.expr<1xi32>
+}
+// CHECK-LABEL: func.func @test_maxloc_1d_eq_mask(
+// CHECK-NOT: arith.constant -2147483648
+// CHECK: fir.do_loop {{.*}} iter_args(%{{.*}} = %{{.*}}, %{{.*}} = %true) -> (i32, i1)
+// CHECK: hlfir.apply
+// CHECK: arith.andi
+// CHECK: fir.if {{.*}} -> (i32, i1) {
+// CHECK: fir.result %{{.*}}, %false : i32, i1
+// CHECK-NOT: arith.cmpi sgt
+
+// Partial reduction (DIM=1) with equality mask on 2D array — maxloc.
+func.func @test_maxloc_dim1_eq_mask(%input: !fir.box<!fir.array<?x?xi32>>,
+ %val: i32) -> !hlfir.expr<?xi32> {
+ %c0 = arith.constant 0 : index
+ %c1 = arith.constant 1 : index
+ %dim = arith.constant 1 : i32
+ %d0:3 = fir.box_dims %input, %c0 : (!fir.box<!fir.array<?x?xi32>>, index) -> (index, index, index)
+ %d1:3 = fir.box_dims %input, %c1 : (!fir.box<!fir.array<?x?xi32>>, index) -> (index, index, index)
+ %shape = fir.shape %d0#1, %d1#1 : (index, index) -> !fir.shape<2>
+ %mask = hlfir.elemental %shape unordered : (!fir.shape<2>) -> !hlfir.expr<?x?x!fir.logical<4>> {
+ ^bb0(%i: index, %j: index):
+ %elem = hlfir.designate %input (%i, %j) : (!fir.box<!fir.array<?x?xi32>>, index, index) -> !fir.ref<i32>
+ %ld = fir.load %elem : !fir.ref<i32>
+ %cmp = arith.cmpi eq, %ld, %val : i32
+ %conv = fir.convert %cmp : (i1) -> !fir.logical<4>
+ hlfir.yield_element %conv : !fir.logical<4>
+ }
+ %res = hlfir.maxloc %input dim %dim mask %mask {fastmath = #arith.fastmath<contract>} : (!fir.box<!fir.array<?x?xi32>>, i32, !hlfir.expr<?x?x!fir.logical<4>>) -> !hlfir.expr<?xi32>
+ hlfir.destroy %mask : !hlfir.expr<?x?x!fir.logical<4>>
+ return %res : !hlfir.expr<?xi32>
+}
+// CHECK-LABEL: func.func @test_maxloc_dim1_eq_mask(
+// CHECK-NOT: arith.constant -2147483648
+// CHECK: hlfir.elemental
+// CHECK: fir.do_loop {{.*}} iter_args(%{{.*}} = %{{.*}}, %{{.*}} = %true) -> (i32, i1)
+// CHECK: hlfir.apply
+// CHECK: arith.andi
+// CHECK: fir.if {{.*}} -> (i32, i1) {
+// CHECK: fir.result %{{.*}}, %false : i32, i1
+// CHECK-NOT: arith.cmpi sgt
+
+// hlfir.expr input with equality mask.
+func.func @test_minloc_expr_eq_mask(%input: !hlfir.expr<?xi32>,
+ %val: i32) -> !hlfir.expr<1xi32> {
+ %0 = hlfir.shape_of %input : (!hlfir.expr<?xi32>) -> !fir.shape<1>
+ %mask = hlfir.elemental %0 unordered : (!fir.shape<1>) -> !hlfir.expr<?x!fir.logical<4>> {
+ ^bb0(%i: index):
+ %elem = hlfir.apply %input, %i : (!hlfir.expr<?xi32>, index) -> i32
+ %cmp = arith.cmpi eq, %elem, %val : i32
+ %conv = fir.convert %cmp : (i1) -> !fir.logical<4>
+ hlfir.yield_element %conv : !fir.logical<4>
+ }
+ %res = hlfir.minloc %input mask %mask {fastmath = #arith.fastmath<contract>} : (!hlfir.expr<?xi32>, !hlfir.expr<?x!fir.logical<4>>) -> !hlfir.expr<1xi32>
+ hlfir.destroy %mask : !hlfir.expr<?x!fir.logical<4>>
+ return %res : !hlfir.expr<1xi32>
+}
+// CHECK-LABEL: func.func @test_minloc_expr_eq_mask(
+// CHECK-NOT: arith.constant 2147483647
+// CHECK: fir.do_loop {{.*}} iter_args(%{{.*}} = %{{.*}}, %{{.*}} = %true) -> (i32, i1)
+// CHECK: hlfir.apply
+// CHECK: arith.andi
+// CHECK: fir.if {{.*}} -> (i32, i1) {
+// CHECK: fir.result %{{.*}}, %false : i32, i1
+// CHECK-NOT: arith.cmpi slt
+
+// 3D total reduction with equality mask.
+func.func @test_minloc_3d_eq_mask(%input: !fir.box<!fir.array<?x?x?xi...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/211722
More information about the flang-commits
mailing list