Skip to content

Commit 2d12d31

Browse files
authored
[flang] Propagate contiguous attribute through HLFIR. (#138797)
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. Depends on #139003
1 parent 3aad7d7 commit 2d12d31

File tree

14 files changed

+551
-72
lines changed

14 files changed

+551
-72
lines changed

flang/include/flang/Optimizer/Builder/HLFIRTools.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "flang/Optimizer/Dialect/FIROps.h"
1818
#include "flang/Optimizer/Dialect/FortranVariableInterface.h"
1919
#include "flang/Optimizer/HLFIR/HLFIRDialect.h"
20+
#include "flang/Optimizer/HLFIR/HLFIROps.h"
2021
#include <optional>
2122

2223
namespace fir {
@@ -533,6 +534,12 @@ Entity gen1DSection(mlir::Location loc, fir::FirOpBuilder &builder,
533534
mlir::ArrayRef<mlir::Value> extents,
534535
mlir::ValueRange oneBasedIndices,
535536
mlir::ArrayRef<mlir::Value> typeParams);
537+
538+
/// Return true iff the given hlfir.designate produces
539+
/// a contiguous part of the memref object given that it is
540+
/// contiguous.
541+
bool designatePreservesContinuity(hlfir::DesignateOp op);
542+
536543
} // namespace hlfir
537544

538545
#endif // FORTRAN_OPTIMIZER_BUILDER_HLFIRTOOLS_H

flang/include/flang/Optimizer/Dialect/FIROpsSupport.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,13 @@ inline bool hasBindcAttr(mlir::Operation *op) {
221221
return hasProcedureAttr<fir::FortranProcedureFlagsEnum::bind_c>(op);
222222
}
223223

224+
/// Return true, if \p rebox operation keeps the input array
225+
/// continuous if it is initially continuous.
226+
/// When \p checkWhole is false, then the checking is only done
227+
/// for continuity in the innermost dimension, otherwise,
228+
/// the checking is done for continuity of the whole result of rebox.
229+
bool reboxPreservesContinuity(fir::ReboxOp rebox, bool checkWhole = true);
230+
224231
} // namespace fir
225232

226233
#endif // FORTRAN_OPTIMIZER_DIALECT_FIROPSSUPPORT_H

flang/include/flang/Optimizer/Dialect/FortranVariableInterface.td

Lines changed: 43 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -22,51 +22,57 @@ def fir_FortranVariableOpInterface : OpInterface<"FortranVariableOpInterface"> {
2222
query about all their Fortran properties.
2323
}];
2424

25-
let methods = [
26-
InterfaceMethod<
27-
/*desc=*/"Get the address produced by the definition",
28-
/*retTy=*/"mlir::Value",
29-
/*methodName=*/"getBase",
30-
/*args=*/(ins),
31-
/*methodBody=*/[{}],
32-
/*defaultImplementation=*/[{
25+
let methods =
26+
[InterfaceMethod<
27+
/*desc=*/"Get the address produced by the definition",
28+
/*retTy=*/"mlir::Value",
29+
/*methodName=*/"getBase",
30+
/*args=*/(ins),
31+
/*methodBody=*/[{}],
32+
/*defaultImplementation=*/[{
3333
ConcreteOp op = mlir::cast<ConcreteOp>(this->getOperation());
3434
return op.getResult();
35-
}]
36-
>,
37-
InterfaceMethod<
38-
/*desc=*/"Get Fortran attributes",
39-
/*retTy=*/"std::optional<fir::FortranVariableFlagsEnum>",
40-
/*methodName=*/"getFortranAttrs",
41-
/*args=*/(ins),
42-
/*methodBody=*/[{}],
43-
/*defaultImplementation=*/[{
35+
}]>,
36+
InterfaceMethod<
37+
/*desc=*/"Get Fortran attributes",
38+
/*retTy=*/"std::optional<fir::FortranVariableFlagsEnum>",
39+
/*methodName=*/"getFortranAttrs",
40+
/*args=*/(ins),
41+
/*methodBody=*/[{}],
42+
/*defaultImplementation=*/[{
4443
ConcreteOp op = mlir::cast<ConcreteOp>(this->getOperation());
4544
return op.getFortran_attrs();
46-
}]
47-
>,
48-
InterfaceMethod<
49-
/*desc=*/"Get the shape of the variable. May be a null value.",
50-
/*retTy=*/"mlir::Value",
51-
/*methodName=*/"getShape",
52-
/*args=*/(ins),
53-
/*methodBody=*/[{}],
54-
/*defaultImplementation=*/[{
45+
}]>,
46+
InterfaceMethod<
47+
/*desc=*/"Get the shape of the variable. May be a null value.",
48+
/*retTy=*/"mlir::Value",
49+
/*methodName=*/"getShape",
50+
/*args=*/(ins),
51+
/*methodBody=*/[{}],
52+
/*defaultImplementation=*/[{
5553
ConcreteOp op = mlir::cast<ConcreteOp>(this->getOperation());
5654
return op.getShape();
57-
}]
58-
>,
59-
InterfaceMethod<
60-
/*desc=*/"Get explicit type parameters of the variable",
61-
/*retTy=*/"mlir::OperandRange",
62-
/*methodName=*/"getExplicitTypeParams",
63-
/*args=*/(ins),
64-
/*methodBody=*/[{}],
65-
/*defaultImplementation=*/[{
55+
}]>,
56+
InterfaceMethod<
57+
/*desc=*/"Get explicit type parameters of the variable",
58+
/*retTy=*/"mlir::OperandRange",
59+
/*methodName=*/"getExplicitTypeParams",
60+
/*args=*/(ins),
61+
/*methodBody=*/[{}],
62+
/*defaultImplementation=*/[{
6663
ConcreteOp op = mlir::cast<ConcreteOp>(this->getOperation());
6764
return op.getTypeparams();
68-
}]
69-
>,
65+
}]>,
66+
InterfaceMethod<
67+
/*desc=*/"Set Fortran attributes",
68+
/*retTy=*/"void",
69+
/*methodName=*/"setFortranAttrs",
70+
/*args=*/(ins "fir::FortranVariableFlagsEnum":$flags),
71+
/*methodBody=*/[{}],
72+
/*defaultImplementation=*/[{
73+
ConcreteOp op = mlir::cast<ConcreteOp>(this->getOperation());
74+
op.setFortran_attrs(fir::FortranVariableFlagsAttr::get(op->getContext(), flags));
75+
}]>,
7076
];
7177

7278
let extraClassDeclaration = [{

flang/include/flang/Optimizer/HLFIR/HLFIROps.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ def hlfir_ParentComponentOp : hlfir_Op<"parent_comp", [AttrSizedOperandSegments,
313313
std::optional<fir::FortranVariableFlagsEnum> getFortranAttrs() const {
314314
return std::nullopt;
315315
}
316+
void setFortranAttrs(fir::FortranVariableFlagsEnum flags) {}
316317
}];
317318

318319
let results = (outs AnyFortranVariable);
@@ -1078,6 +1079,7 @@ def hlfir_NullOp : hlfir_Op<"null", [NoMemoryEffect, fir_FortranVariableOpInterf
10781079
std::optional<fir::FortranVariableFlagsEnum> getFortranAttrs() const {
10791080
return std::nullopt;
10801081
}
1082+
void setFortranAttrs(fir::FortranVariableFlagsEnum flags) {}
10811083
mlir::Value getShape() const {return mlir::Value{};}
10821084
mlir::OperandRange getExplicitTypeParams() const {
10831085
// Return an empty range.
@@ -1766,6 +1768,7 @@ def hlfir_ForallIndexOp : hlfir_Op<"forall_index", [fir_FortranVariableOpInterfa
17661768
std::optional<fir::FortranVariableFlagsEnum> getFortranAttrs() const {
17671769
return std::nullopt;
17681770
}
1771+
void setFortranAttrs(fir::FortranVariableFlagsEnum flags) {}
17691772
mlir::Value getShape() const {return mlir::Value{};}
17701773
mlir::OperandRange getExplicitTypeParams() const {
17711774
// Return an empty range.

flang/include/flang/Optimizer/HLFIR/Passes.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,8 @@ def InlineHLFIRAssign : Pass<"inline-hlfir-assign"> {
6969
let summary = "Inline hlfir.assign operations";
7070
}
7171

72+
def PropagateFortranVariableAttributes : Pass<"propagate-fortran-attrs"> {
73+
let summary = "Propagate FortranVariableFlagsAttr attributes through HLFIR";
74+
}
75+
7276
#endif //FORTRAN_DIALECT_HLFIR_PASSES

flang/lib/Optimizer/Builder/HLFIRTools.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1633,3 +1633,32 @@ hlfir::Entity hlfir::gen1DSection(mlir::Location loc,
16331633
sectionShape, typeParams);
16341634
return hlfir::Entity{designate.getResult()};
16351635
}
1636+
1637+
bool hlfir::designatePreservesContinuity(hlfir::DesignateOp op) {
1638+
if (op.getComponent() || op.getComplexPart() || !op.getSubstring().empty())
1639+
return false;
1640+
auto subscripts = op.getIndices();
1641+
unsigned i = 0;
1642+
for (auto isTriplet : llvm::enumerate(op.getIsTriplet())) {
1643+
// TODO: we should allow any number of leading triplets
1644+
// that describe a whole dimension slice, then one optional
1645+
// triplet describing potentially partial dimension slice,
1646+
// then any number of non-triplet subscripts.
1647+
// For the time being just allow a single leading
1648+
// triplet and then any number of non-triplet subscripts.
1649+
if (isTriplet.value()) {
1650+
if (isTriplet.index() != 0) {
1651+
return false;
1652+
} else {
1653+
i += 2;
1654+
mlir::Value step = subscripts[i++];
1655+
auto constantStep = fir::getIntIfConstant(step);
1656+
if (!constantStep || *constantStep != 1)
1657+
return false;
1658+
}
1659+
} else {
1660+
++i;
1661+
}
1662+
}
1663+
return true;
1664+
}

flang/lib/Optimizer/Dialect/FIROps.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4721,6 +4721,48 @@ mlir::Type fir::applyPathToType(mlir::Type eleTy, mlir::ValueRange path) {
47214721
return eleTy;
47224722
}
47234723

4724+
bool fir::reboxPreservesContinuity(fir::ReboxOp rebox, bool checkWhole) {
4725+
// If slicing is not involved, then the rebox does not affect
4726+
// the continuity of the array.
4727+
auto sliceArg = rebox.getSlice();
4728+
if (!sliceArg)
4729+
return true;
4730+
4731+
if (auto sliceOp =
4732+
mlir::dyn_cast_or_null<fir::SliceOp>(sliceArg.getDefiningOp())) {
4733+
if (sliceOp.getFields().empty() && sliceOp.getSubstr().empty()) {
4734+
// TODO: generalize code for the triples analysis with
4735+
// hlfir::designatePreservesContinuity, especially when
4736+
// recognition of the whole dimension slices is added.
4737+
auto triples = sliceOp.getTriples();
4738+
assert((triples.size() % 3) == 0 && "invalid triples size");
4739+
4740+
// A slice with step=1 in the innermost dimension preserves
4741+
// the continuity of the array in the innermost dimension.
4742+
// If checkWhole is false, then check only the innermost slice triples.
4743+
std::size_t checkUpTo = checkWhole ? triples.size() : 3;
4744+
checkUpTo = std::min(checkUpTo, triples.size());
4745+
for (std::size_t i = 0; i < checkUpTo; i += 3) {
4746+
if (triples[i] != triples[i + 1]) {
4747+
// This is a section of the dimension. Only allow it
4748+
// to be the first triple.
4749+
if (i != 0)
4750+
return false;
4751+
auto constantStep = fir::getIntIfConstant(triples[i + 2]);
4752+
if (!constantStep || *constantStep != 1)
4753+
return false;
4754+
}
4755+
}
4756+
return true;
4757+
}
4758+
}
4759+
return false;
4760+
}
4761+
4762+
//===----------------------------------------------------------------------===//
4763+
// DeclareOp
4764+
//===----------------------------------------------------------------------===//
4765+
47244766
llvm::LogicalResult fir::DeclareOp::verify() {
47254767
auto fortranVar =
47264768
mlir::cast<fir::FortranVariableOpInterface>(this->getOperation());

flang/lib/Optimizer/HLFIR/Transforms/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ add_flang_library(HLFIRTransforms
1010
ScheduleOrderedAssignments.cpp
1111
SimplifyHLFIRIntrinsics.cpp
1212
OptimizedBufferization.cpp
13+
PropagateFortranVariableAttributes.cpp
1314

1415
DEPENDS
1516
CUFAttrsIncGen
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
//===- PropagateFortranVariableAttributes.cpp -----------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
/// \file
9+
/// This file defines a pass that propagates FortranVariableFlagsAttr
10+
/// attributes through HLFIR. For example, it can set contiguous attribute
11+
/// on hlfir.designate that produces a contiguous slice of a contiguous
12+
/// Fortran array. This pass can be applied multiple times to expose
13+
/// more Fortran attributes, e.g. after inlining and constant propagation.
14+
//===----------------------------------------------------------------------===//
15+
16+
#include "flang/Optimizer/Builder/HLFIRTools.h"
17+
#include "flang/Optimizer/Dialect/FIROpsSupport.h"
18+
#include "flang/Optimizer/HLFIR/HLFIRDialect.h"
19+
#include "flang/Optimizer/HLFIR/HLFIROps.h"
20+
#include "flang/Optimizer/HLFIR/Passes.h"
21+
#include "llvm/ADT/TypeSwitch.h"
22+
23+
namespace hlfir {
24+
#define GEN_PASS_DEF_PROPAGATEFORTRANVARIABLEATTRIBUTES
25+
#include "flang/Optimizer/HLFIR/Passes.h.inc"
26+
} // namespace hlfir
27+
28+
#define DEBUG_TYPE "propagate-fortran-attrs"
29+
30+
namespace {
31+
class PropagateFortranVariableAttributes
32+
: public hlfir::impl::PropagateFortranVariableAttributesBase<
33+
PropagateFortranVariableAttributes> {
34+
public:
35+
using PropagateFortranVariableAttributesBase<
36+
PropagateFortranVariableAttributes>::
37+
PropagateFortranVariableAttributesBase;
38+
void runOnOperation() override;
39+
};
40+
41+
class Propagator {
42+
public:
43+
void process(mlir::Operation *op);
44+
45+
private:
46+
static bool isContiguous(mlir::Operation *op) {
47+
// Treat data allocations as contiguous, so that we can propagate
48+
// the continuity from them. Allocations of fir.box must not be treated
49+
// as contiguous.
50+
if (mlir::isa<fir::AllocaOp, fir::AllocMemOp>(op) &&
51+
!mlir::isa<fir::BaseBoxType>(
52+
fir::unwrapRefType(op->getResult(0).getType())))
53+
return true;
54+
auto varOp = mlir::dyn_cast<fir::FortranVariableOpInterface>(op);
55+
if (!varOp)
56+
return false;
57+
return hlfir::Entity{varOp}.isSimplyContiguous();
58+
}
59+
60+
static void setContiguousAttr(fir::FortranVariableOpInterface op);
61+
};
62+
} // namespace
63+
64+
void Propagator::setContiguousAttr(fir::FortranVariableOpInterface op) {
65+
LLVM_DEBUG(llvm::dbgs() << "Setting continuity for:\n" << op << "\n");
66+
fir::FortranVariableFlagsEnum attrs =
67+
op.getFortranAttrs().value_or(fir::FortranVariableFlagsEnum::None);
68+
attrs = attrs | fir::FortranVariableFlagsEnum::contiguous;
69+
op.setFortranAttrs(attrs);
70+
}
71+
72+
void Propagator::process(mlir::Operation *op) {
73+
if (!isContiguous(op))
74+
return;
75+
llvm::SmallVector<mlir::Operation *> workList{op};
76+
while (!workList.empty()) {
77+
mlir::Operation *current = workList.pop_back_val();
78+
LLVM_DEBUG(llvm::dbgs() << "Propagating continuity from operation:\n"
79+
<< *current << "\n");
80+
81+
for (mlir::OpOperand &use : current->getUses()) {
82+
mlir::Operation *useOp = use.getOwner();
83+
if (auto varOp = mlir::dyn_cast<fir::FortranVariableOpInterface>(useOp)) {
84+
// If the user is not currently contiguous, set the contiguous
85+
// attribute and skip it. The propagation will pick it up later.
86+
mlir::Value memref;
87+
mlir::TypeSwitch<mlir::Operation *, void>(useOp)
88+
.Case<hlfir::DeclareOp, hlfir::DesignateOp>(
89+
[&](auto op) { memref = op.getMemref(); })
90+
.Default([&](auto op) {});
91+
92+
if (memref == use.get() && !isContiguous(varOp)) {
93+
// Make additional checks for hlfir.designate.
94+
if (auto designateOp = mlir::dyn_cast<hlfir::DesignateOp>(useOp))
95+
if (!hlfir::designatePreservesContinuity(designateOp))
96+
continue;
97+
98+
setContiguousAttr(varOp);
99+
}
100+
continue;
101+
}
102+
mlir::TypeSwitch<mlir::Operation *, void>(useOp)
103+
.Case(
104+
[&](fir::ConvertOp op) { workList.push_back(op.getOperation()); })
105+
.Case([&](fir::EmboxOp op) {
106+
if (op.getMemref() == use.get())
107+
workList.push_back(op.getOperation());
108+
})
109+
.Case([&](fir::ReboxOp op) {
110+
if (op.getBox() == use.get() && fir::reboxPreservesContinuity(op))
111+
workList.push_back(op.getOperation());
112+
});
113+
}
114+
}
115+
}
116+
117+
void PropagateFortranVariableAttributes::runOnOperation() {
118+
mlir::Operation *rootOp = getOperation();
119+
mlir::MLIRContext *context = &getContext();
120+
mlir::RewritePatternSet patterns(context);
121+
Propagator propagator;
122+
rootOp->walk<mlir::WalkOrder::PreOrder>([&](mlir::Operation *op) {
123+
propagator.process(op);
124+
return mlir::WalkResult::advance();
125+
});
126+
}

flang/lib/Optimizer/Passes/Pipelines.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,8 @@ void createHLFIRToFIRPassPipeline(mlir::PassManager &pm, bool enableOpenMP,
249249
return hlfir::createSimplifyHLFIRIntrinsics(
250250
{/*allowNewSideEffects=*/true});
251251
});
252+
addNestedPassToAllTopLevelOperations<PassConstructor>(
253+
pm, hlfir::createPropagateFortranVariableAttributes);
252254
addNestedPassToAllTopLevelOperations<PassConstructor>(
253255
pm, hlfir::createOptimizedBufferization);
254256
addNestedPassToAllTopLevelOperations<PassConstructor>(

0 commit comments

Comments
 (0)