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

--- Comment #9 from GCC Commits <cvs-commit at gcc dot gnu.org> ---
The master branch has been updated by Jakub Jelinek <[email protected]>:

https://gcc.gnu.org/g:b61bb45f622b94dd2a2ebafb4a7e815b6bdf5dad

commit r17-2847-gb61bb45f622b94dd2a2ebafb4a7e815b6bdf5dad
Author: Jakub Jelinek <[email protected]>
Date:   Fri Jul 31 09:07:26 2026 +0200

    bitintlower: Fix up handle_plus_minus [PR126503]

    The following testcase is miscompiled on aarch64 (but not on x86_64).
    The difference is that x86_64/i686 define optabs that make it use IFN_UADDC
    and IFN_USUBC, those are then used both in the loop and to perform the
    most significant limb, so
      # _6 = PHI <0(2), _7(3)>
      # _9 = PHI <0(2), _10(3)>
      _8 = VIEW_CONVERT_EXPR<unsigned long[5]>(a)[_6];
      _11 = .USUBC (0, _8, _9);
      _12 = IMAGPART_EXPR <_11>;
      _13 = REALPART_EXPR <_11>;
      VIEW_CONVERT_EXPR<unsigned long[7]>(<retval>)[_6] = _13;
      _14 = _6 + 1;
      _15 = VIEW_CONVERT_EXPR<unsigned long[5]>(a)[_14];
      _16 = .USUBC (0, _15, _12);
      _10 = IMAGPART_EXPR <_16>;
      _17 = REALPART_EXPR <_16>;
      VIEW_CONVERT_EXPR<unsigned long[7]>(<retval>)[_14] = _17;
      _7 = _6 + 2;
      if (_7 != 4)
    in the loop and
      _18 = MEM <unsigned long> [(_BitInt(257) *)&a + 32B];
      _19 = (<unnamed-signed:1>) _18;
      _20 = (<unnamed-unsigned:1>) _19;
      _21 = (unsigned long) _20;
      _22 = .USUBC (0, _21, _10);
      _23 = IMAGPART_EXPR <_22>;
      _24 = REALPART_EXPR <_22>;
      _25 = (<unnamed-signed:1>) _24;
      _26 = (unsigned long) _25;
      MEM <unsigned long> [(unsigned _BitInt(400) *)&<retval> + 32B] = _26;
    ...
    after the loop.  Now, on targets which don't support the optab, we instead
    use
      # _6 = PHI <0(2), _7(3)>
      # _9 = PHI <0(2), _10(3)>
      _8 = VIEW_CONVERT_EXPR<unsigned long[6]>(a)[_6];
      _11 = .SUB_OVERFLOW (0, _8);
      _13 = REALPART_EXPR <_11>;
      _14 = IMAGPART_EXPR <_11>;
      _15 = .SUB_OVERFLOW (_13, _9);
      _16 = IMAGPART_EXPR <_15>;
      _12 = _14 + _16;
      _17 = REALPART_EXPR <_15>;
      VIEW_CONVERT_EXPR<unsigned long[8]>(<retval>)[_6] = _17;
      _18 = _6 + 1;
      _19 = VIEW_CONVERT_EXPR<unsigned long[6]>(a)[_18];
      _20 = .SUB_OVERFLOW (0, _19);
      _21 = REALPART_EXPR <_20>;
      _22 = IMAGPART_EXPR <_20>;
      _23 = .SUB_OVERFLOW (_21, _12);
      _24 = IMAGPART_EXPR <_23>;
      _10 = _22 + _24;
      _25 = REALPART_EXPR <_23>;
      VIEW_CONVERT_EXPR<unsigned long[8]>(<retval>)[_18] = _25;
      _7 = _6 + 2;
      if (_7 != 4)
    in the loop (i.e. instead of one .USUBC 2 .SUB_OVERFLOW) and then
    after the loop for the most significant limb
      _26 = MEM <unsigned long> [(_BitInt(257) *)&a + 32B];
      _27 = (<unnamed-signed:1>) _26;
      _28 = (<unnamed-signed:1>) _10;
      _29 = 0 - _27;
      _30 = _29 - _28;
      _31 = (unsigned long) _30;
      MEM <unsigned long> [(unsigned _BitInt(400) *)&<retval> + 32B] = _31;
    Now, the last thing is what is wrong.  We need to do two subtractions
    (or after folding one negation and one subtraction), and while in the
    original operation signed overflow is indeed undefined, it just means
    that the two operations together don't overflow, but one of them can.
    In this testcase (in foo function) on aarch64, _26 is 1 (the most
    significant bit of 257-bit negative value) and _10 is also 1 (borrow
    from within the loop).  When we perform this computation in signed 1-bit
    precision, we have 0 - -1 (overflow) and -1 - -1 (another overflow).
    The RTL emitted for this then results in miscompilation, but we really
    shouldn't introduce UB into the IL for something that didn't have UB
    originally.

    So, the following patch forces use of unsigned type for these and casts
    to the signed one only at the end.

    2026-07-31  Jakub Jelinek  <[email protected]>

            PR tree-optimization/126503
            * gimple-lower-bitint.cc (bitint_large_huge::handle_plus_minus): If
            IFN_ADDC/IFN_SUBC can't be used and rhs1_type is not the limb type
            and is signed, perform both additions or both subtractions in
            unsigned type for the rhs1_type and cast to rhs1_type at the end.

            * gcc.dg/torture/bitint-106.c: New test.

    Reviewed-by: Richard Biener <[email protected]>

Reply via email to