From 04d39c78bc42d42309a7372eb8c466d1e34a63e1 Mon Sep 17 00:00:00 2001 From: Andres Freund Date: Mon, 13 Mar 2017 20:22:10 -0700 Subject: [PATCH] WIP: Beginning of a LLVM JIT infrastructure. This needs to do a lot more, especially around error handling, and memory management. --- src/backend/lib/Makefile | 2 +- src/backend/lib/llvmjit.c | 217 +++++++++++++++++++++++++++++++++++ src/backend/utils/misc/guc.c | 27 +++++ src/include/lib/llvmjit.h | 33 ++++++ 4 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 src/backend/lib/llvmjit.c create mode 100644 src/include/lib/llvmjit.h diff --git a/src/backend/lib/Makefile b/src/backend/lib/Makefile index 2d2ba84fe9..d4c9b3ab1a 100644 --- a/src/backend/lib/Makefile +++ b/src/backend/lib/Makefile @@ -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 index 0000000000..e39736c3ab --- /dev/null +++ b/src/backend/lib/llvmjit.c @@ -0,0 +1,217 @@ +/* + * + */ + +#include "postgres.h" + +#ifdef USE_LLVM + +#include "lib/llvmjit.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 */ diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 4feb26aa7a..c80022ee1a 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -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 index 0000000000..54c5075955 --- /dev/null +++ b/src/include/lib/llvmjit.h @@ -0,0 +1,33 @@ +#ifndef LLVMJIT_H +#define LLVMJIT_H +#ifdef USE_LLVM + +#undef PM + +#include +#include +#include +#include +#include +#include + +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 */ -- 2.39.5