Support for optimizing and emitting code in LLVM JIT provider.
authorAndres Freund <andres@anarazel.de>
Thu, 22 Mar 2018 18:05:22 +0000 (11:05 -0700)
committerAndres Freund <andres@anarazel.de>
Thu, 22 Mar 2018 18:05:22 +0000 (11:05 -0700)
This commit introduces the ability to actually generate code using
LLVM. In particular, this adds:

- Ability to emit code both in heavily optimized and largely
  unoptimized fashion
- Batching facility to allow functions to be defined in small
  increments, but optimized and emitted in executable form in larger
  batches (for performance and memory efficiency)
- Type and function declaration synchronization between runtime
  generated code and normal postgres code. This is critical to be able
  to access struct fields etc.
- Developer oriented jit_dump_bitcode GUC, for inspecting / debugging
  the generated code.
- per JitContext statistics of number of functions, time spent
  generating code, optimizing, and emitting it.  This will later be
  employed for EXPLAIN support.

This commit doesn't yet contain any code actually generating
functions. That'll follow in later commits.

Documentation for GUCs added, and for JIT in general, will be added in
later commits.

Author: Andres Freund, with contributions by Pierre Ducroquet
Testing-By: Thomas Munro, Peter Eisentraut
Discussion: https://postgr.es/m/20170901064131.tazjxwus3k2w3ybh@alap3.anarazel.de

.gitignore
src/Makefile.global.in
src/backend/common.mk
src/backend/jit/jit.c
src/backend/jit/llvm/Makefile
src/backend/jit/llvm/llvmjit.c
src/backend/jit/llvm/llvmjit_types.c [new file with mode: 0644]
src/backend/utils/misc/guc.c
src/include/jit/jit.h
src/include/jit/llvmjit.h
src/tools/pgindent/typedefs.list

index a59e3da3bef682406d604d44e6e948cd8593ede7..794e35b73cbad440a629043ddcf53f89a96741a4 100644 (file)
@@ -1,6 +1,7 @@
 # Global excludes across all subdirectories
 *.o
 *.obj
+*.bc
 *.so
 *.so.[0-9]
 *.so.[0-9].[0-9]
index 3bbdf17b7447733a892315c878b5a9382220e181..859adfc3cb0257f15ae0348da05f906d1f488a04 100644 (file)
@@ -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 $@ $<
index 5d599dbd0ca2519436702a934e2fb666aa53aaf3..6eaa353aea9fc0d14301a8e4760d4de35b4b36de 100644 (file)
@@ -46,3 +46,4 @@ clean-local:
    rm -f $(subsysfilename) $(OBJS)
 
 $(call recurse,coverage)
+$(call recurse,install)
index 6c842a0fe9f38cb455a648085c71d34ec360bff2..300b9ff73ad389a04042c6bc2dcb649638af41e3 100644 (file)
@@ -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;
index 4b58a3450f3be568e5cdfb7a80d2fb3964a3c293..63b5e2db36fbe1203e96b1324aaa0103ce1d292f 100644 (file)
@@ -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
index 9c579229153568424fb768a82be20a6a3f139af3..6b07c143b2b3f45ffacba68b97014f780835079f 100644 (file)
 
 #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 (file)
index 0000000..90c1d55
--- /dev/null
@@ -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
+};
index a3e39dc3449f53d2358af8a986f10ece372e4912..10a0ffda28c0cf5a321fc14a34f25b20eb163392 100644 (file)
@@ -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
index a2f3dd9d4f266a8aba58a7394ca294d29c479b57..35301674c8a0c5fdf472d53af7d3bc37b0920dea 100644 (file)
 #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);
index 63b2fdfe5910403a801fa06948b14b4c67b8f81a..bd201bb7ca142483bd498a3af755e926a69acf55 100644 (file)
@@ -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);
 
 
 /*
index b7c0efef8f53d9ff2ebf9f1e5439ffe3a416b4b5..63d8c634e2b4e8675f3762f52f95b1d1e312be9c 100644 (file)
@@ -1103,6 +1103,9 @@ LDAPURLDesc
 LDAP_TIMEVAL
 LINE
 LLVMJitContext
+LLVMJitHandle
+LLVMTypeRef
+LLVMValueRef
 LOCALLOCK
 LOCALLOCKOWNER
 LOCALLOCKTAG