[gcc r16-6023] i386: Fix and rework Windows TLS support
Eric Botcazou
ebotcazou@gcc.gnu.org
Thu Dec 11 12:38:06 GMT 2025
https://gcc.gnu.org/g:680df0a476512393e6e9cbd0255d3863baacf826
commit r16-6023-g680df0a476512393e6e9cbd0255d3863baacf826
Author: Eric Botcazou <ebotcazou@adacore.com>
Date: Thu Dec 11 10:02:35 2025 +0100
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_<mode>): Use DEFAULT_TLS_SEG_OFFSET
(*load_tp_x32_zext): Likewise.
(*add_tp_<mode>): 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:
---
gcc/config/i386/i386.cc | 53 +++++++++++++++++++++----------------
gcc/config/i386/i386.h | 3 +++
gcc/config/i386/i386.md | 24 ++++++-----------
gcc/config/i386/predicates.md | 20 +++++++-------
gcc/config/mingw/mingw32.h | 6 ++++-
gcc/testsuite/gcc.dg/tls/struct-2.c | 21 +++++++++++++++
6 files changed, 78 insertions(+), 49 deletions(-)
diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
index 75a9cb6211a1..001169b0d66b 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 b93411796af3..4f5954507cea 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 b5d838934251..7eafdf9ec1b6 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>mode, const0_rtx);
- set_mem_addr_space (operands[1], as);
+ operands[1] = gen_const_mem (<MODE>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_<mode>"
@@ -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>mode, const0_rtx);
- set_mem_addr_space (operands[2], as);
+ operands[2] = gen_const_mem (<MODE>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 2863b3ec3337..db60fd810b3d 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 be2461f4db83..3ff9552c31df 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 000000000000..ac8538c49ee9
--- /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;
+}
More information about the Gcc-cvs
mailing list