Patchwork [rs6000] Preserve link stack for 476 cpus

login
register
mail settings
Submitter Peter Bergner
Date Nov. 1, 2011, 7 p.m.
Message ID <1320174025.2996.249.camel@otta>
Download mbox | patch
Permalink /patch/123116/
State New
Headers show

Comments

Peter Bergner - Nov. 1, 2011, 7 p.m.
On Mon, 2011-10-31 at 19:05 -0400, David Edelsohn wrote:
> Okay, go ahead with PPC64 support as well.  Hopefully no one ever will
> have to use it.  That implies the option should not explicitly
> reference ppc476.

Ok, for completeness, I attached what I committed below, which includes
the support for 64-bit because it makes the code cleaner and changes
the option name back to -mpreserve-link-stack.  Thanks.

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.
David Edelsohn - Nov. 2, 2011, 5:18 p.m.
On Tue, Nov 1, 2011 at 3:00 PM, Peter Bergner <bergner@vnet.ibm.com> wrote:

> +/* 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)
> +    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));
> +      DECL_WEAK (decl) = 1;
> +      ASM_WEAKEN_DECL (asm_out_file, decl, name, 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;

The two new functions have mistakes because I did not realize the
semantics of HAVE_GAS_HIDDEN.  HAVE_GAS_HIDDEN is not a macro to be
tested at runtime, but a macro tested at compile time.

if (HAVE_GAS_HIDDEN)

should be

#ifdef HAVE_GAS_HIDDEN

Please correct.

Thanks, David
IainS - Nov. 2, 2011, 6:17 p.m.
On 2 Nov 2011, at 17:18, David Edelsohn wrote:

> On Tue, Nov 1, 2011 at 3:00 PM, Peter Bergner <bergner@vnet.ibm.com>  
> wrote:
>
>> +/* 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)
>> +    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));
>> +      DECL_WEAK (decl) = 1;
>> +      ASM_WEAKEN_DECL (asm_out_file, decl, name, 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;
>
> The two new functions have mistakes because I did not realize the
> semantics of HAVE_GAS_HIDDEN.  HAVE_GAS_HIDDEN is not a macro to be
> tested at runtime, but a macro tested at compile time.
>
> if (HAVE_GAS_HIDDEN)
>
> should be
>
> #ifdef HAVE_GAS_HIDDEN

also in macho_branch_islands () :

	  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

which breaks bootstrap for darwin - I'm not sure why you have an entry  
here (this is mach-o-specific code)
  - I doubt there will ever be a mach-o implementation with a PPC476 -
but, in any case the call to get_ppc64_thunk_name needs wrapping  
somehow.

cheers
Iain

Patch

Index: gcc/config.gcc
===================================================================
--- gcc/config.gcc	(revision 180740)
+++ gcc/config.gcc	(revision 180741)
@@ -2145,6 +2145,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 180741)
@@ -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 180740)
+++ gcc/config/rs6000/rs6000-protos.h	(revision 180741)
@@ -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 180741)
@@ -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-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 180740)
+++ gcc/config/rs6000/rs6000.c	(revision 180741)
@@ -1613,7 +1613,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.  */
@@ -3239,6 +3238,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 && flag_pic);
+
   return ret;
 }
 
@@ -5868,6 +5872,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);
@@ -18866,6 +18872,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));
@@ -22526,7 +22534,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,",
@@ -22551,10 +22567,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",
@@ -25037,11 +25067,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);
@@ -25187,8 +25230,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");
@@ -27895,4 +27948,73 @@  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)
+    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));
+      DECL_WEAK (decl) = 1;
+      ASM_WEAKEN_DECL (asm_out_file, decl, name, 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 180740)
+++ gcc/config/rs6000/rs6000.h	(revision 180741)
@@ -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 180740)
+++ gcc/config/rs6000/rs6000.md	(revision 180741)
@@ -12087,27 +12087,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")