PR #104914

On TRULY_NOOP_TRUNCATION_MODES_P (DImode, SImode)) == true platforms,
zero_extract (SI, SI) can be sign-extended.  So, if a zero_extract (DI,
DI) following with an sign_extend(SI, DI) can be merged to a single
zero_extract (SI, SI).

gcc/ChangeLog:
        PR: 104914.
        * combine.cc (try_combine): Combine zero_extract (DI, DI) and
          following sign_extend (DI, SI) for
          TRULY_NOOP_TRUNCATION_MODES_P (DImode, SImode)) == true.
          (subst): Allow replacing reg(DI) with subreg(SI (reg DI))
          if to is SImode and from is DImode for
          TRULY_NOOP_TRUNCATION_MODES_P (DImode, SImode)) == true.

gcc/testsuite/ChangeLog:
        PR: 104914.
        * gcc.target/mips/pr104914.c: New testcase.
---
 gcc/combine.cc                           | 88 ++++++++++++++++++++----
 gcc/testsuite/gcc.target/mips/pr104914.c | 17 +++++
 2 files changed, 90 insertions(+), 15 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/mips/pr104914.c

diff --git a/gcc/combine.cc b/gcc/combine.cc
index e46d202d0a7..701b7c33b17 100644
--- a/gcc/combine.cc
+++ b/gcc/combine.cc
@@ -3294,15 +3294,64 @@ try_combine (rtx_insn *i3, rtx_insn *i2, rtx_insn *i1, 
rtx_insn *i0,
       n_occurrences = 0;               /* `subst' counts here */
       subst_low_luid = DF_INSN_LUID (i2);
 
-      /* If I1 feeds into I2 and I1DEST is in I1SRC, we need to make a unique
-        copy of I2SRC each time we substitute it, in order to avoid creating
-        self-referential RTL when we will be substituting I1SRC for I1DEST
-        later.  Likewise if I0 feeds into I2, either directly or indirectly
-        through I1, and I0DEST is in I0SRC.  */
-      newpat = subst (PATTERN (i3), i2dest, i2src, false, false,
-                     (i1_feeds_i2_n && i1dest_in_i1src)
-                     || ((i0_feeds_i2_n || (i0_feeds_i1_n && i1_feeds_i2_n))
-                         && i0dest_in_i0src));
+      /* Try to combine zero_extract (DImode) and sign_extend (SImode to 
DImode)
+        for TARGET_TRULY_NOOP_TRUNCATION.  The RTL may look like:
+
+        (insn 10 49 11 2 (set (zero_extract:DI (reg/v:DI 200 [ val ])
+               (const_int 8 [0x8])
+               (const_int 0 [0]))
+            (subreg:DI (reg:QI 202 [ *buf_8(D) ]) 0)) "xx.c":4:29 278 {*insvdi}
+            (expr_list:REG_DEAD (reg:QI 202 [ *buf_8(D) ]) (nil)))
+        (insn 11 10 12 2 (set (reg/v:DI 200 [ val ])
+
+        (sign_extend:DI (subreg:SI (reg/v:DI 200 [ val ]) 0))) 238 
{extendsidi2}
+            (nil))
+
+        Since these architectures (MIPS64 as an example), the 32bit operation
+        instructions will sign-extend the reuslt to 64bit.  The result can be:
+
+        (insn 10 49 11 2 (set (zero_extract:SI (subreg:SI (reg/v:DI 200 [ val 
]) 0)
+              (const_int 8 [0x8])
+              (const_int 0 [0]))
+            (subreg:SI (reg:QI 202 [ *buf_8(D) ]) 0)) "xx.c":4:29 280 {*insvsi}
+            (expr_list:REG_DEAD (reg:QI 202 [ *buf_8(D) ]) (nil)))
+       */
+      if (i0 == 0 && i1 == 0 && i3 != 0 && i2 != 0 && GET_CODE (i2) == INSN
+         && GET_CODE (i3) == INSN && GET_CODE (PATTERN (i2)) == SET
+         && GET_CODE (PATTERN (i3)) == SET
+         && GET_CODE (SET_DEST (single_set (i2))) == ZERO_EXTRACT
+         && GET_CODE (SET_SRC (single_set (i3))) == SIGN_EXTEND
+         && SUBREG_P (XEXP (SET_SRC (single_set (i3)), 0))
+         && REGNO (SUBREG_REG (XEXP (SET_SRC (single_set (i3)), 0)))
+                == REGNO (SET_DEST (single_set (i3)))
+         && REGNO (XEXP (SET_DEST (single_set (i2)), 0))
+                == REGNO (SET_DEST (single_set (i3)))
+         && GET_MODE (SET_DEST (single_set (i2))) == DImode
+         && GET_MODE (SET_DEST (single_set (i3))) == DImode
+         && GET_MODE (XEXP (SET_SRC (single_set (i3)), 0)) == SImode
+         && TRULY_NOOP_TRUNCATION_MODES_P (DImode, SImode))
+       {
+         newpat = copy_rtx (PATTERN (i2));
+         PUT_MODE (SET_DEST (newpat), SImode);
+         PUT_MODE (SET_SRC (newpat), SImode);
+
+         rtx i2dest_r = XEXP (SET_DEST (newpat), 0);
+         rtx i3src_r = XEXP (SET_SRC (single_set (i3)), 0);
+         newpat = subst (newpat, i2dest_r, i3src_r, false, false, false);
+       }
+      else
+       {
+         /* If I1 feeds into I2 and I1DEST is in I1SRC, we need to make a
+            unique copy of I2SRC each time we substitute it, in order to
+            avoid creating self-referential RTL when we will be substituting
+            I1SRC for I1DEST later.  Likewise if I0 feeds into I2, either
+            directly or indirectly through I1, and I0DEST is in I0SRC.  */
+         newpat = subst (
+             PATTERN (i3), i2dest, i2src, false, false,
+             (i1_feeds_i2_n && i1dest_in_i1src)
+                 || ((i0_feeds_i2_n || (i0_feeds_i1_n && i1_feeds_i2_n))
+                     && i0dest_in_i0src));
+       }
       substed_i2 = true;
 
       /* Record whether I2's body now appears within I3's body.  */
@@ -5482,13 +5531,22 @@ subst (rtx x, rtx from, rtx to, bool in_dest, bool 
in_cond, bool unique_copy)
            }
          else if (fmt[i] == 'e')
            {
-             /* If this is a register being set, ignore it.  */
              new_rtx = XEXP (x, i);
-             if (in_dest
-                 && i == 0
-                 && (((code == SUBREG || code == ZERO_EXTRACT)
-                      && REG_P (new_rtx))
-                     || code == STRICT_LOW_PART))
+             /* Allow replacing reg with subreg if it is sign extension.  */
+             if (in_dest && (code == SUBREG || code == ZERO_EXTRACT)
+                 && TRULY_NOOP_TRUNCATION_MODES_P (DImode, SImode)
+                 && GET_MODE (from) == DImode && GET_MODE (to) == SImode
+                 && i == 0)
+               {
+                 new_rtx
+                     = (unique_copy && n_occurrences ? copy_rtx (to) : to);
+                 n_occurrences++;
+               }
+             /* If this is a register being set, ignore it.  */
+             else if (in_dest && i == 0
+                      && (((code == SUBREG || code == ZERO_EXTRACT)
+                           && REG_P (new_rtx))
+                          || code == STRICT_LOW_PART))
                ;
 
              else if (COMBINE_RTX_EQUAL_P (XEXP (x, i), from))
diff --git a/gcc/testsuite/gcc.target/mips/pr104914.c 
b/gcc/testsuite/gcc.target/mips/pr104914.c
new file mode 100644
index 00000000000..fd6ef6af446
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/pr104914.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-march=mips64r2 -mabi=64" } */
+
+/* { dg-final { scan-assembler-not "\tdins\t" } } */
+
+NOMIPS16 int test (const unsigned char *buf)
+{
+  int val;
+  ((unsigned char*)&val)[0] = *buf++;
+  ((unsigned char*)&val)[1] = *buf++;
+  ((unsigned char*)&val)[2] = *buf++;
+  ((unsigned char*)&val)[3] = *buf++;
+  if(val > 0)
+    return 1;
+  else
+    return 0;
+}
-- 
2.30.2

Reply via email to