brintos

brintos / llvm-project-archived public Read only

0
0
Text · 37.2 KiB · 8bdf13e Raw
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