1 //===- IndirectionUtils.h - Utilities for adding indirections ---*- C++ -*-===// 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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Contains utilities for adding indirections and breaking up modules. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #ifndef LLVM_EXECUTIONENGINE_ORC_INDIRECTIONUTILS_H 14 #define LLVM_EXECUTIONENGINE_ORC_INDIRECTIONUTILS_H 15 16 #include "llvm/ADT/StringMap.h" 17 #include "llvm/ADT/StringRef.h" 18 #include "llvm/ExecutionEngine/JITSymbol.h" 19 #include "llvm/ExecutionEngine/Orc/Core.h" 20 #include "llvm/ExecutionEngine/Orc/OrcABISupport.h" 21 #include "llvm/Support/Error.h" 22 #include "llvm/Support/Memory.h" 23 #include "llvm/Support/Process.h" 24 #include "llvm/Transforms/Utils/ValueMapper.h" 25 #include <algorithm> 26 #include <cassert> 27 #include <cstdint> 28 #include <functional> 29 #include <future> 30 #include <map> 31 #include <memory> 32 #include <system_error> 33 #include <utility> 34 #include <vector> 35 36 namespace llvm { 37 38 class Constant; 39 class Function; 40 class FunctionType; 41 class GlobalAlias; 42 class GlobalVariable; 43 class Module; 44 class PointerType; 45 class Triple; 46 class Twine; 47 class Value; 48 49 namespace orc { 50 51 /// Base class for pools of compiler re-entry trampolines. 52 /// These trampolines are callable addresses that save all register state 53 /// before calling a supplied function to return the trampoline landing 54 /// address, then restore all state before jumping to that address. They 55 /// are used by various ORC APIs to support lazy compilation 56 class TrampolinePool { 57 public: 58 using NotifyLandingResolvedFunction = 59 unique_function<void(JITTargetAddress) const>; 60 61 using ResolveLandingFunction = unique_function<void( 62 JITTargetAddress TrampolineAddr, 63 NotifyLandingResolvedFunction OnLandingResolved) const>; 64 65 virtual ~TrampolinePool(); 66 67 /// Get an available trampoline address. 68 /// Returns an error if no trampoline can be created. getTrampoline()69 Expected<JITTargetAddress> getTrampoline() { 70 std::lock_guard<std::mutex> Lock(TPMutex); 71 if (AvailableTrampolines.empty()) { 72 if (auto Err = grow()) 73 return std::move(Err); 74 } 75 assert(!AvailableTrampolines.empty() && "Failed to grow trampoline pool"); 76 auto TrampolineAddr = AvailableTrampolines.back(); 77 AvailableTrampolines.pop_back(); 78 return TrampolineAddr; 79 } 80 81 /// Returns the given trampoline to the pool for re-use. releaseTrampoline(JITTargetAddress TrampolineAddr)82 void releaseTrampoline(JITTargetAddress TrampolineAddr) { 83 std::lock_guard<std::mutex> Lock(TPMutex); 84 AvailableTrampolines.push_back(TrampolineAddr); 85 } 86 87 protected: 88 virtual Error grow() = 0; 89 90 std::mutex TPMutex; 91 std::vector<JITTargetAddress> AvailableTrampolines; 92 }; 93 94 /// A trampoline pool for trampolines within the current process. 95 template <typename ORCABI> class LocalTrampolinePool : public TrampolinePool { 96 public: 97 /// Creates a LocalTrampolinePool with the given RunCallback function. 98 /// Returns an error if this function is unable to correctly allocate, write 99 /// and protect the resolver code block. 100 static Expected<std::unique_ptr<LocalTrampolinePool>> Create(ResolveLandingFunction ResolveLanding)101 Create(ResolveLandingFunction ResolveLanding) { 102 Error Err = Error::success(); 103 104 auto LTP = std::unique_ptr<LocalTrampolinePool>( 105 new LocalTrampolinePool(std::move(ResolveLanding), Err)); 106 107 if (Err) 108 return std::move(Err); 109 return std::move(LTP); 110 } 111 112 private: reenter(void * TrampolinePoolPtr,void * TrampolineId)113 static JITTargetAddress reenter(void *TrampolinePoolPtr, void *TrampolineId) { 114 LocalTrampolinePool<ORCABI> *TrampolinePool = 115 static_cast<LocalTrampolinePool *>(TrampolinePoolPtr); 116 117 std::promise<JITTargetAddress> LandingAddressP; 118 auto LandingAddressF = LandingAddressP.get_future(); 119 120 TrampolinePool->ResolveLanding(pointerToJITTargetAddress(TrampolineId), 121 [&](JITTargetAddress LandingAddress) { 122 LandingAddressP.set_value(LandingAddress); 123 }); 124 return LandingAddressF.get(); 125 } 126 LocalTrampolinePool(ResolveLandingFunction ResolveLanding,Error & Err)127 LocalTrampolinePool(ResolveLandingFunction ResolveLanding, Error &Err) 128 : ResolveLanding(std::move(ResolveLanding)) { 129 130 ErrorAsOutParameter _(&Err); 131 132 /// Try to set up the resolver block. 133 std::error_code EC; 134 ResolverBlock = sys::OwningMemoryBlock(sys::Memory::allocateMappedMemory( 135 ORCABI::ResolverCodeSize, nullptr, 136 sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC)); 137 if (EC) { 138 Err = errorCodeToError(EC); 139 return; 140 } 141 142 ORCABI::writeResolverCode(static_cast<char *>(ResolverBlock.base()), 143 pointerToJITTargetAddress(ResolverBlock.base()), 144 pointerToJITTargetAddress(&reenter), 145 pointerToJITTargetAddress(this)); 146 147 EC = sys::Memory::protectMappedMemory(ResolverBlock.getMemoryBlock(), 148 sys::Memory::MF_READ | 149 sys::Memory::MF_EXEC); 150 if (EC) { 151 Err = errorCodeToError(EC); 152 return; 153 } 154 } 155 grow()156 Error grow() override { 157 assert(AvailableTrampolines.empty() && "Growing prematurely?"); 158 159 std::error_code EC; 160 auto TrampolineBlock = 161 sys::OwningMemoryBlock(sys::Memory::allocateMappedMemory( 162 sys::Process::getPageSizeEstimate(), nullptr, 163 sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC)); 164 if (EC) 165 return errorCodeToError(EC); 166 167 unsigned NumTrampolines = 168 (sys::Process::getPageSizeEstimate() - ORCABI::PointerSize) / 169 ORCABI::TrampolineSize; 170 171 char *TrampolineMem = static_cast<char *>(TrampolineBlock.base()); 172 ORCABI::writeTrampolines( 173 TrampolineMem, pointerToJITTargetAddress(TrampolineMem), 174 pointerToJITTargetAddress(ResolverBlock.base()), NumTrampolines); 175 176 for (unsigned I = 0; I < NumTrampolines; ++I) 177 AvailableTrampolines.push_back(pointerToJITTargetAddress( 178 TrampolineMem + (I * ORCABI::TrampolineSize))); 179 180 if (auto EC = sys::Memory::protectMappedMemory( 181 TrampolineBlock.getMemoryBlock(), 182 sys::Memory::MF_READ | sys::Memory::MF_EXEC)) 183 return errorCodeToError(EC); 184 185 TrampolineBlocks.push_back(std::move(TrampolineBlock)); 186 return Error::success(); 187 } 188 189 ResolveLandingFunction ResolveLanding; 190 191 sys::OwningMemoryBlock ResolverBlock; 192 std::vector<sys::OwningMemoryBlock> TrampolineBlocks; 193 }; 194 195 /// Target-independent base class for compile callback management. 196 class JITCompileCallbackManager { 197 public: 198 using CompileFunction = std::function<JITTargetAddress()>; 199 200 virtual ~JITCompileCallbackManager() = default; 201 202 /// Reserve a compile callback. 203 Expected<JITTargetAddress> getCompileCallback(CompileFunction Compile); 204 205 /// Execute the callback for the given trampoline id. Called by the JIT 206 /// to compile functions on demand. 207 JITTargetAddress executeCompileCallback(JITTargetAddress TrampolineAddr); 208 209 protected: 210 /// Construct a JITCompileCallbackManager. JITCompileCallbackManager(std::unique_ptr<TrampolinePool> TP,ExecutionSession & ES,JITTargetAddress ErrorHandlerAddress)211 JITCompileCallbackManager(std::unique_ptr<TrampolinePool> TP, 212 ExecutionSession &ES, 213 JITTargetAddress ErrorHandlerAddress) 214 : TP(std::move(TP)), ES(ES), 215 CallbacksJD(ES.createBareJITDylib("<Callbacks>")), 216 ErrorHandlerAddress(ErrorHandlerAddress) {} 217 setTrampolinePool(std::unique_ptr<TrampolinePool> TP)218 void setTrampolinePool(std::unique_ptr<TrampolinePool> TP) { 219 this->TP = std::move(TP); 220 } 221 222 private: 223 std::mutex CCMgrMutex; 224 std::unique_ptr<TrampolinePool> TP; 225 ExecutionSession &ES; 226 JITDylib &CallbacksJD; 227 JITTargetAddress ErrorHandlerAddress; 228 std::map<JITTargetAddress, SymbolStringPtr> AddrToSymbol; 229 size_t NextCallbackId = 0; 230 }; 231 232 /// Manage compile callbacks for in-process JITs. 233 template <typename ORCABI> 234 class LocalJITCompileCallbackManager : public JITCompileCallbackManager { 235 public: 236 /// Create a new LocalJITCompileCallbackManager. 237 static Expected<std::unique_ptr<LocalJITCompileCallbackManager>> Create(ExecutionSession & ES,JITTargetAddress ErrorHandlerAddress)238 Create(ExecutionSession &ES, JITTargetAddress ErrorHandlerAddress) { 239 Error Err = Error::success(); 240 auto CCMgr = std::unique_ptr<LocalJITCompileCallbackManager>( 241 new LocalJITCompileCallbackManager(ES, ErrorHandlerAddress, Err)); 242 if (Err) 243 return std::move(Err); 244 return std::move(CCMgr); 245 } 246 247 private: 248 /// Construct a InProcessJITCompileCallbackManager. 249 /// @param ErrorHandlerAddress The address of an error handler in the target 250 /// process to be used if a compile callback fails. LocalJITCompileCallbackManager(ExecutionSession & ES,JITTargetAddress ErrorHandlerAddress,Error & Err)251 LocalJITCompileCallbackManager(ExecutionSession &ES, 252 JITTargetAddress ErrorHandlerAddress, 253 Error &Err) 254 : JITCompileCallbackManager(nullptr, ES, ErrorHandlerAddress) { 255 using NotifyLandingResolvedFunction = 256 TrampolinePool::NotifyLandingResolvedFunction; 257 258 ErrorAsOutParameter _(&Err); 259 auto TP = LocalTrampolinePool<ORCABI>::Create( 260 [this](JITTargetAddress TrampolineAddr, 261 NotifyLandingResolvedFunction NotifyLandingResolved) { 262 NotifyLandingResolved(executeCompileCallback(TrampolineAddr)); 263 }); 264 265 if (!TP) { 266 Err = TP.takeError(); 267 return; 268 } 269 270 setTrampolinePool(std::move(*TP)); 271 } 272 }; 273 274 /// Base class for managing collections of named indirect stubs. 275 class IndirectStubsManager { 276 public: 277 /// Map type for initializing the manager. See init. 278 using StubInitsMap = StringMap<std::pair<JITTargetAddress, JITSymbolFlags>>; 279 280 virtual ~IndirectStubsManager() = default; 281 282 /// Create a single stub with the given name, target address and flags. 283 virtual Error createStub(StringRef StubName, JITTargetAddress StubAddr, 284 JITSymbolFlags StubFlags) = 0; 285 286 /// Create StubInits.size() stubs with the given names, target 287 /// addresses, and flags. 288 virtual Error createStubs(const StubInitsMap &StubInits) = 0; 289 290 /// Find the stub with the given name. If ExportedStubsOnly is true, 291 /// this will only return a result if the stub's flags indicate that it 292 /// is exported. 293 virtual JITEvaluatedSymbol findStub(StringRef Name, bool ExportedStubsOnly) = 0; 294 295 /// Find the implementation-pointer for the stub. 296 virtual JITEvaluatedSymbol findPointer(StringRef Name) = 0; 297 298 /// Change the value of the implementation pointer for the stub. 299 virtual Error updatePointer(StringRef Name, JITTargetAddress NewAddr) = 0; 300 301 private: 302 virtual void anchor(); 303 }; 304 305 template <typename ORCABI> class LocalIndirectStubsInfo { 306 public: LocalIndirectStubsInfo(unsigned NumStubs,sys::OwningMemoryBlock StubsMem)307 LocalIndirectStubsInfo(unsigned NumStubs, sys::OwningMemoryBlock StubsMem) 308 : NumStubs(NumStubs), StubsMem(std::move(StubsMem)) {} 309 create(unsigned MinStubs,unsigned PageSize)310 static Expected<LocalIndirectStubsInfo> create(unsigned MinStubs, 311 unsigned PageSize) { 312 auto ISAS = getIndirectStubsBlockSizes<ORCABI>(MinStubs, PageSize); 313 314 assert((ISAS.StubBytes % PageSize == 0) && 315 "StubBytes is not a page size multiple"); 316 uint64_t PointerAlloc = alignTo(ISAS.PointerBytes, PageSize); 317 318 // Allocate memory for stubs and pointers in one call. 319 std::error_code EC; 320 auto StubsAndPtrsMem = 321 sys::OwningMemoryBlock(sys::Memory::allocateMappedMemory( 322 ISAS.StubBytes + PointerAlloc, nullptr, 323 sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC)); 324 if (EC) 325 return errorCodeToError(EC); 326 327 sys::MemoryBlock StubsBlock(StubsAndPtrsMem.base(), ISAS.StubBytes); 328 auto StubsBlockMem = static_cast<char *>(StubsAndPtrsMem.base()); 329 auto PtrBlockAddress = 330 pointerToJITTargetAddress(StubsBlockMem) + ISAS.StubBytes; 331 332 ORCABI::writeIndirectStubsBlock(StubsBlockMem, 333 pointerToJITTargetAddress(StubsBlockMem), 334 PtrBlockAddress, ISAS.NumStubs); 335 336 if (auto EC = sys::Memory::protectMappedMemory( 337 StubsBlock, sys::Memory::MF_READ | sys::Memory::MF_EXEC)) 338 return errorCodeToError(EC); 339 340 return LocalIndirectStubsInfo(ISAS.NumStubs, std::move(StubsAndPtrsMem)); 341 } 342 getNumStubs()343 unsigned getNumStubs() const { return NumStubs; } 344 getStub(unsigned Idx)345 void *getStub(unsigned Idx) const { 346 return static_cast<char *>(StubsMem.base()) + Idx * ORCABI::StubSize; 347 } 348 getPtr(unsigned Idx)349 void **getPtr(unsigned Idx) const { 350 char *PtrsBase = 351 static_cast<char *>(StubsMem.base()) + NumStubs * ORCABI::StubSize; 352 return reinterpret_cast<void **>(PtrsBase) + Idx; 353 } 354 355 private: 356 unsigned NumStubs = 0; 357 sys::OwningMemoryBlock StubsMem; 358 }; 359 360 /// IndirectStubsManager implementation for the host architecture, e.g. 361 /// OrcX86_64. (See OrcArchitectureSupport.h). 362 template <typename TargetT> 363 class LocalIndirectStubsManager : public IndirectStubsManager { 364 public: createStub(StringRef StubName,JITTargetAddress StubAddr,JITSymbolFlags StubFlags)365 Error createStub(StringRef StubName, JITTargetAddress StubAddr, 366 JITSymbolFlags StubFlags) override { 367 std::lock_guard<std::mutex> Lock(StubsMutex); 368 if (auto Err = reserveStubs(1)) 369 return Err; 370 371 createStubInternal(StubName, StubAddr, StubFlags); 372 373 return Error::success(); 374 } 375 createStubs(const StubInitsMap & StubInits)376 Error createStubs(const StubInitsMap &StubInits) override { 377 std::lock_guard<std::mutex> Lock(StubsMutex); 378 if (auto Err = reserveStubs(StubInits.size())) 379 return Err; 380 381 for (auto &Entry : StubInits) 382 createStubInternal(Entry.first(), Entry.second.first, 383 Entry.second.second); 384 385 return Error::success(); 386 } 387 findStub(StringRef Name,bool ExportedStubsOnly)388 JITEvaluatedSymbol findStub(StringRef Name, bool ExportedStubsOnly) override { 389 std::lock_guard<std::mutex> Lock(StubsMutex); 390 auto I = StubIndexes.find(Name); 391 if (I == StubIndexes.end()) 392 return nullptr; 393 auto Key = I->second.first; 394 void *StubAddr = IndirectStubsInfos[Key.first].getStub(Key.second); 395 assert(StubAddr && "Missing stub address"); 396 auto StubTargetAddr = 397 static_cast<JITTargetAddress>(reinterpret_cast<uintptr_t>(StubAddr)); 398 auto StubSymbol = JITEvaluatedSymbol(StubTargetAddr, I->second.second); 399 if (ExportedStubsOnly && !StubSymbol.getFlags().isExported()) 400 return nullptr; 401 return StubSymbol; 402 } 403 findPointer(StringRef Name)404 JITEvaluatedSymbol findPointer(StringRef Name) override { 405 std::lock_guard<std::mutex> Lock(StubsMutex); 406 auto I = StubIndexes.find(Name); 407 if (I == StubIndexes.end()) 408 return nullptr; 409 auto Key = I->second.first; 410 void *PtrAddr = IndirectStubsInfos[Key.first].getPtr(Key.second); 411 assert(PtrAddr && "Missing pointer address"); 412 auto PtrTargetAddr = 413 static_cast<JITTargetAddress>(reinterpret_cast<uintptr_t>(PtrAddr)); 414 return JITEvaluatedSymbol(PtrTargetAddr, I->second.second); 415 } 416 updatePointer(StringRef Name,JITTargetAddress NewAddr)417 Error updatePointer(StringRef Name, JITTargetAddress NewAddr) override { 418 using AtomicIntPtr = std::atomic<uintptr_t>; 419 420 std::lock_guard<std::mutex> Lock(StubsMutex); 421 auto I = StubIndexes.find(Name); 422 assert(I != StubIndexes.end() && "No stub pointer for symbol"); 423 auto Key = I->second.first; 424 AtomicIntPtr *AtomicStubPtr = reinterpret_cast<AtomicIntPtr *>( 425 IndirectStubsInfos[Key.first].getPtr(Key.second)); 426 *AtomicStubPtr = static_cast<uintptr_t>(NewAddr); 427 return Error::success(); 428 } 429 430 private: reserveStubs(unsigned NumStubs)431 Error reserveStubs(unsigned NumStubs) { 432 if (NumStubs <= FreeStubs.size()) 433 return Error::success(); 434 435 unsigned NewStubsRequired = NumStubs - FreeStubs.size(); 436 unsigned NewBlockId = IndirectStubsInfos.size(); 437 auto ISI = 438 LocalIndirectStubsInfo<TargetT>::create(NewStubsRequired, PageSize); 439 if (!ISI) 440 return ISI.takeError(); 441 for (unsigned I = 0; I < ISI->getNumStubs(); ++I) 442 FreeStubs.push_back(std::make_pair(NewBlockId, I)); 443 IndirectStubsInfos.push_back(std::move(*ISI)); 444 return Error::success(); 445 } 446 createStubInternal(StringRef StubName,JITTargetAddress InitAddr,JITSymbolFlags StubFlags)447 void createStubInternal(StringRef StubName, JITTargetAddress InitAddr, 448 JITSymbolFlags StubFlags) { 449 auto Key = FreeStubs.back(); 450 FreeStubs.pop_back(); 451 *IndirectStubsInfos[Key.first].getPtr(Key.second) = 452 jitTargetAddressToPointer<void *>(InitAddr); 453 StubIndexes[StubName] = std::make_pair(Key, StubFlags); 454 } 455 456 unsigned PageSize = sys::Process::getPageSizeEstimate(); 457 std::mutex StubsMutex; 458 std::vector<LocalIndirectStubsInfo<TargetT>> IndirectStubsInfos; 459 using StubKey = std::pair<uint16_t, uint16_t>; 460 std::vector<StubKey> FreeStubs; 461 StringMap<std::pair<StubKey, JITSymbolFlags>> StubIndexes; 462 }; 463 464 /// Create a local compile callback manager. 465 /// 466 /// The given target triple will determine the ABI, and the given 467 /// ErrorHandlerAddress will be used by the resulting compile callback 468 /// manager if a compile callback fails. 469 Expected<std::unique_ptr<JITCompileCallbackManager>> 470 createLocalCompileCallbackManager(const Triple &T, ExecutionSession &ES, 471 JITTargetAddress ErrorHandlerAddress); 472 473 /// Create a local indriect stubs manager builder. 474 /// 475 /// The given target triple will determine the ABI. 476 std::function<std::unique_ptr<IndirectStubsManager>()> 477 createLocalIndirectStubsManagerBuilder(const Triple &T); 478 479 /// Build a function pointer of FunctionType with the given constant 480 /// address. 481 /// 482 /// Usage example: Turn a trampoline address into a function pointer constant 483 /// for use in a stub. 484 Constant *createIRTypedAddress(FunctionType &FT, JITTargetAddress Addr); 485 486 /// Create a function pointer with the given type, name, and initializer 487 /// in the given Module. 488 GlobalVariable *createImplPointer(PointerType &PT, Module &M, const Twine &Name, 489 Constant *Initializer); 490 491 /// Turn a function declaration into a stub function that makes an 492 /// indirect call using the given function pointer. 493 void makeStub(Function &F, Value &ImplPointer); 494 495 /// Promotes private symbols to global hidden, and renames to prevent clashes 496 /// with other promoted symbols. The same SymbolPromoter instance should be 497 /// used for all symbols to be added to a single JITDylib. 498 class SymbolLinkagePromoter { 499 public: 500 /// Promote symbols in the given module. Returns the set of global values 501 /// that have been renamed/promoted. 502 std::vector<GlobalValue *> operator()(Module &M); 503 504 private: 505 unsigned NextId = 0; 506 }; 507 508 /// Clone a function declaration into a new module. 509 /// 510 /// This function can be used as the first step towards creating a callback 511 /// stub (see makeStub), or moving a function body (see moveFunctionBody). 512 /// 513 /// If the VMap argument is non-null, a mapping will be added between F and 514 /// the new declaration, and between each of F's arguments and the new 515 /// declaration's arguments. This map can then be passed in to moveFunction to 516 /// move the function body if required. Note: When moving functions between 517 /// modules with these utilities, all decls should be cloned (and added to a 518 /// single VMap) before any bodies are moved. This will ensure that references 519 /// between functions all refer to the versions in the new module. 520 Function *cloneFunctionDecl(Module &Dst, const Function &F, 521 ValueToValueMapTy *VMap = nullptr); 522 523 /// Move the body of function 'F' to a cloned function declaration in a 524 /// different module (See related cloneFunctionDecl). 525 /// 526 /// If the target function declaration is not supplied via the NewF parameter 527 /// then it will be looked up via the VMap. 528 /// 529 /// This will delete the body of function 'F' from its original parent module, 530 /// but leave its declaration. 531 void moveFunctionBody(Function &OrigF, ValueToValueMapTy &VMap, 532 ValueMaterializer *Materializer = nullptr, 533 Function *NewF = nullptr); 534 535 /// Clone a global variable declaration into a new module. 536 GlobalVariable *cloneGlobalVariableDecl(Module &Dst, const GlobalVariable &GV, 537 ValueToValueMapTy *VMap = nullptr); 538 539 /// Move global variable GV from its parent module to cloned global 540 /// declaration in a different module. 541 /// 542 /// If the target global declaration is not supplied via the NewGV parameter 543 /// then it will be looked up via the VMap. 544 /// 545 /// This will delete the initializer of GV from its original parent module, 546 /// but leave its declaration. 547 void moveGlobalVariableInitializer(GlobalVariable &OrigGV, 548 ValueToValueMapTy &VMap, 549 ValueMaterializer *Materializer = nullptr, 550 GlobalVariable *NewGV = nullptr); 551 552 /// Clone a global alias declaration into a new module. 553 GlobalAlias *cloneGlobalAliasDecl(Module &Dst, const GlobalAlias &OrigA, 554 ValueToValueMapTy &VMap); 555 556 /// Clone module flags metadata into the destination module. 557 void cloneModuleFlagsMetadata(Module &Dst, const Module &Src, 558 ValueToValueMapTy &VMap); 559 560 } // end namespace orc 561 562 } // end namespace llvm 563 564 #endif // LLVM_EXECUTIONENGINE_ORC_INDIRECTIONUTILS_H 565