1/*-------------------------------------------------------------------------
4 * Cross module inlining suitable for postgres' JIT
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.
14 * Copyright (c) 2016-2025, PostgreSQL Global Development Group
17 * src/backend/lib/llvmjit/llvmjit_inline.cpp
19 *-------------------------------------------------------------------------
42#include <llvm-c/Core.h>
43#include <llvm-c/BitReader.h>
45/* Avoid macro clash with LLVM's C++ headers */
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>
64 * Type used to represent modules InlineWorkListItem's subject is searched for
70 * Item in queue of to-be-checked symbols and corresponding queue.
75 llvm::SmallVector<llvm::ModuleSummaryIndex *, 2>
searchpath;
80 * Information about symbols processed during inlining. Used to prevent
81 * repeated searches and provide additional information.
93 * Map of modules that should be inlined, with a list of the to-be inlined
103 * These are managed statics so LLVM knows to deallocate them during an
104 * LLVMShutdown(), rather than after (which'd cause crashes).
106 typedef llvm::StringMap<std::unique_ptr<llvm::Module> >
ModuleCache;
108 typedef llvm::StringMap<std::unique_ptr<llvm::ModuleSummaryIndex> >
SummaryCache;
117static std::unique_ptr<llvm::Module>
load_module(LLVMContextRef
c, llvm::StringRef Identifier);
118static std::unique_ptr<llvm::ModuleSummaryIndex>
llvm_load_summary(llvm::StringRef path);
123 llvm::StringRef
Name);
130 llvm::SmallPtrSet<const llvm::Function *, 8> &visitedFunctions,
131 int &running_instcount,
132 llvm::StringSet<> &importVars);
134 int &running_instcount,
135 llvm::SmallPtrSet<llvm::GlobalVariable *, 8> &referencedVars,
136 llvm::SmallPtrSet<llvm::Function *, 8> &referencedFunctions);
139static llvm::SmallVector<llvm::GlobalValueSummary *, 1>
142/* verbose debugging for inliner development */
143/* #define INLINE_DEBUG */
147 #define ilog(...) (void) 0
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.
163 * Perform inlining of external function references in M based on a simple
164 * cost based analysis.
169 LLVMContextRef lc = LLVMGetModuleContext(M);
170 llvm::Module *mod = llvm::unwrap(M);
173 if (!globalsToInline)
179 * Build information necessary for inlining external function references in
182static std::unique_ptr<ImportMapTy>
185 std::unique_ptr<ImportMapTy> globalsToInline(
new ImportMapTy());
191 /* attempt to add module to search path */
193 /* if postgres isn't available, no point continuing */
194 if (defaultSearchPath.empty())
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
203 for (
const llvm::Function &funcDecl : mod->functions())
208 /* already has a definition */
209 if (!funcDecl.isDeclaration())
212 /* llvm provides implementation */
213 if (funcDecl.isIntrinsic())
218 worklist.push_back(item);
223 functionStates[funcDecl.getName()] = inlineState;
227 * Iterate over pending worklist items, look them up in index, check
228 * whether they should be inlined.
230 while (!worklist.empty())
237 llvm::GlobalValue::GUID funcGUID;
241#if LLVM_VERSION_MAJOR >= 21
242 funcGUID = llvm::GlobalValue::getGUIDAssumingExternalLinkage(cfuncname);
244 funcGUID = llvm::GlobalValue::getGUID(cfuncname);
247 /* already processed */
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).
262 const llvm::FunctionSummary *fs;
263 llvm::StringRef modPath = gvs->modulePath();
264 llvm::Module *defMod;
265 llvm::Function *funcDef;
267 fs = llvm::cast<llvm::FunctionSummary>(gvs);
269 if (gvs->notEligibleToImport())
271 ilog(
DEBUG1,
"ineligibile to import %s due to summary",
276 if ((
int) fs->instCount() > inlineState.
costLimit)
278 ilog(
DEBUG1,
"ineligibile to import %s due to early threshold: %u vs %u",
279 symbolName.data(), fs->instCount(), inlineState.
costLimit);
285 if (defMod->materializeMetadata())
286 elog(
FATAL,
"failed to materialize metadata");
288 funcDef = defMod->getFunction(cfuncname);
291 * This can happen e.g. in case of a hash collision of the
297 if (funcDef->materialize())
298 elog(
FATAL,
"failed to materialize metadata");
300 Assert(!funcDef->isDeclaration());
301 Assert(funcDef->hasExternalLinkage());
303 llvm::StringSet<> importVars;
304 llvm::SmallPtrSet<const llvm::Function *, 8> visitedFunctions;
305 int running_instcount = 0;
308 * Check whether function, and objects it depends on, are
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.
327 if (running_instcount > inlineState.
costLimit)
329 ilog(
DEBUG1,
"skipping inlining of %s due to late threshold %d vs %d",
330 symbolName.data(), running_instcount, inlineState.
costLimit);
335 ilog(
DEBUG1,
"inline top function %s total_instcount: %d, partial: %d",
336 symbolName.data(), running_instcount, fs->instCount());
338 /* import referenced function itself */
339 importVars.insert(symbolName);
342 llvm::StringSet<> &modGlobalsToInline = (*globalsToInline)[modPath];
343 for (
auto& importVar : importVars)
344 modGlobalsToInline.insert(importVar.first());
345 Assert(modGlobalsToInline.size() > 0);
348 /* mark function as inlined */
352 * Found definition to inline, don't look for further
353 * potential definitions.
362 /* It's possible there's another definition that's inlinable. */
367 * Signal that we're done with symbol, whether successful (inlined =
368 * true above) or not.
373 return globalsToInline;
377 * Perform the actual inlining of external functions (and their dependencies)
383 llvm::IRMover Mover(*mod);
385 for (
const auto& toInline : *globalsToInline)
387 const llvm::StringRef& modPath = toInline.first();
388 const llvm::StringSet<>& modGlobalsToInline = toInline.second;
389 llvm::SetVector<llvm::GlobalValue *> GlobalsToImport;
392 std::unique_ptr<llvm::Module> importMod(std::move((*
module_cache)[modPath]));
395 if (modGlobalsToInline.empty())
398 for (
auto &glob: modGlobalsToInline)
400 llvm::StringRef SymbolName = glob.first();
406 llvm::GlobalValue *valueToImport = importMod->getNamedValue(
funcname);
409 elog(
FATAL,
"didn't refind value %s to import", SymbolName.data());
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.
417 if (llvm::isa<llvm::Function>(valueToImport))
419 llvm::Function *
F = llvm::dyn_cast<llvm::Function>(valueToImport);
420 typedef llvm::GlobalValue::LinkageTypes LinkageTypes;
423 * Per-function info isn't necessarily stripped yet, as the
424 * module is lazy-loaded when stripped above.
426 llvm::stripDebugInfo(*
F);
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.
442 GlobalsToImport.insert(AF);
443 llvm::stripDebugInfo(*AF);
446 if (valueToImport->hasExternalLinkage())
448 valueToImport->setLinkage(LinkageTypes::AvailableExternallyLinkage);
452 GlobalsToImport.insert(valueToImport);
454 modPath.data(), SymbolName.data());
458 if (Mover.move(std::move(importMod), GlobalsToImport.getArrayRef(),
459 [](llvm::GlobalValue &, llvm::IRMover::ValueAdder) {},
460 /*IsPerformingImport=*/false))
461 elog(
FATAL,
"function import failed with linker error");
466 * Return a module identified by modPath, caching it in memory.
468 * Note that such a module may *not* be modified without copying, otherwise
469 * the cache state would get corrupted.
478 std::make_pair(modPath,
load_module(lc, modPath))).first;
481 return it->second.get();
484static std::unique_ptr<llvm::Module>
487 LLVMMemoryBufferRef
buf;
494 if (LLVMCreateMemoryBufferWithContentsOfFile(path, &
buf, &msg))
495 elog(
FATAL,
"failed to open bitcode file \"%s\": %s",
497 if (LLVMGetBitcodeModuleInContext2(lc,
buf, &mod))
498 elog(
FATAL,
"failed to parse bitcode in file \"%s\"", path);
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.
505 llvm::StripDebugInfo(*llvm::unwrap(mod));
507 return std::unique_ptr<llvm::Module>(llvm::unwrap(mod));
511 * Compute list of referenced variables, functions and the instruction count
516 int &running_instcount,
517 llvm::SmallPtrSet<llvm::GlobalVariable *, 8> &referencedVars,
518 llvm::SmallPtrSet<llvm::Function *, 8> &referencedFunctions)
520 llvm::SmallPtrSet<const llvm::User *, 32> Visited;
522 for (llvm::BasicBlock &BB :
F)
524 for (llvm::Instruction &
I : BB)
526 if (llvm::isa<llvm::DbgInfoIntrinsic>(
I))
529 llvm::SmallVector<llvm::User *, 8> Worklist;
530 Worklist.push_back(&
I);
534 while (!Worklist.empty()) {
535 llvm::User *U = Worklist.pop_back_val();
538 if (!Visited.insert(U).second)
541 for (
auto &OI : U->operands()) {
542 llvm::User *Operand = llvm::dyn_cast<llvm::User>(OI);
545 if (llvm::isa<llvm::BlockAddress>(Operand))
547 if (
auto *GV = llvm::dyn_cast<llvm::GlobalVariable>(Operand)) {
548 referencedVars.insert(GV);
549 if (GV->hasInitializer())
550 Worklist.push_back(GV->getInitializer());
553 if (
auto *CF = llvm::dyn_cast<llvm::Function>(Operand)) {
554 referencedFunctions.insert(CF);
557 Worklist.push_back(Operand);
565 * Check whether function F is inlinable and, if so, what globals need to be
568 * References to external functions from, potentially recursively, inlined
569 * functions are added to the passed in worklist.
577 llvm::SmallPtrSet<const llvm::Function *, 8> &visitedFunctions,
578 int &running_instcount,
579 llvm::StringSet<> &importVars)
582 llvm::SmallPtrSet<llvm::GlobalVariable *, 8> referencedVars;
583 llvm::SmallPtrSet<llvm::Function *, 8> referencedFunctions;
585 /* can't rely on what may be inlined */
586 if (
F.isInterposable())
590 * Can't rely on function being present. Alternatively we could create a
591 * static version of these functions?
593 if (
F.hasAvailableExternallyLinkage())
596 ilog(
DEBUG1,
"checking inlinability of %s",
F.getName().data());
599 elog(
FATAL,
"failed to materialize metadata");
601 if (
F.getAttributes().hasFnAttr(llvm::Attribute::NoInline))
603 ilog(
DEBUG1,
"ineligibile to import %s due to noinline",
610 for (llvm::GlobalVariable* rv: referencedVars)
612 if (rv->materialize())
613 elog(
FATAL,
"failed to materialize metadata");
616 * Don't inline functions that access thread local variables. That
617 * doesn't work on current LLVM releases (but might in future).
619 if (rv->isThreadLocal())
621 ilog(
DEBUG1,
"cannot inline %s due to thread-local variable %s",
622 F.getName().data(), rv->getName().data());
627 * Never want to inline externally visible vars, cheap enough to
630 if (rv->hasExternalLinkage() || rv->hasAvailableExternallyLinkage())
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.
638 * XXX: Currently not a problem, but there'd be problems with
639 * nontrivial initializers if they were allowed for postgres.
641 if (!rv->isConstant())
643 ilog(
DEBUG1,
"cannot inline %s due to uncloneable variable %s",
644 F.getName().data(), rv->getName().data());
648 ilog(
DEBUG1,
"memorizing global var %s linkage %d for inlining",
649 rv->getName().data(), (
int)rv->getLinkage());
651 importVars.insert(rv->getName());
652 /* small cost attributed to each cloned global */
653 running_instcount += 5;
656 visitedFunctions.insert(&
F);
659 * Check referenced functions. Check whether used static ones are
660 * inlinable, and remember external ones for inlining.
662 for (llvm::Function* referencedFunction: referencedFunctions)
664 llvm::StringSet<> recImportVars;
666 if (referencedFunction->materialize())
667 elog(
FATAL,
"failed to materialize metadata");
669 if (referencedFunction->isIntrinsic())
672 /* if already visited skip, otherwise remember */
673 if (!visitedFunctions.insert(referencedFunction).second)
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().
681 if (referencedFunction->hasExternalLinkage())
683 llvm::StringRef funcName = referencedFunction->getName();
686 * Don't bother checking for inlining if remaining cost budget is
689 if (subThreshold < 5)
692 auto it = functionStates.find(funcName);
693 if (it == functionStates.end())
702 functionStates[funcName] = inlineState;
703 worklist.push_back({funcName, searchpath});
706 "considering extern function %s at %d for inlining",
707 funcName.data(), subThreshold);
709 else if (!it->second.inlined &&
710 (!it->second.processed || it->second.allowReconsidering) &&
711 it->second.costLimit < subThreshold)
714 * Update inlining threshold if higher. Need to re-queue
715 * to be processed if already processed with lower
718 if (it->second.processed)
721 "reconsidering extern function %s at %d for inlining, increasing from %d",
722 funcName.data(), subThreshold, it->second.costLimit);
724 it->second.processed =
false;
725 it->second.allowReconsidering =
false;
726 worklist.push_back({funcName, searchpath});
728 it->second.costLimit = subThreshold;
733 /* can't rely on what may be inlined */
734 if (referencedFunction->isInterposable())
747 "cannot inline %s due to required function %s not being inlinable",
748 F.getName().data(), referencedFunction->getName().data());
752 /* import referenced function itself */
753 importVars.insert(referencedFunction->getName());
755 /* import referenced function and its dependents */
756 for (
auto& recImportVar : recImportVars)
757 importVars.insert(recImportVar.first());
764 * Attempt to load module summary located at path. Return empty pointer when
767static std::unique_ptr<llvm::ModuleSummaryIndex>
770 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer> > MBOrErr =
771 llvm::MemoryBuffer::getFile(path);
773 if (std::error_code EC = MBOrErr.getError())
775 ilog(
DEBUG1,
"failed to open %s: %s", path.data(),
776 EC.message().c_str());
780 llvm::MemoryBufferRef ref(*MBOrErr.get().get());
782 llvm::Expected<std::unique_ptr<llvm::ModuleSummaryIndex> > IndexOrErr =
783 llvm::getModuleSummaryIndex(ref);
785 return std::move(IndexOrErr.get());
786 elog(
FATAL,
"failed to load summary \"%s\": %s",
788 toString(IndexOrErr.takeError()).c_str());
794 * Attempt to add modpath to the search path.
799 /* only extension in libdir are candidates for inlining for now */
800#if LLVM_VERSION_MAJOR < 16
801#define starts_with startswith
803 if (!modpath.starts_with(
"$libdir/"))
806 /* if there's no match, attempt to load */
819 /* if the entry isn't NULL, it's validly loaded */
821 searchpath.push_back(it->second.get());
825 * Search for all references for functions hashing to guid in the search path,
826 * and return them in search path order.
828static llvm::SmallVector<llvm::GlobalValueSummary *, 1>
831 llvm::SmallVector<llvm::GlobalValueSummary *, 1> matches;
833 for (
auto index : path)
835 llvm::ValueInfo funcVI =
index->getValueInfo(guid);
837 /* if index doesn't know function, we don't have a body, continue */
839 for (
auto &gv : funcVI.getSummaryList())
840 matches.push_back(gv.get());
847 * Create inline wrapper with the name Name, redirecting the call to F.
849static llvm::Function*
852 llvm::StringRef
Name)
854 typedef llvm::GlobalValue::LinkageTypes LinkageTypes;
856 llvm::LLVMContext &Context =
F->getContext();
857 llvm::IRBuilder<> Builder(Context);
859 llvm::BasicBlock *BB;
860 llvm::CallInst *fwdcall;
862 AF = llvm::Function::Create(
F->getFunctionType(),
863 LinkageTypes::AvailableExternallyLinkage,
864 Name, importMod.get());
865 BB = llvm::BasicBlock::Create(Context,
"entry", AF);
867 Builder.SetInsertPoint(BB);
868 fwdcall = Builder.CreateCall(
F, &*AF->arg_begin());
869 fwdcall->addFnAttr(llvm::Attribute::AlwaysInline);
870 Builder.CreateRet(fwdcall);
char pkglib_path[MAXPGPATH]
Assert(PointerIsAligned(start, uint64))
void llvm_split_symbol_name(const char *name, char **modname, char **funcname)
void llvm_inline(LLVMModuleRef M)
llvm::SmallVector< InlineWorkListItem, 128 > InlineWorkList
const int inline_initial_cost
static std::unique_ptr< llvm::ModuleSummaryIndex > llvm_load_summary(llvm::StringRef path)
void llvm_inline_reset_caches(void)
llvm::SmallVector< llvm::ModuleSummaryIndex *, 2 > InlineSearchPath
struct FunctionInlineState FunctionInlineState
static llvm::SmallVector< llvm::GlobalValueSummary *, 1 > summaries_for_guid(const InlineSearchPath &path, llvm::GlobalValue::GUID guid)
const float inline_cost_decay_factor
llvm::StringMap< llvm::StringSet<> > ImportMapTy
static std::unique_ptr< llvm::Module > load_module(LLVMContextRef c, llvm::StringRef Identifier)
struct InlineWorkListItem InlineWorkListItem
static void function_references(llvm::Function &F, int &running_instcount, llvm::SmallPtrSet< llvm::GlobalVariable *, 8 > &referencedVars, llvm::SmallPtrSet< llvm::Function *, 8 > &referencedFunctions)
static void add_module_to_inline_search_path(InlineSearchPath &path, llvm::StringRef modpath)
static llvm::Module * load_module_cached(LLVMContextRef c, llvm::StringRef modPath)
llvm::StringMap< FunctionInlineState > FunctionInlineStates
static llvm::Function * create_redirection_function(std::unique_ptr< llvm::Module > &importMod, llvm::Function *F, llvm::StringRef Name)
llvm::StringMap< std::unique_ptr< llvm::ModuleSummaryIndex > > SummaryCache
llvm::StringMap< std::unique_ptr< llvm::Module > > ModuleCache
llvm::ManagedStatic< ModuleCache > module_cache
llvm::ManagedStatic< SummaryCache > summary_cache
static std::unique_ptr< ImportMapTy > llvm_build_inline_plan(LLVMContextRef lc, llvm::Module *mod)
static void llvm_execute_inline_plan(llvm::Module *mod, ImportMapTy *globalsToInline)
static bool function_inlinable(llvm::Function &F, int threshold, FunctionInlineStates &functionState, InlineWorkList &worklist, InlineSearchPath &searchpath, llvm::SmallPtrSet< const llvm::Function *, 8 > &visitedFunctions, int &running_instcount, llvm::StringSet<> &importVars)
llvm::StringRef symbolName
llvm::SmallVector< llvm::ModuleSummaryIndex *, 2 > searchpath