825 lines · cpp
1//===- ConvertToFIR.cpp - Convert HLFIR to FIR ----------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8// This file defines a pass to lower HLFIR to FIR9//===----------------------------------------------------------------------===//10 11#include "flang/Optimizer/Builder/Character.h"12#include "flang/Optimizer/Builder/FIRBuilder.h"13#include "flang/Optimizer/Builder/HLFIRTools.h"14#include "flang/Optimizer/Builder/MutableBox.h"15#include "flang/Optimizer/Builder/Runtime/Assign.h"16#include "flang/Optimizer/Builder/Runtime/Derived.h"17#include "flang/Optimizer/Builder/Runtime/Inquiry.h"18#include "flang/Optimizer/Builder/Todo.h"19#include "flang/Optimizer/Dialect/FIRDialect.h"20#include "flang/Optimizer/Dialect/FIROps.h"21#include "flang/Optimizer/Dialect/FIRType.h"22#include "flang/Optimizer/Dialect/Support/FIRContext.h"23#include "flang/Optimizer/HLFIR/HLFIROps.h"24#include "flang/Optimizer/HLFIR/Passes.h"25#include "mlir/Transforms/DialectConversion.h"26#include "llvm/ADT/SmallSet.h"27 28namespace hlfir {29#define GEN_PASS_DEF_CONVERTHLFIRTOFIR30#include "flang/Optimizer/HLFIR/Passes.h.inc"31} // namespace hlfir32 33using namespace mlir;34 35namespace {36/// May \p lhs alias with \p rhs?37/// TODO: implement HLFIR alias analysis.38class AssignOpConversion : public mlir::OpRewritePattern<hlfir::AssignOp> {39public:40 explicit AssignOpConversion(mlir::MLIRContext *ctx) : OpRewritePattern{ctx} {}41 42 llvm::LogicalResult43 matchAndRewrite(hlfir::AssignOp assignOp,44 mlir::PatternRewriter &rewriter) const override {45 mlir::Location loc = assignOp->getLoc();46 hlfir::Entity lhs(assignOp.getLhs());47 hlfir::Entity rhs(assignOp.getRhs());48 auto module = assignOp->getParentOfType<mlir::ModuleOp>();49 fir::FirOpBuilder builder(rewriter, module);50 51 if (mlir::isa<hlfir::ExprType>(rhs.getType())) {52 mlir::emitError(loc, "hlfir must be bufferized with --bufferize-hlfir "53 "pass before being converted to FIR");54 return mlir::failure();55 }56 auto [rhsExv, rhsCleanUp] =57 hlfir::translateToExtendedValue(loc, builder, rhs);58 auto [lhsExv, lhsCleanUp] =59 hlfir::translateToExtendedValue(loc, builder, lhs);60 assert(!lhsCleanUp && !rhsCleanUp &&61 "variable to fir::ExtendedValue must not require cleanup");62 63 auto emboxRHS = [&](fir::ExtendedValue &rhsExv) -> mlir::Value {64 // There may be overlap between lhs and rhs. The runtime is able to detect65 // and to make a copy of the rhs before modifying the lhs if needed.66 // The code below relies on this and does not do any compile time alias67 // analysis.68 const bool rhsIsValue = fir::isa_trivial(fir::getBase(rhsExv).getType());69 if (rhsIsValue) {70 // createBox can only be called for fir::ExtendedValue that are71 // already in memory. Place the integer/real/complex/logical scalar72 // in memory.73 // The RHS might be i1, which is not supported for emboxing.74 // If LHS is not polymorphic, we may cast the RHS to the LHS type75 // before emboxing. If LHS is polymorphic we have to figure out76 // the data type for RHS emboxing anyway.77 // It is probably a good idea to make sure that the data type78 // of the RHS is always a valid Fortran storage data type.79 // For the time being, just handle i1 explicitly here.80 mlir::Type rhsType = rhs.getFortranElementType();81 mlir::Value rhsVal = fir::getBase(rhsExv);82 if (rhsType == builder.getI1Type()) {83 rhsType = fir::LogicalType::get(builder.getContext(), 4);84 rhsVal = builder.createConvert(loc, rhsType, rhsVal);85 }86 mlir::Value temp = fir::AllocaOp::create(builder, loc, rhsType);87 fir::StoreOp::create(builder, loc, rhsVal, temp);88 rhsExv = temp;89 }90 return fir::getBase(builder.createBox(loc, rhsExv));91 };92 93 if (assignOp.isAllocatableAssignment()) {94 // Whole allocatable assignment: use the runtime to deal with the95 // reallocation.96 mlir::Value from = emboxRHS(rhsExv);97 mlir::Value to = fir::getBase(lhsExv);98 if (assignOp.mustKeepLhsLengthInAllocatableAssignment()) {99 // Indicate the runtime that it should not reallocate in case of length100 // mismatch, and that it should use the LHS explicit/assumed length if101 // allocating/reallocation the LHS.102 // Note that AssignExplicitLengthCharacter() must be used103 // when isTemporaryLHS() is true here: the LHS is known to be104 // character allocatable in this case, so finalization will not105 // happen (as implied by temporary_lhs attribute), and LHS106 // must keep its length (as implied by keep_lhs_length_if_realloc).107 fir::runtime::genAssignExplicitLengthCharacter(builder, loc, to, from);108 } else if (assignOp.isTemporaryLHS()) {109 // Use AssignTemporary, when the LHS is a compiler generated temporary.110 // Note that it also works properly for polymorphic LHS (i.e. the LHS111 // will have the RHS dynamic type after the assignment).112 fir::runtime::genAssignTemporary(builder, loc, to, from);113 } else if (lhs.isPolymorphic()) {114 // Indicate the runtime that the LHS must have the RHS dynamic type115 // after the assignment.116 fir::runtime::genAssignPolymorphic(builder, loc, to, from);117 } else {118 fir::runtime::genAssign(builder, loc, to, from);119 }120 } else if (lhs.isArray() ||121 // Special case for element-by-element (or scalar) assignments122 // generated for creating polymorphic expressions.123 // The LHS of these assignments is a box describing just124 // a single element, not the whole allocatable temp.125 // They do not have 'realloc' attribute, because reallocation126 // must not happen. The only expected effect of such an127 // assignment is the copy of the contents, because the dynamic128 // types of the LHS and the RHS must match already. We use the129 // runtime in this case so that the polymorphic (including130 // unlimited) content is copied properly.131 (lhs.isPolymorphic() && assignOp.isTemporaryLHS())) {132 // Use the runtime for simplicity. An optimization pass will be added to133 // inline array assignment when profitable.134 mlir::Value from = emboxRHS(rhsExv);135 mlir::Value to = fir::getBase(builder.createBox(loc, lhsExv));136 // This is not a whole allocatable assignment: the runtime will not137 // reallocate and modify "toMutableBox" even if it is taking it by138 // reference.139 auto toMutableBox = builder.createTemporary(loc, to.getType());140 fir::StoreOp::create(builder, loc, to, toMutableBox);141 if (assignOp.isTemporaryLHS())142 fir::runtime::genAssignTemporary(builder, loc, toMutableBox, from);143 else144 fir::runtime::genAssign(builder, loc, toMutableBox, from);145 } else {146 // TODO: use the type specification to see if IsFinalizable is set,147 // or propagate IsFinalizable attribute from lowering.148 bool needFinalization =149 !assignOp.isTemporaryLHS() &&150 mlir::isa<fir::RecordType>(fir::getElementTypeOf(lhsExv));151 152 mlir::ArrayAttr accessGroups;153 if (auto attrs = assignOp.getOperation()->getAttrOfType<mlir::ArrayAttr>(154 "access_groups"))155 accessGroups = attrs;156 157 // genScalarAssignment() must take care of potential overlap158 // between LHS and RHS. Note that the overlap is possible159 // also for components of LHS/RHS, and the Assign() runtime160 // must take care of it.161 fir::factory::genScalarAssignment(162 builder, loc, lhsExv, rhsExv, needFinalization,163 assignOp.isTemporaryLHS(), accessGroups);164 }165 rewriter.eraseOp(assignOp);166 return mlir::success();167 }168};169 170class CopyInOpConversion : public mlir::OpRewritePattern<hlfir::CopyInOp> {171public:172 explicit CopyInOpConversion(mlir::MLIRContext *ctx) : OpRewritePattern{ctx} {}173 174 struct CopyInResult {175 mlir::Value addr;176 mlir::Value wasCopied;177 };178 179 static CopyInResult genNonOptionalCopyIn(mlir::Location loc,180 fir::FirOpBuilder &builder,181 hlfir::CopyInOp copyInOp) {182 mlir::Value inputVariable = copyInOp.getVar();183 mlir::Type resultAddrType = copyInOp.getCopiedIn().getType();184 mlir::Value isContiguous =185 fir::runtime::genIsContiguous(builder, loc, inputVariable);186 mlir::Value addr =187 builder188 .genIfOp(loc, {resultAddrType}, isContiguous,189 /*withElseRegion=*/true)190 .genThen(191 [&]() { fir::ResultOp::create(builder, loc, inputVariable); })192 .genElse([&] {193 // Create temporary on the heap. Note that the runtime is used and194 // that is desired: since the data copy happens under a runtime195 // check (for IsContiguous) the copy loops can hardly provide any196 // value to optimizations, instead, the optimizer just wastes197 // compilation time on these loops.198 mlir::Value temp = copyInOp.getTempBox();199 fir::runtime::genCopyInAssign(builder, loc, temp, inputVariable);200 mlir::Value copy = fir::LoadOp::create(builder, loc, temp);201 // Get rid of allocatable flag in the fir.box.202 if (mlir::cast<fir::BaseBoxType>(resultAddrType).isAssumedRank())203 copy = fir::ReboxAssumedRankOp::create(204 builder, loc, resultAddrType, copy,205 fir::LowerBoundModifierAttribute::Preserve);206 else207 copy = fir::ReboxOp::create(builder, loc, resultAddrType, copy,208 /*shape=*/mlir::Value{},209 /*slice=*/mlir::Value{});210 fir::ResultOp::create(builder, loc, copy);211 })212 .getResults()[0];213 return {addr, builder.genNot(loc, isContiguous)};214 }215 216 static CopyInResult genOptionalCopyIn(mlir::Location loc,217 fir::FirOpBuilder &builder,218 hlfir::CopyInOp copyInOp) {219 mlir::Type resultAddrType = copyInOp.getCopiedIn().getType();220 mlir::Value isPresent = copyInOp.getVarIsPresent();221 auto res =222 builder223 .genIfOp(loc, {resultAddrType, builder.getI1Type()}, isPresent,224 /*withElseRegion=*/true)225 .genThen([&]() {226 CopyInResult res = genNonOptionalCopyIn(loc, builder, copyInOp);227 fir::ResultOp::create(builder, loc,228 mlir::ValueRange{res.addr, res.wasCopied});229 })230 .genElse([&] {231 mlir::Value absent =232 fir::AbsentOp::create(builder, loc, resultAddrType);233 fir::ResultOp::create(builder, loc,234 mlir::ValueRange{absent, isPresent});235 })236 .getResults();237 return {res[0], res[1]};238 }239 240 llvm::LogicalResult241 matchAndRewrite(hlfir::CopyInOp copyInOp,242 mlir::PatternRewriter &rewriter) const override {243 mlir::Location loc = copyInOp.getLoc();244 fir::FirOpBuilder builder(rewriter, copyInOp.getOperation());245 CopyInResult result = copyInOp.getVarIsPresent()246 ? genOptionalCopyIn(loc, builder, copyInOp)247 : genNonOptionalCopyIn(loc, builder, copyInOp);248 rewriter.replaceOp(copyInOp, {result.addr, result.wasCopied});249 return mlir::success();250 }251};252 253class CopyOutOpConversion : public mlir::OpRewritePattern<hlfir::CopyOutOp> {254public:255 explicit CopyOutOpConversion(mlir::MLIRContext *ctx)256 : OpRewritePattern{ctx} {}257 258 llvm::LogicalResult259 matchAndRewrite(hlfir::CopyOutOp copyOutOp,260 mlir::PatternRewriter &rewriter) const override {261 mlir::Location loc = copyOutOp.getLoc();262 fir::FirOpBuilder builder(rewriter, copyOutOp.getOperation());263 264 builder.genIfThen(loc, copyOutOp.getWasCopied())265 .genThen([&]() {266 mlir::Value temp = copyOutOp.getTemp();267 mlir::Value varMutableBox;268 // Generate CopyOutAssign runtime call.269 if (mlir::Value var = copyOutOp.getVar()) {270 // Set the variable descriptor pointer in order to copy data from271 // the temporary to the actualArg. Note that in case the actual272 // argument is ALLOCATABLE/POINTER the CopyOutAssign()273 // implementation should not engage its reallocation, because the274 // temporary is rank, shape and type compatible with it. Moreover,275 // CopyOutAssign() guarantees that there will be no finalization for276 // the LHS even if it is of a derived type with finalization.277 varMutableBox = builder.createTemporary(loc, var.getType());278 fir::StoreOp::create(builder, loc, var, varMutableBox);279 } else {280 // Even when there is no need to copy back the data (e.g., the dummy281 // argument was intent(in), CopyOutAssign is called to282 // destroy/deallocate the temporary.283 varMutableBox = fir::ZeroOp::create(builder, loc, temp.getType());284 }285 fir::runtime::genCopyOutAssign(builder, loc, varMutableBox,286 copyOutOp.getTemp());287 })288 .end();289 rewriter.eraseOp(copyOutOp);290 return mlir::success();291 }292};293 294class DeclareOpConversion : public mlir::OpRewritePattern<hlfir::DeclareOp> {295public:296 explicit DeclareOpConversion(mlir::MLIRContext *ctx)297 : OpRewritePattern{ctx} {}298 299 llvm::LogicalResult300 matchAndRewrite(hlfir::DeclareOp declareOp,301 mlir::PatternRewriter &rewriter) const override {302 mlir::Location loc = declareOp->getLoc();303 mlir::Value memref = declareOp.getMemref();304 fir::FortranVariableFlagsAttr fortranAttrs;305 cuf::DataAttributeAttr dataAttr;306 if (auto attrs = declareOp.getFortranAttrs())307 fortranAttrs =308 fir::FortranVariableFlagsAttr::get(rewriter.getContext(), *attrs);309 if (auto attr = declareOp.getDataAttr())310 dataAttr = cuf::DataAttributeAttr::get(rewriter.getContext(), *attr);311 auto firDeclareOp = fir::DeclareOp::create(312 rewriter, loc, memref.getType(), memref, declareOp.getShape(),313 declareOp.getTypeparams(), declareOp.getDummyScope(),314 /*storage=*/declareOp.getStorage(),315 /*storage_offset=*/declareOp.getStorageOffset(),316 declareOp.getUniqName(), fortranAttrs, dataAttr,317 declareOp.getDummyArgNoAttr());318 319 // Propagate other attributes from hlfir.declare to fir.declare.320 // OpenACC's acc.declare is one example. Right now, the propagation321 // is verbatim.322 llvm::SmallSet<llvm::StringRef, 8> elidedAttrs;323 for (const mlir::NamedAttribute &firAttr : firDeclareOp->getAttrs())324 elidedAttrs.insert(firAttr.getName());325 elidedAttrs.insert(declareOp.getSkipReboxAttrName());326 for (const mlir::NamedAttribute &attr : declareOp->getAttrs())327 if (!elidedAttrs.contains(attr.getName()))328 firDeclareOp->setAttr(attr.getName(), attr.getValue());329 330 auto firBase = firDeclareOp.getResult();331 mlir::Value hlfirBase;332 mlir::Type hlfirBaseType = declareOp.getBase().getType();333 if (mlir::isa<fir::BaseBoxType>(hlfirBaseType)) {334 fir::FirOpBuilder builder(rewriter, declareOp.getOperation());335 // Helper to generate the hlfir fir.box with the local lower bounds and336 // type parameters.337 auto genHlfirBox = [&]() -> mlir::Value {338 if (auto baseBoxType =339 mlir::dyn_cast<fir::BaseBoxType>(firBase.getType())) {340 if (declareOp.getSkipRebox())341 return firBase;342 // Rebox so that lower bounds and attributes are correct.343 if (baseBoxType.isAssumedRank())344 return fir::ReboxAssumedRankOp::create(345 builder, loc, hlfirBaseType, firBase,346 fir::LowerBoundModifierAttribute::SetToOnes);347 if (!fir::extractSequenceType(baseBoxType.getEleTy()) &&348 baseBoxType == hlfirBaseType)349 return firBase;350 return fir::ReboxOp::create(builder, loc, hlfirBaseType, firBase,351 declareOp.getShape(),352 /*slice=*/mlir::Value{});353 } else {354 llvm::SmallVector<mlir::Value> typeParams;355 auto maybeCharType = mlir::dyn_cast<fir::CharacterType>(356 fir::unwrapSequenceType(fir::unwrapPassByRefType(hlfirBaseType)));357 if (!maybeCharType || maybeCharType.hasDynamicLen())358 typeParams.append(declareOp.getTypeparams().begin(),359 declareOp.getTypeparams().end());360 return fir::EmboxOp::create(builder, loc, hlfirBaseType, firBase,361 declareOp.getShape(),362 /*slice=*/mlir::Value{}, typeParams);363 }364 };365 if (!mlir::cast<fir::FortranVariableOpInterface>(declareOp.getOperation())366 .isOptional()) {367 hlfirBase = genHlfirBox();368 // If the original base is a box too, we could as well369 // use the HLFIR box as the FIR base: otherwise, the two370 // boxes are "alive" at the same time, and the FIR box371 // is used for accessing the base_addr and the HLFIR box372 // is used for accessing the bounds etc. Using the HLFIR box,373 // that holds the same base_addr at this point, makes374 // the representation a little bit more clear.375 if (hlfirBase.getType() == declareOp.getOriginalBase().getType())376 firBase = hlfirBase;377 } else {378 // Need to conditionally rebox/embox the optional: the input fir.box379 // may be null and the rebox would be illegal. It is also important to380 // preserve the optional aspect: the hlfir fir.box should be null if381 // the entity is absent so that later fir.is_present on the hlfir base382 // are valid.383 mlir::Value isPresent = fir::IsPresentOp::create(384 builder, loc, builder.getI1Type(), firBase);385 hlfirBase =386 builder387 .genIfOp(loc, {hlfirBaseType}, isPresent,388 /*withElseRegion=*/true)389 .genThen(390 [&] { fir::ResultOp::create(builder, loc, genHlfirBox()); })391 .genElse([&]() {392 mlir::Value absent =393 fir::AbsentOp::create(builder, loc, hlfirBaseType);394 fir::ResultOp::create(builder, loc, absent);395 })396 .getResults()[0];397 }398 } else if (mlir::isa<fir::BoxCharType>(hlfirBaseType)) {399 assert(declareOp.getTypeparams().size() == 1 &&400 "must contain character length");401 hlfirBase = fir::EmboxCharOp::create(402 rewriter, loc, hlfirBaseType, firBase, declareOp.getTypeparams()[0]);403 } else {404 if (hlfirBaseType != firBase.getType()) {405 declareOp.emitOpError()406 << "unhandled HLFIR variable type '" << hlfirBaseType << "'\n";407 return mlir::failure();408 }409 hlfirBase = firBase;410 }411 rewriter.replaceOp(declareOp, {hlfirBase, firBase});412 return mlir::success();413 }414};415 416class DesignateOpConversion417 : public mlir::OpRewritePattern<hlfir::DesignateOp> {418 // Helper method to generate the coordinate of the first element419 // of an array section. It is also called for cases of non-section420 // array element addressing.421 static mlir::Value genSubscriptBeginAddr(422 fir::FirOpBuilder &builder, mlir::Location loc,423 hlfir::DesignateOp designate, mlir::Type baseEleTy, mlir::Value base,424 mlir::Value shape,425 const llvm::SmallVector<mlir::Value> &firBaseTypeParameters) {426 assert(!designate.getIndices().empty());427 llvm::SmallVector<mlir::Value> firstElementIndices;428 auto indices = designate.getIndices();429 int i = 0;430 auto attrs = designate.getIsTripletAttr();431 for (auto isTriplet : attrs.asArrayRef()) {432 // Coordinate of the first element are the index and triplets lower433 // bounds.434 firstElementIndices.push_back(indices[i]);435 i = i + (isTriplet ? 3 : 1);436 }437 438 mlir::Type originalDesignateType = designate.getResult().getType();439 const bool isVolatile = fir::isa_volatile_type(originalDesignateType);440 mlir::Type arrayCoorType = fir::ReferenceType::get(baseEleTy, isVolatile);441 442 base = fir::ArrayCoorOp::create(builder, loc, arrayCoorType, base, shape,443 /*slice=*/mlir::Value{},444 firstElementIndices, firBaseTypeParameters);445 return base;446 }447 448public:449 explicit DesignateOpConversion(mlir::MLIRContext *ctx)450 : OpRewritePattern{ctx} {}451 452 llvm::LogicalResult453 matchAndRewrite(hlfir::DesignateOp designate,454 mlir::PatternRewriter &rewriter) const override {455 mlir::Location loc = designate.getLoc();456 fir::FirOpBuilder builder(rewriter, designate.getOperation());457 458 hlfir::Entity baseEntity(designate.getMemref());459 460 if (baseEntity.isMutableBox())461 TODO(loc, "hlfir::designate load of pointer or allocatable");462 463 mlir::Type designateResultType = designate.getResult().getType();464 llvm::SmallVector<mlir::Value> firBaseTypeParameters;465 auto [base, shape] = hlfir::genVariableFirBaseShapeAndParams(466 loc, builder, baseEntity, firBaseTypeParameters);467 const bool isVolatile = fir::isa_volatile_type(designateResultType) ||468 fir::isa_volatile_type(base.getType());469 mlir::Type baseEleTy = hlfir::getFortranElementType(base.getType());470 mlir::Type resultEleTy = hlfir::getFortranElementType(designateResultType);471 472 mlir::Value fieldIndex;473 if (designate.getComponent()) {474 mlir::Type baseRecordType = baseEntity.getFortranElementType();475 if (fir::isRecordWithTypeParameters(baseRecordType))476 TODO(loc, "hlfir.designate with a parametrized derived type base");477 fieldIndex = fir::FieldIndexOp::create(478 builder, loc, fir::FieldType::get(builder.getContext()),479 designate.getComponent().value(), baseRecordType,480 /*typeParams=*/mlir::ValueRange{});481 if (baseEntity.isScalar()) {482 // Component refs of scalar base right away:483 // - scalar%scalar_component [substring|complex_part] or484 // - scalar%static_size_array_comp485 // - scalar%array(indices) [substring| complex part]486 mlir::Type componentType =487 mlir::cast<fir::RecordType>(baseEleTy).getType(488 designate.getComponent().value());489 mlir::Type coorTy = fir::ReferenceType::get(componentType, isVolatile);490 491 base =492 fir::CoordinateOp::create(builder, loc, coorTy, base, fieldIndex);493 if (mlir::isa<fir::BaseBoxType>(componentType)) {494 auto variableInterface = mlir::cast<fir::FortranVariableOpInterface>(495 designate.getOperation());496 if (variableInterface.isAllocatable() ||497 variableInterface.isPointer()) {498 rewriter.replaceOp(designate, base);499 return mlir::success();500 }501 TODO(loc,502 "addressing parametrized derived type automatic components");503 }504 baseEleTy = hlfir::getFortranElementType(componentType);505 shape = designate.getComponentShape();506 }507 }508 509 if (mlir::isa<fir::BaseBoxType>(designateResultType) ||510 // Convert the component array slices using embox/rebox511 // even if the result is a contiguous array section, e.g.:512 // hlfir.designate %base{"i"} shape %shape :513 // (!fir.box<!fir.array<2x!fir.type<_QMtypesTt{i:i32}>>>,514 // !fir.shape<1>) -> !fir.ref<!fir.array<2xi32>>515 // fir.coordinate_of should probably be a better option, though.516 (fieldIndex && baseEntity.isArray())) {517 // Generate embox or rebox for slicing.518 mlir::Type eleTy = fir::unwrapPassByRefType(designateResultType);519 bool isScalarDesignator = !mlir::isa<fir::SequenceType>(eleTy);520 mlir::Value sourceBox;521 if (isScalarDesignator) {522 // The base box will be used for emboxing the scalar element.523 sourceBox = base;524 // Generate the coordinate of the element.525 base = genSubscriptBeginAddr(builder, loc, designate, baseEleTy, base,526 shape, firBaseTypeParameters);527 shape = nullptr;528 // Type information will be taken from the source box,529 // so the type parameters are not needed.530 firBaseTypeParameters.clear();531 }532 llvm::SmallVector<mlir::Value> triples;533 llvm::SmallVector<mlir::Value> sliceFields;534 mlir::Type idxTy = builder.getIndexType();535 auto subscripts = designate.getIndices();536 if (fieldIndex && baseEntity.isArray()) {537 // array%scalar_comp or array%array_comp(indices)538 // Generate triples for array(:, :, ...).539 triples = genFullSliceTriples(builder, loc, baseEntity);540 sliceFields.push_back(fieldIndex);541 // Add indices in the field path for "array%array_comp(indices)"542 // case. The indices of components provided to the sliceOp must543 // be zero based (fir.slice has no knowledge of the component544 // lower bounds). The component lower bounds are applied here.545 if (!subscripts.empty()) {546 llvm::SmallVector<mlir::Value> lbounds = hlfir::genLowerbounds(547 loc, builder, designate.getComponentShape(), subscripts.size());548 for (auto [i, lb] : llvm::zip(subscripts, lbounds)) {549 mlir::Value iIdx = builder.createConvert(loc, idxTy, i);550 mlir::Value lbIdx = builder.createConvert(loc, idxTy, lb);551 sliceFields.emplace_back(552 mlir::arith::SubIOp::create(builder, loc, iIdx, lbIdx));553 }554 }555 } else if (!isScalarDesignator) {556 // Otherwise, this is an array section with triplets.557 auto undef = fir::UndefOp::create(builder, loc, idxTy);558 unsigned i = 0;559 for (auto isTriplet : designate.getIsTriplet()) {560 triples.push_back(subscripts[i++]);561 if (isTriplet) {562 triples.push_back(subscripts[i++]);563 triples.push_back(subscripts[i++]);564 } else {565 triples.push_back(undef);566 triples.push_back(undef);567 }568 }569 }570 llvm::SmallVector<mlir::Value, 2> substring;571 if (!designate.getSubstring().empty()) {572 substring.push_back(designate.getSubstring()[0]);573 mlir::Type idxTy = builder.getIndexType();574 // fir.slice op substring expects the zero based lower bound.575 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);576 substring[0] = builder.createConvert(loc, idxTy, substring[0]);577 substring[0] =578 mlir::arith::SubIOp::create(builder, loc, substring[0], one);579 substring.push_back(designate.getTypeparams()[0]);580 }581 if (designate.getComplexPart()) {582 if (triples.empty())583 triples = genFullSliceTriples(builder, loc, baseEntity);584 sliceFields.push_back(builder.createIntegerConstant(585 loc, idxTy, *designate.getComplexPart()));586 }587 mlir::Value slice;588 if (!triples.empty())589 slice =590 fir::SliceOp::create(builder, loc, triples, sliceFields, substring);591 else592 assert(sliceFields.empty() && substring.empty());593 594 // If the designate's result type is not a box, then create595 // a box type to be used for the result of the embox/rebox.596 mlir::Type resultType = designateResultType;597 if (!mlir::isa<fir::BaseBoxType>(resultType))598 resultType = fir::wrapInClassOrBoxType(resultType);599 600 resultType = fir::updateTypeWithVolatility(resultType, isVolatile);601 602 mlir::Value resultBox;603 if (mlir::isa<fir::BaseBoxType>(base.getType())) {604 resultBox =605 fir::ReboxOp::create(builder, loc, resultType, base, shape, slice);606 } else {607 resultBox =608 fir::EmboxOp::create(builder, loc, resultType, base, shape, slice,609 firBaseTypeParameters, sourceBox);610 }611 612 if (!mlir::isa<fir::BaseBoxType>(designateResultType)) {613 // If the designate's result is not a box, use the raw address614 // as the new result.615 resultBox = fir::BoxAddrOp::create(rewriter, loc, resultBox);616 resultBox = builder.createConvert(loc, designateResultType, resultBox);617 }618 rewriter.replaceOp(designate, resultBox);619 return mlir::success();620 }621 622 // Otherwise, the result is the address of a scalar, or the address of the623 // first element of a contiguous array section with compile time constant624 // shape. The base may be an array, or a scalar.625 mlir::Type resultAddressType = designateResultType;626 if (auto boxCharType =627 mlir::dyn_cast<fir::BoxCharType>(designateResultType))628 resultAddressType =629 fir::ReferenceType::get(boxCharType.getEleTy(), isVolatile);630 631 // Array element indexing.632 if (!designate.getIndices().empty()) {633 // - array(indices) [substring|complex_part] or634 // - scalar%array_comp(indices) [substring|complex_part]635 // This may be a ranked contiguous array section in which case636 // The first element address is being computed.637 base = genSubscriptBeginAddr(builder, loc, designate, baseEleTy, base,638 shape, firBaseTypeParameters);639 }640 641 // Scalar substring (potentially on the previously built array element or642 // component reference).643 if (!designate.getSubstring().empty())644 base = fir::factory::CharacterExprHelper{builder, loc}.genSubstringBase(645 base, designate.getSubstring()[0], resultAddressType);646 647 // Scalar complex part ref648 if (designate.getComplexPart()) {649 // Sequence types should have already been handled by this point650 assert(!mlir::isa<fir::SequenceType>(designateResultType));651 auto index = builder.createIntegerConstant(loc, builder.getIndexType(),652 *designate.getComplexPart());653 auto coorTy = fir::ReferenceType::get(resultEleTy, isVolatile);654 655 base = fir::CoordinateOp::create(builder, loc, coorTy, base, index);656 }657 658 // Cast/embox the computed scalar address if needed.659 if (mlir::isa<fir::BoxCharType>(designateResultType)) {660 assert(designate.getTypeparams().size() == 1 &&661 "must have character length");662 auto emboxChar =663 fir::EmboxCharOp::create(builder, loc, designateResultType, base,664 designate.getTypeparams()[0]);665 666 rewriter.replaceOp(designate, emboxChar.getResult());667 } else {668 base = builder.createConvert(loc, designateResultType, base);669 670 rewriter.replaceOp(designate, base);671 }672 return mlir::success();673 }674 675private:676 // Generates triple for full slice677 // Used for component and complex part slices when a triple is678 // not specified679 static llvm::SmallVector<mlir::Value>680 genFullSliceTriples(fir::FirOpBuilder &builder, mlir::Location loc,681 hlfir::Entity baseEntity) {682 llvm::SmallVector<mlir::Value> triples;683 mlir::Type idxTy = builder.getIndexType();684 auto one = builder.createIntegerConstant(loc, idxTy, 1);685 for (auto [lb, ub] : hlfir::genBounds(loc, builder, baseEntity)) {686 triples.push_back(builder.createConvert(loc, idxTy, lb));687 triples.push_back(builder.createConvert(loc, idxTy, ub));688 triples.push_back(one);689 }690 return triples;691 }692};693 694class ParentComponentOpConversion695 : public mlir::OpRewritePattern<hlfir::ParentComponentOp> {696public:697 explicit ParentComponentOpConversion(mlir::MLIRContext *ctx)698 : OpRewritePattern{ctx} {}699 700 llvm::LogicalResult701 matchAndRewrite(hlfir::ParentComponentOp parentComponent,702 mlir::PatternRewriter &rewriter) const override {703 mlir::Location loc = parentComponent.getLoc();704 mlir::Type resultType = parentComponent.getType();705 if (!mlir::isa<fir::BoxType>(parentComponent.getType())) {706 mlir::Value baseAddr = parentComponent.getMemref();707 // Scalar parent component ref without any length type parameters. The708 // input may be a fir.class if it is polymorphic, since this is a scalar709 // and the output will be monomorphic, the base address can be extracted710 // from the fir.class.711 if (mlir::isa<fir::BaseBoxType>(baseAddr.getType()))712 baseAddr = fir::BoxAddrOp::create(rewriter, loc, baseAddr);713 rewriter.replaceOpWithNewOp<fir::ConvertOp>(parentComponent, resultType,714 baseAddr);715 return mlir::success();716 }717 // Array parent component ref or PDTs.718 hlfir::Entity base{parentComponent.getMemref()};719 mlir::Value baseAddr = base.getBase();720 if (!mlir::isa<fir::BaseBoxType>(baseAddr.getType())) {721 // Embox cannot directly be used to address parent components: it expects722 // the output type to match the input type when there are no slices. When723 // the types have at least one component, a slice to the first element can724 // be built, and the result set to the parent component type. Just create725 // a fir.box with the base for now since this covers all cases.726 mlir::Type baseBoxType =727 fir::BoxType::get(base.getElementOrSequenceType());728 assert(!base.hasLengthParameters() &&729 "base must be a box if it has any type parameters");730 baseAddr = fir::EmboxOp::create(731 rewriter, loc, baseBoxType, baseAddr, parentComponent.getShape(),732 /*slice=*/mlir::Value{}, /*typeParams=*/mlir::ValueRange{});733 }734 rewriter.replaceOpWithNewOp<fir::ReboxOp>(parentComponent, resultType,735 baseAddr,736 /*shape=*/mlir::Value{},737 /*slice=*/mlir::Value{});738 return mlir::success();739 }740};741 742class NoReassocOpConversion743 : public mlir::OpRewritePattern<hlfir::NoReassocOp> {744public:745 explicit NoReassocOpConversion(mlir::MLIRContext *ctx)746 : OpRewritePattern{ctx} {}747 748 llvm::LogicalResult749 matchAndRewrite(hlfir::NoReassocOp noreassoc,750 mlir::PatternRewriter &rewriter) const override {751 rewriter.replaceOpWithNewOp<fir::NoReassocOp>(noreassoc,752 noreassoc.getVal());753 return mlir::success();754 }755};756 757class NullOpConversion : public mlir::OpRewritePattern<hlfir::NullOp> {758public:759 explicit NullOpConversion(mlir::MLIRContext *ctx) : OpRewritePattern{ctx} {}760 761 llvm::LogicalResult762 matchAndRewrite(hlfir::NullOp nullop,763 mlir::PatternRewriter &rewriter) const override {764 rewriter.replaceOpWithNewOp<fir::ZeroOp>(nullop, nullop.getType());765 return mlir::success();766 }767};768 769class GetExtentOpConversion770 : public mlir::OpRewritePattern<hlfir::GetExtentOp> {771public:772 using mlir::OpRewritePattern<hlfir::GetExtentOp>::OpRewritePattern;773 774 llvm::LogicalResult775 matchAndRewrite(hlfir::GetExtentOp getExtentOp,776 mlir::PatternRewriter &rewriter) const override {777 mlir::Value shape = getExtentOp.getShape();778 mlir::Operation *shapeOp = shape.getDefiningOp();779 // the hlfir.shape_of operation which led to the creation of this get_extent780 // operation should now have been lowered to a fir.shape operation781 if (auto s = mlir::dyn_cast_or_null<fir::ShapeOp>(shapeOp)) {782 fir::ShapeType shapeTy = mlir::cast<fir::ShapeType>(shape.getType());783 llvm::APInt dim = getExtentOp.getDim();784 uint64_t dimVal = dim.getLimitedValue(shapeTy.getRank());785 mlir::Value extent = s.getExtents()[dimVal];786 fir::FirOpBuilder builder(rewriter, getExtentOp.getOperation());787 extent = builder.createConvert(getExtentOp.getLoc(),788 builder.getIndexType(), extent);789 rewriter.replaceOp(getExtentOp, extent);790 return mlir::success();791 }792 return mlir::failure();793 }794};795 796class ConvertHLFIRtoFIR797 : public hlfir::impl::ConvertHLFIRtoFIRBase<ConvertHLFIRtoFIR> {798public:799 void runOnOperation() override {800 // TODO: like "bufferize-hlfir" pass, runtime signature may be added801 // by this pass. This requires the pass to run on the ModuleOp. It would802 // probably be more optimal to have it run on FuncOp and find a way to803 // generate the signatures in a thread safe way.804 auto module = this->getOperation();805 auto *context = &getContext();806 mlir::RewritePatternSet patterns(context);807 patterns.insert<AssignOpConversion, CopyInOpConversion, CopyOutOpConversion,808 DeclareOpConversion, DesignateOpConversion,809 GetExtentOpConversion, NoReassocOpConversion,810 NullOpConversion, ParentComponentOpConversion>(context);811 mlir::ConversionTarget target(*context);812 target.addIllegalDialect<hlfir::hlfirDialect>();813 target.markUnknownOpDynamicallyLegal(814 [](mlir::Operation *) { return true; });815 if (mlir::failed(mlir::applyPartialConversion(module, target,816 std::move(patterns)))) {817 mlir::emitError(mlir::UnknownLoc::get(context),818 "failure in HLFIR to FIR conversion pass");819 signalPassFailure();820 }821 }822};823 824} // namespace825