From 220ffae474db36ba20487427be699f0987a00b0c Mon Sep 17 00:00:00 2001
From: Andreas Krebbel <krebbel@linux.ibm.com>
Date: Wed, 12 Aug 2020 08:02:35 +0200
Subject: [PATCH] IBM Z: Fix PR96308

For the testcase a symbol with a TLS reloc and an unary minus is being
generated.  The backend didn't handle this correctly.

In s390_cannot_force_const_mem an unary minus on a symbolic constant
is rejected now since gas would not allow this.

legitimize_tls_address now makes the NEG rtx the outermost operation
by pulling it out of the CONST rtx.

gcc/ChangeLog:

	PR target/96308
	* config/s390/s390.c (s390_cannot_force_const_mem): Reject an
	unary minus for everything not being a numeric constant.
	(legitimize_tls_address): Move a NEG out of the CONST rtx.

gcc/testsuite/ChangeLog:

	PR target/96308
	* g++.dg/pr96308.C: New test.
---
 gcc/config/s390/s390.c         | 25 +++++++++++++++++++++++++
 gcc/testsuite/g++.dg/pr96308.C |  7 +++++++
 2 files changed, 32 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/pr96308.C

diff --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c
index 22ac5e431214..5488a5dc5e8a 100644
--- a/gcc/config/s390/s390.c
+++ b/gcc/config/s390/s390.c
@@ -4106,6 +4106,18 @@ s390_cannot_force_const_mem (machine_mode mode, rtx x)
       /* Accept all non-symbolic constants.  */
       return false;
 
+    case NEG:
+      /* Accept an unary '-' only on scalar numeric constants.  */
+      switch (GET_CODE (XEXP (x, 0)))
+	{
+	case CONST_INT:
+	case CONST_DOUBLE:
+	case CONST_WIDE_INT:
+	  return false;
+	default:
+	  return true;
+	}
+
     case LABEL_REF:
       /* Labels are OK iff we are non-PIC.  */
       return flag_pic != 0;
@@ -5268,6 +5280,7 @@ legitimize_tls_address (rtx addr, rtx reg)
     {
       switch (XINT (XEXP (addr, 0), 1))
 	{
+	case UNSPEC_NTPOFF:
 	case UNSPEC_INDNTPOFF:
 	  new_rtx = addr;
 	  break;
@@ -5290,6 +5303,18 @@ legitimize_tls_address (rtx addr, rtx reg)
       new_rtx = force_operand (new_rtx, 0);
     }
 
+  /* (const (neg (unspec (symbol_ref)))) -> (neg (const (unspec (symbol_ref)))) */
+  else if (GET_CODE (addr) == CONST && GET_CODE (XEXP (addr, 0)) == NEG)
+    {
+      new_rtx = XEXP (XEXP (addr, 0), 0);
+      if (GET_CODE (new_rtx) != SYMBOL_REF)
+	new_rtx = gen_rtx_CONST (Pmode, new_rtx);
+
+      new_rtx = legitimize_tls_address (new_rtx, reg);
+      new_rtx = gen_rtx_NEG (Pmode, new_rtx);
+      new_rtx = force_operand (new_rtx, 0);
+    }
+
   else
     gcc_unreachable ();  /* for now ... */
 
diff --git a/gcc/testsuite/g++.dg/pr96308.C b/gcc/testsuite/g++.dg/pr96308.C
new file mode 100644
index 000000000000..9009bba5e829
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr96308.C
@@ -0,0 +1,7 @@
+// { dg-do compile }
+// { dg-options "-Os -fno-move-loop-invariants -std=c++11" }
+
+struct NonTrivial3 {
+  ~NonTrivial3();
+};
+void i() { thread_local NonTrivial3 tlarr[10]; }
-- 
GitLab