[Bug tree-optimization/126476] New: Wrong code with bitint shifts

"ktkachov at gcc dot gnu.org via Gcc-bugs" <[email protected]>
Newsgroups gmane.comp.gcc.bugs
Message-ID <[email protected]/bugzilla/>
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126476

            Bug ID: 126476
           Summary: Wrong code with bitint shifts
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Keywords: wrong-code
          Severity: normal
          Priority: P3
         Component: tree-optimization
          Assignee: unassigned at gcc dot gnu.org
          Reporter: ktkachov at gcc dot gnu.org
                CC: jakub at gcc dot gnu.org
  Target Milestone: ---

__attribute__((noipa)) int
f (unsigned _BitInt(4) n)
{
  return ((1ULL << n) & (1ULL << 20)) != 0;
}

__attribute__((noipa)) int
g (unsigned _BitInt(4) n)
{
  return (((1ULL << 40) >> n) & (1ULL << 20)) != 0;
}

int
main (void)
{
  for (unsigned i = 0; i < 16; i++)
    {
      unsigned _BitInt(4) n = (unsigned _BitInt(4)) i;

      if (f (n) != 0)
        __builtin_abort ();
      if (g (n) != 0)
        __builtin_abort ();
    }
  return 0;
}

aborts with GCC but passes with Clang.

     (for cmp (ne eq)
          icmp (eq ne)
      (simplify
       (cmp (bit_and (lshift integer_pow2p@1 @0) integer_pow2p@2)
integer_zerop)
        (with { int c1 = wi::clz (wi::to_wide (@1));
                int c2 = wi::clz (wi::to_wide (@2)); }
         (if (c1 < c2)
          { constant_boolean_node (cmp == NE_EXPR ? false : true, type); }
          (icmp @0 { build_int_cst (TREE_TYPE (@0), c1 - c2); }))))

   ((C << x) & D) != 0 holds exactly when x equals the bit-position distance
   c1 - c2, and the rule materialises that distance with
   build_int_cst (TREE_TYPE (@0), c1 - c2).  TREE_TYPE (@0) is the type of the
   *shift count*, which nothing requires to be able to hold c1 - c2.
   build_int_cst truncates modulo 2^precision silently.

   When the distance does not fit but its residue is a legal shift count, an
   expression that is false for every valid x becomes a comparison that is true
   for one of them.  The rule already has the right answer to hand: an
   unrepresentable distance means the AND is unconditionally zero, which is
   exactly the c1 < c2 branch.  A fits-in-type check routing there is missing.

   Reachable because C23 does not apply the integer promotions to bit-precise
   types, so a _BitInt shift count keeps its narrow type into GIMPLE.

   Here c1 = clz (1) = 63 and c2 = clz (1 << 20) = 43, so the distance is 20,
   which does not fit in unsigned _BitInt(4); 20 mod 16 = 4, and the fold
   emits `n == 4`.  But (1ULL << 4) & (1ULL << 20) is 0.

   Every n in [0, 15] is a legal count for a 64-bit shift, so the program has
   no undefined behaviour.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.