LLVMJIT: Support for optimizing and emitting code.
authorAndres Freund <[email protected]>
Tue, 20 Mar 2018 03:59:42 +0000 (20:59 -0700)
committerAndres Freund <[email protected]>
Wed, 21 Mar 2018 02:34:24 +0000 (19:34 -0700)
Author: Andres Freund, with contributions by Pierre Ducroquet
Discussion: https://postgr.es/m/20170901064131[email protected]

.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 1381210b25af7b288efce23e2d2f44375ccdcd52..c1543c4f3c2156f6837309e019780cc90fc6c3a0 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 a2093798658869004ea3e028c01d021f859c35a3..d2db1a9dae9e467064569cdefe944fb823fa743f 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..aa87a0c833111aeeda2a5620009f420a51f8308b 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,358 @@ 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)
+   {
+       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 (!context->module)
+   {
+       context->compiled = false;
+       context->module = LLVMModuleCreateWithName("pg");
+       context->module_generation = llvm_generation++;
+       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 *) addr;
+   }
+
+#else
+
+#if LLVM_VERSION_MAJOR < 5
+   if ((addr = LLVMOrcGetSymbolAddress(llvm_opt0_orc, funcname)))
+       return (void *) addr;
+   if ((addr = LLVMOrcGetSymbolAddress(llvm_opt3_orc, funcname)))
+       return (void *) addr;
+   elog(ERROR, "failed to lookup symbol \"%s\"", funcname);
+#else
+   if (LLVMOrcGetSymbolAddress(llvm_opt0_orc, &addr, funcname))
+       elog(ERROR, "failed to lookup symbol \"%s\"", funcname);
+   if (addr)
+       return (void *) addr;
+   if (LLVMOrcGetSymbolAddress(llvm_opt3_orc, &addr, funcname))
+       elog(ERROR, "failed to lookup symbol \"%s\"", funcname);
+   if (addr)
+       return (void *) 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.
+    */
+   /* emit the code */
+   {
+       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 +483,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 +496,47 @@ llvm_session_initialize(void)
    LLVMInitializeNativeAsmPrinter();
    LLVMInitializeNativeAsmParser();
 
+   /* synchronize types early, as that also includes 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 separately detect features. The latter is
+    * needed because some CPU architectures default to enabling features not
+    * all CPUs have (weird, huh).
+    */
+   cpu = LLVMGetHostCPUName();
+   features = LLVMGetHostCPUFeatures();
+   elog(DEBUG2, "JIT 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 +548,148 @@ 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");
+
+   if (LLVMCreateMemoryBufferWithContentsOfFile(path, &buf, &msg))
+   {
+       elog(ERROR, "LLVMCreateMemoryBufferWithContentsOfFile(%s) failed: %s",
+            path, msg);
+   }
+
+   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)
+{
+   uint64_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 = (uint64_t) load_external_function(modname, funcname,
+                                                true, NULL);
+   else
+       addr = (uint64_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 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..28da2e9
--- /dev/null
@@ -0,0 +1,59 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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"
+
+
+/*
+ * 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 47e3c0b1133b6d66a1184e35f0010006e4085ae9..c2f6ada0d0c46a84a45a60cdda5a8fb2b84c4c30 100644 (file)
@@ -1725,6 +1725,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..a7bf446d175532359eba492368f08352e3b4a88b 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
 {
    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 +57,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 18ba7fd5745f008ab6b91d8366474b2fc8d7291e..8f96dee1881d2616bfa23c27c44c14cca876af18 100644 (file)
@@ -30,19 +30,41 @@ extern "C"
 
 
 #include "jit/jit.h"
+#include "nodes/pg_list.h"
 
 
 typedef struct LLVMJitContext
 {
    JitContext  base;
+
+   int         counter;
+   LLVMModuleRef module;
+   size_t      module_generation;
+   List       *inline_modules;
+   bool        compiled;
+   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 543cb17e410028d7e5c9c8a5d48fd2c721cca58f..35d8830d8e69882f09bb60ad58b85e535a439934 100644 (file)
@@ -1102,6 +1102,9 @@ LDAPURLDesc
 LDAP_TIMEVAL
 LINE
 LLVMJitContext
+LLVMJitHandle
+LLVMTypeRef
+LLVMValueRef
 LOCALLOCK
 LOCALLOCKOWNER
 LOCALLOCKTAG