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

commit r16-9761-gdbae82bbb5aee537b0f87604658ea45742167669
Author: Jakub Jelinek <[email protected]>
Date:   Fri Sep 18 11:03:28 2026 +0200

    bitintlower: Fix big-endian separate_ext loop condition [PR127378]
    
    On the following testcase on powerpc64-linux (both -m32 and -m64) we emit
    incorrect loop condition in the separate_ext loop.
    For little endian, we do the addition on the least significant 3 limbs
    (straight line code), 2 lowest limbs regular, the third on 7 bits and
    then sign extension, and then a separate_ext loop
      <bb 3> [local count: 1073741824]:
      # _34 = PHI <3(2), _35(3)>
      VIEW_CONVERT_EXPR<unsigned long[8]>(a)[_34] = _33;
      _35 = _34 + 1;
      if (_35 != 8)
        goto <bb 3>; [0.05%]
      else
        goto <bb 4>; [99.95%]
    to copy the sign extension into the remaining 5 limbs.
    On big endian, we do the least significant 3 limbs similarly (of course,
    the result is 512-bit, so with 56, 48, 40 instead of 0, 8, 16 on LE)
    and then do the separate_ext loop:
      <bb 3> [local count: 1073741824]:
      # _35 = PHI <4(2), _36(3)>
      VIEW_CONVERT_EXPR<unsigned long[8]>(a)[_35] = _34;
      _36 = _35 + 18446744073709551615;
      if (_36 != 0)
        goto <bb 3>; [0.05%]
      else
        goto <bb 4>; [99.95%]
    That is wrong, we start on limb 4 (i.e. offset 32) and go down until
    1 (i.e. offset 8) rather than 0 (i.e. offset 0), because like in LE in this
    case we compare idx_next (idx + increment, in BE case -1) instead of idx.
    There was already code to handle specially BE rem != 0 case.
    Note, the starting offset is
              else if (i == (bo_shift != 0))
                idx = create_loop (size_int (bo_idx
                                             + (bitint_big_endian
                                                ? total - 1 - start - i
                                                : start + i)), &idx_next);
    and we want to perform the same number of iterations on LE and BE,
    just the LE offsets bo_idx + start + i + I for I going from 0 up until
    bo_idx + start + i + I == bo_idx + end - 1 inclusive, while for BE
    they should be bo_idx + total - 1 - start - i - I for I going from 0
    up until bo_idx + total - 1 - start - i - I == bo_idx + total - 1 - end + 1
    inclusive.  So, the BE correction is to add total - 1 and subtract instead
    of add the start + i up to end - 1 values.
    
    So, this patch uses the exact same BE correction, with one exception.
    If bo_idx + total - 1 - end + 1 is 0 (which is quite the common case,
    when bo_idx is 0 (e.g. whenever it is not a bit-field store) and bo_shift
    isn't non-zero either (again, e.g. no bit-field store) and rem == 0),
    I think comparing idx != 0 instead of idx_next != (size_t) -1 is faster
    (plus we'd in the evaluation need to care about types of all the addends,
    do everything say in unsigned HOST_WIDE_INT and then map all ones in UHWI
    to all ones in target size_t).
    
    2026-09-18  Jakub Jelinek  <[email protected]>
                Manjunath Matti  <[email protected]>
    
            PR middle-end/127378
            * gimple-lower-bitint.cc (bitint_large_huge::lower_mergeable_stmt): 
On
            big endian, compare idx_next in separate_ext loop against
            bo_idx + total - 1 - end, and if that wraps around, compare idx
            against 0 instead.
    
            * gcc.dg/bitint-145.c: New test.
    
    Reviewed-by: Richard Biener <[email protected]>
    (cherry picked from commit d9d078c216150bbe0397ded72f035c35b476e88e)

Diff:
---
 gcc/gimple-lower-bitint.cc        | 11 +++++++----
 gcc/testsuite/gcc.dg/bitint-145.c | 35 +++++++++++++++++++++++++++++++++++
 2 files changed, 42 insertions(+), 4 deletions(-)

diff --git a/gcc/gimple-lower-bitint.cc b/gcc/gimple-lower-bitint.cc
index e6716acb7ed0..6d1bb01cbed6 100644
--- a/gcc/gimple-lower-bitint.cc
+++ b/gcc/gimple-lower-bitint.cc
@@ -3270,15 +3270,18 @@ bitint_large_huge::lower_mergeable_stmt (gimple *stmt, 
tree_code &cmp_code,
                                       bitint_big_endian
                                       ? size_int (-1) : size_one_node);
              insert_before (g);
-             if (bitint_big_endian && rem != 0)
-               g = gimple_build_cond (NE_EXPR, idx,
-                                      size_int (bo_idx + 1),
+             /* For big-endian, if bo_idx + total - 1 - end is all ones, then
+                compare idx (which is equal to idx_next + 1) against 0
+                instead.  */
+             if (bitint_big_endian && bo_idx + total - end == 0)
+               g = gimple_build_cond (NE_EXPR, idx, size_zero_node,
                                       NULL_TREE, NULL_TREE);
              else
                g = gimple_build_cond (NE_EXPR, idx_next,
                                       size_int (bo_idx
                                                 + (bitint_big_endian
-                                                   ? 0 : end)),
+                                                   ? total - 1 - end
+                                                   : end)),
                                       NULL_TREE, NULL_TREE);
              insert_before (g);
              m_gsi = gsi_for_stmt (stmt);
diff --git a/gcc/testsuite/gcc.dg/bitint-145.c 
b/gcc/testsuite/gcc.dg/bitint-145.c
new file mode 100644
index 000000000000..8434f5173864
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/bitint-145.c
@@ -0,0 +1,35 @@
+/* PR middle-end/127378 */
+/* { dg-do run { target bitint } } */
+/* { dg-options "-std=c23 -O2" } */
+
+#if __BITINT_MAXWIDTH__ >= 768
+unsigned _BitInt(512) a;
+unsigned _BitInt(768) b = -1;
+
+__attribute__((noipa)) void
+foo (unsigned _BitInt(135) *p)
+{
+  a = (_BitInt(135)) (p[0] + p[1]);
+}
+
+__attribute__((noipa)) void
+bar (unsigned _BitInt(200) *p)
+{
+  b = p[0] + p[1];
+}
+#endif
+
+int
+main ()
+{
+#if __BITINT_MAXWIDTH__ >= 768
+  static unsigned _BitInt(135) p[2] = { -1, 0 };
+  static unsigned _BitInt(200) q[2] = { 1, 2 };
+  foo (p);
+  if (a != (unsigned _BitInt(512)) -1)
+    __builtin_abort ();
+  bar (q);
+  if (b != 3)
+    __builtin_abort ();
+#endif
+}

Reply via email to