-
Notifications
You must be signed in to change notification settings - Fork 13.4k
[flang] Propagate contiguous attribute through HLFIR. #138797
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
This change allows marking more designators producing an opaque box with 'contiguous' attribute, e.g. like in test1 case in flang/test/HLFIR/propagate-contiguous-attribute.fir. This would make isSimplyContiguous() return true for such designators allowing merging hlfir.eval_in_mem with hlfir.assign where the LHS is a contiguous array section.
@llvm/pr-subscribers-flang-fir-hlfir Author: Slava Zakharin (vzakhari) ChangesThis change allows marking more designators producing an opaque Patch is 37.36 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/138797.diff 14 Files Affected:
diff --git a/flang/include/flang/Optimizer/Builder/HLFIRTools.h b/flang/include/flang/Optimizer/Builder/HLFIRTools.h
index ac80873dc374f..ed00cec04dc39 100644
--- a/flang/include/flang/Optimizer/Builder/HLFIRTools.h
+++ b/flang/include/flang/Optimizer/Builder/HLFIRTools.h
@@ -17,6 +17,7 @@
#include "flang/Optimizer/Dialect/FIROps.h"
#include "flang/Optimizer/Dialect/FortranVariableInterface.h"
#include "flang/Optimizer/HLFIR/HLFIRDialect.h"
+#include "flang/Optimizer/HLFIR/HLFIROps.h"
#include <optional>
namespace fir {
@@ -533,6 +534,12 @@ Entity gen1DSection(mlir::Location loc, fir::FirOpBuilder &builder,
mlir::ArrayRef<mlir::Value> extents,
mlir::ValueRange oneBasedIndices,
mlir::ArrayRef<mlir::Value> typeParams);
+
+/// Return true iff the given hlfir.designate produces
+/// a contiguous part of the memref object given that it is
+/// contiguous.
+bool designatePreservesContinuity(hlfir::DesignateOp op);
+
} // namespace hlfir
#endif // FORTRAN_OPTIMIZER_BUILDER_HLFIRTOOLS_H
diff --git a/flang/include/flang/Optimizer/Dialect/FIROpsSupport.h b/flang/include/flang/Optimizer/Dialect/FIROpsSupport.h
index b01d72199bf1d..e71a622725bf4 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROpsSupport.h
+++ b/flang/include/flang/Optimizer/Dialect/FIROpsSupport.h
@@ -221,6 +221,13 @@ inline bool hasBindcAttr(mlir::Operation *op) {
return hasProcedureAttr<fir::FortranProcedureFlagsEnum::bind_c>(op);
}
+/// Return true, if \p rebox operation keeps the input array
+/// continuous if it is initially continuous.
+/// When \p checkWhole is false, then the checking is only done
+/// for continuity in the innermost dimension, otherwise,
+/// the checking is done for continuity of the whole result of rebox.
+bool reboxPreservesContinuity(fir::ReboxOp rebox, bool checkWhole = true);
+
} // namespace fir
#endif // FORTRAN_OPTIMIZER_DIALECT_FIROPSSUPPORT_H
diff --git a/flang/include/flang/Optimizer/Dialect/FortranVariableInterface.td b/flang/include/flang/Optimizer/Dialect/FortranVariableInterface.td
index 0fe2e60a1a95c..c2c9a03d2b890 100644
--- a/flang/include/flang/Optimizer/Dialect/FortranVariableInterface.td
+++ b/flang/include/flang/Optimizer/Dialect/FortranVariableInterface.td
@@ -22,51 +22,57 @@ def fir_FortranVariableOpInterface : OpInterface<"FortranVariableOpInterface"> {
query about all their Fortran properties.
}];
- let methods = [
- InterfaceMethod<
- /*desc=*/"Get the address produced by the definition",
- /*retTy=*/"mlir::Value",
- /*methodName=*/"getBase",
- /*args=*/(ins),
- /*methodBody=*/[{}],
- /*defaultImplementation=*/[{
+ let methods =
+ [InterfaceMethod<
+ /*desc=*/"Get the address produced by the definition",
+ /*retTy=*/"mlir::Value",
+ /*methodName=*/"getBase",
+ /*args=*/(ins),
+ /*methodBody=*/[{}],
+ /*defaultImplementation=*/[{
ConcreteOp op = mlir::cast<ConcreteOp>(this->getOperation());
return op.getResult();
- }]
- >,
- InterfaceMethod<
- /*desc=*/"Get Fortran attributes",
- /*retTy=*/"std::optional<fir::FortranVariableFlagsEnum>",
- /*methodName=*/"getFortranAttrs",
- /*args=*/(ins),
- /*methodBody=*/[{}],
- /*defaultImplementation=*/[{
+ }]>,
+ InterfaceMethod<
+ /*desc=*/"Get Fortran attributes",
+ /*retTy=*/"std::optional<fir::FortranVariableFlagsEnum>",
+ /*methodName=*/"getFortranAttrs",
+ /*args=*/(ins),
+ /*methodBody=*/[{}],
+ /*defaultImplementation=*/[{
ConcreteOp op = mlir::cast<ConcreteOp>(this->getOperation());
return op.getFortran_attrs();
- }]
- >,
- InterfaceMethod<
- /*desc=*/"Get the shape of the variable. May be a null value.",
- /*retTy=*/"mlir::Value",
- /*methodName=*/"getShape",
- /*args=*/(ins),
- /*methodBody=*/[{}],
- /*defaultImplementation=*/[{
+ }]>,
+ InterfaceMethod<
+ /*desc=*/"Get the shape of the variable. May be a null value.",
+ /*retTy=*/"mlir::Value",
+ /*methodName=*/"getShape",
+ /*args=*/(ins),
+ /*methodBody=*/[{}],
+ /*defaultImplementation=*/[{
ConcreteOp op = mlir::cast<ConcreteOp>(this->getOperation());
return op.getShape();
- }]
- >,
- InterfaceMethod<
- /*desc=*/"Get explicit type parameters of the variable",
- /*retTy=*/"mlir::OperandRange",
- /*methodName=*/"getExplicitTypeParams",
- /*args=*/(ins),
- /*methodBody=*/[{}],
- /*defaultImplementation=*/[{
+ }]>,
+ InterfaceMethod<
+ /*desc=*/"Get explicit type parameters of the variable",
+ /*retTy=*/"mlir::OperandRange",
+ /*methodName=*/"getExplicitTypeParams",
+ /*args=*/(ins),
+ /*methodBody=*/[{}],
+ /*defaultImplementation=*/[{
ConcreteOp op = mlir::cast<ConcreteOp>(this->getOperation());
return op.getTypeparams();
- }]
- >,
+ }]>,
+ InterfaceMethod<
+ /*desc=*/"Set Fortran attributes",
+ /*retTy=*/"void",
+ /*methodName=*/"setFortranAttrs",
+ /*args=*/(ins "fir::FortranVariableFlagsEnum":$flags),
+ /*methodBody=*/[{}],
+ /*defaultImplementation=*/[{
+ ConcreteOp op = mlir::cast<ConcreteOp>(this->getOperation());
+ op.setFortran_attrs(fir::FortranVariableFlagsAttr::get(op->getContext(), flags));
+ }]>,
];
let extraClassDeclaration = [{
diff --git a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
index f69930d5b53b3..4a83b4601fd8a 100644
--- a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
+++ b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
@@ -313,6 +313,7 @@ def hlfir_ParentComponentOp : hlfir_Op<"parent_comp", [AttrSizedOperandSegments,
std::optional<fir::FortranVariableFlagsEnum> getFortranAttrs() const {
return std::nullopt;
}
+ void setFortranAttrs(fir::FortranVariableFlagsEnum flags) {}
}];
let results = (outs AnyFortranVariable);
@@ -1078,6 +1079,7 @@ def hlfir_NullOp : hlfir_Op<"null", [NoMemoryEffect, fir_FortranVariableOpInterf
std::optional<fir::FortranVariableFlagsEnum> getFortranAttrs() const {
return std::nullopt;
}
+ void setFortranAttrs(fir::FortranVariableFlagsEnum flags) {}
mlir::Value getShape() const {return mlir::Value{};}
mlir::OperandRange getExplicitTypeParams() const {
// Return an empty range.
@@ -1766,6 +1768,7 @@ def hlfir_ForallIndexOp : hlfir_Op<"forall_index", [fir_FortranVariableOpInterfa
std::optional<fir::FortranVariableFlagsEnum> getFortranAttrs() const {
return std::nullopt;
}
+ void setFortranAttrs(fir::FortranVariableFlagsEnum flags) {}
mlir::Value getShape() const {return mlir::Value{};}
mlir::OperandRange getExplicitTypeParams() const {
// Return an empty range.
diff --git a/flang/include/flang/Optimizer/HLFIR/Passes.td b/flang/include/flang/Optimizer/HLFIR/Passes.td
index eae0c9ca2e366..d445140118073 100644
--- a/flang/include/flang/Optimizer/HLFIR/Passes.td
+++ b/flang/include/flang/Optimizer/HLFIR/Passes.td
@@ -69,4 +69,8 @@ def InlineHLFIRAssign : Pass<"inline-hlfir-assign"> {
let summary = "Inline hlfir.assign operations";
}
+def PropagateFortranVariableAttributes : Pass<"propagate-fortran-attrs"> {
+ let summary = "Propagate FortranVariableFlagsAttr attributes through HLFIR";
+}
+
#endif //FORTRAN_DIALECT_HLFIR_PASSES
diff --git a/flang/lib/Optimizer/Builder/HLFIRTools.cpp b/flang/lib/Optimizer/Builder/HLFIRTools.cpp
index 51ea7305d3d26..5d904407c2270 100644
--- a/flang/lib/Optimizer/Builder/HLFIRTools.cpp
+++ b/flang/lib/Optimizer/Builder/HLFIRTools.cpp
@@ -1608,3 +1608,32 @@ hlfir::Entity hlfir::gen1DSection(mlir::Location loc,
sectionShape, typeParams);
return hlfir::Entity{designate.getResult()};
}
+
+bool hlfir::designatePreservesContinuity(hlfir::DesignateOp op) {
+ if (op.getComponent() || op.getComplexPart() || !op.getSubstring().empty())
+ return false;
+ auto subscripts = op.getIndices();
+ unsigned i = 0;
+ for (auto isTriplet : llvm::enumerate(op.getIsTriplet())) {
+ // TODO: we should allow any number of leading triplets
+ // that describe a whole dimension slice, then one optional
+ // triplet describing potentially partial dimension slice,
+ // then any number of non-triplet subscripts.
+ // For the time being just allow a single leading
+ // triplet and then any number of non-triplet subscripts.
+ if (isTriplet.value()) {
+ if (isTriplet.index() != 0) {
+ return false;
+ } else {
+ i += 2;
+ mlir::Value step = subscripts[i++];
+ auto constantStep = fir::getIntIfConstant(step);
+ if (!constantStep || *constantStep != 1)
+ return false;
+ }
+ } else {
+ ++i;
+ }
+ }
+ return true;
+}
diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp
index 05ef69169bae5..f88b64f821ac2 100644
--- a/flang/lib/Optimizer/Dialect/FIROps.cpp
+++ b/flang/lib/Optimizer/Dialect/FIROps.cpp
@@ -4721,6 +4721,48 @@ mlir::Type fir::applyPathToType(mlir::Type eleTy, mlir::ValueRange path) {
return eleTy;
}
+bool fir::reboxPreservesContinuity(fir::ReboxOp rebox, bool checkWhole) {
+ // If slicing is not involved, then the rebox does not affect
+ // the continuity of the array.
+ auto sliceArg = rebox.getSlice();
+ if (!sliceArg)
+ return true;
+
+ if (auto sliceOp =
+ mlir::dyn_cast_or_null<fir::SliceOp>(sliceArg.getDefiningOp())) {
+ if (sliceOp.getFields().empty() && sliceOp.getSubstr().empty()) {
+ // TODO: generalize code for the triples analysis with
+ // hlfir::designatePreservesContinuity, especially when
+ // recognition of the whole dimension slices is added.
+ auto triples = sliceOp.getTriples();
+ assert((triples.size() % 3) == 0 && "invalid triples size");
+
+ // A slice with step=1 in the innermost dimension preserves
+ // the continuity of the array in the innermost dimension.
+ // If checkWhole is false, then check only the innermost slice triples.
+ std::size_t checkUpTo = checkWhole ? triples.size() : 3;
+ checkUpTo = std::min(checkUpTo, triples.size());
+ for (std::size_t i = 0; i < checkUpTo; i += 3) {
+ if (triples[i] != triples[i + 1]) {
+ // This is a section of the dimension. Only allow it
+ // to be the first triple.
+ if (i != 0)
+ return false;
+ auto constantStep = fir::getIntIfConstant(triples[i + 2]);
+ if (!constantStep || *constantStep != 1)
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+//===----------------------------------------------------------------------===//
+// DeclareOp
+//===----------------------------------------------------------------------===//
+
llvm::LogicalResult fir::DeclareOp::verify() {
auto fortranVar =
mlir::cast<fir::FortranVariableOpInterface>(this->getOperation());
diff --git a/flang/lib/Optimizer/HLFIR/Transforms/CMakeLists.txt b/flang/lib/Optimizer/HLFIR/Transforms/CMakeLists.txt
index 7eb3cb4001d5f..d959428ebd203 100644
--- a/flang/lib/Optimizer/HLFIR/Transforms/CMakeLists.txt
+++ b/flang/lib/Optimizer/HLFIR/Transforms/CMakeLists.txt
@@ -10,6 +10,7 @@ add_flang_library(HLFIRTransforms
ScheduleOrderedAssignments.cpp
SimplifyHLFIRIntrinsics.cpp
OptimizedBufferization.cpp
+ PropagateFortranVariableAttributes.cpp
DEPENDS
CUFAttrsIncGen
diff --git a/flang/lib/Optimizer/HLFIR/Transforms/PropagateFortranVariableAttributes.cpp b/flang/lib/Optimizer/HLFIR/Transforms/PropagateFortranVariableAttributes.cpp
new file mode 100644
index 0000000000000..1b996e2399ec0
--- /dev/null
+++ b/flang/lib/Optimizer/HLFIR/Transforms/PropagateFortranVariableAttributes.cpp
@@ -0,0 +1,123 @@
+//===- PropagateFortranVariableAttributes.cpp -----------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// This file defines a pass that propagates FortranVariableFlagsAttr
+/// attributes through HLFIR. For example, it can set contiguous attribute
+/// on hlfir.designate that produces a contiguous slice of a contiguous
+/// Fortran array. This pass can be applied multiple times to expose
+/// more Fortran attributes, e.g. after inlining and constant propagation.
+//===----------------------------------------------------------------------===//
+
+#include "flang/Optimizer/Builder/HLFIRTools.h"
+#include "flang/Optimizer/Dialect/FIROpsSupport.h"
+#include "flang/Optimizer/HLFIR/HLFIRDialect.h"
+#include "flang/Optimizer/HLFIR/HLFIROps.h"
+#include "flang/Optimizer/HLFIR/Passes.h"
+#include "llvm/ADT/TypeSwitch.h"
+
+namespace hlfir {
+#define GEN_PASS_DEF_PROPAGATEFORTRANVARIABLEATTRIBUTES
+#include "flang/Optimizer/HLFIR/Passes.h.inc"
+} // namespace hlfir
+
+#define DEBUG_TYPE "propagate-fortran-attrs"
+
+namespace {
+class PropagateFortranVariableAttributes
+ : public hlfir::impl::PropagateFortranVariableAttributesBase<
+ PropagateFortranVariableAttributes> {
+public:
+ using PropagateFortranVariableAttributesBase<
+ PropagateFortranVariableAttributes>::
+ PropagateFortranVariableAttributesBase;
+ void runOnOperation() override;
+};
+
+class Propagator {
+public:
+ Propagator() {}
+
+ void process(mlir::Operation *op);
+
+private:
+ static bool isContiguous(mlir::Operation *op) {
+ if (mlir::isa<fir::AllocaOp, fir::AllocMemOp>(op))
+ return true;
+ auto varOp = mlir::dyn_cast<fir::FortranVariableOpInterface>(op);
+ if (!varOp)
+ return false;
+ return hlfir::Entity{varOp}.isSimplyContiguous();
+ }
+
+ static void setContiguousAttr(fir::FortranVariableOpInterface op);
+};
+} // namespace
+
+void Propagator::setContiguousAttr(fir::FortranVariableOpInterface op) {
+ LLVM_DEBUG(llvm::dbgs() << "Setting continuity for:\n" << op << "\n");
+ fir::FortranVariableFlagsEnum attrs =
+ op.getFortranAttrs().value_or(fir::FortranVariableFlagsEnum::None);
+ attrs = attrs | fir::FortranVariableFlagsEnum::contiguous;
+ op.setFortranAttrs(attrs);
+}
+
+void Propagator::process(mlir::Operation *op) {
+ if (!isContiguous(op))
+ return;
+ llvm::SmallVector<mlir::Operation *> workList{op};
+ while (!workList.empty()) {
+ mlir::Operation *current = workList.pop_back_val();
+ LLVM_DEBUG(llvm::dbgs() << "Propagating continuity from operation:\n"
+ << *current << "\n");
+
+ for (mlir::OpOperand &use : current->getUses()) {
+ mlir::Operation *useOp = use.getOwner();
+ if (auto varOp = mlir::dyn_cast<fir::FortranVariableOpInterface>(useOp)) {
+ // If the user is not currently contiguous, set the contiguous
+ // attribute and skip it. The propagation will pick it up later.
+ mlir::Value memref;
+ mlir::TypeSwitch<mlir::Operation *, void>(useOp)
+ .Case<hlfir::DeclareOp, hlfir::DesignateOp>(
+ [&](auto op) { memref = op.getMemref(); })
+ .Default([&](auto op) {});
+
+ if (memref == use.get() && !isContiguous(varOp)) {
+ // Make additional checks for hlfir.designate.
+ if (auto designateOp = mlir::dyn_cast<hlfir::DesignateOp>(useOp))
+ if (!hlfir::designatePreservesContinuity(designateOp))
+ continue;
+
+ setContiguousAttr(varOp);
+ }
+ continue;
+ }
+ mlir::TypeSwitch<mlir::Operation *, void>(useOp)
+ .Case(
+ [&](fir::ConvertOp op) { workList.push_back(op.getOperation()); })
+ .Case([&](fir::EmboxOp op) {
+ if (op.getMemref() == use.get())
+ workList.push_back(op.getOperation());
+ })
+ .Case([&](fir::ReboxOp op) {
+ if (op.getBox() == use.get() && fir::reboxPreservesContinuity(op))
+ workList.push_back(op.getOperation());
+ });
+ }
+ }
+}
+
+void PropagateFortranVariableAttributes::runOnOperation() {
+ mlir::Operation *rootOp = getOperation();
+ mlir::MLIRContext *context = &getContext();
+ mlir::RewritePatternSet patterns(context);
+ Propagator propagator;
+ rootOp->walk<mlir::WalkOrder::PreOrder>([&](mlir::Operation *op) {
+ propagator.process(op);
+ return mlir::WalkResult::advance();
+ });
+}
diff --git a/flang/lib/Optimizer/Passes/Pipelines.cpp b/flang/lib/Optimizer/Passes/Pipelines.cpp
index a3ef473ea39b7..77751908e35be 100644
--- a/flang/lib/Optimizer/Passes/Pipelines.cpp
+++ b/flang/lib/Optimizer/Passes/Pipelines.cpp
@@ -249,6 +249,8 @@ void createHLFIRToFIRPassPipeline(mlir::PassManager &pm, bool enableOpenMP,
return hlfir::createSimplifyHLFIRIntrinsics(
{/*allowNewSideEffects=*/true});
});
+ addNestedPassToAllTopLevelOperations<PassConstructor>(
+ pm, hlfir::createPropagateFortranVariableAttributes);
addNestedPassToAllTopLevelOperations<PassConstructor>(
pm, hlfir::createOptimizedBufferization);
addNestedPassToAllTopLevelOperations<PassConstructor>(
diff --git a/flang/lib/Optimizer/Transforms/LoopVersioning.cpp b/flang/lib/Optimizer/Transforms/LoopVersioning.cpp
index 42e149bb3dba2..858e35ccb5f81 100644
--- a/flang/lib/Optimizer/Transforms/LoopVersioning.cpp
+++ b/flang/lib/Optimizer/Transforms/LoopVersioning.cpp
@@ -209,46 +209,15 @@ static mlir::Value unwrapPassThroughOps(mlir::Value val) {
return val;
}
-/// Return true, if \p rebox operation keeps the input array
-/// continuous in the innermost dimension, if it is initially continuous
-/// in the innermost dimension.
-static bool reboxPreservesContinuity(fir::ReboxOp rebox) {
- // If slicing is not involved, then the rebox does not affect
- // the continuity of the array.
- auto sliceArg = rebox.getSlice();
- if (!sliceArg)
- return true;
-
- // A slice with step=1 in the innermost dimension preserves
- // the continuity of the array in the innermost dimension.
- if (auto sliceOp =
- mlir::dyn_cast_or_null<fir::SliceOp>(sliceArg.getDefiningOp())) {
- if (sliceOp.getFields().empty() && sliceOp.getSubstr().empty()) {
- auto triples = sliceOp.getTriples();
- if (triples.size() > 2)
- if (auto innermostStep = fir::getIntIfConstant(triples[2]))
- if (*innermostStep == 1)
- return true;
- }
-
- LLVM_DEBUG(llvm::dbgs()
- << "REBOX with slicing may produce non-contiguous array: "
- << sliceOp << '\n'
- << rebox << '\n');
- return false;
- }
-
- LLVM_DEBUG(llvm::dbgs() << "REBOX with unknown slice" << sliceArg << '\n'
- << rebox << '\n');
- return false;
-}
-
/// if a value comes from a fir.rebox, follow the rebox to the original source,
/// of the value, otherwise return the value
static mlir::Value unwrapReboxOp(mlir::Value val) {
while (fir::ReboxOp rebox = val.getDefiningOp<fir::ReboxOp>()) {
- if (!reboxPreservesContinuity(rebox))
+ if (!fir::reboxPreservesContinuity(rebox, /*checkWhole=*/false)) {
+ LLVM_DEBUG(llvm::dbgs() << "REBOX may produce non-contiguous array: "
+ << rebox << '\n');
break;
+ }
val = rebox.getBox();
}
return val;
diff --git a/flang/test/Driver/mlir-pass-pipeline.f90 b/flang/test/Driver/mlir-pass-pipeline.f90
index 45370895db397..99f192ce7aec2 100644
--- a/flang/test/Driver/mlir-pass-pipeline.f90
+++ b/flang/test/Driver/mlir-pass-pipeline.f90
@@ -36,18 +36,22 @@
! O2-NEXT: Pipeline Collection : ['fir.global', 'func.func', 'omp.declare_reduction', 'omp.private']
! O2-NEXT: 'fir.global' Pipeline
! O2-NEXT: SimplifyHLFIRIntrinsics
+! O2-NEXT: PropagateFortranVariableAttributes
! O2-NEXT: OptimizedBufferization
! O2-NEXT: InlineHLFIRAssign
! O2-NEXT: 'func.func' Pipeline
! O2-NEXT: SimplifyHLFIRIntrinsics
+! O2-NEXT: PropagateFortranVariableAttributes
! O2-NEXT: OptimizedBufferization
! O2-NEXT: Inline...
[truncated]
|
@llvm/pr-subscribers-flang-driver Author: Slava Zakharin (vzakhari) ChangesThis change allows marking more designators producing an opaque Patch is 37.36 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/138797.diff 14 Files Affected:
diff --git a/flang/include/flang/Optimizer/Builder/HLFIRTools.h b/flang/include/flang/Optimizer/Builder/HLFIRTools.h
index ac80873dc374f..ed00cec04dc39 100644
--- a/flang/include/flang/Optimizer/Builder/HLFIRTools.h
+++ b/flang/include/flang/Optimizer/Builder/HLFIRTools.h
@@ -17,6 +17,7 @@
#include "flang/Optimizer/Dialect/FIROps.h"
#include "flang/Optimizer/Dialect/FortranVariableInterface.h"
#include "flang/Optimizer/HLFIR/HLFIRDialect.h"
+#include "flang/Optimizer/HLFIR/HLFIROps.h"
#include <optional>
namespace fir {
@@ -533,6 +534,12 @@ Entity gen1DSection(mlir::Location loc, fir::FirOpBuilder &builder,
mlir::ArrayRef<mlir::Value> extents,
mlir::ValueRange oneBasedIndices,
mlir::ArrayRef<mlir::Value> typeParams);
+
+/// Return true iff the given hlfir.designate produces
+/// a contiguous part of the memref object given that it is
+/// contiguous.
+bool designatePreservesContinuity(hlfir::DesignateOp op);
+
} // namespace hlfir
#endif // FORTRAN_OPTIMIZER_BUILDER_HLFIRTOOLS_H
diff --git a/flang/include/flang/Optimizer/Dialect/FIROpsSupport.h b/flang/include/flang/Optimizer/Dialect/FIROpsSupport.h
index b01d72199bf1d..e71a622725bf4 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROpsSupport.h
+++ b/flang/include/flang/Optimizer/Dialect/FIROpsSupport.h
@@ -221,6 +221,13 @@ inline bool hasBindcAttr(mlir::Operation *op) {
return hasProcedureAttr<fir::FortranProcedureFlagsEnum::bind_c>(op);
}
+/// Return true, if \p rebox operation keeps the input array
+/// continuous if it is initially continuous.
+/// When \p checkWhole is false, then the checking is only done
+/// for continuity in the innermost dimension, otherwise,
+/// the checking is done for continuity of the whole result of rebox.
+bool reboxPreservesContinuity(fir::ReboxOp rebox, bool checkWhole = true);
+
} // namespace fir
#endif // FORTRAN_OPTIMIZER_DIALECT_FIROPSSUPPORT_H
diff --git a/flang/include/flang/Optimizer/Dialect/FortranVariableInterface.td b/flang/include/flang/Optimizer/Dialect/FortranVariableInterface.td
index 0fe2e60a1a95c..c2c9a03d2b890 100644
--- a/flang/include/flang/Optimizer/Dialect/FortranVariableInterface.td
+++ b/flang/include/flang/Optimizer/Dialect/FortranVariableInterface.td
@@ -22,51 +22,57 @@ def fir_FortranVariableOpInterface : OpInterface<"FortranVariableOpInterface"> {
query about all their Fortran properties.
}];
- let methods = [
- InterfaceMethod<
- /*desc=*/"Get the address produced by the definition",
- /*retTy=*/"mlir::Value",
- /*methodName=*/"getBase",
- /*args=*/(ins),
- /*methodBody=*/[{}],
- /*defaultImplementation=*/[{
+ let methods =
+ [InterfaceMethod<
+ /*desc=*/"Get the address produced by the definition",
+ /*retTy=*/"mlir::Value",
+ /*methodName=*/"getBase",
+ /*args=*/(ins),
+ /*methodBody=*/[{}],
+ /*defaultImplementation=*/[{
ConcreteOp op = mlir::cast<ConcreteOp>(this->getOperation());
return op.getResult();
- }]
- >,
- InterfaceMethod<
- /*desc=*/"Get Fortran attributes",
- /*retTy=*/"std::optional<fir::FortranVariableFlagsEnum>",
- /*methodName=*/"getFortranAttrs",
- /*args=*/(ins),
- /*methodBody=*/[{}],
- /*defaultImplementation=*/[{
+ }]>,
+ InterfaceMethod<
+ /*desc=*/"Get Fortran attributes",
+ /*retTy=*/"std::optional<fir::FortranVariableFlagsEnum>",
+ /*methodName=*/"getFortranAttrs",
+ /*args=*/(ins),
+ /*methodBody=*/[{}],
+ /*defaultImplementation=*/[{
ConcreteOp op = mlir::cast<ConcreteOp>(this->getOperation());
return op.getFortran_attrs();
- }]
- >,
- InterfaceMethod<
- /*desc=*/"Get the shape of the variable. May be a null value.",
- /*retTy=*/"mlir::Value",
- /*methodName=*/"getShape",
- /*args=*/(ins),
- /*methodBody=*/[{}],
- /*defaultImplementation=*/[{
+ }]>,
+ InterfaceMethod<
+ /*desc=*/"Get the shape of the variable. May be a null value.",
+ /*retTy=*/"mlir::Value",
+ /*methodName=*/"getShape",
+ /*args=*/(ins),
+ /*methodBody=*/[{}],
+ /*defaultImplementation=*/[{
ConcreteOp op = mlir::cast<ConcreteOp>(this->getOperation());
return op.getShape();
- }]
- >,
- InterfaceMethod<
- /*desc=*/"Get explicit type parameters of the variable",
- /*retTy=*/"mlir::OperandRange",
- /*methodName=*/"getExplicitTypeParams",
- /*args=*/(ins),
- /*methodBody=*/[{}],
- /*defaultImplementation=*/[{
+ }]>,
+ InterfaceMethod<
+ /*desc=*/"Get explicit type parameters of the variable",
+ /*retTy=*/"mlir::OperandRange",
+ /*methodName=*/"getExplicitTypeParams",
+ /*args=*/(ins),
+ /*methodBody=*/[{}],
+ /*defaultImplementation=*/[{
ConcreteOp op = mlir::cast<ConcreteOp>(this->getOperation());
return op.getTypeparams();
- }]
- >,
+ }]>,
+ InterfaceMethod<
+ /*desc=*/"Set Fortran attributes",
+ /*retTy=*/"void",
+ /*methodName=*/"setFortranAttrs",
+ /*args=*/(ins "fir::FortranVariableFlagsEnum":$flags),
+ /*methodBody=*/[{}],
+ /*defaultImplementation=*/[{
+ ConcreteOp op = mlir::cast<ConcreteOp>(this->getOperation());
+ op.setFortran_attrs(fir::FortranVariableFlagsAttr::get(op->getContext(), flags));
+ }]>,
];
let extraClassDeclaration = [{
diff --git a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
index f69930d5b53b3..4a83b4601fd8a 100644
--- a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
+++ b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
@@ -313,6 +313,7 @@ def hlfir_ParentComponentOp : hlfir_Op<"parent_comp", [AttrSizedOperandSegments,
std::optional<fir::FortranVariableFlagsEnum> getFortranAttrs() const {
return std::nullopt;
}
+ void setFortranAttrs(fir::FortranVariableFlagsEnum flags) {}
}];
let results = (outs AnyFortranVariable);
@@ -1078,6 +1079,7 @@ def hlfir_NullOp : hlfir_Op<"null", [NoMemoryEffect, fir_FortranVariableOpInterf
std::optional<fir::FortranVariableFlagsEnum> getFortranAttrs() const {
return std::nullopt;
}
+ void setFortranAttrs(fir::FortranVariableFlagsEnum flags) {}
mlir::Value getShape() const {return mlir::Value{};}
mlir::OperandRange getExplicitTypeParams() const {
// Return an empty range.
@@ -1766,6 +1768,7 @@ def hlfir_ForallIndexOp : hlfir_Op<"forall_index", [fir_FortranVariableOpInterfa
std::optional<fir::FortranVariableFlagsEnum> getFortranAttrs() const {
return std::nullopt;
}
+ void setFortranAttrs(fir::FortranVariableFlagsEnum flags) {}
mlir::Value getShape() const {return mlir::Value{};}
mlir::OperandRange getExplicitTypeParams() const {
// Return an empty range.
diff --git a/flang/include/flang/Optimizer/HLFIR/Passes.td b/flang/include/flang/Optimizer/HLFIR/Passes.td
index eae0c9ca2e366..d445140118073 100644
--- a/flang/include/flang/Optimizer/HLFIR/Passes.td
+++ b/flang/include/flang/Optimizer/HLFIR/Passes.td
@@ -69,4 +69,8 @@ def InlineHLFIRAssign : Pass<"inline-hlfir-assign"> {
let summary = "Inline hlfir.assign operations";
}
+def PropagateFortranVariableAttributes : Pass<"propagate-fortran-attrs"> {
+ let summary = "Propagate FortranVariableFlagsAttr attributes through HLFIR";
+}
+
#endif //FORTRAN_DIALECT_HLFIR_PASSES
diff --git a/flang/lib/Optimizer/Builder/HLFIRTools.cpp b/flang/lib/Optimizer/Builder/HLFIRTools.cpp
index 51ea7305d3d26..5d904407c2270 100644
--- a/flang/lib/Optimizer/Builder/HLFIRTools.cpp
+++ b/flang/lib/Optimizer/Builder/HLFIRTools.cpp
@@ -1608,3 +1608,32 @@ hlfir::Entity hlfir::gen1DSection(mlir::Location loc,
sectionShape, typeParams);
return hlfir::Entity{designate.getResult()};
}
+
+bool hlfir::designatePreservesContinuity(hlfir::DesignateOp op) {
+ if (op.getComponent() || op.getComplexPart() || !op.getSubstring().empty())
+ return false;
+ auto subscripts = op.getIndices();
+ unsigned i = 0;
+ for (auto isTriplet : llvm::enumerate(op.getIsTriplet())) {
+ // TODO: we should allow any number of leading triplets
+ // that describe a whole dimension slice, then one optional
+ // triplet describing potentially partial dimension slice,
+ // then any number of non-triplet subscripts.
+ // For the time being just allow a single leading
+ // triplet and then any number of non-triplet subscripts.
+ if (isTriplet.value()) {
+ if (isTriplet.index() != 0) {
+ return false;
+ } else {
+ i += 2;
+ mlir::Value step = subscripts[i++];
+ auto constantStep = fir::getIntIfConstant(step);
+ if (!constantStep || *constantStep != 1)
+ return false;
+ }
+ } else {
+ ++i;
+ }
+ }
+ return true;
+}
diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp
index 05ef69169bae5..f88b64f821ac2 100644
--- a/flang/lib/Optimizer/Dialect/FIROps.cpp
+++ b/flang/lib/Optimizer/Dialect/FIROps.cpp
@@ -4721,6 +4721,48 @@ mlir::Type fir::applyPathToType(mlir::Type eleTy, mlir::ValueRange path) {
return eleTy;
}
+bool fir::reboxPreservesContinuity(fir::ReboxOp rebox, bool checkWhole) {
+ // If slicing is not involved, then the rebox does not affect
+ // the continuity of the array.
+ auto sliceArg = rebox.getSlice();
+ if (!sliceArg)
+ return true;
+
+ if (auto sliceOp =
+ mlir::dyn_cast_or_null<fir::SliceOp>(sliceArg.getDefiningOp())) {
+ if (sliceOp.getFields().empty() && sliceOp.getSubstr().empty()) {
+ // TODO: generalize code for the triples analysis with
+ // hlfir::designatePreservesContinuity, especially when
+ // recognition of the whole dimension slices is added.
+ auto triples = sliceOp.getTriples();
+ assert((triples.size() % 3) == 0 && "invalid triples size");
+
+ // A slice with step=1 in the innermost dimension preserves
+ // the continuity of the array in the innermost dimension.
+ // If checkWhole is false, then check only the innermost slice triples.
+ std::size_t checkUpTo = checkWhole ? triples.size() : 3;
+ checkUpTo = std::min(checkUpTo, triples.size());
+ for (std::size_t i = 0; i < checkUpTo; i += 3) {
+ if (triples[i] != triples[i + 1]) {
+ // This is a section of the dimension. Only allow it
+ // to be the first triple.
+ if (i != 0)
+ return false;
+ auto constantStep = fir::getIntIfConstant(triples[i + 2]);
+ if (!constantStep || *constantStep != 1)
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+//===----------------------------------------------------------------------===//
+// DeclareOp
+//===----------------------------------------------------------------------===//
+
llvm::LogicalResult fir::DeclareOp::verify() {
auto fortranVar =
mlir::cast<fir::FortranVariableOpInterface>(this->getOperation());
diff --git a/flang/lib/Optimizer/HLFIR/Transforms/CMakeLists.txt b/flang/lib/Optimizer/HLFIR/Transforms/CMakeLists.txt
index 7eb3cb4001d5f..d959428ebd203 100644
--- a/flang/lib/Optimizer/HLFIR/Transforms/CMakeLists.txt
+++ b/flang/lib/Optimizer/HLFIR/Transforms/CMakeLists.txt
@@ -10,6 +10,7 @@ add_flang_library(HLFIRTransforms
ScheduleOrderedAssignments.cpp
SimplifyHLFIRIntrinsics.cpp
OptimizedBufferization.cpp
+ PropagateFortranVariableAttributes.cpp
DEPENDS
CUFAttrsIncGen
diff --git a/flang/lib/Optimizer/HLFIR/Transforms/PropagateFortranVariableAttributes.cpp b/flang/lib/Optimizer/HLFIR/Transforms/PropagateFortranVariableAttributes.cpp
new file mode 100644
index 0000000000000..1b996e2399ec0
--- /dev/null
+++ b/flang/lib/Optimizer/HLFIR/Transforms/PropagateFortranVariableAttributes.cpp
@@ -0,0 +1,123 @@
+//===- PropagateFortranVariableAttributes.cpp -----------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// This file defines a pass that propagates FortranVariableFlagsAttr
+/// attributes through HLFIR. For example, it can set contiguous attribute
+/// on hlfir.designate that produces a contiguous slice of a contiguous
+/// Fortran array. This pass can be applied multiple times to expose
+/// more Fortran attributes, e.g. after inlining and constant propagation.
+//===----------------------------------------------------------------------===//
+
+#include "flang/Optimizer/Builder/HLFIRTools.h"
+#include "flang/Optimizer/Dialect/FIROpsSupport.h"
+#include "flang/Optimizer/HLFIR/HLFIRDialect.h"
+#include "flang/Optimizer/HLFIR/HLFIROps.h"
+#include "flang/Optimizer/HLFIR/Passes.h"
+#include "llvm/ADT/TypeSwitch.h"
+
+namespace hlfir {
+#define GEN_PASS_DEF_PROPAGATEFORTRANVARIABLEATTRIBUTES
+#include "flang/Optimizer/HLFIR/Passes.h.inc"
+} // namespace hlfir
+
+#define DEBUG_TYPE "propagate-fortran-attrs"
+
+namespace {
+class PropagateFortranVariableAttributes
+ : public hlfir::impl::PropagateFortranVariableAttributesBase<
+ PropagateFortranVariableAttributes> {
+public:
+ using PropagateFortranVariableAttributesBase<
+ PropagateFortranVariableAttributes>::
+ PropagateFortranVariableAttributesBase;
+ void runOnOperation() override;
+};
+
+class Propagator {
+public:
+ Propagator() {}
+
+ void process(mlir::Operation *op);
+
+private:
+ static bool isContiguous(mlir::Operation *op) {
+ if (mlir::isa<fir::AllocaOp, fir::AllocMemOp>(op))
+ return true;
+ auto varOp = mlir::dyn_cast<fir::FortranVariableOpInterface>(op);
+ if (!varOp)
+ return false;
+ return hlfir::Entity{varOp}.isSimplyContiguous();
+ }
+
+ static void setContiguousAttr(fir::FortranVariableOpInterface op);
+};
+} // namespace
+
+void Propagator::setContiguousAttr(fir::FortranVariableOpInterface op) {
+ LLVM_DEBUG(llvm::dbgs() << "Setting continuity for:\n" << op << "\n");
+ fir::FortranVariableFlagsEnum attrs =
+ op.getFortranAttrs().value_or(fir::FortranVariableFlagsEnum::None);
+ attrs = attrs | fir::FortranVariableFlagsEnum::contiguous;
+ op.setFortranAttrs(attrs);
+}
+
+void Propagator::process(mlir::Operation *op) {
+ if (!isContiguous(op))
+ return;
+ llvm::SmallVector<mlir::Operation *> workList{op};
+ while (!workList.empty()) {
+ mlir::Operation *current = workList.pop_back_val();
+ LLVM_DEBUG(llvm::dbgs() << "Propagating continuity from operation:\n"
+ << *current << "\n");
+
+ for (mlir::OpOperand &use : current->getUses()) {
+ mlir::Operation *useOp = use.getOwner();
+ if (auto varOp = mlir::dyn_cast<fir::FortranVariableOpInterface>(useOp)) {
+ // If the user is not currently contiguous, set the contiguous
+ // attribute and skip it. The propagation will pick it up later.
+ mlir::Value memref;
+ mlir::TypeSwitch<mlir::Operation *, void>(useOp)
+ .Case<hlfir::DeclareOp, hlfir::DesignateOp>(
+ [&](auto op) { memref = op.getMemref(); })
+ .Default([&](auto op) {});
+
+ if (memref == use.get() && !isContiguous(varOp)) {
+ // Make additional checks for hlfir.designate.
+ if (auto designateOp = mlir::dyn_cast<hlfir::DesignateOp>(useOp))
+ if (!hlfir::designatePreservesContinuity(designateOp))
+ continue;
+
+ setContiguousAttr(varOp);
+ }
+ continue;
+ }
+ mlir::TypeSwitch<mlir::Operation *, void>(useOp)
+ .Case(
+ [&](fir::ConvertOp op) { workList.push_back(op.getOperation()); })
+ .Case([&](fir::EmboxOp op) {
+ if (op.getMemref() == use.get())
+ workList.push_back(op.getOperation());
+ })
+ .Case([&](fir::ReboxOp op) {
+ if (op.getBox() == use.get() && fir::reboxPreservesContinuity(op))
+ workList.push_back(op.getOperation());
+ });
+ }
+ }
+}
+
+void PropagateFortranVariableAttributes::runOnOperation() {
+ mlir::Operation *rootOp = getOperation();
+ mlir::MLIRContext *context = &getContext();
+ mlir::RewritePatternSet patterns(context);
+ Propagator propagator;
+ rootOp->walk<mlir::WalkOrder::PreOrder>([&](mlir::Operation *op) {
+ propagator.process(op);
+ return mlir::WalkResult::advance();
+ });
+}
diff --git a/flang/lib/Optimizer/Passes/Pipelines.cpp b/flang/lib/Optimizer/Passes/Pipelines.cpp
index a3ef473ea39b7..77751908e35be 100644
--- a/flang/lib/Optimizer/Passes/Pipelines.cpp
+++ b/flang/lib/Optimizer/Passes/Pipelines.cpp
@@ -249,6 +249,8 @@ void createHLFIRToFIRPassPipeline(mlir::PassManager &pm, bool enableOpenMP,
return hlfir::createSimplifyHLFIRIntrinsics(
{/*allowNewSideEffects=*/true});
});
+ addNestedPassToAllTopLevelOperations<PassConstructor>(
+ pm, hlfir::createPropagateFortranVariableAttributes);
addNestedPassToAllTopLevelOperations<PassConstructor>(
pm, hlfir::createOptimizedBufferization);
addNestedPassToAllTopLevelOperations<PassConstructor>(
diff --git a/flang/lib/Optimizer/Transforms/LoopVersioning.cpp b/flang/lib/Optimizer/Transforms/LoopVersioning.cpp
index 42e149bb3dba2..858e35ccb5f81 100644
--- a/flang/lib/Optimizer/Transforms/LoopVersioning.cpp
+++ b/flang/lib/Optimizer/Transforms/LoopVersioning.cpp
@@ -209,46 +209,15 @@ static mlir::Value unwrapPassThroughOps(mlir::Value val) {
return val;
}
-/// Return true, if \p rebox operation keeps the input array
-/// continuous in the innermost dimension, if it is initially continuous
-/// in the innermost dimension.
-static bool reboxPreservesContinuity(fir::ReboxOp rebox) {
- // If slicing is not involved, then the rebox does not affect
- // the continuity of the array.
- auto sliceArg = rebox.getSlice();
- if (!sliceArg)
- return true;
-
- // A slice with step=1 in the innermost dimension preserves
- // the continuity of the array in the innermost dimension.
- if (auto sliceOp =
- mlir::dyn_cast_or_null<fir::SliceOp>(sliceArg.getDefiningOp())) {
- if (sliceOp.getFields().empty() && sliceOp.getSubstr().empty()) {
- auto triples = sliceOp.getTriples();
- if (triples.size() > 2)
- if (auto innermostStep = fir::getIntIfConstant(triples[2]))
- if (*innermostStep == 1)
- return true;
- }
-
- LLVM_DEBUG(llvm::dbgs()
- << "REBOX with slicing may produce non-contiguous array: "
- << sliceOp << '\n'
- << rebox << '\n');
- return false;
- }
-
- LLVM_DEBUG(llvm::dbgs() << "REBOX with unknown slice" << sliceArg << '\n'
- << rebox << '\n');
- return false;
-}
-
/// if a value comes from a fir.rebox, follow the rebox to the original source,
/// of the value, otherwise return the value
static mlir::Value unwrapReboxOp(mlir::Value val) {
while (fir::ReboxOp rebox = val.getDefiningOp<fir::ReboxOp>()) {
- if (!reboxPreservesContinuity(rebox))
+ if (!fir::reboxPreservesContinuity(rebox, /*checkWhole=*/false)) {
+ LLVM_DEBUG(llvm::dbgs() << "REBOX may produce non-contiguous array: "
+ << rebox << '\n');
break;
+ }
val = rebox.getBox();
}
return val;
diff --git a/flang/test/Driver/mlir-pass-pipeline.f90 b/flang/test/Driver/mlir-pass-pipeline.f90
index 45370895db397..99f192ce7aec2 100644
--- a/flang/test/Driver/mlir-pass-pipeline.f90
+++ b/flang/test/Driver/mlir-pass-pipeline.f90
@@ -36,18 +36,22 @@
! O2-NEXT: Pipeline Collection : ['fir.global', 'func.func', 'omp.declare_reduction', 'omp.private']
! O2-NEXT: 'fir.global' Pipeline
! O2-NEXT: SimplifyHLFIRIntrinsics
+! O2-NEXT: PropagateFortranVariableAttributes
! O2-NEXT: OptimizedBufferization
! O2-NEXT: InlineHLFIRAssign
! O2-NEXT: 'func.func' Pipeline
! O2-NEXT: SimplifyHLFIRIntrinsics
+! O2-NEXT: PropagateFortranVariableAttributes
! O2-NEXT: OptimizedBufferization
! O2-NEXT: Inline...
[truncated]
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
This look good to me because it will help after inlining gave more info, but for the use case of the test, I think lowering could be made nicer by setting the CONTIGUOUS attribute when it knows about contiguity of hlfir.designate fir.box result here using the semantic Fortran::evaluate::IsSimplyContiguous(designatorNode,getConverter().getFoldingContext(),/*namedConstantSectionsAreAlwaysContiguous=*/false))
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks!
Not for this patch, but we seem to be growing more and more of these "walk backwards through FIR IR to find the source of the value" patterns in different passes. Adding a new pass-through operation like fir.convert now could block a lot of optimizations.
I wonder if there needs to be some sort of fir operation interface describing things like fir.convert and fir.declare. Or do you think that is too specific? I guess the problem is handling what the "pass through" means in various cases e.g. a designate is a "pass through" in some contexts but not in this one.
Thank you, Jean! I will make the lowering change in a separate patch. |
Yes, we've been talking about the pass-through operation interface in the context of FIR alias analysis. I would be glad to introduce some abstraction (especially that I am going to add another case in FIR alias analysis in another patch), but I do not see too much generality between FIR alias analysis and the attribute propagation so far. So I do not have a good proposal right now, but the idea is still perfectly valid. |
Contiguous variables represented with a box do not have explicit shape, but it looks like the base/shape computation was assuming that. This caused generation of raw address fir.array_coor without the shape. This patch is needed to fix failures hapenning with llvm#138797.
+1 here. I think we are just waiting to have more use cases to come with a reasonable and robust abstraction. As long as the backward walks are centralized as much as possible in HLFIRTools.cpp/Alias analysis, changing the model should not be a huge change. |
Here is another case which may or may not fit depending upon the abstraction https://github.com/llvm/llvm-project/blob/main/flang/lib/Optimizer/OpenMP/LowerNontemporal.cpp |
Note that the contiguous propagation works in a forward manner, so it is a bit different from the backward walking in the alias analysis. I guess we may need to have different operation interfaces that abstract the walk with the particular property in mind, e.g. MemSourceOpInterface can be used for finding the producer of the memory being accessed through a result of the operation. I do not think this interface works directly for the contiguous propagation, so we will need a different one. Again, I do not have a good proposal right now :) |
This change allows marking more designators producing an opaque
box with 'contiguous' attribute, e.g. like in test1 case
in flang/test/HLFIR/propagate-contiguous-attribute.fir.
This would make isSimplyContiguous() return true for such
designators allowing merging hlfir.eval_in_mem with hlfir.assign
where the LHS is a contiguous array section.