brintos

brintos / llvm-project-archived public Read only

0
0
Text · 18.1 KiB · 61d6d2a Raw
419 lines · cpp
1//===-- BoxedProcedure.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#include "flang/Optimizer/CodeGen/CodeGen.h"10 11#include "flang/Optimizer/Builder/FIRBuilder.h"12#include "flang/Optimizer/Builder/LowLevelIntrinsics.h"13#include "flang/Optimizer/Dialect/FIRDialect.h"14#include "flang/Optimizer/Dialect/FIROps.h"15#include "flang/Optimizer/Dialect/FIRType.h"16#include "flang/Optimizer/Dialect/Support/FIRContext.h"17#include "flang/Optimizer/Support/FatalError.h"18#include "flang/Optimizer/Support/InternalNames.h"19#include "mlir/IR/PatternMatch.h"20#include "mlir/Pass/Pass.h"21#include "mlir/Transforms/DialectConversion.h"22#include "llvm/ADT/DenseMap.h"23 24namespace fir {25#define GEN_PASS_DEF_BOXEDPROCEDUREPASS26#include "flang/Optimizer/CodeGen/CGPasses.h.inc"27} // namespace fir28 29#define DEBUG_TYPE "flang-procedure-pointer"30 31using namespace fir;32 33namespace {34/// Options to the procedure pointer pass.35struct BoxedProcedureOptions {36  // Lower the boxproc abstraction to function pointers and thunks where37  // required.38  bool useThunks = true;39};40 41/// This type converter rewrites all `!fir.boxproc<Func>` types to `Func` types.42class BoxprocTypeRewriter : public mlir::TypeConverter {43public:44  using mlir::TypeConverter::convertType;45 46  /// Does the type \p ty need to be converted?47  /// Any type that is a `!fir.boxproc` in whole or in part will need to be48  /// converted to a function type to lower the IR to function pointer form in49  /// the default implementation performed in this pass. Other implementations50  /// are possible, so those may convert `!fir.boxproc` to some other type or51  /// not at all depending on the implementation target's characteristics and52  /// preference.53  bool needsConversion(mlir::Type ty) {54    if (mlir::isa<BoxProcType>(ty))55      return true;56    if (auto funcTy = mlir::dyn_cast<mlir::FunctionType>(ty)) {57      for (auto t : funcTy.getInputs())58        if (needsConversion(t))59          return true;60      for (auto t : funcTy.getResults())61        if (needsConversion(t))62          return true;63      return false;64    }65    if (auto tupleTy = mlir::dyn_cast<mlir::TupleType>(ty)) {66      for (auto t : tupleTy.getTypes())67        if (needsConversion(t))68          return true;69      return false;70    }71    if (auto recTy = mlir::dyn_cast<RecordType>(ty)) {72      auto [visited, inserted] = visitedTypes.try_emplace(ty, false);73      if (!inserted)74        return visited->second;75      bool wasAlreadyVisitingRecordType = needConversionIsVisitingRecordType;76      needConversionIsVisitingRecordType = true;77      bool result = false;78      for (auto t : recTy.getTypeList()) {79        if (needsConversion(t.second)) {80          result = true;81          break;82        }83      }84      // Only keep the result cached if the fir.type visited was a "top-level85      // type". Nested types with a recursive reference to the "top-level type"86      // may incorrectly have been resolved as not needed conversions because it87      // had not been determined yet if the "top-level type" needed conversion.88      // This is not an issue to determine the "top-level type" need of89      // conversion, but the result should not be kept and later used in other90      // contexts.91      needConversionIsVisitingRecordType = wasAlreadyVisitingRecordType;92      if (needConversionIsVisitingRecordType)93        visitedTypes.erase(ty);94      else95        visitedTypes.find(ty)->second = result;96      return result;97    }98    if (auto boxTy = mlir::dyn_cast<BaseBoxType>(ty))99      return needsConversion(boxTy.getEleTy());100    if (isa_ref_type(ty))101      return needsConversion(unwrapRefType(ty));102    if (auto t = mlir::dyn_cast<SequenceType>(ty))103      return needsConversion(unwrapSequenceType(ty));104    if (auto t = mlir::dyn_cast<TypeDescType>(ty))105      return needsConversion(t.getOfTy());106    return false;107  }108 109  BoxprocTypeRewriter(mlir::Location location) : loc{location} {110    addConversion([](mlir::Type ty) { return ty; });111    addConversion(112        [&](BoxProcType boxproc) { return convertType(boxproc.getEleTy()); });113    addConversion([&](mlir::TupleType tupTy) {114      llvm::SmallVector<mlir::Type> memTys;115      for (auto ty : tupTy.getTypes())116        memTys.push_back(convertType(ty));117      return mlir::TupleType::get(tupTy.getContext(), memTys);118    });119    addConversion([&](mlir::FunctionType funcTy) {120      llvm::SmallVector<mlir::Type> inTys;121      llvm::SmallVector<mlir::Type> resTys;122      for (auto ty : funcTy.getInputs())123        inTys.push_back(convertType(ty));124      for (auto ty : funcTy.getResults())125        resTys.push_back(convertType(ty));126      return mlir::FunctionType::get(funcTy.getContext(), inTys, resTys);127    });128    addConversion([&](ReferenceType ty) {129      return ReferenceType::get(convertType(ty.getEleTy()));130    });131    addConversion([&](PointerType ty) {132      return PointerType::get(convertType(ty.getEleTy()));133    });134    addConversion(135        [&](HeapType ty) { return HeapType::get(convertType(ty.getEleTy())); });136    addConversion([&](fir::LLVMPointerType ty) {137      return fir::LLVMPointerType::get(convertType(ty.getEleTy()));138    });139    addConversion(140        [&](BoxType ty) { return BoxType::get(convertType(ty.getEleTy())); });141    addConversion([&](ClassType ty) {142      return ClassType::get(convertType(ty.getEleTy()));143    });144    addConversion([&](SequenceType ty) {145      // TODO: add ty.getLayoutMap() as needed.146      return SequenceType::get(ty.getShape(), convertType(ty.getEleTy()));147    });148    addConversion([&](RecordType ty) -> mlir::Type {149      if (!needsConversion(ty))150        return ty;151      if (auto converted = convertedTypes.lookup(ty))152        return converted;153      auto rec = RecordType::get(ty.getContext(),154                                 ty.getName().str() + boxprocSuffix.str());155      if (rec.isFinalized())156        return rec;157      [[maybe_unused]] auto it = convertedTypes.try_emplace(ty, rec);158      assert(it.second && "expected ty to not be in the map");159      std::vector<RecordType::TypePair> ps = ty.getLenParamList();160      std::vector<RecordType::TypePair> cs;161      for (auto t : ty.getTypeList()) {162        if (needsConversion(t.second))163          cs.emplace_back(t.first, convertType(t.second));164        else165          cs.emplace_back(t.first, t.second);166      }167      rec.finalize(ps, cs);168      rec.pack(ty.isPacked());169      return rec;170    });171    addConversion([&](TypeDescType ty) {172      return TypeDescType::get(convertType(ty.getOfTy()));173    });174    addSourceMaterialization(materializeProcedure);175    addTargetMaterialization(materializeProcedure);176  }177 178  static mlir::Value materializeProcedure(mlir::OpBuilder &builder,179                                          BoxProcType type,180                                          mlir::ValueRange inputs,181                                          mlir::Location loc) {182    assert(inputs.size() == 1);183    return ConvertOp::create(builder, loc, unwrapRefType(type.getEleTy()),184                             inputs[0]);185  }186 187  void setLocation(mlir::Location location) { loc = location; }188 189private:190  // Maps to deal with recursive derived types (avoid infinite loops).191  // Caching is also beneficial for apps with big types (dozens of192  // components and or parent types), so the lifetime of the cache193  // is the whole pass.194  llvm::DenseMap<mlir::Type, bool> visitedTypes;195  bool needConversionIsVisitingRecordType = false;196  llvm::DenseMap<mlir::Type, mlir::Type> convertedTypes;197  mlir::Location loc;198};199 200/// A `boxproc` is an abstraction for a Fortran procedure reference. Typically,201/// Fortran procedures can be referenced directly through a function pointer.202/// However, Fortran has one-level dynamic scoping between a host procedure and203/// its internal procedures. This allows internal procedures to directly access204/// and modify the state of the host procedure's variables.205///206/// There are any number of possible implementations possible.207///208/// The implementation used here is to convert `boxproc` values to function209/// pointers everywhere. If a `boxproc` value includes a frame pointer to the210/// host procedure's data, then a thunk will be created at runtime to capture211/// the frame pointer during execution. In LLVM IR, the frame pointer is212/// designated with the `nest` attribute. The thunk's address will then be used213/// as the call target instead of the original function's address directly.214class BoxedProcedurePass215    : public fir::impl::BoxedProcedurePassBase<BoxedProcedurePass> {216public:217  using BoxedProcedurePassBase<BoxedProcedurePass>::BoxedProcedurePassBase;218 219  inline mlir::ModuleOp getModule() { return getOperation(); }220 221  void runOnOperation() override final {222    if (options.useThunks) {223      auto *context = &getContext();224      mlir::IRRewriter rewriter(context);225      BoxprocTypeRewriter typeConverter(mlir::UnknownLoc::get(context));226      getModule().walk([&](mlir::Operation *op) {227        bool opIsValid = true;228        typeConverter.setLocation(op->getLoc());229        if (auto addr = mlir::dyn_cast<BoxAddrOp>(op)) {230          mlir::Type ty = addr.getVal().getType();231          mlir::Type resTy = addr.getResult().getType();232          if (llvm::isa<mlir::FunctionType>(ty) ||233              llvm::isa<fir::BoxProcType>(ty)) {234            // Rewrite all `fir.box_addr` ops on values of type `!fir.boxproc`235            // or function type to be `fir.convert` ops.236            rewriter.setInsertionPoint(addr);237            rewriter.replaceOpWithNewOp<ConvertOp>(238                addr, typeConverter.convertType(addr.getType()), addr.getVal());239            opIsValid = false;240          } else if (typeConverter.needsConversion(resTy)) {241            rewriter.startOpModification(op);242            op->getResult(0).setType(typeConverter.convertType(resTy));243            rewriter.finalizeOpModification(op);244          }245        } else if (auto func = mlir::dyn_cast<mlir::func::FuncOp>(op)) {246          mlir::FunctionType ty = func.getFunctionType();247          if (typeConverter.needsConversion(ty)) {248            rewriter.startOpModification(func);249            auto toTy =250                mlir::cast<mlir::FunctionType>(typeConverter.convertType(ty));251            if (!func.empty())252              for (auto e : llvm::enumerate(toTy.getInputs())) {253                unsigned i = e.index();254                auto &block = func.front();255                block.insertArgument(i, e.value(), func.getLoc());256                block.getArgument(i + 1).replaceAllUsesWith(257                    block.getArgument(i));258                block.eraseArgument(i + 1);259              }260            func.setType(toTy);261            rewriter.finalizeOpModification(func);262          }263        } else if (auto embox = mlir::dyn_cast<EmboxProcOp>(op)) {264          // Rewrite all `fir.emboxproc` ops to either `fir.convert` or a thunk265          // as required.266          mlir::Type toTy = typeConverter.convertType(267              mlir::cast<BoxProcType>(embox.getType()).getEleTy());268          rewriter.setInsertionPoint(embox);269          if (embox.getHost()) {270            // Create the thunk.271            auto module = embox->getParentOfType<mlir::ModuleOp>();272            FirOpBuilder builder(rewriter, module);273            const auto triple{fir::getTargetTriple(module)};274            auto loc = embox.getLoc();275            mlir::Type i8Ty = builder.getI8Type();276            mlir::Type i8Ptr = builder.getRefType(i8Ty);277            // For PPC32 and PPC64, the thunk is populated by a call to278            // __trampoline_setup, which is defined in279            // compiler-rt/lib/builtins/trampoline_setup.c and requires the280            // thunk size greater than 32 bytes.  For AArch64, RISCV and x86_64,281            // the thunk setup doesn't go through __trampoline_setup and fits in282            // 32 bytes.283            fir::SequenceType::Extent thunkSize = triple.getTrampolineSize();284            mlir::Type buffTy = SequenceType::get({thunkSize}, i8Ty);285            auto buffer = AllocaOp::create(builder, loc, buffTy);286            mlir::Value closure =287                builder.createConvert(loc, i8Ptr, embox.getHost());288            mlir::Value tramp = builder.createConvert(loc, i8Ptr, buffer);289            mlir::Value func =290                builder.createConvert(loc, i8Ptr, embox.getFunc());291            fir::CallOp::create(292                builder, loc, factory::getLlvmInitTrampoline(builder),293                llvm::ArrayRef<mlir::Value>{tramp, func, closure});294            auto adjustCall = fir::CallOp::create(295                builder, loc, factory::getLlvmAdjustTrampoline(builder),296                llvm::ArrayRef<mlir::Value>{tramp});297            rewriter.replaceOpWithNewOp<ConvertOp>(embox, toTy,298                                                   adjustCall.getResult(0));299            opIsValid = false;300          } else {301            // Just forward the function as a pointer.302            rewriter.replaceOpWithNewOp<ConvertOp>(embox, toTy,303                                                   embox.getFunc());304            opIsValid = false;305          }306        } else if (auto global = mlir::dyn_cast<GlobalOp>(op)) {307          auto ty = global.getType();308          if (typeConverter.needsConversion(ty)) {309            rewriter.startOpModification(global);310            auto toTy = typeConverter.convertType(ty);311            global.setType(toTy);312            rewriter.finalizeOpModification(global);313          }314        } else if (auto mem = mlir::dyn_cast<AllocaOp>(op)) {315          auto ty = mem.getType();316          if (typeConverter.needsConversion(ty)) {317            rewriter.setInsertionPoint(mem);318            auto toTy = typeConverter.convertType(unwrapRefType(ty));319            bool isPinned = mem.getPinned();320            llvm::StringRef uniqName =321                mem.getUniqName().value_or(llvm::StringRef());322            llvm::StringRef bindcName =323                mem.getBindcName().value_or(llvm::StringRef());324            rewriter.replaceOpWithNewOp<AllocaOp>(325                mem, toTy, uniqName, bindcName, isPinned, mem.getTypeparams(),326                mem.getShape());327            opIsValid = false;328          }329        } else if (auto mem = mlir::dyn_cast<AllocMemOp>(op)) {330          auto ty = mem.getType();331          if (typeConverter.needsConversion(ty)) {332            rewriter.setInsertionPoint(mem);333            auto toTy = typeConverter.convertType(unwrapRefType(ty));334            llvm::StringRef uniqName =335                mem.getUniqName().value_or(llvm::StringRef());336            llvm::StringRef bindcName =337                mem.getBindcName().value_or(llvm::StringRef());338            rewriter.replaceOpWithNewOp<AllocMemOp>(339                mem, toTy, uniqName, bindcName, mem.getTypeparams(),340                mem.getShape());341            opIsValid = false;342          }343        } else if (auto coor = mlir::dyn_cast<CoordinateOp>(op)) {344          auto ty = coor.getType();345          mlir::Type baseTy = coor.getBaseType();346          if (typeConverter.needsConversion(ty) ||347              typeConverter.needsConversion(baseTy)) {348            rewriter.setInsertionPoint(coor);349            auto toTy = typeConverter.convertType(ty);350            auto toBaseTy = typeConverter.convertType(baseTy);351            rewriter.replaceOpWithNewOp<CoordinateOp>(352                coor, toTy, coor.getRef(), coor.getCoor(), toBaseTy,353                coor.getFieldIndicesAttr());354            opIsValid = false;355          }356        } else if (auto index = mlir::dyn_cast<FieldIndexOp>(op)) {357          auto ty = index.getType();358          mlir::Type onTy = index.getOnType();359          if (typeConverter.needsConversion(ty) ||360              typeConverter.needsConversion(onTy)) {361            rewriter.setInsertionPoint(index);362            auto toTy = typeConverter.convertType(ty);363            auto toOnTy = typeConverter.convertType(onTy);364            rewriter.replaceOpWithNewOp<FieldIndexOp>(365                index, toTy, index.getFieldId(), toOnTy, index.getTypeparams());366            opIsValid = false;367          }368        } else if (auto index = mlir::dyn_cast<LenParamIndexOp>(op)) {369          auto ty = index.getType();370          mlir::Type onTy = index.getOnType();371          if (typeConverter.needsConversion(ty) ||372              typeConverter.needsConversion(onTy)) {373            rewriter.setInsertionPoint(index);374            auto toTy = typeConverter.convertType(ty);375            auto toOnTy = typeConverter.convertType(onTy);376            rewriter.replaceOpWithNewOp<LenParamIndexOp>(377                index, toTy, index.getFieldId(), toOnTy, index.getTypeparams());378            opIsValid = false;379          }380        } else {381          rewriter.startOpModification(op);382          // Convert the operands if needed383          for (auto i : llvm::enumerate(op->getResultTypes()))384            if (typeConverter.needsConversion(i.value())) {385              auto toTy = typeConverter.convertType(i.value());386              op->getResult(i.index()).setType(toTy);387            }388 389          // Convert the type attributes if needed390          for (const mlir::NamedAttribute &attr : op->getAttrDictionary())391            if (auto tyAttr = llvm::dyn_cast<mlir::TypeAttr>(attr.getValue()))392              if (typeConverter.needsConversion(tyAttr.getValue())) {393                auto toTy = typeConverter.convertType(tyAttr.getValue());394                op->setAttr(attr.getName(), mlir::TypeAttr::get(toTy));395              }396          rewriter.finalizeOpModification(op);397        }398        // Ensure block arguments are updated if needed.399        if (opIsValid && op->getNumRegions() != 0) {400          rewriter.startOpModification(op);401          for (mlir::Region &region : op->getRegions())402            for (mlir::Block &block : region.getBlocks())403              for (mlir::BlockArgument blockArg : block.getArguments())404                if (typeConverter.needsConversion(blockArg.getType())) {405                  mlir::Type toTy =406                      typeConverter.convertType(blockArg.getType());407                  blockArg.setType(toTy);408                }409          rewriter.finalizeOpModification(op);410        }411      });412    }413  }414 415private:416  BoxedProcedureOptions options;417};418} // namespace419