Hi,

This is a backport of the patch for PR middle-end/53623 plus all bug
fixes caused by it.  Tested on Linux/x86-32, Linux/x86-64 and x32.  OK
for 4.8 branch?

Thanks.


H.J.
---
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 469ee31..44bf322 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,82 @@
+2015-02-15  H.J. Lu  <hongjiu...@intel.com>
+
+       Backport from mainline:
+       2014-06-13  Jeff Law  <l...@redhat.com>
+
+       PR rtl-optimization/61094
+       PR rtl-optimization/61446
+       * ree.c (combine_reaching_defs): Get the mode for the copy from
+       the extension insn rather than the defining insn.
+
+       2014-06-02  Jeff Law  <l...@redhat.com>
+
+       PR rtl-optimization/61094
+       * ree.c (combine_reaching_defs): Do not reextend an insn if it
+       was marked as do_no_reextend.  If a copy is needed to eliminate
+       an extension, then mark it as do_not_reextend.
+
+       2014-02-14  Jeff Law  <l...@redhat.com>
+
+       PR rtl-optimization/60131
+       * ree.c (get_extended_src_reg): New function.
+       (combine_reaching_defs): Use it rather than assuming location
+       of REG.
+       (find_and_remove_re): Verify first operand of extension is
+       a REG before adding the insns to the copy list.
+
+       2014-01-17  Jeff Law  <l...@redhat.com>
+
+       * ree.c (combine_set_extension): Temporarily disable test for
+       changing number of hard registers.
+
+       2014-01-15  Jeff Law  <l...@redhat.com>
+
+       PR tree-optimization/59747
+       * ree.c (find_and_remove_re): Properly handle case where a second
+       eliminated extension requires widening a copy created for elimination
+       of a prior extension.
+       (combine_set_extension): Ensure that the number of hard regs needed
+       for a destination register does not change when we widen it.
+
+       2014-01-10  Jeff Law  <l...@redhat.com>
+
+       PR middle-end/59743
+       * ree.c (combine_reaching_defs): Ensure the defining statement
+       occurs before the extension when optimizing extensions with
+       different source and destination hard registers.
+
+       2014-01-10  Jakub Jelinek  <ja...@redhat.com>
+
+       PR rtl-optimization/59754
+       * ree.c (combine_reaching_defs): Disallow !SCALAR_INT_MODE_P
+       modes in the REGNO != REGNO case.
+
+       2014-01-08  Jeff Law  <l...@redhat.com>
+
+       * ree.c (get_sub_rtx): New function, extracted from...
+       (merge_def_and_ext): Here.
+       (combine_reaching_defs): Use get_sub_rtx.
+
+       2014-01-07  Jeff Law  <l...@redhat.com>
+
+       PR middle-end/53623
+       * ree.c (combine_set_extension): Handle case where source
+       and destination registers in an extension insn are different.
+       (combine_reaching_defs): Allow source and destination
+       registers in extension to be different under limited
+       circumstances.
+       (add_removable_extension): Remove restriction that the
+       source and destination registers in the extension are the
+       same.
+       (find_and_remove_re): Emit a copy from the extension's
+       destination to its source after the defining insn if
+       the source and destination registers are different.
+
+       2013-12-12  Jeff Law  <l...@redhat.com>
+
+       * i386.md (simple LEA peephole2): Add missing mode to zero_extend
+       for zero-extended MULT simple LEA pattern.
+
 2015-02-12  Jakub Jelinek  <ja...@redhat.com>
 
        Backported from mainline
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 372ae63..aabd6ec 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -17265,7 +17265,7 @@
    && REGNO (operands[0]) == REGNO (operands[1])
    && peep2_regno_dead_p (0, FLAGS_REG)"
   [(parallel [(set (match_dup 0)
-                  (zero_extend (ashift:SI (match_dup 1) (match_dup 2))))
+                  (zero_extend:DI (ashift:SI (match_dup 1) (match_dup 2))))
              (clobber (reg:CC FLAGS_REG))])]
   "operands[2] = GEN_INT (exact_log2 (INTVAL (operands[2])));")
 
diff --git a/gcc/ree.c b/gcc/ree.c
index c7e106f..bc566ad 100644
--- a/gcc/ree.c
+++ b/gcc/ree.c
@@ -327,8 +327,30 @@ combine_set_extension (ext_cand *cand, rtx curr_insn, rtx 
*orig_set)
 {
   rtx orig_src = SET_SRC (*orig_set);
   enum machine_mode orig_mode = GET_MODE (SET_DEST (*orig_set));
-  rtx new_reg = gen_rtx_REG (cand->mode, REGNO (SET_DEST (*orig_set)));
   rtx new_set;
+  rtx cand_pat = PATTERN (cand->insn);
+
+  /* If the extension's source/destination registers are not the same
+     then we need to change the original load to reference the destination
+     of the extension.  Then we need to emit a copy from that destination
+     to the original destination of the load.  */
+  rtx new_reg;
+  bool copy_needed
+    = (REGNO (SET_DEST (cand_pat)) != REGNO (XEXP (SET_SRC (cand_pat), 0)));
+  if (copy_needed)
+    new_reg = gen_rtx_REG (cand->mode, REGNO (SET_DEST (cand_pat)));
+  else
+    new_reg = gen_rtx_REG (cand->mode, REGNO (SET_DEST (*orig_set)));
+
+#if 0
+  /* Rethinking test.  Temporarily disabled.  */
+  /* We're going to be widening the result of DEF_INSN, ensure that doing so
+     doesn't change the number of hard registers needed for the result.  */
+  if (HARD_REGNO_NREGS (REGNO (new_reg), cand->mode)
+      != HARD_REGNO_NREGS (REGNO (SET_DEST (*orig_set)),
+                          GET_MODE (SET_DEST (*orig_set))))
+       return false;
+#endif
 
   /* Merge constants by directly moving the constant into the register under
      some conditions.  Recall that RTL constants are sign-extended.  */
@@ -387,7 +409,8 @@ combine_set_extension (ext_cand *cand, rtx curr_insn, rtx 
*orig_set)
       if (dump_file)
         {
           fprintf (dump_file,
-                  "Tentatively merged extension with definition:\n");
+                  "Tentatively merged extension with definition %s:\n",
+                  (copy_needed) ? "(copy needed)" : "");
           print_rtl_single (dump_file, curr_insn);
         }
       return true;
@@ -531,6 +554,8 @@ struct ATTRIBUTE_PACKED ext_modified
   /* Kind of modification of the insn.  */
   ENUM_BITFIELD(ext_modified_kind) kind : 2;
 
+  unsigned int do_not_reextend : 1;
+
   /* True if the insn is scheduled to be deleted.  */
   unsigned int deleted : 1;
 };
@@ -614,27 +639,21 @@ make_defs_and_copies_lists (rtx extend_insn, const_rtx 
set_pat,
   return ret;
 }
 
-/* Merge the DEF_INSN with an extension.  Calls combine_set_extension
-   on the SET pattern.  */
-
-static bool
-merge_def_and_ext (ext_cand *cand, rtx def_insn, ext_state *state)
+/* If DEF_INSN has single SET expression, possibly buried inside
+   a PARALLEL, return the address of the SET expression, else
+   return NULL.  This is similar to single_set, except that
+   single_set allows multiple SETs when all but one is dead.  */
+static rtx *
+get_sub_rtx (rtx def_insn)
 {
-  enum machine_mode ext_src_mode;
-  enum rtx_code code;
-  rtx *sub_rtx;
-  rtx s_expr;
-  int i;
-
-  ext_src_mode = GET_MODE (XEXP (SET_SRC (cand->expr), 0));
-  code = GET_CODE (PATTERN (def_insn));
-  sub_rtx = NULL;
+  enum rtx_code code = GET_CODE (PATTERN (def_insn));
+  rtx *sub_rtx = NULL;
 
   if (code == PARALLEL)
     {
-      for (i = 0; i < XVECLEN (PATTERN (def_insn), 0); i++)
+      for (int i = 0; i < XVECLEN (PATTERN (def_insn), 0); i++)
         {
-          s_expr = XVECEXP (PATTERN (def_insn), 0, i);
+          rtx s_expr = XVECEXP (PATTERN (def_insn), 0, i);
           if (GET_CODE (s_expr) != SET)
             continue;
 
@@ -643,7 +662,7 @@ merge_def_and_ext (ext_cand *cand, rtx def_insn, ext_state 
*state)
           else
             {
               /* PARALLEL with multiple SETs.  */
-              return false;
+              return NULL;
             }
         }
     }
@@ -652,10 +671,27 @@ merge_def_and_ext (ext_cand *cand, rtx def_insn, 
ext_state *state)
   else
     {
       /* It is not a PARALLEL or a SET, what could it be ? */
-      return false;
+      return NULL;
     }
 
   gcc_assert (sub_rtx != NULL);
+  return sub_rtx;
+}
+
+/* Merge the DEF_INSN with an extension.  Calls combine_set_extension
+   on the SET pattern.  */
+
+static bool
+merge_def_and_ext (ext_cand *cand, rtx def_insn, ext_state *state)
+{
+  enum machine_mode ext_src_mode;
+  rtx *sub_rtx;
+
+  ext_src_mode = GET_MODE (XEXP (SET_SRC (cand->expr), 0));
+  sub_rtx = get_sub_rtx (def_insn);
+
+  if (sub_rtx == NULL)
+    return false;
 
   if (REG_P (SET_DEST (*sub_rtx))
       && (GET_MODE (SET_DEST (*sub_rtx)) == ext_src_mode
@@ -683,6 +719,18 @@ merge_def_and_ext (ext_cand *cand, rtx def_insn, ext_state 
*state)
   return false;
 }
 
+/* Given SRC, which should be one or more extensions of a REG, strip
+   away the extensions and return the REG.  */
+
+static inline rtx
+get_extended_src_reg (rtx src)
+{
+  while (GET_CODE (src) == SIGN_EXTEND || GET_CODE (src) == ZERO_EXTEND)
+    src = XEXP (src, 0);
+  gcc_assert (REG_P (src));
+  return src;
+}
+
 /* This function goes through all reaching defs of the source
    of the candidate for elimination (CAND) and tries to combine
    the extension with the definition instruction.  The changes
@@ -709,6 +757,108 @@ combine_reaching_defs (ext_cand *cand, const_rtx set_pat, 
ext_state *state)
   if (!outcome)
     return false;
 
+  /* If the destination operand of the extension is a different
+     register than the source operand, then additional restrictions
+     are needed.  Note we have to handle cases where we have nested
+     extensions in the source operand.  */
+  bool copy_needed
+    = (REGNO (SET_DEST (PATTERN (cand->insn)))
+       != REGNO (get_extended_src_reg (SET_SRC (PATTERN (cand->insn)))));
+  if (copy_needed)
+    {
+      /* In theory we could handle more than one reaching def, it
+        just makes the code to update the insn stream more complex.  */
+      if (state->defs_list.length () != 1)
+       return false;
+
+      /* We require the candidate not already be modified.  It may,
+        for example have been changed from a (sign_extend (reg))
+        into (zero_extend (sign_extend (reg))).
+
+        Handling that case shouldn't be terribly difficult, but the code
+        here and the code to emit copies would need auditing.  Until
+        we see a need, this is the safe thing to do.  */
+      if (state->modified[INSN_UID (cand->insn)].kind != EXT_MODIFIED_NONE)
+       return false;
+
+      /* Transformation of
+        (set (reg1) (expression))
+        (set (reg2) (any_extend (reg1)))
+        into
+        (set (reg2) (any_extend (expression)))
+        (set (reg1) (reg2))
+        is only valid for scalar integral modes, as it relies on the low
+        subreg of reg1 to have the value of (expression), which is not true
+        e.g. for vector modes.  */
+      if (!SCALAR_INT_MODE_P (GET_MODE (SET_DEST (PATTERN (cand->insn)))))
+       return false;
+
+      /* There's only one reaching def.  */
+      rtx def_insn = state->defs_list[0];
+
+      /* The defining statement must not have been modified either.  */
+      if (state->modified[INSN_UID (def_insn)].kind != EXT_MODIFIED_NONE)
+       return false;
+
+      /* The defining statement and candidate insn must be in the same block.
+        This is merely to keep the test for safety and updating the insn
+        stream simple.  Also ensure that within the block the candidate
+        follows the defining insn.  */
+      if (BLOCK_FOR_INSN (cand->insn) != BLOCK_FOR_INSN (def_insn)
+         || DF_INSN_LUID (def_insn) > DF_INSN_LUID (cand->insn))
+       return false;
+
+      /* If there is an overlap between the destination of DEF_INSN and
+        CAND->insn, then this transformation is not safe.  Note we have
+        to test in the widened mode.  */
+      rtx *dest_sub_rtx = get_sub_rtx (def_insn);
+      if (dest_sub_rtx == NULL
+         || !REG_P (SET_DEST (*dest_sub_rtx)))
+       return false;
+
+      rtx tmp_reg = gen_rtx_REG (GET_MODE (SET_DEST (PATTERN (cand->insn))),
+                                REGNO (SET_DEST (*dest_sub_rtx)));
+      if (reg_overlap_mentioned_p (tmp_reg, SET_DEST (PATTERN (cand->insn))))
+       return false;
+
+      /* The destination register of the extension insn must not be
+        used or set between the def_insn and cand->insn exclusive.  */
+      if (reg_used_between_p (SET_DEST (PATTERN (cand->insn)),
+                             def_insn, cand->insn)
+         || reg_set_between_p (SET_DEST (PATTERN (cand->insn)),
+                               def_insn, cand->insn))
+       return false;
+
+      /* We must be able to copy between the two registers.   Generate,
+        recognize and verify constraints of the copy.  Also fail if this
+        generated more than one insn.
+
+         This generates garbage since we throw away the insn when we're
+        done, only to recreate it later if this test was successful. 
+
+        Make sure to get the mode from the extension (cand->insn).  This
+        is different than in the code to emit the copy as we have not
+        modified the defining insn yet.  */
+      start_sequence ();
+      rtx pat = PATTERN (cand->insn);
+      rtx new_dst = gen_rtx_REG (GET_MODE (SET_DEST (pat)),
+                                 REGNO (XEXP (SET_SRC (pat), 0)));
+      rtx new_src = gen_rtx_REG (GET_MODE (SET_DEST (pat)),
+                                 REGNO (SET_DEST (pat)));
+      emit_move_insn (new_dst, new_src);
+
+      rtx insn = get_insns();
+      end_sequence ();
+      if (NEXT_INSN (insn))
+       return false;
+      if (recog_memoized (insn) == -1)
+       return false;
+      extract_insn (insn);
+      if (!constrain_operands (1))
+       return false;
+    }
+
+
   /* If cand->insn has been already modified, update cand->mode to a wider
      mode if possible, or punt.  */
   if (state->modified[INSN_UID (cand->insn)].kind != EXT_MODIFIED_NONE)
@@ -772,11 +922,15 @@ combine_reaching_defs (ext_cand *cand, const_rtx set_pat, 
ext_state *state)
             fprintf (dump_file, "All merges were successful.\n");
 
          FOR_EACH_VEC_ELT (state->modified_list, i, def_insn)
-           if (state->modified[INSN_UID (def_insn)].kind == EXT_MODIFIED_NONE)
-             state->modified[INSN_UID (def_insn)].kind
-               = (cand->code == ZERO_EXTEND
-                  ? EXT_MODIFIED_ZEXT : EXT_MODIFIED_SEXT);
+           {
+             ext_modified *modified = &state->modified[INSN_UID (def_insn)];
+             if (modified->kind == EXT_MODIFIED_NONE)
+               modified->kind = (cand->code == ZERO_EXTEND ? EXT_MODIFIED_ZEXT
+                                                           : 
EXT_MODIFIED_SEXT);
 
+             if (copy_needed)
+               modified->do_not_reextend = 1;
+           }
           return true;
         }
       else
@@ -825,8 +979,7 @@ add_removable_extension (const_rtx expr, rtx insn,
 
   if (REG_P (dest)
       && (code == SIGN_EXTEND || code == ZERO_EXTEND)
-      && REG_P (XEXP (src, 0))
-      && REGNO (dest) == REGNO (XEXP (src, 0)))
+      && REG_P (XEXP (src, 0)))
     {
       struct df_link *defs, *def;
       ext_cand *cand;
@@ -910,6 +1063,7 @@ find_and_remove_re (void)
   int num_re_opportunities = 0, num_realized = 0, i;
   vec<ext_cand> reinsn_list;
   vec<rtx> reinsn_del_list;
+  vec<rtx> reinsn_copy_list;
   ext_state state;
 
   /* Construct DU chain to get all reaching definitions of each
@@ -921,6 +1075,7 @@ find_and_remove_re (void)
 
   max_insn_uid = get_max_uid ();
   reinsn_del_list.create (0);
+  reinsn_copy_list.create (0);
   reinsn_list = find_removable_extensions ();
   state.defs_list.create (0);
   state.copies_list.create (0);
@@ -947,17 +1102,62 @@ find_and_remove_re (void)
           if (dump_file)
             fprintf (dump_file, "Eliminated the extension.\n");
           num_realized++;
-          reinsn_del_list.safe_push (curr_cand->insn);
+         /* If the RHS of the current candidate is not (extend (reg)), then
+            we do not allow the optimization of extensions where
+            the source and destination registers do not match.  Thus
+            checking REG_P here is correct.  */
+         if (REG_P (XEXP (SET_SRC (PATTERN (curr_cand->insn)), 0))
+             && (REGNO (SET_DEST (PATTERN (curr_cand->insn)))
+                 != REGNO (XEXP (SET_SRC (PATTERN (curr_cand->insn)), 0))))
+           {
+              reinsn_copy_list.safe_push (curr_cand->insn);
+              reinsn_copy_list.safe_push (state.defs_list[0]);
+           }
+         reinsn_del_list.safe_push (curr_cand->insn);
          state.modified[INSN_UID (curr_cand->insn)].deleted = 1;
         }
     }
 
+  /* The copy list contains pairs of insns which describe copies we
+     need to insert into the INSN stream.
+
+     The first insn in each pair is the extension insn, from which
+     we derive the source and destination of the copy.
+
+     The second insn in each pair is the memory reference where the
+     extension will ultimately happen.  We emit the new copy
+     immediately after this insn.
+
+     It may first appear that the arguments for the copy are reversed.
+     Remember that the memory reference will be changed to refer to the
+     destination of the extention.  So we're actually emitting a copy
+     from the new destination to the old destination.  */
+  for (unsigned int i = 0; i < reinsn_copy_list.length (); i += 2)
+    {
+      rtx curr_insn = reinsn_copy_list[i];
+      rtx def_insn = reinsn_copy_list[i + 1];
+
+      /* Use the mode of the destination of the defining insn
+        for the mode of the copy.  This is necessary if the
+        defining insn was used to eliminate a second extension
+        that was wider than the first.  */
+      rtx sub_rtx = *get_sub_rtx (def_insn);
+      rtx pat = PATTERN (curr_insn);
+      rtx new_dst = gen_rtx_REG (GET_MODE (SET_DEST (sub_rtx)),
+                                REGNO (XEXP (SET_SRC (pat), 0)));
+      rtx new_src = gen_rtx_REG (GET_MODE (SET_DEST (sub_rtx)),
+                                REGNO (SET_DEST (pat)));
+      rtx set = gen_rtx_SET (VOIDmode, new_dst, new_src);
+      emit_insn_after (set, def_insn);
+    }
+
   /* Delete all useless extensions here in one sweep.  */
   FOR_EACH_VEC_ELT (reinsn_del_list, i, curr_insn)
     delete_insn (curr_insn);
 
   reinsn_list.release ();
   reinsn_del_list.release ();
+  reinsn_copy_list.release ();
   state.defs_list.release ();
   state.copies_list.release ();
   state.modified_list.release ();
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index ff0934b..a408fed 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,32 @@
+2015-02-15  H.J. Lu  <hongjiu...@intel.com>
+
+       Backport from mainline:
+       2014-06-02  Jeff Law  <l...@redhat.com>
+
+       PR rtl-optimization/61094
+       * g++.dg/pr61094: New test.
+
+       2014-02-14  Jeff Law  <l...@redhat.com>
+
+       PR rtl-optimization/60131
+       * g++.dg/torture/pr60131.C: New test.
+
+       2014-01-15  Jeff Law  <l...@redhat.com>
+
+       PR tree-optimization/59747
+       * gcc.c-torture/execute/pr59747.c: New test.
+
+       2014-01-10  Jeff Law  <l...@redhat.com>
+
+       PR middle-end/59743
+       * gcc.c-torture/compile/pr59743.c: New test.
+
+       Backport from mainline:
+       2014-01-07  Jeff Law  <l...@redhat.com>
+
+       PR middle-end/53623
+       * gcc.target/i386/pr53623.c: New test.
+
 2015-02-13  Mikael Morin  <mik...@gcc.gnu.org>
 
        PR fortran/63744
diff --git a/gcc/testsuite/g++.dg/pr61094.C b/gcc/testsuite/g++.dg/pr61094.C
new file mode 100644
index 0000000..35adc25
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr61094.C
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "-O3" }  */
+
+template <typename> struct A {
+  unsigned _width, _height, _depth, _spectrum;
+  template <typename t> A(t p1) {
+    int a = p1.size();
+    if (a) {
+      _width = p1._width;
+      _depth = _height = _spectrum = p1._spectrum;
+    }
+  }
+  long size() { return (long)_width * _height * _depth * _spectrum; }
+};
+
+int d;
+void fn1(void *);
+A<int> *fn2();
+void fn3() {
+  int b;
+  for (;;) {
+    A<char> c(*fn2());
+    fn1(&c);
+    if (d || !b)
+      throw;
+  }
+}
+
+
+
+
diff --git a/gcc/testsuite/g++.dg/torture/pr60131.C 
b/gcc/testsuite/g++.dg/torture/pr60131.C
new file mode 100644
index 0000000..23dde31
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/pr60131.C
@@ -0,0 +1,23 @@
+// { dg-do compile }
+struct A { short a; };
+int **b;
+unsigned long c;
+
+bool foo ();
+unsigned bar (unsigned i);
+extern void baz () __attribute__((noreturn));
+
+int *
+test (unsigned x, struct A *y)
+{
+  unsigned v;
+  if (foo () || y[x].a == -1)
+    {
+      c = bar (x);
+      return 0;
+    }
+  v = y[x].a;
+  if (v >= 23)
+    baz ();
+  return b[v];
+}
diff --git a/gcc/testsuite/gcc.c-torture/compile/pr59743.c 
b/gcc/testsuite/gcc.c-torture/compile/pr59743.c
new file mode 100644
index 0000000..8dadba5
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/compile/pr59743.c
@@ -0,0 +1,23 @@
+/* PR middle-end/59743 */
+
+typedef union {
+  long all;
+  struct {
+    int low;
+    int high;
+  } s;
+} udwords;
+int a, b, c, d;
+void __udivmoddi4() {
+  udwords r;
+  d = __builtin_clz(0);
+  r.s.low = 0;
+  for (; d; --d) {
+    r.s.high = r.s.high << 1 | r.s.low >> a;
+    r.s.low = r.s.low << b >> 1;
+    int s = -r.all;
+    c = s;
+    r.all--;
+  }
+}
+
diff --git a/gcc/testsuite/gcc.c-torture/execute/pr59747.c 
b/gcc/testsuite/gcc.c-torture/execute/pr59747.c
new file mode 100644
index 0000000..d45a908
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/pr59747.c
@@ -0,0 +1,27 @@
+extern void abort (void);
+extern void exit (int);
+
+int a[6], b, c = 1, d;
+short e;
+
+int __attribute__ ((noinline))
+fn1 (int p)
+{
+  b = a[p];
+}
+
+int
+main ()
+{
+  if (sizeof (long long) != 8)
+    exit (0);
+
+  a[0] = 1;
+  if (c)
+    e--;
+  d = e;
+  long long f = e;
+  if (fn1 ((f >> 56) & 1) != 0)
+    abort ();
+  exit (0);
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr53623.c 
b/gcc/testsuite/gcc.target/i386/pr53623.c
new file mode 100644
index 0000000..35c578b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr53623.c
@@ -0,0 +1,25 @@
+/* { dg-do compile { target {! ia32 } } } */
+/* { dg-options "-O2 -fdump-rtl-ree" } */
+
+
+#include <stdint.h>
+
+typedef (*inst_t)(int64_t rdi, int64_t rsi, int64_t rdx);
+
+int16_t code[256];
+inst_t dispatch[256];
+
+void an_inst(int64_t rdi, int64_t rsi, int64_t rdx) {
+  rdx = code[rdx];
+  uint8_t inst = (uint8_t) rdx;
+  rdx >>= 8;
+  dispatch[inst](rdi, rsi, rdx);
+}
+
+int main(void) {
+  return 0;
+}
+
+/* { dg-final { scan-rtl-dump "copy needed" "ree" } } */
+/* { dg-final { cleanup-rtl-dump "ree" } } */
+

Reply via email to