diff mbox

S/390: Function hotpatching option and function attribute

Message ID 20131205080630.GA28310@linux.vnet.ibm.com
State New
Headers show

Commit Message

Dominik Vogt Dec. 5, 2013, 8:06 a.m. UTC
Hi,

Andreas Krebbel an I have ported the function hotpatching feature
from i386 (aka ms_hook_prologue attribute) to S/390.  Andreas has
already internally approved the attached patch and will commit it
soon (for legal reasons he has to do that himself).

The attached patch introduces command line options as well as a
function attribute to control the function hotpatching prologue:

  -m[no-]hotpatch
    Enable or disable hotpatching prologues for all functions in
    the compilation unit (with the default prologue size).

  -mhotpatch=<n>
    Same as -mhotpatch, but the size of the prologue is <n+2>
    halfwords (i.e. the trampoline area is <n> halwords).

  __attribute__ ((hotpatch)) and 
  __attribute__ ((hotpatch(<n>)))
    Work like the option, but only for the specific function.  If
    the attribute and one of the options is given, the attribute
    always wins.

<n> above is limited to values 0 through 1000000 (default 12).
Functions that are explicitly inline cannot be hotpatched, and
hotpatched functions are never implicitly inlined.

The layout of a hotpatchable function prologue is

    nopr %r7 (<n> times, 2 bytes each, aka "trampoline area")
  function_label:
    nop 0 (four bytes)

The function label alignment is changed to eight bytes to make
sure that the eight bytes directly in front of the label reside
in the same cacheline.

To hotpatch a function, the program first writes the address of
the new version of the hotpatched function to the eight bytes
before the label (four bytes on 32-bit), then writes code to read
that address and to jump to it into the trampoline area and then
replaces the nop behind the label with a relative backwards jump
into the trampoline area.  (Some maintenance of the Icache is
necessary in this procedure.)

If the program can make sure that the patched functions are always
close enough to the original in the memory map, the trampoline
area can be omitted (<n> = 0), and a short relative jump to the
patched function can be written over the four byte nop.

The patch includes a number of test cases that all run without
trouble here.  The tests validate the syntax of the options an the
attribute and check for the correct number of nops.  I have
verified the correct layout of the trampoline manually (i.e. the
correct placement of the nops).

Ciao

Dominik ^_^  ^_^
diff mbox

Patch

From b7f291267fb8c8afd1be38436308e4707dbe0c17 Mon Sep 17 00:00:00 2001
From: Dominik Vogt <vogt@de.ibm.com>
Date: Mon, 18 Nov 2013 15:41:46 +0000
Subject: [PATCH] S/390: Function hotpatching option and function attribute

---
 gcc/config/s390/s390-protos.h                      |   1 +
 gcc/config/s390/s390.c                             | 197 +++++++++++++++++++++
 gcc/config/s390/s390.h                             |   5 +-
 gcc/config/s390/s390.opt                           |   8 +
 gcc/doc/extend.texi                                |  11 ++
 gcc/doc/invoke.texi                                |  18 +-
 gcc/testsuite/gcc.target/s390/hotpatch-1.c         |  20 +++
 gcc/testsuite/gcc.target/s390/hotpatch-10.c        |  21 +++
 gcc/testsuite/gcc.target/s390/hotpatch-11.c        |  20 +++
 gcc/testsuite/gcc.target/s390/hotpatch-12.c        |  20 +++
 gcc/testsuite/gcc.target/s390/hotpatch-2.c         |  20 +++
 gcc/testsuite/gcc.target/s390/hotpatch-3.c         |  20 +++
 gcc/testsuite/gcc.target/s390/hotpatch-4.c         |  26 +++
 gcc/testsuite/gcc.target/s390/hotpatch-5.c         |  21 +++
 gcc/testsuite/gcc.target/s390/hotpatch-6.c         |  21 +++
 gcc/testsuite/gcc.target/s390/hotpatch-7.c         |  21 +++
 gcc/testsuite/gcc.target/s390/hotpatch-8.c         |  28 +++
 gcc/testsuite/gcc.target/s390/hotpatch-9.c         |  21 +++
 gcc/testsuite/gcc.target/s390/hotpatch-compile-1.c |  27 +++
 gcc/testsuite/gcc.target/s390/hotpatch-compile-2.c |  27 +++
 gcc/testsuite/gcc.target/s390/hotpatch-compile-3.c |  27 +++
 gcc/testsuite/gcc.target/s390/hotpatch-compile-4.c |  11 ++
 gcc/testsuite/gcc.target/s390/hotpatch-compile-5.c |  28 +++
 gcc/testsuite/gcc.target/s390/hotpatch-compile-6.c |  11 ++
 gcc/testsuite/gcc.target/s390/hotpatch-compile-7.c |  68 +++++++
 25 files changed, 696 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/s390/hotpatch-1.c
 create mode 100644 gcc/testsuite/gcc.target/s390/hotpatch-10.c
 create mode 100644 gcc/testsuite/gcc.target/s390/hotpatch-11.c
 create mode 100644 gcc/testsuite/gcc.target/s390/hotpatch-12.c
 create mode 100644 gcc/testsuite/gcc.target/s390/hotpatch-2.c
 create mode 100644 gcc/testsuite/gcc.target/s390/hotpatch-3.c
 create mode 100644 gcc/testsuite/gcc.target/s390/hotpatch-4.c
 create mode 100644 gcc/testsuite/gcc.target/s390/hotpatch-5.c
 create mode 100644 gcc/testsuite/gcc.target/s390/hotpatch-6.c
 create mode 100644 gcc/testsuite/gcc.target/s390/hotpatch-7.c
 create mode 100644 gcc/testsuite/gcc.target/s390/hotpatch-8.c
 create mode 100644 gcc/testsuite/gcc.target/s390/hotpatch-9.c
 create mode 100644 gcc/testsuite/gcc.target/s390/hotpatch-compile-1.c
 create mode 100644 gcc/testsuite/gcc.target/s390/hotpatch-compile-2.c
 create mode 100644 gcc/testsuite/gcc.target/s390/hotpatch-compile-3.c
 create mode 100644 gcc/testsuite/gcc.target/s390/hotpatch-compile-4.c
 create mode 100644 gcc/testsuite/gcc.target/s390/hotpatch-compile-5.c
 create mode 100644 gcc/testsuite/gcc.target/s390/hotpatch-compile-6.c
 create mode 100644 gcc/testsuite/gcc.target/s390/hotpatch-compile-7.c

diff --git a/gcc/config/s390/s390-protos.h b/gcc/config/s390/s390-protos.h
index 67283df..7b43ed0 100644
--- a/gcc/config/s390/s390-protos.h
+++ b/gcc/config/s390/s390-protos.h
@@ -110,5 +110,6 @@  extern bool s390_decompose_shift_count (rtx, rtx *, HOST_WIDE_INT *);
 extern int s390_branch_condition_mask (rtx);
 extern int s390_compare_and_branch_condition_mask (rtx);
 extern bool s390_extzv_shift_ok (int, int, unsigned HOST_WIDE_INT);
+extern void s390_asm_output_function_label (FILE *, const char *, tree);
 
 #endif /* RTX_CODE */
diff --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c
index 6c69cca..6b49212 100644
--- a/gcc/config/s390/s390.c
+++ b/gcc/config/s390/s390.c
@@ -434,6 +434,65 @@  struct GTY(()) machine_function
    bytes on a z10 (or higher) CPU.  */
 #define PREDICT_DISTANCE (TARGET_Z10 ? 384 : 2048)
 
+static const int s390_hotpatch_trampoline_halfwords_default = 12;
+static const int s390_hotpatch_trampoline_halfwords_max = 1000000;
+static int s390_hotpatch_trampoline_halfwords = -1;
+
+/* Return the argument of the given hotpatch attribute or the default value if
+   no argument is present.  */
+
+static inline int
+get_hotpatch_attribute (tree hotpatch_attr)
+{
+  const_tree args;
+
+  args = TREE_VALUE (hotpatch_attr);
+
+  return (args) ?
+    TREE_INT_CST_LOW (TREE_VALUE (args)):
+    s390_hotpatch_trampoline_halfwords_default;
+}
+
+/* Check whether the hotpatch attribute is applied to a function and, if it has
+   an argument, the argument is valid.  */
+
+static tree
+s390_handle_hotpatch_attribute (tree *node, tree name, tree args,
+				int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+	       name);
+      *no_add_attrs = true;
+    }
+  else if (args)
+    {
+      tree expr = TREE_VALUE (args);
+
+      if (TREE_CODE (expr) != INTEGER_CST
+	  || !INTEGRAL_TYPE_P (TREE_TYPE (expr))
+	  || TREE_INT_CST_HIGH (expr) != 0
+	  || TREE_INT_CST_LOW (expr) > (unsigned int)
+	  s390_hotpatch_trampoline_halfwords_max)
+	{
+	  error ("requested %qE attribute is not a non-negative integer"
+		 " constant or too large (max. %d)", name,
+		 s390_hotpatch_trampoline_halfwords_max);
+	  *no_add_attrs = true;
+	}
+    }
+
+  return NULL_TREE;
+}
+
+static const struct attribute_spec s390_attribute_table[] = {
+  { "hotpatch", 0, 1, true, false, false, s390_handle_hotpatch_attribute, false
+  },
+  /* End element.  */
+  { NULL,        0, 0, false, false, false, NULL, false }
+};
+
 /* Return the alignment for LABEL.  We default to the -falign-labels
    value except for the literal pool base label.  */
 int
@@ -1622,6 +1681,46 @@  s390_init_machine_status (void)
 static void
 s390_option_override (void)
 {
+  unsigned int i;
+  cl_deferred_option *opt;
+  vec<cl_deferred_option> *v =
+    (vec<cl_deferred_option> *) s390_deferred_options;
+
+  if (v)
+    FOR_EACH_VEC_ELT (*v, i, opt)
+      {
+	switch (opt->opt_index)
+	  {
+	  case OPT_mhotpatch:
+	    s390_hotpatch_trampoline_halfwords = (opt->value) ?
+	      s390_hotpatch_trampoline_halfwords_default : -1;
+	    break;
+	  case OPT_mhotpatch_:
+	    {
+	      int val;
+
+	      val = integral_argument (opt->arg);
+	      if (val == -1)
+		{
+		  /* argument is not a plain number */
+		  error ("argument to %qs should be a non-negative integer",
+			 "-mhotpatch=");
+		  break;
+		}
+	      else if (val > s390_hotpatch_trampoline_halfwords_max)
+		{
+		  error ("argument to %qs is too large (max. %d)",
+			 "-mhotpatch=", s390_hotpatch_trampoline_halfwords_max);
+		  break;
+		}
+	      s390_hotpatch_trampoline_halfwords = val;
+	      break;
+	    }
+	  default:
+	    gcc_unreachable ();
+	  }
+      }
+
   /* Set up function hooks.  */
   init_machine_status = s390_init_machine_status;
 
@@ -5347,6 +5446,98 @@  get_some_local_dynamic_name (void)
   gcc_unreachable ();
 }
 
+/* Returns -1 if the function should not be made hotpatchable.  Otherwise it
+   returns a number >= 0 that is the desired size of the hotpatch trampoline
+   in halfwords. */
+
+static int s390_function_num_hotpatch_trampoline_halfwords (tree decl)
+{
+  tree attr;
+
+  gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
+  attr = lookup_attribute ("hotpatch", DECL_ATTRIBUTES (decl));
+  if (attr || s390_hotpatch_trampoline_halfwords >= 0)
+    {
+      if (MAIN_NAME_P (DECL_NAME (decl))
+	  || DECL_DECLARED_INLINE_P (decl)
+	  || lookup_attribute ("always_inline", DECL_ATTRIBUTES (decl))
+	  || DECL_ARTIFICIAL (decl))
+	{
+	  /* - Making the main function hotpatchable is useless.
+	     - Explicitly inlined functions cannot be hotpatched.
+	     - Artificial functions need not be hotpatched, so they may be
+	     inlined.
+	  */
+	  return -1;
+	}
+      else
+	{
+	  return (attr) ?
+	    get_hotpatch_attribute (attr) : s390_hotpatch_trampoline_halfwords;
+	}
+    }
+
+  return -1;
+}
+
+/* Hook to determine if one function can safely inline another.  */
+
+static bool
+s390_can_inline_p (tree caller, tree callee)
+{
+  if (s390_function_num_hotpatch_trampoline_halfwords (callee) >= 0)
+    return false;
+
+  return default_target_can_inline_p (caller, callee);
+}
+
+/* Write the extra assembler code needed to declare a function properly.  */
+
+void
+s390_asm_output_function_label (FILE *asm_out_file, const char *fname,
+				tree decl)
+{
+  int hotpatch_trampoline_halfwords = -1;
+
+  if (decl)
+    {
+      hotpatch_trampoline_halfwords =
+	s390_function_num_hotpatch_trampoline_halfwords (decl);
+      if (hotpatch_trampoline_halfwords >= 0
+	  && decl_function_context (decl) != NULL_TREE)
+	{
+	  warning_at (0, DECL_SOURCE_LOCATION (decl),
+		      "hotpatch_prologue is not compatible with nested"
+		      " function");
+	  hotpatch_trampoline_halfwords = -1;
+	}
+    }
+
+  if (hotpatch_trampoline_halfwords > 0)
+    {
+      int i;
+
+      /* Add a trampoline code area before the function label and initialize it
+	 with two-byte nop instructions.  This area can be overwritten with code
+	 that jumps to a patched version of the function.  */
+      for (i = 0; i < hotpatch_trampoline_halfwords; i++)
+	asm_fprintf (asm_out_file, "\tnopr\t%%r7\n");
+      /* Note:  The function label must be aligned so that (a) the bytes of the
+	 following nop do not cross a cacheline boundary, and (b) a jump address
+	 (eight bytes for 64 bit targets, 4 bytes for 32 bit targets) can be
+	 stored directly before the label without crossing a cacheline
+	 boundary.  All this is necessary to make sure the trampoline code can
+	 be changed atomically.  */
+    }
+
+  ASM_OUTPUT_LABEL (asm_out_file, fname);
+
+  /* Output a four-byte nop if hotpatching is enabled.  This can be overwritten
+     atomically with a relative backwards jump to the trampoline area.  */
+  if (hotpatch_trampoline_halfwords >= 0)
+    asm_fprintf (asm_out_file, "\tnop\t0\n");
+}
+
 /* Output machine-dependent UNSPECs occurring in address constant X
    in assembler syntax to stdio stream FILE.  Returns true if the
    constant X could be recognized, false otherwise.  */
@@ -11920,6 +12111,12 @@  s390_loop_unroll_adjust (unsigned nunroll, struct loop *loop)
 #undef TARGET_HARD_REGNO_SCRATCH_OK
 #define TARGET_HARD_REGNO_SCRATCH_OK s390_hard_regno_scratch_ok
 
+#undef TARGET_ATTRIBUTE_TABLE
+#define TARGET_ATTRIBUTE_TABLE s390_attribute_table
+
+#undef TARGET_CAN_INLINE_P
+#define TARGET_CAN_INLINE_P s390_can_inline_p
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 
 #include "gt-s390.h"
diff --git a/gcc/config/s390/s390.h b/gcc/config/s390/s390.h
index bca18fe..75b642b 100644
--- a/gcc/config/s390/s390.h
+++ b/gcc/config/s390/s390.h
@@ -217,7 +217,7 @@  enum processor_flags
 #define STACK_BOUNDARY 64
 
 /* Allocation boundary (in *bits*) for the code of a function.  */
-#define FUNCTION_BOUNDARY 32
+#define FUNCTION_BOUNDARY 64
 
 /* There is no point aligning anything to a rounder boundary than this.  */
 #define BIGGEST_ALIGNMENT 64
@@ -878,6 +878,9 @@  do {									\
   fputc ('\n', (FILE));							\
 } while (0)
 
+#undef ASM_OUTPUT_FUNCTION_LABEL
+#define ASM_OUTPUT_FUNCTION_LABEL(FILE, NAME, DECL) \
+  s390_asm_output_function_label (FILE, NAME, DECL)
 
 /* Miscellaneous parameters.  */
 
diff --git a/gcc/config/s390/s390.opt b/gcc/config/s390/s390.opt
index 7dedb83..ef92f46 100644
--- a/gcc/config/s390/s390.opt
+++ b/gcc/config/s390/s390.opt
@@ -96,6 +96,14 @@  mhard-float
 Target Report RejectNegative Negative(msoft-float) InverseMask(SOFT_FLOAT, HARD_FLOAT)
 Enable hardware floating point
 
+mhotpatch
+Target Report Var(s390_deferred_options) Defer
+Prepend the function label with 12 two-byte Nop instructions, and add a four byte Nop instruction after the label for hotpatching.
+
+mhotpatch=
+Target RejectNegative Report Joined Var(s390_deferred_options) Defer
+Prepend the function label with the given number of two-byte Nop instructions, and add a four byte Nop instruction after the label for hotpatching.
+
 mlong-double-128
 Target Report RejectNegative Negative(mlong-double-64) Mask(LONG_DOUBLE_128)
 Use 128-bit long double
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index da2c63e..d2b9167 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -3259,6 +3259,17 @@  this function attribute to make GCC generate the ``hot-patching'' function
 prologue used in Win32 API functions in Microsoft Windows XP Service Pack 2
 and newer.
 
+@item hotpatch [(@var{prologue-halfwords})]
+@cindex @code{hotpatch} attribute
+
+On S/390 System z targets, you can use this function attribute to
+make GCC generate a ``hot-patching'' function prologue.  The
+@code{hotpatch} has no effect on funtions that are explicitly
+inline.  If the @option{-mhotpatch} or @option{-mno-hotpatch}
+command-line option is used at the same time, the @code{hotpatch}
+attribute takes precedence.  If an argument is given, the maximum
+allowed value is 1000000.
+
 @item naked
 @cindex function without a prologue/epilogue code
 Use this attribute on the ARM, AVR, MCORE, MSP430, NDS32, RL78, RX and SPU
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index b30e889..afd35c6 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -933,7 +933,8 @@  See RS/6000 and PowerPC Options.
 -msmall-exec  -mno-small-exec  -mmvcle -mno-mvcle @gol
 -m64  -m31  -mdebug  -mno-debug  -mesa  -mzarch @gol
 -mtpf-trace -mno-tpf-trace  -mfused-madd  -mno-fused-madd @gol
--mwarn-framesize  -mwarn-dynamicstack  -mstack-size -mstack-guard}
+-mwarn-framesize  -mwarn-dynamicstack  -mstack-size -mstack-guard @gol
+-mhotpatch[=@var{halfwords}] -mno-hotpatch}
 
 @emph{Score Options}
 @gccoptlist{-meb -mel @gol
@@ -19721,6 +19722,21 @@  values have to be exact powers of 2 and @var{stack-size} has to be greater than
 In order to be efficient the extra code makes the assumption that the stack starts
 at an address aligned to the value given by @var{stack-size}.
 The @var{stack-guard} option can only be used in conjunction with @var{stack-size}.
+
+@item -mhotpatch[=@var{halfwords}]
+@itemx -mno-hotpatch
+@opindex mhotpatch
+If the hotpatch option is enabled, a ``hot-patching'' function
+prologue is generated for all functions in the compilation unit.
+The funtion label is prepended with the given number of two-byte
+Nop instructions (@var{halfwords}, maximum 1000000) or 12 Nop
+instructions if no argument is present.  Functions with a
+hot-patching prologue are never inlined automatically, and a
+hot-patching prologue is never generated for functions functions
+that are explicitly inline.
+
+This option can be overridden for individual functions with the
+@code{hotpatch} attribute.
 @end table
 
 @node Score Options
diff --git a/gcc/testsuite/gcc.target/s390/hotpatch-1.c b/gcc/testsuite/gcc.target/s390/hotpatch-1.c
new file mode 100644
index 0000000..66b9fee
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/hotpatch-1.c
@@ -0,0 +1,20 @@ 
+/* Functional tests for the function hotpatching feature.  */
+
+/* { dg-do run } */
+/* { dg-options "-O3 -mzarch -mhotpatch" } */
+
+#include <stdio.h>
+
+static void hp1(void)
+{
+  printf("hello, world!\n");
+}
+
+int main (void)
+{
+  return 0;
+}
+
+/* Check number of occurences of certain instructions.  */
+/* { dg-final { scan-assembler-times "nopr\t%r7" 12 } } */
+/* { dg-final { scan-assembler-times "nop\t0" 1 } } */
diff --git a/gcc/testsuite/gcc.target/s390/hotpatch-10.c b/gcc/testsuite/gcc.target/s390/hotpatch-10.c
new file mode 100644
index 0000000..98ec22a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/hotpatch-10.c
@@ -0,0 +1,21 @@ 
+/* Functional tests for the function hotpatching feature.  */
+
+/* { dg-do run } */
+/* { dg-options "-O3 -mzarch -mno-hotpatch" } */
+
+#include <stdio.h>
+
+__attribute__ ((hotpatch(2)))
+static void hp1(void)
+{
+  printf("hello, world!\n");
+}
+
+int main (void)
+{
+  return 0;
+}
+
+/* Check number of occurences of certain instructions.  */
+/* { dg-final { scan-assembler-times "nopr\t%r7" 2 } } */
+/* { dg-final { scan-assembler-times "nop\t0" 1 } } */
diff --git a/gcc/testsuite/gcc.target/s390/hotpatch-11.c b/gcc/testsuite/gcc.target/s390/hotpatch-11.c
new file mode 100644
index 0000000..4814817
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/hotpatch-11.c
@@ -0,0 +1,20 @@ 
+/* Functional tests for the function hotpatching feature.  */
+
+/* { dg-do run } */
+/* { dg-options "-O3 -mzarch -mhotpatch -mno-hotpatch" } */
+
+#include <stdio.h>
+
+static void hp1(void)
+{
+  printf("hello, world!\n");
+}
+
+int main (void)
+{
+  return 0;
+}
+
+/* Check number of occurences of certain instructions.  */
+/* { dg-final { scan-assembler-not "nopr\t%r7" } } */
+/* { dg-final { scan-assembler-not "nop\t0" } } */
diff --git a/gcc/testsuite/gcc.target/s390/hotpatch-12.c b/gcc/testsuite/gcc.target/s390/hotpatch-12.c
new file mode 100644
index 0000000..c8c1c6b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/hotpatch-12.c
@@ -0,0 +1,20 @@ 
+/* Functional tests for the function hotpatching feature.  */
+
+/* { dg-do run } */
+/* { dg-options "-O3 -mzarch -mno-hotpatch -mhotpatch=1" } */
+
+#include <stdio.h>
+
+static void hp1(void)
+{
+  printf("hello, world!\n");
+}
+
+int main (void)
+{
+  return 0;
+}
+
+/* Check number of occurences of certain instructions.  */
+/* { dg-final { scan-assembler-times "nopr\t%r7" 1 } } */
+/* { dg-final { scan-assembler-times "nop\t0" 1 } } */
diff --git a/gcc/testsuite/gcc.target/s390/hotpatch-2.c b/gcc/testsuite/gcc.target/s390/hotpatch-2.c
new file mode 100644
index 0000000..c41ba82
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/hotpatch-2.c
@@ -0,0 +1,20 @@ 
+/* Functional tests for the function hotpatching feature.  */
+
+/* { dg-do run } */
+/* { dg-options "-O3 -mzarch -mhotpatch=1" } */
+
+#include <stdio.h>
+
+static void hp1(void)
+{
+  printf("hello, world!\n");
+}
+
+int main (void)
+{
+  return 0;
+}
+
+/* Check number of occurences of certain instructions.  */
+/* { dg-final { scan-assembler-times "nopr\t%r7" 1 } } */
+/* { dg-final { scan-assembler-times "nop\t0" 1 } } */
diff --git a/gcc/testsuite/gcc.target/s390/hotpatch-3.c b/gcc/testsuite/gcc.target/s390/hotpatch-3.c
new file mode 100644
index 0000000..ebd96f8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/hotpatch-3.c
@@ -0,0 +1,20 @@ 
+/* Functional tests for the function hotpatching feature.  */
+
+/* { dg-do run } */
+/* { dg-options "-O3 -mzarch -mhotpatch=0" } */
+
+#include <stdio.h>
+
+static void hp1(void)
+{
+  printf("hello, world!\n");
+}
+
+int main (void)
+{
+  return 0;
+}
+
+/* Check number of occurences of certain instructions.  */
+/* { dg-final { scan-assembler-not "nopr\t%r7" } } */
+/* { dg-final { scan-assembler-times "nop\t0" 1 } } */
diff --git a/gcc/testsuite/gcc.target/s390/hotpatch-4.c b/gcc/testsuite/gcc.target/s390/hotpatch-4.c
new file mode 100644
index 0000000..cbcccfb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/hotpatch-4.c
@@ -0,0 +1,26 @@ 
+/* Functional tests for the function hotpatching feature.  */
+
+/* { dg-do run } */
+/* { dg-options "-O3 -mzarch -mhotpatch" } */
+
+#include <stdio.h>
+
+static inline void hp1(void)
+{
+  printf("hello, world!\n");
+}
+
+__attribute__ ((always_inline))
+static void hp2(void) /* { dg-warning "always_inline function might not be inlinable" } */
+{
+  printf("hello, world!\n");
+}
+
+int main (void)
+{
+  return 0;
+}
+
+/* Check number of occurences of certain instructions.  */
+/* { dg-final { scan-assembler-not "nopr\t%r7" } } */
+/* { dg-final { scan-assembler-not "nop\t0" } } */
diff --git a/gcc/testsuite/gcc.target/s390/hotpatch-5.c b/gcc/testsuite/gcc.target/s390/hotpatch-5.c
new file mode 100644
index 0000000..9543cdb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/hotpatch-5.c
@@ -0,0 +1,21 @@ 
+/* Functional tests for the function hotpatching feature.  */
+
+/* { dg-do run } */
+/* { dg-options "-O3 -mzarch -mhotpatch" } */
+
+#include <stdio.h>
+
+__attribute__ ((hotpatch))
+static void hp1(void)
+{
+  printf("hello, world!\n");
+}
+
+int main (void)
+{
+  return 0;
+}
+
+/* Check number of occurences of certain instructions.  */
+/* { dg-final { scan-assembler-times "nopr\t%r7" 12 } } */
+/* { dg-final { scan-assembler-times "nop\t0" 1 } } */
diff --git a/gcc/testsuite/gcc.target/s390/hotpatch-6.c b/gcc/testsuite/gcc.target/s390/hotpatch-6.c
new file mode 100644
index 0000000..252afb7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/hotpatch-6.c
@@ -0,0 +1,21 @@ 
+/* Functional tests for the function hotpatching feature.  */
+
+/* { dg-do run } */
+/* { dg-options "-O3 -mzarch -mhotpatch" } */
+
+#include <stdio.h>
+
+__attribute__ ((hotpatch(1)))
+static void hp1(void)
+{
+  printf("hello, world!\n");
+}
+
+int main (void)
+{
+  return 0;
+}
+
+/* Check number of occurences of certain instructions.  */
+/* { dg-final { scan-assembler-times "nopr\t%r7" 1 } } */
+/* { dg-final { scan-assembler-times "nop\t0" 1 } } */
diff --git a/gcc/testsuite/gcc.target/s390/hotpatch-7.c b/gcc/testsuite/gcc.target/s390/hotpatch-7.c
new file mode 100644
index 0000000..a4b101b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/hotpatch-7.c
@@ -0,0 +1,21 @@ 
+/* Functional tests for the function hotpatching feature.  */
+
+/* { dg-do run } */
+/* { dg-options "-O3 -mzarch -mhotpatch" } */
+
+#include <stdio.h>
+
+__attribute__ ((hotpatch(0)))
+static void hp1(void)
+{
+  printf("hello, world!\n");
+}
+
+int main (void)
+{
+  return 0;
+}
+
+/* Check number of occurences of certain instructions.  */
+/* { dg-final { scan-assembler-not "nopr\t%r7" } } */
+/* { dg-final { scan-assembler-times "nop\t0" 1 } } */
diff --git a/gcc/testsuite/gcc.target/s390/hotpatch-8.c b/gcc/testsuite/gcc.target/s390/hotpatch-8.c
new file mode 100644
index 0000000..c7a3188
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/hotpatch-8.c
@@ -0,0 +1,28 @@ 
+/* Functional tests for the function hotpatching feature.  */
+
+/* { dg-do run } */
+/* { dg-options "-O3 -mzarch -mhotpatch" } */
+
+#include <stdio.h>
+
+__attribute__ ((hotpatch))
+static inline void hp1(void)
+{
+  printf("hello, world!\n");
+}
+
+__attribute__ ((hotpatch))
+__attribute__ ((always_inline))
+static void hp2(void) /* { dg-warning "always_inline function might not be inlinable" } */
+{
+  printf("hello, world!\n");
+}
+
+int main (void)
+{
+  return 0;
+}
+
+/* Check number of occurences of certain instructions.  */
+/* { dg-final { scan-assembler-not "nopr\t%r7" } } */
+/* { dg-final { scan-assembler-not "nop\t0" } } */
diff --git a/gcc/testsuite/gcc.target/s390/hotpatch-9.c b/gcc/testsuite/gcc.target/s390/hotpatch-9.c
new file mode 100644
index 0000000..cd80e1d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/hotpatch-9.c
@@ -0,0 +1,21 @@ 
+/* Functional tests for the function hotpatching feature.  */
+
+/* { dg-do run } */
+/* { dg-options "-O3 -mzarch -mhotpatch=1" } */
+
+#include <stdio.h>
+
+__attribute__ ((hotpatch(2)))
+static void hp1(void)
+{
+  printf("hello, world!\n");
+}
+
+int main (void)
+{
+  return 0;
+}
+
+/* Check number of occurences of certain instructions.  */
+/* { dg-final { scan-assembler-times "nopr\t%r7" 2 } } */
+/* { dg-final { scan-assembler-times "nop\t0" 1 } } */
diff --git a/gcc/testsuite/gcc.target/s390/hotpatch-compile-1.c b/gcc/testsuite/gcc.target/s390/hotpatch-compile-1.c
new file mode 100644
index 0000000..dba39a6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/hotpatch-compile-1.c
@@ -0,0 +1,27 @@ 
+/* Functional tests for the function hotpatching feature.  */
+
+/* { dg-do run } */
+/* { dg-options "-O3 -mzarch -mhotpatch" } */
+
+#include <stdio.h>
+
+static void hp1(void)
+{
+  printf("hello, world!\n");
+}
+
+static inline void hp2(void)
+{
+  printf("hello, world!\n");
+}
+
+__attribute__ ((always_inline))
+static void hp3(void) /* { dg-warning "always_inline function might not be inlinable" } */
+{
+  printf("hello, world!\n");
+}
+
+int main (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/s390/hotpatch-compile-2.c b/gcc/testsuite/gcc.target/s390/hotpatch-compile-2.c
new file mode 100644
index 0000000..0a8727a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/hotpatch-compile-2.c
@@ -0,0 +1,27 @@ 
+/* Functional tests for the function hotpatching feature.  */
+
+/* { dg-do run } */
+/* { dg-options "-O3 -mzarch -mhotpatch=0" } */
+
+#include <stdio.h>
+
+static void hp1(void)
+{
+  printf("hello, world!\n");
+}
+
+static inline void hp2(void)
+{
+  printf("hello, world!\n");
+}
+
+__attribute__ ((always_inline))
+static void hp3(void) /* { dg-warning "always_inline function might not be inlinable" } */
+{
+  printf("hello, world!\n");
+}
+
+int main (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/s390/hotpatch-compile-3.c b/gcc/testsuite/gcc.target/s390/hotpatch-compile-3.c
new file mode 100644
index 0000000..462c36f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/hotpatch-compile-3.c
@@ -0,0 +1,27 @@ 
+/* Functional tests for the function hotpatching feature.  */
+
+/* { dg-do run } */
+/* { dg-options "-O3 -mzarch -mhotpatch=1" } */
+
+#include <stdio.h>
+
+static void hp1(void)
+{
+  printf("hello, world!\n");
+}
+
+static inline void hp2(void)
+{
+  printf("hello, world!\n");
+}
+
+__attribute__ ((always_inline))
+static void hp3(void) /* { dg-warning "always_inline function might not be inlinable" } */
+{
+  printf("hello, world!\n");
+}
+
+int main (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/s390/hotpatch-compile-4.c b/gcc/testsuite/gcc.target/s390/hotpatch-compile-4.c
new file mode 100644
index 0000000..80ac24a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/hotpatch-compile-4.c
@@ -0,0 +1,11 @@ 
+/* Functional tests for the function hotpatching feature.  */
+
+/* { dg-do run } */
+/* { dg-options "-O3 -mzarch -mhotpatch=-1" } */
+
+int main (void)
+{
+  return 0;
+}
+
+/* { dg-excess-errors "argument to '-mhotpatch=' should be a non-negative integer" } */
diff --git a/gcc/testsuite/gcc.target/s390/hotpatch-compile-5.c b/gcc/testsuite/gcc.target/s390/hotpatch-compile-5.c
new file mode 100644
index 0000000..4b4d91b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/hotpatch-compile-5.c
@@ -0,0 +1,28 @@ 
+/* Functional tests for the function hotpatching feature.  */
+
+/* { dg-do run } */
+/* { dg-options "-O3 -mzarch -mhotpatch=1000000" } */
+
+#include <stdio.h>
+
+static void hp1(void)
+{
+  printf("hello, world!\n");
+}
+
+__attribute__ ((hotpatch(1000000)))
+static void hp2(void)
+{
+  printf("hello, world!\n");
+}
+
+__attribute__ ((hotpatch(1000001)))
+static void hp3(void)
+{ /* { dg-error "requested 'hotpatch' attribute is not a non-negative integer constant or too large .max. 1000000." } */
+  printf("hello, world!\n");
+}
+
+int main (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/s390/hotpatch-compile-6.c b/gcc/testsuite/gcc.target/s390/hotpatch-compile-6.c
new file mode 100644
index 0000000..1217e84
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/hotpatch-compile-6.c
@@ -0,0 +1,11 @@ 
+/* Functional tests for the function hotpatching feature.  */
+
+/* { dg-do run } */
+/* { dg-options "-O3 -mzarch -mhotpatch=1000001" } */
+
+int main (void)
+{
+  return 0;
+}
+
+/* { dg-excess-errors "argument to '-mhotpatch=' is too large .max. 1000000." } */
diff --git a/gcc/testsuite/gcc.target/s390/hotpatch-compile-7.c b/gcc/testsuite/gcc.target/s390/hotpatch-compile-7.c
new file mode 100644
index 0000000..3941684
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/hotpatch-compile-7.c
@@ -0,0 +1,68 @@ 
+/* Functional tests for the function hotpatching feature.  */
+
+/* { dg-do run } */
+/* { dg-options "-O3 -mzarch -mno-hotpatch" } */
+
+#include <stdio.h>
+
+__attribute__ ((hotpatch))
+static void hp1(void)
+{
+  printf("hello, world!\n");
+}
+
+__attribute__ ((hotpatch))
+static inline void hp2(void)
+{
+  printf("hello, world!\n");
+}
+
+__attribute__ ((hotpatch))
+__attribute__ ((always_inline))
+static void hp3(void) /* { dg-warning "always_inline function might not be inlinable" } */
+{
+  printf("hello, world!\n");
+}
+
+__attribute__ ((hotpatch(0)))
+static void hp4(void)
+{
+  printf("hello, world!\n");
+}
+
+__attribute__ ((hotpatch(0)))
+static inline void hp5(void)
+{
+  printf("hello, world!\n");
+}
+
+__attribute__ ((hotpatch(0)))
+__attribute__ ((always_inline))
+static void hp6(void) /* { dg-warning "always_inline function might not be inlinable" } */
+{
+  printf("hello, world!\n");
+}
+
+__attribute__ ((hotpatch(1)))
+static void hp7(void)
+{
+  printf("hello, world!\n");
+}
+
+__attribute__ ((hotpatch(1)))
+static inline void hp8(void)
+{
+  printf("hello, world!\n");
+}
+
+__attribute__ ((hotpatch(1)))
+__attribute__ ((always_inline))
+static void hp9(void) /* { dg-warning "always_inline function might not be inlinable" } */
+{
+  printf("hello, world!\n");
+}
+
+int main (void)
+{
+  return 0;
+}
-- 
1.8.3.1