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.

Reply via email to