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),
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

Reply via email to