On Sun, Sep 27, 2026 at 4:16 PM mironu <[email protected]> wrote:
>
> The MIPS backend currently uses rdhwr to access the thread pointer.
> This instruction is unavailable on older MIPS processors such as the
> R3900.
>
> Add -mtp=soft to allow the MIPS backend to obtain the thread pointer
> through the __mips_read_tp runtime helper instead of using rdhwr.
> The default -mtp=rdhwr behavior is unchanged.
>
> Tests cover the software thread-pointer path for normal MIPS, MIPS16,
> and MIPS16 with abicalls, as well as the default rdhwr path.
So at Cavium, we had patches to allow us to use a GRP instead of
rdhwr. This was for Octeon 1 which was just a normal MIPS64r1 (not r2
which added rdhwr); we removed that hack once Octeon 2 came around
which had native rdhwr support (Octeon 3 supported it too).
This was only done for speed reasons because the Linux kernel would do
a trap and handle the rdhwr anyways.
Can you expand on why you want to allow not to use rdhwr? And the
effects on the ABI here; is __mips_read_tp documented as part of some
ABI documentation? Or did you make this up?
>
> gcc/ChangeLog:
>
> * config/mips/mips-opts.h (enum mips_thread_pointer_setting):
> Add MIPS_TP_SOFT.
> * config/mips/mips.cc (mips_expand_thread_pointer):
> Generate a call to __mips_read_tp when -mtp=soft is used.
> * config/mips/mips.opt: Add -mtp option with rdhwr and soft
> thread-pointer access methods.
> * config/mips/mips.opt.urls: Add URL for -mtp.
> * doc/invoke.texi: Document -mtp.
>
> gcc/testsuite/ChangeLog:
>
> * gcc.target/mips/tls-hard-tp.c: New test.
> * gcc.target/mips/tls-soft-tp-mips16-abicalls.c: New test.
> * gcc.target/mips/tls-soft-tp-mips16.c: New test.
> * gcc.target/mips/tls-soft-tp.c: New test.
> * gcc.target/mips/mips.exp: Add tp to mips_option_groups.
> ---
> gcc/config/mips/mips-opts.h | 6 ++++++
> gcc/config/mips/mips.cc | 17 +++++++++++++++--
> gcc/config/mips/mips.opt | 14 ++++++++++++++
> gcc/config/mips/mips.opt.urls | 3 +++
> gcc/doc/invoke.texi | 7 +++++++
> gcc/testsuite/gcc.target/mips/mips.exp | 1 +
> gcc/testsuite/gcc.target/mips/tls-hard-tp.c | 13 +++++++++++++
> .../mips/tls-soft-tp-mips16-abicalls.c | 14 ++++++++++++++
> .../gcc.target/mips/tls-soft-tp-mips16.c | 14 ++++++++++++++
> gcc/testsuite/gcc.target/mips/tls-soft-tp.c | 13 +++++++++++++
> 10 files changed, 100 insertions(+), 2 deletions(-)
> create mode 100644 gcc/testsuite/gcc.target/mips/tls-hard-tp.c
> create mode 100644
> gcc/testsuite/gcc.target/mips/tls-soft-tp-mips16-abicalls.c
> create mode 100644 gcc/testsuite/gcc.target/mips/tls-soft-tp-mips16.c
> create mode 100644 gcc/testsuite/gcc.target/mips/tls-soft-tp.c
>
> diff --git a/gcc/config/mips/mips-opts.h b/gcc/config/mips/mips-opts.h
> index d8b1b4c632c..c53fdbe4b5c 100644
> --- a/gcc/config/mips/mips-opts.h
> +++ b/gcc/config/mips/mips-opts.h
> @@ -41,6 +41,12 @@ enum mips_r10k_cache_barrier_setting {
> R10K_CACHE_BARRIER_LOAD_STORE
> };
>
> +/* Enumerates the setting of the -mtp option. */
> +enum mips_thread_pointer_setting {
> + MIPS_TP_RDHWR,
> + MIPS_TP_SOFT
> +};
> +
> /* No enumeration is defined to index the -march= values (entries in
> mips_cpu_info_table), with the type int being used instead, but we
> need to distinguish the special "from-abi" and "native" values. */
> diff --git a/gcc/config/mips/mips.cc b/gcc/config/mips/mips.cc
> index e54a310b2b4..1930ea6d5c6 100644
> --- a/gcc/config/mips/mips.cc
> +++ b/gcc/config/mips/mips.cc
> @@ -3610,6 +3610,9 @@ mips_add_offset (rtx temp, rtx reg, HOST_WIDE_INT
> offset)
> /* The __tls_get_attr symbol. */
> static GTY(()) rtx mips_tls_symbol;
>
> +/* The helper used to read the thread pointer in soft mode. */
> +static GTY(()) rtx mips_read_tp_symbol;
> +
> /* Return an instruction sequence that calls __tls_get_addr. SYM is
> the TLS symbol we are referencing and TYPE is the symbol type to use
> (either global dynamic or local dynamic). V0 is an RTX for the
> @@ -3646,9 +3649,19 @@ mips_call_tls_get_addr (rtx sym, enum mips_symbol_type
> type, rtx v0)
> rtx
> mips_expand_thread_pointer (rtx tp)
> {
> - rtx fn;
> + rtx fn, v0;
>
> - if (TARGET_MIPS16)
> + if (mips_thread_pointer == MIPS_TP_SOFT)
> + {
> + if (!mips_read_tp_symbol)
> + mips_read_tp_symbol = init_one_libfunc ("__mips_read_tp");
> +
> + v0 = gen_rtx_REG (Pmode, GP_RETURN);
> + mips_expand_call (MIPS_CALL_NORMAL, v0, mips_read_tp_symbol,
> + const0_rtx, NULL_RTX, false);
> + emit_move_insn (tp, v0);
> + }
> + else if (TARGET_MIPS16)
> {
> if (!mips16_rdhwr_stub)
> mips16_rdhwr_stub = new mips16_rdhwr_one_only_stub ();
> diff --git a/gcc/config/mips/mips.opt b/gcc/config/mips/mips.opt
> index eb14761ea27..e0fadb4e43a 100644
> --- a/gcc/config/mips/mips.opt
> +++ b/gcc/config/mips/mips.opt
> @@ -425,6 +425,20 @@ mtune=
> Target RejectNegative Joined Var(mips_tune_option) ToLower
> Enum(mips_arch_opt_value)
> -mtune=PROCESSOR Optimize the output for PROCESSOR.
>
> +mtp=
> +Target RejectNegative Joined Enum(mips_thread_pointer_setting)
> Var(mips_thread_pointer) Init(MIPS_TP_RDHWR)
> +-mtp=METHOD Specify how to access the thread pointer.
> +
> +Enum
> +Name(mips_thread_pointer_setting) Type(enum mips_thread_pointer_setting)
> +Valid arguments to -mtp=:
> +
> +EnumValue
> +Enum(mips_thread_pointer_setting) String(rdhwr) Value(MIPS_TP_RDHWR)
> +
> +EnumValue
> +Enum(mips_thread_pointer_setting) String(soft) Value(MIPS_TP_SOFT)
> +
> mstrict-align
> Target Var(TARGET_STRICT_ALIGN) Init(0)
> Don't generate code with unaligned load store, only valid for MIPS R6.
> diff --git a/gcc/config/mips/mips.opt.urls b/gcc/config/mips/mips.opt.urls
> index 6287d9f1c70..1f1bbef5dc9 100644
> --- a/gcc/config/mips/mips.opt.urls
> +++ b/gcc/config/mips/mips.opt.urls
> @@ -243,6 +243,9 @@ UrlSuffix(gcc/MIPS-Options.html#index-mmadd4)
> mtune=
> UrlSuffix(gcc/MIPS-Options.html#index-mtune-9)
>
> +mtp=
> +UrlSuffix(gcc/MIPS-Options.html#index-mtp)
> +
> mstrict-align
> UrlSuffix(gcc/MIPS-Options.html#index-mstrict-align-3)
>
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index c5d17c059fd..1bbb90e43f9 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -29125,6 +29125,13 @@ particular member of that family.
> @code{_MIPS_TUNE_@var{foo}}, which work in the same way as the
> @option{-march} ones described above.
>
> +@opindex mtp
> +@item -mtp=@var{method}
> +Specify how to access the thread pointer. The default, @samp{rdhwr},
> +uses the @code{rdhwr $3,$29} instruction. @samp{soft} generates a call
> +to @code{__mips_read_tp} instead. The helper must return the current
> +thread pointer in the normal MIPS function return register.
> +
> @opindex mips1
> @item -mips1
> Equivalent to @option{-march=mips1}.
> diff --git a/gcc/testsuite/gcc.target/mips/mips.exp
> b/gcc/testsuite/gcc.target/mips/mips.exp
> index 9492b1dcaf8..8ded794b761 100644
> --- a/gcc/testsuite/gcc.target/mips/mips.exp
> +++ b/gcc/testsuite/gcc.target/mips/mips.exp
> @@ -314,6 +314,7 @@ foreach option {
> code-readable
> nan
> r10k-cache-barrier
> + tp
> tune
> } {
> lappend mips_option_groups $option "-m$option=.*"
> diff --git a/gcc/testsuite/gcc.target/mips/tls-hard-tp.c
> b/gcc/testsuite/gcc.target/mips/tls-hard-tp.c
> new file mode 100644
> index 00000000000..4b23d561b02
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/mips/tls-hard-tp.c
> @@ -0,0 +1,13 @@
> +/* { dg-do compile } */
> +/* { dg-options "-march=mips32r2 -mgp32 -mno-abicalls" } */
> +
> +extern __thread int x __attribute__ ((tls_model ("initial-exec")));
> +
> +int
> +foo (void)
> +{
> + return x;
> +}
> +
> +/* { dg-final { scan-assembler "rdhwr" } } */
> +/* { dg-final { scan-assembler-not "__mips_read_tp" } } */
> diff --git a/gcc/testsuite/gcc.target/mips/tls-soft-tp-mips16-abicalls.c
> b/gcc/testsuite/gcc.target/mips/tls-soft-tp-mips16-abicalls.c
> new file mode 100644
> index 00000000000..11f73026592
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/mips/tls-soft-tp-mips16-abicalls.c
> @@ -0,0 +1,14 @@
> +/* { dg-do compile } */
> +/* { dg-options "-march=r3900 -mips16 -mtp=soft -mgp32 -mabicalls
> -mno-shared" } */
> +
> +extern __thread int x __attribute__ ((tls_model ("initial-exec")));
> +
> +int
> +foo (void)
> +{
> + return x;
> +}
> +
> +/* { dg-final { scan-assembler "__mips_read_tp" } } */
> +/* { dg-final { scan-assembler-not "rdhwr" } } */
> +/* { dg-final { scan-assembler-not "__mips16_rdhwr" } } */
> diff --git a/gcc/testsuite/gcc.target/mips/tls-soft-tp-mips16.c
> b/gcc/testsuite/gcc.target/mips/tls-soft-tp-mips16.c
> new file mode 100644
> index 00000000000..0efa8b37cb3
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/mips/tls-soft-tp-mips16.c
> @@ -0,0 +1,14 @@
> +/* { dg-do compile } */
> +/* { dg-options "-march=r3900 -mips16 -mtp=soft -mgp32 -mno-abicalls" } */
> +
> +extern __thread int x __attribute__ ((tls_model ("initial-exec")));
> +
> +int
> +foo (void)
> +{
> + return x;
> +}
> +
> +/* { dg-final { scan-assembler "__mips_read_tp" } } */
> +/* { dg-final { scan-assembler-not "rdhwr" } } */
> +/* { dg-final { scan-assembler-not "__mips16_rdhwr" } } */
> diff --git a/gcc/testsuite/gcc.target/mips/tls-soft-tp.c
> b/gcc/testsuite/gcc.target/mips/tls-soft-tp.c
> new file mode 100644
> index 00000000000..a771d431dec
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/mips/tls-soft-tp.c
> @@ -0,0 +1,13 @@
> +/* { dg-do compile } */
> +/* { dg-options "-march=r3900 -mtp=soft -mgp32 -mno-abicalls" } */
> +
> +extern __thread int x __attribute__ ((tls_model ("initial-exec")));
> +
> +int
> +foo (void)
> +{
> + return x;
> +}
> +
> +/* { dg-final { scan-assembler "__mips_read_tp" } } */
> +/* { dg-final { scan-assembler-not "rdhwr" } } */
> --
> 2.43.0
>