llvmjit: Use explicit LLVMContextRef for inlining
authorDaniel Gustafsson <dgustafsson@postgresql.org>
Wed, 27 Sep 2023 11:02:21 +0000 (13:02 +0200)
committerDaniel Gustafsson <dgustafsson@postgresql.org>
Wed, 27 Sep 2023 11:02:21 +0000 (13:02 +0200)
When performing inlining LLVM unfortunately "leaks" types (the
types survive and are usable, but a new round of inlining will
recreate new structurally equivalent types). This accumulation
will over time amount to a memory leak which for some queries
can be large enough to trigger the OOM process killer.

To avoid accumulation of types, all IR related data is stored
in an LLVMContextRef which is dropped and recreated in order
to release all types.  Dropping and recreating incurs overhead,
so it will be done only after 100 queries. This is a heuristic
which might be revisited, but until we can get the size of the
context from LLVM we are flying a bit blind.

This issue has been reported several times, there may be more
references to it in the archives on top of the threads linked
below.

Backpatching of this fix will be handled once it has matured
in master for a bit.

Reported-By: Justin Pryzby <pryzby@telsasoft.com>
Reported-By: Kurt Roeckx <kurt@roeckx.be>
Reported-By: Jaime Casanova <jcasanov@systemguards.com.ec>
Reported-By: Lauri Laanmets <pcspets@gmail.com>
Author: Andres Freund and Daniel Gustafsson
Discussion: https://postgr.es/m/7acc8678-df5f-4923-9cf6-e843131ae89d@www.fastmail.com
Discussion: https://postgr.es/m/20201218235607.GC30237@telsasoft.com
Discussion: https://postgr.es/m/CAPH-tTxLf44s3CvUUtQpkDr1D8Hxqc2NGDzGXS1ODsfiJ6WSqA@mail.gmail.com

src/backend/jit/llvm/llvmjit.c
src/backend/jit/llvm/llvmjit_deform.c
src/backend/jit/llvm/llvmjit_expr.c
src/backend/jit/llvm/llvmjit_inline.cpp
src/include/jit/llvmjit.h
src/include/jit/llvmjit_emit.h

index caf261a559f8ad05a43d4456a132cb0200822e45..4dfaf797432155a20ae741ca145c3e52065d9647 100644 (file)
@@ -42,6 +42,8 @@
 #include "utils/memutils.h"
 #include "utils/resowner_private.h"
 
+#define LLVMJIT_LLVM_CONTEXT_REUSE_MAX 100
+
 /* Handle of a module emitted via ORC JIT */
 typedef struct LLVMJitHandle
 {
@@ -81,8 +83,15 @@ static LLVMModuleRef llvm_types_module = NULL;
 
 static bool llvm_session_initialized = false;
 static size_t llvm_generation = 0;
+
+/* number of LLVMJitContexts that currently are in use */
+static size_t llvm_jit_context_in_use_count = 0;
+
+/* how many times has the current LLVMContextRef been used */
+static size_t llvm_llvm_context_reuse_count = 0;
 static const char *llvm_triple = NULL;
 static const char *llvm_layout = NULL;
+static LLVMContextRef llvm_context;
 
 
 static LLVMTargetRef llvm_targetref;
@@ -103,6 +112,8 @@ static void llvm_compile_module(LLVMJitContext *context);
 static void llvm_optimize_module(LLVMJitContext *context, LLVMModuleRef module);
 
 static void llvm_create_types(void);
+static void llvm_set_target(void);
+static void llvm_recreate_llvm_context(void);
 static uint64_t llvm_resolve_symbol(const char *name, void *ctx);
 
 #if LLVM_VERSION_MAJOR > 11
@@ -124,6 +135,63 @@ _PG_jit_provider_init(JitProviderCallbacks *cb)
    cb->compile_expr = llvm_compile_expr;
 }
 
+
+/*
+ * Every now and then create a new LLVMContextRef. Unfortunately, during every
+ * round of inlining, types may "leak" (they can still be found/used via the
+ * context, but new types will be created the next time in inlining is
+ * performed). To prevent that from slowly accumulating problematic amounts of
+ * memory, recreate the LLVMContextRef we use. We don't want to do so too
+ * often, as that implies some overhead (particularly re-loading the module
+ * summaries / modules is fairly expensive). A future TODO would be to make
+ * this more finegrained and only drop/recreate the LLVMContextRef when we know
+ * there has been inlining. If we can get the size of the context from LLVM
+ * then that might be a better way to determine when to drop/recreate rather
+ * then the usagecount heuristic currently employed.
+ */
+static void
+llvm_recreate_llvm_context(void)
+{
+   if (!llvm_context)
+       elog(ERROR, "Trying to recreate a non-existing context");
+
+   /*
+    * We can only safely recreate the LLVM context if no other code is being
+    * JITed, otherwise we'd release the types in use for that.
+    */
+   if (llvm_jit_context_in_use_count > 0)
+   {
+       llvm_llvm_context_reuse_count++;
+       return;
+   }
+
+   if (llvm_llvm_context_reuse_count <= LLVMJIT_LLVM_CONTEXT_REUSE_MAX)
+   {
+       llvm_llvm_context_reuse_count++;
+       return;
+   }
+
+   /*
+    * Need to reset the modules that the inlining code caches before
+    * disposing of the context. LLVM modules exist within a specific LLVM
+    * context, therefore disposing of the context before resetting the cache
+    * would lead to dangling pointers to modules.
+    */
+   llvm_inline_reset_caches();
+
+   LLVMContextDispose(llvm_context);
+   llvm_context = LLVMContextCreate();
+   llvm_llvm_context_reuse_count = 0;
+
+   /*
+    * Re-build cached type information, so code generation code can rely on
+    * that information to be present (also prevents the variables to be
+    * dangling references).
+    */
+   llvm_create_types();
+}
+
+
 /*
  * Create a context for JITing work.
  *
@@ -140,6 +208,8 @@ llvm_create_context(int jitFlags)
 
    llvm_session_initialize();
 
+   llvm_recreate_llvm_context();
+
    ResourceOwnerEnlargeJIT(CurrentResourceOwner);
 
    context = MemoryContextAllocZero(TopMemoryContext,
@@ -150,6 +220,8 @@ llvm_create_context(int jitFlags)
    context->base.resowner = CurrentResourceOwner;
    ResourceOwnerRememberJIT(CurrentResourceOwner, PointerGetDatum(context));
 
+   llvm_jit_context_in_use_count++;
+
    return context;
 }
 
@@ -159,9 +231,15 @@ llvm_create_context(int jitFlags)
 static void
 llvm_release_context(JitContext *context)
 {
-   LLVMJitContext *llvm_context = (LLVMJitContext *) context;
+   LLVMJitContext *llvm_jit_context = (LLVMJitContext *) context;
    ListCell   *lc;
 
+   /*
+    * Consider as cleaned up even if we skip doing so below, that way we can
+    * verify the tracking is correct (see llvm_shutdown()).
+    */
+   llvm_jit_context_in_use_count--;
+
    /*
     * When this backend is exiting, don't clean up LLVM. As an error might
     * have occurred from within LLVM, we do not want to risk reentering. All
@@ -172,13 +250,13 @@ llvm_release_context(JitContext *context)
 
    llvm_enter_fatal_on_oom();
 
-   if (llvm_context->module)
+   if (llvm_jit_context->module)
    {
-       LLVMDisposeModule(llvm_context->module);
-       llvm_context->module = NULL;
+       LLVMDisposeModule(llvm_jit_context->module);
+       llvm_jit_context->module = NULL;
    }
 
-   foreach(lc, llvm_context->handles)
+   foreach(lc, llvm_jit_context->handles)
    {
        LLVMJitHandle *jit_handle = (LLVMJitHandle *) lfirst(lc);
 
@@ -208,8 +286,8 @@ llvm_release_context(JitContext *context)
 
        pfree(jit_handle);
    }
-   list_free(llvm_context->handles);
-   llvm_context->handles = NIL;
+   list_free(llvm_jit_context->handles);
+   llvm_jit_context->handles = NIL;
 
    llvm_leave_fatal_on_oom();
 }
@@ -229,7 +307,7 @@ llvm_mutable_module(LLVMJitContext *context)
    {
        context->compiled = false;
        context->module_generation = llvm_generation++;
-       context->module = LLVMModuleCreateWithName("pg");
+       context->module = LLVMModuleCreateWithNameInContext("pg", llvm_context);
        LLVMSetTarget(context->module, llvm_triple);
        LLVMSetDataLayout(context->module, llvm_layout);
    }
@@ -787,6 +865,14 @@ llvm_session_initialize(void)
    LLVMInitializeNativeAsmPrinter();
    LLVMInitializeNativeAsmParser();
 
+   if (llvm_context == NULL)
+   {
+       llvm_context = LLVMContextCreate();
+
+       llvm_jit_context_in_use_count = 0;
+       llvm_llvm_context_reuse_count = 0;
+   }
+
    /*
     * When targeting an LLVM version with opaque pointers enabled by default,
     * turn them off for the context we build our code in.  We don't need to
@@ -803,6 +889,11 @@ llvm_session_initialize(void)
     */
    llvm_create_types();
 
+   /*
+    * Extract target information from loaded module.
+    */
+   llvm_set_target();
+
    if (LLVMGetTargetFromTriple(llvm_triple, &llvm_targetref, &error) != 0)
    {
        elog(FATAL, "failed to query triple %s", error);
@@ -898,6 +989,10 @@ llvm_shutdown(int code, Datum arg)
        return;
    }
 
+   if (llvm_jit_context_in_use_count != 0)
+       elog(PANIC, "LLVMJitContext in use count not 0 at exit (is %zu)",
+            llvm_jit_context_in_use_count);
+
 #if LLVM_VERSION_MAJOR > 11
    {
        if (llvm_opt3_orc)
@@ -968,6 +1063,23 @@ load_return_type(LLVMModuleRef mod, const char *name)
    return typ;
 }
 
+/*
+ * Load triple & layout from clang emitted file so we're guaranteed to be
+ * compatible.
+ */
+static void
+llvm_set_target(void)
+{
+   if (!llvm_types_module)
+       elog(ERROR, "failed to extract target information, llvmjit_types.c not loaded");
+
+   if (llvm_triple == NULL)
+       llvm_triple = pstrdup(LLVMGetTarget(llvm_types_module));
+
+   if (llvm_layout == NULL)
+       llvm_layout = pstrdup(LLVMGetDataLayoutStr(llvm_types_module));
+}
+
 /*
  * Load required information, types, function signatures from llvmjit_types.c
  * and make them available in global variables.
@@ -991,19 +1103,12 @@ llvm_create_types(void)
    }
 
    /* eagerly load contents, going to need it all */
-   if (LLVMParseBitcode2(buf, &llvm_types_module))
+   if (LLVMParseBitcodeInContext2(llvm_context, buf, &llvm_types_module))
    {
-       elog(ERROR, "LLVMParseBitcode2 of %s failed", path);
+       elog(ERROR, "LLVMParseBitcodeInContext2 of %s failed", path);
    }
    LLVMDisposeMemoryBuffer(buf);
 
-   /*
-    * Load triple & layout from clang emitted file so we're guaranteed to be
-    * compatible.
-    */
-   llvm_triple = pstrdup(LLVMGetTarget(llvm_types_module));
-   llvm_layout = pstrdup(LLVMGetDataLayoutStr(llvm_types_module));
-
    TypeSizeT = llvm_pg_var_type("TypeSizeT");
    TypeParamBool = load_return_type(llvm_types_module, "FunctionReturningBool");
    TypeStorageBool = llvm_pg_var_type("TypeStorageBool");
index 15d4a7b431a250f4c6e0388919bca866e5990c69..a729143c47cdcb1fd973ee119bacbdd1cdd8de73 100644 (file)
@@ -37,6 +37,7 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc,
    char       *funcname;
 
    LLVMModuleRef mod;
+   LLVMContextRef lc;
    LLVMBuilderRef b;
 
    LLVMTypeRef deform_sig;
@@ -99,6 +100,7 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc,
        return NULL;
 
    mod = llvm_mutable_module(context);
+   lc = LLVMGetModuleContext(mod);
 
    funcname = llvm_expand_funcname(context, "deform");
 
@@ -133,8 +135,8 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc,
 
        param_types[0] = l_ptr(StructTupleTableSlot);
 
-       deform_sig = LLVMFunctionType(LLVMVoidType(), param_types,
-                                     lengthof(param_types), 0);
+       deform_sig = LLVMFunctionType(LLVMVoidTypeInContext(lc),
+                                     param_types, lengthof(param_types), 0);
    }
    v_deform_fn = LLVMAddFunction(mod, funcname, deform_sig);
    LLVMSetLinkage(v_deform_fn, LLVMInternalLinkage);
@@ -142,17 +144,17 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc,
    llvm_copy_attributes(AttributeTemplate, v_deform_fn);
 
    b_entry =
-       LLVMAppendBasicBlock(v_deform_fn, "entry");
+       LLVMAppendBasicBlockInContext(lc, v_deform_fn, "entry");
    b_adjust_unavail_cols =
-       LLVMAppendBasicBlock(v_deform_fn, "adjust_unavail_cols");
+       LLVMAppendBasicBlockInContext(lc, v_deform_fn, "adjust_unavail_cols");
    b_find_start =
-       LLVMAppendBasicBlock(v_deform_fn, "find_startblock");
+       LLVMAppendBasicBlockInContext(lc, v_deform_fn, "find_startblock");
    b_out =
-       LLVMAppendBasicBlock(v_deform_fn, "outblock");
+       LLVMAppendBasicBlockInContext(lc, v_deform_fn, "outblock");
    b_dead =
-       LLVMAppendBasicBlock(v_deform_fn, "deadblock");
+       LLVMAppendBasicBlockInContext(lc, v_deform_fn, "deadblock");
 
-   b = LLVMCreateBuilder();
+   b = LLVMCreateBuilderInContext(lc);
 
    attcheckattnoblocks = palloc(sizeof(LLVMBasicBlockRef) * natts);
    attstartblocks = palloc(sizeof(LLVMBasicBlockRef) * natts);
@@ -221,7 +223,7 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc,
                         LLVMBuildStructGEP(b, v_tuplep,
                                            FIELDNO_HEAPTUPLEHEADERDATA_BITS,
                                            ""),
-                        l_ptr(LLVMInt8Type()),
+                        l_ptr(LLVMInt8TypeInContext(lc)),
                         "t_bits");
    v_infomask1 =
        l_load_struct_gep(b, v_tuplep,
@@ -236,14 +238,14 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc,
    v_hasnulls =
        LLVMBuildICmp(b, LLVMIntNE,
                      LLVMBuildAnd(b,
-                                  l_int16_const(HEAP_HASNULL),
+                                  l_int16_const(lc, HEAP_HASNULL),
                                   v_infomask1, ""),
-                     l_int16_const(0),
+                     l_int16_const(lc, 0),
                      "hasnulls");
 
    /* t_infomask2 & HEAP_NATTS_MASK */
    v_maxatt = LLVMBuildAnd(b,
-                           l_int16_const(HEAP_NATTS_MASK),
+                           l_int16_const(lc, HEAP_NATTS_MASK),
                            v_infomask2,
                            "maxatt");
 
@@ -256,13 +258,13 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc,
                      l_load_struct_gep(b, v_tuplep,
                                        FIELDNO_HEAPTUPLEHEADERDATA_HOFF,
                                        ""),
-                     LLVMInt32Type(), "t_hoff");
+                     LLVMInt32TypeInContext(lc), "t_hoff");
 
    v_tupdata_base =
        LLVMBuildGEP(b,
                     LLVMBuildBitCast(b,
                                      v_tuplep,
-                                     l_ptr(LLVMInt8Type()),
+                                     l_ptr(LLVMInt8TypeInContext(lc)),
                                      ""),
                     &v_hoff, 1,
                     "v_tupdata_base");
@@ -319,7 +321,7 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc,
        LLVMBuildCondBr(b,
                        LLVMBuildICmp(b, LLVMIntULT,
                                      v_maxatt,
-                                     l_int16_const(natts),
+                                     l_int16_const(lc, natts),
                                      ""),
                        b_adjust_unavail_cols,
                        b_find_start);
@@ -328,8 +330,8 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc,
        LLVMPositionBuilderAtEnd(b, b_adjust_unavail_cols);
 
        v_params[0] = v_slot;
-       v_params[1] = LLVMBuildZExt(b, v_maxatt, LLVMInt32Type(), "");
-       v_params[2] = l_int32_const(natts);
+       v_params[1] = LLVMBuildZExt(b, v_maxatt, LLVMInt32TypeInContext(lc), "");
+       v_params[2] = l_int32_const(lc, natts);
        LLVMBuildCall(b, llvm_pg_func(mod, "slot_getmissingattrs"),
                      v_params, lengthof(v_params), "");
        LLVMBuildBr(b, b_find_start);
@@ -352,7 +354,7 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc,
 
        for (attnum = 0; attnum < natts; attnum++)
        {
-           LLVMValueRef v_attno = l_int16_const(attnum);
+           LLVMValueRef v_attno = l_int16_const(lc, attnum);
 
            LLVMAddCase(v_switch, v_attno, attcheckattnoblocks[attnum]);
        }
@@ -375,7 +377,7 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc,
        Form_pg_attribute att = TupleDescAttr(desc, attnum);
        LLVMValueRef v_incby;
        int         alignto;
-       LLVMValueRef l_attno = l_int16_const(attnum);
+       LLVMValueRef l_attno = l_int16_const(lc, attnum);
        LLVMValueRef v_attdatap;
        LLVMValueRef v_resultp;
 
@@ -436,14 +438,14 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc,
            else
                b_next = attcheckattnoblocks[attnum + 1];
 
-           v_nullbyteno = l_int32_const(attnum >> 3);
-           v_nullbytemask = l_int8_const(1 << ((attnum) & 0x07));
+           v_nullbyteno = l_int32_const(lc, attnum >> 3);
+           v_nullbytemask = l_int8_const(lc, 1 << ((attnum) & 0x07));
            v_nullbyte = l_load_gep1(b, v_bits, v_nullbyteno, "attnullbyte");
 
            v_nullbit = LLVMBuildICmp(b,
                                      LLVMIntEQ,
                                      LLVMBuildAnd(b, v_nullbyte, v_nullbytemask, ""),
-                                     l_int8_const(0),
+                                     l_int8_const(lc, 0),
                                      "attisnull");
 
            v_attisnull = LLVMBuildAnd(b, v_hasnulls, v_nullbit, "");
@@ -454,7 +456,7 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc,
 
            /* store null-byte */
            LLVMBuildStore(b,
-                          l_int8_const(1),
+                          l_int8_const(lc, 1),
                           LLVMBuildGEP(b, v_tts_nulls, &l_attno, 1, ""));
            /* store zero datum */
            LLVMBuildStore(b,
@@ -524,7 +526,7 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc,
                    l_load_gep1(b, v_tupdata_base, v_off, "padbyte");
                v_ispad =
                    LLVMBuildICmp(b, LLVMIntEQ,
-                                 v_possible_padbyte, l_int8_const(0),
+                                 v_possible_padbyte, l_int8_const(lc, 0),
                                  "ispadbyte");
                LLVMBuildCondBr(b, v_ispad,
                                attalignblocks[attnum],
@@ -639,7 +641,7 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc,
        v_resultp = LLVMBuildGEP(b, v_tts_values, &l_attno, 1, "");
 
        /* store null-byte (false) */
-       LLVMBuildStore(b, l_int8_const(0),
+       LLVMBuildStore(b, l_int8_const(lc, 0),
                       LLVMBuildGEP(b, v_tts_nulls, &l_attno, 1, ""));
 
        /*
@@ -650,7 +652,7 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc,
        {
            LLVMValueRef v_tmp_loaddata;
            LLVMTypeRef vartypep =
-               LLVMPointerType(LLVMIntType(att->attlen * 8), 0);
+               LLVMPointerType(LLVMIntTypeInContext(lc, att->attlen * 8), 0);
 
            v_tmp_loaddata =
                LLVMBuildPointerCast(b, v_attdatap, vartypep, "");
@@ -739,11 +741,11 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc,
        LLVMValueRef v_off = LLVMBuildLoad(b, v_offp, "");
        LLVMValueRef v_flags;
 
-       LLVMBuildStore(b, l_int16_const(natts), v_nvalidp);
-       v_off = LLVMBuildTrunc(b, v_off, LLVMInt32Type(), "");
+       LLVMBuildStore(b, l_int16_const(lc, natts), v_nvalidp);
+       v_off = LLVMBuildTrunc(b, v_off, LLVMInt32TypeInContext(lc), "");
        LLVMBuildStore(b, v_off, v_slotoffp);
        v_flags = LLVMBuildLoad(b, v_flagsp, "tts_flags");
-       v_flags = LLVMBuildOr(b, v_flags, l_int16_const(TTS_FLAG_SLOW), "");
+       v_flags = LLVMBuildOr(b, v_flags, l_int16_const(lc, TTS_FLAG_SLOW), "");
        LLVMBuildStore(b, v_flags, v_flagsp);
        LLVMBuildRetVoid(b);
    }
index 2ac335e23899e7620701bb0aa890dd2d2eca9d9e..4b51aa1ce0773c4311e61437cee0df1fcde8791a 100644 (file)
@@ -84,6 +84,7 @@ llvm_compile_expr(ExprState *state)
 
    LLVMBuilderRef b;
    LLVMModuleRef mod;
+   LLVMContextRef lc;
    LLVMValueRef eval_fn;
    LLVMBasicBlockRef entry;
    LLVMBasicBlockRef *opblocks;
@@ -145,8 +146,9 @@ llvm_compile_expr(ExprState *state)
    INSTR_TIME_SET_CURRENT(starttime);
 
    mod = llvm_mutable_module(context);
+   lc = LLVMGetModuleContext(mod);
 
-   b = LLVMCreateBuilder();
+   b = LLVMCreateBuilderInContext(lc);
 
    funcname = llvm_expand_funcname(context, "evalexpr");
 
@@ -157,7 +159,7 @@ llvm_compile_expr(ExprState *state)
    LLVMSetVisibility(eval_fn, LLVMDefaultVisibility);
    llvm_copy_attributes(AttributeTemplate, eval_fn);
 
-   entry = LLVMAppendBasicBlock(eval_fn, "entry");
+   entry = LLVMAppendBasicBlockInContext(lc, eval_fn, "entry");
 
    /* build state */
    v_state = LLVMGetParam(eval_fn, 0);
@@ -303,7 +305,7 @@ llvm_compile_expr(ExprState *state)
                                          "");
                    LLVMBuildCondBr(b,
                                    LLVMBuildICmp(b, LLVMIntUGE, v_nvalid,
-                                                 l_int16_const(op->d.fetch.last_var),
+                                                 l_int16_const(lc, op->d.fetch.last_var),
                                                  ""),
                                    opblocks[opno + 1], b_fetch);
 
@@ -341,7 +343,7 @@ llvm_compile_expr(ExprState *state)
                        LLVMValueRef params[2];
 
                        params[0] = v_slot;
-                       params[1] = l_int32_const(op->d.fetch.last_var);
+                       params[1] = l_int32_const(lc, op->d.fetch.last_var);
 
                        LLVMBuildCall(b,
                                      llvm_pg_func(mod, "slot_getsomeattrs_int"),
@@ -378,7 +380,7 @@ llvm_compile_expr(ExprState *state)
                        v_nulls = v_scannulls;
                    }
 
-                   v_attnum = l_int32_const(op->d.var.attnum);
+                   v_attnum = l_int32_const(lc, op->d.var.attnum);
                    value = l_load_gep1(b, v_values, v_attnum, "");
                    isnull = l_load_gep1(b, v_nulls, v_attnum, "");
                    LLVMBuildStore(b, value, v_resvaluep);
@@ -444,12 +446,12 @@ llvm_compile_expr(ExprState *state)
                    }
 
                    /* load data */
-                   v_attnum = l_int32_const(op->d.assign_var.attnum);
+                   v_attnum = l_int32_const(lc, op->d.assign_var.attnum);
                    v_value = l_load_gep1(b, v_values, v_attnum, "");
                    v_isnull = l_load_gep1(b, v_nulls, v_attnum, "");
 
                    /* compute addresses of targets */
-                   v_resultnum = l_int32_const(op->d.assign_var.resultnum);
+                   v_resultnum = l_int32_const(lc, op->d.assign_var.resultnum);
                    v_rvaluep = LLVMBuildGEP(b, v_resultvalues,
                                             &v_resultnum, 1, "");
                    v_risnullp = LLVMBuildGEP(b, v_resultnulls,
@@ -478,7 +480,7 @@ llvm_compile_expr(ExprState *state)
                    v_isnull = LLVMBuildLoad(b, v_tmpisnullp, "");
 
                    /* compute addresses of targets */
-                   v_resultnum = l_int32_const(resultnum);
+                   v_resultnum = l_int32_const(lc, resultnum);
                    v_rvaluep =
                        LLVMBuildGEP(b, v_resultvalues, &v_resultnum, 1, "");
                    v_risnullp =
@@ -1703,7 +1705,7 @@ llvm_compile_expr(ExprState *state)
                    v_cmpresult =
                        LLVMBuildTrunc(b,
                                       LLVMBuildLoad(b, v_resvaluep, ""),
-                                      LLVMInt32Type(), "");
+                                      LLVMInt32TypeInContext(lc), "");
 
                    switch (rctype)
                    {
@@ -1729,7 +1731,7 @@ llvm_compile_expr(ExprState *state)
                    v_result = LLVMBuildICmp(b,
                                             predicate,
                                             v_cmpresult,
-                                            l_int32_const(0),
+                                            l_int32_const(lc, 0),
                                             "");
                    v_result = LLVMBuildZExt(b, v_result, TypeSizeT, "");
 
@@ -1872,7 +1874,7 @@ llvm_compile_expr(ExprState *state)
                    LLVMValueRef value,
                                isnull;
 
-                   v_aggno = l_int32_const(op->d.aggref.aggno);
+                   v_aggno = l_int32_const(lc, op->d.aggref.aggno);
 
                    /* load agg value / null */
                    value = l_load_gep1(b, v_aggvalues, v_aggno, "aggvalue");
@@ -1906,7 +1908,7 @@ llvm_compile_expr(ExprState *state)
                     * expression). So load it from memory each time round.
                     */
                    v_wfuncnop = l_ptr_const(&wfunc->wfuncno,
-                                            l_ptr(LLVMInt32Type()));
+                                            l_ptr(LLVMInt32TypeInContext(lc)));
                    v_wfuncno = LLVMBuildLoad(b, v_wfuncnop, "v_wfuncno");
 
                    /* load window func value / null */
@@ -2014,7 +2016,7 @@ llvm_compile_expr(ExprState *state)
                    /* strict function, check for NULL args */
                    for (int argno = 0; argno < nargs; argno++)
                    {
-                       LLVMValueRef v_argno = l_int32_const(argno);
+                       LLVMValueRef v_argno = l_int32_const(lc, argno);
                        LLVMValueRef v_argisnull;
                        LLVMBasicBlockRef b_argnotnull;
 
@@ -2071,7 +2073,7 @@ llvm_compile_expr(ExprState *state)
                                                        FIELDNO_AGGSTATE_ALL_PERGROUPS,
                                                        "aggstate.all_pergroups");
 
-                   v_setoff = l_int32_const(op->d.agg_plain_pergroup_nullcheck.setoff);
+                   v_setoff = l_int32_const(lc, op->d.agg_plain_pergroup_nullcheck.setoff);
 
                    v_pergroup_allaggs = l_load_gep1(b, v_allpergroupsp, v_setoff, "");
 
@@ -2139,8 +2141,8 @@ llvm_compile_expr(ExprState *state)
                        l_load_struct_gep(b, v_aggstatep,
                                          FIELDNO_AGGSTATE_ALL_PERGROUPS,
                                          "aggstate.all_pergroups");
-                   v_setoff = l_int32_const(op->d.agg_trans.setoff);
-                   v_transno = l_int32_const(op->d.agg_trans.transno);
+                   v_setoff = l_int32_const(lc, op->d.agg_trans.setoff);
+                   v_transno = l_int32_const(lc, op->d.agg_trans.transno);
                    v_pergroupp =
                        LLVMBuildGEP(b,
                                     l_load_gep1(b, v_allpergroupsp, v_setoff, ""),
@@ -2243,7 +2245,7 @@ llvm_compile_expr(ExprState *state)
 
                    /* set aggstate globals */
                    LLVMBuildStore(b, v_aggcontext, v_curaggcontext);
-                   LLVMBuildStore(b, l_int32_const(op->d.agg_trans.setno),
+                   LLVMBuildStore(b, l_int32_const(lc, op->d.agg_trans.setno),
                                   v_current_setp);
                    LLVMBuildStore(b, v_pertransp, v_current_pertransp);
 
@@ -2479,11 +2481,14 @@ BuildV1Call(LLVMJitContext *context, LLVMBuilderRef b,
            LLVMModuleRef mod, FunctionCallInfo fcinfo,
            LLVMValueRef *v_fcinfo_isnull)
 {
+   LLVMContextRef lc;
    LLVMValueRef v_fn;
    LLVMValueRef v_fcinfo_isnullp;
    LLVMValueRef v_retval;
    LLVMValueRef v_fcinfo;
 
+   lc = LLVMGetModuleContext(mod);
+
    v_fn = llvm_function_reference(context, b, mod, fcinfo);
 
    v_fcinfo = l_ptr_const(fcinfo, l_ptr(StructFunctionCallInfoData));
@@ -2505,12 +2510,12 @@ BuildV1Call(LLVMJitContext *context, LLVMBuilderRef b,
        LLVMValueRef v_lifetime = create_LifetimeEnd(mod);
        LLVMValueRef params[2];
 
-       params[0] = l_int64_const(sizeof(NullableDatum) * fcinfo->nargs);
-       params[1] = l_ptr_const(fcinfo->args, l_ptr(LLVMInt8Type()));
+       params[0] = l_int64_const(lc, sizeof(NullableDatum) * fcinfo->nargs);
+       params[1] = l_ptr_const(fcinfo->args, l_ptr(LLVMInt8TypeInContext(lc)));
        LLVMBuildCall(b, v_lifetime, params, lengthof(params), "");
 
-       params[0] = l_int64_const(sizeof(fcinfo->isnull));
-       params[1] = l_ptr_const(&fcinfo->isnull, l_ptr(LLVMInt8Type()));
+       params[0] = l_int64_const(lc, sizeof(fcinfo->isnull));
+       params[1] = l_ptr_const(&fcinfo->isnull, l_ptr(LLVMInt8TypeInContext(lc)));
        LLVMBuildCall(b, v_lifetime, params, lengthof(params), "");
    }
 
@@ -2556,6 +2561,7 @@ create_LifetimeEnd(LLVMModuleRef mod)
    LLVMTypeRef sig;
    LLVMValueRef fn;
    LLVMTypeRef param_types[2];
+   LLVMContextRef lc;
 
    /* LLVM 5+ has a variadic pointer argument */
 #if LLVM_VERSION_MAJOR < 5
@@ -2568,12 +2574,12 @@ create_LifetimeEnd(LLVMModuleRef mod)
    if (fn)
        return fn;
 
-   param_types[0] = LLVMInt64Type();
-   param_types[1] = l_ptr(LLVMInt8Type());
+   lc = LLVMGetModuleContext(mod);
+   param_types[0] = LLVMInt64TypeInContext(lc);
+   param_types[1] = l_ptr(LLVMInt8TypeInContext(lc));
 
-   sig = LLVMFunctionType(LLVMVoidType(),
-                          param_types, lengthof(param_types),
-                          false);
+   sig = LLVMFunctionType(LLVMVoidTypeInContext(lc), param_types,
+                          lengthof(param_types), false);
    fn = LLVMAddFunction(mod, nm, sig);
 
    LLVMSetFunctionCallConv(fn, LLVMCCallConv);
index c765add8564c8f6c6f72791574066fdaf5ae3297..d92d7f3c8819d67757a0f7f0d3238a80e9ce9752 100644 (file)
@@ -114,12 +114,12 @@ typedef llvm::StringMap<std::unique_ptr<llvm::ModuleSummaryIndex> > SummaryCache
 llvm::ManagedStatic<SummaryCache> summary_cache;
 
 
-static std::unique_ptr<ImportMapTy> llvm_build_inline_plan(llvm::Module *mod);
+static std::unique_ptr<ImportMapTy> llvm_build_inline_plan(LLVMContextRef lc, llvm::Module *mod);
 static void llvm_execute_inline_plan(llvm::Module *mod,
                                     ImportMapTy *globalsToInline);
 
-static llvm::Module* load_module_cached(llvm::StringRef modPath);
-static std::unique_ptr<llvm::Module> load_module(llvm::StringRef Identifier);
+static llvm::Module* load_module_cached(LLVMContextRef c, llvm::StringRef modPath);
+static std::unique_ptr<llvm::Module> load_module(LLVMContextRef c, llvm::StringRef Identifier);
 static std::unique_ptr<llvm::ModuleSummaryIndex> llvm_load_summary(llvm::StringRef path);
 
 
@@ -152,6 +152,18 @@ summaries_for_guid(const InlineSearchPath& path, llvm::GlobalValue::GUID guid);
 #define ilog(...)  (void) 0
 #endif
 
+/*
+ * Reset inlining related state. This needs to be called before the currently
+ * used LLVMContextRef is disposed (and a new one create), otherwise we would
+ * have dangling references to deleted modules.
+ */
+void
+llvm_inline_reset_caches(void)
+{
+   module_cache->clear();
+   summary_cache->clear();
+}
+
 /*
  * Perform inlining of external function references in M based on a simple
  * cost based analysis.
@@ -159,9 +171,10 @@ summaries_for_guid(const InlineSearchPath& path, llvm::GlobalValue::GUID guid);
 void
 llvm_inline(LLVMModuleRef M)
 {
+   LLVMContextRef lc = LLVMGetModuleContext(M);
    llvm::Module *mod = llvm::unwrap(M);
 
-   std::unique_ptr<ImportMapTy> globalsToInline = llvm_build_inline_plan(mod);
+   std::unique_ptr<ImportMapTy> globalsToInline = llvm_build_inline_plan(lc, mod);
    if (!globalsToInline)
        return;
    llvm_execute_inline_plan(mod, globalsToInline.get());
@@ -172,7 +185,7 @@ llvm_inline(LLVMModuleRef M)
  * mod.
  */
 static std::unique_ptr<ImportMapTy>
-llvm_build_inline_plan(llvm::Module *mod)
+llvm_build_inline_plan(LLVMContextRef lc, llvm::Module *mod)
 {
    std::unique_ptr<ImportMapTy> globalsToInline(new ImportMapTy());
    FunctionInlineStates functionStates;
@@ -271,7 +284,7 @@ llvm_build_inline_plan(llvm::Module *mod)
                continue;
            }
 
-           defMod = load_module_cached(modPath);
+           defMod = load_module_cached(lc, modPath);
            if (defMod->materializeMetadata())
                elog(FATAL, "failed to materialize metadata");
 
@@ -466,20 +479,20 @@ llvm_execute_inline_plan(llvm::Module *mod, ImportMapTy *globalsToInline)
  * the cache state would get corrupted.
  */
 static llvm::Module*
-load_module_cached(llvm::StringRef modPath)
+load_module_cached(LLVMContextRef lc, llvm::StringRef modPath)
 {
    auto it = module_cache->find(modPath);
    if (it == module_cache->end())
    {
        it = module_cache->insert(
-           std::make_pair(modPath, load_module(modPath))).first;
+           std::make_pair(modPath, load_module(lc, modPath))).first;
    }
 
    return it->second.get();
 }
 
 static std::unique_ptr<llvm::Module>
-load_module(llvm::StringRef Identifier)
+load_module(LLVMContextRef lc, llvm::StringRef Identifier)
 {
    LLVMMemoryBufferRef buf;
    LLVMModuleRef mod;
@@ -491,7 +504,7 @@ load_module(llvm::StringRef Identifier)
    if (LLVMCreateMemoryBufferWithContentsOfFile(path, &buf, &msg))
        elog(FATAL, "failed to open bitcode file \"%s\": %s",
             path, msg);
-   if (LLVMGetBitcodeModuleInContext2(LLVMGetGlobalContext(), buf, &mod))
+   if (LLVMGetBitcodeModuleInContext2(lc, buf, &mod))
        elog(FATAL, "failed to parse bitcode in file \"%s\"", path);
 
    /*
index 17b3b7593ff7681476b65d558740927b755f85b4..6d90a16f792b84e3e0a8ac91d51110af3e4cefa2 100644 (file)
@@ -42,6 +42,13 @@ typedef struct LLVMJitContext
    /* number of modules created */
    size_t      module_generation;
 
+   /*
+    * The LLVM Context used by this JIT context. An LLVM context is reused
+    * across many compilations, but occasionally reset to prevent it using
+    * too much memory due to more and more types accumulating.
+    */
+   LLVMContextRef llvm_context;
+
    /* current, "open for write", module */
    LLVMModuleRef module;
 
@@ -99,6 +106,7 @@ extern LLVMValueRef llvm_function_reference(LLVMJitContext *context,
                        LLVMModuleRef mod,
                        FunctionCallInfo fcinfo);
 
+extern void llvm_inline_reset_caches(void);
 extern void llvm_inline(LLVMModuleRef mod);
 
 /*
index 0745dcac9c23ebd86a572bd70d34a8cfd08301bf..5e74543be4728ad4f49d32d42899de54dd97bade 100644 (file)
@@ -44,36 +44,36 @@ l_ptr(LLVMTypeRef t)
  * Emit constant integer.
  */
 static inline LLVMValueRef
-l_int8_const(int8 i)
+l_int8_const(LLVMContextRef lc, int8 i)
 {
-   return LLVMConstInt(LLVMInt8Type(), i, false);
+   return LLVMConstInt(LLVMInt8TypeInContext(lc), i, false);
 }
 
 /*
  * Emit constant integer.
  */
 static inline LLVMValueRef
-l_int16_const(int16 i)
+l_int16_const(LLVMContextRef lc, int16 i)
 {
-   return LLVMConstInt(LLVMInt16Type(), i, false);
+   return LLVMConstInt(LLVMInt16TypeInContext(lc), i, false);
 }
 
 /*
  * Emit constant integer.
  */
 static inline LLVMValueRef
-l_int32_const(int32 i)
+l_int32_const(LLVMContextRef lc, int32 i)
 {
-   return LLVMConstInt(LLVMInt32Type(), i, false);
+   return LLVMConstInt(LLVMInt32TypeInContext(lc), i, false);
 }
 
 /*
  * Emit constant integer.
  */
 static inline LLVMValueRef
-l_int64_const(int64 i)
+l_int64_const(LLVMContextRef lc, int64 i)
 {
-   return LLVMConstInt(LLVMInt64Type(), i, false);
+   return LLVMConstInt(LLVMInt64TypeInContext(lc), i, false);
 }
 
 /*
@@ -137,12 +137,15 @@ l_bb_before_v(LLVMBasicBlockRef r, const char *fmt,...)
 {
    char        buf[512];
    va_list     args;
+   LLVMContextRef lc;
 
    va_start(args, fmt);
    vsnprintf(buf, sizeof(buf), fmt, args);
    va_end(args);
 
-   return LLVMInsertBasicBlock(r, buf);
+   lc = LLVMGetTypeContext(LLVMTypeOf(LLVMGetBasicBlockParent(r)));
+
+   return LLVMInsertBasicBlockInContext(lc, r, buf);
 }
 
 /* separate, because pg_attribute_printf(2, 3) can't appear in definition */
@@ -157,12 +160,15 @@ l_bb_append_v(LLVMValueRef f, const char *fmt,...)
 {
    char        buf[512];
    va_list     args;
+   LLVMContextRef lc;
 
    va_start(args, fmt);
    vsnprintf(buf, sizeof(buf), fmt, args);
    va_end(args);
 
-   return LLVMAppendBasicBlock(f, buf);
+   lc = LLVMGetTypeContext(LLVMTypeOf(f));
+
+   return LLVMAppendBasicBlockInContext(lc, f, buf);
 }
 
 /*
@@ -174,7 +180,7 @@ l_callsite_ro(LLVMValueRef f)
    const char  argname[] = "readonly";
    LLVMAttributeRef ref;
 
-   ref = LLVMCreateStringAttribute(LLVMGetGlobalContext(),
+   ref = LLVMCreateStringAttribute(LLVMGetTypeContext(LLVMTypeOf(f)),
                                    argname,
                                    sizeof(argname) - 1,
                                    NULL, 0);
@@ -194,7 +200,7 @@ l_callsite_alwaysinline(LLVMValueRef f)
 
    id = LLVMGetEnumAttributeKindForName(argname,
                                         sizeof(argname) - 1);
-   attr = LLVMCreateEnumAttribute(LLVMGetGlobalContext(), id, 0);
+   attr = LLVMCreateEnumAttribute(LLVMGetTypeContext(LLVMTypeOf(f)), id, 0);
    LLVMAddCallSiteAttribute(f, LLVMAttributeFunctionIndex, attr);
 }