diff --git a/.gitignore b/.gitignore index a59e3da3be..794e35b73c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Global excludes across all subdirectories *.o *.obj +*.bc *.so *.so.[0-9] *.so.[0-9].[0-9] diff --git a/src/Makefile.global.in b/src/Makefile.global.in index 3bbdf17b74..859adfc3cb 100644 --- a/src/Makefile.global.in +++ b/src/Makefile.global.in @@ -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 $@ $< diff --git a/src/backend/common.mk b/src/backend/common.mk index 5d599dbd0c..6eaa353aea 100644 --- a/src/backend/common.mk +++ b/src/backend/common.mk @@ -46,3 +46,4 @@ clean-local: rm -f $(subsysfilename) $(OBJS) $(call recurse,coverage) +$(call recurse,install) diff --git a/src/backend/jit/jit.c b/src/backend/jit/jit.c index 6c842a0fe9..300b9ff73a 100644 --- a/src/backend/jit/jit.c +++ b/src/backend/jit/jit.c @@ -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; diff --git a/src/backend/jit/llvm/Makefile b/src/backend/jit/llvm/Makefile index 4b58a3450f..63b5e2db36 100644 --- a/src/backend/jit/llvm/Makefile +++ b/src/backend/jit/llvm/Makefile @@ -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 diff --git a/src/backend/jit/llvm/llvmjit.c b/src/backend/jit/llvm/llvmjit.c index 9c57922915..6b07c143b2 100644 --- a/src/backend/jit/llvm/llvmjit.c +++ b/src/backend/jit/llvm/llvmjit.c @@ -19,18 +19,59 @@ #include "utils/memutils.h" #include "utils/resowner_private.h" +#include "portability/instr_time.h" #include "storage/ipc.h" +#include +#include +#include +#include +#include +#include #include +#include +#include +#include + + +/* 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 index 0000000000..90c1d55cba --- /dev/null +++ b/src/backend/jit/llvm/llvmjit_types.c @@ -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 +}; diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index a3e39dc344..10a0ffda28 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -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 diff --git a/src/include/jit/jit.h b/src/include/jit/jit.h index a2f3dd9d4f..35301674c8 100644 --- a/src/include/jit/jit.h +++ b/src/include/jit/jit.h @@ -11,14 +11,34 @@ #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); diff --git a/src/include/jit/llvmjit.h b/src/include/jit/llvmjit.h index 63b2fdfe59..bd201bb7ca 100644 --- a/src/include/jit/llvmjit.h +++ b/src/include/jit/llvmjit.h @@ -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); /* diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b7c0efef8f..63d8c634e2 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1103,6 +1103,9 @@ LDAPURLDesc LDAP_TIMEVAL LINE LLVMJitContext +LLVMJitHandle +LLVMTypeRef +LLVMValueRef LOCALLOCK LOCALLOCKOWNER LOCALLOCKTAG