diff mbox series

[v2,3/4] Improve functionality of ree pass with various constants with AND operation.

Message ID 9d06fa96-0a59-8e47-1869-d6e34a24163a@linux.ibm.com
State New
Headers show
Series None | expand

Commit Message

Ajit Agarwal Sept. 19, 2023, 9:21 a.m. UTC
Hello Jeff:

This patch eliminates redundant zero and sign extension with ree pass for rs6000
target.

Bootstrapped and regtested for powerpc64-linux-gnu.

Thanks & Regards
Ajit


ree: Improve ree pass

For rs6000 target we see redundant zero and sign extension and ree pass
s improved to eliminate such redundant zero and sign extension. Support of
zero_extend/sign_extend/AND.

2023-09-04  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.
---
 gcc/ree.cc                                    | 487 ++++++++++++++++--
 gcc/rtl.h                                     |   1 +
 gcc/rtlanal.cc                                |  15 +
 gcc/testsuite/g++.target/powerpc/sext-elim.C  |  17 +
 .../g++.target/powerpc/zext-elim-1.C          |  19 +
 .../g++.target/powerpc/zext-elim-2.C          |  11 +
 gcc/testsuite/g++.target/powerpc/zext-elim.C  |  30 ++
 7 files changed, 534 insertions(+), 46 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 fc04249fa84..931b9b08821 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 blocls */
+	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,258 @@  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 false;
+
+  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;
+	    }
+	}
+     }
+  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);
+
+      /* Block has IF_THEN_ELSE  */
+      if (insn && set
+	  && GET_CODE (set) == SET && SET_SRC (set)
+	  && 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)
+	 && 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))
+    {
+      if (GET_CODE (PATTERN (BB_END (bb))) != USE)
+	return false;
+
+      if (REGNO (XEXP (PATTERN (BB_END (bb)), 0)) != REGNO (SET_DEST (cand->expr)))
+	return false;
+    }
+
+  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 +1051,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 +1092,6 @@  merge_def_and_ext (ext_cand *cand, rtx_insn *def_insn, ext_state *state)
 	  return true;
 	}
     }
-
   return false;
 }
 
@@ -744,7 +1101,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 +1241,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 +1333,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 +1360,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 +1403,71 @@  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)];
 	      if (modified->kind == EXT_MODIFIED_NONE)
 		modified->kind = (cand->code == ZERO_EXTEND ? EXT_MODIFIED_ZEXT
-						            : EXT_MODIFIED_SEXT);
+							    : 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 +1501,7 @@  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 +1520,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 +1715,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 0e9491b89b4..f47e41b9037 100644
--- a/gcc/rtl.h
+++ b/gcc/rtl.h
@@ -3636,6 +3636,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 8b48fc243a1..d971777c630 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..36fef0b7a2f
--- /dev/null
+++ b/gcc/testsuite/g++.target/powerpc/sext-elim.C
@@ -0,0 +1,17 @@ 
+/* { dg-do compile { target { powerpc*-*-* } } } */
+/* { dg-require-effective-target lp64 } */
+/* { dg-require-effective-target powerpc_p9vector_ok } */
+/* { 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..bc6cc0eb3ca
--- /dev/null
+++ b/gcc/testsuite/g++.target/powerpc/zext-elim-1.C
@@ -0,0 +1,19 @@ 
+/* { dg-do compile { target { powerpc*-*-* } } } */
+/* { dg-require-effective-target lp64 } */
+/* { dg-require-effective-target powerpc_p9vector_ok } */
+/* { 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..4e72925104f
--- /dev/null
+++ b/gcc/testsuite/g++.target/powerpc/zext-elim-2.C
@@ -0,0 +1,11 @@ 
+/* { dg-do compile { target { powerpc*-*-* } } } */
+/* { dg-require-effective-target lp64 } */
+/* { dg-require-effective-target powerpc_p9vector_ok } */
+/* { 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..56eabbe0c19
--- /dev/null
+++ b/gcc/testsuite/g++.target/powerpc/zext-elim.C
@@ -0,0 +1,30 @@ 
+/* { dg-do compile { target { powerpc*-*-* } } } */
+/* { dg-require-effective-target lp64 } */
+/* { dg-require-effective-target powerpc_p9vector_ok } */
+/* { 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" } } */