diff --git a/gcc/cse.c b/gcc/cse.c
index e3c0710215df0acca924ce74ffa54582125d0136..f15ae8693fbb243323dd049e21648a49546a4608 100644
--- a/gcc/cse.c
+++ b/gcc/cse.c
@@ -559,6 +559,7 @@ static struct table_elt *insert_with_costs (rtx, struct table_elt *, unsigned,
 static struct table_elt *insert (rtx, struct table_elt *, unsigned,
 				 machine_mode);
 static void merge_equiv_classes (struct table_elt *, struct table_elt *);
+static void invalidate_reg (rtx, bool);
 static void invalidate (rtx, machine_mode);
 static void remove_invalid_refs (unsigned int);
 static void remove_invalid_subreg_refs (unsigned int, poly_uint64,
@@ -1818,7 +1819,85 @@ check_dependence (const_rtx x, rtx exp, machine_mode mode, rtx addr)
     }
   return false;
 }
-

+
+/* Remove from the hash table, or mark as invalid, all expressions whose
+   values could be altered by storing in register X.
+
+   CLOBBER_HIGH is set if X was part of a CLOBBER_HIGH expression.  */
+
+static void
+invalidate_reg (rtx x, bool clobber_high)
+{
+  gcc_assert (GET_CODE (x) == REG);
+
+  /* If X is a register, dependencies on its contents are recorded
+     through the qty number mechanism.  Just change the qty number of
+     the register, mark it as invalid for expressions that refer to it,
+     and remove it itself.  */
+  unsigned int regno = REGNO (x);
+  unsigned int hash = HASH (x, GET_MODE (x));
+
+  /* Remove REGNO from any quantity list it might be on and indicate
+     that its value might have changed.  If it is a pseudo, remove its
+     entry from the hash table.
+
+     For a hard register, we do the first two actions above for any
+     additional hard registers corresponding to X.  Then, if any of these
+     registers are in the table, we must remove any REG entries that
+     overlap these registers.  */
+
+  delete_reg_equiv (regno);
+  REG_TICK (regno)++;
+  SUBREG_TICKED (regno) = -1;
+
+  if (regno >= FIRST_PSEUDO_REGISTER)
+    {
+      gcc_assert (!clobber_high);
+      remove_pseudo_from_table (x, hash);
+    }
+  else
+    {
+      HOST_WIDE_INT in_table = TEST_HARD_REG_BIT (hard_regs_in_table, regno);
+      unsigned int endregno = END_REGNO (x);
+      unsigned int rn;
+      struct table_elt *p, *next;
+
+      CLEAR_HARD_REG_BIT (hard_regs_in_table, regno);
+
+      for (rn = regno + 1; rn < endregno; rn++)
+	{
+	  in_table |= TEST_HARD_REG_BIT (hard_regs_in_table, rn);
+	  CLEAR_HARD_REG_BIT (hard_regs_in_table, rn);
+	  delete_reg_equiv (rn);
+	  REG_TICK (rn)++;
+	  SUBREG_TICKED (rn) = -1;
+	}
+
+      if (in_table)
+	for (hash = 0; hash < HASH_SIZE; hash++)
+	  for (p = table[hash]; p; p = next)
+	    {
+	      next = p->next_same_hash;
+
+	      if (!REG_P (p->exp) || REGNO (p->exp) >= FIRST_PSEUDO_REGISTER)
+		continue;
+
+	      if (clobber_high)
+		{
+		  if (reg_is_clobbered_by_clobber_high (p->exp, x))
+		    remove_from_table (p, hash);
+		}
+	      else
+		{
+		  unsigned int tregno = REGNO (p->exp);
+		  unsigned int tendregno = END_REGNO (p->exp);
+		  if (tendregno > regno && tregno < endregno)
+		    remove_from_table (p, hash);
+		}
+	    }
+    }
+}
+
 /* Remove from the hash table, or mark as invalid, all expressions whose
    values could be altered by storing in X.  X is a register, a subreg, or
    a memory reference with nonvarying address (because, when a memory
@@ -1841,65 +1920,7 @@ invalidate (rtx x, machine_mode full_mode)
   switch (GET_CODE (x))
     {
     case REG:
-      {
-	/* If X is a register, dependencies on its contents are recorded
-	   through the qty number mechanism.  Just change the qty number of
-	   the register, mark it as invalid for expressions that refer to it,
-	   and remove it itself.  */
-	unsigned int regno = REGNO (x);
-	unsigned int hash = HASH (x, GET_MODE (x));
-
-	/* Remove REGNO from any quantity list it might be on and indicate
-	   that its value might have changed.  If it is a pseudo, remove its
-	   entry from the hash table.
-
-	   For a hard register, we do the first two actions above for any
-	   additional hard registers corresponding to X.  Then, if any of these
-	   registers are in the table, we must remove any REG entries that
-	   overlap these registers.  */
-
-	delete_reg_equiv (regno);
-	REG_TICK (regno)++;
-	SUBREG_TICKED (regno) = -1;
-
-	if (regno >= FIRST_PSEUDO_REGISTER)
-	  remove_pseudo_from_table (x, hash);
-	else
-	  {
-	    HOST_WIDE_INT in_table
-	      = TEST_HARD_REG_BIT (hard_regs_in_table, regno);
-	    unsigned int endregno = END_REGNO (x);
-	    unsigned int tregno, tendregno, rn;
-	    struct table_elt *p, *next;
-
-	    CLEAR_HARD_REG_BIT (hard_regs_in_table, regno);
-
-	    for (rn = regno + 1; rn < endregno; rn++)
-	      {
-		in_table |= TEST_HARD_REG_BIT (hard_regs_in_table, rn);
-		CLEAR_HARD_REG_BIT (hard_regs_in_table, rn);
-		delete_reg_equiv (rn);
-		REG_TICK (rn)++;
-		SUBREG_TICKED (rn) = -1;
-	      }
-
-	    if (in_table)
-	      for (hash = 0; hash < HASH_SIZE; hash++)
-		for (p = table[hash]; p; p = next)
-		  {
-		    next = p->next_same_hash;
-
-		    if (!REG_P (p->exp)
-			|| REGNO (p->exp) >= FIRST_PSEUDO_REGISTER)
-		      continue;
-
-		    tregno = REGNO (p->exp);
-		    tendregno = END_REGNO (p->exp);
-		    if (tendregno > regno && tregno < endregno)
-		      remove_from_table (p, hash);
-		  }
-	  }
-      }
+      invalidate_reg (x, false);
       return;

     case SUBREG:
@@ -4391,6 +4412,8 @@ canonicalize_insn (rtx_insn *insn, struct set **psets, int n_sets)
       if (MEM_P (XEXP (x, 0)))
 	canon_reg (XEXP (x, 0), insn);
     }
+  else if (GET_CODE (x) == CLOBBER_HIGH)
+    gcc_assert (REG_P (XEXP (x, 0)));
   else if (GET_CODE (x) == USE
 	   && ! (REG_P (XEXP (x, 0))
 		 && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER))
@@ -4422,6 +4445,8 @@ canonicalize_insn (rtx_insn *insn, struct set **psets, int n_sets)
 	      if (MEM_P (XEXP (y, 0)))
 		canon_reg (XEXP (y, 0), insn);
 	    }
+	  else if (GET_CODE (y) == CLOBBER_HIGH)
+	    gcc_assert (REG_P (XEXP (y, 0)));
 	  else if (GET_CODE (y) == USE
 		   && ! (REG_P (XEXP (y, 0))
 			 && REGNO (XEXP (y, 0)) < FIRST_PSEUDO_REGISTER))
@@ -6122,6 +6147,12 @@ invalidate_from_clobbers (rtx_insn *insn)
 	    invalidate (XEXP (ref, 0), GET_MODE (ref));
 	}
     }
+  if (GET_CODE (x) == CLOBBER_HIGH)
+    {
+      rtx ref = XEXP (x, 0);
+      gcc_assert (REG_P (ref));
+      invalidate_reg (ref, true);
+    }
   else if (GET_CODE (x) == PARALLEL)
     {
       int i;
@@ -6138,6 +6169,12 @@ invalidate_from_clobbers (rtx_insn *insn)
 		       || GET_CODE (ref) == ZERO_EXTRACT)
 		invalidate (XEXP (ref, 0), GET_MODE (ref));
 	    }
+	  else if (GET_CODE (y) == CLOBBER_HIGH)
+	    {
+	      rtx ref = XEXP (y, 0);
+	      gcc_assert (REG_P (ref));
+	      invalidate_reg (ref, true);
+	    }
 	}
     }
 }
@@ -6155,8 +6192,17 @@ invalidate_from_sets_and_clobbers (rtx_insn *insn)
   if (CALL_P (insn))
     {
       for (tem = CALL_INSN_FUNCTION_USAGE (insn); tem; tem = XEXP (tem, 1))
-	if (GET_CODE (XEXP (tem, 0)) == CLOBBER)
-	  invalidate (SET_DEST (XEXP (tem, 0)), VOIDmode);
+	{
+	  rtx temx = XEXP (tem, 0);
+	  if (GET_CODE (temx) == CLOBBER)
+	    invalidate (SET_DEST (temx), VOIDmode);
+	  else if (GET_CODE (temx) == CLOBBER_HIGH)
+	    {
+	      rtx temref = XEXP (temx, 0);
+	      gcc_assert (REG_P (temref));
+	      invalidate_reg (temref, true);
+	    }
+	}
     }

   /* Ensure we invalidate the destination register of a CALL insn.
@@ -6183,6 +6229,12 @@ invalidate_from_sets_and_clobbers (rtx_insn *insn)
 		       || GET_CODE (clobbered) == ZERO_EXTRACT)
 		invalidate (XEXP (clobbered, 0), GET_MODE (clobbered));
 	    }
+	  else if (GET_CODE (y) == CLOBBER_HIGH)
+	    {
+	      rtx ref = XEXP (y, 0);
+	      gcc_assert (REG_P (ref));
+	      invalidate_reg (ref, true);
+	    }
 	  else if (GET_CODE (y) == SET && GET_CODE (SET_SRC (y)) == CALL)
 	    invalidate (SET_DEST (y), VOIDmode);
 	}
@@ -6842,6 +6894,10 @@ count_reg_usage (rtx x, int *counts, rtx dest, int incr)
 	count_reg_usage (XEXP (XEXP (x, 0), 0), counts, NULL_RTX, incr);
       return;

+    case CLOBBER_HIGH:
+      gcc_assert (REG_P ((XEXP (x, 0))));
+      return;
+
     case SET:
       /* Unless we are setting a REG, count everything in SET_DEST.  */
       if (!REG_P (SET_DEST (x)))
@@ -6894,7 +6950,8 @@ count_reg_usage (rtx x, int *counts, rtx dest, int incr)
 	  || (REG_NOTE_KIND (x) != REG_NONNEG && GET_CODE (XEXP (x,0)) == USE)
 	  /* FUNCTION_USAGE expression lists may include (CLOBBER (mem /u)),
 	     involving registers in the address.  */
-	  || GET_CODE (XEXP (x, 0)) == CLOBBER)
+	  || GET_CODE (XEXP (x, 0)) == CLOBBER
+	  || GET_CODE (XEXP (x, 0)) == CLOBBER_HIGH)
 	count_reg_usage (XEXP (x, 0), counts, NULL_RTX, incr);

       count_reg_usage (XEXP (x, 1), counts, NULL_RTX, incr);
@@ -6978,7 +7035,9 @@ insn_live_p (rtx_insn *insn, int *counts)
 	      if (set_live_p (elt, insn, counts))
 		return true;
 	    }
-	  else if (GET_CODE (elt) != CLOBBER && GET_CODE (elt) != USE)
+	  else if (GET_CODE (elt) != CLOBBER
+		   && GET_CODE (elt) != CLOBBER_HIGH
+		   && GET_CODE (elt) != USE)
 	    return true;
 	}
       return false;
diff --git a/gcc/cselib.h b/gcc/cselib.h
index dd949196b7d4a46f2b157ae3874241ee116d022d..ad4cce4ebbda1556124259be4d84edf410fb14cd 100644
--- a/gcc/cselib.h
+++ b/gcc/cselib.h
@@ -92,7 +92,7 @@ extern bool cselib_dummy_expand_value_rtx_cb (rtx, bitmap, int,
 					      cselib_expand_callback, void *);
 extern rtx cselib_subst_to_values (rtx, machine_mode);
 extern rtx cselib_subst_to_values_from_insn (rtx, machine_mode, rtx_insn *);
-extern void cselib_invalidate_rtx (rtx);
+extern void cselib_invalidate_rtx (rtx, const_rtx = NULL);

 extern void cselib_reset_table (unsigned int);
 extern unsigned int cselib_get_next_uid (void);
diff --git a/gcc/cselib.c b/gcc/cselib.c
index 9c8f206c909925c58f0ab1a14853caf738bbfc4f..92f91a463f2519b09545d0b050de5521427d4929 100644
--- a/gcc/cselib.c
+++ b/gcc/cselib.c
@@ -54,7 +54,8 @@ static unsigned int cselib_hash_rtx (rtx, int, machine_mode);
 static cselib_val *new_cselib_val (unsigned int, machine_mode, rtx);
 static void add_mem_for_addr (cselib_val *, cselib_val *, rtx);
 static cselib_val *cselib_lookup_mem (rtx, int);
-static void cselib_invalidate_regno (unsigned int, machine_mode);
+static void cselib_invalidate_regno (unsigned int, machine_mode,
+				     const_rtx = NULL);
 static void cselib_invalidate_mem (rtx);
 static void cselib_record_set (rtx, cselib_val *, cselib_val *);
 static void cselib_record_sets (rtx_insn *);
@@ -1661,6 +1662,7 @@ cselib_expand_value_rtx_1 (rtx orig, struct expand_value_data *evd,
       /* SCRATCH must be shared because they represent distinct values.  */
       return orig;
     case CLOBBER:
+    case CLOBBER_HIGH:
       if (REG_P (XEXP (orig, 0)) && HARD_REGISTER_NUM_P (REGNO (XEXP (orig, 0))))
 	return orig;
       break;
@@ -2163,7 +2165,8 @@ cselib_lookup (rtx x, machine_mode mode,
    invalidating call clobbered registers across a call.  */

 static void
-cselib_invalidate_regno (unsigned int regno, machine_mode mode)
+cselib_invalidate_regno (unsigned int regno, machine_mode mode,
+			 const_rtx setter)
 {
   unsigned int endregno;
   unsigned int i;
@@ -2186,6 +2189,9 @@ cselib_invalidate_regno (unsigned int regno, machine_mode mode)
 	i = regno - max_value_regs;

       endregno = end_hard_regno (mode, regno);
+
+      if (setter && GET_CODE (setter) == CLOBBER_HIGH)
+	gcc_assert (endregno == regno + 1);
     }
   else
     {
@@ -2218,6 +2224,19 @@ cselib_invalidate_regno (unsigned int regno, machine_mode mode)
 	      continue;
 	    }

+	  /* Ignore if clobber high and the register isn't clobbered.  */
+	  if (setter && GET_CODE (setter) == CLOBBER_HIGH)
+	    {
+	      gcc_assert (endregno == regno + 1);
+	      const_rtx x = XEXP (setter, 0);
+	      if (!reg_is_clobbered_by_clobber_high (i, GET_MODE (v->val_rtx),
+						     x))
+		{
+		  l = &(*l)->next;
+		  continue;
+		}
+	    }
+
 	  /* We have an overlap.  */
 	  if (*l == REG_VALUES (i))
 	    {
@@ -2352,10 +2371,10 @@ cselib_invalidate_mem (rtx mem_rtx)
   *vp = &dummy_val;
 }

-/* Invalidate DEST, which is being assigned to or clobbered.  */
+/* Invalidate DEST, which is being assigned to or clobbered by SETTER.  */

 void
-cselib_invalidate_rtx (rtx dest)
+cselib_invalidate_rtx (rtx dest, const_rtx setter)
 {
   while (GET_CODE (dest) == SUBREG
 	 || GET_CODE (dest) == ZERO_EXTRACT
@@ -2363,7 +2382,7 @@ cselib_invalidate_rtx (rtx dest)
     dest = XEXP (dest, 0);

   if (REG_P (dest))
-    cselib_invalidate_regno (REGNO (dest), GET_MODE (dest));
+    cselib_invalidate_regno (REGNO (dest), GET_MODE (dest), setter);
   else if (MEM_P (dest))
     cselib_invalidate_mem (dest);
 }
@@ -2371,10 +2390,10 @@ cselib_invalidate_rtx (rtx dest)
 /* A wrapper for cselib_invalidate_rtx to be called via note_stores.  */

 static void
-cselib_invalidate_rtx_note_stores (rtx dest, const_rtx ignore ATTRIBUTE_UNUSED,
+cselib_invalidate_rtx_note_stores (rtx dest, const_rtx setter,
 				   void *data ATTRIBUTE_UNUSED)
 {
-  cselib_invalidate_rtx (dest);
+  cselib_invalidate_rtx (dest, setter);
 }

 /* Record the result of a SET instruction.  DEST is being set; the source
@@ -2710,9 +2729,12 @@ cselib_process_insn (rtx_insn *insn)
   if (CALL_P (insn))
     {
       for (x = CALL_INSN_FUNCTION_USAGE (insn); x; x = XEXP (x, 1))
-	if (GET_CODE (XEXP (x, 0)) == CLOBBER)
-	  cselib_invalidate_rtx (XEXP (XEXP (x, 0), 0));
-      /* Flush evertything on setjmp.  */
+	{
+	  gcc_assert (GET_CODE (XEXP (x, 0)) != CLOBBER_HIGH);
+	  if (GET_CODE (XEXP (x, 0)) == CLOBBER)
+	    cselib_invalidate_rtx (XEXP (XEXP (x, 0), 0));
+	}
+      /* Flush everything on setjmp.  */
       if (cselib_preserve_constants
 	  && find_reg_note (insn, REG_SETJMP, NULL))
 	{
