diff options
Diffstat (limited to 'src/backend/jit/llvm')
-rw-r--r-- | src/backend/jit/llvm/Makefile | 55 | ||||
-rw-r--r-- | src/backend/jit/llvm/llvmjit.c | 113 | ||||
-rw-r--r-- | src/backend/jit/llvm/llvmjit_error.cpp | 141 |
3 files changed, 309 insertions, 0 deletions
diff --git a/src/backend/jit/llvm/Makefile b/src/backend/jit/llvm/Makefile new file mode 100644 index 00000000000..856b94e12b4 --- /dev/null +++ b/src/backend/jit/llvm/Makefile @@ -0,0 +1,55 @@ +#------------------------------------------------------------------------- +# +# Makefile-- +# Makefile the LLVM JIT provider, building it into a shared library. +# +# Note that this file is recursed into from src/Makefile, not by the +# parent directory.. +# +# IDENTIFICATION +# src/backend/jit/llvm/Makefile +# +#------------------------------------------------------------------------- + +subdir = src/backend/jit/llvm +top_builddir = ../../../.. +include $(top_builddir)/src/Makefile.global + +ifneq ($(with_llvm), yes) + $(error "not building with LLVM support") +endif + +PGFILEDESC = "llvmjit - JIT using LLVM" +NAME = llvmjit + +# All files in this directy use LLVM. +CFLAGS += $(LLVM_CFLAGS) +CXXFLAGS += $(LLVM_CXXFLAGS) +override CPPFLAGS := $(LLVM_CPPFLAGS) $(CPPFLAGS) +SHLIB_LINK += $(LLVM_LIBS) +SHLIB_PREREQS += submake-generated-headers + +# Because this module includes C++ files, we need to use a C++ +# compiler for linking. Makefile.shlib uses $(COMPILER) to build +# loadable modules. +override COMPILER = $(CXX) $(CFLAGS) + +OBJS=$(WIN32RES) + +# Infrastructure +OBJS += llvmjit.o llvmjit_error.o +# Code generation +OBJS += + +all: all-shared-lib + +install: all installdirs install-lib + +installdirs: installdirs-lib + +uninstall: uninstall-lib + +include $(top_srcdir)/src/Makefile.shlib + +clean distclean maintainer-clean: clean-lib + rm -f $(OBJS) diff --git a/src/backend/jit/llvm/llvmjit.c b/src/backend/jit/llvm/llvmjit.c new file mode 100644 index 00000000000..9c579229153 --- /dev/null +++ b/src/backend/jit/llvm/llvmjit.c @@ -0,0 +1,113 @@ +/*------------------------------------------------------------------------- + * + * llvmjit.c + * Core part of the LLVM JIT provider. + * + * Copyright (c) 2016-2018, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/backend/jit/llvm/llvmjit.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "jit/llvmjit.h" + +#include "miscadmin.h" + +#include "utils/memutils.h" +#include "utils/resowner_private.h" +#include "storage/ipc.h" + + +#include <llvm-c/Target.h> + + +static bool llvm_session_initialized = false; + + +static void llvm_release_context(JitContext *context); +static void llvm_session_initialize(void); +static void llvm_shutdown(int code, Datum arg); + + +PG_MODULE_MAGIC; + + +/* + * Initialize LLVM JIT provider. + */ +void +_PG_jit_provider_init(JitProviderCallbacks *cb) +{ + cb->reset_after_error = llvm_reset_after_error; + cb->release_context = llvm_release_context; +} + +/* + * Create a context for JITing work. + * + * The context, including subsidiary resources, will be cleaned up either when + * the context is explicitly released, or when the lifetime of + * CurrentResourceOwner ends (usually the end of the current [sub]xact). + */ +LLVMJitContext * +llvm_create_context(int jitFlags) +{ + LLVMJitContext *context; + + llvm_assert_in_fatal_section(); + + llvm_session_initialize(); + + ResourceOwnerEnlargeJIT(CurrentResourceOwner); + + context = MemoryContextAllocZero(TopMemoryContext, + sizeof(LLVMJitContext)); + context->base.flags = jitFlags; + + /* ensure cleanup */ + context->base.resowner = CurrentResourceOwner; + ResourceOwnerRememberJIT(CurrentResourceOwner, PointerGetDatum(context)); + + return context; +} + +/* + * Release resources required by one llvm context. + */ +static void +llvm_release_context(JitContext *context) +{ +} + +/* + * Per session initialization. + */ +static void +llvm_session_initialize(void) +{ + MemoryContext oldcontext; + + if (llvm_session_initialized) + return; + + oldcontext = MemoryContextSwitchTo(TopMemoryContext); + + LLVMInitializeNativeTarget(); + LLVMInitializeNativeAsmPrinter(); + LLVMInitializeNativeAsmParser(); + + before_shmem_exit(llvm_shutdown, 0); + + llvm_session_initialized = true; + + MemoryContextSwitchTo(oldcontext); +} + +static void +llvm_shutdown(int code, Datum arg) +{ +} diff --git a/src/backend/jit/llvm/llvmjit_error.cpp b/src/backend/jit/llvm/llvmjit_error.cpp new file mode 100644 index 00000000000..edc1c479d01 --- /dev/null +++ b/src/backend/jit/llvm/llvmjit_error.cpp @@ -0,0 +1,141 @@ +/*------------------------------------------------------------------------- + * + * llvmjit_error.cpp + * LLVM error related handling that requires interfacing with C++ + * + * Unfortunately neither (re)setting the C++ new handler, nor the LLVM OOM + * handler are exposed to C. Therefore this file wraps the necesary code. + * + * Copyright (c) 2016-2018, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/backend/jit/llvm/llvmjit_error.c + * + *------------------------------------------------------------------------- + */ + +extern "C" +{ +#include "postgres.h" +} + +#include <llvm/Support/ErrorHandling.h> + +#include "jit/llvmjit.h" + + +static int fatal_new_handler_depth = 0; +static std::new_handler old_new_handler = NULL; + +static void fatal_system_new_handler(void); +#if LLVM_VERSION_MAJOR > 4 +static void fatal_llvm_new_handler(void *user_data, const std::string& reason, bool gen_crash_diag); +#endif +static void fatal_llvm_error_handler(void *user_data, const std::string& reason, bool gen_crash_diag); + + +/* + * Enter a section in which C++ and LLVM errors are treated as FATAL errors. + * + * This is necessary for LLVM as LLVM's error handling for such cases + * (exit()ing, throwing std::bad_alloc() if compiled with exceptions, abort()) + * isn't compatible with postgres error handling. Thus in section where LLVM + * code, not LLVM generated functions!, is executing, standard new, LLVM OOM + * and LLVM fatal errors (some OOM errors masquerade as those) are redirected + * to our own error handlers. + * + * These error handlers FATAL, because there's no reliable way from within + * LLVM to throw an error that's guaranteed not to corrupt LLVM's state. + * + * To avoid disturbing extensions using C++ and/or LLVM, these handlers are + * unset when not executing LLVM code. There is no need to call + * llvm_leave_fatal_on_oom() when ERRORing out, error recovery resets the + * handlers in that case. + */ +void +llvm_enter_fatal_on_oom(void) +{ + if (fatal_new_handler_depth == 0) + { + old_new_handler = std::set_new_handler(fatal_system_new_handler); +#if LLVM_VERSION_MAJOR > 4 + llvm::install_bad_alloc_error_handler(fatal_llvm_new_handler); +#endif + llvm::install_fatal_error_handler(fatal_llvm_error_handler); + } + fatal_new_handler_depth++; +} + +/* + * Leave fatal error section started with llvm_enter_fatal_on_oom(). + */ +void +llvm_leave_fatal_on_oom(void) +{ + fatal_new_handler_depth--; + if (fatal_new_handler_depth == 0) + { + std::set_new_handler(old_new_handler); +#if LLVM_VERSION_MAJOR > 4 + llvm::remove_bad_alloc_error_handler(); +#endif + llvm::remove_fatal_error_handler(); + } +} + +/* + * Reset fatal error handling. This should only be called in error recovery + * loops like PostgresMain()'s. + */ +void +llvm_reset_after_error(void) +{ + if (fatal_new_handler_depth != 0) + { + std::set_new_handler(old_new_handler); +#if LLVM_VERSION_MAJOR > 4 + llvm::remove_bad_alloc_error_handler(); +#endif + llvm::remove_fatal_error_handler(); + } + fatal_new_handler_depth = 0; +} + +void +llvm_assert_in_fatal_section(void) +{ + Assert(fatal_new_handler_depth > 0); +} + +static void +fatal_system_new_handler(void) +{ + ereport(FATAL, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"), + errdetail("while in LLVM"))); +} + +#if LLVM_VERSION_MAJOR > 4 +static void +fatal_llvm_new_handler(void *user_data, + const std::string& reason, + bool gen_crash_diag) +{ + ereport(FATAL, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"), + errdetail("While in LLVM: %s", reason.c_str()))); +} +#endif + +static void +fatal_llvm_error_handler(void *user_data, + const std::string& reason, + bool gen_crash_diag) +{ + ereport(FATAL, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("fatal llvm error: %s", + reason.c_str()))); +} |