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
{
@@ -99,8 +101,15 @@ LLVMModuleRef llvm_types_module = NULL;
99
101
100
102
static bool llvm_session_initialized = false;
101
103
static size_t llvm_generation = 0 ;
104
+
105
+ /* number of LLVMJitContexts that currently are in use */
106
+ static size_t llvm_jit_context_in_use_count = 0 ;
107
+
108
+ /* how many times has the current LLVMContextRef been used */
109
+ static size_t llvm_llvm_context_reuse_count = 0 ;
102
110
static const char * llvm_triple = NULL ;
103
111
static const char * llvm_layout = NULL ;
112
+ static LLVMContextRef llvm_context ;
104
113
105
114
106
115
static LLVMTargetRef llvm_targetref ;
@@ -121,6 +130,8 @@ static void llvm_compile_module(LLVMJitContext *context);
121
130
static void llvm_optimize_module (LLVMJitContext * context ,LLVMModuleRef module );
122
131
123
132
static void llvm_create_types (void );
133
+ static void llvm_set_target (void );
134
+ static void llvm_recreate_llvm_context (void );
124
135
static uint64_t llvm_resolve_symbol (const char * name ,void * ctx );
125
136
126
137
#if LLVM_VERSION_MAJOR > 11
@@ -142,6 +153,63 @@ _PG_jit_provider_init(JitProviderCallbacks *cb)
142
153
cb -> compile_expr = llvm_compile_expr ;
143
154
}
144
155
156
+
157
+ /*
158
+ * Every now and then create a new LLVMContextRef. Unfortunately, during every
159
+ * round of inlining, types may "leak" (they can still be found/used via the
160
+ * context, but new types will be created the next time in inlining is
161
+ * performed). To prevent that from slowly accumulating problematic amounts of
162
+ * memory, recreate the LLVMContextRef we use. We don't want to do so too
163
+ * often, as that implies some overhead (particularly re-loading the module
164
+ * summaries / modules is fairly expensive). A future TODO would be to make
165
+ * this more finegrained and only drop/recreate the LLVMContextRef when we know
166
+ * there has been inlining. If we can get the size of the context from LLVM
167
+ * then that might be a better way to determine when to drop/recreate rather
168
+ * then the usagecount heuristic currently employed.
169
+ */
170
+ static void
171
+ llvm_recreate_llvm_context (void )
172
+ {
173
+ if (!llvm_context )
174
+ elog (ERROR ,"Trying to recreate a non-existing context" );
175
+
176
+ /*
177
+ * We can only safely recreate the LLVM context if no other code is being
178
+ * JITed, otherwise we'd release the types in use for that.
179
+ */
180
+ if (llvm_jit_context_in_use_count > 0 )
181
+ {
182
+ llvm_llvm_context_reuse_count ++ ;
183
+ return ;
184
+ }
185
+
186
+ if (llvm_llvm_context_reuse_count <=LLVMJIT_LLVM_CONTEXT_REUSE_MAX )
187
+ {
188
+ llvm_llvm_context_reuse_count ++ ;
189
+ return ;
190
+ }
191
+
192
+ /*
193
+ * Need to reset the modules that the inlining code caches before
194
+ * disposing of the context. LLVM modules exist within a specific LLVM
195
+ * context, therefore disposing of the context before resetting the cache
196
+ * would lead to dangling pointers to modules.
197
+ */
198
+ llvm_inline_reset_caches ();
199
+
200
+ LLVMContextDispose (llvm_context );
201
+ llvm_context = LLVMContextCreate ();
202
+ llvm_llvm_context_reuse_count = 0 ;
203
+
204
+ /*
205
+ * Re-build cached type information, so code generation code can rely on
206
+ * that information to be present (also prevents the variables to be
207
+ * dangling references).
208
+ */
209
+ llvm_create_types ();
210
+ }
211
+
212
+
145
213
/*
146
214
* Create a context for JITing work.
147
215
*
@@ -158,6 +226,8 @@ llvm_create_context(int jitFlags)
158
226
159
227
llvm_session_initialize ();
160
228
229
+ llvm_recreate_llvm_context ();
230
+
161
231
ResourceOwnerEnlargeJIT (CurrentResourceOwner );
162
232
163
233
context = MemoryContextAllocZero (TopMemoryContext ,
@@ -168,6 +238,8 @@ llvm_create_context(int jitFlags)
168
238
context -> base .resowner = CurrentResourceOwner ;
169
239
ResourceOwnerRememberJIT (CurrentResourceOwner ,PointerGetDatum (context ));
170
240
241
+ llvm_jit_context_in_use_count ++ ;
242
+
171
243
return context ;
172
244
}
173
245
@@ -177,9 +249,15 @@ llvm_create_context(int jitFlags)
177
249
static void
178
250
llvm_release_context (JitContext * context )
179
251
{
180
- LLVMJitContext * llvm_context = (LLVMJitContext * )context ;
252
+ LLVMJitContext * llvm_jit_context = (LLVMJitContext * )context ;
181
253
ListCell * lc ;
182
254
255
+ /*
256
+ * Consider as cleaned up even if we skip doing so below, that way we can
257
+ * verify the tracking is correct (see llvm_shutdown()).
258
+ */
259
+ llvm_jit_context_in_use_count -- ;
260
+
183
261
/*
184
262
* When this backend is exiting, don't clean up LLVM. As an error might
185
263
* have occurred from within LLVM, we do not want to risk reentering. All
@@ -190,13 +268,13 @@ llvm_release_context(JitContext *context)
190
268
191
269
llvm_enter_fatal_on_oom ();
192
270
193
- if (llvm_context -> module )
271
+ if (llvm_jit_context -> module )
194
272
{
195
- LLVMDisposeModule (llvm_context -> module );
196
- llvm_context -> module = NULL ;
273
+ LLVMDisposeModule (llvm_jit_context -> module );
274
+ llvm_jit_context -> module = NULL ;
197
275
}
198
276
199
- foreach (lc ,llvm_context -> handles )
277
+ foreach (lc ,llvm_jit_context -> handles )
200
278
{
201
279
LLVMJitHandle * jit_handle = (LLVMJitHandle * )lfirst (lc );
202
280
@@ -226,8 +304,8 @@ llvm_release_context(JitContext *context)
226
304
227
305
pfree (jit_handle );
228
306
}
229
- list_free (llvm_context -> handles );
230
- llvm_context -> handles = NIL ;
307
+ list_free (llvm_jit_context -> handles );
308
+ llvm_jit_context -> handles = NIL ;
231
309
232
310
llvm_leave_fatal_on_oom ();
233
311
}
@@ -247,7 +325,7 @@ llvm_mutable_module(LLVMJitContext *context)
247
325
{
248
326
context -> compiled = false;
249
327
context -> module_generation = llvm_generation ++ ;
250
- context -> module = LLVMModuleCreateWithName ("pg" );
328
+ context -> module = LLVMModuleCreateWithNameInContext ("pg" , llvm_context );
251
329
LLVMSetTarget (context -> module ,llvm_triple );
252
330
LLVMSetDataLayout (context -> module ,llvm_layout );
253
331
}
@@ -831,6 +909,14 @@ llvm_session_initialize(void)
831
909
LLVMInitializeNativeAsmPrinter ();
832
910
LLVMInitializeNativeAsmParser ();
833
911
912
+ if (llvm_context == NULL )
913
+ {
914
+ llvm_context = LLVMContextCreate ();
915
+
916
+ llvm_jit_context_in_use_count = 0 ;
917
+ llvm_llvm_context_reuse_count = 0 ;
918
+ }
919
+
834
920
/*
835
921
* When targeting LLVM 15, turn off opaque pointers for the context we
836
922
* build our code in. We don't need to do so for other contexts (e.g.
@@ -850,6 +936,11 @@ llvm_session_initialize(void)
850
936
*/
851
937
llvm_create_types ();
852
938
939
+ /*
940
+ * Extract target information from loaded module.
941
+ */
942
+ llvm_set_target ();
943
+
853
944
if (LLVMGetTargetFromTriple (llvm_triple ,& llvm_targetref ,& error )!= 0 )
854
945
{
855
946
elog (FATAL ,"failed to query triple %s" ,error );
@@ -945,6 +1036,10 @@ llvm_shutdown(int code, Datum arg)
945
1036
return ;
946
1037
}
947
1038
1039
+ if (llvm_jit_context_in_use_count != 0 )
1040
+ elog (PANIC ,"LLVMJitContext in use count not 0 at exit (is %zu)" ,
1041
+ llvm_jit_context_in_use_count );
1042
+
948
1043
#if LLVM_VERSION_MAJOR > 11
949
1044
{
950
1045
if (llvm_opt3_orc )
@@ -1007,6 +1102,23 @@ load_return_type(LLVMModuleRef mod, const char *name)
1007
1102
return typ ;
1008
1103
}
1009
1104
1105
+ /*
1106
+ * Load triple & layout from clang emitted file so we're guaranteed to be
1107
+ * compatible.
1108
+ */
1109
+ static void
1110
+ llvm_set_target (void )
1111
+ {
1112
+ if (!llvm_types_module )
1113
+ elog (ERROR ,"failed to extract target information, llvmjit_types.c not loaded" );
1114
+
1115
+ if (llvm_triple == NULL )
1116
+ llvm_triple = pstrdup (LLVMGetTarget (llvm_types_module ));
1117
+
1118
+ if (llvm_layout == NULL )
1119
+ llvm_layout = pstrdup (LLVMGetDataLayoutStr (llvm_types_module ));
1120
+ }
1121
+
1010
1122
/*
1011
1123
* Load required information, types, function signatures from llvmjit_types.c
1012
1124
* and make them available in global variables.
@@ -1030,19 +1142,12 @@ llvm_create_types(void)
1030
1142
}
1031
1143
1032
1144
/* eagerly load contents, going to need it all */
1033
- if (LLVMParseBitcode2 ( buf ,& llvm_types_module ))
1145
+ if (LLVMParseBitcodeInContext2 ( llvm_context , buf ,& llvm_types_module ))
1034
1146
{
1035
- elog (ERROR ,"LLVMParseBitcode2 of %s failed" ,path );
1147
+ elog (ERROR ,"LLVMParseBitcodeInContext2 of %s failed" ,path );
1036
1148
}
1037
1149
LLVMDisposeMemoryBuffer (buf );
1038
1150
1039
- /*
1040
- * Load triple & layout from clang emitted file so we're guaranteed to be
1041
- * compatible.
1042
- */
1043
- llvm_triple = pstrdup (LLVMGetTarget (llvm_types_module ));
1044
- llvm_layout = pstrdup (LLVMGetDataLayoutStr (llvm_types_module ));
1045
-
1046
1151
TypeSizeT = llvm_pg_var_type ("TypeSizeT" );
1047
1152
TypeParamBool = load_return_type (llvm_types_module ,"FunctionReturningBool" );
1048
1153
TypeStorageBool = llvm_pg_var_type ("TypeStorageBool" );