Basic JIT provider and error handling infrastructure.
This commit introduces: 1) JIT provider abstraction, which allows JIT functionality to be implemented in separate shared libraries. That's desirable because it allows to install JIT support as a separate package, and because it allows experimentation with different forms of JITing. 2) JITContexts which can be, using functions introduced in follow up commits, used to emit JITed functions, and have them be cleaned up on error. 3) The outline of a LLVM JIT provider, which will be fleshed out in subsequent commits. Documentation for GUCs added, and for JIT in general, will be added in later commits. Author: Andres Freund, with architectural input from Jeff Davis Discussion: https://postgr.es/m/20170901064131.tazjxwus3k2w3ybh@alap3.anarazel.de
This commit is contained in:
parent
4317cc68a2
commit
432bb9e04d
|
@ -31,6 +31,10 @@ SUBDIRS = \
|
||||||
test/isolation \
|
test/isolation \
|
||||||
test/perl
|
test/perl
|
||||||
|
|
||||||
|
ifeq ($(with_llvm), yes)
|
||||||
|
SUBDIRS += backend/jit/llvm
|
||||||
|
endif
|
||||||
|
|
||||||
# There are too many interdependencies between the subdirectories, so
|
# There are too many interdependencies between the subdirectories, so
|
||||||
# don't attempt parallel make here.
|
# don't attempt parallel make here.
|
||||||
.NOTPARALLEL:
|
.NOTPARALLEL:
|
||||||
|
|
|
@ -19,7 +19,8 @@ include $(top_builddir)/src/Makefile.global
|
||||||
|
|
||||||
SUBDIRS = access bootstrap catalog parser commands executor foreign lib libpq \
|
SUBDIRS = access bootstrap catalog parser commands executor foreign lib libpq \
|
||||||
main nodes optimizer port postmaster regex replication rewrite \
|
main nodes optimizer port postmaster regex replication rewrite \
|
||||||
statistics storage tcop tsearch utils $(top_builddir)/src/timezone
|
statistics storage tcop tsearch utils $(top_builddir)/src/timezone \
|
||||||
|
jit
|
||||||
|
|
||||||
include $(srcdir)/common.mk
|
include $(srcdir)/common.mk
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Makefile--
|
||||||
|
# Makefile for JIT code that's provider independent.
|
||||||
|
#
|
||||||
|
# Note that the LLVM JIT provider is recursed into by src/Makefile,
|
||||||
|
# not from here.
|
||||||
|
#
|
||||||
|
# IDENTIFICATION
|
||||||
|
# src/backend/jit/Makefile
|
||||||
|
#
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
subdir = src/backend/jit
|
||||||
|
top_builddir = ../../..
|
||||||
|
include $(top_builddir)/src/Makefile.global
|
||||||
|
|
||||||
|
override CPPFLAGS += -DDLSUFFIX=\"$(DLSUFFIX)\"
|
||||||
|
|
||||||
|
OBJS = jit.o
|
||||||
|
|
||||||
|
include $(top_srcdir)/src/backend/common.mk
|
|
@ -0,0 +1,156 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* jit.c
|
||||||
|
* Provider independent JIT infrastructure.
|
||||||
|
*
|
||||||
|
* Code related to loading JIT providers, redirecting calls into JIT providers
|
||||||
|
* and error handling. No code specific to a specific JIT implementation
|
||||||
|
* should end up here.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016-2018, PostgreSQL Global Development Group
|
||||||
|
*
|
||||||
|
* IDENTIFICATION
|
||||||
|
* src/backend/jit/jit.c
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include "fmgr.h"
|
||||||
|
#include "jit/jit.h"
|
||||||
|
#include "miscadmin.h"
|
||||||
|
#include "utils/resowner_private.h"
|
||||||
|
#include "utils/fmgrprotos.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* GUCs */
|
||||||
|
bool jit_enabled = true;
|
||||||
|
char *jit_provider = "llvmjit";
|
||||||
|
|
||||||
|
static JitProviderCallbacks provider;
|
||||||
|
static bool provider_successfully_loaded = false;
|
||||||
|
static bool provider_failed_loading = false;
|
||||||
|
|
||||||
|
|
||||||
|
static bool provider_init(void);
|
||||||
|
static bool file_exists(const char *name);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SQL level function returning whether JIT is available in the current
|
||||||
|
* backend. Will attempt to load JIT provider if necessary.
|
||||||
|
*/
|
||||||
|
Datum
|
||||||
|
pg_jit_available(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
PG_RETURN_BOOL(provider_init());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return whether a JIT provider has successfully been loaded, caching the
|
||||||
|
* result.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
provider_init(void)
|
||||||
|
{
|
||||||
|
char path[MAXPGPATH];
|
||||||
|
JitProviderInit init;
|
||||||
|
|
||||||
|
/* don't even try to load if not enabled */
|
||||||
|
if (!jit_enabled)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Don't retry loading after failing - attempting to load JIT provider
|
||||||
|
* isn't cheap.
|
||||||
|
*/
|
||||||
|
if (provider_failed_loading)
|
||||||
|
return false;
|
||||||
|
if (provider_successfully_loaded)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check whether shared library exists. We do that check before actually
|
||||||
|
* attempting to load the shared library (via load_external_function()),
|
||||||
|
* because that'd error out in case the shlib isn't available.
|
||||||
|
*/
|
||||||
|
snprintf(path, MAXPGPATH, "%s/%s%s", pkglib_path, jit_provider, DLSUFFIX);
|
||||||
|
elog(DEBUG1, "probing availability of JIT provider at %s", path);
|
||||||
|
if (!file_exists(path))
|
||||||
|
{
|
||||||
|
elog(DEBUG1,
|
||||||
|
"provider not available, disabling JIT for current session");
|
||||||
|
provider_failed_loading = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If loading functions fails, signal failure. We do so because
|
||||||
|
* load_external_function() might error out despite the above check if
|
||||||
|
* e.g. the library's dependencies aren't installed. We want to signal
|
||||||
|
* ERROR in that case, so the user is notified, but we don't want to
|
||||||
|
* continually retry.
|
||||||
|
*/
|
||||||
|
provider_failed_loading = true;
|
||||||
|
|
||||||
|
/* and initialize */
|
||||||
|
init = (JitProviderInit)
|
||||||
|
load_external_function(path, "_PG_jit_provider_init", true, NULL);
|
||||||
|
init(&provider);
|
||||||
|
|
||||||
|
provider_successfully_loaded = true;
|
||||||
|
provider_failed_loading = false;
|
||||||
|
|
||||||
|
elog(DEBUG1, "successfully loaded JIT provider in current session");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset JIT provider's error handling. This'll be called after an error has
|
||||||
|
* been thrown and the main-loop has re-established control.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
jit_reset_after_error(void)
|
||||||
|
{
|
||||||
|
if (provider_successfully_loaded)
|
||||||
|
provider.reset_after_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Release resources required by one JIT context.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
jit_release_context(JitContext *context)
|
||||||
|
{
|
||||||
|
if (provider_successfully_loaded)
|
||||||
|
provider.release_context(context);
|
||||||
|
|
||||||
|
ResourceOwnerForgetJIT(context->resowner, PointerGetDatum(context));
|
||||||
|
pfree(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
file_exists(const char *name)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
AssertArg(name != NULL);
|
||||||
|
|
||||||
|
if (stat(name, &st) == 0)
|
||||||
|
return S_ISDIR(st.st_mode) ? false : true;
|
||||||
|
else if (!(errno == ENOENT || errno == ENOTDIR))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode_for_file_access(),
|
||||||
|
errmsg("could not access file \"%s\": %m", name)));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -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)
|
|
@ -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)
|
||||||
|
{
|
||||||
|
}
|
|
@ -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())));
|
||||||
|
}
|
|
@ -42,6 +42,7 @@
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "commands/async.h"
|
#include "commands/async.h"
|
||||||
#include "commands/prepare.h"
|
#include "commands/prepare.h"
|
||||||
|
#include "jit/jit.h"
|
||||||
#include "libpq/libpq.h"
|
#include "libpq/libpq.h"
|
||||||
#include "libpq/pqformat.h"
|
#include "libpq/pqformat.h"
|
||||||
#include "libpq/pqsignal.h"
|
#include "libpq/pqsignal.h"
|
||||||
|
@ -3950,6 +3951,8 @@ PostgresMain(int argc, char *argv[],
|
||||||
/* We also want to cleanup temporary slots on error. */
|
/* We also want to cleanup temporary slots on error. */
|
||||||
ReplicationSlotCleanup();
|
ReplicationSlotCleanup();
|
||||||
|
|
||||||
|
jit_reset_after_error();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now return to normal top-level context and clear ErrorContext for
|
* Now return to normal top-level context and clear ErrorContext for
|
||||||
* next time.
|
* next time.
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
#include "commands/variable.h"
|
#include "commands/variable.h"
|
||||||
#include "commands/trigger.h"
|
#include "commands/trigger.h"
|
||||||
#include "funcapi.h"
|
#include "funcapi.h"
|
||||||
|
#include "jit/jit.h"
|
||||||
#include "libpq/auth.h"
|
#include "libpq/auth.h"
|
||||||
#include "libpq/libpq.h"
|
#include "libpq/libpq.h"
|
||||||
#include "libpq/pqformat.h"
|
#include "libpq/pqformat.h"
|
||||||
|
@ -1714,6 +1715,16 @@ static struct config_bool ConfigureNamesBool[] =
|
||||||
NULL, NULL, NULL
|
NULL, NULL, NULL
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
{"jit", PGC_USERSET, QUERY_TUNING_OTHER,
|
||||||
|
gettext_noop("Allow JIT compilation."),
|
||||||
|
NULL
|
||||||
|
},
|
||||||
|
&jit_enabled,
|
||||||
|
true,
|
||||||
|
NULL, NULL, NULL
|
||||||
|
},
|
||||||
|
|
||||||
/* End-of-list marker */
|
/* End-of-list marker */
|
||||||
{
|
{
|
||||||
{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
|
{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
|
||||||
|
@ -3707,6 +3718,17 @@ static struct config_string ConfigureNamesString[] =
|
||||||
check_wal_consistency_checking, assign_wal_consistency_checking, NULL
|
check_wal_consistency_checking, assign_wal_consistency_checking, NULL
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
{"jit_provider", PGC_POSTMASTER, FILE_LOCATIONS,
|
||||||
|
gettext_noop("JIT provider to use."),
|
||||||
|
NULL,
|
||||||
|
GUC_SUPERUSER_ONLY
|
||||||
|
},
|
||||||
|
&jit_provider,
|
||||||
|
"llvmjit",
|
||||||
|
NULL, NULL, NULL
|
||||||
|
},
|
||||||
|
|
||||||
/* End-of-list marker */
|
/* End-of-list marker */
|
||||||
{
|
{
|
||||||
{NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL, NULL
|
{NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL, NULL
|
||||||
|
|
|
@ -606,6 +606,8 @@
|
||||||
|
|
||||||
#dynamic_library_path = '$libdir'
|
#dynamic_library_path = '$libdir'
|
||||||
|
|
||||||
|
#jit = on # allow JIT compilation
|
||||||
|
#jit_provider = 'llvmjit' # JIT implementation to use
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
# LOCK MANAGEMENT
|
# LOCK MANAGEMENT
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
#include "access/hash.h"
|
#include "access/hash.h"
|
||||||
|
#include "jit/jit.h"
|
||||||
#include "storage/predicate.h"
|
#include "storage/predicate.h"
|
||||||
#include "storage/proc.h"
|
#include "storage/proc.h"
|
||||||
#include "utils/memutils.h"
|
#include "utils/memutils.h"
|
||||||
|
@ -124,6 +125,7 @@ typedef struct ResourceOwnerData
|
||||||
ResourceArray snapshotarr; /* snapshot references */
|
ResourceArray snapshotarr; /* snapshot references */
|
||||||
ResourceArray filearr; /* open temporary files */
|
ResourceArray filearr; /* open temporary files */
|
||||||
ResourceArray dsmarr; /* dynamic shmem segments */
|
ResourceArray dsmarr; /* dynamic shmem segments */
|
||||||
|
ResourceArray jitarr; /* JIT contexts */
|
||||||
|
|
||||||
/* We can remember up to MAX_RESOWNER_LOCKS references to local locks. */
|
/* We can remember up to MAX_RESOWNER_LOCKS references to local locks. */
|
||||||
int nlocks; /* number of owned locks */
|
int nlocks; /* number of owned locks */
|
||||||
|
@ -437,6 +439,7 @@ ResourceOwnerCreate(ResourceOwner parent, const char *name)
|
||||||
ResourceArrayInit(&(owner->snapshotarr), PointerGetDatum(NULL));
|
ResourceArrayInit(&(owner->snapshotarr), PointerGetDatum(NULL));
|
||||||
ResourceArrayInit(&(owner->filearr), FileGetDatum(-1));
|
ResourceArrayInit(&(owner->filearr), FileGetDatum(-1));
|
||||||
ResourceArrayInit(&(owner->dsmarr), PointerGetDatum(NULL));
|
ResourceArrayInit(&(owner->dsmarr), PointerGetDatum(NULL));
|
||||||
|
ResourceArrayInit(&(owner->jitarr), PointerGetDatum(NULL));
|
||||||
|
|
||||||
return owner;
|
return owner;
|
||||||
}
|
}
|
||||||
|
@ -538,6 +541,14 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
|
||||||
PrintDSMLeakWarning(res);
|
PrintDSMLeakWarning(res);
|
||||||
dsm_detach(res);
|
dsm_detach(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Ditto for JIT contexts */
|
||||||
|
while (ResourceArrayGetAny(&(owner->jitarr), &foundres))
|
||||||
|
{
|
||||||
|
JitContext *context = (JitContext *) PointerGetDatum(foundres);
|
||||||
|
|
||||||
|
jit_release_context(context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (phase == RESOURCE_RELEASE_LOCKS)
|
else if (phase == RESOURCE_RELEASE_LOCKS)
|
||||||
{
|
{
|
||||||
|
@ -685,6 +696,7 @@ ResourceOwnerDelete(ResourceOwner owner)
|
||||||
Assert(owner->snapshotarr.nitems == 0);
|
Assert(owner->snapshotarr.nitems == 0);
|
||||||
Assert(owner->filearr.nitems == 0);
|
Assert(owner->filearr.nitems == 0);
|
||||||
Assert(owner->dsmarr.nitems == 0);
|
Assert(owner->dsmarr.nitems == 0);
|
||||||
|
Assert(owner->jitarr.nitems == 0);
|
||||||
Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
|
Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -711,6 +723,7 @@ ResourceOwnerDelete(ResourceOwner owner)
|
||||||
ResourceArrayFree(&(owner->snapshotarr));
|
ResourceArrayFree(&(owner->snapshotarr));
|
||||||
ResourceArrayFree(&(owner->filearr));
|
ResourceArrayFree(&(owner->filearr));
|
||||||
ResourceArrayFree(&(owner->dsmarr));
|
ResourceArrayFree(&(owner->dsmarr));
|
||||||
|
ResourceArrayFree(&(owner->jitarr));
|
||||||
|
|
||||||
pfree(owner);
|
pfree(owner);
|
||||||
}
|
}
|
||||||
|
@ -1253,3 +1266,38 @@ PrintDSMLeakWarning(dsm_segment *seg)
|
||||||
elog(WARNING, "dynamic shared memory leak: segment %u still referenced",
|
elog(WARNING, "dynamic shared memory leak: segment %u still referenced",
|
||||||
dsm_segment_handle(seg));
|
dsm_segment_handle(seg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure there is room for at least one more entry in a ResourceOwner's
|
||||||
|
* JIT context reference array.
|
||||||
|
*
|
||||||
|
* This is separate from actually inserting an entry because if we run out of
|
||||||
|
* memory, it's critical to do so *before* acquiring the resource.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ResourceOwnerEnlargeJIT(ResourceOwner owner)
|
||||||
|
{
|
||||||
|
ResourceArrayEnlarge(&(owner->jitarr));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remember that a JIT context is owned by a ResourceOwner
|
||||||
|
*
|
||||||
|
* Caller must have previously done ResourceOwnerEnlargeJIT()
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ResourceOwnerRememberJIT(ResourceOwner owner, Datum handle)
|
||||||
|
{
|
||||||
|
ResourceArrayAdd(&(owner->jitarr), handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Forget that a JIT context is owned by a ResourceOwner
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ResourceOwnerForgetJIT(ResourceOwner owner, Datum handle)
|
||||||
|
{
|
||||||
|
if (!ResourceArrayRemove(&(owner->jitarr), handle))
|
||||||
|
elog(ERROR, "JIT context %p is not owned by resource owner %s",
|
||||||
|
DatumGetPointer(handle), owner->name);
|
||||||
|
}
|
||||||
|
|
|
@ -53,6 +53,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 201803212
|
#define CATALOG_VERSION_NO 201803213
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -3371,6 +3371,8 @@ DATA(insert OID = 3935 ( pg_sleep_for PGNSP PGUID 14 1 0 0 0 f f f t f v s 1 0
|
||||||
DESCR("sleep for the specified interval");
|
DESCR("sleep for the specified interval");
|
||||||
DATA(insert OID = 3936 ( pg_sleep_until PGNSP PGUID 14 1 0 0 0 f f f t f v s 1 0 2278 "1184" _null_ _null_ _null_ _null_ _null_ "select pg_catalog.pg_sleep(extract(epoch from $1) operator(pg_catalog.-) extract(epoch from pg_catalog.clock_timestamp()))" _null_ _null_ _null_ ));
|
DATA(insert OID = 3936 ( pg_sleep_until PGNSP PGUID 14 1 0 0 0 f f f t f v s 1 0 2278 "1184" _null_ _null_ _null_ _null_ _null_ "select pg_catalog.pg_sleep(extract(epoch from $1) operator(pg_catalog.-) extract(epoch from pg_catalog.clock_timestamp()))" _null_ _null_ _null_ ));
|
||||||
DESCR("sleep until the specified time");
|
DESCR("sleep until the specified time");
|
||||||
|
DATA(insert OID = 315 ( pg_jit_available PGNSP PGUID 12 1 0 0 0 f f f t f v s 0 0 16 "" _null_ _null_ _null_ _null_ _null_ pg_jit_available _null_ _null_ _null_ ));
|
||||||
|
DESCR("Is JIT compilation available in this session?");
|
||||||
|
|
||||||
DATA(insert OID = 2971 ( text PGNSP PGUID 12 1 0 0 0 f f f t f i s 1 0 25 "16" _null_ _null_ _null_ _null_ _null_ booltext _null_ _null_ _null_ ));
|
DATA(insert OID = 2971 ( text PGNSP PGUID 12 1 0 0 0 f f f t f i s 1 0 25 "16" _null_ _null_ _null_ _null_ _null_ booltext _null_ _null_ _null_ ));
|
||||||
DESCR("convert boolean to text");
|
DESCR("convert boolean to text");
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* jit.h
|
||||||
|
* Provider independent JIT infrastructure.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016-2018, PostgreSQL Global Development Group
|
||||||
|
*
|
||||||
|
* src/include/jit/jit.h
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#ifndef JIT_H
|
||||||
|
#define JIT_H
|
||||||
|
|
||||||
|
#include "utils/resowner.h"
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct JitContext
|
||||||
|
{
|
||||||
|
int flags;
|
||||||
|
|
||||||
|
ResourceOwner resowner;
|
||||||
|
} JitContext;
|
||||||
|
|
||||||
|
typedef struct JitProviderCallbacks JitProviderCallbacks;
|
||||||
|
|
||||||
|
extern void _PG_jit_provider_init(JitProviderCallbacks *cb);
|
||||||
|
typedef void (*JitProviderInit) (JitProviderCallbacks *cb);
|
||||||
|
typedef void (*JitProviderResetAfterErrorCB) (void);
|
||||||
|
typedef void (*JitProviderReleaseContextCB) (JitContext *context);
|
||||||
|
|
||||||
|
struct JitProviderCallbacks
|
||||||
|
{
|
||||||
|
JitProviderResetAfterErrorCB reset_after_error;
|
||||||
|
JitProviderReleaseContextCB release_context;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* GUCs */
|
||||||
|
extern bool jit_enabled;
|
||||||
|
extern char *jit_provider;
|
||||||
|
|
||||||
|
|
||||||
|
extern void jit_reset_after_error(void);
|
||||||
|
extern void jit_release_context(JitContext *context);
|
||||||
|
|
||||||
|
#endif /* JIT_H */
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* llvmjit.h
|
||||||
|
* LLVM JIT provider.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016-2018, PostgreSQL Global Development Group
|
||||||
|
*
|
||||||
|
* src/include/jit/llvmjit.h
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#ifndef LLVMJIT_H
|
||||||
|
#define LLVMJIT_H
|
||||||
|
|
||||||
|
#ifndef USE_LLVM
|
||||||
|
#error "llvmjit.h should only be included by code dealing with llvm"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <llvm-c/Types.h>
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* File needs to be includable by both C and C++ code, and include other
|
||||||
|
* headers doing the same. Therefore wrap C portion in our own extern "C" if
|
||||||
|
* in C++ mode.
|
||||||
|
*/
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#include "jit/jit.h"
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct LLVMJitContext
|
||||||
|
{
|
||||||
|
JitContext base;
|
||||||
|
} LLVMJitContext;
|
||||||
|
|
||||||
|
extern void llvm_enter_fatal_on_oom(void);
|
||||||
|
extern void llvm_leave_fatal_on_oom(void);
|
||||||
|
extern void llvm_reset_after_error(void);
|
||||||
|
extern void llvm_assert_in_fatal_section(void);
|
||||||
|
|
||||||
|
extern LLVMJitContext *llvm_create_context(int jitFlags);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* LLVMJIT_H */
|
|
@ -88,4 +88,11 @@ extern void ResourceOwnerRememberDSM(ResourceOwner owner,
|
||||||
extern void ResourceOwnerForgetDSM(ResourceOwner owner,
|
extern void ResourceOwnerForgetDSM(ResourceOwner owner,
|
||||||
dsm_segment *);
|
dsm_segment *);
|
||||||
|
|
||||||
|
/* support for JITContext management */
|
||||||
|
extern void ResourceOwnerEnlargeJIT(ResourceOwner owner);
|
||||||
|
extern void ResourceOwnerRememberJIT(ResourceOwner owner,
|
||||||
|
Datum handle);
|
||||||
|
extern void ResourceOwnerForgetJIT(ResourceOwner owner,
|
||||||
|
Datum handle);
|
||||||
|
|
||||||
#endif /* RESOWNER_PRIVATE_H */
|
#endif /* RESOWNER_PRIVATE_H */
|
||||||
|
|
|
@ -5,3 +5,4 @@
|
||||||
/ecpg/test/expected/
|
/ecpg/test/expected/
|
||||||
/snowball/libstemmer/
|
/snowball/libstemmer/
|
||||||
/pl/plperl/ppport\.h$
|
/pl/plperl/ppport\.h$
|
||||||
|
/jit/llvmjit\.h$
|
||||||
|
|
|
@ -1055,6 +1055,8 @@ IterateForeignScan_function
|
||||||
IterateJsonStringValuesState
|
IterateJsonStringValuesState
|
||||||
JEntry
|
JEntry
|
||||||
JHashState
|
JHashState
|
||||||
|
JitContext
|
||||||
|
JitProviderCallbacks
|
||||||
JOBOBJECTINFOCLASS
|
JOBOBJECTINFOCLASS
|
||||||
JOBOBJECT_BASIC_LIMIT_INFORMATION
|
JOBOBJECT_BASIC_LIMIT_INFORMATION
|
||||||
JOBOBJECT_BASIC_UI_RESTRICTIONS
|
JOBOBJECT_BASIC_UI_RESTRICTIONS
|
||||||
|
@ -1099,6 +1101,7 @@ LDAPMessage
|
||||||
LDAPURLDesc
|
LDAPURLDesc
|
||||||
LDAP_TIMEVAL
|
LDAP_TIMEVAL
|
||||||
LINE
|
LINE
|
||||||
|
LLVMJitContext
|
||||||
LOCALLOCK
|
LOCALLOCK
|
||||||
LOCALLOCKOWNER
|
LOCALLOCKOWNER
|
||||||
LOCALLOCKTAG
|
LOCALLOCKTAG
|
||||||
|
|
Loading…
Reference in New Issue