--- 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 @@ -9988,6 +9988,14 @@ generate code that is not binary compatible with code generated without that switch. Use it to conform to a non-default application binary interface. Not all targets provide complete support for this switch. + +@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; 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; /* 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; } +/* 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; +} +/* 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; +} /* 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; +} + /* 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; +} /* 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. +;; 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))])] + "") + ;; 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; }) --- 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; +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; /* 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); } /* 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 151 fixup descriptions. + * This file contains 152 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 163 +#define REGEX_COUNT 164 #define MACH_LIST_SIZE_LIMIT 279 -#define FIX_COUNT 151 +#define FIX_COUNT 152 /* * 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 struct B { + static __thread T t; +}; + +template +__thread T B::t = 42; + +void bar () +{ + int j = B::t; + int k = B::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= Limits the size of inlined functions to \n")); printf (_(" -fmessage-length= Limits diagnostics messages lengths to 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)