4747#include "utils/memutils.h"
4848#include "utils/resowner_private.h"
4949
50+ #define LLVMJIT_LLVM_CONTEXT_REUSE_MAX 100
51+
5052/* Handle of a module emitted via ORC JIT */
5153typedef struct LLVMJitHandle
5254{
@@ -100,8 +102,15 @@ LLVMModuleRef llvm_types_module = NULL;
100102
101103static bool llvm_session_initialized = false;
102104static size_t llvm_generation = 0 ;
105+
106+ /* number of LLVMJitContexts that currently are in use */
107+ static size_t llvm_jit_context_in_use_count = 0 ;
108+
109+ /* how many times has the current LLVMContextRef been used */
110+ static size_t llvm_llvm_context_reuse_count = 0 ;
103111static const char * llvm_triple = NULL ;
104112static const char * llvm_layout = NULL ;
113+ static LLVMContextRef llvm_context ;
105114
106115
107116static LLVMTargetRef llvm_targetref ;
@@ -122,6 +131,8 @@ static void llvm_compile_module(LLVMJitContext *context);
122131static void llvm_optimize_module (LLVMJitContext * context , LLVMModuleRef module );
123132
124133static void llvm_create_types (void );
134+ static void llvm_set_target (void );
135+ static void llvm_recreate_llvm_context (void );
125136static uint64_t llvm_resolve_symbol (const char * name , void * ctx );
126137
127138#if LLVM_VERSION_MAJOR > 11
@@ -143,6 +154,63 @@ _PG_jit_provider_init(JitProviderCallbacks *cb)
143154 cb -> compile_expr = llvm_compile_expr ;
144155}
145156
157+
158+ /*
159+ * Every now and then create a new LLVMContextRef. Unfortunately, during every
160+ * round of inlining, types may "leak" (they can still be found/used via the
161+ * context, but new types will be created the next time in inlining is
162+ * performed). To prevent that from slowly accumulating problematic amounts of
163+ * memory, recreate the LLVMContextRef we use. We don't want to do so too
164+ * often, as that implies some overhead (particularly re-loading the module
165+ * summaries / modules is fairly expensive). A future TODO would be to make
166+ * this more finegrained and only drop/recreate the LLVMContextRef when we know
167+ * there has been inlining. If we can get the size of the context from LLVM
168+ * then that might be a better way to determine when to drop/recreate rather
169+ * then the usagecount heuristic currently employed.
170+ */
171+ static void
172+ llvm_recreate_llvm_context (void )
173+ {
174+ if (!llvm_context )
175+ elog (ERROR , "Trying to recreate a non-existing context" );
176+
177+ /*
178+ * We can only safely recreate the LLVM context if no other code is being
179+ * JITed, otherwise we'd release the types in use for that.
180+ */
181+ if (llvm_jit_context_in_use_count > 0 )
182+ {
183+ llvm_llvm_context_reuse_count ++ ;
184+ return ;
185+ }
186+
187+ if (llvm_llvm_context_reuse_count <= LLVMJIT_LLVM_CONTEXT_REUSE_MAX )
188+ {
189+ llvm_llvm_context_reuse_count ++ ;
190+ return ;
191+ }
192+
193+ /*
194+ * Need to reset the modules that the inlining code caches before
195+ * disposing of the context. LLVM modules exist within a specific LLVM
196+ * context, therefore disposing of the context before resetting the cache
197+ * would lead to dangling pointers to modules.
198+ */
199+ llvm_inline_reset_caches ();
200+
201+ LLVMContextDispose (llvm_context );
202+ llvm_context = LLVMContextCreate ();
203+ llvm_llvm_context_reuse_count = 0 ;
204+
205+ /*
206+ * Re-build cached type information, so code generation code can rely on
207+ * that information to be present (also prevents the variables to be
208+ * dangling references).
209+ */
210+ llvm_create_types ();
211+ }
212+
213+
146214/*
147215 * Create a context for JITing work.
148216 *
@@ -159,6 +227,8 @@ llvm_create_context(int jitFlags)
159227
160228 llvm_session_initialize ();
161229
230+ llvm_recreate_llvm_context ();
231+
162232 ResourceOwnerEnlargeJIT (CurrentResourceOwner );
163233
164234 context = MemoryContextAllocZero (TopMemoryContext ,
@@ -169,6 +239,8 @@ llvm_create_context(int jitFlags)
169239 context -> base .resowner = CurrentResourceOwner ;
170240 ResourceOwnerRememberJIT (CurrentResourceOwner , PointerGetDatum (context ));
171241
242+ llvm_jit_context_in_use_count ++ ;
243+
172244 return context ;
173245}
174246
@@ -178,9 +250,15 @@ llvm_create_context(int jitFlags)
178250static void
179251llvm_release_context (JitContext * context )
180252{
181- LLVMJitContext * llvm_context = (LLVMJitContext * ) context ;
253+ LLVMJitContext * llvm_jit_context = (LLVMJitContext * ) context ;
182254 ListCell * lc ;
183255
256+ /*
257+ * Consider as cleaned up even if we skip doing so below, that way we can
258+ * verify the tracking is correct (see llvm_shutdown()).
259+ */
260+ llvm_jit_context_in_use_count -- ;
261+
184262 /*
185263 * When this backend is exiting, don't clean up LLVM. As an error might
186264 * have occurred from within LLVM, we do not want to risk reentering. All
@@ -191,13 +269,13 @@ llvm_release_context(JitContext *context)
191269
192270 llvm_enter_fatal_on_oom ();
193271
194- if (llvm_context -> module )
272+ if (llvm_jit_context -> module )
195273 {
196- LLVMDisposeModule (llvm_context -> module );
197- llvm_context -> module = NULL ;
274+ LLVMDisposeModule (llvm_jit_context -> module );
275+ llvm_jit_context -> module = NULL ;
198276 }
199277
200- foreach (lc , llvm_context -> handles )
278+ foreach (lc , llvm_jit_context -> handles )
201279 {
202280 LLVMJitHandle * jit_handle = (LLVMJitHandle * ) lfirst (lc );
203281
@@ -227,8 +305,8 @@ llvm_release_context(JitContext *context)
227305
228306 pfree (jit_handle );
229307 }
230- list_free (llvm_context -> handles );
231- llvm_context -> handles = NIL ;
308+ list_free (llvm_jit_context -> handles );
309+ llvm_jit_context -> handles = NIL ;
232310
233311 llvm_leave_fatal_on_oom ();
234312}
@@ -248,7 +326,7 @@ llvm_mutable_module(LLVMJitContext *context)
248326 {
249327 context -> compiled = false;
250328 context -> module_generation = llvm_generation ++ ;
251- context -> module = LLVMModuleCreateWithName ("pg" );
329+ context -> module = LLVMModuleCreateWithNameInContext ("pg" , llvm_context );
252330 LLVMSetTarget (context -> module , llvm_triple );
253331 LLVMSetDataLayout (context -> module , llvm_layout );
254332 }
@@ -832,6 +910,14 @@ llvm_session_initialize(void)
832910 LLVMInitializeNativeAsmPrinter ();
833911 LLVMInitializeNativeAsmParser ();
834912
913+ if (llvm_context == NULL )
914+ {
915+ llvm_context = LLVMContextCreate ();
916+
917+ llvm_jit_context_in_use_count = 0 ;
918+ llvm_llvm_context_reuse_count = 0 ;
919+ }
920+
835921 /*
836922 * When targeting LLVM 15, turn off opaque pointers for the context we
837923 * build our code in. We don't need to do so for other contexts (e.g.
@@ -851,6 +937,11 @@ llvm_session_initialize(void)
851937 */
852938 llvm_create_types ();
853939
940+ /*
941+ * Extract target information from loaded module.
942+ */
943+ llvm_set_target ();
944+
854945 if (LLVMGetTargetFromTriple (llvm_triple , & llvm_targetref , & error ) != 0 )
855946 {
856947 elog (FATAL , "failed to query triple %s" , error );
@@ -946,6 +1037,10 @@ llvm_shutdown(int code, Datum arg)
9461037 return ;
9471038 }
9481039
1040+ if (llvm_jit_context_in_use_count != 0 )
1041+ elog (PANIC , "LLVMJitContext in use count not 0 at exit (is %zu)" ,
1042+ llvm_jit_context_in_use_count );
1043+
9491044#if LLVM_VERSION_MAJOR > 11
9501045 {
9511046 if (llvm_opt3_orc )
@@ -1008,6 +1103,23 @@ load_return_type(LLVMModuleRef mod, const char *name)
10081103 return typ ;
10091104}
10101105
1106+ /*
1107+ * Load triple & layout from clang emitted file so we're guaranteed to be
1108+ * compatible.
1109+ */
1110+ static void
1111+ llvm_set_target (void )
1112+ {
1113+ if (!llvm_types_module )
1114+ elog (ERROR , "failed to extract target information, llvmjit_types.c not loaded" );
1115+
1116+ if (llvm_triple == NULL )
1117+ llvm_triple = pstrdup (LLVMGetTarget (llvm_types_module ));
1118+
1119+ if (llvm_layout == NULL )
1120+ llvm_layout = pstrdup (LLVMGetDataLayoutStr (llvm_types_module ));
1121+ }
1122+
10111123/*
10121124 * Load required information, types, function signatures from llvmjit_types.c
10131125 * and make them available in global variables.
@@ -1031,19 +1143,12 @@ llvm_create_types(void)
10311143 }
10321144
10331145 /* eagerly load contents, going to need it all */
1034- if (LLVMParseBitcode2 ( buf , & llvm_types_module ))
1146+ if (LLVMParseBitcodeInContext2 ( llvm_context , buf , & llvm_types_module ))
10351147 {
1036- elog (ERROR , "LLVMParseBitcode2 of %s failed" , path );
1148+ elog (ERROR , "LLVMParseBitcodeInContext2 of %s failed" , path );
10371149 }
10381150 LLVMDisposeMemoryBuffer (buf );
10391151
1040- /*
1041- * Load triple & layout from clang emitted file so we're guaranteed to be
1042- * compatible.
1043- */
1044- llvm_triple = pstrdup (LLVMGetTarget (llvm_types_module ));
1045- llvm_layout = pstrdup (LLVMGetDataLayoutStr (llvm_types_module ));
1046-
10471152 TypeSizeT = llvm_pg_var_type ("TypeSizeT" );
10481153 TypeParamBool = load_return_type (llvm_types_module , "FunctionReturningBool" );
10491154 TypeStorageBool = llvm_pg_var_type ("TypeStorageBool" );
0 commit comments