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