diff mbox series

[v2] IBM Z: Use @PLT symbols for local functions in 64-bit mode

Message ID 20210707190332.58009-1-iii@linux.ibm.com
State New
Headers show
Series [v2] IBM Z: Use @PLT symbols for local functions in 64-bit mode | expand

Commit Message

Ilya Leoshkevich July 7, 2021, 7:03 p.m. UTC
Bootstrapped and regtested on s390x-redhat-linux.  Ok for master?

v1: https://gcc.gnu.org/pipermail/gcc-patches/2021-June/573614.html
v1 -> v2: Do not use UNSPEC_PLT in 64-bit code and rename it to
          UNSPEC_PLT31 (Ulrich, Andreas).  Do not append @PLT only to
          weak symbols in non-PIC code (Ulrich).  Add TLS tests.



This helps with generating code for kernel hotpatches, which contain
individual functions and are loaded more than 2G away from vmlinux.
This should not create performance regressions for the normal use
cases, because for local functions ld replaces @PLT calls with direct
calls.

gcc/ChangeLog:

	* config/s390/predicates.md (bras_sym_operand): Accept all
	functions in 64-bit mode, use UNSPEC_PLT31.
	(larl_operand): Use UNSPEC_PLT31.
	* config/s390/s390.c (s390_loadrelative_operand_p): Likewise.
	(legitimize_pic_address): Likewise.
	(s390_emit_tls_call_insn): Mark __tls_get_offset as function,
	use UNSPEC_PLT31.
	(s390_delegitimize_address): Use UNSPEC_PLT31.
	(s390_output_addr_const_extra): Likewise.
	(print_operand): Add @PLT to TLS calls, handle %K.
	(s390_function_profiler): Mark __fentry__/_mcount as function,
	use UNSPEC_PLT31.
	(s390_output_mi_thunk): Use only UNSPEC_GOT.
	(s390_emit_call): Use UNSPEC_PLT31.
	(s390_emit_tpf_eh_return): Mark __tpf_eh_return as function.
	* config/s390/s390.md (UNSPEC_PLT31): Rename from UNSPEC_PLT.
	(*movdi_64): Use %K.
	(reload_base_64): Likewise.
	(*sibcall_brc): Likewise.
	(*sibcall_brcl): Likewise.
	(*sibcall_value_brc): Likewise.
	(*sibcall_value_brcl): Likewise.
	(*bras): Likewise.
	(*brasl): Likewise.
	(*bras_r): Likewise.
	(*brasl_r): Likewise.
	(*bras_tls): Likewise.
	(*brasl_tls): Likewise.
	(main_base_64): Likewise.
	(reload_base_64): Likewise.
	(@split_stack_call<mode>): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/visibility/noPLT.C: Skip on s390x.
	* gcc.target/s390/nodatarel-1.c: Move foostatic to the new
	tests.
	* gcc.target/s390/pr80080-4.c: Allow @PLT suffix.
	* gcc.target/s390/risbg-ll-3.c: Likewise.
	* gcc.target/s390/call.h: Common code for the new tests.
	* gcc.target/s390/call31-z10-pic-nodatarel.c: New test.
	* gcc.target/s390/call31-z10-pic.c: New test.
	* gcc.target/s390/call31-z10.c: New test.
	* gcc.target/s390/call31-z9-pic-nodatarel.c: New test.
	* gcc.target/s390/call31-z9-pic.c: New test.
	* gcc.target/s390/call31-z9.c: New test.
	* gcc.target/s390/call64-z10-pic-nodatarel.c: New test.
	* gcc.target/s390/call64-z10-pic.c: New test.
	* gcc.target/s390/call64-z10.c: New test.
	* gcc.target/s390/call64-z9-pic-nodatarel.c: New test.
	* gcc.target/s390/call64-z9-pic.c: New test.
	* gcc.target/s390/call64-z9.c: New test.
	* gcc.target/s390/tls.h: Common code for the new TLS tests.
	* gcc.target/s390/tls31-pic.c: New test.
	* gcc.target/s390/tls31.c: New test.
	* gcc.target/s390/tls64-pic.c: New test.
	* gcc.target/s390/tls64.c: New test.
---
 gcc/config/s390/predicates.md                 |  9 ++-
 gcc/config/s390/s390.c                        | 73 ++++++++++++++-----
 gcc/config/s390/s390.md                       | 32 ++++----
 gcc/testsuite/g++.dg/ext/visibility/noPLT.C   |  2 +-
 gcc/testsuite/gcc.target/s390/call.h          | 40 ++++++++++
 .../s390/call31-z10-pic-nodatarel.c           | 16 ++++
 .../gcc.target/s390/call31-z10-pic.c          | 16 ++++
 gcc/testsuite/gcc.target/s390/call31-z10.c    | 15 ++++
 .../gcc.target/s390/call31-z9-pic-nodatarel.c | 16 ++++
 gcc/testsuite/gcc.target/s390/call31-z9-pic.c | 16 ++++
 gcc/testsuite/gcc.target/s390/call31-z9.c     | 15 ++++
 .../s390/call64-z10-pic-nodatarel.c           | 17 +++++
 .../gcc.target/s390/call64-z10-pic.c          | 17 +++++
 gcc/testsuite/gcc.target/s390/call64-z10.c    | 15 ++++
 .../gcc.target/s390/call64-z9-pic-nodatarel.c | 17 +++++
 gcc/testsuite/gcc.target/s390/call64-z9-pic.c | 17 +++++
 gcc/testsuite/gcc.target/s390/call64-z9.c     | 15 ++++
 gcc/testsuite/gcc.target/s390/nodatarel-1.c   | 26 +------
 gcc/testsuite/gcc.target/s390/pr80080-4.c     |  2 +-
 gcc/testsuite/gcc.target/s390/risbg-ll-3.c    |  6 +-
 gcc/testsuite/gcc.target/s390/tls.h           | 23 ++++++
 gcc/testsuite/gcc.target/s390/tls31-pic.c     | 14 ++++
 gcc/testsuite/gcc.target/s390/tls31.c         |  9 +++
 gcc/testsuite/gcc.target/s390/tls64-pic.c     | 14 ++++
 gcc/testsuite/gcc.target/s390/tls64.c         |  9 +++
 25 files changed, 382 insertions(+), 69 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/s390/call.h
 create mode 100644 gcc/testsuite/gcc.target/s390/call31-z10-pic-nodatarel.c
 create mode 100644 gcc/testsuite/gcc.target/s390/call31-z10-pic.c
 create mode 100644 gcc/testsuite/gcc.target/s390/call31-z10.c
 create mode 100644 gcc/testsuite/gcc.target/s390/call31-z9-pic-nodatarel.c
 create mode 100644 gcc/testsuite/gcc.target/s390/call31-z9-pic.c
 create mode 100644 gcc/testsuite/gcc.target/s390/call31-z9.c
 create mode 100644 gcc/testsuite/gcc.target/s390/call64-z10-pic-nodatarel.c
 create mode 100644 gcc/testsuite/gcc.target/s390/call64-z10-pic.c
 create mode 100644 gcc/testsuite/gcc.target/s390/call64-z10.c
 create mode 100644 gcc/testsuite/gcc.target/s390/call64-z9-pic-nodatarel.c
 create mode 100644 gcc/testsuite/gcc.target/s390/call64-z9-pic.c
 create mode 100644 gcc/testsuite/gcc.target/s390/call64-z9.c
 create mode 100644 gcc/testsuite/gcc.target/s390/tls.h
 create mode 100644 gcc/testsuite/gcc.target/s390/tls31-pic.c
 create mode 100644 gcc/testsuite/gcc.target/s390/tls31.c
 create mode 100644 gcc/testsuite/gcc.target/s390/tls64-pic.c
 create mode 100644 gcc/testsuite/gcc.target/s390/tls64.c

Comments

Ilya Leoshkevich July 7, 2021, 9:26 p.m. UTC | #1
On Wed, 2021-07-07 at 21:03 +0200, Ilya Leoshkevich wrote:
> Bootstrapped and regtested on s390x-redhat-linux.  Ok for master?
> 
> v1: https://gcc.gnu.org/pipermail/gcc-patches/2021-June/573614.html
> v1 -> v2: Do not use UNSPEC_PLT in 64-bit code and rename it to
>           UNSPEC_PLT31 (Ulrich, Andreas).  Do not append @PLT only to
>           weak symbols in non-PIC code (Ulrich).  Add TLS tests.
> 
> 
> 
> This helps with generating code for kernel hotpatches, which contain
> individual functions and are loaded more than 2G away from vmlinux.
> This should not create performance regressions for the normal use
> cases, because for local functions ld replaces @PLT calls with direct
> calls.

Please disregard this patch, I just realized I missed two
output_asm_insn () calls in s390.c: one in function_profiler () and
one in s390_output_mi_thunk ().  I'll send a v3.
diff mbox series

Patch

diff --git a/gcc/config/s390/predicates.md b/gcc/config/s390/predicates.md
index 15093cb4b30..99c343aa32c 100644
--- a/gcc/config/s390/predicates.md
+++ b/gcc/config/s390/predicates.md
@@ -101,10 +101,13 @@ 
 
 (define_special_predicate "bras_sym_operand"
   (ior (and (match_code "symbol_ref")
-	    (match_test "!flag_pic || SYMBOL_REF_LOCAL_P (op)"))
+	    (ior (match_test "!flag_pic")
+		 (match_test "SYMBOL_REF_LOCAL_P (op)")
+		 (and (match_test "TARGET_64BIT")
+		      (match_test "SYMBOL_REF_FUNCTION_P (op)"))))
        (and (match_code "const")
 	    (and (match_test "GET_CODE (XEXP (op, 0)) == UNSPEC")
-		 (match_test "XINT (XEXP (op, 0), 1) == UNSPEC_PLT")))))
+		 (match_test "XINT (XEXP (op, 0), 1) == UNSPEC_PLT31")))))
 
 ;; Return true if OP is a PLUS that is not a legitimate
 ;; operand for the LA instruction.
@@ -197,7 +200,7 @@ 
       && XINT (op, 1) == UNSPEC_GOTENT)
     return true;
   if (GET_CODE (op) == UNSPEC
-      && XINT (op, 1) == UNSPEC_PLT)
+      && XINT (op, 1) == UNSPEC_PLT31)
     return true;
   if (GET_CODE (op) == UNSPEC
       && XINT (op, 1) == UNSPEC_INDNTPOFF)
diff --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c
index 590dd8f35bc..2ab7fb7f205 100644
--- a/gcc/config/s390/s390.c
+++ b/gcc/config/s390/s390.c
@@ -3291,7 +3291,7 @@  s390_loadrelative_operand_p (rtx addr, rtx *symref, HOST_WIDE_INT *addend)
   if (GET_CODE (addr) == SYMBOL_REF
       || (GET_CODE (addr) == UNSPEC
 	  && (XINT (addr, 1) == UNSPEC_GOTENT
-	      || XINT (addr, 1) == UNSPEC_PLT)))
+	      || XINT (addr, 1) == UNSPEC_PLT31)))
     {
       if (symref)
 	*symref = addr;
@@ -4964,7 +4964,7 @@  legitimize_pic_address (rtx orig, rtx reg)
        || (SYMBOL_REF_P (addr) && s390_rel_address_ok_p (addr))
        || (GET_CODE (addr) == UNSPEC &&
 	   (XINT (addr, 1) == UNSPEC_GOTENT
-	    || XINT (addr, 1) == UNSPEC_PLT)))
+	    || XINT (addr, 1) == UNSPEC_PLT31)))
       && GET_CODE (addend) == CONST_INT)
     {
       /* This can be locally addressed.  */
@@ -5125,7 +5125,7 @@  legitimize_pic_address (rtx orig, rtx reg)
 
 	  /* For @PLT larl is used.  This is handled like local
 	     symbol refs.  */
-	case UNSPEC_PLT:
+	case UNSPEC_PLT31:
 	  gcc_unreachable ();
 	  break;
 
@@ -5191,7 +5191,10 @@  s390_emit_tls_call_insn (rtx result_reg, rtx tls_call)
     emit_insn (s390_load_got ());
 
   if (!s390_tls_symbol)
-    s390_tls_symbol = gen_rtx_SYMBOL_REF (Pmode, "__tls_get_offset");
+    {
+      s390_tls_symbol = gen_rtx_SYMBOL_REF (Pmode, "__tls_get_offset");
+      SYMBOL_REF_FLAGS (s390_tls_symbol) |= SYMBOL_FLAG_FUNCTION;
+    }
 
   insn = s390_emit_call (s390_tls_symbol, tls_call, result_reg,
 			 gen_rtx_REG (Pmode, RETURN_REGNUM));
@@ -7596,7 +7599,7 @@  s390_delegitimize_address (rtx orig_x)
       y = XEXP (x, 0);
       if (GET_CODE (y) == UNSPEC
 	  && (XINT (y, 1) == UNSPEC_GOTENT
-	      || XINT (y, 1) == UNSPEC_PLT))
+	      || XINT (y, 1) == UNSPEC_PLT31))
 	y = XVECEXP (y, 0, 0);
       else
 	return orig_x;
@@ -7849,7 +7852,7 @@  s390_output_addr_const_extra (FILE *file, rtx x)
 	output_addr_const (file, XVECEXP (x, 0, 0));
 	fprintf (file, "@GOTOFF");
 	return true;
-      case UNSPEC_PLT:
+      case UNSPEC_PLT31:
 	output_addr_const (file, XVECEXP (x, 0, 0));
 	fprintf (file, "@PLT");
 	return true;
@@ -7943,6 +7946,7 @@  print_operand_address (FILE *file, rtx addr)
     'E': print opcode suffix for branch on index instruction.
     'G': print the size of the operand in bytes.
     'J': print tls_load/tls_gdcall/tls_ldcall suffix
+    'K': print @PLT suffix for call targets and load address values.
     'M': print the second word of a TImode operand.
     'N': print the second word of a DImode operand.
     'O': print only the displacement of a memory reference or address.
@@ -8129,6 +8133,29 @@  print_operand (FILE *file, rtx x, int code)
     case 'Y':
       print_shift_count_operand (file, x);
       return;
+
+    case 'K':
+      /* Append @PLT to both local and non-local symbols in order to support
+	 Linux Kernel livepatching: patches contain individual functions and
+	 are loaded further than 2G away from vmlinux, and therefore they must
+	 call even static functions via PLT.  ld will optimize @PLT away for
+	 normal code, and keep it for patches.
+
+	 Do not indiscriminately add @PLT in 31-bit mode due to the %r12
+	 restriction, use UNSPEC_PLT31 instead.
+
+	 @PLT only makes sense for functions, data is taken care of by
+	 -mno-pic-data-is-text-relative.
+
+	 Adding @PLT interferes with handling of weak symbols in non-PIC code,
+	 since their addresses are loaded with larl, which then always produces
+	 a non-NULL result, so skip them here as well.  */
+      if (TARGET_64BIT
+	  && GET_CODE (x) == SYMBOL_REF
+	  && SYMBOL_REF_FUNCTION_P (x)
+	  && !(SYMBOL_REF_WEAK (x) && !flag_pic))
+	fprintf (file, "@PLT");
+      return;
     }
 
   switch (GET_CODE (x))
@@ -13125,9 +13152,10 @@  s390_function_profiler (FILE *file, int labelno ATTRIBUTE_UNUSED)
   op[3] = GEN_INT (UNITS_PER_LONG);
 
   op[2] = gen_rtx_SYMBOL_REF (Pmode, flag_fentry ? "__fentry__" : "_mcount");
-  if (flag_pic)
+  SYMBOL_REF_FLAGS (op[2]) |= SYMBOL_FLAG_FUNCTION;
+  if (flag_pic && !TARGET_64BIT)
     {
-      op[2] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op[2]), UNSPEC_PLT);
+      op[2] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op[2]), UNSPEC_PLT31);
       op[2] = gen_rtx_CONST (Pmode, op[2]);
     }
 
@@ -13246,9 +13274,11 @@  s390_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
   if (flag_pic && !SYMBOL_REF_LOCAL_P (op[0]))
     {
       nonlocal = 1;
-      op[0] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op[0]),
-			      TARGET_64BIT ? UNSPEC_PLT : UNSPEC_GOT);
-      op[0] = gen_rtx_CONST (Pmode, op[0]);
+      if (!TARGET_64BIT)
+	{
+	  op[0] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op[0]), UNSPEC_GOT);
+	  op[0] = gen_rtx_CONST (Pmode, op[0]);
+	}
     }
 
   /* Operand 1 is the 'this' pointer.  */
@@ -13729,7 +13759,7 @@  rtx_insn *
 s390_emit_call (rtx addr_location, rtx tls_call, rtx result_reg,
 		rtx retaddr_reg)
 {
-  bool plt_call = false;
+  bool plt31_call_p = false;
   rtx_insn *insn;
   rtx vec[4] = { NULL_RTX };
   int elts = 0;
@@ -13744,15 +13774,15 @@  s390_emit_call (rtx addr_location, rtx tls_call, rtx result_reg,
     {
       /* When calling a global routine in PIC mode, we must
 	 replace the symbol itself with the PLT stub.  */
-      if (flag_pic && !SYMBOL_REF_LOCAL_P (addr_location))
+      if (flag_pic && !SYMBOL_REF_LOCAL_P (addr_location) && !TARGET_64BIT)
 	{
-	  if (TARGET_64BIT || retaddr_reg != NULL_RTX)
+	  if (retaddr_reg != NULL_RTX)
 	    {
 	      addr_location = gen_rtx_UNSPEC (Pmode,
 					      gen_rtvec (1, addr_location),
-					      UNSPEC_PLT);
+					      UNSPEC_PLT31);
 	      addr_location = gen_rtx_CONST (Pmode, addr_location);
-	      plt_call = true;
+	      plt31_call_p = true;
 	    }
 	  else
 	    /* For -fpic code the PLT entries might use r12 which is
@@ -13773,7 +13803,7 @@  s390_emit_call (rtx addr_location, rtx tls_call, rtx result_reg,
      register 1.  */
   if (retaddr_reg == NULL_RTX
       && GET_CODE (addr_location) != SYMBOL_REF
-      && !plt_call)
+      && !plt31_call_p)
     {
       emit_move_insn (gen_rtx_REG (Pmode, SIBCALL_REGNUM), addr_location);
       addr_location = gen_rtx_REG (Pmode, SIBCALL_REGNUM);
@@ -13781,7 +13811,7 @@  s390_emit_call (rtx addr_location, rtx tls_call, rtx result_reg,
 
   if (TARGET_INDIRECT_BRANCH_NOBP_CALL
       && GET_CODE (addr_location) != SYMBOL_REF
-      && !plt_call)
+      && !plt31_call_p)
     {
       /* Indirect branch thunks require the target to be a single GPR.  */
       addr_location = force_reg (Pmode, addr_location);
@@ -13833,7 +13863,7 @@  s390_emit_call (rtx addr_location, rtx tls_call, rtx result_reg,
   insn = emit_call_insn (*call);
 
   /* 31-bit PLT stubs and tls calls use the GOT register implicitly.  */
-  if ((!TARGET_64BIT && plt_call) || tls_call != NULL_RTX)
+  if (plt31_call_p || tls_call != NULL_RTX)
     {
       /* s390_function_ok_for_sibcall should
 	 have denied sibcalls in this case.  */
@@ -13889,7 +13919,10 @@  s390_emit_tpf_eh_return (rtx target)
   rtx reg, orig_ra;
 
   if (!s390_tpf_eh_return_symbol)
-    s390_tpf_eh_return_symbol = gen_rtx_SYMBOL_REF (Pmode, "__tpf_eh_return");
+    {
+      s390_tpf_eh_return_symbol = gen_rtx_SYMBOL_REF (Pmode, "__tpf_eh_return");
+      SYMBOL_REF_FLAGS (s390_tpf_eh_return_symbol) |= SYMBOL_FLAG_FUNCTION;
+    }
 
   reg = gen_rtx_REG (Pmode, 2);
   orig_ra = gen_rtx_REG (Pmode, 3);
diff --git a/gcc/config/s390/s390.md b/gcc/config/s390/s390.md
index 0c5b4dc9029..8ad21b0f4f7 100644
--- a/gcc/config/s390/s390.md
+++ b/gcc/config/s390/s390.md
@@ -79,7 +79,7 @@ 
    UNSPEC_GOTENT
    UNSPEC_GOT
    UNSPEC_GOTOFF
-   UNSPEC_PLT
+   UNSPEC_PLT31
    UNSPEC_PLTOFF
 
    ; Literal pool
@@ -1906,7 +1906,7 @@ 
    vlgvg\t%0,%v1,0
    vleg\t%v0,%1,0
    vsteg\t%v1,%0,0
-   larl\t%0,%1"
+   larl\t%0,%1%K1"
   [(set_attr "op_type" "RI,RI,RI,RI,RI,RIL,RIL,RIL,RRE,RRE,RRE,RXY,RIL,RRE,RXY,
                         RXY,RR,RX,RXY,RX,RXY,RIL,SIL,*,*,RS,RS,VRI,VRR,VRS,VRS,
                         VRX,VRX,RIL")
@@ -2180,7 +2180,7 @@ 
         (match_operand:SI 1 "larl_operand" "X"))]
   "!TARGET_64BIT
    && !FP_REG_P (operands[0])"
-  "larl\t%0,%1"
+  "larl\t%0,%1%K1"
    [(set_attr "op_type" "RIL")
     (set_attr "type"    "larl")
     (set_attr "z10prop" "z10_fwd_A1")
@@ -10373,7 +10373,7 @@ 
   [(call (mem:QI (match_operand 0 "bras_sym_operand" "X"))
          (match_operand 1 "const_int_operand" "n"))]
   "SIBLING_CALL_P (insn) && TARGET_SMALL_EXEC"
-  "j\t%0"
+  "j\t%0%K0"
   [(set_attr "op_type" "RI")
    (set_attr "type"    "branch")])
 
@@ -10381,7 +10381,7 @@ 
   [(call (mem:QI (match_operand 0 "bras_sym_operand" "X"))
          (match_operand 1 "const_int_operand" "n"))]
   "SIBLING_CALL_P (insn)"
-  "jg\t%0"
+  "jg\t%0%K0"
   [(set_attr "op_type" "RIL")
    (set_attr "type"    "branch")])
 
@@ -10434,7 +10434,7 @@ 
 	(call (mem:QI (match_operand 1 "bras_sym_operand" "X"))
 	      (match_operand 2 "const_int_operand" "n")))]
   "SIBLING_CALL_P (insn) && TARGET_SMALL_EXEC"
-  "j\t%1"
+  "j\t%1%K1"
   [(set_attr "op_type" "RI")
    (set_attr "type"    "branch")])
 
@@ -10443,7 +10443,7 @@ 
 	(call (mem:QI (match_operand 1 "bras_sym_operand" "X"))
 	      (match_operand 2 "const_int_operand" "n")))]
   "SIBLING_CALL_P (insn)"
-  "jg\t%1"
+  "jg\t%1%K1"
   [(set_attr "op_type" "RIL")
    (set_attr "type"    "branch")])
 
@@ -10470,7 +10470,7 @@ 
   "!SIBLING_CALL_P (insn)
    && TARGET_SMALL_EXEC
    && GET_MODE (operands[2]) == Pmode"
-  "bras\t%2,%0"
+  "bras\t%2,%0%K0"
   [(set_attr "op_type" "RI")
    (set_attr "type"    "jsr")
    (set_attr "z196prop" "z196_cracked")])
@@ -10482,7 +10482,7 @@ 
   "!SIBLING_CALL_P (insn)
 
    && GET_MODE (operands[2]) == Pmode"
-  "brasl\t%2,%0"
+  "brasl\t%2,%0%K0"
   [(set_attr "op_type" "RIL")
    (set_attr "type"    "jsr")
    (set_attr "z196prop" "z196_cracked")
@@ -10576,7 +10576,7 @@ 
   "!SIBLING_CALL_P (insn)
    && TARGET_SMALL_EXEC
    && GET_MODE (operands[3]) == Pmode"
-  "bras\t%3,%1"
+  "bras\t%3,%1%K1"
   [(set_attr "op_type" "RI")
    (set_attr "type"    "jsr")
    (set_attr "z196prop" "z196_cracked")])
@@ -10589,7 +10589,7 @@ 
   "!SIBLING_CALL_P (insn)
 
    && GET_MODE (operands[3]) == Pmode"
-  "brasl\t%3,%1"
+  "brasl\t%3,%1%K1"
   [(set_attr "op_type" "RIL")
    (set_attr "type"    "jsr")
    (set_attr "z196prop" "z196_cracked")
@@ -10720,7 +10720,7 @@ 
   "!SIBLING_CALL_P (insn)
    && TARGET_SMALL_EXEC
    && GET_MODE (operands[3]) == Pmode"
-  "bras\t%3,%1%J4"
+  "bras\t%3,%1%K1%J4"
   [(set_attr "op_type" "RI")
    (set_attr "type"    "jsr")
    (set_attr "z196prop" "z196_cracked")])
@@ -10734,7 +10734,7 @@ 
   "!SIBLING_CALL_P (insn)
 
    && GET_MODE (operands[3]) == Pmode"
-  "brasl\t%3,%1%J4"
+  "brasl\t%3,%1%K1%J4"
   [(set_attr "op_type" "RIL")
    (set_attr "type"    "jsr")
    (set_attr "z196prop" "z196_cracked")
@@ -11343,7 +11343,7 @@ 
   [(set (match_operand 0 "register_operand" "=a")
         (unspec [(label_ref (match_operand 1 "" ""))] UNSPEC_MAIN_BASE))]
   "GET_MODE (operands[0]) == Pmode"
-  "larl\t%0,%1"
+  "larl\t%0,%1%K1"
   [(set_attr "op_type" "RIL")
    (set_attr "type"    "larl")
    (set_attr "z10prop" "z10_fwd_A1")
@@ -11363,7 +11363,7 @@ 
   [(set (match_operand 0 "register_operand" "=a")
         (unspec [(label_ref (match_operand 1 "" ""))] UNSPEC_RELOAD_BASE))]
   "GET_MODE (operands[0]) == Pmode"
-  "larl\t%0,%1"
+  "larl\t%0,%1%K1"
   [(set_attr "op_type" "RIL")
    (set_attr "type"    "larl")
    (set_attr "z10prop" "z10_fwd_A1")])
@@ -12220,7 +12220,7 @@ 
   ""
 {
   s390_output_split_stack_data (operands[1], operands[2], operands[3], operands[4]);
-  return "jg\t%0";
+  return "jg\t%0%K0";
 }
   [(set_attr "op_type" "RIL")
    (set_attr "type"  "branch")])
diff --git a/gcc/testsuite/g++.dg/ext/visibility/noPLT.C b/gcc/testsuite/g++.dg/ext/visibility/noPLT.C
index 38af05fd6e3..b888303d56d 100644
--- a/gcc/testsuite/g++.dg/ext/visibility/noPLT.C
+++ b/gcc/testsuite/g++.dg/ext/visibility/noPLT.C
@@ -1,5 +1,5 @@ 
 /* Test that -fvisibility=hidden prevents PLT. */
-/* { dg-do compile { target fpic } } */
+/* { dg-do compile { target { fpic && { ! s390x-*-* } } } } */
 /* { dg-require-visibility "" } */
 /* { dg-options "-fPIC -fvisibility=hidden" } */
 /* { dg-final { scan-assembler-not "methodEv@PLT|indirect_symbol.*methodEv" } } */
diff --git a/gcc/testsuite/gcc.target/s390/call.h b/gcc/testsuite/gcc.target/s390/call.h
new file mode 100644
index 00000000000..059a6722887
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/call.h
@@ -0,0 +1,40 @@ 
+/* Common code for testing the function call code generation.  */
+
+__attribute__ ((noipa)) void
+foo (void)
+{
+  return;
+}
+
+void *
+usefoo (void)
+{
+  foo ();
+  return foo;
+}
+
+__attribute__ ((noipa)) static void
+foostatic (void)
+{
+  return;
+}
+
+void *
+usefoostatic (void)
+{
+  foostatic ();
+  return foostatic;
+}
+
+__attribute__ ((weak)) void fooweak (void);
+
+void *
+usefooweak (void)
+{
+  fooweak ();
+  return fooweak;
+}
+
+__attribute__ ((__used__, section (".foos"), aligned (sizeof (void *))))
+static void
+*foos[] = { foo, foostatic, fooweak };
diff --git a/gcc/testsuite/gcc.target/s390/call31-z10-pic-nodatarel.c b/gcc/testsuite/gcc.target/s390/call31-z10-pic-nodatarel.c
new file mode 100644
index 00000000000..3e6eff6c9db
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/call31-z10-pic-nodatarel.c
@@ -0,0 +1,16 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O3 -m31 -march=z10 -mzarch -fPIC -mno-pic-data-is-text-relative" } */
+
+#include "call.h"
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,foo@PLT\n} } } */
+/* { dg-final { scan-assembler {lrl\t%r2,foo@GOTENT\n} } } */
+
+/* In 31-bit mode we can't use @PLT as freely as in 64-bit mode.  */
+/* { dg-final { scan-assembler {brasl\t%r\d+,foostatic\n} } } */
+/* { dg-final { scan-assembler {larl\t%r2,foostatic\n} } } */
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,fooweak@PLT\n} } } */
+/* { dg-final { scan-assembler {lrl\t%r2,fooweak@GOTENT\n} } } */
+
+/* { dg-final { scan-assembler {foos:\n\t.long\tfoo\n\t.long\tfoostatic\n\t.long\tfooweak\n} } } */
diff --git a/gcc/testsuite/gcc.target/s390/call31-z10-pic.c b/gcc/testsuite/gcc.target/s390/call31-z10-pic.c
new file mode 100644
index 00000000000..34a5ca959b2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/call31-z10-pic.c
@@ -0,0 +1,16 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O3 -m31 -march=z10 -mzarch -fPIC" } */
+
+#include "call.h"
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,foo@PLT\n} } } */
+/* { dg-final { scan-assembler {lrl\t%r2,foo@GOTENT\n} } } */
+
+/* In 31-bit mode we can't use @PLT as freely as in 64-bit mode.  */
+/* { dg-final { scan-assembler {brasl\t%r\d+,foostatic\n} } } */
+/* { dg-final { scan-assembler {larl\t%r2,foostatic\n} } } */
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,fooweak@PLT\n} } } */
+/* { dg-final { scan-assembler {lrl\t%r2,fooweak@GOTENT\n} } } */
+
+/* { dg-final { scan-assembler {foos:\n\t.long\tfoo\n\t.long\tfoostatic\n\t.long\tfooweak\n} } } */
diff --git a/gcc/testsuite/gcc.target/s390/call31-z10.c b/gcc/testsuite/gcc.target/s390/call31-z10.c
new file mode 100644
index 00000000000..935f7115cd5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/call31-z10.c
@@ -0,0 +1,15 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O3 -m31 -march=z10 -mzarch" } */
+
+#include "call.h"
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,foo\n} } } */
+/* { dg-final { scan-assembler {larl\t%r2,foo\n} } } */
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,foostatic\n} } } */
+/* { dg-final { scan-assembler {larl\t%r2,foostatic\n} } } */
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,fooweak\n} } } */
+/* { dg-final { scan-assembler {larl\t%r2,fooweak\n} } } */
+
+/* { dg-final { scan-assembler {foos:\n\t.long\tfoo\n\t.long\tfoostatic\n\t.long\tfooweak\n} } } */
diff --git a/gcc/testsuite/gcc.target/s390/call31-z9-pic-nodatarel.c b/gcc/testsuite/gcc.target/s390/call31-z9-pic-nodatarel.c
new file mode 100644
index 00000000000..a4a3170f071
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/call31-z9-pic-nodatarel.c
@@ -0,0 +1,16 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O3 -m31 -march=z9-ec -fPIC -mno-pic-data-is-text-relative" } */
+
+#include "call.h"
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,foo@PLT\n} } } */
+/* { dg-final { scan-assembler {larl\t%r\d+,foo@GOTENT\n} } } */
+
+/* In 31-bit mode we can't use @PLT as freely as in 64-bit mode.  */
+/* { dg-final { scan-assembler {brasl\t%r\d+,foostatic\n} } } */
+/* { dg-final { scan-assembler {larl\t%r2,foostatic\n} } } */
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,fooweak@PLT\n} } } */
+/* { dg-final { scan-assembler {larl\t%r\d+,fooweak@GOTENT\n} } } */
+
+/* { dg-final { scan-assembler {foos:\n\t.long\tfoo\n\t.long\tfoostatic\n\t.long\tfooweak\n} } } */
diff --git a/gcc/testsuite/gcc.target/s390/call31-z9-pic.c b/gcc/testsuite/gcc.target/s390/call31-z9-pic.c
new file mode 100644
index 00000000000..9950d504e72
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/call31-z9-pic.c
@@ -0,0 +1,16 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O3 -m31 -march=z9-ec -fPIC" } */
+
+#include "call.h"
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,foo@PLT\n} } } */
+/* { dg-final { scan-assembler {larl\t%r\d+,foo@GOTENT\n} } } */
+
+/* In 31-bit mode we can't use @PLT as freely as in 64-bit mode.  */
+/* { dg-final { scan-assembler {brasl\t%r\d+,foostatic\n} } } */
+/* { dg-final { scan-assembler {larl\t%r2,foostatic\n} } } */
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,fooweak@PLT\n} } } */
+/* { dg-final { scan-assembler {larl\t%r\d+,fooweak@GOTENT\n} } } */
+
+/* { dg-final { scan-assembler {foos:\n\t.long\tfoo\n\t.long\tfoostatic\n\t.long\tfooweak\n} } } */
diff --git a/gcc/testsuite/gcc.target/s390/call31-z9.c b/gcc/testsuite/gcc.target/s390/call31-z9.c
new file mode 100644
index 00000000000..2f211135b25
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/call31-z9.c
@@ -0,0 +1,15 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O3 -m31 -march=z9-ec" } */
+
+#include "call.h"
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,foo\n} } } */
+/* { dg-final { scan-assembler {larl\t%r2,foo\n} } } */
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,foostatic\n} } } */
+/* { dg-final { scan-assembler {larl\t%r2,foostatic\n} } } */
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,fooweak\n} } } */
+/* { dg-final { scan-assembler {larl\t%r2,fooweak\n} } } */
+
+/* { dg-final { scan-assembler {foos:\n\t.long\tfoo\n\t.long\tfoostatic\n\t.long\tfooweak\n} } } */
diff --git a/gcc/testsuite/gcc.target/s390/call64-z10-pic-nodatarel.c b/gcc/testsuite/gcc.target/s390/call64-z10-pic-nodatarel.c
new file mode 100644
index 00000000000..3547ca979e1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/call64-z10-pic-nodatarel.c
@@ -0,0 +1,17 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O3 -m64 -march=z10 -fPIC -mno-pic-data-is-text-relative" } */
+
+#include "call.h"
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,foo@PLT\n} } } */
+/* { dg-final { scan-assembler {lgrl\t%r2,foo@GOTENT\n} } } */
+
+/* ld is supposed to optimize foostatic@PLT to just foostatic.  */
+/* { dg-final { scan-assembler {brasl\t%r\d+,foostatic@PLT\n} } } */
+/* { dg-final { scan-assembler {larl\t%r2,foostatic@PLT\n} } } */
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,fooweak@PLT\n} } } */
+/* { dg-final { scan-assembler {lgrl\t%r2,fooweak@GOTENT\n} } } */
+
+/* @PLT is not used for data, since it produces R_390_PLT64 relocation.  */
+/* { dg-final { scan-assembler {foos:\n\t.quad\tfoo\n\t.quad\tfoostatic\n\t.quad\tfooweak\n} } } */
diff --git a/gcc/testsuite/gcc.target/s390/call64-z10-pic.c b/gcc/testsuite/gcc.target/s390/call64-z10-pic.c
new file mode 100644
index 00000000000..6ad36eb9e7e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/call64-z10-pic.c
@@ -0,0 +1,17 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O3 -m64 -march=z10 -fPIC" } */
+
+#include "call.h"
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,foo@PLT\n} } } */
+/* { dg-final { scan-assembler {lgrl\t%r2,foo@GOTENT\n} } } */
+
+/* ld is supposed to optimize foostatic@PLT to just foostatic.  */
+/* { dg-final { scan-assembler {brasl\t%r\d+,foostatic@PLT\n} } } */
+/* { dg-final { scan-assembler {larl\t%r2,foostatic@PLT\n} } } */
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,fooweak@PLT\n} } } */
+/* { dg-final { scan-assembler {lgrl\t%r2,fooweak@GOTENT\n} } } */
+
+/* @PLT is not used for data, since it produces R_390_PLT64 relocation.  */
+/* { dg-final { scan-assembler {foos:\n\t.quad\tfoo\n\t.quad\tfoostatic\n\t.quad\tfooweak\n} } } */
diff --git a/gcc/testsuite/gcc.target/s390/call64-z10.c b/gcc/testsuite/gcc.target/s390/call64-z10.c
new file mode 100644
index 00000000000..677d9e4fc5d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/call64-z10.c
@@ -0,0 +1,15 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O3 -m64 -march=z10" } */
+
+#include "call.h"
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,foo@PLT\n} } } */
+/* { dg-final { scan-assembler {larl\t%r2,foo@PLT\n} } } */
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,foostatic@PLT\n} } } */
+/* { dg-final { scan-assembler {larl\t%r2,foostatic@PLT\n} } } */
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,fooweak\n} } } */
+/* { dg-final { scan-assembler {larl\t%r2,fooweak\n} } } */
+
+/* { dg-final { scan-assembler {foos:\n\t.quad\tfoo\n\t.quad\tfoostatic\n\t.quad\tfooweak\n} } } */
diff --git a/gcc/testsuite/gcc.target/s390/call64-z9-pic-nodatarel.c b/gcc/testsuite/gcc.target/s390/call64-z9-pic-nodatarel.c
new file mode 100644
index 00000000000..4f498981f23
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/call64-z9-pic-nodatarel.c
@@ -0,0 +1,17 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O3 -m64 -march=z9-ec -fPIC -mno-pic-data-is-text-relative" } */
+
+#include "call.h"
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,foo@PLT\n} } } */
+/* { dg-final { scan-assembler {larl\t%r\d+,foo@GOTENT\n} } } */
+
+/* ld is supposed to optimize foostatic@PLT to just foostatic.  */
+/* { dg-final { scan-assembler {brasl\t%r\d+,foostatic@PLT\n} } } */
+/* { dg-final { scan-assembler {larl\t%r2,foostatic@PLT\n} } } */
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,fooweak@PLT\n} } } */
+/* { dg-final { scan-assembler {larl\t%r\d+,fooweak@GOTENT\n} } } */
+
+/* @PLT is not used for data, since it produces R_390_PLT64 relocation.  */
+/* { dg-final { scan-assembler {foos:\n\t.quad\tfoo\n\t.quad\tfoostatic\n\t.quad\tfooweak\n} } } */
diff --git a/gcc/testsuite/gcc.target/s390/call64-z9-pic.c b/gcc/testsuite/gcc.target/s390/call64-z9-pic.c
new file mode 100644
index 00000000000..1652f91e660
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/call64-z9-pic.c
@@ -0,0 +1,17 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O3 -m64 -march=z9-ec -fPIC" } */
+
+#include "call.h"
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,foo@PLT\n} } } */
+/* { dg-final { scan-assembler {larl\t%r\d+,foo@GOTENT\n} } } */
+
+/* ld is supposed to optimize foostatic@PLT to just foostatic.  */
+/* { dg-final { scan-assembler {brasl\t%r\d+,foostatic@PLT\n} } } */
+/* { dg-final { scan-assembler {larl\t%r2,foostatic@PLT\n} } } */
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,fooweak@PLT\n} } } */
+/* { dg-final { scan-assembler {larl\t%r\d+,fooweak@GOTENT\n} } } */
+
+/* @PLT is not used for data, since it produces R_390_PLT64 relocation.  */
+/* { dg-final { scan-assembler {foos:\n\t.quad\tfoo\n\t.quad\tfoostatic\n\t.quad\tfooweak\n} } } */
diff --git a/gcc/testsuite/gcc.target/s390/call64-z9.c b/gcc/testsuite/gcc.target/s390/call64-z9.c
new file mode 100644
index 00000000000..02ec6c00e7f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/call64-z9.c
@@ -0,0 +1,15 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O3 -m64 -march=z9-ec" } */
+
+#include "call.h"
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,foo@PLT\n} } } */
+/* { dg-final { scan-assembler {larl\t%r2,foo@PLT\n} } } */
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,foostatic@PLT\n} } } */
+/* { dg-final { scan-assembler {larl\t%r2,foostatic@PLT\n} } } */
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,fooweak\n} } } */
+/* { dg-final { scan-assembler {larl\t%r2,fooweak\n} } } */
+
+/* { dg-final { scan-assembler {foos:\n\t.quad\tfoo\n\t.quad\tfoostatic\n\t.quad\tfooweak\n} } } */
diff --git a/gcc/testsuite/gcc.target/s390/nodatarel-1.c b/gcc/testsuite/gcc.target/s390/nodatarel-1.c
index f53332f901d..02e64c3eddf 100644
--- a/gcc/testsuite/gcc.target/s390/nodatarel-1.c
+++ b/gcc/testsuite/gcc.target/s390/nodatarel-1.c
@@ -15,12 +15,6 @@  foo ()
   return a;
 }
 
-static int __attribute__((noinline,noclone))
-foostatic (void)
-{
-  return a;
-}
-
 /* Just to make a potentially modified.  */
 
 void
@@ -29,7 +23,7 @@  bar (int b)
   a = b;
 }
 
-/* { dg-final { scan-assembler-times "\\.LANCHOR\\d+@GOTENT" 3 } } */
+/* { dg-final { scan-assembler-times "\\.LANCHOR\\d+@GOTENT" 2 } } */
 
 /* The exrl target is a label_ref which should not be affected at
    all.  */
@@ -63,21 +57,3 @@  fooptr ()
 }
 
 /* { dg-final { scan-assembler-times "foo@GOTENT" 1 } } */
-
-
-/* A static function can be addressed relatively.  */
-
-int
-callfoostatic ()
-{
-  return foostatic ();
-}
-
-void *
-foostaticptr ()
-{
-  return &foostatic;
-}
-
-
-/* { dg-final { scan-assembler-not "foostatic@" } } */
diff --git a/gcc/testsuite/gcc.target/s390/pr80080-4.c b/gcc/testsuite/gcc.target/s390/pr80080-4.c
index 5fc6a558008..3f16a197764 100644
--- a/gcc/testsuite/gcc.target/s390/pr80080-4.c
+++ b/gcc/testsuite/gcc.target/s390/pr80080-4.c
@@ -13,4 +13,4 @@  void foo4(int *mem)
     }
 }
 
-/* { dg-final { scan-assembler {(?n)\n\tlt\t.*\n\tjne\t(\.L\d+)\n(.*\n)*\tcs\t.*\n\tber\t%r14\n\1:\n\tjg\tbar\n} } } */
+/* { dg-final { scan-assembler {(?n)\n\tlt\t.*\n\tjne\t(\.L\d+)\n(.*\n)*\tcs\t.*\n\tber\t%r14\n\1:\n\tjg\tbar(@PLT)?\n} } } */
diff --git a/gcc/testsuite/gcc.target/s390/risbg-ll-3.c b/gcc/testsuite/gcc.target/s390/risbg-ll-3.c
index 864b0d6c417..02f6e046eb9 100644
--- a/gcc/testsuite/gcc.target/s390/risbg-ll-3.c
+++ b/gcc/testsuite/gcc.target/s390/risbg-ll-3.c
@@ -23,7 +23,7 @@  i64 f1 (i64 v_a, i64 v_b)
 extern i64 f2_foo();
 i64 f2 (i64 v_a, i64 v_b)
 {
-/* { dg-final { scan-assembler "f2:\n\trisbg\t%r2,%r3,60,62,0\n\tbner\t%r14\n\tjg\tf2_foo\n" { target { lp64 } } } } */
+/* { dg-final { scan-assembler "f2:\n\trisbg\t%r2,%r3,60,62,0\n\tbner\t%r14\n\tjg\tf2_foo.*\n" { target { lp64 } } } } */
 /* { dg-final { scan-assembler "f2:\n\trisbgn\t%r3,%r2,0,0\\\+32-1,64-0-32\n\trisbg\t%r3,%r5,60,62,0" { target { ! lp64 } } } } */
   i64 v_anda = v_a & -15;
   i64 v_andb = v_b & 14;
@@ -37,8 +37,8 @@  i64 f2 (i64 v_a, i64 v_b)
 void f2_bar ();
 void f2_cconly (i64 v_a, i64 v_b)
 {
-/* { dg-final { scan-assembler "f2_cconly:\n\trisbg\t%r2,%r3,60,62,0\n\tber\t%r14\n\tjg\tf2_bar\n" { target { lp64 } } } } */
-/* { dg-final { scan-assembler "f2_cconly:\n\trisbgn\t%r3,%r2,0,0\\\+32-1,64-0-32\n\trisbg\t%r3,%r5,60,62,0\n\tber\t%r14\n\tjg\tf2_bar\n" { target { ! lp64 } } } } */
+/* { dg-final { scan-assembler "f2_cconly:\n\trisbg\t%r2,%r3,60,62,0\n\tber\t%r14\n\tjg\tf2_bar(@PLT)?\n" { target { lp64 } } } } */
+/* { dg-final { scan-assembler "f2_cconly:\n\trisbgn\t%r3,%r2,0,0\\\+32-1,64-0-32\n\trisbg\t%r3,%r5,60,62,0\n\tber\t%r14\n\tjg\tf2_bar(@PLT)?\n" { target { ! lp64 } } } } */
   if ((v_a & -15) | (v_b & 14))
     f2_bar();
 }
diff --git a/gcc/testsuite/gcc.target/s390/tls.h b/gcc/testsuite/gcc.target/s390/tls.h
new file mode 100644
index 00000000000..f639bd38c8d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/tls.h
@@ -0,0 +1,23 @@ 
+/* Common code for testing the TLS code generation.  */
+
+__thread int
+foo;
+
+int
+setfoo (int x)
+{
+  int result = foo;
+  foo = x;
+  return result;
+}
+
+static __thread int
+foostatic;
+
+int
+setfoostatic (int x)
+{
+  int result = foostatic;
+  foostatic = x;
+  return result;
+}
diff --git a/gcc/testsuite/gcc.target/s390/tls31-pic.c b/gcc/testsuite/gcc.target/s390/tls31-pic.c
new file mode 100644
index 00000000000..20327b4702b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/tls31-pic.c
@@ -0,0 +1,14 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O3 -m31 -fPIC" } */
+
+#include "tls.h"
+
+/* foo must use the global dynamic model.
+   __tls_get_offset must be referenced through PLT.  */
+
+/* { dg-final { scan-assembler-times {\tbrasl\t%r14,__tls_get_offset@PLT:tls_gdcall:foo\n} 1 } } */
+
+/* foostatic must use the local dynamic model.
+   __tls_get_offset must be referenced through PLT.  */
+
+/* { dg-final { scan-assembler-times {\tbrasl\t%r14,__tls_get_offset@PLT:tls_ldcall} 1 } } */
diff --git a/gcc/testsuite/gcc.target/s390/tls31.c b/gcc/testsuite/gcc.target/s390/tls31.c
new file mode 100644
index 00000000000..1dba97da802
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/tls31.c
@@ -0,0 +1,9 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O3 -m31" } */
+
+#include "tls.h"
+
+/* foo must use the initial-exec model, foostatic must use the local-exec
+   model.  */
+
+/* { dg-final { scan-assembler-times {\tear} 2 } } */
diff --git a/gcc/testsuite/gcc.target/s390/tls64-pic.c b/gcc/testsuite/gcc.target/s390/tls64-pic.c
new file mode 100644
index 00000000000..4779d2271b0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/tls64-pic.c
@@ -0,0 +1,14 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O3 -m64 -fPIC" } */
+
+#include "tls.h"
+
+/* foo must use the global dynamic model.
+   __tls_get_offset must be referenced through PLT.  */
+
+/* { dg-final { scan-assembler-times {\tbrasl\t%r14,__tls_get_offset@PLT:tls_gdcall:foo\n} 1 } } */
+
+/* foostatic must use the local dynamic model.
+   __tls_get_offset must be referenced through PLT.  */
+
+/* { dg-final { scan-assembler-times {\tbrasl\t%r14,__tls_get_offset@PLT:tls_ldcall} 1 } } */
diff --git a/gcc/testsuite/gcc.target/s390/tls64.c b/gcc/testsuite/gcc.target/s390/tls64.c
new file mode 100644
index 00000000000..4f0c97cf43d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/tls64.c
@@ -0,0 +1,9 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O3 -m64" } */
+
+#include "tls.h"
+
+/* foo must use the initial-exec model, foostatic must use the local-exec
+   model.  */
+
+/* { dg-final { scan-assembler-times {\tear} 4 } } */