Patchwork rs6000 toc reference rtl

login
register
mail settings
Submitter Alan Modra
Date Sept. 5, 2011, 3:41 p.m.
Message ID <20110905154126.GS18554@bubble.grove.modra.org>
Download mbox | patch
Permalink /patch/113386/
State New
Headers show

Comments

Alan Modra - Sept. 5, 2011, 3:41 p.m.
This patch changes the rtl used by rs6000 for a TOC reference address.
The old rtl for -mcmodel=small is:
  (plus (reg tocreg) (const (unspec [(symbol_ref sym)] UNSPEC_TOCREL)))
while the old rtl for -mcmodel=medium/large is:
  (set (reg rn)
    (const (plus (reg r2) (high (const (unspec [(symbol_ref sym)]
					       UNSPEC_TOCREL))))))
  (lo_sum (reg rn) (const (unspec [(symbol_ref sym)] UNSPEC_TOCREL)))

These change to
  (unspec [(symbol_ref sym) (reg tocreg)] UNSPEC_TOCREL)
and
  (set (reg rn)
    (high (unspec [(symbol_ref sym) (reg r2)] UNSPEC_TOCREL)))
  (lo_sum (reg rn) (unspec [(symbol_ref sym) (reg r2)] UNSPEC_TOCREL))
respectively.

Why do I want to do this, apart from simplifying the rtl?  Well,
df-scan.c and other parts of gcc don't look inside a CONST for regs
(arguably a bug), so we don't see r2 as being live for the high part
of a large-toc sequence.  Consequently, Mike's change to split rtl for
indirect calls sometimes sees the scheduler moving the r2 load in the
indirect call sequence before a toc reference.  While I could have
tackled the CONST problem, I reckon doing so would be more likely to
destabilize other targets than the changes to cselib.c and cprop.c
below.

Bootstrapped and regression tested powerpc-linux and powerpc64-linux,
and also with a -mcmodel=small default.  OK for mainline?  Further
comments/explanation interspersed with changelog.

	* cselib.c (preserve_only_constants): Remove HIGH rtx containing
	value references.
	* cprop.c (cprop_constant_p): Return false for HIGH rtx containing
	value references.

Regs inside a HIGH cause value refs to appear.

	* config/rs6000/predicates.md (input_operand): Match unspec.  Remove
	redundant tests.

legitimate_constant_pool_address_p true now implies
toc_relative_expr_p is true.

	* rs6000-protos.h (toc_relative_expr_p): Update prototype.
	* const/rs6000/rs6000.c (tocrel_base, tocrel_offset): Make const.

const_rtx is a pain.  mode_dependent_address forces a const_rtx down
to here, but we need to pass this on to output_addr_const.

	(legitimate_constant_pool_address_p): Move TARGET_TOC test and
	register checks to..
	(toc_relative_expr_p): ..here.  Add "strict" param.  Match new rtl
	generated by create_TOC_reference.

toc_relative_expr_p is needed in input_operand for 'R' constraint, but
we can't allow medium/large toc to match without ensuring reload
actually gives us a reg.

	(rs6000_delegitimize_address): Handle new rtl for toc refs.

Simpler checks here should fix 50191 as a side-effect.

	(rs6000_cannot_force_const_mem, rs6000_find_base_term): Likewise.
	(use_toc_relative_ref): New function, split out from..
	(rs6000_emit_move): ..here.  Remove redundant tests.
	(rs6000_legitimize_reload_address): Formatting.  Remove redundant
	code.  Use use_toc_relative_ref.

The existing lo_sum/high case handles new toc refs.

	(print_operand): Formatting, style.  Adjust for toc changes.
	(print_operand_address): Likewise.
	(rs6000_output_addr_const_extra): Likewise.
	(create_TOC_reference): Put TOC_REGISTER in UNSPEC_TOCREL rather
	than a PLUS.  Use this formulation for both high and low part
	of -mcmodel=medium/large toc reference too.

Making the low-part depend on r2 is deliberate and necessary in the
face of ld editing code to nop out the high part.

	* config/rs6000/rs6000.md (tls_gd, tls_gd_high): Similarly avoid
	a PLUS in high part of addresses here.
	(tls_ld, tls_ld_high, tls_got_dtprel, tls_got_dtprel_high): Likewise.
	(tls_got_tprel, tls_got_tprel_high, largetoc_high): Likewise.
	(largetoc_high, largetoc_low): Move earlier.  Cope when no
	base reg available.
Alan Modra - Sept. 7, 2011, 9:12 a.m.
On Tue, Sep 06, 2011 at 01:11:26AM +0930, Alan Modra wrote:
> Consequently, Mike's change to split rtl for
> indirect calls sometimes sees the scheduler moving the r2 load in the
> indirect call sequence before a toc reference.

Actually, this isn't correct.  Mike's change adding rs6000.c
rs6000_call_indirect_aix just made it more likely.  Even before this
post-reload scheduling could move the r2 load around, since rs6000.md
call_indirect_aix patterns were (and still are) split post-reload.

Here's an example I was shown today of such damage (qemu compiled
with gcc-4.6-redhat).

.LVL57151:
	ld 0,0(31)			# load opd+0, function addr
	addis 4,2,.LC4758@toc@ha
	ld 11,16(31)
	mr 7,3
	std 2,40(1)			# save r2
	mr 5,25
	addi 4,4,.LC4758@toc@l
	mtctr 0				#
	mr 6,26
	ld 2,8(31)			# load opd+8, new toc ptr in r2
	mr 3,28
.LBB255670:
.LBB255668:
	.loc 8 98 0
	addis 27,2,.LC4761@toc@ha	# oops, should be using old r2
.LVL57152:
	addi 27,27,.LC4761@toc@l
.LBE255668:
.LBE255670:
	.loc 3 9212 0
	addis 25,2,.LC4762@toc@ha	# oops again
	.loc 3 9198 0
	bctrl				# make the call
	ld 2,40(1)			# restore r2

r27 and r25 set up here for later use now contain bogus values.
The blame rests on my 2011-06-20 change.

Patch

Index: gcc/cselib.c
===================================================================
--- gcc/cselib.c	(revision 178035)
+++ gcc/cselib.c	(working copy)
@@ -325,8 +325,9 @@  preserve_only_constants (void **x, void 
       && v->locs->next == NULL)
     {
       if (CONSTANT_P (v->locs->loc)
-	  && (GET_CODE (v->locs->loc) != CONST
-	      || !references_value_p (v->locs->loc, 0)))
+	  && !((GET_CODE (v->locs->loc) == CONST
+		|| GET_CODE (v->locs->loc) == HIGH)
+	       && references_value_p (v->locs->loc, 0)))
 	return 1;
       if (cfa_base_preserved_val)
 	{
Index: gcc/cprop.c
===================================================================
--- gcc/cprop.c	(revision 178035)
+++ gcc/cprop.c	(working copy)
@@ -253,7 +253,13 @@  insert_set_in_table (rtx dest, rtx src, 
 static bool
 cprop_constant_p (const_rtx x)
 {
-  return CONSTANT_P (x) && (GET_CODE (x) != CONST || shared_const_p (x));
+  if (!CONSTANT_P (x))
+    return false;
+  if (GET_CODE (x) == CONST)
+    return shared_const_p (x);
+  if (GET_CODE (x) == HIGH)
+    return !references_value_p (x, false);
+  return true;
 }
 
 /* Scan pattern PAT of INSN and add an entry to the hash TABLE (set or
Index: gcc/config/rs6000/predicates.md
===================================================================
--- gcc/config/rs6000/predicates.md	(revision 178035)
+++ gcc/config/rs6000/predicates.md	(working copy)
@@ -815,7 +815,7 @@  (define_predicate "current_file_function
 ;; Return 1 if this operand is a valid input for a move insn.
 (define_predicate "input_operand"
   (match_code "label_ref,symbol_ref,const,high,reg,subreg,mem,
-	       const_double,const_vector,const_int,plus")
+	       const_double,const_vector,const_int,plus,unspec")
 {
   /* Memory is always valid.  */
   if (memory_operand (op, mode))
@@ -823,7 +823,6 @@  (define_predicate "input_operand"
 
   /* For floating-point, easy constants are valid.  */
   if (SCALAR_FLOAT_MODE_P (mode)
-      && CONSTANT_P (op)
       && easy_fp_constant (op, mode))
     return 1;
 
@@ -857,11 +856,7 @@  (define_predicate "input_operand"
     return 1;
 
   /* A SYMBOL_REF referring to the TOC is valid.  */
-  if (legitimate_constant_pool_address_p (op, mode, false))
-    return 1;
-
-  /* A constant pool expression (relative to the TOC) is valid */
-  if (toc_relative_expr_p (op))
+  if (toc_relative_expr_p (op, false))
     return 1;
 
   /* V.4 allows SYMBOL_REFs and CONSTs that are in the small data region
Index: gcc/config/rs6000/rs6000-protos.h
===================================================================
--- gcc/config/rs6000/rs6000-protos.h	(revision 178035)
+++ gcc/config/rs6000/rs6000-protos.h	(working copy)
@@ -38,7 +38,7 @@  extern bool macho_lo_sum_memory_operand 
 extern int num_insns_constant (rtx, enum machine_mode);
 extern int num_insns_constant_wide (HOST_WIDE_INT);
 extern int small_data_operand (rtx, enum machine_mode);
-extern bool toc_relative_expr_p (rtx);
+extern bool toc_relative_expr_p (const_rtx, bool);
 extern bool invalid_e500_subreg (rtx, enum machine_mode);
 extern void validate_condition_mode (enum rtx_code, enum machine_mode);
 extern bool legitimate_constant_pool_address_p (const_rtx, enum machine_mode,
Index: gcc/config/rs6000/rs6000.c
===================================================================
--- gcc/config/rs6000/rs6000.c	(revision 178035)
+++ gcc/config/rs6000/rs6000.c	(working copy)
@@ -5271,15 +5271,32 @@  constant_pool_expr_p (rtx op)
 	  && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (base), Pmode));
 }
 
-static rtx tocrel_base, tocrel_offset;
+static const_rtx tocrel_base, tocrel_offset;
 
 bool
-toc_relative_expr_p (rtx op)
+toc_relative_expr_p (const_rtx op, bool strict)
 {
-  if (GET_CODE (op) != CONST)
+  if (!TARGET_TOC)
     return false;
 
-  split_const (op, &tocrel_base, &tocrel_offset);
+  if (TARGET_CMODEL != CMODEL_SMALL)
+    {
+      /* Only match the low part.  */
+      if (GET_CODE (op) != LO_SUM
+	  || GET_CODE (XEXP (op, 0)) != REG
+	  || !INT_REG_OK_FOR_BASE_P (XEXP (op, 0), strict))
+	return false;
+      op = XEXP (op, 1);
+    }
+
+  tocrel_base = op;
+  tocrel_offset = const0_rtx;
+  if (GET_CODE (op) == PLUS && CONST_INT_P (XEXP (op, 1)))
+    {
+      tocrel_base = XEXP (op, 0);
+      tocrel_offset = XEXP (op, 1);
+    }
+
   return (GET_CODE (tocrel_base) == UNSPEC
 	  && XINT (tocrel_base, 1) == UNSPEC_TOCREL);
 }
@@ -5291,14 +5308,7 @@  bool
 legitimate_constant_pool_address_p (const_rtx x, enum machine_mode mode,
 				    bool strict)
 {
-  return (TARGET_TOC
-	  && (GET_CODE (x) == PLUS || GET_CODE (x) == LO_SUM)
-	  && GET_CODE (XEXP (x, 0)) == REG
-	  && (REGNO (XEXP (x, 0)) == TOC_REGISTER
-	      || ((TARGET_MINIMAL_TOC
-		   || TARGET_CMODEL != CMODEL_SMALL)
-		  && INT_REG_OK_FOR_BASE_P (XEXP (x, 0), strict)))
-	  && toc_relative_expr_p (XEXP (x, 1))
+  return (toc_relative_expr_p (x, strict)
 	  && (TARGET_CMODEL != CMODEL_MEDIUM
 	      || constant_pool_expr_p (XVECEXP (tocrel_base, 0, 0))
 	      || mode == QImode
@@ -5764,49 +5774,37 @@  rs6000_output_dwarf_dtprel (FILE *file, 
 static rtx
 rs6000_delegitimize_address (rtx orig_x)
 {
-  rtx x, y;
+  rtx x, y, offset;
 
   orig_x = delegitimize_mem_from_attrs (orig_x);
   x = orig_x;
   if (MEM_P (x))
     x = XEXP (x, 0);
 
-  if (GET_CODE (x) == (TARGET_CMODEL != CMODEL_SMALL ? LO_SUM : PLUS)
-      && GET_CODE (XEXP (x, 1)) == CONST)
+  y = x;
+  if (TARGET_CMODEL != CMODEL_SMALL
+      && GET_CODE (y) == LO_SUM)
+    y = XEXP (y, 1);
+
+  offset = NULL_RTX;
+  if (GET_CODE (y) == PLUS
+      && GET_MODE (y) == Pmode
+      && CONST_INT_P (XEXP (y, 1)))
     {
-      rtx offset = NULL_RTX;
+      offset = XEXP (y, 1);
+      y = XEXP (y, 0);
+    }
 
-      y = XEXP (XEXP (x, 1), 0);
-      if (GET_CODE (y) == PLUS
-	  && GET_MODE (y) == Pmode
-	  && CONST_INT_P (XEXP (y, 1)))
-	{
-	  offset = XEXP (y, 1);
-	  y = XEXP (y, 0);
-	}
-      if (GET_CODE (y) == UNSPEC
-          && XINT (y, 1) == UNSPEC_TOCREL
-	  && ((GET_CODE (XEXP (x, 0)) == REG
-	       && (REGNO (XEXP (x, 0)) == TOC_REGISTER
-		   || TARGET_MINIMAL_TOC
-		   || TARGET_CMODEL != CMODEL_SMALL))
-	      || (TARGET_CMODEL != CMODEL_SMALL
-		  && GET_CODE (XEXP (x, 0)) == CONST
-		  && GET_CODE (XEXP (XEXP (x, 0), 0)) == PLUS
-		  && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == REG
-		  && REGNO (XEXP (XEXP (XEXP (x, 0), 0), 0)) == TOC_REGISTER
-		  && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)) == HIGH
-		  && rtx_equal_p (XEXP (x, 1),
-				  XEXP (XEXP (XEXP (XEXP (x, 0), 0), 1), 0)))))
-	{
-	  y = XVECEXP (y, 0, 0);
-	  if (offset != NULL_RTX)
-	    y = gen_rtx_PLUS (Pmode, y, offset);
-	  if (!MEM_P (orig_x))
-	    return y;
-	  else
-	    return replace_equiv_address_nv (orig_x, y);
-	}
+  if (GET_CODE (y) == UNSPEC
+      && XINT (y, 1) == UNSPEC_TOCREL)
+    {
+      y = XVECEXP (y, 0, 0);
+      if (offset != NULL_RTX)
+	y = gen_rtx_PLUS (Pmode, y, offset);
+      if (!MEM_P (orig_x))
+	return y;
+      else
+	return replace_equiv_address_nv (orig_x, y);
     }
 
   if (TARGET_MACHO
@@ -6047,9 +6045,8 @@  rs6000_tls_referenced_p (rtx x)
 static bool
 rs6000_cannot_force_const_mem (enum machine_mode mode ATTRIBUTE_UNUSED, rtx x)
 {
-  if (GET_CODE (x) == CONST
-      && GET_CODE (XEXP (x, 0)) == PLUS
-      && GET_CODE (XEXP (XEXP (x, 0), 1)) == HIGH)
+  if (GET_CODE (x) == HIGH
+      && GET_CODE (XEXP (x, 0)) == UNSPEC)
     return true;
 
   return rs6000_tls_referenced_p (x);
@@ -6064,6 +6061,21 @@  rs6000_tls_symbol_ref_1 (rtx *x, void *d
   return RS6000_SYMBOL_REF_TLS_P (*x);
 }
 
+/* Return true iff the given SYMBOL_REF refers to a constant pool entry
+   that we have put in the TOC, or for cmodel=medium, if the SYMBOL_REF
+   can be addressed relative to the toc pointer.  */
+
+static bool
+use_toc_relative_ref (rtx sym)
+{
+  return ((constant_pool_expr_p (sym)
+	   && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (sym),
+					       get_pool_mode (sym)))
+	  || (TARGET_CMODEL == CMODEL_MEDIUM
+	      && !CONSTANT_POOL_ADDRESS_P (sym)
+	      && SYMBOL_REF_LOCAL_P (sym)));
+}
+
 /* Our implementation of LEGITIMIZE_RELOAD_ADDRESS.  Returns a value to
    replace the input X, or the original X if no replacement is called for.
    The output parameter *WIN is 1 if the calling macro should goto WIN,
@@ -6101,7 +6113,7 @@  rs6000_legitimize_reload_address (rtx x,
     {
       push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
 		   BASE_REG_CLASS, GET_MODE (x), VOIDmode, 0, 0,
-		   opnum, (enum reload_type)type);
+		   opnum, (enum reload_type) type);
       *win = 1;
       return x;
     }
@@ -6112,7 +6124,7 @@  rs6000_legitimize_reload_address (rtx x,
     {
       push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
 		   BASE_REG_CLASS, Pmode, VOIDmode, 0, 0,
-		   opnum, (enum reload_type)type);
+		   opnum, (enum reload_type) type);
       *win = 1;
       return x;
     }
@@ -6130,30 +6142,11 @@  rs6000_legitimize_reload_address (rtx x,
 	 floating point constant.  */
       push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
 		   BASE_REG_CLASS, Pmode, VOIDmode, 0, 0,
-		   opnum, (enum reload_type)type);
-      *win = 1;
-      return x;
-    }
-#endif
-
-  if (TARGET_CMODEL != CMODEL_SMALL
-      && GET_CODE (x) == LO_SUM
-      && GET_CODE (XEXP (x, 0)) == PLUS
-      && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG
-      && REGNO (XEXP (XEXP (x, 0), 0)) == TOC_REGISTER
-      && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST
-      && GET_CODE (XEXP (XEXP (XEXP (x, 0), 1), 0)) == HIGH
-      && GET_CODE (XEXP (x, 1)) == CONST
-      && GET_CODE (XEXP (XEXP (x, 1), 0)) == UNSPEC
-      && XINT (XEXP (XEXP (x, 1), 0), 1) == UNSPEC_TOCREL
-      && rtx_equal_p (XEXP (XEXP (XEXP (XEXP (x, 0), 1), 0), 0), XEXP (x, 1)))
-    {
-      push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
-		   BASE_REG_CLASS, Pmode, VOIDmode, 0, 0,
 		   opnum, (enum reload_type) type);
       *win = 1;
       return x;
     }
+#endif
 
   /* Force ld/std non-word aligned offset into base register by wrapping
      in offset 0.  */
@@ -6210,7 +6203,7 @@  rs6000_legitimize_reload_address (rtx x,
 
       push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
 		   BASE_REG_CLASS, GET_MODE (x), VOIDmode, 0, 0,
-		   opnum, (enum reload_type)type);
+		   opnum, (enum reload_type) type);
       *win = 1;
       return x;
     }
@@ -6250,7 +6243,7 @@  rs6000_legitimize_reload_address (rtx x,
 
       push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
 		   BASE_REG_CLASS, Pmode, VOIDmode, 0, 0,
-		   opnum, (enum reload_type)type);
+		   opnum, (enum reload_type) type);
       *win = 1;
       return x;
     }
@@ -6277,8 +6270,7 @@  rs6000_legitimize_reload_address (rtx x,
   if (TARGET_TOC
       && reg_offset_p
       && GET_CODE (x) == SYMBOL_REF
-      && constant_pool_expr_p (x)
-      && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (x), mode))
+      && use_toc_relative_ref (x))
     {
       x = create_TOC_reference (x, NULL_RTX);
       if (TARGET_CMODEL != CMODEL_SMALL)
@@ -6513,9 +6505,13 @@  rs6000_debug_mode_dependent_address (con
 rtx
 rs6000_find_base_term (rtx op)
 {
-  rtx base, offset;
+  rtx base;
 
-  split_const (op, &base, &offset);
+  base = op;
+  if (GET_CODE (base) == CONST)
+    base = XEXP (base, 0);
+  if (GET_CODE (base) == PLUS)
+    base = XEXP (base, 0);
   if (GET_CODE (base) == UNSPEC)
     switch (XINT (base, 1))
       {
@@ -7170,15 +7163,9 @@  rs6000_emit_move (rtx dest, rtx source, 
       /* If this is a SYMBOL_REF that refers to a constant pool entry,
 	 and we have put it in the TOC, we just need to make a TOC-relative
 	 reference to it.  */
-      if ((TARGET_TOC
-	   && GET_CODE (operands[1]) == SYMBOL_REF
-	   && constant_pool_expr_p (operands[1])
-	   && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (operands[1]),
-					       get_pool_mode (operands[1])))
-	  || (TARGET_CMODEL == CMODEL_MEDIUM
-	      && GET_CODE (operands[1]) == SYMBOL_REF
-	      && !CONSTANT_POOL_ADDRESS_P (operands[1])
-	      && SYMBOL_REF_LOCAL_P (operands[1])))
+      if (TARGET_TOC
+	  && GET_CODE (operands[1]) == SYMBOL_REF
+	  && use_toc_relative_ref (operands[1]))
 	{
 	  rtx reg = NULL_RTX;
 	  if (TARGET_CMODEL != CMODEL_SMALL)
@@ -7193,10 +7180,6 @@  rs6000_emit_move (rtx dest, rtx source, 
       else if (mode == Pmode
 	       && CONSTANT_P (operands[1])
 	       && GET_CODE (operands[1]) != HIGH
-	       && !(TARGET_CMODEL != CMODEL_SMALL
-		    && GET_CODE (operands[1]) == CONST
-		    && GET_CODE (XEXP (operands[1], 0)) == PLUS
-		    && GET_CODE (XEXP (XEXP (operands[1], 0), 1)) == HIGH)
 	       && ((GET_CODE (operands[1]) != CONST_INT
 		    && ! easy_fp_constant (operands[1], mode))
 		   || (GET_CODE (operands[1]) == CONST_INT
@@ -7204,9 +7187,7 @@  rs6000_emit_move (rtx dest, rtx source, 
 			   > (TARGET_CMODEL != CMODEL_SMALL ? 3 : 2)))
 		   || (GET_CODE (operands[0]) == REG
 		       && FP_REGNO_P (REGNO (operands[0]))))
-	       && ! legitimate_constant_pool_address_p (operands[1], mode,
-							false)
-	       && ! toc_relative_expr_p (operands[1])
+	       && !toc_relative_expr_p (operands[1], false)
 	       && (TARGET_CMODEL == CMODEL_SMALL
 		   || can_create_pseudo_p ()
 		   || (REG_P (operands[0])
@@ -15310,7 +15330,7 @@  print_operand (FILE *file, rtx x, int co
 
     case 'D':
       /* Like 'J' but get to the GT bit only.  */
-      gcc_assert (GET_CODE (x) == REG);
+      gcc_assert (REG_P (x));
 
       /* Bit 1 is GT bit.  */
       i = 4 * (REGNO (x) - CR0_REGNO) + 1;
@@ -15430,9 +15450,9 @@  print_operand (FILE *file, rtx x, int co
     case 'L':
       /* Write second word of DImode or DFmode reference.  Works on register
 	 or non-indexed memory only.  */
-      if (GET_CODE (x) == REG)
+      if (REG_P (x))
 	fputs (reg_names[REGNO (x) + 1], file);
-      else if (GET_CODE (x) == MEM)
+      else if (MEM_P (x))
 	{
 	  /* Handle possible auto-increment.  Since it is pre-increment and
 	     we have already done it, we can just use an offset of word.  */
@@ -15601,7 +15622,7 @@  print_operand (FILE *file, rtx x, int co
 
     case 't':
       /* Like 'J' but get to the OVERFLOW/UNORDERED bit.  */
-      gcc_assert (GET_CODE (x) == REG && GET_MODE (x) == CCmode);
+      gcc_assert (REG_P (x) && GET_MODE (x) == CCmode);
 
       /* Bit 3 is OV bit.  */
       i = 4 * (REGNO (x) - CR0_REGNO) + 3;
@@ -15641,7 +15662,7 @@  print_operand (FILE *file, rtx x, int co
 
     case 'U':
       /* Print `u' if this has an auto-increment or auto-decrement.  */
-      if (GET_CODE (x) == MEM
+      if (MEM_P (x)
 	  && (GET_CODE (XEXP (x, 0)) == PRE_INC
 	      || GET_CODE (XEXP (x, 0)) == PRE_DEC
 	      || GET_CODE (XEXP (x, 0)) == PRE_MODIFY))
@@ -15750,7 +15771,7 @@  print_operand (FILE *file, rtx x, int co
       return;
 
     case 'X':
-      if (GET_CODE (x) == MEM
+      if (MEM_P (x)
 	  && (legitimate_indexed_address_p (XEXP (x, 0), 0)
 	      || (GET_CODE (XEXP (x, 0)) == PRE_MODIFY
 		  && legitimate_indexed_address_p (XEXP (XEXP (x, 0), 1), 0))))
@@ -15759,9 +15780,9 @@  print_operand (FILE *file, rtx x, int co
 
     case 'Y':
       /* Like 'L', for third word of TImode  */
-      if (GET_CODE (x) == REG)
+      if (REG_P (x))
 	fputs (reg_names[REGNO (x) + 2], file);
-      else if (GET_CODE (x) == MEM)
+      else if (MEM_P (x))
 	{
 	  if (GET_CODE (XEXP (x, 0)) == PRE_INC
 	      || GET_CODE (XEXP (x, 0)) == PRE_DEC)
@@ -15809,9 +15831,9 @@  print_operand (FILE *file, rtx x, int co
 
     case 'Z':
       /* Like 'L', for last word of TImode.  */
-      if (GET_CODE (x) == REG)
+      if (REG_P (x))
 	fputs (reg_names[REGNO (x) + 3], file);
-      else if (GET_CODE (x) == MEM)
+      else if (MEM_P (x))
 	{
 	  if (GET_CODE (XEXP (x, 0)) == PRE_INC
 	      || GET_CODE (XEXP (x, 0)) == PRE_DEC)
@@ -15831,7 +15854,7 @@  print_operand (FILE *file, rtx x, int co
       {
 	rtx tmp;
 
-	gcc_assert (GET_CODE (x) == MEM);
+	gcc_assert (MEM_P (x));
 
 	tmp = XEXP (x, 0);
 
@@ -15842,7 +15865,7 @@  print_operand (FILE *file, rtx x, int co
 		|| GET_MODE (x) == TImode))
 	  {
 	    /* Handle [reg].  */
-	    if (GET_CODE (tmp) == REG)
+	    if (REG_P (tmp))
 	      {
 		fprintf (file, "0(%s)", reg_names[REGNO (tmp)]);
 		break;
@@ -15853,7 +15876,7 @@  print_operand (FILE *file, rtx x, int co
 	      {
 		int x;
 
-		gcc_assert (GET_CODE (XEXP (tmp, 0)) == REG);
+		gcc_assert (REG_P (XEXP (tmp, 0)));
 
 		x = INTVAL (XEXP (tmp, 1));
 		fprintf (file, "%d(%s)", x, reg_names[REGNO (XEXP (tmp, 0))]);
@@ -15870,7 +15893,7 @@  print_operand (FILE *file, rtx x, int co
 	else if (VECTOR_MEM_VSX_P (GET_MODE (x))
 		 && GET_CODE (tmp) == PRE_MODIFY)
 	  tmp = XEXP (tmp, 1);
-	if (GET_CODE (tmp) == REG)
+	if (REG_P (tmp))
 	  fprintf (file, "0,%s", reg_names[REGNO (tmp)]);
 	else
 	  {
@@ -15893,9 +15916,9 @@  print_operand (FILE *file, rtx x, int co
       }
 
     case 0:
-      if (GET_CODE (x) == REG)
+      if (REG_P (x))
 	fprintf (file, "%s", reg_names[REGNO (x)]);
-      else if (GET_CODE (x) == MEM)
+      else if (MEM_P (x))
 	{
 	  /* We need to handle PRE_INC and PRE_DEC here, since we need to
 	     know the width from the mode.  */
@@ -15909,14 +15932,14 @@  print_operand (FILE *file, rtx x, int co
 	}
       else
 	{
-	  if (toc_relative_expr_p (x))
+	  if (toc_relative_expr_p (x, false))
 	    /* This hack along with a corresponding hack in
 	       rs6000_output_addr_const_extra arranges to output addends
 	       where the assembler expects to find them.  eg.
-	       (const (plus (unspec [symbol_ref ("x") tocrel]) 4))
+	       (plus (unspec [(symbol_ref ("x")) (reg 2)] tocrel) 4)
 	       without this hack would be output as "x@toc+4".  We
 	       want "x+4@toc".  */
-	    output_addr_const (file, tocrel_base);
+	    output_addr_const (file, CONST_CAST_RTX (tocrel_base));
 	  else
 	    output_addr_const (file, x);
 	}
@@ -15939,7 +15968,7 @@  print_operand (FILE *file, rtx x, int co
 void
 print_operand_address (FILE *file, rtx x)
 {
-  if (GET_CODE (x) == REG)
+  if (REG_P (x))
     fprintf (file, "0(%s)", reg_names[ REGNO (x) ]);
   else if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == CONST
 	   || GET_CODE (x) == LABEL_REF)
@@ -15951,9 +15980,9 @@  print_operand_address (FILE *file, rtx x
       else
 	gcc_assert (!TARGET_TOC);
     }
-  else if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == REG)
+  else if (GET_CODE (x) == PLUS && REG_P (XEXP (x, 0))
+	   && REG_P (XEXP (x, 1)))
     {
-      gcc_assert (REG_P (XEXP (x, 0)));
       if (REGNO (XEXP (x, 0)) == 0)
 	fprintf (file, "%s,%s", reg_names[ REGNO (XEXP (x, 1)) ],
 		 reg_names[ REGNO (XEXP (x, 0)) ]);
@@ -15961,11 +15990,12 @@  print_operand_address (FILE *file, rtx x
 	fprintf (file, "%s,%s", reg_names[ REGNO (XEXP (x, 0)) ],
 		 reg_names[ REGNO (XEXP (x, 1)) ]);
     }
-  else if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT)
+  else if (GET_CODE (x) == PLUS && REG_P (XEXP (x, 0))
+	   && GET_CODE (XEXP (x, 1)) == CONST_INT)
     fprintf (file, HOST_WIDE_INT_PRINT_DEC "(%s)",
 	     INTVAL (XEXP (x, 1)), reg_names[ REGNO (XEXP (x, 0)) ]);
 #if TARGET_MACHO
-  else if (GET_CODE (x) == LO_SUM && GET_CODE (XEXP (x, 0)) == REG
+  else if (GET_CODE (x) == LO_SUM && REG_P (XEXP (x, 0))
 	   && CONSTANT_P (XEXP (x, 1)))
     {
       fprintf (file, "lo16(");
@@ -15973,29 +16003,29 @@  print_operand_address (FILE *file, rtx x
       fprintf (file, ")(%s)", reg_names[ REGNO (XEXP (x, 0)) ]);
     }
 #endif
-  else if (legitimate_constant_pool_address_p (x, QImode, true))
+#if TARGET_ELF
+  else if (GET_CODE (x) == LO_SUM && REG_P (XEXP (x, 0))
+	   && CONSTANT_P (XEXP (x, 1)))
+    {
+      output_addr_const (file, XEXP (x, 1));
+      fprintf (file, "@l(%s)", reg_names[ REGNO (XEXP (x, 0)) ]);
+    }
+#endif
+  else if (toc_relative_expr_p (x, false))
     {
       /* This hack along with a corresponding hack in
 	 rs6000_output_addr_const_extra arranges to output addends
 	 where the assembler expects to find them.  eg.
 	 (lo_sum (reg 9)
-	 .       (const (plus (unspec [symbol_ref ("x") tocrel]) 8)))
+	 .       (plus (unspec [(symbol_ref ("x")) (reg 2)] tocrel) 8))
 	 without this hack would be output as "x@toc+8@l(9)".  We
 	 want "x+8@toc@l(9)".  */
-      output_addr_const (file, tocrel_base);
+      output_addr_const (file, CONST_CAST_RTX (tocrel_base));
       if (GET_CODE (x) == LO_SUM)
-	fprintf (file, "@l(%s)", reg_names[ REGNO (XEXP (x, 0)) ]);
+	fprintf (file, "@l(%s)", reg_names[REGNO (XEXP (x, 0))]);
       else
-	fprintf (file, "(%s)", reg_names[REGNO (XEXP (x, 0))]);
-    }
-#if TARGET_ELF
-  else if (GET_CODE (x) == LO_SUM && GET_CODE (XEXP (x, 0)) == REG
-	   && CONSTANT_P (XEXP (x, 1)))
-    {
-      output_addr_const (file, XEXP (x, 1));
-      fprintf (file, "@l(%s)", reg_names[ REGNO (XEXP (x, 0)) ]);
+	fprintf (file, "(%s)", reg_names[REGNO (XVECEXP (tocrel_base, 0, 1))]);
     }
-#endif
   else
     gcc_unreachable ();
 }
@@ -16009,13 +16039,15 @@  rs6000_output_addr_const_extra (FILE *fi
     switch (XINT (x, 1))
       {
       case UNSPEC_TOCREL:
-	gcc_assert (GET_CODE (XVECEXP (x, 0, 0)) == SYMBOL_REF);
+	gcc_checking_assert (GET_CODE (XVECEXP (x, 0, 0)) == SYMBOL_REF
+			     && REG_P (XVECEXP (x, 0, 1))
+			     && REGNO (XVECEXP (x, 0, 1)) == TOC_REGISTER);
 	output_addr_const (file, XVECEXP (x, 0, 0));
 	if (x == tocrel_base && tocrel_offset != const0_rtx)
 	  {
 	    if (INTVAL (tocrel_offset) >= 0)
 	      fprintf (file, "+");
-	    output_addr_const (file, tocrel_offset);
+	    output_addr_const (file, CONST_CAST_RTX (tocrel_offset));
 	  }
 	if (!TARGET_AIX || (TARGET_ELF && TARGET_MINIMAL_TOC))
 	  {
@@ -19033,7 +19065,7 @@  uses_TOC (void)
 rtx
 create_TOC_reference (rtx symbol, rtx largetoc_reg)
 {
-  rtx tocrel, tocreg;
+  rtx tocrel, tocreg, hi;
 
   if (TARGET_DEBUG_ADDR)
     {
@@ -19051,24 +19083,18 @@  create_TOC_reference (rtx symbol, rtx la
   if (!can_create_pseudo_p ())
     df_set_regs_ever_live (TOC_REGISTER, true);
 
-  tocrel = gen_rtx_CONST (Pmode,
-			  gen_rtx_UNSPEC (Pmode, gen_rtvec (1, symbol),
-					  UNSPEC_TOCREL));
   tocreg = gen_rtx_REG (Pmode, TOC_REGISTER);
-  if (TARGET_CMODEL != CMODEL_SMALL)
+  tocrel = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, symbol, tocreg), UNSPEC_TOCREL);
+  if (TARGET_CMODEL == CMODEL_SMALL)
+    return tocrel;
+
+  hi = gen_rtx_HIGH (Pmode, copy_rtx (tocrel));
+  if (largetoc_reg != NULL)
     {
-      rtx hi = gen_rtx_CONST (Pmode,
-			      gen_rtx_PLUS (Pmode, tocreg, 
-					    gen_rtx_HIGH (Pmode, tocrel)));
-      if (largetoc_reg != NULL)
-	{
-	  emit_move_insn (largetoc_reg, hi);
-	  hi = largetoc_reg;
-	}
-      return gen_rtx_LO_SUM (Pmode, hi, copy_rtx (tocrel));
+      emit_move_insn (largetoc_reg, hi);
+      hi = largetoc_reg;
     }
-  else
-    return gen_rtx_PLUS (Pmode, tocreg, tocrel);
+  return gen_rtx_LO_SUM (Pmode, hi, tocrel);
 }
 
 /* Issue assembly directives that create a reference to the given DWARF
Index: gcc/config/rs6000/rs6000.md
===================================================================
--- gcc/config/rs6000/rs6000.md	(revision 178035)
+++ gcc/config/rs6000/rs6000.md	(working copy)
@@ -11543,10 +11544,8 @@  (define_insn_and_split "*tls_gd<TLSmode:
   "addi %0,%1,%2@got@tlsgd"
   "&& TARGET_CMODEL != CMODEL_SMALL"
   [(set (match_dup 3)
-	(const:TLSmode
-	  (plus:TLSmode (match_dup 1)
-	    (high:TLSmode
-	      (unspec:TLSmode [(match_dup 2)] UNSPEC_TLSGD)))))
+  	(high:TLSmode
+	    (unspec:TLSmode [(match_dup 1) (match_dup 2)] UNSPEC_TLSGD)))
    (set (match_dup 0)
    	(lo_sum:TLSmode (match_dup 3)
 	    (unspec:TLSmode [(match_dup 2)] UNSPEC_TLSGD)))]
@@ -11561,11 +11560,10 @@  (define_insn_and_split "*tls_gd<TLSmode:
 
 (define_insn "*tls_gd_high<TLSmode:tls_abi_suffix>"
   [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b")
-     (const:TLSmode
-       (plus:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b")
-	 (high:TLSmode
-	   (unspec:TLSmode [(match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")]
-			   UNSPEC_TLSGD)))))]
+     (high:TLSmode
+       (unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b")
+			(match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")]
+		       UNSPEC_TLSGD)))]
   "HAVE_AS_TLS && TARGET_TLS_MARKERS && TARGET_CMODEL != CMODEL_SMALL"
   "addis %0,%1,%2@got@tlsgd@ha"
   [(set_attr "length" "4")])
@@ -11680,10 +11678,8 @@  (define_insn_and_split "*tls_ld<TLSmode:
   "addi %0,%1,%&@got@tlsld"
   "&& TARGET_CMODEL != CMODEL_SMALL"
   [(set (match_dup 2)
-	(const:TLSmode
-	  (plus:TLSmode (match_dup 1)
-	    (high:TLSmode
-	      (unspec:TLSmode [(const_int 0)] UNSPEC_TLSLD)))))
+  	(high:TLSmode
+	    (unspec:TLSmode [(const_int 0) (match_dup 1)] UNSPEC_TLSLD)))
    (set (match_dup 0)
    	(lo_sum:TLSmode (match_dup 2)
 	    (unspec:TLSmode [(const_int 0)] UNSPEC_TLSLD)))]
@@ -11698,10 +11694,10 @@  (define_insn_and_split "*tls_ld<TLSmode:
 
 (define_insn "*tls_ld_high<TLSmode:tls_abi_suffix>"
   [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b")
-     (const:TLSmode
-       (plus:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b")
-	 (high:TLSmode
-	   (unspec:TLSmode [(const_int 0)] UNSPEC_TLSLD)))))]
+     (high:TLSmode
+       (unspec:TLSmode [(const_int 0)
+			(match_operand:TLSmode 1 "gpc_reg_operand" "b")]
+		       UNSPEC_TLSLD)))]
   "HAVE_AS_TLS && TARGET_TLS_MARKERS && TARGET_CMODEL != CMODEL_SMALL"
   "addis %0,%1,%&@got@tlsld@ha"
   [(set_attr "length" "4")])
@@ -11777,10 +11773,8 @@  (define_insn_and_split "tls_got_dtprel_<
   "l<TLSmode:tls_insn_suffix> %0,%2@got@dtprel(%1)"
   "&& TARGET_CMODEL != CMODEL_SMALL"
   [(set (match_dup 3)
-	(const:TLSmode
-	  (plus:TLSmode (match_dup 1)
-	    (high:TLSmode
-	      (unspec:TLSmode [(match_dup 2)] UNSPEC_TLSGOTDTPREL)))))
+	(high:TLSmode
+	    (unspec:TLSmode [(match_dup 1) (match_dup 2)] UNSPEC_TLSGOTDTPREL)))
    (set (match_dup 0)
 	(lo_sum:TLSmode (match_dup 3)
 	    (unspec:TLSmode [(match_dup 2)] UNSPEC_TLSGOTDTPREL)))]
@@ -11795,11 +11789,10 @@  (define_insn_and_split "tls_got_dtprel_<
 
 (define_insn "*tls_got_dtprel_high<TLSmode:tls_abi_suffix>"
   [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b")
-     (const:TLSmode
-       (plus:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b")
-	 (high:TLSmode
-	   (unspec:TLSmode [(match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")]
-			   UNSPEC_TLSGOTDTPREL)))))]
+     (high:TLSmode
+       (unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b")
+			(match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")]
+		       UNSPEC_TLSGOTDTPREL)))]
   "HAVE_AS_TLS && TARGET_CMODEL != CMODEL_SMALL"
   "addis %0,%1,%2@got@dtprel@ha"
   [(set_attr "length" "4")])
@@ -11849,10 +11842,8 @@  (define_insn_and_split "tls_got_tprel_<T
   "l<TLSmode:tls_insn_suffix> %0,%2@got@tprel(%1)"
   "&& TARGET_CMODEL != CMODEL_SMALL"
   [(set (match_dup 3)
-	(const:TLSmode
-	  (plus:TLSmode (match_dup 1)
-	    (high:TLSmode
-	      (unspec:TLSmode [(match_dup 2)] UNSPEC_TLSGOTTPREL)))))
+	(high:TLSmode
+	    (unspec:TLSmode [(match_dup 1) (match_dup 2)] UNSPEC_TLSGOTTPREL)))
    (set (match_dup 0)
 	(lo_sum:TLSmode (match_dup 3)
 	    (unspec:TLSmode [(match_dup 2)] UNSPEC_TLSGOTTPREL)))]
@@ -11867,11 +11858,10 @@  (define_insn_and_split "tls_got_tprel_<T
 
 (define_insn "*tls_got_tprel_high<TLSmode:tls_abi_suffix>"
   [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b")
-     (const:TLSmode
-       (plus:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b")
-	 (high:TLSmode
-	   (unspec:TLSmode [(match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")]
-			   UNSPEC_TLSGOTTPREL)))))]
+     (high:TLSmode
+       (unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b")
+			(match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")]
+		       UNSPEC_TLSGOTTPREL)))]
   "HAVE_AS_TLS && TARGET_CMODEL != CMODEL_SMALL"
   "addis %0,%1,%2@got@tprel@ha"
   [(set_attr "length" "4")])
@@ -12163,6 +12153,25 @@  (define_expand "builtin_setjmp_receiver"
   DONE;
 }")
 
+;; Largetoc support
+(define_insn "largetoc_high"
+  [(set (match_operand:DI 0 "gpc_reg_operand" "=b*r")
+        (high:DI
+	  (unspec [(match_operand:DI 1 "" "")
+		   (match_operand:DI 2 "gpc_reg_operand" "b")]
+		  UNSPEC_TOCREL)))]
+   "TARGET_ELF && TARGET_CMODEL != CMODEL_SMALL"
+   "{cau|addis} %0,%2,%1@toc@ha")
+
+(define_insn "largetoc_low"
+  [(set (match_operand:DI 0 "gpc_reg_operand" "=r,r")
+        (lo_sum:DI (match_operand:DI 1 "gpc_reg_operand" "b,!*r")
+	           (match_operand:DI 2 "" "")))]
+   "TARGET_ELF && TARGET_CMODEL != CMODEL_SMALL"
+   "@
+    {cal %0,%2@l(%1)|addi %0,%1,%2@l}
+    {ai|addic} %0,%1,%2@l")
+
 ;; Elf specific ways of loading addresses for non-PIC code.
 ;; The output of this could be r0, but we make a very strong
 ;; preference for a base register because it will usually
@@ -12181,22 +12190,6 @@  (define_insn "elf_low"
    "@
     {cal|la} %0,%2@l(%1)
     {ai|addic} %0,%1,%K2")
-
-;; Largetoc support
-(define_insn "largetoc_high"
-  [(set (match_operand:DI 0 "gpc_reg_operand" "=b")
-	(const:DI
-	  (plus:DI (match_operand:DI 1 "gpc_reg_operand" "b")
-		   (high:DI (match_operand:DI 2 "" "")))))]
-   "TARGET_ELF && TARGET_CMODEL != CMODEL_SMALL"
-   "{cau|addis} %0,%1,%2@ha")
-
-(define_insn "largetoc_low"
-  [(set (match_operand:DI 0 "gpc_reg_operand" "=r")
-        (lo_sum:DI (match_operand:DI 1 "gpc_reg_operand" "b")
-	           (match_operand:DI 2 "" "")))]
-   "TARGET_ELF && TARGET_CMODEL != CMODEL_SMALL"
-   "{cal %0,%2@l(%1)|addi %0,%1,%2@l}")
 
 ;; Call and call_value insns
 (define_expand "call"