diff mbox

PowerPC64 large toc model

Message ID 20100608125703.GI7312@bubble.grove.modra.org
State New
Headers show

Commit Message

Alan Modra June 8, 2010, 12:57 p.m. UTC
PowerPC64 gcc support for a larger TOC via -mcmodel option.

-mcmodel=small
  uses 16-bit toc offsets, ie. what we had before.

-mcmodel=medium
  uses 32-bit toc offsets and addresses static data, string constants
  etc. relative to the toc pointer.  Limits data to around 2G.

-mcmodel=large
  uses 32-bit toc offsets, no limit on static data.

Default is -mcmodel=medium unless you specify other toc options.  For
example, -mminimal-toc gets you -mcmodel=small.

Bootstrapped and regression tested powerpc64-linux.  Comments follow
associated changelog entry.

	* doc/invoke.texi: Add mcmodel to powerpc options.
	* configure.ac: Add HAVE_LD_LARGE_TOC test.
	* configure: Regenerate.
	* config.in: Regenerate.
	* config/rs6000/linux64.opt (mcmodel): New.
	* config/rs6000/linux64.h (TARGET_USES_LINUX64_OPT): Define.
	(TARGET_CMODEL): Define.
	(SUBSUBTARGET_OVERRIDE_OPTIONS): Check user -mcmodel choice,
	select CMODEL_MEDIUM default.
	* config/rs6000/rs6000.h (enum rs6000_cmodel): New.
	(TARGET_CMODEL): Define default.
	* config/rs6000/rs6000.c (cmodel): New variable.
	(rs6000_explicit_options): Add cmodel field.
	(rs6000_handle_option): Handle -mcmodel.
	(create_TOC_reference): Add bigtoc_reg param.  Generate high,
	lo_sum rtl for CMODEL_MEDIUM and CMODEL_LARGE.  Update all callers.
	(rs6000_delegitimize_address): Recognise new toc reference rtl
	and minimal-toc rtl.
	(rs6000_legitimize_reload_address): Handle new toc references.
	(print_operand_address): Handle legitimate_constant_pool_address_p
	match before lo_sum.
	(rs6000_eliminate_indexed_memrefs): Tidy.
	(rs6000_emit_move): Tweak threshold for inlining constants.
	Keep rs6000_emit_allocate_stack large stack frame offsets
	loaded into r0 inline.
	(rs6000_generate_compare <cmptf_internal2>): One more clobber.
Basic big toc support.

	(tocrel_base, tocrel_offset): New variables.
	(toc_relative_expr_p): Set them here.
	(print_operand_address): Skip over any offset on constant pool
	address.
	(rs6000_output_addr_const_extra): Print tocrel_offset before @toc.
I'm not too proud of this hack but can't see a better way of
rearranging the assembly.

	(rs6000_mode_dependent_address <LO_SUM>): False for new toc refs.
Necessary to support insns using "o" constraint.

	(offsettable_ok_by_alignment): New function.
	(rs6000_emit_move): Address suitably aligned local symbol_refs
	relative to the toc pointer for -mcmodel=medium.
We can't support toc pointer relative addressing for non-local vars
since ELF shared library semantics require such variables to be
overridable by the main executable or other shared libs.  That would
mean text relocs.

	(legitimate_constant_pool_address_p): Add strict param.  Allow
	lo_sum version of addressing.  Verify reg used for -mminimal-toc
	and -mcmodel != small.  Update all callers.
	* config/rs6000/constraints.md: Update for above change.
	* config/rs6000/predicates.md: Likewise.
Without properly checking the reg I found testsuite failures due to
reload not giving the high part address a hard reg.  I'm not sure why
this problem didn't trigger with -mminimal-toc..
.../gcc.c-torture/compile/20051216-1.c:131:1: error: insn does not satisfy its constraints:
(insn 105 562 666 27 ... (set (reg/v:DF 60 28 [orig:208 share_x ] [208])
        (mem/u/c/i:DF (lo_sum:DI (mem/c:DI (plus:DI (reg/f:DI 1 1)
                        (const_int 208 [0xd0])) [0 %sfp+208 S8 A64])
                (const:DI (unspec:DI [
                            (symbol_ref/u:DI ("*.LC2") [flags 0x2])
                        ] 49))) [0 S8 A64])) 360 {*movdf_hardfloat64} (expr_list:REG_EQUAL (const_double:DF 0.0 [0x0.0p+0])
        (nil)))

	* config/rs6000/rs6000.md (tls_gd_aix): Generate -mcmodel=medium/large
	code.
	(tls_gd): Split for -mcmodel=medium/large.
	(tls_gd_high, tls_gd_low): New.
	(tls_ld_aix, tls_ld, tls_ld_high, tls_ld_low): Similarly.
	(tls_got_dtprel, tls_got_dtprel_high, tls_got_dtprel_low): Similarly.
	(tls_got_tprel, tls_got_tprel_high, tls_got_tprel_low): Similarly.
	(bigtoc_high, bigtoc_low): New.
	(cmptf_internal2): Add clobber.
	* config/rs6000/rs6000-protos.h: Update.

Comments

David Edelsohn June 14, 2010, 6:48 p.m. UTC | #1
On Tue, Jun 8, 2010 at 8:57 AM, Alan Modra <amodra@gmail.com> wrote:
> PowerPC64 gcc support for a larger TOC via -mcmodel option.
>
> -mcmodel=small
>  uses 16-bit toc offsets, ie. what we had before.
>
> -mcmodel=medium
>  uses 32-bit toc offsets and addresses static data, string constants
>  etc. relative to the toc pointer.  Limits data to around 2G.
>
> -mcmodel=large
>  uses 32-bit toc offsets, no limit on static data.
>
> Default is -mcmodel=medium unless you specify other toc options.  For
> example, -mminimal-toc gets you -mcmodel=small.
>
> Bootstrapped and regression tested powerpc64-linux.  Comments follow
> associated changelog entry.
>
>        * doc/invoke.texi: Add mcmodel to powerpc options.
>        * configure.ac: Add HAVE_LD_LARGE_TOC test.
>        * configure: Regenerate.
>        * config.in: Regenerate.
>        * config/rs6000/linux64.opt (mcmodel): New.
>        * config/rs6000/linux64.h (TARGET_USES_LINUX64_OPT): Define.
>        (TARGET_CMODEL): Define.
>        (SUBSUBTARGET_OVERRIDE_OPTIONS): Check user -mcmodel choice,
>        select CMODEL_MEDIUM default.
>        * config/rs6000/rs6000.h (enum rs6000_cmodel): New.
>        (TARGET_CMODEL): Define default.
>        * config/rs6000/rs6000.c (cmodel): New variable.
>        (rs6000_explicit_options): Add cmodel field.
>        (rs6000_handle_option): Handle -mcmodel.
>        (create_TOC_reference): Add bigtoc_reg param.  Generate high,
>        lo_sum rtl for CMODEL_MEDIUM and CMODEL_LARGE.  Update all callers.
>        (rs6000_delegitimize_address): Recognise new toc reference rtl
>        and minimal-toc rtl.
>        (rs6000_legitimize_reload_address): Handle new toc references.
>        (print_operand_address): Handle legitimate_constant_pool_address_p
>        match before lo_sum.
>        (rs6000_eliminate_indexed_memrefs): Tidy.
>        (rs6000_emit_move): Tweak threshold for inlining constants.
>        Keep rs6000_emit_allocate_stack large stack frame offsets
>        loaded into r0 inline.
>        (rs6000_generate_compare <cmptf_internal2>): One more clobber.
> Basic big toc support.
>
>        (tocrel_base, tocrel_offset): New variables.
>        (toc_relative_expr_p): Set them here.
>        (print_operand_address): Skip over any offset on constant pool
>        address.
>        (rs6000_output_addr_const_extra): Print tocrel_offset before @toc.
> I'm not too proud of this hack but can't see a better way of
> rearranging the assembly.
>
>        (rs6000_mode_dependent_address <LO_SUM>): False for new toc refs.
> Necessary to support insns using "o" constraint.
>
>        (offsettable_ok_by_alignment): New function.
>        (rs6000_emit_move): Address suitably aligned local symbol_refs
>        relative to the toc pointer for -mcmodel=medium.
> We can't support toc pointer relative addressing for non-local vars
> since ELF shared library semantics require such variables to be
> overridable by the main executable or other shared libs.  That would
> mean text relocs.
>
>        (legitimate_constant_pool_address_p): Add strict param.  Allow
>        lo_sum version of addressing.  Verify reg used for -mminimal-toc
>        and -mcmodel != small.  Update all callers.
>        * config/rs6000/constraints.md: Update for above change.
>        * config/rs6000/predicates.md: Likewise.
> Without properly checking the reg I found testsuite failures due to
> reload not giving the high part address a hard reg.  I'm not sure why
> this problem didn't trigger with -mminimal-toc..
> .../gcc.c-torture/compile/20051216-1.c:131:1: error: insn does not satisfy its constraints:
> (insn 105 562 666 27 ... (set (reg/v:DF 60 28 [orig:208 share_x ] [208])
>        (mem/u/c/i:DF (lo_sum:DI (mem/c:DI (plus:DI (reg/f:DI 1 1)
>                        (const_int 208 [0xd0])) [0 %sfp+208 S8 A64])
>                (const:DI (unspec:DI [
>                            (symbol_ref/u:DI ("*.LC2") [flags 0x2])
>                        ] 49))) [0 S8 A64])) 360 {*movdf_hardfloat64} (expr_list:REG_EQUAL (const_double:DF 0.0 [0x0.0p+0])
>        (nil)))
>
>        * config/rs6000/rs6000.md (tls_gd_aix): Generate -mcmodel=medium/large
>        code.
>        (tls_gd): Split for -mcmodel=medium/large.
>        (tls_gd_high, tls_gd_low): New.
>        (tls_ld_aix, tls_ld, tls_ld_high, tls_ld_low): Similarly.
>        (tls_got_dtprel, tls_got_dtprel_high, tls_got_dtprel_low): Similarly.
>        (tls_got_tprel, tls_got_tprel_high, tls_got_tprel_low): Similarly.
>        (bigtoc_high, bigtoc_low): New.
>        (cmptf_internal2): Add clobber.
>        * config/rs6000/rs6000-protos.h: Update.

The patch is okay, with the appropriate updates for the other recent patches.

But please change bigtoc_reg, bigtoc_high and bigtoc_low to something
else.  Maybe largetoc?  Bigtoc means something specific on AIX and I
want to avoid confusion.

Thanks, David
Alan Modra June 15, 2010, 7:41 a.m. UTC | #2
On Mon, Jun 14, 2010 at 02:48:42PM -0400, David Edelsohn wrote:
> On Tue, Jun 8, 2010 at 8:57 AM, Alan Modra <amodra@gmail.com> wrote:
> > PowerPC64 gcc support for a larger TOC via -mcmodel option.
> The patch is okay, with the appropriate updates for the other recent patches.

For reference, that was just making the first arg of
legitimate_constant_pool_address_p a const_rtx, to suit the similar
change to rs6000_mode_dependent_address.

> But please change bigtoc_reg, bigtoc_high and bigtoc_low to something
> else.  Maybe largetoc?

Committed with those changes.
Richard Biener June 18, 2010, 10 a.m. UTC | #3
On Tue, Jun 15, 2010 at 9:41 AM, Alan Modra <amodra@gmail.com> wrote:
> On Mon, Jun 14, 2010 at 02:48:42PM -0400, David Edelsohn wrote:
>> On Tue, Jun 8, 2010 at 8:57 AM, Alan Modra <amodra@gmail.com> wrote:
>> > PowerPC64 gcc support for a larger TOC via -mcmodel option.
>> The patch is okay, with the appropriate updates for the other recent patches.
>
> For reference, that was just making the first arg of
> legitimate_constant_pool_address_p a const_rtx, to suit the similar
> change to rs6000_mode_dependent_address.
>
>> But please change bigtoc_reg, bigtoc_high and bigtoc_low to something
>> else.  Maybe largetoc?
>
> Committed with those changes.

This broke bootstrap on linux64 with

11:55 < richi> ../../mem-ref2/gcc/config/rs6000/rs6000.c: In function
               'rs6000_override_options':
11:55 < richi> ../../mem-ref2/gcc/config/rs6000/rs6000.c:2775:3: error: suggest
               braces around empty body in an 'if' statement
               [-Werror=empty-body]

because in


          else                                                  \
            {                                                   \
              if (!rs6000_explicit_options.cmodel)              \
                SET_CMODEL (CMODEL_MEDIUM);                 \

SET_CMODEL expands to nothing.

Richard.
diff mbox

Patch

Index: gcc/doc/invoke.texi
===================================================================
--- gcc/doc/invoke.texi	(revision 160429)
+++ gcc/doc/invoke.texi	(working copy)
@@ -744,6 +744,7 @@  See RS/6000 and PowerPC Options.
 @emph{RS/6000 and PowerPC Options}
 @gccoptlist{-mcpu=@var{cpu-type} @gol
 -mtune=@var{cpu-type} @gol
+-mcmodel=@var{code-model} @gol
 -mpower  -mno-power  -mpower2  -mno-power2 @gol
 -mpowerpc  -mpowerpc64  -mno-powerpc @gol
 -maltivec  -mno-altivec @gol
@@ -14976,6 +14977,22 @@  values for @var{cpu_type} are used for @
 architecture, registers, and mnemonics set by @option{-mcpu}, but the
 scheduling parameters set by @option{-mtune}.
 
+@item -mcmodel=small
+@opindex mcmodel=small
+Generate PowerPC64 code for the small model: The TOC is limited to
+64k.
+
+@item -mcmodel=medium
+@opindex mcmodel=medium
+Generate PowerPC64 code for the medium model: The TOC and other static
+data may be up to a total of 4G in size.
+
+@item -mcmodel=large
+@opindex mcmodel=large
+Generate PowerPC64 code for the large model: The TOC may be up to 4G
+in size.  Other data and code is only limited by the 64-bit address
+space.
+
 @item -maltivec
 @itemx -mno-altivec
 @opindex maltivec
Index: gcc/configure.ac
===================================================================
--- gcc/configure.ac	(revision 160429)
+++ gcc/configure.ac	(working copy)
@@ -3996,6 +3996,36 @@  EOF
       AC_DEFINE(HAVE_LD_NO_DOT_SYMS, 1,
     [Define if your PowerPC64 linker only needs function descriptor syms.])
     fi
+
+    AC_CACHE_CHECK(linker large toc support,
+    gcc_cv_ld_large_toc,
+    [gcc_cv_ld_large_toc=no
+    if test $in_tree_ld = yes ; then
+      if test "$gcc_cv_gld_major_version" -eq 2 -a "$gcc_cv_gld_minor_version" -ge 21 -o "$gcc_cv_gld_major_version" -gt 2; then
+        gcc_cv_ld_large_toc=yes
+      fi
+    elif test x$gcc_cv_as != x -a x$gcc_cv_ld != x ; then
+      cat > conftest.s <<EOF
+	.section ".tbss","awT",@nobits
+	.align 3
+ie0:	.space 8
+	.global _start
+	.text
+_start:
+	addis 9,13,ie0@got@tprel@ha
+	ld 9,ie0@got@tprel@l(9)
+EOF
+      if $gcc_cv_as -a64 -o conftest.o conftest.s > /dev/null 2>&1 \
+         && $gcc_cv_ld -melf64ppc --no-toc-sort -o conftest conftest.o > /dev/null 2>&1; then
+        gcc_cv_ld_large_toc=yes
+      fi
+      rm -f conftest conftest.o conftest.s
+    fi
+    ])
+    if test x"$gcc_cv_ld_large_toc" = xyes; then
+      AC_DEFINE(HAVE_LD_LARGE_TOC, 1,
+    [Define if your PowerPC64 linker supports a large TOC.])
+    fi
     ;;
 esac
 
Index: gcc/config/rs6000/linux64.opt
===================================================================
--- gcc/config/rs6000/linux64.opt	(revision 160429)
+++ gcc/config/rs6000/linux64.opt	(working copy)
@@ -22,3 +22,7 @@ 
 mprofile-kernel
 Target Report Var(profile_kernel)
 Call mcount for profiling before a function prologue
+
+mcmodel=
+Target RejectNegative Joined
+Select code model
Index: gcc/config/rs6000/linux64.h
===================================================================
--- gcc/config/rs6000/linux64.h	(revision 160429)
+++ gcc/config/rs6000/linux64.h	(working copy)
@@ -63,6 +63,16 @@  extern int dot_symbols;
 
 #define TARGET_PROFILE_KERNEL profile_kernel
 
+#define TARGET_USES_LINUX64_OPT 1
+#ifdef HAVE_LD_LARGE_TOC
+extern enum rs6000_cmodel cmodel;
+#undef TARGET_CMODEL
+#define TARGET_CMODEL cmodel
+#define SET_CMODEL(opt) cmodel = opt
+#else
+#define SET_CMODEL(opt)
+#endif
+
 #undef  PROCESSOR_DEFAULT
 #define PROCESSOR_DEFAULT PROCESSOR_POWER6
 #undef  PROCESSOR_DEFAULT64
@@ -114,6 +124,23 @@  extern int dot_symbols;
 	      target_flags |= MASK_POWERPC64;			\
 	      error ("-m64 requires a PowerPC64 cpu");		\
 	    }							\
+	  if ((target_flags_explicit & MASK_MINIMAL_TOC) != 0)	\
+	    {							\
+	      if (rs6000_explicit_options.cmodel		\
+		  && cmodel != CMODEL_SMALL)			\
+		error ("-mcmodel incompatible with other toc options"); \
+	      SET_CMODEL (CMODEL_SMALL);			\
+	    }							\
+	  else							\
+	    {							\
+	      if (!rs6000_explicit_options.cmodel)		\
+		SET_CMODEL (CMODEL_MEDIUM);			\
+	      if (cmodel != CMODEL_SMALL)			\
+		{						\
+		  TARGET_NO_FP_IN_TOC = 0;			\
+		  TARGET_NO_SUM_IN_TOC = 0;			\
+		}						\
+	    }							\
 	}							\
       else							\
 	{							\
@@ -124,6 +151,11 @@  extern int dot_symbols;
 	      TARGET_PROFILE_KERNEL = 0;			\
 	      error (INVALID_32BIT, "profile-kernel");		\
 	    }							\
+	  if (rs6000_explicit_options.cmodel)			\
+	    {							\
+	      SET_CMODEL (CMODEL_SMALL);			\
+	      error (INVALID_32BIT, "cmodel");			\
+	    }							\
 	}							\
     }								\
   while (0)
Index: gcc/config/rs6000/rs6000.h
===================================================================
--- gcc/config/rs6000/rs6000.h	(revision 160429)
+++ gcc/config/rs6000/rs6000.h	(working copy)
@@ -293,6 +293,20 @@  extern const char *host_detect_local_cpu
 #define TARGET_SECURE_PLT 0
 #endif
 
+/* Code model for 64-bit linux.
+   small: 16-bit toc offsets.
+   medium: 32-bit toc offsets, static data and code within 2G of TOC pointer.
+   large: 32-bit toc offsets, no limit on static data and code.  */
+enum rs6000_cmodel {
+  CMODEL_SMALL,
+  CMODEL_MEDIUM,
+  CMODEL_LARGE
+};
+
+#ifndef TARGET_CMODEL
+#define TARGET_CMODEL CMODEL_SMALL
+#endif
+
 #define TARGET_32BIT		(! TARGET_64BIT)
 
 #ifndef HAVE_AS_TLS
Index: gcc/config/rs6000/rs6000.c
===================================================================
--- gcc/config/rs6000/rs6000.c	(revision 160429)
+++ gcc/config/rs6000/rs6000.c	(working copy)
@@ -279,6 +279,9 @@  static GTY(()) section *toc_section;
 /* String from -malign-XXXXX.  */
 int rs6000_alignment_flags;
 
+/* Code model for 64-bit linux.  */
+enum rs6000_cmodel cmodel;
+
 /* True for any options that were explicitly set.  */
 static struct {
   bool aix_struct_ret;		/* True if -maix-struct-ret was used.  */
@@ -290,6 +293,7 @@  static struct {
   bool long_double;	        /* True if -mlong-double- was used.  */
   bool ieee;			/* True if -mabi=ieee/ibmlongdouble used.  */
   bool vrsave;			/* True if -mvrsave was used.  */
+  bool cmodel;			/* True if -mcmodel was used.  */
 } rs6000_explicit_options;
 
 struct builtin_description
@@ -3645,6 +3649,22 @@  rs6000_handle_option (size_t code, const
       break;
 #endif
 
+#if defined (HAVE_LD_LARGE_TOC) && defined (TARGET_USES_LINUX64_OPT)
+    case OPT_mcmodel_:
+      if (strcmp (arg, "small") == 0)
+	cmodel = CMODEL_SMALL;
+      else if (strcmp (arg, "medium") == 0)
+	cmodel = CMODEL_MEDIUM;
+      else if (strcmp (arg, "large") == 0)
+	cmodel = CMODEL_LARGE;
+      else
+	{
+	  error ("invalid option for -mcmodel: '%s'", arg);
+	  return false;
+	}
+      rs6000_explicit_options.cmodel = true;
+#endif
+
 #ifdef TARGET_USES_AIX64_OPT
     case OPT_maix64:
 #else
@@ -5098,26 +5118,29 @@  constant_pool_expr_p (rtx op)
 	  && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (base), Pmode));
 }
 
+static rtx tocrel_base, tocrel_offset;
+
 bool
 toc_relative_expr_p (rtx op)
 {
-  rtx base, offset;
-
   if (GET_CODE (op) != CONST)
     return false;
 
-  split_const (op, &base, &offset);
-  return (GET_CODE (base) == UNSPEC
-	  && XINT (base, 1) == UNSPEC_TOCREL);
+  split_const (op, &tocrel_base, &tocrel_offset);
+  return (GET_CODE (tocrel_base) == UNSPEC
+	  && XINT (tocrel_base, 1) == UNSPEC_TOCREL);
 }
 
 bool
-legitimate_constant_pool_address_p (rtx x)
+legitimate_constant_pool_address_p (rtx x, bool strict)
 {
   return (TARGET_TOC
-	  && GET_CODE (x) == PLUS
+	  && (GET_CODE (x) == PLUS || GET_CODE (x) == LO_SUM)
 	  && GET_CODE (XEXP (x, 0)) == REG
-	  && (TARGET_MINIMAL_TOC || REGNO (XEXP (x, 0)) == TOC_REGISTER)
+	  && (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)));
 }
 
@@ -5146,7 +5169,7 @@  rs6000_legitimate_offset_address_p (enum
     return false;
   if (!reg_offset_addressing_ok_p (mode))
     return virtual_stack_registers_memory_p (x);
-  if (legitimate_constant_pool_address_p (x))
+  if (legitimate_constant_pool_address_p (x, strict))
     return true;
   if (GET_CODE (XEXP (x, 1)) != CONST_INT)
     return false;
@@ -5494,7 +5517,8 @@  rs6000_legitimize_address (rtx x, rtx ol
 	   && constant_pool_expr_p (x)
 	   && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (x), Pmode))
     {
-      return create_TOC_reference (x);
+      rtx reg = TARGET_CMODEL != CMODEL_SMALL ? gen_reg_rtx (Pmode) : NULL_RTX;
+      return create_TOC_reference (x, reg);
     }
   else
     return x;
@@ -5585,10 +5609,13 @@  rs6000_delegitimize_address (rtx orig_x)
   if (MEM_P (x))
     x = XEXP (x, 0);
 
-  if (GET_CODE (x) == PLUS
-      && GET_CODE (XEXP (x, 1)) == CONST
+  if ((GET_CODE (x) == PLUS
+       || GET_CODE (x) == LO_SUM)
       && GET_CODE (XEXP (x, 0)) == REG
-      && REGNO (XEXP (x, 0)) == TOC_REGISTER)
+      && (REGNO (XEXP (x, 0)) == TOC_REGISTER
+	  || TARGET_MINIMAL_TOC
+	  || TARGET_CMODEL != CMODEL_SMALL)
+      && GET_CODE (XEXP (x, 1)) == CONST)
     {
       y = XEXP (XEXP (x, 1), 0);
       if (GET_CODE (y) == UNSPEC
@@ -5600,7 +5627,6 @@  rs6000_delegitimize_address (rtx orig_x)
 	  else
 	    return replace_equiv_address_nv (orig_x, y);
 	}
-      return orig_x;
     }
 
   if (TARGET_MACHO
@@ -5896,6 +5922,24 @@  rs6000_legitimize_reload_address (rtx 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)) == 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 (x, 0), 1), 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;
+    }
+
   /* Force ld/std non-word aligned offset into base register by wrapping
      in offset 0.  */
   if (GET_CODE (x) == PLUS
@@ -6021,7 +6065,11 @@  rs6000_legitimize_reload_address (rtx x,
       && constant_pool_expr_p (x)
       && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (x), mode))
     {
-      x = create_TOC_reference (x);
+      x = create_TOC_reference (x, NULL_RTX);
+      if (TARGET_CMODEL != CMODEL_SMALL)
+	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;
     }
@@ -6104,7 +6152,7 @@  rs6000_legitimate_address_p (enum machin
     return 1;
   if (reg_offset_p && legitimate_small_data_p (mode, x))
     return 1;
-  if (reg_offset_p && legitimate_constant_pool_address_p (x))
+  if (reg_offset_p && legitimate_constant_pool_address_p (x, reg_ok_strict))
     return 1;
   /* If not REG_OK_STRICT (before reload) let pass any stack offset.  */
   if (! reg_ok_strict
@@ -6212,7 +6260,9 @@  rs6000_mode_dependent_address (const_rtx
       break;
 
     case LO_SUM:
-      return true;
+      /* Anything in the constant pool is sufficiently aligned that
+	 all bytes have the same high part address.  */
+      return !legitimate_constant_pool_address_p (addr, false);
 
     /* Auto-increment cases are now treated generically in recog.c.  */
     case PRE_MODIFY:
@@ -6568,23 +6618,54 @@  rs6000_emit_set_long_const (rtx dest, HO
 static void
 rs6000_eliminate_indexed_memrefs (rtx operands[2])
 {
+  if (reload_in_progress)
+    return;
+
   if (GET_CODE (operands[0]) == MEM
       && GET_CODE (XEXP (operands[0], 0)) != REG
-      && ! legitimate_constant_pool_address_p (XEXP (operands[0], 0))
-      && ! reload_in_progress)
+      && ! legitimate_constant_pool_address_p (XEXP (operands[0], 0), false))
     operands[0]
       = replace_equiv_address (operands[0],
 			       copy_addr_to_reg (XEXP (operands[0], 0)));
 
   if (GET_CODE (operands[1]) == MEM
       && GET_CODE (XEXP (operands[1], 0)) != REG
-      && ! legitimate_constant_pool_address_p (XEXP (operands[1], 0))
-      && ! reload_in_progress)
+      && ! legitimate_constant_pool_address_p (XEXP (operands[1], 0), false))
     operands[1]
       = replace_equiv_address (operands[1],
 			       copy_addr_to_reg (XEXP (operands[1], 0)));
 }
 
+/* Return true if memory accesses to DECL are known to never straddle
+   a 32k boundary.  */
+
+static bool
+offsettable_ok_by_alignment (tree decl)
+{
+  unsigned HOST_WIDE_INT dsize;
+
+  /* Presume any compiler generated symbol_ref is suitably aligned.  */
+  if (!decl)
+    return true;
+
+  if (TREE_CODE (decl) != VAR_DECL
+      && TREE_CODE (decl) != PARM_DECL
+      && TREE_CODE (decl) != RESULT_DECL
+      && TREE_CODE (decl) != FIELD_DECL)
+    return true;
+
+  if (!host_integerp (DECL_SIZE_UNIT (decl), 1))
+    return false;
+
+  dsize = tree_low_cst (DECL_SIZE_UNIT (decl), 1);
+  if (dsize <= 1)
+    return true;
+  if (dsize > 32768)
+    return false;
+
+  return DECL_ALIGN_UNIT (decl) >= dsize;
+}
+
 /* Emit a move from SOURCE to DEST in mode MODE.  */
 void
 rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
@@ -6898,25 +6979,43 @@  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])))
+      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])
+	      && offsettable_ok_by_alignment (SYMBOL_REF_DECL (operands[1]))))
 	{
-	  operands[1] = create_TOC_reference (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);
 	}
       else if (mode == Pmode
 	       && CONSTANT_P (operands[1])
 	       && ((GET_CODE (operands[1]) != CONST_INT
 		    && ! easy_fp_constant (operands[1], mode))
 		   || (GET_CODE (operands[1]) == CONST_INT
-		       && num_insns_constant (operands[1], mode) > 2)
+		       && (num_insns_constant (operands[1], mode)
+			   > (TARGET_CMODEL != CMODEL_SMALL ? 3 : 2)))
 		   || (GET_CODE (operands[0]) == REG
 		       && FP_REGNO_P (REGNO (operands[0]))))
 	       && GET_CODE (operands[1]) != HIGH
-	       && ! legitimate_constant_pool_address_p (operands[1])
-	       && ! toc_relative_expr_p (operands[1]))
+	       && ! legitimate_constant_pool_address_p (operands[1], false)
+	       && ! toc_relative_expr_p (operands[1])
+	       && (TARGET_CMODEL == CMODEL_SMALL
+		   || can_create_pseudo_p ()
+		   || (REG_P (operands[0])
+		       && INT_REG_OK_FOR_BASE_P (operands[0], true))))
 	{
 
 #if TARGET_MACHO
@@ -6962,9 +7061,17 @@  rs6000_emit_move (rtx dest, rtx source, 
 			get_pool_constant (XEXP (operands[1], 0)),
 			get_pool_mode (XEXP (operands[1], 0))))
 	    {
-	      operands[1]
-		= gen_const_mem (mode,
-				 create_TOC_reference (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);
+	      operands[1] = gen_const_mem (mode, tocref);
 	      set_mem_alias_set (operands[1], get_TOC_alias_set ());
 	    }
 	}
@@ -15328,14 +15435,6 @@  print_operand_address (FILE *file, rtx x
   else if (GET_CODE (x) == PLUS && 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_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
 #if TARGET_MACHO
   else if (GET_CODE (x) == LO_SUM && GET_CODE (XEXP (x, 0)) == REG
 	   && CONSTANT_P (XEXP (x, 1)))
@@ -15345,11 +15444,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))
+  else if (legitimate_constant_pool_address_p (x, true))
+    {
+      /* 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)))
+	 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);
+      if (GET_CODE (x) == LO_SUM)
+	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, "(%s)", reg_names[REGNO (XEXP (x, 0))]);
+      fprintf (file, "@l(%s)", reg_names[ REGNO (XEXP (x, 0)) ]);
     }
+#endif
   else
     gcc_unreachable ();
 }
@@ -15363,9 +15480,14 @@  rs6000_output_addr_const_extra (FILE *fi
     switch (XINT (x, 1))
       {
       case UNSPEC_TOCREL:
-	x = XVECEXP (x, 0, 0);
-	gcc_assert (GET_CODE (x) == SYMBOL_REF);
-	output_addr_const (file, x);
+	gcc_assert (GET_CODE (XVECEXP (x, 0, 0)) == SYMBOL_REF);
+	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);
+	  }
 	if (!TARGET_AIX || (TARGET_ELF && TARGET_MINIMAL_TOC))
 	  {
 	    putc ('-', file);
@@ -15689,7 +15811,7 @@  rs6000_generate_compare (rtx cmp, enum m
 	  && !TARGET_IEEEQUAD
 	  && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_LONG_DOUBLE_128)
 	emit_insn (gen_rtx_PARALLEL (VOIDmode,
-	  gen_rtvec (9,
+	  gen_rtvec (10,
 		     gen_rtx_SET (VOIDmode,
 				  compare_result,
 				  gen_rtx_COMPARE (comp_mode, op0, op1)),
@@ -15700,7 +15822,8 @@  rs6000_generate_compare (rtx cmp, enum m
 		     gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode)),
 		     gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode)),
 		     gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode)),
-		     gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode)))));
+		     gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode)),
+		     gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (Pmode)))));
       else if (GET_CODE (op1) == UNSPEC
 	       && XINT (op1, 1) == UNSPEC_SP_TEST)
 	{
@@ -18259,8 +18382,10 @@  uses_TOC (void)
 #endif
 
 rtx
-create_TOC_reference (rtx symbol)
+create_TOC_reference (rtx symbol, rtx bigtoc_reg)
 {
+  rtx tocrel, tocreg;
+
   if (TARGET_DEBUG_ADDR)
     {
       if (GET_CODE (symbol) == SYMBOL_REF)
@@ -18276,10 +18401,23 @@  create_TOC_reference (rtx symbol)
 
   if (!can_create_pseudo_p ())
     df_set_regs_ever_live (TOC_REGISTER, true);
-  return gen_rtx_PLUS (Pmode,
-	   gen_rtx_REG (Pmode, TOC_REGISTER),
-	     gen_rtx_CONST (Pmode,
-	       gen_rtx_UNSPEC (Pmode, gen_rtvec (1, symbol), UNSPEC_TOCREL)));
+
+  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)
+    {
+      rtx hi = gen_rtx_PLUS (Pmode, tocreg, gen_rtx_HIGH (Pmode, tocrel));
+      if (bigtoc_reg != NULL)
+	{
+	  emit_move_insn (bigtoc_reg, hi);
+	  hi = bigtoc_reg;
+	}
+      return gen_rtx_LO_SUM (Pmode, hi, copy_rtx (tocrel));
+    }
+  else
+    return gen_rtx_PLUS (Pmode, tocreg, tocrel);
 }
 
 /* Issue assembly directives that create a reference to the given DWARF
Index: gcc/config/rs6000/constraints.md
===================================================================
--- gcc/config/rs6000/constraints.md	(revision 160429)
+++ gcc/config/rs6000/constraints.md	(working copy)
@@ -166,7 +166,7 @@  (define_address_constraint "a"
 
 (define_constraint "R"
   "AIX TOC entry"
-  (match_test "legitimate_constant_pool_address_p (op)"))
+  (match_test "legitimate_constant_pool_address_p (op, false)"))
 
 ;; General constraints
 
Index: gcc/config/rs6000/predicates.md
===================================================================
--- gcc/config/rs6000/predicates.md	(revision 160429)
+++ gcc/config/rs6000/predicates.md	(working copy)
@@ -837,7 +837,7 @@  (define_predicate "input_operand"
     return 1;
 
   /* A SYMBOL_REF referring to the TOC is valid.  */
-  if (legitimate_constant_pool_address_p (op))
+  if (legitimate_constant_pool_address_p (op, false))
     return 1;
 
   /* A constant pool expression (relative to the TOC) is valid */
Index: gcc/config/rs6000/rs6000.md
===================================================================
--- gcc/config/rs6000/rs6000.md	(revision 160429)
+++ gcc/config/rs6000/rs6000.md	(working copy)
@@ -11003,7 +11003,12 @@  (define_insn_and_split "tls_gd_aix<TLSmo
 		   UNSPEC_TLSGD)
    (clobber (reg:SI LR_REGNO))]
   "HAVE_AS_TLS && DEFAULT_ABI == ABI_AIX"
-  "addi %0,%1,%2@got@tlsgd\;bl %z3\;%."
+{
+  if (TARGET_CMODEL != CMODEL_SMALL)
+    return "addis %0,%1,%2@got@tlsgd@ha\;addi %0,%0,%2@got@tlsgd@l\;bl %z3\;%.";
+  else
+    return "addi %0,%1,%2@got@tlsgd\;bl %z3\;%.";
+}
   "&& TARGET_TLS_MARKERS"
   [(set (match_dup 0)
 	(unspec:TLSmode [(match_dup 1)
@@ -11016,7 +11021,10 @@  (define_insn_and_split "tls_gd_aix<TLSmo
 	      (clobber (reg:SI LR_REGNO))])]
   ""
   [(set_attr "type" "two")
-   (set_attr "length" "12")])
+   (set (attr "length")
+     (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL"))
+     		   (const_int 16)
+     		   (const_int 12)))])
 
 (define_insn_and_split "tls_gd_sysv<TLSmode:tls_sysv_suffix>"
   [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b")
@@ -11052,13 +11060,47 @@  (define_insn_and_split "tls_gd_sysv<TLSm
   [(set_attr "type" "two")
    (set_attr "length" "8")])
 
-(define_insn "*tls_gd<TLSmode:tls_abi_suffix>"
+(define_insn_and_split "*tls_gd<TLSmode:tls_abi_suffix>"
   [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b")
 	(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"
   "addi %0,%1,%2@got@tlsgd"
+  "&& TARGET_CMODEL != CMODEL_SMALL"
+  [(set (match_dup 3)
+  	(plus:TLSmode (match_dup 1)
+	  (high:TLSmode
+	    (unspec:TLSmode [(match_dup 2)] UNSPEC_TLSGD))))
+   (set (match_dup 0)
+   	(lo_sum:TLSmode (match_dup 3)
+	    (unspec:TLSmode [(match_dup 2)] UNSPEC_TLSGD)))]
+  "
+{
+  operands[3] = gen_reg_rtx (TARGET_64BIT ? DImode : SImode);
+}"
+  [(set (attr "length")
+     (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL"))
+     		   (const_int 8)
+     		   (const_int 4)))])
+
+(define_insn "*tls_gd_high<TLSmode:tls_abi_suffix>"
+  [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b")
+     (plus:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b")
+       (high:TLSmode
+	  (unspec:TLSmode [(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")])
+
+(define_insn "*tls_gd_low<TLSmode:tls_abi_suffix>"
+  [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b")
+     (lo_sum:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b")
+       (unspec:TLSmode [(match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")]
+		       UNSPEC_TLSGD)))]
+  "HAVE_AS_TLS && TARGET_TLS_MARKERS && TARGET_CMODEL != CMODEL_SMALL"
+  "addi %0,%1,%2@got@tlsgd@l"
   [(set_attr "length" "4")])
 
 (define_insn "*tls_gd_call_aix<TLSmode:tls_abi_suffix>"
@@ -11101,7 +11143,12 @@  (define_insn_and_split "tls_ld_aix<TLSmo
 		   UNSPEC_TLSLD)
    (clobber (reg:SI LR_REGNO))]
   "HAVE_AS_TLS && DEFAULT_ABI == ABI_AIX"
-  "addi %0,%1,%&@got@tlsld\;bl %z2\;%."
+{
+  if (TARGET_CMODEL != CMODEL_SMALL)
+    return "addis %0,%1,%&@got@tlsld@ha\;addi %0,%0,%&@got@tlsld@l\;bl %z2\;%.";
+  else
+    return "addi %0,%1,%&@got@tlsld\;bl %z2\;%.";
+}
   "&& TARGET_TLS_MARKERS"
   [(set (match_dup 0)
 	(unspec:TLSmode [(match_dup 1)]
@@ -11112,7 +11159,11 @@  (define_insn_and_split "tls_ld_aix<TLSmo
 	      (unspec:TLSmode [(const_int 0)] UNSPEC_TLSLD)
 	      (clobber (reg:SI LR_REGNO))])]
   ""
-  [(set_attr "length" "12")])
+  [(set_attr "type" "two")
+   (set (attr "length")
+     (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL"))
+     		   (const_int 16)
+     		   (const_int 12)))])
 
 (define_insn_and_split "tls_ld_sysv<TLSmode:tls_sysv_suffix>"
   [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b")
@@ -11145,12 +11196,44 @@  (define_insn_and_split "tls_ld_sysv<TLSm
   ""
   [(set_attr "length" "8")])
 
-(define_insn "*tls_ld<TLSmode:tls_abi_suffix>"
+(define_insn_and_split "*tls_ld<TLSmode:tls_abi_suffix>"
   [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b")
 	(unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b")]
 			UNSPEC_TLSLD))]
   "HAVE_AS_TLS && TARGET_TLS_MARKERS"
   "addi %0,%1,%&@got@tlsld"
+  "&& TARGET_CMODEL != CMODEL_SMALL"
+  [(set (match_dup 2)
+  	(plus:TLSmode (match_dup 1)
+	  (high:TLSmode
+	    (unspec:TLSmode [(const_int 0)] UNSPEC_TLSLD))))
+   (set (match_dup 0)
+   	(lo_sum:TLSmode (match_dup 2)
+	    (unspec:TLSmode [(const_int 0)] UNSPEC_TLSLD)))]
+  "
+{
+  operands[2] = gen_reg_rtx (TARGET_64BIT ? DImode : SImode);
+}"
+  [(set (attr "length")
+     (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL"))
+     		   (const_int 8)
+     		   (const_int 4)))])
+
+(define_insn "*tls_ld_high<TLSmode:tls_abi_suffix>"
+  [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b")
+     (plus:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b")
+       (high:TLSmode
+	  (unspec:TLSmode [(const_int 0)] UNSPEC_TLSLD))))]
+  "HAVE_AS_TLS && TARGET_TLS_MARKERS && TARGET_CMODEL != CMODEL_SMALL"
+  "addis %0,%1,%&@got@tlsld@ha"
+  [(set_attr "length" "4")])
+
+(define_insn "*tls_ld_low<TLSmode:tls_abi_suffix>"
+  [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b")
+     (lo_sum:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b")
+       (unspec:TLSmode [(const_int 0)] UNSPEC_TLSLD)))]
+  "HAVE_AS_TLS && TARGET_TLS_MARKERS && TARGET_CMODEL != CMODEL_SMALL"
+  "addi %0,%1,%&@got@tlsld@l"
   [(set_attr "length" "4")])
 
 (define_insn "*tls_ld_call_aix<TLSmode:tls_abi_suffix>"
@@ -11207,13 +11290,48 @@  (define_insn "tls_dtprel_lo_<TLSmode:tls
   "HAVE_AS_TLS"
   "addi %0,%1,%2@dtprel@l")
 
-(define_insn "tls_got_dtprel_<TLSmode:tls_abi_suffix>"
+(define_insn_and_split "tls_got_dtprel_<TLSmode:tls_abi_suffix>"
   [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=r")
 	(unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b")
 			 (match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")]
 			UNSPEC_TLSGOTDTPREL))]
   "HAVE_AS_TLS"
-  "l<TLSmode:tls_insn_suffix> %0,%2@got@dtprel(%1)")
+  "l<TLSmode:tls_insn_suffix> %0,%2@got@dtprel(%1)"
+  "&& TARGET_CMODEL != CMODEL_SMALL"
+  [(set (match_dup 3)
+	(plus:TLSmode (match_dup 1)
+	  (high:TLSmode
+	    (unspec:TLSmode [(match_dup 2)] UNSPEC_TLSGOTDTPREL))))
+   (set (match_dup 0)
+	(lo_sum:TLSmode (match_dup 3)
+	    (unspec:TLSmode [(match_dup 2)] UNSPEC_TLSGOTDTPREL)))]
+  "
+{
+  operands[3] = gen_reg_rtx (TARGET_64BIT ? DImode : SImode);
+}"
+  [(set (attr "length")
+     (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL"))
+     		   (const_int 8)
+     		   (const_int 4)))])
+
+(define_insn "*tls_got_dtprel_high<TLSmode:tls_abi_suffix>"
+  [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b")
+     (plus:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b")
+       (high:TLSmode
+	 (unspec:TLSmode [(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")])
+
+(define_insn "*tls_got_dtprel_low<TLSmode:tls_abi_suffix>"
+  [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=r")
+     (lo_sum:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b")
+	 (unspec:TLSmode [(match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")]
+			 UNSPEC_TLSGOTDTPREL)))]
+  "HAVE_AS_TLS && TARGET_CMODEL != CMODEL_SMALL"
+  "l<TLSmode:tls_insn_suffix> %0,%2@got@dtprel@l(%1)"
+  [(set_attr "length" "4")])
 
 (define_insn "tls_tprel_<TLSmode:tls_abi_suffix>"
   [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=r")
@@ -11242,13 +11360,48 @@  (define_insn "tls_tprel_lo_<TLSmode:tls_
 ;; "b" output constraint here and on tls_tls input to support linker tls
 ;; optimization.  The linker may edit the instructions emitted by a
 ;; tls_got_tprel/tls_tls pair to addis,addi.
-(define_insn "tls_got_tprel_<TLSmode:tls_abi_suffix>"
+(define_insn_and_split "tls_got_tprel_<TLSmode:tls_abi_suffix>"
   [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b")
 	(unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b")
 			 (match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")]
 			UNSPEC_TLSGOTTPREL))]
   "HAVE_AS_TLS"
-  "l<TLSmode:tls_insn_suffix> %0,%2@got@tprel(%1)")
+  "l<TLSmode:tls_insn_suffix> %0,%2@got@tprel(%1)"
+  "&& TARGET_CMODEL != CMODEL_SMALL"
+  [(set (match_dup 3)
+	(plus:TLSmode (match_dup 1)
+	  (high:TLSmode
+	    (unspec:TLSmode [(match_dup 2)] UNSPEC_TLSGOTTPREL))))
+   (set (match_dup 0)
+	(lo_sum:TLSmode (match_dup 3)
+	    (unspec:TLSmode [(match_dup 2)] UNSPEC_TLSGOTTPREL)))]
+  "
+{
+  operands[3] = gen_reg_rtx (TARGET_64BIT ? DImode : SImode);
+}"
+  [(set (attr "length")
+     (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL"))
+     		   (const_int 8)
+     		   (const_int 4)))])
+
+(define_insn "*tls_got_tprel_high<TLSmode:tls_abi_suffix>"
+  [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b")
+     (plus:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b")
+       (high:TLSmode
+	 (unspec:TLSmode [(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")])
+
+(define_insn "*tls_got_tprel_low<TLSmode:tls_abi_suffix>"
+  [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=r")
+     (lo_sum:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b")
+	 (unspec:TLSmode [(match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")]
+			 UNSPEC_TLSGOTTPREL)))]
+  "HAVE_AS_TLS && TARGET_CMODEL != CMODEL_SMALL"
+  "l<TLSmode:tls_insn_suffix> %0,%2@got@tprel@l(%1)"
+  [(set_attr "length" "4")])
 
 (define_insn "tls_tls_<TLSmode:tls_abi_suffix>"
   [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=r")
@@ -11257,7 +11410,6 @@  (define_insn "tls_tls_<TLSmode:tls_abi_s
 			UNSPEC_TLSTLS))]
   "HAVE_AS_TLS"
   "add %0,%1,%2@tls")
-
 
 ;; Next come insns related to the calling sequence.
 ;;
@@ -11547,6 +11699,21 @@  (define_insn "elf_low"
    "@
     {cal|la} %0,%2@l(%1)
     {ai|addic} %0,%1,%K2")
+
+;; Bigtoc support
+(define_insn "bigtoc_high"
+  [(set (match_operand:DI 0 "gpc_reg_operand" "=b")
+        (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 "bigtoc_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}")
 
 ;; A function pointer under AIX is a pointer to a data area whose first word
 ;; contains the actual address of the function, whose second word contains a
@@ -12821,26 +12988,27 @@  (define_insn_and_split "*cmptf_internal2
     (clobber (match_scratch:DF 7 "=d"))
     (clobber (match_scratch:DF 8 "=d"))
     (clobber (match_scratch:DF 9 "=d"))
-    (clobber (match_scratch:DF 10 "=d"))]
+    (clobber (match_scratch:DF 10 "=d"))
+    (clobber (match_scratch:GPR 11 "=b"))]
   "!TARGET_IEEEQUAD && TARGET_XL_COMPAT
    && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT && TARGET_LONG_DOUBLE_128"
   "#"
   "&& reload_completed"
-  [(set (match_dup 3) (match_dup 13))
-   (set (match_dup 4) (match_dup 14))
+  [(set (match_dup 3) (match_dup 14))
+   (set (match_dup 4) (match_dup 15))
    (set (match_dup 9) (abs:DF (match_dup 5)))
    (set (match_dup 0) (compare:CCFP (match_dup 9) (match_dup 3)))
    (set (pc) (if_then_else (ne (match_dup 0) (const_int 0))
-			   (label_ref (match_dup 11))
+			   (label_ref (match_dup 12))
 			   (pc)))
    (set (match_dup 0) (compare:CCFP (match_dup 5) (match_dup 7)))
-   (set (pc) (label_ref (match_dup 12)))
-   (match_dup 11)
+   (set (pc) (label_ref (match_dup 13)))
+   (match_dup 12)
    (set (match_dup 10) (minus:DF (match_dup 5) (match_dup 7)))
    (set (match_dup 9) (minus:DF (match_dup 6) (match_dup 8)))
    (set (match_dup 9) (plus:DF (match_dup 10) (match_dup 9)))
    (set (match_dup 0) (compare:CCFP (match_dup 9) (match_dup 4)))
-   (match_dup 12)]
+   (match_dup 13)]
 {
   REAL_VALUE_TYPE rv;
   const int lo_word = FLOAT_WORDS_BIG_ENDIAN ? GET_MODE_SIZE (DFmode) : 0;
@@ -12850,22 +13018,23 @@  (define_insn_and_split "*cmptf_internal2
   operands[6] = simplify_gen_subreg (DFmode, operands[1], TFmode, lo_word);
   operands[7] = simplify_gen_subreg (DFmode, operands[2], TFmode, hi_word);
   operands[8] = simplify_gen_subreg (DFmode, operands[2], TFmode, lo_word);
-  operands[11] = gen_label_rtx ();
   operands[12] = gen_label_rtx ();
+  operands[13] = gen_label_rtx ();
   real_inf (&rv);
-  operands[13] = force_const_mem (DFmode,
-				  CONST_DOUBLE_FROM_REAL_VALUE (rv, DFmode));
   operands[14] = force_const_mem (DFmode,
+				  CONST_DOUBLE_FROM_REAL_VALUE (rv, DFmode));
+  operands[15] = force_const_mem (DFmode,
 				  CONST_DOUBLE_FROM_REAL_VALUE (dconst0,
 								DFmode));
   if (TARGET_TOC)
     {
-      operands[13] = gen_const_mem (DFmode,
-				    create_TOC_reference (XEXP (operands[13], 0)));
-      operands[14] = gen_const_mem (DFmode,
-				    create_TOC_reference (XEXP (operands[14], 0)));
-      set_mem_alias_set (operands[13], get_TOC_alias_set ());
+      rtx tocref;
+      tocref = create_TOC_reference (XEXP (operands[14], 0), operands[11]);
+      operands[14] = gen_const_mem (DFmode, tocref);
+      tocref = create_TOC_reference (XEXP (operands[15], 0), operands[11]);
+      operands[15] = gen_const_mem (DFmode, tocref);
       set_mem_alias_set (operands[14], get_TOC_alias_set ());
+      set_mem_alias_set (operands[15], get_TOC_alias_set ());
     }
 })
 
Index: gcc/config/rs6000/rs6000-protos.h
===================================================================
--- gcc/config/rs6000/rs6000-protos.h	(revision 160429)
+++ gcc/config/rs6000/rs6000-protos.h	(working copy)
@@ -39,7 +39,7 @@  extern int small_data_operand (rtx, enum
 extern bool toc_relative_expr_p (rtx);
 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 (rtx);
+extern bool legitimate_constant_pool_address_p (rtx, bool);
 extern bool legitimate_indirect_address_p (rtx, int);
 extern bool legitimate_indexed_address_p (rtx, int);
 extern bool avoiding_indexed_address_p (enum machine_mode);
@@ -111,7 +111,7 @@  extern void rs6000_emit_swrsqrt (rtx, rt
 extern void output_toc (FILE *, rtx, int, enum machine_mode);
 extern rtx rs6000_longcall_ref (rtx);
 extern void rs6000_fatal_bad_address (rtx);
-extern rtx create_TOC_reference (rtx);
+extern rtx create_TOC_reference (rtx, rtx);
 extern void rs6000_split_multireg_move (rtx, rtx);
 extern void rs6000_emit_move (rtx, rtx, enum machine_mode);
 extern rtx rs6000_secondary_memory_needed_rtx (enum machine_mode);