Files
clang-p2996/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
Jean Perier 82b7a238cf [flang] hlfir.assign default implementation
Add the default unoptimized implementation implementation
of hlfir.assign. It relies on the runtime for array assignment
and always makes a temp of the right hand side for arrays.

Assignment optimization will be done when all HLFIR pieces are in place
and aliasing analysis is available.

Differential Revision: https://reviews.llvm.org/D139426
2022-12-07 09:50:47 +01:00

293 lines
12 KiB
C++

//===- ConvertToFIR.cpp - Convert HLFIR to FIR ----------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// This file defines a pass to lower HLFIR to FIR
//===----------------------------------------------------------------------===//
#include "flang/Optimizer/Builder/FIRBuilder.h"
#include "flang/Optimizer/Builder/HLFIRTools.h"
#include "flang/Optimizer/Builder/MutableBox.h"
#include "flang/Optimizer/Builder/Runtime/Assign.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/HLFIR/HLFIROps.h"
#include "flang/Optimizer/HLFIR/Passes.h"
#include "flang/Optimizer/Support/FIRContext.h"
#include "mlir/Transforms/DialectConversion.h"
namespace hlfir {
#define GEN_PASS_DEF_CONVERTHLFIRTOFIR
#include "flang/Optimizer/HLFIR/Passes.h.inc"
} // namespace hlfir
using namespace mlir;
namespace {
/// May \p lhs alias with \p rhs?
/// TODO: implement HLFIR alias analysis.
static bool mayAlias(hlfir::Entity lhs, hlfir::Entity rhs) { return true; }
class AssignOpConversion : public mlir::OpRewritePattern<hlfir::AssignOp> {
public:
explicit AssignOpConversion(mlir::MLIRContext *ctx) : OpRewritePattern{ctx} {}
mlir::LogicalResult
matchAndRewrite(hlfir::AssignOp assignOp,
mlir::PatternRewriter &rewriter) const override {
mlir::Location loc = assignOp->getLoc();
hlfir::Entity lhs(assignOp.getLhs());
hlfir::Entity rhs(assignOp.getRhs());
auto module = assignOp->getParentOfType<mlir::ModuleOp>();
fir::FirOpBuilder builder(rewriter, fir::getKindMapping(module));
if (rhs.getType().isa<hlfir::ExprType>()) {
mlir::emitError(loc, "hlfir must be bufferized with --bufferize-hlfir "
"pass before being converted to FIR");
return mlir::failure();
}
auto [rhsExv, rhsCleanUp] =
hlfir::translateToExtendedValue(loc, builder, rhs);
auto [lhsExv, lhsCleanUp] =
hlfir::translateToExtendedValue(loc, builder, lhs);
assert(!lhsCleanUp && !rhsCleanUp &&
"variable to fir::ExtendedValue must not require cleanup");
if (lhs.isArray()) {
// Use the runtime for simplicity. An optimization pass will be added to
// inline array assignment when profitable.
auto to = fir::getBase(builder.createBox(loc, lhsExv));
auto from = fir::getBase(builder.createBox(loc, rhsExv));
bool cleanUpTemp = false;
mlir::Type fromHeapType = fir::HeapType::get(
fir::unwrapRefType(from.getType().cast<fir::BoxType>().getEleTy()));
if (mayAlias(rhs, lhs)) {
/// Use the runtime to make a quick and dirty temp with the rhs value.
/// Overkill for scalar rhs that could be done in much more clever ways.
/// Note that temp descriptor must have the allocatable flag set so that
/// the runtime will allocate it with the shape and type parameters of
// the RHS.
mlir::Type fromBoxHeapType = fir::BoxType::get(fromHeapType);
auto fromMutableBox = builder.createTemporary(loc, fromBoxHeapType);
mlir::Value unallocatedBox = fir::factory::createUnallocatedBox(
builder, loc, fromBoxHeapType, {});
builder.create<fir::StoreOp>(loc, unallocatedBox, fromMutableBox);
fir::runtime::genAssign(builder, loc, fromMutableBox, from);
cleanUpTemp = true;
from = builder.create<fir::LoadOp>(loc, fromMutableBox);
}
auto toMutableBox = builder.createTemporary(loc, to.getType());
// As per 10.2.1.2 point 1 (1) polymorphic variables must be allocatable.
// It is assumed here that they have been reallocated with the dynamic
// type and that the mutableBox will not be modified.
builder.create<fir::StoreOp>(loc, to, toMutableBox);
fir::runtime::genAssign(builder, loc, toMutableBox, from);
if (cleanUpTemp) {
mlir::Value addr =
builder.create<fir::BoxAddrOp>(loc, fromHeapType, from);
builder.create<fir::FreeMemOp>(loc, addr);
}
} else {
// Assume overlap does not matter for scalar (dealt with memmove for
// characters).
// This is not true if this is a derived type with "recursive" allocatable
// components, in which case an overlap would matter because the LHS
// reallocation, if any, may modify the RHS component value before it is
// copied into the LHS.
if (fir::isRecordWithAllocatableMember(lhs.getFortranElementType()))
TODO(loc, "assignment with allocatable components");
fir::factory::genScalarAssignment(builder, loc, lhsExv, rhsExv);
}
rewriter.eraseOp(assignOp);
return mlir::success();
}
};
class DeclareOpConversion : public mlir::OpRewritePattern<hlfir::DeclareOp> {
public:
explicit DeclareOpConversion(mlir::MLIRContext *ctx)
: OpRewritePattern{ctx} {}
mlir::LogicalResult
matchAndRewrite(hlfir::DeclareOp declareOp,
mlir::PatternRewriter &rewriter) const override {
mlir::Location loc = declareOp->getLoc();
mlir::Value memref = declareOp.getMemref();
fir::FortranVariableFlagsAttr fortranAttrs;
if (auto attrs = declareOp.getFortranAttrs())
fortranAttrs =
fir::FortranVariableFlagsAttr::get(rewriter.getContext(), *attrs);
auto firBase = rewriter
.create<fir::DeclareOp>(
loc, memref.getType(), memref, declareOp.getShape(),
declareOp.getTypeparams(), declareOp.getUniqName(),
fortranAttrs)
.getResult();
mlir::Value hlfirBase;
mlir::Type hlfirBaseType = declareOp.getBase().getType();
if (hlfirBaseType.isa<fir::BaseBoxType>()) {
// Need to conditionally rebox/embox for optional.
if (mlir::cast<fir::FortranVariableOpInterface>(declareOp.getOperation())
.isOptional())
TODO(loc, "converting hlfir declare of optional box to fir");
if (!firBase.getType().isa<fir::BaseBoxType>()) {
llvm::SmallVector<mlir::Value> typeParams;
auto maybeCharType =
fir::unwrapSequenceType(fir::unwrapPassByRefType(hlfirBaseType))
.dyn_cast<fir::CharacterType>();
if (!maybeCharType || maybeCharType.hasDynamicLen())
typeParams.append(declareOp.getTypeparams().begin(),
declareOp.getTypeparams().end());
hlfirBase = rewriter.create<fir::EmboxOp>(
loc, hlfirBaseType, firBase, declareOp.getShape(),
/*slice=*/mlir::Value{}, typeParams);
} else {
// Rebox so that lower bounds are correct.
hlfirBase = rewriter.create<fir::ReboxOp>(loc, hlfirBaseType, firBase,
declareOp.getShape(),
/*slice=*/mlir::Value{});
}
} else if (hlfirBaseType.isa<fir::BoxCharType>()) {
assert(declareOp.getTypeparams().size() == 1 &&
"must contain character length");
hlfirBase = rewriter.create<fir::EmboxCharOp>(
loc, hlfirBaseType, firBase, declareOp.getTypeparams()[0]);
} else {
if (hlfirBaseType != firBase.getType()) {
declareOp.emitOpError()
<< "unhandled HLFIR variable type '" << hlfirBaseType << "'\n";
return mlir::failure();
}
hlfirBase = firBase;
}
rewriter.replaceOp(declareOp, {hlfirBase, firBase});
return mlir::success();
}
};
class DesignateOpConversion
: public mlir::OpRewritePattern<hlfir::DesignateOp> {
public:
explicit DesignateOpConversion(mlir::MLIRContext *ctx)
: OpRewritePattern{ctx} {}
mlir::LogicalResult
matchAndRewrite(hlfir::DesignateOp designate,
mlir::PatternRewriter &rewriter) const override {
mlir::Location loc = designate.getLoc();
auto module = designate->getParentOfType<mlir::ModuleOp>();
fir::FirOpBuilder builder(rewriter, fir::getKindMapping(module));
if (designate.getComponent() || designate.getComplexPart() ||
!designate.getSubstring().empty()) {
// build path.
TODO(loc, "hlfir::designate with complex part or substring or component");
}
hlfir::Entity baseEntity(designate.getMemref());
if (baseEntity.isMutableBox())
TODO(loc, "hlfir::designate load of pointer or allocatable");
mlir::Type designateResultType = designate.getResult().getType();
llvm::SmallVector<mlir::Value> firBaseTypeParameters;
auto [base, shape] = hlfir::genVariableFirBaseShapeAndParams(
loc, builder, baseEntity, firBaseTypeParameters);
if (designateResultType.isa<fir::BoxType>()) {
// Generate embox or rebox.
if (designate.getIndices().empty())
TODO(loc, "hlfir::designate whole part");
// Otherwise, this is an array section with triplets.
llvm::SmallVector<mlir::Value> triples;
auto undef = builder.create<fir::UndefOp>(loc, builder.getIndexType());
auto subscripts = designate.getIndices();
unsigned i = 0;
for (auto isTriplet : designate.getIsTriplet()) {
triples.push_back(subscripts[i++]);
if (isTriplet) {
triples.push_back(subscripts[i++]);
triples.push_back(subscripts[i++]);
} else {
triples.push_back(undef);
triples.push_back(undef);
}
}
mlir::Value slice = builder.create<fir::SliceOp>(
loc, triples, /*path=*/mlir::ValueRange{});
llvm::SmallVector<mlir::Type> resultType{designateResultType};
mlir::Value resultBox;
if (base.getType().isa<fir::BoxType>())
resultBox =
builder.create<fir::ReboxOp>(loc, resultType, base, shape, slice);
else
resultBox = builder.create<fir::EmboxOp>(loc, resultType, base, shape,
slice, firBaseTypeParameters);
rewriter.replaceOp(designate, resultBox);
return mlir::success();
}
// Indexing a single element (use fir.array_coor of fir.coordinate_of).
if (designate.getIndices().empty()) {
// Scalar substring or complex part.
// generate fir.coordinate_of.
TODO(loc, "hlfir::designate to fir.coordinate_of");
}
// Generate fir.array_coor
mlir::Type resultType = designateResultType;
if (auto boxCharType = designateResultType.dyn_cast<fir::BoxCharType>())
resultType = fir::ReferenceType::get(boxCharType.getEleTy());
auto arrayCoor = builder.create<fir::ArrayCoorOp>(
loc, resultType, base, shape,
/*slice=*/mlir::Value{}, designate.getIndices(), firBaseTypeParameters);
if (designateResultType.isa<fir::BoxCharType>()) {
assert(designate.getTypeparams().size() == 1 &&
"must have character length");
auto emboxChar = builder.create<fir::EmboxCharOp>(
loc, designateResultType, arrayCoor, designate.getTypeparams()[0]);
rewriter.replaceOp(designate, emboxChar.getResult());
} else {
rewriter.replaceOp(designate, arrayCoor.getResult());
}
return mlir::success();
}
};
class ConvertHLFIRtoFIR
: public hlfir::impl::ConvertHLFIRtoFIRBase<ConvertHLFIRtoFIR> {
public:
void runOnOperation() override {
// TODO: like "bufferize-hlfir" pass, runtime signature may be added
// by this pass. This requires the pass to run on the ModuleOp. It would
// probably be more optimal to have it run on FuncOp and find a way to
// generate the signatures in a thread safe way.
auto module = this->getOperation();
auto *context = &getContext();
mlir::RewritePatternSet patterns(context);
patterns
.insert<AssignOpConversion, DeclareOpConversion, DesignateOpConversion>(
context);
mlir::ConversionTarget target(*context);
target.addIllegalDialect<hlfir::hlfirDialect>();
target.markUnknownOpDynamicallyLegal(
[](mlir::Operation *) { return true; });
if (mlir::failed(mlir::applyPartialConversion(module, target,
std::move(patterns)))) {
mlir::emitError(mlir::UnknownLoc::get(context),
"failure in HLFIR to FIR conversion pass");
signalPassFailure();
}
}
};
} // namespace
std::unique_ptr<mlir::Pass> hlfir::createConvertHLFIRtoFIRPass() {
return std::make_unique<ConvertHLFIRtoFIR>();
}