LCOV - code coverage report
Current view: top level - src/backend/jit/llvm - llvmjit_inline.cpp (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 0.0 % 264 0
Test Date: 2026-07-03 19:57:34 Functions: 0.0 % 13 0
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 0.0 % 667 0

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

Generated by: LCOV version 2.0-1