diff --git a/gcc/config/s390/predicates.md b/gcc/config/s390/predicates.md
index 15093cb4b30dfda3a568b5c935412ed8019fb390..99c343aa32c5bf5eeb7479f333793f99ce9d0d93 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 800e0abc016bfa93c69c53dd8c34e211c10af72c..b1d3b99784de5be41175e13fb757bb23b8e8ba02 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]);
     }
 
@@ -13142,7 +13170,7 @@ s390_function_profiler (FILE *file, int labelno ATTRIBUTE_UNUSED)
 	warning (OPT_Wcannot_profile, "nested functions cannot be profiled "
 		 "with %<-mfentry%> on s390");
       else
-	output_asm_insn ("brasl\t0,%2", op);
+	output_asm_insn ("brasl\t0,%2%K2", op);
     }
   else if (TARGET_64BIT)
     {
@@ -13154,7 +13182,7 @@ s390_function_profiler (FILE *file, int labelno ATTRIBUTE_UNUSED)
 	  output_asm_insn ("stg\t%0,%1", op);
 	  if (flag_dwarf2_cfi_asm)
 	    output_asm_insn (".cfi_rel_offset\t%0,%3", op);
-	  output_asm_insn ("brasl\t%0,%2", op);
+	  output_asm_insn ("brasl\t%0,%2%K2", op);
 	  output_asm_insn ("lg\t%0,%1", op);
 	  if (flag_dwarf2_cfi_asm)
 	    output_asm_insn (".cfi_restore\t%0", op);
@@ -13170,7 +13198,7 @@ s390_function_profiler (FILE *file, int labelno ATTRIBUTE_UNUSED)
 	  output_asm_insn ("st\t%0,%1", op);
 	  if (flag_dwarf2_cfi_asm)
 	    output_asm_insn (".cfi_rel_offset\t%0,%3", op);
-	  output_asm_insn ("brasl\t%0,%2", op);
+	  output_asm_insn ("brasl\t%0,%2%K2", op);
 	  output_asm_insn ("l\t%0,%1", op);
 	  if (flag_dwarf2_cfi_asm)
 	    output_asm_insn (".cfi_restore\t%0", op);
@@ -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.  */
@@ -13338,7 +13368,7 @@ s390_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
 	}
 
       /* Jump to target.  */
-      output_asm_insn ("jg\t%0", op);
+      output_asm_insn ("jg\t%0%K0", op);
 
       /* Output literal pool if required.  */
       if (op[5])
@@ -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 0c5b4dc90293da99ba389e03da555184b5edea56..8ad21b0f4f70080b54a47cfd4663bd4d632bdcbf 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 38af05fd6e355724155f4d92b3e2e2b77f4e7f31..b888303d56dab23665aa0a36a4eb294129f7cbd6 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/g++.target/s390/mi-thunk.C b/gcc/testsuite/g++.target/s390/mi-thunk.C
new file mode 100644
index 0000000000000000000000000000000000000000..7c25dc04bba5e22196ff3dd5a8ef458993ff92ed
--- /dev/null
+++ b/gcc/testsuite/g++.target/s390/mi-thunk.C
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-O0 -fPIC" } */
+
+class A {
+public:
+  virtual int a (void);
+};
+
+class B {
+public:
+  virtual int b (void);
+};
+
+class C : public B, public A {
+public:
+  virtual int a (void);
+};
+
+int C::a (void) { return b(); }
+
+/* { dg-final { scan-assembler {\n_ZThn8_N1C1aEv:\n} { target lp64 } } } */
+/* { dg-final { scan-assembler {\n_ZThn4_N1C1aEv:\n} { target { ! lp64 } } } } */
+/* { dg-final { scan-assembler {\n\tjg\t.LTHUNK0@PLT\n} { target lp64 } } } */
diff --git a/gcc/testsuite/gcc.target/s390/call-z10-pic-nodatarel.c b/gcc/testsuite/gcc.target/s390/call-z10-pic-nodatarel.c
new file mode 100644
index 0000000000000000000000000000000000000000..49984614bc6f718ada64283c41a96ffe13a82575
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/call-z10-pic-nodatarel.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -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 {lgrl\t%r2,foo@GOTENT\n} { target lp64 } } } */
+/* { dg-final { scan-assembler {lrl\t%r2,foo@GOTENT\n} { target { ! lp64 } } } } */
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,foostatic@PLT\n} { target lp64 } } } */
+/* { dg-final { scan-assembler {brasl\t%r\d+,foostatic\n} { target { ! lp64 } } } } */
+/* { dg-final { scan-assembler {larl\t%r2,foostatic@PLT\n} { target lp64 } } } */
+/* { dg-final { scan-assembler {larl\t%r2,foostatic\n} { target { ! lp64 } } } } */
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,fooweak@PLT\n} } } */
+/* { dg-final { scan-assembler {lgrl\t%r2,fooweak@GOTENT\n} { target lp64 } } } */
+/* { dg-final { scan-assembler {lrl\t%r2,fooweak@GOTENT\n} { target { ! lp64 } } } } */
+
+/* { dg-final { scan-assembler {foos:\n\t.quad\tfoo\n\t.quad\tfoostatic\n\t.quad\tfooweak\n} { target lp64 } } } */
+/* { dg-final { scan-assembler {foos:\n\t.long\tfoo\n\t.long\tfoostatic\n\t.long\tfooweak\n} { target { ! lp64 } } } } */
diff --git a/gcc/testsuite/gcc.target/s390/call-z10-pic.c b/gcc/testsuite/gcc.target/s390/call-z10-pic.c
new file mode 100644
index 0000000000000000000000000000000000000000..210c56cc4cad0a30539948cbc8c4281232306459
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/call-z10-pic.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -march=z10 -mzarch -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} { target lp64 } } } */
+/* { dg-final { scan-assembler {lrl\t%r2,foo@GOTENT\n} { target { ! lp64 } } } } */
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,foostatic@PLT\n} { target lp64 } } } */
+/* { dg-final { scan-assembler {brasl\t%r\d+,foostatic\n} { target { ! lp64 } } } } */
+/* { dg-final { scan-assembler {larl\t%r2,foostatic@PLT\n} { target lp64 } } } */
+/* { dg-final { scan-assembler {larl\t%r2,foostatic\n} { target { ! lp64 } } } } */
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,fooweak@PLT\n} } } */
+/* { dg-final { scan-assembler {lgrl\t%r2,fooweak@GOTENT\n} { target lp64 } } } */
+/* { dg-final { scan-assembler {lrl\t%r2,fooweak@GOTENT\n} { target { ! lp64 } } } } */
+
+/* { dg-final { scan-assembler {foos:\n\t.quad\tfoo\n\t.quad\tfoostatic\n\t.quad\tfooweak\n} { target lp64 } } } */
+/* { dg-final { scan-assembler {foos:\n\t.long\tfoo\n\t.long\tfoostatic\n\t.long\tfooweak\n} { target { ! lp64 } } } } */
diff --git a/gcc/testsuite/gcc.target/s390/call-z10.c b/gcc/testsuite/gcc.target/s390/call-z10.c
new file mode 100644
index 0000000000000000000000000000000000000000..2d7dc735df4aa39338b25d89d7179c41935b9c00
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/call-z10.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -march=z10 -mzarch" } */
+
+#include "call.h"
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,foo@PLT\n} { target lp64 } } } */
+/* { dg-final { scan-assembler {brasl\t%r\d+,foo\n} { target { ! lp64 } } } } */
+/* { dg-final { scan-assembler {larl\t%r2,foo@PLT\n} { target lp64 } } } */
+/* { dg-final { scan-assembler {larl\t%r2,foo\n} { target { ! lp64 } } } } */
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,foostatic@PLT\n} { target lp64 } } } */
+/* { dg-final { scan-assembler {brasl\t%r\d+,foostatic\n} { target { ! lp64 } } } } */
+/* { dg-final { scan-assembler {larl\t%r2,foostatic@PLT\n} { target lp64 } } } */
+/* { dg-final { scan-assembler {larl\t%r2,foostatic\n} { target { ! lp64 } } } } */
+
+/* { 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} { target lp64 } } } */
+/* { dg-final { scan-assembler {foos:\n\t.long\tfoo\n\t.long\tfoostatic\n\t.long\tfooweak\n} { target { ! lp64 } } } } */
diff --git a/gcc/testsuite/gcc.target/s390/call-z9-pic-nodatarel.c b/gcc/testsuite/gcc.target/s390/call-z9-pic-nodatarel.c
new file mode 100644
index 0000000000000000000000000000000000000000..b1ae318d3e99427f6fc254c7847466dbb5833242
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/call-z9-pic-nodatarel.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -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} } } */
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,foostatic@PLT\n} { target lp64 } } } */
+/* { dg-final { scan-assembler {brasl\t%r\d+,foostatic\n} { target { ! lp64 } } } } */
+/* { dg-final { scan-assembler {larl\t%r2,foostatic@PLT\n} { target lp64 } } } */
+/* { dg-final { scan-assembler {larl\t%r2,foostatic\n} { target { ! lp64 } } } } */
+
+/* { 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.quad\tfoo\n\t.quad\tfoostatic\n\t.quad\tfooweak\n} { target lp64 } } } */
+/* { dg-final { scan-assembler {foos:\n\t.long\tfoo\n\t.long\tfoostatic\n\t.long\tfooweak\n} { target { ! lp64 } } } } */
diff --git a/gcc/testsuite/gcc.target/s390/call-z9-pic.c b/gcc/testsuite/gcc.target/s390/call-z9-pic.c
new file mode 100644
index 0000000000000000000000000000000000000000..a89e78d8df8e5a356c2d163ac003b58e633cf0ea
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/call-z9-pic.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -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} } } */
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,foostatic@PLT\n} { target lp64 } } } */
+/* { dg-final { scan-assembler {brasl\t%r\d+,foostatic\n} { target { ! lp64 } } } } */
+/* { dg-final { scan-assembler {larl\t%r2,foostatic@PLT\n} { target lp64 } } } */
+/* { dg-final { scan-assembler {larl\t%r2,foostatic\n} { target { ! lp64 } } } } */
+
+/* { 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.quad\tfoo\n\t.quad\tfoostatic\n\t.quad\tfooweak\n} { target lp64 } } } */
+/* { dg-final { scan-assembler {foos:\n\t.long\tfoo\n\t.long\tfoostatic\n\t.long\tfooweak\n} { target { ! lp64 } } } } */
diff --git a/gcc/testsuite/gcc.target/s390/call-z9.c b/gcc/testsuite/gcc.target/s390/call-z9.c
new file mode 100644
index 0000000000000000000000000000000000000000..21d035aecb6b065af2cc7398a41a02b53c91fc4f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/call-z9.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -march=z9-ec" } */
+
+#include "call.h"
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,foo@PLT\n} { target lp64 } } } */
+/* { dg-final { scan-assembler {brasl\t%r\d+,foo\n} { target { ! lp64 } } } } */
+/* { dg-final { scan-assembler {larl\t%r2,foo@PLT\n} { target lp64 } } } */
+/* { dg-final { scan-assembler {larl\t%r2,foo\n} { target { ! lp64 } } } } */
+
+/* { dg-final { scan-assembler {brasl\t%r\d+,foostatic@PLT\n} { target lp64 } } } */
+/* { dg-final { scan-assembler {brasl\t%r\d+,foostatic\n} { target { ! lp64 } } } } */
+/* { dg-final { scan-assembler {larl\t%r2,foostatic@PLT\n} { target lp64 } } } */
+/* { dg-final { scan-assembler {larl\t%r2,foostatic\n} { target { ! lp64 } } } } */
+
+/* { 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} { target lp64 } } } */
+/* { dg-final { scan-assembler {foos:\n\t.long\tfoo\n\t.long\tfoostatic\n\t.long\tfooweak\n} { target { ! lp64 } } } } */
diff --git a/gcc/testsuite/gcc.target/s390/call.h b/gcc/testsuite/gcc.target/s390/call.h
new file mode 100644
index 0000000000000000000000000000000000000000..059a672288739a1e33303e80049b67aa51f440fd
--- /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/mfentry-m64-pic.c b/gcc/testsuite/gcc.target/s390/mfentry-m64-pic.c
new file mode 100644
index 0000000000000000000000000000000000000000..32d55ce2b75383ecf08e8c8206046d0a7c1a8a49
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/mfentry-m64-pic.c
@@ -0,0 +1,9 @@
+/* { dg-do compile { target { lp64 } } } */
+/* { dg-options "-pg -mfentry -fPIC" } */
+
+void
+profileme (void)
+{
+  /* __fentry__ must be referenced through PLT.  */
+  /* { dg-final { scan-assembler "brasl\t0,__fentry__@PLT\n" } } */
+}
diff --git a/gcc/testsuite/gcc.target/s390/nodatarel-1.c b/gcc/testsuite/gcc.target/s390/nodatarel-1.c
index f53332f901dbc4c6ec65f32735e09440967595b1..02e64c3eddf02e29485c7cb92c9025e0a89aebca 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 5fc6a558008ff0502588bfba94a16c7ea748c6f5..3f16a197764eaead1d258d2a544215fccc9bd2dc 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 864b0d6c417b8f45cbdae0d4e2aff00f68002609..02f6e046eb9d56388c82cf8609f5b7cae7a66989 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-pic.c b/gcc/testsuite/gcc.target/s390/tls-pic.c
new file mode 100644
index 0000000000000000000000000000000000000000..17c5726be4f0a32b510f444d293fac39246f543c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/tls-pic.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -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/tls.c b/gcc/testsuite/gcc.target/s390/tls.c
new file mode 100644
index 0000000000000000000000000000000000000000..3d479d8e48f38a2ab105f76bcda31f8fe6004071
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/tls.c
@@ -0,0 +1,10 @@
+/* { dg-do compile } */
+/* { dg-options "-O3" } */
+
+#include "tls.h"
+
+/* foo must use the initial-exec model, foostatic must use the local-exec
+   model.  */
+
+/* { dg-final { scan-assembler-times {\tear} 4 { target lp64 } } } */
+/* { dg-final { scan-assembler-times {\tear} 2 { target { ! lp64 } } } } */
diff --git a/gcc/testsuite/gcc.target/s390/tls.h b/gcc/testsuite/gcc.target/s390/tls.h
new file mode 100644
index 0000000000000000000000000000000000000000..f639bd38c8d74bbe911f0e201f3e6c7afcbcc7ed
--- /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;
+}