Reduce TLS variable access from 3 to 2 instructions by adding
combine patterns that merge address calculation with load.

Before:
    addis 9,13,x@tprel@ha
    addi 9,9,x@tprel@l
    lwz 3,0(9)

After:
    addis 9,13,x@tprel@ha
    lwz 3,x@tprel@l(9)

Bootstrapped and tested on powerpc64le-linux-gnu with no regressions.

2026-03-30  Vijay Shankar  <[email protected]>

gcc/ChangeLog:
        PR target/27479
        * config/rs6000/rs6000.md (*tprel_ld_<az>_<HSI:mode>): New pattern.
        (*tprel_ld_QI_signed): New pattern.
        (*tprel_ld_QI): New pattern.
        (*tprel_ld_DI): New pattern.
        (*tprel_ld_<SFDF:mode>): New pattern.

gcc/testsuite/ChangeLog:
        PR target/27479
        * gcc.target/powerpc/PR27479.c: New Test.
---
 gcc/config/rs6000/rs6000.md                |  56 ++++++++++++
 gcc/testsuite/gcc.target/powerpc/PR27479.c | 101 +++++++++++++++++++++
 2 files changed, 157 insertions(+)
 create mode 100644 gcc/testsuite/gcc.target/powerpc/PR27479.c

diff --git a/gcc/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md
index 308955155..f192010ee 100644
--- a/gcc/config/rs6000/rs6000.md
+++ b/gcc/config/rs6000/rs6000.md
@@ -10567,6 +10567,62 @@
   "la %0,%1@got@tlsgd@pcrel"
   [(set_attr "prefixed" "yes")])
 
+;;combine patterns for tls access
+(define_insn "*tprel_ld_<az>_<HSI:mode>"
+  [(set (match_operand:DI 0 "gpc_reg_operand" "=r")
+       (any_extend:DI
+         (mem:HSI
+           (unspec:P
+             [(match_operand:P 1 "gpc_reg_operand" "b")
+              (match_operand:P 2 "rs6000_tls_symbol_ref" "")]
+             UNSPEC_TLSTPRELLO))))]
+  "HAVE_AS_TLS"
+  "l<HSI:wd><az> %0,%2@tprel@l(%1)")
+
+;; Two instructions needed: lbz (zero-extend load byte) + extsb (sign-extend)
+;; because there is no sign-extended load byte instruction, only zero-extended.
+(define_insn "*tprel_ld_QI_signed"
+  [(set (match_operand:DI 0 "gpc_reg_operand" "=r")
+       (sign_extend:DI
+         (mem:QI
+           (unspec:P
+             [(match_operand:P 1 "gpc_reg_operand" "b")
+              (match_operand:P 2 "rs6000_tls_symbol_ref" "")]
+             UNSPEC_TLSTPRELLO))))]
+  "HAVE_AS_TLS"
+  "lbz %0,%2@tprel@l(%1)\n\textsb %0,%0")
+
+(define_insn "*tprel_ld_QI"
+  [(set (match_operand:DI 0 "gpc_reg_operand" "=r")
+       (zero_extend:DI
+         (mem:QI
+           (unspec:P
+             [(match_operand:P 1 "gpc_reg_operand" "b")
+              (match_operand:P 2 "rs6000_tls_symbol_ref" "")]
+             UNSPEC_TLSTPRELLO))))]
+  "HAVE_AS_TLS"
+  "lbz %0,%2@tprel@l(%1)")
+
+(define_insn "*tprel_ld_DI"
+  [(set (match_operand:DI 0 "gpc_reg_operand" "=r")
+       (mem:DI
+         (unspec:P
+           [(match_operand:P 1 "gpc_reg_operand" "b")
+            (match_operand:P 2 "rs6000_tls_symbol_ref" "")]
+           UNSPEC_TLSTPRELLO)))]
+  "HAVE_AS_TLS"
+  "ld %0,%2@tprel@l(%1)")
+
+(define_insn "*tprel_ld_<SFDF:mode>"
+  [(set (match_operand:SFDF 0 "gpc_reg_operand" "=f")
+       (mem:SFDF
+         (unspec:P
+           [(match_operand:P 1 "gpc_reg_operand" "b")
+            (match_operand:P 2 "rs6000_tls_symbol_ref" "")]
+           UNSPEC_TLSTPRELLO)))]
+  "HAVE_AS_TLS"
+  "lf<SFDF:sd> %0,%2@tprel@l(%1)")
+
 (define_insn_and_split "*tls_gd<bits>"
   [(set (match_operand:P 0 "gpc_reg_operand" "=b")
        (unspec:P [(match_operand:P 1 "rs6000_tls_symbol_ref" "")
diff --git a/gcc/testsuite/gcc.target/powerpc/PR27479.c 
b/gcc/testsuite/gcc.target/powerpc/PR27479.c
new file mode 100644
index 000000000..236f72b4a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/PR27479.c
@@ -0,0 +1,101 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mdejagnu-cpu=power9" } */
+
+/* verify TLS access for 27479*/
+struct xyz
+{
+  unsigned int x, y, z;
+};
+
+__thread struct xyz x;
+
+/* TLS variables of many types.  */
+__thread unsigned char t_uc;
+__thread signed char t_sc;
+__thread unsigned short t_us;
+__thread short t_ss;
+__thread unsigned int t_ui;
+__thread int t_si;
+__thread unsigned long t_ul;
+__thread long t_sl;
+__thread _Bool t_bool;
+__thread int *t_ptr;
+
+/* Floating-point.  */
+__thread float t_f;
+__thread double t_d;
+
+unsigned long
+foo_uc (void)
+{
+  return t_uc;
+}
+
+long
+foo_sc (void)
+{
+  return t_sc;
+}
+
+unsigned long
+foo_us (void)
+{
+  return t_us;
+}
+
+long
+foo_ss (void)
+{
+  return t_ss;
+}
+
+unsigned long
+foo_ui (void)
+{
+  return t_ui;
+}
+
+long
+foo_si (void)
+{
+  return t_si;
+}
+
+unsigned long
+foo_ul (void)
+{
+  return t_ul;
+}
+
+long
+foo_sl (void)
+{
+  return t_sl;
+}
+
+unsigned long
+foo_bool (void)
+{
+  return t_bool;
+}
+
+int *
+foo_ptr (void)
+{
+  return t_ptr;
+}
+
+/* Floating-point accessors.  */
+float
+foo_f (void)
+{
+  return t_f;
+}
+
+double
+foo_d (void)
+{
+  return t_d;
+}
+
+/* { dg-final { scan-assembler-not {\maddi\M} } } */
-- 
2.47.3

Reply via email to