Menu

#2883 "2**100" returns 0 due to compiler optimizations

open
nobody
None
4 days ago
7 days ago
PuQ
No

Version: 6.0.5
Compiler: Clang 21+

print 2**100

Expected output: 1.26765060022823e+30

Actual output: 0

After some testing and debugging, I found Clang has assumed that signed integer overflow (undefined behavior) can never happen (if no -fwrapv flag set), and further optimized away the integer_power_overflow branch (at least -O1 level) since LLVM 21.

f_power() in internal.c:

...
intgr_t tprev, t;
intgr_t tmag = llabs(a.v.int_val);
tprev = t = 1;
for (i = 0; i < b.v.int_val; i++) {
    tprev = t;
    t *= tmag; // signed integer overflow (UB)
    if (t < tprev) // -> if (false), optimized away
        goto integer_power_overflow; // never
}
...

I made a patch (based on 6.1 b9821fb) to fix it, but not sure whether there is a better solution.

1 Attachments

Discussion

  • Bastian Märkisch

    FWIW: my Windows binary compiled with clang 22.1.7 does not exhibit this problem.

     
    • Ethan Merritt

      Ethan Merritt - 6 days ago

      I can reproduce this on linux with clang 22.1.8

      Very annoying that an explicit test for overflow is optimized away because "overflow cannot happen".

      Did you test or at least consider whether the patched code will be accepted by older compilers? How old? As I recall, "inline" was not accepted in c89. But we bumped the stated requirement for building gnuplot 6.0 to c99, so I guess the question is specifically whether this is accepted by c99.

       
      • Ethan Merritt

        Ethan Merritt - 6 days ago

        Following up on myself after testing with gcc 4.3.2 (not-yet-official c99 support) on an old machine.

        Inclusion of the new function from C files works fine. Inclusion from C++ files fails due to some problem with type definitions in the standard headers.

        In file included from ./plot.h:38,
                         from wxterminal/wxt_gui.h:117,
                         from wxterminal/wxt_gui.cpp:95:
        ./syscfg.h: In function ‘bool gp_ckd_mul_intgr(intgr_t*, intgr_t, intgr_t)’:
        ./syscfg.h:429: error: ‘INT64_MAX’ was not declared in this scope
        ./syscfg.h:432: error: ‘INT64_MIN’ was not declared in this scope
        ./syscfg.h:437: error: ‘INT64_MIN’ was not declared in this scope
        ./syscfg.h:440: error: ‘INT64_MAX’ was not declared in this scope
        make[4]: *** [wxterminal/wxt_gui.o] Error 1
        

        If I patch around that, the code in the main program seems to work as intended.
        This was obviously fixed in gcc or maybe in the C++ standard at some later date since compilation shows no problem with recent versions of gcc. I'm not sure how much trouble we want to go to in order to support old gcc versions, but if there's a simple fix that would be good.

         
  • Ethan Merritt

    Ethan Merritt - 5 days ago

    Here's a matching addition to fix f_prod(). This one is needed for current gcc, even aside from the issue of clang compiler options.

    Example of failure before
    N = 2**21 - 1
    prod [i=1:3] N =       9223358842721533951
    N*N*N =                9223358842721533951
    prod [i=1:4] N =       26388270678017
    N*N*N*N =              1.93427762203723e+25
    
    After the fix
    prod [i=1:4] N =       1.93427762203723e+25
    N*N*N*N =              1.93427762203723e+25
    

    I did not find any other affected routines, but I suppose there could be some. The core code does not make much use of 64-bit integer arithmetic.

     
    • PuQ

      PuQ - 5 days ago

      Two edge cases mistakenly treated as overflow:

      gnuplot> print 9007199254740993 - 9007199254740992
      0.0
      gnuplot> print -9007199254740992 + 9007199254740993
      0.0
      
       
      • PuQ

        PuQ - 5 days ago

        The same bug in f_sum():

        gnuplot> f(x) = x & 1 ? 9007199254740993 : -9007199254740992
        gnuplot> print sum[i=1:2] f(i)
        0.0
        
         
  • Ethan Merritt

    Ethan Merritt - 4 days ago

    Ugh. I am extremely embarrassed that I missed this pitfall when I first added support for 64 bit integers. Everywhere that this code pattern appears is potentially susceptible to errors due to loss of precision:
    (double)(<FOO>.v.int_val)
    This pitfall is intrinsic to the IEEE floating point representation, so it is relevant not just for recent clang or gcc compiler options. In some places the loss of precision affects possible overflow; in others it does not.

    So I am now thinking we need the full set of your gp_* built-ins. I suggest that they go in a new header file, maybe overflow.h, rather than in syscfg.h. It seems that as of now only internal.c needs to include this header. That bypasses the issue of old C++ compilers failing to accept it (shown to be a problem with gcc 4.3.2).

     

    Last edit: Ethan Merritt 4 days ago
    • PuQ

      PuQ - 4 days ago

      I moved them into overflow.h as suggested, and licensed it under BSD 2-Clause.

       
      • Ethan Merritt

        Ethan Merritt - 4 days ago

        Looks good.

        The only other thing I see is that when you add a file to the package, it needs to be listed in .../src/Makefile.am gnuplot_SOURCES so that the automake system tracks it and includes it in the source package from "make dist".

        diff --git a/src/Makefile.am b/src/Makefile.am
        index 8e0685733..132111689 100644
        --- a/src/Makefile.am
        +++ b/src/Makefile.am
        @@ -56,7 +56,7 @@ help.c help.h hidden3d.c hidden3d.h history.c internal.c internal.h \
         interpol.c interpol.h jitter.c jitter.h libcerf.c libcerf.h \
         loadpath.c loadpath.h marks.c marks.h \
         matrix.c matrix.h misc.c misc.h mouse.c mouse.h mousecmn.h \
        -multiplot.c multiplot.h overflow.h parse.c parse.h plot.c plot.h plot2d.c plot2d.h \
        +multiplot.c multiplot.h parse.c parse.h plot.c plot.h plot2d.c plot2d.h \
         plot3d.c plot3d.h pm3d.c pm3d.h qt_table.h readline.c readline.h save.c \
         save.h scanner.c scanner.h set.c setshow.h show.c specfun.c specfun.h \
         standard.c standard.h stats.h stats.c stdfn.c stdfn.h syscfg.h tables.c tables.h \
        
         

Log in to post a comment.

MongoDB Logo MongoDB