diff --git a/config/c-compiler.m4 b/config/c-compiler.m4 index 7cbb8ec3249c8d775e39214693c72f6953bb9a10..29db5b16b04ca9c4dfc4305d2e5007136a328052 100644 --- a/config/c-compiler.m4 +++ b/config/c-compiler.m4 @@ -122,7 +122,7 @@ fi])# PGAC_C_FUNCNAME_SUPPORT # PGAC_C_STATIC_ASSERT -# ----------------------- +# -------------------- # Check if the C compiler understands _Static_assert(), # and define HAVE__STATIC_ASSERT if so. # @@ -161,6 +161,63 @@ fi])# PGAC_C_TYPES_COMPATIBLE +# PGAC_C_BUILTIN_CONSTANT_P +# ------------------------- +# Check if the C compiler understands __builtin_constant_p(), +# and define HAVE__BUILTIN_CONSTANT_P if so. +AC_DEFUN([PGAC_C_BUILTIN_CONSTANT_P], +[AC_CACHE_CHECK(for __builtin_constant_p, pgac_cv__builtin_constant_p, +[AC_TRY_COMPILE([static int x; static int y[__builtin_constant_p(x) ? x : 1];], +[], +[pgac_cv__builtin_constant_p=yes], +[pgac_cv__builtin_constant_p=no])]) +if test x"$pgac_cv__builtin_constant_p" = xyes ; then +AC_DEFINE(HAVE__BUILTIN_CONSTANT_P, 1, + [Define to 1 if your compiler understands __builtin_constant_p.]) +fi])# PGAC_C_BUILTIN_CONSTANT_P + + + +# PGAC_C_BUILTIN_UNREACHABLE +# -------------------------- +# Check if the C compiler understands __builtin_unreachable(), +# and define HAVE__BUILTIN_UNREACHABLE if so. +# +# NB: Don't get the idea of putting a for(;;); or such before the +# __builtin_unreachable() call. Some compilers would remove it before linking +# and only a warning instead of an error would be produced. +AC_DEFUN([PGAC_C_BUILTIN_UNREACHABLE], +[AC_CACHE_CHECK(for __builtin_unreachable, pgac_cv__builtin_unreachable, +[AC_TRY_LINK([], +[__builtin_unreachable();], +[pgac_cv__builtin_unreachable=yes], +[pgac_cv__builtin_unreachable=no])]) +if test x"$pgac_cv__builtin_unreachable" = xyes ; then +AC_DEFINE(HAVE__BUILTIN_UNREACHABLE, 1, + [Define to 1 if your compiler understands __builtin_unreachable.]) +fi])# PGAC_C_BUILTIN_UNREACHABLE + + + +# PGAC_C_VA_ARGS +# -------------- +# Check if the C compiler understands C99-style variadic macros, +# and define HAVE__VA_ARGS if so. +AC_DEFUN([PGAC_C_VA_ARGS], +[AC_CACHE_CHECK(for __VA_ARGS__, pgac_cv__va_args, +[AC_TRY_COMPILE([#include <stdio.h>], +[#define debug(...) fprintf(stderr, __VA_ARGS__) +debug("%s", "blarg"); +], +[pgac_cv__va_args=yes], +[pgac_cv__va_args=no])]) +if test x"$pgac_cv__va_args" = xyes ; then +AC_DEFINE(HAVE__VA_ARGS, 1, + [Define to 1 if your compiler understands __VA_ARGS__ in macros.]) +fi])# PGAC_C_VA_ARGS + + + # PGAC_PROG_CC_CFLAGS_OPT # ----------------------- # Given a string, check if the compiler supports the string as a diff --git a/configure b/configure index 10ae1523048ecec084f4d3e08d120098392adc82..9efd866059fea207a711e8bd8e2e7c669828a2ca 100755 --- a/configure +++ b/configure @@ -15659,6 +15659,184 @@ cat >>confdefs.h <<\_ACEOF #define HAVE__BUILTIN_TYPES_COMPATIBLE_P 1 _ACEOF +fi +{ $as_echo "$as_me:$LINENO: checking for __builtin_constant_p" >&5 +$as_echo_n "checking for __builtin_constant_p... " >&6; } +if test "${pgac_cv__builtin_constant_p+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +static int x; static int y[__builtin_constant_p(x) ? x : 1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + pgac_cv__builtin_constant_p=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + pgac_cv__builtin_constant_p=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $pgac_cv__builtin_constant_p" >&5 +$as_echo "$pgac_cv__builtin_constant_p" >&6; } +if test x"$pgac_cv__builtin_constant_p" = xyes ; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE__BUILTIN_CONSTANT_P 1 +_ACEOF + +fi +{ $as_echo "$as_me:$LINENO: checking for __builtin_unreachable" >&5 +$as_echo_n "checking for __builtin_unreachable... " >&6; } +if test "${pgac_cv__builtin_unreachable+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +__builtin_unreachable(); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + pgac_cv__builtin_unreachable=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + pgac_cv__builtin_unreachable=no +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $pgac_cv__builtin_unreachable" >&5 +$as_echo "$pgac_cv__builtin_unreachable" >&6; } +if test x"$pgac_cv__builtin_unreachable" = xyes ; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE__BUILTIN_UNREACHABLE 1 +_ACEOF + +fi +{ $as_echo "$as_me:$LINENO: checking for __VA_ARGS__" >&5 +$as_echo_n "checking for __VA_ARGS__... " >&6; } +if test "${pgac_cv__va_args+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdio.h> +int +main () +{ +#define debug(...) fprintf(stderr, __VA_ARGS__) +debug("%s", "blarg"); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + pgac_cv__va_args=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + pgac_cv__va_args=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $pgac_cv__va_args" >&5 +$as_echo "$pgac_cv__va_args" >&6; } +if test x"$pgac_cv__va_args" = xyes ; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE__VA_ARGS 1 +_ACEOF + fi { $as_echo "$as_me:$LINENO: checking whether struct tm is in sys/time.h or time.h" >&5 $as_echo_n "checking whether struct tm is in sys/time.h or time.h... " >&6; } diff --git a/configure.in b/configure.in index e2682f3da5a2169236b42b9a1f84025a0231d603..f31f7efda018f443ed838985f7d3be974960b3e9 100644 --- a/configure.in +++ b/configure.in @@ -1106,6 +1106,9 @@ AC_C_VOLATILE PGAC_C_FUNCNAME_SUPPORT PGAC_C_STATIC_ASSERT PGAC_C_TYPES_COMPATIBLE +PGAC_C_BUILTIN_CONSTANT_P +PGAC_C_BUILTIN_UNREACHABLE +PGAC_C_VA_ARGS PGAC_STRUCT_TIMEZONE PGAC_UNION_SEMUN PGAC_STRUCT_SOCKADDR_UN diff --git a/contrib/cube/cubescan.l b/contrib/cube/cubescan.l index c1849301585aad1f1c021c52e5ddd1e91d9f5616..a74eb4ba37e0b05d3f94413aac88e4b2827e294c 100644 --- a/contrib/cube/cubescan.l +++ b/contrib/cube/cubescan.l @@ -11,7 +11,13 @@ /* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */ #undef fprintf -#define fprintf(file, fmt, msg) ereport(ERROR, (errmsg_internal("%s", msg))) +#define fprintf(file, fmt, msg) fprintf_to_ereport(fmt, msg) + +static void +fprintf_to_ereport(const char *fmt, const char *msg) +{ + ereport(ERROR, (errmsg_internal("%s", msg))); +} /* Handles to the buffer that the lexer uses internally */ static YY_BUFFER_STATE scanbufhandle; diff --git a/contrib/seg/segscan.l b/contrib/seg/segscan.l index e4feab39b379840ba24433342c3ba6350d106142..ce5ff31e47cca31ac5189bf105998c220184aab9 100644 --- a/contrib/seg/segscan.l +++ b/contrib/seg/segscan.l @@ -10,7 +10,13 @@ /* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */ #undef fprintf -#define fprintf(file, fmt, msg) ereport(ERROR, (errmsg_internal("%s", msg))) +#define fprintf(file, fmt, msg) fprintf_to_ereport(fmt, msg) + +static void +fprintf_to_ereport(const char *fmt, const char *msg) +{ + ereport(ERROR, (errmsg_internal("%s", msg))); +} /* Handles to the buffer that the lexer uses internally */ static YY_BUFFER_STATE scanbufhandle; diff --git a/src/backend/bootstrap/bootscanner.l b/src/backend/bootstrap/bootscanner.l index bdd7dcb1b324639cd900858aed8ee3446b7fe76b..ce57de61b264e3572bb2b4ad50db4fb2c826c5e0 100644 --- a/src/backend/bootstrap/bootscanner.l +++ b/src/backend/bootstrap/bootscanner.l @@ -42,7 +42,13 @@ /* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */ #undef fprintf -#define fprintf(file, fmt, msg) ereport(ERROR, (errmsg_internal("%s", msg))) +#define fprintf(file, fmt, msg) fprintf_to_ereport(fmt, msg) + +static void +fprintf_to_ereport(const char *fmt, const char *msg) +{ + ereport(ERROR, (errmsg_internal("%s", msg))); +} static int yyline = 1; /* line number for error reporting */ diff --git a/src/backend/parser/scan.l b/src/backend/parser/scan.l index 622cb1f57c4d780caba2090578bc96b7d6df0a35..23c83c4fd9030dc46a8bae130df248c266e08b83 100644 --- a/src/backend/parser/scan.l +++ b/src/backend/parser/scan.l @@ -42,7 +42,13 @@ /* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */ #undef fprintf -#define fprintf(file, fmt, msg) ereport(ERROR, (errmsg_internal("%s", msg))) +#define fprintf(file, fmt, msg) fprintf_to_ereport(fmt, msg) + +static void +fprintf_to_ereport(const char *fmt, const char *msg) +{ + ereport(ERROR, (errmsg_internal("%s", msg))); +} /* * GUC variables. This is a DIRECT violation of the warning given at the diff --git a/src/backend/replication/repl_scanner.l b/src/backend/replication/repl_scanner.l index 9ccf02a040bc5e670ebeb572a69c522fd36a2d06..a1244983060e645d4ab0c464a67e6da7ca50c1b2 100644 --- a/src/backend/replication/repl_scanner.l +++ b/src/backend/replication/repl_scanner.l @@ -19,7 +19,13 @@ /* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */ #undef fprintf -#define fprintf(file, fmt, msg) ereport(ERROR, (errmsg_internal("%s", msg))) +#define fprintf(file, fmt, msg) fprintf_to_ereport(fmt, msg) + +static void +fprintf_to_ereport(const char *fmt, const char *msg) +{ + ereport(ERROR, (errmsg_internal("%s", msg))); +} /* Handle to the buffer that the lexer uses internally */ static YY_BUFFER_STATE scanbufhandle; diff --git a/src/include/c.h b/src/include/c.h index f7db157f83a1cf39ae44eda6944e673e752c5514..59af5b5cb8efdc1394e38575db72efe7f81a046a 100644 --- a/src/include/c.h +++ b/src/include/c.h @@ -748,6 +748,18 @@ typedef NameData *Name; #endif /* HAVE__BUILTIN_TYPES_COMPATIBLE_P */ +/* + * Mark a point as unreachable in a portable fashion. This should preferably + * be something that the compiler understands, to aid code generation. + * In assert-enabled builds, we prefer abort() for debugging reasons. + */ +#if defined(HAVE__BUILTIN_UNREACHABLE) && !defined(USE_ASSERT_CHECKING) +#define pg_unreachable() __builtin_unreachable() +#else +#define pg_unreachable() abort() +#endif + + /* * Function inlining support -- Allow modules to define functions that may be * inlined, if the compiler supports it. diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index edaf85319b9202fe913e3f24b174f16f5fa5ee5a..8aabf3c87a4706a2181f5d59eb93d8587ce65044 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -635,12 +635,21 @@ /* Define to 1 if you have the <winldap.h> header file. */ #undef HAVE_WINLDAP_H +/* Define to 1 if your compiler understands __builtin_constant_p. */ +#undef HAVE__BUILTIN_CONSTANT_P + /* Define to 1 if your compiler understands __builtin_types_compatible_p. */ #undef HAVE__BUILTIN_TYPES_COMPATIBLE_P +/* Define to 1 if your compiler understands __builtin_unreachable. */ +#undef HAVE__BUILTIN_UNREACHABLE + /* Define to 1 if your compiler understands _Static_assert. */ #undef HAVE__STATIC_ASSERT +/* Define to 1 if your compiler understands __VA_ARGS__ in macros. */ +#undef HAVE__VA_ARGS + /* Define to the appropriate snprintf format for 64-bit ints. */ #undef INT64_FORMAT diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32 index 626ad47883554f591604dcc80e9dc84531003d32..31b9a49d4c5f6fb139835bd201cd851ee0774489 100644 --- a/src/include/pg_config.h.win32 +++ b/src/include/pg_config.h.win32 @@ -526,12 +526,21 @@ /* Define to 1 if you have the <winldap.h> header file. */ /* #undef HAVE_WINLDAP_H */ +/* Define to 1 if your compiler understands __builtin_constant_p. */ +/* #undef HAVE__BUILTIN_CONSTANT_P */ + /* Define to 1 if your compiler understands __builtin_types_compatible_p. */ /* #undef HAVE__BUILTIN_TYPES_COMPATIBLE_P */ +/* Define to 1 if your compiler understands __builtin_unreachable. */ +/* #undef HAVE__BUILTIN_UNREACHABLE */ + /* Define to 1 if your compiler understands _Static_assert. */ /* #undef HAVE__STATIC_ASSERT */ +/* Define to 1 if your compiler understands __VA_ARGS__ in macros. */ +#define HAVE__VA_ARGS 1 + /* Define to the appropriate snprintf format for 64-bit ints, if any. */ #define INT64_FORMAT "%lld" diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h index cbbda040050596abd4b768aa85f2e3babe765f0f..5e937fb10c3211b2c12d0ec2d7ee71fe506cb7f8 100644 --- a/src/include/utils/elog.h +++ b/src/include/utils/elog.h @@ -101,15 +101,33 @@ * ereport_domain() directly, or preferably they can override the TEXTDOMAIN * macro. * - * When elevel >= ERROR, we add an abort() call to give the compiler a hint - * that the ereport() expansion will not return, but the abort() isn't actually - * reached because the longjmp happens in errfinish(). + * If elevel >= ERROR, the call will not return; we try to inform the compiler + * of that via pg_unreachable(). However, no useful optimization effect is + * obtained unless the compiler sees elevel as a compile-time constant, else + * we're just adding code bloat. So, if __builtin_constant_p is available, + * use that to cause the second if() to vanish completely for non-constant + * cases. We avoid using a local variable because it's not necessary and + * prevents gcc from making the unreachability deduction at optlevel -O0. *---------- */ +#ifdef HAVE__BUILTIN_CONSTANT_P #define ereport_domain(elevel, domain, rest) \ - (errstart(elevel, __FILE__, __LINE__, PG_FUNCNAME_MACRO, domain) ? \ - (errfinish rest) : (void) 0), \ - ((elevel) >= ERROR ? abort() : (void) 0) + do { \ + if (errstart(elevel, __FILE__, __LINE__, PG_FUNCNAME_MACRO, domain)) \ + errfinish rest; \ + if (__builtin_constant_p(elevel) && (elevel) >= ERROR) \ + pg_unreachable(); \ + } while(0) +#else /* !HAVE__BUILTIN_CONSTANT_P */ +#define ereport_domain(elevel, domain, rest) \ + do { \ + const int elevel_ = (elevel); \ + if (errstart(elevel_, __FILE__, __LINE__, PG_FUNCNAME_MACRO, domain)) \ + errfinish rest; \ + if (elevel_ >= ERROR) \ + pg_unreachable(); \ + } while(0) +#endif /* HAVE__BUILTIN_CONSTANT_P */ #define ereport(elevel, rest) \ ereport_domain(elevel, TEXTDOMAIN, rest) @@ -212,7 +230,37 @@ extern int getinternalerrposition(void); * elog(ERROR, "portal \"%s\" not found", stmt->portalname); *---------- */ -#define elog elog_start(__FILE__, __LINE__, PG_FUNCNAME_MACRO), elog_finish +#ifdef HAVE__VA_ARGS +/* + * If we have variadic macros, we can give the compiler a hint about the + * call not returning when elevel >= ERROR. See comments for ereport(). + * Note that historically elog() has called elog_start (which saves errno) + * before evaluating "elevel", so we preserve that behavior here. + */ +#ifdef HAVE__BUILTIN_CONSTANT_P +#define elog(elevel, ...) \ + do { \ + elog_start(__FILE__, __LINE__, PG_FUNCNAME_MACRO); \ + elog_finish(elevel, __VA_ARGS__); \ + if (__builtin_constant_p(elevel) && (elevel) >= ERROR) \ + pg_unreachable(); \ + } while(0) +#else /* !HAVE__BUILTIN_CONSTANT_P */ +#define elog(elevel, ...) \ + do { \ + int elevel_; \ + elog_start(__FILE__, __LINE__, PG_FUNCNAME_MACRO); \ + elevel_ = (elevel); \ + elog_finish(elevel_, __VA_ARGS__); \ + if (elevel_ >= ERROR) \ + pg_unreachable(); \ + } while(0) +#endif /* HAVE__BUILTIN_CONSTANT_P */ +#else /* !HAVE__VA_ARGS */ +#define elog \ + elog_start(__FILE__, __LINE__, PG_FUNCNAME_MACRO), \ + elog_finish +#endif /* HAVE__VA_ARGS */ extern void elog_start(const char *filename, int lineno, const char *funcname); extern void @@ -299,14 +347,14 @@ extern PGDLLIMPORT ErrorContextCallback *error_context_stack; /* * gcc understands __attribute__((noreturn)); for other compilers, insert - * a useless exit() call so that the compiler gets the point. + * pg_unreachable() so that the compiler gets the point. */ #ifdef __GNUC__ #define PG_RE_THROW() \ pg_re_throw() #else #define PG_RE_THROW() \ - (pg_re_throw(), exit(1)) + (pg_re_throw(), pg_unreachable()) #endif extern PGDLLIMPORT sigjmp_buf *PG_exception_stack;