LCOV - differential code coverage report
Current view: top level - src/backend/jit/llvm - llvmjit_inline.cpp (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage HEAD vs 15 Lines: 91.3 % 264 241 23 241
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 12 12 12
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  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(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 CBC         500 : llvm_inline(LLVMModuleRef M)
     161                 : {
     162             500 :     llvm::Module *mod = llvm::unwrap(M);
     163                 : 
     164             500 :     std::unique_ptr<ImportMapTy> globalsToInline = llvm_build_inline_plan(mod);
     165             500 :     if (!globalsToInline)
     166 UBC           0 :         return;
     167 CBC         500 :     llvm_execute_inline_plan(mod, globalsToInline.get());
     168             500 : }
     169                 : 
     170                 : /*
     171                 :  * Build information necessary for inlining external function references in
     172                 :  * mod.
     173                 :  */
     174                 : static std::unique_ptr<ImportMapTy>
     175             500 : llvm_build_inline_plan(llvm::Module *mod)
     176                 : {
     177             500 :     std::unique_ptr<ImportMapTy> globalsToInline(new ImportMapTy());
     178             500 :     FunctionInlineStates functionStates;
     179             500 :     InlineWorkList worklist;
     180                 : 
     181             500 :     InlineSearchPath defaultSearchPath;
     182                 : 
     183                 :     /* attempt to add module to search path */
     184             500 :     add_module_to_inline_search_path(defaultSearchPath, "$libdir/postgres");
     185                 :     /* if postgres isn't available, no point continuing */
     186             500 :     if (defaultSearchPath.empty())
     187 UBC           0 :         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 CBC        7667 :     for (const llvm::Function &funcDecl : mod->functions())
     196                 :     {
     197            7167 :         InlineWorkListItem item = {};
     198            7167 :         FunctionInlineState inlineState = {};
     199                 : 
     200                 :         /* already has a definition */
     201            7167 :         if (!funcDecl.isDeclaration())
     202            4895 :             continue;
     203                 : 
     204                 :         /* llvm provides implementation */
     205            2272 :         if (funcDecl.isIntrinsic())
     206             408 :             continue;
     207                 : 
     208            1864 :         item.symbolName = funcDecl.getName();
     209            1864 :         item.searchpath = defaultSearchPath;
     210            1864 :         worklist.push_back(item);
     211            1864 :         inlineState.costLimit = inline_initial_cost;
     212            1864 :         inlineState.processed = false;
     213            1864 :         inlineState.inlined = false;
     214            1864 :         inlineState.allowReconsidering = false;
     215            1864 :         functionStates[funcDecl.getName()] = inlineState;
     216            7167 :     }
     217                 : 
     218                 :     /*
     219                 :      * Iterate over pending worklist items, look them up in index, check
     220                 :      * whether they should be inlined.
     221                 :      */
     222           11811 :     while (!worklist.empty())
     223                 :     {
     224           11311 :         InlineWorkListItem item = worklist.pop_back_val();
     225           11311 :         llvm::StringRef symbolName = item.symbolName;
     226                 :         char *cmodname;
     227                 :         char *cfuncname;
     228           11311 :         FunctionInlineState &inlineState = functionStates[symbolName];
     229                 :         llvm::GlobalValue::GUID funcGUID;
     230                 : 
     231           11311 :         llvm_split_symbol_name(symbolName.data(), &cmodname, &cfuncname);
     232                 : 
     233           11311 :         funcGUID = llvm::GlobalValue::getGUID(cfuncname);
     234                 : 
     235                 :         /* already processed */
     236           11311 :         if (inlineState.processed)
     237 UBC           0 :             continue;
     238                 : 
     239                 : 
     240 CBC       11311 :         if (cmodname)
     241              18 :             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           17481 :         for (const auto &gvs : summaries_for_guid(item.searchpath, funcGUID))
     249                 :         {
     250                 :             const llvm::FunctionSummary *fs;
     251            9310 :             llvm::StringRef modPath = gvs->modulePath();
     252                 :             llvm::Module *defMod;
     253                 :             llvm::Function *funcDef;
     254                 : 
     255            9310 :             fs = llvm::cast<llvm::FunctionSummary>(gvs);
     256                 : 
     257                 : #if LLVM_VERSION_MAJOR > 3
     258            9310 :             if (gvs->notEligibleToImport())
     259                 :             {
     260                 :                 ilog(DEBUG1, "ineligibile to import %s due to summary",
     261                 :                      symbolName.data());
     262            5176 :                 continue;
     263                 :             }
     264                 : #endif
     265                 : 
     266            9310 :             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            4228 :                 inlineState.allowReconsidering = true;
     271            4228 :                 continue;
     272                 :             }
     273                 : 
     274            5082 :             defMod = load_module_cached(modPath);
     275            5082 :             if (defMod->materializeMetadata())
     276 UBC           0 :                 elog(FATAL, "failed to materialize metadata");
     277                 : 
     278 CBC        5082 :             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            5082 :             if (!funcDef)
     285 UBC           0 :                 continue;
     286                 : 
     287 CBC        5082 :             if (funcDef->materialize())
     288 UBC           0 :                 elog(FATAL, "failed to materialize metadata");
     289                 : 
     290 CBC        5082 :             Assert(!funcDef->isDeclaration());
     291            5082 :             Assert(funcDef->hasExternalLinkage());
     292                 : 
     293            5082 :             llvm::StringSet<> importVars;
     294            5082 :             llvm::SmallPtrSet<const llvm::Function *, 8> visitedFunctions;
     295            5082 :             int running_instcount = 0;
     296                 : 
     297                 :             /*
     298                 :              * Check whether function, and objects it depends on, are
     299                 :              * inlinable.
     300                 :              */
     301            5082 :             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            4088 :                 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             948 :                     inlineState.allowReconsidering = true;
     322             948 :                     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            3140 :                 importVars.insert(symbolName);
     330                 : 
     331                 :                 {
     332            3140 :                     llvm::StringSet<> &modGlobalsToInline = (*globalsToInline)[modPath];
     333            9570 :                     for (auto& importVar : importVars)
     334            6430 :                         modGlobalsToInline.insert(importVar.first());
     335            3140 :                     Assert(modGlobalsToInline.size() > 0);
     336                 :                 }
     337                 : 
     338                 :                 /* mark function as inlined */
     339            3140 :                 inlineState.inlined = true;
     340                 : 
     341                 :                 /*
     342                 :                  * Found definition to inline, don't look for further
     343                 :                  * potential definitions.
     344                 :                  */
     345            3140 :                 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           20481 :         }
     355                 : 
     356                 :         /*
     357                 :          * Signal that we're done with symbol, whether successful (inlined =
     358                 :          * true above) or not.
     359                 :          */
     360           11311 :         inlineState.processed = true;
     361           11311 :     }
     362                 : 
     363             500 :     return globalsToInline;
     364             500 : }
     365                 : 
     366                 : /*
     367                 :  * Perform the actual inlining of external functions (and their dependencies)
     368                 :  * into mod.
     369                 :  */
     370                 : static void
     371             500 : llvm_execute_inline_plan(llvm::Module *mod, ImportMapTy *globalsToInline)
     372                 : {
     373             500 :     llvm::IRMover Mover(*mod);
     374                 : 
     375            3099 :     for (const auto& toInline : *globalsToInline)
     376                 :     {
     377            2599 :         const llvm::StringRef& modPath = toInline.first();
     378            2599 :         const llvm::StringSet<>& modGlobalsToInline = toInline.second;
     379            2599 :         llvm::SetVector<llvm::GlobalValue *> GlobalsToImport;
     380                 : 
     381            2599 :         Assert(module_cache->count(modPath));
     382            2599 :         std::unique_ptr<llvm::Module> importMod(std::move((*module_cache)[modPath]));
     383            2599 :         module_cache->erase(modPath);
     384                 : 
     385            2599 :         if (modGlobalsToInline.empty())
     386 UBC           0 :             continue;
     387                 : 
     388 CBC        8903 :         for (auto &glob: modGlobalsToInline)
     389                 :         {
     390            6304 :             llvm::StringRef SymbolName = glob.first();
     391                 :             char *modname;
     392                 :             char *funcname;
     393                 : 
     394            6304 :             llvm_split_symbol_name(SymbolName.data(), &modname, &funcname);
     395                 : 
     396            6304 :             llvm::GlobalValue *valueToImport = importMod->getNamedValue(funcname);
     397                 : 
     398            6304 :             if (!valueToImport)
     399 UBC           0 :                 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 CBC        6304 :             if (llvm::isa<llvm::Function>(valueToImport))
     408                 :             {
     409            3177 :                 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            3177 :                 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            3177 :                 if (modname)
     427                 :                 {
     428                 :                     llvm::Function *AF;
     429                 : 
     430              16 :                     AF = create_redirection_function(importMod, F, SymbolName);
     431                 : 
     432              16 :                     GlobalsToImport.insert(AF);
     433              16 :                     llvm::stripDebugInfo(*AF);
     434                 :                 }
     435                 : 
     436            3177 :                 if (valueToImport->hasExternalLinkage())
     437                 :                 {
     438            3140 :                     valueToImport->setLinkage(LinkageTypes::AvailableExternallyLinkage);
     439                 :                 }
     440                 :             }
     441                 : 
     442            6304 :             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            2599 :         if (Mover.move(std::move(importMod), GlobalsToImport.getArrayRef(),
     456            2599 :                        [](llvm::GlobalValue &, llvm::IRMover::ValueAdder) {}
     457                 :                        IRMOVE_PARAMS))
     458 UBC           0 :             elog(FATAL, "function import failed with linker error");
     459 CBC        2599 :     }
     460             500 : }
     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            5082 : load_module_cached(llvm::StringRef modPath)
     470                 : {
     471            5082 :     auto it = module_cache->find(modPath);
     472            5082 :     if (it == module_cache->end())
     473                 :     {
     474            5500 :         it = module_cache->insert(
     475            5500 :             std::make_pair(modPath, load_module(modPath))).first;
     476                 :     }
     477                 : 
     478            5082 :     return it->second.get();
     479                 : }
     480                 : 
     481                 : static std::unique_ptr<llvm::Module>
     482            2750 : load_module(llvm::StringRef Identifier)
     483                 : {
     484                 :     LLVMMemoryBufferRef buf;
     485                 :     LLVMModuleRef mod;
     486                 :     char path[MAXPGPATH];
     487                 :     char *msg;
     488                 : 
     489            2750 :     snprintf(path, MAXPGPATH,"%s/bitcode/%s", pkglib_path, Identifier.data());
     490                 : 
     491            2750 :     if (LLVMCreateMemoryBufferWithContentsOfFile(path, &buf, &msg))
     492 UBC           0 :         elog(FATAL, "failed to open bitcode file \"%s\": %s",
     493                 :              path, msg);
     494 CBC        2750 :     if (LLVMGetBitcodeModuleInContext2(LLVMGetGlobalContext(), buf, &mod))
     495 UBC           0 :         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 CBC        2750 :     llvm::StripDebugInfo(*llvm::unwrap(mod));
     503                 : 
     504            5500 :     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            7587 : 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            7587 :     llvm::SmallPtrSet<const llvm::User *, 32> Visited;
     518                 : 
     519          217383 :     for (llvm::BasicBlock &BB : F)
     520                 :     {
     521         1066460 :         for (llvm::Instruction &I : BB)
     522                 :         {
     523          480781 :             if (llvm::isa<llvm::DbgInfoIntrinsic>(I))
     524 UBC           0 :                 continue;
     525                 : 
     526 CBC      480781 :             llvm::SmallVector<llvm::User *, 8> Worklist;
     527          480781 :             Worklist.push_back(&I);
     528                 : 
     529          480781 :             running_instcount++;
     530                 : 
     531         1780390 :             while (!Worklist.empty()) {
     532         1299609 :                 llvm::User *U = Worklist.pop_back_val();
     533                 : 
     534                 :                 /* visited before */
     535         1299609 :                 if (!Visited.insert(U).second)
     536          670912 :                     continue;
     537                 : 
     538         1734789 :                 for (auto &OI : U->operands()) {
     539         1106092 :                     llvm::User *Operand = llvm::dyn_cast<llvm::User>(OI);
     540         1106092 :                     if (!Operand)
     541          320099 :                         continue;
     542          907513 :                     if (llvm::isa<llvm::BlockAddress>(Operand))
     543 UBC           0 :                         continue;
     544 CBC      907513 :                     if (auto *GV = llvm::dyn_cast<llvm::GlobalVariable>(Operand)) {
     545           35715 :                         referencedVars.insert(GV);
     546           35715 :                         if (GV->hasInitializer())
     547           32835 :                             Worklist.push_back(GV->getInitializer());
     548           35715 :                         continue;
     549                 :                     }
     550          871798 :                     if (auto *CF = llvm::dyn_cast<llvm::Function>(Operand)) {
     551           85805 :                         referencedFunctions.insert(CF);
     552           85805 :                         continue;
     553                 :                     }
     554          785993 :                     Worklist.push_back(Operand);
     555                 :                 }
     556                 :             }
     557          480781 :         }
     558                 :     }
     559            7587 : }
     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            7593 : 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            7593 :     int subThreshold = threshold * inline_cost_decay_factor;
     579            7593 :     llvm::SmallPtrSet<llvm::GlobalVariable *, 8> referencedVars;
     580            7593 :     llvm::SmallPtrSet<llvm::Function *, 8> referencedFunctions;
     581                 : 
     582                 :     /* can't rely on what may be inlined */
     583            7593 :     if (F.isInterposable())
     584 UBC           0 :         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 CBC        7593 :     if (F.hasAvailableExternallyLinkage())
     591 UBC           0 :         return false;
     592                 : 
     593                 :     ilog(DEBUG1, "checking inlinability of %s", F.getName().data());
     594                 : 
     595 CBC        7593 :     if (F.materialize())
     596 UBC           0 :         elog(FATAL, "failed to materialize metadata");
     597                 : 
     598                 : #if LLVM_VERSION_MAJOR < 14
     599                 : #define hasFnAttr hasFnAttribute
     600                 : #endif
     601                 : 
     602 CBC        7593 :     if (F.getAttributes().hasFnAttr(llvm::Attribute::NoInline))
     603                 :     {
     604                 :         ilog(DEBUG1, "ineligibile to import %s due to noinline",
     605                 :              F.getName().data());
     606               6 :         return false;
     607                 :     }
     608                 : 
     609            7587 :     function_references(F, running_instcount, referencedVars, referencedFunctions);
     610                 : 
     611           33943 :     for (llvm::GlobalVariable* rv: referencedVars)
     612                 :     {
     613           27344 :         if (rv->materialize())
     614 UBC           0 :             elog(FATAL, "failed to materialize metadata");
     615                 : 
     616                 :         /*
     617                 :          * Don't inline functions that access thread local variables.  That
     618                 :          * doesn't work on current LLVM releases (but might in future).
     619                 :          */
     620 CBC       27344 :         if (rv->isThreadLocal())
     621                 :         {
     622                 :             ilog(DEBUG1, "cannot inline %s due to thread-local variable %s",
     623                 :                  F.getName().data(), rv->getName().data());
     624             988 :             return false;
     625                 :         }
     626                 : 
     627                 :         /*
     628                 :          * Never want to inline externally visible vars, cheap enough to
     629                 :          * reference.
     630                 :          */
     631           27344 :         if (rv->hasExternalLinkage() || rv->hasAvailableExternallyLinkage())
     632             887 :             continue;
     633                 : 
     634                 :         /*
     635                 :          * If variable is file-local, we need to inline it, to be able to
     636                 :          * inline the function itself. Can't do that if the variable can be
     637                 :          * modified, because they'd obviously get out of sync.
     638                 :          *
     639                 :          * XXX: Currently not a problem, but there'd be problems with
     640                 :          * nontrivial initializers if they were allowed for postgres.
     641                 :          */
     642           26457 :         if (!rv->isConstant())
     643                 :         {
     644                 :             ilog(DEBUG1, "cannot inline %s due to uncloneable variable %s",
     645                 :                  F.getName().data(), rv->getName().data());
     646             988 :             return false;
     647                 :         }
     648                 : 
     649                 :         ilog(DEBUG1, "memorizing global var %s linkage %d for inlining",
     650                 :              rv->getName().data(), (int)rv->getLinkage());
     651                 : 
     652           25469 :         importVars.insert(rv->getName());
     653                 :         /* small cost attributed to each cloned global */
     654           25469 :         running_instcount += 5;
     655                 :     }
     656                 : 
     657            6599 :     visitedFunctions.insert(&F);
     658                 : 
     659                 :     /*
     660                 :      * Check referenced functions. Check whether used static ones are
     661                 :      * inlinable, and remember external ones for inlining.
     662                 :      */
     663           44276 :     for (llvm::Function* referencedFunction: referencedFunctions)
     664                 :     {
     665           37715 :         llvm::StringSet<> recImportVars;
     666                 : 
     667           37715 :         if (referencedFunction->materialize())
     668 UBC           0 :             elog(FATAL, "failed to materialize metadata");
     669                 : 
     670 CBC       37715 :         if (referencedFunction->isIntrinsic())
     671            3591 :             continue;
     672                 : 
     673                 :         /* if already visited skip, otherwise remember */
     674           34124 :         if (!visitedFunctions.insert(referencedFunction).second)
     675           12565 :             continue;
     676                 : 
     677                 :         /*
     678                 :          * We don't inline external functions directly here, instead we put
     679                 :          * them on the worklist if appropriate and check them from
     680                 :          * llvm_build_inline_plan().
     681                 :          */
     682           21559 :         if (referencedFunction->hasExternalLinkage())
     683                 :         {
     684           19048 :             llvm::StringRef funcName = referencedFunction->getName();
     685                 : 
     686                 :             /*
     687                 :              * Don't bother checking for inlining if remaining cost budget is
     688                 :              * very small.
     689                 :              */
     690           19048 :             if (subThreshold < 5)
     691            2186 :                 continue;
     692                 : 
     693           16862 :             auto it = functionStates.find(funcName);
     694           16862 :             if (it == functionStates.end())
     695                 :             {
     696                 :                 FunctionInlineState inlineState;
     697                 : 
     698            9078 :                 inlineState.costLimit = subThreshold;
     699            9078 :                 inlineState.processed = false;
     700            9078 :                 inlineState.inlined = false;
     701            9078 :                 inlineState.allowReconsidering = false;
     702                 : 
     703            9078 :                 functionStates[funcName] = inlineState;
     704            9078 :                 worklist.push_back({funcName, searchpath});
     705                 : 
     706                 :                 ilog(DEBUG1,
     707                 :                      "considering extern function %s at %d for inlining",
     708                 :                      funcName.data(), subThreshold);
     709                 :             }
     710            7784 :             else if (!it->second.inlined &&
     711           13205 :                      (!it->second.processed || it->second.allowReconsidering) &&
     712           13205 :                      it->second.costLimit < subThreshold)
     713                 :             {
     714                 :                 /*
     715                 :                  * Update inlining threshold if higher. Need to re-queue
     716                 :                  * to be processed if already processed with lower
     717                 :                  * threshold.
     718                 :                  */
     719             407 :                 if (it->second.processed)
     720                 :                 {
     721                 :                     ilog(DEBUG1,
     722                 :                          "reconsidering extern function %s at %d for inlining, increasing from %d",
     723                 :                          funcName.data(), subThreshold, it->second.costLimit);
     724                 : 
     725             369 :                     it->second.processed = false;
     726             369 :                     it->second.allowReconsidering = false;
     727             369 :                     worklist.push_back({funcName, searchpath});
     728                 :                 }
     729             407 :                 it->second.costLimit = subThreshold;
     730                 :             }
     731           16862 :             continue;
     732           16862 :         }
     733                 : 
     734                 :         /* can't rely on what may be inlined */
     735            2511 :         if (referencedFunction->isInterposable())
     736 UBC           0 :             return false;
     737                 : 
     738 CBC        2511 :         if (!function_inlinable(*referencedFunction,
     739                 :                                 subThreshold,
     740                 :                                 functionStates,
     741                 :                                 worklist,
     742                 :                                 searchpath,
     743                 :                                 visitedFunctions,
     744                 :                                 running_instcount,
     745                 :                                 recImportVars))
     746                 :         {
     747                 :             ilog(DEBUG1,
     748                 :                  "cannot inline %s due to required function %s not being inlinable",
     749                 :                  F.getName().data(), referencedFunction->getName().data());
     750              38 :             return false;
     751                 :         }
     752                 : 
     753                 :         /* import referenced function itself */
     754            2473 :         importVars.insert(referencedFunction->getName());
     755                 : 
     756                 :         /* import referenced function and its dependents */
     757           52416 :         for (auto& recImportVar : recImportVars)
     758           49943 :             importVars.insert(recImportVar.first());
     759           37715 :     }
     760                 : 
     761            6561 :     return true;
     762           17040 : }
     763                 : 
     764                 : /*
     765                 :  * Attempt to load module summary located at path. Return empty pointer when
     766                 :  * loading fails.
     767                 :  */
     768                 : static std::unique_ptr<llvm::ModuleSummaryIndex>
     769             249 : llvm_load_summary(llvm::StringRef path)
     770                 : {
     771                 :     llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer> > MBOrErr =
     772             249 :         llvm::MemoryBuffer::getFile(path);
     773                 : 
     774             249 :     if (std::error_code EC = MBOrErr.getError())
     775                 :     {
     776                 :         ilog(DEBUG1, "failed to open %s: %s", path.data(),
     777                 :              EC.message().c_str());
     778                 :     }
     779                 :     else
     780                 :     {
     781             249 :         llvm::MemoryBufferRef ref(*MBOrErr.get().get());
     782                 : 
     783                 : #if LLVM_VERSION_MAJOR > 3
     784                 :         llvm::Expected<std::unique_ptr<llvm::ModuleSummaryIndex> > IndexOrErr =
     785             249 :             llvm::getModuleSummaryIndex(ref);
     786             249 :         if (IndexOrErr)
     787             249 :             return std::move(IndexOrErr.get());
     788 UBC           0 :         elog(FATAL, "failed to load summary \"%s\": %s",
     789                 :              path.data(),
     790                 :              toString(IndexOrErr.takeError()).c_str());
     791                 : #else
     792                 :         llvm::ErrorOr<std::unique_ptr<llvm::ModuleSummaryIndex> > IndexOrErr =
     793                 :             llvm::getModuleSummaryIndex(ref, [](const llvm::DiagnosticInfo &) {});
     794                 :         if (IndexOrErr)
     795                 :             return std::move(IndexOrErr.get());
     796                 :         elog(FATAL, "failed to load summary \"%s\": %s",
     797                 :              path.data(),
     798                 :              IndexOrErr.getError().message().c_str());
     799                 : #endif
     800 CBC         249 :     }
     801 UBC           0 :     return nullptr;
     802 CBC         249 : }
     803                 : 
     804                 : /*
     805                 :  * Attempt to add modpath to the search path.
     806                 :  */
     807                 : static void
     808             518 : add_module_to_inline_search_path(InlineSearchPath& searchpath, llvm::StringRef modpath)
     809                 : {
     810                 :     /* only extension in libdir are candidates for inlining for now */
     811             518 :     if (!modpath.startswith("$libdir/"))
     812 UBC           0 :         return;
     813                 : 
     814                 :     /* if there's no match, attempt to load */
     815 CBC         518 :     auto it = summary_cache->find(modpath);
     816             518 :     if (it == summary_cache->end())
     817                 :     {
     818             249 :         std::string path(modpath);
     819             249 :         path = path.replace(0, strlen("$libdir"), std::string(pkglib_path) + "/bitcode");
     820             249 :         path += ".index.bc";
     821             249 :         (*summary_cache)[modpath] = llvm_load_summary(path);
     822             249 :         it = summary_cache->find(modpath);
     823             249 :     }
     824                 : 
     825             518 :     Assert(it != summary_cache->end());
     826                 : 
     827                 :     /* if the entry isn't NULL, it's validly loaded */
     828             518 :     if (it->second)
     829             518 :         searchpath.push_back(it->second.get());
     830                 : }
     831                 : 
     832                 : /*
     833                 :  * Search for all references for functions hashing to guid in the search path,
     834                 :  * and return them in search path order.
     835                 :  */
     836                 : static llvm::SmallVector<llvm::GlobalValueSummary *, 1>
     837           11311 : summaries_for_guid(const InlineSearchPath& path, llvm::GlobalValue::GUID guid)
     838                 : {
     839           11311 :     llvm::SmallVector<llvm::GlobalValueSummary *, 1> matches;
     840                 : 
     841           23298 :     for (auto index : path)
     842                 :     {
     843                 : #if LLVM_VERSION_MAJOR > 4
     844           11987 :         llvm::ValueInfo funcVI = index->getValueInfo(guid);
     845                 : 
     846                 :         /* if index doesn't know function, we don't have a body, continue */
     847           11987 :         if (funcVI)
     848           18620 :             for (auto &gv : funcVI.getSummaryList())
     849            9310 :                 matches.push_back(gv.get());
     850                 : #else
     851                 :         const llvm::const_gvsummary_iterator &I =
     852                 :             index->findGlobalValueSummaryList(guid);
     853                 :         if (I != index->end())
     854                 :         {
     855                 :             for (auto &gv : I->second)
     856                 :                 matches.push_back(gv.get());
     857                 :         }
     858                 : #endif
     859                 :     }
     860                 : 
     861           11311 :     return matches;
     862 UBC           0 : }
     863                 : 
     864                 : /*
     865                 :  * Create inline wrapper with the name Name, redirecting the call to F.
     866                 :  */
     867                 : static llvm::Function*
     868 CBC          16 : create_redirection_function(std::unique_ptr<llvm::Module> &importMod,
     869                 :                             llvm::Function *F,
     870                 :                             llvm::StringRef Name)
     871                 : {
     872                 :     typedef llvm::GlobalValue::LinkageTypes LinkageTypes;
     873                 : 
     874              16 :     llvm::LLVMContext &Context = F->getContext();
     875              16 :     llvm::IRBuilder<> Builder(Context);
     876                 :     llvm::Function *AF;
     877                 :     llvm::BasicBlock *BB;
     878                 :     llvm::CallInst *fwdcall;
     879                 : #if LLVM_VERSION_MAJOR < 14
     880                 :     llvm::Attribute inlineAttribute;
     881                 : #endif
     882                 : 
     883              16 :     AF = llvm::Function::Create(F->getFunctionType(),
     884                 :                                 LinkageTypes::AvailableExternallyLinkage,
     885                 :                                 Name, importMod.get());
     886              16 :     BB = llvm::BasicBlock::Create(Context, "entry", AF);
     887                 : 
     888              16 :     Builder.SetInsertPoint(BB);
     889              16 :     fwdcall = Builder.CreateCall(F, &*AF->arg_begin());
     890                 : #if LLVM_VERSION_MAJOR < 14
     891                 :     inlineAttribute = llvm::Attribute::get(Context,
     892                 :                                            llvm::Attribute::AlwaysInline);
     893                 :     fwdcall->addAttribute(~0U, inlineAttribute);
     894                 : #else
     895              16 :     fwdcall->addFnAttr(llvm::Attribute::AlwaysInline);
     896                 : #endif
     897              16 :     Builder.CreateRet(fwdcall);
     898                 : 
     899              16 :     return AF;
     900              16 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a