Changes from v2:
* Update tprel_ld_QI_signed to use gen_lowpart to correctly
obtain the low QImode part of the destination
Changes from v1:
* update tprel_ld_QI_signed to use define_insn_and_split
* renamed PR27479.c to pr27479.c and removed dead code
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.
Bootstrapped and tested on powerpc64-linux-gnu with no regressions.
2026-04-15 Vijay Shankar <[email protected]>
gcc/ChangeLog:
PR target/27479
* config/rs6000/rs6000.md (*tprel_ld_<az>_<HSI:mode>): New pattern.
(*tprel_ld_QI_signed): likewise.
(*tprel_ld_QI): likewise.
(*tprel_ld_DI): likewise.
(*tprel_ld_<SFDF:mode>): likewise.
gcc/testsuite/ChangeLog:
PR target/27479
* gcc.target/powerpc/pr27479.c: New Test.
---
gcc/config/rs6000/rs6000.md | 73 +++++++++++++++++
gcc/testsuite/gcc.target/powerpc/pr27479.c | 95 ++++++++++++++++++++++
2 files changed, 168 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 ddfed3b63bc..8f694ac4d1c 100644
--- a/gcc/config/rs6000/rs6000.md
+++ b/gcc/config/rs6000/rs6000.md
@@ -10650,6 +10650,79 @@
"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)"
+ [(set_attr "type" "load")])
+
+;; 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_and_split "*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"
+ "#"
+ "&& reload_completed"
+ [(set (match_dup 0)
+ (zero_extend:DI
+ (mem:QI
+ (unspec:P
+ [(match_dup 1)
+ (match_dup 2)]
+ UNSPEC_TLSTPRELLO))))
+ (set (match_dup 0)
+ (sign_extend:DI (match_dup 3)))]
+ {
+ operands[3] = gen_lowpart (QImode, operands[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)"
+ [(set_attr "type" "load")])
+
+(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)"
+ [(set_attr "type" "load")])
+
+(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)"
+ [(set_attr "type" "fpload")])
+
(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 00000000000..de05237ba5a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/pr27479.c
@@ -0,0 +1,95 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mdejagnu-cpu=power9" } */
+
+/* verify TLS access for PR27479 */
+
+/* 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.52.0