| 1 | /*------------------------------------------------------------------------- |
| 2 | * |
| 3 | * llvmjit_inline.cpp |
| 4 | * Cross module inlining suitable for postgres' JIT |
| 5 | * |
| 6 | * The inliner iterates over external functions referenced from the passed |
| 7 | * module and attempts to inline those. It does so by utilizing pre-built |
| 8 | * indexes over both postgres core code and extension modules. When a match |
| 9 | * for an external function is found - not guaranteed! - the index will then |
| 10 | * be used to judge their instruction count / inline worthiness. After doing |
| 11 | * so for all external functions, all the referenced functions (and |
| 12 | * prerequisites) will be imported. |
| 13 | * |
| 14 | * Copyright (c) 2016-2019, PostgreSQL Global Development Group |
| 15 | * |
| 16 | * IDENTIFICATION |
| 17 | * src/backend/lib/llvmjit/llvmjit_inline.cpp |
| 18 | * |
| 19 | *------------------------------------------------------------------------- |
| 20 | */ |
| 21 | |
| 22 | extern "C" |
| 23 | { |
| 24 | #include "postgres.h" |
| 25 | } |
| 26 | |
| 27 | #include "jit/llvmjit.h" |
| 28 | |
| 29 | extern "C" |
| 30 | { |
| 31 | #include <fcntl.h> |
| 32 | #include <sys/mman.h> |
| 33 | #include <sys/stat.h> |
| 34 | #include <sys/types.h> |
| 35 | #include <unistd.h> |
| 36 | |
| 37 | #include "common/string.h" |
| 38 | #include "miscadmin.h" |
| 39 | #include "storage/fd.h" |
| 40 | } |
| 41 | |
| 42 | #include <llvm-c/Core.h> |
| 43 | #include <llvm-c/BitReader.h> |
| 44 | |
| 45 | /* Avoid macro clash with LLVM's C++ headers */ |
| 46 | #undef Min |
| 47 | |
| 48 | #include <llvm/ADT/SetVector.h> |
| 49 | #include <llvm/ADT/StringSet.h> |
| 50 | #include <llvm/ADT/StringMap.h> |
| 51 | #include <llvm/Analysis/ModuleSummaryAnalysis.h> |
| 52 | #if LLVM_VERSION_MAJOR > 3 |
| 53 | #include <llvm/Bitcode/BitcodeReader.h> |
| 54 | #else |
| 55 | #include <llvm/Bitcode/ReaderWriter.h> |
| 56 | #include <llvm/Support/Error.h> |
| 57 | #endif |
| 58 | #include <llvm/IR/Attributes.h> |
| 59 | #include <llvm/IR/CallSite.h> |
| 60 | #include <llvm/IR/DebugInfo.h> |
| 61 | #include <llvm/IR/IntrinsicInst.h> |
| 62 | #include <llvm/IR/IRBuilder.h> |
| 63 | #include <llvm/IR/ModuleSummaryIndex.h> |
| 64 | #include <llvm/Linker/IRMover.h> |
| 65 | #include <llvm/Support/ManagedStatic.h> |
| 66 | |
| 67 | |
| 68 | /* |
| 69 | * Type used to represent modules InlineWorkListItem's subject is searched for |
| 70 | * in. |
| 71 | */ |
| 72 | typedef llvm::SmallVector<llvm::ModuleSummaryIndex *, 2> InlineSearchPath; |
| 73 | |
| 74 | /* |
| 75 | * Item in queue of to-be-checked symbols and corresponding queue. |
| 76 | */ |
| 77 | typedef struct InlineWorkListItem |
| 78 | { |
| 79 | llvm::StringRef symbolName; |
| 80 | llvm::SmallVector<llvm::ModuleSummaryIndex *, 2> searchpath; |
| 81 | } InlineWorkListItem; |
| 82 | typedef llvm::SmallVector<InlineWorkListItem, 128> InlineWorkList; |
| 83 | |
| 84 | /* |
| 85 | * Information about symbols processed during inlining. Used to prevent |
| 86 | * repeated searches and provide additional information. |
| 87 | */ |
| 88 | typedef struct FunctionInlineState |
| 89 | { |
| 90 | int costLimit; |
| 91 | bool processed; |
| 92 | bool inlined; |
| 93 | bool allowReconsidering; |
| 94 | } FunctionInlineState; |
| 95 | typedef llvm::StringMap<FunctionInlineState> FunctionInlineStates; |
| 96 | |
| 97 | /* |
| 98 | * Map of modules that should be inlined, with a list of the to-be inlined |
| 99 | * symbols. |
| 100 | */ |
| 101 | typedef llvm::StringMap<llvm::StringSet<> > ImportMapTy; |
| 102 | |
| 103 | |
| 104 | const float inline_cost_decay_factor = 0.5; |
| 105 | const int inline_initial_cost = 150; |
| 106 | |
| 107 | /* |
| 108 | * These are managed statics so LLVM knows to deallocate them during an |
| 109 | * LLVMShutdown(), rather than after (which'd cause crashes). |
| 110 | */ |
| 111 | typedef llvm::StringMap<std::unique_ptr<llvm::Module> > ModuleCache; |
| 112 | llvm::ManagedStatic<ModuleCache> module_cache; |
| 113 | typedef llvm::StringMap<std::unique_ptr<llvm::ModuleSummaryIndex> > SummaryCache; |
| 114 | llvm::ManagedStatic<SummaryCache> summary_cache; |
| 115 | |
| 116 | |
| 117 | static std::unique_ptr<ImportMapTy> llvm_build_inline_plan(llvm::Module *mod); |
| 118 | static void llvm_execute_inline_plan(llvm::Module *mod, |
| 119 | ImportMapTy *globalsToInline); |
| 120 | |
| 121 | static llvm::Module* load_module_cached(llvm::StringRef modPath); |
| 122 | static std::unique_ptr<llvm::Module> load_module(llvm::StringRef Identifier); |
| 123 | static std::unique_ptr<llvm::ModuleSummaryIndex> llvm_load_summary(llvm::StringRef path); |
| 124 | |
| 125 | |
| 126 | static llvm::Function* create_redirection_function(std::unique_ptr<llvm::Module> &importMod, |
| 127 | llvm::Function *F, |
| 128 | llvm::StringRef Name); |
| 129 | |
| 130 | static bool function_inlinable(llvm::Function &F, |
| 131 | int threshold, |
| 132 | FunctionInlineStates &functionState, |
| 133 | InlineWorkList &worklist, |
| 134 | InlineSearchPath &searchpath, |
| 135 | llvm::SmallPtrSet<const llvm::Function *, 8> &visitedFunctions, |
| 136 | int &running_instcount, |
| 137 | llvm::StringSet<> &importVars); |
| 138 | static void function_references(llvm::Function &F, |
| 139 | int &running_instcount, |
| 140 | llvm::SmallPtrSet<llvm::GlobalVariable *, 8> &referencedVars, |
| 141 | llvm::SmallPtrSet<llvm::Function *, 8> &referencedFunctions); |
| 142 | |
| 143 | static void add_module_to_inline_search_path(InlineSearchPath& path, llvm::StringRef modpath); |
| 144 | static llvm::SmallVector<llvm::GlobalValueSummary *, 1> |
| 145 | summaries_for_guid(const InlineSearchPath& path, llvm::GlobalValue::GUID guid); |
| 146 | |
| 147 | /* verbose debugging for inliner development */ |
| 148 | /* #define INLINE_DEBUG */ |
| 149 | #ifdef INLINE_DEBUG |
| 150 | #define ilog elog |
| 151 | #else |
| 152 | #define ilog(...) (void) 0 |
| 153 | #endif |
| 154 | |
| 155 | /* |
| 156 | * Perform inlining of external function references in M based on a simple |
| 157 | * cost based analysis. |
| 158 | */ |
| 159 | void |
| 160 | llvm_inline(LLVMModuleRef M) |
| 161 | { |
| 162 | llvm::Module *mod = llvm::unwrap(M); |
| 163 | |
| 164 | std::unique_ptr<ImportMapTy> globalsToInline = llvm_build_inline_plan(mod); |
| 165 | if (!globalsToInline) |
| 166 | return; |
| 167 | llvm_execute_inline_plan(mod, globalsToInline.get()); |
| 168 | } |
| 169 | |
| 170 | /* |
| 171 | * Build information necessary for inlining external function references in |
| 172 | * mod. |
| 173 | */ |
| 174 | static std::unique_ptr<ImportMapTy> |
| 175 | llvm_build_inline_plan(llvm::Module *mod) |
| 176 | { |
| 177 | std::unique_ptr<ImportMapTy> globalsToInline(new ImportMapTy()); |
| 178 | FunctionInlineStates functionStates; |
| 179 | InlineWorkList worklist; |
| 180 | |
| 181 | InlineSearchPath defaultSearchPath; |
| 182 | |
| 183 | /* attempt to add module to search path */ |
| 184 | add_module_to_inline_search_path(defaultSearchPath, "$libdir/postgres" ); |
| 185 | /* if postgres isn't available, no point continuing */ |
| 186 | if (defaultSearchPath.empty()) |
| 187 | return nullptr; |
| 188 | |
| 189 | /* |
| 190 | * Start inlining with current references to external functions by putting |
| 191 | * them on the inlining worklist. If, during inlining of those, new extern |
| 192 | * functions need to be inlined, they'll also be put there, with a lower |
| 193 | * priority. |
| 194 | */ |
| 195 | for (const llvm::Function &funcDecl : mod->functions()) |
| 196 | { |
| 197 | InlineWorkListItem item = {}; |
| 198 | FunctionInlineState inlineState = {}; |
| 199 | |
| 200 | /* already has a definition */ |
| 201 | if (!funcDecl.isDeclaration()) |
| 202 | continue; |
| 203 | |
| 204 | /* llvm provides implementation */ |
| 205 | if (funcDecl.isIntrinsic()) |
| 206 | continue; |
| 207 | |
| 208 | item.symbolName = funcDecl.getName(); |
| 209 | item.searchpath = defaultSearchPath; |
| 210 | worklist.push_back(item); |
| 211 | inlineState.costLimit = inline_initial_cost; |
| 212 | inlineState.processed = false; |
| 213 | inlineState.inlined = false; |
| 214 | inlineState.allowReconsidering = false; |
| 215 | functionStates[funcDecl.getName()] = inlineState; |
| 216 | } |
| 217 | |
| 218 | /* |
| 219 | * Iterate over pending worklist items, look them up in index, check |
| 220 | * whether they should be inlined. |
| 221 | */ |
| 222 | while (!worklist.empty()) |
| 223 | { |
| 224 | InlineWorkListItem item = worklist.pop_back_val(); |
| 225 | llvm::StringRef symbolName = item.symbolName; |
| 226 | char *cmodname; |
| 227 | char *cfuncname; |
| 228 | FunctionInlineState &inlineState = functionStates[symbolName]; |
| 229 | llvm::GlobalValue::GUID funcGUID; |
| 230 | |
| 231 | llvm_split_symbol_name(symbolName.data(), &cmodname, &cfuncname); |
| 232 | |
| 233 | funcGUID = llvm::GlobalValue::getGUID(cfuncname); |
| 234 | |
| 235 | /* already processed */ |
| 236 | if (inlineState.processed) |
| 237 | continue; |
| 238 | |
| 239 | |
| 240 | if (cmodname) |
| 241 | add_module_to_inline_search_path(item.searchpath, cmodname); |
| 242 | |
| 243 | /* |
| 244 | * Iterate over all known definitions of function, via the index. Then |
| 245 | * look up module(s), check if function actually is defined (there |
| 246 | * could be hash conflicts). |
| 247 | */ |
| 248 | for (const auto &gvs : summaries_for_guid(item.searchpath, funcGUID)) |
| 249 | { |
| 250 | const llvm::FunctionSummary *fs; |
| 251 | llvm::StringRef modPath = gvs->modulePath(); |
| 252 | llvm::Module *defMod; |
| 253 | llvm::Function *funcDef; |
| 254 | |
| 255 | fs = llvm::cast<llvm::FunctionSummary>(gvs); |
| 256 | |
| 257 | #if LLVM_VERSION_MAJOR > 3 |
| 258 | if (gvs->notEligibleToImport()) |
| 259 | { |
| 260 | ilog(DEBUG1, "ineligibile to import %s due to summary" , |
| 261 | symbolName.data()); |
| 262 | continue; |
| 263 | } |
| 264 | #endif |
| 265 | |
| 266 | if ((int) fs->instCount() > inlineState.costLimit) |
| 267 | { |
| 268 | ilog(DEBUG1, "ineligibile to import %s due to early threshold: %u vs %u" , |
| 269 | symbolName.data(), fs->instCount(), inlineState.costLimit); |
| 270 | inlineState.allowReconsidering = true; |
| 271 | continue; |
| 272 | } |
| 273 | |
| 274 | defMod = load_module_cached(modPath); |
| 275 | if (defMod->materializeMetadata()) |
| 276 | elog(FATAL, "failed to materialize metadata" ); |
| 277 | |
| 278 | funcDef = defMod->getFunction(cfuncname); |
| 279 | |
| 280 | /* |
| 281 | * This can happen e.g. in case of a hash collision of the |
| 282 | * function's name. |
| 283 | */ |
| 284 | if (!funcDef) |
| 285 | continue; |
| 286 | |
| 287 | if (funcDef->materialize()) |
| 288 | elog(FATAL, "failed to materialize metadata" ); |
| 289 | |
| 290 | Assert(!funcDef->isDeclaration()); |
| 291 | Assert(funcDef->hasExternalLinkage()); |
| 292 | |
| 293 | llvm::StringSet<> importVars; |
| 294 | llvm::SmallPtrSet<const llvm::Function *, 8> visitedFunctions; |
| 295 | int running_instcount = 0; |
| 296 | |
| 297 | /* |
| 298 | * Check whether function, and objects it depends on, are |
| 299 | * inlinable. |
| 300 | */ |
| 301 | if (function_inlinable(*funcDef, |
| 302 | inlineState.costLimit, |
| 303 | functionStates, |
| 304 | worklist, |
| 305 | item.searchpath, |
| 306 | visitedFunctions, |
| 307 | running_instcount, |
| 308 | importVars)) |
| 309 | { |
| 310 | /* |
| 311 | * Check whether function and all its dependencies are too |
| 312 | * big. Dependencies already counted for other functions that |
| 313 | * will get inlined are not counted again. While this make |
| 314 | * things somewhat order dependent, I can't quite see a point |
| 315 | * in a different behaviour. |
| 316 | */ |
| 317 | if (running_instcount > inlineState.costLimit) |
| 318 | { |
| 319 | ilog(DEBUG1, "skipping inlining of %s due to late threshold %d vs %d" , |
| 320 | symbolName.data(), running_instcount, inlineState.costLimit); |
| 321 | inlineState.allowReconsidering = true; |
| 322 | continue; |
| 323 | } |
| 324 | |
| 325 | ilog(DEBUG1, "inline top function %s total_instcount: %d, partial: %d" , |
| 326 | symbolName.data(), running_instcount, fs->instCount()); |
| 327 | |
| 328 | /* import referenced function itself */ |
| 329 | importVars.insert(symbolName); |
| 330 | |
| 331 | { |
| 332 | llvm::StringSet<> &modGlobalsToInline = (*globalsToInline)[modPath]; |
| 333 | for (auto& importVar : importVars) |
| 334 | modGlobalsToInline.insert(importVar.first()); |
| 335 | Assert(modGlobalsToInline.size() > 0); |
| 336 | } |
| 337 | |
| 338 | /* mark function as inlined */ |
| 339 | inlineState.inlined = true; |
| 340 | |
| 341 | /* |
| 342 | * Found definition to inline, don't look for further |
| 343 | * potential definitions. |
| 344 | */ |
| 345 | break; |
| 346 | } |
| 347 | else |
| 348 | { |
| 349 | ilog(DEBUG1, "had to skip inlining %s" , |
| 350 | symbolName.data()); |
| 351 | |
| 352 | /* It's possible there's another definition that's inlinable. */ |
| 353 | } |
| 354 | } |
| 355 | |
| 356 | /* |
| 357 | * Signal that we're done with symbol, whether successful (inlined = |
| 358 | * true above) or not. |
| 359 | */ |
| 360 | inlineState.processed = true; |
| 361 | } |
| 362 | |
| 363 | return globalsToInline; |
| 364 | } |
| 365 | |
| 366 | /* |
| 367 | * Perform the actual inlining of external functions (and their dependencies) |
| 368 | * into mod. |
| 369 | */ |
| 370 | static void |
| 371 | llvm_execute_inline_plan(llvm::Module *mod, ImportMapTy *globalsToInline) |
| 372 | { |
| 373 | llvm::IRMover Mover(*mod); |
| 374 | |
| 375 | for (const auto& toInline : *globalsToInline) |
| 376 | { |
| 377 | const llvm::StringRef& modPath = toInline.first(); |
| 378 | const llvm::StringSet<>& modGlobalsToInline = toInline.second; |
| 379 | llvm::SetVector<llvm::GlobalValue *> GlobalsToImport; |
| 380 | |
| 381 | Assert(module_cache->count(modPath)); |
| 382 | std::unique_ptr<llvm::Module> importMod(std::move((*module_cache)[modPath])); |
| 383 | module_cache->erase(modPath); |
| 384 | |
| 385 | if (modGlobalsToInline.empty()) |
| 386 | continue; |
| 387 | |
| 388 | for (auto &glob: modGlobalsToInline) |
| 389 | { |
| 390 | llvm::StringRef SymbolName = glob.first(); |
| 391 | char *modname; |
| 392 | char *funcname; |
| 393 | |
| 394 | llvm_split_symbol_name(SymbolName.data(), &modname, &funcname); |
| 395 | |
| 396 | llvm::GlobalValue *valueToImport = importMod->getNamedValue(funcname); |
| 397 | |
| 398 | if (!valueToImport) |
| 399 | elog(FATAL, "didn't refind value %s to import" , SymbolName.data()); |
| 400 | |
| 401 | /* |
| 402 | * For functions (global vars are only inlined if already static), |
| 403 | * mark imported variables as being clones from other |
| 404 | * functions. That a) avoids symbol conflicts b) allows the |
| 405 | * optimizer to perform inlining. |
| 406 | */ |
| 407 | if (llvm::isa<llvm::Function>(valueToImport)) |
| 408 | { |
| 409 | llvm::Function *F = llvm::dyn_cast<llvm::Function>(valueToImport); |
| 410 | typedef llvm::GlobalValue::LinkageTypes LinkageTypes; |
| 411 | |
| 412 | /* |
| 413 | * Per-function info isn't necessarily stripped yet, as the |
| 414 | * module is lazy-loaded when stripped above. |
| 415 | */ |
| 416 | llvm::stripDebugInfo(*F); |
| 417 | |
| 418 | /* |
| 419 | * If the to-be-imported function is one referenced including |
| 420 | * its module name, create a tiny inline function that just |
| 421 | * forwards the call. One might think a GlobalAlias would do |
| 422 | * the trick, but a) IRMover doesn't override a declaration |
| 423 | * with an alias pointing to a definition (instead renaming |
| 424 | * it), b) Aliases can't be AvailableExternally. |
| 425 | */ |
| 426 | if (modname) |
| 427 | { |
| 428 | llvm::Function *AF; |
| 429 | |
| 430 | AF = create_redirection_function(importMod, F, SymbolName); |
| 431 | |
| 432 | GlobalsToImport.insert(AF); |
| 433 | llvm::stripDebugInfo(*AF); |
| 434 | } |
| 435 | |
| 436 | if (valueToImport->hasExternalLinkage()) |
| 437 | { |
| 438 | valueToImport->setLinkage(LinkageTypes::AvailableExternallyLinkage); |
| 439 | } |
| 440 | } |
| 441 | |
| 442 | GlobalsToImport.insert(valueToImport); |
| 443 | ilog(DEBUG1, "performing import of %s %s" , |
| 444 | modPath.data(), SymbolName.data()); |
| 445 | |
| 446 | } |
| 447 | |
| 448 | #if LLVM_VERSION_MAJOR > 4 |
| 449 | #define IRMOVE_PARAMS , /*IsPerformingImport=*/false |
| 450 | #elif LLVM_VERSION_MAJOR > 3 |
| 451 | #define IRMOVE_PARAMS , /*LinkModuleInlineAsm=*/false, /*IsPerformingImport=*/false |
| 452 | #else |
| 453 | #define IRMOVE_PARAMS |
| 454 | #endif |
| 455 | if (Mover.move(std::move(importMod), GlobalsToImport.getArrayRef(), |
| 456 | [](llvm::GlobalValue &, llvm::IRMover::ValueAdder) {} |
| 457 | IRMOVE_PARAMS)) |
| 458 | elog(FATAL, "function import failed with linker error" ); |
| 459 | } |
| 460 | } |
| 461 | |
| 462 | /* |
| 463 | * Return a module identified by modPath, caching it in memory. |
| 464 | * |
| 465 | * Note that such a module may *not* be modified without copying, otherwise |
| 466 | * the cache state would get corrupted. |
| 467 | */ |
| 468 | static llvm::Module* |
| 469 | load_module_cached(llvm::StringRef modPath) |
| 470 | { |
| 471 | auto it = module_cache->find(modPath); |
| 472 | if (it == module_cache->end()) |
| 473 | { |
| 474 | it = module_cache->insert( |
| 475 | std::make_pair(modPath, load_module(modPath))).first; |
| 476 | } |
| 477 | |
| 478 | return it->second.get(); |
| 479 | } |
| 480 | |
| 481 | static std::unique_ptr<llvm::Module> |
| 482 | load_module(llvm::StringRef Identifier) |
| 483 | { |
| 484 | LLVMMemoryBufferRef buf; |
| 485 | LLVMModuleRef mod; |
| 486 | char path[MAXPGPATH]; |
| 487 | char *msg; |
| 488 | |
| 489 | snprintf(path, MAXPGPATH,"%s/bitcode/%s" , pkglib_path, Identifier.data()); |
| 490 | |
| 491 | if (LLVMCreateMemoryBufferWithContentsOfFile(path, &buf, &msg)) |
| 492 | elog(FATAL, "failed to open bitcode file \"%s\": %s" , |
| 493 | path, msg); |
| 494 | if (LLVMGetBitcodeModuleInContext2(LLVMGetGlobalContext(), buf, &mod)) |
| 495 | elog(FATAL, "failed to parse bitcode in file \"%s\"" , path); |
| 496 | |
| 497 | /* |
| 498 | * Currently there's no use in more detailed debug info for JITed |
| 499 | * code. Until that changes, not much point in wasting memory and cycles |
| 500 | * on processing debuginfo. |
| 501 | */ |
| 502 | llvm::StripDebugInfo(*llvm::unwrap(mod)); |
| 503 | |
| 504 | return std::unique_ptr<llvm::Module>(llvm::unwrap(mod)); |
| 505 | } |
| 506 | |
| 507 | /* |
| 508 | * Compute list of referenced variables, functions and the instruction count |
| 509 | * for a function. |
| 510 | */ |
| 511 | static void |
| 512 | function_references(llvm::Function &F, |
| 513 | int &running_instcount, |
| 514 | llvm::SmallPtrSet<llvm::GlobalVariable *, 8> &referencedVars, |
| 515 | llvm::SmallPtrSet<llvm::Function *, 8> &referencedFunctions) |
| 516 | { |
| 517 | llvm::SmallPtrSet<const llvm::User *, 32> Visited; |
| 518 | |
| 519 | for (llvm::BasicBlock &BB : F) |
| 520 | { |
| 521 | for (llvm::Instruction &I : BB) |
| 522 | { |
| 523 | if (llvm::isa<llvm::DbgInfoIntrinsic>(I)) |
| 524 | continue; |
| 525 | |
| 526 | llvm::SmallVector<llvm::User *, 8> Worklist; |
| 527 | Worklist.push_back(&I); |
| 528 | |
| 529 | running_instcount++; |
| 530 | |
| 531 | while (!Worklist.empty()) { |
| 532 | llvm::User *U = Worklist.pop_back_val(); |
| 533 | |
| 534 | /* visited before */ |
| 535 | if (!Visited.insert(U).second) |
| 536 | continue; |
| 537 | |
| 538 | for (auto &OI : U->operands()) { |
| 539 | llvm::User *Operand = llvm::dyn_cast<llvm::User>(OI); |
| 540 | if (!Operand) |
| 541 | continue; |
| 542 | if (llvm::isa<llvm::BlockAddress>(Operand)) |
| 543 | continue; |
| 544 | if (auto *GV = llvm::dyn_cast<llvm::GlobalVariable>(Operand)) { |
| 545 | referencedVars.insert(GV); |
| 546 | if (GV->hasInitializer()) |
| 547 | Worklist.push_back(GV->getInitializer()); |
| 548 | continue; |
| 549 | } |
| 550 | if (auto *CF = llvm::dyn_cast<llvm::Function>(Operand)) { |
| 551 | referencedFunctions.insert(CF); |
| 552 | continue; |
| 553 | } |
| 554 | Worklist.push_back(Operand); |
| 555 | } |
| 556 | } |
| 557 | } |
| 558 | } |
| 559 | } |
| 560 | |
| 561 | /* |
| 562 | * Check whether function F is inlinable and, if so, what globals need to be |
| 563 | * imported. |
| 564 | * |
| 565 | * References to external functions from, potentially recursively, inlined |
| 566 | * functions are added to the passed in worklist. |
| 567 | */ |
| 568 | static bool |
| 569 | function_inlinable(llvm::Function &F, |
| 570 | int threshold, |
| 571 | FunctionInlineStates &functionStates, |
| 572 | InlineWorkList &worklist, |
| 573 | InlineSearchPath &searchpath, |
| 574 | llvm::SmallPtrSet<const llvm::Function *, 8> &visitedFunctions, |
| 575 | int &running_instcount, |
| 576 | llvm::StringSet<> &importVars) |
| 577 | { |
| 578 | int subThreshold = threshold * inline_cost_decay_factor; |
| 579 | llvm::SmallPtrSet<llvm::GlobalVariable *, 8> referencedVars; |
| 580 | llvm::SmallPtrSet<llvm::Function *, 8> referencedFunctions; |
| 581 | |
| 582 | /* can't rely on what may be inlined */ |
| 583 | if (F.isInterposable()) |
| 584 | return false; |
| 585 | |
| 586 | /* |
| 587 | * Can't rely on function being present. Alternatively we could create a |
| 588 | * static version of these functions? |
| 589 | */ |
| 590 | if (F.hasAvailableExternallyLinkage()) |
| 591 | return false; |
| 592 | |
| 593 | ilog(DEBUG1, "checking inlinability of %s" , F.getName().data()); |
| 594 | |
| 595 | if (F.materialize()) |
| 596 | elog(FATAL, "failed to materialize metadata" ); |
| 597 | |
| 598 | if (F.getAttributes().hasFnAttribute(llvm::Attribute::NoInline)) |
| 599 | { |
| 600 | ilog(DEBUG1, "ineligibile to import %s due to noinline" , |
| 601 | F.getName().data()); |
| 602 | return false; |
| 603 | } |
| 604 | |
| 605 | function_references(F, running_instcount, referencedVars, referencedFunctions); |
| 606 | |
| 607 | for (llvm::GlobalVariable* rv: referencedVars) |
| 608 | { |
| 609 | if (rv->materialize()) |
| 610 | elog(FATAL, "failed to materialize metadata" ); |
| 611 | |
| 612 | /* |
| 613 | * Never want to inline externally visible vars, cheap enough to |
| 614 | * reference. |
| 615 | */ |
| 616 | if (rv->hasExternalLinkage() || rv->hasAvailableExternallyLinkage()) |
| 617 | continue; |
| 618 | |
| 619 | /* |
| 620 | * If variable is file-local, we need to inline it, to be able to |
| 621 | * inline the function itself. Can't do that if the variable can be |
| 622 | * modified, because they'd obviously get out of sync. |
| 623 | * |
| 624 | * XXX: Currently not a problem, but there'd be problems with |
| 625 | * nontrivial initializers if they were allowed for postgres. |
| 626 | */ |
| 627 | if (!rv->isConstant()) |
| 628 | { |
| 629 | ilog(DEBUG1, "cannot inline %s due to uncloneable variable %s" , |
| 630 | F.getName().data(), rv->getName().data()); |
| 631 | return false; |
| 632 | } |
| 633 | |
| 634 | ilog(DEBUG1, "memorizing global var %s linkage %d for inlining" , |
| 635 | rv->getName().data(), (int)rv->getLinkage()); |
| 636 | |
| 637 | importVars.insert(rv->getName()); |
| 638 | /* small cost attributed to each cloned global */ |
| 639 | running_instcount += 5; |
| 640 | } |
| 641 | |
| 642 | visitedFunctions.insert(&F); |
| 643 | |
| 644 | /* |
| 645 | * Check referenced functions. Check whether used static ones are |
| 646 | * inlinable, and remember external ones for inlining. |
| 647 | */ |
| 648 | for (llvm::Function* referencedFunction: referencedFunctions) |
| 649 | { |
| 650 | llvm::StringSet<> recImportVars; |
| 651 | |
| 652 | if (referencedFunction->materialize()) |
| 653 | elog(FATAL, "failed to materialize metadata" ); |
| 654 | |
| 655 | if (referencedFunction->isIntrinsic()) |
| 656 | continue; |
| 657 | |
| 658 | /* if already visited skip, otherwise remember */ |
| 659 | if (!visitedFunctions.insert(referencedFunction).second) |
| 660 | continue; |
| 661 | |
| 662 | /* |
| 663 | * We don't inline external functions directly here, instead we put |
| 664 | * them on the worklist if appropriate and check them from |
| 665 | * llvm_build_inline_plan(). |
| 666 | */ |
| 667 | if (referencedFunction->hasExternalLinkage()) |
| 668 | { |
| 669 | llvm::StringRef funcName = referencedFunction->getName(); |
| 670 | |
| 671 | /* |
| 672 | * Don't bother checking for inlining if remaining cost budget is |
| 673 | * very small. |
| 674 | */ |
| 675 | if (subThreshold < 5) |
| 676 | continue; |
| 677 | |
| 678 | auto it = functionStates.find(funcName); |
| 679 | if (it == functionStates.end()) |
| 680 | { |
| 681 | FunctionInlineState inlineState; |
| 682 | |
| 683 | inlineState.costLimit = subThreshold; |
| 684 | inlineState.processed = false; |
| 685 | inlineState.inlined = false; |
| 686 | inlineState.allowReconsidering = false; |
| 687 | |
| 688 | functionStates[funcName] = inlineState; |
| 689 | worklist.push_back({funcName, searchpath}); |
| 690 | |
| 691 | ilog(DEBUG1, |
| 692 | "considering extern function %s at %d for inlining" , |
| 693 | funcName.data(), subThreshold); |
| 694 | } |
| 695 | else if (!it->second.inlined && |
| 696 | (!it->second.processed || it->second.allowReconsidering) && |
| 697 | it->second.costLimit < subThreshold) |
| 698 | { |
| 699 | /* |
| 700 | * Update inlining threshold if higher. Need to re-queue |
| 701 | * to be processed if already processed with lower |
| 702 | * threshold. |
| 703 | */ |
| 704 | if (it->second.processed) |
| 705 | { |
| 706 | ilog(DEBUG1, |
| 707 | "reconsidering extern function %s at %d for inlining, increasing from %d" , |
| 708 | funcName.data(), subThreshold, it->second.costLimit); |
| 709 | |
| 710 | it->second.processed = false; |
| 711 | it->second.allowReconsidering = false; |
| 712 | worklist.push_back({funcName, searchpath}); |
| 713 | } |
| 714 | it->second.costLimit = subThreshold; |
| 715 | } |
| 716 | continue; |
| 717 | } |
| 718 | |
| 719 | /* can't rely on what may be inlined */ |
| 720 | if (referencedFunction->isInterposable()) |
| 721 | return false; |
| 722 | |
| 723 | if (!function_inlinable(*referencedFunction, |
| 724 | subThreshold, |
| 725 | functionStates, |
| 726 | worklist, |
| 727 | searchpath, |
| 728 | visitedFunctions, |
| 729 | running_instcount, |
| 730 | recImportVars)) |
| 731 | { |
| 732 | ilog(DEBUG1, |
| 733 | "cannot inline %s due to required function %s not being inlinable" , |
| 734 | F.getName().data(), referencedFunction->getName().data()); |
| 735 | return false; |
| 736 | } |
| 737 | |
| 738 | /* import referenced function itself */ |
| 739 | importVars.insert(referencedFunction->getName()); |
| 740 | |
| 741 | /* import referenced function and its dependants */ |
| 742 | for (auto& recImportVar : recImportVars) |
| 743 | importVars.insert(recImportVar.first()); |
| 744 | } |
| 745 | |
| 746 | return true; |
| 747 | } |
| 748 | |
| 749 | /* |
| 750 | * Attempt to load module summary located at path. Return empty pointer when |
| 751 | * loading fails. |
| 752 | */ |
| 753 | static std::unique_ptr<llvm::ModuleSummaryIndex> |
| 754 | llvm_load_summary(llvm::StringRef path) |
| 755 | { |
| 756 | llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer> > MBOrErr = |
| 757 | llvm::MemoryBuffer::getFile(path); |
| 758 | |
| 759 | if (std::error_code EC = MBOrErr.getError()) |
| 760 | { |
| 761 | ilog(DEBUG1, "failed to open %s: %s" , path.data(), |
| 762 | EC.message().c_str()); |
| 763 | } |
| 764 | else |
| 765 | { |
| 766 | llvm::MemoryBufferRef ref(*MBOrErr.get().get()); |
| 767 | |
| 768 | #if LLVM_VERSION_MAJOR > 3 |
| 769 | llvm::Expected<std::unique_ptr<llvm::ModuleSummaryIndex> > IndexOrErr = |
| 770 | llvm::getModuleSummaryIndex(ref); |
| 771 | if (IndexOrErr) |
| 772 | return std::move(IndexOrErr.get()); |
| 773 | elog(FATAL, "failed to load summary \"%s\": %s" , |
| 774 | path.data(), |
| 775 | toString(IndexOrErr.takeError()).c_str()); |
| 776 | #else |
| 777 | llvm::ErrorOr<std::unique_ptr<llvm::ModuleSummaryIndex> > IndexOrErr = |
| 778 | llvm::getModuleSummaryIndex(ref, [](const llvm::DiagnosticInfo &) {}); |
| 779 | if (IndexOrErr) |
| 780 | return std::move(IndexOrErr.get()); |
| 781 | elog(FATAL, "failed to load summary \"%s\": %s" , |
| 782 | path.data(), |
| 783 | IndexOrErr.getError().message().c_str()); |
| 784 | #endif |
| 785 | } |
| 786 | return nullptr; |
| 787 | } |
| 788 | |
| 789 | /* |
| 790 | * Attempt to add modpath to the search path. |
| 791 | */ |
| 792 | static void |
| 793 | add_module_to_inline_search_path(InlineSearchPath& searchpath, llvm::StringRef modpath) |
| 794 | { |
| 795 | /* only extension in libdir are candidates for inlining for now */ |
| 796 | if (!modpath.startswith("$libdir/" )) |
| 797 | return; |
| 798 | |
| 799 | /* if there's no match, attempt to load */ |
| 800 | auto it = summary_cache->find(modpath); |
| 801 | if (it == summary_cache->end()) |
| 802 | { |
| 803 | std::string path(modpath); |
| 804 | path = path.replace(0, strlen("$libdir" ), std::string(pkglib_path) + "/bitcode" ); |
| 805 | path += ".index.bc" ; |
| 806 | (*summary_cache)[modpath] = llvm_load_summary(path); |
| 807 | it = summary_cache->find(modpath); |
| 808 | } |
| 809 | |
| 810 | Assert(it != summary_cache->end()); |
| 811 | |
| 812 | /* if the entry isn't NULL, it's validly loaded */ |
| 813 | if (it->second) |
| 814 | searchpath.push_back(it->second.get()); |
| 815 | } |
| 816 | |
| 817 | /* |
| 818 | * Search for all references for functions hashing to guid in the search path, |
| 819 | * and return them in search path order. |
| 820 | */ |
| 821 | static llvm::SmallVector<llvm::GlobalValueSummary *, 1> |
| 822 | summaries_for_guid(const InlineSearchPath& path, llvm::GlobalValue::GUID guid) |
| 823 | { |
| 824 | llvm::SmallVector<llvm::GlobalValueSummary *, 1> matches; |
| 825 | |
| 826 | for (auto index : path) |
| 827 | { |
| 828 | #if LLVM_VERSION_MAJOR > 4 |
| 829 | llvm::ValueInfo funcVI = index->getValueInfo(guid); |
| 830 | |
| 831 | /* if index doesn't know function, we don't have a body, continue */ |
| 832 | if (funcVI) |
| 833 | for (auto &gv : funcVI.getSummaryList()) |
| 834 | matches.push_back(gv.get()); |
| 835 | #else |
| 836 | const llvm::const_gvsummary_iterator &I = |
| 837 | index->findGlobalValueSummaryList(guid); |
| 838 | if (I != index->end()) |
| 839 | { |
| 840 | for (auto &gv : I->second) |
| 841 | matches.push_back(gv.get()); |
| 842 | } |
| 843 | #endif |
| 844 | } |
| 845 | |
| 846 | return matches; |
| 847 | } |
| 848 | |
| 849 | /* |
| 850 | * Create inline wrapper with the name Name, redirecting the call to F. |
| 851 | */ |
| 852 | static llvm::Function* |
| 853 | create_redirection_function(std::unique_ptr<llvm::Module> &importMod, |
| 854 | llvm::Function *F, |
| 855 | llvm::StringRef Name) |
| 856 | { |
| 857 | typedef llvm::GlobalValue::LinkageTypes LinkageTypes; |
| 858 | |
| 859 | llvm::LLVMContext &Context = F->getContext(); |
| 860 | llvm::IRBuilder<> Builder(Context); |
| 861 | llvm::Function *AF; |
| 862 | llvm::BasicBlock *BB; |
| 863 | llvm::CallInst *fwdcall; |
| 864 | llvm::Attribute inlineAttribute; |
| 865 | |
| 866 | AF = llvm::Function::Create(F->getFunctionType(), |
| 867 | LinkageTypes::AvailableExternallyLinkage, |
| 868 | Name, importMod.get()); |
| 869 | BB = llvm::BasicBlock::Create(Context, "entry" , AF); |
| 870 | |
| 871 | Builder.SetInsertPoint(BB); |
| 872 | fwdcall = Builder.CreateCall(F, &*AF->arg_begin()); |
| 873 | inlineAttribute = llvm::Attribute::get(Context, |
| 874 | llvm::Attribute::AlwaysInline); |
| 875 | fwdcall->addAttribute(~0U, inlineAttribute); |
| 876 | Builder.CreateRet(fwdcall); |
| 877 | |
| 878 | return AF; |
| 879 | } |
| 880 | |