--- /dev/null
+--- gcc/cp/lex.c.jj Sat May 25 00:02:23 2002
++++ gcc/cp/lex.c Wed Jun 19 19:33:51 2002
+@@ -396,6 +396,7 @@ static const struct resword reswords[] =
+ { "__restrict__", RID_RESTRICT, 0 },
+ { "__signed", RID_SIGNED, 0 },
+ { "__signed__", RID_SIGNED, 0 },
++ { "__thread", RID_THREAD, 0 },
+ { "__typeof", RID_TYPEOF, 0 },
+ { "__typeof__", RID_TYPEOF, 0 },
+ { "__volatile", RID_VOLATILE, 0 },
+@@ -502,6 +503,7 @@ const short rid_to_yy[RID_MAX] =
+ /* RID_BOUNDED */ 0,
+ /* RID_UNBOUNDED */ 0,
+ /* RID_COMPLEX */ TYPESPEC,
++ /* RID_THREAD */ SCSPEC,
+
+ /* C++ */
+ /* RID_FRIEND */ SCSPEC,
+--- gcc/cp/decl.c.jj Wed Jun 19 19:33:51 2002
++++ gcc/cp/decl.c Wed Jun 19 19:33:51 2002
+@@ -7122,7 +7122,8 @@ check_tag_decl (declspecs)
+ || value == ridpointers[(int) RID_VIRTUAL]
+ || value == ridpointers[(int) RID_CONST]
+ || value == ridpointers[(int) RID_VOLATILE]
+- || value == ridpointers[(int) RID_EXPLICIT])
++ || value == ridpointers[(int) RID_EXPLICIT]
++ || value == ridpointers[(int) RID_THREAD])
+ ob_modifier = value;
+ }
+
+@@ -7596,6 +7597,12 @@ static tree
+ obscure_complex_init (decl, init)
+ tree decl, init;
+ {
++ if (TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL (decl))
++ {
++ error ("run-time initialization of thread-local storage");
++ return NULL_TREE;
++ }
++
+ if (! flag_no_inline && TREE_STATIC (decl))
+ {
+ if (extract_init (decl, init))
+@@ -9290,6 +9297,16 @@ grokvardecl (type, declarator, specbits_
+ TREE_PUBLIC (decl) = DECL_EXTERNAL (decl);
+ }
+
++ if (RIDBIT_SETP (RID_THREAD, specbits))
++ {
++ if (targetm.have_tls)
++ DECL_THREAD_LOCAL (decl) = 1;
++ else
++ /* A mere warning is sure to result in improper semantics
++ at runtime. Don't bother to allow this to compile. */
++ error ("thread-local storage not supported for this target");
++ }
++
+ if (TREE_PUBLIC (decl))
+ {
+ /* [basic.link]: A name with no linkage (notably, the name of a class
+@@ -10192,10 +10209,22 @@ grokdeclarator (declarator, declspecs, d
+ }
+ else if (RIDBIT_SETP (i, specbits))
+ pedwarn ("duplicate `%s'", IDENTIFIER_POINTER (id));
++
++ /* Diagnose "__thread extern". Recall that this list
++ is in the reverse order seen in the text. */
++ if (i == (int)RID_THREAD)
++ {
++ if (RIDBIT_SETP (RID_EXTERN, specbits))
++ error ("`__thread' before `extern'");
++ if (RIDBIT_SETP (RID_STATIC, specbits))
++ error ("`__thread' before `static'");
++ }
++
+ if (i == (int)RID_EXTERN
+ && TREE_PURPOSE (spec) == error_mark_node)
+ /* This extern was part of a language linkage. */
+ extern_langp = 1;
++
+ RIDBIT_SET (i, specbits);
+ goto found;
+ }
+@@ -10492,6 +10521,7 @@ grokdeclarator (declarator, declspecs, d
+ {
+ if (RIDBIT_SETP (RID_STATIC, specbits)) nclasses++;
+ if (RIDBIT_SETP (RID_EXTERN, specbits) && !extern_langp) nclasses++;
++ if (RIDBIT_SETP (RID_THREAD, specbits)) nclasses++;
+ if (decl_context == PARM && nclasses > 0)
+ error ("storage class specifiers invalid in parameter declarations");
+ if (RIDBIT_SETP (RID_TYPEDEF, specbits))
+@@ -10523,6 +10553,13 @@ grokdeclarator (declarator, declspecs, d
+ /* Warn about storage classes that are invalid for certain
+ kinds of declarations (parameters, typenames, etc.). */
+
++ /* "static __thread" and "extern __thread" are allowed. */
++ if (nclasses == 2
++ && RIDBIT_SETP (RID_THREAD, specbits)
++ && (RIDBIT_SETP (RID_EXTERN, specbits)
++ || RIDBIT_SETP (RID_STATIC, specbits)))
++ nclasses = 1;
++
+ if (nclasses > 1)
+ error ("multiple storage classes in declaration of `%s'", name);
+ else if (decl_context != NORMAL && nclasses > 0)
+@@ -10578,6 +10615,7 @@ grokdeclarator (declarator, declspecs, d
+ RIDBIT_RESET (RID_REGISTER, specbits);
+ RIDBIT_RESET (RID_AUTO, specbits);
+ RIDBIT_RESET (RID_EXTERN, specbits);
++ RIDBIT_RESET (RID_THREAD, specbits);
+ }
+ }
+ else if (RIDBIT_SETP (RID_EXTERN, specbits) && initialized && !funcdef_flag)
+@@ -10600,6 +10638,14 @@ grokdeclarator (declarator, declspecs, d
+ if (RIDBIT_SETP (RID_AUTO, specbits))
+ error ("top-level declaration of `%s' specifies `auto'", name);
+ }
++ else if (RIDBIT_SETP (RID_THREAD, specbits)
++ && !RIDBIT_SETP (RID_EXTERN, specbits)
++ && !RIDBIT_SETP (RID_STATIC, specbits))
++ {
++ error ("function-scope `%s' implicitly auto and declared `__thread'",
++ name);
++ RIDBIT_RESET (RID_THREAD, specbits);
++ }
+
+ if (nclasses > 0 && friendp)
+ error ("storage class specifiers invalid in friend function declarations");
+@@ -11800,6 +11846,8 @@ friend declaration requires class-key, i
+ error ("storage class `auto' invalid for function `%s'", name);
+ else if (RIDBIT_SETP (RID_REGISTER, specbits))
+ error ("storage class `register' invalid for function `%s'", name);
++ else if (RIDBIT_SETP (RID_THREAD, specbits))
++ error ("storage class `__thread' invalid for function `%s'", name);
+
+ /* Function declaration not at top level.
+ Storage classes other than `extern' are not allowed
+--- gcc/doc/extend.texi.jj Wed Jun 19 19:33:51 2002
++++ gcc/doc/extend.texi Wed Jun 19 19:33:51 2002
+@@ -432,6 +432,7 @@ extensions, accepted by GCC in C89 mode
+ * Target Builtins:: Built-in functions specific to particular targets.
+ * Pragmas:: Pragmas accepted by GCC.
+ * Unnamed Fields:: Unnamed struct/union fields within structs/unions.
++* Thread-Local:: Per-thread variables.
+ @end menu
+
+ @node Statement Exprs
+@@ -6119,6 +6120,265 @@ It is ambiguous which @code{a} is being
+ Such constructs are not supported and must be avoided. In the future,
+ such constructs may be detected and treated as compilation errors.
+
++@node Thread-Local
++@section Thread-Local Storage
++@cindex Thread-Local Storage
++@cindex TLS
++@cindex __thread
++
++Thread-local storage (@acronym{TLS}) is a mechanism by which variables
++are allocated such that there is one instance of the variable per extant
++thread. The run-time model GCC uses to implement this originates
++in the IA-64 processor-specific ABI, but has since been migrated
++to other processors as well. It requires significant support from
++the linker (@command{ld}), dynamic linker (@command{ld.so}), and
++system libraries (@file{libc.so} and @file{libpthread.so}), so it
++is not available everywhere.
++
++At the user level, the extension is visible with a new storage
++class keyword: @code{__thread}. For example:
++
++@example
++__thread int i;
++extern __thread struct state s;
++static __thread char *p;
++@end example
++
++The @code{__thread} specifier may be used alone, with the @code{extern}
++or @code{static} specifiers, but with no other storage class specifier.
++When used with @code{extern} or @code{static}, @code{__thread} must appear
++immediately after the other storage class specifier.
++
++The @code{__thread} specifier may be applied to any global, file-scoped
++static, function-scoped static, or static data member of a class. It may
++not be applied to block-scoped automatic or non-static data member.
++
++When the address-of operator is applied to a thread-local variable, it is
++evaluated at run-time and returns the address of the current thread's
++instance of that variable. An address so obtained may be used by any
++thread. When a thread terminates, any pointers to thread-local variables
++in that thread become invalid.
++
++No static initialization may refer to the address of a thread-local variable.
++
++In C++, if an initializer is present for a thread-local variable, it must
++be a @var{constant-expression}, as defined in 5.19.2 of the ANSI/ISO C++
++standard.
++
++See @uref{http://people.redhat.com/drepper/tls.pdf,
++ELF Handling For Thread-Local Storage} for a detailed explanation of
++the four thread-local storage addressing models, and how the run-time
++is expected to function.
++
++@menu
++* C99 Thread-Local Edits::
++* C++98 Thread-Local Edits::
++@end menu
++
++@node C99 Thread-Local Edits
++@subsection ISO/IEC 9899:1999 Edits for Thread-Local Storage
++
++The following are a set of changes to ISO/IEC 9899:1999 (aka C99)
++that document the exact semantics of the language extension.
++
++@itemize @bullet
++@item
++@cite{5.1.2 Execution environments}
++
++Add new text after paragraph 1
++
++@quotation
++Within either execution environment, a @dfn{thread} is a flow of
++control within a program. It is implementation defined whether
++or not there may be more than one thread associated with a program.
++It is implementation defined how threads beyond the first are
++created, the name and type of the function called at thread
++startup, and how threads may be terminated. However, objects
++with thread storage duration shall be initialized before thread
++startup.
++@end quotation
++
++@item
++@cite{6.2.4 Storage durations of objects}
++
++Add new text before paragraph 3
++
++@quotation
++An object whose identifier is declared with the storage-class
++specifier @w{@code{__thread}} has @dfn{thread storage duration}.
++Its lifetime is the entire execution of the thread, and its
++stored value is initialized only once, prior to thread startup.
++@end quotation
++
++@item
++@cite{6.4.1 Keywords}
++
++Add @code{__thread}.
++
++@item
++@cite{6.7.1 Storage-class specifiers}
++
++Add @code{__thread} to the list of storage class specifiers in
++paragraph 1.
++
++Change paragraph 2 to
++
++@quotation
++With the exception of @code{__thread}, at most one storage-class
++specifier may be given [@dots{}]. The @code{__thread} specifier may
++be used alone, or immediately following @code{extern} or
++@code{static}.
++@end quotation
++
++Add new text after paragraph 6
++
++@quotation
++The declaration of an identifier for a variable that has
++block scope that specifies @code{__thread} shall also
++specify either @code{extern} or @code{static}.
++
++The @code{__thread} specifier shall be used only with
++variables.
++@end quotation
++@end itemize
++
++@node C++98 Thread-Local Edits
++@subsection ISO/IEC 14882:1998 Edits for Thread-Local Storage
++
++The following are a set of changes to ISO/IEC 14882:1998 (aka C++98)
++that document the exact semantics of the language extension.
++
++@itemize @bullet
++@b{[intro.execution]}
++
++New text after paragraph 4
++
++@quotation
++A @dfn{thread} is a flow of control within the abstract machine.
++It is implementation defined whether or not there may be more than
++one thread.
++@end quotation
++
++New text after paragraph 7
++
++@quotation
++It is unspecified whether additional action must be taken to
++ensure when and whether side effects are visible to other threads.
++@end quotation
++
++@item
++@b{[lex.key]}
++
++Add @code{__thread}.
++
++@item
++@b{[basic.start.main]}
++
++Add after paragraph 5
++
++@quotation
++The thread that begins execution at the @code{main} function is called
++the @dfn{main thread}. It is implementation defined how functions
++beginning threads other than the main thread are designated or typed.
++A function so designated, as well as the @code{main} function, is called
++a @dfn{thread startup function}. It is implementation defined what
++happens if a thread startup function returns. It is implementation
++defined what happens to other threads when any thread calls @code{exit}.
++@end quotation
++
++@item
++@b{[basic.start.init]}
++
++Add after paragraph 4
++
++@quotation
++The storage for an object of thread storage duration shall be
++staticly initialized before the first statement of the thread startup
++function. An object of thread storage duration shall not require
++dynamic initialization.
++@end quotation
++
++@item
++@b{[basic.start.term]}
++
++Add after paragraph 3
++
++@quotation
++The type of an object with thread storage duration shall not have a
++non-trivial destructor, nor shall it be an array type whose elements
++(directly or indirectly) have non-trivial destructors.
++@end quotation
++
++@item
++@b{[basic.stc]}
++
++Add ``thread storage duration'' to the list in paragraph 1.
++
++Change paragraph 2
++
++@quotation
++Thread, static, and automatic storage durations are associated with
++objects introduced by declarations [@dots{}].
++@end quotation
++
++Add @code{__thread} to the list of specifiers in paragraph 3.
++
++@item
++@b{[basic.stc.thread]}
++
++New section before @b{[basic.stc.static]}
++
++@quotation
++The keyword @code{__thread} applied to an non-local object gives the
++object thread storage duration.
++
++A local variable or class data member declared both @code{static}
++and @code{__thread} gives the variable or member thread storage
++duration.
++@end quotation
++
++@item
++@b{[basic.stc.static]}
++
++Change paragraph 1
++
++@quotation
++All objects which have neither thread storage duration, dynamic
++storage duration nor are local [@dots{}].
++@end quotation
++
++@item
++@b{[dcl.stc]}
++
++Add @code{__thread} to the list in paragraph 1.
++
++Change paragraph 1
++
++@quotation
++With the exception of @code{__thread}, at most one
++@var{storage-class-specifier} shall appear in a given
++@var{decl-specifier-seq}. The @code{__thread} specifier may
++be used alone, or immediately following the @code{extern} or
++@code{static} specifiers. [@dots{}]
++@end quotation
++
++Add after paragraph 5
++
++@quotation
++The @code{__thread} specifier can be applied only to the names of objects
++and to anonymous unions.
++@end quotation
++
++@item
++@b{[class.mem]}
++
++Add after paragraph 6
++
++@quotation
++Non-@code{static} members shall not be @code{__thread}.
++@end quotation
++@end itemize
++
+ @node C++ Extensions
+ @chapter Extensions to the C++ Language
+ @cindex extensions, C++ language
+--- gcc/doc/invoke.texi.jj Tue May 21 20:27:44 2002
++++ gcc/doc/invoke.texi Wed Jun 19 19:33:52 2002
+@@ -674,7 +674,7 @@ in the following sections.
+ -fverbose-asm -fpack-struct -fstack-check @gol
+ -fstack-limit-register=@var{reg} -fstack-limit-symbol=@var{sym} @gol
+ -fargument-alias -fargument-noalias @gol
+--fargument-noalias-global -fleading-underscore}
++-fargument-noalias-global -fleading-underscore -ftls-model=@var{model}}
+ @end table
+
+ @menu
+@@ -9910,6 +9910,14 @@ is to help link with legacy assembly cod
+
+ Be warned that you should know what you are doing when invoking this
+ option, and that not all targets provide complete support for it.
++
++@item -ftls-model=@var{model}
++Alter the thread-local storage model to be used (@pxref{Thread-Local}).
++The @var{model} argument should be one of @code{global-dynamic},
++@code{local-dynamic}, @code{initial-exec} or @code{local-exec}.
++
++The default without @option{-fpic} is @code{initial-exec}; with
++@option{-fpic} the default is @code{global-dynamic}.
+ @end table
+
+ @c man end
+--- gcc/config/i386/i386.c.jj Wed Jun 19 19:33:51 2002
++++ gcc/config/i386/i386.c Wed Jun 19 23:18:18 2002
+@@ -536,6 +536,10 @@ int const svr4_dbx_register_map[FIRST_PS
+ rtx ix86_compare_op0 = NULL_RTX;
+ rtx ix86_compare_op1 = NULL_RTX;
+
++/* The encoding characters for the four TLS models present in ELF. */
++
++static char const tls_model_chars[] = " GLil";
++
+ #define MAX_386_STACK_LOCALS 3
+ /* Size of the register save area. */
+ #define X86_64_VARARGS_SIZE (REGPARM_MAX * UNITS_PER_WORD + SSE_REGPARM_MAX * 16)
+@@ -544,6 +548,7 @@ rtx ix86_compare_op1 = NULL_RTX;
+ struct machine_function
+ {
+ rtx stack_locals[(int) MAX_MACHINE_MODE][MAX_386_STACK_LOCALS];
++ const char *some_ld_name;
+ int save_varrargs_registers;
+ int accesses_prev_frame;
+ };
+@@ -596,6 +601,9 @@ enum cmodel ix86_cmodel;
+ /* Asm dialect. */
+ const char *ix86_asm_string;
+ enum asm_dialect ix86_asm_dialect = ASM_ATT;
++/* TLS dialext. */
++const char *ix86_tls_dialect_string;
++enum tls_dialect ix86_tls_dialect = TLS_DIALECT_GNU;
+
+ /* which cpu are we scheduling for */
+ enum processor_type ix86_cpu;
+@@ -646,12 +654,17 @@ static char internal_label_prefix[16];
+ static int internal_label_prefix_len;
+ \f
+ static int local_symbolic_operand PARAMS ((rtx, enum machine_mode));
++static int tls_symbolic_operand_1 PARAMS ((rtx, enum tls_model));
+ static void output_pic_addr_const PARAMS ((FILE *, rtx, int));
+ static void put_condition_code PARAMS ((enum rtx_code, enum machine_mode,
+ int, int, FILE *));
++static const char *get_some_local_dynamic_name PARAMS ((void));
++static int get_some_local_dynamic_name_1 PARAMS ((rtx *, void *));
++static rtx maybe_get_pool_constant PARAMS ((rtx));
+ static rtx ix86_expand_int_compare PARAMS ((enum rtx_code, rtx, rtx));
+ static enum rtx_code ix86_prepare_fp_compare_args PARAMS ((enum rtx_code,
+ rtx *, rtx *));
++static rtx get_thread_pointer PARAMS ((void));
+ static rtx gen_push PARAMS ((rtx));
+ static int memory_address_length PARAMS ((rtx addr));
+ static int ix86_flags_dependant PARAMS ((rtx, rtx, enum attr_type));
+@@ -820,6 +833,11 @@ static enum x86_64_reg_class merge_class
+ #undef TARGET_SCHED_REORDER
+ #define TARGET_SCHED_REORDER ix86_sched_reorder
+
++#ifdef HAVE_AS_TLS
++#undef TARGET_HAVE_TLS
++#define TARGET_HAVE_TLS true
++#endif
++
+ struct gcc_target targetm = TARGET_INITIALIZER;
+ \f
+ /* Sometimes certain combinations of command options do not make
+@@ -1109,6 +1127,17 @@ override_options ()
+ ix86_branch_cost = i;
+ }
+
++ if (ix86_tls_dialect_string)
++ {
++ if (strcmp (ix86_tls_dialect_string, "gnu") == 0)
++ ix86_tls_dialect = TLS_DIALECT_GNU;
++ else if (strcmp (ix86_tls_dialect_string, "sun") == 0)
++ ix86_tls_dialect = TLS_DIALECT_SUN;
++ else
++ error ("bad value (%s) for -mtls-dialect= switch",
++ ix86_tls_dialect_string);
++ }
++
+ /* Keep nonleaf frame pointers. */
+ if (TARGET_OMIT_LEAF_FRAME_POINTER)
+ flag_omit_frame_pointer = 1;
+@@ -3007,6 +3036,70 @@ local_symbolic_operand (op, mode)
+ return 0;
+ }
+
++/* Test for various thread-local symbols. See ix86_encode_section_info. */
++
++int
++tls_symbolic_operand (op, mode)
++ register rtx op;
++ enum machine_mode mode ATTRIBUTE_UNUSED;
++{
++ const char *symbol_str;
++
++ if (GET_CODE (op) != SYMBOL_REF)
++ return 0;
++ symbol_str = XSTR (op, 0);
++
++ if (symbol_str[0] != '%')
++ return 0;
++ return strchr (tls_model_chars, symbol_str[1]) - tls_model_chars;
++}
++
++static int
++tls_symbolic_operand_1 (op, kind)
++ rtx op;
++ enum tls_model kind;
++{
++ const char *symbol_str;
++
++ if (GET_CODE (op) != SYMBOL_REF)
++ return 0;
++ symbol_str = XSTR (op, 0);
++
++ return symbol_str[0] == '%' && symbol_str[1] == tls_model_chars[kind];
++}
++
++int
++global_dynamic_symbolic_operand (op, mode)
++ register rtx op;
++ enum machine_mode mode ATTRIBUTE_UNUSED;
++{
++ return tls_symbolic_operand_1 (op, TLS_MODEL_GLOBAL_DYNAMIC);
++}
++
++int
++local_dynamic_symbolic_operand (op, mode)
++ register rtx op;
++ enum machine_mode mode ATTRIBUTE_UNUSED;
++{
++ return tls_symbolic_operand_1 (op, TLS_MODEL_LOCAL_DYNAMIC);
++}
++
++int
++initial_exec_symbolic_operand (op, mode)
++ register rtx op;
++ enum machine_mode mode ATTRIBUTE_UNUSED;
++{
++ return tls_symbolic_operand_1 (op, TLS_MODEL_INITIAL_EXEC);
++}
++
++int
++local_exec_symbolic_operand (op, mode)
++ register rtx op;
++ enum machine_mode mode ATTRIBUTE_UNUSED;
++{
++ return tls_symbolic_operand_1 (op, TLS_MODEL_LOCAL_EXEC);
++}
++
+ /* Test for a valid operand for a call instruction. Don't allow the
+ arg pointer register or virtual regs since they may decay into
+ reg + const, which the patterns can't handle. */
+@@ -3858,7 +3951,7 @@ ix86_asm_file_end (file)
+ {
+ rtx xops[2];
+
+- if (! TARGET_DEEP_BRANCH_PREDICTION || pic_label_name[0] == 0)
++ if (pic_label_name[0] == 0)
+ return;
+
+ /* The trick here is to create a linkonce section containing the
+@@ -3896,17 +3989,33 @@ ix86_asm_file_end (file)
+ output_asm_insn ("ret", xops);
+ }
+
+-void
+-load_pic_register ()
++/* Emit code for the SET_GOT patterns. */
++
++const char *
++output_set_got (dest)
++ rtx dest;
+ {
+- rtx gotsym, pclab;
++ rtx xops[3];
+
+- if (TARGET_64BIT)
+- abort ();
++ xops[0] = dest;
++ xops[1] = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
++
++ if (! TARGET_DEEP_BRANCH_PREDICTION || !flag_pic)
++ {
++ xops[2] = gen_rtx_LABEL_REF (Pmode, gen_label_rtx ());
++
++ if (!flag_pic)
++ output_asm_insn ("mov{l}\t{%2, %0|%0, %2}", xops);
++ else
++ output_asm_insn ("call\t%a2", xops);
+
+- gotsym = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
++ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
++ CODE_LABEL_NUMBER (XEXP (xops[2], 0)));
+
+- if (TARGET_DEEP_BRANCH_PREDICTION)
++ if (flag_pic)
++ output_asm_insn ("pop{l}\t%0", xops);
++ }
++ else
+ {
+ if (! pic_label_name[0])
+ {
+@@ -3915,16 +4024,17 @@ load_pic_register ()
+ else
+ ASM_GENERATE_INTERNAL_LABEL (pic_label_name, "LPR", 0);
+ }
+- pclab = gen_rtx_MEM (QImode, gen_rtx_SYMBOL_REF (Pmode, pic_label_name));
+- }
++ xops[2] = gen_rtx_SYMBOL_REF (Pmode, pic_label_name);
++ xops[2] = gen_rtx_MEM (QImode, xops[2]);
++ output_asm_insn ("call\t%X2", xops);
++ }
++
++ if (!flag_pic || TARGET_DEEP_BRANCH_PREDICTION)
++ output_asm_insn ("add{l}\t{%1, %0|%0, %1}", xops);
+ else
+- {
+- pclab = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ());
+- }
+-
+- emit_insn (gen_prologue_get_pc (pic_offset_table_rtx, pclab));
+-
+- emit_insn (gen_prologue_set_got (pic_offset_table_rtx, gotsym, pclab));
++ output_asm_insn ("add{l}\t{%1+[.-%a2], %0|%0, %a1+(.-%a2)}", xops);
++
++ return "";
+ }
+
+ /* Generate an "push" pattern for input ARG. */
+@@ -4253,7 +4363,15 @@ ix86_expand_prologue ()
+ #endif
+
+ if (pic_reg_used)
+- load_pic_register ();
++ {
++ insn = emit_insn (gen_set_got (pic_offset_table_rtx));
++
++ /* ??? The current_function_uses_pic_offset_table flag is woefully
++ inaccurate, as it isn't updated as code gets deleted. Allow the
++ thing to be removed. A better solution would be to actually get
++ proper liveness for ebx, as then we won't save/restore it too. */
++ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx, NULL);
++ }
+
+ /* If we are profiling, make sure no instructions are scheduled before
+ the call to mcount. However, if -fpic, the above call will have
+@@ -4694,6 +4812,114 @@ ix86_find_base_term (x)
+ return term;
+ }
+ \f
++/* Determine if a given RTX is a valid constant. We already know this
++ satisfies CONSTANT_P. */
++
++bool
++legitimate_constant_p (x)
++ rtx x;
++{
++ rtx inner;
++
++ switch (GET_CODE (x))
++ {
++ case SYMBOL_REF:
++ /* TLS symbols are not constant. */
++ if (tls_symbolic_operand (x, Pmode))
++ return false;
++ break;
++
++ case CONST:
++ inner = XEXP (x, 0);
++
++ /* Offsets of TLS symbols are never valid.
++ Discourage CSE from creating them. */
++ if (GET_CODE (inner) == PLUS
++ && tls_symbolic_operand (XEXP (inner, 0), Pmode))
++ return false;
++
++ /* Only some unspecs are valid as "constants". */
++ if (GET_CODE (inner) == UNSPEC)
++ switch (XINT (inner, 1))
++ {
++ case UNSPEC_TPOFF:
++ return local_exec_symbolic_operand (XVECEXP (inner, 0, 0), Pmode);
++ case UNSPEC_TP:
++ return true;
++ default:
++ return false;
++ }
++ break;
++
++ default:
++ break;
++ }
++
++ /* Otherwise we handle everything else in the move patterns. */
++ return true;
++}
++
++/* Determine if a given RTX is a valid constant address. */
++
++bool
++constant_address_p (x)
++ rtx x;
++{
++ switch (GET_CODE (x))
++ {
++ case LABEL_REF:
++ case CONST_INT:
++ return true;
++
++ case CONST_DOUBLE:
++ return TARGET_64BIT;
++
++ case CONST:
++ case SYMBOL_REF:
++ return !flag_pic && legitimate_constant_p (x);
++
++ default:
++ return false;
++ }
++}
++
++/* Nonzero if the constant value X is a legitimate general operand
++ when generating PIC code. It is given that flag_pic is on and
++ that X satisfies CONSTANT_P or is a CONST_DOUBLE. */
++
++bool
++legitimate_pic_operand_p (x)
++ rtx x;
++{
++ rtx inner;
++
++ switch (GET_CODE (x))
++ {
++ case CONST:
++ inner = XEXP (x, 0);
++
++ /* Only some unspecs are valid as "constants". */
++ if (GET_CODE (inner) == UNSPEC)
++ switch (XINT (inner, 1))
++ {
++ case UNSPEC_TPOFF:
++ return local_exec_symbolic_operand (XVECEXP (inner, 0, 0), Pmode);
++ case UNSPEC_TP:
++ return true;
++ default:
++ return false;
++ }
++ /* FALLTHRU */
++
++ case SYMBOL_REF:
++ case LABEL_REF:
++ return legitimate_pic_address_disp_p (x);
++
++ default:
++ return true;
++ }
++}
++
+ /* Determine if a given CONST RTX is a valid memory displacement
+ in PIC mode. */
+
+@@ -4701,6 +4927,8 @@ int
+ legitimate_pic_address_disp_p (disp)
+ register rtx disp;
+ {
++ bool saw_plus;
++
+ /* In 64bit mode we can allow direct addresses of symbols and labels
+ when they are not dynamic symbols. */
+ if (TARGET_64BIT)
+@@ -4737,25 +4965,39 @@ legitimate_pic_address_disp_p (disp)
+ return 1;
+ }
+
++ saw_plus = false;
+ if (GET_CODE (disp) == PLUS)
+ {
+ if (GET_CODE (XEXP (disp, 1)) != CONST_INT)
+ return 0;
+ disp = XEXP (disp, 0);
++ saw_plus = true;
+ }
+
+ if (GET_CODE (disp) != UNSPEC
+ || XVECLEN (disp, 0) != 1)
+ return 0;
+
+- /* Must be @GOT or @GOTOFF. */
+ switch (XINT (disp, 1))
+ {
+- case 6: /* @GOT */
++ case UNSPEC_GOT:
++ if (saw_plus)
++ return false;
+ return GET_CODE (XVECEXP (disp, 0, 0)) == SYMBOL_REF;
+-
+- case 7: /* @GOTOFF */
++ case UNSPEC_GOTOFF:
+ return local_symbolic_operand (XVECEXP (disp, 0, 0), Pmode);
++ case UNSPEC_GOTTPOFF:
++ if (saw_plus)
++ return false;
++ return initial_exec_symbolic_operand (XVECEXP (disp, 0, 0), Pmode);
++ case UNSPEC_NTPOFF:
++ if (saw_plus)
++ return false;
++ return local_exec_symbolic_operand (XVECEXP (disp, 0, 0), Pmode);
++ case UNSPEC_DTPOFF:
++ if (saw_plus)
++ return false;
++ return local_dynamic_symbolic_operand (XVECEXP (disp, 0, 0), Pmode);
+ }
+
+ return 0;
+@@ -4882,12 +5124,6 @@ legitimate_address_p (mode, addr, strict
+ {
+ reason_rtx = disp;
+
+- if (!CONSTANT_ADDRESS_P (disp))
+- {
+- reason = "displacement is not constant";
+- goto report_error;
+- }
+-
+ if (TARGET_64BIT)
+ {
+ if (!x86_64_sign_extended_value (disp))
+@@ -4905,8 +5141,30 @@ legitimate_address_p (mode, addr, strict
+ }
+ }
+
+- if (flag_pic && SYMBOLIC_CONST (disp))
++ if (GET_CODE (disp) == CONST
++ && GET_CODE (XEXP (disp, 0)) == UNSPEC)
++ switch (XINT (XEXP (disp, 0), 1))
++ {
++ case UNSPEC_GOT:
++ case UNSPEC_GOTOFF:
++ case UNSPEC_GOTPCREL:
++ if (!flag_pic)
++ abort ();
++ goto is_legitimate_pic;
++
++ case UNSPEC_GOTTPOFF:
++ case UNSPEC_NTPOFF:
++ case UNSPEC_DTPOFF:
++ break;
++
++ default:
++ reason = "invalid address unspec";
++ goto report_error;
++ }
++
++ else if (flag_pic && SYMBOLIC_CONST (disp))
+ {
++ is_legitimate_pic:
+ if (TARGET_64BIT && (index || base))
+ {
+ reason = "non-constant pic memory reference";
+@@ -4949,6 +5207,11 @@ legitimate_address_p (mode, addr, strict
+ goto report_error;
+ }
+ }
++ else if (!CONSTANT_ADDRESS_P (disp))
++ {
++ reason = "displacement is not constant";
++ goto report_error;
++ }
+ }
+
+ /* Everything looks valid. */
+@@ -5129,7 +5392,102 @@ legitimize_pic_address (orig, reg)
+ }
+ return new;
+ }
++
++void
++ix86_encode_section_info (decl)
++ tree decl;
++{
++ bool local_p;
++ rtx rtl, symbol;
++
++ rtl = DECL_P (decl) ? DECL_RTL (decl) : TREE_CST_RTL (decl);
++ if (GET_CODE (rtl) != MEM)
++ return;
++ symbol = XEXP (rtl, 0);
++ if (GET_CODE (symbol) != SYMBOL_REF)
++ return;
++
++ local_p = !DECL_P (decl) || !TREE_PUBLIC (decl) || MODULE_LOCAL_P (decl);
++
++ /* For basic x86, if using PIC, mark a SYMBOL_REF for a non-global
++ symbol so that we may access it directly in the GOT. */
++
++ if (flag_pic)
++ SYMBOL_REF_FLAG (symbol) = local_p;
++
++ /* For ELF, encode thread-local data with %[GLil] for "global dynamic",
++ "local dynamic", "initial exec" or "local exec" TLS models
++ respectively. */
++
++ if (TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL (decl))
++ {
++ const char *symbol_str;
++ char *newstr;
++ size_t len;
++ enum tls_model kind;
++
++ if (!flag_pic)
++ {
++ if (local_p)
++ kind = TLS_MODEL_LOCAL_EXEC;
++ else
++ kind = TLS_MODEL_INITIAL_EXEC;
++ }
++ /* Local dynamic is inefficient when we're not combining the
++ parts of the address. */
++ else if (optimize && local_p)
++ kind = TLS_MODEL_LOCAL_DYNAMIC;
++ else
++ kind = TLS_MODEL_GLOBAL_DYNAMIC;
++ if (kind < flag_tls_default)
++ kind = flag_tls_default;
++
++ symbol_str = XSTR (symbol, 0);
++
++ if (symbol_str[0] == '%')
++ {
++ if (symbol_str[1] == tls_model_chars[kind])
++ return;
++ symbol_str += 2;
++ }
++ len = strlen (symbol_str) + 1;
++ newstr = alloca (len + 2);
++
++ newstr[0] = '%';
++ newstr[1] = tls_model_chars[kind];
++ memcpy (newstr + 2, symbol_str, len);
++
++ XSTR (symbol, 0) = ggc_alloc_string (newstr, len + 2 - 1);
++ }
++}
++
++/* Undo the above when printing symbol names. */
++
++const char *
++ix86_strip_name_encoding (str)
++ const char *str;
++{
++ if (str[0] == '%')
++ str += 2;
++ if (str [0] == '*')
++ str += 1;
++ return str;
++}
+ \f
++/* Load the thread pointer into a register. */
++
++static rtx
++get_thread_pointer ()
++{
++ rtx tp;
++
++ tp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), UNSPEC_TP);
++ tp = gen_rtx_CONST (Pmode, tp);
++ tp = force_reg (Pmode, tp);
++
++ return tp;
++}
++
+ /* Try machine-dependent ways of modifying an illegitimate address
+ to be legitimate. If we find one, return the new, valid address.
+ This macro is used in only one place: `memory_address' in explow.c.
+@@ -5167,6 +5525,84 @@ legitimize_address (x, oldx, mode)
+ debug_rtx (x);
+ }
+
++ log = tls_symbolic_operand (x, mode);
++ if (log)
++ {
++ rtx dest, base, off, pic;
++
++ switch (log)
++ {
++ case TLS_MODEL_GLOBAL_DYNAMIC:
++ dest = gen_reg_rtx (Pmode);
++ emit_insn (gen_tls_global_dynamic (dest, x));
++ break;
++
++ case TLS_MODEL_LOCAL_DYNAMIC:
++ base = gen_reg_rtx (Pmode);
++ emit_insn (gen_tls_local_dynamic_base (base));
++
++ off = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, x), UNSPEC_DTPOFF);
++ off = gen_rtx_CONST (Pmode, off);
++
++ return gen_rtx_PLUS (Pmode, base, off);
++
++ case TLS_MODEL_INITIAL_EXEC:
++ if (flag_pic)
++ {
++ current_function_uses_pic_offset_table = 1;
++ pic = pic_offset_table_rtx;
++ }
++ else
++ {
++ pic = gen_reg_rtx (Pmode);
++ emit_insn (gen_set_got (pic));
++ }
++
++ base = get_thread_pointer ();
++
++ off = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, x), UNSPEC_GOTTPOFF);
++ off = gen_rtx_CONST (Pmode, off);
++ off = gen_rtx_PLUS (Pmode, pic, off);
++ off = gen_rtx_MEM (Pmode, off);
++ RTX_UNCHANGING_P (off) = 1;
++ set_mem_alias_set (off, ix86_GOT_alias_set ());
++
++ /* Damn Sun for specifing a set of dynamic relocations without
++ considering the two-operand nature of the architecture!
++ We'd be much better off with a "GOTNTPOFF" relocation that
++ already contained the negated constant. */
++ /* ??? Using negl and reg+reg addressing appears to be a lose
++ size-wise. The negl is two bytes, just like the extra movl
++ incurred by the two-operand subl, but reg+reg addressing
++ uses the two-byte modrm form, unlike plain reg. */
++
++ dest = gen_reg_rtx (Pmode);
++ emit_insn (gen_subsi3 (dest, base, off));
++ break;
++
++ case TLS_MODEL_LOCAL_EXEC:
++ base = get_thread_pointer ();
++
++ off = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, x),
++ TARGET_GNU_TLS ? UNSPEC_NTPOFF : UNSPEC_TPOFF);
++ off = gen_rtx_CONST (Pmode, off);
++
++ if (TARGET_GNU_TLS)
++ return gen_rtx_PLUS (Pmode, base, off);
++ else
++ {
++ dest = gen_reg_rtx (Pmode);
++ emit_insn (gen_subsi3 (dest, base, off));
++ }
++ break;
++
++ default:
++ abort ();
++ }
++
++ return dest;
++ }
++
+ if (flag_pic && SYMBOLIC_CONST (x))
+ return legitimize_pic_address (x, 0);
+
+@@ -5410,18 +5846,30 @@ output_pic_addr_const (file, x, code)
+ output_pic_addr_const (file, XVECEXP (x, 0, 0), code);
+ switch (XINT (x, 1))
+ {
+- case 6:
++ case UNSPEC_GOT:
+ fputs ("@GOT", file);
+ break;
+- case 7:
++ case UNSPEC_GOTOFF:
+ fputs ("@GOTOFF", file);
+ break;
+- case 8:
++ case UNSPEC_PLT:
+ fputs ("@PLT", file);
+ break;
+- case 15:
++ case UNSPEC_GOTPCREL:
+ fputs ("@GOTPCREL(%RIP)", file);
+ break;
++ case UNSPEC_GOTTPOFF:
++ fputs ("@GOTTPOFF", file);
++ break;
++ case UNSPEC_TPOFF:
++ fputs ("@TPOFF", file);
++ break;
++ case UNSPEC_NTPOFF:
++ fputs ("@NTPOFF", file);
++ break;
++ case UNSPEC_DTPOFF:
++ fputs ("@DTPOFF", file);
++ break;
+ default:
+ output_operand_lossage ("invalid UNSPEC as operand");
+ break;
+@@ -5710,6 +6158,43 @@ print_reg (x, code, file)
+ }
+ }
+
++/* Locate some local-dynamic symbol still in use by this function
++ so that we can print its name in some tls_local_dynamic_base
++ pattern. */
++
++static const char *
++get_some_local_dynamic_name ()
++{
++ rtx insn;
++
++ if (cfun->machine->some_ld_name)
++ return cfun->machine->some_ld_name;
++
++ for (insn = get_insns (); insn ; insn = NEXT_INSN (insn))
++ if (INSN_P (insn)
++ && for_each_rtx (&PATTERN (insn), get_some_local_dynamic_name_1, 0))
++ return cfun->machine->some_ld_name;
++
++ abort ();
++}
++
++static int
++get_some_local_dynamic_name_1 (px, data)
++ rtx *px;
++ void *data ATTRIBUTE_UNUSED;
++{
++ rtx x = *px;
++
++ if (GET_CODE (x) == SYMBOL_REF
++ && local_dynamic_symbolic_operand (x, Pmode))
++ {
++ cfun->machine->some_ld_name = XSTR (x, 0);
++ return 1;
++ }
++
++ return 0;
++}
++
+ /* Meaning of CODE:
+ L,W,B,Q,S,T -- print the opcode suffix for specified size of operand.
+ C -- print opcode suffix for set/cmov insn.
+@@ -5734,6 +6219,7 @@ print_reg (x, code, file)
+ D -- print condition for SSE cmp instruction.
+ P -- if PIC, print an @PLT suffix.
+ X -- don't print any sort of PIC '@' suffix for a symbol.
++ & -- print some in-use local-dynamic symbol name.
+ */
+
+ void
+@@ -5751,6 +6237,10 @@ print_operand (file, x, code)
+ putc ('*', file);
+ return;
+
++ case '&':
++ assemble_name (file, get_some_local_dynamic_name ());
++ return;
++
+ case 'A':
+ if (ASSEMBLER_DIALECT == ASM_ATT)
+ putc ('*', file);
+@@ -6078,6 +6568,18 @@ print_operand (file, x, code)
+ REAL_VALUE_TO_DECIMAL (r, "%.22e", dstr);
+ fprintf (file, "%s", dstr);
+ }
++
++ else if (GET_CODE (x) == CONST
++ && GET_CODE (XEXP (x, 0)) == UNSPEC
++ && XINT (XEXP (x, 0), 1) == UNSPEC_TP)
++ {
++ if (ASSEMBLER_DIALECT == ASM_INTEL)
++ fputs ("DWORD PTR ", file);
++ if (ASSEMBLER_DIALECT == ASM_ATT || USER_LABEL_PREFIX[0] == 0)
++ putc ('%', file);
++ fputs ("gs:0", file);
++ }
++
+ else
+ {
+ if (code != 'P')
+@@ -6226,6 +6728,43 @@ print_operand_address (file, addr)
+ }
+ }
+ }
++
++bool
++output_addr_const_extra (file, x)
++ FILE *file;
++ rtx x;
++{
++ rtx op;
++
++ if (GET_CODE (x) != UNSPEC)
++ return false;
++
++ op = XVECEXP (x, 0, 0);
++ switch (XINT (x, 1))
++ {
++ case UNSPEC_GOTTPOFF:
++ output_addr_const (file, op);
++ fputs ("@GOTTPOFF", file);
++ break;
++ case UNSPEC_TPOFF:
++ output_addr_const (file, op);
++ fputs ("@TPOFF", file);
++ break;
++ case UNSPEC_NTPOFF:
++ output_addr_const (file, op);
++ fputs ("@NTPOFF", file);
++ break;
++ case UNSPEC_DTPOFF:
++ output_addr_const (file, op);
++ fputs ("@DTPOFF", file);
++ break;
++
++ default:
++ return false;
++ }
++
++ return true;
++}
+ \f
+ /* Split one or more DImode RTL references into pairs of SImode
+ references. The RTL can be REG, offsettable MEM, integer constant, or
+@@ -6763,51 +7302,117 @@ ix86_expand_clear (dest)
+ emit_insn (tmp);
+ }
+
++/* X is an unchanging MEM. If it is a constant pool reference, return
++ the constant pool rtx, else NULL. */
++
++static rtx
++maybe_get_pool_constant (x)
++ rtx x;
++{
++ x = XEXP (x, 0);
++
++ if (flag_pic)
++ {
++ if (GET_CODE (x) != PLUS)
++ return NULL_RTX;
++ if (XEXP (x, 0) != pic_offset_table_rtx)
++ return NULL_RTX;
++ x = XEXP (x, 1);
++ if (GET_CODE (x) != CONST)
++ return NULL_RTX;
++ x = XEXP (x, 0);
++ if (GET_CODE (x) != UNSPEC)
++ return NULL_RTX;
++ if (XINT (x, 1) != UNSPEC_GOTOFF)
++ return NULL_RTX;
++ x = XVECEXP (x, 0, 0);
++ }
++
++ if (GET_CODE (x) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (x))
++ return get_pool_constant (x);
++
++ return NULL_RTX;
++}
++
+ void
+ ix86_expand_move (mode, operands)
+ enum machine_mode mode;
+ rtx operands[];
+ {
+ int strict = (reload_in_progress || reload_completed);
+- rtx insn;
++ rtx insn, op0, op1, tmp;
++
++ op0 = operands[0];
++ op1 = operands[1];
+
+- if (flag_pic && mode == Pmode && symbolic_operand (operands[1], Pmode))
++ /* ??? We have a slight problem. We need to say that tls symbols are
++ not legitimate constants so that reload does not helpfully reload
++ these constants from a REG_EQUIV, which we cannot handle. (Recall
++ that general- and local-dynamic address resolution requires a
++ function call.)
++
++ However, if we say that tls symbols are not legitimate constants,
++ then emit_move_insn helpfully drop them into the constant pool.
++
++ It is far easier to work around emit_move_insn than reload. Recognize
++ the MEM that we would have created and extract the symbol_ref. */
++
++ if (mode == Pmode
++ && GET_CODE (op1) == MEM
++ && RTX_UNCHANGING_P (op1))
+ {
+- /* Emit insns to move operands[1] into operands[0]. */
++ tmp = maybe_get_pool_constant (op1);
++ /* Note that we only care about symbolic constants here, which
++ unlike CONST_INT will always have a proper mode. */
++ if (tmp && GET_MODE (tmp) == Pmode)
++ op1 = tmp;
++ }
+
+- if (GET_CODE (operands[0]) == MEM)
+- operands[1] = force_reg (Pmode, operands[1]);
++ if (tls_symbolic_operand (op1, Pmode))
++ {
++ op1 = legitimize_address (op1, op1, VOIDmode);
++ if (GET_CODE (op0) == MEM)
++ {
++ tmp = gen_reg_rtx (mode);
++ emit_insn (gen_rtx_SET (VOIDmode, tmp, op1));
++ op1 = tmp;
++ }
++ }
++ else if (flag_pic && mode == Pmode && symbolic_operand (op1, Pmode))
++ {
++ if (GET_CODE (op0) == MEM)
++ op1 = force_reg (Pmode, op1);
+ else
+ {
+- rtx temp = operands[0];
++ rtx temp = op0;
+ if (GET_CODE (temp) != REG)
+ temp = gen_reg_rtx (Pmode);
+- temp = legitimize_pic_address (operands[1], temp);
+- if (temp == operands[0])
++ temp = legitimize_pic_address (op1, temp);
++ if (temp == op0)
+ return;
+- operands[1] = temp;
++ op1 = temp;
+ }
+ }
+ else
+ {
+- if (GET_CODE (operands[0]) == MEM
++ if (GET_CODE (op0) == MEM
+ && (PUSH_ROUNDING (GET_MODE_SIZE (mode)) != GET_MODE_SIZE (mode)
+- || !push_operand (operands[0], mode))
+- && GET_CODE (operands[1]) == MEM)
+- operands[1] = force_reg (mode, operands[1]);
+-
+- if (push_operand (operands[0], mode)
+- && ! general_no_elim_operand (operands[1], mode))
+- operands[1] = copy_to_mode_reg (mode, operands[1]);
++ || !push_operand (op0, mode))
++ && GET_CODE (op1) == MEM)
++ op1 = force_reg (mode, op1);
++
++ if (push_operand (op0, mode)
++ && ! general_no_elim_operand (op1, mode))
++ op1 = copy_to_mode_reg (mode, op1);
+
+ /* Force large constants in 64bit compilation into register
+ to get them CSEed. */
+ if (TARGET_64BIT && mode == DImode
+- && immediate_operand (operands[1], mode)
+- && !x86_64_zero_extended_value (operands[1])
+- && !register_operand (operands[0], mode)
++ && immediate_operand (op1, mode)
++ && !x86_64_zero_extended_value (op1)
++ && !register_operand (op0, mode)
+ && optimize && !reload_completed && !reload_in_progress)
+- operands[1] = copy_to_mode_reg (mode, operands[1]);
++ op1 = copy_to_mode_reg (mode, op1);
+
+ if (FLOAT_MODE_P (mode))
+ {
+@@ -6817,13 +7422,13 @@ ix86_expand_move (mode, operands)
+
+ if (strict)
+ ;
+- else if (GET_CODE (operands[1]) == CONST_DOUBLE
+- && register_operand (operands[0], mode))
+- operands[1] = validize_mem (force_const_mem (mode, operands[1]));
++ else if (GET_CODE (op1) == CONST_DOUBLE
++ && register_operand (op0, mode))
++ op1 = validize_mem (force_const_mem (mode, op1));
+ }
+ }
+
+- insn = gen_rtx_SET (VOIDmode, operands[0], operands[1]);
++ insn = gen_rtx_SET (VOIDmode, op0, op1);
+
+ emit_insn (insn);
+ }
+@@ -8588,13 +9193,14 @@ ix86_split_to_parts (operand, parts, mod
+ if (size < 2 || size > 3)
+ abort ();
+
+- /* Optimize constant pool reference to immediates. This is used by fp moves,
+- that force all constants to memory to allow combining. */
+-
+- if (GET_CODE (operand) == MEM
+- && GET_CODE (XEXP (operand, 0)) == SYMBOL_REF
+- && CONSTANT_POOL_ADDRESS_P (XEXP (operand, 0)))
+- operand = get_pool_constant (XEXP (operand, 0));
++ /* Optimize constant pool reference to immediates. This is used by fp
++ moves, that force all constants to memory to allow combining. */
++ if (GET_CODE (operand) == MEM && RTX_UNCHANGING_P (operand))
++ {
++ rtx tmp = maybe_get_pool_constant (operand);
++ if (tmp)
++ operand = tmp;
++ }
+
+ if (GET_CODE (operand) == MEM && !offsettable_memref_p (operand))
+ {
+@@ -9790,6 +10396,55 @@ ix86_expand_strlensi_unroll_1 (out, alig
+
+ emit_label (end_0_label);
+ }
++
++void
++ix86_expand_call (retval, fnaddr, callarg1, callarg2, pop)
++ rtx retval, fnaddr, callarg1, callarg2, pop;
++{
++ rtx use = NULL, call;
++
++ if (pop == const0_rtx)
++ pop = NULL;
++ if (TARGET_64BIT && pop)
++ abort ();
++
++ /* Static functions and indirect calls don't need the pic register. */
++ if (! TARGET_64BIT && flag_pic
++ && GET_CODE (XEXP (fnaddr, 0)) == SYMBOL_REF
++ && ! SYMBOL_REF_FLAG (XEXP (fnaddr, 0)))
++ {
++ current_function_uses_pic_offset_table = 1;
++ use_reg (&use, pic_offset_table_rtx);
++ }
++
++ if (TARGET_64BIT && INTVAL (callarg2) >= 0)
++ {
++ rtx al = gen_rtx_REG (QImode, 0);
++ emit_move_insn (al, callarg2);
++ use_reg (&use, al);
++ }
++
++ if (! call_insn_operand (XEXP (fnaddr, 0), Pmode))
++ {
++ fnaddr = copy_to_mode_reg (Pmode, XEXP (fnaddr, 0));
++ fnaddr = gen_rtx_MEM (QImode, fnaddr);
++ }
++
++ call = gen_rtx_CALL (VOIDmode, fnaddr, callarg1);
++ if (retval)
++ call = gen_rtx_SET (VOIDmode, retval, call);
++ if (pop)
++ {
++ pop = gen_rtx_PLUS (Pmode, stack_pointer_rtx, pop);
++ pop = gen_rtx_SET (VOIDmode, stack_pointer_rtx, pop);
++ call = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, call, pop));
++ }
++
++ call = emit_call_insn (call);
++ if (use)
++ CALL_INSN_FUNCTION_USAGE (call) = use;
++}
++
+ \f
+ /* Clear stack slot assignments remembered from previous functions.
+ This is called from INIT_EXPANDERS once before RTL is emitted for each
+@@ -9849,6 +10504,24 @@ assign_386_stack_local (mode, n)
+
+ return ix86_stack_locals[(int) mode][n];
+ }
++
++/* Construct the SYMBOL_REF for the tls_get_addr function. */
++
++rtx
++ix86_tls_get_addr ()
++{
++ static rtx symbol;
++
++ if (!symbol)
++ {
++ symbol = gen_rtx_SYMBOL_REF (Pmode, (TARGET_GNU_TLS
++ ? "___tls_get_addr"
++ : "__tls_get_addr"));
++ ggc_add_rtx_root (&symbol, 1);
++ }
++
++ return symbol;
++}
+ \f
+ /* Calculate the length of the memory address in the instruction
+ encoding. Does not include the one-byte modrm, opcode, or prefix. */
+--- gcc/config/i386/i386-protos.h.jj Wed Jun 19 15:18:02 2002
++++ gcc/config/i386/i386-protos.h Wed Jun 19 19:33:52 2002
+@@ -28,7 +28,6 @@ extern int ix86_frame_pointer_required P
+ extern void ix86_setup_frame_addresses PARAMS ((void));
+
+ extern void ix86_asm_file_end PARAMS ((FILE *));
+-extern void load_pic_register PARAMS ((void));
+ extern HOST_WIDE_INT ix86_initial_elimination_offset PARAMS((int, int));
+ extern void ix86_expand_prologue PARAMS ((void));
+ extern void ix86_expand_epilogue PARAMS ((int));
+@@ -36,6 +35,9 @@ extern void ix86_expand_epilogue PARAMS
+ extern void ix86_output_addr_vec_elt PARAMS ((FILE *, int));
+ extern void ix86_output_addr_diff_elt PARAMS ((FILE *, int, int));
+
++extern void ix86_encode_section_info PARAMS ((tree));
++extern const char *ix86_strip_name_encoding PARAMS ((const char *));
++
+ #ifdef RTX_CODE
+ extern int ix86_aligned_p PARAMS ((rtx));
+
+@@ -51,6 +53,11 @@ extern int x86_64_immediate_operand PARA
+ extern int x86_64_zext_immediate_operand PARAMS ((rtx, enum machine_mode));
+ extern int const_int_1_operand PARAMS ((rtx, enum machine_mode));
+ extern int symbolic_operand PARAMS ((rtx, enum machine_mode));
++extern int tls_symbolic_operand PARAMS ((rtx, enum machine_mode));
++extern int global_dynamic_symbolic_operand PARAMS ((rtx, enum machine_mode));
++extern int local_dynamic_symbolic_operand PARAMS ((rtx, enum machine_mode));
++extern int initial_exec_symbolic_operand PARAMS ((rtx, enum machine_mode));
++extern int local_exec_symbolic_operand PARAMS ((rtx, enum machine_mode));
+ extern int pic_symbolic_operand PARAMS ((rtx, enum machine_mode));
+ extern int call_insn_operand PARAMS ((rtx, enum machine_mode));
+ extern int constant_call_address_operand PARAMS ((rtx, enum machine_mode));
+@@ -84,6 +91,9 @@ extern int ix86_expand_movstr PARAMS ((r
+ extern int ix86_expand_clrstr PARAMS ((rtx, rtx, rtx));
+ extern int ix86_expand_strlen PARAMS ((rtx, rtx, rtx, rtx));
+
++extern bool legitimate_constant_p PARAMS ((rtx));
++extern bool constant_address_p PARAMS ((rtx));
++extern bool legitimate_pic_operand_p PARAMS ((rtx));
+ extern int legitimate_pic_address_disp_p PARAMS ((rtx));
+ extern int legitimate_address_p PARAMS ((enum machine_mode, rtx, int));
+ extern rtx legitimize_pic_address PARAMS ((rtx, rtx));
+@@ -92,10 +102,12 @@ extern rtx legitimize_address PARAMS ((r
+ extern void print_reg PARAMS ((rtx, int, FILE*));
+ extern void print_operand PARAMS ((FILE*, rtx, int));
+ extern void print_operand_address PARAMS ((FILE*, rtx));
++extern bool output_addr_const_extra PARAMS ((FILE*, rtx));
+
+ extern void split_di PARAMS ((rtx[], int, rtx[], rtx[]));
+ extern void split_ti PARAMS ((rtx[], int, rtx[], rtx[]));
+
++extern const char *output_set_got PARAMS ((rtx));
+ extern const char *output_387_binary_op PARAMS ((rtx, rtx*));
+ extern const char *output_fix_trunc PARAMS ((rtx, rtx*));
+ extern const char *output_fp_compare PARAMS ((rtx, rtx*, int, int));
+@@ -121,6 +133,7 @@ extern void ix86_expand_branch PARAMS ((
+ extern int ix86_expand_setcc PARAMS ((enum rtx_code, rtx));
+ extern int ix86_expand_int_movcc PARAMS ((rtx[]));
+ extern int ix86_expand_fp_movcc PARAMS ((rtx[]));
++extern void ix86_expand_call PARAMS ((rtx, rtx, rtx, rtx, rtx));
+ extern void x86_initialize_trampoline PARAMS ((rtx, rtx, rtx));
+ extern rtx ix86_zero_extend_to_Pmode PARAMS ((rtx));
+ extern void ix86_split_long_move PARAMS ((rtx[]));
+@@ -181,6 +194,8 @@ extern rtx ix86_expand_builtin PARAMS ((
+
+ #endif
+
++extern rtx ix86_tls_get_addr PARAMS ((void));
++
+ #ifdef TREE_CODE
+ extern int ix86_return_pops_args PARAMS ((tree, tree, int));
+ extern tree ix86_build_va_list PARAMS ((void));
+--- gcc/config/i386/i386.h.jj Wed Jun 19 19:33:51 2002
++++ gcc/config/i386/i386.h Wed Jun 19 20:20:56 2002
+@@ -282,6 +282,9 @@ extern int x86_prefetch_sse;
+
+ #define TARGET_RED_ZONE (!(target_flags & MASK_NO_RED_ZONE))
+
++#define TARGET_GNU_TLS (ix86_tls_dialect == TLS_DIALECT_GNU)
++#define TARGET_SUN_TLS (ix86_tls_dialect == TLS_DIALECT_SUN)
++
+ /* WARNING: Do not mark empty strings for translation, as calling
+ gettext on an empty string does NOT return an empty
+ string. */
+@@ -451,6 +454,8 @@ extern int ix86_arch;
+ "" /* Undocumented. */ }, \
+ { "asm=", &ix86_asm_string, \
+ N_("Use given assembler dialect") }, \
++ { "tls-dialect=", &ix86_tls_dialect_string, \
++ N_("Use given thread-local storage dialect") }, \
+ SUBTARGET_OPTIONS \
+ }
+
+@@ -1934,15 +1939,12 @@ do { \
+
+ #define MAX_REGS_PER_ADDRESS 2
+
+-#define CONSTANT_ADDRESS_P(X) \
+- (GET_CODE (X) == LABEL_REF || GET_CODE (X) == SYMBOL_REF \
+- || GET_CODE (X) == CONST_INT || GET_CODE (X) == CONST \
+- || GET_CODE (X) == CONST_DOUBLE)
++#define CONSTANT_ADDRESS_P(X) constant_address_p (X)
+
+ /* Nonzero if the constant value X is a legitimate general operand.
+ It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE. */
+
+-#define LEGITIMATE_CONSTANT_P(X) 1
++#define LEGITIMATE_CONSTANT_P(X) legitimate_constant_p (X)
+
+ #ifdef REG_OK_STRICT
+ #define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \
+@@ -2005,9 +2007,7 @@ do { \
+ when generating PIC code. It is given that flag_pic is on and
+ that X satisfies CONSTANT_P or is a CONST_DOUBLE. */
+
+-#define LEGITIMATE_PIC_OPERAND_P(X) \
+- (! SYMBOLIC_CONST (X) \
+- || legitimate_pic_address_disp_p (X))
++#define LEGITIMATE_PIC_OPERAND_P(X) legitimate_pic_operand_p (X)
+
+ #define SYMBOLIC_CONST(X) \
+ (GET_CODE (X) == SYMBOL_REF \
+@@ -2251,33 +2251,23 @@ enum ix86_builtins
+ On i386, if using PIC, mark a SYMBOL_REF for a non-global symbol
+ so that we may access it directly in the GOT. */
+
+-#define ENCODE_SECTION_INFO(DECL) \
+-do { \
+- if (flag_pic) \
+- { \
+- rtx rtl = (TREE_CODE_CLASS (TREE_CODE (DECL)) != 'd' \
+- ? TREE_CST_RTL (DECL) : DECL_RTL (DECL)); \
+- \
+- if (GET_CODE (rtl) == MEM) \
+- { \
+- if (TARGET_DEBUG_ADDR \
+- && TREE_CODE_CLASS (TREE_CODE (DECL)) == 'd') \
+- { \
+- fprintf (stderr, "Encode %s, public = %d\n", \
+- IDENTIFIER_POINTER (DECL_NAME (DECL)), \
+- TREE_PUBLIC (DECL)); \
+- } \
+- \
+- SYMBOL_REF_FLAG (XEXP (rtl, 0)) \
+- = (TREE_CODE_CLASS (TREE_CODE (DECL)) != 'd' \
+- || ! TREE_PUBLIC (DECL) \
+- || MODULE_LOCAL_P (DECL)); \
+- } \
+- } \
+-} while (0)
+-
++#define ENCODE_SECTION_INFO(DECL) ix86_encode_section_info(DECL)
+ #define REDO_SECTION_INFO_P(DECL) 1
+
++#define STRIP_NAME_ENCODING(VAR,STR) ((VAR) = ix86_strip_name_encoding (STR))
++
++#define ASM_OUTPUT_LABELREF(FILE,NAME) \
++ do { \
++ const char *xname = (NAME); \
++ if (xname[0] == '%') \
++ xname += 2; \
++ if (xname[0] == '*') \
++ xname += 1; \
++ else \
++ fputs (user_label_prefix, FILE); \
++ fputs (xname, FILE); \
++ } while (0)
++
+ /* The `FINALIZE_PIC' macro serves as a hook to emit these special
+ codes once the function is being compiled into assembly code, but
+ not before. (It is not done before, because in the case of
+@@ -2923,7 +2913,7 @@ extern int const svr4_dbx_register_map[F
+ print_operand function. */
+
+ #define PRINT_OPERAND_PUNCT_VALID_P(CODE) \
+- ((CODE) == '*' || (CODE) == '+')
++ ((CODE) == '*' || (CODE) == '+' || (CODE) == '&')
+
+ /* Print the name of a register based on its machine mode and number.
+ If CODE is 'w', pretend the mode is HImode.
+@@ -2942,6 +2932,12 @@ extern int const svr4_dbx_register_map[F
+ #define PRINT_OPERAND_ADDRESS(FILE, ADDR) \
+ print_operand_address ((FILE), (ADDR))
+
++#define OUTPUT_ADDR_CONST_EXTRA(FILE, X, FAIL) \
++do { \
++ if (! output_addr_const_extra (FILE, (X))) \
++ goto FAIL; \
++} while (0);
++
+ /* Print the name of a register for based on its machine mode and number.
+ This macro is used to print debugging output.
+ This macro is different from PRINT_REG in that it may be used in
+@@ -3071,7 +3067,12 @@ extern int const svr4_dbx_register_map[F
+ {"memory_displacement_operand", {MEM}}, \
+ {"cmpsi_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF, \
+ LABEL_REF, SUBREG, REG, MEM, AND}}, \
+- {"long_memory_operand", {MEM}},
++ {"long_memory_operand", {MEM}}, \
++ {"tls_symbolic_operand", {SYMBOL_REF}}, \
++ {"global_dynamic_symbolic_operand", {SYMBOL_REF}}, \
++ {"local_dynamic_symbolic_operand", {SYMBOL_REF}}, \
++ {"initial_exec_symbolic_operand", {SYMBOL_REF}}, \
++ {"local_exec_symbolic_operand", {SYMBOL_REF}},
+
+ /* A list of predicates that do special things with modes, and so
+ should not elicit warnings for VOIDmode match_operand. */
+@@ -3112,6 +3113,16 @@ enum asm_dialect {
+ };
+ extern const char *ix86_asm_string;
+ extern enum asm_dialect ix86_asm_dialect;
++
++enum tls_dialect
++{
++ TLS_DIALECT_GNU,
++ TLS_DIALECT_SUN
++};
++
++extern enum tls_dialect ix86_tls_dialect;
++extern const char *ix86_tls_dialect_string;
++
+ /* Value of -mcmodel specified by user. */
+ extern const char *ix86_cmodel_string;
+ extern enum cmodel ix86_cmodel;
+--- gcc/config/i386/i386.md.jj Tue Jun 4 20:28:36 2002
++++ gcc/config/i386/i386.md Wed Jun 19 19:33:52 2002
+@@ -49,55 +49,59 @@
+ ;; 'k' Likewise, print the SImode name of the register.
+ ;; 'h' Print the QImode name for a "high" register, either ah, bh, ch or dh.
+ ;; 'y' Print "st(0)" instead of "st" as a register.
+-;;
++
+ ;; UNSPEC usage:
+-;; 0 This is a `scas' operation. The mode of the UNSPEC is always SImode.
+-;; operand 0 is the memory address to scan.
+-;; operand 1 is a register containing the value to scan for. The mode
+-;; of the scas opcode will be the same as the mode of this operand.
+-;; operand 2 is the known alignment of operand 0.
+-;; 1 This is a `sin' operation. The mode of the UNSPEC is MODE_FLOAT.
+-;; operand 0 is the argument for `sin'.
+-;; 2 This is a `cos' operation. The mode of the UNSPEC is MODE_FLOAT.
+-;; operand 0 is the argument for `cos'.
+-;; 3 This is part of a `stack probe' operation. The mode of the UNSPEC is
+-;; always SImode. operand 0 is the size of the stack allocation.
+-;; 4 This is the source of a fake SET of the frame pointer which is used to
+-;; prevent insns referencing it being scheduled across the initial
+-;; decrement of the stack pointer.
+-;; 5 This is a `bsf' operation.
+-;; 6 This is the @GOT offset of a PIC address.
+-;; 7 This is the @GOTOFF offset of a PIC address.
+-;; 8 This is a reference to a symbol's @PLT address.
+-;; 9 This is an `fnstsw' operation.
+-;; 10 This is a `sahf' operation.
+-;; 11 This is a `fstcw' operation
+-;; 12 This is behaviour of add when setting carry flag.
+-;; 13 This is a `eh_return' placeholder.
+-
+-;; For SSE/MMX support:
+-;; 30 This is `fix', guaranteed to be truncating.
+-;; 31 This is a `emms' operation.
+-;; 32 This is a `maskmov' operation.
+-;; 33 This is a `movmsk' operation.
+-;; 34 This is a `non-temporal' move.
+-;; 36 This is used to distinguish COMISS from UCOMISS.
+-;; 37 This is a `ldmxcsr' operation.
+-;; 38 This is a forced `movaps' instruction (rather than whatever movti does)
+-;; 39 This is a forced `movups' instruction (rather than whatever movti does)
+-;; 40 This is a `stmxcsr' operation.
+-;; 41 This is a `shuffle' operation.
+-;; 42 This is a `rcp' operation.
+-;; 43 This is a `rsqsrt' operation.
+-;; 44 This is a `sfence' operation.
+-;; 45 This is a noop to prevent excessive combiner cleverness.
+-;; 46 This is a `femms' operation.
+-;; 49 This is a 'pavgusb' operation.
+-;; 50 This is a `pfrcp' operation.
+-;; 51 This is a `pfrcpit1' operation.
+-;; 52 This is a `pfrcpit2' operation.
+-;; 53 This is a `pfrsqrt' operation.
+-;; 54 This is a `pfrsqrit1' operation.
++;; ??? Note that the 3.1 branch, unlike mainline, has not had all
++;; of the uses of UNSPEC replaced with their symbolic constants.
++;; Thus you cannot change these arbitrarily without pain.
++
++(define_constants
++ [(UNSPEC_SCAS 0)
++ (UNSPEC_SIN 1)
++ (UNSPEC_COS 2)
++ (UNSPEC_STACK_PROBE 3)
++ (UNSPEC_STACK_ALLOC 4)
++ (UNSPEC_BSF 5)
++ (UNSPEC_GOT 6)
++ (UNSPEC_GOTOFF 7)
++ (UNSPEC_PLT 8)
++ (UNSPEC_FNSTSW 9)
++ (UNSPEC_SAHF 10)
++ (UNSPEC_FSTCW 11)
++ (UNSPEC_FLDCW 12)
++ (UNSPEC_ADD_CARRY 12)
++ (UNSPEC_EH_RETURN 13)
++ (UNSPEC_GOTPCREL 15)
++ (UNSPEC_SET_GOT 16)
++
++ ; For TLS support:
++ (UNSPEC_GOTTPOFF 20)
++ (UNSPEC_TPOFF 21)
++ (UNSPEC_NTPOFF 22)
++ (UNSPEC_DTPOFF 23)
++ (UNSPEC_TP 24)
++ (UNSPEC_TLS_GD 25)
++ (UNSPEC_TLS_LD_BASE 26)
++
++ ; For SSE/MMX support:
++ (UNSPEC_FIX 30)
++ (UNSPEC_MASKMOV 32)
++ (UNSPEC_MOVMSK 33)
++ (UNSPEC_MOVNT 34)
++ (UNSPEC_MOVA 38)
++ (UNSPEC_MOVU 39)
++ (UNSPEC_SHUFFLE 41)
++ (UNSPEC_RCP 42)
++ (UNSPEC_RSQRT 43)
++ (UNSPEC_SFENCE 44)
++ (UNSPEC_NOP 45) ; prevents combiner cleverness
++ (UNSPEC_PAVGUSB 49)
++ (UNSPEC_PFRCP 50)
++ (UNSPEC_PFRCPIT1 51)
++ (UNSPEC_PFRCPIT2 52)
++ (UNSPEC_PFRSQRT 53)
++ (UNSPEC_PFRSQIT1 54)
++ ])
+
+ ;; Insns whose names begin with "x86_" are emitted by gen_FOO calls
+ ;; from i386.c.
+@@ -1759,7 +1763,7 @@
+ return "lea{l}\t{%1, %0|%0, %1}";
+
+ default:
+- if (flag_pic && SYMBOLIC_CONST (operands[1]))
++ if (flag_pic && !LEGITIMATE_PIC_OPERAND_P (operands[1]))
+ abort();
+ return "mov{l}\t{%1, %0|%0, %1}";
+ }
+@@ -2525,7 +2529,7 @@
+ case TYPE_LEA:
+ return "lea{q}\t{%a1, %0|%0, %a1}";
+ default:
+- if (flag_pic && SYMBOLIC_CONST (operands[1]))
++ if (flag_pic && !LEGITIMATE_PIC_OPERAND_P (operands[1]))
+ abort ();
+ if (get_attr_mode (insn) == MODE_SI)
+ return "mov{l}\t{%k1, %k0|%k0, %k1}";
+@@ -13474,21 +13478,8 @@
+ (match_operand:SI 3 "" "")))])]
+ "!TARGET_64BIT"
+ {
+- if (operands[3] == const0_rtx)
+- {
+- emit_insn (gen_call (operands[0], operands[1], constm1_rtx));
+- DONE;
+- }
+- /* Static functions and indirect calls don't need
+- current_function_uses_pic_offset_table. */
+- if (flag_pic
+- && GET_CODE (XEXP (operands[0], 0)) == SYMBOL_REF
+- && ! SYMBOL_REF_FLAG (XEXP (operands[0], 0)))
+- current_function_uses_pic_offset_table = 1;
+- if (! call_insn_operand (XEXP (operands[0], 0), Pmode))
+- XEXP (operands[0], 0) = copy_to_mode_reg (Pmode, XEXP (operands[0], 0));
+- if (TARGET_64BIT)
+- abort();
++ ix86_expand_call (NULL, operands[0], operands[1], operands[2], operands[3]);
++ DONE;
+ })
+
+ (define_insn "*call_pop_0"
+@@ -13530,37 +13521,12 @@
+ [(call (match_operand:QI 0 "" "")
+ (match_operand 1 "" ""))
+ (use (match_operand 2 "" ""))]
+- ;; Operand 1 not used on the i386.
+ ""
+ {
+- rtx insn;
+- /* Static functions and indirect calls don't need
+- current_function_uses_pic_offset_table. */
+- if (flag_pic
+- && GET_CODE (XEXP (operands[0], 0)) == SYMBOL_REF
+- && ! SYMBOL_REF_FLAG (XEXP (operands[0], 0)))
+- current_function_uses_pic_offset_table = 1;
+-
+- if (! call_insn_operand (XEXP (operands[0], 0), Pmode))
+- XEXP (operands[0], 0) = copy_to_mode_reg (Pmode, XEXP (operands[0], 0));
+- if (TARGET_64BIT && INTVAL (operands[2]) >= 0)
+- {
+- rtx reg = gen_rtx_REG (QImode, 0);
+- emit_move_insn (reg, operands[2]);
+- insn = emit_call_insn (gen_call_exp (operands[0], operands[1]));
+- use_reg (&CALL_INSN_FUNCTION_USAGE (insn), reg);
+- DONE;
+- }
+- insn = emit_call_insn (gen_call_exp (operands[0], operands[1]));
+- DONE;
++ ix86_expand_call (NULL, operands[0], operands[1], operands[2], NULL);
++ DONE;
+ })
+
+-(define_expand "call_exp"
+- [(call (match_operand:QI 0 "" "")
+- (match_operand 1 "" ""))]
+- ""
+- "")
+-
+ (define_insn "*call_0"
+ [(call (mem:QI (match_operand 0 "constant_call_address_operand" ""))
+ (match_operand 1 "" ""))]
+@@ -13612,7 +13578,6 @@
+ [(set_attr "type" "call")])
+
+ ;; Call subroutine, returning value in operand 0
+-;; (which must be a hard register).
+
+ (define_expand "call_value_pop"
+ [(parallel [(set (match_operand 0 "" "")
+@@ -13623,20 +13588,9 @@
+ (match_operand:SI 4 "" "")))])]
+ "!TARGET_64BIT"
+ {
+- if (operands[4] == const0_rtx)
+- {
+- emit_insn (gen_call_value (operands[0], operands[1], operands[2],
+- constm1_rtx));
+- DONE;
+- }
+- /* Static functions and indirect calls don't need
+- current_function_uses_pic_offset_table. */
+- if (flag_pic
+- && GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF
+- && ! SYMBOL_REF_FLAG (XEXP (operands[1], 0)))
+- current_function_uses_pic_offset_table = 1;
+- if (! call_insn_operand (XEXP (operands[1], 0), Pmode))
+- XEXP (operands[1], 0) = copy_to_mode_reg (Pmode, XEXP (operands[1], 0));
++ ix86_expand_call (operands[0], operands[1], operands[2],
++ operands[3], operands[4]);
++ DONE;
+ })
+
+ (define_expand "call_value"
+@@ -13647,36 +13601,10 @@
+ ;; Operand 2 not used on the i386.
+ ""
+ {
+- rtx insn;
+- /* Static functions and indirect calls don't need
+- current_function_uses_pic_offset_table. */
+- if (flag_pic
+- && GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF
+- && ! SYMBOL_REF_FLAG (XEXP (operands[1], 0)))
+- current_function_uses_pic_offset_table = 1;
+- if (! call_insn_operand (XEXP (operands[1], 0), Pmode))
+- XEXP (operands[1], 0) = copy_to_mode_reg (Pmode, XEXP (operands[1], 0));
+- if (TARGET_64BIT && INTVAL (operands[3]) >= 0)
+- {
+- rtx reg = gen_rtx_REG (QImode, 0);
+- emit_move_insn (reg, operands[3]);
+- insn = emit_call_insn (gen_call_value_exp (operands[0], operands[1],
+- operands[2]));
+- use_reg (&CALL_INSN_FUNCTION_USAGE (insn), reg);
+- DONE;
+- }
+- insn = emit_call_insn (gen_call_value_exp (operands[0], operands[1],
+- operands[2]));
++ ix86_expand_call (operands[0], operands[1], operands[2], operands[3], NULL);
+ DONE;
+ })
+
+-(define_expand "call_value_exp"
+- [(set (match_operand 0 "" "")
+- (call (match_operand:QI 1 "" "")
+- (match_operand:SI 2 "" "")))]
+- ""
+- "")
+-
+ ;; Call subroutine returning any type.
+
+ (define_expand "untyped_call"
+@@ -13693,12 +13621,10 @@
+ simply pretend the untyped call returns a complex long double
+ value. */
+
+- emit_call_insn (TARGET_FLOAT_RETURNS_IN_80387
+- ? gen_call_value (gen_rtx_REG (XCmode, FIRST_FLOAT_REG),
+- operands[0], const0_rtx,
+- GEN_INT (SSE_REGPARM_MAX - 1))
+- : gen_call (operands[0], const0_rtx,
+- GEN_INT (SSE_REGPARM_MAX - 1)));
++ ix86_expand_call ((TARGET_FLOAT_RETURNS_IN_80387
++ ? gen_rtx_REG (XCmode, FIRST_FLOAT_REG) : NULL),
++ operands[0], const0_rtx, GEN_INT (SSE_REGPARM_MAX - 1),
++ NULL);
+
+ for (i = 0; i < XVECLEN (operands[2], 0); i++)
+ {
+@@ -13781,45 +13707,39 @@
+ ""
+ "ix86_expand_prologue (); DONE;")
+
+-(define_insn "prologue_set_got"
++(define_expand "set_got"
++ [(parallel [(set (match_operand:SI 0 "register_operand" "")
++ (unspec:SI [(const_int 0)] UNSPEC_SET_GOT))
++ (clobber (reg:CC 17))])]
++ "!TARGET_64BIT"
++ "")
++
++(define_insn "*set_got_nopic"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+- (unspec_volatile:SI
+- [(plus:SI (match_dup 0)
+- (plus:SI (match_operand:SI 1 "symbolic_operand" "")
+- (minus:SI (pc) (match_operand 2 "" ""))))] 1))
++ (unspec:SI [(const_int 0)] UNSPEC_SET_GOT))
+ (clobber (reg:CC 17))]
+- "!TARGET_64BIT"
+-{
+- if (GET_CODE (operands[2]) == LABEL_REF)
+- operands[2] = XEXP (operands[2], 0);
+- if (TARGET_DEEP_BRANCH_PREDICTION)
+- return "add{l}\t{%1, %0|%0, %1}";
+- else
+- return "add{l}\t{%1+[.-%X2], %0|%0, %a1+(.-%X2)}";
+-}
+- [(set_attr "type" "alu")
+- ; Since this insn may have two constant operands, we must set the
+- ; length manually.
+- (set_attr "length_immediate" "4")
+- (set_attr "mode" "SI")])
++ "!TARGET_64BIT && !flag_pic"
++ { return output_set_got (operands[0]); }
++ [(set_attr "type" "multi")
++ (set_attr "length" "11")])
++
++(define_insn "*set_got_deep"
++ [(set (match_operand:SI 0 "register_operand" "=b")
++ (unspec:SI [(const_int 0)] UNSPEC_SET_GOT))
++ (clobber (reg:CC 17))]
++ "!TARGET_64BIT && TARGET_DEEP_BRANCH_PREDICTION"
++ { return output_set_got (operands[0]); }
++ [(set_attr "type" "multi")
++ (set_attr "length" "11")])
+
+-(define_insn "prologue_get_pc"
++(define_insn "*set_got_nodeep"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+- (unspec_volatile:SI [(plus:SI (pc) (match_operand 1 "" ""))] 2))]
++ (unspec:SI [(const_int 0)] UNSPEC_SET_GOT))
++ (clobber (reg:CC 17))]
+ "!TARGET_64BIT"
+-{
+- if (GET_CODE (operands[1]) == LABEL_REF)
+- operands[1] = XEXP (operands[1], 0);
+- output_asm_insn ("call\t%X1", operands);
+- if (! TARGET_DEEP_BRANCH_PREDICTION)
+- {
+- ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
+- CODE_LABEL_NUMBER (operands[1]));
+- return "pop{l}\t%0";
+- }
+- RET;
+-}
+- [(set_attr "type" "multi")])
++ { return output_set_got (operands[0]); }
++ [(set_attr "type" "multi")
++ (set_attr "length" "12")])
+
+ (define_expand "epilogue"
+ [(const_int 1)]
+@@ -13996,6 +13916,127 @@
+ ;; ffshi2 is not useful -- 4 word prefix ops are needed, which is larger
+ ;; and slower than the two-byte movzx insn needed to do the work in SImode.
+ \f
++;; Thread-local storage patterns for ELF.
++;;
++;; Note that these code sequences must appear exactly as shown
++;; in order to allow linker relaxation.
++
++(define_insn "*tls_global_dynamic_gnu"
++ [(set (match_operand:SI 0 "register_operand" "=a")
++ (unspec:SI [(match_operand:SI 1 "register_operand" "b")
++ (match_operand:SI 2 "tls_symbolic_operand" "")
++ (match_operand:SI 3 "call_insn_operand" "")]
++ UNSPEC_TLS_GD))
++ (clobber (match_scratch:SI 4 "=d"))
++ (clobber (match_scratch:SI 5 "=c"))
++ (clobber (reg:CC 17))]
++ "TARGET_GNU_TLS"
++ "lea{l}\t{%a2@TLSGD(,%1,1), %0|%0, %a2@TLSGD[%1*1]}\;call\t%P3"
++ [(set_attr "type" "multi")
++ (set_attr "length" "12")])
++
++(define_insn "*tls_global_dynamic_sun"
++ [(set (match_operand:SI 0 "register_operand" "=a")
++ (unspec:SI [(match_operand:SI 1 "register_operand" "b")
++ (match_operand:SI 2 "tls_symbolic_operand" "")
++ (match_operand:SI 3 "call_insn_operand" "")]
++ UNSPEC_TLS_GD))
++ (clobber (match_scratch:SI 4 "=d"))
++ (clobber (match_scratch:SI 5 "=c"))
++ (clobber (reg:CC 17))]
++ "TARGET_SUN_TLS"
++ "lea{l}\t{%a2@DTLNDX(%1), %4|%4, %a2@DTLNDX[%1]}
++ push{l}\t%4\;call\t%a2@TLSPLT\;pop{l}\t%4\;nop"
++ [(set_attr "type" "multi")
++ (set_attr "length" "14")])
++
++(define_expand "tls_global_dynamic"
++ [(parallel [(set (match_operand:SI 0 "register_operand" "")
++ (unspec:SI
++ [(match_dup 2)
++ (match_operand:SI 1 "tls_symbolic_operand" "")
++ (match_dup 3)]
++ UNSPEC_TLS_GD))
++ (clobber (match_scratch:SI 4 ""))
++ (clobber (match_scratch:SI 5 ""))
++ (clobber (reg:CC 17))])]
++ ""
++{
++ if (!flag_pic)
++ abort ();
++ current_function_uses_pic_offset_table = 1;
++ operands[2] = pic_offset_table_rtx;
++ operands[3] = ix86_tls_get_addr ();
++})
++
++(define_insn "*tls_local_dynamic_base_gnu"
++ [(set (match_operand:SI 0 "register_operand" "=a")
++ (unspec:SI [(match_operand:SI 1 "register_operand" "b")
++ (match_operand:SI 2 "call_insn_operand" "")]
++ UNSPEC_TLS_LD_BASE))
++ (clobber (match_scratch:SI 3 "=d"))
++ (clobber (match_scratch:SI 4 "=c"))
++ (clobber (reg:CC 17))]
++ "TARGET_GNU_TLS"
++ "lea{l}\t{%&@TLSLDM(%1), %0|%0, %&@TLSLDM[%1]}\;call\t%P2"
++ [(set_attr "type" "multi")
++ (set_attr "length" "11")])
++
++(define_insn "*tls_local_dynamic_base_sun"
++ [(set (match_operand:SI 0 "register_operand" "=a")
++ (unspec:SI [(match_operand:SI 1 "register_operand" "b")
++ (match_operand:SI 2 "call_insn_operand" "")]
++ UNSPEC_TLS_LD_BASE))
++ (clobber (match_scratch:SI 3 "=d"))
++ (clobber (match_scratch:SI 4 "=c"))
++ (clobber (reg:CC 17))]
++ "TARGET_SUN_TLS"
++ "lea{l}\t{%&@TMDNX(%1), %3|%3, %&@TMDNX[%1]}
++ push{l}\t%3\;call\t%&@TLSPLT\;pop{l}\t%3"
++ [(set_attr "type" "multi")
++ (set_attr "length" "13")])
++
++(define_expand "tls_local_dynamic_base"
++ [(parallel [(set (match_operand:SI 0 "register_operand" "")
++ (unspec:SI [(match_dup 1) (match_dup 2)]
++ UNSPEC_TLS_LD_BASE))
++ (clobber (match_scratch:SI 3 ""))
++ (clobber (match_scratch:SI 4 ""))
++ (clobber (reg:CC 17))])]
++ ""
++{
++ if (!flag_pic)
++ abort ();
++ current_function_uses_pic_offset_table = 1;
++ operands[1] = pic_offset_table_rtx;
++ operands[2] = ix86_tls_get_addr ();
++})
++
++;; Local dynamic of a single variable is a lose. Show combine how
++;; to convert that back to global dynamic.
++
++(define_insn_and_split "*tls_local_dynamic_once"
++ [(set (match_operand:SI 0 "register_operand" "=a")
++ (plus:SI (unspec:SI [(match_operand:SI 1 "register_operand" "b")
++ (match_operand:SI 2 "call_insn_operand" "")]
++ UNSPEC_TLS_LD_BASE)
++ (const:SI (unspec:SI
++ [(match_operand:SI 3 "tls_symbolic_operand" "")]
++ UNSPEC_DTPOFF))))
++ (clobber (match_scratch:SI 4 "=d"))
++ (clobber (match_scratch:SI 5 "=c"))
++ (clobber (reg:CC 17))]
++ ""
++ "#"
++ ""
++ [(parallel [(set (match_dup 0)
++ (unspec:SI [(match_dup 1) (match_dup 3) (match_dup 2)]
++ UNSPEC_TLS_GD))
++ (clobber (match_dup 4))
++ (clobber (match_dup 5))
++ (clobber (reg:CC 17))])]
++ "")
++\f
+ ;; These patterns match the binary 387 instructions for addM3, subM3,
+ ;; mulM3 and divM3. There are three patterns for each of DFmode and
+ ;; SFmode. The first is the normal insn, the second the same insn but
+@@ -16835,7 +16876,7 @@
+ [(label_ref (match_operand 0 "" ""))]
+ "!TARGET_64BIT && flag_pic"
+ {
+- load_pic_register ();
++ emit_insn (gen_set_got (pic_offset_table_rtx));
+ DONE;
+ })
+ \f
+--- gcc/config/ia64/ia64-protos.h.jj Tue Apr 23 20:28:20 2002
++++ gcc/config/ia64/ia64-protos.h Wed Jun 19 19:33:52 2002
+@@ -31,6 +31,7 @@ extern int call_operand PARAMS((rtx, enu
+ extern int sdata_symbolic_operand PARAMS((rtx, enum machine_mode));
+ extern int got_symbolic_operand PARAMS((rtx, enum machine_mode));
+ extern int symbolic_operand PARAMS((rtx, enum machine_mode));
++extern int tls_symbolic_operand PARAMS((rtx, enum machine_mode));
+ extern int function_operand PARAMS((rtx, enum machine_mode));
+ extern int setjmp_operand PARAMS((rtx, enum machine_mode));
+ extern int move_operand PARAMS((rtx, enum machine_mode));
+@@ -67,6 +68,7 @@ extern int general_tfmode_operand PARAMS
+ extern int destination_tfmode_operand PARAMS((rtx, enum machine_mode));
+ extern int tfreg_or_fp01_operand PARAMS((rtx, enum machine_mode));
+
++extern rtx ia64_expand_move PARAMS ((rtx, rtx));
+ extern int ia64_move_ok PARAMS((rtx, rtx));
+ extern int ia64_depz_field_mask PARAMS((rtx, rtx));
+ extern rtx ia64_gp_save_reg PARAMS((int));
+--- gcc/config/ia64/ia64.c.jj Wed Jun 19 22:36:54 2002
++++ gcc/config/ia64/ia64.c Wed Jun 19 22:46:07 2002
+@@ -95,6 +95,13 @@ static const char * const ia64_output_re
+ /* String used with the -mfixed-range= option. */
+ const char *ia64_fixed_range_string;
+
++/* Determines whether we use adds, addl, or movl to generate our
++ TLS immediate offsets. */
++int ia64_tls_size = 22;
++
++/* String used with the -mtls-size= option. */
++const char *ia64_tls_size_string;
++
+ /* Determines whether we run our final scheduling pass or not. We always
+ avoid the normal second scheduling pass. */
+ static int ia64_flag_schedule_insns2;
+@@ -104,6 +111,8 @@ static int ia64_flag_schedule_insns2;
+
+ unsigned int ia64_section_threshold;
+ \f
++static rtx gen_tls_get_addr PARAMS ((void));
++static rtx gen_thread_pointer PARAMS ((void));
+ static int find_gr_spill PARAMS ((int));
+ static int next_scratch_gr_reg PARAMS ((void));
+ static void mark_reg_gr_used_mask PARAMS ((rtx, void *));
+@@ -214,6 +223,11 @@ static const struct attribute_spec ia64_
+ #undef TARGET_SCHED_CYCLE_DISPLAY
+ #define TARGET_SCHED_CYCLE_DISPLAY ia64_cycle_display
+
++#ifdef HAVE_AS_TLS
++#undef TARGET_HAVE_TLS
++#define TARGET_HAVE_TLS true
++#endif
++
+ struct gcc_target targetm = TARGET_INITIALIZER;
+ \f
+ /* Return 1 if OP is a valid operand for the MEM of a CALL insn. */
+@@ -250,7 +264,10 @@ sdata_symbolic_operand (op, mode)
+ if (CONSTANT_POOL_ADDRESS_P (op))
+ return GET_MODE_SIZE (get_pool_mode (op)) <= ia64_section_threshold;
+ else
+- return XSTR (op, 0)[0] == SDATA_NAME_FLAG_CHAR;
++ {
++ const char *str = XSTR (op, 0);
++ return (str[0] == ENCODE_SECTION_INFO_CHAR && str[1] == 's');
++ }
+
+ default:
+ break;
+@@ -324,6 +341,35 @@ symbolic_operand (op, mode)
+ return 0;
+ }
+
++/* Return tls_model if OP refers to a TLS symbol. */
++
++int
++tls_symbolic_operand (op, mode)
++ rtx op;
++ enum machine_mode mode ATTRIBUTE_UNUSED;
++{
++ const char *str;
++
++ if (GET_CODE (op) != SYMBOL_REF)
++ return 0;
++ str = XSTR (op, 0);
++ if (str[0] != ENCODE_SECTION_INFO_CHAR)
++ return 0;
++ switch (str[1])
++ {
++ case 'G':
++ return TLS_MODEL_GLOBAL_DYNAMIC;
++ case 'L':
++ return TLS_MODEL_LOCAL_DYNAMIC;
++ case 'i':
++ return TLS_MODEL_INITIAL_EXEC;
++ case 'l':
++ return TLS_MODEL_LOCAL_EXEC;
++ }
++ return 0;
++}
++
++
+ /* Return 1 if OP refers to a function. */
+
+ int
+@@ -922,6 +968,9 @@ ia64_expand_load_address (dest, src, scr
+ else
+ temp = dest;
+
++ if (tls_symbolic_operand (src, Pmode))
++ abort ();
++
+ if (TARGET_AUTO_PIC)
+ emit_insn (gen_load_gprel64 (temp, src));
+ else if (GET_CODE (src) == SYMBOL_REF && SYMBOL_REF_FLAG (src))
+@@ -964,6 +1013,185 @@ ia64_expand_load_address (dest, src, scr
+ emit_move_insn (dest, temp);
+ }
+
++static rtx
++gen_tls_get_addr ()
++{
++ static rtx tga;
++ if (!tga)
++ {
++ tga = init_one_libfunc ("__tls_get_addr");
++ ggc_add_rtx_root (&tga, 1);
++ }
++ return tga;
++}
++
++static rtx
++gen_thread_pointer ()
++{
++ static rtx tp;
++ if (!tp)
++ {
++ tp = gen_rtx_REG (Pmode, 13);
++ RTX_UNCHANGING_P (tp) = 1;
++ ggc_add_rtx_root (&tp, 1);
++ }
++ return tp;
++}
++
++rtx
++ia64_expand_move (op0, op1)
++ rtx op0, op1;
++{
++ enum machine_mode mode = GET_MODE (op0);
++
++ if (!reload_in_progress && !reload_completed && !ia64_move_ok (op0, op1))
++ op1 = force_reg (mode, op1);
++
++ if (mode == Pmode)
++ {
++ enum tls_model tls_kind;
++ if ((tls_kind = tls_symbolic_operand (op1, Pmode)))
++ {
++ rtx tga_op1, tga_op2, tga_ret, tga_eqv, tmp, insns;
++
++ switch (tls_kind)
++ {
++ case TLS_MODEL_GLOBAL_DYNAMIC:
++ start_sequence ();
++
++ tga_op1 = gen_reg_rtx (Pmode);
++ emit_insn (gen_load_ltoff_dtpmod (tga_op1, op1));
++ tga_op1 = gen_rtx_MEM (Pmode, tga_op1);
++ RTX_UNCHANGING_P (tga_op1) = 1;
++
++ tga_op2 = gen_reg_rtx (Pmode);
++ emit_insn (gen_load_ltoff_dtprel (tga_op2, op1));
++ tga_op2 = gen_rtx_MEM (Pmode, tga_op2);
++ RTX_UNCHANGING_P (tga_op2) = 1;
++
++ tga_ret = emit_library_call_value (gen_tls_get_addr (), NULL_RTX,
++ LCT_CONST, Pmode, 2, tga_op1,
++ Pmode, tga_op2, Pmode);
++
++ insns = get_insns ();
++ end_sequence ();
++
++ emit_libcall_block (insns, op0, tga_ret, op1);
++ return NULL_RTX;
++
++ case TLS_MODEL_LOCAL_DYNAMIC:
++ /* ??? This isn't the completely proper way to do local-dynamic
++ If the call to __tls_get_addr is used only by a single symbol,
++ then we should (somehow) move the dtprel to the second arg
++ to avoid the extra add. */
++ start_sequence ();
++
++ tga_op1 = gen_reg_rtx (Pmode);
++ emit_insn (gen_load_ltoff_dtpmod (tga_op1, op1));
++ tga_op1 = gen_rtx_MEM (Pmode, tga_op1);
++ RTX_UNCHANGING_P (tga_op1) = 1;
++
++ tga_op2 = const0_rtx;
++
++ tga_ret = emit_library_call_value (gen_tls_get_addr (), NULL_RTX,
++ LCT_CONST, Pmode, 2, tga_op1,
++ Pmode, tga_op2, Pmode);
++
++ insns = get_insns ();
++ end_sequence ();
++
++ tga_eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx),
++ UNSPEC_LD_BASE);
++ tmp = gen_reg_rtx (Pmode);
++ emit_libcall_block (insns, tmp, tga_ret, tga_eqv);
++
++ if (register_operand (op0, Pmode))
++ tga_ret = op0;
++ else
++ tga_ret = gen_reg_rtx (Pmode);
++ if (TARGET_TLS64)
++ {
++ emit_insn (gen_load_dtprel (tga_ret, op1));
++ emit_insn (gen_adddi3 (tga_ret, tmp, tga_ret));
++ }
++ else
++ emit_insn (gen_add_dtprel (tga_ret, tmp, op1));
++ if (tga_ret == op0)
++ return NULL_RTX;
++ op1 = tga_ret;
++ break;
++
++ case TLS_MODEL_INITIAL_EXEC:
++ tmp = gen_reg_rtx (Pmode);
++ emit_insn (gen_load_ltoff_tprel (tmp, op1));
++ tmp = gen_rtx_MEM (Pmode, tmp);
++ RTX_UNCHANGING_P (tmp) = 1;
++ tmp = force_reg (Pmode, tmp);
++
++ if (register_operand (op0, Pmode))
++ op1 = op0;
++ else
++ op1 = gen_reg_rtx (Pmode);
++ emit_insn (gen_adddi3 (op1, tmp, gen_thread_pointer ()));
++ if (op1 == op0)
++ return NULL_RTX;
++ break;
++
++ case TLS_MODEL_LOCAL_EXEC:
++ if (register_operand (op0, Pmode))
++ tmp = op0;
++ else
++ tmp = gen_reg_rtx (Pmode);
++ if (TARGET_TLS64)
++ {
++ emit_insn (gen_load_tprel (tmp, op1));
++ emit_insn (gen_adddi3 (tmp, gen_thread_pointer (), tmp));
++ }
++ else
++ emit_insn (gen_add_tprel (tmp, gen_thread_pointer (), op1));
++ if (tmp == op0)
++ return NULL_RTX;
++ op1 = tmp;
++ break;
++
++ default:
++ abort ();
++ }
++ }
++ else if (!TARGET_NO_PIC && symbolic_operand (op1, DImode))
++ {
++ /* Before optimization starts, delay committing to any particular
++ type of PIC address load. If this function gets deferred, we
++ may acquire information that changes the value of the
++ sdata_symbolic_operand predicate.
++
++ But don't delay for function pointers. Loading a function address
++ actually loads the address of the descriptor not the function.
++ If we represent these as SYMBOL_REFs, then they get cse'd with
++ calls, and we end up with calls to the descriptor address instead
++ of calls to the function address. Functions are not candidates
++ for sdata anyways.
++
++ Don't delay for LABEL_REF because the splitter loses REG_LABEL
++ notes. Don't delay for pool addresses on general principals;
++ they'll never become non-local behind our back. */
++
++ if (rtx_equal_function_value_matters
++ && GET_CODE (op1) != LABEL_REF
++ && ! (GET_CODE (op1) == SYMBOL_REF
++ && (SYMBOL_REF_FLAG (op1)
++ || CONSTANT_POOL_ADDRESS_P (op1)
++ || STRING_POOL_ADDRESS_P (op1))))
++ emit_insn (gen_movdi_symbolic (op0, op1));
++ else
++ ia64_expand_load_address (op0, op1, NULL_RTX);
++ return NULL_RTX;
++ }
++ }
++
++ return op1;
++}
++
+ rtx
+ ia64_gp_save_reg (setjmp_p)
+ int setjmp_p;
+@@ -3944,6 +4172,16 @@ ia64_override_options ()
+ if (ia64_fixed_range_string)
+ fix_range (ia64_fixed_range_string);
+
++ if (ia64_tls_size_string)
++ {
++ char *end;
++ unsigned long tmp = strtoul (ia64_tls_size_string, &end, 10);
++ if (*end || (tmp != 14 && tmp != 22 && tmp != 64))
++ error ("bad value (%s) for -mtls-size= switch", ia64_tls_size_string);
++ else
++ ia64_tls_size = tmp;
++ }
++
+ ia64_flag_schedule_insns2 = flag_schedule_insns_after_reload;
+ flag_schedule_insns_after_reload = 0;
+
+@@ -4607,6 +4845,13 @@ rtx_needs_barrier (x, flags, pred)
+ need_barrier |= rtx_needs_barrier (XVECEXP (x, 0, 2), flags, pred);
+ break;
+
++ case UNSPEC_LTOFF_DTPMOD:
++ case UNSPEC_LTOFF_DTPREL:
++ case UNSPEC_DTPREL:
++ case UNSPEC_LTOFF_TPREL:
++ case UNSPEC_TPREL:
++ break;
++
+ default:
+ abort ();
+ }
+@@ -6872,6 +7117,9 @@ ia64_encode_section_info (decl)
+ tree decl;
+ {
+ const char *symbol_str;
++ bool is_local, is_small;
++ rtx symbol;
++ char encoding = 0;
+
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ {
+@@ -6885,75 +7133,111 @@ ia64_encode_section_info (decl)
+ || GET_CODE (XEXP (DECL_RTL (decl), 0)) != SYMBOL_REF)
+ return;
+
+- symbol_str = XSTR (XEXP (DECL_RTL (decl), 0), 0);
++ symbol = XEXP (DECL_RTL (decl), 0);
++ symbol_str = XSTR (symbol, 0);
++
++ /* A non-decl is an entry in the constant pool. */
++ if (!DECL_P (decl))
++ is_local = true;
++ /* Static variables are always local. */
++ else if (! TREE_PUBLIC (decl))
++ is_local = true;
++ /* A variable is local if the user tells us so. */
++ else if (MODULE_LOCAL_P (decl))
++ is_local = true;
++ /* Otherwise, variables defined outside this object may not be local. */
++ else if (DECL_EXTERNAL (decl))
++ is_local = false;
++ /* Linkonce and weak data are never local. */
++ else if (DECL_ONE_ONLY (decl) || DECL_WEAK (decl))
++ is_local = false;
++ /* If PIC, then assume that any global name can be overridden by
++ symbols resolved from other modules. */
++ else if (flag_pic)
++ is_local = false;
++ /* Uninitialized COMMON variable may be unified with symbols
++ resolved from other modules. */
++ else if (DECL_COMMON (decl)
++ && (DECL_INITIAL (decl) == NULL
++ || DECL_INITIAL (decl) == error_mark_node))
++ is_local = false;
++ /* Otherwise we're left with initialized (or non-common) global data
++ which is of necessity defined locally. */
++ else
++ is_local = true;
+
+- /* We assume that -fpic is used only to create a shared library (dso).
+- With -fpic, no global data can ever be sdata.
+- Without -fpic, global common uninitialized data can never be sdata, since
+- it can unify with a real definition in a dso. */
+- /* ??? Actually, we can put globals in sdata, as long as we don't use gprel
+- to access them. The linker may then be able to do linker relaxation to
+- optimize references to them. Currently sdata implies use of gprel. */
+- /* We need the DECL_EXTERNAL check for C++. static class data members get
+- both TREE_STATIC and DECL_EXTERNAL set, to indicate that they are
+- statically allocated, but the space is allocated somewhere else. Such
+- decls can not be own data. */
+- if (! TARGET_NO_SDATA
+- && ((TREE_STATIC (decl) && ! DECL_EXTERNAL (decl)
+- && ! (DECL_ONE_ONLY (decl) || DECL_WEAK (decl))
+- && ! (TREE_PUBLIC (decl)
+- && (flag_pic
+- || (DECL_COMMON (decl)
+- && (DECL_INITIAL (decl) == 0
+- || DECL_INITIAL (decl) == error_mark_node)))))
+- || MODULE_LOCAL_P (decl))
+- /* Either the variable must be declared without a section attribute,
+- or the section must be sdata or sbss. */
+- && (DECL_SECTION_NAME (decl) == 0
+- || ! strcmp (TREE_STRING_POINTER (DECL_SECTION_NAME (decl)),
+- ".sdata")
+- || ! strcmp (TREE_STRING_POINTER (DECL_SECTION_NAME (decl)),
+- ".sbss")))
++ is_small = false;
++ if (TARGET_NO_SDATA)
++ ;
++ else if (TREE_CODE (decl) == VAR_DECL && DECL_SECTION_NAME (decl))
++ {
++ const char *section = TREE_STRING_POINTER (DECL_SECTION_NAME (decl));
++ if (strcmp (section, ".sdata") == 0
++ || strcmp (section, ".sbss") == 0)
++ is_small = true;
++ }
++ else
+ {
+ HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (decl));
+
+- /* If the variable has already been defined in the output file, then it
+- is too late to put it in sdata if it wasn't put there in the first
+- place. The test is here rather than above, because if it is already
+- in sdata, then it can stay there. */
+-
+- if (TREE_ASM_WRITTEN (decl))
+- ;
+-
+- /* If this is an incomplete type with size 0, then we can't put it in
+- sdata because it might be too big when completed.
+- Objects bigger than threshold should have SDATA_NAME_FLAG_CHAR
+- added if they are in .sdata or .sbss explicitely. */
+- else if (((size > 0
+- && size <= (HOST_WIDE_INT) ia64_section_threshold)
+- || DECL_SECTION_NAME (decl))
+- && symbol_str[0] != SDATA_NAME_FLAG_CHAR)
+- {
+- size_t len = strlen (symbol_str);
+- char *newstr = alloca (len + 1);
+- const char *string;
++ /* If this is an incomplete type with size 0, then we can't put it
++ in sdata because it might be too big when completed. */
++ if (size > 0 && size <= ia64_section_threshold)
++ is_small = true;
++ }
+
+- *newstr = SDATA_NAME_FLAG_CHAR;
+- memcpy (newstr + 1, symbol_str, len + 1);
+-
+- string = ggc_alloc_string (newstr, len + 1);
+- XSTR (XEXP (DECL_RTL (decl), 0), 0) = string;
++ if (TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL (decl))
++ {
++ enum tls_model kind;
++ if (!flag_pic)
++ {
++ if (is_local)
++ kind = TLS_MODEL_LOCAL_EXEC;
++ else
++ kind = TLS_MODEL_INITIAL_EXEC;
+ }
+- }
+- /* This decl is marked as being in small data/bss but it shouldn't
+- be; one likely explanation for this is that the decl has been
+- moved into a different section from the one it was in when
+- ENCODE_SECTION_INFO was first called. Remove the '@'. */
+- else if (symbol_str[0] == SDATA_NAME_FLAG_CHAR)
++ else if (is_local)
++ kind = TLS_MODEL_LOCAL_DYNAMIC;
++ else
++ kind = TLS_MODEL_GLOBAL_DYNAMIC;
++ if (kind < flag_tls_default)
++ kind = flag_tls_default;
++
++ encoding = " GLil"[kind];
++ }
++ /* Determine if DECL will wind up in .sdata/.sbss. */
++ else if (is_local && is_small)
++ encoding = 's';
++
++ /* Finally, encode this into the symbol string. */
++ if (encoding)
+ {
+- XSTR (XEXP (DECL_RTL (decl), 0), 0)
+- = ggc_strdup (symbol_str + 1);
++ char *newstr;
++ size_t len;
++
++ if (symbol_str[0] == ENCODE_SECTION_INFO_CHAR)
++ {
++ if (encoding == symbol_str[1])
++ return;
++ /* ??? Sdata became thread or thread becaome not thread. Lose. */
++ abort ();
++ }
++
++ len = strlen (symbol_str);
++ newstr = alloca (len + 3);
++ newstr[0] = ENCODE_SECTION_INFO_CHAR;
++ newstr[1] = encoding;
++ memcpy (newstr + 2, symbol_str, len + 1);
++
++ XSTR (symbol, 0) = ggc_alloc_string (newstr, len + 2);
+ }
++
++ /* This decl is marked as being in small data/bss but it shouldn't be;
++ one likely explanation for this is that the decl has been moved into
++ a different section from the one it was in when encode_section_info
++ was first called. Remove the encoding. */
++ else if (symbol_str[0] == ENCODE_SECTION_INFO_CHAR)
++ XSTR (symbol, 0) = ggc_strdup (symbol_str + 2);
+ }
+ \f
+ /* Output assembly directives for prologue regions. */
+--- gcc/config/ia64/ia64.h.jj Tue Apr 23 20:28:21 2002
++++ gcc/config/ia64/ia64.h Wed Jun 19 19:33:52 2002
+@@ -109,6 +109,11 @@ extern int target_flags;
+
+ #define TARGET_DWARF2_ASM (target_flags & MASK_DWARF2_ASM)
+
++extern int ia64_tls_size;
++#define TARGET_TLS14 (ia64_tls_size == 14)
++#define TARGET_TLS22 (ia64_tls_size == 22)
++#define TARGET_TLS64 (ia64_tls_size == 64)
++
+ /* This macro defines names of command options to set and clear bits in
+ `target_flags'. Its definition is an initializer with a subgrouping for
+ each command option. */
+@@ -177,10 +182,13 @@ extern int target_flags;
+ subgrouping for each command option. */
+
+ extern const char *ia64_fixed_range_string;
++extern const char *ia64_tls_size_string;
+ #define TARGET_OPTIONS \
+ { \
+ { "fixed-range=", &ia64_fixed_range_string, \
+ N_("Specify range of registers to make fixed")}, \
++ { "tls-size=", &ia64_tls_size_string, \
++ N_("Specify bit size of immediate TLS offsets")}, \
+ }
+
+ /* Sometimes certain combinations of command options do not make sense on a
+@@ -1801,7 +1809,7 @@ do { \
+ && (DECL_ONE_ONLY (DECL) || DECL_WEAK (DECL) || DECL_COMMON (DECL) \
+ || DECL_SECTION_NAME (DECL) != 0))
+
+-#define SDATA_NAME_FLAG_CHAR '@'
++#define ENCODE_SECTION_INFO_CHAR '@'
+
+ #define IA64_DEFAULT_GVALUE 8
+
+@@ -1811,8 +1819,8 @@ do { \
+ #define STRIP_NAME_ENCODING(VAR, SYMBOL_NAME) \
+ do { \
+ (VAR) = (SYMBOL_NAME); \
+- if ((VAR)[0] == SDATA_NAME_FLAG_CHAR) \
+- (VAR)++; \
++ if ((VAR)[0] == ENCODE_SECTION_INFO_CHAR) \
++ (VAR) += 2; \
+ if ((VAR)[0] == '*') \
+ (VAR)++; \
+ } while (0)
+--- gcc/config/ia64/ia64.md.jj Tue Apr 23 20:28:21 2002
++++ gcc/config/ia64/ia64.md Wed Jun 19 19:33:52 2002
+@@ -68,7 +68,19 @@
+ ;; 23 cycle display
+ ;; 24 addp4
+ ;; 25 prologue_use
+-;;
++
++;; More unspec:
++
++(define_constants
++ [; Relocations
++ (UNSPEC_LTOFF_DTPMOD 14)
++ (UNSPEC_LTOFF_DTPREL 15)
++ (UNSPEC_DTPREL 16)
++ (UNSPEC_LTOFF_TPREL 17)
++ (UNSPEC_TPREL 18)
++ (UNSPEC_LD_BASE 26)
++ ])
++
+ ;; unspec_volatile:
+ ;; 0 alloc
+ ;; 1 blockage
+@@ -272,12 +284,12 @@
+ [(set (match_operand:QI 0 "general_operand" "")
+ (match_operand:QI 1 "general_operand" ""))]
+ ""
+- "
+ {
+- if (! reload_in_progress && ! reload_completed
+- && ! ia64_move_ok (operands[0], operands[1]))
+- operands[1] = force_reg (QImode, operands[1]);
+-}")
++ rtx op1 = ia64_expand_move (operands[0], operands[1]);
++ if (!op1)
++ DONE;
++ operands[1] = op1;
++})
+
+ (define_insn "*movqi_internal"
+ [(set (match_operand:QI 0 "destination_operand" "=r,r,r, m, r,*f,*f")
+@@ -297,12 +309,12 @@
+ [(set (match_operand:HI 0 "general_operand" "")
+ (match_operand:HI 1 "general_operand" ""))]
+ ""
+- "
+ {
+- if (! reload_in_progress && ! reload_completed
+- && ! ia64_move_ok (operands[0], operands[1]))
+- operands[1] = force_reg (HImode, operands[1]);
+-}")
++ rtx op1 = ia64_expand_move (operands[0], operands[1]);
++ if (!op1)
++ DONE;
++ operands[1] = op1;
++})
+
+ (define_insn "*movhi_internal"
+ [(set (match_operand:HI 0 "destination_operand" "=r,r,r, m, r,*f,*f")
+@@ -322,12 +334,12 @@
+ [(set (match_operand:SI 0 "general_operand" "")
+ (match_operand:SI 1 "general_operand" ""))]
+ ""
+- "
+ {
+- if (! reload_in_progress && ! reload_completed
+- && ! ia64_move_ok (operands[0], operands[1]))
+- operands[1] = force_reg (SImode, operands[1]);
+-}")
++ rtx op1 = ia64_expand_move (operands[0], operands[1]);
++ if (!op1)
++ DONE;
++ operands[1] = op1;
++})
+
+ (define_insn "*movsi_internal"
+ [(set (match_operand:SI 0 "destination_operand" "=r,r,r,r, m, r,*f,*f, r,*d")
+@@ -351,32 +363,12 @@
+ [(set (match_operand:DI 0 "general_operand" "")
+ (match_operand:DI 1 "general_operand" ""))]
+ ""
+- "
+ {
+- if (! reload_in_progress && ! reload_completed
+- && ! ia64_move_ok (operands[0], operands[1]))
+- operands[1] = force_reg (DImode, operands[1]);
+- if (! TARGET_NO_PIC && symbolic_operand (operands[1], DImode))
+- {
+- /* Before optimization starts, delay committing to any particular
+- type of PIC address load. If this function gets deferred, we
+- may acquire information that changes the value of the
+- sdata_symbolic_operand predicate. */
+- /* But don't delay for function pointers. Loading a function address
+- actually loads the address of the descriptor not the function.
+- If we represent these as SYMBOL_REFs, then they get cse'd with
+- calls, and we end up with calls to the descriptor address instead of
+- calls to the function address. Functions are not candidates for
+- sdata anyways. */
+- if (rtx_equal_function_value_matters
+- && ! (GET_CODE (operands[1]) == SYMBOL_REF
+- && SYMBOL_REF_FLAG (operands[1])))
+- emit_insn (gen_movdi_symbolic (operands[0], operands[1], gen_reg_rtx (DImode)));
+- else
+- ia64_expand_load_address (operands[0], operands[1], NULL_RTX);
+- DONE;
+- }
+-}")
++ rtx op1 = ia64_expand_move (operands[0], operands[1]);
++ if (!op1)
++ DONE;
++ operands[1] = op1;
++})
+
+ ;; This is used during early compilation to delay the decision on
+ ;; how to refer to a variable as long as possible. This is especially
+@@ -384,19 +376,22 @@
+ ;; deferred functions, since we may acquire additional information
+ ;; on the variables used in the meantime.
+
+-;; ??? This causes us to lose REG_LABEL notes, because the insn splitter
+-;; does not attempt to preserve any REG_NOTES on the input instruction.
+-
+ (define_insn_and_split "movdi_symbolic"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (match_operand:DI 1 "symbolic_operand" "s"))
+- (clobber (match_operand:DI 2 "register_operand" "+r"))
++ (clobber (match_scratch:DI 2 "=r"))
+ (use (reg:DI 1))]
+ ""
+ "* abort ();"
+- ""
++ "!no_new_pseudos || reload_completed"
+ [(const_int 0)]
+- "ia64_expand_load_address (operands[0], operands[1], operands[2]); DONE;")
++{
++ rtx scratch = operands[2];
++ if (!reload_completed)
++ gen_reg_rtx (Pmode);
++ ia64_expand_load_address (operands[0], operands[1], scratch);
++ DONE;
++})
+
+ (define_insn "*movdi_internal"
+ [(set (match_operand:DI 0 "destination_operand"
+@@ -510,6 +505,131 @@
+ "addl %0 = @ltoff(%1), gp"
+ [(set_attr "itanium_class" "ialu")])
+
++(define_insn "load_ltoff_dtpmod"
++ [(set (match_operand:DI 0 "register_operand" "=r")
++ (plus:DI (reg:DI 1)
++ (unspec:DI [(match_operand:DI 1 "symbolic_operand" "")]
++ UNSPEC_LTOFF_DTPMOD)))]
++ ""
++ "addl %0 = @ltoff(@dtpmod(%1)), gp"
++ [(set_attr "itanium_class" "ialu")])
++
++(define_insn "load_ltoff_dtprel"
++ [(set (match_operand:DI 0 "register_operand" "=r")
++ (plus:DI (reg:DI 1)
++ (unspec:DI [(match_operand:DI 1 "symbolic_operand" "")]
++ UNSPEC_LTOFF_DTPREL)))]
++ ""
++ "addl %0 = @ltoff(@dtprel(%1)), gp"
++ [(set_attr "itanium_class" "ialu")])
++
++(define_expand "load_dtprel"
++ [(set (match_operand:DI 0 "register_operand" "")
++ (unspec:DI [(match_operand:DI 1 "symbolic_operand" "")]
++ UNSPEC_DTPREL))]
++ ""
++ "")
++
++(define_insn "*load_dtprel64"
++ [(set (match_operand:DI 0 "register_operand" "=r")
++ (unspec:DI [(match_operand:DI 1 "symbolic_operand" "")]
++ UNSPEC_DTPREL))]
++ "TARGET_TLS64"
++ "movl %0 = @dtprel(%1)"
++ [(set_attr "itanium_class" "long_i")])
++
++(define_insn "*load_dtprel22"
++ [(set (match_operand:DI 0 "register_operand" "=r")
++ (unspec:DI [(match_operand:DI 1 "symbolic_operand" "")]
++ UNSPEC_DTPREL))]
++ ""
++ "addl %0 = @dtprel(%1), r0"
++ [(set_attr "itanium_class" "ialu")])
++
++(define_expand "add_dtprel"
++ [(set (match_operand:DI 0 "register_operand" "")
++ (plus:DI (match_operand:DI 1 "register_operand" "")
++ (unspec:DI [(match_operand:DI 2 "symbolic_operand" "")]
++ UNSPEC_DTPREL)))]
++ "!TARGET_TLS64"
++ "")
++
++(define_insn "*add_dtprel14"
++ [(set (match_operand:DI 0 "register_operand" "=r")
++ (plus:DI (match_operand:DI 1 "register_operand" "r")
++ (unspec:DI [(match_operand:DI 2 "symbolic_operand" "")]
++ UNSPEC_DTPREL)))]
++ "TARGET_TLS14"
++ "adds %0 = @dtprel(%2), %1"
++ [(set_attr "itanium_class" "ialu")])
++
++(define_insn "*add_dtprel22"
++ [(set (match_operand:DI 0 "register_operand" "=r")
++ (plus:DI (match_operand:DI 1 "register_operand" "a")
++ (unspec:DI [(match_operand:DI 2 "symbolic_operand" "")]
++ UNSPEC_DTPREL)))]
++ "TARGET_TLS22"
++ "addl %0 = @dtprel(%2), %1"
++ [(set_attr "itanium_class" "ialu")])
++
++(define_insn "load_ltoff_tprel"
++ [(set (match_operand:DI 0 "register_operand" "=r")
++ (plus:DI (reg:DI 1)
++ (unspec:DI [(match_operand:DI 1 "symbolic_operand" "")]
++ UNSPEC_LTOFF_TPREL)))]
++ ""
++ "addl %0 = @ltoff(@tprel(%1)), gp"
++ [(set_attr "itanium_class" "ialu")])
++
++(define_expand "load_tprel"
++ [(set (match_operand:DI 0 "register_operand" "")
++ (unspec:DI [(match_operand:DI 1 "symbolic_operand" "")]
++ UNSPEC_TPREL))]
++ ""
++ "")
++
++(define_insn "*load_tprel64"
++ [(set (match_operand:DI 0 "register_operand" "=r")
++ (unspec:DI [(match_operand:DI 1 "symbolic_operand" "")]
++ UNSPEC_TPREL))]
++ "TARGET_TLS64"
++ "movl %0 = @tprel(%1)"
++ [(set_attr "itanium_class" "long_i")])
++
++(define_insn "*load_tprel22"
++ [(set (match_operand:DI 0 "register_operand" "=r")
++ (unspec:DI [(match_operand:DI 1 "symbolic_operand" "")]
++ UNSPEC_TPREL))]
++ ""
++ "addl %0 = @tprel(%1), r0"
++ [(set_attr "itanium_class" "ialu")])
++
++(define_expand "add_tprel"
++ [(set (match_operand:DI 0 "register_operand" "")
++ (plus:DI (match_operand:DI 1 "register_operand" "")
++ (unspec:DI [(match_operand:DI 2 "symbolic_operand" "")]
++ UNSPEC_TPREL)))]
++ "!TARGET_TLS64"
++ "")
++
++(define_insn "*add_tprel14"
++ [(set (match_operand:DI 0 "register_operand" "=r")
++ (plus:DI (match_operand:DI 1 "register_operand" "r")
++ (unspec:DI [(match_operand:DI 2 "symbolic_operand" "")]
++ UNSPEC_TPREL)))]
++ "TARGET_TLS14"
++ "adds %0 = @tprel(%2), %1"
++ [(set_attr "itanium_class" "ialu")])
++
++(define_insn "*add_tprel22"
++ [(set (match_operand:DI 0 "register_operand" "=r")
++ (plus:DI (match_operand:DI 1 "register_operand" "a")
++ (unspec:DI [(match_operand:DI 2 "symbolic_operand" "")]
++ UNSPEC_TPREL)))]
++ "TARGET_TLS22"
++ "addl %0 = @tprel(%2), %1"
++ [(set_attr "itanium_class" "ialu")])
++
+ ;; With no offsettable memory references, we've got to have a scratch
+ ;; around to play with the second word.
+ (define_expand "movti"
+@@ -517,12 +637,12 @@
+ (match_operand:TI 1 "general_operand" ""))
+ (clobber (match_scratch:DI 2 ""))])]
+ ""
+- "
+ {
+- if (! reload_in_progress && ! reload_completed
+- && ! ia64_move_ok (operands[0], operands[1]))
+- operands[1] = force_reg (TImode, operands[1]);
+-}")
++ rtx op1 = ia64_expand_move (operands[0], operands[1]);
++ if (!op1)
++ DONE;
++ operands[1] = op1;
++})
+
+ (define_insn_and_split "*movti_internal"
+ [(set (match_operand:TI 0 "nonimmediate_operand" "=r,r,m")
+@@ -639,12 +759,12 @@
+ [(set (match_operand:SF 0 "general_operand" "")
+ (match_operand:SF 1 "general_operand" ""))]
+ ""
+- "
+ {
+- if (! reload_in_progress && ! reload_completed
+- && ! ia64_move_ok (operands[0], operands[1]))
+- operands[1] = force_reg (SFmode, operands[1]);
+-}")
++ rtx op1 = ia64_expand_move (operands[0], operands[1]);
++ if (!op1)
++ DONE;
++ operands[1] = op1;
++})
+
+ (define_insn "*movsf_internal"
+ [(set (match_operand:SF 0 "destination_operand" "=f,f, Q,*r, f,*r,*r, m")
+@@ -665,12 +785,12 @@
+ [(set (match_operand:DF 0 "general_operand" "")
+ (match_operand:DF 1 "general_operand" ""))]
+ ""
+- "
+ {
+- if (! reload_in_progress && ! reload_completed
+- && ! ia64_move_ok (operands[0], operands[1]))
+- operands[1] = force_reg (DFmode, operands[1]);
+-}")
++ rtx op1 = ia64_expand_move (operands[0], operands[1]);
++ if (!op1)
++ DONE;
++ operands[1] = op1;
++})
+
+ (define_insn "*movdf_internal"
+ [(set (match_operand:DF 0 "destination_operand" "=f,f, Q,*r, f,*r,*r, m")
+--- gcc/config/ia64/sysv4.h.jj Mon Jun 3 17:24:18 2002
++++ gcc/config/ia64/sysv4.h Wed Jun 19 19:33:52 2002
+@@ -42,8 +42,7 @@ extern int size_directive_output;
+ #undef ASM_OUTPUT_ALIGNED_LOCAL
+ #define ASM_OUTPUT_ALIGNED_DECL_LOCAL(FILE, DECL, NAME, SIZE, ALIGN) \
+ do { \
+- if ((DECL) \
+- && XSTR (XEXP (DECL_RTL (DECL), 0), 0)[0] == SDATA_NAME_FLAG_CHAR) \
++ if ((DECL) && sdata_symbolic_operand (XEXP (DECL_RTL (DECL), 0), Pmode)) \
+ sbss_section (); \
+ else \
+ bss_section (); \
+@@ -62,8 +61,8 @@ do { \
+ #define ASM_OUTPUT_LABELREF(STREAM, NAME) \
+ do { \
+ const char *name_ = NAME; \
+- if (*name_ == SDATA_NAME_FLAG_CHAR) \
+- name_++; \
++ if (*name_ == ENCODE_SECTION_INFO_CHAR) \
++ name_ += 2; \
+ if (*name_ == '*') \
+ name_++; \
+ else \
+@@ -149,9 +148,11 @@ do { \
+ 0 .text
+ 1 .rodata
+ 2 .data
+- 3 .sdata
+- 4 .bss
++ 3 .bss
++ 4 .sdata
+ 5 .sbss
++ 6 .tdata
++ 7 .tbss
+ */
+ #define DO_SELECT_SECTION(SECNUM, DECL, RELOC) \
+ do \
+@@ -167,9 +168,10 @@ do { \
+ } \
+ else if (TREE_CODE (DECL) == VAR_DECL) \
+ { \
+- if (XSTR (XEXP (DECL_RTL (DECL), 0), 0)[0] \
+- == SDATA_NAME_FLAG_CHAR) \
+- SECNUM = 3; \
++ if (DECL_THREAD_LOCAL (DECL)) \
++ SECNUM = 6; \
++ else if (sdata_symbolic_operand (XEXP (DECL_RTL (DECL), 0), Pmode))\
++ SECNUM = 4; \
+ /* ??? We need the extra RELOC check, because the default \
+ is to only check RELOC if flag_pic is set, and we don't \
+ set flag_pic (yet?). */ \
+@@ -184,13 +186,18 @@ do { \
+ SECNUM = 0x201; \
+ else \
+ SECNUM = 0x301; \
++ \
++ if (SECNUM >= 2 \
++ && (!DECL_INITIAL (DECL) \
++ || DECL_INITIAL (DECL) == error_mark_node)) \
++ SECNUM++; \
+ } \
+ /* This could be a CONSTRUCTOR containing ADDR_EXPR of a VAR_DECL, \
+ in which case we can't put it in a shared library rodata. */ \
+ else if (flag_pic && (RELOC)) \
+- SECNUM = 3; \
+- else \
+ SECNUM = 2; \
++ else \
++ SECNUM = 1; \
+ } \
+ while (0)
+
+@@ -206,8 +213,8 @@ do { \
+ text_section, \
+ const_section, \
+ data_section, \
+- sdata_section, \
+ bss_section, \
++ sdata_section, \
+ sbss_section \
+ }; \
+ \
+@@ -217,6 +224,12 @@ do { \
+ \
+ switch (sec) \
+ { \
++ case 6: \
++ named_section (NULL_TREE, ".tdata", RELOC); \
++ break; \
++ case 7: \
++ named_section (NULL_TREE, ".tbss", RELOC); \
++ break; \
+ case 0x101: \
+ mergeable_string_section (DECL, ALIGN, 0); \
+ break; \
+@@ -244,9 +257,11 @@ do { \
+ { ".text.", ".gnu.linkonce.t." }, \
+ { ".rodata.", ".gnu.linkonce.r." }, \
+ { ".data.", ".gnu.linkonce.d." }, \
+- { ".sdata.", ".gnu.linkonce.s." }, \
+ { ".bss.", ".gnu.linkonce.b." }, \
+- { ".sbss.", ".gnu.linkonce.sb." } \
++ { ".sdata.", ".gnu.linkonce.s." }, \
++ { ".sbss.", ".gnu.linkonce.sb." }, \
++ { ".tdata.", ".gnu.linkonce.td." }, \
++ { ".tbss.", ".gnu.linkonce.tb." } \
+ }; \
+ \
+ int nlen, plen, sec; \
+--- gcc/config/elfos.h.jj Tue Dec 18 01:30:48 2001
++++ gcc/config/elfos.h Wed Jun 19 19:33:52 2002
+@@ -288,24 +288,28 @@ const_section () \
+ const char *name; \
+ char *string; \
+ const char *prefix; \
+- static const char *const prefixes[4][2] = \
++ static const char *const prefixes[][2] = \
+ { \
+ { ".text.", ".gnu.linkonce.t." }, \
+ { ".rodata.", ".gnu.linkonce.r." }, \
+ { ".data.", ".gnu.linkonce.d." }, \
+- { ".bss.", ".gnu.linkonce.b." } \
++ { ".bss.", ".gnu.linkonce.b." }, \
++ { ".tdata", ".gnu.linkonce.td." }, \
++ { ".tbss", ".gnu.linkonce.tb." }, \
+ }; \
+ \
+ if (TREE_CODE (DECL) == FUNCTION_DECL) \
+ sec = 0; \
+ else if (DECL_INITIAL (DECL) == 0 \
+ || DECL_INITIAL (DECL) == error_mark_node) \
+- sec = 3; \
++ sec = 3; \
+ else if (DECL_READONLY_SECTION (DECL, RELOC)) \
+ sec = 1; \
+ else \
+ sec = 2; \
+- \
++ if (TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL (decl)) \
++ sec = (sec == 3 ? 5 : 4); \
++ \
+ name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (DECL)); \
+ /* Strip off any encoding in name. */ \
+ STRIP_NAME_ENCODING (name, name); \
+@@ -367,10 +371,18 @@ const_section () \
+ } \
+ else if (TREE_CODE (DECL) == VAR_DECL) \
+ { \
+- if (!TREE_READONLY (DECL) || TREE_SIDE_EFFECTS (DECL) \
+- || !DECL_INITIAL (DECL) \
+- || (DECL_INITIAL (DECL) != error_mark_node \
+- && !TREE_CONSTANT (DECL_INITIAL (DECL)))) \
++ if (DECL_THREAD_LOCAL (DECL)) \
++ { \
++ if (!DECL_INITIAL (DECL) \
++ || DECL_INITIAL (DECL) == error_mark_node) \
++ named_section (NULL_TREE, ".tbss", RELOC); \
++ else \
++ named_section (NULL_TREE, ".tdata", RELOC); \
++ } \
++ else if (!TREE_READONLY (DECL) || TREE_SIDE_EFFECTS (DECL) \
++ || !DECL_INITIAL (DECL) \
++ || (DECL_INITIAL (DECL) != error_mark_node \
++ && !TREE_CONSTANT (DECL_INITIAL (DECL)))) \
+ { \
+ if (flag_pic && ((RELOC) & 2)) \
+ named_section (NULL_TREE, ".data.rel", RELOC); \
+--- gcc/fixinc/fixincl.x.jj Thu May 2 12:15:15 2002
++++ gcc/fixinc/fixincl.x Wed Jun 19 19:33:52 2002
+@@ -5,7 +5,7 @@
+ * files which are fixed to work correctly with ANSI C and placed in a
+ * directory that GNU C will search.
+ *
+- * This file contains 142 fixup descriptions.
++ * This file contains 143 fixup descriptions.
+ *
+ * See README for more information.
+ *
+@@ -4568,6 +4568,41 @@ static const char* apzSysz_Stdlib_For_Su
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
++ * Description of Thread_Keyword fix
++ */
++tSCC zThread_KeywordName[] =
++ "thread_keyword";
++
++/*
++ * File name selection pattern
++ */
++tSCC zThread_KeywordList[] =
++ "|pthread.h|bits/sigthread.h|";
++/*
++ * Machine/OS name selection pattern
++ */
++#define apzThread_KeywordMachs (const char**)NULL
++
++/*
++ * content selection pattern - do fix if pattern found
++ */
++tSCC zThread_KeywordSelect0[] =
++ " __thread([,)])";
++
++#define THREAD_KEYWORD_TEST_CT 1
++static tTestDesc aThread_KeywordTests[] = {
++ { TT_EGREP, zThread_KeywordSelect0, (regex_t*)NULL }, };
++
++/*
++ * Fix Command Arguments for Thread_Keyword
++ */
++static const char* apzThread_KeywordPatch[] = {
++ "format",
++ " __thr%1",
++ (char*)NULL };
++
++/* * * * * * * * * * * * * * * * * * * * * * * * * *
++ *
+ * Description of Tinfo_Cplusplus fix
+ */
+ tSCC zTinfo_CplusplusName[] =
+@@ -5581,9 +5616,9 @@ static const char* apzX11_SprintfPatch[]
+ *
+ * List of all fixes
+ */
+-#define REGEX_COUNT 151
++#define REGEX_COUNT 152
+ #define MACH_LIST_SIZE_LIMIT 279
+-#define FIX_COUNT 142
++#define FIX_COUNT 143
+
+ /*
+ * Enumerate the fixes
+@@ -5705,6 +5740,7 @@ typedef enum {
+ SVR4_PROFIL_FIXIDX,
+ SYSV68_STRING_FIXIDX,
+ SYSZ_STDLIB_FOR_SUN_FIXIDX,
++ THREAD_KEYWORD_FIXIDX,
+ TINFO_CPLUSPLUS_FIXIDX,
+ ULTRIX_ATEXIT_PARAM_FIXIDX,
+ ULTRIX_ATOF_PARAM_FIXIDX,
+@@ -6314,6 +6350,11 @@ tFixDesc fixDescList[ FIX_COUNT ] = {
+ SYSZ_STDLIB_FOR_SUN_TEST_CT, FD_MACH_ONLY | FD_SUBROUTINE,
+ aSysz_Stdlib_For_SunTests, apzSysz_Stdlib_For_SunPatch, 0 },
+
++ { zThread_KeywordName, zThread_KeywordList,
++ apzThread_KeywordMachs,
++ THREAD_KEYWORD_TEST_CT, FD_MACH_ONLY | FD_SUBROUTINE,
++ aThread_KeywordTests, apzThread_KeywordPatch, 0 },
++
+ { zTinfo_CplusplusName, zTinfo_CplusplusList,
+ apzTinfo_CplusplusMachs,
+ TINFO_CPLUSPLUS_TEST_CT, FD_MACH_ONLY | FD_SUBROUTINE,
+--- gcc/fixinc/inclhack.def.jj Thu May 2 12:15:17 2002
++++ gcc/fixinc/inclhack.def Wed Jun 19 19:33:52 2002
+@@ -1,4 +1,3 @@
+-
+ /* -*- Mode: C -*- */
+
+ autogen definitions fixincl;
+@@ -2886,6 +2885,24 @@ fix = {
+ };
+
+
++/*
++ * __thread is now a keyword.
++ */
++fix = {
++ hackname = thread_keyword;
++ files = "pthread.h";
++ files = "bits/sigthread.h";
++ select = " __thread([,)])";
++ c_fix = format;
++ c_fix_arg = " __thr%1";
++
++ test_text =
++ "extern int pthread_create (pthread_t *__restrict __thread,\n"
++ "extern int pthread_kill (pthread_t __thread, int __signo);\n"
++ "extern int pthread_cancel (pthread_t __thread);";
++};
++
++
+ /*
+ * if the #if says _cplusplus, not the double underscore __cplusplus
+ * that it should be
+--- gcc/testsuite/g++.dg/dg.exp.jj Thu Feb 14 13:26:12 2002
++++ gcc/testsuite/g++.dg/dg.exp Wed Jun 19 19:33:52 2002
+@@ -28,10 +28,12 @@ if ![info exists DEFAULT_CXXFLAGS] then
+ # Initialize `dg'.
+ dg-init
+
+-# Gather a list of all tests, excluding those in special/; those are handled
+-# well, specially.
+-set all [lsort [find $srcdir/$subdir *.C]]
+-set tests [prune [prune $all $srcdir/$subdir/special/*] $srcdir/$subdir/debug/*]
++# Gather a list of all tests, with the exception of those in directories
++# that are handled specially.
++set tests [lsort [find $srcdir/$subdir *.C]]
++set tests [prune $tests $srcdir/$subdir/debug/*]
++set tests [prune $tests $srcdir/$subdir/special/*]
++set tests [prune $tests $srcdir/$subdir/tls/*]
+
+ # Main loop.
+ dg-runtest $tests "" $DEFAULT_CXXFLAGS
+--- gcc/testsuite/g++.dg/tls/diag-1.C.jj Wed Jun 19 19:33:52 2002
++++ gcc/testsuite/g++.dg/tls/diag-1.C Wed Jun 19 19:33:52 2002
+@@ -0,0 +1,30 @@
++/* Valid __thread specifiers. */
++
++__thread int g1;
++extern __thread int g2;
++static __thread int g3;
++
++void foo()
++{
++ extern __thread int l1;
++ static __thread int l2;
++}
++
++struct A {
++ static __thread int i;
++};
++
++__thread int A::i = 42;
++
++template <typename T> struct B {
++ static __thread T t;
++};
++
++template <typename T>
++__thread T B<T>::t = 42;
++
++void bar ()
++{
++ int j = B<int>::t;
++ int k = B<const int>::t;
++}
+--- gcc/testsuite/g++.dg/tls/diag-2.C.jj Wed Jun 19 19:33:52 2002
++++ gcc/testsuite/g++.dg/tls/diag-2.C Wed Jun 19 19:33:52 2002
+@@ -0,0 +1,25 @@
++/* Invalid __thread specifiers. */
++
++__thread extern int g1; /* { dg-error "`__thread' before `extern'" } */
++__thread static int g2; /* { dg-error "`__thread' before `static'" } */
++__thread __thread int g3; /* { dg-error "duplicate `__thread'" } */
++typedef __thread int g4; /* { dg-error "multiple storage classes" } */
++
++void foo()
++{
++ __thread int l1; /* { dg-error "implicitly auto and declared `__thread'" } */
++ auto __thread int l2; /* { dg-error "multiple storage classes" } */
++ __thread extern int l3; /* { dg-error "`__thread' before `extern'" } */
++ register __thread int l4; /* { dg-error "multiple storage classes" } */
++}
++
++__thread void f1 (); /* { dg-error "invalid for function" } */
++extern __thread void f2 (); /* { dg-error "invalid for function" } */
++static __thread void f3 (); /* { dg-error "invalid for function" } */
++__thread void f4 () { } /* { dg-error "invalid for function" } */
++
++void bar(__thread int p1); /* { dg-error "(invalid in parameter)|(specified for parameter)" } */
++
++struct A {
++ __thread int i; /* { dg-error "specified for field" } */
++};
+--- gcc/testsuite/g++.dg/tls/init-1.C.jj Wed Jun 19 19:33:52 2002
++++ gcc/testsuite/g++.dg/tls/init-1.C Wed Jun 19 19:33:52 2002
+@@ -0,0 +1,13 @@
++/* Valid initializations. */
++
++__thread int i = 42;
++
++static int j;
++__thread int *p = &j;
++
++/* Note that this is valid in C++ (unlike C) as a run-time initialization. */
++int *q = &i;
++
++/* Valid because "const int k" is an integral constant expression in C++. */
++__thread const int k = 42;
++__thread const int l = k;
+--- gcc/testsuite/g++.dg/tls/init-2.C.jj Wed Jun 19 19:33:52 2002
++++ gcc/testsuite/g++.dg/tls/init-2.C Wed Jun 19 19:33:52 2002
+@@ -0,0 +1,13 @@
++/* Invalid initializations. */
++
++extern __thread int i;
++__thread int *p = &i; /* { dg-error "run-time initialization" } */
++
++extern int f();
++__thread int j = f(); /* { dg-error "run-time initialization" } */
++
++struct S
++{
++ S();
++};
++__thread S s; /* { dg-error "run-time initialization" } */
+--- gcc/testsuite/g++.dg/tls/tls.exp.jj Wed Jun 19 19:33:52 2002
++++ gcc/testsuite/g++.dg/tls/tls.exp Wed Jun 19 19:33:52 2002
+@@ -0,0 +1,44 @@
++# Copyright (C) 2002 Free Software Foundation, Inc.
++
++# This program 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 2 of the License, or
++# (at your option) any later version.
++#
++# This program 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 this program; if not, write to the Free Software
++# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++
++# GCC testsuite that uses the `dg.exp' driver.
++
++# Load support procs.
++load_lib g++-dg.exp
++
++# Test for thread-local data supported by the platform. If it
++# isn't, everything will fail with the "not supported" message.
++
++set comp_output [g++_target_compile \
++ "$srcdir/$subdir/trivial.C" "trivial.S" assembly ""]
++if { [string match "*not supported*" $comp_output] } {
++ return 0
++}
++
++# If a testcase doesn't have special options, use these.
++global DEFAULT_CXXFLAGS
++if ![info exists DEFAULT_CXXFLAGS] then {
++ set DEFAULT_CXXFLAGS " -ansi -pedantic-errors -Wno-long-long"
++}
++
++# Initialize `dg'.
++dg-init
++
++# Main loop.
++dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.C]] "" $DEFAULT_CXXFLAGS
++
++# All done.
++dg-finish
+--- gcc/testsuite/g++.dg/tls/trivial.C.jj Wed Jun 19 19:33:52 2002
++++ gcc/testsuite/g++.dg/tls/trivial.C Wed Jun 19 19:33:52 2002
+@@ -0,0 +1 @@
++__thread int i;
+--- gcc/testsuite/gcc.dg/tls/diag-1.c.jj Wed Jun 19 19:33:52 2002
++++ gcc/testsuite/gcc.dg/tls/diag-1.c Wed Jun 19 19:33:52 2002
+@@ -0,0 +1,11 @@
++/* Valid __thread specifiers. */
++
++__thread int g1;
++extern __thread int g2;
++static __thread int g3;
++
++void foo()
++{
++ extern __thread int l1;
++ static __thread int l2;
++}
+--- gcc/testsuite/gcc.dg/tls/diag-2.c.jj Wed Jun 19 19:33:52 2002
++++ gcc/testsuite/gcc.dg/tls/diag-2.c Wed Jun 19 19:33:52 2002
+@@ -0,0 +1,21 @@
++/* Invalid __thread specifiers. */
++
++__thread extern int g1; /* { dg-error "`__thread' before `extern'" } */
++__thread static int g2; /* { dg-error "`__thread' before `static'" } */
++__thread __thread int g3; /* { dg-error "duplicate `__thread'" } */
++typedef __thread int g4; /* { dg-error "multiple storage classes" } */
++
++void foo()
++{
++ __thread int l1; /* { dg-error "implicitly auto and declared `__thread'" } */
++ auto __thread int l2; /* { dg-error "multiple storage classes" } */
++ __thread extern int l3; /* { dg-error "`__thread' before `extern'" } */
++ register __thread int l4; /* { dg-error "multiple storage classes" } */
++}
++
++__thread void f1 (); /* { dg-error "invalid storage class for function" } */
++extern __thread void f2 (); /* { dg-error "invalid storage class for function" } */
++static __thread void f3 (); /* { dg-error "invalid storage class for function" } */
++__thread void f4 () { } /* { dg-error "function definition declared `__thread'" } */
++
++void bar(__thread int p1); /* { dg-error "storage class specified for parameter" } */
+--- gcc/testsuite/gcc.dg/tls/init-1.c.jj Wed Jun 19 19:33:52 2002
++++ gcc/testsuite/gcc.dg/tls/init-1.c Wed Jun 19 19:33:52 2002
+@@ -0,0 +1,4 @@
++/* Invalid initializations. */
++
++extern __thread int i;
++int *p = &i; /* { dg-error "initializer element is not constant" } */
+--- gcc/testsuite/gcc.dg/tls/tls.exp.jj Wed Jun 19 19:33:52 2002
++++ gcc/testsuite/gcc.dg/tls/tls.exp Wed Jun 19 19:33:52 2002
+@@ -0,0 +1,45 @@
++# Copyright (C) 2002 Free Software Foundation, Inc.
++
++# This program 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 2 of the License, or
++# (at your option) any later version.
++#
++# This program 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 this program; if not, write to the Free Software
++# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++
++# GCC testsuite that uses the `dg.exp' driver.
++
++# Load support procs.
++load_lib gcc-dg.exp
++
++# Test for thread-local data supported by the platform. If it
++# isn't, everything will fail with the "not supported" message.
++
++set comp_output [gcc_target_compile \
++ "$srcdir/$subdir/trivial.c" "trivial.S" assembly ""]
++if { [string match "*not supported*" $comp_output] } {
++ return 0
++}
++
++# If a testcase doesn't have special options, use these.
++global DEFAULT_CFLAGS
++if ![info exists DEFAULT_CFLAGS] then {
++ set DEFAULT_CFLAGS " -ansi -pedantic-errors"
++}
++
++# Initialize `dg'.
++dg-init
++
++# Main loop.
++dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cS\]]] \
++ "" $DEFAULT_CFLAGS
++
++# All done.
++dg-finish
+--- gcc/testsuite/gcc.dg/tls/trivial.c.jj Wed Jun 19 19:33:52 2002
++++ gcc/testsuite/gcc.dg/tls/trivial.c Wed Jun 19 19:33:52 2002
+@@ -0,0 +1 @@
++__thread int i;
+--- gcc/c-common.h.jj Wed Apr 17 15:34:36 2002
++++ gcc/c-common.h Wed Jun 19 19:33:52 2002
+@@ -58,7 +58,7 @@ enum rid
+ RID_VOLATILE, RID_SIGNED, RID_AUTO, RID_RESTRICT,
+
+ /* C extensions */
+- RID_BOUNDED, RID_UNBOUNDED, RID_COMPLEX,
++ RID_BOUNDED, RID_UNBOUNDED, RID_COMPLEX, RID_THREAD,
+
+ /* C++ */
+ RID_FRIEND, RID_VIRTUAL, RID_EXPLICIT, RID_EXPORT, RID_MUTABLE,
+--- gcc/c-decl.c.jj Wed Jun 19 19:33:50 2002
++++ gcc/c-decl.c Wed Jun 19 19:33:52 2002
+@@ -3571,9 +3571,19 @@ start_decl (declarator, declspecs, initi
+ /* ANSI specifies that a tentative definition which is not merged with
+ a non-tentative definition behaves exactly like a definition with an
+ initializer equal to zero. (Section 3.7.2)
+- -fno-common gives strict ANSI behavior. Usually you don't want it.
+- This matters only for variables with external linkage. */
+- if (! flag_no_common || ! TREE_PUBLIC (decl))
++
++ -fno-common gives strict ANSI behavior, though this tends to break
++ a large body of code that grew up without this rule.
++
++ Thread-local variables are never common, since there's no entrenched
++ body of code to break, and it allows more efficient variable references
++ in the presense of dynamic linking. */
++
++ if (TREE_CODE (decl) == VAR_DECL
++ && !initialized
++ && TREE_PUBLIC (decl)
++ && !DECL_THREAD_LOCAL (decl)
++ && !flag_no_common)
+ DECL_COMMON (decl) = 1;
+
+ /* Set attributes here so if duplicate decl, will have proper attributes. */
+@@ -4172,7 +4182,7 @@ grokdeclarator (declarator, declspecs, d
+ enum rid i = C_RID_CODE (id);
+ if ((int) i <= (int) RID_LAST_MODIFIER)
+ {
+- if (i == RID_LONG && (specbits & (1 << (int) i)))
++ if (i == RID_LONG && (specbits & (1 << (int) RID_LONG)))
+ {
+ if (longlong)
+ error ("`long long long' is too long for GCC");
+@@ -4186,6 +4196,19 @@ grokdeclarator (declarator, declspecs, d
+ }
+ else if (specbits & (1 << (int) i))
+ pedwarn ("duplicate `%s'", IDENTIFIER_POINTER (id));
++
++ /* Diagnose "__thread extern". Recall that this list
++ is in the reverse order seen in the text. */
++ if (i == RID_THREAD
++ && (specbits & (1 << (int) RID_EXTERN
++ | 1 << (int) RID_STATIC)))
++ {
++ if (specbits & 1 << (int) RID_EXTERN)
++ error ("`__thread' before `extern'");
++ else
++ error ("`__thread' before `static'");
++ }
++
+ specbits |= 1 << (int) i;
+ goto found;
+ }
+@@ -4438,6 +4461,12 @@ grokdeclarator (declarator, declspecs, d
+ if (specbits & 1 << (int) RID_REGISTER) nclasses++;
+ if (specbits & 1 << (int) RID_TYPEDEF) nclasses++;
+
++ /* "static __thread" and "extern __thread" are allowed. */
++ if ((specbits & (1 << (int) RID_THREAD
++ | 1 << (int) RID_STATIC
++ | 1 << (int) RID_EXTERN)) == (1 << (int) RID_THREAD))
++ nclasses++;
++
+ /* Warn about storage classes that are invalid for certain
+ kinds of declarations (parameters, typenames, etc.). */
+
+@@ -4447,7 +4476,8 @@ grokdeclarator (declarator, declspecs, d
+ && (specbits
+ & ((1 << (int) RID_REGISTER)
+ | (1 << (int) RID_AUTO)
+- | (1 << (int) RID_TYPEDEF))))
++ | (1 << (int) RID_TYPEDEF)
++ | (1 << (int) RID_THREAD))))
+ {
+ if (specbits & 1 << (int) RID_AUTO
+ && (pedantic || current_binding_level == global_binding_level))
+@@ -4456,8 +4486,10 @@ grokdeclarator (declarator, declspecs, d
+ error ("function definition declared `register'");
+ if (specbits & 1 << (int) RID_TYPEDEF)
+ error ("function definition declared `typedef'");
++ if (specbits & 1 << (int) RID_THREAD)
++ error ("function definition declared `__thread'");
+ specbits &= ~((1 << (int) RID_TYPEDEF) | (1 << (int) RID_REGISTER)
+- | (1 << (int) RID_AUTO));
++ | (1 << (int) RID_AUTO) | (1 << (int) RID_THREAD));
+ }
+ else if (decl_context != NORMAL && nclasses > 0)
+ {
+@@ -4480,7 +4512,7 @@ grokdeclarator (declarator, declspecs, d
+ }
+ specbits &= ~((1 << (int) RID_TYPEDEF) | (1 << (int) RID_REGISTER)
+ | (1 << (int) RID_AUTO) | (1 << (int) RID_STATIC)
+- | (1 << (int) RID_EXTERN));
++ | (1 << (int) RID_EXTERN) | (1 << (int) RID_THREAD));
+ }
+ }
+ else if (specbits & 1 << (int) RID_EXTERN && initialized && ! funcdef_flag)
+@@ -4491,12 +4523,25 @@ grokdeclarator (declarator, declspecs, d
+ else
+ error ("`%s' has both `extern' and initializer", name);
+ }
+- else if (specbits & 1 << (int) RID_EXTERN && funcdef_flag
+- && current_binding_level != global_binding_level)
+- error ("nested function `%s' declared `extern'", name);
+- else if (current_binding_level == global_binding_level
+- && specbits & (1 << (int) RID_AUTO))
+- error ("top-level declaration of `%s' specifies `auto'", name);
++ else if (current_binding_level == global_binding_level)
++ {
++ if (specbits & 1 << (int) RID_AUTO)
++ error ("top-level declaration of `%s' specifies `auto'", name);
++ }
++ else
++ {
++ if (specbits & 1 << (int) RID_EXTERN && funcdef_flag)
++ error ("nested function `%s' declared `extern'", name);
++ else if ((specbits & (1 << (int) RID_THREAD
++ | 1 << (int) RID_EXTERN
++ | 1 << (int) RID_STATIC))
++ == (1 << (int) RID_THREAD))
++ {
++ error ("function-scope `%s' implicitly auto and declared `__thread'",
++ name);
++ specbits &= ~(1 << (int) RID_THREAD);
++ }
++ }
+ }
+
+ /* Now figure out the structure of the declarator proper.
+@@ -5095,6 +5140,8 @@ grokdeclarator (declarator, declspecs, d
+ pedwarn ("invalid storage class for function `%s'", name);
+ if (specbits & (1 << (int) RID_REGISTER))
+ error ("invalid storage class for function `%s'", name);
++ if (specbits & (1 << (int) RID_THREAD))
++ error ("invalid storage class for function `%s'", name);
+ /* Function declaration not at top level.
+ Storage classes other than `extern' are not allowed
+ and `extern' makes no difference. */
+@@ -5187,22 +5234,32 @@ grokdeclarator (declarator, declspecs, d
+ pedwarn_with_decl (decl, "variable `%s' declared `inline'");
+
+ DECL_EXTERNAL (decl) = extern_ref;
++
+ /* At top level, the presence of a `static' or `register' storage
+ class specifier, or the absence of all storage class specifiers
+ makes this declaration a definition (perhaps tentative). Also,
+ the absence of both `static' and `register' makes it public. */
+ if (current_binding_level == global_binding_level)
+ {
+- TREE_PUBLIC (decl)
+- = !(specbits
+- & ((1 << (int) RID_STATIC) | (1 << (int) RID_REGISTER)));
+- TREE_STATIC (decl) = ! DECL_EXTERNAL (decl);
++ TREE_PUBLIC (decl) = !(specbits & ((1 << (int) RID_STATIC)
++ | (1 << (int) RID_REGISTER)));
++ TREE_STATIC (decl) = !extern_ref;
+ }
+ /* Not at top level, only `static' makes a static definition. */
+ else
+ {
+ TREE_STATIC (decl) = (specbits & (1 << (int) RID_STATIC)) != 0;
+- TREE_PUBLIC (decl) = DECL_EXTERNAL (decl);
++ TREE_PUBLIC (decl) = extern_ref;
++ }
++
++ if (specbits & 1 << (int) RID_THREAD)
++ {
++ if (targetm.have_tls)
++ DECL_THREAD_LOCAL (decl) = 1;
++ else
++ /* A mere warning is sure to result in improper semantics
++ at runtime. Don't bother to allow this to compile. */
++ error ("thread-local storage not supported for this target");
+ }
+ }
+
+--- gcc/c-parse.in.jj Wed Apr 17 15:34:46 2002
++++ gcc/c-parse.in Wed Jun 19 19:33:52 2002
+@@ -3329,6 +3329,7 @@ static const struct resword reswords[] =
+ { "__restrict__", RID_RESTRICT, 0 },
+ { "__signed", RID_SIGNED, 0 },
+ { "__signed__", RID_SIGNED, 0 },
++ { "__thread", RID_THREAD, 0 },
+ { "__typeof", RID_TYPEOF, 0 },
+ { "__typeof__", RID_TYPEOF, 0 },
+ { "__unbounded", RID_UNBOUNDED, 0 },
+@@ -3424,6 +3425,7 @@ static const short rid_to_yy[RID_MAX] =
+ /* RID_BOUNDED */ TYPE_QUAL,
+ /* RID_UNBOUNDED */ TYPE_QUAL,
+ /* RID_COMPLEX */ TYPESPEC,
++ /* RID_THREAD */ SCSPEC,
+
+ /* C++ */
+ /* RID_FRIEND */ 0,
+--- gcc/config.in.jj Sat Jun 8 00:38:25 2002
++++ gcc/config.in Wed Jun 19 19:33:52 2002
+@@ -523,6 +523,9 @@
+ /* Define if your assembler supports marking sections with SHF_MERGE flag. */
+ #undef HAVE_GAS_SHF_MERGE
+
++/* Define if your assembler supports thread-local storage. */
++#undef HAVE_AS_TLS
++
+ /* Define if your assembler supports explicit relocations. */
+ #undef HAVE_AS_EXPLICIT_RELOCS
+
+--- gcc/flags.h.jj Sat Mar 23 12:02:51 2002
++++ gcc/flags.h Wed Jun 19 19:33:52 2002
+@@ -450,11 +450,22 @@ extern int flag_pretend_float;
+
+ extern int flag_pedantic_errors;
+
+-/* Nonzero means generate position-independent code.
+- This is not fully implemented yet. */
++/* Nonzero means generate position-independent code. 1 vs 2 for a
++ target-dependent "small" or "large" mode. */
+
+ extern int flag_pic;
+
++/* Set to the default thread-local storage (tls) model to use. */
++
++enum tls_model {
++ TLS_MODEL_GLOBAL_DYNAMIC = 1,
++ TLS_MODEL_LOCAL_DYNAMIC,
++ TLS_MODEL_INITIAL_EXEC,
++ TLS_MODEL_LOCAL_EXEC
++};
++
++extern enum tls_model flag_tls_default;
++
+ /* Nonzero means generate extra code for exception handling and enable
+ exception handling. */
+
+--- gcc/configure.in.jj Sat Jun 8 00:38:27 2002
++++ gcc/configure.in Wed Jun 19 19:33:52 2002
+@@ -1715,6 +1715,72 @@ if test x"$gcc_cv_as_shf_merge" = xyes;
+ fi
+ AC_MSG_RESULT($gcc_cv_as_shf_merge)
+
++AC_MSG_CHECKING(assembler thread-local storage support)
++gcc_cv_as_tls=no
++conftest_s=
++tls_first_major=
++tls_first_minor=
++case "$target" in
++changequote(,)dnl
++ i[34567]86-*-*)
++changequote([,])dnl
++ conftest_s='
++ .section ".tdata","awT",@progbits
++foo: .long 25
++ .text
++ movl %gs:0, %eax
++ leal foo@TLSGD(,%ebx,1), %eax
++ leal foo@TLSLDM(%ebx), %eax
++ leal foo@DTPOFF(%eax), %edx
++ movl foo@GOTTPOFF(%ebx), %eax
++ subl foo@GOTTPOFF(%ebx), %eax
++ movl $foo@TPOFF, %eax
++ subl $foo@TPOFF, %eax
++ leal foo@NTPOFF(%ecx), %eax'
++ tls_first_major=2
++ tls_first_minor=13
++ ;;
++ ia64-*-*)
++ conftest_s='
++ .section ".tdata","awT",@progbits
++foo: data8 25
++ .text
++ addl r16 = @ltoff(@dtpmod(foo#)), gp
++ addl r17 = @ltoff(@dtprel(foo#)), gp
++ addl r18 = @ltoff(@tprel(foo#)), gp
++ addl r19 = @dtprel(foo#), gp
++ adds r21 = @dtprel(foo#), r13
++ movl r23 = @dtprel(foo#)
++ addl r20 = @tprel(foo#), gp
++ adds r22 = @tprel(foo#), r13
++ movl r24 = @tprel(foo#)'
++ tls_first_major=2
++ tls_first_minor=13
++ ;;
++esac
++if test -z "$tls_first_major"; then
++ :
++elif test x$gcc_cv_gas_major_version != x -a x$gcc_cv_gas_minor_version != x
++then
++ if test "$gcc_cv_gas_major_version" -eq "$tls_first_major" \
++ -a "$gcc_cv_gas_minor_version" -ge "$tls_first_minor" \
++ -o "$gcc_cv_gas_major_version" -gt "$tls_first_major"; then
++ gcc_cv_as_tls=yes
++ fi
++elif test x$gcc_cv_as != x; then
++ echo "$conftest_s" > conftest.s
++ if $gcc_cv_as --fatal-warnings -o conftest.o conftest.s > /dev/null 2>&1
++ then
++ gcc_cv_as_tls=yes
++ fi
++ rm -f conftest.s conftest.o
++fi
++if test "$gcc_cv_as_tls" = yes; then
++ AC_DEFINE(HAVE_AS_TLS, 1,
++ [Define if your assembler supports thread-local storage.])
++fi
++AC_MSG_RESULT($gcc_cv_as_tls)
++
+ case "$target" in
+ # All TARGET_ABI_OSF targets.
+ alpha*-*-osf* | alpha*-*-linux* | alpha*-*-*bsd*)
+--- gcc/configure.jj Sat Jun 8 00:38:27 2002
++++ gcc/configure Wed Jun 19 22:28:09 2002
+@@ -7391,6 +7391,73 @@ EOF
+ fi
+ echo "$ac_t""$gcc_cv_as_shf_merge" 1>&6
+
++echo $ac_n "checking assembler thread-local storage support""... $ac_c" 1>&6
++echo "configure:7399: checking assembler thread-local storage support" >&5
++gcc_cv_as_tls=no
++conftest_s=
++tls_first_major=
++tls_first_minor=
++case "$target" in
++ i[34567]86-*-*)
++ conftest_s='
++ .section ".tdata","awT",@progbits
++foo: .long 25
++ .text
++ movl %gs:0, %eax
++ leal foo@TLSGD(,%ebx,1), %eax
++ leal foo@TLSLDM(%ebx), %eax
++ leal foo@DTPOFF(%eax), %edx
++ movl foo@GOTTPOFF(%ebx), %eax
++ subl foo@GOTTPOFF(%ebx), %eax
++ movl $foo@TPOFF, %eax
++ subl $foo@TPOFF, %eax
++ leal foo@NTPOFF(%ecx), %eax'
++ tls_first_major=2
++ tls_first_minor=13
++ ;;
++ ia64-*-*)
++ conftest_s='
++ .section ".tdata","awT",@progbits
++foo: data8 25
++ .text
++ addl r16 = @ltoff(@dtpmod(foo#)), gp
++ addl r17 = @ltoff(@dtprel(foo#)), gp
++ addl r18 = @ltoff(@tprel(foo#)), gp
++ addl r19 = @dtprel(foo#), gp
++ adds r21 = @dtprel(foo#), r13
++ movl r23 = @dtprel(foo#)
++ addl r20 = @tprel(foo#), gp
++ adds r22 = @tprel(foo#), r13
++ movl r24 = @tprel(foo#)'
++ tls_first_major=2
++ tls_first_minor=13
++ ;;
++esac
++if test -z "$tls_first_major"; then
++ :
++elif test x$gcc_cv_gas_major_version != x -a x$gcc_cv_gas_minor_version != x
++then
++ if test "$gcc_cv_gas_major_version" -eq "$tls_first_major" \
++ -a "$gcc_cv_gas_minor_version" -ge "$tls_first_minor" \
++ -o "$gcc_cv_gas_major_version" -gt "$tls_first_major"; then
++ gcc_cv_as_tls=yes
++ fi
++elif test x$gcc_cv_as != x; then
++ echo "$conftest_s" > conftest.s
++ if $gcc_cv_as --fatal-warnings -o conftest.o conftest.s > /dev/null 2>&1
++ then
++ gcc_cv_as_tls=yes
++ fi
++ rm -f conftest.s conftest.o
++fi
++if test "$gcc_cv_as_tls" = yes; then
++ cat >> confdefs.h <<\EOF
++#define HAVE_AS_TLS 1
++EOF
++
++fi
++echo "$ac_t""$gcc_cv_as_tls" 1>&6
++
+ case "$target" in
+ # All TARGET_ABI_OSF targets.
+ alpha*-*-osf* | alpha*-*-linux* | alpha*-*-*bsd*)
+--- gcc/output.h.jj Wed Jun 19 19:33:50 2002
++++ gcc/output.h Wed Jun 19 19:33:52 2002
+@@ -514,7 +514,8 @@ extern void no_asm_to_stream PARAMS ((FI
+ #define SECTION_STRINGS 0x10000 /* contains zero terminated strings without
+ embedded zeros */
+ #define SECTION_OVERRIDE 0x20000 /* allow override of default flags */
+-#define SECTION_MACH_DEP 0x40000 /* subsequent bits reserved for target */
++#define SECTION_TLS 0x40000 /* contains thread-local storage */
++#define SECTION_MACH_DEP 0x80000 /* subsequent bits reserved for target */
+
+ extern unsigned int get_named_section_flags PARAMS ((const char *));
+ extern bool set_named_section_flags PARAMS ((const char *, unsigned int));
+--- gcc/print-tree.c.jj Mon Mar 18 23:19:57 2002
++++ gcc/print-tree.c Wed Jun 19 19:33:52 2002
+@@ -363,6 +363,8 @@ print_node (file, prefix, node, indent)
+
+ if (TREE_CODE (node) == VAR_DECL && DECL_IN_TEXT_SECTION (node))
+ fputs (" in-text-section", file);
++ if (TREE_CODE (node) == VAR_DECL && DECL_THREAD_LOCAL (node))
++ fputs (" thread-local", file);
+
+ if (TREE_CODE (node) == PARM_DECL && DECL_TRANSPARENT_UNION (node))
+ fputs (" transparent-union", file);
+--- gcc/target-def.h.jj Tue Feb 12 16:19:14 2002
++++ gcc/target-def.h Wed Jun 19 19:33:52 2002
+@@ -98,6 +98,10 @@ Foundation, 59 Temple Place - Suite 330,
+ #define TARGET_HAVE_NAMED_SECTIONS false
+ #endif
+
++#ifndef TARGET_HAVE_TLS
++#define TARGET_HAVE_TLS false
++#endif
++
+ #ifndef TARGET_ASM_EXCEPTION_SECTION
+ #define TARGET_ASM_EXCEPTION_SECTION default_exception_section
+ #endif
+@@ -194,9 +198,10 @@ Foundation, 59 Temple Place - Suite 330,
+ TARGET_INIT_BUILTINS, \
+ TARGET_EXPAND_BUILTIN, \
+ TARGET_SECTION_TYPE_FLAGS, \
++ TARGET_CANNOT_MODIFY_JUMPS_P, \
+ TARGET_HAVE_NAMED_SECTIONS, \
+ TARGET_HAVE_CTORS_DTORS, \
+- TARGET_CANNOT_MODIFY_JUMPS_P \
++ TARGET_HAVE_TLS \
+ }
+
+ #include "hooks.h"
+--- gcc/target.h.jj Tue Feb 12 16:19:14 2002
++++ gcc/target.h Wed Jun 19 19:33:52 2002
+@@ -178,6 +178,10 @@ struct gcc_target
+ /* ??? Should be merged with SELECT_SECTION and UNIQUE_SECTION. */
+ unsigned int (* section_type_flags) PARAMS ((tree, const char *, int));
+
++ /* True if new jumps cannot be created, to replace existing ones or
++ not, at the current point in the compilation. */
++ bool (* cannot_modify_jumps_p) PARAMS ((void));
++
+ /* True if arbitrary sections are supported. */
+ bool have_named_sections;
+
+@@ -185,9 +189,8 @@ struct gcc_target
+ false if we're using collect2 for the job. */
+ bool have_ctors_dtors;
+
+- /* True if new jumps cannot be created, to replace existing ones or
+- not, at the current point in the compilation. */
+- bool (* cannot_modify_jumps_p) PARAMS ((void));
++ /* True if thread-local storage is supported. */
++ bool have_tls;
+ };
+
+ extern struct gcc_target targetm;
+--- gcc/toplev.c.jj Thu May 30 11:08:44 2002
++++ gcc/toplev.c Wed Jun 19 19:33:52 2002
+@@ -682,12 +682,15 @@ int flag_shared_data;
+ int flag_delayed_branch;
+
+ /* Nonzero if we are compiling pure (sharable) code.
+- Value is 1 if we are doing reasonable (i.e. simple
+- offset into offset table) pic. Value is 2 if we can
+- only perform register offsets. */
++ Value is 1 if we are doing "small" pic; value is 2 if we're doing
++ "large" pic. */
+
+ int flag_pic;
+
++/* Set to the default thread-local storage (tls) model to use. */
++
++enum tls_model flag_tls_default = TLS_MODEL_GLOBAL_DYNAMIC;
++
+ /* Nonzero means generate extra code for exception handling and enable
+ exception handling. */
+
+@@ -3609,6 +3612,7 @@ display_help ()
+ printf (_(" -finline-limit=<number> Limits the size of inlined functions to <number>\n"));
+ printf (_(" -fmessage-length=<number> Limits diagnostics messages lengths to <number> characters per line. 0 suppresses line-wrapping\n"));
+ printf (_(" -fdiagnostics-show-location=[once | every-line] Indicates how often source location information should be emitted, as prefix, at the beginning of diagnostics when line-wrapping\n"));
++ printf (_(" -ftls-model=[global-dynamic | local-dynamic | initial-exec | local-exec] Indicates the default thread-local storage code generation model\n"));
+
+ for (i = ARRAY_SIZE (f_options); i--;)
+ {
+@@ -3887,6 +3891,19 @@ decode_f_option (arg)
+ MAX_INLINE_INSNS);
+ set_param_value ("max-inline-insns", val);
+ }
++ else if ((option_value = skip_leading_substring (arg, "tls-model=")))
++ {
++ if (strcmp (option_value, "global-dynamic") == 0)
++ flag_tls_default = TLS_MODEL_GLOBAL_DYNAMIC;
++ else if (strcmp (option_value, "local-dynamic") == 0)
++ flag_tls_default = TLS_MODEL_LOCAL_DYNAMIC;
++ else if (strcmp (option_value, "initial-exec") == 0)
++ flag_tls_default = TLS_MODEL_INITIAL_EXEC;
++ else if (strcmp (option_value, "local-exec") == 0)
++ flag_tls_default = TLS_MODEL_LOCAL_EXEC;
++ else
++ warning ("`%s': unknown tls-model option", arg - 2);
++ }
+ #ifdef INSN_SCHEDULING
+ else if ((option_value = skip_leading_substring (arg, "sched-verbose=")))
+ fix_sched_param ("verbose", option_value);
+--- gcc/tree.c.jj Sun Apr 28 23:20:20 2002
++++ gcc/tree.c Wed Jun 19 19:33:52 2002
+@@ -1510,12 +1510,13 @@ staticp (arg)
+ case FUNCTION_DECL:
+ /* Nested functions aren't static, since taking their address
+ involves a trampoline. */
+- return (decl_function_context (arg) == 0 || DECL_NO_STATIC_CHAIN (arg))
+- && ! DECL_NON_ADDR_CONST_P (arg);
++ return ((decl_function_context (arg) == 0 || DECL_NO_STATIC_CHAIN (arg))
++ && ! DECL_NON_ADDR_CONST_P (arg));
+
+ case VAR_DECL:
+- return (TREE_STATIC (arg) || DECL_EXTERNAL (arg))
+- && ! DECL_NON_ADDR_CONST_P (arg);
++ return ((TREE_STATIC (arg) || DECL_EXTERNAL (arg))
++ && ! DECL_THREAD_LOCAL (arg)
++ && ! DECL_NON_ADDR_CONST_P (arg));
+
+ case CONSTRUCTOR:
+ return TREE_STATIC (arg);
+--- gcc/tree.h.jj Wed Jun 19 19:33:50 2002
++++ gcc/tree.h Wed Jun 19 19:33:52 2002
+@@ -1614,6 +1614,10 @@ struct tree_type
+ /* In a FUNCTION_DECL, nonzero if the function cannot be inlined. */
+ #define DECL_UNINLINABLE(NODE) (FUNCTION_DECL_CHECK (NODE)->decl.uninlinable)
+
++/* In a VAR_DECL, nonzero if the data should be allocated from
++ thread-local storage. */
++#define DECL_THREAD_LOCAL(NODE) (VAR_DECL_CHECK (NODE)->decl.thread_local_flag)
++
+ /* In a FUNCTION_DECL, the saved representation of the body of the
+ entire function. Usually a COMPOUND_STMT, but in C++ this may also
+ be a RETURN_INIT, CTOR_INITIALIZER, or TRY_BLOCK. */
+@@ -1792,7 +1796,8 @@ struct tree_decl
+ unsigned non_addressable : 1;
+ unsigned user_align : 1;
+ unsigned uninlinable : 1;
+- /* Three unused bits. */
++ unsigned thread_local_flag : 1;
++ /* Two unused bits. */
+
+ unsigned lang_flag_0 : 1;
+ unsigned lang_flag_1 : 1;
+--- gcc/varasm.c.jj Wed Jun 19 19:33:51 2002
++++ gcc/varasm.c Wed Jun 19 19:33:52 2002
+@@ -1599,14 +1599,24 @@ assemble_variable (decl, top_level, at_e
+
+ /* Handle uninitialized definitions. */
+
+- if ((DECL_INITIAL (decl) == 0 || DECL_INITIAL (decl) == error_mark_node)
+- /* If the target can't output uninitialized but not common global data
+- in .bss, then we have to use .data. */
+-#if ! defined ASM_EMIT_BSS
+- && DECL_COMMON (decl)
++ /* If the decl has been given an explicit section name, then it
++ isn't common, and shouldn't be handled as such. */
++ if (DECL_SECTION_NAME (decl) || dont_output_data)
++ ;
++ /* We don't implement common thread-local data at present. */
++ else if (DECL_THREAD_LOCAL (decl))
++ {
++ if (DECL_COMMON (decl))
++ sorry ("thread-local COMMON data not implemented");
++ }
++#ifndef ASM_EMIT_BSS
++ /* If the target can't output uninitialized but not common global data
++ in .bss, then we have to use .data. */
++ else if (!DECL_COMMON (decl))
++ ;
+ #endif
+- && DECL_SECTION_NAME (decl) == NULL_TREE
+- && ! dont_output_data)
++ else if (DECL_INITIAL (decl) == 0
++ || DECL_INITIAL (decl) == error_mark_node)
+ {
+ unsigned HOST_WIDE_INT size = tree_low_cst (DECL_SIZE_UNIT (decl), 1);
+ unsigned HOST_WIDE_INT rounded = size;
+@@ -5324,14 +5334,22 @@ default_section_type_flags (decl, name,
+ if (decl && DECL_ONE_ONLY (decl))
+ flags |= SECTION_LINKONCE;
+
++ if (decl && TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL (decl))
++ flags |= SECTION_TLS | SECTION_WRITE;
++
+ if (strcmp (name, ".bss") == 0
+ || strncmp (name, ".bss.", 5) == 0
+ || strncmp (name, ".gnu.linkonce.b.", 16) == 0
+ || strcmp (name, ".sbss") == 0
+ || strncmp (name, ".sbss.", 6) == 0
+- || strncmp (name, ".gnu.linkonce.sb.", 17) == 0)
++ || strncmp (name, ".gnu.linkonce.sb.", 17) == 0
++ || strcmp (name, ".tbss") == 0)
+ flags |= SECTION_BSS;
+
++ if (strcmp (name, ".tdata") == 0
++ || strcmp (name, ".tbss") == 0)
++ flags |= SECTION_TLS;
++
+ return flags;
+ }
+
+@@ -5374,6 +5392,8 @@ default_elf_asm_named_section (name, fla
+ *f++ = 'M';
+ if (flags & SECTION_STRINGS)
+ *f++ = 'S';
++ if (flags & SECTION_TLS)
++ *f++ = 'T';
+ *f = '\0';
+
+ if (flags & SECTION_BSS)