Hi Kito,
Thanks for the suggestion! patch v4 is sailed (with the commit message updated
accordingly)
Best,
Luke Zhuang
------------------------------------------------------------------
From:Kito Cheng <[email protected]>
Send Time:Thu, Jun 25, 2026, 11:35
To:Luke Zhuang<[email protected]>
CC:"gcc-patches"<[email protected]>; palmer<[email protected]>;
"rdapp.gcc"<[email protected]>; andrew<[email protected]>;
"jim.wilson.gcc"<[email protected]>;
"juzhe.zhong"<[email protected]>; rdsandiford<[email protected]>;
"jeffrey.law"<[email protected]>;
"zhuangzhi.zz"<[email protected]>
Subject:Re: [PATCH v3] RISC-V: Declare TLSDESC clobbers for vector registers
and CSRs per psABI
> -(define_insn "@tlsdesc<mode>"
> +(define_expand "@tlsdesc<mode>"
> + [(unspec:P [(match_operand:P 0 "symbolic_operand" "")]
> + UNSPEC_TLSDESC)]
> + "TARGET_TLSDESC"
> +{
> + if (TARGET_VECTOR)
> + emit_insn (gen_tlsdesc_vec<mode> (operands[0]));
Just always use tlsdesc_vec's one should be fine, extra clobber for
non-available register is harmless :)
> + else
> + emit_insn (gen_tlsdesc_base<mode> (operands[0]));
> + DONE;
> +})
> +
> +;; Base TLSDESC: clobbers a0 and t0 only (no V extension).
> +(define_insn "tlsdesc_base<mode>"
> [(set (reg:P A0_REGNUM)
> - (unspec:P
> - [(match_operand:P 0 "symbolic_operand" "")]
> - UNSPEC_TLSDESC))
> + (unspec:P [(match_operand:P 0 "symbolic_operand" "")]
> + UNSPEC_TLSDESC))
> (clobber (reg:P T0_REGNUM))]
> - "TARGET_TLSDESC"
> + "TARGET_TLSDESC && !TARGET_VECTOR"
> + {
> + return ".LT%=: auipc\ta0,%%tlsdesc_hi(%0)\;"
> + "<load>\tt0,%%tlsdesc_load_lo(.LT%=)(a0)\;"
> + "addi\ta0,a0,%%tlsdesc_add_lo(.LT%=)\;"
> + "jalr\tt0,t0,%%tlsdesc_call(.LT%=)";
> + }
> + [(set_attr "type" "multi")
> + (set_attr "length" "16")
> + (set_attr "mode" "<MODE>")])
> +
> +;; TLSDESC with V extension: additionally clobbers all 32 vector registers
> +;; (using LMUL=8 groups: v0, v8, v16, v24 each cover 8 physical registers)
> +;; and vector CSRs (vl, vtype, vxrm, vxsat) per the psABI.
> +(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:RVVM8QI 96))
> + (clobber (reg:RVVM8QI 104))
> + (clobber (reg:RVVM8QI 112))
> + (clobber (reg:RVVM8QI 120))
> + (clobber (reg:SI VL_REGNUM))
> + (clobber (reg:SI VTYPE_REGNUM))
> + (clobber (reg:SI VXRM_REGNUM))
> + (clobber (reg:SI VXSAT_REGNUM))]
> + "TARGET_TLSDESC && TARGET_VECTOR"
> {
> return ".LT%=: auipc\ta0,%%tlsdesc_hi(%0)\;"
> "<load>\tt0,%%tlsdesc_load_lo(.LT%=)(a0)\;"
> 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..6a004d6820f
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/tlsdesc_clobber.c
> @@ -0,0 +1,29 @@
> +/* 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 } } */
> +
> +/* ra/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\)} } } */
> +/* { dg-final { scan-assembler-not {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..a5fb350f76e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/tlsdesc_clobber_v.c
> @@ -0,0 +1,56 @@
> +/* 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, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12;
> +
> + /* Write two v-regs. */
> + asm volatile ("# def v1" : "=vr"(v1));
> + asm volatile ("# def v2" : "=vr"(v2));
> + asm volatile ("# def v3" : "=vr"(v3));
> + asm volatile ("# def v4" : "=vr"(v4));
> + asm volatile ("# def v5" : "=vr"(v5));
> + asm volatile ("# def v6" : "=vr"(v6));
> + asm volatile ("# def v7" : "=vr"(v7));
> + asm volatile ("# def v8" : "=vr"(v8));
> + asm volatile ("# def v9" : "=vr"(v9));
> + asm volatile ("# def v10" : "=vr"(v10));
> + asm volatile ("# def v11" : "=vr"(v11));
> + asm volatile ("# def v12" : "=vr"(v12));
> +
> + /* 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));
> + asm volatile ("# use v3" : : "vr"(v3));
> + asm volatile ("# use v4" : : "vr"(v4));
> + asm volatile ("# use v5" : : "vr"(v5));
> + asm volatile ("# use v6" : : "vr"(v6));
> + asm volatile ("# use v7" : : "vr"(v7));
> + asm volatile ("# use v8" : : "vr"(v8));
> + asm volatile ("# use v9" : : "vr"(v9));
> + asm volatile ("# use v10" : : "vr"(v10));
> + asm volatile ("# use v11" : : "vr"(v11));
> + asm volatile ("# use v12" : : "vr"(v12));
> +}
> +
> +/* { 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} 12 } } */
> +/* { dg-final { scan-assembler-times {\tvl[0-9]+re[0-9]+\.v\t} 12 } } */
> 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..ffb9c9a61a4
> --- /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
>