#include "flang/Optimizer/CodeGen/CodeGen.h"
#include "flang/Optimizer/Builder/Character.h"
#include "flang/Optimizer/Builder/FIRBuilder.h"
#include "flang/Optimizer/Builder/MutableBox.h"
#include "flang/Optimizer/Builder/Runtime/Allocatable.h"
#include "flang/Optimizer/Builder/Runtime/Transformational.h"
#include "flang/Optimizer/Builder/Todo.h"
#include "flang/Optimizer/Dialect/FIRDialect.h"
#include "flang/Optimizer/Dialect/FIROps.h"
#include "flang/Optimizer/Dialect/FIRType.h"
#include "flang/Optimizer/OpenACC/Support/RegisterOpenACCExtensions.h"
#include "flang/Optimizer/OpenMP/Support/RegisterOpenMPExtensions.h"
#include "mlir/Pass/Pass.h"
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
namespace fir {
#define GEN_PASS_DEF_LOWERREPACKARRAYSPASS
#include "flang/Optimizer/CodeGen/CGPasses.h.inc"
}
#define DEBUG_TYPE "lower-repack-arrays"
namespace {
class PackArrayConversion : public mlir::OpRewritePattern<fir::PackArrayOp> {
public:
using OpRewritePattern::OpRewritePattern;
mlir::LogicalResult
matchAndRewrite(fir::PackArrayOp op,
mlir::PatternRewriter &rewriter) const override;
private:
static constexpr llvm::StringRef bufferName = ".repacked";
static mlir::Value allocateTempBuffer(fir::FirOpBuilder &builder,
mlir::Location loc, bool useStack,
mlir::Value origBox,
llvm::ArrayRef<mlir::Value> lbounds,
llvm::ArrayRef<mlir::Value> extents,
llvm::ArrayRef<mlir::Value> typeParams);
static mlir::FailureOr<mlir::Value> genRepackedBox(fir::FirOpBuilder &builder,
mlir::Location loc,
fir::PackArrayOp packOp);
};
class UnpackArrayConversion
: public mlir::OpRewritePattern<fir::UnpackArrayOp> {
public:
using OpRewritePattern::OpRewritePattern;
mlir::LogicalResult
matchAndRewrite(fir::UnpackArrayOp op,
mlir::PatternRewriter &rewriter) const override;
};
}
static bool canAllocateTempOnStack(mlir::Value box) {
return !fir::isPolymorphicType(box.getType());
}
template <typename OP>
static bool repackIsSafe(OP op) {
bool isSafe = true;
if (auto isSafeAttrs = op.getIsSafe()) {
for (auto attr : *isSafeAttrs) {
auto iface = mlir::cast<fir::SafeTempArrayCopyAttrInterface>(attr);
if (iface.isDynamicallySafe())
TODO(op.getLoc(), "dynamically safe array repacking");
else
isSafe = false;
}
}
return isSafe;
}
mlir::LogicalResult
PackArrayConversion::matchAndRewrite(fir::PackArrayOp op,
mlir::PatternRewriter &rewriter) const {
mlir::Value box = op.getArray();
if (!repackIsSafe(op)) {
rewriter.replaceOp(op, box);
return mlir::success();
}
mlir::Location loc = op.getLoc();
fir::FirOpBuilder builder(rewriter, op.getOperation());
if (op.getMaxSize() || op.getMaxElementSize() || op.getMinStride())
TODO(loc, "fir.pack_array with constraints");
if (op.getHeuristics() != fir::PackArrayHeuristics::None)
TODO(loc, "fir.pack_array with heuristics");
auto boxType = mlir::cast<fir::BaseBoxType>(box.getType());
auto isPresent =
fir::IsPresentOp::create(builder, loc, builder.getI1Type(), box);
fir::IfOp ifOp = fir::IfOp::create(builder, loc, boxType, isPresent,
true);
builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
auto newBox = genRepackedBox(builder, loc, op);
if (mlir::failed(newBox))
return newBox;
fir::ResultOp::create(builder, loc, *newBox);
builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
fir::ResultOp::create(builder, loc, box);
rewriter.replaceOp(op, ifOp.getResult(0));
return mlir::success();
}
mlir::Value PackArrayConversion::allocateTempBuffer(
fir::FirOpBuilder &builder, mlir::Location loc, bool useStack,
mlir::Value origBox, llvm::ArrayRef<mlir::Value> lbounds,
llvm::ArrayRef<mlir::Value> extents,
llvm::ArrayRef<mlir::Value> typeParams) {
auto tempType = mlir::cast<fir::SequenceType>(
fir::extractSequenceType(origBox.getType()));
assert(tempType.getDimension() == extents.size() &&
"number of extents does not match the rank");
mlir::Value shape = builder.genShape(loc, extents);
auto [base, isHeapAllocation] = builder.createArrayTemp(
loc, tempType, shape, extents, typeParams,
fir::FirOpBuilder::genTempDeclareOp,
fir::isPolymorphicType(origBox.getType()) ? origBox : nullptr, useStack,
bufferName);
if (useStack && canAllocateTempOnStack(origBox))
assert(!isHeapAllocation && "temp must have been allocated on the stack");
mlir::Type ptrType = base.getType();
if (auto tempBoxType = mlir::dyn_cast<fir::BaseBoxType>(ptrType)) {
base = fir::BoxAddrOp::create(builder, loc, fir::boxMemRefType(tempBoxType),
base);
ptrType = base.getType();
}
bool useDynamicType = fir::isBoxedRecordType(origBox.getType()) ||
fir::isPolymorphicType(origBox.getType());
mlir::Type tempBoxType =
fir::wrapInClassOrBoxType(fir::unwrapRefType(ptrType),
useDynamicType);
shape = builder.genShape(loc, lbounds, extents);
mlir::Value newBox =
builder.createBox(loc, tempBoxType, base, shape, nullptr,
typeParams, useDynamicType ? origBox : nullptr);
return builder.createConvert(loc, origBox.getType(), newBox);
}
mlir::FailureOr<mlir::Value>
PackArrayConversion::genRepackedBox(fir::FirOpBuilder &builder,
mlir::Location loc, fir::PackArrayOp op) {
mlir::OpBuilder::InsertionGuard guard(builder);
mlir::Value box = op.getArray();
llvm::SmallVector<mlir::Value> typeParams(op.getTypeparams().begin(),
op.getTypeparams().end());
auto boxType = mlir::cast<fir::BaseBoxType>(box.getType());
mlir::Type indexType = builder.getIndexType();
unsigned numTypeParams = 0;
if (typeParams.size() == 0) {
if (auto recordType =
mlir::dyn_cast<fir::RecordType>(boxType.unwrapInnerType()))
if (recordType.getNumLenParams() != 0)
TODO(loc,
"allocating temporary for a parameterized derived type array");
if (auto charType =
mlir::dyn_cast<fir::CharacterType>(boxType.unwrapInnerType())) {
if (charType.hasDynamicLen()) {
numTypeParams = 1;
} else {
mlir::Value length =
builder.createIntegerConstant(loc, indexType, charType.getLen());
typeParams.push_back(length);
}
}
}
auto isNotContiguous =
builder.genNot(loc, fir::IsContiguousBoxOp::create(builder, loc, box,
op.getInnermost()));
auto dataAddr =
fir::BoxAddrOp::create(builder, loc, fir::boxMemRefType(boxType), box);
auto isNotEmpty =
fir::IsPresentOp::create(builder, loc, builder.getI1Type(), dataAddr);
auto doPack =
mlir::arith::AndIOp::create(builder, loc, isNotContiguous, isNotEmpty);
fir::IfOp ifOp =
fir::IfOp::create(builder, loc, boxType, doPack, true);
ifOp.setUnlikelyIfWeights();
builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
fir::ResultOp::create(builder, loc, box);
builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
llvm::SmallVector<mlir::Value, Fortran::common::maxRank> lbounds, extents;
fir::factory::genDimInfoFromBox(builder, loc, box, &lbounds, &extents,
nullptr);
if (numTypeParams != 0) {
if (auto charType =
mlir::dyn_cast<fir::CharacterType>(boxType.unwrapInnerType()))
if (charType.hasDynamicLen()) {
fir::factory::CharacterExprHelper charHelper(builder, loc);
mlir::Value len = charHelper.readLengthFromBox(box, charType);
typeParams.push_back(builder.createConvert(loc, indexType, len));
}
if (numTypeParams != typeParams.size())
return emitError(loc) << "failed to compute the type parameters for "
<< op.getOperation() << '\n';
}
mlir::Value tempBox = allocateTempBuffer(builder, loc, op.getStack(), box,
lbounds, extents, typeParams);
if (!op.getNoCopy())
fir::runtime::genShallowCopy(builder, loc, tempBox, box,
true);
fir::ResultOp::create(builder, loc, tempBox);
return ifOp.getResult(0);
}
mlir::LogicalResult
UnpackArrayConversion::matchAndRewrite(fir::UnpackArrayOp op,
mlir::PatternRewriter &rewriter) const {
if (!repackIsSafe(op)) {
rewriter.eraseOp(op);
return mlir::success();
}
mlir::Location loc = op.getLoc();
fir::FirOpBuilder builder(rewriter, op.getOperation());
mlir::Type predicateType = builder.getI1Type();
mlir::Value tempBox = op.getTemp();
mlir::Value originalBox = op.getOriginal();
auto isPresent =
fir::IsPresentOp::create(builder, loc, predicateType, originalBox);
builder.genIfThen(loc, isPresent).genThen([&]() {
mlir::Type addrType =
fir::HeapType::get(fir::extractSequenceType(tempBox.getType()));
mlir::Value tempAddr =
fir::BoxAddrOp::create(builder, loc, addrType, tempBox);
mlir::Value originalAddr =
fir::BoxAddrOp::create(builder, loc, addrType, originalBox);
auto isNotSame = builder.genPtrCompare(loc, mlir::arith::CmpIPredicate::ne,
tempAddr, originalAddr);
builder.genIfThen(loc, isNotSame)
.genThen([&]() {
if (!op.getNoCopy())
fir::runtime::genShallowCopy(builder, loc, originalBox, tempBox,
true);
if (!op.getStack() || !canAllocateTempOnStack(originalBox))
fir::FreeMemOp::create(builder, loc, tempAddr);
})
.getIfOp()
.setUnlikelyIfWeights();
});
rewriter.eraseOp(op);
return mlir::success();
}
namespace {
class LowerRepackArraysPass
: public fir::impl::LowerRepackArraysPassBase<LowerRepackArraysPass> {
public:
using LowerRepackArraysPassBase<
LowerRepackArraysPass>::LowerRepackArraysPassBase;
void runOnOperation() override final {
auto *context = &getContext();
mlir::ModuleOp module = getOperation();
mlir::RewritePatternSet patterns(context);
patterns.insert<PackArrayConversion>(context);
patterns.insert<UnpackArrayConversion>(context);
mlir::GreedyRewriteConfig config;
config.setRegionSimplificationLevel(
mlir::GreedySimplifyRegionLevel::Disabled);
(void)applyPatternsGreedily(module, std::move(patterns), config);
}
void getDependentDialects(mlir::DialectRegistry ®istry) const override {
fir::acc::registerTransformationalAttrsDependentDialects(registry);
fir::omp::registerTransformationalAttrsDependentDialects(registry);
}
};
}