blob: 22ad5371543c9867f6cedb3c11210877b8ff2f36 [file] [log] [blame]
//===- 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/Character.h"
#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/Runtime/Derived.h"
#include "flang/Optimizer/Builder/Runtime/Inquiry.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/Dialect/Support/FIRContext.h"
#include "flang/Optimizer/HLFIR/HLFIROps.h"
#include "flang/Optimizer/HLFIR/Passes.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;
static mlir::Value genAllocatableTempFromSourceBox(mlir::Location loc,
fir::FirOpBuilder &builder,
mlir::Value sourceBox) {
assert(sourceBox.getType().isa<fir::BaseBoxType>() &&
"must be a base box type");
// 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.
// This has the huge benefit of dealing with all cases, including
// polymorphic entities.
mlir::Type fromHeapType = fir::HeapType::get(fir::unwrapRefType(
sourceBox.getType().cast<fir::BaseBoxType>().getEleTy()));
mlir::Type fromBoxHeapType = fir::BoxType::get(fromHeapType);
mlir::Value fromMutableBox =
fir::factory::genNullBoxStorage(builder, loc, fromBoxHeapType);
fir::runtime::genAssignTemporary(builder, loc, fromMutableBox, sourceBox);
mlir::Value copy = builder.create<fir::LoadOp>(loc, fromMutableBox);
return copy;
}
namespace {
/// May \p lhs alias with \p rhs?
/// TODO: implement HLFIR alias analysis.
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, 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");
auto emboxRHS = [&](fir::ExtendedValue &rhsExv) -> mlir::Value {
// There may be overlap between lhs and rhs. The runtime is able to detect
// and to make a copy of the rhs before modifying the lhs if needed.
// The code below relies on this and does not do any compile time alias
// analysis.
const bool rhsIsValue = fir::isa_trivial(fir::getBase(rhsExv).getType());
if (rhsIsValue) {
// createBox can only be called for fir::ExtendedValue that are
// already in memory. Place the integer/real/complex/logical scalar
// in memory.
// The RHS might be i1, which is not supported for emboxing.
// If LHS is not polymorphic, we may cast the RHS to the LHS type
// before emboxing. If LHS is polymorphic we have to figure out
// the data type for RHS emboxing anyway.
// It is probably a good idea to make sure that the data type
// of the RHS is always a valid Fortran storage data type.
// For the time being, just handle i1 explicitly here.
mlir::Type rhsType = rhs.getFortranElementType();
mlir::Value rhsVal = fir::getBase(rhsExv);
if (rhsType == builder.getI1Type()) {
rhsType = fir::LogicalType::get(builder.getContext(), 4);
rhsVal = builder.createConvert(loc, rhsType, rhsVal);
}
mlir::Value temp = builder.create<fir::AllocaOp>(loc, rhsType);
builder.create<fir::StoreOp>(loc, rhsVal, temp);
rhsExv = temp;
}
return fir::getBase(builder.createBox(loc, rhsExv));
};
if (assignOp.isAllocatableAssignment()) {
// Whole allocatable assignment: use the runtime to deal with the
// reallocation.
mlir::Value from = emboxRHS(rhsExv);
mlir::Value to = fir::getBase(lhsExv);
if (assignOp.mustKeepLhsLengthInAllocatableAssignment()) {
// Indicate the runtime that it should not reallocate in case of length
// mismatch, and that it should use the LHS explicit/assumed length if
// allocating/reallocation the LHS.
fir::runtime::genAssignExplicitLengthCharacter(builder, loc, to, from);
} else if (lhs.isPolymorphic()) {
// Indicate the runtime that the LHS must have the RHS dynamic type
// after the assignment.
fir::runtime::genAssignPolymorphic(builder, loc, to, from);
} else {
fir::runtime::genAssign(builder, loc, to, from);
}
} else if (lhs.isArray()) {
// Use the runtime for simplicity. An optimization pass will be added to
// inline array assignment when profitable.
mlir::Value from = emboxRHS(rhsExv);
mlir::Value to = fir::getBase(builder.createBox(loc, lhsExv));
// This is not a whole allocatable assignment: the runtime will not
// reallocate and modify "toMutableBox" even if it is taking it by
// reference.
auto toMutableBox = builder.createTemporary(loc, to.getType());
builder.create<fir::StoreOp>(loc, to, toMutableBox);
fir::runtime::genAssign(builder, loc, toMutableBox, from);
} 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 CopyInOpConversion : public mlir::OpRewritePattern<hlfir::CopyInOp> {
public:
explicit CopyInOpConversion(mlir::MLIRContext *ctx) : OpRewritePattern{ctx} {}
struct CopyInResult {
mlir::Value addr;
mlir::Value wasCopied;
};
static CopyInResult genNonOptionalCopyIn(mlir::Location loc,
fir::FirOpBuilder &builder,
hlfir::CopyInOp copyInOp) {
mlir::Value inputVariable = copyInOp.getVar();
mlir::Type resultAddrType = copyInOp.getCopiedIn().getType();
mlir::Value isContiguous =
fir::runtime::genIsContiguous(builder, loc, inputVariable);
mlir::Value addr =
builder
.genIfOp(loc, {resultAddrType}, isContiguous,
/*withElseRegion=*/true)
.genThen(
[&]() { builder.create<fir::ResultOp>(loc, inputVariable); })
.genElse([&] {
// Create temporary on the heap. Note that the runtime is used and
// that is desired: since the data copy happens under a runtime
// check (for IsContiguous) the copy loops can hardly provide any
// value to optimizations, instead, the optimizer just wastes
// compilation time on these loops.
mlir::Value temp =
genAllocatableTempFromSourceBox(loc, builder, inputVariable);
// Get rid of allocatable flag in the fir.box.
temp = builder.create<fir::ReboxOp>(loc, resultAddrType, temp,
/*shape=*/mlir::Value{},
/*slice=*/mlir::Value{});
builder.create<fir::ResultOp>(loc, temp);
})
.getResults()[0];
return {addr, builder.genNot(loc, isContiguous)};
}
static CopyInResult genOptionalCopyIn(mlir::Location loc,
fir::FirOpBuilder &builder,
hlfir::CopyInOp copyInOp) {
mlir::Type resultAddrType = copyInOp.getCopiedIn().getType();
mlir::Value isPresent = copyInOp.getVarIsPresent();
auto res =
builder
.genIfOp(loc, {resultAddrType, builder.getI1Type()}, isPresent,
/*withElseRegion=*/true)
.genThen([&]() {
CopyInResult res = genNonOptionalCopyIn(loc, builder, copyInOp);
builder.create<fir::ResultOp>(
loc, mlir::ValueRange{res.addr, res.wasCopied});
})
.genElse([&] {
mlir::Value absent =
builder.create<fir::AbsentOp>(loc, resultAddrType);
builder.create<fir::ResultOp>(
loc, mlir::ValueRange{absent, isPresent});
})
.getResults();
return {res[0], res[1]};
}
mlir::LogicalResult
matchAndRewrite(hlfir::CopyInOp copyInOp,
mlir::PatternRewriter &rewriter) const override {
mlir::Location loc = copyInOp.getLoc();
auto module = copyInOp->getParentOfType<mlir::ModuleOp>();
fir::FirOpBuilder builder(rewriter, fir::getKindMapping(module));
CopyInResult result = copyInOp.getVarIsPresent()
? genOptionalCopyIn(loc, builder, copyInOp)
: genNonOptionalCopyIn(loc, builder, copyInOp);
rewriter.replaceOp(copyInOp, {result.addr, result.wasCopied});
return mlir::success();
}
};
class CopyOutOpConversion : public mlir::OpRewritePattern<hlfir::CopyOutOp> {
public:
explicit CopyOutOpConversion(mlir::MLIRContext *ctx)
: OpRewritePattern{ctx} {}
mlir::LogicalResult
matchAndRewrite(hlfir::CopyOutOp copyOutOp,
mlir::PatternRewriter &rewriter) const override {
mlir::Location loc = copyOutOp.getLoc();
auto module = copyOutOp->getParentOfType<mlir::ModuleOp>();
fir::FirOpBuilder builder(rewriter, fir::getKindMapping(module));
builder.genIfThen(loc, copyOutOp.getWasCopied())
.genThen([&]() {
mlir::Value temp = copyOutOp.getTemp();
if (mlir::Value var = copyOutOp.getVar()) {
auto mutableBoxTo = builder.createTemporary(loc, var.getType());
builder.create<fir::StoreOp>(loc, var, mutableBoxTo);
// Generate CopyOutAssign() call to copy data from the temporary
// to the actualArg. Note that in case the actual argument
// is ALLOCATABLE/POINTER the CopyOutAssign() implementation
// should not engage its reallocation, because the temporary
// is rank, shape and type compatible with it.
// Moreover, CopyOutAssign() guarantees that there will be no
// finalization for the LHS even if it is of a derived type
// with finalization.
fir::runtime::genCopyOutAssign(builder, loc, mutableBoxTo, temp,
/*skipToInit=*/true);
}
// Destroy components of the temporary (if any).
fir::runtime::genDerivedTypeDestroyWithoutFinalization(builder, loc,
temp);
mlir::Type heapType =
fir::HeapType::get(fir::dyn_cast_ptrOrBoxEleTy(temp.getType()));
mlir::Value tempAddr =
builder.create<fir::BoxAddrOp>(loc, heapType, temp);
// Deallocate the top-level entity of the temporary.
//
// Note that this FreeMemOp is coupled with the runtime
// allocation engaged by the code generated by
// genAllocatableTempFromSourceBox().
builder.create<fir::FreeMemOp>(loc, tempAddr);
})
.end();
rewriter.eraseOp(copyOutOp);
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>()) {
auto module = declareOp->getParentOfType<mlir::ModuleOp>();
fir::FirOpBuilder builder(rewriter, fir::getKindMapping(module));
// Helper to generate the hlfir fir.box with the local lower bounds and
// type parameters.
auto genHlfirBox = [&]() -> mlir::Value {
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());
return builder.create<fir::EmboxOp>(
loc, hlfirBaseType, firBase, declareOp.getShape(),
/*slice=*/mlir::Value{}, typeParams);
} else {
// Rebox so that lower bounds are correct.
return builder.create<fir::ReboxOp>(loc, hlfirBaseType, firBase,
declareOp.getShape(),
/*slice=*/mlir::Value{});
}
};
if (!mlir::cast<fir::FortranVariableOpInterface>(declareOp.getOperation())
.isOptional()) {
hlfirBase = genHlfirBox();
} else {
// Need to conditionally rebox/embox the optional: the input fir.box
// may be null and the rebox would be illegal. It is also important to
// preserve the optional aspect: the hlfir fir.box should be null if
// the entity is absent so that later fir.is_present on the hlfir base
// are valid.
mlir::Value isPresent =
builder.create<fir::IsPresentOp>(loc, builder.getI1Type(), firBase);
hlfirBase = builder
.genIfOp(loc, {hlfirBaseType}, isPresent,
/*withElseRegion=*/true)
.genThen([&] {
builder.create<fir::ResultOp>(loc, genHlfirBox());
})
.genElse([&]() {
mlir::Value absent =
builder.create<fir::AbsentOp>(loc, hlfirBaseType);
builder.create<fir::ResultOp>(loc, absent);
})
.getResults()[0];
}
} 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> {
// Helper method to generate the coordinate of the first element
// of an array section. It is also called for cases of non-section
// array element addressing.
static mlir::Value genSubscriptBeginAddr(
fir::FirOpBuilder &builder, mlir::Location loc,
hlfir::DesignateOp designate, mlir::Type baseEleTy, mlir::Value base,
mlir::Value shape,
const llvm::SmallVector<mlir::Value> &firBaseTypeParameters) {
assert(!designate.getIndices().empty());
llvm::SmallVector<mlir::Value> firstElementIndices;
auto indices = designate.getIndices();
int i = 0;
for (auto isTriplet : designate.getIsTripletAttr().asArrayRef()) {
// Coordinate of the first element are the index and triplets lower
// bounds
firstElementIndices.push_back(indices[i]);
i = i + (isTriplet ? 3 : 1);
}
mlir::Type arrayCoorType = fir::ReferenceType::get(baseEleTy);
base = builder.create<fir::ArrayCoorOp>(
loc, arrayCoorType, base, shape,
/*slice=*/mlir::Value{}, firstElementIndices, firBaseTypeParameters);
return base;
}
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));
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);
mlir::Type baseEleTy = hlfir::getFortranElementType(base.getType());
mlir::Type resultEleTy = hlfir::getFortranElementType(designateResultType);
mlir::Value fieldIndex;
if (designate.getComponent()) {
mlir::Type baseRecordType = baseEntity.getFortranElementType();
if (fir::isRecordWithTypeParameters(baseRecordType))
TODO(loc, "hlfir.designate with a parametrized derived type base");
fieldIndex = builder.create<fir::FieldIndexOp>(
loc, fir::FieldType::get(builder.getContext()),
designate.getComponent().value(), baseRecordType,
/*typeParams=*/mlir::ValueRange{});
if (baseEntity.isScalar()) {
// Component refs of scalar base right away:
// - scalar%scalar_component [substring|complex_part] or
// - scalar%static_size_array_comp
// - scalar%array(indices) [substring| complex part]
mlir::Type componentType = baseEleTy.cast<fir::RecordType>().getType(
designate.getComponent().value());
mlir::Type coorTy = fir::ReferenceType::get(componentType);
base = builder.create<fir::CoordinateOp>(loc, coorTy, base, fieldIndex);
if (componentType.isa<fir::BaseBoxType>()) {
auto variableInterface = mlir::cast<fir::FortranVariableOpInterface>(
designate.getOperation());
if (variableInterface.isAllocatable() ||
variableInterface.isPointer()) {
rewriter.replaceOp(designate, base);
return mlir::success();
}
TODO(loc,
"addressing parametrized derived type automatic components");
}
baseEleTy = hlfir::getFortranElementType(componentType);
shape = designate.getComponentShape();
} else {
// array%component[(indices) substring|complex part] cases.
// Component ref of array bases are dealt with below in embox/rebox.
assert(designateResultType.isa<fir::BaseBoxType>());
}
}
if (designateResultType.isa<fir::BaseBoxType>()) {
// Generate embox or rebox.
mlir::Type eleTy = fir::unwrapPassByRefType(designateResultType);
bool isScalarDesignator = !eleTy.isa<fir::SequenceType>();
mlir::Value sourceBox;
if (isScalarDesignator) {
// The base box will be used for emboxing the scalar element.
sourceBox = base;
// Generate the coordinate of the element.
base = genSubscriptBeginAddr(builder, loc, designate, baseEleTy, base,
shape, firBaseTypeParameters);
shape = nullptr;
// Type information will be taken from the source box,
// so the type parameters are not needed.
firBaseTypeParameters.clear();
}
llvm::SmallVector<mlir::Value> triples;
llvm::SmallVector<mlir::Value> sliceFields;
mlir::Type idxTy = builder.getIndexType();
auto subscripts = designate.getIndices();
if (fieldIndex && baseEntity.isArray()) {
// array%scalar_comp or array%array_comp(indices)
// Generate triples for array(:, :, ...).
triples = genFullSliceTriples(builder, loc, baseEntity);
sliceFields.push_back(fieldIndex);
// Add indices in the field path for "array%array_comp(indices)"
// case. The indices of components provided to the sliceOp must
// be zero based (fir.slice has no knowledge of the component
// lower bounds). The component lower bounds are applied here.
if (!subscripts.empty()) {
llvm::SmallVector<mlir::Value> lbounds = hlfir::genLowerbounds(
loc, builder, designate.getComponentShape(), subscripts.size());
for (auto [i, lb] : llvm::zip(subscripts, lbounds)) {
mlir::Value iIdx = builder.createConvert(loc, idxTy, i);
mlir::Value lbIdx = builder.createConvert(loc, idxTy, lb);
sliceFields.emplace_back(
builder.create<mlir::arith::SubIOp>(loc, iIdx, lbIdx));
}
}
} else if (!isScalarDesignator) {
// Otherwise, this is an array section with triplets.
auto undef = builder.create<fir::UndefOp>(loc, idxTy);
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);
}
}
}
llvm::SmallVector<mlir::Value, 2> substring;
if (!designate.getSubstring().empty()) {
substring.push_back(designate.getSubstring()[0]);
mlir::Type idxTy = builder.getIndexType();
// fir.slice op substring expects the zero based lower bound.
mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
substring[0] = builder.createConvert(loc, idxTy, substring[0]);
substring[0] =
builder.create<mlir::arith::SubIOp>(loc, substring[0], one);
substring.push_back(designate.getTypeparams()[0]);
}
if (designate.getComplexPart()) {
if (triples.empty())
triples = genFullSliceTriples(builder, loc, baseEntity);
sliceFields.push_back(builder.createIntegerConstant(
loc, idxTy, *designate.getComplexPart()));
}
mlir::Value slice;
if (!triples.empty())
slice =
builder.create<fir::SliceOp>(loc, triples, sliceFields, substring);
else
assert(sliceFields.empty() && substring.empty());
llvm::SmallVector<mlir::Type> resultType{designateResultType};
mlir::Value resultBox;
if (base.getType().isa<fir::BaseBoxType>())
resultBox =
builder.create<fir::ReboxOp>(loc, resultType, base, shape, slice);
else
resultBox =
builder.create<fir::EmboxOp>(loc, resultType, base, shape, slice,
firBaseTypeParameters, sourceBox);
rewriter.replaceOp(designate, resultBox);
return mlir::success();
}
// Otherwise, the result is the address of a scalar, or the address of the
// first element of a contiguous array section with compile time constant
// shape. The base may be an array, or a scalar.
mlir::Type resultAddressType = designateResultType;
if (auto boxCharType = designateResultType.dyn_cast<fir::BoxCharType>())
resultAddressType = fir::ReferenceType::get(boxCharType.getEleTy());
// Array element indexing.
if (!designate.getIndices().empty()) {
// - array(indices) [substring|complex_part] or
// - scalar%array_comp(indices) [substring|complex_part]
// This may be a ranked contiguous array section in which case
// The first element address is being computed.
base = genSubscriptBeginAddr(builder, loc, designate, baseEleTy, base,
shape, firBaseTypeParameters);
}
// Scalar substring (potentially on the previously built array element or
// component reference).
if (!designate.getSubstring().empty())
base = fir::factory::CharacterExprHelper{builder, loc}.genSubstringBase(
base, designate.getSubstring()[0], resultAddressType);
// Scalar complex part ref
if (designate.getComplexPart()) {
// Sequence types should have already been handled by this point
assert(!designateResultType.isa<fir::SequenceType>());
auto index = builder.createIntegerConstant(loc, builder.getIndexType(),
*designate.getComplexPart());
auto coorTy = fir::ReferenceType::get(resultEleTy);
base = builder.create<fir::CoordinateOp>(loc, coorTy, base, index);
}
// Cast/embox the computed scalar address if needed.
if (designateResultType.isa<fir::BoxCharType>()) {
assert(designate.getTypeparams().size() == 1 &&
"must have character length");
auto emboxChar = builder.create<fir::EmboxCharOp>(
loc, designateResultType, base, designate.getTypeparams()[0]);
rewriter.replaceOp(designate, emboxChar.getResult());
} else {
base = builder.createConvert(loc, designateResultType, base);
rewriter.replaceOp(designate, base);
}
return mlir::success();
}
private:
// Generates triple for full slice
// Used for component and complex part slices when a triple is
// not specified
static llvm::SmallVector<mlir::Value>
genFullSliceTriples(fir::FirOpBuilder &builder, mlir::Location loc,
hlfir::Entity baseEntity) {
llvm::SmallVector<mlir::Value> triples;
mlir::Type idxTy = builder.getIndexType();
auto one = builder.createIntegerConstant(loc, idxTy, 1);
for (auto [lb, ub] : hlfir::genBounds(loc, builder, baseEntity)) {
triples.push_back(builder.createConvert(loc, idxTy, lb));
triples.push_back(builder.createConvert(loc, idxTy, ub));
triples.push_back(one);
}
return triples;
}
};
class ParentComponentOpConversion
: public mlir::OpRewritePattern<hlfir::ParentComponentOp> {
public:
explicit ParentComponentOpConversion(mlir::MLIRContext *ctx)
: OpRewritePattern{ctx} {}
mlir::LogicalResult
matchAndRewrite(hlfir::ParentComponentOp parentComponent,
mlir::PatternRewriter &rewriter) const override {
mlir::Location loc = parentComponent.getLoc();
mlir::Type resultType = parentComponent.getType();
if (!parentComponent.getType().isa<fir::BoxType>()) {
mlir::Value baseAddr = parentComponent.getMemref();
// Scalar parent component ref without any length type parameters. The
// input may be a fir.class if it is polymorphic, since this is a scalar
// and the output will be monomorphic, the base address can be extracted
// from the fir.class.
if (baseAddr.getType().isa<fir::BaseBoxType>())
baseAddr = rewriter.create<fir::BoxAddrOp>(loc, baseAddr);
rewriter.replaceOpWithNewOp<fir::ConvertOp>(parentComponent, resultType,
baseAddr);
return mlir::success();
}
// Array parent component ref or PDTs.
hlfir::Entity base{parentComponent.getMemref()};
mlir::Value baseAddr = base.getBase();
if (!baseAddr.getType().isa<fir::BaseBoxType>()) {
// Embox cannot directly be used to address parent components: it expects
// the output type to match the input type when there are no slices. When
// the types have at least one component, a slice to the first element can
// be built, and the result set to the parent component type. Just create
// a fir.box with the base for now since this covers all cases.
mlir::Type baseBoxType =
fir::BoxType::get(base.getElementOrSequenceType());
assert(!base.hasLengthParameters() &&
"base must be a box if it has any type parameters");
baseAddr = rewriter.create<fir::EmboxOp>(
loc, baseBoxType, baseAddr, parentComponent.getShape(),
/*slice=*/mlir::Value{}, /*typeParams=*/mlir::ValueRange{});
}
rewriter.replaceOpWithNewOp<fir::ReboxOp>(parentComponent, resultType,
baseAddr,
/*shape=*/mlir::Value{},
/*slice=*/mlir::Value{});
return mlir::success();
}
};
class NoReassocOpConversion
: public mlir::OpRewritePattern<hlfir::NoReassocOp> {
public:
explicit NoReassocOpConversion(mlir::MLIRContext *ctx)
: OpRewritePattern{ctx} {}
mlir::LogicalResult
matchAndRewrite(hlfir::NoReassocOp noreassoc,
mlir::PatternRewriter &rewriter) const override {
rewriter.replaceOpWithNewOp<fir::NoReassocOp>(noreassoc,
noreassoc.getVal());
return mlir::success();
}
};
class NullOpConversion : public mlir::OpRewritePattern<hlfir::NullOp> {
public:
explicit NullOpConversion(mlir::MLIRContext *ctx) : OpRewritePattern{ctx} {}
mlir::LogicalResult
matchAndRewrite(hlfir::NullOp nullop,
mlir::PatternRewriter &rewriter) const override {
rewriter.replaceOpWithNewOp<fir::ZeroOp>(nullop, nullop.getType());
return mlir::success();
}
};
class GetExtentOpConversion
: public mlir::OpRewritePattern<hlfir::GetExtentOp> {
public:
using mlir::OpRewritePattern<hlfir::GetExtentOp>::OpRewritePattern;
mlir::LogicalResult
matchAndRewrite(hlfir::GetExtentOp getExtentOp,
mlir::PatternRewriter &rewriter) const override {
mlir::Value shape = getExtentOp.getShape();
mlir::Operation *shapeOp = shape.getDefiningOp();
// the hlfir.shape_of operation which led to the creation of this get_extent
// operation should now have been lowered to a fir.shape operation
if (auto s = mlir::dyn_cast_or_null<fir::ShapeOp>(shapeOp)) {
fir::ShapeType shapeTy = shape.getType().cast<fir::ShapeType>();
llvm::APInt dim = getExtentOp.getDim();
uint64_t dimVal = dim.getLimitedValue(shapeTy.getRank());
mlir::Value extent = s.getExtents()[dimVal];
rewriter.replaceOp(getExtentOp, extent);
return mlir::success();
}
return mlir::failure();
}
};
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, CopyInOpConversion, CopyOutOpConversion,
DeclareOpConversion, DesignateOpConversion,
GetExtentOpConversion, NoReassocOpConversion,
NullOpConversion, ParentComponentOpConversion>(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>();
}