From: rth Date: Thu, 23 May 2002 21:55:30 +0000 (+0000) Subject: * configure.in (HAVE_AS_TLS): New test. X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=2d6788fec3c05a3d5cb0133a5faedea13a9724c5;p=thirdparty%2Fgcc.git * configure.in (HAVE_AS_TLS): New test. * config.in, configure: Rebuild. * config/i386/i386.c (TARGET_HAVE_TLS): Set if HAVE_AS_TLS. (ix86_tls_dialect_string, ix86_tls_dialect): New. (override_options): Set it. (tls_model_chars, tls_symbolic_operand): New. (tls_symbolic_operand_1, global_dynamic_symbolic_operand): New. (local_dynamic_symbolic_operand, initial_exec_symbolic_operand): New. (local_exec_symbolic_operand): New. (get_pic_label_name): Merge into output_set_got. (ix86_asm_file_end): Emit pic_label_name if defined. (legitimate_constant_p, constant_address_p): New. (legitimate_pic_operand_p): New. (legitimate_pic_address_disp_p): Handle GOTTPOFF, NTPOFF, DTPOFF. (legitimate_address_p): Likewise. (ix86_encode_section_info): Rename from i386_; handle tls decls. (ix86_strip_name_encoding): New. (get_thread_pointer): New. (legitimize_address): Handle tls symbols. (output_pic_addr_const): Handle GOTTPOFF, TPOFF, NTPOFF, DTPOFF. Remove UNSPEC_PLT. (struct machine_function): Add some_ld_name. (get_some_local_dynamic_name, get_some_local_dynamic_name_1): Set it. (print_operand) [&]: Use it. Handle UNSPEC_TP. (output_addr_const_extra): New. (maybe_get_pool_constant): New. (ix86_split_to_parts): Use it. (ix86_expand_move): Handle tls symbols. (ix86_tls_get_addr): New. * config/i386/i386.h (TARGET_GNU_TLS, TARGET_SUN_TLS): New. (TARGET_OPTIONS): Add tls-dialect. (CONSTANT_ADDRESS_P): Use new out-of-line function. (LEGITIMATE_CONSTANT_P): Likewise. (LEGITIMATE_PIC_OPERAND_P): Likewise. (TARGET_STRIP_NAME_ENCODING): New. (ASM_OUTPUT_LABELREF): New. (PRINT_OPERAND_PUNCT_VALID_P): Add '&'. (OUTPUT_ADDR_CONST_EXTRA): New. (PREDICATE_CODES): Update. (ix86_tls_dialect, ix86_tls_dialect_string): New. * config/i386/i386.md: Regroup and renumber unspec constants. (tls_global_dynamic_gnu, tls_global_dynamic_sun): New. (tls_local_dynamic_base_gnu, tls_local_dynamic_base_sun): New. (tls_global_dynamic, tls_local_dynamic_base): New. (tls_local_dynamic_once): New. * config/i386/i386-protos.h: Update. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@53812 138bc75d-0d04-0410-961f-82ee72b054a4 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 7eef475a7ae3..6209ff60bde1 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,52 @@ +2002-05-23 Richard Henderson + + * configure.in (HAVE_AS_TLS): New test. + * config.in, configure: Rebuild. + * config/i386/i386.c (TARGET_HAVE_TLS): Set if HAVE_AS_TLS. + (ix86_tls_dialect_string, ix86_tls_dialect): New. + (override_options): Set it. + (tls_model_chars, tls_symbolic_operand): New. + (tls_symbolic_operand_1, global_dynamic_symbolic_operand): New. + (local_dynamic_symbolic_operand, initial_exec_symbolic_operand): New. + (local_exec_symbolic_operand): New. + (get_pic_label_name): Merge into output_set_got. + (ix86_asm_file_end): Emit pic_label_name if defined. + (legitimate_constant_p, constant_address_p): New. + (legitimate_pic_operand_p): New. + (legitimate_pic_address_disp_p): Handle GOTTPOFF, NTPOFF, DTPOFF. + (legitimate_address_p): Likewise. + (ix86_encode_section_info): Rename from i386_; handle tls decls. + (ix86_strip_name_encoding): New. + (get_thread_pointer): New. + (legitimize_address): Handle tls symbols. + (output_pic_addr_const): Handle GOTTPOFF, TPOFF, NTPOFF, DTPOFF. + Remove UNSPEC_PLT. + (struct machine_function): Add some_ld_name. + (get_some_local_dynamic_name, get_some_local_dynamic_name_1): Set it. + (print_operand) [&]: Use it. Handle UNSPEC_TP. + (output_addr_const_extra): New. + (maybe_get_pool_constant): New. + (ix86_split_to_parts): Use it. + (ix86_expand_move): Handle tls symbols. + (ix86_tls_get_addr): New. + * config/i386/i386.h (TARGET_GNU_TLS, TARGET_SUN_TLS): New. + (TARGET_OPTIONS): Add tls-dialect. + (CONSTANT_ADDRESS_P): Use new out-of-line function. + (LEGITIMATE_CONSTANT_P): Likewise. + (LEGITIMATE_PIC_OPERAND_P): Likewise. + (TARGET_STRIP_NAME_ENCODING): New. + (ASM_OUTPUT_LABELREF): New. + (PRINT_OPERAND_PUNCT_VALID_P): Add '&'. + (OUTPUT_ADDR_CONST_EXTRA): New. + (PREDICATE_CODES): Update. + (ix86_tls_dialect, ix86_tls_dialect_string): New. + * config/i386/i386.md: Regroup and renumber unspec constants. + (tls_global_dynamic_gnu, tls_global_dynamic_sun): New. + (tls_local_dynamic_base_gnu, tls_local_dynamic_base_sun): New. + (tls_global_dynamic, tls_local_dynamic_base): New. + (tls_local_dynamic_once): New. + * config/i386/i386-protos.h: Update. + 2002-05-23 Richard Henderson * genemit.c (gen_insn): Print file:lineno comment before function. diff --git a/gcc/config.in b/gcc/config.in index f7881df034e5..73a466bc3756 100644 --- a/gcc/config.in +++ b/gcc/config.in @@ -528,6 +528,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 diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h index 21babe91c2d1..1690d8a9e084 100644 --- a/gcc/config/i386/i386-protos.h +++ b/gcc/config/i386/i386-protos.h @@ -50,6 +50,11 @@ extern int x86_64_immediate_operand PARAMS ((rtx, enum machine_mode)); 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)); @@ -83,6 +88,9 @@ extern int ix86_expand_movstr PARAMS ((rtx, rtx, rtx, rtx)); 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)); @@ -91,6 +99,7 @@ extern rtx legitimize_address PARAMS ((rtx, rtx, enum machine_mode)); 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[])); @@ -198,6 +207,8 @@ extern void i386_pe_asm_named_section PARAMS ((const char *, unsigned int)); extern void x86_output_mi_thunk PARAMS ((FILE *, int, tree)); #endif +extern rtx ix86_tls_get_addr PARAMS ((void)); + /* In winnt.c */ extern void i386_pe_encode_section_info PARAMS ((tree, int)); extern const char *i386_pe_strip_name_encoding PARAMS ((const char *)); diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index f76dbcfe206c..ba52c7c3eac1 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -544,6 +544,10 @@ int const svr4_dbx_register_map[FIRST_PSEUDO_REGISTER] = 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) @@ -552,6 +556,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; }; @@ -604,6 +609,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 unit we are generating floating point math for. */ enum fpmath_unit ix86_fpmath; @@ -653,13 +661,17 @@ static char internal_label_prefix[16]; static int internal_label_prefix_len; static int local_symbolic_operand PARAMS ((rtx, enum machine_mode)); -static const char *get_pic_label_name PARAMS ((void)); +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)); @@ -698,7 +710,9 @@ struct ix86_address static int ix86_decompose_address PARAMS ((rtx, struct ix86_address *)); -static void i386_encode_section_info PARAMS ((tree, int)) ATTRIBUTE_UNUSED; +static void ix86_encode_section_info PARAMS ((tree, int)) ATTRIBUTE_UNUSED; +static const char *ix86_strip_name_encoding PARAMS ((const char *)) + ATTRIBUTE_UNUSED; struct builtin_description; static rtx ix86_expand_sse_comi PARAMS ((const struct builtin_description *, @@ -831,6 +845,11 @@ static enum x86_64_reg_class merge_classes PARAMS ((enum x86_64_reg_class, #define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD \ ia32_multipass_dfa_lookahead +#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 @@ -1120,6 +1139,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; @@ -2962,6 +2992,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 + 1; +} + +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. */ @@ -3802,14 +3896,6 @@ ix86_setup_frame_addresses () static char pic_label_name[32]; -static const char * -get_pic_label_name () -{ - if (! pic_label_name[0]) - ASM_GENERATE_INTERNAL_LABEL (pic_label_name, "LPR", 0); - return pic_label_name; -} - /* This function generates code for -fpic that loads %ebx with the return address of the caller and then returns. */ @@ -3819,7 +3905,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; /* ??? Binutils 2.10 and earlier has a linkonce elimination bug related @@ -3890,7 +3976,10 @@ output_set_got (dest) } else { - xops[2] = gen_rtx_SYMBOL_REF (Pmode, get_pic_label_name ()); + if (! pic_label_name[0]) + ASM_GENERATE_INTERNAL_LABEL (pic_label_name, "LPR", 0); + + 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); } @@ -4676,6 +4765,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. */ @@ -4683,6 +4880,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) @@ -4718,23 +4917,40 @@ 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) return 0; - /* Must be @GOT or @GOTOFF. */ switch (XINT (disp, 1)) { case UNSPEC_GOT: + if (saw_plus) + return false; return GET_CODE (XVECEXP (disp, 0, 0)) == SYMBOL_REF; 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: + /* ??? Could support offset here. */ + if (saw_plus) + return false; + return local_exec_symbolic_operand (XVECEXP (disp, 0, 0), Pmode); + case UNSPEC_DTPOFF: + /* ??? Could support offset here. */ + if (saw_plus) + return false; + return local_dynamic_symbolic_operand (XVECEXP (disp, 0, 0), Pmode); } return 0; @@ -4873,12 +5089,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)) @@ -4896,8 +5106,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"; @@ -4940,6 +5172,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. */ @@ -5122,23 +5359,100 @@ legitimize_pic_address (orig, reg) return new; } -/* If using PIC, mark a SYMBOL_REF for a non-global symbol so that we - may access it directly in the GOT. */ - static void -i386_encode_section_info (decl, first) +ix86_encode_section_info (decl, first) tree decl; int first ATTRIBUTE_UNUSED; { + bool local_p = (*targetm.binds_local_p) (decl); + 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; + + /* 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)) { - rtx rtl = DECL_P (decl) ? DECL_RTL (decl) : TREE_CST_RTL (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 (GET_CODE (rtl) == MEM && GET_CODE (XEXP (rtl, 0)) == SYMBOL_REF) - SYMBOL_REF_FLAG (XEXP (rtl, 0)) = (*targetm.binds_local_p) (decl); + 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. */ + +static 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. @@ -5176,6 +5490,85 @@ legitimize_address (x, oldx, mode) debug_rtx (x); } + /* Note that tls_symbolic_operand return is biased by 1 to return true. */ + log = tls_symbolic_operand (x, mode); + if (log) + { + rtx dest, base, off, pic; + + switch (log - 1) + { + 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); @@ -5425,12 +5818,21 @@ output_pic_addr_const (file, x, code) case UNSPEC_GOTOFF: fputs ("@GOTOFF", file); break; - case UNSPEC_PLT: - fputs ("@PLT", file); - break; 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; @@ -5720,6 +6122,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. @@ -5744,6 +6183,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 @@ -5761,6 +6201,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); @@ -6090,6 +6534,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') @@ -6238,6 +6694,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 @@ -6775,51 +7268,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]; + + /* ??? 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.) - if (flag_pic && mode == Pmode && symbolic_operand (operands[1], Pmode)) + 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]); + || !push_operand (op0, mode)) + && GET_CODE (op1) == MEM) + op1 = force_reg (mode, op1); - if (push_operand (operands[0], mode) - && ! general_no_elim_operand (operands[1], mode)) - operands[1] = copy_to_mode_reg (mode, operands[1]); + 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)) { @@ -6829,13 +7388,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); } @@ -8684,13 +9243,14 @@ ix86_split_to_parts (operand, parts, mode) 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)) { @@ -9993,6 +10553,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. */ diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h index aac6efab9d4b..59463704d0b4 100644 --- a/gcc/config/i386/i386.h +++ b/gcc/config/i386/i386.h @@ -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. */ @@ -426,6 +429,8 @@ extern int x86_prefetch_sse; "" /* 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 \ } @@ -1876,15 +1881,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) \ @@ -1947,9 +1949,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 \ @@ -2392,7 +2392,20 @@ enum ix86_builtins IX86_BUILTIN_MAX }; -#define TARGET_ENCODE_SECTION_INFO i386_encode_section_info +#define TARGET_ENCODE_SECTION_INFO ix86_encode_section_info +#define TARGET_STRIP_NAME_ENCODING ix86_strip_name_encoding + +#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 @@ -3047,7 +3060,7 @@ extern int const svr4_dbx_register_map[FIRST_PSEUDO_REGISTER]; 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. @@ -3066,6 +3079,12 @@ extern int const svr4_dbx_register_map[FIRST_PSEUDO_REGISTER]; #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 @@ -3195,7 +3214,12 @@ extern int const svr4_dbx_register_map[FIRST_PSEUDO_REGISTER]; {"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. */ @@ -3233,6 +3257,15 @@ enum fpmath_unit extern enum fpmath_unit ix86_fpmath; extern const char *ix86_fpmath_string; +enum tls_dialect +{ + TLS_DIALECT_GNU, + TLS_DIALECT_SUN +}; + +extern enum tls_dialect ix86_tls_dialect; +extern const char *ix86_tls_dialect_string; + enum cmodel { CM_32, /* The traditional 32-bit ABI. */ CM_SMALL, /* Assumes all code and data fits in the low 31 bits. */ diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index 1bdfac82a83a..2d19645f3a2b 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -53,23 +53,36 @@ ;; UNSPEC usage: (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_ADD_CARRY 12) + [; Relocation specifiers + (UNSPEC_GOT 0) + (UNSPEC_GOTOFF 1) + (UNSPEC_GOTPCREL 2) + (UNSPEC_GOTTPOFF 3) + (UNSPEC_TPOFF 4) + (UNSPEC_NTPOFF 5) + (UNSPEC_DTPOFF 6) + + ; Prologue support + (UNSPEC_STACK_PROBE 10) + (UNSPEC_STACK_ALLOC 11) + (UNSPEC_SET_GOT 12) (UNSPEC_SSE_PROLOGUE_SAVE 13) - (UNSPEC_FLDCW 14) - (UNSPEC_GOTPCREL 15) - (UNSPEC_SET_GOT 16) + + ; TLS support + (UNSPEC_TP 15) + (UNSPEC_TLS_GD 16) + (UNSPEC_TLS_LD_BASE 17) + + ; Other random patterns + (UNSPEC_SCAS 20) + (UNSPEC_SIN 21) + (UNSPEC_COS 22) + (UNSPEC_BSF 23) + (UNSPEC_FNSTSW 24) + (UNSPEC_SAHF 25) + (UNSPEC_FSTCW 26) + (UNSPEC_ADD_CARRY 27) + (UNSPEC_FLDCW 28) ; For SSE/MMX support: (UNSPEC_FIX 30) @@ -13330,6 +13343,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 diff --git a/gcc/configure b/gcc/configure index 60e65d3a36c0..483178de2187 100755 --- a/gcc/configure +++ b/gcc/configure @@ -7244,7 +7244,7 @@ libgcc_visibility=$gcc_cv_as_hidden echo $ac_n "checking assembler leb128 support""... $ac_c" 1>&6 -echo "configure:7228: checking assembler leb128 support" >&5 +echo "configure:7248: checking assembler leb128 support" >&5 gcc_cv_as_leb128=no if 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 2 -a "$gcc_cv_gas_minor_version" -ge 11 -o "$gcc_cv_gas_major_version" -gt 2 && grep 'obj_format = elf' ../gas/Makefile > /dev/null; then @@ -7289,7 +7289,7 @@ fi echo "$ac_t""$gcc_cv_as_leb128" 1>&6 echo $ac_n "checking assembler eh_frame optimization""... $ac_c" 1>&6 -echo "configure:7273: checking assembler eh_frame optimization" >&5 +echo "configure:7293: checking assembler eh_frame optimization" >&5 gcc_cv_as_eh_frame=no if 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 2 -a "$gcc_cv_gas_minor_version" -ge 12 -o "$gcc_cv_gas_major_version" -gt 2 && grep 'obj_format = elf' ../gas/Makefile > /dev/null; then @@ -7370,7 +7370,7 @@ fi echo "$ac_t""$gcc_cv_as_eh_frame" 1>&6 echo $ac_n "checking assembler section merging support""... $ac_c" 1>&6 -echo "configure:7354: checking assembler section merging support" >&5 +echo "configure:7374: checking assembler section merging support" >&5 gcc_cv_as_shf_merge=no if 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 2 -a "$gcc_cv_gas_minor_version" -ge 12 -o "$gcc_cv_gas_major_version" -gt 2 && grep 'obj_format = elf' ../gas/Makefile > /dev/null; then @@ -7392,11 +7392,61 @@ 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:7397: 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 + ;; +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*) echo $ac_n "checking assembler supports explicit relocations""... $ac_c" 1>&6 -echo "configure:7380: checking assembler supports explicit relocations" >&5 +echo "configure:7450: checking assembler supports explicit relocations" >&5 if eval "test \"`echo '$''{'gcc_cv_as_explicit_relocs'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -7446,7 +7496,7 @@ EOF ;; sparc*-*-*) echo $ac_n "checking assembler .register pseudo-op support""... $ac_c" 1>&6 -echo "configure:7430: checking assembler .register pseudo-op support" >&5 +echo "configure:7500: checking assembler .register pseudo-op support" >&5 if eval "test \"`echo '$''{'gcc_cv_as_register_pseudo_op'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -7474,7 +7524,7 @@ EOF fi echo $ac_n "checking assembler supports -relax""... $ac_c" 1>&6 -echo "configure:7458: checking assembler supports -relax" >&5 +echo "configure:7528: checking assembler supports -relax" >&5 if eval "test \"`echo '$''{'gcc_cv_as_relax_opt'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -7502,7 +7552,7 @@ EOF fi echo $ac_n "checking assembler and linker support unaligned pc related relocs""... $ac_c" 1>&6 -echo "configure:7486: checking assembler and linker support unaligned pc related relocs" >&5 +echo "configure:7556: checking assembler and linker support unaligned pc related relocs" >&5 if eval "test \"`echo '$''{'gcc_cv_as_sparc_ua_pcrel'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -7529,7 +7579,7 @@ EOF fi echo $ac_n "checking assembler and linker support unaligned pc related relocs against hidden symbols""... $ac_c" 1>&6 -echo "configure:7513: checking assembler and linker support unaligned pc related relocs against hidden symbols" >&5 +echo "configure:7583: checking assembler and linker support unaligned pc related relocs against hidden symbols" >&5 if eval "test \"`echo '$''{'gcc_cv_as_sparc_ua_pcrel_hidden'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -7570,7 +7620,7 @@ EOF if test "x$gcc_cv_as_flags64" != xno; then echo $ac_n "checking for assembler offsetable %lo() support""... $ac_c" 1>&6 -echo "configure:7554: checking for assembler offsetable %lo() support" >&5 +echo "configure:7624: checking for assembler offsetable %lo() support" >&5 if eval "test \"`echo '$''{'gcc_cv_as_offsetable_lo10'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -7610,7 +7660,7 @@ EOF i[34567]86-*-* | x86_64-*-*) echo $ac_n "checking assembler instructions""... $ac_c" 1>&6 -echo "configure:7594: checking assembler instructions" >&5 +echo "configure:7664: checking assembler instructions" >&5 gcc_cv_as_instructions= if 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 2 -a "$gcc_cv_gas_minor_version" -ge 9 -o "$gcc_cv_gas_major_version" -gt 2; then @@ -7637,7 +7687,7 @@ EOF echo "$ac_t""$gcc_cv_as_instructions" 1>&6 echo $ac_n "checking assembler GOTOFF in data directives""... $ac_c" 1>&6 -echo "configure:7621: checking assembler GOTOFF in data directives" >&5 +echo "configure:7691: checking assembler GOTOFF in data directives" >&5 gcc_cv_as_gotoff_in_data=no if test x$gcc_cv_gas_major_version != x -a x$gcc_cv_gas_minor_version != x then @@ -7667,7 +7717,7 @@ EOF esac echo $ac_n "checking assembler dwarf2 debug_line support""... $ac_c" 1>&6 -echo "configure:7651: checking assembler dwarf2 debug_line support" >&5 +echo "configure:7721: checking assembler dwarf2 debug_line support" >&5 gcc_cv_as_dwarf2_debug_line=no # ??? Not all targets support dwarf2 debug_line, even within a version # of gas. Moreover, we need to emit a valid instruction to trigger any @@ -7723,7 +7773,7 @@ fi echo "$ac_t""$gcc_cv_as_dwarf2_debug_line" 1>&6 echo $ac_n "checking assembler --gdwarf2 support""... $ac_c" 1>&6 -echo "configure:7707: checking assembler --gdwarf2 support" >&5 +echo "configure:7777: checking assembler --gdwarf2 support" >&5 gcc_cv_as_gdwarf2_flag=no if test x$gcc_cv_gas_major_version != x -a x$gcc_cv_gas_minor_version != x; then @@ -7752,7 +7802,7 @@ fi echo "$ac_t""$gcc_cv_as_gdwarf2_flag" 1>&6 echo $ac_n "checking assembler --gstabs support""... $ac_c" 1>&6 -echo "configure:7736: checking assembler --gstabs support" >&5 +echo "configure:7806: checking assembler --gstabs support" >&5 gcc_cv_as_gstabs_flag=no if test x$gcc_cv_gas_major_version != x -a x$gcc_cv_gas_minor_version != x; then @@ -7780,7 +7830,7 @@ fi echo "$ac_t""$gcc_cv_as_gstabs_flag" 1>&6 echo $ac_n "checking linker PT_GNU_EH_FRAME support""... $ac_c" 1>&6 -echo "configure:7764: checking linker PT_GNU_EH_FRAME support" >&5 +echo "configure:7834: checking linker PT_GNU_EH_FRAME support" >&5 gcc_cv_ld_eh_frame_hdr=no if test x$gcc_cv_gld_major_version != x -a x$gcc_cv_gld_minor_version != x; then if test "$gcc_cv_gld_major_version" -eq 2 -a "$gcc_cv_gld_minor_version" -ge 12 -o "$gcc_cv_gld_major_version" -gt 2 && grep 'EMUL = elf' ../ld/Makefile > /dev/null; then @@ -7943,7 +7993,7 @@ fi echo $ac_n "checking whether to enable maintainer-specific portions of Makefiles""... $ac_c" 1>&6 -echo "configure:7927: checking whether to enable maintainer-specific portions of Makefiles" >&5 +echo "configure:7997: checking whether to enable maintainer-specific portions of Makefiles" >&5 # Check whether --enable-maintainer-mode or --disable-maintainer-mode was given. if test "${enable_maintainer_mode+set}" = set; then enableval="$enable_maintainer_mode" diff --git a/gcc/configure.in b/gcc/configure.in index 71bfca708f91..8dcb671cf32a 100644 --- a/gcc/configure.in +++ b/gcc/configure.in @@ -1718,6 +1718,55 @@ if test x"$gcc_cv_as_shf_merge" = xyes; then 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 + ;; +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*)