On Thu, 1 Oct 2026, Jakub Jelinek wrote:

> Hi!
> 
> The following testcase is miscompiled for 2 reasons.
> 
> One missed optimization is that fold_stmt_1 doesn't seem to attempt to
> use fold_const_call for internal functions into whose argument a constant
> is propagated.  For normal builtins, I think fold_stmt_1 -> gimple_fold_call
> -> gimple_fold_builtin -> fold_call_stmt -> fold_builtin_n ->
> fold_builtin_{1,2,3} -> fold_const_call optimizes it, but for internal
> fns we just have selected set of optimizations we do on them but
> fold_const_call isn't among those.  That would be certainly nice to get
> fixed, but this patch doesn't do that.

Indeed, but the following should already do it:

  /* Dispatch to pattern-based folding.  */
  if (!inplace
      || is_gimple_assign (stmt)
      || gimple_code (stmt) == GIMPLE_COND) 
    {
      gimple_seq seq = NULL;
      gimple_match_op res_op;
      if (gimple_simplify (stmt, &res_op, inplace ? NULL : &seq,
                           valueize, valueize)
          && replace_stmt_with_simplification (gsi, &res_op, &seq, 
inplace,
                                               dce_worklist))
        changed = true;

via

/* Helper that matches and simplifies the toplevel result from 
   a gimple_simplify run (where we don't want to build 
   a stmt in case it's used in in-place folding).  Replaces
   RES_OP with a simplified and/or canonicalized result and
   returns whether any change was made.  */
   
static bool
gimple_resimplify1 (gimple_seq *seq, gimple_match_op *res_op,
                    tree (*valueize)(tree))
{     
  if (constant_for_folding (res_op->ops[0]))
    { 
      tree tem = NULL_TREE;
      if (res_op->code.is_tree_code ())
        {
          auto code = tree_code (res_op->code);
          if (IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (code))
              && TREE_CODE_LENGTH (code) == 1)
            tem = const_unop (code, res_op->type, res_op->ops[0]);
        }
      else
        tem = fold_const_call (combined_fn (res_op->code), res_op->type,
                               res_op->ops[0]); 


> The other problem is forgotten flag.  Because of the above missed
> optimization we end up with .BITREVERSE (1) call before bitintlower, where
> that 1 is unsigned _BitInt(129).  If there is a partial limb (like in
> this case 1 bit), we emit handling of the result partial limb in straight
> line code (but that actually reads some bits from the least significant limb
> of the function argument), and then have a loop to handle the remaining
> limbs.  We use there handle_operand with an index which is not constant
> in the loop (obviously) and using this read any limbs in the argument in
> reverse order, including the partial limb.
> Now, handle_operand has an optimization, for most of the lowerings we have
> a guarantee that the partial limb is accessed only in straight line code with
> a constant index and e.g. for constants reading the partial limb in that
> case simply folds into the constant value of that partial limb.  So, we
> optimize and emit into .rodata only fewer limbs (all but the partial one)
> unless it is one of the even more optimized ones (where we treat 0, all ones
> or values fitting into fewer limbs by always using a constant or by
> extending at runtime).  The exception to this has been the LSHIFT_EXPR
> lowering and we have the m_var_msb flag to tell we need all the bits
> including the partial limb.  Given the way .BSWAP/.BITREVERSE expansion
> works, we need that flag for those too.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

OK.

> 2026-10-01  Jakub Jelinek  <[email protected]>
> 
>       PR middle-end/127669
>       * gimple-lower-bitint.cc (bitint_large_huge::lower_bswap_bitreverse):
>       Set m_var_msb to true.
> 
>       * gcc.dg/torture/bitint-110.c: New test.
> 
> --- a/gcc/gimple-lower-bitint.cc      2026-09-18 11:03:21.676039449 +0200
> +++ b/gcc/gimple-lower-bitint.cc      2026-09-30 14:29:15.870162767 +0200
> @@ -6172,6 +6172,9 @@ bitint_large_huge::lower_bswap_bitrevers
>    unsigned prec = TYPE_PRECISION (type);
>    tree p = build_int_cst (sizetype,
>                         prec / limb_prec - (prec % limb_prec == 0));
> +  /* For .BSWAP/.BITREVERSE, we can use handle_operand with non-INTEGER_CST
> +     idx even to access the most significant partial limb.  */
> +  m_var_msb = true;
>    /* For IFN .BSWAP or .BITREVERSE and
>       FN corresponding __builtin_bswapN or __builtin_bitreverseN where N
>       is limb_prec, lower
> --- a/gcc/testsuite/gcc.dg/torture/bitint-110.c       2026-09-30 
> 14:29:49.463717815 +0200
> +++ b/gcc/testsuite/gcc.dg/torture/bitint-110.c       2026-09-30 
> 14:31:29.115397910 +0200
> @@ -0,0 +1,30 @@
> +/* PR middle-end/127669 */
> +/* { dg-do run { target bitint } } */
> +
> +#if __BITINT_MAXWIDTH__ >= 129
> +typedef unsigned _BitInt(129) T;
> +
> +[[gnu::noipa]] static T
> +foo (T v)
> +{
> +  return v;
> +}
> +
> +static T
> +bar ()
> +{
> +  return 1;
> +}
> +#endif
> +
> +int
> +main ()
> +{
> +#if __BITINT_MAXWIDTH__ >= 129
> +  T x = __builtin_bitreverseg ((T) 1);
> +  T y = __builtin_bitreverseg (bar ());
> +  T z = __builtin_bitreverseg (foo (1));
> +  if (x != y || x != z)
> +    __builtin_abort ();
> +#endif
> +}
> 
>       Jakub
> 
> 

-- 
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Stefan Gaiser, Jochen Jaser, Abhinav Puri; (HRB 36809, AG Nuernberg)

Reply via email to