predicates.md (input_operand): Don't match constant pool addresses.
authorAlan Modra <amodra@gmail.com>
Sun, 20 May 2012 23:31:35 +0000 (09:01 +0930)
committerAlan Modra <amodra@gcc.gnu.org>
Sun, 20 May 2012 23:31:35 +0000 (09:01 +0930)
* config/rs6000/predicates.md (input_operand): Don't match
constant pool addresses.  Remove label_ref, high and plus from
match_code list.  Remove redundant CONSTANT_P test.
(splat_input_operand): Similarly update match_code list.
(small_toc_ref): New predicate.
* config/rs6000/rs6000-protos.h (toc_relative_expr_p): Update prototype.
* config/rs6000/rs6000.c (tocrel_base, tocrel_offset): Make 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.
(rs6000_legitimize_address): Update cerate_TOC_reference call.
(rs6000_delegitimize_address): Handle new rtl for toc refs.
(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.  Update
create_TOC_reference calls.
(rs6000_legitimize_reload_address): Formatting.  Handle splitting
of medium/large model toc addresses.  Use use_toc_relative_ref.
(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.  Before reload,
always use the small model formulation.
* 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.
(largetoc_high_plus): New insn.
(movsi_internal1, movsi_internal1_single, movsf_softfloat,
movdi_mfpgpr, movdi_internal64): Don't handle 'R' constraint here..
(tocref): ..instead do so here, new insn and split.

From-SVN: r187699

gcc/ChangeLog
gcc/config/rs6000/predicates.md
gcc/config/rs6000/rs6000-protos.h
gcc/config/rs6000/rs6000.c
gcc/config/rs6000/rs6000.md

index f7d72d57e850c7b7f0830f61155fabc6d8886acd..55cf1fd30bdefa1d7e2f85607b32a3a4c645c963 100644 (file)
@@ -1,3 +1,42 @@
+2012-05-21  Alan Modra  <amodra@gmail.com>
+
+       * config/rs6000/predicates.md (input_operand): Don't match
+       constant pool addresses.  Remove label_ref, high and plus from
+       match_code list.  Remove redundant CONSTANT_P test.
+       (splat_input_operand): Similarly update match_code list.
+       (small_toc_ref): New predicate.
+       * config/rs6000/rs6000-protos.h (toc_relative_expr_p): Update prototype.
+       * config/rs6000/rs6000.c (tocrel_base, tocrel_offset): Make 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.
+       (rs6000_legitimize_address): Update cerate_TOC_reference call.
+       (rs6000_delegitimize_address): Handle new rtl for toc refs.
+       (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.  Update
+       create_TOC_reference calls.
+       (rs6000_legitimize_reload_address): Formatting.  Handle splitting
+       of medium/large model toc addresses.  Use use_toc_relative_ref.
+       (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.  Before reload,
+       always use the small model formulation.
+       * 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.
+       (largetoc_high_plus): New insn.
+       (movsi_internal1, movsi_internal1_single, movsf_softfloat,
+       movdi_mfpgpr, movdi_internal64): Don't handle 'R' constraint here..
+       (tocref): ..instead do so here, new insn and split.
+
 2012-05-20  H.J. Lu  <hongjiu.lu@intel.com>
 
        * config/i386/driver-i386.c (host_detect_local_cpu): Support
index 232773d5d142a64ed5512593da15c0422d484ffe..70a4d2ca7088bb72816a3e2229b02f309572f0a2 100644 (file)
@@ -1,5 +1,5 @@
 ;; Predicate definitions for POWER and PowerPC.
-;; Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011
+;; Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
 ;; Free Software Foundation, Inc.
 ;;
 ;; This file is part of GCC.
 
 ;; 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")
+  (match_code "symbol_ref,const,reg,subreg,mem,
+              const_double,const_vector,const_int")
 {
   /* Memory is always valid.  */
   if (memory_operand (op, mode))
 
   /* For floating-point, easy constants are valid.  */
   if (SCALAR_FLOAT_MODE_P (mode)
-      && CONSTANT_P (op)
       && easy_fp_constant (op, mode))
     return 1;
 
   if (register_operand (op, mode))
     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))
-    return 1;
-
   /* V.4 allows SYMBOL_REFs and CONSTs that are in the small data region
      to be valid.  */
   if (DEFAULT_ABI == ABI_V4
 
 ;; Return 1 if this operand is a valid input for a vsx_splat insn.
 (define_predicate "splat_input_operand"
-  (match_code "label_ref,symbol_ref,const,high,reg,subreg,mem,
-              const_double,const_vector,const_int,plus")
+  (match_code "symbol_ref,const,reg,subreg,mem,
+              const_double,const_vector,const_int")
 {
   if (MEM_P (op))
     {
          && GET_MODE (XEXP (XVECEXP (op, 0, 0), 0)) == BLKmode
          && XEXP (XVECEXP (op, 0, 0), 1) == const0_rtx);
 })
+
+;; Match a small code model toc reference (or medium and large
+;; model toc references before reload).
+(define_predicate "small_toc_ref"
+  (match_code "unspec,plus")
+{
+  if (GET_CODE (op) == PLUS && CONST_INT_P (XEXP (op, 1)))
+    op = XEXP (op, 0);
+
+  return GET_CODE (op) == UNSPEC && XINT (op, 1) == UNSPEC_TOCREL;
+})
index 23327f853fde53a0812d58b49f34dd1df1187d30..119482fbafcfadea266b14c19708e5b282df5a06 100644 (file)
@@ -1,6 +1,6 @@
 /* Definitions of target machine for GNU compiler, for IBM RS/6000.
    Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
-   2010, 2011
+   2010, 2011, 2012
    Free Software Foundation, Inc.
    Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu)
 
@@ -38,7 +38,7 @@ extern bool macho_lo_sum_memory_operand (rtx, enum machine_mode);
 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 0896af3bccf5a4ac25d28e6d2cb562805b417cfd..dbd1b4a37ca9baac55dd812671b9efaf40d46b69 100644 (file)
@@ -5307,15 +5307,37 @@ 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;
+
+/* Return true if OP is a toc pointer relative address (the output
+   of create_TOC_reference).  If STRICT, do not match high part or
+   non-split -mcmodel=large/medium toc pointer relative addresses.  */
 
 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
+         && REG_P (XEXP (op, 0))
+         && INT_REG_OK_FOR_BASE_P (XEXP (op, 0), strict))
+       op = XEXP (op, 1);
+      else if (strict)
+       return false;
+    }
+
+  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);
 }
@@ -5327,14 +5349,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
@@ -5714,10 +5729,7 @@ rs6000_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
           && GET_CODE (x) == SYMBOL_REF
           && constant_pool_expr_p (x)
           && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (x), Pmode))
-    {
-      rtx reg = TARGET_CMODEL != CMODEL_SMALL ? gen_reg_rtx (Pmode) : NULL_RTX;
-      return create_TOC_reference (x, reg);
-    }
+    return create_TOC_reference (x, NULL_RTX);
   else
     return x;
 }
@@ -5800,49 +5812,55 @@ rs6000_output_dwarf_dtprel (FILE *file, int size, rtx x)
 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)))
+  if (GET_CODE (y) == UNSPEC
+      && XINT (y, 1) == UNSPEC_TOCREL)
+    {
+#ifdef ENABLE_CHECKING
+      if (REG_P (XVECEXP (y, 0, 1))
+         && REGNO (XVECEXP (y, 0, 1)) == TOC_REGISTER)
        {
-         offset = XEXP (y, 1);
-         y = XEXP (y, 0);
+         /* All good.  */
        }
-      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);
+      else if (GET_CODE (XVECEXP (y, 0, 1)) == DEBUG_EXPR)
+       {
+         /* Weirdness alert.  df_note_compute can replace r2 with a
+            debug_expr when this unspec is in a debug_insn.
+            Seen in gcc.dg/pr51957-1.c  */
        }
+      else
+       {
+         debug_rtx (orig_x);
+         abort ();
+       }
+#endif
+      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
@@ -6104,9 +6122,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);
@@ -6121,6 +6138,21 @@ rs6000_tls_symbol_ref_1 (rtx *x, void *data ATTRIBUTE_UNUSED)
   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,
@@ -6158,7 +6190,7 @@ rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
     {
       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;
     }
@@ -6169,7 +6201,7 @@ rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
     {
       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;
     }
@@ -6187,24 +6219,18 @@ rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
         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);
+                  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)))
+      && reg_offset_p
+      && small_toc_ref (x, VOIDmode))
     {
+      rtx hi = gen_rtx_HIGH (Pmode, copy_rtx (x));
+      x = gen_rtx_LO_SUM (Pmode, hi, x);
       push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
                   BASE_REG_CLASS, Pmode, VOIDmode, 0, 0,
                   opnum, (enum reload_type) type);
@@ -6267,7 +6293,7 @@ rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
 
       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;
     }
@@ -6308,7 +6334,7 @@ rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
 
       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;
     }
@@ -6335,8 +6361,7 @@ rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
   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)
@@ -6571,9 +6596,13 @@ rs6000_debug_mode_dependent_address (const_rtx addr)
 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))
       {
@@ -7225,33 +7254,13 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
       /* 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])))
-       {
-         rtx reg = NULL_RTX;
-         if (TARGET_CMODEL != CMODEL_SMALL)
-           {
-             if (can_create_pseudo_p ())
-               reg = gen_reg_rtx (Pmode);
-             else
-               reg = operands[0];
-           }
-         operands[1] = create_TOC_reference (operands[1], reg);
-       }
+      if (TARGET_TOC
+         && GET_CODE (operands[1]) == SYMBOL_REF
+         && use_toc_relative_ref (operands[1]))
+       operands[1] = create_TOC_reference (operands[1], operands[0]);
       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
@@ -7259,9 +7268,7 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
                           > (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])
@@ -7311,16 +7318,8 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
                        get_pool_constant (XEXP (operands[1], 0)),
                        get_pool_mode (XEXP (operands[1], 0))))
            {
-             rtx tocref;
-             rtx reg = NULL_RTX;
-             if (TARGET_CMODEL != CMODEL_SMALL)
-               {
-                 if (can_create_pseudo_p ())
-                   reg = gen_reg_rtx (Pmode);
-                 else
-                   reg = operands[0];
-               }
-             tocref = create_TOC_reference (XEXP (operands[1], 0), reg);
+             rtx tocref = create_TOC_reference (XEXP (operands[1], 0),
+                                                operands[0]);
              operands[1] = gen_const_mem (mode, tocref);
              set_mem_alias_set (operands[1], get_TOC_alias_set ());
            }
@@ -14730,7 +14729,7 @@ print_operand (FILE *file, rtx x, int code)
 
     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;
@@ -14850,9 +14849,9 @@ print_operand (FILE *file, rtx x, int code)
     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.  */
@@ -15021,7 +15020,7 @@ print_operand (FILE *file, rtx x, int code)
 
     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;
@@ -15061,7 +15060,7 @@ print_operand (FILE *file, rtx x, int code)
 
     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))
@@ -15153,7 +15152,7 @@ print_operand (FILE *file, rtx x, int code)
       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))))
@@ -15162,9 +15161,9 @@ print_operand (FILE *file, rtx x, int code)
 
     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)
@@ -15212,9 +15211,9 @@ print_operand (FILE *file, rtx x, int code)
 
     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)
@@ -15234,7 +15233,7 @@ print_operand (FILE *file, rtx x, int code)
       {
        rtx tmp;
 
-       gcc_assert (GET_CODE (x) == MEM);
+       gcc_assert (MEM_P (x));
 
        tmp = XEXP (x, 0);
 
@@ -15245,7 +15244,7 @@ print_operand (FILE *file, rtx x, int code)
                || GET_MODE (x) == TImode))
          {
            /* Handle [reg].  */
-           if (GET_CODE (tmp) == REG)
+           if (REG_P (tmp))
              {
                fprintf (file, "0(%s)", reg_names[REGNO (tmp)]);
                break;
@@ -15256,7 +15255,7 @@ print_operand (FILE *file, rtx x, int code)
              {
                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))]);
@@ -15273,7 +15272,7 @@ print_operand (FILE *file, rtx x, int code)
        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
          {
@@ -15296,9 +15295,9 @@ print_operand (FILE *file, rtx x, int code)
       }
 
     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.  */
@@ -15315,14 +15314,14 @@ print_operand (FILE *file, rtx x, int code)
        }
       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);
        }
@@ -15342,7 +15341,7 @@ print_operand (FILE *file, rtx x, int code)
 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)
@@ -15354,9 +15353,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)) ]);
@@ -15364,11 +15363,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(");
@@ -15376,29 +15376,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))]);
+       fprintf (file, "(%s)", reg_names[REGNO (XVECEXP (tocrel_base, 0, 1))]);
     }
-#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)) ]);
-    }
-#endif
   else
     gcc_unreachable ();
 }
@@ -15412,13 +15412,15 @@ rs6000_output_addr_const_extra (FILE *file, rtx x)
     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))
          {
@@ -18501,7 +18503,7 @@ uses_TOC (void)
 rtx
 create_TOC_reference (rtx symbol, rtx largetoc_reg)
 {
-  rtx tocrel, tocreg;
+  rtx tocrel, tocreg, hi;
 
   if (TARGET_DEBUG_ADDR)
     {
@@ -18519,24 +18521,18 @@ create_TOC_reference (rtx symbol, rtx largetoc_reg)
   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 || can_create_pseudo_p ())
+    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 85c8c1acbe2edc776aec97bc39c63a0ea13ac7e7..e852c3e5306d8eb712b4ca0b24a2b6ebea33dc88 100644 (file)
@@ -1,6 +1,7 @@
 ;; Machine description for IBM RISC System 6000 (POWER) for GNU C compiler
 ;; Copyright (C) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-;; 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
+;; 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+;; 2011, 2012
 ;; Free Software Foundation, Inc.
 ;; Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu)
 
    (set_attr "length" "4")])
 
 (define_insn "*movsi_internal1"
-  [(set (match_operand:SI 0 "rs6000_nonimmediate_operand" "=r,r,r,m,r,r,r,r,r,*q,*c*l,*h,*h")
-       (match_operand:SI 1 "input_operand" "r,U,m,r,I,L,n,R,*h,r,r,r,0"))]
+  [(set (match_operand:SI 0 "rs6000_nonimmediate_operand" "=r,r,r,m,r,r,r,r,*q,*c*l,*h,*h")
+       (match_operand:SI 1 "input_operand" "r,U,m,r,I,L,n,*h,r,r,r,0"))]
   "!TARGET_SINGLE_FPU &&
    (gpc_reg_operand (operands[0], SImode) || gpc_reg_operand (operands[1], SImode))"
   "@
    {lil|li} %0,%1
    {liu|lis} %0,%v1
    #
-   {cal|la} %0,%a1
    mf%1 %0
    mt%0 %1
    mt%0 %1
    mt%0 %1
    {cror 0,0,0|nop}"
-  [(set_attr "type" "*,*,load,store,*,*,*,*,mfjmpr,*,mtjmpr,*,*")
-   (set_attr "length" "4,4,4,4,4,4,8,4,4,4,4,4,4")])
+  [(set_attr "type" "*,*,load,store,*,*,*,mfjmpr,*,mtjmpr,*,*")
+   (set_attr "length" "4,4,4,4,4,4,8,4,4,4,4,4")])
 
 (define_insn "*movsi_internal1_single"
-  [(set (match_operand:SI 0 "rs6000_nonimmediate_operand" "=r,r,r,m,r,r,r,r,r,*q,*c*l,*h,*h,m,*f")
-        (match_operand:SI 1 "input_operand" "r,U,m,r,I,L,n,R,*h,r,r,r,0,f,m"))]
+  [(set (match_operand:SI 0 "rs6000_nonimmediate_operand" "=r,r,r,m,r,r,r,r,*q,*c*l,*h,*h,m,*f")
+        (match_operand:SI 1 "input_operand" "r,U,m,r,I,L,n,*h,r,r,r,0,f,m"))]
   "TARGET_SINGLE_FPU &&
    (gpc_reg_operand (operands[0], SImode) || gpc_reg_operand (operands[1], SImode))"
   "@
    {lil|li} %0,%1
    {liu|lis} %0,%v1
    #
-   {cal|la} %0,%a1
    mf%1 %0
    mt%0 %1
    mt%0 %1
    {cror 0,0,0|nop}
    stfs%U0%X0 %1, %0
    lfs%U1%X1 %0, %1"
-  [(set_attr "type" "*,*,load,store,*,*,*,*,mfjmpr,*,mtjmpr,*,*,*,*")
-   (set_attr "length" "4,4,4,4,4,4,8,4,4,4,4,4,4,4,4")])
+  [(set_attr "type" "*,*,load,store,*,*,*,mfjmpr,*,mtjmpr,*,*,*,*")
+   (set_attr "length" "4,4,4,4,4,4,8,4,4,4,4,4,4,4")])
 
 ;; Split a load of a large constant into the appropriate two-insn
 ;; sequence.
    (set_attr "length" "4,4,4,4,4,4,4,4,4,4,4,8")])
 
 (define_insn "*movsf_softfloat"
-  [(set (match_operand:SF 0 "nonimmediate_operand" "=r,cl,q,r,r,m,r,r,r,r,r,*h")
-       (match_operand:SF 1 "input_operand" "r,r,r,h,m,r,I,L,R,G,Fn,0"))]
+  [(set (match_operand:SF 0 "nonimmediate_operand" "=r,cl,q,r,r,m,r,r,r,r,*h")
+       (match_operand:SF 1 "input_operand" "r,r,r,h,m,r,I,L,G,Fn,0"))]
   "(gpc_reg_operand (operands[0], SFmode)
    || gpc_reg_operand (operands[1], SFmode))
    && (TARGET_SOFT_FLOAT || !TARGET_FPRS)"
    {st%U0%X0|stw%U0%X0} %1,%0
    {lil|li} %0,%1
    {liu|lis} %0,%v1
-   {cal|la} %0,%a1
    #
    #
    {cror 0,0,0|nop}"
-  [(set_attr "type" "*,mtjmpr,*,mfjmpr,load,store,*,*,*,*,*,*")
-   (set_attr "length" "4,4,4,4,4,4,4,4,4,4,8,4")])
+  [(set_attr "type" "*,mtjmpr,*,mfjmpr,load,store,*,*,*,*,*")
+   (set_attr "length" "4,4,4,4,4,4,4,4,4,8,4")])
 
 \f
 (define_expand "movdf"
 { rs6000_split_multireg_move (operands[0], operands[1]); DONE; })
 
 (define_insn "*movdi_mfpgpr"
-  [(set (match_operand:DI 0 "nonimmediate_operand" "=r,r,m,r,r,r,r,*d,*d,m,r,*h,*h,r,*d")
-       (match_operand:DI 1 "input_operand" "r,m,r,I,L,nF,R,d,m,d,*h,r,0,*d,r"))]
+  [(set (match_operand:DI 0 "nonimmediate_operand" "=r,r,m,r,r,r,*d,*d,m,r,*h,*h,r,*d")
+       (match_operand:DI 1 "input_operand" "r,m,r,I,L,nF,d,m,d,*h,r,0,*d,r"))]
   "TARGET_POWERPC64 && TARGET_MFPGPR && TARGET_HARD_FLOAT && TARGET_FPRS
    && (gpc_reg_operand (operands[0], DImode)
        || gpc_reg_operand (operands[1], DImode))"
    li %0,%1
    lis %0,%v1
    #
-   la %0,%a1
    fmr %0,%1
    lfd%U1%X1 %0,%1
    stfd%U0%X0 %1,%0
    {cror 0,0,0|nop}
    mftgpr %0,%1
    mffgpr %0,%1"
-  [(set_attr "type" "*,load,store,*,*,*,*,fp,fpload,fpstore,mfjmpr,mtjmpr,*,mftgpr,mffgpr")
-   (set_attr "length" "4,4,4,4,4,20,4,4,4,4,4,4,4,4,4")])
+  [(set_attr "type" "*,load,store,*,*,*,fp,fpload,fpstore,mfjmpr,mtjmpr,*,mftgpr,mffgpr")
+   (set_attr "length" "4,4,4,4,4,20,4,4,4,4,4,4,4,4")])
 
 (define_insn "*movdi_internal64"
-  [(set (match_operand:DI 0 "nonimmediate_operand" "=r,r,m,r,r,r,r,*d,*d,m,r,*h,*h,?wa")
-       (match_operand:DI 1 "input_operand" "r,m,r,I,L,nF,R,d,m,d,*h,r,0,O"))]
+  [(set (match_operand:DI 0 "nonimmediate_operand" "=r,r,m,r,r,r,*d,*d,m,r,*h,*h,?wa")
+       (match_operand:DI 1 "input_operand" "r,m,r,I,L,nF,d,m,d,*h,r,0,O"))]
   "TARGET_POWERPC64 && (!TARGET_MFPGPR || !TARGET_HARD_FLOAT || !TARGET_FPRS)
    && (gpc_reg_operand (operands[0], DImode)
        || gpc_reg_operand (operands[1], DImode))"
    li %0,%1
    lis %0,%v1
    #
-   la %0,%a1
    fmr %0,%1
    lfd%U1%X1 %0,%1
    stfd%U0%X0 %1,%0
    mt%0 %1
    {cror 0,0,0|nop}
    xxlxor %x0,%x0,%x0"
-  [(set_attr "type" "*,load,store,*,*,*,*,fp,fpload,fpstore,mfjmpr,mtjmpr,*,vecsimple")
-   (set_attr "length" "4,4,4,4,4,20,4,4,4,4,4,4,4,4")])
+  [(set_attr "type" "*,load,store,*,*,*,fp,fpload,fpstore,mfjmpr,mtjmpr,*,vecsimple")
+   (set_attr "length" "4,4,4,4,4,20,4,4,4,4,4,4,4")])
 
 ;; immediate value valid for a single instruction hiding in a const_double
 (define_insn ""
   "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)))]
 
 (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")])
   "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)))]
 
 (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")])
   "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)))]
 
 (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")])
   "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)))]
 
 (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")])
   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_high_plus"
+  [(set (match_operand:DI 0 "gpc_reg_operand" "=b*r")
+        (high:DI
+         (plus:DI
+           (unspec [(match_operand:DI 1 "" "")
+                    (match_operand:DI 2 "gpc_reg_operand" "b")]
+                   UNSPEC_TOCREL)
+           (match_operand 3 "const_int_operand" "n"))))]
+   "TARGET_ELF && TARGET_CMODEL != CMODEL_SMALL"
+   "{cau|addis} %0,%2,%1+%3@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")
+
+(define_insn_and_split "*tocref<mode>"
+  [(set (match_operand:P 0 "gpc_reg_operand" "=b*r")
+       (match_operand:P 1 "small_toc_ref" "R"))]
+   "TARGET_TOC"
+   "{cal|la} %0,%a1"
+   "&& TARGET_ELF && TARGET_CMODEL != CMODEL_SMALL && reload_completed"
+  [(set (match_dup 0) (high:P (match_dup 1)))
+   (set (match_dup 0) (lo_sum:P (match_dup 0) (match_dup 1)))])
+
 ;; 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
    "@
     {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}")
 \f
 ;; Call and call_value insns
 (define_expand "call"