//===- 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() && "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().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(loc, fromMutableBox); return copy; } namespace { /// May \p lhs alias with \p rhs? /// TODO: implement HLFIR alias analysis. class AssignOpConversion : public mlir::OpRewritePattern { 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(); fir::FirOpBuilder builder(rewriter, module); if (rhs.getType().isa()) { 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(loc, rhsType); builder.create(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. // Note that AssignExplicitLengthCharacter() must be used // when isTemporaryLHS() is true here: the LHS is known to be // character allocatable in this case, so finalization will not // happen (as implied by temporary_lhs attribute), and LHS // must keep its length (as implied by keep_lhs_length_if_realloc). fir::runtime::genAssignExplicitLengthCharacter(builder, loc, to, from); } else if (assignOp.isTemporaryLHS()) { // Use AssignTemporary, when the LHS is a compiler generated temporary. // Note that it also works properly for polymorphic LHS (i.e. the LHS // will have the RHS dynamic type after the assignment). fir::runtime::genAssignTemporary(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() || // Special case for element-by-element (or scalar) assignments // generated for creating polymorphic expressions. // The LHS of these assignments is a box describing just // a single element, not the whole allocatable temp. // They do not have 'realloc' attribute, because reallocation // must not happen. The only expected effect of such an // assignment is the copy of the contents, because the dynamic // types of the LHS and the RHS must match already. We use the // runtime in this case so that the polymorphic (including // unlimited) content is copied properly. (lhs.isPolymorphic() && assignOp.isTemporaryLHS())) { // 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(loc, to, toMutableBox); if (assignOp.isTemporaryLHS()) fir::runtime::genAssignTemporary(builder, loc, toMutableBox, from); else fir::runtime::genAssign(builder, loc, toMutableBox, from); } else { // TODO: use the type specification to see if IsFinalizable is set, // or propagate IsFinalizable attribute from lowering. bool needFinalization = !assignOp.isTemporaryLHS() && mlir::isa(fir::getElementTypeOf(lhsExv)); // genScalarAssignment() must take care of potential overlap // between LHS and RHS. Note that the overlap is possible // also for components of LHS/RHS, and the Assign() runtime // must take care of it. fir::factory::genScalarAssignment(builder, loc, lhsExv, rhsExv, needFinalization, assignOp.isTemporaryLHS()); } rewriter.eraseOp(assignOp); return mlir::success(); } }; class CopyInOpConversion : public mlir::OpRewritePattern { 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(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(loc, resultAddrType, temp, /*shape=*/mlir::Value{}, /*slice=*/mlir::Value{}); builder.create(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( loc, mlir::ValueRange{res.addr, res.wasCopied}); }) .genElse([&] { mlir::Value absent = builder.create(loc, resultAddrType); builder.create( 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(); fir::FirOpBuilder builder(rewriter, copyInOp.getOperation()); 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 { public: explicit CopyOutOpConversion(mlir::MLIRContext *ctx) : OpRewritePattern{ctx} {} mlir::LogicalResult matchAndRewrite(hlfir::CopyOutOp copyOutOp, mlir::PatternRewriter &rewriter) const override { mlir::Location loc = copyOutOp.getLoc(); fir::FirOpBuilder builder(rewriter, copyOutOp.getOperation()); 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(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(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(loc, tempAddr); }) .end(); rewriter.eraseOp(copyOutOp); return mlir::success(); } }; class DeclareOpConversion : public mlir::OpRewritePattern { 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 firDeclareOp = rewriter.create( loc, memref.getType(), memref, declareOp.getShape(), declareOp.getTypeparams(), declareOp.getUniqName(), fortranAttrs); // Propagate other attributes from hlfir.declare to fir.declare. // OpenACC's acc.declare is one example. Right now, the propagation // is verbatim. mlir::NamedAttrList elidedAttrs = mlir::NamedAttrList{firDeclareOp->getAttrs()}; for (const mlir::NamedAttribute &attr : declareOp->getAttrs()) if (!elidedAttrs.get(attr.getName())) firDeclareOp->setAttr(attr.getName(), attr.getValue()); auto firBase = firDeclareOp.getResult(); mlir::Value hlfirBase; mlir::Type hlfirBaseType = declareOp.getBase().getType(); if (hlfirBaseType.isa()) { fir::FirOpBuilder builder(rewriter, declareOp.getOperation()); // Helper to generate the hlfir fir.box with the local lower bounds and // type parameters. auto genHlfirBox = [&]() -> mlir::Value { if (!firBase.getType().isa()) { llvm::SmallVector typeParams; auto maybeCharType = fir::unwrapSequenceType(fir::unwrapPassByRefType(hlfirBaseType)) .dyn_cast(); if (!maybeCharType || maybeCharType.hasDynamicLen()) typeParams.append(declareOp.getTypeparams().begin(), declareOp.getTypeparams().end()); return builder.create( loc, hlfirBaseType, firBase, declareOp.getShape(), /*slice=*/mlir::Value{}, typeParams); } else { // Rebox so that lower bounds are correct. return builder.create(loc, hlfirBaseType, firBase, declareOp.getShape(), /*slice=*/mlir::Value{}); } }; if (!mlir::cast(declareOp.getOperation()) .isOptional()) { hlfirBase = genHlfirBox(); // If the original base is a box too, we could as well // use the HLFIR box as the FIR base: otherwise, the two // boxes are "alive" at the same time, and the FIR box // is used for accessing the base_addr and the HLFIR box // is used for accessing the bounds etc. Using the HLFIR box, // that holds the same base_addr at this point, makes // the representation a little bit more clear. if (hlfirBase.getType() == firBase.getType()) firBase = hlfirBase; } 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(loc, builder.getI1Type(), firBase); hlfirBase = builder .genIfOp(loc, {hlfirBaseType}, isPresent, /*withElseRegion=*/true) .genThen([&] { builder.create(loc, genHlfirBox()); }) .genElse([&]() { mlir::Value absent = builder.create(loc, hlfirBaseType); builder.create(loc, absent); }) .getResults()[0]; } } else if (hlfirBaseType.isa()) { assert(declareOp.getTypeparams().size() == 1 && "must contain character length"); hlfirBase = rewriter.create( 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 { // 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 &firBaseTypeParameters) { assert(!designate.getIndices().empty()); llvm::SmallVector 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( 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(); fir::FirOpBuilder builder(rewriter, designate.getOperation()); 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 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( 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().getType( designate.getComponent().value()); mlir::Type coorTy = fir::ReferenceType::get(componentType); base = builder.create(loc, coorTy, base, fieldIndex); if (componentType.isa()) { auto variableInterface = mlir::cast( 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()); } } if (designateResultType.isa()) { // Generate embox or rebox. mlir::Type eleTy = fir::unwrapPassByRefType(designateResultType); bool isScalarDesignator = !eleTy.isa(); 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 triples; llvm::SmallVector 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 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(loc, iIdx, lbIdx)); } } } else if (!isScalarDesignator) { // Otherwise, this is an array section with triplets. auto undef = builder.create(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 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(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(loc, triples, sliceFields, substring); else assert(sliceFields.empty() && substring.empty()); llvm::SmallVector resultType{designateResultType}; mlir::Value resultBox; if (base.getType().isa()) resultBox = builder.create(loc, resultType, base, shape, slice); else resultBox = builder.create(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()) 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()); auto index = builder.createIntegerConstant(loc, builder.getIndexType(), *designate.getComplexPart()); auto coorTy = fir::ReferenceType::get(resultEleTy); base = builder.create(loc, coorTy, base, index); } // Cast/embox the computed scalar address if needed. if (designateResultType.isa()) { assert(designate.getTypeparams().size() == 1 && "must have character length"); auto emboxChar = builder.create( 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 genFullSliceTriples(fir::FirOpBuilder &builder, mlir::Location loc, hlfir::Entity baseEntity) { llvm::SmallVector 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 { 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()) { 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()) baseAddr = rewriter.create(loc, baseAddr); rewriter.replaceOpWithNewOp(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()) { // 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( loc, baseBoxType, baseAddr, parentComponent.getShape(), /*slice=*/mlir::Value{}, /*typeParams=*/mlir::ValueRange{}); } rewriter.replaceOpWithNewOp(parentComponent, resultType, baseAddr, /*shape=*/mlir::Value{}, /*slice=*/mlir::Value{}); return mlir::success(); } }; class NoReassocOpConversion : public mlir::OpRewritePattern { public: explicit NoReassocOpConversion(mlir::MLIRContext *ctx) : OpRewritePattern{ctx} {} mlir::LogicalResult matchAndRewrite(hlfir::NoReassocOp noreassoc, mlir::PatternRewriter &rewriter) const override { rewriter.replaceOpWithNewOp(noreassoc, noreassoc.getVal()); return mlir::success(); } }; class NullOpConversion : public mlir::OpRewritePattern { public: explicit NullOpConversion(mlir::MLIRContext *ctx) : OpRewritePattern{ctx} {} mlir::LogicalResult matchAndRewrite(hlfir::NullOp nullop, mlir::PatternRewriter &rewriter) const override { rewriter.replaceOpWithNewOp(nullop, nullop.getType()); return mlir::success(); } }; class GetExtentOpConversion : public mlir::OpRewritePattern { public: using mlir::OpRewritePattern::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(shapeOp)) { fir::ShapeType shapeTy = shape.getType().cast(); 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 { 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(context); mlir::ConversionTarget target(*context); target.addIllegalDialect(); 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 hlfir::createConvertHLFIRtoFIRPass() { return std::make_unique(); }