aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--src/Makefile.global.in21
-rw-r--r--src/backend/common.mk1
-rw-r--r--src/backend/jit/jit.c1
-rw-r--r--src/backend/jit/llvm/Makefile14
-rw-r--r--src/backend/jit/llvm/llvmjit.c588
-rw-r--r--src/backend/jit/llvm/llvmjit_types.c60
-rw-r--r--src/backend/utils/misc/guc.c11
-rw-r--r--src/include/jit/jit.h21
-rw-r--r--src/include/jit/llvmjit.h30
-rw-r--r--src/tools/pgindent/typedefs.list3
11 files changed, 748 insertions, 3 deletions
diff --git a/.gitignore b/.gitignore
index a59e3da3bef..794e35b73cb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
# Global excludes across all subdirectories
*.o
*.obj
+*.bc
*.so
*.so.[0-9]
*.so.[0-9].[0-9]
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index 3bbdf17b744..859adfc3cb0 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -951,3 +951,24 @@ coverage-clean:
rm -f `find . -name '*.gcda' -print`
endif # enable_coverage
+
+##########################################################################
+#
+# LLVM support
+#
+
+ifndef COMPILE.c.bc
+# -Wno-ignored-attributes added so gnu_printf doesn't trigger
+# warnings, when the main binary is compiled with C.
+COMPILE.c.bc = $(CLANG) -Wno-ignored-attributes $(BITCODE_CFLAGS) $(CPPFLAGS) -flto=thin -emit-llvm -c
+endif
+
+ifndef COMPILE.cxx.bc
+COMPILE.cxx.bc = $(CLANG) -xc++ -Wno-ignored-attributes $(BITCODE_CXXFLAGS) $(CPPFLAGS) -flto=thin -emit-llvm -c
+endif
+
+%.bc : %.c
+ $(COMPILE.c.bc) -o $@ $<
+
+%.bc : %.cpp
+ $(COMPILE.cxx.bc) -o $@ $<
diff --git a/src/backend/common.mk b/src/backend/common.mk
index 5d599dbd0ca..6eaa353aea9 100644
--- a/src/backend/common.mk
+++ b/src/backend/common.mk
@@ -46,3 +46,4 @@ clean-local:
rm -f $(subsysfilename) $(OBJS)
$(call recurse,coverage)
+$(call recurse,install)
diff --git a/src/backend/jit/jit.c b/src/backend/jit/jit.c
index 6c842a0fe9f..300b9ff73ad 100644
--- a/src/backend/jit/jit.c
+++ b/src/backend/jit/jit.c
@@ -33,6 +33,7 @@
/* GUCs */
bool jit_enabled = true;
char *jit_provider = "llvmjit";
+bool jit_dump_bitcode = false;
static JitProviderCallbacks provider;
static bool provider_successfully_loaded = false;
diff --git a/src/backend/jit/llvm/Makefile b/src/backend/jit/llvm/Makefile
index 4b58a3450f3..63b5e2db36f 100644
--- a/src/backend/jit/llvm/Makefile
+++ b/src/backend/jit/llvm/Makefile
@@ -41,15 +41,23 @@ OBJS += llvmjit.o llvmjit_error.o llvmjit_wrap.o
# Code generation
OBJS +=
-all: all-shared-lib
+all: all-shared-lib llvmjit_types.bc
-install: all installdirs install-lib
+install: all installdirs install-lib install-types
installdirs: installdirs-lib
-uninstall: uninstall-lib
+uninstall: uninstall-lib uninstall-types
+
+# Note this is intentionally not in bitcodedir, as it's not for inlining */
+install-types: llvmjit_types.bc
+ $(INSTALL_DATA) llvmjit_types.bc '$(DESTDIR)$(pkglibdir)'
+
+uninstall-types:
+ rm -f '$(DESTDIR)$(pkglibdir)/llvmjit_types.bc'
include $(top_srcdir)/src/Makefile.shlib
clean distclean maintainer-clean: clean-lib
rm -f $(OBJS)
+ rm -f llvmjit_types.bc
diff --git a/src/backend/jit/llvm/llvmjit.c b/src/backend/jit/llvm/llvmjit.c
index 9c579229153..6b07c143b2b 100644
--- a/src/backend/jit/llvm/llvmjit.c
+++ b/src/backend/jit/llvm/llvmjit.c
@@ -19,18 +19,59 @@
#include "utils/memutils.h"
#include "utils/resowner_private.h"
+#include "portability/instr_time.h"
#include "storage/ipc.h"
+#include <llvm-c/Analysis.h>
+#include <llvm-c/BitReader.h>
+#include <llvm-c/BitWriter.h>
+#include <llvm-c/Core.h>
+#include <llvm-c/OrcBindings.h>
+#include <llvm-c/Support.h>
#include <llvm-c/Target.h>
+#include <llvm-c/Transforms/IPO.h>
+#include <llvm-c/Transforms/PassManagerBuilder.h>
+#include <llvm-c/Transforms/Scalar.h>
+
+
+/* Handle of a module emitted via ORC JIT */
+typedef struct LLVMJitHandle
+{
+ LLVMOrcJITStackRef stack;
+ LLVMOrcModuleHandle orc_handle;
+} LLVMJitHandle;
+
+
+/* types & functions commonly needed for JITing */
+LLVMTypeRef TypeSizeT;
+
+LLVMValueRef AttributeTemplate;
+LLVMValueRef FuncStrlen;
static bool llvm_session_initialized = false;
+static size_t llvm_generation = 0;
+static const char *llvm_triple = NULL;
+static const char *llvm_layout = NULL;
+
+
+static LLVMTargetMachineRef llvm_opt0_targetmachine;
+static LLVMTargetMachineRef llvm_opt3_targetmachine;
+
+static LLVMTargetRef llvm_targetref;
+static LLVMOrcJITStackRef llvm_opt0_orc;
+static LLVMOrcJITStackRef llvm_opt3_orc;
static void llvm_release_context(JitContext *context);
static void llvm_session_initialize(void);
static void llvm_shutdown(int code, Datum arg);
+static void llvm_compile_module(LLVMJitContext *context);
+static void llvm_optimize_module(LLVMJitContext *context, LLVMModuleRef module);
+
+static void llvm_create_types(void);
+static uint64_t llvm_resolve_symbol(const char *name, void *ctx);
PG_MODULE_MAGIC;
@@ -81,6 +122,359 @@ llvm_create_context(int jitFlags)
static void
llvm_release_context(JitContext *context)
{
+ LLVMJitContext *llvm_context = (LLVMJitContext *) context;
+
+ llvm_enter_fatal_on_oom();
+
+ /*
+ * 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
+ * resource cleanup is going to happen through process exit.
+ */
+ if (!proc_exit_inprogress)
+ {
+ if (llvm_context->module)
+ {
+ LLVMDisposeModule(llvm_context->module);
+ llvm_context->module = NULL;
+ }
+
+ while (llvm_context->handles != NIL)
+ {
+ LLVMJitHandle *jit_handle;
+
+ jit_handle = (LLVMJitHandle *) linitial(llvm_context->handles);
+ llvm_context->handles = list_delete_first(llvm_context->handles);
+
+ LLVMOrcRemoveModule(jit_handle->stack, jit_handle->orc_handle);
+ pfree(jit_handle);
+ }
+ }
+}
+
+/*
+ * Return module which may be modified, e.g. by creating new functions.
+ */
+LLVMModuleRef
+llvm_mutable_module(LLVMJitContext *context)
+{
+ llvm_assert_in_fatal_section();
+
+ /*
+ * If there's no in-progress module, create a new one.
+ */
+ if (!context->module)
+ {
+ context->compiled = false;
+ context->module_generation = llvm_generation++;
+ context->module = LLVMModuleCreateWithName("pg");
+ LLVMSetTarget(context->module, llvm_triple);
+ LLVMSetDataLayout(context->module, llvm_layout);
+ }
+
+ return context->module;
+}
+
+/*
+ * Expand function name to be non-conflicting. This should be used by code
+ * generating code, when adding new externally visible function definitions to
+ * a Module.
+ */
+char *
+llvm_expand_funcname(struct LLVMJitContext *context, const char *basename)
+{
+ Assert(context->module != NULL);
+
+ context->base.created_functions++;
+
+ /*
+ * Previously we used dots to separate, but turns out some tools, e.g.
+ * GDB, don't like that and truncate name.
+ */
+ return psprintf("%s_%zu_%d",
+ basename,
+ context->module_generation,
+ context->counter++);
+}
+
+/*
+ * Return pointer to function funcname, which has to exist. If there's pending
+ * code to be optimized and emitted, do so first.
+ */
+void *
+llvm_get_function(LLVMJitContext *context, const char *funcname)
+{
+ LLVMOrcTargetAddress addr = 0;
+#if defined(HAVE_DECL_LLVMORCGETSYMBOLADDRESSIN) && HAVE_DECL_LLVMORCGETSYMBOLADDRESSIN
+ ListCell *lc;
+#endif
+
+ llvm_assert_in_fatal_section();
+
+ /*
+ * If there is a pending / not emitted module, compile and emit now.
+ * Otherwise we migh not find the [correct] function.
+ */
+ if (!context->compiled)
+ {
+ llvm_compile_module(context);
+ }
+
+ /*
+ * ORC's symbol table is of *unmangled* symbols. Therefore we don't need
+ * to mangle here.
+ */
+
+#if defined(HAVE_DECL_LLVMORCGETSYMBOLADDRESSIN) && HAVE_DECL_LLVMORCGETSYMBOLADDRESSIN
+ foreach(lc, context->handles)
+ {
+ LLVMJitHandle *handle = (LLVMJitHandle *) lfirst(lc);
+
+ addr = 0;
+ if (LLVMOrcGetSymbolAddressIn(handle->stack, &addr, handle->orc_handle, funcname))
+ elog(ERROR, "failed to lookup symbol \"%s\"", funcname);
+ if (addr)
+ return (void *) (uintptr_t) addr;
+ }
+
+#else
+
+#if LLVM_VERSION_MAJOR < 5
+ if ((addr = LLVMOrcGetSymbolAddress(llvm_opt0_orc, funcname)))
+ return (void *) (uintptr_t) addr;
+ if ((addr = LLVMOrcGetSymbolAddress(llvm_opt3_orc, funcname)))
+ return (void *) (uintptr_t) addr;
+#else
+ if (LLVMOrcGetSymbolAddress(llvm_opt0_orc, &addr, funcname))
+ elog(ERROR, "failed to lookup symbol \"%s\"", funcname);
+ if (addr)
+ return (void *) (uintptr_t) addr;
+ if (LLVMOrcGetSymbolAddress(llvm_opt3_orc, &addr, funcname))
+ elog(ERROR, "failed to lookup symbol \"%s\"", funcname);
+ if (addr)
+ return (void *) (uintptr_t) addr;
+#endif /* LLVM_VERSION_MAJOR */
+
+#endif /* HAVE_DECL_LLVMORCGETSYMBOLADDRESSIN */
+
+ elog(ERROR, "failed to JIT: %s", funcname);
+
+ return NULL;
+}
+
+/*
+ * Return declaration for passed function, adding it to the module if
+ * necessary.
+ *
+ * This is used to make functions imported by llvm_create_types() known to the
+ * module that's currently being worked on.
+ */
+LLVMValueRef
+llvm_get_decl(LLVMModuleRef mod, LLVMValueRef v_src)
+{
+ LLVMValueRef v_fn;
+
+ /* don't repeatedly add function */
+ v_fn = LLVMGetNamedFunction(mod, LLVMGetValueName(v_src));
+ if (v_fn)
+ return v_fn;
+
+ v_fn = LLVMAddFunction(mod,
+ LLVMGetValueName(v_src),
+ LLVMGetElementType(LLVMTypeOf(v_src)));
+ llvm_copy_attributes(v_src, v_fn);
+
+ return v_fn;
+}
+
+/*
+ * Copy attributes from one function to another.
+ */
+void
+llvm_copy_attributes(LLVMValueRef v_from, LLVMValueRef v_to)
+{
+ int num_attributes;
+ int attno;
+ LLVMAttributeRef *attrs;
+
+ num_attributes =
+ LLVMGetAttributeCountAtIndex(v_from, LLVMAttributeFunctionIndex);
+
+ attrs = palloc(sizeof(LLVMAttributeRef) * num_attributes);
+ LLVMGetAttributesAtIndex(v_from, LLVMAttributeFunctionIndex, attrs);
+
+ for (attno = 0; attno < num_attributes; attno++)
+ {
+ LLVMAddAttributeAtIndex(v_to, LLVMAttributeFunctionIndex,
+ attrs[attno]);
+ }
+}
+
+/*
+ * Optimize code in module using the flags set in context.
+ */
+static void
+llvm_optimize_module(LLVMJitContext *context, LLVMModuleRef module)
+{
+ LLVMPassManagerBuilderRef llvm_pmb;
+ LLVMPassManagerRef llvm_mpm;
+ LLVMPassManagerRef llvm_fpm;
+ LLVMValueRef func;
+ int compile_optlevel;
+
+ if (context->base.flags & PGJIT_OPT3)
+ compile_optlevel = 3;
+ else
+ compile_optlevel = 0;
+
+ /*
+ * Have to create a new pass manager builder every pass through, as the
+ * inliner has some per-builder state. Otherwise one ends up only inlining
+ * a function the first time though.
+ */
+ llvm_pmb = LLVMPassManagerBuilderCreate();
+ LLVMPassManagerBuilderSetOptLevel(llvm_pmb, compile_optlevel);
+ llvm_fpm = LLVMCreateFunctionPassManagerForModule(module);
+
+ if (context->base.flags & PGJIT_OPT3)
+ {
+ /* TODO: Unscientifically determined threshhold */
+ LLVMPassManagerBuilderUseInlinerWithThreshold(llvm_pmb, 512);
+ }
+ else
+ {
+ /* we rely on mem2reg heavily, so emit even in the O0 case */
+ LLVMAddPromoteMemoryToRegisterPass(llvm_fpm);
+ }
+
+ LLVMPassManagerBuilderPopulateFunctionPassManager(llvm_pmb, llvm_fpm);
+
+ /*
+ * Do function level optimization. This could be moved to the point where
+ * functions are emitted, to reduce memory usage a bit.
+ */
+ LLVMInitializeFunctionPassManager(llvm_fpm);
+ for (func = LLVMGetFirstFunction(context->module);
+ func != NULL;
+ func = LLVMGetNextFunction(func))
+ LLVMRunFunctionPassManager(llvm_fpm, func);
+ LLVMFinalizeFunctionPassManager(llvm_fpm);
+ LLVMDisposePassManager(llvm_fpm);
+
+ /*
+ * Perform module level optimization. We do so even in the non-optimized
+ * case, so always-inline functions etc get inlined. It's cheap enough.
+ */
+ llvm_mpm = LLVMCreatePassManager();
+ LLVMPassManagerBuilderPopulateModulePassManager(llvm_pmb,
+ llvm_mpm);
+ /* always use always-inliner pass */
+ if (!(context->base.flags & PGJIT_OPT3))
+ LLVMAddAlwaysInlinerPass(llvm_mpm);
+ LLVMRunPassManager(llvm_mpm, context->module);
+ LLVMDisposePassManager(llvm_mpm);
+
+ LLVMPassManagerBuilderDispose(llvm_pmb);
+}
+
+/*
+ * Emit code for the currently pending module.
+ */
+static void
+llvm_compile_module(LLVMJitContext *context)
+{
+ LLVMOrcModuleHandle orc_handle;
+ MemoryContext oldcontext;
+ static LLVMOrcJITStackRef compile_orc;
+ instr_time starttime;
+ instr_time endtime;
+
+ if (context->base.flags & PGJIT_OPT3)
+ compile_orc = llvm_opt3_orc;
+ else
+ compile_orc = llvm_opt0_orc;
+
+ if (jit_dump_bitcode)
+ {
+ char *filename;
+
+ filename = psprintf("%u.%zu.bc",
+ MyProcPid,
+ context->module_generation);
+ LLVMWriteBitcodeToFile(context->module, filename);
+ pfree(filename);
+ }
+
+
+ /* optimize according to the chosen optimization settings */
+ INSTR_TIME_SET_CURRENT(starttime);
+ llvm_optimize_module(context, context->module);
+ INSTR_TIME_SET_CURRENT(endtime);
+ INSTR_TIME_ACCUM_DIFF(context->base.optimization_counter,
+ endtime, starttime);
+
+ if (jit_dump_bitcode)
+ {
+ char *filename;
+
+ filename = psprintf("%u.%zu.optimized.bc",
+ MyProcPid,
+ context->module_generation);
+ LLVMWriteBitcodeToFile(context->module, filename);
+ pfree(filename);
+ }
+
+ /*
+ * Emit the code. Note that this can, depending on the optimization
+ * settings, take noticeable resources as code emission executes low-level
+ * instruction combining/selection passes etc. Without optimization a
+ * faster instruction selection mechanism is used.
+ */
+ INSTR_TIME_SET_CURRENT(starttime);
+#if LLVM_VERSION_MAJOR < 5
+ {
+ orc_handle = LLVMOrcAddEagerlyCompiledIR(compile_orc, context->module,
+ llvm_resolve_symbol, NULL);
+ }
+#else
+ {
+ LLVMSharedModuleRef smod;
+
+ smod = LLVMOrcMakeSharedModule(context->module);
+ if (LLVMOrcAddEagerlyCompiledIR(compile_orc, &orc_handle, smod,
+ llvm_resolve_symbol, NULL))
+ {
+ elog(ERROR, "failed to jit module");
+ }
+ LLVMOrcDisposeSharedModuleRef(smod);
+ }
+#endif
+ INSTR_TIME_SET_CURRENT(endtime);
+ INSTR_TIME_ACCUM_DIFF(context->base.emission_counter,
+ endtime, starttime);
+
+ context->module = NULL;
+ context->compiled = true;
+
+ /* remember emitted code for cleanup and lookups */
+ oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+ {
+ LLVMJitHandle *handle;
+
+ handle = (LLVMJitHandle *) palloc(sizeof(LLVMJitHandle));
+ handle->stack = compile_orc;
+ handle->orc_handle = orc_handle;
+
+ context->handles = lappend(context->handles, handle);
+ }
+ MemoryContextSwitchTo(oldcontext);
+
+ ereport(DEBUG1,
+ (errmsg("time to opt: %.3fs, emit: %.3fs",
+ INSTR_TIME_GET_DOUBLE(context->base.optimization_counter),
+ INSTR_TIME_GET_DOUBLE(context->base.emission_counter)),
+ errhidestmt(true),
+ errhidecontext(true)));
}
/*
@@ -90,6 +484,9 @@ static void
llvm_session_initialize(void)
{
MemoryContext oldcontext;
+ char *error = NULL;
+ char *cpu = NULL;
+ char *features = NULL;
if (llvm_session_initialized)
return;
@@ -100,6 +497,50 @@ llvm_session_initialize(void)
LLVMInitializeNativeAsmPrinter();
LLVMInitializeNativeAsmParser();
+ /*
+ * Synchronize types early, as that also includes inferring the target
+ * triple.
+ */
+ llvm_create_types();
+
+ if (LLVMGetTargetFromTriple(llvm_triple, &llvm_targetref, &error) != 0)
+ {
+ elog(FATAL, "failed to query triple %s\n", error);
+ }
+
+ /*
+ * We want the generated code to use all available features. Therefore
+ * grab the host CPU string and detect features of the current CPU. The
+ * latter is needed because some CPU architectures default to enabling
+ * features not all CPUs have (weird, huh).
+ */
+ cpu = LLVMGetHostCPUName();
+ features = LLVMGetHostCPUFeatures();
+ elog(DEBUG2, "LLVMJIT detected CPU \"%s\", with features \"%s\"",
+ cpu, features);
+
+ llvm_opt0_targetmachine =
+ LLVMCreateTargetMachine(llvm_targetref, llvm_triple, cpu, features,
+ LLVMCodeGenLevelNone,
+ LLVMRelocDefault,
+ LLVMCodeModelJITDefault);
+ llvm_opt3_targetmachine =
+ LLVMCreateTargetMachine(llvm_targetref, llvm_triple, cpu, features,
+ LLVMCodeGenLevelAggressive,
+ LLVMRelocDefault,
+ LLVMCodeModelJITDefault);
+
+ LLVMDisposeMessage(cpu);
+ cpu = NULL;
+ LLVMDisposeMessage(features);
+ features = NULL;
+
+ /* force symbols in main binary to be loaded */
+ LLVMLoadLibraryPermanently(NULL);
+
+ llvm_opt0_orc = LLVMOrcCreateInstance(llvm_opt0_targetmachine);
+ llvm_opt3_orc = LLVMOrcCreateInstance(llvm_opt3_targetmachine);
+
before_shmem_exit(llvm_shutdown, 0);
llvm_session_initialized = true;
@@ -111,3 +552,150 @@ static void
llvm_shutdown(int code, Datum arg)
{
}
+
+/* helper for llvm_create_types */
+static LLVMTypeRef
+load_type(LLVMModuleRef mod, const char *name)
+{
+ LLVMValueRef value;
+ LLVMTypeRef typ;
+
+ /* this'll return a *pointer* to the global */
+ value = LLVMGetNamedGlobal(mod, name);
+ if (!value)
+ elog(ERROR, "type %s is unknown", name);
+
+ /* therefore look at the contained type and return that */
+ typ = LLVMTypeOf(value);
+ Assert(typ != NULL);
+ typ = LLVMGetElementType(typ);
+ Assert(typ != NULL);
+ return typ;
+}
+
+/*
+ * Load required information, types, function signatures from llvmjit_types.c
+ * and make them available in global variables.
+ *
+ * Those global variables are then used while emitting code.
+ */
+static void
+llvm_create_types(void)
+{
+ char path[MAXPGPATH];
+ LLVMMemoryBufferRef buf;
+ char *msg;
+ LLVMModuleRef mod = NULL;
+
+ snprintf(path, MAXPGPATH, "%s/%s", pkglib_path, "llvmjit_types.bc");
+
+ /* open file */
+ if (LLVMCreateMemoryBufferWithContentsOfFile(path, &buf, &msg))
+ {
+ elog(ERROR, "LLVMCreateMemoryBufferWithContentsOfFile(%s) failed: %s",
+ path, msg);
+ }
+
+ /* eagerly load contents, going to need it all */
+ if (LLVMParseBitcode2(buf, &mod))
+ {
+ elog(ERROR, "LLVMParseBitcode2 of %s failed", path);
+ }
+ LLVMDisposeMemoryBuffer(buf);
+
+ /*
+ * Load triple & layout from clang emitted file so we're guaranteed to be
+ * compatible.
+ */
+ llvm_triple = pstrdup(LLVMGetTarget(mod));
+ llvm_layout = pstrdup(LLVMGetDataLayoutStr(mod));
+
+ TypeSizeT = load_type(mod, "TypeSizeT");
+
+ AttributeTemplate = LLVMGetNamedFunction(mod, "AttributeTemplate");
+ FuncStrlen = LLVMGetNamedFunction(mod, "strlen");
+
+ /*
+ * Leave the module alive, otherwise references to function would be
+ * dangling.
+ */
+
+ return;
+}
+
+/*
+ * Split a symbol into module / function parts. If the function is in the
+ * main binary (or an external library) *modname will be NULL.
+ */
+void
+llvm_split_symbol_name(const char *name, char **modname, char **funcname)
+{
+ *modname = NULL;
+ *funcname = NULL;
+
+ /*
+ * Module function names are pgextern.$module.$funcname
+ */
+ if (strncmp(name, "pgextern.", strlen("pgextern.")) == 0)
+ {
+ /*
+ * Symbol names cannot contain a ., therefore we can split based on
+ * first and last occurance of one.
+ */
+ *funcname = rindex(name, '.');
+ (*funcname)++; /* jump over . */
+
+ *modname = pnstrdup(name + strlen("pgextern."),
+ *funcname - name - strlen("pgextern.") - 1);
+ Assert(funcname);
+
+ *funcname = pstrdup(*funcname);
+ }
+ else
+ {
+ *modname = NULL;
+ *funcname = pstrdup(name);
+ }
+}
+
+/*
+ * Attempt to resolve symbol, so LLVM can emit a reference to it.
+ */
+static uint64_t
+llvm_resolve_symbol(const char *symname, void *ctx)
+{
+ uintptr_t addr;
+ char *funcname;
+ char *modname;
+
+ /*
+ * OSX prefixes all object level symbols with an underscore. But neither
+ * dlsym() nor PG's inliner expect that. So undo.
+ */
+#if defined(__darwin__)
+ if (symname[0] != '_')
+ elog(ERROR, "expected prefixed symbol name, but got \"%s\"", symname);
+ symname++;
+#endif
+
+ llvm_split_symbol_name(symname, &modname, &funcname);
+
+ /* functions that aren't resolved to names shouldn't ever get here */
+ Assert(funcname);
+
+ if (modname)
+ addr = (uintptr_t) load_external_function(modname, funcname,
+ true, NULL);
+ else
+ addr = (uintptr_t) LLVMSearchForAddressOfSymbol(symname);
+
+ pfree(funcname);
+ if (modname)
+ pfree(modname);
+
+ /* let LLVM will error out - should never happen */
+ if (!addr)
+ elog(WARNING, "failed to resolve name %s", symname);
+
+ return (uint64_t) addr;
+}
diff --git a/src/backend/jit/llvm/llvmjit_types.c b/src/backend/jit/llvm/llvmjit_types.c
new file mode 100644
index 00000000000..90c1d55cbae
--- /dev/null
+++ b/src/backend/jit/llvm/llvmjit_types.c
@@ -0,0 +1,60 @@
+/*-------------------------------------------------------------------------
+ *
+ * llvmjit_types.c
+ * List of types needed by JIT emitting code.
+ *
+ * JIT emitting code often needs to access struct elements, create functions
+ * with the correct signature etc. To allow synchronizing these types with a
+ * low chance of definitions getting out of sync, this file lists types and
+ * functions that directly need to be accessed from LLVM.
+ *
+ * When LlVM is first used in a backend, a bitcode version of this file, will
+ * be loaded. The needed types and signatures will be stored into Struct*,
+ * Type*, Func* variables.
+ *
+ * NB: This file will not be linked into the server, it's just converted to
+ * bitcode.
+ *
+ *
+ * Copyright (c) 2016-2018, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/backend/lib/llvmjit_types.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "fmgr.h"
+
+/*
+ * List of types needed for JITing. These have to be non-static, otherwise
+ * clang/LLVM will omit them. As this file will never be linked into
+ * anything, that's harmless.
+ */
+size_t TypeSizeT;
+
+
+/*
+ * To determine which attributes functions need to have (depends e.g. on
+ * compiler version and settings) to be compatible for inlining, we simply
+ * copy the attributes of this function.
+ */
+extern Datum AttributeTemplate(PG_FUNCTION_ARGS);
+Datum
+AttributeTemplate(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_NULL();
+}
+
+
+/*
+ * To force signatures of functions used during JITing to be present,
+ * reference the functions required. This again has to be non-static, to avoid
+ * being removed as unnecessary.
+ */
+void *referenced_functions[] =
+{
+ strlen
+};
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index a3e39dc3449..10a0ffda28c 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -1734,6 +1734,17 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
+ {
+ {"jit_dump_bitcode", PGC_SUSET, DEVELOPER_OPTIONS,
+ gettext_noop("Write out LLVM bitcode to facilitate JIT debugging."),
+ NULL,
+ GUC_NOT_IN_SAMPLE
+ },
+ &jit_dump_bitcode,
+ false,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
diff --git a/src/include/jit/jit.h b/src/include/jit/jit.h
index a2f3dd9d4f2..35301674c8a 100644
--- a/src/include/jit/jit.h
+++ b/src/include/jit/jit.h
@@ -11,14 +11,34 @@
#ifndef JIT_H
#define JIT_H
+#include "executor/instrument.h"
#include "utils/resowner.h"
+/* Flags deterimining what kind of JIT operations to perform */
+#define PGJIT_NONE 0
+#define PGJIT_PERFORM 1 << 0
+#define PGJIT_OPT3 1 << 1
+
+
typedef struct JitContext
{
+ /* see PGJIT_* above */
int flags;
ResourceOwner resowner;
+
+ /* number of emitted functions */
+ size_t created_functions;
+
+ /* accumulated time to generate code */
+ instr_time generation_counter;
+
+ /* accumulated time for optimization */
+ instr_time optimization_counter;
+
+ /* accumulated time for code emission */
+ instr_time emission_counter;
} JitContext;
typedef struct JitProviderCallbacks JitProviderCallbacks;
@@ -38,6 +58,7 @@ struct JitProviderCallbacks
/* GUCs */
extern bool jit_enabled;
extern char *jit_provider;
+extern bool jit_dump_bitcode;
extern void jit_reset_after_error(void);
diff --git a/src/include/jit/llvmjit.h b/src/include/jit/llvmjit.h
index 63b2fdfe591..bd201bb7ca1 100644
--- a/src/include/jit/llvmjit.h
+++ b/src/include/jit/llvmjit.h
@@ -30,19 +30,49 @@ extern "C"
#include "jit/jit.h"
+#include "nodes/pg_list.h"
typedef struct LLVMJitContext
{
JitContext base;
+
+ /* number of modules created */
+ size_t module_generation;
+
+ /* current, "open for write", module */
+ LLVMModuleRef module;
+
+ /* is there any pending code that needs to be emitted */
+ bool compiled;
+
+ /* # of objects emitted, used to generate non-conflicting names */
+ int counter;
+
+ /* list of handles for code emitted via Orc */
+ List *handles;
} LLVMJitContext;
+
+/* type and struct definitions */
+extern LLVMTypeRef TypeSizeT;
+
+extern LLVMValueRef AttributeTemplate;
+extern LLVMValueRef FuncStrlen;
+
+
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);
+extern LLVMModuleRef llvm_mutable_module(LLVMJitContext *context);
+extern char *llvm_expand_funcname(LLVMJitContext *context, const char *basename);
+extern void *llvm_get_function(LLVMJitContext *context, const char *funcname);
+extern void llvm_split_symbol_name(const char *name, char **modname, char **funcname);
+extern LLVMValueRef llvm_get_decl(LLVMModuleRef mod, LLVMValueRef f);
+extern void llvm_copy_attributes(LLVMValueRef from, LLVMValueRef to);
/*
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index b7c0efef8f5..63d8c634e2b 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1103,6 +1103,9 @@ LDAPURLDesc
LDAP_TIMEVAL
LINE
LLVMJitContext
+LLVMJitHandle
+LLVMTypeRef
+LLVMValueRef
LOCALLOCK
LOCALLOCKOWNER
LOCALLOCKTAG