brintos

brintos / llvm-project-archived public Read only

0
0
Text · 25.1 KiB · 27c8bb8 Raw
634 lines · cpp
1//===-- HlfirIntrinsics.cpp -----------------------------------------------===//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//9// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/10//11//===----------------------------------------------------------------------===//12 13#include "flang/Lower/HlfirIntrinsics.h"14 15#include "flang/Optimizer/Builder/BoxValue.h"16#include "flang/Optimizer/Builder/FIRBuilder.h"17#include "flang/Optimizer/Builder/HLFIRTools.h"18#include "flang/Optimizer/Builder/IntrinsicCall.h"19#include "flang/Optimizer/Builder/MutableBox.h"20#include "flang/Optimizer/Builder/Todo.h"21#include "flang/Optimizer/Dialect/FIRType.h"22#include "flang/Optimizer/HLFIR/HLFIRDialect.h"23#include "flang/Optimizer/HLFIR/HLFIROps.h"24#include "mlir/IR/Value.h"25#include "llvm/ADT/SmallVector.h"26#include <mlir/IR/ValueRange.h>27 28namespace {29 30class HlfirTransformationalIntrinsic {31public:32  explicit HlfirTransformationalIntrinsic(fir::FirOpBuilder &builder,33                                          mlir::Location loc)34      : builder(builder), loc(loc) {}35 36  virtual ~HlfirTransformationalIntrinsic() = default;37 38  hlfir::EntityWithAttributes39  lower(const Fortran::lower::PreparedActualArguments &loweredActuals,40        const fir::IntrinsicArgumentLoweringRules *argLowering,41        mlir::Type stmtResultType) {42    mlir::Value res = lowerImpl(loweredActuals, argLowering, stmtResultType);43    for (const hlfir::CleanupFunction &fn : cleanupFns)44      fn();45    return {hlfir::EntityWithAttributes{res}};46  }47 48protected:49  fir::FirOpBuilder &builder;50  mlir::Location loc;51  llvm::SmallVector<hlfir::CleanupFunction, 3> cleanupFns;52 53  virtual mlir::Value54  lowerImpl(const Fortran::lower::PreparedActualArguments &loweredActuals,55            const fir::IntrinsicArgumentLoweringRules *argLowering,56            mlir::Type stmtResultType) = 0;57 58  llvm::SmallVector<mlir::Value> getOperandVector(59      const Fortran::lower::PreparedActualArguments &loweredActuals,60      const fir::IntrinsicArgumentLoweringRules *argLowering);61 62  mlir::Type computeResultType(mlir::Value argArray, mlir::Type stmtResultType);63 64  template <typename OP, typename... BUILD_ARGS>65  inline OP createOp(BUILD_ARGS... args) {66    return OP::create(builder, loc, args...);67  }68 69  mlir::Value loadBoxAddress(70      const std::optional<Fortran::lower::PreparedActualArgument> &arg);71 72  mlir::Value73  loadTrivialScalar(const Fortran::lower::PreparedActualArgument &arg);74 75  mlir::Value loadOptionalValue(Fortran::lower::PreparedActualArgument &arg);76 77  void addCleanup(std::optional<hlfir::CleanupFunction> cleanup) {78    if (cleanup)79      cleanupFns.emplace_back(std::move(*cleanup));80  }81};82 83template <typename OP, bool HAS_MASK>84class HlfirReductionIntrinsic : public HlfirTransformationalIntrinsic {85public:86  using HlfirTransformationalIntrinsic::HlfirTransformationalIntrinsic;87 88protected:89  mlir::Value90  lowerImpl(const Fortran::lower::PreparedActualArguments &loweredActuals,91            const fir::IntrinsicArgumentLoweringRules *argLowering,92            mlir::Type stmtResultType) override;93};94using HlfirSumLowering = HlfirReductionIntrinsic<hlfir::SumOp, true>;95using HlfirProductLowering = HlfirReductionIntrinsic<hlfir::ProductOp, true>;96using HlfirMaxvalLowering = HlfirReductionIntrinsic<hlfir::MaxvalOp, true>;97using HlfirMinvalLowering = HlfirReductionIntrinsic<hlfir::MinvalOp, true>;98using HlfirAnyLowering = HlfirReductionIntrinsic<hlfir::AnyOp, false>;99using HlfirAllLowering = HlfirReductionIntrinsic<hlfir::AllOp, false>;100 101template <typename OP>102class HlfirMinMaxLocIntrinsic : public HlfirTransformationalIntrinsic {103public:104  using HlfirTransformationalIntrinsic::HlfirTransformationalIntrinsic;105 106protected:107  mlir::Value108  lowerImpl(const Fortran::lower::PreparedActualArguments &loweredActuals,109            const fir::IntrinsicArgumentLoweringRules *argLowering,110            mlir::Type stmtResultType) override;111};112using HlfirMinlocLowering = HlfirMinMaxLocIntrinsic<hlfir::MinlocOp>;113using HlfirMaxlocLowering = HlfirMinMaxLocIntrinsic<hlfir::MaxlocOp>;114 115template <typename OP>116class HlfirProductIntrinsic : public HlfirTransformationalIntrinsic {117public:118  using HlfirTransformationalIntrinsic::HlfirTransformationalIntrinsic;119 120protected:121  mlir::Value122  lowerImpl(const Fortran::lower::PreparedActualArguments &loweredActuals,123            const fir::IntrinsicArgumentLoweringRules *argLowering,124            mlir::Type stmtResultType) override;125};126using HlfirMatmulLowering = HlfirProductIntrinsic<hlfir::MatmulOp>;127using HlfirDotProductLowering = HlfirProductIntrinsic<hlfir::DotProductOp>;128 129class HlfirTransposeLowering : public HlfirTransformationalIntrinsic {130public:131  using HlfirTransformationalIntrinsic::HlfirTransformationalIntrinsic;132 133protected:134  mlir::Value135  lowerImpl(const Fortran::lower::PreparedActualArguments &loweredActuals,136            const fir::IntrinsicArgumentLoweringRules *argLowering,137            mlir::Type stmtResultType) override;138};139 140class HlfirCountLowering : public HlfirTransformationalIntrinsic {141public:142  using HlfirTransformationalIntrinsic::HlfirTransformationalIntrinsic;143 144protected:145  mlir::Value146  lowerImpl(const Fortran::lower::PreparedActualArguments &loweredActuals,147            const fir::IntrinsicArgumentLoweringRules *argLowering,148            mlir::Type stmtResultType) override;149};150 151class HlfirCharExtremumLowering : public HlfirTransformationalIntrinsic {152public:153  HlfirCharExtremumLowering(fir::FirOpBuilder &builder, mlir::Location loc,154                            hlfir::CharExtremumPredicate pred)155      : HlfirTransformationalIntrinsic(builder, loc), pred{pred} {}156 157protected:158  mlir::Value159  lowerImpl(const Fortran::lower::PreparedActualArguments &loweredActuals,160            const fir::IntrinsicArgumentLoweringRules *argLowering,161            mlir::Type stmtResultType) override;162 163protected:164  hlfir::CharExtremumPredicate pred;165};166 167class HlfirCharTrimLowering : public HlfirTransformationalIntrinsic {168public:169  HlfirCharTrimLowering(fir::FirOpBuilder &builder, mlir::Location loc)170      : HlfirTransformationalIntrinsic(builder, loc) {}171 172protected:173  mlir::Value174  lowerImpl(const Fortran::lower::PreparedActualArguments &loweredActuals,175            const fir::IntrinsicArgumentLoweringRules *argLowering,176            mlir::Type stmtResultType) override;177};178 179class HlfirCShiftLowering : public HlfirTransformationalIntrinsic {180public:181  using HlfirTransformationalIntrinsic::HlfirTransformationalIntrinsic;182 183protected:184  mlir::Value185  lowerImpl(const Fortran::lower::PreparedActualArguments &loweredActuals,186            const fir::IntrinsicArgumentLoweringRules *argLowering,187            mlir::Type stmtResultType) override;188};189 190class HlfirEOShiftLowering : public HlfirTransformationalIntrinsic {191public:192  using HlfirTransformationalIntrinsic::HlfirTransformationalIntrinsic;193 194protected:195  mlir::Value196  lowerImpl(const Fortran::lower::PreparedActualArguments &loweredActuals,197            const fir::IntrinsicArgumentLoweringRules *argLowering,198            mlir::Type stmtResultType) override;199};200 201class HlfirReshapeLowering : public HlfirTransformationalIntrinsic {202public:203  using HlfirTransformationalIntrinsic::HlfirTransformationalIntrinsic;204 205protected:206  mlir::Value207  lowerImpl(const Fortran::lower::PreparedActualArguments &loweredActuals,208            const fir::IntrinsicArgumentLoweringRules *argLowering,209            mlir::Type stmtResultType) override;210};211 212class HlfirIndexLowering : public HlfirTransformationalIntrinsic {213public:214  using HlfirTransformationalIntrinsic::HlfirTransformationalIntrinsic;215 216protected:217  mlir::Value218  lowerImpl(const Fortran::lower::PreparedActualArguments &loweredActuals,219            const fir::IntrinsicArgumentLoweringRules *argLowering,220            mlir::Type stmtResultType) override;221};222 223} // namespace224 225mlir::Value HlfirTransformationalIntrinsic::loadBoxAddress(226    const std::optional<Fortran::lower::PreparedActualArgument> &arg) {227  if (!arg)228    return mlir::Value{};229 230  hlfir::Entity actual = arg->getActual(loc, builder);231 232  if (!arg->handleDynamicOptional()) {233    if (actual.isMutableBox()) {234      // this is a box address type but is not dynamically optional. Just load235      // the box, assuming it is well formed (!fir.ref<!fir.box<...>> ->236      // !fir.box<...>)237      return fir::LoadOp::create(builder, loc, actual.getBase());238    }239    return actual;240  }241 242  auto [exv, cleanup] = hlfir::translateToExtendedValue(loc, builder, actual);243  addCleanup(cleanup);244 245  mlir::Value isPresent = arg->getIsPresent();246  // createBox will not do create any invalid memory dereferences if exv is247  // absent. The created fir.box will not be usable, but the SelectOp below248  // ensures it won't be.249  mlir::Value box = builder.createBox(loc, exv);250  mlir::Type boxType = box.getType();251  auto absent = fir::AbsentOp::create(builder, loc, boxType);252  auto boxOrAbsent = mlir::arith::SelectOp::create(builder, loc, boxType,253                                                   isPresent, box, absent);254 255  return boxOrAbsent;256}257 258mlir::Value HlfirTransformationalIntrinsic::loadOptionalValue(259    Fortran::lower::PreparedActualArgument &arg) {260  mlir::Type eleType = arg.getFortranElementType();261 262  // For an elemental call, getActual() may produce263  // a designator denoting the array element to be passed264  // to the subprogram. If the actual array is dynamically265  // optional the designator must be generated under266  // isPresent check (see also genIntrinsicRefCore).267  return builder268      .genIfOp(loc, {eleType}, arg.getIsPresent(),269               /*withElseRegion=*/true)270      .genThen([&]() {271        hlfir::Entity actual = arg.getActual(loc, builder);272        assert(eleType == actual.getFortranElementType() &&273               "result type mismatch in genOptionalValue");274        assert(actual.isScalar() && fir::isa_trivial(eleType) &&275               "must be a numerical or logical scalar");276        hlfir::Entity val = hlfir::loadTrivialScalar(loc, builder, actual);277        fir::ResultOp::create(builder, loc, val);278      })279      .genElse([&]() {280        mlir::Value zero = fir::factory::createZeroValue(builder, loc, eleType);281        fir::ResultOp::create(builder, loc, zero);282      })283      .getResults()[0];284}285 286mlir::Value HlfirTransformationalIntrinsic::loadTrivialScalar(287    const Fortran::lower::PreparedActualArgument &arg) {288  hlfir::Entity actual = arg.getActual(loc, builder);289  return hlfir::loadTrivialScalar(loc, builder, actual);290}291 292llvm::SmallVector<mlir::Value> HlfirTransformationalIntrinsic::getOperandVector(293    const Fortran::lower::PreparedActualArguments &loweredActuals,294    const fir::IntrinsicArgumentLoweringRules *argLowering) {295  llvm::SmallVector<mlir::Value> operands;296  operands.reserve(loweredActuals.size());297 298  for (size_t i = 0; i < loweredActuals.size(); ++i) {299    std::optional<Fortran::lower::PreparedActualArgument> arg =300        loweredActuals[i];301    if (!arg) {302      operands.emplace_back();303      continue;304    }305    mlir::Value valArg;306    if (!argLowering) {307      valArg = loadTrivialScalar(*arg);308      operands.emplace_back(valArg);309      continue;310    }311    fir::ArgLoweringRule argRules =312        fir::lowerIntrinsicArgumentAs(*argLowering, i);313    if (argRules.lowerAs == fir::LowerIntrinsicArgAs::Box) {314      valArg = loadBoxAddress(arg);315    } else if (argRules.handleDynamicOptional) {316      if (argRules.lowerAs == fir::LowerIntrinsicArgAs::Value) {317        if (arg->handleDynamicOptional())318          valArg = loadOptionalValue(*arg);319        else320          valArg = loadTrivialScalar(*arg);321      } else {322        TODO(loc, "hlfir transformational intrinsic dynamically optional "323                  "argument without box lowering");324      }325    } else {326      hlfir::Entity actual = arg->getActual(loc, builder);327      if (argRules.lowerAs != fir::LowerIntrinsicArgAs::Inquired)328        valArg = hlfir::derefPointersAndAllocatables(loc, builder, actual);329      else330        valArg = actual.getBase();331    }332    operands.emplace_back(valArg);333  }334  return operands;335}336 337mlir::Type338HlfirTransformationalIntrinsic::computeResultType(mlir::Value argArray,339                                                  mlir::Type stmtResultType) {340  mlir::Type normalisedResult =341      hlfir::getFortranElementOrSequenceType(stmtResultType);342  if (auto array = mlir::dyn_cast<fir::SequenceType>(normalisedResult)) {343    hlfir::ExprType::Shape resultShape =344        hlfir::ExprType::Shape{array.getShape()};345    mlir::Type elementType = array.getEleTy();346    return hlfir::ExprType::get(builder.getContext(), resultShape, elementType,347                                fir::isPolymorphicType(stmtResultType));348  } else if (auto resCharType =349                 mlir::dyn_cast<fir::CharacterType>(stmtResultType)) {350    normalisedResult = hlfir::ExprType::get(351        builder.getContext(), hlfir::ExprType::Shape{}, resCharType,352        /*polymorphic=*/false);353  }354  return normalisedResult;355}356 357template <typename OP, bool HAS_MASK>358mlir::Value HlfirReductionIntrinsic<OP, HAS_MASK>::lowerImpl(359    const Fortran::lower::PreparedActualArguments &loweredActuals,360    const fir::IntrinsicArgumentLoweringRules *argLowering,361    mlir::Type stmtResultType) {362  auto operands = getOperandVector(loweredActuals, argLowering);363  mlir::Value array = operands[0];364  mlir::Value dim = operands[1];365  // dim, mask can be NULL if these arguments are not given366  if (dim)367    dim = hlfir::loadTrivialScalar(loc, builder, hlfir::Entity{dim});368 369  mlir::Type resultTy = computeResultType(array, stmtResultType);370 371  OP op;372  if constexpr (HAS_MASK)373    op = createOp<OP>(resultTy, array, dim,374                      /*mask=*/operands[2]);375  else376    op = createOp<OP>(resultTy, array, dim);377  return op;378}379 380template <typename OP>381mlir::Value HlfirMinMaxLocIntrinsic<OP>::lowerImpl(382    const Fortran::lower::PreparedActualArguments &loweredActuals,383    const fir::IntrinsicArgumentLoweringRules *argLowering,384    mlir::Type stmtResultType) {385  auto operands = getOperandVector(loweredActuals, argLowering);386  mlir::Value array = operands[0];387  mlir::Value dim = operands[1];388  mlir::Value mask = operands[2];389  mlir::Value back = operands[4];390  // dim, mask and back can be NULL if these arguments are not given.391  if (dim)392    dim = hlfir::loadTrivialScalar(loc, builder, hlfir::Entity{dim});393  if (back)394    back = hlfir::loadTrivialScalar(loc, builder, hlfir::Entity{back});395 396  mlir::Type resultTy = computeResultType(array, stmtResultType);397 398  return createOp<OP>(resultTy, array, dim, mask, back);399}400 401template <typename OP>402mlir::Value HlfirProductIntrinsic<OP>::lowerImpl(403    const Fortran::lower::PreparedActualArguments &loweredActuals,404    const fir::IntrinsicArgumentLoweringRules *argLowering,405    mlir::Type stmtResultType) {406  auto operands = getOperandVector(loweredActuals, argLowering);407  mlir::Type resultType = computeResultType(operands[0], stmtResultType);408  return createOp<OP>(resultType, operands[0], operands[1]);409}410 411mlir::Value HlfirTransposeLowering::lowerImpl(412    const Fortran::lower::PreparedActualArguments &loweredActuals,413    const fir::IntrinsicArgumentLoweringRules *argLowering,414    mlir::Type stmtResultType) {415  auto operands = getOperandVector(loweredActuals, argLowering);416  hlfir::ExprType::Shape resultShape;417  mlir::Type normalisedResult =418      hlfir::getFortranElementOrSequenceType(stmtResultType);419  auto array = mlir::cast<fir::SequenceType>(normalisedResult);420  llvm::ArrayRef<int64_t> arrayShape = array.getShape();421  assert(arrayShape.size() == 2 && "arguments to transpose have a rank of 2");422  mlir::Type elementType = array.getEleTy();423  resultShape.push_back(arrayShape[0]);424  resultShape.push_back(arrayShape[1]);425  if (auto resCharType = mlir::dyn_cast<fir::CharacterType>(elementType))426    if (!resCharType.hasConstantLen()) {427      // The FunctionRef expression might have imprecise character428      // type at this point, and we can improve it by propagating429      // the constant length from the argument.430      auto argCharType = mlir::dyn_cast<fir::CharacterType>(431          hlfir::getFortranElementType(operands[0].getType()));432      if (argCharType && argCharType.hasConstantLen())433        elementType = fir::CharacterType::get(434            builder.getContext(), resCharType.getFKind(), argCharType.getLen());435    }436 437  mlir::Type resultTy =438      hlfir::ExprType::get(builder.getContext(), resultShape, elementType,439                           fir::isPolymorphicType(stmtResultType));440  return createOp<hlfir::TransposeOp>(resultTy, operands[0]);441}442 443mlir::Value HlfirCountLowering::lowerImpl(444    const Fortran::lower::PreparedActualArguments &loweredActuals,445    const fir::IntrinsicArgumentLoweringRules *argLowering,446    mlir::Type stmtResultType) {447  auto operands = getOperandVector(loweredActuals, argLowering);448  mlir::Value array = operands[0];449  mlir::Value dim = operands[1];450  if (dim)451    dim = hlfir::loadTrivialScalar(loc, builder, hlfir::Entity{dim});452  mlir::Type resultType = computeResultType(array, stmtResultType);453  return createOp<hlfir::CountOp>(resultType, array, dim);454}455 456mlir::Value HlfirCharExtremumLowering::lowerImpl(457    const Fortran::lower::PreparedActualArguments &loweredActuals,458    const fir::IntrinsicArgumentLoweringRules *argLowering,459    mlir::Type stmtResultType) {460  auto operands = getOperandVector(loweredActuals, argLowering);461  assert(operands.size() >= 2);462  return createOp<hlfir::CharExtremumOp>(pred, mlir::ValueRange{operands});463}464 465mlir::Value HlfirCharTrimLowering::lowerImpl(466    const Fortran::lower::PreparedActualArguments &loweredActuals,467    const fir::IntrinsicArgumentLoweringRules *argLowering,468    mlir::Type stmtResultType) {469  auto operands = getOperandVector(loweredActuals, argLowering);470  assert(operands.size() == 1);471  return createOp<hlfir::CharTrimOp>(operands[0]);472}473 474mlir::Value HlfirCShiftLowering::lowerImpl(475    const Fortran::lower::PreparedActualArguments &loweredActuals,476    const fir::IntrinsicArgumentLoweringRules *argLowering,477    mlir::Type stmtResultType) {478  auto operands = getOperandVector(loweredActuals, argLowering);479  assert(operands.size() == 3);480  mlir::Value dim = operands[2];481  if (!dim) {482    // If DIM is not present, drop the last element which is a null Value.483    operands.truncate(2);484  } else {485    // If DIM is present, then dereference it if it is a ref.486    dim = hlfir::loadTrivialScalar(loc, builder, hlfir::Entity{dim});487    operands[2] = dim;488  }489 490  mlir::Type resultType = computeResultType(operands[0], stmtResultType);491  return createOp<hlfir::CShiftOp>(resultType, operands);492}493 494mlir::Value HlfirEOShiftLowering::lowerImpl(495    const Fortran::lower::PreparedActualArguments &loweredActuals,496    const fir::IntrinsicArgumentLoweringRules *argLowering,497    mlir::Type stmtResultType) {498  auto operands = getOperandVector(loweredActuals, argLowering);499  assert(operands.size() == 4);500  mlir::Value array = operands[0];501  mlir::Value shift = operands[1];502  mlir::Value boundary = operands[2];503  mlir::Value dim = operands[3];504  // If DIM is present, then dereference it if it is a ref.505  if (dim)506    dim = hlfir::loadTrivialScalar(loc, builder, hlfir::Entity{dim});507 508  mlir::Type resultType = computeResultType(array, stmtResultType);509 510  if (boundary && fir::isa_trivial(boundary.getType())) {511    mlir::Type elementType = hlfir::getFortranElementType(resultType);512    if (auto logicalTy = mlir::dyn_cast<fir::LogicalType>(elementType)) {513      // Scalar logical constant boundary might be represented using i1, i2, ...514      // type. We need to cast it to fir.logical type of the ARRAY/result.515      if (boundary.getType() != logicalTy)516        boundary = builder.createConvert(loc, logicalTy, boundary);517    } else {518      // When the boundary is a constant like '1u', the lowering converts519      // it into a signless arith.constant value (which is a requirement520      // of the Arith dialect). If the ARRAY/RESULT is also UNSIGNED,521      // we have to cast the boundary to the same unsigned type.522      auto resultIntTy = mlir::dyn_cast<mlir::IntegerType>(elementType);523      auto boundaryIntTy =524          mlir::dyn_cast<mlir::IntegerType>(boundary.getType());525      if (resultIntTy && boundaryIntTy &&526          resultIntTy.getSignedness() != boundaryIntTy.getSignedness())527        boundary = builder.createConvert(loc, resultIntTy, boundary);528    }529  }530 531  return createOp<hlfir::EOShiftOp>(resultType, array, shift, boundary, dim);532}533 534mlir::Value HlfirReshapeLowering::lowerImpl(535    const Fortran::lower::PreparedActualArguments &loweredActuals,536    const fir::IntrinsicArgumentLoweringRules *argLowering,537    mlir::Type stmtResultType) {538  auto operands = getOperandVector(loweredActuals, argLowering);539  assert(operands.size() == 4);540  mlir::Type resultType = computeResultType(operands[0], stmtResultType);541  return createOp<hlfir::ReshapeOp>(resultType, operands[0], operands[1],542                                    operands[2], operands[3]);543}544 545mlir::Value HlfirIndexLowering::lowerImpl(546    const Fortran::lower::PreparedActualArguments &loweredActuals,547    const fir::IntrinsicArgumentLoweringRules *argLowering,548    mlir::Type stmtResultType) {549  auto operands = getOperandVector(loweredActuals, argLowering);550  // 'kind' optional operand is unused here as it has already been551  // translated into result type.552  assert(operands.size() == 4);553  mlir::Value substr = operands[1];554  mlir::Value str = operands[0];555  mlir::Value back = operands[2];556  mlir::Value result =557      createOp<hlfir::IndexOp>(stmtResultType, substr, str, back);558  return result;559}560 561std::optional<hlfir::EntityWithAttributes> Fortran::lower::lowerHlfirIntrinsic(562    fir::FirOpBuilder &builder, mlir::Location loc, const std::string &name,563    const Fortran::lower::PreparedActualArguments &loweredActuals,564    const fir::IntrinsicArgumentLoweringRules *argLowering,565    mlir::Type stmtResultType) {566  // If the result is of a derived type that may need finalization,567  // we have to use DestroyOp with 'finalize' attribute for the result568  // of the intrinsic operation.569  if (name == "sum")570    return HlfirSumLowering{builder, loc}.lower(loweredActuals, argLowering,571                                                stmtResultType);572  if (name == "product")573    return HlfirProductLowering{builder, loc}.lower(loweredActuals, argLowering,574                                                    stmtResultType);575  if (name == "any")576    return HlfirAnyLowering{builder, loc}.lower(loweredActuals, argLowering,577                                                stmtResultType);578  if (name == "all")579    return HlfirAllLowering{builder, loc}.lower(loweredActuals, argLowering,580                                                stmtResultType);581  if (name == "matmul")582    return HlfirMatmulLowering{builder, loc}.lower(loweredActuals, argLowering,583                                                   stmtResultType);584  if (name == "dot_product")585    return HlfirDotProductLowering{builder, loc}.lower(586        loweredActuals, argLowering, stmtResultType);587  // FIXME: the result may need finalization.588  if (name == "transpose")589    return HlfirTransposeLowering{builder, loc}.lower(590        loweredActuals, argLowering, stmtResultType);591  if (name == "count")592    return HlfirCountLowering{builder, loc}.lower(loweredActuals, argLowering,593                                                  stmtResultType);594  if (name == "maxval")595    return HlfirMaxvalLowering{builder, loc}.lower(loweredActuals, argLowering,596                                                   stmtResultType);597  if (name == "minval")598    return HlfirMinvalLowering{builder, loc}.lower(loweredActuals, argLowering,599                                                   stmtResultType);600  if (name == "minloc")601    return HlfirMinlocLowering{builder, loc}.lower(loweredActuals, argLowering,602                                                   stmtResultType);603  if (name == "maxloc")604    return HlfirMaxlocLowering{builder, loc}.lower(loweredActuals, argLowering,605                                                   stmtResultType);606  if (name == "cshift")607    return HlfirCShiftLowering{builder, loc}.lower(loweredActuals, argLowering,608                                                   stmtResultType);609  if (name == "eoshift")610    return HlfirEOShiftLowering{builder, loc}.lower(loweredActuals, argLowering,611                                                    stmtResultType);612  if (name == "reshape")613    return HlfirReshapeLowering{builder, loc}.lower(loweredActuals, argLowering,614                                                    stmtResultType);615  if (name == "index")616    return HlfirIndexLowering{builder, loc}.lower(loweredActuals, argLowering,617                                                  stmtResultType);618 619  if (mlir::isa<fir::CharacterType>(stmtResultType)) {620    if (name == "min")621      return HlfirCharExtremumLowering{builder, loc,622                                       hlfir::CharExtremumPredicate::min}623          .lower(loweredActuals, argLowering, stmtResultType);624    if (name == "max")625      return HlfirCharExtremumLowering{builder, loc,626                                       hlfir::CharExtremumPredicate::max}627          .lower(loweredActuals, argLowering, stmtResultType);628    if (name == "trim")629      return HlfirCharTrimLowering{builder, loc}.lower(630          loweredActuals, argLowering, stmtResultType);631  }632  return std::nullopt;633}634