From 23e5dce848ed8dac7b590da5c77321344b30310d Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Mon, 13 Mar 2017 20:22:10 -0700
Subject: [PATCH 04/16] WIP: Beginning of a LLVM JIT infrastructure.

This needs to do a lot more, especially around error handling, and
memory management.
---
 configure                             |   2 +-
 configure.in                          |   2 +-
 src/backend/executor/execUtils.c      |   2 +
 src/backend/lib/Makefile              |   2 +-
 src/backend/lib/llvmjit.c             | 519 ++++++++++++++++++++++++++++++++++
 src/backend/utils/misc/guc.c          |  27 ++
 src/backend/utils/resowner/resowner.c |  40 +++
 src/include/lib/llvmjit.h             |  83 ++++++
 src/include/nodes/execnodes.h         |   4 +-
 src/include/utils/resowner_private.h  |   7 +
 10 files changed, 684 insertions(+), 4 deletions(-)
 create mode 100644 src/backend/lib/llvmjit.c
 create mode 100644 src/include/lib/llvmjit.h

diff --git a/configure b/configure
index fe905e294b..b6adceb990 100755
--- a/configure
+++ b/configure
@@ -6546,7 +6546,7 @@ done
         -L*) LDFLAGS="$LDFLAGS $pgac_option";;
       esac
     done
-    for pgac_option in `$LLVM_CONFIG --libs --system-libs engine`; do
+    for pgac_option in `$LLVM_CONFIG --libs --system-libs engine debuginfodwarf orcjit passes perfjitevents`; do
       case $pgac_option in
         -l*) LLVM_LIBS="$LLVM_LIBS $pgac_option";;
       esac
diff --git a/configure.in b/configure.in
index a99da9dff3..7028a31137 100644
--- a/configure.in
+++ b/configure.in
@@ -872,7 +872,7 @@ if test "$with_llvm" = yes ; then
         -L*) LDFLAGS="$LDFLAGS $pgac_option";;
       esac
     done
-    for pgac_option in `$LLVM_CONFIG --libs --system-libs engine`; do
+    for pgac_option in `$LLVM_CONFIG --libs --system-libs engine debuginfodwarf orcjit passes perfjitevents`; do
       case $pgac_option in
         -l*) LLVM_LIBS="$LLVM_LIBS $pgac_option";;
       esac
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 5928c38f90..aee6111c14 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -156,6 +156,8 @@ CreateExecutorState(void)
 	estate->es_epqScanDone = NULL;
 	estate->es_sourceText = NULL;
 
+	estate->es_jit = NULL;
+
 	/*
 	 * Return the executor state structure
 	 */
diff --git a/src/backend/lib/Makefile b/src/backend/lib/Makefile
index d1fefe43f2..dd1390f9cf 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 dshash.o hyperloglog.o ilist.o \
-	   knapsack.o pairingheap.o rbtree.o stringinfo.o
+	knapsack.o llvmjit.o pairingheap.o rbtree.o stringinfo.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..460cb6b325
--- /dev/null
+++ b/src/backend/lib/llvmjit.c
@@ -0,0 +1,519 @@
+/*
+ * JIT infrastructure.
+ */
+
+#include "postgres.h"
+
+
+#include "lib/llvmjit.h"
+
+#include "utils/memutils.h"
+#include "utils/resowner_private.h"
+
+#ifdef USE_LLVM
+
+#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 <llvm-c/OrcBindings.h>
+#include <llvm-c/Support.h>
+#include <llvm-c/Transforms/IPO.h>
+#include <llvm-c/Transforms/Scalar.h>
+
+
+/* GUCs */
+bool jit_log_ir = 0;
+bool jit_dump_bitcode = 0;
+
+static bool llvm_initialized = false;
+static LLVMPassManagerBuilderRef llvm_pmb;
+
+/* very common public things */
+const char *llvm_triple = NULL;
+
+LLVMTargetMachineRef llvm_targetmachine;
+
+LLVMTypeRef TypeSizeT;
+LLVMTypeRef TypeMemoryContext;
+LLVMTypeRef TypePGFunction;
+
+LLVMTypeRef StructHeapTupleFieldsField3;
+LLVMTypeRef StructHeapTupleFields;
+LLVMTypeRef StructHeapTupleHeaderData;
+LLVMTypeRef StructHeapTupleDataChoice;
+LLVMTypeRef StructHeapTupleData;
+LLVMTypeRef StructMinimalTupleData;
+LLVMTypeRef StructItemPointerData;
+LLVMTypeRef StructBlockId;
+LLVMTypeRef StructFormPgAttribute;
+LLVMTypeRef StructTupleConstr;
+LLVMTypeRef StructtupleDesc;
+LLVMTypeRef StructTupleTableSlot;
+LLVMTypeRef StructMemoryContextData;
+LLVMTypeRef StructPGFinfoRecord;
+LLVMTypeRef StructFmgrInfo;
+LLVMTypeRef StructFunctionCallInfoData;
+LLVMTypeRef StructExprState;
+LLVMTypeRef StructExprContext;
+
+
+static LLVMTargetRef llvm_targetref;
+static LLVMOrcJITStackRef llvm_orc;
+
+static void llvm_shutdown(void);
+static void llvm_create_types(void);
+
+
+static void
+llvm_shutdown(void)
+{
+	/* unregister profiling support, needs to be flushed to be useful */
+	if (llvm_orc)
+	{
+		LLVMOrcUnregisterPerf(llvm_orc);
+		llvm_orc = NULL;
+	}
+}
+
+void
+llvm_initialize(void)
+{
+	char *error = NULL;
+	MemoryContext oldcontext;
+
+	if (llvm_initialized)
+		return;
+
+	oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+
+	LLVMInitializeNativeTarget();
+	LLVMInitializeNativeAsmPrinter();
+	LLVMInitializeNativeAsmParser();
+
+	/* force symbols in main binary to be loaded */
+	LLVMLoadLibraryPermanently("");
+
+	llvm_triple = LLVMGetDefaultTargetTriple();
+
+	if (LLVMGetTargetFromTriple(llvm_triple, &llvm_targetref, &error) != 0)
+	{
+		elog(FATAL, "failed to query triple %s\n", error);
+	}
+
+	llvm_targetmachine =
+		LLVMCreateTargetMachine(llvm_targetref, llvm_triple, NULL, NULL,
+								LLVMCodeGenLevelAggressive,
+								LLVMRelocDefault,
+								LLVMCodeModelJITDefault);
+
+	llvm_pmb = LLVMPassManagerBuilderCreate();
+	LLVMPassManagerBuilderSetOptLevel(llvm_pmb, 3);
+
+	llvm_orc = LLVMOrcCreateInstance(llvm_targetmachine);
+
+	LLVMOrcRegisterGDB(llvm_orc);
+	LLVMOrcRegisterPerf(llvm_orc);
+
+	atexit(llvm_shutdown);
+
+	llvm_create_types();
+
+	llvm_initialized = true;
+	MemoryContextSwitchTo(oldcontext);
+}
+
+static void
+llvm_create_types(void)
+{
+	/* so we don't constantly have to decide between 32/64 bit */
+#if SIZEOF_DATUM == 8
+	TypeSizeT = LLVMInt64Type();
+#else
+	TypeSizeT = LLVMInt32Type();
+#endif
+
+	/*
+	 * XXX: should rather load these from disk using bitcode? It's ugly to
+	 * duplicate the information, but in either case we're going to have to
+	 * use member indexes for structs :(.
+	 */
+	{
+		LLVMTypeRef members[2];
+		members[0] = LLVMInt16Type(); /* bi_hi */
+		members[1] = LLVMInt16Type(); /* bi_lo */
+		StructBlockId = LLVMStructCreateNamed(LLVMGetGlobalContext(),
+											  "struct.BlockId");
+		LLVMStructSetBody(StructBlockId, members, 2, false);
+	}
+
+	{
+		LLVMTypeRef members[2];
+		members[0] = StructBlockId;  /* ip_blkid */
+		members[1] = LLVMInt16Type(); /* ip_posid */
+
+		StructItemPointerData = LLVMStructCreateNamed(LLVMGetGlobalContext(),
+											  "struct.ItemPointerData");
+		LLVMStructSetBody(StructItemPointerData, members, lengthof(members), false);
+	}
+
+
+	{
+		LLVMTypeRef members[1];
+
+		members[0] = LLVMInt32Type() ;  /* cid | xvac */
+
+		StructHeapTupleFieldsField3 = LLVMStructCreateNamed(LLVMGetGlobalContext(),
+															"struct.StructHeapTupleFieldsField3");
+		LLVMStructSetBody(StructHeapTupleFieldsField3, members, lengthof(members), false);
+	}
+
+	{
+		LLVMTypeRef members[1];
+
+		members[0] = LLVMInt32Type() ;  /* ? */
+
+		StructPGFinfoRecord = LLVMStructCreateNamed(LLVMGetGlobalContext(),
+													"struct.PGFinfoRecord");
+		LLVMStructSetBody(StructPGFinfoRecord, members, lengthof(members), false);
+	}
+
+	{
+		LLVMTypeRef members[3];
+		members[0] = LLVMInt32Type(); /* xmin */
+		members[1] = LLVMInt32Type(); /* xmax */
+		members[2] = StructHeapTupleFieldsField3; /* cid | xvac */
+
+		StructHeapTupleFields = LLVMStructCreateNamed(LLVMGetGlobalContext(),
+													  "struct.HeapTupleFields");
+		LLVMStructSetBody(StructHeapTupleFields, members, lengthof(members), false);
+	}
+
+	{
+		LLVMTypeRef members[1];
+
+		members[0] = StructHeapTupleFields; /* t_heap | t_datum */
+
+		StructHeapTupleDataChoice = LLVMStructCreateNamed(LLVMGetGlobalContext(),
+														  "struct.HeapTupleHeaderDataChoice");
+		LLVMStructSetBody(StructHeapTupleDataChoice, members, lengthof(members), false);
+
+	}
+
+	{
+		LLVMTypeRef members[6];
+
+		members[0] = StructHeapTupleDataChoice; /* t_heap | t_datum */
+		members[1] = StructItemPointerData; /* t_ctid */
+		members[2] = LLVMInt16Type(); /* t_infomask2 */
+		members[3] = LLVMInt16Type(); /* t_infomask1 */
+		members[4] = LLVMInt8Type(); /* t_hoff */
+		members[5] = LLVMArrayType(LLVMInt8Type(), 0); /* t_bits */
+		/* t_bits and other data follow */
+
+		StructHeapTupleHeaderData = LLVMStructCreateNamed(LLVMGetGlobalContext(),
+														  "struct.HeapTupleHeaderData");
+		LLVMStructSetBody(StructHeapTupleHeaderData, members, lengthof(members), 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 = LLVMStructCreateNamed(LLVMGetGlobalContext(),
+														  "struct.HeapTupleData");
+		LLVMStructSetBody(StructHeapTupleData, members, lengthof(members), false);
+	}
+
+	{
+		StructMinimalTupleData = LLVMStructCreateNamed(LLVMGetGlobalContext(),
+													   "struct.MinimalTupleData");
+	}
+
+
+	{
+		StructFormPgAttribute = LLVMStructCreateNamed(LLVMGetGlobalContext(),
+													  "struct.Form_pg_attribute");
+	}
+
+	{
+		StructTupleConstr = LLVMStructCreateNamed(LLVMGetGlobalContext(),
+												  "struct.TupleConstr");
+	}
+
+	{
+		LLVMTypeRef members[7];
+
+		members[0] = LLVMInt32Type(); /* natts */
+		members[1] = LLVMInt32Type(); /* tdtypeid */
+		members[2] = LLVMInt32Type(); /* tdtypemod */
+		members[3] = LLVMInt8Type(); /* tdhasoid */
+		members[4] = LLVMInt32Type(); /* tsrefcount */
+		members[5] = LLVMPointerType(StructTupleConstr, 0); /* constr */
+		members[6] = LLVMArrayType(LLVMPointerType(StructFormPgAttribute, 0), 0); /* attrs */
+
+		StructtupleDesc = LLVMStructCreateNamed(LLVMGetGlobalContext(),
+												"struct.tupleDesc");
+		LLVMStructSetBody(StructtupleDesc, members, lengthof(members), false);
+	}
+
+	{
+		StructMemoryContextData = LLVMStructCreateNamed(LLVMGetGlobalContext(),
+														"struct.MemoryContext");
+	}
+
+	{
+		TypeMemoryContext = LLVMPointerType(StructMemoryContextData, 0);
+	}
+
+	{
+		LLVMTypeRef members[15];
+
+		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(StructtupleDesc, 0); /* tupleDescriptor */
+		members[ 7] = TypeMemoryContext; /* mcxt */
+		members[ 8] = LLVMInt32Type(); /* buffer */
+		members[ 9] = LLVMInt32Type(); /* nvalid */
+		members[10] = LLVMPointerType(TypeSizeT, 0); /* values */
+		members[11] = LLVMPointerType(LLVMInt8Type(), 0); /* nulls */
+		members[12] = LLVMPointerType(StructMinimalTupleData, 0); /* mintuple */
+		members[13] = StructHeapTupleData; /* minhdr */
+		members[14] = LLVMInt64Type(); /* off: FIXME, deterministic type, not long */
+
+		StructTupleTableSlot = LLVMStructCreateNamed(LLVMGetGlobalContext(),
+													 "struct.TupleTableSlot");
+		LLVMStructSetBody(StructTupleTableSlot, members, lengthof(members), false);
+	}
+
+	{
+		StructFmgrInfo = LLVMStructCreateNamed(LLVMGetGlobalContext(),
+											   "struct.FmgrInfo");
+	}
+
+	{
+		LLVMTypeRef members[8];
+
+		members[0] = LLVMPointerType(StructFmgrInfo, 0); /* flinfo */
+		members[1] = LLVMPointerType(StructPGFinfoRecord, 0); /* context */
+		members[2] = LLVMPointerType(StructPGFinfoRecord, 0); /* resultinfo */
+		members[3] = LLVMInt32Type(); /* fncollation */
+		members[4] = LLVMInt8Type(); /* isnull */
+		members[5] = LLVMInt16Type(); /* nargs */
+		members[6] = LLVMArrayType(TypeSizeT, FUNC_MAX_ARGS);
+		members[7] = LLVMArrayType(LLVMInt8Type(), FUNC_MAX_ARGS);
+
+		StructFunctionCallInfoData = LLVMStructCreateNamed(LLVMGetGlobalContext(),
+														   "struct.FunctionCallInfoData");
+		LLVMStructSetBody(StructFunctionCallInfoData, members, lengthof(members), false);
+	}
+
+	{
+		LLVMTypeRef members[14];
+
+		members[ 0] = LLVMInt32Type(); /* tag */
+		members[ 1] = LLVMInt8Type(); /* flags */
+		members[ 2] = LLVMInt8Type(); /* resnull */
+		members[ 3] = TypeSizeT; /* resvalue */
+		members[ 4] = LLVMPointerType(StructTupleTableSlot, 0); /* resultslot */
+		members[ 5] = LLVMPointerType(TypeSizeT, 0); /* steps */
+		members[ 6] = LLVMPointerType(TypeSizeT, 0); /* evalfunc */
+		members[ 7] = LLVMPointerType(TypeSizeT, 0); /* expr */
+		members[ 8] = TypeSizeT; /* steps_len */
+		members[ 9] = TypeSizeT; /* steps_alloc */
+		members[10] = LLVMPointerType(TypeSizeT, 0); /* innermost caseval */
+		members[11] = LLVMPointerType(LLVMInt8Type(), 0); /* innermost casenull */
+		members[12] = LLVMPointerType(TypeSizeT, 0); /* innermost domainval */
+		members[13] = LLVMPointerType(LLVMInt8Type(), 0); /* innermost domainnull */
+
+		StructExprState = LLVMStructCreateNamed(LLVMGetGlobalContext(),
+												"struct.ExprState");
+		LLVMStructSetBody(StructExprState, members, lengthof(members), false);
+	}
+
+	{
+		LLVMTypeRef members[16];
+
+		members[ 0] = LLVMInt32Type(); /* tag */
+		members[ 1] = LLVMPointerType(StructTupleTableSlot, 0); /* scantuple */
+		members[ 2] = LLVMPointerType(StructTupleTableSlot, 0); /* innertuple */
+		members[ 3] = LLVMPointerType(StructTupleTableSlot, 0); /* outertuple */
+
+		members[ 4] = LLVMPointerType(TypeSizeT, 0); /* per_query_memory */
+		members[ 5] = LLVMPointerType(TypeSizeT, 0); /* per_tuple_memory */
+
+		members[ 6] = LLVMPointerType(TypeSizeT, 0); /* param_exec */
+		members[ 7] = LLVMPointerType(TypeSizeT, 0); /* param_list_info */
+
+		members[ 8] = LLVMPointerType(TypeSizeT, 0); /* aggvalues */
+		members[ 9] = LLVMPointerType(LLVMInt8Type(), 0); /* aggnulls */
+
+		members[10] = TypeSizeT; /* casvalue */
+		members[11] = LLVMInt8Type(); /* casenull */
+
+		members[12] = TypeSizeT; /* domainvalue */
+		members[13] = LLVMInt8Type(); /* domainnull */
+
+		members[14] = LLVMPointerType(TypeSizeT, 0); /* estate */
+		members[15] = LLVMPointerType(TypeSizeT, 0); /* callbacks */
+
+		StructExprContext = LLVMStructCreateNamed(LLVMGetGlobalContext(),
+												  "struct.ExprContext");
+		LLVMStructSetBody(StructExprContext, members, lengthof(members), false);
+	}
+
+	{
+		LLVMTypeRef params[1];
+		params[0] = LLVMPointerType(StructFunctionCallInfoData, 0);
+		TypePGFunction = LLVMFunctionType(TypeSizeT, params, lengthof(params), 0);
+	}
+}
+
+static uint64_t
+llvm_resolve_symbol(const char *name, void *ctx)
+{
+	return (uint64_t) LLVMSearchForAddressOfSymbol(name);
+}
+
+void *
+llvm_get_function(LLVMJitContext *context, const char *funcname)
+{
+	/*
+	 * If there is a pending, not emitted, module, compile and emit
+	 * now. Otherwise we migh not find the [correct] function.
+	 */
+	if (!context->compiled)
+	{
+		int handle;
+		LLVMSharedModuleRef smod = LLVMOrcMakeSharedModule(context->module);
+		MemoryContext oldcontext;
+
+		if (jit_log_ir)
+		{
+			LLVMDumpModule(context->module);
+		}
+
+		if (jit_dump_bitcode)
+		{
+			/* FIXME: invent module rather than function specific name */
+			char *filename = psprintf("%s.bc", funcname);
+			LLVMWriteBitcodeToFile(context->module, filename);
+			pfree(filename);
+		}
+
+
+		/* perform optimization */
+		{
+			LLVMValueRef func;
+			LLVMPassManagerRef llvm_fpm;
+			LLVMPassManagerRef llvm_mpm;
+
+			llvm_fpm = LLVMCreateFunctionPassManagerForModule(context->module);
+			llvm_mpm = LLVMCreatePassManager();
+
+			LLVMPassManagerBuilderPopulateFunctionPassManager(llvm_pmb, llvm_fpm);
+			LLVMPassManagerBuilderPopulateModulePassManager(llvm_pmb, llvm_mpm);
+			LLVMPassManagerBuilderPopulateLTOPassManager(llvm_pmb, llvm_mpm, true, true);
+
+			LLVMAddAnalysisPasses(llvm_targetmachine, llvm_mpm);
+			LLVMAddAnalysisPasses(llvm_targetmachine, llvm_fpm);
+
+			LLVMAddDeadStoreEliminationPass(llvm_fpm);
+
+			/* do function level optimization */
+			LLVMInitializeFunctionPassManager(llvm_fpm);
+			for (func = LLVMGetFirstFunction(context->module);
+				 func != NULL;
+				 func = LLVMGetNextFunction(func))
+				LLVMRunFunctionPassManager(llvm_fpm, func);
+			LLVMFinalizeFunctionPassManager(llvm_fpm);
+
+			/* do module level optimization */
+			LLVMRunPassManager(llvm_mpm, context->module);
+
+			LLVMDisposePassManager(llvm_fpm);
+			LLVMDisposePassManager(llvm_mpm);
+		}
+
+		/* and emit the code */
+		{
+			handle =
+				LLVMOrcAddEagerlyCompiledIR(llvm_orc, smod,
+											llvm_resolve_symbol, NULL);
+
+			oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+			context->handles = lappend_int(context->handles, handle);
+			MemoryContextSwitchTo(oldcontext);
+
+			LLVMOrcDisposeSharedModuleRef(smod);
+
+			ResourceOwnerEnlargeJIT(CurrentResourceOwner);
+			ResourceOwnerRememberJIT(CurrentResourceOwner, PointerGetDatum(context));
+		}
+
+		context->module = NULL;
+		context->compiled = true;
+	}
+
+	/* search all emitted modules for function we're asked for */
+	{
+		void *addr;
+		char *mangled;
+		ListCell *lc;
+
+		LLVMOrcGetMangledSymbol(llvm_orc, &mangled, funcname);
+		foreach(lc, context->handles)
+		{
+			int handle = lfirst_int(lc);
+
+			addr = (void *) LLVMOrcGetSymbolAddressIn(llvm_orc, handle, mangled);
+			if (addr)
+				return addr;
+		}
+	}
+
+	elog(ERROR, "failed to JIT: %s", funcname);
+
+	return NULL;
+}
+
+void
+llvm_release_handle(ResourceOwner resowner, Datum handle)
+{
+	LLVMJitContext *context = (LLVMJitContext *) DatumGetPointer(handle);
+	ListCell *lc;
+
+	foreach(lc, context->handles)
+	{
+		int handle = lfirst_int(lc);
+
+		LLVMOrcRemoveModule(llvm_orc, handle);
+	}
+	list_free(context->handles);
+	context->handles = NIL;
+
+	ResourceOwnerForgetJIT(resowner, handle);
+}
+
+#else  /* USE_LLVM */
+
+void
+llvm_release_handle(ResourceOwner resowner, Datum handle)
+{
+}
+
+#endif
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 246fea8693..2edc0b33c5 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -42,6 +42,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"
@@ -995,6 +996,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/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 4a4a287148..3c89db5003 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -27,6 +27,7 @@
 #include "utils/rel.h"
 #include "utils/resowner_private.h"
 #include "utils/snapmgr.h"
+#include "lib/llvmjit.h"
 
 
 /*
@@ -124,6 +125,7 @@ typedef struct ResourceOwnerData
 	ResourceArray snapshotarr;	/* snapshot references */
 	ResourceArray filearr;		/* open temporary files */
 	ResourceArray dsmarr;		/* dynamic shmem segments */
+	ResourceArray jitarr;		/* JIT handles */
 
 	/* We can remember up to MAX_RESOWNER_LOCKS references to local locks. */
 	int			nlocks;			/* number of owned locks */
@@ -437,6 +439,7 @@ ResourceOwnerCreate(ResourceOwner parent, const char *name)
 	ResourceArrayInit(&(owner->snapshotarr), PointerGetDatum(NULL));
 	ResourceArrayInit(&(owner->filearr), FileGetDatum(-1));
 	ResourceArrayInit(&(owner->dsmarr), PointerGetDatum(NULL));
+	ResourceArrayInit(&(owner->jitarr), Int32GetDatum(-1));
 
 	return owner;
 }
@@ -552,6 +555,21 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 				PrintDSMLeakWarning(res);
 			dsm_detach(res);
 		}
+
+		/* Ditto for jited functions */
+		while (ResourceArrayGetAny(&(owner->jitarr), &foundres))
+		{
+			if (isTopLevel)
+				llvm_release_handle(owner, foundres);
+			else
+			{
+				ResourceOwnerForgetJIT(owner, foundres);
+				ResourceOwnerEnlargeJIT(owner->parent);
+				ResourceOwnerRememberJIT(owner->parent, foundres);
+
+			}
+		}
+
 	}
 	else if (phase == RESOURCE_RELEASE_LOCKS)
 	{
@@ -699,6 +717,7 @@ ResourceOwnerDelete(ResourceOwner owner)
 	Assert(owner->snapshotarr.nitems == 0);
 	Assert(owner->filearr.nitems == 0);
 	Assert(owner->dsmarr.nitems == 0);
+	Assert(owner->jitarr.nitems == 0);
 	Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
 
 	/*
@@ -725,6 +744,7 @@ ResourceOwnerDelete(ResourceOwner owner)
 	ResourceArrayFree(&(owner->snapshotarr));
 	ResourceArrayFree(&(owner->filearr));
 	ResourceArrayFree(&(owner->dsmarr));
+	ResourceArrayFree(&(owner->jitarr));
 
 	pfree(owner);
 }
@@ -1267,3 +1287,23 @@ PrintDSMLeakWarning(dsm_segment *seg)
 	elog(WARNING, "dynamic shared memory leak: segment %u still referenced",
 		 dsm_segment_handle(seg));
 }
+
+void
+ResourceOwnerEnlargeJIT(ResourceOwner owner)
+{
+	ResourceArrayEnlarge(&(owner->jitarr));
+}
+
+void
+ResourceOwnerRememberJIT(ResourceOwner owner, Datum handle)
+{
+	ResourceArrayAdd(&(owner->jitarr), handle);
+}
+
+void
+ResourceOwnerForgetJIT(ResourceOwner owner, Datum handle)
+{
+	if (!ResourceArrayRemove(&(owner->jitarr), handle))
+		elog(ERROR, "jit %lu is not owned by resource owner %s",
+			 handle, owner->name);
+}
diff --git a/src/include/lib/llvmjit.h b/src/include/lib/llvmjit.h
new file mode 100644
index 0000000000..82b0b91c93
--- /dev/null
+++ b/src/include/lib/llvmjit.h
@@ -0,0 +1,83 @@
+#ifndef LLVMJIT_H
+#define LLVMJIT_H
+
+#include "utils/resowner.h"
+
+#ifdef USE_LLVM
+
+/* symbol conflict :( */
+#undef PM
+
+#include "nodes/pg_list.h"
+
+#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>
+#include <llvm-c/IRReader.h>
+#include <llvm-c/BitReader.h>
+#include <llvm-c/Linker.h>
+#include <llvm-c/OrcBindings.h>
+#include <llvm-c/Transforms/PassManagerBuilder.h>
+
+typedef struct LLVMJitContext
+{
+	int counter;
+	LLVMModuleRef module;
+	bool compiled;
+	List *handles;
+} LLVMJitContext;
+
+extern bool jit_log_ir;
+extern bool jit_dump_bitcode;
+
+extern LLVMTargetMachineRef llvm_targetmachine;
+extern const char *llvm_triple;
+
+extern LLVMTypeRef TypeSizeT;
+extern LLVMTypeRef TypePGFunction;
+extern LLVMTypeRef TypeMemoryContext;
+
+extern LLVMTypeRef StructFormPgAttribute;
+extern LLVMTypeRef StructTupleConstr;
+extern LLVMTypeRef StructtupleDesc;
+extern LLVMTypeRef StructHeapTupleFields;
+extern LLVMTypeRef StructHeapTupleFieldsField3;
+extern LLVMTypeRef StructHeapTupleHeaderData;
+extern LLVMTypeRef StructHeapTupleDataChoice;
+extern LLVMTypeRef StructHeapTupleData;
+extern LLVMTypeRef StructMinimalTupleData;
+extern LLVMTypeRef StructItemPointerData;
+extern LLVMTypeRef StructBlockId;
+extern LLVMTypeRef StructTupleTableSlot;
+extern LLVMTypeRef StructMemoryContextData;
+extern LLVMTypeRef StructPGFinfoRecord;
+extern LLVMTypeRef StructFmgrInfo;
+extern LLVMTypeRef StructFunctionCallInfoData;
+extern LLVMTypeRef StructExprState;
+extern LLVMTypeRef StructExprContext;
+
+extern void llvm_initialize(void);
+extern void llvm_dispose_module(LLVMModuleRef mod, const char *funcname);
+
+extern void *llvm_get_function(LLVMJitContext *context, const char *funcname);
+
+extern void llvm_perf_support(LLVMExecutionEngineRef EE);
+extern void llvm_shutdown_perf_support(LLVMExecutionEngineRef EE);
+
+extern void llvm_perf_orc_support(LLVMOrcJITStackRef llvm_orc);
+extern void llvm_shutdown_orc_perf_support(LLVMOrcJITStackRef llvm_orc);
+
+#else
+
+typedef struct LLVMJitContext
+{
+} LLVMJitContext;
+
+#endif /* USE_LLVM */
+
+extern void llvm_release_handle(ResourceOwner resowner, Datum handle);
+
+#endif /* LLVMJIT_H */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 90a60abc4d..0dc9fa8d79 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -508,7 +508,9 @@ typedef struct EState
 	bool	   *es_epqScanDone; /* true if EPQ tuple has been fetched */
 
 	/* The per-query shared memory area to use for parallel execution. */
-	struct dsa_area *es_query_dsa;
+	struct dsa_area   *es_query_dsa;
+
+	struct LLVMJitContext *es_jit;
 } EState;
 
 
diff --git a/src/include/utils/resowner_private.h b/src/include/utils/resowner_private.h
index 2420b651b3..1921e4e666 100644
--- a/src/include/utils/resowner_private.h
+++ b/src/include/utils/resowner_private.h
@@ -88,4 +88,11 @@ extern void ResourceOwnerRememberDSM(ResourceOwner owner,
 extern void ResourceOwnerForgetDSM(ResourceOwner owner,
 					   dsm_segment *);
 
+/* support for JITed functions */
+extern void ResourceOwnerEnlargeJIT(ResourceOwner owner);
+extern void ResourceOwnerRememberJIT(ResourceOwner owner,
+									 Datum handle);
+extern void ResourceOwnerForgetJIT(ResourceOwner owner,
+								   Datum handle);
+
 #endif							/* RESOWNER_PRIVATE_H */
-- 
2.14.1.2.g4274c698f4.dirty

