The attached patch takes care of the problem described below.

(I tried to stick the new code into the regular combine-simplify
stuff, but that does not work well.  At the time when
combine_simplify_rtx is called, it's not known whether
change_zero_ext will expand the ZERO_EXTRACT to "(and (lshiftrt"
and does not know the constant it could pull out of the zero
extract, so this is why the patch does it in change_zero_ext().)

To take the code wrapping the ZERO_EXTRACT into account it was
necessary to move the code dealing with the set source to a
separate function that is called recursively.

The patch has been bootstrapped and regression tested on s390 and
s390x and has no noticeably bad side effects so far.

P.S.:  Is it necessary to pass PITER by pointer or could it be
passed simply by value?

On Wed, Dec 07, 2016 at 09:21:35PM +0100, Dominik Vogt wrote:
> I guess this has something to do with the extra logic for
> replacing zero_extract with "(and (lshiftrt ...".  In one specific
> situation, the replacement generates something odd.  Initially,
> there are these insns:
> 
> --
> (insn 6 3 7 2 (set (reg:DI 67)
>         (lshiftrt:DI (reg:DI 2 %r2 [ v_x ])
>             (const_int 12 [0xc])))
> 
> (insn 7 6 8 2 (parallel [
>             (set (reg:SI 66 [ v_conv ])
>                 (and:SI (subreg:SI (reg:DI 67) 4)
>                     (const_int -4 [0xfffffffffffffffc])))
>             (clobber (reg:CC 33 %cc))
> --
> 
> Combine tries:
> 
> --
> Trying 6 -> 7:
> Failed to match this instruction:
> (parallel [
>         (set (reg:SI 66 [ v_conv ])
>             (and:SI (subreg:SI (zero_extract:DI (reg:DI 2 %r2 [
> v_x ])
>                         (const_int 32 [0x20])
>                         (const_int 20 [0x14])) 4)
>                 (const_int -4 [0xfffffffffffffffc])))
>         (clobber (reg:CC 33 %cc))
>     ])
> ...
>         (const_int -4 [0xfffffffffffffffc])))
> Failed to match this instruction:
> (set (reg:SI 66 [ v_conv ])
>     (and:SI (subreg:SI (and:DI (lshiftrt:DI (reg:DI 2 %r2 [ v_x ])
>     ^^^^^^^^^^^^^^^^^^^^^^^^^^
>                     (const_int 12 [0xc]))
>                 (const_int 4294967295 [0xffffffff])) 4)
>         (const_int -4 [0xfffffffffffffffc])))
> 
> --
> 
> The two "and"s could be combined into a single one.  Now, I can of
> course add some pattern to the backend to deal with it, but this
> should probably be handled in combin?
> 
> (The C code is (with the proper types):)
> --
> #define i64 signed long long 
> #define ui64 unsigned long long 
> #define i32 signed int 
> #define ui32 unsigned int 
> i32 f43 (i64 v_x) 
> { 
>   i64 v_shr3 = ((ui64)v_x) >> 12; 
>   i32 v_shr3_tr = (ui32)v_shr3; 
>   i32 v_conv = v_shr3_tr & -4; 
>   return v_conv; 
> } 

Ciao

Dominik ^_^  ^_^

-- 

Dominik Vogt
IBM Germany
gcc/ChangeLog-change_zero_ext-4

        * combine.c (change_zero_ext, change_zero_ext_src): Move code dealing
        with the source to new function.
        Replace "(and (subreg (and ..." with "(and (subreg (...".
>From d6926e6ae23ea056223e5053220c398fa2b06f0d Mon Sep 17 00:00:00 2001
From: Dominik Vogt <v...@linux.vnet.ibm.com>
Date: Fri, 9 Dec 2016 19:13:45 +0100
Subject: [PATCH] combine: Simplify "(and (subreg (zero_extract" in
 change_zero_ext.

When change_zero_ext expands
  (and:SI (subreg:SI (zero_extract:DI ...
it ends up with two nested ANDs:
  (and:SI (subreg:SI (and:DI (lshiftrt:DI ...

Which requires special support from the backend.  So, merge the inner AND
constant into the outer and dump the inner AND.

At the time make_compound_statement is called, the code does not know that the
zero_extract may be expanded to "(and (lshiftrt" later (in change_zero_ext) and
should not make that simplification itself.
---
 gcc/combine.c | 172 +++++++++++++++++++++++++++++++++++++---------------------
 1 file changed, 109 insertions(+), 63 deletions(-)

diff --git a/gcc/combine.c b/gcc/combine.c
index 9c7999b..afa20a7 100644
--- a/gcc/combine.c
+++ b/gcc/combine.c
@@ -11216,81 +11216,127 @@ recog_for_combine_1 (rtx *pnewpat, rtx_insn *insn, 
rtx *pnotes)
   return insn_code_number;
 }
 
-/* Change every ZERO_EXTRACT and ZERO_EXTEND of a SUBREG that can be
-   expressed as an AND and maybe an LSHIFTRT, to that formulation.
-   Return whether anything was so changed.  */
-
+/* Helper function for change_zero_ext that processes a single
+   subexpression of the SET source passed in through PITER.  */
 static bool
-change_zero_ext (rtx pat)
+change_zero_ext_src (subrtx_ptr_iterator *piter)
 {
   bool changed = false;
-  rtx *src = &SET_SRC (pat);
+  rtx x = ***piter;
+  machine_mode mode = GET_MODE (x);
+  int size;
+
+  if (GET_CODE (x) == AND
+      && CONST_INT_P (XEXP (x, 1))
+      && GET_CODE (XEXP (x, 0)) == SUBREG
+      && subreg_lowpart_p (XEXP (x, 0))
+      && GET_CODE (XEXP (XEXP (x, 0), 0)) == ZERO_EXTRACT
+      && GET_MODE_PRECISION (mode)
+         < GET_MODE_PRECISION (GET_MODE (XEXP (XEXP (x, 0), 0))))
+    {
+      subrtx_ptr_iterator::array_type array;
+      subrtx_ptr_iterator tmp_iter (array, &XEXP (XEXP (x, 0), 0),
+                                   NONCONST_BOUNDS);
+
+      change_zero_ext_src (&tmp_iter);
+      rtx sr = XEXP (x, 0);
+      rtx inner = XEXP (sr, 0);
+      if (GET_CODE (inner) == AND
+         && CONST_INT_P (XEXP (inner, 1)))
+       {
+         machine_mode inner_mode = GET_MODE (inner);
+
+         /* Merge the inner AND into the outer.  */
+         int pmax
+           = MAX (GET_MODE_PRECISION (mode), GET_MODE_PRECISION (inner_mode));
+         int pmin
+           = MIN (GET_MODE_PRECISION (mode), GET_MODE_PRECISION (inner_mode));
+
+         wide_int mask = wi::mask (pmin, false, pmax);
+         mask &= wi::uhwi (UINTVAL (XEXP (x, 1)), pmax);
+         mask &= wi::uhwi (UINTVAL (XEXP (inner, 1)), pmax);
+         rtx y = gen_rtx_SUBREG (mode, XEXP (inner, 0), SUBREG_BYTE (sr));
+         x = gen_rtx_AND (mode, y, immed_wide_int_const (mask, mode));
+         SUBST (***piter, x);
+         return true;
+       }
+      else
+       return false;
+    }
 
-  subrtx_ptr_iterator::array_type array;
-  FOR_EACH_SUBRTX_PTR (iter, array, src, NONCONST)
+  if (GET_CODE (x) == ZERO_EXTRACT
+      && CONST_INT_P (XEXP (x, 1))
+      && CONST_INT_P (XEXP (x, 2))
+      && GET_MODE (XEXP (x, 0)) != VOIDmode
+      && GET_MODE_PRECISION (GET_MODE (XEXP (x, 0)))
+        <= GET_MODE_PRECISION (mode))
     {
-      rtx x = **iter;
-      machine_mode mode = GET_MODE (x);
-      int size;
-
-      if (GET_CODE (x) == ZERO_EXTRACT
-         && CONST_INT_P (XEXP (x, 1))
-         && CONST_INT_P (XEXP (x, 2))
-         && GET_MODE (XEXP (x, 0)) != VOIDmode
-         && GET_MODE_PRECISION (GET_MODE (XEXP (x, 0)))
-             <= GET_MODE_PRECISION (mode))
-       {
-         machine_mode inner_mode = GET_MODE (XEXP (x, 0));
+      machine_mode inner_mode = GET_MODE (XEXP (x, 0));
 
-         size = INTVAL (XEXP (x, 1));
+      size = INTVAL (XEXP (x, 1));
 
-         int start = INTVAL (XEXP (x, 2));
-         if (BITS_BIG_ENDIAN)
-           start = GET_MODE_PRECISION (inner_mode) - size - start;
+      int start = INTVAL (XEXP (x, 2));
+      if (BITS_BIG_ENDIAN)
+       start = GET_MODE_PRECISION (inner_mode) - size - start;
 
-         if (start)
-           x = gen_rtx_LSHIFTRT (inner_mode, XEXP (x, 0), GEN_INT (start));
-         else
-           x = XEXP (x, 0);
-         if (mode != inner_mode)
-           x = gen_lowpart_SUBREG (mode, x);
-       }
-      else if (GET_CODE (x) == ZERO_EXTEND
-              && SCALAR_INT_MODE_P (mode)
-              && GET_CODE (XEXP (x, 0)) == SUBREG
-              && SCALAR_INT_MODE_P (GET_MODE (SUBREG_REG (XEXP (x, 0))))
-              && !paradoxical_subreg_p (XEXP (x, 0))
-              && subreg_lowpart_p (XEXP (x, 0)))
-       {
-         size = GET_MODE_PRECISION (GET_MODE (XEXP (x, 0)));
-         x = SUBREG_REG (XEXP (x, 0));
-         if (GET_MODE (x) != mode)
-           x = gen_lowpart_SUBREG (mode, x);
-       }
-      else if (GET_CODE (x) == ZERO_EXTEND
-              && SCALAR_INT_MODE_P (mode)
-              && REG_P (XEXP (x, 0))
-              && HARD_REGISTER_P (XEXP (x, 0))
-              && can_change_dest_mode (XEXP (x, 0), 0, mode))
-       {
-         size = GET_MODE_PRECISION (GET_MODE (XEXP (x, 0)));
-         x = gen_rtx_REG (mode, REGNO (XEXP (x, 0)));
-       }
+      if (start)
+       x = gen_rtx_LSHIFTRT (inner_mode, XEXP (x, 0), GEN_INT (start));
       else
-       continue;
-
-      if (!(GET_CODE (x) == LSHIFTRT
-           && CONST_INT_P (XEXP (x, 1))
-           && size + INTVAL (XEXP (x, 1)) == GET_MODE_PRECISION (mode)))
-       {
-         wide_int mask = wi::mask (size, false, GET_MODE_PRECISION (mode));
-         x = gen_rtx_AND (mode, x, immed_wide_int_const (mask, mode));
-       }
+       x = XEXP (x, 0);
+      if (mode != inner_mode)
+       x = gen_lowpart_SUBREG (mode, x);
+    }
+  else if (GET_CODE (x) == ZERO_EXTEND
+          && SCALAR_INT_MODE_P (mode)
+          && GET_CODE (XEXP (x, 0)) == SUBREG
+          && SCALAR_INT_MODE_P (GET_MODE (SUBREG_REG (XEXP (x, 0))))
+          && !paradoxical_subreg_p (XEXP (x, 0))
+          && subreg_lowpart_p (XEXP (x, 0)))
+    {
+      size = GET_MODE_PRECISION (GET_MODE (XEXP (x, 0)));
+      x = SUBREG_REG (XEXP (x, 0));
+      if (GET_MODE (x) != mode)
+       x = gen_lowpart_SUBREG (mode, x);
+    }
+  else if (GET_CODE (x) == ZERO_EXTEND
+          && SCALAR_INT_MODE_P (mode)
+          && REG_P (XEXP (x, 0))
+          && HARD_REGISTER_P (XEXP (x, 0))
+          && can_change_dest_mode (XEXP (x, 0), 0, mode))
+    {
+      size = GET_MODE_PRECISION (GET_MODE (XEXP (x, 0)));
+      x = gen_rtx_REG (mode, REGNO (XEXP (x, 0)));
+    }
+  else
+    return changed;
 
-      SUBST (**iter, x);
-      changed = true;
+  if (!(GET_CODE (x) == LSHIFTRT
+       && CONST_INT_P (XEXP (x, 1))
+       && size + INTVAL (XEXP (x, 1)) == GET_MODE_PRECISION (mode)))
+    {
+      wide_int mask = wi::mask (size, false, GET_MODE_PRECISION (mode));
+      x = gen_rtx_AND (mode, x, immed_wide_int_const (mask, mode));
     }
 
+  SUBST (***piter, x);
+  return true;
+}
+
+/* Change every ZERO_EXTRACT and ZERO_EXTEND of a SUBREG that can be
+   expressed as an AND and maybe an LSHIFTRT, to that formulation.
+   Peel of SIGN_EXTEND from expressions containing a ZERO_EXTRACT if the
+   SIGN_EXTEND is a no-op.  Return whether anything was so changed.  */
+
+static bool
+change_zero_ext (rtx pat)
+{
+  bool changed = false;
+  rtx *src = &SET_SRC (pat);
+
+  subrtx_ptr_iterator::array_type array;
+  FOR_EACH_SUBRTX_PTR (iter, array, src, NONCONST)
+    changed |= change_zero_ext_src (&iter);
+
   if (changed)
     FOR_EACH_SUBRTX_PTR (iter, array, src, NONCONST)
       maybe_swap_commutative_operands (**iter);
-- 
2.3.0

Reply via email to