brintos

brintos / llvm-project-archived public Read only

0
0
Text · 10.2 KiB · 4ba2ea5 Raw
262 lines · cpp
1//===- SetRuntimeCallAttributes.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//===----------------------------------------------------------------------===//10/// \file11/// SetRuntimeCallAttributesPass looks for fir.call operations12/// that are calling into Fortran runtime, and tries to set different13/// attributes on them to enable more optimizations in LLVM backend14/// (granted that they are preserved all the way to LLVM IR).15/// This pass is currently only attaching fir.call wide atttributes,16/// such as ones corresponding to llvm.memory, nosync, nocallbac, etc.17/// It is not designed to attach attributes to the arguments and the results18/// of a call.19//===----------------------------------------------------------------------===//20#include "flang/Common/static-multimap-view.h"21#include "flang/Optimizer/Builder/Runtime/RTBuilder.h"22#include "flang/Optimizer/Dialect/FIRDialect.h"23#include "flang/Optimizer/Dialect/FIROpsSupport.h"24#include "flang/Optimizer/Support/InternalNames.h"25#include "flang/Optimizer/Transforms/Passes.h"26#include "flang/Runtime/io-api.h"27#include "mlir/Dialect/LLVMIR/LLVMAttrs.h"28 29namespace fir {30#define GEN_PASS_DEF_SETRUNTIMECALLATTRIBUTES31#include "flang/Optimizer/Transforms/Passes.h.inc"32} // namespace fir33 34#define DEBUG_TYPE "set-runtime-call-attrs"35 36using namespace Fortran::runtime;37using namespace Fortran::runtime::io;38 39#define mkIOKey(X) FirmkKey(IONAME(X))40#define mkRTKey(X) FirmkKey(RTNAME(X))41 42// Return LLVM dialect MemoryEffectsAttr for the given Fortran runtime call.43// This function is computing a generic value of this attribute44// by analyzing the arguments and their types.45// It tries to figure out if an "indirect" memory access is possible46// during this call. If it is not possible, then the memory effects47// are:48//   * other = NoModRef49//   * argMem = ModRef50//   * inaccessibleMem = ModRef51//52// Otherwise, it returns an empty attribute meaning ModRef for all kinds53// of memory.54//55// The attribute deduction is conservative in a sense that it applies56// to most of the runtime calls, but it may still be incorrect for some57// runtime calls.58static mlir::LLVM::MemoryEffectsAttr getGenericMemoryAttr(fir::CallOp callOp) {59  bool maybeIndirectAccess = false;60  for (auto arg : callOp.getArgOperands()) {61    mlir::Type argType = arg.getType();62    if (mlir::isa<fir::BaseBoxType>(argType)) {63      // If it is a null/absent box, then this particular call64      // cannot access memory indirectly through the box's65      // base_addr.66      auto def = arg.getDefiningOp();67      if (!mlir::isa_and_nonnull<fir::ZeroOp, fir::AbsentOp>(def)) {68        maybeIndirectAccess = true;69        break;70      }71    }72    if (auto refType = mlir::dyn_cast<fir::ReferenceType>(argType)) {73      if (!fir::isa_trivial(refType.getElementType())) {74        maybeIndirectAccess = true;75        break;76      }77    }78    if (auto ptrType = mlir::dyn_cast<mlir::LLVM::LLVMPointerType>(argType)) {79      maybeIndirectAccess = true;80      break;81    }82  }83  if (!maybeIndirectAccess) {84    return mlir::LLVM::MemoryEffectsAttr::get(85        callOp->getContext(),86        {/*other=*/mlir::LLVM::ModRefInfo::NoModRef,87         /*argMem=*/mlir::LLVM::ModRefInfo::ModRef,88         /*inaccessibleMem=*/mlir::LLVM::ModRefInfo::ModRef,89         /*errnoMem=*/mlir::LLVM::ModRefInfo::NoModRef,90         /*targetMem0=*/mlir::LLVM::ModRefInfo::NoModRef,91         /*targetMem1=*/mlir::LLVM::ModRefInfo::NoModRef});92  }93 94  return {};95}96 97namespace {98class SetRuntimeCallAttributesPass99    : public fir::impl::SetRuntimeCallAttributesBase<100          SetRuntimeCallAttributesPass> {101public:102  void runOnOperation() override;103};104 105// A helper to match a type against a list of types.106template <typename T, typename... Ts>107constexpr bool IsAny = std::disjunction_v<std::is_same<T, Ts>...>;108} // end anonymous namespace109 110// MemoryAttrDesc type provides get() method for computing111// mlir::LLVM::MemoryEffectsAttr for the given Fortran runtime call.112// If needed, add specializations for particular runtime calls.113namespace {114// Default implementation just uses getGenericMemoryAttr().115// Note that it may be incorrect for some runtime calls.116template <typename KEY, typename Enable = void>117struct MemoryAttrDesc {118  static mlir::LLVM::MemoryEffectsAttr get(fir::CallOp callOp) {119    return getGenericMemoryAttr(callOp);120  }121};122} // end anonymous namespace123 124// NosyncAttrDesc type provides get() method for computing125// LLVM nosync attribute for the given call.126namespace {127// Default implementation always returns LLVM nosync.128// This should be true for the majority of the Fortran runtime calls.129template <typename KEY, typename Enable = void>130struct NosyncAttrDesc {131  static std::optional<mlir::NamedAttribute> get(fir::CallOp callOp) {132    // TODO: replace llvm.nosync with an LLVM dialect callback.133    return mlir::NamedAttribute("llvm.nosync",134                                mlir::UnitAttr::get(callOp->getContext()));135  }136};137} // end anonymous namespace138 139// NocallbackAttrDesc type provides get() method for computing140// LLVM nocallback attribute for the given call.141namespace {142// Default implementation always returns LLVM nocallback.143// It must be specialized for Fortran runtime functions that may call144// user functions during their execution (e.g. defined IO, assignment).145template <typename KEY, typename Enable = void>146struct NocallbackAttrDesc {147  static std::optional<mlir::NamedAttribute> get(fir::CallOp callOp) {148    // TODO: replace llvm.nocallback with an LLVM dialect callback.149    return mlir::NamedAttribute("llvm.nocallback",150                                mlir::UnitAttr::get(callOp->getContext()));151  }152};153 154// Derived types IO may call back into a Fortran module.155// This specialization is conservative for Input/OutputDerivedType,156// and it might be improved by checking if the NonTbpDefinedIoTable157// pointer argument is null.158template <typename KEY>159struct NocallbackAttrDesc<160    KEY, std::enable_if_t<161             IsAny<KEY, mkIOKey(OutputDerivedType), mkIOKey(InputDerivedType),162                   mkIOKey(OutputNamelist), mkIOKey(InputNamelist)>>> {163  static std::optional<mlir::NamedAttribute> get(fir::CallOp) {164    return std::nullopt;165  }166};167} // end anonymous namespace168 169namespace {170// RuntimeFunction provides different callbacks that compute values171// of fir.call attributes for a Fortran runtime function.172struct RuntimeFunction {173  using MemoryAttrGeneratorTy = mlir::LLVM::MemoryEffectsAttr (*)(fir::CallOp);174  using NamedAttrGeneratorTy =175      std::optional<mlir::NamedAttribute> (*)(fir::CallOp);176  using Key = std::string_view;177  constexpr operator Key() const { return key; }178  Key key;179  MemoryAttrGeneratorTy memoryAttrGenerator;180  NamedAttrGeneratorTy nosyncAttrGenerator;181  NamedAttrGeneratorTy nocallbackAttrGenerator;182};183 184// Helper type to create a RuntimeFunction descriptor given185// the KEY and a function name.186template <typename KEY>187struct RuntimeFactory {188  static constexpr RuntimeFunction create(const char name[]) {189    // GCC 7 does not recognize this as a constant expression:190    //   ((const char *)RuntimeFunction<>::name) == nullptr191    // This comparison comes from the basic_string_view(const char *)192    // constructor. We have to use the other constructor193    // that takes explicit length parameter.194    return RuntimeFunction{195        std::string_view{name, std::char_traits<char>::length(name)},196        MemoryAttrDesc<KEY>::get, NosyncAttrDesc<KEY>::get,197        NocallbackAttrDesc<KEY>::get};198  }199};200} // end anonymous namespace201 202#define KNOWN_IO_FUNC(X) RuntimeFactory<mkIOKey(X)>::create(mkIOKey(X)::name)203#define KNOWN_RUNTIME_FUNC(X)                                                  \204  RuntimeFactory<mkRTKey(X)>::create(mkRTKey(X)::name)205 206// A table of RuntimeFunction descriptors for all recognized207// Fortran runtime functions.208static constexpr RuntimeFunction runtimeFuncsTable[] = {209#include "flang/Optimizer/Transforms/RuntimeFunctions.inc"210};211 212static constexpr Fortran::common::StaticMultimapView<RuntimeFunction>213    runtimeFuncs(runtimeFuncsTable);214static_assert(runtimeFuncs.Verify() && "map must be sorted");215 216// Set attributes for the given Fortran runtime call.217// The symbolTable is used to cache the name lookups in the module.218static void setRuntimeCallAttributes(fir::CallOp callOp,219                                     mlir::SymbolTableCollection &symbolTable) {220  auto iface = mlir::cast<mlir::CallOpInterface>(callOp.getOperation());221  auto funcOp = mlir::dyn_cast_or_null<mlir::func::FuncOp>(222      iface.resolveCallableInTable(&symbolTable));223 224  if (!funcOp || !funcOp->hasAttrOfType<mlir::UnitAttr>(225                     fir::FIROpsDialect::getFirRuntimeAttrName()))226    return;227 228  llvm::StringRef name = funcOp.getName();229  if (auto range = runtimeFuncs.equal_range(name);230      range.first != range.second) {231    // There should not be duplicate entries.232    assert(range.first + 1 == range.second);233    const RuntimeFunction &desc = *range.first;234    LLVM_DEBUG(llvm::dbgs()235               << "Identified runtime function call: " << desc.key << '\n');236    if (mlir::LLVM::MemoryEffectsAttr memoryAttr =237            desc.memoryAttrGenerator(callOp))238      callOp->setAttr(fir::FIROpsDialect::getFirCallMemoryAttrName(),239                      memoryAttr);240    if (auto attr = desc.nosyncAttrGenerator(callOp))241      callOp->setAttr(attr->getName(), attr->getValue());242    if (auto attr = desc.nocallbackAttrGenerator(callOp))243      callOp->setAttr(attr->getName(), attr->getValue());244    LLVM_DEBUG(llvm::dbgs() << "Operation with attrs: " << callOp << '\n');245  }246}247 248void SetRuntimeCallAttributesPass::runOnOperation() {249  mlir::func::FuncOp funcOp = getOperation();250  // Exit early for declarations to skip the debug output for them.251  if (funcOp.isDeclaration())252    return;253  LLVM_DEBUG(llvm::dbgs() << "=== Begin " DEBUG_TYPE " ===\n");254  LLVM_DEBUG(llvm::dbgs() << "Func-name:" << funcOp.getSymName() << "\n");255 256  mlir::SymbolTableCollection symbolTable;257  funcOp.walk([&](fir::CallOp callOp) {258    setRuntimeCallAttributes(callOp, symbolTable);259  });260  LLVM_DEBUG(llvm::dbgs() << "=== End " DEBUG_TYPE " ===\n");261}262