51
51
#endif
52
52
53
53
54
+ #define LLVMJIT_LLVM_CONTEXT_REUSE_MAX 100
55
+
54
56
/* Handle of a module emitted via ORC JIT */
55
57
typedef struct LLVMJitHandle
56
58
{
@@ -103,8 +105,15 @@ LLVMModuleRef llvm_types_module = NULL;
103
105
104
106
static bool llvm_session_initialized = false;
105
107
static size_t llvm_generation = 0 ;
108
+
109
+ /* number of LLVMJitContexts that currently are in use */
110
+ static size_t llvm_jit_context_in_use_count = 0 ;
111
+
112
+ /* how many times has the current LLVMContextRef been used */
113
+ static size_t llvm_llvm_context_reuse_count = 0 ;
106
114
static const char * llvm_triple = NULL ;
107
115
static const char * llvm_layout = NULL ;
116
+ static LLVMContextRef llvm_context ;
108
117
109
118
110
119
static LLVMTargetRef llvm_targetref ;
@@ -125,6 +134,8 @@ static void llvm_compile_module(LLVMJitContext *context);
125
134
static void llvm_optimize_module (LLVMJitContext * context ,LLVMModuleRef module );
126
135
127
136
static void llvm_create_types (void );
137
+ static void llvm_set_target (void );
138
+ static void llvm_recreate_llvm_context (void );
128
139
static uint64_t llvm_resolve_symbol (const char * name ,void * ctx );
129
140
130
141
#if LLVM_VERSION_MAJOR > 11
@@ -146,6 +157,63 @@ _PG_jit_provider_init(JitProviderCallbacks *cb)
146
157
cb -> compile_expr = llvm_compile_expr ;
147
158
}
148
159
160
+
161
+ /*
162
+ * Every now and then create a new LLVMContextRef. Unfortunately, during every
163
+ * round of inlining, types may "leak" (they can still be found/used via the
164
+ * context, but new types will be created the next time in inlining is
165
+ * performed). To prevent that from slowly accumulating problematic amounts of
166
+ * memory, recreate the LLVMContextRef we use. We don't want to do so too
167
+ * often, as that implies some overhead (particularly re-loading the module
168
+ * summaries / modules is fairly expensive). A future TODO would be to make
169
+ * this more finegrained and only drop/recreate the LLVMContextRef when we know
170
+ * there has been inlining. If we can get the size of the context from LLVM
171
+ * then that might be a better way to determine when to drop/recreate rather
172
+ * then the usagecount heuristic currently employed.
173
+ */
174
+ static void
175
+ llvm_recreate_llvm_context (void )
176
+ {
177
+ if (!llvm_context )
178
+ elog (ERROR ,"Trying to recreate a non-existing context" );
179
+
180
+ /*
181
+ * We can only safely recreate the LLVM context if no other code is being
182
+ * JITed, otherwise we'd release the types in use for that.
183
+ */
184
+ if (llvm_jit_context_in_use_count > 0 )
185
+ {
186
+ llvm_llvm_context_reuse_count ++ ;
187
+ return ;
188
+ }
189
+
190
+ if (llvm_llvm_context_reuse_count <=LLVMJIT_LLVM_CONTEXT_REUSE_MAX )
191
+ {
192
+ llvm_llvm_context_reuse_count ++ ;
193
+ return ;
194
+ }
195
+
196
+ /*
197
+ * Need to reset the modules that the inlining code caches before
198
+ * disposing of the context. LLVM modules exist within a specific LLVM
199
+ * context, therefore disposing of the context before resetting the cache
200
+ * would lead to dangling pointers to modules.
201
+ */
202
+ llvm_inline_reset_caches ();
203
+
204
+ LLVMContextDispose (llvm_context );
205
+ llvm_context = LLVMContextCreate ();
206
+ llvm_llvm_context_reuse_count = 0 ;
207
+
208
+ /*
209
+ * Re-build cached type information, so code generation code can rely on
210
+ * that information to be present (also prevents the variables to be
211
+ * dangling references).
212
+ */
213
+ llvm_create_types ();
214
+ }
215
+
216
+
149
217
/*
150
218
* Create a context for JITing work.
151
219
*
@@ -162,6 +230,8 @@ llvm_create_context(int jitFlags)
162
230
163
231
llvm_session_initialize ();
164
232
233
+ llvm_recreate_llvm_context ();
234
+
165
235
ResourceOwnerEnlargeJIT (CurrentResourceOwner );
166
236
167
237
context = MemoryContextAllocZero (TopMemoryContext ,
@@ -172,6 +242,8 @@ llvm_create_context(int jitFlags)
172
242
context -> base .resowner = CurrentResourceOwner ;
173
243
ResourceOwnerRememberJIT (CurrentResourceOwner ,PointerGetDatum (context ));
174
244
245
+ llvm_jit_context_in_use_count ++ ;
246
+
175
247
return context ;
176
248
}
177
249
@@ -181,7 +253,13 @@ llvm_create_context(int jitFlags)
181
253
static void
182
254
llvm_release_context (JitContext * context )
183
255
{
184
- LLVMJitContext * llvm_context = (LLVMJitContext * )context ;
256
+ LLVMJitContext * llvm_jit_context = (LLVMJitContext * )context ;
257
+
258
+ /*
259
+ * Consider as cleaned up even if we skip doing so below, that way we can
260
+ * verify the tracking is correct (see llvm_shutdown()).
261
+ */
262
+ llvm_jit_context_in_use_count -- ;
185
263
186
264
/*
187
265
* When this backend is exiting, don't clean up LLVM. As an error might
@@ -193,18 +271,18 @@ llvm_release_context(JitContext *context)
193
271
194
272
llvm_enter_fatal_on_oom ();
195
273
196
- if (llvm_context -> module )
274
+ if (llvm_jit_context -> module )
197
275
{
198
- LLVMDisposeModule (llvm_context -> module );
199
- llvm_context -> module = NULL ;
276
+ LLVMDisposeModule (llvm_jit_context -> module );
277
+ llvm_jit_context -> module = NULL ;
200
278
}
201
279
202
- while (llvm_context -> handles != NIL )
280
+ while (llvm_jit_context -> handles != NIL )
203
281
{
204
282
LLVMJitHandle * jit_handle ;
205
283
206
- jit_handle = (LLVMJitHandle * )linitial (llvm_context -> handles );
207
- llvm_context -> handles = list_delete_first (llvm_context -> handles );
284
+ jit_handle = (LLVMJitHandle * )linitial (llvm_jit_context -> handles );
285
+ llvm_jit_context -> handles = list_delete_first (llvm_jit_context -> handles );
208
286
209
287
#if LLVM_VERSION_MAJOR > 11
210
288
{
@@ -232,6 +310,8 @@ llvm_release_context(JitContext *context)
232
310
233
311
pfree (jit_handle );
234
312
}
313
+ list_free (llvm_jit_context -> handles );
314
+ llvm_jit_context -> handles = NIL ;
235
315
236
316
llvm_leave_fatal_on_oom ();
237
317
}
@@ -251,7 +331,7 @@ llvm_mutable_module(LLVMJitContext *context)
251
331
{
252
332
context -> compiled = false;
253
333
context -> module_generation = llvm_generation ++ ;
254
- context -> module = LLVMModuleCreateWithName ("pg" );
334
+ context -> module = LLVMModuleCreateWithNameInContext ("pg" , llvm_context );
255
335
LLVMSetTarget (context -> module ,llvm_triple );
256
336
LLVMSetDataLayout (context -> module ,llvm_layout );
257
337
}
@@ -835,6 +915,14 @@ llvm_session_initialize(void)
835
915
LLVMInitializeNativeAsmPrinter ();
836
916
LLVMInitializeNativeAsmParser ();
837
917
918
+ if (llvm_context == NULL )
919
+ {
920
+ llvm_context = LLVMContextCreate ();
921
+
922
+ llvm_jit_context_in_use_count = 0 ;
923
+ llvm_llvm_context_reuse_count = 0 ;
924
+ }
925
+
838
926
/*
839
927
* When targeting LLVM 15, turn off opaque pointers for the context we
840
928
* build our code in. We don't need to do so for other contexts (e.g.
@@ -854,6 +942,11 @@ llvm_session_initialize(void)
854
942
*/
855
943
llvm_create_types ();
856
944
945
+ /*
946
+ * Extract target information from loaded module.
947
+ */
948
+ llvm_set_target ();
949
+
857
950
if (LLVMGetTargetFromTriple (llvm_triple ,& llvm_targetref ,& error )!= 0 )
858
951
{
859
952
elog (FATAL ,"failed to query triple %s" ,error );
@@ -949,6 +1042,10 @@ llvm_shutdown(int code, Datum arg)
949
1042
return ;
950
1043
}
951
1044
1045
+ if (llvm_jit_context_in_use_count != 0 )
1046
+ elog (PANIC ,"LLVMJitContext in use count not 0 at exit (is %zu)" ,
1047
+ llvm_jit_context_in_use_count );
1048
+
952
1049
#if LLVM_VERSION_MAJOR > 11
953
1050
{
954
1051
if (llvm_opt3_orc )
@@ -1011,6 +1108,23 @@ load_return_type(LLVMModuleRef mod, const char *name)
1011
1108
return typ ;
1012
1109
}
1013
1110
1111
+ /*
1112
+ * Load triple & layout from clang emitted file so we're guaranteed to be
1113
+ * compatible.
1114
+ */
1115
+ static void
1116
+ llvm_set_target (void )
1117
+ {
1118
+ if (!llvm_types_module )
1119
+ elog (ERROR ,"failed to extract target information, llvmjit_types.c not loaded" );
1120
+
1121
+ if (llvm_triple == NULL )
1122
+ llvm_triple = pstrdup (LLVMGetTarget (llvm_types_module ));
1123
+
1124
+ if (llvm_layout == NULL )
1125
+ llvm_layout = pstrdup (LLVMGetDataLayoutStr (llvm_types_module ));
1126
+ }
1127
+
1014
1128
/*
1015
1129
* Load required information, types, function signatures from llvmjit_types.c
1016
1130
* and make them available in global variables.
@@ -1034,19 +1148,12 @@ llvm_create_types(void)
1034
1148
}
1035
1149
1036
1150
/* eagerly load contents, going to need it all */
1037
- if (LLVMParseBitcode2 ( buf ,& llvm_types_module ))
1151
+ if (LLVMParseBitcodeInContext2 ( llvm_context , buf ,& llvm_types_module ))
1038
1152
{
1039
- elog (ERROR ,"LLVMParseBitcode2 of %s failed" ,path );
1153
+ elog (ERROR ,"LLVMParseBitcodeInContext2 of %s failed" ,path );
1040
1154
}
1041
1155
LLVMDisposeMemoryBuffer (buf );
1042
1156
1043
- /*
1044
- * Load triple & layout from clang emitted file so we're guaranteed to be
1045
- * compatible.
1046
- */
1047
- llvm_triple = pstrdup (LLVMGetTarget (llvm_types_module ));
1048
- llvm_layout = pstrdup (LLVMGetDataLayoutStr (llvm_types_module ));
1049
-
1050
1157
TypeSizeT = llvm_pg_var_type ("TypeSizeT" );
1051
1158
TypeParamBool = load_return_type (llvm_types_module ,"FunctionReturningBool" );
1052
1159
TypeStorageBool = llvm_pg_var_type ("TypeStorageBool" );