42
42
#include "utils/memutils.h"
43
43
#include "utils/resowner_private.h"
44
44
45
+ #define LLVMJIT_LLVM_CONTEXT_REUSE_MAX 100
46
+
45
47
/* Handle of a module emitted via ORC JIT */
46
48
typedef struct LLVMJitHandle
47
49
{
@@ -81,8 +83,15 @@ static LLVMModuleRef llvm_types_module = NULL;
81
83
82
84
static bool llvm_session_initialized = false;
83
85
static size_t llvm_generation = 0 ;
86
+
87
+ /* number of LLVMJitContexts that currently are in use */
88
+ static size_t llvm_jit_context_in_use_count = 0 ;
89
+
90
+ /* how many times has the current LLVMContextRef been used */
91
+ static size_t llvm_llvm_context_reuse_count = 0 ;
84
92
static const char * llvm_triple = NULL ;
85
93
static const char * llvm_layout = NULL ;
94
+ static LLVMContextRef llvm_context ;
86
95
87
96
88
97
static LLVMTargetRef llvm_targetref ;
@@ -103,6 +112,8 @@ static void llvm_compile_module(LLVMJitContext *context);
103
112
static void llvm_optimize_module (LLVMJitContext * context ,LLVMModuleRef module );
104
113
105
114
static void llvm_create_types (void );
115
+ static void llvm_set_target (void );
116
+ static void llvm_recreate_llvm_context (void );
106
117
static uint64_t llvm_resolve_symbol (const char * name ,void * ctx );
107
118
108
119
#if LLVM_VERSION_MAJOR > 11
@@ -124,6 +135,63 @@ _PG_jit_provider_init(JitProviderCallbacks *cb)
124
135
cb -> compile_expr = llvm_compile_expr ;
125
136
}
126
137
138
+
139
+ /*
140
+ * Every now and then create a new LLVMContextRef. Unfortunately, during every
141
+ * round of inlining, types may "leak" (they can still be found/used via the
142
+ * context, but new types will be created the next time in inlining is
143
+ * performed). To prevent that from slowly accumulating problematic amounts of
144
+ * memory, recreate the LLVMContextRef we use. We don't want to do so too
145
+ * often, as that implies some overhead (particularly re-loading the module
146
+ * summaries / modules is fairly expensive). A future TODO would be to make
147
+ * this more finegrained and only drop/recreate the LLVMContextRef when we know
148
+ * there has been inlining. If we can get the size of the context from LLVM
149
+ * then that might be a better way to determine when to drop/recreate rather
150
+ * then the usagecount heuristic currently employed.
151
+ */
152
+ static void
153
+ llvm_recreate_llvm_context (void )
154
+ {
155
+ if (!llvm_context )
156
+ elog (ERROR ,"Trying to recreate a non-existing context" );
157
+
158
+ /*
159
+ * We can only safely recreate the LLVM context if no other code is being
160
+ * JITed, otherwise we'd release the types in use for that.
161
+ */
162
+ if (llvm_jit_context_in_use_count > 0 )
163
+ {
164
+ llvm_llvm_context_reuse_count ++ ;
165
+ return ;
166
+ }
167
+
168
+ if (llvm_llvm_context_reuse_count <=LLVMJIT_LLVM_CONTEXT_REUSE_MAX )
169
+ {
170
+ llvm_llvm_context_reuse_count ++ ;
171
+ return ;
172
+ }
173
+
174
+ /*
175
+ * Need to reset the modules that the inlining code caches before
176
+ * disposing of the context. LLVM modules exist within a specific LLVM
177
+ * context, therefore disposing of the context before resetting the cache
178
+ * would lead to dangling pointers to modules.
179
+ */
180
+ llvm_inline_reset_caches ();
181
+
182
+ LLVMContextDispose (llvm_context );
183
+ llvm_context = LLVMContextCreate ();
184
+ llvm_llvm_context_reuse_count = 0 ;
185
+
186
+ /*
187
+ * Re-build cached type information, so code generation code can rely on
188
+ * that information to be present (also prevents the variables to be
189
+ * dangling references).
190
+ */
191
+ llvm_create_types ();
192
+ }
193
+
194
+
127
195
/*
128
196
* Create a context for JITing work.
129
197
*
@@ -140,6 +208,8 @@ llvm_create_context(int jitFlags)
140
208
141
209
llvm_session_initialize ();
142
210
211
+ llvm_recreate_llvm_context ();
212
+
143
213
ResourceOwnerEnlargeJIT (CurrentResourceOwner );
144
214
145
215
context = MemoryContextAllocZero (TopMemoryContext ,
@@ -150,6 +220,8 @@ llvm_create_context(int jitFlags)
150
220
context -> base .resowner = CurrentResourceOwner ;
151
221
ResourceOwnerRememberJIT (CurrentResourceOwner ,PointerGetDatum (context ));
152
222
223
+ llvm_jit_context_in_use_count ++ ;
224
+
153
225
return context ;
154
226
}
155
227
@@ -159,9 +231,15 @@ llvm_create_context(int jitFlags)
159
231
static void
160
232
llvm_release_context (JitContext * context )
161
233
{
162
- LLVMJitContext * llvm_context = (LLVMJitContext * )context ;
234
+ LLVMJitContext * llvm_jit_context = (LLVMJitContext * )context ;
163
235
ListCell * lc ;
164
236
237
+ /*
238
+ * Consider as cleaned up even if we skip doing so below, that way we can
239
+ * verify the tracking is correct (see llvm_shutdown()).
240
+ */
241
+ llvm_jit_context_in_use_count -- ;
242
+
165
243
/*
166
244
* When this backend is exiting, don't clean up LLVM. As an error might
167
245
* have occurred from within LLVM, we do not want to risk reentering. All
@@ -172,13 +250,13 @@ llvm_release_context(JitContext *context)
172
250
173
251
llvm_enter_fatal_on_oom ();
174
252
175
- if (llvm_context -> module )
253
+ if (llvm_jit_context -> module )
176
254
{
177
- LLVMDisposeModule (llvm_context -> module );
178
- llvm_context -> module = NULL ;
255
+ LLVMDisposeModule (llvm_jit_context -> module );
256
+ llvm_jit_context -> module = NULL ;
179
257
}
180
258
181
- foreach (lc ,llvm_context -> handles )
259
+ foreach (lc ,llvm_jit_context -> handles )
182
260
{
183
261
LLVMJitHandle * jit_handle = (LLVMJitHandle * )lfirst (lc );
184
262
@@ -208,8 +286,8 @@ llvm_release_context(JitContext *context)
208
286
209
287
pfree (jit_handle );
210
288
}
211
- list_free (llvm_context -> handles );
212
- llvm_context -> handles = NIL ;
289
+ list_free (llvm_jit_context -> handles );
290
+ llvm_jit_context -> handles = NIL ;
213
291
214
292
llvm_leave_fatal_on_oom ();
215
293
}
@@ -229,7 +307,7 @@ llvm_mutable_module(LLVMJitContext *context)
229
307
{
230
308
context -> compiled = false;
231
309
context -> module_generation = llvm_generation ++ ;
232
- context -> module = LLVMModuleCreateWithName ("pg" );
310
+ context -> module = LLVMModuleCreateWithNameInContext ("pg" , llvm_context );
233
311
LLVMSetTarget (context -> module ,llvm_triple );
234
312
LLVMSetDataLayout (context -> module ,llvm_layout );
235
313
}
@@ -787,6 +865,14 @@ llvm_session_initialize(void)
787
865
LLVMInitializeNativeAsmPrinter ();
788
866
LLVMInitializeNativeAsmParser ();
789
867
868
+ if (llvm_context == NULL )
869
+ {
870
+ llvm_context = LLVMContextCreate ();
871
+
872
+ llvm_jit_context_in_use_count = 0 ;
873
+ llvm_llvm_context_reuse_count = 0 ;
874
+ }
875
+
790
876
/*
791
877
* When targeting an LLVM version with opaque pointers enabled by default,
792
878
* turn them off for the context we build our code in. We don't need to
@@ -803,6 +889,11 @@ llvm_session_initialize(void)
803
889
*/
804
890
llvm_create_types ();
805
891
892
+ /*
893
+ * Extract target information from loaded module.
894
+ */
895
+ llvm_set_target ();
896
+
806
897
if (LLVMGetTargetFromTriple (llvm_triple ,& llvm_targetref ,& error )!= 0 )
807
898
{
808
899
elog (FATAL ,"failed to query triple %s" ,error );
@@ -898,6 +989,10 @@ llvm_shutdown(int code, Datum arg)
898
989
return ;
899
990
}
900
991
992
+ if (llvm_jit_context_in_use_count != 0 )
993
+ elog (PANIC ,"LLVMJitContext in use count not 0 at exit (is %zu)" ,
994
+ llvm_jit_context_in_use_count );
995
+
901
996
#if LLVM_VERSION_MAJOR > 11
902
997
{
903
998
if (llvm_opt3_orc )
@@ -968,6 +1063,23 @@ load_return_type(LLVMModuleRef mod, const char *name)
968
1063
return typ ;
969
1064
}
970
1065
1066
+ /*
1067
+ * Load triple & layout from clang emitted file so we're guaranteed to be
1068
+ * compatible.
1069
+ */
1070
+ static void
1071
+ llvm_set_target (void )
1072
+ {
1073
+ if (!llvm_types_module )
1074
+ elog (ERROR ,"failed to extract target information, llvmjit_types.c not loaded" );
1075
+
1076
+ if (llvm_triple == NULL )
1077
+ llvm_triple = pstrdup (LLVMGetTarget (llvm_types_module ));
1078
+
1079
+ if (llvm_layout == NULL )
1080
+ llvm_layout = pstrdup (LLVMGetDataLayoutStr (llvm_types_module ));
1081
+ }
1082
+
971
1083
/*
972
1084
* Load required information, types, function signatures from llvmjit_types.c
973
1085
* and make them available in global variables.
@@ -991,19 +1103,12 @@ llvm_create_types(void)
991
1103
}
992
1104
993
1105
/* eagerly load contents, going to need it all */
994
- if (LLVMParseBitcode2 ( buf ,& llvm_types_module ))
1106
+ if (LLVMParseBitcodeInContext2 ( llvm_context , buf ,& llvm_types_module ))
995
1107
{
996
- elog (ERROR ,"LLVMParseBitcode2 of %s failed" ,path );
1108
+ elog (ERROR ,"LLVMParseBitcodeInContext2 of %s failed" ,path );
997
1109
}
998
1110
LLVMDisposeMemoryBuffer (buf );
999
1111
1000
- /*
1001
- * Load triple & layout from clang emitted file so we're guaranteed to be
1002
- * compatible.
1003
- */
1004
- llvm_triple = pstrdup (LLVMGetTarget (llvm_types_module ));
1005
- llvm_layout = pstrdup (LLVMGetDataLayoutStr (llvm_types_module ));
1006
-
1007
1112
TypeSizeT = llvm_pg_var_type ("TypeSizeT" );
1008
1113
TypeParamBool = load_return_type (llvm_types_module ,"FunctionReturningBool" );
1009
1114
TypeStorageBool = llvm_pg_var_type ("TypeStorageBool" );