Hi,
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.
Tested on i686-w64-mingw32 and x86_64-w64-mingw32, OK for the mainline?
2025-12-10 Eric Botcazou <[email protected]>
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.
2025-12-10 Eric Botcazou <[email protected]>
* gcc.dg/tls/struct-2.c: New test.
--
Eric Botcazoudiff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
index db43045753b..097025c73f6 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 df7135f84d4..574685a5398 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -23507,10 +23507,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"
@@ -23523,10 +23521,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>"
@@ -23543,10 +23539,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"
@@ -23564,10 +23558,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..b1c9aba568c 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);
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
/* { 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;
}