From: Eric Botcazou Date: Thu, 11 Dec 2025 09:02:35 +0000 (+0100) Subject: i386: Fix and rework Windows TLS support X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=680df0a476512393e6e9cbd0255d3863baacf826;p=thirdparty%2Fgcc.git i386: Fix and rework Windows TLS support The compiler can emit invalid movabs instructions at -O0 for TLS accesses in 64-bit mode on Windows, for example with the attached testcase: eric@fomalhaut:~/build/gcc/x86_64-w64-mingw32> gcc/xgcc -Bgcc -c struct-2.c /tmp/ccOM8wdd.s: Assembler messages: /tmp/ccOM8wdd.s:34: Error: operand type mismatch for `movabs' This fixes the issue by leveraging the existing pic_32bit_operand predicate, and fixing an oversight present in it for a couple of decades. The patch also reworks the support to make use of the get_thread_pointer machinery as for other platforms, of more comments and of shorter lines. gcc/ PR target/80881 * config/i386/i386.h (DEFAULT_TLS_SEG_OFFSET): New define. * config/mingw/mingw32.h (DEFAULT_TLS_SEG_OFFSET): Likewise. * config/i386/i386.cc (ix86_tls_index): Fix long line. (legitimize_tls_address): Use get_thread_pointer, add comments and fix long lines. * config/i386/i386.md (*load_tp_): Use DEFAULT_TLS_SEG_OFFSET (*load_tp_x32_zext): Likewise. (*add_tp_): Likewise. (*add_tp_x32_zext): Likewise. * config/i386/predicates.md (pic_32bit_operand): Fix oversight. (symbolic_operand): Accept UNSPEC_SECREL32 with or without offset. gcc/testsuite/ * gcc.dg/tls/struct-2.c: New test. --- diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc index 75a9cb6211a..001169b0d66 100644 --- a/gcc/config/i386/i386.cc +++ b/gcc/config/i386/i386.cc @@ -12454,7 +12454,6 @@ get_thread_pointer (machine_mode tp_mode, bool to_reg) static GTY(()) rtx ix86_tls_index_symbol; -#if TARGET_WIN32_TLS static rtx ix86_tls_index (void) { @@ -12462,11 +12461,13 @@ ix86_tls_index (void) ix86_tls_index_symbol = gen_rtx_SYMBOL_REF (SImode, "_tls_index"); if (flag_pic) - return gen_rtx_CONST (Pmode, gen_rtx_UNSPEC (Pmode, gen_rtvec (1, ix86_tls_index_symbol), UNSPEC_PCREL)); + return gen_rtx_CONST (Pmode, + gen_rtx_UNSPEC (Pmode, + gen_rtvec (1, ix86_tls_index_symbol), + UNSPEC_PCREL)); else return ix86_tls_index_symbol; } -#endif /* Construct the SYMBOL_REF for the tls_get_addr function. */ @@ -12548,26 +12549,33 @@ legitimize_tls_address (rtx x, enum tls_model model, bool for_mov) machine_mode tp_mode = Pmode; int type; -#if TARGET_WIN32_TLS - off = gen_const_mem (SImode, ix86_tls_index ()); - set_mem_alias_set (off, GOT_ALIAS_SET); - - tp = gen_const_mem (Pmode, GEN_INT (TARGET_64BIT ? 88 : 44)); - set_mem_addr_space (tp, DEFAULT_TLS_SEG_REG); - - if (TARGET_64BIT) - off = convert_to_mode (Pmode, off, 1); - - base = force_reg (Pmode, off); - tp = copy_to_mode_reg (Pmode, tp); - - tp = gen_const_mem (Pmode, gen_rtx_PLUS (Pmode, tp, gen_rtx_MULT (Pmode, base, GEN_INT (UNITS_PER_WORD)))); - set_mem_alias_set (tp, GOT_ALIAS_SET); - - base = force_reg (Pmode, tp); + /* Windows implements a single form of TLS. */ + if (TARGET_WIN32_TLS) + { + /* Load the 32-bit index. */ + rtx ind = gen_const_mem (SImode, ix86_tls_index ()); + set_mem_alias_set (ind, GOT_ALIAS_SET); + if (TARGET_64BIT) + ind = convert_to_mode (Pmode, ind, 1); + ind = force_reg (Pmode, ind); + + /* Add it to the thread pointer and load the base. */ + tp = get_thread_pointer (Pmode, true); + rtx addr = gen_rtx_PLUS (Pmode, tp, + gen_rtx_MULT (Pmode, ind, + GEN_INT (UNITS_PER_WORD))); + base = gen_const_mem (Pmode, addr); + set_mem_alias_set (base, GOT_ALIAS_SET); + + /* Add the 32-bit section-relative offset to the base. */ + base = force_reg (Pmode, base); + off = gen_rtx_CONST (Pmode, + gen_rtx_UNSPEC (SImode, + gen_rtvec (1, x), + UNSPEC_SECREL32)); + return gen_rtx_PLUS (Pmode, base, off); + } - return gen_rtx_PLUS (Pmode, base, gen_rtx_CONST (Pmode, gen_rtx_UNSPEC (SImode, gen_rtvec (1, x), UNSPEC_SECREL32))); -#else /* Fall back to global dynamic model if tool chain cannot support local dynamic. */ if (TARGET_SUN_TLS && !TARGET_64BIT @@ -12790,7 +12798,6 @@ legitimize_tls_address (rtx x, enum tls_model model, bool for_mov) } return dest; -#endif } /* Return true if the TLS address requires insn using integer registers. diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h index b93411796af..4f5954507ce 100644 --- a/gcc/config/i386/i386.h +++ b/gcc/config/i386/i386.h @@ -619,6 +619,9 @@ extern GTY(()) tree x86_mfence; #define DEFAULT_TLS_SEG_REG \ (TARGET_64BIT ? ADDR_SPACE_SEG_FS : ADDR_SPACE_SEG_GS) +/* The default TLS segment offset used by target. */ +#define DEFAULT_TLS_SEG_OFFSET 0 + /* Subtargets may reset this to 1 in order to enable 96-bit long double with the rounding mode forced to 53 bits. */ #define TARGET_96_ROUND_53_LONG_DOUBLE 0 diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index b5d83893425..7eafdf9ec1b 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -23549,10 +23549,8 @@ [(set (match_dup 0) (match_dup 1))] { - addr_space_t as = DEFAULT_TLS_SEG_REG; - - operands[1] = gen_const_mem (mode, const0_rtx); - set_mem_addr_space (operands[1], as); + operands[1] = gen_const_mem (mode, GEN_INT (DEFAULT_TLS_SEG_OFFSET)); + set_mem_addr_space (operands[1], DEFAULT_TLS_SEG_REG); }) (define_insn_and_split "*load_tp_x32_zext" @@ -23565,10 +23563,8 @@ [(set (match_dup 0) (zero_extend:DI (match_dup 1)))] { - addr_space_t as = DEFAULT_TLS_SEG_REG; - - operands[1] = gen_const_mem (SImode, const0_rtx); - set_mem_addr_space (operands[1], as); + operands[1] = gen_const_mem (SImode, GEN_INT (DEFAULT_TLS_SEG_OFFSET)); + set_mem_addr_space (operands[1], DEFAULT_TLS_SEG_REG); }) (define_insn_and_split "*add_tp_" @@ -23585,10 +23581,8 @@ (plus:PTR (match_dup 1) (match_dup 2))) (clobber (reg:CC FLAGS_REG))])] { - addr_space_t as = DEFAULT_TLS_SEG_REG; - - operands[2] = gen_const_mem (mode, const0_rtx); - set_mem_addr_space (operands[2], as); + operands[2] = gen_const_mem (mode, GEN_INT (DEFAULT_TLS_SEG_OFFSET)); + set_mem_addr_space (operands[2], DEFAULT_TLS_SEG_REG); }) (define_insn_and_split "*add_tp_x32_zext" @@ -23606,10 +23600,8 @@ (plus:SI (match_dup 1) (match_dup 2)))) (clobber (reg:CC FLAGS_REG))])] { - addr_space_t as = DEFAULT_TLS_SEG_REG; - - operands[2] = gen_const_mem (SImode, const0_rtx); - set_mem_addr_space (operands[2], as); + operands[2] = gen_const_mem (SImode, GEN_INT (DEFAULT_TLS_SEG_OFFSET)); + set_mem_addr_space (operands[2], DEFAULT_TLS_SEG_REG); }) ;; GNU2 TLS patterns can be split. diff --git a/gcc/config/i386/predicates.md b/gcc/config/i386/predicates.md index 2863b3ec333..db60fd810b3 100644 --- a/gcc/config/i386/predicates.md +++ b/gcc/config/i386/predicates.md @@ -543,12 +543,12 @@ /* Rule out relocations that translate into 64bit constants. */ if (TARGET_64BIT && GET_CODE (op) == CONST) { - op = XEXP (op, 0); - if (GET_CODE (op) == PLUS && CONST_INT_P (XEXP (op, 1))) - op = XEXP (op, 0); - if (GET_CODE (op) == UNSPEC - && (XINT (op, 1) == UNSPEC_GOTOFF - || XINT (op, 1) == UNSPEC_GOT)) + rtx tmp = XEXP (op, 0); + if (GET_CODE (tmp) == PLUS && CONST_INT_P (XEXP (tmp, 1))) + tmp = XEXP (tmp, 0); + if (GET_CODE (tmp) == UNSPEC + && (XINT (tmp, 1) == UNSPEC_GOTOFF + || XINT (tmp, 1) == UNSPEC_GOT)) return false; } @@ -578,6 +578,7 @@ || (GET_CODE (op) == UNSPEC && (XINT (op, 1) == UNSPEC_GOT || XINT (op, 1) == UNSPEC_GOTOFF + || XINT (op, 1) == UNSPEC_SECREL32 || XINT (op, 1) == UNSPEC_PCREL || XINT (op, 1) == UNSPEC_GOTPCREL))) return true; @@ -589,9 +590,10 @@ if (SYMBOL_REF_P (op) || LABEL_REF_P (op)) return true; - /* Only @GOTOFF gets offsets. */ + /* Only @GOTOFF and @SECREL32 get offsets. */ if (GET_CODE (op) != UNSPEC - || XINT (op, 1) != UNSPEC_GOTOFF) + || (XINT (op, 1) != UNSPEC_GOTOFF + && XINT (op, 1) != UNSPEC_SECREL32)) return false; op = XVECEXP (op, 0, 0); @@ -1901,7 +1903,7 @@ { int nelt = XVECLEN (op, 0); int elt, i; - + if (nelt < 2) return false; diff --git a/gcc/config/mingw/mingw32.h b/gcc/config/mingw/mingw32.h index be2461f4db8..3ff9552c31d 100644 --- a/gcc/config/mingw/mingw32.h +++ b/gcc/config/mingw/mingw32.h @@ -315,7 +315,11 @@ do { \ #define TARGET_ASM_SELECT_SECTION mingw_pe_select_section #undef DEFAULT_TLS_SEG_REG -#define DEFAULT_TLS_SEG_REG (TARGET_64BIT ? ADDR_SPACE_SEG_GS : ADDR_SPACE_SEG_FS) +#define DEFAULT_TLS_SEG_REG \ + (TARGET_64BIT ? ADDR_SPACE_SEG_GS : ADDR_SPACE_SEG_FS) + +#undef DEFAULT_TLS_SEG_OFFSET +#define DEFAULT_TLS_SEG_OFFSET (TARGET_64BIT ? 88 : 44) #define HAVE_ENABLE_EXECUTE_STACK #undef CHECK_EXECUTE_STACK_ENABLED diff --git a/gcc/testsuite/gcc.dg/tls/struct-2.c b/gcc/testsuite/gcc.dg/tls/struct-2.c new file mode 100644 index 00000000000..ac8538c49ee --- /dev/null +++ b/gcc/testsuite/gcc.dg/tls/struct-2.c @@ -0,0 +1,21 @@ +/* { dg-do assemble } */ +/* { dg-require-effective-target tls } */ +/* { dg-add-options tls } */ + +struct pixel +{ + unsigned int r, g, b; +}; + +struct line +{ + unsigned int length; + struct pixel data[16]; +}; + +__thread struct line L; + +unsigned int read_r (unsigned int i) +{ + return i < L.length ? L.data[i].r : 0; +}