LCOV - code coverage report
Current view: top level - src/backend/jit/llvm - llvmjit_inline.cpp (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 227 253 89.7 %
Date: 2023-12-07 07:10:44 Functions: 12 13 92.3 %
Legend: Lines: hit not hit

          Line data    Source code
       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-2023, 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/DebugInfo.h>
      60             : #include <llvm/IR/IntrinsicInst.h>
      61             : #include <llvm/IR/IRBuilder.h>
      62             : #include <llvm/IR/ModuleSummaryIndex.h>
      63             : #include <llvm/Linker/IRMover.h>
      64             : #include <llvm/Support/ManagedStatic.h>
      65             : #include <llvm/Support/MemoryBuffer.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(LLVMContextRef lc, llvm::Module *mod);
     118             : static void llvm_execute_inline_plan(llvm::Module *mod,
     119             :                                      ImportMapTy *globalsToInline);
     120             : 
     121             : static llvm::Module* load_module_cached(LLVMContextRef c, llvm::StringRef modPath);
     122             : static std::unique_ptr<llvm::Module> load_module(LLVMContextRef c, 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             :  * Reset inlining related state. This needs to be called before the currently
     157             :  * used LLVMContextRef is disposed (and a new one create), otherwise we would
     158             :  * have dangling references to deleted modules.
     159             :  */
     160             : void
     161           0 : llvm_inline_reset_caches(void)
     162             : {
     163           0 :     module_cache->clear();
     164           0 :     summary_cache->clear();
     165           0 : }
     166             : 
     167             : /*
     168             :  * Perform inlining of external function references in M based on a simple
     169             :  * cost based analysis.
     170             :  */
     171             : void
     172         978 : llvm_inline(LLVMModuleRef M)
     173             : {
     174         978 :     LLVMContextRef lc = LLVMGetModuleContext(M);
     175         978 :     llvm::Module *mod = llvm::unwrap(M);
     176             : 
     177         978 :     std::unique_ptr<ImportMapTy> globalsToInline = llvm_build_inline_plan(lc, mod);
     178         978 :     if (!globalsToInline)
     179           0 :         return;
     180         978 :     llvm_execute_inline_plan(mod, globalsToInline.get());
     181             : }
     182             : 
     183             : /*
     184             :  * Build information necessary for inlining external function references in
     185             :  * mod.
     186             :  */
     187             : static std::unique_ptr<ImportMapTy>
     188         978 : llvm_build_inline_plan(LLVMContextRef lc, llvm::Module *mod)
     189             : {
     190        1956 :     std::unique_ptr<ImportMapTy> globalsToInline(new ImportMapTy());
     191        1956 :     FunctionInlineStates functionStates;
     192        1956 :     InlineWorkList worklist;
     193             : 
     194        1956 :     InlineSearchPath defaultSearchPath;
     195             : 
     196             :     /* attempt to add module to search path */
     197         978 :     add_module_to_inline_search_path(defaultSearchPath, "$libdir/postgres");
     198             :     /* if postgres isn't available, no point continuing */
     199         978 :     if (defaultSearchPath.empty())
     200           0 :         return nullptr;
     201             : 
     202             :     /*
     203             :      * Start inlining with current references to external functions by putting
     204             :      * them on the inlining worklist. If, during inlining of those, new extern
     205             :      * functions need to be inlined, they'll also be put there, with a lower
     206             :      * priority.
     207             :      */
     208       15128 :     for (const llvm::Function &funcDecl : mod->functions())
     209             :     {
     210       14150 :         InlineWorkListItem item = {};
     211       14150 :         FunctionInlineState inlineState = {};
     212             : 
     213             :         /* already has a definition */
     214       14150 :         if (!funcDecl.isDeclaration())
     215        9696 :             continue;
     216             : 
     217             :         /* llvm provides implementation */
     218        4454 :         if (funcDecl.isIntrinsic())
     219         794 :             continue;
     220             : 
     221        3660 :         item.symbolName = funcDecl.getName();
     222        3660 :         item.searchpath = defaultSearchPath;
     223        3660 :         worklist.push_back(item);
     224        3660 :         inlineState.costLimit = inline_initial_cost;
     225        3660 :         inlineState.processed = false;
     226        3660 :         inlineState.inlined = false;
     227        3660 :         inlineState.allowReconsidering = false;
     228        3660 :         functionStates[funcDecl.getName()] = inlineState;
     229             :     }
     230             : 
     231             :     /*
     232             :      * Iterate over pending worklist items, look them up in index, check
     233             :      * whether they should be inlined.
     234             :      */
     235       18504 :     while (!worklist.empty())
     236             :     {
     237       17526 :         InlineWorkListItem item = worklist.pop_back_val();
     238       17526 :         llvm::StringRef symbolName = item.symbolName;
     239             :         char *cmodname;
     240             :         char *cfuncname;
     241       17526 :         FunctionInlineState &inlineState = functionStates[symbolName];
     242             :         llvm::GlobalValue::GUID funcGUID;
     243             : 
     244       17526 :         llvm_split_symbol_name(symbolName.data(), &cmodname, &cfuncname);
     245             : 
     246       17526 :         funcGUID = llvm::GlobalValue::getGUID(cfuncname);
     247             : 
     248             :         /* already processed */
     249       17526 :         if (inlineState.processed)
     250           0 :             continue;
     251             : 
     252             : 
     253       17526 :         if (cmodname)
     254          38 :             add_module_to_inline_search_path(item.searchpath, cmodname);
     255             : 
     256             :         /*
     257             :          * Iterate over all known definitions of function, via the index. Then
     258             :          * look up module(s), check if function actually is defined (there
     259             :          * could be hash conflicts).
     260             :          */
     261       34308 :         for (const auto &gvs : summaries_for_guid(item.searchpath, funcGUID))
     262             :         {
     263             :             const llvm::FunctionSummary *fs;
     264       16782 :             llvm::StringRef modPath = gvs->modulePath();
     265             :             llvm::Module *defMod;
     266             :             llvm::Function *funcDef;
     267             : 
     268       16782 :             fs = llvm::cast<llvm::FunctionSummary>(gvs);
     269             : 
     270             : #if LLVM_VERSION_MAJOR > 3
     271       16782 :             if (gvs->notEligibleToImport())
     272             :             {
     273             :                 ilog(DEBUG1, "ineligibile to import %s due to summary",
     274             :                      symbolName.data());
     275        8420 :                 continue;
     276             :             }
     277             : #endif
     278             : 
     279       16782 :             if ((int) fs->instCount() > inlineState.costLimit)
     280             :             {
     281             :                 ilog(DEBUG1, "ineligibile to import %s due to early threshold: %u vs %u",
     282             :                      symbolName.data(), fs->instCount(), inlineState.costLimit);
     283        7378 :                 inlineState.allowReconsidering = true;
     284        7378 :                 continue;
     285             :             }
     286             : 
     287        9404 :             defMod = load_module_cached(lc, modPath);
     288        9404 :             if (defMod->materializeMetadata())
     289           0 :                 elog(FATAL, "failed to materialize metadata");
     290             : 
     291        9404 :             funcDef = defMod->getFunction(cfuncname);
     292             : 
     293             :             /*
     294             :              * This can happen e.g. in case of a hash collision of the
     295             :              * function's name.
     296             :              */
     297        9404 :             if (!funcDef)
     298           0 :                 continue;
     299             : 
     300        9404 :             if (funcDef->materialize())
     301           0 :                 elog(FATAL, "failed to materialize metadata");
     302             : 
     303             :             Assert(!funcDef->isDeclaration());
     304             :             Assert(funcDef->hasExternalLinkage());
     305             : 
     306        9404 :             llvm::StringSet<> importVars;
     307        9404 :             llvm::SmallPtrSet<const llvm::Function *, 8> visitedFunctions;
     308        9404 :             int running_instcount = 0;
     309             : 
     310             :             /*
     311             :              * Check whether function, and objects it depends on, are
     312             :              * inlinable.
     313             :              */
     314        9404 :             if (function_inlinable(*funcDef,
     315             :                                    inlineState.costLimit,
     316             :                                    functionStates,
     317             :                                    worklist,
     318             :                                    item.searchpath,
     319             :                                    visitedFunctions,
     320             :                                    running_instcount,
     321             :                                    importVars))
     322             :             {
     323             :                 /*
     324             :                  * Check whether function and all its dependencies are too
     325             :                  * big. Dependencies already counted for other functions that
     326             :                  * will get inlined are not counted again. While this make
     327             :                  * things somewhat order dependent, I can't quite see a point
     328             :                  * in a different behaviour.
     329             :                  */
     330        7910 :                 if (running_instcount > inlineState.costLimit)
     331             :                 {
     332             :                     ilog(DEBUG1, "skipping inlining of %s due to late threshold %d vs %d",
     333             :                          symbolName.data(), running_instcount, inlineState.costLimit);
     334        1042 :                     inlineState.allowReconsidering = true;
     335        1042 :                     continue;
     336             :                 }
     337             : 
     338             :                 ilog(DEBUG1, "inline top function %s total_instcount: %d, partial: %d",
     339             :                      symbolName.data(), running_instcount, fs->instCount());
     340             : 
     341             :                 /* import referenced function itself */
     342        6868 :                 importVars.insert(symbolName);
     343             : 
     344             :                 {
     345        6868 :                     llvm::StringSet<> &modGlobalsToInline = (*globalsToInline)[modPath];
     346       17082 :                     for (auto& importVar : importVars)
     347       10214 :                         modGlobalsToInline.insert(importVar.first());
     348             :                     Assert(modGlobalsToInline.size() > 0);
     349             :                 }
     350             : 
     351             :                 /* mark function as inlined */
     352        6868 :                 inlineState.inlined = true;
     353             : 
     354             :                 /*
     355             :                  * Found definition to inline, don't look for further
     356             :                  * potential definitions.
     357             :                  */
     358        6868 :                 break;
     359             :             }
     360             :             else
     361             :             {
     362             :                 ilog(DEBUG1, "had to skip inlining %s",
     363             :                      symbolName.data());
     364             : 
     365             :                 /* It's possible there's another definition that's inlinable. */
     366             :             }
     367             :         }
     368             : 
     369             :         /*
     370             :          * Signal that we're done with symbol, whether successful (inlined =
     371             :          * true above) or not.
     372             :          */
     373       17526 :         inlineState.processed = true;
     374             :     }
     375             : 
     376         978 :     return globalsToInline;
     377             : }
     378             : 
     379             : /*
     380             :  * Perform the actual inlining of external functions (and their dependencies)
     381             :  * into mod.
     382             :  */
     383             : static void
     384         978 : llvm_execute_inline_plan(llvm::Module *mod, ImportMapTy *globalsToInline)
     385             : {
     386        1956 :     llvm::IRMover Mover(*mod);
     387             : 
     388        5754 :     for (const auto& toInline : *globalsToInline)
     389             :     {
     390        4776 :         const llvm::StringRef& modPath = toInline.first();
     391        4776 :         const llvm::StringSet<>& modGlobalsToInline = toInline.second;
     392        4776 :         llvm::SetVector<llvm::GlobalValue *> GlobalsToImport;
     393             : 
     394             :         Assert(module_cache->count(modPath));
     395        4776 :         std::unique_ptr<llvm::Module> importMod(std::move((*module_cache)[modPath]));
     396        4776 :         module_cache->erase(modPath);
     397             : 
     398        4776 :         if (modGlobalsToInline.empty())
     399           0 :             continue;
     400             : 
     401       14814 :         for (auto &glob: modGlobalsToInline)
     402             :         {
     403       10038 :             llvm::StringRef SymbolName = glob.first();
     404             :             char *modname;
     405             :             char *funcname;
     406             : 
     407       10038 :             llvm_split_symbol_name(SymbolName.data(), &modname, &funcname);
     408             : 
     409       10038 :             llvm::GlobalValue *valueToImport = importMod->getNamedValue(funcname);
     410             : 
     411       10038 :             if (!valueToImport)
     412           0 :                 elog(FATAL, "didn't refind value %s to import", SymbolName.data());
     413             : 
     414             :             /*
     415             :              * For functions (global vars are only inlined if already static),
     416             :              * mark imported variables as being clones from other
     417             :              * functions. That a) avoids symbol conflicts b) allows the
     418             :              * optimizer to perform inlining.
     419             :             */
     420       10038 :             if (llvm::isa<llvm::Function>(valueToImport))
     421             :             {
     422        6950 :                 llvm::Function *F = llvm::dyn_cast<llvm::Function>(valueToImport);
     423             :                 typedef llvm::GlobalValue::LinkageTypes LinkageTypes;
     424             : 
     425             :                 /*
     426             :                  * Per-function info isn't necessarily stripped yet, as the
     427             :                  * module is lazy-loaded when stripped above.
     428             :                  */
     429        6950 :                 llvm::stripDebugInfo(*F);
     430             : 
     431             :                 /*
     432             :                  * If the to-be-imported function is one referenced including
     433             :                  * its module name, create a tiny inline function that just
     434             :                  * forwards the call. One might think a GlobalAlias would do
     435             :                  * the trick, but a) IRMover doesn't override a declaration
     436             :                  * with an alias pointing to a definition (instead renaming
     437             :                  * it), b) Aliases can't be AvailableExternally.
     438             :                  */
     439        6950 :                 if (modname)
     440             :                 {
     441             :                     llvm::Function *AF;
     442             : 
     443          34 :                     AF = create_redirection_function(importMod, F, SymbolName);
     444             : 
     445          34 :                     GlobalsToImport.insert(AF);
     446          34 :                     llvm::stripDebugInfo(*AF);
     447             :                 }
     448             : 
     449        6950 :                 if (valueToImport->hasExternalLinkage())
     450             :                 {
     451        6868 :                     valueToImport->setLinkage(LinkageTypes::AvailableExternallyLinkage);
     452             :                 }
     453             :             }
     454             : 
     455       10038 :             GlobalsToImport.insert(valueToImport);
     456             :             ilog(DEBUG1, "performing import of %s %s",
     457             :                  modPath.data(), SymbolName.data());
     458             : 
     459             :         }
     460             : 
     461             : #if LLVM_VERSION_MAJOR > 4
     462             : #define IRMOVE_PARAMS , /*IsPerformingImport=*/false
     463             : #elif LLVM_VERSION_MAJOR > 3
     464             : #define IRMOVE_PARAMS , /*LinkModuleInlineAsm=*/false, /*IsPerformingImport=*/false
     465             : #else
     466             : #define IRMOVE_PARAMS
     467             : #endif
     468        4776 :         if (Mover.move(std::move(importMod), GlobalsToImport.getArrayRef(),
     469        2380 :                        [](llvm::GlobalValue &, llvm::IRMover::ValueAdder) {}
     470        9552 :                        IRMOVE_PARAMS))
     471           0 :             elog(FATAL, "function import failed with linker error");
     472             :     }
     473         978 : }
     474             : 
     475             : /*
     476             :  * Return a module identified by modPath, caching it in memory.
     477             :  *
     478             :  * Note that such a module may *not* be modified without copying, otherwise
     479             :  * the cache state would get corrupted.
     480             :  */
     481             : static llvm::Module*
     482        9404 : load_module_cached(LLVMContextRef lc, llvm::StringRef modPath)
     483             : {
     484        9404 :     auto it = module_cache->find(modPath);
     485        9404 :     if (it == module_cache->end())
     486             :     {
     487        4962 :         it = module_cache->insert(
     488        4962 :             std::make_pair(modPath, load_module(lc, modPath))).first;
     489             :     }
     490             : 
     491       18808 :     return it->second.get();
     492             : }
     493             : 
     494             : static std::unique_ptr<llvm::Module>
     495        4962 : load_module(LLVMContextRef lc, llvm::StringRef Identifier)
     496             : {
     497             :     LLVMMemoryBufferRef buf;
     498             :     LLVMModuleRef mod;
     499             :     char path[MAXPGPATH];
     500             :     char *msg;
     501             : 
     502        4962 :     snprintf(path, MAXPGPATH,"%s/bitcode/%s", pkglib_path, Identifier.data());
     503             : 
     504        4962 :     if (LLVMCreateMemoryBufferWithContentsOfFile(path, &buf, &msg))
     505           0 :         elog(FATAL, "failed to open bitcode file \"%s\": %s",
     506             :              path, msg);
     507        4962 :     if (LLVMGetBitcodeModuleInContext2(lc, buf, &mod))
     508           0 :         elog(FATAL, "failed to parse bitcode in file \"%s\"", path);
     509             : 
     510             :     /*
     511             :      * Currently there's no use in more detailed debug info for JITed
     512             :      * code. Until that changes, not much point in wasting memory and cycles
     513             :      * on processing debuginfo.
     514             :      */
     515        4962 :     llvm::StripDebugInfo(*llvm::unwrap(mod));
     516             : 
     517        9924 :     return std::unique_ptr<llvm::Module>(llvm::unwrap(mod));
     518             : }
     519             : 
     520             : /*
     521             :  * Compute list of referenced variables, functions and the instruction count
     522             :  * for a function.
     523             :  */
     524             : static void
     525       14324 : function_references(llvm::Function &F,
     526             :                     int &running_instcount,
     527             :                     llvm::SmallPtrSet<llvm::GlobalVariable *, 8> &referencedVars,
     528             :                     llvm::SmallPtrSet<llvm::Function *, 8> &referencedFunctions)
     529             : {
     530       28648 :     llvm::SmallPtrSet<const llvm::User *, 32> Visited;
     531             : 
     532      169554 :     for (llvm::BasicBlock &BB : F)
     533             :     {
     534     1004592 :         for (llvm::Instruction &I : BB)
     535             :         {
     536      849362 :             if (llvm::isa<llvm::DbgInfoIntrinsic>(I))
     537           0 :                 continue;
     538             : 
     539     1698724 :             llvm::SmallVector<llvm::User *, 8> Worklist;
     540      849362 :             Worklist.push_back(&I);
     541             : 
     542      849362 :             running_instcount++;
     543             : 
     544     3140752 :             while (!Worklist.empty()) {
     545     2291390 :                 llvm::User *U = Worklist.pop_back_val();
     546             : 
     547             :                 /* visited before */
     548     2291390 :                 if (!Visited.insert(U).second)
     549     1193412 :                     continue;
     550             : 
     551     3023484 :                 for (auto &OI : U->operands()) {
     552     1925506 :                     llvm::User *Operand = llvm::dyn_cast<llvm::User>(OI);
     553     1925506 :                     if (!Operand)
     554      535062 :                         continue;
     555     1593162 :                     if (llvm::isa<llvm::BlockAddress>(Operand))
     556           0 :                         continue;
     557     1593162 :                     if (auto *GV = llvm::dyn_cast<llvm::GlobalVariable>(Operand)) {
     558       53932 :                         referencedVars.insert(GV);
     559       53932 :                         if (GV->hasInitializer())
     560       51584 :                             Worklist.push_back(GV->getInitializer());
     561       53932 :                         continue;
     562             :                     }
     563     1539230 :                     if (auto *CF = llvm::dyn_cast<llvm::Function>(Operand)) {
     564      148786 :                         referencedFunctions.insert(CF);
     565      148786 :                         continue;
     566             :                     }
     567     1390444 :                     Worklist.push_back(Operand);
     568             :                 }
     569             :             }
     570             :         }
     571             :     }
     572       14324 : }
     573             : 
     574             : /*
     575             :  * Check whether function F is inlinable and, if so, what globals need to be
     576             :  * imported.
     577             :  *
     578             :  * References to external functions from, potentially recursively, inlined
     579             :  * functions are added to the passed in worklist.
     580             :  */
     581             : static bool
     582       14336 : function_inlinable(llvm::Function &F,
     583             :                    int threshold,
     584             :                    FunctionInlineStates &functionStates,
     585             :                    InlineWorkList &worklist,
     586             :                    InlineSearchPath &searchpath,
     587             :                    llvm::SmallPtrSet<const llvm::Function *, 8> &visitedFunctions,
     588             :                    int &running_instcount,
     589             :                    llvm::StringSet<> &importVars)
     590             : {
     591       14336 :     int subThreshold = threshold * inline_cost_decay_factor;
     592       28672 :     llvm::SmallPtrSet<llvm::GlobalVariable *, 8> referencedVars;
     593       28672 :     llvm::SmallPtrSet<llvm::Function *, 8> referencedFunctions;
     594             : 
     595             :     /* can't rely on what may be inlined */
     596       14336 :     if (F.isInterposable())
     597           0 :         return false;
     598             : 
     599             :     /*
     600             :      * Can't rely on function being present. Alternatively we could create a
     601             :      * static version of these functions?
     602             :      */
     603       14336 :     if (F.hasAvailableExternallyLinkage())
     604           0 :         return false;
     605             : 
     606             :     ilog(DEBUG1, "checking inlinability of %s", F.getName().data());
     607             : 
     608       14336 :     if (F.materialize())
     609           0 :         elog(FATAL, "failed to materialize metadata");
     610             : 
     611             : #if LLVM_VERSION_MAJOR < 14
     612             : #define hasFnAttr hasFnAttribute
     613             : #endif
     614             : 
     615       14336 :     if (F.getAttributes().hasFnAttr(llvm::Attribute::NoInline))
     616             :     {
     617             :         ilog(DEBUG1, "ineligibile to import %s due to noinline",
     618             :              F.getName().data());
     619          12 :         return false;
     620             :     }
     621             : 
     622       14324 :     function_references(F, running_instcount, referencedVars, referencedFunctions);
     623             : 
     624       52700 :     for (llvm::GlobalVariable* rv: referencedVars)
     625             :     {
     626       39858 :         if (rv->materialize())
     627           0 :             elog(FATAL, "failed to materialize metadata");
     628             : 
     629             :         /*
     630             :          * Don't inline functions that access thread local variables.  That
     631             :          * doesn't work on current LLVM releases (but might in future).
     632             :          */
     633       39858 :         if (rv->isThreadLocal())
     634             :         {
     635             :             ilog(DEBUG1, "cannot inline %s due to thread-local variable %s",
     636             :                  F.getName().data(), rv->getName().data());
     637        1482 :             return false;
     638             :         }
     639             : 
     640             :         /*
     641             :          * Never want to inline externally visible vars, cheap enough to
     642             :          * reference.
     643             :          */
     644       39858 :         if (rv->hasExternalLinkage() || rv->hasAvailableExternallyLinkage())
     645        1406 :             continue;
     646             : 
     647             :         /*
     648             :          * If variable is file-local, we need to inline it, to be able to
     649             :          * inline the function itself. Can't do that if the variable can be
     650             :          * modified, because they'd obviously get out of sync.
     651             :          *
     652             :          * XXX: Currently not a problem, but there'd be problems with
     653             :          * nontrivial initializers if they were allowed for postgres.
     654             :          */
     655       38452 :         if (!rv->isConstant())
     656             :         {
     657             :             ilog(DEBUG1, "cannot inline %s due to uncloneable variable %s",
     658             :                  F.getName().data(), rv->getName().data());
     659        1482 :             return false;
     660             :         }
     661             : 
     662             :         ilog(DEBUG1, "memorizing global var %s linkage %d for inlining",
     663             :              rv->getName().data(), (int)rv->getLinkage());
     664             : 
     665       36970 :         importVars.insert(rv->getName());
     666             :         /* small cost attributed to each cloned global */
     667       36970 :         running_instcount += 5;
     668             :     }
     669             : 
     670       12842 :     visitedFunctions.insert(&F);
     671             : 
     672             :     /*
     673             :      * Check referenced functions. Check whether used static ones are
     674             :      * inlinable, and remember external ones for inlining.
     675             :      */
     676       79838 :     for (llvm::Function* referencedFunction: referencedFunctions)
     677             :     {
     678       67130 :         llvm::StringSet<> recImportVars;
     679             : 
     680       67130 :         if (referencedFunction->materialize())
     681           0 :             elog(FATAL, "failed to materialize metadata");
     682             : 
     683       67130 :         if (referencedFunction->isIntrinsic())
     684        9972 :             continue;
     685             : 
     686             :         /* if already visited skip, otherwise remember */
     687       57158 :         if (!visitedFunctions.insert(referencedFunction).second)
     688       21976 :             continue;
     689             : 
     690             :         /*
     691             :          * We don't inline external functions directly here, instead we put
     692             :          * them on the worklist if appropriate and check them from
     693             :          * llvm_build_inline_plan().
     694             :          */
     695       35182 :         if (referencedFunction->hasExternalLinkage())
     696             :         {
     697       30250 :             llvm::StringRef funcName = referencedFunction->getName();
     698             : 
     699             :             /*
     700             :              * Don't bother checking for inlining if remaining cost budget is
     701             :              * very small.
     702             :              */
     703       30250 :             if (subThreshold < 5)
     704        6676 :                 continue;
     705             : 
     706       23574 :             auto it = functionStates.find(funcName);
     707       23574 :             if (it == functionStates.end())
     708             :             {
     709             :                 FunctionInlineState inlineState;
     710             : 
     711       13232 :                 inlineState.costLimit = subThreshold;
     712       13232 :                 inlineState.processed = false;
     713       13232 :                 inlineState.inlined = false;
     714       13232 :                 inlineState.allowReconsidering = false;
     715             : 
     716       13232 :                 functionStates[funcName] = inlineState;
     717       13232 :                 worklist.push_back({funcName, searchpath});
     718             : 
     719             :                 ilog(DEBUG1,
     720             :                      "considering extern function %s at %d for inlining",
     721             :                      funcName.data(), subThreshold);
     722             :             }
     723       10342 :             else if (!it->second.inlined &&
     724       17596 :                      (!it->second.processed || it->second.allowReconsidering) &&
     725        7254 :                      it->second.costLimit < subThreshold)
     726             :             {
     727             :                 /*
     728             :                  * Update inlining threshold if higher. Need to re-queue
     729             :                  * to be processed if already processed with lower
     730             :                  * threshold.
     731             :                  */
     732         642 :                 if (it->second.processed)
     733             :                 {
     734             :                     ilog(DEBUG1,
     735             :                          "reconsidering extern function %s at %d for inlining, increasing from %d",
     736             :                          funcName.data(), subThreshold, it->second.costLimit);
     737             : 
     738         634 :                     it->second.processed = false;
     739         634 :                     it->second.allowReconsidering = false;
     740         634 :                     worklist.push_back({funcName, searchpath});
     741             :                 }
     742         642 :                 it->second.costLimit = subThreshold;
     743             :             }
     744       23574 :             continue;
     745             :         }
     746             : 
     747             :         /* can't rely on what may be inlined */
     748        4932 :         if (referencedFunction->isInterposable())
     749           0 :             return false;
     750             : 
     751        4932 :         if (!function_inlinable(*referencedFunction,
     752             :                                 subThreshold,
     753             :                                 functionStates,
     754             :                                 worklist,
     755             :                                 searchpath,
     756             :                                 visitedFunctions,
     757             :                                 running_instcount,
     758             :                                 recImportVars))
     759             :         {
     760             :             ilog(DEBUG1,
     761             :                  "cannot inline %s due to required function %s not being inlinable",
     762             :                  F.getName().data(), referencedFunction->getName().data());
     763         134 :             return false;
     764             :         }
     765             : 
     766             :         /* import referenced function itself */
     767        4798 :         importVars.insert(referencedFunction->getName());
     768             : 
     769             :         /* import referenced function and its dependents */
     770       94420 :         for (auto& recImportVar : recImportVars)
     771       89622 :             importVars.insert(recImportVar.first());
     772             :     }
     773             : 
     774       12708 :     return true;
     775             : }
     776             : 
     777             : /*
     778             :  * Attempt to load module summary located at path. Return empty pointer when
     779             :  * loading fails.
     780             :  */
     781             : static std::unique_ptr<llvm::ModuleSummaryIndex>
     782         488 : llvm_load_summary(llvm::StringRef path)
     783             : {
     784             :     llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer> > MBOrErr =
     785         976 :         llvm::MemoryBuffer::getFile(path);
     786             : 
     787         488 :     if (std::error_code EC = MBOrErr.getError())
     788             :     {
     789             :         ilog(DEBUG1, "failed to open %s: %s", path.data(),
     790             :              EC.message().c_str());
     791             :     }
     792             :     else
     793             :     {
     794         488 :         llvm::MemoryBufferRef ref(*MBOrErr.get().get());
     795             : 
     796             : #if LLVM_VERSION_MAJOR > 3
     797             :         llvm::Expected<std::unique_ptr<llvm::ModuleSummaryIndex> > IndexOrErr =
     798         976 :             llvm::getModuleSummaryIndex(ref);
     799         488 :         if (IndexOrErr)
     800         488 :             return std::move(IndexOrErr.get());
     801           0 :         elog(FATAL, "failed to load summary \"%s\": %s",
     802             :              path.data(),
     803             :              toString(IndexOrErr.takeError()).c_str());
     804             : #else
     805             :         llvm::ErrorOr<std::unique_ptr<llvm::ModuleSummaryIndex> > IndexOrErr =
     806             :             llvm::getModuleSummaryIndex(ref, [](const llvm::DiagnosticInfo &) {});
     807             :         if (IndexOrErr)
     808             :             return std::move(IndexOrErr.get());
     809             :         elog(FATAL, "failed to load summary \"%s\": %s",
     810             :              path.data(),
     811             :              IndexOrErr.getError().message().c_str());
     812             : #endif
     813             :     }
     814           0 :     return nullptr;
     815             : }
     816             : 
     817             : /*
     818             :  * Attempt to add modpath to the search path.
     819             :  */
     820             : static void
     821        1016 : add_module_to_inline_search_path(InlineSearchPath& searchpath, llvm::StringRef modpath)
     822             : {
     823             :     /* only extension in libdir are candidates for inlining for now */
     824        1016 :     if (!modpath.startswith("$libdir/"))
     825           0 :         return;
     826             : 
     827             :     /* if there's no match, attempt to load */
     828        1016 :     auto it = summary_cache->find(modpath);
     829        1016 :     if (it == summary_cache->end())
     830             :     {
     831         976 :         std::string path(modpath);
     832         488 :         path = path.replace(0, strlen("$libdir"), std::string(pkglib_path) + "/bitcode");
     833         488 :         path += ".index.bc";
     834         488 :         (*summary_cache)[modpath] = llvm_load_summary(path);
     835         488 :         it = summary_cache->find(modpath);
     836             :     }
     837             : 
     838             :     Assert(it != summary_cache->end());
     839             : 
     840             :     /* if the entry isn't NULL, it's validly loaded */
     841        1016 :     if (it->second)
     842        1016 :         searchpath.push_back(it->second.get());
     843             : }
     844             : 
     845             : /*
     846             :  * Search for all references for functions hashing to guid in the search path,
     847             :  * and return them in search path order.
     848             :  */
     849             : static llvm::SmallVector<llvm::GlobalValueSummary *, 1>
     850       17526 : summaries_for_guid(const InlineSearchPath& path, llvm::GlobalValue::GUID guid)
     851             : {
     852       17526 :     llvm::SmallVector<llvm::GlobalValueSummary *, 1> matches;
     853             : 
     854       36204 :     for (auto index : path)
     855             :     {
     856             : #if LLVM_VERSION_MAJOR > 4
     857       18678 :         llvm::ValueInfo funcVI = index->getValueInfo(guid);
     858             : 
     859             :         /* if index doesn't know function, we don't have a body, continue */
     860       18678 :         if (funcVI)
     861       33564 :             for (auto &gv : funcVI.getSummaryList())
     862       16782 :                 matches.push_back(gv.get());
     863             : #else
     864             :         const llvm::const_gvsummary_iterator &I =
     865             :             index->findGlobalValueSummaryList(guid);
     866             :         if (I != index->end())
     867             :         {
     868             :             for (auto &gv : I->second)
     869             :                 matches.push_back(gv.get());
     870             :         }
     871             : #endif
     872             :     }
     873             : 
     874       17526 :     return matches;
     875             : }
     876             : 
     877             : /*
     878             :  * Create inline wrapper with the name Name, redirecting the call to F.
     879             :  */
     880             : static llvm::Function*
     881          34 : create_redirection_function(std::unique_ptr<llvm::Module> &importMod,
     882             :                             llvm::Function *F,
     883             :                             llvm::StringRef Name)
     884             : {
     885             :     typedef llvm::GlobalValue::LinkageTypes LinkageTypes;
     886             : 
     887          34 :     llvm::LLVMContext &Context = F->getContext();
     888          34 :     llvm::IRBuilder<> Builder(Context);
     889             :     llvm::Function *AF;
     890             :     llvm::BasicBlock *BB;
     891             :     llvm::CallInst *fwdcall;
     892             : #if LLVM_VERSION_MAJOR < 14
     893          34 :     llvm::Attribute inlineAttribute;
     894             : #endif
     895             : 
     896          68 :     AF = llvm::Function::Create(F->getFunctionType(),
     897             :                                 LinkageTypes::AvailableExternallyLinkage,
     898          34 :                                 Name, importMod.get());
     899          34 :     BB = llvm::BasicBlock::Create(Context, "entry", AF);
     900             : 
     901          34 :     Builder.SetInsertPoint(BB);
     902          34 :     fwdcall = Builder.CreateCall(F, &*AF->arg_begin());
     903             : #if LLVM_VERSION_MAJOR < 14
     904             :     inlineAttribute = llvm::Attribute::get(Context,
     905          34 :                                            llvm::Attribute::AlwaysInline);
     906          34 :     fwdcall->addAttribute(~0U, inlineAttribute);
     907             : #else
     908             :     fwdcall->addFnAttr(llvm::Attribute::AlwaysInline);
     909             : #endif
     910          34 :     Builder.CreateRet(fwdcall);
     911             : 
     912          68 :     return AF;
     913             : }

Generated by: LCOV version 1.14