aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean Perier <jperier@nvidia.com>2026-02-06 09:11:06 -0800
committerJean Perier <jperier@nvidia.com>2026-02-06 12:14:47 -0800
commitc47ea7fc614c9a1c02b4548184c5c1d2f869ed45 (patch)
tree272b5d200e4d932db94a6a79f1097a5d85e3daae
parent507c92ac08bad62d8ffa283d01ef93a65b90328c (diff)
downloadllvm-users/jeanPerier/extract_array_section_analyzer.zip
llvm-users/jeanPerier/extract_array_section_analyzer.tar.gz
llvm-users/jeanPerier/extract_array_section_analyzer.tar.bz2
[flang][NFC] Extract ArraySectionAnalyzer from OptimizedBufferizationusers/jeanPerier/extract_array_section_analyzer
Extract `ArraySectionAnalyzer` from `OptimizedBufferization` into a standalone analysis utility so it can be reused by other passes (e.g., `ScheduleOrderedAssignments`). This is an NFC change that moves the `ArraySectionAnalyzer` class and its helper methods to `flang/Optimizer/Analysis/ArraySectionAnalyzer.h` and `.cpp`. Also extracts the logic to detect if a designate is using the indices of an elemental operation in storage order.
-rw-r--r--flang/include/flang/Optimizer/Analysis/ArraySectionAnalyzer.h119
-rw-r--r--flang/lib/Optimizer/Analysis/ArraySectionAnalyzer.cpp300
-rw-r--r--flang/lib/Optimizer/Analysis/CMakeLists.txt1
-rw-r--r--flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp362
4 files changed, 428 insertions, 354 deletions
diff --git a/flang/include/flang/Optimizer/Analysis/ArraySectionAnalyzer.h b/flang/include/flang/Optimizer/Analysis/ArraySectionAnalyzer.h
new file mode 100644
index 0000000..0a9ff13
--- /dev/null
+++ b/flang/include/flang/Optimizer/Analysis/ArraySectionAnalyzer.h
@@ -0,0 +1,119 @@
+//===- ArraySectionAnalyzer.h - Analyze array sections --------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_OPTIMIZER_ANALYSIS_ARRAYSECTIONANALYZER_H
+#define FORTRAN_OPTIMIZER_ANALYSIS_ARRAYSECTIONANALYZER_H
+
+#include "mlir/IR/Operation.h"
+#include "mlir/IR/Value.h"
+
+namespace mlir {
+class Operation;
+class Value;
+} // namespace mlir
+
+namespace hlfir {
+class ElementalOpInterface;
+class DesignateOp;
+} // namespace hlfir
+
+namespace fir {
+class ArraySectionAnalyzer {
+public:
+ // The result of the analyzis is one of the values below.
+ enum class SlicesOverlapKind {
+ // Slices overlap is unknown.
+ Unknown,
+ // Slices are definitely identical.
+ DefinitelyIdentical,
+ // Slices are definitely disjoint.
+ DefinitelyDisjoint,
+ // Slices may be either disjoint or identical,
+ // i.e. there is definitely no partial overlap.
+ EitherIdenticalOrDisjoint
+ };
+
+ // Analyzes two hlfir.designate results and returns the overlap kind.
+ // The callers may use this method when the alias analysis reports
+ // an alias of some kind, so that we can run Fortran specific analysis
+ // on the array slices to see if they are identical or disjoint.
+ // Note that the alias analysis are not able to give such an answer
+ // about the references.
+ static SlicesOverlapKind analyze(mlir::Value ref1, mlir::Value ref2);
+
+ static bool isDesignatingArrayInOrder(hlfir::DesignateOp designate,
+ hlfir::ElementalOpInterface elemental);
+
+private:
+ struct SectionDesc {
+ // An array section is described by <lb, ub, stride> tuple.
+ // If the designator's subscript is not a triple, then
+ // the section descriptor is constructed as <lb, nullptr, nullptr>.
+ mlir::Value lb, ub, stride;
+
+ SectionDesc(mlir::Value lb, mlir::Value ub, mlir::Value stride);
+
+ // Normalize the section descriptor:
+ // 1. If UB is nullptr, then it is set to LB.
+ // 2. If LB==UB, then stride does not matter,
+ // so it is reset to nullptr.
+ // 3. If STRIDE==1, then it is reset to nullptr.
+ void normalize();
+
+ bool operator==(const SectionDesc &other) const;
+ };
+
+ // Given an operand_iterator over the indices operands,
+ // read the subscript values and return them as SectionDesc
+ // updating the iterator. If isTriplet is true,
+ // the subscript is a triplet, and the result is <lb, ub, stride>.
+ // Otherwise, the subscript is a scalar index, and the result
+ // is <index, nullptr, nullptr>.
+ static SectionDesc readSectionDesc(mlir::Operation::operand_iterator &it,
+ bool isTriplet);
+
+ // Return the ordered lower and upper bounds of the section.
+ // If stride is known to be non-negative, then the ordered
+ // bounds match the <lb, ub> of the descriptor.
+ // If stride is known to be negative, then the ordered
+ // bounds are <ub, lb> of the descriptor.
+ // If stride is unknown, we cannot deduce any order,
+ // so the result is <nullptr, nullptr>
+ static std::pair<mlir::Value, mlir::Value>
+ getOrderedBounds(const SectionDesc &desc);
+
+ // Given two array sections <lb1, ub1, stride1> and
+ // <lb2, ub2, stride2>, return true only if the sections
+ // are known to be disjoint.
+ //
+ // For example, for any positive constant C:
+ // X:Y does not overlap with (Y+C):Z
+ // X:Y does not overlap with Z:(X-C)
+ static bool areDisjointSections(const SectionDesc &desc1,
+ const SectionDesc &desc2);
+
+ // Given two array sections <lb1, ub1, stride1> and
+ // <lb2, ub2, stride2>, return true only if the sections
+ // are known to be identical.
+ //
+ // For example:
+ // <x, x, stride>
+ // <x, nullptr, nullptr>
+ //
+ // These sections are identical, from the point of which array
+ // elements are being addresses, even though the shape
+ // of the array slices might be different.
+ static bool areIdenticalSections(const SectionDesc &desc1,
+ const SectionDesc &desc2);
+
+ // Return true, if v1 is known to be less than v2.
+ static bool isLess(mlir::Value v1, mlir::Value v2);
+};
+} // namespace fir
+
+#endif // FORTRAN_OPTIMIZER_ANALYSIS_ARRAYSECTIONANALYZER_H
diff --git a/flang/lib/Optimizer/Analysis/ArraySectionAnalyzer.cpp b/flang/lib/Optimizer/Analysis/ArraySectionAnalyzer.cpp
new file mode 100644
index 0000000..f5ee298
--- /dev/null
+++ b/flang/lib/Optimizer/Analysis/ArraySectionAnalyzer.cpp
@@ -0,0 +1,300 @@
+//===- ArraySectionAnalyzer.cpp - Analyze array sections ------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Optimizer/Analysis/ArraySectionAnalyzer.h"
+#include "flang/Optimizer/Dialect/FIROps.h"
+#include "flang/Optimizer/Dialect/FIROpsSupport.h"
+#include "flang/Optimizer/HLFIR/HLFIROps.h"
+#include "mlir/Dialect/Arith/IR/Arith.h"
+#include "llvm/Support/Debug.h"
+
+#define DEBUG_TYPE "array-section-analyzer"
+
+using namespace fir;
+
+ArraySectionAnalyzer::SectionDesc::SectionDesc(mlir::Value lb, mlir::Value ub,
+ mlir::Value stride)
+ : lb(lb), ub(ub), stride(stride) {
+ assert(lb && "lower bound or index must be specified");
+ normalize();
+}
+
+void ArraySectionAnalyzer::SectionDesc::normalize() {
+ if (!ub)
+ ub = lb;
+ if (lb == ub)
+ stride = nullptr;
+ if (stride)
+ if (auto val = fir::getIntIfConstant(stride))
+ if (*val == 1)
+ stride = nullptr;
+}
+
+bool ArraySectionAnalyzer::SectionDesc::operator==(
+ const SectionDesc &other) const {
+ return lb == other.lb && ub == other.ub && stride == other.stride;
+}
+
+ArraySectionAnalyzer::SectionDesc
+ArraySectionAnalyzer::readSectionDesc(mlir::Operation::operand_iterator &it,
+ bool isTriplet) {
+ if (isTriplet)
+ return {*it++, *it++, *it++};
+ return {*it++, nullptr, nullptr};
+}
+
+std::pair<mlir::Value, mlir::Value>
+ArraySectionAnalyzer::getOrderedBounds(const SectionDesc &desc) {
+ mlir::Value stride = desc.stride;
+ // Null stride means stride=1.
+ if (!stride)
+ return {desc.lb, desc.ub};
+ // Reverse the bounds, if stride is negative.
+ if (auto val = fir::getIntIfConstant(stride)) {
+ if (*val >= 0)
+ return {desc.lb, desc.ub};
+ else
+ return {desc.ub, desc.lb};
+ }
+
+ return {nullptr, nullptr};
+}
+
+bool ArraySectionAnalyzer::areDisjointSections(const SectionDesc &desc1,
+ const SectionDesc &desc2) {
+ auto [lb1, ub1] = getOrderedBounds(desc1);
+ auto [lb2, ub2] = getOrderedBounds(desc2);
+ if (!lb1 || !lb2)
+ return false;
+ // Note that this comparison must be made on the ordered bounds,
+ // otherwise 'a(x:y:1) = a(z:x-1:-1) + 1' may be incorrectly treated
+ // as not overlapping (x=2, y=10, z=9).
+ if (isLess(ub1, lb2) || isLess(ub2, lb1))
+ return true;
+ return false;
+}
+
+bool ArraySectionAnalyzer::areIdenticalSections(const SectionDesc &desc1,
+ const SectionDesc &desc2) {
+ if (desc1 == desc2)
+ return true;
+ return false;
+}
+
+ArraySectionAnalyzer::SlicesOverlapKind
+ArraySectionAnalyzer::analyze(mlir::Value ref1, mlir::Value ref2) {
+ if (ref1 == ref2)
+ return SlicesOverlapKind::DefinitelyIdentical;
+
+ auto des1 = ref1.getDefiningOp<hlfir::DesignateOp>();
+ auto des2 = ref2.getDefiningOp<hlfir::DesignateOp>();
+ // We only support a pair of designators right now.
+ if (!des1 || !des2)
+ return SlicesOverlapKind::Unknown;
+
+ if (des1.getMemref() != des2.getMemref()) {
+ // If the bases are different, then there is unknown overlap.
+ LLVM_DEBUG(llvm::dbgs() << "No identical base for:\n"
+ << des1 << "and:\n"
+ << des2 << "\n");
+ return SlicesOverlapKind::Unknown;
+ }
+
+ // Require all components of the designators to be the same.
+ // It might be too strict, e.g. we may probably allow for
+ // different type parameters.
+ if (des1.getComponent() != des2.getComponent() ||
+ des1.getComponentShape() != des2.getComponentShape() ||
+ des1.getSubstring() != des2.getSubstring() ||
+ des1.getComplexPart() != des2.getComplexPart() ||
+ des1.getTypeparams() != des2.getTypeparams()) {
+ LLVM_DEBUG(llvm::dbgs() << "Different designator specs for:\n"
+ << des1 << "and:\n"
+ << des2 << "\n");
+ return SlicesOverlapKind::Unknown;
+ }
+
+ // Analyze the subscripts.
+ auto des1It = des1.getIndices().begin();
+ auto des2It = des2.getIndices().begin();
+ bool identicalTriplets = true;
+ bool identicalIndices = true;
+ for (auto [isTriplet1, isTriplet2] :
+ llvm::zip(des1.getIsTriplet(), des2.getIsTriplet())) {
+ SectionDesc desc1 = readSectionDesc(des1It, isTriplet1);
+ SectionDesc desc2 = readSectionDesc(des2It, isTriplet2);
+
+ // See if we can prove that any of the sections do not overlap.
+ // This is mostly a Polyhedron/nf performance hack that looks for
+ // particular relations between the lower and upper bounds
+ // of the array sections, e.g. for any positive constant C:
+ // X:Y does not overlap with (Y+C):Z
+ // X:Y does not overlap with Z:(X-C)
+ if (areDisjointSections(desc1, desc2))
+ return SlicesOverlapKind::DefinitelyDisjoint;
+
+ if (!areIdenticalSections(desc1, desc2)) {
+ if (isTriplet1 || isTriplet2) {
+ // For example:
+ // hlfir.designate %6#0 (%c2:%c7999:%c1, %c1:%c120:%c1, %0)
+ // hlfir.designate %6#0 (%c2:%c7999:%c1, %c1:%c120:%c1, %1)
+ //
+ // If all the triplets (section speficiers) are the same, then
+ // we do not care if %0 is equal to %1 - the slices are either
+ // identical or completely disjoint.
+ //
+ // Also, treat these as identical sections:
+ // hlfir.designate %6#0 (%c2:%c2:%c1)
+ // hlfir.designate %6#0 (%c2)
+ identicalTriplets = false;
+ LLVM_DEBUG(llvm::dbgs() << "Triplet mismatch for:\n"
+ << des1 << "and:\n"
+ << des2 << "\n");
+ } else {
+ identicalIndices = false;
+ LLVM_DEBUG(llvm::dbgs() << "Indices mismatch for:\n"
+ << des1 << "and:\n"
+ << des2 << "\n");
+ }
+ }
+ }
+
+ if (identicalTriplets) {
+ if (identicalIndices)
+ return SlicesOverlapKind::DefinitelyIdentical;
+ else
+ return SlicesOverlapKind::EitherIdenticalOrDisjoint;
+ }
+
+ LLVM_DEBUG(llvm::dbgs() << "Different sections for:\n"
+ << des1 << "and:\n"
+ << des2 << "\n");
+ return SlicesOverlapKind::Unknown;
+}
+
+bool ArraySectionAnalyzer::isLess(mlir::Value v1, mlir::Value v2) {
+ auto removeConvert = [](mlir::Value v) -> mlir::Operation * {
+ auto *op = v.getDefiningOp();
+ while (auto conv = mlir::dyn_cast_or_null<fir::ConvertOp>(op))
+ op = conv.getValue().getDefiningOp();
+ return op;
+ };
+
+ auto isPositiveConstant = [](mlir::Value v) -> bool {
+ if (auto val = fir::getIntIfConstant(v))
+ return *val > 0;
+ return false;
+ };
+
+ auto *op1 = removeConvert(v1);
+ auto *op2 = removeConvert(v2);
+ if (!op1 || !op2)
+ return false;
+
+ // Check if they are both constants.
+ if (auto val1 = fir::getIntIfConstant(op1->getResult(0)))
+ if (auto val2 = fir::getIntIfConstant(op2->getResult(0)))
+ return *val1 < *val2;
+
+ // Handle some variable cases (C > 0):
+ // v2 = v1 + C
+ // v2 = C + v1
+ // v1 = v2 - C
+ if (auto addi = mlir::dyn_cast<mlir::arith::AddIOp>(op2))
+ if ((addi.getLhs().getDefiningOp() == op1 &&
+ isPositiveConstant(addi.getRhs())) ||
+ (addi.getRhs().getDefiningOp() == op1 &&
+ isPositiveConstant(addi.getLhs())))
+ return true;
+ if (auto subi = mlir::dyn_cast<mlir::arith::SubIOp>(op1))
+ if (subi.getLhs().getDefiningOp() == op2 &&
+ isPositiveConstant(subi.getRhs()))
+ return true;
+ return false;
+}
+
+/// Returns the array indices for the given hlfir.designate.
+/// It recognizes the computations used to transform the one-based indices
+/// into the array's lb-based indices, and returns the one-based indices
+/// in these cases.
+static llvm::SmallVector<mlir::Value>
+getDesignatorIndices(hlfir::DesignateOp designate) {
+ mlir::Value memref = designate.getMemref();
+
+ // If the object is a box, then the indices may be adjusted
+ // according to the box's lower bound(s). Scan through
+ // the computations to try to find the one-based indices.
+ if (mlir::isa<fir::BaseBoxType>(memref.getType())) {
+ // Look for the following pattern:
+ // %13 = fir.load %12 : !fir.ref<!fir.box<...>
+ // %14:3 = fir.box_dims %13, %c0 : (!fir.box<...>, index) -> ...
+ // %17 = arith.subi %14#0, %c1 : index
+ // %18 = arith.addi %arg2, %17 : index
+ // %19 = hlfir.designate %13 (%18) : (!fir.box<...>, index) -> ...
+ //
+ // %arg2 is a one-based index.
+
+ auto isNormalizedLb = [memref](mlir::Value v, unsigned dim) {
+ // Return true, if v and dim are such that:
+ // %14:3 = fir.box_dims %13, %dim : (!fir.box<...>, index) -> ...
+ // %17 = arith.subi %14#0, %c1 : index
+ // %19 = hlfir.designate %13 (...) : (!fir.box<...>, index) -> ...
+ if (auto subOp =
+ mlir::dyn_cast_or_null<mlir::arith::SubIOp>(v.getDefiningOp())) {
+ auto cst = fir::getIntIfConstant(subOp.getRhs());
+ if (!cst || *cst != 1)
+ return false;
+ if (auto dimsOp = mlir::dyn_cast_or_null<fir::BoxDimsOp>(
+ subOp.getLhs().getDefiningOp())) {
+ if (memref != dimsOp.getVal() ||
+ dimsOp.getResult(0) != subOp.getLhs())
+ return false;
+ auto dimsOpDim = fir::getIntIfConstant(dimsOp.getDim());
+ return dimsOpDim && dimsOpDim == dim;
+ }
+ }
+ return false;
+ };
+
+ llvm::SmallVector<mlir::Value> newIndices;
+ for (auto index : llvm::enumerate(designate.getIndices())) {
+ if (auto addOp = mlir::dyn_cast_or_null<mlir::arith::AddIOp>(
+ index.value().getDefiningOp())) {
+ for (unsigned opNum = 0; opNum < 2; ++opNum)
+ if (isNormalizedLb(addOp->getOperand(opNum), index.index())) {
+ newIndices.push_back(addOp->getOperand((opNum + 1) % 2));
+ break;
+ }
+
+ // If new one-based index was not added, exit early.
+ if (newIndices.size() <= index.index())
+ break;
+ }
+ }
+
+ // If any of the indices is not adjusted to the array's lb,
+ // then return the original designator indices.
+ if (newIndices.size() != designate.getIndices().size())
+ return designate.getIndices();
+
+ return newIndices;
+ }
+
+ return designate.getIndices();
+}
+
+bool fir::ArraySectionAnalyzer::isDesignatingArrayInOrder(
+ hlfir::DesignateOp designate, hlfir::ElementalOpInterface elemental) {
+
+ auto indices = getDesignatorIndices(designate);
+ auto elementalIndices = elemental.getIndices();
+ if (indices.size() == elementalIndices.size())
+ return std::equal(indices.begin(), indices.end(), elementalIndices.begin(),
+ elementalIndices.end());
+ return false;
+}
diff --git a/flang/lib/Optimizer/Analysis/CMakeLists.txt b/flang/lib/Optimizer/Analysis/CMakeLists.txt
index c890b96..6a7a648 100644
--- a/flang/lib/Optimizer/Analysis/CMakeLists.txt
+++ b/flang/lib/Optimizer/Analysis/CMakeLists.txt
@@ -1,5 +1,6 @@
add_flang_library(FIRAnalysis
AliasAnalysis.cpp
+ ArraySectionAnalyzer.cpp
TBAAForest.cpp
DEPENDS
diff --git a/flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp b/flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp
index 5351a9a..5889122 100644
--- a/flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp
+++ b/flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "flang/Optimizer/Analysis/AliasAnalysis.h"
+#include "flang/Optimizer/Analysis/ArraySectionAnalyzer.h"
#include "flang/Optimizer/Builder/FIRBuilder.h"
#include "flang/Optimizer/Builder/HLFIRTools.h"
#include "flang/Optimizer/Dialect/FIROps.h"
@@ -88,13 +89,6 @@ private:
/// determines if the transformation can be applied to this elemental
static std::optional<MatchInfo> findMatch(hlfir::ElementalOp elemental);
- /// Returns the array indices for the given hlfir.designate.
- /// It recognizes the computations used to transform the one-based indices
- /// into the array's lb-based indices, and returns the one-based indices
- /// in these cases.
- static llvm::SmallVector<mlir::Value>
- getDesignatorIndices(hlfir::DesignateOp designate);
-
public:
using mlir::OpRewritePattern<hlfir::ElementalOp>::OpRewritePattern;
@@ -167,344 +161,6 @@ containsReadOrWriteEffectOn(const mlir::MemoryEffects::EffectInstance &effect,
return mlir::AliasResult::NoAlias;
}
-// Helper class for analyzing two array slices represented
-// by two hlfir.designate operations.
-class ArraySectionAnalyzer {
-public:
- // The result of the analyzis is one of the values below.
- enum class SlicesOverlapKind {
- // Slices overlap is unknown.
- Unknown,
- // Slices are definitely identical.
- DefinitelyIdentical,
- // Slices are definitely disjoint.
- DefinitelyDisjoint,
- // Slices may be either disjoint or identical,
- // i.e. there is definitely no partial overlap.
- EitherIdenticalOrDisjoint
- };
-
- // Analyzes two hlfir.designate results and returns the overlap kind.
- // The callers may use this method when the alias analysis reports
- // an alias of some kind, so that we can run Fortran specific analysis
- // on the array slices to see if they are identical or disjoint.
- // Note that the alias analysis are not able to give such an answer
- // about the references.
- static SlicesOverlapKind analyze(mlir::Value ref1, mlir::Value ref2);
-
-private:
- struct SectionDesc {
- // An array section is described by <lb, ub, stride> tuple.
- // If the designator's subscript is not a triple, then
- // the section descriptor is constructed as <lb, nullptr, nullptr>.
- mlir::Value lb, ub, stride;
-
- SectionDesc(mlir::Value lb, mlir::Value ub, mlir::Value stride)
- : lb(lb), ub(ub), stride(stride) {
- assert(lb && "lower bound or index must be specified");
- normalize();
- }
-
- // Normalize the section descriptor:
- // 1. If UB is nullptr, then it is set to LB.
- // 2. If LB==UB, then stride does not matter,
- // so it is reset to nullptr.
- // 3. If STRIDE==1, then it is reset to nullptr.
- void normalize() {
- if (!ub)
- ub = lb;
- if (lb == ub)
- stride = nullptr;
- if (stride)
- if (auto val = fir::getIntIfConstant(stride))
- if (*val == 1)
- stride = nullptr;
- }
-
- bool operator==(const SectionDesc &other) const {
- return lb == other.lb && ub == other.ub && stride == other.stride;
- }
- };
-
- // Given an operand_iterator over the indices operands,
- // read the subscript values and return them as SectionDesc
- // updating the iterator. If isTriplet is true,
- // the subscript is a triplet, and the result is <lb, ub, stride>.
- // Otherwise, the subscript is a scalar index, and the result
- // is <index, nullptr, nullptr>.
- static SectionDesc readSectionDesc(mlir::Operation::operand_iterator &it,
- bool isTriplet) {
- if (isTriplet)
- return {*it++, *it++, *it++};
- return {*it++, nullptr, nullptr};
- }
-
- // Return the ordered lower and upper bounds of the section.
- // If stride is known to be non-negative, then the ordered
- // bounds match the <lb, ub> of the descriptor.
- // If stride is known to be negative, then the ordered
- // bounds are <ub, lb> of the descriptor.
- // If stride is unknown, we cannot deduce any order,
- // so the result is <nullptr, nullptr>
- static std::pair<mlir::Value, mlir::Value>
- getOrderedBounds(const SectionDesc &desc) {
- mlir::Value stride = desc.stride;
- // Null stride means stride=1.
- if (!stride)
- return {desc.lb, desc.ub};
- // Reverse the bounds, if stride is negative.
- if (auto val = fir::getIntIfConstant(stride)) {
- if (*val >= 0)
- return {desc.lb, desc.ub};
- else
- return {desc.ub, desc.lb};
- }
-
- return {nullptr, nullptr};
- }
-
- // Given two array sections <lb1, ub1, stride1> and
- // <lb2, ub2, stride2>, return true only if the sections
- // are known to be disjoint.
- //
- // For example, for any positive constant C:
- // X:Y does not overlap with (Y+C):Z
- // X:Y does not overlap with Z:(X-C)
- static bool areDisjointSections(const SectionDesc &desc1,
- const SectionDesc &desc2) {
- auto [lb1, ub1] = getOrderedBounds(desc1);
- auto [lb2, ub2] = getOrderedBounds(desc2);
- if (!lb1 || !lb2)
- return false;
- // Note that this comparison must be made on the ordered bounds,
- // otherwise 'a(x:y:1) = a(z:x-1:-1) + 1' may be incorrectly treated
- // as not overlapping (x=2, y=10, z=9).
- if (isLess(ub1, lb2) || isLess(ub2, lb1))
- return true;
- return false;
- }
-
- // Given two array sections <lb1, ub1, stride1> and
- // <lb2, ub2, stride2>, return true only if the sections
- // are known to be identical.
- //
- // For example:
- // <x, x, stride>
- // <x, nullptr, nullptr>
- //
- // These sections are identical, from the point of which array
- // elements are being addresses, even though the shape
- // of the array slices might be different.
- static bool areIdenticalSections(const SectionDesc &desc1,
- const SectionDesc &desc2) {
- if (desc1 == desc2)
- return true;
- return false;
- }
-
- // Return true, if v1 is known to be less than v2.
- static bool isLess(mlir::Value v1, mlir::Value v2);
-};
-
-ArraySectionAnalyzer::SlicesOverlapKind
-ArraySectionAnalyzer::analyze(mlir::Value ref1, mlir::Value ref2) {
- if (ref1 == ref2)
- return SlicesOverlapKind::DefinitelyIdentical;
-
- auto des1 = ref1.getDefiningOp<hlfir::DesignateOp>();
- auto des2 = ref2.getDefiningOp<hlfir::DesignateOp>();
- // We only support a pair of designators right now.
- if (!des1 || !des2)
- return SlicesOverlapKind::Unknown;
-
- if (des1.getMemref() != des2.getMemref()) {
- // If the bases are different, then there is unknown overlap.
- LLVM_DEBUG(llvm::dbgs() << "No identical base for:\n"
- << des1 << "and:\n"
- << des2 << "\n");
- return SlicesOverlapKind::Unknown;
- }
-
- // Require all components of the designators to be the same.
- // It might be too strict, e.g. we may probably allow for
- // different type parameters.
- if (des1.getComponent() != des2.getComponent() ||
- des1.getComponentShape() != des2.getComponentShape() ||
- des1.getSubstring() != des2.getSubstring() ||
- des1.getComplexPart() != des2.getComplexPart() ||
- des1.getTypeparams() != des2.getTypeparams()) {
- LLVM_DEBUG(llvm::dbgs() << "Different designator specs for:\n"
- << des1 << "and:\n"
- << des2 << "\n");
- return SlicesOverlapKind::Unknown;
- }
-
- // Analyze the subscripts.
- auto des1It = des1.getIndices().begin();
- auto des2It = des2.getIndices().begin();
- bool identicalTriplets = true;
- bool identicalIndices = true;
- for (auto [isTriplet1, isTriplet2] :
- llvm::zip(des1.getIsTriplet(), des2.getIsTriplet())) {
- SectionDesc desc1 = readSectionDesc(des1It, isTriplet1);
- SectionDesc desc2 = readSectionDesc(des2It, isTriplet2);
-
- // See if we can prove that any of the sections do not overlap.
- // This is mostly a Polyhedron/nf performance hack that looks for
- // particular relations between the lower and upper bounds
- // of the array sections, e.g. for any positive constant C:
- // X:Y does not overlap with (Y+C):Z
- // X:Y does not overlap with Z:(X-C)
- if (areDisjointSections(desc1, desc2))
- return SlicesOverlapKind::DefinitelyDisjoint;
-
- if (!areIdenticalSections(desc1, desc2)) {
- if (isTriplet1 || isTriplet2) {
- // For example:
- // hlfir.designate %6#0 (%c2:%c7999:%c1, %c1:%c120:%c1, %0)
- // hlfir.designate %6#0 (%c2:%c7999:%c1, %c1:%c120:%c1, %1)
- //
- // If all the triplets (section speficiers) are the same, then
- // we do not care if %0 is equal to %1 - the slices are either
- // identical or completely disjoint.
- //
- // Also, treat these as identical sections:
- // hlfir.designate %6#0 (%c2:%c2:%c1)
- // hlfir.designate %6#0 (%c2)
- identicalTriplets = false;
- LLVM_DEBUG(llvm::dbgs() << "Triplet mismatch for:\n"
- << des1 << "and:\n"
- << des2 << "\n");
- } else {
- identicalIndices = false;
- LLVM_DEBUG(llvm::dbgs() << "Indices mismatch for:\n"
- << des1 << "and:\n"
- << des2 << "\n");
- }
- }
- }
-
- if (identicalTriplets) {
- if (identicalIndices)
- return SlicesOverlapKind::DefinitelyIdentical;
- else
- return SlicesOverlapKind::EitherIdenticalOrDisjoint;
- }
-
- LLVM_DEBUG(llvm::dbgs() << "Different sections for:\n"
- << des1 << "and:\n"
- << des2 << "\n");
- return SlicesOverlapKind::Unknown;
-}
-
-bool ArraySectionAnalyzer::isLess(mlir::Value v1, mlir::Value v2) {
- auto removeConvert = [](mlir::Value v) -> mlir::Operation * {
- auto *op = v.getDefiningOp();
- while (auto conv = mlir::dyn_cast_or_null<fir::ConvertOp>(op))
- op = conv.getValue().getDefiningOp();
- return op;
- };
-
- auto isPositiveConstant = [](mlir::Value v) -> bool {
- if (auto val = fir::getIntIfConstant(v))
- return *val > 0;
- return false;
- };
-
- auto *op1 = removeConvert(v1);
- auto *op2 = removeConvert(v2);
- if (!op1 || !op2)
- return false;
-
- // Check if they are both constants.
- if (auto val1 = fir::getIntIfConstant(op1->getResult(0)))
- if (auto val2 = fir::getIntIfConstant(op2->getResult(0)))
- return *val1 < *val2;
-
- // Handle some variable cases (C > 0):
- // v2 = v1 + C
- // v2 = C + v1
- // v1 = v2 - C
- if (auto addi = mlir::dyn_cast<mlir::arith::AddIOp>(op2))
- if ((addi.getLhs().getDefiningOp() == op1 &&
- isPositiveConstant(addi.getRhs())) ||
- (addi.getRhs().getDefiningOp() == op1 &&
- isPositiveConstant(addi.getLhs())))
- return true;
- if (auto subi = mlir::dyn_cast<mlir::arith::SubIOp>(op1))
- if (subi.getLhs().getDefiningOp() == op2 &&
- isPositiveConstant(subi.getRhs()))
- return true;
- return false;
-}
-
-llvm::SmallVector<mlir::Value>
-ElementalAssignBufferization::getDesignatorIndices(
- hlfir::DesignateOp designate) {
- mlir::Value memref = designate.getMemref();
-
- // If the object is a box, then the indices may be adjusted
- // according to the box's lower bound(s). Scan through
- // the computations to try to find the one-based indices.
- if (mlir::isa<fir::BaseBoxType>(memref.getType())) {
- // Look for the following pattern:
- // %13 = fir.load %12 : !fir.ref<!fir.box<...>
- // %14:3 = fir.box_dims %13, %c0 : (!fir.box<...>, index) -> ...
- // %17 = arith.subi %14#0, %c1 : index
- // %18 = arith.addi %arg2, %17 : index
- // %19 = hlfir.designate %13 (%18) : (!fir.box<...>, index) -> ...
- //
- // %arg2 is a one-based index.
-
- auto isNormalizedLb = [memref](mlir::Value v, unsigned dim) {
- // Return true, if v and dim are such that:
- // %14:3 = fir.box_dims %13, %dim : (!fir.box<...>, index) -> ...
- // %17 = arith.subi %14#0, %c1 : index
- // %19 = hlfir.designate %13 (...) : (!fir.box<...>, index) -> ...
- if (auto subOp =
- mlir::dyn_cast_or_null<mlir::arith::SubIOp>(v.getDefiningOp())) {
- auto cst = fir::getIntIfConstant(subOp.getRhs());
- if (!cst || *cst != 1)
- return false;
- if (auto dimsOp = mlir::dyn_cast_or_null<fir::BoxDimsOp>(
- subOp.getLhs().getDefiningOp())) {
- if (memref != dimsOp.getVal() ||
- dimsOp.getResult(0) != subOp.getLhs())
- return false;
- auto dimsOpDim = fir::getIntIfConstant(dimsOp.getDim());
- return dimsOpDim && dimsOpDim == dim;
- }
- }
- return false;
- };
-
- llvm::SmallVector<mlir::Value> newIndices;
- for (auto index : llvm::enumerate(designate.getIndices())) {
- if (auto addOp = mlir::dyn_cast_or_null<mlir::arith::AddIOp>(
- index.value().getDefiningOp())) {
- for (unsigned opNum = 0; opNum < 2; ++opNum)
- if (isNormalizedLb(addOp->getOperand(opNum), index.index())) {
- newIndices.push_back(addOp->getOperand((opNum + 1) % 2));
- break;
- }
-
- // If new one-based index was not added, exit early.
- if (newIndices.size() <= index.index())
- break;
- }
- }
-
- // If any of the indices is not adjusted to the array's lb,
- // then return the original designator indices.
- if (newIndices.size() != designate.getIndices().size())
- return designate.getIndices();
-
- return newIndices;
- }
-
- return designate.getIndices();
-}
-
std::optional<ElementalAssignBufferization::MatchInfo>
ElementalAssignBufferization::findMatch(hlfir::ElementalOp elemental) {
mlir::Operation::user_range users = elemental->getUsers();
@@ -627,22 +283,20 @@ ElementalAssignBufferization::findMatch(hlfir::ElementalOp elemental) {
if (!res.isPartial()) {
if (auto designate =
effect.getValue().getDefiningOp<hlfir::DesignateOp>()) {
- ArraySectionAnalyzer::SlicesOverlapKind overlap =
- ArraySectionAnalyzer::analyze(match.array, designate.getMemref());
+ fir::ArraySectionAnalyzer::SlicesOverlapKind overlap =
+ fir::ArraySectionAnalyzer::analyze(match.array,
+ designate.getMemref());
if (overlap ==
- ArraySectionAnalyzer::SlicesOverlapKind::DefinitelyDisjoint)
+ fir::ArraySectionAnalyzer::SlicesOverlapKind::DefinitelyDisjoint)
continue;
- if (overlap == ArraySectionAnalyzer::SlicesOverlapKind::Unknown) {
+ if (overlap == fir::ArraySectionAnalyzer::SlicesOverlapKind::Unknown) {
LLVM_DEBUG(llvm::dbgs() << "possible read conflict: " << designate
<< " at " << elemental.getLoc() << "\n");
return std::nullopt;
}
- auto indices = getDesignatorIndices(designate);
- auto elementalIndices = elemental.getIndices();
- if (indices.size() == elementalIndices.size() &&
- std::equal(indices.begin(), indices.end(), elementalIndices.begin(),
- elementalIndices.end()))
+ if (fir::ArraySectionAnalyzer::isDesignatingArrayInOrder(designate,
+ elemental))
continue;
LLVM_DEBUG(llvm::dbgs() << "possible read conflict: " << designate