"Roger Sayle" <ro...@nextmovesoftware.com> writes:
> This patch adds compile-time evaluation of signed saturating left shift
> (SS_ASHIFT) and unsigned saturating left shift (US_ASHIFT) to simplify-rtx's
> simplify_const_binary_operation.  US_ASHIFT saturates to the maximum
> unsigned value on overflow (which occurs when the shift is greater than
> the leading zero count), while SS_ASHIFT saturates on overflow to the
> maximum signed value for positive arguments, and the minimum signed value
> for negative arguments (which occurs when the shift count is greater than
> the number of leading redundant sign bits, clrsb).  This suggests
> some additional simplifications that this patch implements in
> simplify_binary_operation_1; us_ashift:HI of 0xffff remains 0xffff
> (much like any ashift of 0x0000 remains 0x0000), and ss_ashift:HI of
> 0x7fff remains 0x7ffff, and of 0x8000 remains 0x8000.
>
> Conveniently the bfin backend provides instructions/built-ins that allow
> this functionality to be tested.  The two functions below
>
> short stest_sat_max() { return __builtin_bfin_shl_fr1x16(10000,8); }
> short stest_sat_min() { return __builtin_bfin_shl_fr1x16(-10000,8); }
>
> previously on bfin-elf with -O2 generated:
>
> _stest_sat_max:
>         nop;
>         nop;
>         R0 = 10000 (X);
>         R0 = R0 << 8 (V,S);
>         rts;
>
> _stest_sat_min:
>         nop;
>         nop;
>         R0 = -10000 (X);
>         R0 = R0 << 8 (V,S);
>         rts;
>
> With this patch, bfin-elf now generates:
>
> _stest_sat_max:
>         nop;
>         nop;
>         nop;
>         R0 = 32767 (X);
>         rts;
>
> _stest_sat_min:
>         nop;
>         nop;
>         nop;
>         R0 = -32768 (X);
>         rts;
>
> This patch has been tested on x86_64-pc-linux-gnu with make bootstrap and
> make -k check with no new failures, and on a cross-compiler to bfin-elf
> with no regressions.  Ok for mainline?
>
>
> 2021-10-25  Roger Sayle  <ro...@nextmovesoftware.com>
>
> gcc/ChangeLog
>       * simplify-rtx (simplify_binary_operation_1) [SS_ASHIFT]: Simplify
>       shifts of the mode's smin_value and smax_value when the bit count
>       operand doesn't have side-effects.
>       [US_ASHIFT]: Likewise, simplify shifts of the mode's umax_value
>       when the bit count operand doesn't have side-effects.
>       (simplify_const_binary_operation) [SS_ASHIFT, US_ASHIFT]: Perform
>       compile-time evaluation of saturating left shifts with constant
>       arguments.
>
> gcc/testsuite/ChangeLog
>       * gcc.target/bfin/ssashift-1.c: New test case.
>
>
> Roger
> --
>
> diff --git a/gcc/simplify-rtx.c b/gcc/simplify-rtx.c
> index 2bb18fb..5903d55 100644
> --- a/gcc/simplify-rtx.c
> +++ b/gcc/simplify-rtx.c
> @@ -4064,9 +4064,25 @@ simplify_context::simplify_binary_operation_1 
> (rtx_code code,
>       }
>        break;
>  
> -    case ASHIFT:
>      case SS_ASHIFT:
> +      if (CONST_INT_P (trueop0)
> +       && HWI_COMPUTABLE_MODE_P (mode)
> +       && (UINTVAL (trueop0) == (GET_MODE_MASK (mode) >> 1)
> +           || mode_signbit_p (mode, trueop0))
> +       && ! side_effects_p (op1))
> +     return op0;
> +      goto simplify_ashift;
> +
>      case US_ASHIFT:
> +      if (CONST_INT_P (trueop0)
> +       && HWI_COMPUTABLE_MODE_P (mode)
> +       && UINTVAL (trueop0) == GET_MODE_MASK (mode)
> +       && ! side_effects_p (op1))
> +     return op0;
> +      /* FALLTHRU */
> +
> +    case ASHIFT:
> +simplify_ashift:
>        if (trueop1 == CONST0_RTX (mode))
>       return op0;
>        if (trueop0 == CONST0_RTX (mode) && ! side_effects_p (op1))
> @@ -5004,6 +5020,8 @@ simplify_const_binary_operation (enum rtx_code code, 
> machine_mode mode,
>       case LSHIFTRT:
>       case ASHIFTRT:
>       case ASHIFT:
> +     case SS_ASHIFT:
> +     case US_ASHIFT:
>         {
>           wide_int wop1 = pop1;
>           if (SHIFT_COUNT_TRUNCATED)
> @@ -5025,6 +5043,27 @@ simplify_const_binary_operation (enum rtx_code code, 
> machine_mode mode,
>               result = wi::lshift (pop0, wop1);
>               break;
>  
> +           case SS_ASHIFT:
> +             if (wi::leu_p (wop1, wi::clrsb (pop0)))
> +               result = wi::lshift (pop0, wop1);
> +             else if (wi::neg_p (pop0))
> +               result = wi::min_value (GET_MODE_PRECISION (int_mode),
> +                                       SIGNED);
> +             else
> +               result = wi::max_value (GET_MODE_PRECISION (int_mode),
> +                                       SIGNED);
> +             break;
> +
> +           case US_ASHIFT:
> +             if (wi::eq_p (pop0, 0))
> +               result = pop0;
> +             else if (wi::leu_p (wop1, wi::clz (pop0)))
> +               result = wi::lshift (pop0, wop1);

I guess in the SS_ASHIFT case we're relying on clrsb (0) doing something
sensible (return the number of bits minus 1, which it does).  We could
also rely on wi::clz doing something sensible here (like we already do in
wi::min_precision) and remove the special case for zero.  Either way
is fine though.

> +             else
> +               result = wi::max_value (GET_MODE_PRECISION (int_mode),
> +                                       UNSIGNED);
> +             break;

Very minor clean-up, but: the modes can be passed directly to
wi::min_value and wi::max_value, due to overloads in rtl.h.

OK with that change (and with or without dropping the zero check).

Thanks,
Richard

> +
>             default:
>               gcc_unreachable ();
>             }
> diff --git a/gcc/testsuite/gcc.target/bfin/ssashift-1.c 
> b/gcc/testsuite/gcc.target/bfin/ssashift-1.c
> new file mode 100644
> index 0000000..aba90a6
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/bfin/ssashift-1.c
> @@ -0,0 +1,52 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2" } */
> +
> +int test_ok_pos()
> +{
> +  int x = 100;
> +  return __builtin_bfin_shl_fr1x32(x,24);
> +}
> +
> +int test_ok_neg()
> +{
> +  int x = -100;
> +  return __builtin_bfin_shl_fr1x32(x,24);
> +}
> +
> +int test_sat_max()
> +{
> +  int x = 10000;
> +  return __builtin_bfin_shl_fr1x32(x,24);
> +}
> +
> +int test_sat_min()
> +{
> +  int x = -10000;
> +  return __builtin_bfin_shl_fr1x32(x,24);
> +}
> +
> +short stest_ok_pos()
> +{
> +  short x = 100;
> +  return __builtin_bfin_shl_fr1x16(x,8);
> +}
> +
> +short stest_ok_neg()
> +{
> +  short x = -100;
> +  return __builtin_bfin_shl_fr1x16(x,8);
> +}
> +
> +short stest_sat_max()
> +{
> +  short x = 10000;
> +  return __builtin_bfin_shl_fr1x16(x,8);
> +}
> +
> +short stest_sat_min()
> +{
> +  short x = -10000;
> +  return __builtin_bfin_shl_fr1x16(x,8);
> +}
> +/* { dg-final { scan-assembler-not "\\(S\\)" } } */
> +/* { dg-final { scan-assembler-not "\\(V,S\\)" } } */

Reply via email to