diff --git a/configure b/configure index 762d77c3ede..c1d176928cb 100755 --- a/configure +++ b/configure @@ -7015,6 +7015,306 @@ if test x"$pgac_cv_prog_CLANGXX_cxxflags__fexcess_precision_standard" = x"yes"; BITCODE_CXXFLAGS="${BITCODE_CXXFLAGS} -fexcess-precision=standard" fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${CLANG} supports -Xclang -no-opaque-pointers, for BITCODE_CFLAGS" >&5 +$as_echo_n "checking whether ${CLANG} supports -Xclang -no-opaque-pointers, for BITCODE_CFLAGS... " >&6; } +if ${pgac_cv_prog_CLANG_cflags__Xclang__no_opaque_pointers+:} false; then : + $as_echo_n "(cached) " >&6 +else + pgac_save_CFLAGS=$CFLAGS +pgac_save_CC=$CC +CC=${CLANG} +CFLAGS="${BITCODE_CFLAGS} -Xclang -no-opaque-pointers" +ac_save_c_werror_flag=$ac_c_werror_flag +ac_c_werror_flag=yes +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + pgac_cv_prog_CLANG_cflags__Xclang__no_opaque_pointers=yes +else + pgac_cv_prog_CLANG_cflags__Xclang__no_opaque_pointers=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_c_werror_flag=$ac_save_c_werror_flag +CFLAGS="$pgac_save_CFLAGS" +CC="$pgac_save_CC" +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_prog_CLANG_cflags__Xclang__no_opaque_pointers" >&5 +$as_echo "$pgac_cv_prog_CLANG_cflags__Xclang__no_opaque_pointers" >&6; } +if test x"$pgac_cv_prog_CLANG_cflags__Xclang__no_opaque_pointers" = x"yes"; then + BITCODE_CFLAGS="${BITCODE_CFLAGS} -Xclang -no-opaque-pointers" +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${CLANGXX} supports -Xclang -no-opaque-pointers, for BITCODE_CXXFLAGS" >&5 +$as_echo_n "checking whether ${CLANGXX} supports -Xclang -no-opaque-pointers, for BITCODE_CXXFLAGS... " >&6; } +if ${pgac_cv_prog_CLANGXX_cxxflags__Xclang__no_opaque_pointers+:} false; then : + $as_echo_n "(cached) " >&6 +else + pgac_save_CXXFLAGS=$CXXFLAGS +pgac_save_CXX=$CXX +CXX=${CLANGXX} +CXXFLAGS="${BITCODE_CXXFLAGS} -Xclang -no-opaque-pointers" +ac_save_cxx_werror_flag=$ac_cxx_werror_flag +ac_cxx_werror_flag=yes +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + pgac_cv_prog_CLANGXX_cxxflags__Xclang__no_opaque_pointers=yes +else + pgac_cv_prog_CLANGXX_cxxflags__Xclang__no_opaque_pointers=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_cxx_werror_flag=$ac_save_cxx_werror_flag +CXXFLAGS="$pgac_save_CXXFLAGS" +CXX="$pgac_save_CXX" +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_prog_CLANGXX_cxxflags__Xclang__no_opaque_pointers" >&5 +$as_echo "$pgac_cv_prog_CLANGXX_cxxflags__Xclang__no_opaque_pointers" >&6; } +if test x"$pgac_cv_prog_CLANGXX_cxxflags__Xclang__no_opaque_pointers" = x"yes"; then + BITCODE_CXXFLAGS="${BITCODE_CXXFLAGS} -Xclang -no-opaque-pointers" +fi + + + NOT_THE_CFLAGS="" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${CLANG} supports -Wunused-command-line-argument, for NOT_THE_CFLAGS" >&5 +$as_echo_n "checking whether ${CLANG} supports -Wunused-command-line-argument, for NOT_THE_CFLAGS... " >&6; } +if ${pgac_cv_prog_CLANG_cflags__Wunused_command_line_argument+:} false; then : + $as_echo_n "(cached) " >&6 +else + pgac_save_CFLAGS=$CFLAGS +pgac_save_CC=$CC +CC=${CLANG} +CFLAGS="${NOT_THE_CFLAGS} -Wunused-command-line-argument" +ac_save_c_werror_flag=$ac_c_werror_flag +ac_c_werror_flag=yes +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + pgac_cv_prog_CLANG_cflags__Wunused_command_line_argument=yes +else + pgac_cv_prog_CLANG_cflags__Wunused_command_line_argument=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_c_werror_flag=$ac_save_c_werror_flag +CFLAGS="$pgac_save_CFLAGS" +CC="$pgac_save_CC" +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_prog_CLANG_cflags__Wunused_command_line_argument" >&5 +$as_echo "$pgac_cv_prog_CLANG_cflags__Wunused_command_line_argument" >&6; } +if test x"$pgac_cv_prog_CLANG_cflags__Wunused_command_line_argument" = x"yes"; then + NOT_THE_CFLAGS="${NOT_THE_CFLAGS} -Wunused-command-line-argument" +fi + + if test -n "$NOT_THE_CFLAGS"; then + BITCODE_CFLAGS="$BITCODE_CFLAGS -Wno-unused-command-line-argument" + fi + NOT_THE_CFLAGS="" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${CLANG} supports -Wcompound-token-split-by-macro, for NOT_THE_CFLAGS" >&5 +$as_echo_n "checking whether ${CLANG} supports -Wcompound-token-split-by-macro, for NOT_THE_CFLAGS... " >&6; } +if ${pgac_cv_prog_CLANG_cflags__Wcompound_token_split_by_macro+:} false; then : + $as_echo_n "(cached) " >&6 +else + pgac_save_CFLAGS=$CFLAGS +pgac_save_CC=$CC +CC=${CLANG} +CFLAGS="${NOT_THE_CFLAGS} -Wcompound-token-split-by-macro" +ac_save_c_werror_flag=$ac_c_werror_flag +ac_c_werror_flag=yes +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + pgac_cv_prog_CLANG_cflags__Wcompound_token_split_by_macro=yes +else + pgac_cv_prog_CLANG_cflags__Wcompound_token_split_by_macro=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_c_werror_flag=$ac_save_c_werror_flag +CFLAGS="$pgac_save_CFLAGS" +CC="$pgac_save_CC" +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_prog_CLANG_cflags__Wcompound_token_split_by_macro" >&5 +$as_echo "$pgac_cv_prog_CLANG_cflags__Wcompound_token_split_by_macro" >&6; } +if test x"$pgac_cv_prog_CLANG_cflags__Wcompound_token_split_by_macro" = x"yes"; then + NOT_THE_CFLAGS="${NOT_THE_CFLAGS} -Wcompound-token-split-by-macro" +fi + + if test -n "$NOT_THE_CFLAGS"; then + BITCODE_CFLAGS="$BITCODE_CFLAGS -Wno-compound-token-split-by-macro" + fi + NOT_THE_CFLAGS="" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${CLANG} supports -Wdeprecated-non-prototype, for NOT_THE_CFLAGS" >&5 +$as_echo_n "checking whether ${CLANG} supports -Wdeprecated-non-prototype, for NOT_THE_CFLAGS... " >&6; } +if ${pgac_cv_prog_CLANG_cflags__Wdeprecated_non_prototype+:} false; then : + $as_echo_n "(cached) " >&6 +else + pgac_save_CFLAGS=$CFLAGS +pgac_save_CC=$CC +CC=${CLANG} +CFLAGS="${NOT_THE_CFLAGS} -Wdeprecated-non-prototype" +ac_save_c_werror_flag=$ac_c_werror_flag +ac_c_werror_flag=yes +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + pgac_cv_prog_CLANG_cflags__Wdeprecated_non_prototype=yes +else + pgac_cv_prog_CLANG_cflags__Wdeprecated_non_prototype=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_c_werror_flag=$ac_save_c_werror_flag +CFLAGS="$pgac_save_CFLAGS" +CC="$pgac_save_CC" +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_prog_CLANG_cflags__Wdeprecated_non_prototype" >&5 +$as_echo "$pgac_cv_prog_CLANG_cflags__Wdeprecated_non_prototype" >&6; } +if test x"$pgac_cv_prog_CLANG_cflags__Wdeprecated_non_prototype" = x"yes"; then + NOT_THE_CFLAGS="${NOT_THE_CFLAGS} -Wdeprecated-non-prototype" +fi + + if test -n "$NOT_THE_CFLAGS"; then + BITCODE_CFLAGS="$BITCODE_CFLAGS -Wno-deprecated-non-prototype" + fi + NOT_THE_CFLAGS="" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${CLANG} supports -Wformat-truncation, for NOT_THE_CFLAGS" >&5 +$as_echo_n "checking whether ${CLANG} supports -Wformat-truncation, for NOT_THE_CFLAGS... " >&6; } +if ${pgac_cv_prog_CLANG_cflags__Wformat_truncation+:} false; then : + $as_echo_n "(cached) " >&6 +else + pgac_save_CFLAGS=$CFLAGS +pgac_save_CC=$CC +CC=${CLANG} +CFLAGS="${NOT_THE_CFLAGS} -Wformat-truncation" +ac_save_c_werror_flag=$ac_c_werror_flag +ac_c_werror_flag=yes +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + pgac_cv_prog_CLANG_cflags__Wformat_truncation=yes +else + pgac_cv_prog_CLANG_cflags__Wformat_truncation=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_c_werror_flag=$ac_save_c_werror_flag +CFLAGS="$pgac_save_CFLAGS" +CC="$pgac_save_CC" +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_prog_CLANG_cflags__Wformat_truncation" >&5 +$as_echo "$pgac_cv_prog_CLANG_cflags__Wformat_truncation" >&6; } +if test x"$pgac_cv_prog_CLANG_cflags__Wformat_truncation" = x"yes"; then + NOT_THE_CFLAGS="${NOT_THE_CFLAGS} -Wformat-truncation" +fi + + if test -n "$NOT_THE_CFLAGS"; then + BITCODE_CFLAGS="$BITCODE_CFLAGS -Wno-format-truncation" + fi + NOT_THE_CFLAGS="" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${CLANG} supports -Wstringop-truncation, for NOT_THE_CFLAGS" >&5 +$as_echo_n "checking whether ${CLANG} supports -Wstringop-truncation, for NOT_THE_CFLAGS... " >&6; } +if ${pgac_cv_prog_CLANG_cflags__Wstringop_truncation+:} false; then : + $as_echo_n "(cached) " >&6 +else + pgac_save_CFLAGS=$CFLAGS +pgac_save_CC=$CC +CC=${CLANG} +CFLAGS="${NOT_THE_CFLAGS} -Wstringop-truncation" +ac_save_c_werror_flag=$ac_c_werror_flag +ac_c_werror_flag=yes +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + pgac_cv_prog_CLANG_cflags__Wstringop_truncation=yes +else + pgac_cv_prog_CLANG_cflags__Wstringop_truncation=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_c_werror_flag=$ac_save_c_werror_flag +CFLAGS="$pgac_save_CFLAGS" +CC="$pgac_save_CC" +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_prog_CLANG_cflags__Wstringop_truncation" >&5 +$as_echo "$pgac_cv_prog_CLANG_cflags__Wstringop_truncation" >&6; } +if test x"$pgac_cv_prog_CLANG_cflags__Wstringop_truncation" = x"yes"; then + NOT_THE_CFLAGS="${NOT_THE_CFLAGS} -Wstringop-truncation" +fi + + if test -n "$NOT_THE_CFLAGS"; then + BITCODE_CFLAGS="$BITCODE_CFLAGS -Wno-stringop-truncation" + fi fi # supply -g if --enable-debug diff --git a/configure.in b/configure.in index 39de633b751..b897445ece6 100644 --- a/configure.in +++ b/configure.in @@ -589,6 +589,35 @@ if test "$with_llvm" = yes ; then PGAC_PROG_VARCXX_VARFLAGS_OPT(CLANGXX, BITCODE_CXXFLAGS, [-fwrapv]) PGAC_PROG_VARCC_VARFLAGS_OPT(CLANG, BITCODE_CFLAGS, [-fexcess-precision=standard]) PGAC_PROG_VARCXX_VARFLAGS_OPT(CLANGXX, BITCODE_CXXFLAGS, [-fexcess-precision=standard]) + + PGAC_PROG_VARCC_VARFLAGS_OPT(CLANG, BITCODE_CFLAGS, [-Xclang -no-opaque-pointers]) + PGAC_PROG_VARCXX_VARFLAGS_OPT(CLANGXX, BITCODE_CXXFLAGS, [-Xclang -no-opaque-pointers]) + + NOT_THE_CFLAGS="" + PGAC_PROG_VARCC_VARFLAGS_OPT(CLANG, NOT_THE_CFLAGS, [-Wunused-command-line-argument]) + if test -n "$NOT_THE_CFLAGS"; then + BITCODE_CFLAGS="$BITCODE_CFLAGS -Wno-unused-command-line-argument" + fi + NOT_THE_CFLAGS="" + PGAC_PROG_VARCC_VARFLAGS_OPT(CLANG, NOT_THE_CFLAGS, [-Wcompound-token-split-by-macro]) + if test -n "$NOT_THE_CFLAGS"; then + BITCODE_CFLAGS="$BITCODE_CFLAGS -Wno-compound-token-split-by-macro" + fi + NOT_THE_CFLAGS="" + PGAC_PROG_VARCC_VARFLAGS_OPT(CLANG, NOT_THE_CFLAGS, [-Wdeprecated-non-prototype]) + if test -n "$NOT_THE_CFLAGS"; then + BITCODE_CFLAGS="$BITCODE_CFLAGS -Wno-deprecated-non-prototype" + fi + NOT_THE_CFLAGS="" + PGAC_PROG_VARCC_VARFLAGS_OPT(CLANG, NOT_THE_CFLAGS, [-Wformat-truncation]) + if test -n "$NOT_THE_CFLAGS"; then + BITCODE_CFLAGS="$BITCODE_CFLAGS -Wno-format-truncation" + fi + NOT_THE_CFLAGS="" + PGAC_PROG_VARCC_VARFLAGS_OPT(CLANG, NOT_THE_CFLAGS, [-Wstringop-truncation]) + if test -n "$NOT_THE_CFLAGS"; then + BITCODE_CFLAGS="$BITCODE_CFLAGS -Wno-stringop-truncation" + fi fi # supply -g if --enable-debug diff --git a/doc/src/sgml/parallel.sgml b/doc/src/sgml/parallel.sgml index 6c30916655d..8f98b71ffff 100644 --- a/doc/src/sgml/parallel.sgml +++ b/doc/src/sgml/parallel.sgml @@ -136,7 +136,7 @@ EXPLAIN SELECT * FROM pgbench_accounts WHERE filler LIKE '%x%'; In addition, the system must not be running in single-user mode. Since - the entire database system is running in single process in this situation, + the entire database system is running as a single process in this situation, no background workers will be available. diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml index 5c4685addd7..09bb3944004 100644 --- a/doc/src/sgml/ref/alter_table.sgml +++ b/doc/src/sgml/ref/alter_table.sgml @@ -899,7 +899,7 @@ WITH ( MODULUS numeric_literal, REM constraint. This does not work, however, if any of the partition keys is an expression and the partition does not accept NULL values. If attaching a list partition that will - not accept NULL values, also add + not accept NULL values, also add a NOT NULL constraint to the partition key column, unless it's an expression. diff --git a/doc/src/sgml/sources.sgml b/doc/src/sgml/sources.sgml index 1a3d905ab7e..7d1d16efd08 100644 --- a/doc/src/sgml/sources.sgml +++ b/doc/src/sgml/sources.sgml @@ -886,9 +886,9 @@ BETTER: unrecognized node type: 42 Function-Like Macros and Inline Functions - Both, macros with arguments and static inline - functions, may be used. The latter are preferable if there are - multiple-evaluation hazards when written as a macro, as e.g. the + Both macros with arguments and static inline + functions may be used. The latter are preferable if there are + multiple-evaluation hazards when written as a macro, as e.g., the case with #define Max(x, y) ((x) > (y) ? (x) : (y)) diff --git a/src/backend/access/common/bufmask.c b/src/backend/access/common/bufmask.c index 57021f6ca1a..5bc29991311 100644 --- a/src/backend/access/common/bufmask.c +++ b/src/backend/access/common/bufmask.c @@ -78,7 +78,7 @@ mask_unused_space(Page page) if (pd_lower > pd_upper || pd_special < pd_upper || pd_lower < SizeOfPageHeaderData || pd_special > BLCKSZ) { - elog(ERROR, "invalid page pd_lower %u pd_upper %u pd_special %u\n", + elog(ERROR, "invalid page pd_lower %u pd_upper %u pd_special %u", pd_lower, pd_upper, pd_special); } diff --git a/src/backend/jit/README b/src/backend/jit/README index e2fac8558e8..5427bdf2153 100644 --- a/src/backend/jit/README +++ b/src/backend/jit/README @@ -10,11 +10,11 @@ SQL expressions to evaluate an SQL predicate like WHERE a.col = 3, it is possible to generate a function than can be natively executed by the CPU that just handles that expression, yielding a speedup. -That this is done at query execution time, possibly even only in cases -where the relevant task is done a number of times, makes it JIT, -rather than ahead-of-time (AOT). Given the way JIT compilation is used -in PostgreSQL, the lines between interpretation, AOT and JIT are -somewhat blurry. +This is JIT, rather than ahead-of-time (AOT) compilation, because it +is done at query execution time, and perhaps only in cases where the +relevant task is repeated a number of times. Given the way JIT +compilation is used in PostgreSQL, the lines between interpretation, +AOT and JIT are somewhat blurry. Note that the interpreted program turned into a native program does not necessarily have to be a program in the classical sense. E.g. it @@ -99,7 +99,7 @@ Lifetimes of JITed functions are managed via JITContext. Exactly one such context should be created for work in which all created JITed function should have the same lifetime. E.g. there's exactly one JITContext for each query executed, in the query's EState. Only the -release of an JITContext is exposed to the provider independent +release of a JITContext is exposed to the provider independent facility, as the creation of one is done on-demand by the JIT implementations. @@ -231,7 +231,7 @@ needs to be referenced as an offset to one block of memory stored in an ExprState, rather than absolute pointers into memory. Once that is addressed, adding an LRU cache that's keyed by the -generated LLVM IR will allow to use optimized functions even for +generated LLVM IR will allow the usage of optimized functions even for faster queries. A longer term project is to move expression compilation to the planner diff --git a/src/backend/jit/llvm/Makefile b/src/backend/jit/llvm/Makefile index e2db4cea65c..0d22279578c 100644 --- a/src/backend/jit/llvm/Makefile +++ b/src/backend/jit/llvm/Makefile @@ -22,6 +22,12 @@ endif PGFILEDESC = "llvmjit - JIT using LLVM" NAME = llvmjit +# LLVM 14 produces deprecation warnings. We'll need to make some changes +# before the relevant functions are removed, but for now silence the warnings. +ifeq ($(GCC), yes) +LLVM_CFLAGS += -Wno-deprecated-declarations +endif + # All files in this directy use LLVM. CFLAGS += $(LLVM_CFLAGS) CXXFLAGS += $(LLVM_CXXFLAGS) diff --git a/src/backend/jit/llvm/llvmjit.c b/src/backend/jit/llvm/llvmjit.c index 7510698f863..dc3efd27460 100644 --- a/src/backend/jit/llvm/llvmjit.c +++ b/src/backend/jit/llvm/llvmjit.c @@ -29,7 +29,13 @@ #include #include #include +#if LLVM_VERSION_MAJOR > 11 +#include +#include +#include +#else #include +#endif #include #include #include @@ -43,8 +49,13 @@ /* Handle of a module emitted via ORC JIT */ typedef struct LLVMJitHandle { +#if LLVM_VERSION_MAJOR > 11 + LLVMOrcLLJITRef lljit; + LLVMOrcResourceTrackerRef resource_tracker; +#else LLVMOrcJITStackRef stack; LLVMOrcModuleHandle orc_handle; +#endif } LLVMJitHandle; @@ -94,12 +105,15 @@ 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; +#if LLVM_VERSION_MAJOR > 11 +static LLVMOrcThreadSafeContextRef llvm_ts_context; +static LLVMOrcLLJITRef llvm_opt0_orc; +static LLVMOrcLLJITRef llvm_opt3_orc; +#else /* LLVM_VERSION_MAJOR > 11 */ static LLVMOrcJITStackRef llvm_opt0_orc; static LLVMOrcJITStackRef llvm_opt3_orc; +#endif /* LLVM_VERSION_MAJOR > 11 */ static void llvm_release_context(JitContext *context); @@ -111,6 +125,10 @@ 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); +#if LLVM_VERSION_MAJOR > 11 +static LLVMOrcLLJITRef llvm_create_jit_instance(LLVMTargetMachineRef tm); +static char *llvm_error_message(LLVMErrorRef error); +#endif /* LLVM_VERSION_MAJOR > 11 */ PG_MODULE_MAGIC; @@ -163,32 +181,57 @@ 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 (proc_exit_inprogress) + return; + + llvm_enter_fatal_on_oom(); + + if (llvm_context->module) { - if (llvm_context->module) - { - LLVMDisposeModule(llvm_context->module); - llvm_context->module = NULL; - } + LLVMDisposeModule(llvm_context->module); + llvm_context->module = NULL; + } - while (llvm_context->handles != NIL) - { - LLVMJitHandle *jit_handle; + while (llvm_context->handles != NIL) + { + LLVMJitHandle *jit_handle; - jit_handle = (LLVMJitHandle *) linitial(llvm_context->handles); - llvm_context->handles = list_delete_first(llvm_context->handles); + jit_handle = (LLVMJitHandle *) linitial(llvm_context->handles); + llvm_context->handles = list_delete_first(llvm_context->handles); +#if LLVM_VERSION_MAJOR > 11 + { + LLVMOrcExecutionSessionRef ee; + LLVMOrcSymbolStringPoolRef sp; + + LLVMOrcResourceTrackerRemove(jit_handle->resource_tracker); + LLVMOrcReleaseResourceTracker(jit_handle->resource_tracker); + + /* + * Without triggering cleanup of the string pool, we'd leak + * memory. It'd be sufficient to do this far less often, but in + * experiments the required time was small enough to just always + * do it. + */ + ee = LLVMOrcLLJITGetExecutionSession(jit_handle->lljit); + sp = LLVMOrcExecutionSessionGetSymbolStringPool(ee); + LLVMOrcSymbolStringPoolClearDeadEntries(sp); + } +#else /* LLVM_VERSION_MAJOR > 11 */ + { LLVMOrcRemoveModule(jit_handle->stack, jit_handle->orc_handle); - pfree(jit_handle); } +#endif /* LLVM_VERSION_MAJOR > 11 */ + + pfree(jit_handle); } + + llvm_leave_fatal_on_oom(); } /* @@ -243,8 +286,8 @@ llvm_expand_funcname(struct LLVMJitContext *context, const char *basename) void * llvm_get_function(LLVMJitContext *context, const char *funcname) { - LLVMOrcTargetAddress addr = 0; -#if defined(HAVE_DECL_LLVMORCGETSYMBOLADDRESSIN) && HAVE_DECL_LLVMORCGETSYMBOLADDRESSIN +#if LLVM_VERSION_MAJOR > 11 || \ + defined(HAVE_DECL_LLVMORCGETSYMBOLADDRESSIN) && HAVE_DECL_LLVMORCGETSYMBOLADDRESSIN ListCell *lc; #endif @@ -264,10 +307,41 @@ llvm_get_function(LLVMJitContext *context, const char *funcname) * to mangle here. */ -#if defined(HAVE_DECL_LLVMORCGETSYMBOLADDRESSIN) && HAVE_DECL_LLVMORCGETSYMBOLADDRESSIN +#if LLVM_VERSION_MAJOR > 11 foreach(lc, context->handles) { LLVMJitHandle *handle = (LLVMJitHandle *) lfirst(lc); + instr_time starttime; + instr_time endtime; + LLVMErrorRef error; + LLVMOrcJITTargetAddress addr; + + INSTR_TIME_SET_CURRENT(starttime); + + addr = 0; + error = LLVMOrcLLJITLookup(handle->lljit, &addr, funcname); + if (error) + elog(ERROR, "failed to look up symbol \"%s\": %s", + funcname, llvm_error_message(error)); + + /* + * LLJIT only actually emits code the first time a symbol is + * referenced. Thus add lookup time to emission time. That's counting + * a bit more than with older LLVM versions, but unlikely to ever + * matter. + */ + INSTR_TIME_SET_CURRENT(endtime); + INSTR_TIME_ACCUM_DIFF(context->base.instr.emission_counter, + endtime, starttime); + + if (addr) + return (void *) (uintptr_t) addr; + } +#elif defined(HAVE_DECL_LLVMORCGETSYMBOLADDRESSIN) && HAVE_DECL_LLVMORCGETSYMBOLADDRESSIN + foreach(lc, context->handles) + { + LLVMOrcTargetAddress addr; + LLVMJitHandle *handle = (LLVMJitHandle *) lfirst(lc); addr = 0; if (LLVMOrcGetSymbolAddressIn(handle->stack, &addr, handle->orc_handle, funcname)) @@ -275,26 +349,29 @@ llvm_get_function(LLVMJitContext *context, const char *funcname) if (addr) return (void *) (uintptr_t) addr; } +#elif LLVM_VERSION_MAJOR < 5 + { + LLVMOrcTargetAddress addr; + 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 + { + LLVMOrcTargetAddress addr; -#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 look up symbol \"%s\"", funcname); - if (addr) - return (void *) (uintptr_t) addr; - if (LLVMOrcGetSymbolAddress(llvm_opt3_orc, &addr, funcname)) - elog(ERROR, "failed to look up symbol \"%s\"", funcname); - if (addr) - return (void *) (uintptr_t) addr; -#endif /* LLVM_VERSION_MAJOR */ - -#endif /* HAVE_DECL_LLVMORCGETSYMBOLADDRESSIN */ + if (LLVMOrcGetSymbolAddress(llvm_opt0_orc, &addr, funcname)) + elog(ERROR, "failed to look up symbol \"%s\"", funcname); + if (addr) + return (void *) (uintptr_t) addr; + if (LLVMOrcGetSymbolAddress(llvm_opt3_orc, &addr, funcname)) + elog(ERROR, "failed to look up symbol \"%s\"", funcname); + if (addr) + return (void *) (uintptr_t) addr; + } +#endif elog(ERROR, "failed to JIT: %s", funcname); @@ -327,26 +404,55 @@ llvm_get_decl(LLVMModuleRef mod, LLVMValueRef v_src) } /* - * Copy attributes from one function to another. + * Copy attributes from one function to another, for a specific index (an + * index can reference return value, function and parameter attributes). */ -void -llvm_copy_attributes(LLVMValueRef v_from, LLVMValueRef v_to) +static void +llvm_copy_attributes_at_index(LLVMValueRef v_from, LLVMValueRef v_to, uint32 index) { int num_attributes; - int attno; LLVMAttributeRef *attrs; + int attno; - num_attributes = - LLVMGetAttributeCountAtIndex(v_from, LLVMAttributeFunctionIndex); + num_attributes = LLVMGetAttributeCountAtIndexPG(v_from, index); + + /* + * Not just for efficiency: LLVM <= 3.9 crashes when + * LLVMGetAttributesAtIndex() is called for an index with 0 attributes. + */ + if (num_attributes == 0) + return; attrs = palloc(sizeof(LLVMAttributeRef) * num_attributes); - LLVMGetAttributesAtIndex(v_from, LLVMAttributeFunctionIndex, attrs); + LLVMGetAttributesAtIndex(v_from, index, attrs); for (attno = 0; attno < num_attributes; attno++) - { - LLVMAddAttributeAtIndex(v_to, LLVMAttributeFunctionIndex, - attrs[attno]); - } + LLVMAddAttributeAtIndex(v_to, index, attrs[attno]); + + pfree(attrs); +} + +/* + * Copy all attributes from one function to another. I.e. function, return and + * parameters will be copied. + */ +void +llvm_copy_attributes(LLVMValueRef v_from, LLVMValueRef v_to) +{ + uint32 param_count; + int paramidx; + + /* copy function attributes */ + llvm_copy_attributes_at_index(v_from, v_to, LLVMAttributeFunctionIndex); + + /* and the return value attributes */ + llvm_copy_attributes_at_index(v_from, v_to, LLVMAttributeReturnIndex); + + /* and each function parameter's attribute */ + param_count = LLVMCountParams(v_from); + + for (paramidx = 1; paramidx <= param_count; paramidx++) + llvm_copy_attributes_at_index(v_from, v_to, paramidx); } /* @@ -396,6 +502,8 @@ llvm_function_reference(LLVMJitContext *context, v_fn = LLVMAddGlobal(mod, TypePGFunction, funcname); LLVMSetInitializer(v_fn, v_fn_addr); LLVMSetGlobalConstant(v_fn, true); + LLVMSetLinkage(v_fn, LLVMPrivateLinkage); + LLVMSetUnnamedAddr(v_fn, true); return LLVMBuildLoad(builder, v_fn, ""); } @@ -487,11 +595,15 @@ llvm_optimize_module(LLVMJitContext *context, LLVMModuleRef module) static void llvm_compile_module(LLVMJitContext *context) { - LLVMOrcModuleHandle orc_handle; + LLVMJitHandle *handle; MemoryContext oldcontext; - static LLVMOrcJITStackRef compile_orc; instr_time starttime; instr_time endtime; +#if LLVM_VERSION_MAJOR > 11 + LLVMOrcLLJITRef compile_orc; +#else + LLVMOrcJITStackRef compile_orc; +#endif if (context->base.flags & PGJIT_OPT3) compile_orc = llvm_opt3_orc; @@ -538,6 +650,9 @@ llvm_compile_module(LLVMJitContext *context) pfree(filename); } + handle = (LLVMJitHandle *) + MemoryContextAlloc(TopMemoryContext, sizeof(LLVMJitHandle)); + /* * Emit the code. Note that this can, depending on the optimization * settings, take noticeable resources as code emission executes low-level @@ -545,13 +660,42 @@ llvm_compile_module(LLVMJitContext *context) * faster instruction selection mechanism is used. */ INSTR_TIME_SET_CURRENT(starttime); -#if LLVM_VERSION_MAJOR > 6 +#if LLVM_VERSION_MAJOR > 11 + { + LLVMOrcThreadSafeModuleRef ts_module; + LLVMErrorRef error; + LLVMOrcJITDylibRef jd = LLVMOrcLLJITGetMainJITDylib(compile_orc); + + ts_module = LLVMOrcCreateNewThreadSafeModule(context->module, llvm_ts_context); + + handle->lljit = compile_orc; + handle->resource_tracker = LLVMOrcJITDylibCreateResourceTracker(jd); + + /* + * NB: This doesn't actually emit code. That happens lazily the first + * time a symbol defined in the module is requested. Due to that + * llvm_get_function() also accounts for emission time. + */ + + context->module = NULL; /* will be owned by LLJIT */ + error = LLVMOrcLLJITAddLLVMIRModuleWithRT(compile_orc, + handle->resource_tracker, + ts_module); + + if (error) + elog(ERROR, "failed to JIT module: %s", + llvm_error_message(error)); + + handle->lljit = compile_orc; + + /* LLVMOrcLLJITAddLLVMIRModuleWithRT takes ownership of the module */ + } +#elif LLVM_VERSION_MAJOR > 6 { - if (LLVMOrcAddEagerlyCompiledIR(compile_orc, &orc_handle, context->module, + handle->stack = compile_orc; + if (LLVMOrcAddEagerlyCompiledIR(compile_orc, &handle->orc_handle, context->module, llvm_resolve_symbol, NULL)) - { elog(ERROR, "failed to JIT module"); - } /* LLVMOrcAddEagerlyCompiledIR takes ownership of the module */ } @@ -560,20 +704,23 @@ llvm_compile_module(LLVMJitContext *context) LLVMSharedModuleRef smod; smod = LLVMOrcMakeSharedModule(context->module); - if (LLVMOrcAddEagerlyCompiledIR(compile_orc, &orc_handle, smod, + handle->stack = compile_orc; + if (LLVMOrcAddEagerlyCompiledIR(compile_orc, &handle->orc_handle, smod, llvm_resolve_symbol, NULL)) - { elog(ERROR, "failed to JIT module"); - } + LLVMOrcDisposeSharedModuleRef(smod); } #else /* LLVM 4.0 and 3.9 */ { - orc_handle = LLVMOrcAddEagerlyCompiledIR(compile_orc, context->module, - llvm_resolve_symbol, NULL); + handle->stack = compile_orc; + handle->orc_handle = LLVMOrcAddEagerlyCompiledIR(compile_orc, context->module, + llvm_resolve_symbol, NULL); + LLVMDisposeModule(context->module); } #endif + INSTR_TIME_SET_CURRENT(endtime); INSTR_TIME_ACCUM_DIFF(context->base.instr.emission_counter, endtime, starttime); @@ -583,15 +730,7 @@ llvm_compile_module(LLVMJitContext *context) /* 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); - } + context->handles = lappend(context->handles, handle); MemoryContextSwitchTo(oldcontext); ereport(DEBUG1, @@ -613,6 +752,8 @@ llvm_session_initialize(void) char *error = NULL; char *cpu = NULL; char *features = NULL; + LLVMTargetMachineRef opt0_tm; + LLVMTargetMachineRef opt3_tm; if (llvm_session_initialized) return; @@ -623,6 +764,16 @@ llvm_session_initialize(void) LLVMInitializeNativeAsmPrinter(); LLVMInitializeNativeAsmParser(); + /* + * When targeting an LLVM version with opaque pointers enabled by + * default, turn them off for the context we build our code in. We don't + * need to do so for other contexts (e.g. llvm_ts_context). Once the IR is + * generated, it carries the necessary information. + */ +#if LLVM_VERSION_MAJOR > 14 + LLVMContextSetOpaquePointers(LLVMGetGlobalContext(), false); +#endif + /* * Synchronize types early, as that also includes inferring the target * triple. @@ -631,7 +782,7 @@ llvm_session_initialize(void) if (LLVMGetTargetFromTriple(llvm_triple, &llvm_targetref, &error) != 0) { - elog(FATAL, "failed to query triple %s\n", error); + elog(FATAL, "failed to query triple %s", error); } /* @@ -645,12 +796,12 @@ llvm_session_initialize(void) elog(DEBUG2, "LLVMJIT detected CPU \"%s\", with features \"%s\"", cpu, features); - llvm_opt0_targetmachine = + opt0_tm = LLVMCreateTargetMachine(llvm_targetref, llvm_triple, cpu, features, LLVMCodeGenLevelNone, LLVMRelocDefault, LLVMCodeModelJITDefault); - llvm_opt3_targetmachine = + opt3_tm = LLVMCreateTargetMachine(llvm_targetref, llvm_triple, cpu, features, LLVMCodeGenLevelAggressive, LLVMRelocDefault, @@ -664,27 +815,41 @@ llvm_session_initialize(void) /* force symbols in main binary to be loaded */ LLVMLoadLibraryPermanently(NULL); - llvm_opt0_orc = LLVMOrcCreateInstance(llvm_opt0_targetmachine); - llvm_opt3_orc = LLVMOrcCreateInstance(llvm_opt3_targetmachine); - -#if defined(HAVE_DECL_LLVMCREATEGDBREGISTRATIONLISTENER) && HAVE_DECL_LLVMCREATEGDBREGISTRATIONLISTENER - if (jit_debugging_support) +#if LLVM_VERSION_MAJOR > 11 { - LLVMJITEventListenerRef l = LLVMCreateGDBRegistrationListener(); + llvm_ts_context = LLVMOrcCreateNewThreadSafeContext(); - LLVMOrcRegisterJITEventListener(llvm_opt0_orc, l); - LLVMOrcRegisterJITEventListener(llvm_opt3_orc, l); + llvm_opt0_orc = llvm_create_jit_instance(opt0_tm); + opt0_tm = 0; + + llvm_opt3_orc = llvm_create_jit_instance(opt3_tm); + opt3_tm = 0; } +#else /* LLVM_VERSION_MAJOR > 11 */ + { + llvm_opt0_orc = LLVMOrcCreateInstance(opt0_tm); + llvm_opt3_orc = LLVMOrcCreateInstance(opt3_tm); + +#if defined(HAVE_DECL_LLVMCREATEGDBREGISTRATIONLISTENER) && HAVE_DECL_LLVMCREATEGDBREGISTRATIONLISTENER + if (jit_debugging_support) + { + LLVMJITEventListenerRef l = LLVMCreateGDBRegistrationListener(); + + LLVMOrcRegisterJITEventListener(llvm_opt0_orc, l); + LLVMOrcRegisterJITEventListener(llvm_opt3_orc, l); + } #endif #if defined(HAVE_DECL_LLVMCREATEPERFJITEVENTLISTENER) && HAVE_DECL_LLVMCREATEPERFJITEVENTLISTENER - if (jit_profiling_support) - { - LLVMJITEventListenerRef l = LLVMCreatePerfJITEventListener(); + if (jit_profiling_support) + { + LLVMJITEventListenerRef l = LLVMCreatePerfJITEventListener(); - LLVMOrcRegisterJITEventListener(llvm_opt0_orc, l); - LLVMOrcRegisterJITEventListener(llvm_opt3_orc, l); - } + LLVMOrcRegisterJITEventListener(llvm_opt0_orc, l); + LLVMOrcRegisterJITEventListener(llvm_opt3_orc, l); + } #endif + } +#endif /* LLVM_VERSION_MAJOR > 11 */ before_shmem_exit(llvm_shutdown, 0); @@ -696,27 +861,63 @@ llvm_session_initialize(void) static void llvm_shutdown(int code, Datum arg) { - /* unregister profiling support, needs to be flushed to be useful */ + /* + * If llvm_shutdown() is reached while in a fatal-on-oom section an error + * has occurred in the middle of LLVM code. It is not safe to call back + * into LLVM (which is why a FATAL error was thrown). + * + * We do need to shutdown LLVM in other shutdown cases, otherwise + * e.g. profiling data won't be written out. + */ + if (llvm_in_fatal_on_oom()) + { + Assert(proc_exit_inprogress); + return; + } - if (llvm_opt3_orc) +#if LLVM_VERSION_MAJOR > 11 + { + if (llvm_opt3_orc) + { + LLVMOrcDisposeLLJIT(llvm_opt3_orc); + llvm_opt3_orc = NULL; + } + if (llvm_opt0_orc) + { + LLVMOrcDisposeLLJIT(llvm_opt0_orc); + llvm_opt0_orc = NULL; + } + if (llvm_ts_context) + { + LLVMOrcDisposeThreadSafeContext(llvm_ts_context); + llvm_ts_context = NULL; + } + } +#else /* LLVM_VERSION_MAJOR > 11 */ { + /* unregister profiling support, needs to be flushed to be useful */ + + if (llvm_opt3_orc) + { #if defined(HAVE_DECL_LLVMORCREGISTERPERF) && HAVE_DECL_LLVMORCREGISTERPERF - if (jit_profiling_support) - LLVMOrcUnregisterPerf(llvm_opt3_orc); + if (jit_profiling_support) + LLVMOrcUnregisterPerf(llvm_opt3_orc); #endif - LLVMOrcDisposeInstance(llvm_opt3_orc); - llvm_opt3_orc = NULL; - } + LLVMOrcDisposeInstance(llvm_opt3_orc); + llvm_opt3_orc = NULL; + } - if (llvm_opt0_orc) - { + if (llvm_opt0_orc) + { #if defined(HAVE_DECL_LLVMORCREGISTERPERF) && HAVE_DECL_LLVMORCREGISTERPERF - if (jit_profiling_support) - LLVMOrcUnregisterPerf(llvm_opt0_orc); + if (jit_profiling_support) + LLVMOrcUnregisterPerf(llvm_opt0_orc); #endif - LLVMOrcDisposeInstance(llvm_opt0_orc); - llvm_opt0_orc = NULL; + LLVMOrcDisposeInstance(llvm_opt0_orc); + llvm_opt0_orc = NULL; + } } +#endif /* LLVM_VERSION_MAJOR > 11 */ } /* helper for llvm_create_types, returning a global var's type */ @@ -912,3 +1113,156 @@ llvm_resolve_symbol(const char *symname, void *ctx) return (uint64_t) addr; } + +#if LLVM_VERSION_MAJOR > 11 + +static LLVMErrorRef +llvm_resolve_symbols(LLVMOrcDefinitionGeneratorRef GeneratorObj, void *Ctx, + LLVMOrcLookupStateRef *LookupState, LLVMOrcLookupKind Kind, + LLVMOrcJITDylibRef JD, LLVMOrcJITDylibLookupFlags JDLookupFlags, + LLVMOrcCLookupSet LookupSet, size_t LookupSetSize) +{ +#if LLVM_VERSION_MAJOR > 14 + LLVMOrcCSymbolMapPairs symbols = palloc0(sizeof(LLVMOrcCSymbolMapPair) * LookupSetSize); +#else + LLVMOrcCSymbolMapPairs symbols = palloc0(sizeof(LLVMJITCSymbolMapPair) * LookupSetSize); +#endif + LLVMErrorRef error; + LLVMOrcMaterializationUnitRef mu; + + for (int i = 0; i < LookupSetSize; i++) + { + const char *name = LLVMOrcSymbolStringPoolEntryStr(LookupSet[i].Name); + +#if LLVM_VERSION_MAJOR > 12 + LLVMOrcRetainSymbolStringPoolEntry(LookupSet[i].Name); +#endif + symbols[i].Name = LookupSet[i].Name; + symbols[i].Sym.Address = llvm_resolve_symbol(name, NULL); + symbols[i].Sym.Flags.GenericFlags = LLVMJITSymbolGenericFlagsExported; + } + + mu = LLVMOrcAbsoluteSymbols(symbols, LookupSetSize); + error = LLVMOrcJITDylibDefine(JD, mu); + if (error != LLVMErrorSuccess) + LLVMOrcDisposeMaterializationUnit(mu); + + pfree(symbols); + + return error; +} + +/* + * We cannot throw errors through LLVM (without causing a FATAL at least), so + * just use WARNING here. That's OK anyway, as the error is also reported at + * the top level action (with less detail) and there might be multiple + * invocations of errors with details. + * + * This doesn't really happen during normal operation, but in cases like + * symbol resolution breakage. So just using elog(WARNING) is fine. + */ +static void +llvm_log_jit_error(void *ctx, LLVMErrorRef error) +{ + elog(WARNING, "error during JITing: %s", + llvm_error_message(error)); +} + +/* + * Create our own object layer, so we can add event listeners. + */ +static LLVMOrcObjectLayerRef +llvm_create_object_layer(void *Ctx, LLVMOrcExecutionSessionRef ES, const char *Triple) +{ + LLVMOrcObjectLayerRef objlayer = + LLVMOrcCreateRTDyldObjectLinkingLayerWithSectionMemoryManager(ES); + +#if defined(HAVE_DECL_LLVMCREATEGDBREGISTRATIONLISTENER) && HAVE_DECL_LLVMCREATEGDBREGISTRATIONLISTENER + if (jit_debugging_support) + { + LLVMJITEventListenerRef l = LLVMCreateGDBRegistrationListener(); + + LLVMOrcRTDyldObjectLinkingLayerRegisterJITEventListener(objlayer, l); + } +#endif + +#if defined(HAVE_DECL_LLVMCREATEPERFJITEVENTLISTENER) && HAVE_DECL_LLVMCREATEPERFJITEVENTLISTENER + if (jit_profiling_support) + { + LLVMJITEventListenerRef l = LLVMCreatePerfJITEventListener(); + + LLVMOrcRTDyldObjectLinkingLayerRegisterJITEventListener(objlayer, l); + } +#endif + + return objlayer; +} + +/* + * Create LLJIT instance, using the passed in target machine. Note that the + * target machine afterwards is owned by the LLJIT instance. + */ +static LLVMOrcLLJITRef +llvm_create_jit_instance(LLVMTargetMachineRef tm) +{ + LLVMOrcLLJITRef lljit; + LLVMOrcJITTargetMachineBuilderRef tm_builder; + LLVMOrcLLJITBuilderRef lljit_builder; + LLVMErrorRef error; + LLVMOrcDefinitionGeneratorRef main_gen; + LLVMOrcDefinitionGeneratorRef ref_gen; + + lljit_builder = LLVMOrcCreateLLJITBuilder(); + tm_builder = LLVMOrcJITTargetMachineBuilderCreateFromTargetMachine(tm); + LLVMOrcLLJITBuilderSetJITTargetMachineBuilder(lljit_builder, tm_builder); + + LLVMOrcLLJITBuilderSetObjectLinkingLayerCreator(lljit_builder, + llvm_create_object_layer, + NULL); + + error = LLVMOrcCreateLLJIT(&lljit, lljit_builder); + if (error) + elog(ERROR, "failed to create lljit instance: %s", + llvm_error_message(error)); + + LLVMOrcExecutionSessionSetErrorReporter(LLVMOrcLLJITGetExecutionSession(lljit), + llvm_log_jit_error, NULL); + + /* + * Symbol resolution support for symbols in the postgres binary / + * libraries already loaded. + */ + error = LLVMOrcCreateDynamicLibrarySearchGeneratorForProcess(&main_gen, + LLVMOrcLLJITGetGlobalPrefix(lljit), + 0, NULL); + if (error) + elog(ERROR, "failed to create generator: %s", + llvm_error_message(error)); + LLVMOrcJITDylibAddGenerator(LLVMOrcLLJITGetMainJITDylib(lljit), main_gen); + + /* + * Symbol resolution support for "special" functions, e.g. a call into an + * SQL callable function. + */ +#if LLVM_VERSION_MAJOR > 14 + ref_gen = LLVMOrcCreateCustomCAPIDefinitionGenerator(llvm_resolve_symbols, NULL, NULL); +#else + ref_gen = LLVMOrcCreateCustomCAPIDefinitionGenerator(llvm_resolve_symbols, NULL); +#endif + LLVMOrcJITDylibAddGenerator(LLVMOrcLLJITGetMainJITDylib(lljit), ref_gen); + + return lljit; +} + +static char * +llvm_error_message(LLVMErrorRef error) +{ + char *orig = LLVMGetErrorMessage(error); + char *msg = pstrdup(orig); + + LLVMDisposeErrorMessage(orig); + + return msg; +} + +#endif /* LLVM_VERSION_MAJOR > 11 */ diff --git a/src/backend/jit/llvm/llvmjit_error.cpp b/src/backend/jit/llvm/llvmjit_error.cpp index baabfc4eb4c..b9bdb8576d6 100644 --- a/src/backend/jit/llvm/llvmjit_error.cpp +++ b/src/backend/jit/llvm/llvmjit_error.cpp @@ -23,15 +23,22 @@ extern "C" #include "jit/llvmjit.h" +#include static int fatal_new_handler_depth = 0; static std::new_handler old_new_handler = NULL; static void fatal_system_new_handler(void); #if LLVM_VERSION_MAJOR > 4 +static void fatal_llvm_new_handler(void *user_data, const char *reason, bool gen_crash_diag); +#if LLVM_VERSION_MAJOR < 14 static void fatal_llvm_new_handler(void *user_data, const std::string& reason, bool gen_crash_diag); #endif +#endif +static void fatal_llvm_error_handler(void *user_data, const char *reason, bool gen_crash_diag); +#if LLVM_VERSION_MAJOR < 14 static void fatal_llvm_error_handler(void *user_data, const std::string& reason, bool gen_crash_diag); +#endif /* @@ -83,6 +90,16 @@ llvm_leave_fatal_on_oom(void) } } +/* + * Are we currently in an fatal-on-oom section? Useful to skip cleanup in case + * of errors. + */ +bool +llvm_in_fatal_on_oom(void) +{ + return fatal_new_handler_depth > 0; +} + /* * Reset fatal error handling. This should only be called in error recovery * loops like PostgresMain()'s. @@ -119,23 +136,41 @@ fatal_system_new_handler(void) #if LLVM_VERSION_MAJOR > 4 static void fatal_llvm_new_handler(void *user_data, - const std::string& reason, + const char *reason, bool gen_crash_diag) { ereport(FATAL, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"), - errdetail("While in LLVM: %s", reason.c_str()))); + errdetail("While in LLVM: %s", reason))); +} +#if LLVM_VERSION_MAJOR < 14 +static void +fatal_llvm_new_handler(void *user_data, + const std::string& reason, + bool gen_crash_diag) +{ + fatal_llvm_new_handler(user_data, reason.c_str(), gen_crash_diag); } #endif +#endif static void fatal_llvm_error_handler(void *user_data, - const std::string& reason, + const char *reason, bool gen_crash_diag) { ereport(FATAL, (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("fatal llvm error: %s", - reason.c_str()))); + errmsg("fatal llvm error: %s", reason))); +} + +#if LLVM_VERSION_MAJOR < 14 +static void +fatal_llvm_error_handler(void *user_data, + const std::string& reason, + bool gen_crash_diag) +{ + fatal_llvm_error_handler(user_data, reason.c_str(), gen_crash_diag); } +#endif diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c index 0da318218fd..12138e49577 100644 --- a/src/backend/jit/llvm/llvmjit_expr.c +++ b/src/backend/jit/llvm/llvmjit_expr.c @@ -152,7 +152,7 @@ llvm_compile_expr(ExprState *state) param_types[0] = l_ptr(StructExprState); /* state */ param_types[1] = l_ptr(StructExprContext); /* econtext */ - param_types[2] = l_ptr(TypeParamBool); /* isnull */ + param_types[2] = l_ptr(TypeStorageBool); /* isnull */ eval_sig = LLVMFunctionType(TypeSizeT, param_types, lengthof(param_types), @@ -259,8 +259,6 @@ llvm_compile_expr(ExprState *state) v_tmpvalue = LLVMBuildLoad(b, v_tmpvaluep, ""); v_tmpisnull = LLVMBuildLoad(b, v_tmpisnullp, ""); - v_tmpisnull = - LLVMBuildTrunc(b, v_tmpisnull, TypeParamBool, ""); LLVMBuildStore(b, v_tmpisnull, v_isnullp); diff --git a/src/backend/jit/llvm/llvmjit_inline.cpp b/src/backend/jit/llvm/llvmjit_inline.cpp index c489a632ff9..c7deff0c5b3 100644 --- a/src/backend/jit/llvm/llvmjit_inline.cpp +++ b/src/backend/jit/llvm/llvmjit_inline.cpp @@ -62,6 +62,7 @@ extern "C" #include #include #include +#include /* @@ -594,7 +595,11 @@ function_inlinable(llvm::Function &F, if (F.materialize()) elog(FATAL, "failed to materialize metadata"); - if (F.getAttributes().hasFnAttribute(llvm::Attribute::NoInline)) +#if LLVM_VERSION_MAJOR < 14 +#define hasFnAttr hasFnAttribute +#endif + + if (F.getAttributes().hasFnAttr(llvm::Attribute::NoInline)) { ilog(DEBUG1, "ineligibile to import %s due to noinline", F.getName().data()); @@ -608,6 +613,17 @@ function_inlinable(llvm::Function &F, if (rv->materialize()) elog(FATAL, "failed to materialize metadata"); + /* + * Don't inline functions that access thread local variables. That + * doesn't work on current LLVM releases (but might in future). + */ + if (rv->isThreadLocal()) + { + ilog(DEBUG1, "cannot inline %s due to thread-local variable %s", + F.getName().data(), rv->getName().data()); + return false; + } + /* * Never want to inline externally visible vars, cheap enough to * reference. @@ -860,7 +876,9 @@ create_redirection_function(std::unique_ptr &importMod, llvm::Function *AF; llvm::BasicBlock *BB; llvm::CallInst *fwdcall; +#if LLVM_VERSION_MAJOR < 14 llvm::Attribute inlineAttribute; +#endif AF = llvm::Function::Create(F->getFunctionType(), LinkageTypes::AvailableExternallyLinkage, @@ -869,9 +887,13 @@ create_redirection_function(std::unique_ptr &importMod, Builder.SetInsertPoint(BB); fwdcall = Builder.CreateCall(F, &*AF->arg_begin()); +#if LLVM_VERSION_MAJOR < 14 inlineAttribute = llvm::Attribute::get(Context, llvm::Attribute::AlwaysInline); fwdcall->addAttribute(~0U, inlineAttribute); +#else + fwdcall->addFnAttr(llvm::Attribute::AlwaysInline); +#endif Builder.CreateRet(fwdcall); return AF; diff --git a/src/backend/jit/llvm/llvmjit_wrap.cpp b/src/backend/jit/llvm/llvmjit_wrap.cpp index 7490805fdca..9fc813dc949 100644 --- a/src/backend/jit/llvm/llvmjit_wrap.cpp +++ b/src/backend/jit/llvm/llvmjit_wrap.cpp @@ -16,6 +16,13 @@ extern "C" #include "postgres.h" } +#include + +/* Avoid macro clash with LLVM's C++ headers */ +#undef Min + +#include +#include #include #include @@ -44,3 +51,28 @@ char *LLVMGetHostCPUFeatures(void) { return strdup(Features.getString().c_str()); } #endif + +/* + * Like LLVM's LLVMGetAttributeCountAtIndex(), works around a bug in LLVM 3.9. + * + * In LLVM <= 3.9, LLVMGetAttributeCountAtIndex() segfaults if there are no + * attributes at an index (fixed in LLVM commit ce9bb1097dc2). + */ +unsigned +LLVMGetAttributeCountAtIndexPG(LLVMValueRef F, uint32 Idx) +{ + /* + * This is more expensive, so only do when using a problematic LLVM + * version. + */ +#if LLVM_VERSION_MAJOR < 4 + if (!llvm::unwrap(F)->getAttributes().hasAttributes(Idx)) + return 0; +#endif + + /* + * There is no nice public API to determine the count nicely, so just + * always fall back to LLVM's C API. + */ + return LLVMGetAttributeCountAtIndex(F, Idx); +} diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c index 4cce6b58122..1d609afbd9f 100644 --- a/src/backend/optimizer/util/tlist.c +++ b/src/backend/optimizer/util/tlist.c @@ -1013,7 +1013,7 @@ apply_pathtarget_labeling_to_tlist(List *tlist, PathTarget *target) * * The outputs of this function are two parallel lists, one a list of * PathTargets and the other an integer list of bool flags indicating - * whether the corresponding PathTarget contains any evaluatable SRFs. + * whether the corresponding PathTarget contains any evaluable SRFs. * The lists are given in the order they'd need to be evaluated in, with * the "lowest" PathTarget first. So the last list entry is always the * originally given PathTarget, and any entries before it indicate evaluation diff --git a/src/bin/pg_upgrade/info.c b/src/bin/pg_upgrade/info.c index fd0b44c3ce9..968db0d777d 100644 --- a/src/bin/pg_upgrade/info.c +++ b/src/bin/pg_upgrade/info.c @@ -433,11 +433,10 @@ get_rel_infos(ClusterInfo *cluster, DbInfo *dbinfo) query[0] = '\0'; /* initialize query string to empty */ /* - * Create a CTE that collects OIDs of regular user tables, including - * matviews and sequences, but excluding toast tables and indexes. We - * assume that relations with OIDs >= FirstNormalObjectId belong to the - * user. (That's probably redundant with the namespace-name exclusions, - * but let's be safe.) + * Create a CTE that collects OIDs of regular user tables and matviews, + * but excluding toast tables and indexes. We assume that relations with + * OIDs >= FirstNormalObjectId belong to the user. (That's probably + * redundant with the namespace-name exclusions, but let's be safe.) * * pg_largeobject contains user data that does not appear in pg_dump * output, so we have to copy that system table. It's easiest to do that diff --git a/src/include/jit/llvmjit.h b/src/include/jit/llvmjit.h index 94e6612823b..c111ed802a4 100644 --- a/src/include/jit/llvmjit.h +++ b/src/include/jit/llvmjit.h @@ -90,6 +90,7 @@ extern LLVMValueRef FuncExecAggInitGroup; extern void llvm_enter_fatal_on_oom(void); extern void llvm_leave_fatal_on_oom(void); +extern bool llvm_in_fatal_on_oom(void); extern void llvm_reset_after_error(void); extern void llvm_assert_in_fatal_section(void); @@ -133,6 +134,8 @@ extern char *LLVMGetHostCPUName(void); extern char *LLVMGetHostCPUFeatures(void); #endif +extern unsigned LLVMGetAttributeCountAtIndexPG(LLVMValueRef F, uint32 Idx); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/src/include/jit/llvmjit_emit.h b/src/include/jit/llvmjit_emit.h index fdc51b1e907..949be7ee078 100644 --- a/src/include/jit/llvmjit_emit.h +++ b/src/include/jit/llvmjit_emit.h @@ -1,6 +1,6 @@ /* * llvmjit_emit.h - * Helpers to make emitting LLVM IR a it more concise and pgindent proof. + * Helpers to make emitting LLVM IR a bit more concise and pgindent proof. * * Copyright (c) 2018, PostgreSQL Global Development Group * diff --git a/src/test/regress/expected/expressions.out b/src/test/regress/expected/expressions.out index 719455b0ebb..42d129c58ae 100644 --- a/src/test/regress/expected/expressions.out +++ b/src/test/regress/expected/expressions.out @@ -45,7 +45,7 @@ SELECT now()::timestamp::text = localtimestamp::text; t (1 row) --- current_role/user/user is tested in rolnames.sql +-- current_role/user/user is tested in rolenames.sql -- current database / catalog SELECT current_catalog = current_database(); ?column? diff --git a/src/test/regress/sql/expressions.sql b/src/test/regress/sql/expressions.sql index 3427fdfdd72..1d7601fe031 100644 --- a/src/test/regress/sql/expressions.sql +++ b/src/test/regress/sql/expressions.sql @@ -22,7 +22,7 @@ SELECT length(current_timestamp::text) >= length(current_timestamp(0)::text); -- localtimestamp SELECT now()::timestamp::text = localtimestamp::text; --- current_role/user/user is tested in rolnames.sql +-- current_role/user/user is tested in rolenames.sql -- current database / catalog SELECT current_catalog = current_database(); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 224908e11e7..635f9319ecd 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1133,6 +1133,7 @@ LLVMJitHandle LLVMMemoryBufferRef LLVMModuleRef LLVMOrcJITStackRef +LLVMOrcLookupStateRef LLVMOrcModuleHandle LLVMOrcTargetAddress LLVMPassManagerBuilderRef