WIP: Beginning of a LLVM JIT infrastructure.
authorAndres Freund <[email protected]>
Tue, 14 Mar 2017 03:22:10 +0000 (20:22 -0700)
committerAndres Freund <[email protected]>
Tue, 14 Mar 2017 06:34:02 +0000 (23:34 -0700)
This needs to do a lot more, especially around error handling, and
memory management.

src/backend/lib/Makefile
src/backend/lib/llvmjit.c [new file with mode: 0644]
src/backend/utils/misc/guc.c
src/include/lib/llvmjit.h [new file with mode: 0644]

index 2d2ba84fe9d1202a5f0c51307ccaca8529b57245..d4c9b3ab1aa727c58af7d17d0f087796bf4a9506 100644 (file)
@@ -13,6 +13,6 @@ top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
 OBJS = binaryheap.o bipartite_match.o hyperloglog.o ilist.o pairingheap.o \
-       rbtree.o stringinfo.o
+       rbtree.o stringinfo.o llvmjit.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/lib/llvmjit.c b/src/backend/lib/llvmjit.c
new file mode 100644 (file)
index 0000000..e39736c
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ *
+ */
+
+#include "postgres.h"
+
+#ifdef USE_LLVM
+
+#include "lib/llvmjit.h"
+
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <llvm-c/Core.h>
+#include <llvm-c/ExecutionEngine.h>
+#include <llvm-c/Target.h>
+#include <llvm-c/Analysis.h>
+#include <llvm-c/BitWriter.h>
+
+#include "utils/memutils.h"
+
+/* GUCs */
+bool jit_log_ir = 0;
+bool jit_dump_bitcode = 0;
+
+static bool llvm_initialized = false;
+
+/* very common public things */
+LLVMModuleRef llvm_mod;
+LLVMExecutionEngineRef llvm_engine;
+LLVMTypeRef TypeSizeT;
+LLVMTypeRef StructHeapTupleFields;
+LLVMTypeRef StructHeapTupleHeaderData;
+LLVMTypeRef StructHeapTupleData;
+LLVMTypeRef StructItemPointerData;
+LLVMTypeRef StructBlockId;
+LLVMTypeRef StructTupleTableSlot;
+
+void
+llvm_initialize(void)
+{
+   char *error = NULL;
+   struct LLVMMCJITCompilerOptions options;
+   MemoryContext oldcontext;
+
+   if (llvm_initialized)
+       return;
+
+   oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+
+   LLVMLinkInMCJIT();
+   LLVMInitializeNativeTarget();
+   LLVMInitializeNativeAsmPrinter();
+   LLVMInitializeNativeAsmParser();
+
+   llvm_mod = LLVMModuleCreateWithName("top_module");
+   LLVMSetTarget(llvm_mod, "x86_64-unknown-linux-gnu");
+
+   LLVMInitializeMCJITCompilerOptions(&options, sizeof(options));
+   options.OptLevel = 3;
+   options.NoFramePointerElim = false;
+   if (LLVMCreateMCJITCompilerForModule(&llvm_engine, llvm_mod, &options, sizeof(options),
+                                        &error) != 0)
+   {
+       fprintf(stderr, "failed to create mcjit execution engine\n");
+       abort();
+   }
+
+   if (error)
+   {
+       fprintf(stderr, "error: %s\n", error);
+       LLVMDisposeMessage(error);
+       exit(EXIT_FAILURE);
+   }
+
+   /* so we don't constantly have to decide between 32/64 bit */
+#if SIZEOF_DATUM == 8
+   TypeSizeT = LLVMInt64Type();
+#else
+   TypeSizeT = LLVMInt32Type();
+#endif
+
+   /* FIXME: should rather load these from disk using bitcode? */
+   {
+       LLVMTypeRef members[2];
+       members[0] = LLVMInt16Type(); /* bi_hi */
+       members[1] = LLVMInt16Type(); /* bi_lo */
+       StructBlockId = LLVMStructCreateNamed(LLVMGetGlobalContext(),
+                                             "BlockId");
+       LLVMStructSetBody(StructBlockId, members, 2, false);
+   }
+
+   {
+       LLVMTypeRef members[2];
+       members[0] = StructBlockId;  /* ip_blkid */
+       members[1] = LLVMInt16Type(); /* ip_posid */
+       StructItemPointerData = LLVMStructType(members, 2, false);
+   }
+
+   {
+       LLVMTypeRef members[3];
+       members[0] = LLVMInt32Type(); /* xmin */
+       members[1] = LLVMInt32Type(); /* xmax */
+       members[2] = LLVMInt32Type(); /* cid | xvac */
+       StructHeapTupleFields = LLVMStructType(members, 3, false);
+   }
+   {
+       LLVMTypeRef members[5];
+       members[0] = StructHeapTupleFields; /* t_heap | t_datum */
+       members[1] = StructItemPointerData; /* t_ctid */
+       members[2] = LLVMInt16Type(); /* t_infomask2 */
+       members[3] = LLVMInt16Type(); /* t_infomask1 */
+       members[4] = LLVMInt8Type(); /* t_hoff */
+       /* t_bits and other data follow */
+       StructHeapTupleHeaderData = LLVMStructType(members, 5, false);
+   }
+
+   {
+       LLVMTypeRef members[4];
+       members[0] = LLVMInt32Type(); /* t_len */
+       members[1] = StructItemPointerData; /* t_self */
+       members[2] = LLVMInt32Type(); /* t_tableOid */
+       members[3] = LLVMPointerType(StructHeapTupleHeaderData, 0); /* t_data */
+       StructHeapTupleData = LLVMStructType(members, 4, false);
+   }
+   {
+       LLVMTypeRef members[16];
+       members[ 0] = LLVMInt32Type(); /* type */
+       members[ 1] = LLVMInt8Type(); /* isempty */
+       members[ 2] = LLVMInt8Type(); /* shouldFree */
+       members[ 3] = LLVMInt8Type(); /* shouldFreeMin */
+       members[ 4] = LLVMInt8Type(); /* slow */
+       members[ 5] = LLVMPointerType(StructHeapTupleData, 0); /* tuple */
+       members[ 6] = LLVMPointerType(LLVMInt64Type(), 0); /* tupleDescriptor */
+       members[ 7] = LLVMPointerType(LLVMInt64Type(), 0); /* mcxt */
+       members[ 8] = LLVMInt32Type(); /* buffer */
+       members[ 9] = LLVMInt32Type(); /* nvalid */
+       members[10] = LLVMPointerType(TypeSizeT, 0); /* values */
+       members[11] = LLVMPointerType(LLVMInt8Type(), 0); /*nulls */
+       members[12] = LLVMPointerType(LLVMInt64Type(), 0); /* dp */
+       members[13] = LLVMPointerType(LLVMInt64Type(), 0); /* mintuple */
+       members[14] = StructHeapTupleData; /* minhdr */
+       members[15] = LLVMInt32Type(); /* off */
+       StructTupleTableSlot = LLVMStructCreateNamed(LLVMGetGlobalContext(),
+                                                    "TupleTableSlot");
+       LLVMStructSetBody(StructTupleTableSlot, members, 16, false);
+   }
+
+   llvm_initialized = true;
+   MemoryContextSwitchTo(oldcontext);
+}
+
+
+void
+llvm_add_module(LLVMModuleRef mod, const char *name)
+{
+#ifdef USE_ASSERT_CHECKING
+   char *error = NULL;
+#endif
+
+   if (jit_log_ir)
+   {
+       LLVMDumpModule(mod);
+   }
+
+#ifdef USE_ASSERT_CHECKING
+   if (LLVMVerifyModule(mod, LLVMPrintMessageAction, &error))
+   {
+       elog(WARNING, "failed to JIT: %s", error);
+       LLVMDisposeMessage(error);
+   }
+#endif
+
+   if (jit_dump_bitcode)
+   {
+       char *filename = psprintf("%s.bc", name);
+       LLVMWriteBitcodeToFile(mod, filename);
+       pfree(filename);
+   }
+}
+
+void *
+llvm_get_function(const char *funcname)
+{
+   void *ret = (void *) LLVMGetFunctionAddress(llvm_engine, funcname);
+
+   /* to fix profiling: */
+   {
+       int fd = open("/dev/zero", O_RDWR);
+       mmap((void *)TYPEALIGN_DOWN(4096, ret), 4096, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, fd, 0);
+       close(fd);
+   }
+
+   return ret;
+}
+
+void
+llvm_dispose_module(LLVMModuleRef mod, const char *funcname)
+{
+   LLVMModuleRef outmod = NULL;
+   char *error = NULL;
+
+   LLVMRemoveModule(llvm_engine, mod, &outmod, &error);
+   LLVMDisposeModule(outmod);
+
+   if (error)
+   {
+       elog(ERROR, "failed to remove module for function %s: %s",
+            funcname, error);
+       LLVMDisposeMessage(error);
+   }
+}
+
+#endif /* USE_LLVM */
index 4feb26aa7a0a863b6cfc69bc48c15a0d50a10dd7..c80022ee1a0ffa5f700a1b4bdbfcadd48b81517e 100644 (file)
@@ -41,6 +41,7 @@
 #include "commands/variable.h"
 #include "commands/trigger.h"
 #include "funcapi.h"
+#include "lib/llvmjit.h"
 #include "libpq/auth.h"
 #include "libpq/be-fsstubs.h"
 #include "libpq/libpq.h"
@@ -996,6 +997,32 @@ static struct config_bool ConfigureNamesBool[] =
        false,
        NULL, NULL, NULL
    },
+
+#ifdef USE_LLVM
+   {
+       {"jit_log_ir", PGC_USERSET, DEVELOPER_OPTIONS,
+           gettext_noop("just-in-time debugging: print IR to stdout"),
+           NULL,
+           GUC_NOT_IN_SAMPLE
+       },
+       &jit_log_ir,
+       false,
+       NULL, NULL, NULL
+   },
+
+   {
+       {"jit_dump_bitcode", PGC_USERSET, DEVELOPER_OPTIONS,
+           gettext_noop("just-in-time debuggin: write out bitcode"),
+           NULL,
+           GUC_NOT_IN_SAMPLE
+       },
+       &jit_dump_bitcode,
+       false,
+       NULL, NULL, NULL
+   },
+
+#endif
+
    {
        {"zero_damaged_pages", PGC_SUSET, DEVELOPER_OPTIONS,
            gettext_noop("Continues processing past damaged page headers."),
diff --git a/src/include/lib/llvmjit.h b/src/include/lib/llvmjit.h
new file mode 100644 (file)
index 0000000..54c5075
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef LLVMJIT_H
+#define LLVMJIT_H
+#ifdef USE_LLVM
+
+#undef PM
+
+#include <llvm-c/Core.h>
+#include <llvm-c/Core.h>
+#include <llvm-c/ExecutionEngine.h>
+#include <llvm-c/Target.h>
+#include <llvm-c/Analysis.h>
+#include <llvm-c/BitWriter.h>
+
+extern bool jit_log_ir;
+extern bool jit_dump_bitcode;
+
+extern LLVMModuleRef llvm_root_mod;
+extern LLVMExecutionEngineRef llvm_engine;
+extern LLVMTypeRef TypeSizeT;
+extern LLVMTypeRef StructHeapTupleFields;
+extern LLVMTypeRef StructHeapTupleHeaderData;
+extern LLVMTypeRef StructHeapTupleData;
+extern LLVMTypeRef StructItemPointerData;
+extern LLVMTypeRef StructBlockId;
+extern LLVMTypeRef StructTupleTableSlot;
+
+extern void llvm_initialize(void);
+extern void llvm_add_module(LLVMModuleRef mod, const char *funcname);
+extern void * llvm_get_function(const char *funcname);
+extern void llvm_dispose_module(LLVMModuleRef mod, const char *funcname);
+
+#endif /* USE_LLVM */
+#endif /* LLVMJIT_H */