8000 llvmjit: Use explicit LLVMContextRef for inlining · postgrespro/postgres@2cf5058 · GitHub
[go: up one dir, main page]

Skip to content
  • Commit 2cf5058

    Browse files
    llvmjit: Use explicit LLVMContextRef for inlining
    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. This is a backpatch of 9dce220 to all supported branches. 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 Backpatch-through: v12
    1 parent f07a303 commit 2cf5058

    File tree

    6 files changed

    +244
    -103
    lines changed

    6 files changed

    +244
    -103
    lines changed

    src/backend/jit/llvm/llvmjit.c

    Lines changed: 122 additions & 17 deletions
    Original file line numberDiff line numberDiff line change
    @@ -47,6 +47,8 @@
    4747
    #include "utils/memutils.h"
    4848
    #include "utils/resowner_private.h"
    4949

    50+
    #define LLVMJIT_LLVM_CONTEXT_REUSE_MAX 100
    51+
    5052
    /* Handle of a module emitted via ORC JIT */
    5153
    typedef struct LLVMJitHandle
    5254
    {
    @@ -100,8 +102,15 @@ LLVMModuleRef llvm_types_module = NULL;
    100102

    101103
    static bool llvm_session_initialized = false;
    102104
    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;
    103111
    static const char *llvm_triple = NULL;
    104112
    static const char *llvm_layout = NULL;
    113+
    static LLVMContextRef llvm_context;
    105114

    106115

    107116
    static LLVMTargetRef llvm_targetref;
    @@ -122,6 +131,8 @@ static void llvm_compile_module(LLVMJitContext *context);
    122131
    static void llvm_optimize_module(LLVMJitContext *context, LLVMModuleRef module);
    123132

    124133
    static void llvm_create_types(void);
    134+
    static void llvm_set_target(void);
    135+
    static void llvm_recreate_llvm_context(void);
    125136
    static uint64_t llvm_resolve_symbol(const char *name, void *ctx);
    126137

    127138
    #if LLVM_VERSION_MAJOR > 11
    @@ -143,6 +154,63 @@ _PG_jit_provider_init(JitProviderCallbacks *cb)
    143154
    cb->compile_expr = llvm_compile_expr;
    144155
    }
    145156

    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+
    146214
    /*
    147215
    * Create a context for JITing work.
    148216
    *
    @@ -159,6 +227,8 @@ llvm_create_context(int jitFlags)
    159227

    160228
    llvm_session_initialize();
    161229

    230+
    llvm_recreate_llvm_context();
    231+
    162232
    ResourceOwnerEnlargeJIT(CurrentResourceOwner);
    163233

    164234
    context = MemoryContextAllocZero(TopMemoryContext,
    @@ -169,6 +239,8 @@ llvm_create_context(int jitFlags)
    169239
    context->base.resowner = CurrentResourceOwner;
    170240
    ResourceOwnerRememberJIT(CurrentResourceOwner, PointerGetDatum(context));
    171241

    242+
    llvm_jit_context_in_use_count++;
    243+
    172244
    return context;
    173245
    }
    174246

    @@ -178,9 +250,15 @@ llvm_create_context(int jitFlags)
    178250
    static void
    179251
    llvm_release_context(JitContext *context)
    180252
    {
    181-
    LLVMJitContext *llvm_context = (LLVMJitContext *) context;
    253+
    LLVMJitContext *llvm_jit_context = (LLVMJitContext *) context;
    182254
    ListCell *lc;
    183255

    256+
    /*
    257+
    * Consider as cleaned up even if we skip doing so below, that way we can CDA2
    258+
    * verify the tracking is correct (see llvm_shutdown()).
    259+
    */
    260+
    llvm_jit_context_in_use_count--;
    261+
    184262
    /*
    185263
    * When this backend is exiting, don't clean up LLVM. As an error might
    186264
    * have occurred from within LLVM, we do not want to risk reentering. All
    @@ -191,13 +269,13 @@ llvm_release_context(JitContext *context)
    191269

    192270
    llvm_enter_fatal_on_oom();
    193271

    194-
    if (llvm_context->module)
    272+
    if (llvm_jit_context->module)
    195273
    {
    196-
    LLVMDisposeModule(llvm_context->module);
    197-
    llvm_context->module = NULL;
    274+
    LLVMDisposeModule(llvm_jit_context->module);
    275+
    llvm_jit_context->module = NULL;
    198276
    }
    199277

    200-
    foreach(lc, llvm_context->handles)
    278+
    foreach(lc, llvm_jit_context->handles)
    201279
    {
    202280
    LLVMJitHandle *jit_handle = (LLVMJitHandle *) lfirst(lc);
    203281

    @@ -227,8 +305,8 @@ llvm_release_context(JitContext *context)
    227305

    228306
    pfree(jit_handle);
    229307
    }
    230-
    list_free(llvm_context->handles);
    231-
    llvm_context->handles = NIL;
    308+
    list_free(llvm_jit_context->handles);
    309+
    llvm_jit_context->handles = NIL;
    232310

    233311
    llvm_leave_fatal_on_oom();
    234312
    }
    @@ -248,7 +326,7 @@ llvm_mutable_module(LLVMJitContext *context)
    248326
    {
    249327
    context->compiled = false;
    250328
    context->module_generation = llvm_generation++;
    251-
    context->module = LLVMModuleCreateWithName("pg");
    329+
    context->module = LLVMModuleCreateWithNameInContext("pg", llvm_context);
    252330
    LLVMSetTarget(context->module, llvm_triple);
    253331
    LLVMSetDataLayout(context->module, llvm_layout);
    254332
    }
    @@ -832,6 +910,14 @@ llvm_session_initialize(void)
    832910
    LLVMInitializeNativeAsmPrinter();
    833911
    LLVMInitializeNativeAsmParser();
    834912

    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+
    835921
    /*
    836922
    * When targeting LLVM 15, turn off opaque pointers for the context we
    837923
    * build our code in. We don't need to do so for other contexts (e.g.
    @@ -851,6 +937,11 @@ llvm_session_initialize(void)
    851937
    */
    852938
    llvm_create_types();
    853939

    940+
    /*
    941+
    * Extract target information from loaded module.
    942+
    */
    943+
    llvm_set_target();
    944+
    854945
    if (LLVMGetTargetFromTriple(llvm_triple, &llvm_targetref, &error) != 0)
    855946
    {
    856947
    elog(FATAL, "failed to query triple %s", error);
    @@ -946,6 +1037,10 @@ llvm_shutdown(int code, Datum arg)
    9461037
    return;
    9471038
    }
    9481039

    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+
    9491044
    #if LLVM_VERSION_MAJOR > 11
    9501045
    {
    9511046
    if (llvm_opt3_orc)
    @@ -1008,6 +1103,23 @@ load_return_type(LLVMModuleRef mod, const char *name)
    10081103
    return typ;
    10091104
    }
    10101105

    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+
    10111123
    /*
    10121124
    * Load required information, types, function signatures from llvmjit_types.c
    10131125
    * and make them available in global variables.
    @@ -1031,19 +1143,12 @@ llvm_create_types(void)
    10311143
    }
    10321144

    10331145
    /* eagerly load contents, going to need it all */
    1034-
    if (LLVMParseBitcode2(buf, &llvm_types_module))
    1146+
    if (LLVMParseBitcodeInContext2(llvm_context, buf, &llvm_types_module))
    10351147
    {
    1036-
    elog(ERROR, "LLVMParseBitcode2 of %s failed", path);
    1148+
    elog(ERROR, "LLVMParseBitcodeInContext2 of %s failed", path);
    10371149
    }
    10381150
    LLVMDisposeMemoryBuffer(buf);
    10391151

    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-
    10471152
    TypeSizeT = llvm_pg_var_type("TypeSizeT");
    10481153
    TypeParamBool = load_return_type(llvm_types_module, "FunctionReturningBool");
    10491154
    TypeStorageBool = llvm_pg_var_type("TypeStorageBool");

    0 commit comments

    Comments
     (0)
    0