brintos

brintos / llvm-project-archived public Read only

0
0
Text · 22.7 KiB · d895cd7 Raw
640 lines · cpp
1//===----- HipStdPar.cpp - HIP C++ Standard Parallelism Support Passes ----===//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 implements two passes that enable HIP C++ Standard Parallelism9// Support:10//11// 1. AcceleratorCodeSelection (required): Given that only algorithms are12//    accelerated, and that the accelerated implementation exists in the form of13//    a compute kernel, we assume that only the kernel, and all functions14//    reachable from it, constitute code that the user expects the accelerator15//    to execute. Thus, we identify the set of all functions reachable from16//    kernels, and then remove all unreachable ones. This last part is necessary17//    because it is possible for code that the user did not expect to execute on18//    an accelerator to contain constructs that cannot be handled by the target19//    BE, which cannot be provably demonstrated to be dead code in general, and20//    thus can lead to mis-compilation. The degenerate case of this is when a21//    Module contains no kernels (the parent TU had no algorithm invocations fit22//    for acceleration), which we handle by completely emptying said module.23//    **NOTE**: The above does not handle indirectly reachable functions i.e.24//              it is possible to obtain a case where the target of an indirect25//              call is otherwise unreachable and thus is removed; this26//              restriction is aligned with the current `-hipstdpar` limitations27//              and will be relaxed in the future.28//29// 2. AllocationInterposition (required only when on-demand paging is30//    unsupported): Some accelerators or operating systems might not support31//    transparent on-demand paging. Thus, they would only be able to access32//    memory that is allocated by an accelerator-aware mechanism. For such cases33//    the user can opt into enabling allocation / deallocation interposition,34//    whereby we replace calls to known allocation / deallocation functions with35//    calls to runtime implemented equivalents that forward the requests to36//    accelerator-aware interfaces. We also support freeing system allocated37//    memory that ends up in one of the runtime equivalents, since this can38//    happen if e.g. a library that was compiled without interposition returns39//    an allocation that can be validly passed to `free`.40//41// 3. MathFixup (required): Some accelerators might have an incomplete42//    implementation for the intrinsics used to implement some of the math43//    functions in <cmath> / their corresponding libcall lowerings. Since this44//    can vary quite significantly between accelerators, we replace calls to a45//    set of intrinsics / lib functions known to be problematic with calls to a46//    HIPSTDPAR specific forwarding layer, which gives an uniform interface for47//    accelerators to implement in their own runtime components. This pass48//    should run before AcceleratorCodeSelection so as to prevent the spurious49//    removal of the HIPSTDPAR specific forwarding functions.50//===----------------------------------------------------------------------===//51 52#include "llvm/Transforms/HipStdPar/HipStdPar.h"53 54#include "llvm/ADT/STLExtras.h"55#include "llvm/ADT/SmallPtrSet.h"56#include "llvm/ADT/SmallVector.h"57#include "llvm/Analysis/CallGraph.h"58#include "llvm/Analysis/OptimizationRemarkEmitter.h"59#include "llvm/IR/Constants.h"60#include "llvm/IR/Function.h"61#include "llvm/IR/IRBuilder.h"62#include "llvm/IR/Intrinsics.h"63#include "llvm/IR/Module.h"64#include "llvm/Transforms/Utils/ModuleUtils.h"65 66#include <cassert>67#include <string>68#include <utility>69 70using namespace llvm;71 72template<typename T>73static inline void eraseFromModule(T &ToErase) {74  ToErase.replaceAllUsesWith(PoisonValue::get(ToErase.getType()));75  ToErase.eraseFromParent();76}77 78static bool checkIfSupported(GlobalVariable &G) {79  if (!G.isThreadLocal())80    return true;81 82  G.dropDroppableUses();83 84  if (!G.isConstantUsed())85    return true;86 87  std::string W;88  raw_string_ostream OS(W);89 90  OS << "Accelerator does not support the thread_local variable "91    << G.getName();92 93  Instruction *I = nullptr;94  SmallVector<User *> Tmp(G.users());95  SmallPtrSet<User *, 5> Visited;96  do {97    auto U = std::move(Tmp.back());98    Tmp.pop_back();99 100    if (!Visited.insert(U).second)101      continue;102 103    if (isa<Instruction>(U))104      I = cast<Instruction>(U);105    else106      Tmp.insert(Tmp.end(), U->user_begin(), U->user_end());107  } while (!I && !Tmp.empty());108 109  assert(I && "thread_local global should have at least one non-constant use.");110 111  G.getContext().diagnose(112    DiagnosticInfoUnsupported(*I->getParent()->getParent(), W,113                              I->getDebugLoc(), DS_Error));114 115  return false;116}117 118static inline void clearModule(Module &M) { // TODO: simplify.119  while (!M.functions().empty())120    eraseFromModule(*M.begin());121  while (!M.globals().empty())122    eraseFromModule(*M.globals().begin());123  while (!M.aliases().empty())124    eraseFromModule(*M.aliases().begin());125  while (!M.ifuncs().empty())126    eraseFromModule(*M.ifuncs().begin());127}128 129static SmallVector<std::reference_wrapper<Use>>130collectIndirectableUses(GlobalVariable *G) {131  // We are interested only in use chains that end in an Instruction.132  SmallVector<std::reference_wrapper<Use>> Uses;133 134  SmallVector<std::reference_wrapper<Use>> Stack(G->use_begin(), G->use_end());135  while (!Stack.empty()) {136    Use &U = Stack.pop_back_val();137    if (isa<Instruction>(U.getUser()))138      Uses.emplace_back(U);139    else140      transform(U.getUser()->uses(), std::back_inserter(Stack),141                [](auto &&U) { return std::ref(U); });142  }143 144  return Uses;145}146 147static inline GlobalVariable *getGlobalForName(GlobalVariable *G) {148  // Create an anonymous global which stores the variable's name, which will be149  // used by the HIPSTDPAR runtime to look up the program-wide symbol.150  LLVMContext &Ctx = G->getContext();151  auto *CDS = ConstantDataArray::getString(Ctx, G->getName());152 153  GlobalVariable *N = G->getParent()->getOrInsertGlobal("", CDS->getType());154  N->setInitializer(CDS);155  N->setLinkage(GlobalValue::LinkageTypes::PrivateLinkage);156  N->setConstant(true);157 158  return N;159}160 161static inline GlobalVariable *getIndirectionGlobal(Module *M) {162  // Create an anonymous global which stores a pointer to a pointer, which will163  // be externally initialised by the HIPSTDPAR runtime with the address of the164  // program-wide symbol.165  Type *PtrTy = PointerType::get(166      M->getContext(), M->getDataLayout().getDefaultGlobalsAddressSpace());167  GlobalVariable *NewG = M->getOrInsertGlobal("", PtrTy);168 169  NewG->setInitializer(PoisonValue::get(NewG->getValueType()));170  NewG->setLinkage(GlobalValue::LinkageTypes::PrivateLinkage);171  NewG->setConstant(true);172  NewG->setExternallyInitialized(true);173 174  return NewG;175}176 177static Constant *178appendIndirectedGlobal(const GlobalVariable *IndirectionTable,179                       SmallVector<Constant *> &SymbolIndirections,180                       GlobalVariable *ToIndirect) {181  Module *M = ToIndirect->getParent();182 183  auto *InitTy = cast<StructType>(IndirectionTable->getValueType());184  auto *SymbolListTy = cast<StructType>(InitTy->getStructElementType(2));185  Type *NameTy = SymbolListTy->getElementType(0);186  Type *IndirectTy = SymbolListTy->getElementType(1);187 188  Constant *NameG = getGlobalForName(ToIndirect);189  Constant *IndirectG = getIndirectionGlobal(M);190  Constant *Entry = ConstantStruct::get(191      SymbolListTy, {ConstantExpr::getAddrSpaceCast(NameG, NameTy),192                     ConstantExpr::getAddrSpaceCast(IndirectG, IndirectTy)});193  SymbolIndirections.push_back(Entry);194 195  return IndirectG;196}197 198static void fillIndirectionTable(GlobalVariable *IndirectionTable,199                                 SmallVector<Constant *> Indirections) {200  Module *M = IndirectionTable->getParent();201  size_t SymCnt = Indirections.size();202 203  auto *InitTy = cast<StructType>(IndirectionTable->getValueType());204  Type *SymbolListTy = InitTy->getStructElementType(1);205  auto *SymbolTy = cast<StructType>(InitTy->getStructElementType(2));206 207  Constant *Count = ConstantInt::get(InitTy->getStructElementType(0), SymCnt);208  M->removeGlobalVariable(IndirectionTable);209  GlobalVariable *Symbols =210      M->getOrInsertGlobal("", ArrayType::get(SymbolTy, SymCnt));211  Symbols->setLinkage(GlobalValue::LinkageTypes::PrivateLinkage);212  Symbols->setInitializer(213      ConstantArray::get(ArrayType::get(SymbolTy, SymCnt), {Indirections}));214  Symbols->setConstant(true);215 216  Constant *ASCSymbols = ConstantExpr::getAddrSpaceCast(Symbols, SymbolListTy);217  Constant *Init = ConstantStruct::get(218      InitTy, {Count, ASCSymbols, PoisonValue::get(SymbolTy)});219  M->insertGlobalVariable(IndirectionTable);220  IndirectionTable->setInitializer(Init);221}222 223static void replaceWithIndirectUse(const Use &U, const GlobalVariable *G,224                                   Constant *IndirectedG) {225  auto *I = cast<Instruction>(U.getUser());226 227  IRBuilder<> Builder(I);228  unsigned OpIdx = U.getOperandNo();229  Value *Op = I->getOperand(OpIdx);230 231  // We walk back up the use chain, which could be an arbitrarily long sequence232  // of constexpr AS casts, ptr-to-int and GEP instructions, until we reach the233  // indirected global.234  while (auto *CE = dyn_cast<ConstantExpr>(Op)) {235    assert((CE->getOpcode() == Instruction::GetElementPtr ||236            CE->getOpcode() == Instruction::AddrSpaceCast ||237            CE->getOpcode() == Instruction::PtrToInt) &&238           "Only GEP, ASCAST or PTRTOINT constant uses supported!");239 240    Instruction *NewI = Builder.Insert(CE->getAsInstruction());241    I->replaceUsesOfWith(Op, NewI);242    I = NewI;243    Op = I->getOperand(0);244    OpIdx = 0;245    Builder.SetInsertPoint(I);246  }247 248  assert(Op == G && "Must reach indirected global!");249 250  I->setOperand(OpIdx, Builder.CreateLoad(G->getType(), IndirectedG));251}252 253static inline bool isValidIndirectionTable(GlobalVariable *IndirectionTable) {254  std::string W;255  raw_string_ostream OS(W);256 257  Type *Ty = IndirectionTable->getValueType();258  bool Valid = false;259 260  if (!isa<StructType>(Ty)) {261    OS << "The Indirection Table must be a struct type; ";262    Ty->print(OS);263    OS << " is incorrect.\n";264  } else if (cast<StructType>(Ty)->getNumElements() != 3u) {265    OS << "The Indirection Table must have 3 elements; "266       << cast<StructType>(Ty)->getNumElements() << " is incorrect.\n";267  } else if (!isa<IntegerType>(cast<StructType>(Ty)->getStructElementType(0))) {268    OS << "The first element in the Indirection Table must be an integer; ";269    cast<StructType>(Ty)->getStructElementType(0)->print(OS);270    OS << " is incorrect.\n";271  } else if (!isa<PointerType>(cast<StructType>(Ty)->getStructElementType(1))) {272    OS << "The second element in the Indirection Table must be a pointer; ";273    cast<StructType>(Ty)->getStructElementType(1)->print(OS);274    OS << " is incorrect.\n";275  } else if (!isa<StructType>(cast<StructType>(Ty)->getStructElementType(2))) {276    OS << "The third element in the Indirection Table must be a struct type; ";277    cast<StructType>(Ty)->getStructElementType(2)->print(OS);278    OS << " is incorrect.\n";279  } else {280    Valid = true;281  }282 283  if (!Valid)284    IndirectionTable->getContext().diagnose(DiagnosticInfoGeneric(W, DS_Error));285 286  return Valid;287}288 289static void indirectGlobals(GlobalVariable *IndirectionTable,290                            SmallVector<GlobalVariable *> ToIndirect) {291  // We replace globals with an indirected access via a pointer that will get292  // set by the HIPSTDPAR runtime, using their accessible, program-wide unique293  // address as set by the host linker-loader.294  SmallVector<Constant *> SymbolIndirections;295  for (auto &&G : ToIndirect) {296    SmallVector<std::reference_wrapper<Use>> Uses = collectIndirectableUses(G);297 298    if (Uses.empty())299      continue;300 301    Constant *IndirectedGlobal =302        appendIndirectedGlobal(IndirectionTable, SymbolIndirections, G);303 304    for_each(Uses,305             [=](auto &&U) { replaceWithIndirectUse(U, G, IndirectedGlobal); });306 307    eraseFromModule(*G);308  }309 310  if (SymbolIndirections.empty())311    return;312 313  fillIndirectionTable(IndirectionTable, std::move(SymbolIndirections));314}315 316static inline void maybeHandleGlobals(Module &M) {317  unsigned GlobAS = M.getDataLayout().getDefaultGlobalsAddressSpace();318 319  SmallVector<GlobalVariable *> ToIndirect;320  for (auto &&G : M.globals()) {321    if (!checkIfSupported(G))322      return clearModule(M);323    if (G.getAddressSpace() != GlobAS)324      continue;325    if (G.isConstant() && G.hasInitializer() && G.hasAtLeastLocalUnnamedAddr())326      continue;327 328    ToIndirect.push_back(&G);329  }330 331  if (ToIndirect.empty())332    return;333 334  if (auto *IT = M.getNamedGlobal("__hipstdpar_symbol_indirection_table")) {335    if (!isValidIndirectionTable(IT))336      return clearModule(M);337    return indirectGlobals(IT, std::move(ToIndirect));338  } else {339    for (auto &&G : ToIndirect) {340      // We will internalise these, so we provide a poison initialiser.341      if (!G->hasInitializer())342        G->setInitializer(PoisonValue::get(G->getValueType()));343    }344  }345}346 347template<unsigned N>348static inline void removeUnreachableFunctions(349  const SmallPtrSet<const Function *, N>& Reachable, Module &M) {350  removeFromUsedLists(M, [&](Constant *C) {351    if (auto F = dyn_cast<Function>(C))352      return !Reachable.contains(F);353 354    return false;355  });356 357  SmallVector<std::reference_wrapper<Function>> ToRemove;358  copy_if(M, std::back_inserter(ToRemove), [&](auto &&F) {359    return !F.isIntrinsic() && !Reachable.contains(&F);360  });361 362  for_each(ToRemove, eraseFromModule<Function>);363}364 365static inline bool isAcceleratorExecutionRoot(const Function *F) {366    if (!F)367      return false;368 369    return F->getCallingConv() == CallingConv::AMDGPU_KERNEL;370}371 372static inline bool checkIfSupported(const Function *F, const CallBase *CB) {373  const auto Dx = F->getName().rfind("__hipstdpar_unsupported");374 375  if (Dx == StringRef::npos)376    return true;377 378  const auto N = F->getName().substr(0, Dx);379 380  std::string W;381  raw_string_ostream OS(W);382 383  if (N == "__ASM")384    OS << "Accelerator does not support the ASM block:\n"385      << cast<ConstantDataArray>(CB->getArgOperand(0))->getAsCString();386  else387    OS << "Accelerator does not support the " << N << " function.";388 389  auto Caller = CB->getParent()->getParent();390 391  Caller->getContext().diagnose(392    DiagnosticInfoUnsupported(*Caller, W, CB->getDebugLoc(), DS_Error));393 394  return false;395}396 397PreservedAnalyses398  HipStdParAcceleratorCodeSelectionPass::run(Module &M,399                                             ModuleAnalysisManager &MAM) {400  auto &CGA = MAM.getResult<CallGraphAnalysis>(M);401 402  SmallPtrSet<const Function *, 32> Reachable;403  for (auto &&CGN : CGA) {404    if (!isAcceleratorExecutionRoot(CGN.first))405      continue;406 407    Reachable.insert(CGN.first);408 409    SmallVector<const Function *> Tmp({CGN.first});410    do {411      auto F = std::move(Tmp.back());412      Tmp.pop_back();413 414      for (auto &&N : *CGA[F]) {415        if (!N.second)416          continue;417        if (!N.second->getFunction())418          continue;419        if (Reachable.contains(N.second->getFunction()))420          continue;421 422        if (!checkIfSupported(N.second->getFunction(),423                              dyn_cast<CallBase>(*N.first)))424          return PreservedAnalyses::none();425 426        Reachable.insert(N.second->getFunction());427        Tmp.push_back(N.second->getFunction());428      }429    } while (!std::empty(Tmp));430  }431 432  if (std::empty(Reachable))433    clearModule(M);434  else435    removeUnreachableFunctions(Reachable, M);436 437  maybeHandleGlobals(M);438 439  return PreservedAnalyses::none();440}441 442static constexpr std::pair<StringLiteral, StringLiteral> ReplaceMap[]{443    {"aligned_alloc", "__hipstdpar_aligned_alloc"},444    {"calloc", "__hipstdpar_calloc"},445    {"free", "__hipstdpar_free"},446    {"malloc", "__hipstdpar_malloc"},447    {"memalign", "__hipstdpar_aligned_alloc"},448    {"mmap", "__hipstdpar_mmap"},449    {"munmap", "__hipstdpar_munmap"},450    {"posix_memalign", "__hipstdpar_posix_aligned_alloc"},451    {"realloc", "__hipstdpar_realloc"},452    {"reallocarray", "__hipstdpar_realloc_array"},453    {"_ZdaPv", "__hipstdpar_operator_delete"},454    {"_ZdaPvm", "__hipstdpar_operator_delete_sized"},455    {"_ZdaPvSt11align_val_t", "__hipstdpar_operator_delete_aligned"},456    {"_ZdaPvmSt11align_val_t", "__hipstdpar_operator_delete_aligned_sized"},457    {"_ZdlPv", "__hipstdpar_operator_delete"},458    {"_ZdlPvm", "__hipstdpar_operator_delete_sized"},459    {"_ZdlPvSt11align_val_t", "__hipstdpar_operator_delete_aligned"},460    {"_ZdlPvmSt11align_val_t", "__hipstdpar_operator_delete_aligned_sized"},461    {"_Znam", "__hipstdpar_operator_new"},462    {"_ZnamRKSt9nothrow_t", "__hipstdpar_operator_new_nothrow"},463    {"_ZnamSt11align_val_t", "__hipstdpar_operator_new_aligned"},464    {"_ZnamSt11align_val_tRKSt9nothrow_t",465     "__hipstdpar_operator_new_aligned_nothrow"},466 467    {"_Znwm", "__hipstdpar_operator_new"},468    {"_ZnwmRKSt9nothrow_t", "__hipstdpar_operator_new_nothrow"},469    {"_ZnwmSt11align_val_t", "__hipstdpar_operator_new_aligned"},470    {"_ZnwmSt11align_val_tRKSt9nothrow_t",471     "__hipstdpar_operator_new_aligned_nothrow"},472    {"__builtin_calloc", "__hipstdpar_calloc"},473    {"__builtin_free", "__hipstdpar_free"},474    {"__builtin_malloc", "__hipstdpar_malloc"},475    {"__builtin_operator_delete", "__hipstdpar_operator_delete"},476    {"__builtin_operator_new", "__hipstdpar_operator_new"},477    {"__builtin_realloc", "__hipstdpar_realloc"},478    {"__libc_calloc", "__hipstdpar_calloc"},479    {"__libc_free", "__hipstdpar_free"},480    {"__libc_malloc", "__hipstdpar_malloc"},481    {"__libc_memalign", "__hipstdpar_aligned_alloc"},482    {"__libc_realloc", "__hipstdpar_realloc"}};483 484static constexpr std::pair<StringLiteral, StringLiteral> HiddenMap[]{485    // hidden_malloc and hidden_free are only kept for backwards compatibility /486    // legacy purposes, and we should remove them in the future487    {"__hipstdpar_hidden_malloc", "__libc_malloc"},488    {"__hipstdpar_hidden_free", "__libc_free"},489    {"__hipstdpar_hidden_memalign", "__libc_memalign"},490    {"__hipstdpar_hidden_mmap", "mmap"},491    {"__hipstdpar_hidden_munmap", "munmap"}};492 493PreservedAnalyses494HipStdParAllocationInterpositionPass::run(Module &M, ModuleAnalysisManager&) {495  SmallDenseMap<StringRef, StringRef> AllocReplacements(std::cbegin(ReplaceMap),496                                                        std::cend(ReplaceMap));497 498  for (auto &&F : M) {499    if (!F.hasName())500      continue;501    auto It = AllocReplacements.find(F.getName());502    if (It == AllocReplacements.end())503      continue;504 505    if (auto R = M.getFunction(It->second)) {506      F.replaceAllUsesWith(R);507    } else {508      std::string W;509      raw_string_ostream OS(W);510 511      OS << "cannot be interposed, missing: " << AllocReplacements[F.getName()]512        << ". Tried to run the allocation interposition pass without the "513        << "replacement functions available.";514 515      F.getContext().diagnose(DiagnosticInfoUnsupported(F, W,516                                                        F.getSubprogram(),517                                                        DS_Warning));518    }519  }520 521  for (auto &&HR : HiddenMap) {522    if (auto F = M.getFunction(HR.first)) {523      auto R = M.getOrInsertFunction(HR.second, F->getFunctionType(),524                                     F->getAttributes());525      F->replaceAllUsesWith(R.getCallee());526 527      eraseFromModule(*F);528    }529  }530 531  return PreservedAnalyses::none();532}533 534static constexpr std::pair<StringLiteral, StringLiteral> MathLibToHipStdPar[]{535    {"acosh", "__hipstdpar_acosh_f64"},536    {"acoshf", "__hipstdpar_acosh_f32"},537    {"asinh", "__hipstdpar_asinh_f64"},538    {"asinhf", "__hipstdpar_asinh_f32"},539    {"atanh", "__hipstdpar_atanh_f64"},540    {"atanhf", "__hipstdpar_atanh_f32"},541    {"cbrt", "__hipstdpar_cbrt_f64"},542    {"cbrtf", "__hipstdpar_cbrt_f32"},543    {"erf", "__hipstdpar_erf_f64"},544    {"erff", "__hipstdpar_erf_f32"},545    {"erfc", "__hipstdpar_erfc_f64"},546    {"erfcf", "__hipstdpar_erfc_f32"},547    {"fdim", "__hipstdpar_fdim_f64"},548    {"fdimf", "__hipstdpar_fdim_f32"},549    {"expm1", "__hipstdpar_expm1_f64"},550    {"expm1f", "__hipstdpar_expm1_f32"},551    {"hypot", "__hipstdpar_hypot_f64"},552    {"hypotf", "__hipstdpar_hypot_f32"},553    {"ilogb", "__hipstdpar_ilogb_f64"},554    {"ilogbf", "__hipstdpar_ilogb_f32"},555    {"lgamma", "__hipstdpar_lgamma_f64"},556    {"lgammaf", "__hipstdpar_lgamma_f32"},557    {"log1p", "__hipstdpar_log1p_f64"},558    {"log1pf", "__hipstdpar_log1p_f32"},559    {"logb", "__hipstdpar_logb_f64"},560    {"logbf", "__hipstdpar_logb_f32"},561    {"nextafter", "__hipstdpar_nextafter_f64"},562    {"nextafterf", "__hipstdpar_nextafter_f32"},563    {"nexttoward", "__hipstdpar_nexttoward_f64"},564    {"nexttowardf", "__hipstdpar_nexttoward_f32"},565    {"remainder", "__hipstdpar_remainder_f64"},566    {"remainderf", "__hipstdpar_remainder_f32"},567    {"remquo", "__hipstdpar_remquo_f64"},568    {"remquof", "__hipstdpar_remquo_f32"},569    {"scalbln", "__hipstdpar_scalbln_f64"},570    {"scalblnf", "__hipstdpar_scalbln_f32"},571    {"scalbn", "__hipstdpar_scalbn_f64"},572    {"scalbnf", "__hipstdpar_scalbn_f32"},573    {"tgamma", "__hipstdpar_tgamma_f64"},574    {"tgammaf", "__hipstdpar_tgamma_f32"}};575 576PreservedAnalyses HipStdParMathFixupPass::run(Module &M,577                                              ModuleAnalysisManager &) {578  if (M.empty())579    return PreservedAnalyses::all();580 581  SmallVector<std::pair<Function *, std::string>> ToReplace;582  for (auto &&F : M) {583    if (!F.hasName())584      continue;585 586    StringRef N = F.getName();587    Intrinsic::ID ID = F.getIntrinsicID();588 589    switch (ID) {590    case Intrinsic::not_intrinsic: {591      auto It =592          find_if(MathLibToHipStdPar, [&](auto &&M) { return M.first == N; });593      if (It == std::cend(MathLibToHipStdPar))594        continue;595      ToReplace.emplace_back(&F, It->second);596      break;597    }598    case Intrinsic::acos:599    case Intrinsic::asin:600    case Intrinsic::atan:601    case Intrinsic::atan2:602    case Intrinsic::cosh:603    case Intrinsic::modf:604    case Intrinsic::sinh:605    case Intrinsic::tan:606    case Intrinsic::tanh:607      break;608    default: {609      if (F.getReturnType()->isDoubleTy()) {610        switch (ID) {611        case Intrinsic::cos:612        case Intrinsic::exp:613        case Intrinsic::exp2:614        case Intrinsic::log:615        case Intrinsic::log10:616        case Intrinsic::log2:617        case Intrinsic::pow:618        case Intrinsic::sin:619          break;620        default:621          continue;622        }623        break;624      }625      continue;626    }627    }628 629    ToReplace.emplace_back(&F, N);630    llvm::replace(ToReplace.back().second, '.', '_');631    StringRef Prefix = "llvm";632    ToReplace.back().second.replace(0, Prefix.size(), "__hipstdpar");633  }634  for (auto &&[F, NewF] : ToReplace)635    F->replaceAllUsesWith(636        M.getOrInsertFunction(NewF, F->getFunctionType()).getCallee());637 638  return PreservedAnalyses::none();639}640