[This is my first time contributing to GNU community. Please don't hesitate to
let me know if I've done anything inappropriately :)]
One concern I have with this patch myself is about ra register. My solution of
modeling TLSDESC call as a real CALL_INSN with a custom ABI, which is mirrored
from AArch64 TLSDESC with SVE, is fairly intuitive. But ra register would be
implicitly treated as clobbered in the current GCC framework. Not sure how much
negative affect it would have (may need to save/restore ra in some
circumstances), and I haven't thought of a good way to avoid it. Please correct
me if I misunderstood something.
Another alternative way is explicitly listing all v-regs as "clobbered" in the
define_insn, like what's done in <80c13ac5>. But that may be little bit ugly
for RISCV I'm afraid, like this:
(define_insn "*tlsdesc_vec<mode>"
[(set (reg:P A0_REGNUM)
(unspec:P [(match_operand:P 0 "symbolic_operand" "")]
UNSPEC_TLSDESC))
(clobber (reg:P T0_REGNUM))
(clobber (reg:RVVM1BI 96))
(clobber (reg:RVVM1BI 97))
(clobber (reg:RVVM1BI 98))
(clobber (reg:RVVM1BI 99))
(clobber (reg:RVVM1BI 100))
(clobber (reg:RVVM1BI 101))
(clobber (reg:RVVM1BI 102))
(clobber (reg:RVVM1BI 103))
(clobber (reg:RVVM1BI 104))
(clobber (reg:RVVM1BI 105))
(clobber (reg:RVVM1BI 106))
...
------------------------------------------------------------------
发件人:Luke Zhuang <[email protected]>
发送时间:2026年6月23日(周二) 17:27
收件人:"gcc-patches"<[email protected]>
抄 送:"kito.cheng"<[email protected]>; palmer<[email protected]>;
"rdapp.gcc"<[email protected]>; andrew<[email protected]>;
"jim.wilson.gcc"<[email protected]>;
"juzhe.zhong"<[email protected]>; rdsandiford<[email protected]>;
"zhuangzhi.zz"<[email protected]>; Luke
Zhuang<[email protected]>
主 题:[PATCH] RISC-V: Declare TLSDESC clobbers for vector registers and CSRs per
psABI
From: "Luke Zhuang" <[email protected]>
Per the RISC-V psABI, the TLSDESC resolver clobbers a0 and t0 in the
base case. With the new psABI update
(https://github.com/riscv-non-isa/riscv-elf-psabi-doc/pull/496
<https://github.com/riscv-non-isa/riscv-elf-psabi-doc/pull/496 >),
when the V extension is enabled, it additionally clobbers all vector
registers and vector CSRs.
Mirroring how AArch64 handles SVE clobbers in its TLSDESC
implementation (<80c13ac5> and <bb6ce448>), this patch models
the TLSDESC call as a real CALL_INSN with a custom ABI
(RISCV_CC_TLSDESC) and describes its clobber set. The only
downside is that it forces the containing function to become
non-leaf, requiring the return address to be saved and restored
(see the tlsdesc_clobber.c test). This overhead is not mandated
by the TLSDESC psABI, but is an inherent consequence of how
GCC's internal passes treat any function containing a CALL_INSN.
Currently seems no way to avoid it within the existing framework.
Besides that, RISC-V now requires 13 predefined ABI IDs: base, V, 10 VLS
variants, and a new TLSDESC ABI. So we also need to increase
NUM_ABI_IDS from 12 to 13 to accommodate the new ID.
Three new tests are added covering GPR, vector register, and vector
CSR clobber behavior.
gcc/ChangeLog:
* config/riscv/riscv.h (enum riscv_cc): Add RISCV_CC_TLSDESC.
* config/riscv/riscv-protos.h (riscv_tlsdesc_abi_id): Declare.
* config/riscv/riscv.cc (riscv_tlsdesc_abi_id): New function.
* config/riscv/riscv.md (VXSAT_REGNUM): New constant.
(@tlsdesc<mode>): Convert to define_expand.
(tlsdesc_call<mode>): New define_insn.
* function-abi.h (NUM_ABI_IDS): Increase from 12 to 13.
gcc/testsuite/ChangeLog:
* gcc.target/riscv/tlsdesc_clobber.c: New test.
* gcc.target/riscv/tlsdesc_clobber_v.c: New test.
* gcc.target/riscv/tlsdesc_clobber_v_csr.c: New test.
---
gcc/config/riscv/riscv-protos.h | 1 +
gcc/config/riscv/riscv.cc | 29 ++++++++++++++
gcc/config/riscv/riscv.h | 1 +
gcc/config/riscv/riscv.md | 26 ++++++++++---
gcc/function-abi.h | 2 +-
.../gcc.target/riscv/tlsdesc_clobber.c | 32 +++++++++++++++
.../gcc.target/riscv/tlsdesc_clobber_v.c | 36 +++++++++++++++++
.../gcc.target/riscv/tlsdesc_clobber_v_csr.c | 39 +++++++++++++++++++
8 files changed, 160 insertions(+), 6 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/riscv/tlsdesc_clobber.c
create mode 100644 gcc/testsuite/gcc.target/riscv/tlsdesc_clobber_v.c
create mode 100644 gcc/testsuite/gcc.target/riscv/tlsdesc_clobber_v_csr.c
diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
index f429ca86a8f..b3a009aa389 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -900,6 +900,7 @@ extern bool arcv_mpy_10c_bypass_p (rtx_insn *, rtx_insn *);
extern bool strided_load_broadcast_p (void);
extern bool riscv_prefer_agnostic_p (void);
+extern enum riscv_cc riscv_tlsdesc_abi_id (void);
extern bool riscv_use_divmod_expander (void);
void riscv_init_cumulative_args (CUMULATIVE_ARGS *, const_tree,
rtx, tree, int, bool);
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index 29d9aaf1ffd..d5a75c5e62e 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -7550,6 +7550,35 @@ riscv_v_abi (riscv_cc abi)
return v_abi;
}
+/* Return the ID of the TLSDESC ABI, initializing the descriptor if it hasn't
+ been already. The TLSDESC resolver always clobbers a0 and t0. When the
+ V extension is enabled, it additionally clobbers all vector registers
+ and all vector CSRs (vl, vtype, vxrm, vxsat) per the psABI. */
+
+riscv_cc
+riscv_tlsdesc_abi_id ()
+{
+ predefined_function_abi &tlsdesc_abi = function_abis[RISCV_CC_TLSDESC];
+ if (!tlsdesc_abi.initialized_p ())
+ {
+ HARD_REG_SET full_reg_clobbers;
+ CLEAR_HARD_REG_SET (full_reg_clobbers);
+ SET_HARD_REG_BIT (full_reg_clobbers, A0_REGNUM);
+ SET_HARD_REG_BIT (full_reg_clobbers, T0_REGNUM);
+ if (TARGET_VECTOR)
+ {
+ for (int regno = V_REG_FIRST; regno <= V_REG_LAST; ++regno)
+ SET_HARD_REG_BIT (full_reg_clobbers, regno);
+ SET_HARD_REG_BIT (full_reg_clobbers, VL_REGNUM);
+ SET_HARD_REG_BIT (full_reg_clobbers, VTYPE_REGNUM);
+ SET_HARD_REG_BIT (full_reg_clobbers, VXRM_REGNUM);
+ SET_HARD_REG_BIT (full_reg_clobbers, VXSAT_REGNUM);
+ }
+ tlsdesc_abi.initialize (RISCV_CC_TLSDESC, full_reg_clobbers);
+ }
+ return RISCV_CC_TLSDESC;
+}
+
static bool
riscv_vector_int_type_p (const_tree type)
{
diff --git a/gcc/config/riscv/riscv.h b/gcc/config/riscv/riscv.h
index d72c06ec37f..5068a5b9c98 100644
--- a/gcc/config/riscv/riscv.h
+++ b/gcc/config/riscv/riscv.h
@@ -795,6 +795,7 @@ enum riscv_cc
RISCV_CC_VLS_V_4096,
RISCV_CC_VLS_V_8192,
RISCV_CC_VLS_V_16384,
+ RISCV_CC_TLSDESC, /* For targets of TLSDESC calls. */
RISCV_CC_UNKNOWN
};
diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md
index b90bfe0745a..022e0c03e28 100644
--- a/gcc/config/riscv/riscv.md
+++ b/gcc/config/riscv/riscv.md
@@ -181,6 +181,7 @@
(VTYPE_REGNUM 67)
(VXRM_REGNUM 68)
(FRM_REGNUM 69)
+ (VXSAT_REGNUM 70)
])
(include "predicates.md")
@@ -2473,11 +2474,26 @@
(set_attr "type" "load")
(set_attr "mode" "<MODE>")])
-(define_insn "@tlsdesc<mode>"
+(define_expand "@tlsdesc<mode>"
+ [(unspec:P [(match_operand:P 0 "symbolic_operand" "")]
+ UNSPEC_TLSDESC)]
+ "TARGET_TLSDESC"
+{
+ rtx_insn *call = emit_call_insn (gen_tlsdesc_call<mode> (operands[0]));
+ CALL_INSN_ABI_ID (call) = riscv_tlsdesc_abi_id ();
+ RTL_CONST_CALL_P (call) = 1;
+ DONE;
+})
+
+;; Model TLSDESC as a call with a custom ABI that clobbers a0 and t0.
+;; When the V extension is enabled, the ABI additionally clobbers all
+;; vector registers and vector CSRs (vl, vtype, vxrm, vxsat) per the psABI.
+(define_insn "tlsdesc_call<mode>"
[(set (reg:P A0_REGNUM)
- (unspec:P
- [(match_operand:P 0 "symbolic_operand" "")]
- UNSPEC_TLSDESC))
+ (call (mem:SI (unspec:P
+ [(match_operand:P 0 "symbolic_operand")]
+ UNSPEC_TLSDESC))
+ (const_int 0)))
(clobber (reg:P T0_REGNUM))]
"TARGET_TLSDESC"
{
@@ -2486,7 +2502,7 @@
"addi\ta0,a0,%%tlsdesc_add_lo(.LT%=)\;"
"jalr\tt0,t0,%%tlsdesc_call(.LT%=)";
}
- [(set_attr "type" "multi")
+ [(set_attr "type" "call")
(set_attr "length" "16")
(set_attr "mode" "<MODE>")])
diff --git a/gcc/function-abi.h b/gcc/function-abi.h
index c3b4956ae66..c22ce637ff7 100644
--- a/gcc/function-abi.h
+++ b/gcc/function-abi.h
@@ -28,7 +28,7 @@ along with GCC; see the file COPYING3. If not see
NUM_ABI_IDS is the maximum number of such ABIs that GCC can handle at once.
A bitfield with this number of bits can represent any combinaion of the
supported ABIs. */
-const size_t NUM_ABI_IDS = 12;
+const size_t NUM_ABI_IDS = 13;
/* Information about one of the target's predefined ABIs. */
class predefined_function_abi
diff --git a/gcc/testsuite/gcc.target/riscv/tlsdesc_clobber.c
b/gcc/testsuite/gcc.target/riscv/tlsdesc_clobber.c
new file mode 100644
index 00000000000..331841ca1b8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/tlsdesc_clobber.c
@@ -0,0 +1,32 @@
+/* Verify that the TLSDESC resolver only clobbers a0 and t0 while assuming
+ all other registers as preserved per its custom ABI (no vector case),
+ which is different from a normal call. */
+
+/* { dg-do compile } */
+/* { dg-require-effective-target tls_native } */
+/* { dg-options "-O2 -fpic -mtls-dialect=desc -march=rv64gc -mabi=lp64d" } */
+/* { dg-require-effective-target fpic } */
+
+extern __thread int tls_var;
+
+long
+test_clobber (long a, long b, long c, long d)
+{
+ /* a=a0, b=a1, c=a2, d=a3.
+ TLSDESC clobbers a0 and t0 only, so the compiler must save/restore
+ a0 (by stack or mv or whatever), but should not do the same thing
+ for a1-a3 like a normal call. */
+ tls_var = 1;
+ return a + b + c + d;
+}
+
+/* The TLSDESC call should be present. */
+/* { dg-final { scan-assembler-times {jalr\tt0,} 1 } } */
+
+/* a1-a3 should NOT be moved to s-regs or saved to stack. */
+/* { dg-final { scan-assembler-not {mv\ts[0-9]+,a[0-9]+} } } */
+/* { dg-final { scan-assembler-not {sd\ta[0-9]+,.*\(sp\)} } } */
+
+/* The downside of modeling TLSDESC call as a real CALL_INSN:
+ ra needs to be saved/restored here. */
+/* { dg-final { scan-assembler {sd\tra,.*\(sp\)} } } */
diff --git a/gcc/testsuite/gcc.target/riscv/tlsdesc_clobber_v.c
b/gcc/testsuite/gcc.target/riscv/tlsdesc_clobber_v.c
new file mode 100644
index 00000000000..574b03f743b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/tlsdesc_clobber_v.c
@@ -0,0 +1,36 @@
+/* Verify that when the V extension is enabled, the TLSDESC resolver clobbers
+ all vector registers per the psABI. The compiler must save/restore any
+ live vector registers across the TLSDESC call. */
+
+/* { dg-do compile } */
+/* { dg-require-effective-target tls_native } */
+/* { dg-options "-O2 -fpic -mtls-dialect=desc -march=rv64gcv -mabi=lp64d" } */
+/* { dg-require-effective-target fpic } */
+
+extern __thread int tls_var;
+
+void
+test_vector_reg_clobber (void)
+{
+ __rvv_int32m1_t v1, v2;
+
+ /* Write two v-regs. */
+ asm volatile ("# def v1" : "=vr"(v1));
+ asm volatile ("# def v2" : "=vr"(v2));
+
+ /* TLSDESC call — clobbers all vector regs. */
+ asm volatile ("" ::: "memory"); /* Prevent scheduling... */
+ tls_var = 1;
+ asm volatile ("" ::: "memory"); /* Prevent scheduling... */
+
+ /* Read the two v-regs; their live-range spans across the TLSDESC call,
+ so the compiler must emit vector store/load pairs to preserve them. */
+ asm volatile ("# use v1" : : "vr"(v1));
+ asm volatile ("# use v2" : : "vr"(v2));
+}
+
+/* { dg-final { scan-assembler-times {jalr\tt0,} 1 } } */
+
+/* Vector stores before and loads after the TLSDESC call. */
+/* { dg-final { scan-assembler-times {\tvs[0-9]+r\.v\t} 2 } } */
+/* { dg-final { scan-assembler-times {\tvl[0-9]+re[0-9]+\.v\t} 2 } } */
\ No newline at end of file
diff --git a/gcc/testsuite/gcc.target/riscv/tlsdesc_clobber_v_csr.c
b/gcc/testsuite/gcc.target/riscv/tlsdesc_clobber_v_csr.c
new file mode 100644
index 00000000000..d730036c812
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/tlsdesc_clobber_v_csr.c
@@ -0,0 +1,39 @@
+/* Verify that when the V extension is enabled, the TLSDESC resolver clobbers
+ vector CSRs (vl, vtype, vxrm, vxsat) per the psABI. The compiler must
+ re-emit a vsetvli after the TLSDESC call to recover the vector status. */
+
+/* { dg-do compile } */
+/* { dg-require-effective-target tls_native } */
+/* { dg-options "-O2 -fpic -mtls-dialect=desc -march=rv64gcv -mabi=lp64d" } */
+/* { dg-require-effective-target fpic } */
+
+typedef int v4si __attribute__ ((vector_size (16)));
+
+extern __thread int tls_var;
+
+void
+test_vector_csr_clobber (v4si *in, v4si *out)
+{
+ v4si vec;
+
+ /* A pair of local load/store whose live-range does not span accross
+ the TLSDESC. It has a special size format so it needs a vsetvli */
+ vec = *in;
+ *out = vec;
+
+ /* TLSDESC call — clobbers all vector CSRs including vl/vtype. */
+ asm volatile ("" ::: "memory"); /* Prevent scheduling... */
+ tls_var = 1;
+ asm volatile ("" ::: "memory"); /* Prevent scheduling... */
+
+ /* Another pair of local load/store. But since TLSDESC clobbers vl/vtype, we
+ must re-emit a vsetvli here. */
+ vec = *in;
+ *out = vec;
+}
+
+/* { dg-final { scan-assembler-times {jalr\tt0,} 1 } } */
+
+/* Two vsetvli: one before each vector segment. The second is needed because
+ TLSDESC clobbers vl/vtype. */
+/* { dg-final { scan-assembler-times {vsetvli|vsetivli} 2 } } */
--
2.47.1