diff mbox

[rs6000] Preserve link stack for 476 cpus

Message ID 1319766189.2996.39.camel@otta
State New
Headers show

Commit Message

Peter Bergner Oct. 28, 2011, 1:43 a.m. UTC
On Thu, 2011-10-13 at 10:03 -0700, Richard Henderson wrote:
> On 10/13/2011 08:49 AM, Peter Bergner wrote:
> > +	  if (TARGET_LINK_STACK)
> > +	    asm_fprintf (file, "\tbl 1f\n\tb 2f\n1:\n\tblr\n2:\n");
> > +	  else
> > +	    asm_fprintf (file, "\tbcl 20,31,1f\n1:\n");
> 
> Wouldn't it be better to set up an out-of-line "blr" insn that could
> be shared by all instances?  That would solve a lot of this sort of
> this sort of branch-to-branch-to-branch ugliness.
> 
> See the i386 port for an example of this, if you need it.

...after returning from a short vacation...

Ok, here's a patch to implement that, and it passes bootstrap and
regtesting.  Richard, is this what you had in mind?  I'll note that
I disabled rs6000_code_end for TARGET_POWERPC64, since I was running
into linker errors when building libgcc.  The merging of the thunk
routines with comdat worked fine, but the thunk function also has a
function descriptor and I couldn't figure out a way to get those
merged properly (if it's even possible), so they led to multiply
defined symbol linker errors.

Peter


	* config.gcc (powerpc*-*-linux*): Add powerpc*-*-linux*ppc476* variant.
	* config/rs6000/476.h: New file.
	* config/rs6000/476.opt: Likewise.
	* config/rs6000/rs6000.h (TARGET_LINK_STACK): New define.
	(SET_TARGET_LINK_STACK): Likewise.
	(TARGET_ASM_CODE_END): Define.
	* config/rs6000/rs6000.c (rs6000_option_override_internal): Enable
	TARGET_LINK_STACK for -mtune=476 and -mtune=476fp.
	(rs6000_legitimize_tls_address): Emit the link stack preserving GOT
	code if TARGET_LINK_STACK.
	(rs6000_emit_load_toc_table): Likewise.
	(output_function_profiler): Likewise
	(macho_branch_islands): Likewise
	(machopic_output_stub): Likewise
	(get_ppc476_thunk_name): New function.
	(rs6000_code_end): Likewise.
	* config/rs6000/rs6000.md (load_toc_v4_PIC_1, load_toc_v4_PIC_1b):
	Convert to a define_expand.
	(load_toc_v4_PIC_1_normal): New define_insn.
	(load_toc_v4_PIC_1_476): Likewise.
	(load_toc_v4_PIC_1b_normal): Likewise.
	(load_toc_v4_PIC_1b_476): Likewise.

Comments

Peter Bergner Oct. 28, 2011, 3:23 a.m. UTC | #1
On Thu, 2011-10-27 at 20:43 -0500, Peter Bergner wrote:
> Index: gcc/config/rs6000/476.opt
[snip]
> +Target Var(rs6000_link_stack) Init(1) Save

Oops, this should actually be Init(-1).  The hunk above was just my
way of testing the modified code more by enabling it by default.
Sorry about that.

Peter
Richard Henderson Oct. 28, 2011, 3:20 p.m. UTC | #2
On 10/27/2011 06:43 PM, Peter Bergner wrote:
> Ok, here's a patch to implement that, and it passes bootstrap and
> regtesting.  Richard, is this what you had in mind?  I'll note that
> I disabled rs6000_code_end for TARGET_POWERPC64, since I was running
> into linker errors when building libgcc.  The merging of the thunk
> routines with comdat worked fine, but the thunk function also has a
> function descriptor and I couldn't figure out a way to get those
> merged properly (if it's even possible), so they led to multiply
> defined symbol linker errors.

That's something you might have to discuss with David and Alan.

You might wind up bypassing some of the normal boilerplate
that gets added by final_start_function etc.

It does look like you're missing the stub for ppc64, and yet
you invoke it?  At least, I don't see anything earlier that
tests ppc64, only in rs6000_code_end.


r~
Peter Bergner Oct. 28, 2011, 4:36 p.m. UTC | #3
On Fri, 2011-10-28 at 08:20 -0700, Richard Henderson wrote:
> On 10/27/2011 06:43 PM, Peter Bergner wrote:
> > Ok, here's a patch to implement that, and it passes bootstrap and
> > regtesting.  Richard, is this what you had in mind?  I'll note that
> > I disabled rs6000_code_end for TARGET_POWERPC64, since I was running
> > into linker errors when building libgcc.  The merging of the thunk
> > routines with comdat worked fine, but the thunk function also has a
> > function descriptor and I couldn't figure out a way to get those
> > merged properly (if it's even possible), so they led to multiply
> > defined symbol linker errors.
> 
> That's something you might have to discuss with David and Alan.

So David, do we even want to bother trying to support this on -m64
given the only cpu that needs this is a 32-bit only cpu?  If so, I
can try and work with Alan to figure out how we can merge the
function descriptors for the thunk routines when using -m64.



> It does look like you're missing the stub for ppc64, and yet
> you invoke it?  At least, I don't see anything earlier that
> tests ppc64, only in rs6000_code_end.

Oops, you're write.  Had I compiled with -m64 -mcpu=power7 -mtune=476fp,
I would have caught that.  I guess (supposing we don't want to support
64-bit) I should have the following hunks instead, correct?

+  /* If not explicitly specified via option, decide whether to generate the
+     extra blr's required to preserve the link stack on some cpus (eg, 476).  */
+  if (TARGET_LINK_STACK == -1)
+    SET_TARGET_LINK_STACK (rs6000_cpu == PROCESSOR_PPC476
+                          && flag_pic
+                          && !TARGET_POWERPC64);
+


+static void
+rs6000_code_end (void)
+{
+  char name[32];
+  tree decl;
+
+  if (!TARGET_LINK_STACK)
+    return;
...



Peter


~
Richard Henderson Oct. 28, 2011, 4:44 p.m. UTC | #4
On 10/28/2011 09:36 AM, Peter Bergner wrote:
> Oops, you're write.  Had I compiled with -m64 -mcpu=power7 -mtune=476fp,
> I would have caught that.  I guess (supposing we don't want to support
> 64-bit) I should have the following hunks instead, correct?
> 
> +  /* If not explicitly specified via option, decide whether to generate the
> +     extra blr's required to preserve the link stack on some cpus (eg, 476).  */
> +  if (TARGET_LINK_STACK == -1)
> +    SET_TARGET_LINK_STACK (rs6000_cpu == PROCESSOR_PPC476
> +                          && flag_pic
> +                          && !TARGET_POWERPC64);

Not quite.  You can't allow the user to set TARGET_LINK_STACK either,
for 64-bit.  Because it won't work without further fixups.  More like

  if (TARGET_POWERPC64)
    SET_TARGET_LINK_STACK (0);
  if (TARGET_LINK_STACK == -1)
    SET_TARGET_LINK_STACK (rs6000_cpu == PROCESSOR_PPC476 && flag_pic);

That first test could possibly be more refined, like testing AIX
calling conventions or DOT_SYMBOLS.  But it hardly seems worthwhile.


r~
Peter Bergner Oct. 28, 2011, 6:35 p.m. UTC | #5
On Fri, 2011-10-28 at 09:44 -0700, Richard Henderson wrote:
> Not quite.  You can't allow the user to set TARGET_LINK_STACK either,
> for 64-bit.  Because it won't work without further fixups.  More like
> 
>   if (TARGET_POWERPC64)
>     SET_TARGET_LINK_STACK (0);
>   if (TARGET_LINK_STACK == -1)
>     SET_TARGET_LINK_STACK (rs6000_cpu == PROCESSOR_PPC476 && flag_pic);

Ah, I forgot about if the user explicitly uses -mpreserve-ppc476-link-stack.
Ok, so how about if we also spit out a warning that we're implicitly disabling
the link stack code rather than doing it silently?  Like so:

  if (TARGET_POWERPC64)
    {
      if (TARGET_LINK_STACK > 0)
	warning (0, "-m64 disables -mpreserve-ppc476-link-stack");
      SET_TARGET_LINK_STACK (0);
    }
  else if (TARGET_LINK_STACK == -1)
    SET_TARGET_LINK_STACK (rs6000_cpu == PROCESSOR_PPC476 && flag_pic);


Peter
Richard Henderson Oct. 28, 2011, 6:57 p.m. UTC | #6
On 10/28/2011 11:35 AM, Peter Bergner wrote:
> On Fri, 2011-10-28 at 09:44 -0700, Richard Henderson wrote:
>> Not quite.  You can't allow the user to set TARGET_LINK_STACK either,
>> for 64-bit.  Because it won't work without further fixups.  More like
>>
>>   if (TARGET_POWERPC64)
>>     SET_TARGET_LINK_STACK (0);
>>   if (TARGET_LINK_STACK == -1)
>>     SET_TARGET_LINK_STACK (rs6000_cpu == PROCESSOR_PPC476 && flag_pic);
> 
> Ah, I forgot about if the user explicitly uses -mpreserve-ppc476-link-stack.
> Ok, so how about if we also spit out a warning that we're implicitly disabling
> the link stack code rather than doing it silently?  Like so:
> 
>   if (TARGET_POWERPC64)
>     {
>       if (TARGET_LINK_STACK > 0)
> 	warning (0, "-m64 disables -mpreserve-ppc476-link-stack");
>       SET_TARGET_LINK_STACK (0);
>     }
>   else if (TARGET_LINK_STACK == -1)
>     SET_TARGET_LINK_STACK (rs6000_cpu == PROCESSOR_PPC476 && flag_pic);

Fine by me.  Final rs6000 approval is dje's bivouac.


r~
David Edelsohn Oct. 28, 2011, 7:37 p.m. UTC | #7
On Fri, Oct 28, 2011 at 12:36 PM, Peter Bergner <bergner@vnet.ibm.com> wrote:

> So David, do we even want to bother trying to support this on -m64
> given the only cpu that needs this is a 32-bit only cpu?  If so, I
> can try and work with Alan to figure out how we can merge the
> function descriptors for the thunk routines when using -m64.

I barely want to bother with this ;-).  So, no, I don't want to bother
with -m64 support.

- David
diff mbox

Patch

Index: gcc/config.gcc
===================================================================
--- gcc/config.gcc	(revision 179091)
+++ gcc/config.gcc	(working copy)
@@ -2133,6 +2133,9 @@  powerpc-*-linux* | powerpc64-*-linux*)
 	esac
 	tmake_file="${tmake_file} t-slibgcc-libgcc"
 	case ${target} in
+	    powerpc*-*-linux*ppc476*)
+		tm_file="${tm_file} rs6000/476.h"
+		extra_options="${extra_options} rs6000/476.opt" ;;
 	    powerpc*-*-linux*altivec*)
 		tm_file="${tm_file} rs6000/linuxaltivec.h" ;;
 	    powerpc*-*-linux*spe*)
Index: gcc/config/rs6000/476.h
===================================================================
--- gcc/config/rs6000/476.h	(revision 0)
+++ gcc/config/rs6000/476.h	(revision 0)
@@ -0,0 +1,32 @@ 
+/* Enable IBM PowerPC 476 support.
+   Copyright (C) 2011 Free Software Foundation, Inc.
+   Contributed by Peter Bergner (bergner@vnet.ibm.com)
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published
+   by the Free Software Foundation; either version 3, or (at your
+   option) any later version.
+
+   GCC is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+   License for more details.
+
+   Under Section 7 of GPL version 3, you are granted additional
+   permissions described in the GCC Runtime Library Exception, version
+   3.1, as published by the Free Software Foundation.
+
+   You should have received a copy of the GNU General Public License and
+   a copy of the GCC Runtime Library Exception along with this program;
+   see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#undef TARGET_LINK_STACK
+#define TARGET_LINK_STACK (rs6000_link_stack)
+
+#undef SET_TARGET_LINK_STACK
+#define SET_TARGET_LINK_STACK(X) do { TARGET_LINK_STACK = (X); } while (0)
+
+#undef TARGET_ASM_CODE_END
+#define TARGET_ASM_CODE_END rs6000_code_end
Index: gcc/config/rs6000/rs6000-protos.h
===================================================================
--- gcc/config/rs6000/rs6000-protos.h	(revision 179091)
+++ gcc/config/rs6000/rs6000-protos.h	(working copy)
@@ -173,6 +173,7 @@  extern void rs6000_emit_eh_reg_restore (
 extern const char * output_isel (rtx *);
 extern void rs6000_call_indirect_aix (rtx, rtx, rtx);
 extern void rs6000_aix_asm_output_dwarf_table_ref (char *);
+extern void get_ppc476_thunk_name (char name[32]);
 
 /* Declare functions in rs6000-c.c */
 
Index: gcc/config/rs6000/476.opt
===================================================================
--- gcc/config/rs6000/476.opt	(revision 0)
+++ gcc/config/rs6000/476.opt	(revision 0)
@@ -0,0 +1,24 @@ 
+; IBM PowerPC 476 options.
+;
+; Copyright (C) 2011 Free Software Foundation, Inc.
+; Contributed by Peter Bergner (bergner@vnet.ibm.com)
+;
+; This file is part of GCC.
+;
+; GCC is free software; you can redistribute it and/or modify it under
+; the terms of the GNU General Public License as published by the Free
+; Software Foundation; either version 3, or (at your option) any later
+; version.
+;
+; GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+; WARRANTY; without even the implied warranty of MERCHANTABILITY or
+; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+; for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with GCC; see the file COPYING3.  If not see
+; <http://www.gnu.org/licenses/>.
+
+mpreserve-ppc476-link-stack
+Target Var(rs6000_link_stack) Init(1) Save
+Preserve the PowerPC 476's link stack by matching up a blr with the bcl/bl insns used for GOT accesses
Index: gcc/config/rs6000/rs6000.c
===================================================================
--- gcc/config/rs6000/rs6000.c	(revision 179091)
+++ gcc/config/rs6000/rs6000.c	(working copy)
@@ -1618,7 +1618,6 @@  static const struct attribute_spec rs600
 #undef TARGET_LEGITIMATE_CONSTANT_P
 #define TARGET_LEGITIMATE_CONSTANT_P rs6000_legitimate_constant_p
 
-struct gcc_target targetm = TARGET_INITIALIZER;
 
 
 /* Simplifications for entries below.  */
@@ -3244,6 +3243,11 @@  rs6000_option_override_internal (bool gl
     target_option_default_node = target_option_current_node
       = build_target_option_node ();
 
+  /* If not explicitly specified via option, decide whether to generate the
+     extra blr's required to preserve the link stack on some cpus (eg, 476).  */
+  if (TARGET_LINK_STACK == -1)
+    SET_TARGET_LINK_STACK (rs6000_cpu == PROCESSOR_PPC476);
+
   return ret;
 }
 
@@ -5932,6 +5936,8 @@  rs6000_legitimize_tls_address (rtx addr,
 		  lab = gen_label_rtx ();
 		  emit_insn (gen_load_toc_v4_PIC_1b (gsym, lab));
 		  emit_move_insn (tmp1, gen_rtx_REG (Pmode, LR_REGNO));
+		  if (TARGET_LINK_STACK)
+		    emit_insn (gen_addsi3 (tmp1, tmp1, GEN_INT (4)));
 		  emit_move_insn (tmp2, mem);
 		  last = emit_insn (gen_addsi3 (got, tmp1, tmp2));
 		  set_unique_reg_note (last, REG_EQUAL, gsym);
@@ -18923,6 +18929,8 @@  rs6000_emit_load_toc_table (int fromprol
 	  lab = gen_label_rtx ();
 	  emit_insn (gen_load_toc_v4_PIC_1b (tocsym, lab));
 	  emit_move_insn (dest, gen_rtx_REG (Pmode, LR_REGNO));
+	  if (TARGET_LINK_STACK)
+	    emit_insn (gen_addsi3 (dest, dest, GEN_INT (4)));
 	  emit_move_insn (temp0, gen_rtx_MEM (Pmode, dest));
 	}
       emit_insn (gen_addsi3 (dest, temp0, dest));
@@ -22493,7 +22501,15 @@  output_function_profiler (FILE *file, in
 	}
       else if (TARGET_SECURE_PLT && flag_pic)
 	{
-	  asm_fprintf (file, "\tbcl 20,31,1f\n1:\n\t{st|stw} %s,4(%s)\n",
+	  if (TARGET_LINK_STACK)
+	    {
+	      char name[32];
+	      get_ppc476_thunk_name (name);
+	      asm_fprintf (file, "\tbl %s\n", name);
+	    }
+	  else
+	    asm_fprintf (file, "\tbcl 20,31,1f\n1:\n");
+	  asm_fprintf (file, "\t{st|stw} %s,4(%s)\n",
 		       reg_names[0], reg_names[1]);
 	  asm_fprintf (file, "\tmflr %s\n", reg_names[12]);
 	  asm_fprintf (file, "\t{cau|addis} %s,%s,",
@@ -22518,10 +22534,24 @@  output_function_profiler (FILE *file, in
 	  asm_fprintf (file, "\t{st|stw} %s,4(%s)\n",
 		       reg_names[0], reg_names[1]);
 	  /* Now, we need to get the address of the label.  */
-	  fputs ("\tbcl 20,31,1f\n\t.long ", file);
-	  assemble_name (file, buf);
-	  fputs ("-.\n1:", file);
-	  asm_fprintf (file, "\tmflr %s\n", reg_names[11]);
+	  if (TARGET_LINK_STACK)
+	    {
+	      char name[32];
+	      get_ppc476_thunk_name (name);
+	      asm_fprintf (file, "\tbl %s\n\tb 1f\n\t.long ", name);
+	      assemble_name (file, buf);
+	      fputs ("-.\n1:", file);
+	      asm_fprintf (file, "\tmflr %s\n", reg_names[11]);
+	      asm_fprintf (file, "\taddi %s,%s,4\n",
+			   reg_names[11], reg_names[11]);
+	    }
+	  else
+	    {
+	      fputs ("\tbcl 20,31,1f\n\t.long ", file);
+	      assemble_name (file, buf);
+	      fputs ("-.\n1:", file);
+	      asm_fprintf (file, "\tmflr %s\n", reg_names[11]);
+	    }
 	  asm_fprintf (file, "\t{l|lwz} %s,0(%s)\n",
 		       reg_names[0], reg_names[11]);
 	  asm_fprintf (file, "\t{cax|add} %s,%s,%s\n",
@@ -25004,11 +25034,24 @@  macho_branch_islands (void)
 #endif /* DBX_DEBUGGING_INFO || XCOFF_DEBUGGING_INFO */
       if (flag_pic)
 	{
-	  strcat (tmp_buf, ":\n\tmflr r0\n\tbcl 20,31,");
-	  strcat (tmp_buf, label);
-	  strcat (tmp_buf, "_pic\n");
-	  strcat (tmp_buf, label);
-	  strcat (tmp_buf, "_pic:\n\tmflr r11\n");
+	  if (TARGET_LINK_STACK)
+	    {
+	      char name[32];
+	      get_ppc64_thunk_name (name);
+	      strcat (tmp_buf, ":\n\tmflr r0\n\tbl ");
+	      strcat (tmp_buf, name);
+	      strcat (tmp_buf, "\n");
+	      strcat (tmp_buf, label);
+	      strcat (tmp_buf, "_pic:\n\tmflr r11\n");
+	    }
+	  else
+	    {
+	      strcat (tmp_buf, ":\n\tmflr r0\n\tbcl 20,31,");
+	      strcat (tmp_buf, label);
+	      strcat (tmp_buf, "_pic\n");
+	      strcat (tmp_buf, label);
+	      strcat (tmp_buf, "_pic:\n\tmflr r11\n");
+	    }
 
 	  strcat (tmp_buf, "\taddis r11,r11,ha16(");
 	  strcat (tmp_buf, name_buf);
@@ -25154,8 +25197,18 @@  machopic_output_stub (FILE *file, const
       sprintf (local_label_0, "\"L%011d$spb\"", label);
 
       fprintf (file, "\tmflr r0\n");
-      fprintf (file, "\tbcl 20,31,%s\n", local_label_0);
-      fprintf (file, "%s:\n\tmflr r11\n", local_label_0);
+      if (TARGET_LINK_STACK)
+	{
+	  char name[32];
+	  get_ppc476_thunk_name (name);
+	  fprintf (file, "\tbl %s\n", name);
+	  fprintf (file, "%s:\n\tmflr r11\n", local_label_0);
+	}
+      else
+	{
+	  fprintf (file, "\tbcl 20,31,%s\n", local_label_0);
+	  fprintf (file, "%s:\n\tmflr r11\n", local_label_0);
+	}
       fprintf (file, "\taddis r11,r11,ha16(%s-%s)\n",
 	       lazy_ptr_name, local_label_0);
       fprintf (file, "\tmtlr r0\n");
@@ -27862,4 +27915,71 @@  rs6000_save_toc_in_prologue_p (void)
   return (cfun && cfun->machine && cfun->machine->save_toc_in_prologue);
 }
 
+/* Fills in the label name that should be used for a 476 link stack thunk.  */
+
+void
+get_ppc476_thunk_name (char name[32])
+{
+  gcc_assert (TARGET_LINK_STACK);
+
+  if (HAVE_GAS_HIDDEN)
+    sprintf (name, "__ppc476.get_thunk");
+  else
+    ASM_GENERATE_INTERNAL_LABEL (name, "LPPC476_", 0);
+}
+
+/* This function emits the simple thunk routine that is used to preserve
+   the link stack on the 476 cpu.  */
+
+static void
+rs6000_code_end (void)
+{
+  char name[32];
+  tree decl;
+
+  if (!TARGET_LINK_STACK || !flag_pic || TARGET_POWERPC64)
+    return;
+
+  get_ppc476_thunk_name (name);
+
+  decl = build_decl (BUILTINS_LOCATION, FUNCTION_DECL, get_identifier (name),
+		     build_function_type_list (void_type_node, NULL_TREE));
+  DECL_RESULT (decl) = build_decl (BUILTINS_LOCATION, RESULT_DECL,
+				   NULL_TREE, void_type_node);
+  TREE_PUBLIC (decl) = 1;
+  TREE_STATIC (decl) = 1;
+
+  if (HAVE_GAS_HIDDEN)
+    {
+      DECL_COMDAT_GROUP (decl) = DECL_ASSEMBLER_NAME (decl);
+      targetm.asm_out.unique_section (decl, 0);
+      switch_to_section (get_named_section (decl, NULL, 0));
+      targetm.asm_out.globalize_label (asm_out_file, name);
+      targetm.asm_out.assemble_visibility (decl, VISIBILITY_HIDDEN);
+      ASM_DECLARE_FUNCTION_NAME (asm_out_file, name, decl);
+    }
+  else
+    {
+      switch_to_section (text_section);
+      ASM_OUTPUT_LABEL (asm_out_file, name);
+    }
+
+  DECL_INITIAL (decl) = make_node (BLOCK);
+  current_function_decl = decl;
+  init_function_start (decl);
+  first_function_block_is_cold = false;
+  /* Make sure unwind info is emitted for the thunk if needed.  */
+  final_start_function (emit_barrier (), asm_out_file, 1);
+
+  fputs ("\tblr\n", asm_out_file);
+
+  final_end_function ();
+  init_insn_lengths ();
+  free_after_compilation (cfun);
+  set_cfun (NULL);
+  current_function_decl = NULL;
+}
+
+struct gcc_target targetm = TARGET_INITIALIZER;
+
 #include "gt-rs6000.h"
Index: gcc/config/rs6000/rs6000.h
===================================================================
--- gcc/config/rs6000/rs6000.h	(revision 179091)
+++ gcc/config/rs6000/rs6000.h	(working copy)
@@ -313,6 +313,14 @@  extern const char *host_detect_local_cpu
 #define HAVE_AS_TLS 0
 #endif
 
+#ifndef TARGET_LINK_STACK
+#define TARGET_LINK_STACK 0
+#endif
+
+#ifndef SET_TARGET_LINK_STACK
+#define SET_TARGET_LINK_STACK(X) do { } while (0)
+#endif
+
 /* Return 1 for a symbol ref for a thread-local storage symbol.  */
 #define RS6000_SYMBOL_REF_TLS_P(RTX) \
   (GET_CODE (RTX) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (RTX) != 0)
Index: gcc/config/rs6000/rs6000.md
===================================================================
--- gcc/config/rs6000/rs6000.md	(revision 179091)
+++ gcc/config/rs6000/rs6000.md	(working copy)
@@ -12081,27 +12081,81 @@  (define_insn "load_toc_v4_pic_si"
   [(set_attr "type" "branch")
    (set_attr "length" "4")])
 
-(define_insn "load_toc_v4_PIC_1"
+(define_expand "load_toc_v4_PIC_1"
+  [(parallel [(set (reg:SI LR_REGNO)
+		   (match_operand:SI 0 "immediate_operand" "s"))
+	      (use (unspec [(match_dup 0)] UNSPEC_TOC))])]
+  "TARGET_ELF && DEFAULT_ABI != ABI_AIX
+   && (flag_pic == 2 || (flag_pic && TARGET_SECURE_PLT))"
+  "")
+
+(define_insn "load_toc_v4_PIC_1_normal"
   [(set (reg:SI LR_REGNO)
 	(match_operand:SI 0 "immediate_operand" "s"))
    (use (unspec [(match_dup 0)] UNSPEC_TOC))]
-  "TARGET_ELF && DEFAULT_ABI != ABI_AIX
+  "!TARGET_LINK_STACK && TARGET_ELF && DEFAULT_ABI != ABI_AIX
    && (flag_pic == 2 || (flag_pic && TARGET_SECURE_PLT))"
   "bcl 20,31,%0\\n%0:"
   [(set_attr "type" "branch")
    (set_attr "length" "4")])
 
-(define_insn "load_toc_v4_PIC_1b"
+(define_insn "load_toc_v4_PIC_1_476"
+  [(set (reg:SI LR_REGNO)
+	(match_operand:SI 0 "immediate_operand" "s"))
+   (use (unspec [(match_dup 0)] UNSPEC_TOC))]
+  "TARGET_LINK_STACK && TARGET_ELF && DEFAULT_ABI != ABI_AIX
+   && (flag_pic == 2 || (flag_pic && TARGET_SECURE_PLT))"
+  "*
+{
+  char name[32];
+  static char templ[32];
+
+  get_ppc476_thunk_name (name);
+  sprintf (templ, \"bl %s\\n%%0:\", name);
+  return templ;
+}"
+  [(set_attr "type" "branch")
+   (set_attr "length" "4")])
+
+(define_expand "load_toc_v4_PIC_1b"
+  [(parallel [(set (reg:SI LR_REGNO)
+		   (unspec:SI [(match_operand:SI 0 "immediate_operand" "s")
+			       (label_ref (match_operand 1 "" ""))]
+		           UNSPEC_TOCPTR))
+	      (match_dup 1)])]
+  "TARGET_ELF && DEFAULT_ABI != ABI_AIX && flag_pic == 2"
+  "")
+
+(define_insn "load_toc_v4_PIC_1b_normal"
   [(set (reg:SI LR_REGNO)
 	(unspec:SI [(match_operand:SI 0 "immediate_operand" "s")
 		    (label_ref (match_operand 1 "" ""))]
 		UNSPEC_TOCPTR))
    (match_dup 1)]
-  "TARGET_ELF && DEFAULT_ABI != ABI_AIX && flag_pic == 2"
+  "!TARGET_LINK_STACK && TARGET_ELF && DEFAULT_ABI != ABI_AIX && flag_pic == 2"
   "bcl 20,31,$+8\;.long %0-$"
   [(set_attr "type" "branch")
    (set_attr "length" "8")])
 
+(define_insn "load_toc_v4_PIC_1b_476"
+  [(set (reg:SI LR_REGNO)
+	(unspec:SI [(match_operand:SI 0 "immediate_operand" "s")
+		    (label_ref (match_operand 1 "" ""))]
+		UNSPEC_TOCPTR))
+   (match_dup 1)]
+  "TARGET_LINK_STACK && TARGET_ELF && DEFAULT_ABI != ABI_AIX && flag_pic == 2"
+  "*
+{
+  char name[32];
+  static char templ[32];
+
+  get_ppc476_thunk_name (name);
+  sprintf (templ, \"bl %s\\n\\tb $+8\\n\\t.long %%0-$\", name);
+  return templ;
+}"
+  [(set_attr "type" "branch")
+   (set_attr "length" "16")])
+
 (define_insn "load_toc_v4_PIC_2"
   [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
 	(mem:SI (plus:SI (match_operand:SI 1 "gpc_reg_operand" "b")