diff options
author | Daniel Gustafsson <dgustafsson@postgresql.org> | 2023-09-27 13:02:21 +0200 |
---|---|---|
committer | Daniel Gustafsson <dgustafsson@postgresql.org> | 2023-09-27 13:02:21 +0200 |
commit | 9dce22033d5d2813e9f8e7d595f57ee5a38b3f8e (patch) | |
tree | 3027d39cfe18ebf288837ea30600dee90fb36eb2 /src/backend/jit/llvm/llvmjit_inline.cpp | |
parent | ef668d8bf56e8c164ed62acdb269aa2f7e7ad5a1 (diff) | |
download | postgresql-9dce22033d5d2813e9f8e7d595f57ee5a38b3f8e.tar.gz postgresql-9dce22033d5d2813e9f8e7d595f57ee5a38b3f8e.zip |
llvmjit: Use explicit LLVMContextRef for inlining
When performing inlining LLVM unfortunately "leaks" types (the
types survive and are usable, but a new round of inlining will
recreate new structurally equivalent types). This accumulation
will over time amount to a memory leak which for some queries
can be large enough to trigger the OOM process killer.
To avoid accumulation of types, all IR related data is stored
in an LLVMContextRef which is dropped and recreated in order
to release all types. Dropping and recreating incurs overhead,
so it will be done only after 100 queries. This is a heuristic
which might be revisited, but until we can get the size of the
context from LLVM we are flying a bit blind.
This issue has been reported several times, there may be more
references to it in the archives on top of the threads linked
below.
Backpatching of this fix will be handled once it has matured
in master for a bit.
Reported-By: Justin Pryzby <pryzby@telsasoft.com>
Reported-By: Kurt Roeckx <kurt@roeckx.be>
Reported-By: Jaime Casanova <jcasanov@systemguards.com.ec>
Reported-By: Lauri Laanmets <pcspets@gmail.com>
Author: Andres Freund and Daniel Gustafsson
Discussion: https://postgr.es/m/7acc8678-df5f-4923-9cf6-e843131ae89d@www.fastmail.com
Discussion: https://postgr.es/m/20201218235607.GC30237@telsasoft.com
Discussion: https://postgr.es/m/CAPH-tTxLf44s3CvUUtQpkDr1D8Hxqc2NGDzGXS1ODsfiJ6WSqA@mail.gmail.com
Diffstat (limited to 'src/backend/jit/llvm/llvmjit_inline.cpp')
-rw-r--r-- | src/backend/jit/llvm/llvmjit_inline.cpp | 33 |
1 files changed, 23 insertions, 10 deletions
diff --git a/src/backend/jit/llvm/llvmjit_inline.cpp b/src/backend/jit/llvm/llvmjit_inline.cpp index c765add8564..d92d7f3c881 100644 --- a/src/backend/jit/llvm/llvmjit_inline.cpp +++ b/src/backend/jit/llvm/llvmjit_inline.cpp @@ -114,12 +114,12 @@ typedef llvm::StringMap<std::unique_ptr<llvm::ModuleSummaryIndex> > SummaryCache llvm::ManagedStatic<SummaryCache> summary_cache; -static std::unique_ptr<ImportMapTy> llvm_build_inline_plan(llvm::Module *mod); +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 llvm::Module* load_module_cached(llvm::StringRef modPath); -static std::unique_ptr<llvm::Module> load_module(llvm::StringRef Identifier); +static llvm::Module* load_module_cached(LLVMContextRef c, llvm::StringRef modPath); +static std::unique_ptr<llvm::Module> load_module(LLVMContextRef c, llvm::StringRef Identifier); static std::unique_ptr<llvm::ModuleSummaryIndex> llvm_load_summary(llvm::StringRef path); @@ -153,15 +153,28 @@ summaries_for_guid(const InlineSearchPath& path, llvm::GlobalValue::GUID guid); #endif /* + * Reset inlining related state. This needs to be called before the currently + * used LLVMContextRef is disposed (and a new one create), otherwise we would + * have dangling references to deleted modules. + */ +void +llvm_inline_reset_caches(void) +{ + module_cache->clear(); + summary_cache->clear(); +} + +/* * Perform inlining of external function references in M based on a simple * cost based analysis. */ void llvm_inline(LLVMModuleRef M) { + LLVMContextRef lc = LLVMGetModuleContext(M); llvm::Module *mod = llvm::unwrap(M); - std::unique_ptr<ImportMapTy> globalsToInline = llvm_build_inline_plan(mod); + std::unique_ptr<ImportMapTy> globalsToInline = llvm_build_inline_plan(lc, mod); if (!globalsToInline) return; llvm_execute_inline_plan(mod, globalsToInline.get()); @@ -172,7 +185,7 @@ llvm_inline(LLVMModuleRef M) * mod. */ static std::unique_ptr<ImportMapTy> -llvm_build_inline_plan(llvm::Module *mod) +llvm_build_inline_plan(LLVMContextRef lc, llvm::Module *mod) { std::unique_ptr<ImportMapTy> globalsToInline(new ImportMapTy()); FunctionInlineStates functionStates; @@ -271,7 +284,7 @@ llvm_build_inline_plan(llvm::Module *mod) continue; } - defMod = load_module_cached(modPath); + defMod = load_module_cached(lc, modPath); if (defMod->materializeMetadata()) elog(FATAL, "failed to materialize metadata"); @@ -466,20 +479,20 @@ llvm_execute_inline_plan(llvm::Module *mod, ImportMapTy *globalsToInline) * the cache state would get corrupted. */ static llvm::Module* -load_module_cached(llvm::StringRef modPath) +load_module_cached(LLVMContextRef lc, llvm::StringRef modPath) { auto it = module_cache->find(modPath); if (it == module_cache->end()) { it = module_cache->insert( - std::make_pair(modPath, load_module(modPath))).first; + std::make_pair(modPath, load_module(lc, modPath))).first; } return it->second.get(); } static std::unique_ptr<llvm::Module> -load_module(llvm::StringRef Identifier) +load_module(LLVMContextRef lc, llvm::StringRef Identifier) { LLVMMemoryBufferRef buf; LLVMModuleRef mod; @@ -491,7 +504,7 @@ load_module(llvm::StringRef Identifier) if (LLVMCreateMemoryBufferWithContentsOfFile(path, &buf, &msg)) elog(FATAL, "failed to open bitcode file \"%s\": %s", path, msg); - if (LLVMGetBitcodeModuleInContext2(LLVMGetGlobalContext(), buf, &mod)) + if (LLVMGetBitcodeModuleInContext2(lc, buf, &mod)) elog(FATAL, "failed to parse bitcode in file \"%s\"", path); /* |