47
47
#include "utils/memutils.h"
48
48
#include "utils/resowner_private.h"
49
49
50
+ #define LLVMJIT_LLVM_CONTEXT_REUSE_MAX 100
51
+
50
52
/* Handle of a module emitted via ORC JIT */
51
53
typedef struct LLVMJitHandle
52
54
{
@@ -100,8 +102,15 @@ LLVMModuleRef llvm_types_module = NULL;
100
102
101
103
static bool llvm_session_initialized = false;
102
104
static 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 ;
103
111
static const char * llvm_triple = NULL ;
104
112
static const char * llvm_layout = NULL ;
113
+ static LLVMContextRef llvm_context ;
105
114
106
115
107
116
static LLVMTargetRef llvm_targetref ;
@@ -122,6 +131,8 @@ static void llvm_compile_module(LLVMJitContext *context);
122
131
static void llvm_optimize_module (LLVMJitContext * context ,LLVMModuleRef module );
123
132
124
133
static void llvm_create_types (void );
134
+ static void llvm_set_target (void );
135
+ static void llvm_recreate_llvm_context (void );
125
136
static uint64_t llvm_resolve_symbol (const char * name ,void * ctx );
126
137
127
138
#if LLVM_VERSION_MAJOR > 11
@@ -143,6 +154,63 @@ _PG_jit_provider_init(JitProviderCallbacks *cb)
143
154
cb -> compile_expr = llvm_compile_expr ;
144
155
}
145
156
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
+
146
214
/*
147
215
* Create a context for JITing work.
148
216
*
@@ -159,6 +227,8 @@ llvm_create_context(int jitFlags)
159
227
160
228
llvm_session_initialize ();
161
229
230
+ llvm_recreate_llvm_context ();
231
+
162
232
ResourceOwnerEnlargeJIT (CurrentResourceOwner );
163
233
164
234
context = MemoryContextAllocZero (TopMemoryContext ,
@@ -169,6 +239,8 @@ llvm_create_context(int jitFlags)
169
239
context -> base .resowner = CurrentResourceOwner ;
170
240
ResourceOwnerRememberJIT (CurrentResourceOwner ,PointerGetDatum (context ));
171
241
242
+ llvm_jit_context_in_use_count ++ ;
243
+
172
244
return context ;
173
245
}
174
246
@@ -178,9 +250,15 @@ llvm_create_context(int jitFlags)
178
250
static void
179
251
llvm_release_context (JitContext * context )
180
252
{
181
- LLVMJitContext * llvm_context = (LLVMJitContext * )context ;
253
+ LLVMJitContext * llvm_jit_context = (LLVMJitContext * )context ;
182
254
ListCell * lc ;
183
255
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
+
184
262
/*
185
263
* When this backend is exiting, don't clean up LLVM. As an error might
186
264
* have occurred from within LLVM, we do not want to risk reentering. All
@@ -191,13 +269,13 @@ llvm_release_context(JitContext *context)
191
269
192
270
llvm_enter_fatal_on_oom ();
193
271
194
- if (llvm_context -> module )
272
+ if (llvm_jit_context -> module )
195
273
{
196
- LLVMDisposeModule (llvm_context -> module );
197
- llvm_context -> module = NULL ;
274
+ LLVMDisposeModule (llvm_jit_context -> module );
275
+ llvm_jit_context -> module = NULL ;
198
276
}
199
277
200
- foreach (lc ,llvm_context -> handles )
278
+ foreach (lc ,llvm_jit_context -> handles )
201
279
{
202
280
LLVMJitHandle * jit_handle = (LLVMJitHandle * )lfirst (lc );
203
281
@@ -227,8 +305,8 @@ llvm_release_context(JitContext *context)
227
305
228
306
pfree (jit_handle );
229
307
}
230
- list_free (llvm_context -> handles );
231
- llvm_context -> handles = NIL ;
308
+ list_free (llvm_jit_context -> handles );
309
+ llvm_jit_context -> handles = NIL ;
232
310
233
311
llvm_leave_fatal_on_oom ();
234
312
}
@@ -248,7 +326,7 @@ llvm_mutable_module(LLVMJitContext *context)
248
326
{
249
327
context -> compiled = false;
250
328
context -> module_generation = llvm_generation ++ ;
251
- context -> module = LLVMModuleCreateWithName ("pg" );
329
+ context -> module = LLVMModuleCreateWithNameInContext ("pg" , llvm_context );
252
330
LLVMSetTarget (context -> module ,llvm_triple );
253
331
LLVMSetDataLayout (context -> module ,llvm_layout );
254
332
}
@@ -832,6 +910,14 @@ llvm_session_initialize(void)
832
910
LLVMInitializeNativeAsmPrinter ();
833
911
LLVMInitializeNativeAsmParser ();
834
912
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
+
835
921
/*
836
922
* When targeting LLVM 15, turn off opaque pointers for the context we
837
923
* build our code in. We don't need to do so for other contexts (e.g.
@@ -851,6 +937,11 @@ llvm_session_initialize(void)
851
937
*/
852
938
llvm_create_types ();
853
939
940
+ /*
941
+ * Extract target information from loaded module.
942
+ */
943
+ llvm_set_target ();
944
+
854
945
if (LLVMGetTargetFromTriple (llvm_triple ,& llvm_targetref ,& error )!= 0 )
855
946
{
856
947
elog (FATAL ,"failed to query triple %s" ,error );
@@ -946,6 +1037,10 @@ llvm_shutdown(int code, Datum arg)
946
1037
return ;
947
1038
}
948
1039
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
+
949
1044
#if LLVM_VERSION_MAJOR > 11
950
1045
{
951
1046
if (llvm_opt3_orc )
@@ -1008,6 +1103,23 @@ load_return_type(LLVMModuleRef mod, const char *name)
1008
1103
return typ ;
1009
1104
}
1010
1105
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
+
1011
1123
/*
1012
1124
* Load required information, types, function signatures from llvmjit_types.c
1013
1125
* and make them available in global variables.
@@ -1031,19 +1143,12 @@ llvm_create_types(void)
1031
1143
}
1032
1144
1033
1145
/* eagerly load contents, going to need it all */
1034
- if (LLVMParseBitcode2 ( buf ,& llvm_types_module ))
1146
+ if (LLVMParseBitcodeInContext2 ( llvm_context , buf ,& llvm_types_module ))
1035
1147
{
1036
- elog (ERROR ,"LLVMParseBitcode2 of %s failed" ,path );
1148
+ elog (ERROR ,"LLVMParseBitcodeInContext2 of %s failed" ,path );
1037
1149
}
1038
1150
LLVMDisposeMemoryBuffer (buf );
1039
1151
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
-
1047
1152
TypeSizeT = llvm_pg_var_type ("TypeSizeT" );
1048
1153
TypeParamBool = load_return_type (llvm_types_module ,"FunctionReturningBool" );
1049
1154
TypeStorageBool = llvm_pg_var_type ("TypeStorageBool" );