* Are some builtin functions (for example log() vs. sqrt()) more equal than others? @ 2021-07-30 15:30 Stefan Kanthak 2021-07-30 16:33 ` Joseph Myers 0 siblings, 1 reply; 5+ messages in thread From: Stefan Kanthak @ 2021-07-30 15:30 UTC (permalink / raw) To: gcc Hi @ll, both the IEEE 754 and the ISO C standards define the (constant) expressions 1.0/0.0 and 0.0/0.0 to yield the floating-point constants INFINITY and NAN alias INDEFINITE, to be provided as macros (ISO/IEC 9899:1999 7.12/4 INFINITY and 7.12/5 NAN) in math.h. JFTR: the statement "NAN is a GNU extension" given in <https://www.gnu.org/software/libc/manual/html_node/Infinity-and-NaN.html> is WRONG since the last millenium! The ISO C standard also defines the behaviour of standard functions like log() and their (constant and well-known) result for (constant) arguments like -0.0, +0.0, -INFINITY, +INFINITY and NAN GCC defines most of these standard functions as builtins, for example log(), and evaluates them at compile time for constant arguments. GCC but refuses the definition of constants using for example log(-0.0), although the resulting function value is a well-defined constant! --- bug.c --- #include <math.h> const double euler = log(1.0); const double infinity = log(-0.0); const double indefinite = log(-1.0); const double log_euler = log(euler); const double log_e = log(log(1.0)); const double log_phi = log(sqrt(5.0) * 0.5 + 0.5); const double log_minusinf = log(-1.0 / 0.0); const double log_plusinf = log(1.0 / 0.0); const double log_nan = log(0.0 / 0.0); --- EOF --- $ gcc bug.c bug.c:4:25: error: initializer element is not constant 4 | const double infinity = log(-0.0); | ^~~ bug.c:5:27: error: initializer element is not constant 5 | const double indefinite = log(-1.0); | ^~~ bug.c:7:26: error: initializer element is not constant 6 | const double log_euler = log(euler); | ^~~ bug.c:9:22: error: initializer element is not constant 7 | const double log_e = log(log(1.0)); | ^~~ bug.c:11:29: error: initializer element is not constant 9 | const double log_minusinf = log(-1.0 / 0.0); | ^~~ bug.c:12:28: error: initializer element is not constant 10 | const double log_plusinf = log(1.0 / 0.0); | ^~~ bug.c:13:24: error: initializer element is not constant 11 | const double log_nan = log(0.0 / 0.0); | ^~~ Especially compare the lines 7 and 8: while GCC fails on log(log(1.0)) it but succeeds on log(sqrt(5.0) * 0.5 + 0.5)! NOT amused Stefan Kanthak ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: Are some builtin functions (for example log() vs. sqrt()) more equal than others? 2021-07-30 15:30 Are some builtin functions (for example log() vs. sqrt()) more equal than others? Stefan Kanthak @ 2021-07-30 16:33 ` Joseph Myers 2021-07-30 19:53 ` Stefan Kanthak 0 siblings, 1 reply; 5+ messages in thread From: Joseph Myers @ 2021-07-30 16:33 UTC (permalink / raw) To: Stefan Kanthak; +Cc: gcc None of these are valid constant expressions as defined by the standard (constant expressions cannot involve evaluated function calls). Some might be accepted as an extension, but I expect that since the optimization for constant arguments is intended for valid calls that would otherwise be executed at runtime, not for static initializers, it's avoiding optimizations that would result in the loss of floating-point exception flag raising. -- Joseph S. Myers joseph@codesourcery.com ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: Are some builtin functions (for example log() vs. sqrt()) more equal than others? 2021-07-30 16:33 ` Joseph Myers @ 2021-07-30 19:53 ` Stefan Kanthak 2021-07-30 20:51 ` Joseph Myers 0 siblings, 1 reply; 5+ messages in thread From: Stefan Kanthak @ 2021-07-30 19:53 UTC (permalink / raw) To: Joseph Myers; +Cc: gcc Joseph Myers <joseph@codesourcery.com> wrote: > None of these are valid constant expressions as defined by the standard > (constant expressions cannot involve evaluated function calls). That's why I ask specifically why GCC bugs on log(log(...)), but not on log(sqrt(...) ...)! GCC also accepts following initializers and places these constants in the .rodata section, i.e. it evaluates the functions during compile time: const double pi = acos(-1.0); const double pi_by_2 = acos(0.0); const double pi_by_two = asin(1.0); const double log_pi = log(acos(-1.0)); const double log_pi_by_2 = log(acos(0.0)); const double log_pi_by_two = log(asin(1.0)); const double sqrt_sqrt_2 = sqrt(sqrt(2.0)); Again: what's SOOO special about log(log(<constant expression>))? > Some might be accepted as an extension, but I expect that since the > optimization for constant arguments is intended for valid calls that > would otherwise be executed at runtime, not for static initializers, > it's avoiding optimizations that would result in the loss of floating- > point exception flag raising. That's no valid excuse: by the standard, the compiler is free to execute static initializers during runtime, before calling the main() routine. JFTR: doing so would but inhibit the placement of such constants in the read-only data section ... what is also allowed by the standard. regards Stefan ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: Are some builtin functions (for example log() vs. sqrt()) more equal than others? 2021-07-30 19:53 ` Stefan Kanthak @ 2021-07-30 20:51 ` Joseph Myers 2021-07-30 21:54 ` Stefan Kanthak 0 siblings, 1 reply; 5+ messages in thread From: Joseph Myers @ 2021-07-30 20:51 UTC (permalink / raw) To: Stefan Kanthak; +Cc: gcc On Fri, 30 Jul 2021, Stefan Kanthak wrote: > Joseph Myers <joseph@codesourcery.com> wrote: > > > None of these are valid constant expressions as defined by the standard > > (constant expressions cannot involve evaluated function calls). > > That's why I ask specifically why GCC bugs on log(log(...)), but not on > log(sqrt(...) ...)! The log(log(1.0)) example you gave would raise divide-by-zero. > > Some might be accepted as an extension, but I expect that since the > > optimization for constant arguments is intended for valid calls that > > would otherwise be executed at runtime, not for static initializers, > > it's avoiding optimizations that would result in the loss of floating- > > point exception flag raising. > > That's no valid excuse: by the standard, the compiler is free to execute > static initializers during runtime, before calling the main() routine. The point of this extension isn't to accept as much as possible. Rather, it turned out when I implemented standard constant expression rules for GCC 4.5 that lots of existing code was using just about anything GCC could fold into a constant in just about any context requiring a constant expression. So for compatibility with existing, questionable, pre-GCC-4.5 code, we still allow "expressions that can be folded into a constant" in various such contexts, with a pedwarn-if-pedantic. But because this isn't a designed, documented extension or something it's actually considered good practice to use, the semantics remain "expressions that can be folded into a constant", with all the dependence that implies on the folding GCC does for optimization purposes - and that folding is designed for optimizing code outside of static initializers, not for use in this extension, with all the corresponding implications for its design. So if some expression doesn't get folded to a constant outside of static initializers (or for that matter, if it does get so folded, but previous GCC versions didn't accept it in static initializers, since this extension is about compatibility with existing code), it's not a bug for it not to be accepted in a static initializer. If, outside of static initializers, some of these expressions don't get folded to a constant *even with -fno-trapping-math*, that's a missed optimization and it would make sense to improve the compiler to fold them given -fno-trapping-math. Executing static initializers at runtime seems more like a C++ thing; it's not within the conventional concept of how C maps to object files. -- Joseph S. Myers joseph@codesourcery.com ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: Are some builtin functions (for example log() vs. sqrt()) more equal than others? 2021-07-30 20:51 ` Joseph Myers @ 2021-07-30 21:54 ` Stefan Kanthak 0 siblings, 0 replies; 5+ messages in thread From: Stefan Kanthak @ 2021-07-30 21:54 UTC (permalink / raw) To: Joseph Myers; +Cc: gcc "Joseph Myers" <joseph@codesourcery.com> wrote: > On Fri, 30 Jul 2021, Stefan Kanthak wrote: > >> Joseph Myers <joseph@codesourcery.com> wrote: >> >> > None of these are valid constant expressions as defined by the standard >> > (constant expressions cannot involve evaluated function calls). >> >> That's why I ask specifically why GCC bugs on log(log(...)), but not on >> log(sqrt(...) ...)! > > The log(log(1.0)) example you gave would raise divide-by-zero. ARGH, my SILLY fault: I named the first constant "euler" since it should of course be set to 2.71..., i.e. this line should have read const double euler = exp(1.0); Unfortunately I wrote but log(1.0) there and introduced the divide-by-zero. Sorry for the confusion. JFTR; in order to provide a short repro, I stripped down a larger program where the values of a static const double table[] = {log2(0.0), ...}; were to be initialized with several logarithms, which but failed. And while stripping it down, I introduced this bug.-( >> > Some might be accepted as an extension, but I expect that since the >> > optimization for constant arguments is intended for valid calls that >> > would otherwise be executed at runtime, not for static initializers, >> > it's avoiding optimizations that would result in the loss of floating- >> > point exception flag raising. >> >> That's no valid excuse: by the standard, the compiler is free to execute >> static initializers during runtime, before calling the main() routine. > > The point of this extension isn't to accept as much as possible. I expected it the other way 'round. > Rather, it turned out when I implemented standard constant expression rules > for GCC 4.5 that lots of existing code was using just about anything GCC > could fold into a constant in just about any context requiring a constant > expression. So for compatibility with existing, questionable, pre-GCC-4.5 > code, we still allow "expressions that can be folded into a constant" in > various such contexts, with a pedwarn-if-pedantic. But because this > isn't a designed, documented extension or something it's actually considered ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ OK. I searched the docs of course before I wrote in, and only asked because I couldn't find anything about this (non-standard) feature. > good practice to use, the semantics remain "expressions that can be folded > into a constant", with all the dependence that implies on the folding GCC > does for optimization purposes - and that folding is designed for > optimizing code outside of static initializers, not for use in this > extension, with all the corresponding implications for its design. > > So if some expression doesn't get folded to a constant outside of static > initializers (or for that matter, if it does get so folded, but previous > GCC versions didn't accept it in static initializers, since this extension > is about compatibility with existing code), it's not a bug for it not to > be accepted in a static initializer. Thanks for the clarification. > If, outside of static initializers, some of these expressions don't get > folded to a constant *even with -fno-trapping-math*, that's a missed > optimization and it would make sense to improve the compiler to fold them > given -fno-trapping-math. In order to get the larger program compiled, I defined the table inside the function which consumed it: double function(...) { const double table[] = {log2(0.0), ...}; ... } GCC evaluated almost all logarithms during compile time and placed them in the .rodata section. BUT: instead to transfer these precomputed constants in the prolog of this function with a call to memcpy() from .rodata to the stack, GCC created a whole lot of movq .LC1...(%rip), %xmm0 movq %xmm0, offset(%rsp) instruction pairs ... one for each precomputed constant. I expected GCC to be a LITTLE smarter there... > Executing static initializers at runtime seems more like a C++ thing; it's > not within the conventional concept of how C maps to object files. regards Stefan ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2021-07-30 21:55 UTC | newest] Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2021-07-30 15:30 Are some builtin functions (for example log() vs. sqrt()) more equal than others? Stefan Kanthak 2021-07-30 16:33 ` Joseph Myers 2021-07-30 19:53 ` Stefan Kanthak 2021-07-30 20:51 ` Joseph Myers 2021-07-30 21:54 ` Stefan Kanthak
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).