diff mbox series

[V3,3/4] ree: Improve ree pass.

Message ID 6a146926-4ad9-4a3c-b526-1585eed43bdf@linux.ibm.com
State New
Headers show
Series None | expand

Commit Message

Ajit Agarwal March 13, 2024, 9:19 a.m. UTC
Hello All:

For rs6000 target we see redundant zero and sign extension and done to improve
ree pass to eliminate such redundant zero and sign extension. Support of
zero_extend/sign_extend/AND. Also support of AND with extension with different
constants like 0x7/0x7F/0x7FFFF other than 1.

Changes since v2:

- Added all constants 0x7/0x7F/0x7FFFF other than 1 for machine modes.
- Improving coding conventions.
- Reorganization of the code.

Bootstrapped and regtested for powerpc64-linux-gnu.

contrib/check_GNU_stype.sh looks good.

spec 2017 INT and FP benchmarks runs looks good.

Thanks & Regards
Ajit


ree: Improve ree pass for rs6000 target

For rs6000 target we see redundant zero and sign extension and done to improve
ree pass to eliminate such redundant zero and sign extension. Support of
zero_extend/sign_extend/AND. Also support of AND with extension with different
constants like 0x7/0x7F/0x7FFFF other than 1.

2024-03-13  Ajit Kumar Agarwal  <aagarwa1@linux.ibm.com>

gcc/ChangeLog:

	* ree.cc (eliminate_across_bbs_p): Add checks to enable extension
	elimination across and within basic blocks.
	(def_arith_p): New function to check definition has arithmetic
	operation.
	(combine_set_extension): Modification to incorporate AND
	and current zero_extend and sign_extend instruction.
	(merge_def_and_ext): Add calls to eliminate_across_bbs_p and
	zero_extend sign_extend and AND instruction.
	(rtx_is_zext_p): New function.
	(feasible_cfg): New function.
	* rtl.h (reg_used_set_between_p): Add prototype.
	* rtlanal.cc (reg_used_set_between_p): New function.

gcc/testsuite/ChangeLog:

	* g++.target/powerpc/zext-elim.C: New testcase.
	* g++.target/powerpc/zext-elim-1.C: New testcase.
	* g++.target/powerpc/zext-elim-2.C: New testcase.
	* g++.target/powerpc/sext-elim.C: New testcase.
---
Changes since v2:

- Added all constants 0x7/0x7F/0x7FFFF other than 1 for machine modes.
- Improving coding conventions.
- Reorganization of the code.
---
 gcc/ree.cc                                    | 517 ++++++++++++++++--
 gcc/rtl.h                                     |   1 +
 gcc/rtlanal.cc                                |  15 +
 gcc/testsuite/g++.target/powerpc/sext-elim.C  |  16 +
 .../g++.target/powerpc/zext-elim-1.C          |  18 +
 .../g++.target/powerpc/zext-elim-2.C          |  10 +
 gcc/testsuite/g++.target/powerpc/zext-elim.C  |  29 +
 7 files changed, 558 insertions(+), 48 deletions(-)
 create mode 100644 gcc/testsuite/g++.target/powerpc/sext-elim.C
 create mode 100644 gcc/testsuite/g++.target/powerpc/zext-elim-1.C
 create mode 100644 gcc/testsuite/g++.target/powerpc/zext-elim-2.C
 create mode 100644 gcc/testsuite/g++.target/powerpc/zext-elim.C
diff mbox series

Patch

diff --git a/gcc/ree.cc b/gcc/ree.cc
index bfc4b4b0412..43fed62d755 100644
--- a/gcc/ree.cc
+++ b/gcc/ree.cc
@@ -253,6 +253,77 @@  struct ext_cand
 
 static int max_insn_uid;
 
+/* Return TRUE if OP can be considered a zero extension from one or
+   more sub-word modes to larger modes up to a full word.
+
+   For example (and:DI (reg) (const_int X))
+
+   Depending on the value of X could be considered a zero extension
+   from QI, HI and SI to larger modes up to DImode.  */
+
+static bool
+rtx_is_zext_p (rtx insn)
+{
+  if (GET_CODE (insn) == AND)
+    {
+      rtx set = XEXP (insn, 0);
+      if (REG_P (set))
+	{
+	  rtx src = XEXP (insn, 1);
+	  machine_mode m_mode = GET_MODE (set);
+
+	  if (CONST_INT_P (src)
+	      && (INTVAL (src) == 1
+		  || (m_mode == QImode && INTVAL (src) == 0x7)
+		  || (m_mode == QImode && INTVAL (src) == 0x0000007F)
+		  || (m_mode == HImode && INTVAL (src) == 0x00007FFF)
+		  || (m_mode == SImode && INTVAL (src) == 0x007FFFFF)))
+		return true;
+
+	}
+      else
+	return false;
+    }
+
+  return false;
+}
+/* Return TRUE if OP can be considered a zero extension from one or
+   more sub-word modes to larger modes up to a full word.
+
+   For example (and:DI (reg) (const_int X))
+
+   Depending on the value of X could be considered a zero extension
+   from QI, HI and SI to larger modes up to DImode.  */
+
+static bool
+rtx_is_zext_p (rtx_insn *insn)
+{
+  rtx body = single_set (insn);
+
+  if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == AND)
+   {
+     rtx set = XEXP (SET_SRC (body), 0);
+
+     if (REG_P (set) && GET_MODE (SET_DEST (body)) == GET_MODE (set))
+       {
+	  rtx src = XEXP (SET_SRC (body), 1);
+	  machine_mode m_mode = GET_MODE (set);
+
+	  if (CONST_INT_P (src)
+	      && (INTVAL (src) == 1
+		  || (m_mode == QImode && INTVAL (src) == 0x7)
+		  || (m_mode == QImode && INTVAL (src) == 0x0000007F)
+		  || (m_mode == HImode && INTVAL (src) == 0x00007FFF)
+		  || (m_mode == SImode && INTVAL (src) == 0x007FFFFF)))
+		return true;
+       }
+     else
+      return false;
+   }
+
+   return false;
+}
+
 /* Update or remove REG_EQUAL or REG_EQUIV notes for INSN.  */
 
 static bool
@@ -319,7 +390,7 @@  combine_set_extension (ext_cand *cand, rtx_insn *curr_insn, rtx *orig_set)
 {
   rtx orig_src = SET_SRC (*orig_set);
   machine_mode orig_mode = GET_MODE (SET_DEST (*orig_set));
-  rtx new_set;
+  rtx new_set = NULL_RTX;
   rtx cand_pat = single_set (cand->insn);
 
   /* If the extension's source/destination registers are not the same
@@ -359,27 +430,41 @@  combine_set_extension (ext_cand *cand, rtx_insn *curr_insn, rtx *orig_set)
   else if (GET_CODE (orig_src) == cand->code)
     {
       /* Here is a sequence of two extensions.  Try to merge them.  */
-      rtx temp_extension
-	= gen_rtx_fmt_e (cand->code, cand->mode, XEXP (orig_src, 0));
+      rtx temp_extension = NULL_RTX;
+      if (GET_CODE (SET_SRC (cand_pat)) == AND)
+	temp_extension
+	  = gen_rtx_AND (cand->mode, XEXP (orig_src, 0), XEXP (orig_src, 1));
+      else
+	temp_extension
+	  = gen_rtx_fmt_e (cand->code, cand->mode, XEXP (orig_src, 0));
       rtx simplified_temp_extension = simplify_rtx (temp_extension);
       if (simplified_temp_extension)
         temp_extension = simplified_temp_extension;
+
       new_set = gen_rtx_SET (new_reg, temp_extension);
     }
   else if (GET_CODE (orig_src) == IF_THEN_ELSE)
     {
       /* Only IF_THEN_ELSE of phi-type copies are combined.  Otherwise,
-         in general, IF_THEN_ELSE should not be combined.  */
-      return false;
+	in general, IF_THEN_ELSE should not be combined.  Relaxed
+	cases with IF_THEN_ELSE across basic blocks.  */
+	return true;
     }
   else
     {
       /* This is the normal case.  */
-      rtx temp_extension
+      rtx temp_extension = NULL_RTX;
+
+      if (GET_CODE (SET_SRC (cand_pat)) == AND)
+	temp_extension
+	= gen_rtx_AND (cand->mode, orig_src, XEXP (SET_SRC (cand_pat), 1));
+      else
+	temp_extension
 	= gen_rtx_fmt_e (cand->code, cand->mode, orig_src);
       rtx simplified_temp_extension = simplify_rtx (temp_extension);
       if (simplified_temp_extension)
         temp_extension = simplified_temp_extension;
+
       new_set = gen_rtx_SET (new_reg, temp_extension);
     }
 
@@ -468,12 +553,13 @@  get_defs (rtx_insn *insn, rtx reg, vec<rtx_insn *> *dest)
   FOR_EACH_INSN_USE (use, insn)
     {
       if (GET_CODE (DF_REF_REG (use)) == SUBREG)
-        return NULL;
+	return NULL;
       if (REGNO (DF_REF_REG (use)) == REGNO (reg))
 	break;
     }
 
-  gcc_assert (use != NULL);
+  if (use == NULL)
+    return NULL;
 
   ref_chain = DF_REF_CHAIN (use);
 
@@ -481,9 +567,9 @@  get_defs (rtx_insn *insn, rtx reg, vec<rtx_insn *> *dest)
     {
       /* Problem getting some definition for this instruction.  */
       if (ref_link->ref == NULL)
-        return NULL;
+	return NULL;
       if (DF_REF_INSN_INFO (ref_link->ref) == NULL)
-        return NULL;
+	return NULL;
       /* As global regs are assumed to be defined at each function call
 	 dataflow can report a call_insn as being a definition of REG.
 	 But we can't do anything with that in this pass so proceed only
@@ -698,6 +784,279 @@  get_sub_rtx (rtx_insn *def_insn)
   return sub_rtx;
 }
 
+/* Return TRUE if reaching definition of def_insn source operand
+   has has arithmetic peration like ASHIFT and LSHIFTRT. If TRUE
+   don't eliminate sign extension.   */
+static bool
+def_arith_p (rtx_insn *insn, rtx orig_src)
+{
+  if (!REG_P (orig_src))
+     return true;
+
+  vec<rtx_insn *> *dest = XCNEWVEC (vec<rtx_insn *>, 4);
+  if (!get_defs (insn, orig_src, dest))
+    return true;
+
+  int i;
+  rtx_insn *def_insn;
+  bool has_arith = false;
+
+  FOR_EACH_VEC_ELT (*dest, i, def_insn)
+    {
+      rtx def_set = single_set (def_insn);
+
+      if (!def_set)
+	{
+	  has_arith = true;
+	  break;
+	}
+
+      if (DEBUG_INSN_P (def_insn))
+	continue;
+
+      /* Return True for following rtl insn.
+	 set (reg x), (ashift ( ...)
+	 set (reg x), (lshiftrt (....)   */
+
+      if ((GET_CODE (PATTERN (def_insn)) == SET
+	   && (GET_CODE (SET_SRC (def_set)) == ASHIFT
+	   || GET_CODE (SET_SRC (def_set)) == LSHIFTRT)))
+	{
+	  has_arith = true;
+	  break;
+	}
+
+      /* Return TRUE for following rtl insn.
+	 set (reg x) , (plus(ashift ( ....)
+	 set (reg x), (plus(lshiftrt (....)  */
+
+      if (GET_CODE (PATTERN (def_insn)) == SET
+	  && (GET_RTX_CLASS (GET_CODE (SET_SRC (def_set))) == RTX_BIN_ARITH
+	  || GET_RTX_CLASS (GET_CODE (SET_SRC (def_set))) == RTX_COMM_ARITH))
+	{
+	  rtx src = XEXP (SET_SRC (def_set),0);
+
+	  if (GET_CODE (src) == LSHIFTRT
+	      || GET_CODE (src) == ASHIFT)
+	    {
+	      has_arith = true;
+	      break;
+	    }
+	}
+#if 0
+      else
+	{
+	  has_arith = true;
+	  break;
+	}
+#endif
+     }
+  XDELETEVEC (dest);
+  return has_arith;
+}
+
+/* Return TRUE if the cfg has following properties.
+	bb1
+	|\
+	| \
+	|  bb2
+	|  /
+	bb3
+
+   whereas bb1 has IF_THEN_ELSE  and bb2 has the definition and bb3 has
+   zero/sign/AND extensions.  */
+
+static bool
+feasible_cfg (ext_cand *cand, rtx_insn *def_insn)
+{
+  basic_block bb = BLOCK_FOR_INSN (cand->insn);
+  edge fallthru_edge;
+  edge e;
+  edge_iterator ei;
+  FOR_EACH_EDGE (e, ei, bb->preds)
+    {
+      rtx_insn *insn = BB_END (e->src) ? PREV_INSN (BB_END (e->src)) : NULL;
+
+      if (insn == NULL)
+	continue;
+
+      if (DEBUG_INSN_P (insn))
+	continue;
+
+      rtx set = single_set (insn);
+
+      if (!set)
+	continue;
+
+      if (GET_CODE (SET_SRC (set)) == COMPARE
+	  || GET_RTX_CLASS (GET_CODE (SET_SRC (set))) == RTX_COMPARE
+	  || GET_RTX_CLASS (GET_CODE (SET_SRC (set))) == RTX_COMM_COMPARE)
+	return false;
+
+      /* Block has IF_THEN_ELSE.  */
+      if (GET_CODE (SET_SRC (set)) == IF_THEN_ELSE)
+	{
+	  if (e->dest == bb)
+	    {
+	      basic_block jump_block = e->dest;
+	      if (jump_block != bb)
+		return false;
+	     }
+	     else
+	       {
+		 /* def_insn block has single successor and fall through
+		    edge target are the block for cand insn.  */
+		 if (single_succ_p (e->dest))
+		   {
+		     fallthru_edge = single_succ_edge (e->dest);
+		     if (BB_END (fallthru_edge->dest)
+			 && bb != fallthru_edge->dest)
+		       return false;
+		   }
+		}
+	  }
+    }
+
+  /* def_insn block has single successor and fall through
+     edge target are the block for cand insn.  */
+  if (single_succ_p (BLOCK_FOR_INSN (def_insn)))
+    {
+      fallthru_edge = single_succ_edge (BLOCK_FOR_INSN (def_insn));
+      if (BB_END (fallthru_edge->dest)
+	  && bb != fallthru_edge->dest)
+	return false;
+    }
+   else
+     return false;
+
+   return true;
+}
+
+/* Return TRUE if the candidate extension INSN and def_insn are
+   feasible for extension elimination.
+
+   Things to consider:
+
+   cfg properties are feasible for extension elimination.
+
+   sign_extend with def insn as PLUS and the reaching definition
+   of def_insn are not ASHIFT and LSHIFTRT.
+
+   zero_extend with def insn as XOR/IOR and the reachin definition
+   of def_insn are not ASHIFT and LSHIFTRT.
+
+   The destination register of the extension insn must not be
+   used or set between the def_insn and cand->insn exclusive.
+
+   AND with zero extension properties has USE and the register
+   of cand insn are same as register of USE operand.  */
+
+static bool
+eliminate_across_bbs_p (ext_cand *cand, rtx_insn *def_insn)
+{
+  basic_block bb = BLOCK_FOR_INSN (cand->insn);
+
+  if (!feasible_cfg (cand, def_insn))
+    return false;
+
+  rtx cand_set = single_set (cand->insn);
+  /* The destination register of the extension insn must not be
+      used or set between the def_insn and cand->insn exclusive.  */
+  if (INSN_CHAIN_CODE_P (GET_CODE (def_insn))
+      && INSN_CHAIN_CODE_P (cand->code))
+    if ((cand->code == ZERO_EXTEND || rtx_is_zext_p (cand->insn))
+	 && REG_P (SET_DEST (cand_set)) && NEXT_INSN (def_insn)
+	 && reg_used_set_between_p(SET_DEST (cand_set), def_insn, cand->insn))
+      return false;
+
+  if ((cand->code == ZERO_EXTEND)
+      && (bb != BLOCK_FOR_INSN (def_insn)
+      || DF_INSN_LUID (def_insn) > DF_INSN_LUID (cand->insn)))
+    return false;
+
+  if (rtx_is_zext_p (cand->insn))
+    {
+      rtx_insn *insn = BB_END (bb);
+      if (GET_CODE (PATTERN (insn)) != USE)
+	return false;
+
+      if (REGNO (XEXP (PATTERN (insn), 0)) != REGNO (SET_DEST (cand->expr)))
+	return false;
+
+      rtx *loc = &REG_NOTES (def_insn);
+       while (*loc)
+	{
+	  enum reg_note kind = REG_NOTE_KIND (*loc);
+	  if (kind == REG_EQUAL || kind == REG_EQUIV)
+	    return false;
+	  loc = &XEXP (*loc, 1);
+	}
+    }
+
+  rtx set = single_set (def_insn);
+
+  if (!set)
+    return false;
+
+  if (cand->code == SIGN_EXTEND
+      && GET_CODE (set) == SET)
+    {
+      rtx orig_src = SET_SRC (set);
+      machine_mode ext_src_mode;
+
+      ext_src_mode = GET_MODE (XEXP (SET_SRC (cand->expr), 0));
+
+      if (GET_MODE (SET_DEST (set)) != ext_src_mode)
+	return false;
+
+      if (GET_CODE (orig_src) != PLUS)
+	return false;
+
+      if (!REG_P (XEXP (orig_src, 0)))
+	return false;
+
+      if (!REG_P (XEXP (orig_src,1)))
+	return false;
+
+      if (GET_CODE (orig_src) == PLUS)
+	{
+	  bool def_src1
+	    = def_arith_p (def_insn,
+			   XEXP (SET_SRC (set), 0));
+	  bool def_src2
+	    = def_arith_p (def_insn,
+			   XEXP (SET_SRC (set), 1));
+
+	  if (def_src1 || def_src2)
+	    return false;
+	}
+    }
+
+  if (cand->code == ZERO_EXTEND
+      && GET_CODE (set) == SET)
+    {
+      if (GET_CODE (SET_SRC (set)) != XOR
+	  && GET_CODE (SET_SRC (set)) != IOR)
+	return false;
+
+      if (GET_CODE (SET_SRC (set)) == XOR
+	  || GET_CODE (SET_SRC (set)) == IOR)
+	 {
+	   bool def_src1
+	     = def_arith_p (def_insn,
+			    XEXP (SET_SRC (set), 0));
+	   bool def_src2
+	     = def_arith_p (def_insn,
+			    XEXP (SET_SRC (set), 1));
+
+	   if (def_src1 || def_src2)
+	     return false;
+	 }
+     }
+
+  return true;
+}
+
 /* Merge the DEF_INSN with an extension.  Calls combine_set_extension
    on the SET pattern.  */
 
@@ -713,12 +1072,32 @@  merge_def_and_ext (ext_cand *cand, rtx_insn *def_insn, ext_state *state)
   if (sub_rtx == NULL)
     return false;
 
-  if (GET_MODE (SET_DEST (*sub_rtx)) == ext_src_mode
-	  || ((state->modified[INSN_UID (def_insn)].kind
-	       == (cand->code == ZERO_EXTEND
+  bool copy_needed
+    = (REGNO (SET_DEST (cand->expr)) != REGNO (XEXP (SET_SRC (cand->expr), 0)));
+
+  bool feasible = eliminate_across_bbs_p (cand, def_insn);
+
+  if (!feasible) return false;
+
+  /* Combine zero_extend/sign_extend/AND and if sign_extend and
+     mode of DEST and SRC are different.  */
+
+  bool is_zext =  rtx_is_zext_p (cand->insn)
+		  || cand->code == ZERO_EXTEND
+		  || cand->code == SIGN_EXTEND;
+
+  bool do_elimination = !copy_needed
+			&& is_zext
+			&& (cand->code == SIGN_EXTEND
+			    || GET_MODE (SET_DEST (*sub_rtx)) != ext_src_mode);
+
+  if (((do_elimination
+	&& 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))
-	      && state->modified[INSN_UID (def_insn)].mode
-		 == ext_src_mode))
+	     && state->modified[INSN_UID (def_insn)].mode
+		== ext_src_mode))
     {
       if (GET_MODE_UNIT_SIZE (GET_MODE (SET_DEST (*sub_rtx)))
 	  >= GET_MODE_UNIT_SIZE (cand->mode))
@@ -734,7 +1113,6 @@  merge_def_and_ext (ext_cand *cand, rtx_insn *def_insn, ext_state *state)
 	  return true;
 	}
     }
-
   return false;
 }
 
@@ -744,7 +1122,9 @@  merge_def_and_ext (ext_cand *cand, rtx_insn *def_insn, ext_state *state)
 static inline rtx
 get_extended_src_reg (rtx src)
 {
-  while (GET_CODE (src) == SIGN_EXTEND || GET_CODE (src) == ZERO_EXTEND)
+  while (GET_CODE (src) == SIGN_EXTEND
+	 || GET_CODE (src) == ZERO_EXTEND
+	 || rtx_is_zext_p (src))
     src = XEXP (src, 0);
   gcc_assert (REG_P (src));
   return src;
@@ -882,8 +1262,7 @@  combine_reaching_defs (ext_cand *cand, const_rtx set_pat, ext_state *state)
 
       /* 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 (set), def_insn, cand->insn)
-	  || reg_set_between_p (SET_DEST (set), def_insn, cand->insn))
+      if (reg_used_set_between_p (SET_DEST (set), def_insn, cand->insn))
 	return false;
 
       /* We must be able to copy between the two registers.   Generate,
@@ -975,10 +1354,8 @@  combine_reaching_defs (ext_cand *cand, const_rtx set_pat, ext_state *state)
 	     used or set between the def_insn2 and def_insn exclusive.
 	     Likewise for the other reg, i.e. check both reg1 and reg2
 	     in the above comment.  */
-	  if (reg_used_between_p (SET_DEST (set), def_insn2, def_insn)
-	      || reg_set_between_p (SET_DEST (set), def_insn2, def_insn)
-	      || reg_used_between_p (src_reg, def_insn2, def_insn)
-	      || reg_set_between_p (src_reg, def_insn2, def_insn))
+	  if (reg_used_set_between_p (SET_DEST (set), def_insn2, def_insn)
+	      || reg_used_set_between_p (src_reg, def_insn2, def_insn))
 	    break;
 
 	  state->defs_list[0] = def_insn2;
@@ -1004,15 +1381,17 @@  combine_reaching_defs (ext_cand *cand, const_rtx set_pat, ext_state *state)
       cand->mode = mode;
     }
 
-  merge_successful = true;
-
+  merge_successful = false;
   /* Go through the defs vector and try to merge all the definitions
      in this vector.  */
   state->modified_list.truncate (0);
   FOR_EACH_VEC_ELT (state->defs_list, defs_ix, def_insn)
     {
       if (merge_def_and_ext (cand, def_insn, state))
-	state->modified_list.safe_push (def_insn);
+	{
+	  merge_successful = true;
+	  state->modified_list.safe_push (def_insn);
+	}
       else
         {
           merge_successful = false;
@@ -1045,34 +1424,75 @@  combine_reaching_defs (ext_cand *cand, const_rtx set_pat, ext_state *state)
 	 definitions could be merged.  */
       if (apply_change_group ())
         {
-          if (dump_file)
-            fprintf (dump_file, "All merges were successful.\n");
+	  if (state->modified_list.length () == 0)
+	     return false;
+
+	  rtx_insn *insn = state->modified_list[0];
+
+	  if ((cand->code == ZERO_EXTEND || cand->code == SIGN_EXTEND)
+	       && GET_CODE (PATTERN (insn)) == SET
+	       && GET_CODE (SET_SRC (PATTERN (insn))) != XOR
+	       && GET_CODE (SET_SRC (PATTERN (insn))) != PLUS
+	       && GET_CODE (SET_SRC (PATTERN (insn))) != IOR)
+	     return false;
+
+	   if (dump_file)
+	     fprintf (dump_file, "All merges were successful.\n");
 
 	  FOR_EACH_VEC_ELT (state->modified_list, i, def_insn)
 	    {
-	      ext_modified *modified = &state->modified[INSN_UID (def_insn)];
+	      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);
+		modified->kind
+		  = (cand->code == ZERO_EXTEND ? EXT_MODIFIED_ZEXT
+					       : EXT_MODIFIED_SEXT);
 
 	      if (copy_needed)
 		modified->do_not_reextend = 1;
 	    }
           return true;
         }
-      else
-        {
-          /* Changes need not be cancelled explicitly as apply_change_group
-             does it.  Print list of definitions in the dump_file for debug
-             purposes.  This extension cannot be deleted.  */
-          if (dump_file)
-            {
-	      fprintf (dump_file,
-		       "Merge cancelled, non-mergeable definitions:\n");
-	      FOR_EACH_VEC_ELT (state->modified_list, i, def_insn)
-	        print_rtl_single (dump_file, def_insn);
-            }
-        }
+	else
+	  {
+	    if (state->modified_list.length () == 0)
+	      return false;
+
+	     rtx_insn *insn = state->modified_list[0];
+
+	     if ((cand->code == ZERO_EXTEND || cand->code == SIGN_EXTEND)
+		  && GET_CODE (PATTERN (insn)) == SET
+		  && GET_CODE (SET_SRC (PATTERN (insn))) != XOR
+		  && GET_CODE (SET_SRC (PATTERN (insn))) != PLUS
+		  && GET_CODE (SET_SRC (PATTERN (insn))) != IOR)
+		return false;
+
+	    if (cand->code == ZERO_EXTEND || cand->code == SIGN_EXTEND)
+	      {
+		FOR_EACH_VEC_ELT (state->modified_list, i, def_insn)
+		  {
+		    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);
+
+		     modified->do_not_reextend = 1;
+		   }
+		  return true;
+	      }
+	    /* Changes need not be cancelled explicitly as apply_change_group
+		does it.  Print list of definitions in the dump_file for debug
+		purposes.  This extension cannot be deleted.  */
+	    if (dump_file)
+	      {
+		fprintf (dump_file,
+			"Merge cancelled, non-mergeable definitions:\n");
+		FOR_EACH_VEC_ELT (state->modified_list, i, def_insn)
+		  print_rtl_single (dump_file, def_insn);
+	      }
+	   }
     }
   else
     {
@@ -1106,7 +1526,8 @@  add_removable_extension (const_rtx expr, rtx_insn *insn,
   mode = GET_MODE (dest);
 
   if (REG_P (dest)
-      && (code == SIGN_EXTEND || code == ZERO_EXTEND)
+      && (code == SIGN_EXTEND || code == ZERO_EXTEND
+      || rtx_is_zext_p (src))
       && REG_P (XEXP (src, 0)))
     {
       rtx reg = XEXP (src, 0);
@@ -1125,7 +1546,7 @@  add_removable_extension (const_rtx expr, rtx_insn *insn,
 	      fprintf (dump_file, "Cannot eliminate extension:\n");
 	      print_rtl_single (dump_file, insn);
 	      fprintf (dump_file, " because it can operate on uninitialized"
-			          " data\n");
+				  " data\n");
 	    }
 	  return;
 	}
@@ -1320,8 +1741,8 @@  find_and_remove_re (void)
 	  if (REG_P (XEXP (SET_SRC (set), 0))
 	      && (REGNO (SET_DEST (set)) != REGNO (XEXP (SET_SRC (set), 0))))
 	    {
-              reinsn_copy_list.safe_push (curr_cand->insn);
-              reinsn_copy_list.safe_push (state.defs_list[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;
diff --git a/gcc/rtl.h b/gcc/rtl.h
index 2370d608161..56198f1a3a1 100644
--- a/gcc/rtl.h
+++ b/gcc/rtl.h
@@ -3639,6 +3639,7 @@  extern int count_occurrences (const_rtx, const_rtx, int);
 extern bool reg_referenced_p (const_rtx, const_rtx);
 extern bool reg_used_between_p (const_rtx, const rtx_insn *, const rtx_insn *);
 extern bool reg_set_between_p (const_rtx, const rtx_insn *, const rtx_insn *);
+extern bool reg_used_set_between_p (rtx, rtx_insn *, rtx_insn *);
 extern int commutative_operand_precedence (rtx);
 extern bool swap_commutative_operands_p (rtx, rtx);
 extern bool modified_between_p (const_rtx, const rtx_insn *, const rtx_insn *);
diff --git a/gcc/rtlanal.cc b/gcc/rtlanal.cc
index d38455bc559..f9af439abf4 100644
--- a/gcc/rtlanal.cc
+++ b/gcc/rtlanal.cc
@@ -1134,6 +1134,21 @@  no_labels_between_p (const rtx_insn *beg, const rtx_insn *end)
   return true;
 }
 
+/* The register reg of the extension to_insn must not be
+   used or set between the from_insn and to_insn exclusive.  */
+
+bool
+reg_used_set_between_p (rtx reg,
+			rtx_insn *from_insn,
+			rtx_insn *to_insn)
+{
+  if (reg_used_between_p (reg, from_insn, to_insn)
+      || reg_set_between_p (reg, from_insn, to_insn))
+    return true;
+
+  return false;
+}
+
 /* Return true if register REG is used in an insn between
    FROM_INSN and TO_INSN (exclusive of those two).  */
 
diff --git a/gcc/testsuite/g++.target/powerpc/sext-elim.C b/gcc/testsuite/g++.target/powerpc/sext-elim.C
new file mode 100644
index 00000000000..97185aa492b
--- /dev/null
+++ b/gcc/testsuite/g++.target/powerpc/sext-elim.C
@@ -0,0 +1,16 @@ 
+/* { dg-do compile { target { powerpc*-*-* } } } */
+/* { dg-require-effective-target lp64 } */
+/* { dg-options "-mcpu=power9 -O2 -free" } */
+
+unsigned long c2l(unsigned char* p)
+{
+  unsigned long res = *p + *(p+1);
+  return res;
+}
+
+long c2sl(signed char* p)
+{
+  long res = *p + *(p+1);
+  return res;
+}
+/* { dg-final { scan-assembler-not "extsw" } } */
diff --git a/gcc/testsuite/g++.target/powerpc/zext-elim-1.C b/gcc/testsuite/g++.target/powerpc/zext-elim-1.C
new file mode 100644
index 00000000000..6a6dfac8ed1
--- /dev/null
+++ b/gcc/testsuite/g++.target/powerpc/zext-elim-1.C
@@ -0,0 +1,18 @@ 
+/* { dg-do compile { target { powerpc*-*-* } } } */
+/* { dg-require-effective-target lp64 } */
+/* { dg-options "-mcpu=power9 -O2 -free" } */
+
+extern unsigned char magic1[256];
+
+unsigned int hash(const unsigned char inp[4])
+{
+   const unsigned long long INIT = 0x1ULL;
+   unsigned long long h1 = INIT;
+   h1 = magic1[((unsigned long long)inp[0]) ^ h1];
+   h1 = magic1[((unsigned long long)inp[1]) ^ h1];
+   h1 = magic1[((unsigned long long)inp[2]) ^ h1];
+   h1 = magic1[((unsigned long long)inp[3]) ^ h1];
+   return h1;
+}
+
+/* { dg-final { scan-assembler-not "rlwinm" } } */
diff --git a/gcc/testsuite/g++.target/powerpc/zext-elim-2.C b/gcc/testsuite/g++.target/powerpc/zext-elim-2.C
new file mode 100644
index 00000000000..210d9f5f53c
--- /dev/null
+++ b/gcc/testsuite/g++.target/powerpc/zext-elim-2.C
@@ -0,0 +1,10 @@ 
+/* { dg-do compile { target { powerpc*-*-* } } } */
+/* { dg-require-effective-target lp64 } */
+/* { dg-options "-mcpu=power9 -O2 -free" } */
+
+unsigned char g(unsigned char t[], unsigned char v)
+{
+  return (t[v & 0x7f] & 0x7f) | (v & 0x80);
+}
+
+/* { dg-final { scan-assembler-times "rlwinm" 2 } } */
diff --git a/gcc/testsuite/g++.target/powerpc/zext-elim.C b/gcc/testsuite/g++.target/powerpc/zext-elim.C
new file mode 100644
index 00000000000..9f850ab8ed2
--- /dev/null
+++ b/gcc/testsuite/g++.target/powerpc/zext-elim.C
@@ -0,0 +1,29 @@ 
+/* { dg-do compile { target { powerpc*-*-* } } } */
+/* { dg-require-effective-target lp64 } */
+/* { dg-options "-mcpu=power9 -O2 -free" } */
+
+#include <stddef.h>
+
+bool foo (int a, int b)
+{
+  if (a > 2)
+    return false;
+
+  if (b < 10)
+    return true;
+
+  return true;
+}
+
+int bar (int a, int b)
+{
+  if (a > 2)
+    return 0;
+
+  if (b < 10)
+    return 1;
+
+  return 0;
+}
+
+/* { dg-final { scan-assembler-not "rldicl" } } */