[PATCH] AIX native TLS support

David Edelsohn dje.gcc@gmail.com
Thu Nov 29 02:55:00 GMT 2012


Below is a first attempt at native TLS support on AIX.  It produces
the correct syntax and works for small testcases.  All of GCC can
build with it enabled, but libstdc++ and libgomp do not run correctly,
so I am not enabling it by default.

The implementation treats local-dynamic as general-dynamic because AIX
local-dynamic is a mess to implement for not much gain.

Unlike PPC64 Linux, AIX requires TLS symbols to be placed in a special
CSECT mapping class and referenced with special TOC symbol
decorations, not assembler decorations left to the linker.  The patch
leaves PPC64 Linux behavior unchanged.

Thanks, David


	* xcoffout.c (xcoff_tls_data_section_name): Define.
	* xcoffout.h (xcoff_tls_data_section_name): Declare.
	* config/rs6000/rs6000.c (tls_data_section): Define.
	(TARGET_USE_BLOCKS_FOR_DECL_P): Define.
	(rs6000_legitimize_tls_address_aix): New function.
	(rs6000_legitimize_tls_address): Use new function for AIX.
	(rs6000_cannot_force_const_mem): No sum in TLS TOC symbols.
	Allow TLS symbol in constant pool other than ELF.
	(rs6000_legitimate_address_p): Allow TLS symbol other than ELF.
	(rs6000_assemble_visibility): Do not emit anything on AIX.
	(output_toc): Handle alias of TLS general-dynamic symbols.
	Emit TLS decorations on symbols.
	(rs6000_use_blocks_for_decl_p): New function.
	(rs6000_xcoff_output_tls_section_asm_op): New function.
	(rs6000_xcoff_asm_init_sections): Initialize tls_data_section.
	(rs6000_xcoff_select_section): Choose tls_data_section for
	thread-local storage.
	(rs6000_xcoff_file_start): Generate xcoff_tls_data_section_name.
	(rs6000_legitimate_constant_p): Allow TLS symbol other than ELF.
	* config/rs6000/rs6000.md (tls_tls_): Restrict to ELF.
	(tls_get_tpointer): New.
	(tle_get_tpointer_internal): New.
	(tls_get_addr<mode>): New.
	(tls_get_addr_internal<mode>): New.

Index: xcoffout.c
===================================================================
--- xcoffout.c	(revision 193917)
+++ xcoffout.c	(working copy)
@@ -66,6 +66,7 @@

 char *xcoff_bss_section_name;
 char *xcoff_private_data_section_name;
+char *xcoff_tls_data_section_name;
 char *xcoff_read_only_section_name;

 /* Last source file name mentioned in a NOTE insn.  */
Index: xcoffout.h
===================================================================
--- xcoffout.h	(revision 193917)
+++ xcoffout.h	(working copy)
@@ -126,6 +126,7 @@

 extern char *xcoff_bss_section_name;
 extern char *xcoff_private_data_section_name;
+extern char *xcoff_tls_data_section_name;
 extern char *xcoff_read_only_section_name;

 /* Last source file name mentioned in a NOTE insn.  */
Index: config/rs6000/rs6000.c
===================================================================
--- config/rs6000/rs6000.c	(revision 193917)
+++ config/rs6000/rs6000.c	(working copy)
@@ -208,6 +208,7 @@

 static GTY(()) section *read_only_data_section;
 static GTY(()) section *private_data_section;
+static GTY(()) section *tls_data_section;
 static GTY(()) section *read_only_private_data_section;
 static GTY(()) section *sdata2_section;
 static GTY(()) section *toc_section;
@@ -1405,6 +1406,8 @@
 #define TARGET_MAX_ANCHOR_OFFSET 0x7fffffff
 #undef TARGET_USE_BLOCKS_FOR_CONSTANT_P
 #define TARGET_USE_BLOCKS_FOR_CONSTANT_P rs6000_use_blocks_for_constant_p
+#undef TARGET_USE_BLOCKS_FOR_DECL_P
+#define TARGET_USE_BLOCKS_FOR_DECL_P rs6000_use_blocks_for_decl_p

 #undef TARGET_BUILTIN_RECIPROCAL
 #define TARGET_BUILTIN_RECIPROCAL rs6000_builtin_reciprocal
@@ -5882,6 +5885,75 @@
   return rs6000_got_symbol;
 }

+/* AIX Thread-Local Address support.  */
+
+static rtx
+rs6000_legitimize_tls_address_aix (rtx addr, enum tls_model model)
+{
+  rtx sym, mem, tocref, tlsreg, tmpreg, dest;
+
+  /* Place addr into TOC constant pool.  */
+  sym = force_const_mem (GET_MODE (addr), addr);
+
+  /* Output the TOC entry and create the MEM referencing the value.  */
+  if (constant_pool_expr_p (XEXP (sym, 0))
+      && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (XEXP
(sym, 0)), Pmode))
+    {
+      tocref = create_TOC_reference (XEXP (sym, 0), NULL_RTX);
+      mem = gen_const_mem (Pmode, tocref);
+      set_mem_alias_set (mem, get_TOC_alias_set ());
+    }
+  else
+    return sym;
+
+  /* Use global-dynamic for local-dynamic.  */
+  if (model == TLS_MODEL_GLOBAL_DYNAMIC
+      || model == TLS_MODEL_LOCAL_DYNAMIC)
+    {
+      rtx module = gen_reg_rtx (Pmode);
+      /* Create new TOC reference for @m symbol.  */
+      const char *name = XSTR (XVECEXP (XEXP (mem, 0), 0, 0), 0);
+      char *name2 = XALLOCAVEC (char, strlen (name) + 1);
+      strcpy (name2, "*LCM");
+      strcat (name2, name + 3);
+      tocref = create_TOC_reference (gen_rtx_SYMBOL_REF (Pmode,
+							 ggc_alloc_string (name2,
+									   strlen (name2))),
+				     NULL_RTX);
+      rtx mem2 = gen_const_mem (Pmode, tocref);
+      set_mem_alias_set (mem2, get_TOC_alias_set ());
+
+      dest = gen_reg_rtx (Pmode);
+      tmpreg = gen_reg_rtx (Pmode);
+      emit_insn (gen_rtx_SET (VOIDmode, tmpreg, mem));
+      emit_insn (gen_rtx_SET (VOIDmode, module, mem2));
+      if (TARGET_32BIT)
+	emit_insn (gen_tls_get_addrsi (dest, module, tmpreg));
+      else
+	emit_insn (gen_tls_get_addrdi (dest, module, tmpreg));
+      return dest;
+    }
+  /* Obtain TLS pointer: 32 bit call or 64 bit GPR 13.  */
+  else if (TARGET_32BIT)
+    {
+      tlsreg = gen_reg_rtx (SImode);
+      emit_insn (gen_tls_get_tpointer (tlsreg));
+    }
+  else
+    tlsreg = gen_rtx_REG (DImode, 13);
+
+  /* Load the TOC value into temporary register.  */
+  tmpreg = gen_reg_rtx (Pmode);
+  emit_insn (gen_rtx_SET (VOIDmode, tmpreg, mem));
+  set_unique_reg_note (get_last_insn (), REG_EQUAL,
+		       gen_rtx_MINUS (Pmode, addr, tlsreg));
+
+  /* Add TOC symbol value to TLS pointer.  */
+  dest = force_reg (Pmode, gen_rtx_PLUS (Pmode, tmpreg, tlsreg));
+
+  return dest;
+}
+
 /* ADDR contains a thread-local SYMBOL_REF.  Generate code to compute
    this (thread-local) address.  */

@@ -5890,6 +5962,9 @@
 {
   rtx dest, insn;

+  if (TARGET_XCOFF)
+    return rs6000_legitimize_tls_address_aix (addr, model);
+
   dest = gen_reg_rtx (Pmode);
   if (model == TLS_MODEL_LOCAL_EXEC && rs6000_tls_size == 16)
     {
@@ -6085,7 +6160,15 @@
       && GET_CODE (XEXP (x, 0)) == UNSPEC)
     return true;

-  return rs6000_tls_referenced_p (x);
+  /* A TLS symbol in the TOC cannot contain a sum.  */
+  if (GET_CODE (x) == CONST
+      && GET_CODE (XEXP (x, 0)) == PLUS
+      && GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF
+      && SYMBOL_REF_TLS_MODEL (XEXP (XEXP (x, 0), 0)) != 0)
+    return true;
+
+  /* Do not place an ELF TLS symbol in the constant pool.  */
+  return TARGET_ELF && rs6000_tls_referenced_p (x);
 }

 /* Return 1 if *X is a thread-local symbol.  This is the same as
@@ -6380,7 +6463,7 @@
       && INTVAL (XEXP (x, 1)) == -16)
     x = XEXP (x, 0);

-  if (RS6000_SYMBOL_REF_TLS_P (x))
+  if (TARGET_ELF && RS6000_SYMBOL_REF_TLS_P (x))
     return 0;
   if (legitimate_indirect_address_p (x, reg_ok_strict))
     return 1;
@@ -15486,6 +15569,9 @@
 static void
 rs6000_assemble_visibility (tree decl, int vis)
 {
+  if (TARGET_XCOFF)
+    return;
+
   /* Functions need to have their entry point symbol visibility set as
      well as their descriptor symbol visibility.  */
   if (DEFAULT_ABI == ABI_AIX
@@ -21934,6 +22020,20 @@
 	  ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "LC");
 	  fprintf (file, "%d\n", ((*(const struct toc_hash_struct **)
 					      found)->labelno));
+
+#ifdef HAVE_AS_TLS
+	  if (TARGET_XCOFF && GET_CODE (x) == SYMBOL_REF
+	      && (SYMBOL_REF_TLS_MODEL (x) == TLS_MODEL_GLOBAL_DYNAMIC
+		  || SYMBOL_REF_TLS_MODEL (x) == TLS_MODEL_LOCAL_DYNAMIC))
+	    {
+	      fputs ("\t.set ", file);
+	      ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "LCM");
+	      fprintf (file, "%d,", labelno);
+	      ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "LCM");
+	      fprintf (file, "%d\n", ((*(const struct toc_hash_struct **)
+			       			      found)->labelno));
+	    }
+#endif
 	  return;
 	}
     }
@@ -22206,6 +22306,29 @@
     }
   else
     output_addr_const (file, x);
+
+#if HAVE_AS_TLS
+  if (TARGET_XCOFF && GET_CODE (base) == SYMBOL_REF)
+    {
+      if (SYMBOL_REF_TLS_MODEL (base) == TLS_MODEL_LOCAL_EXEC)
+	fputs ("[TL]@le", file);
+      else if (SYMBOL_REF_TLS_MODEL (base) == TLS_MODEL_INITIAL_EXEC)
+	fputs ("[TL]@ie", file);
+      /* Use global-dynamic for local-dynamic.  */
+      else if (SYMBOL_REF_TLS_MODEL (base) == TLS_MODEL_GLOBAL_DYNAMIC
+	       || SYMBOL_REF_TLS_MODEL (base) == TLS_MODEL_LOCAL_DYNAMIC)
+	{
+	  fputs ("[TL]\n", file);
+	  (*targetm.asm_out.internal_label) (file, "LCM", labelno);
+	  fputs ("\t.tc .", file);
+	  RS6000_OUTPUT_BASENAME (file, name);
+	  fputs ("[TC],", file);
+	  output_addr_const (file, x);
+	  fputs ("[TL]@m", file);
+	}
+    }
+#endif
+
   putc ('\n', file);
 }
 

@@ -24884,6 +25007,14 @@
 {
   return !ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (x, mode);
 }
+
+/* Do not place thread-local symbols refs in the object blocks.  */
+
+static bool
+rs6000_use_blocks_for_decl_p (const_tree decl)
+{
+  return !DECL_THREAD_LOCAL_P (decl);
+}
 

 /* Return a REG that occurs in ADDR with coefficient 1.
    ADDR can be effectively incremented by incrementing REG.
@@ -25516,6 +25647,14 @@
 	   XCOFF_CSECT_DEFAULT_ALIGNMENT_STR);
 }

+static void
+rs6000_xcoff_output_tls_section_asm_op (const void *directive)
+{
+  fprintf (asm_out_file, "\t.csect %s[TL],%s\n",
+	   *(const char *const *) directive,
+	   XCOFF_CSECT_DEFAULT_ALIGNMENT_STR);
+}
+
 /* A get_unnamed_section callback, used for switching to toc_section.  */

 static void
@@ -25553,6 +25692,11 @@
 			   rs6000_xcoff_output_readwrite_section_asm_op,
 			   &xcoff_private_data_section_name);

+  tls_data_section
+    = get_unnamed_section (SECTION_TLS,
+			   rs6000_xcoff_output_tls_section_asm_op,
+			   &xcoff_tls_data_section_name);
+
   read_only_private_data_section
     = get_unnamed_section (0, rs6000_xcoff_output_readonly_section_asm_op,
 			   &xcoff_private_data_section_name);
@@ -25604,7 +25748,12 @@
     }
   else
     {
-      if (TREE_PUBLIC (decl))
+#if HAVE_AS_TLS
+      if (TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL_P (decl))
+	return tls_data_section;
+      else
+#endif
+	if (TREE_PUBLIC (decl))
 	return data_section;
       else
 	return private_data_section;
@@ -25700,6 +25849,8 @@
 			   main_input_filename, ".bss_");
   rs6000_gen_section_name (&xcoff_private_data_section_name,
 			   main_input_filename, ".rw_");
+  rs6000_gen_section_name (&xcoff_tls_data_section_name,
+			   main_input_filename, ".tls_");
   rs6000_gen_section_name (&xcoff_read_only_section_name,
 			   main_input_filename, ".ro_");

@@ -28164,7 +28315,7 @@
 static bool
 rs6000_legitimate_constant_p (enum machine_mode mode, rtx x)
 {
-  if (rs6000_tls_referenced_p (x))
+  if (TARGET_ELF && rs6000_tls_referenced_p (x))
     return false;

   return ((GET_CODE (x) != CONST_DOUBLE && GET_CODE (x) != CONST_VECTOR)
Index: config/rs6000/rs6000.md
===================================================================
--- config/rs6000/rs6000.md	(revision 193917)
+++ config/rs6000/rs6000.md	(working copy)
@@ -9983,8 +9983,51 @@
 	(unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b")
 			 (match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")]
 			UNSPEC_TLSTLS))]
-  "HAVE_AS_TLS"
+  "TARGET_ELF && HAVE_AS_TLS"
   "add %0,%1,%2@tls")
+
+(define_expand "tls_get_tpointer"
+  [(set (match_operand:SI 0 "gpc_reg_operand" "")
+	(unspec:SI [(const_int 0)] UNSPEC_TLSTLS))]
+  "TARGET_XCOFF && HAVE_AS_TLS"
+  "
+{
+  emit_insn (gen_tls_get_tpointer_internal ());
+  emit_move_insn (operands[0], gen_rtx_REG (SImode, 3));
+  DONE;
+}")
+
+(define_insn "tls_get_tpointer_internal"
+  [(set (reg:SI 3)
+	(unspec:SI [(const_int 0)] UNSPEC_TLSTLS))
+   (clobber (reg:SI LR_REGNO))]
+  "TARGET_XCOFF && HAVE_AS_TLS"
+  "bla __get_tpointer")
+
+(define_expand "tls_get_addr<mode>"
+  [(set (match_operand:P 0 "gpc_reg_operand" "")
+	(unspec:P [(match_operand:P 1 "gpc_reg_operand" "")
+                   (match_operand:P 2 "gpc_reg_operand" "")] UNSPEC_TLSTLS))]
+  "TARGET_XCOFF && HAVE_AS_TLS"
+  "
+{
+  emit_move_insn (gen_rtx_REG (Pmode, 3), operands[1]);
+  emit_move_insn (gen_rtx_REG (Pmode, 4), operands[2]);
+  emit_insn (gen_tls_get_addr_internal<mode> ());
+  emit_move_insn (operands[0], gen_rtx_REG (Pmode, 3));
+  DONE;
+}")
+
+(define_insn "tls_get_addr_internal<mode>"
+  [(set (reg:P 3)
+	(unspec:P [(reg:P 3) (reg:P 4)] UNSPEC_TLSTLS))
+   (clobber (reg:P 0))
+   (clobber (reg:P 5))
+   (clobber (reg:P 11))
+   (clobber (reg:CC CR0_REGNO))
+   (clobber (reg:P LR_REGNO))]
+  "TARGET_XCOFF && HAVE_AS_TLS"
+  "bla __tls_get_addr")
 

 ;; Next come insns related to the calling sequence.
 ;;



More information about the Gcc-patches mailing list