https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126299

--- Comment #6 from migraineman33 at gmail dot com ---
We hit this again in the field last week, and the way it presented
may be useful data for the fix discussion.

The sighting: the first nontrivial file we checksummed on our
FPGA PDP-11 (Fuzix, gcc 13.3.0 cross, -Os) printed a CRC of
"8191>" where the host said 3461363716 -- a letter in a decimal
field.  The trail led to our C support library's shift-subtract
divide, whose normalization loop is guarded by

    while (den < num && !(den & 0x80000000UL)) {

That AND against the sign bit gets folded into the same broken
comparison shape this PR describes:

    tst  r0          ; high word of den
    bne  .+4         ; nonzero -> use its sign
    tst  r1          ; high == 0 -> test the LOW word
    blt  <exit loop> ; ...and branch on ITS sign

so normalization stops the moment the divisor doubles into
0x8000..0xFFFF, and every unsigned 32-bit division with a
numerator above 32767 returns garbage -- which took out all
%lu/%lx formatting in one stroke.  (A quick simulation of the
miscompiled loop reproduces the exact "8191>" string, so the
attribution is solid.)  Worth noting for the fix's scope: sign-bit
TESTS, not just relational compares, arrive at this pattern via
the fold.

Having seen it three times now (kernel lseek, an interpreter's
size guard, and this), we went looking systematically: a small
scanner over objdump output for the emission signature (tst/cmp
of the high word; bne; tst/cmp of the low word; signed branch)
across our built userland found:

  - 13 sites in our own support code: the divide helper above,
    the sign handling in divsi3/modsi3/divdi3/moddi3, and the
    val < 0 tests in two long-to-string converters (%ld of
    32768..65535 printed as garbage negatives).
  - 18 more flagged in the stock C library: fseek (2), strtol,
    labs, fgetpos, gmtime_r (3), localtime_r (2),
    clock_nanosleep (4), and three hits (getopt, regcomp, a
    float scanner) we suspect are pattern false positives but
    have not eyeballed.

The distribution seems relevant to comments 4 and 5: the sites
are overwhelmingly comparisons against zero (the tst form) --
sign tests on file offsets, time values, and string-conversion
inputs.  Genuine two-variable signed comparisons of wide values
were the small minority (the time code and fseek).  So the
"only look at the high word" special case discussed in c#4/c#5
is not just a nice optimization -- in this codebase it IS the
common case, by roughly an order of magnitude.

For what it's worth, our field workaround does exactly that:
sites were rewritten to test the top 16 bits alone,

    if ((int)(v >> 16) < 0)

which gcc compiles to ashc/tst -- the same codegen shape c#5
identifies.  All 13 of our sites validated on hardware after the
rewrite (the checksum tool now agrees with the host byte for
byte).

No patch attached, same reasoning as the original report: the
fix shape is a maintainer's call and you two are clearly already
circling it.  I'm happy to test candidate patches against this
codebase -- between the kernel, the C library, and the divide
helpers it seems to be a pretty good natural test corpus for
this particular bug.

Reply via email to