public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] x86 interrupt attribute
@ 2015-09-29 14:21 Yulia Koval
  2015-09-29 19:49 ` Mike Stump
  0 siblings, 1 reply; 45+ messages in thread
From: Yulia Koval @ 2015-09-29 14:21 UTC (permalink / raw)
  To: gcc-patches; +Cc: ubizjak, hongjiu.lu

[-- Attachment #1: Type: text/plain, Size: 7876 bytes --]

Hi,



The patch below implements interrupt attribute for x86 processors.



The interrupt and exception handlers are called by x86 processors.
X86 hardware pushes information onto stack and calls the handler.  The
requirements are



1. Both interrupt and exception handlers must use the 'IRET'
instruction, instead of the 'RET' instruction, to return from the
handlers.

2. All registers are callee-saved in interrupt and exception handlers.

3. The difference between interrupt and exception handlers is the
exception handler must pop 'ERROR_CODE' off the stack before the
'IRET'

instruction.



The design goals of interrupt and exception handlers for x86 processors

are:



1. Support both 32-bit and 64-bit modes.

2. Flexible for compilers to optimize.

3. Easy to use by programmers.



To implement interrupt and exception handlers for x86 processors, a
compiler should support:



'interrupt' attribute



Use this attribute to indicate that the specified function with
mandatory arguments is an interrupt or exception handler.  The
compiler generates function entry and exit sequences suitable for use
in an interrupt handler when this attribute is present.  The 'IRET'
instruction, instead of the 'RET' instruction, is used to return from
interrupt or exception handlers.  All registers, except for the EFLAGS
register which is restored by the 'IRET' instruction, are preserved by
the compiler.



Any interruptible-without-stack-switch code must be compiled with
-mno-red-zone since interrupt handlers can and will, because of the
hardware design, touch the red zone.



1. interrupt handler must be declared with a mandatory pointer argument:



struct interrupt_frame;



__attribute__ ((interrupt))

void

f (struct interrupt_frame *frame)

{

...

}



and user must properly define the structure the pointer pointing to.



2. exception handler:



The exception handler is very similar to the interrupt handler with a
different mandatory function signature:



typedef unsigned long long int uword_t;

typedef unsigned int uword_t;



struct interrupt_frame;



__attribute__ ((interrupt))

void

f (struct interrupt_frame *frame, uword_t error_code) { ...

}



and compiler pops the error code off stack before the 'IRET' instruction.



The exception handler should only be used for exceptions which push an
error code and all other exceptions must use the interrupt handler.

The system will crash if the wrong handler is used.



Bootstrapped/regtested on Linux/x86_64 and Linux/i686.

Ok for trunk?



2015-09-29  Julia Koval <jkoval@intel.com>

                        H.J. Lu <hongjiu.lu@intel.com>



                PR target/67630

                PR target/67634

                * config/i386/i386-protos.h (ix86_interrupt_return_nregs): New.

                * config/i386/i386.c (ix86_frame): Add nbndregs and nmaskregs.

                (ix86_interrupt_return_nregs): New variable.

                (ix86_nsaved_bndregs): New function.

                (ix86_nsaved_maskregs): Likewise.

                (ix86_reg_save_area_size): Likewise.

                (ix86_nsaved_sseregs): Don't return 0 in interrupt handler.

                (ix86_compute_frame_layout): Set nbndregs and nmaskregs.  Set

                save_regs_using_mov to true to save bound and mask registers.

                Call ix86_reg_save_area_size to get register save area size.

                Allocate space to save full vector registers in
interrupt handler.

                (ix86_emit_save_reg_using_mov): Set alignment to word_mode

                alignment when saving full vector registers in
interrupt handler.

                (ix86_emit_save_regs_using_mov): Use regno_reg_rtx to get

                register size.

                (ix86_emit_restore_regs_using_mov): Likewise.

                (ix86_emit_save_sse_regs_using_mov): Save full vector
registers in

                interrupt handler.

                (ix86_emit_restore_sse_regs_using_mov): Restore full vector

                registers in interrupt handler.

                (ix86_expand_epilogue): Use move to restore bound registers.

                * config/i386/sse.md (*mov<mode>_internal): Handle misaligned

                SSE load and store in interrupt handler.



                PR target/66960

                * config/i386/i386.c (ix86_conditional_register_usage): Set

                ix86_interrupt_return_nregs/

                (ix86_set_current_function): Set is_interrupt and is_exception.

                Mark arguments in interrupt handler as used.

                (ix86_function_ok_for_sibcall): Return false if in interrupt

                handler.

                (type_natural_mode): Don't warn ABI change for MMX in interrupt

                handler.

                (ix86_function_arg_advance): Skip for callee in interrupt

                handler.

                (ix86_function_arg): Handle arguments for callee in interrupt

                handler.

                (ix86_can_use_return_insn_p): Don't use `ret' instruction in

                interrupt handler.

                (ix86_save_reg): Preserve callee-saved and
caller-saved registers

                in interrupt handler if needed.

                (ix86_expand_epilogue): Generate interrupt return for

                interrupt handler and pop the 'ERROR_CODE' off the stack before

                interrupt return in exception handler.

                (ix86_expand_move): Disallow 80387 instructions in exception

                handler.

                (ix86_expand_vector_move): Disallow MMX/3Dnow instructions in

                exception handler.

                (ix86_expand_call): Set make_calls.

                (ix86_handle_interrupt_attribute): New function.

                (ix86_attribute_table): Add interrupt attribute.

                * config/i386/i386.h (machine_function): Add is_interrupt,

                is_exception, make_calls and  interrupt_data_taken.

                * config/i386/i386.md (UNSPEC_INTERRUPT_RETURN): New.

                (*interrupt_return): New pattern.

                * config/i386/predicates.md (interrupt_return_operation): New.

                * doc/extend.texi: Document x86 interrupt attribute.



gcc/testsuite/



                PR target/66960

                PR target/67630

                PR target/67634

                * gcc.target/i386/interrupt-1.c: New test.

                * gcc.target/i386/interrupt-2.c: Likewise.

                * gcc.target/i386/interrupt-3.c: Likewise.

                * gcc.target/i386/interrupt-4.c: Likewise.

                * gcc.target/i386/interrupt-5.c: Likewise.

                * gcc.target/i386/interrupt-6.c: Likewise.

                * gcc.target/i386/interrupt-7.c: Likewise.

                * gcc.target/i386/interrupt-8.c: Likewise.

                * gcc.target/i386/interrupt-9.c: Likewise.

                * gcc.target/i386/interrupt-10.c: Likewise.

                * gcc.target/i386/interrupt-11.c: Likewise.

                * gcc.target/i386/interrupt-12.c: Likewise.

                * gcc.target/i386/interrupt-13.c: Likewise.

                * gcc.target/i386/interrupt-387-err.c: Likewise.

                * gcc.target/i386/interrupt-bnd.c: Likewise.

                * gcc.target/i386/interrupt-iamcu.c: Likewise.

                * gcc.target/i386/interrupt-mmx-err.c: Likewise.

                * gcc.target/i386/interrupt-redzone-1.c: Likewise.

                * gcc.target/i386/interrupt-redzone-2.c: Likewise.

                * gcc.target/i386/interrupt-sibcall.c: Likewise.

                * gcc.target/i386/interrupt-switch-abi.c: Likewise.

                * gcc.target/i386/interrupt-xmm.c: Likewise.

                * gcc.target/i386/interrupt-ymm.c: Likewise.

                * gcc.target/i386/interrupt-zmm.c: Likewise.

[-- Attachment #2: interrupt-attribute-x86.patch --]
[-- Type: application/octet-stream, Size: 50405 bytes --]

diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index 6a17ef4..ea9f70a 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -278,6 +278,8 @@ extern rtx maybe_get_pool_constant (rtx);
 extern char internal_label_prefix[16];
 extern int internal_label_prefix_len;
 
+extern unsigned int ix86_interrupt_return_nregs;
+
 enum ix86_address_seg { SEG_DEFAULT, SEG_FS, SEG_GS };
 struct ix86_address
 {
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index d370521..b7a51e3 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -2380,6 +2380,8 @@ struct ix86_frame
 {
   int nsseregs;
   int nregs;
+  int nbndregs;
+  int nmaskregs;
   int va_arg_size;
   int red_zone_size;
   int outgoing_arguments_size;
@@ -4502,6 +4504,10 @@ ix86_offload_options (void)
   return xstrdup ("-foffload-abi=ilp32");
 }
 
+/* Number registers which must be preserved for interrupt return.  */
+
+unsigned int ix86_interrupt_return_nregs;
+
 /* Update register usage after having seen the compiler flags.  */
 
 static void
@@ -4573,6 +4579,22 @@ ix86_conditional_register_usage (void)
   if (! TARGET_MPX)
     for (i = FIRST_BND_REG; i <= LAST_BND_REG; i++)
       fixed_regs[i] = call_used_regs[i] = 1, reg_names[i] = "";
+
+  /* All integer and vector registers, except for MMX and x87
+     registers which aren't supported in ix86_compute_frame_layout
+     when saving and restoring registers, are preserved in
+     interrupt handler.  No need to preserve BP and SP registers
+     since they are always preserved.  */
+  unsigned int n = 0;
+  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+    if (reg_names[i][0]
+	&& !STACK_REGNO_P (i)
+	&& !MMX_REGNO_P (i)
+	&& i != BP_REG
+	&& i != SP_REG
+	&& (i <= ST7_REG || i >= XMM0_REG))
+      n++;
+  ix86_interrupt_return_nregs = n;
 }
 
 \f
@@ -5290,6 +5312,24 @@ ix86_set_current_function (tree fndecl)
       return;
     }
 
+  if (lookup_attribute ("interrupt", DECL_ATTRIBUTES (fndecl)))
+    {
+      cfun->machine->is_interrupt = true;
+      int nargs = 0;
+      for (tree arg = DECL_ARGUMENTS (fndecl);
+	   arg;
+	   arg = TREE_CHAIN (arg))
+	{
+	  /* Mark arguments in interrupt handler as used to silence
+	     compiler warning since arguments in interrupt handler
+	     are mandatory even if they aren't used.  */
+	  TREE_USED (arg) = 1;
+	  nargs++;
+	}
+      if (nargs == 2)
+        cfun->machine->is_exception = true;
+    }
+
   tree new_tree = DECL_FUNCTION_SPECIFIC_TARGET (fndecl);
   if (new_tree == NULL_TREE)
     new_tree = target_option_default_node;
@@ -5568,6 +5608,11 @@ ix86_function_ok_for_sibcall (tree decl, tree exp)
   tree type, decl_or_type;
   rtx a, b;
 
+  /* Sibling call isn't OK in interrupt handler since it must return
+     with the "IRET" instruction.  */
+  if (cfun->machine->is_interrupt)
+    return false;
+
   /* If we are generating position-independent code, we cannot sibcall
      optimize direct calls to global functions, as the PLT requires
      %ebx be live. (Darwin does not have a PLT.)  */
@@ -6752,6 +6797,7 @@ type_natural_mode (const_tree type, const CUMULATIVE_ARGS *cum,
 		      }
 		  }
 		else if ((size == 8 && !TARGET_64BIT)
+			 && (!cfun || !cfun->machine->is_interrupt)
 			 && !TARGET_MMX
 			 && !TARGET_IAMCU)
 		  {
@@ -7731,6 +7777,11 @@ ix86_function_arg_advance (cumulative_args_t cum_v, machine_mode mode,
   HOST_WIDE_INT bytes, words;
   int nregs;
 
+  /* The argument of interrupt handler is a special case and is
+     handled in ix86_function_arg.  */
+  if (!cum->caller && cfun->machine->is_interrupt)
+    return;
+
   if (mode == BLKmode)
     bytes = int_size_in_bytes (type);
   else
@@ -8047,6 +8098,39 @@ ix86_function_arg (cumulative_args_t cum_v, machine_mode omode,
   HOST_WIDE_INT bytes, words;
   rtx arg;
 
+  if (!cum->caller && cfun->machine->is_interrupt)
+    {
+      /* The first argument of interrupt handler is a pointer and
+	 points to the return address on stack.  The optional second
+	 argument is an integer for error code on stack.  */
+      gcc_assert (type != NULL_TREE);
+      if (POINTER_TYPE_P (type))
+	{
+	  if (cfun->machine->is_exception)
+	    /* (AP) in the current frame in exception handler.  */
+	    arg = arg_pointer_rtx;
+	  else
+	    /* -WORD(AP) in the current frame in interrupt handler.  */
+	    arg = plus_constant (Pmode, arg_pointer_rtx,
+				 -UNITS_PER_WORD);
+	  if (mode != Pmode)
+	    arg = convert_to_mode (mode, arg, 1);
+	}
+      else
+	{
+	  gcc_assert (TREE_CODE (type) == INTEGER_TYPE
+		      && cfun->machine->is_exception
+		      && mode == word_mode);
+	  /* The error code is at -WORD(AP) in the current frame in
+	     exception handler.  */
+	  arg = gen_rtx_MEM (word_mode,
+			     plus_constant (Pmode, arg_pointer_rtx,
+					    -UNITS_PER_WORD));
+	}
+
+      return arg;
+    }
+
   /* All pointer bounds argumntas are handled separately here.  */
   if ((type && POINTER_BOUNDS_TYPE_P (type))
       || POINTER_BOUNDS_MODE_P (mode))
@@ -9762,7 +9846,10 @@ ix86_can_use_return_insn_p (void)
 {
   struct ix86_frame frame;
 
-  if (! reload_completed || frame_pointer_needed)
+  /* Don't use `ret' instruction in interrupt handler.  */
+  if (! reload_completed
+      || frame_pointer_needed
+      || cfun->machine->is_interrupt)
     return 0;
 
   /* Don't allow more than 32k pop, since that's all we can do
@@ -10077,6 +10164,23 @@ ix86_select_alt_pic_regnum (void)
 static bool
 ix86_save_reg (unsigned int regno, bool maybe_eh_return)
 {
+  /* In interrupt handler, we don't preserve MMX and x87 registers
+     which aren't supported when saving and restoring registers.  No
+     need to preserve callee-saved registers unless they are modified.
+     We also preserve all caller-saved registers if a function call
+     is made in interrupt handler since the called function may change
+     them.  Don't explicitly save BP and SP registers since they are
+     always preserved.  */
+  if (cfun->machine->is_interrupt)
+    return ((df_regs_ever_live_p (regno)
+	     || (call_used_regs[regno] && cfun->machine->make_calls))
+	    && !fixed_regs[regno]
+	    && !STACK_REGNO_P (regno)
+	    && !MMX_REGNO_P (regno)
+	    && regno != BP_REG
+	    && regno != SP_REG
+	    && (regno <= ST7_REG || regno >= XMM0_REG));
+
   if (regno == REAL_PIC_OFFSET_TABLE_REGNUM
       && pic_offset_table_rtx)
     {
@@ -10133,6 +10237,34 @@ ix86_nsaved_regs (void)
   return nregs;
 }
 
+/* Return number of saved bound registers.  */
+
+static int
+ix86_nsaved_bndregs (void)
+{
+  int nregs = 0;
+  int regno;
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (BND_REGNO_P (regno) && ix86_save_reg (regno, true))
+      nregs ++;
+  return nregs;
+}
+
+/* Return number of saved mask registers.  */
+
+static int
+ix86_nsaved_maskregs (void)
+{
+  int nregs = 0;
+  int regno;
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (MASK_REGNO_P (regno) && ix86_save_reg (regno, true))
+      nregs ++;
+  return nregs;
+}
+
 /* Return number of saved SSE registrers.  */
 
 static int
@@ -10141,7 +10273,8 @@ ix86_nsaved_sseregs (void)
   int nregs = 0;
   int regno;
 
-  if (!TARGET_64BIT_MS_ABI)
+  /* Always need to save SSE registrers in interrupt handler.  */
+  if (!TARGET_64BIT_MS_ABI && !cfun->machine->is_interrupt)
     return 0;
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
@@ -10210,6 +10343,18 @@ ix86_builtin_setjmp_frame_value (void)
 
 #define SPLIT_STACK_AVAILABLE 256
 
+/* Register save area size.  */
+
+static unsigned int
+ix86_reg_save_area_size (struct ix86_frame *frame)
+{
+  unsigned int size = ((frame->nregs - frame->nbndregs
+			- frame->nmaskregs) * UNITS_PER_WORD);
+  size += frame->nbndregs * (ix86_pmode == PMODE_DI ? 16 : 8);
+  size += frame->nmaskregs * (TARGET_AVX512BW ? 8 : 2);
+  return size;
+}
+
 /* Fill structure ix86_frame about frame of currently computed function.  */
 
 static void
@@ -10222,6 +10367,8 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   HOST_WIDE_INT to_allocate;
 
   frame->nregs = ix86_nsaved_regs ();
+  frame->nbndregs = ix86_nsaved_bndregs ();
+  frame->nmaskregs = ix86_nsaved_maskregs ();
   frame->nsseregs = ix86_nsaved_sseregs ();
 
   /* 64-bit MS ABI seem to require stack alignment to be always 16 except for
@@ -10293,11 +10440,16 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
 	   = !expensive_function_p (count);
     }
 
+  /* We must use move to save bound and mask registers.  */
   frame->save_regs_using_mov
-    = (TARGET_PROLOGUE_USING_MOVE && cfun->machine->use_fast_prologue_epilogue
-       /* If static stack checking is enabled and done with probes,
-	  the registers need to be saved before allocating the frame.  */
-       && flag_stack_check != STATIC_BUILTIN_STACK_CHECK);
+    = (frame->nbndregs > 0
+       || frame->nmaskregs > 0
+       || (TARGET_PROLOGUE_USING_MOVE
+	   && cfun->machine->use_fast_prologue_epilogue
+	   /* If static stack checking is enabled and done with probes,
+	      the registers need to be saved before allocating the
+	      frame.  */
+	   && flag_stack_check != STATIC_BUILTIN_STACK_CHECK));
 
   /* Skip return address.  */
   offset = UNITS_PER_WORD;
@@ -10315,7 +10467,7 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   frame->hard_frame_pointer_offset = offset;
 
   /* Register save area */
-  offset += frame->nregs * UNITS_PER_WORD;
+  offset += ix86_reg_save_area_size (frame);
   frame->reg_save_offset = offset;
 
   /* On SEH target, registers are pushed just before the frame pointer
@@ -10329,9 +10481,17 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
       /* The only ABI that has saved SSE registers (Win64) also has a
          16-byte aligned default stack, and thus we don't need to be
 	 within the re-aligned local stack frame to save them.  */
-      gcc_assert (INCOMING_STACK_BOUNDARY >= 128);
-      offset = (offset + 16 - 1) & -16;
-      offset += frame->nsseregs * 16;
+      if (cfun->machine->is_interrupt)
+	/* We must save full vector registers in interrupt handler.  */
+	offset += frame->nsseregs * (TARGET_AVX512F
+				     ? 64
+				     : (TARGET_AVX ? 32 : 16));
+      else
+	{
+	  gcc_assert (INCOMING_STACK_BOUNDARY >= 128);
+	  offset = (offset + 16 - 1) & -16;
+	  offset += frame->nsseregs * 16;
+	}
     }
   frame->sse_reg_save_offset = offset;
 
@@ -10387,8 +10547,12 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   /* Size prologue needs to allocate.  */
   to_allocate = offset - frame->sse_reg_save_offset;
 
-  if ((!to_allocate && frame->nregs <= 1)
-      || (TARGET_64BIT && to_allocate >= (HOST_WIDE_INT) 0x80000000))
+  /* We must use move to save bound and mask registers.  */
+  if (frame->nbndregs == 0
+      && frame->nmaskregs == 0
+      && ((!to_allocate && frame->nregs <= 1)
+	   || (TARGET_64BIT
+	       && to_allocate >= (HOST_WIDE_INT) 0x80000000)))
     frame->save_regs_using_mov = false;
 
   if (ix86_using_red_zone ()
@@ -10398,7 +10562,7 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
     {
       frame->red_zone_size = to_allocate;
       if (frame->save_regs_using_mov)
-	frame->red_zone_size += frame->nregs * UNITS_PER_WORD;
+	frame->red_zone_size += ix86_reg_save_area_size (frame);
       if (frame->red_zone_size > RED_ZONE_SIZE - RED_ZONE_RESERVE)
 	frame->red_zone_size = RED_ZONE_SIZE - RED_ZONE_RESERVE;
     }
@@ -10558,8 +10722,14 @@ ix86_emit_save_reg_using_mov (machine_mode mode, unsigned int regno,
   addr = choose_baseaddr (cfa_offset);
   mem = gen_frame_mem (mode, addr);
 
-  /* For SSE saves, we need to indicate the 128-bit alignment.  */
-  set_mem_align (mem, GET_MODE_ALIGNMENT (mode));
+  /* For SSE saves, we need to indicate the 128-bit alignment.  In
+     interrupt handler, stack is only aligned to word_mode.  We can't
+     use gen_sse_storeups since RTX_FRAME_RELATED_P is set and
+     dwarf2out_flush_queued_reg_saves doesn't like UNSPEC_STOREU.
+     Also gen_sse_storeups doesn't cover AVX nor AVX512.  */
+  set_mem_align (mem,
+		 GET_MODE_ALIGNMENT (cfun->machine->is_interrupt
+				     ? word_mode : mode));
 
   insn = emit_move_insn (mem, reg);
   RTX_FRAME_RELATED_P (insn) = 1;
@@ -10620,8 +10790,9 @@ ix86_emit_save_regs_using_mov (HOST_WIDE_INT cfa_offset)
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (!SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
       {
-        ix86_emit_save_reg_using_mov (word_mode, regno, cfa_offset);
-	cfa_offset -= UNITS_PER_WORD;
+	enum machine_mode reg_mode = GET_MODE (regno_reg_rtx[regno]);
+	ix86_emit_save_reg_using_mov (reg_mode, regno, cfa_offset);
+	cfa_offset -= GET_MODE_SIZE (reg_mode);
       }
 }
 
@@ -10631,12 +10802,26 @@ static void
 ix86_emit_save_sse_regs_using_mov (HOST_WIDE_INT cfa_offset)
 {
   unsigned int regno;
+  enum machine_mode vector_reg_mode;
+
+  if (cfun->machine->is_interrupt)
+    {
+      /* We must save full vector registers in interrupt handler.  */
+      if (TARGET_AVX512F)
+	vector_reg_mode = V16SFmode;
+      else if (TARGET_AVX)
+	vector_reg_mode = V8SFmode;
+      else
+	vector_reg_mode = V4SFmode;
+    }
+  else
+    vector_reg_mode = V4SFmode;
 
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
       {
-	ix86_emit_save_reg_using_mov (V4SFmode, regno, cfa_offset);
-	cfa_offset -= 16;
+	ix86_emit_save_reg_using_mov (vector_reg_mode, regno, cfa_offset);
+	cfa_offset -= GET_MODE_SIZE (vector_reg_mode);
       }
 }
 
@@ -12081,12 +12266,13 @@ ix86_emit_restore_regs_using_mov (HOST_WIDE_INT cfa_offset,
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (!SSE_REGNO_P (regno) && ix86_save_reg (regno, maybe_eh_return))
       {
-	rtx reg = gen_rtx_REG (word_mode, regno);
+	enum machine_mode reg_mode = GET_MODE (regno_reg_rtx[regno]);
+	rtx reg = gen_rtx_REG (reg_mode, regno);
 	rtx mem;
 	rtx_insn *insn;
 
 	mem = choose_baseaddr (cfa_offset);
-	mem = gen_frame_mem (word_mode, mem);
+	mem = gen_frame_mem (reg_mode, mem);
 	insn = emit_move_insn (reg, mem);
 
         if (m->fs.cfa_reg == crtl->drap_reg && regno == REGNO (crtl->drap_reg))
@@ -12105,7 +12291,7 @@ ix86_emit_restore_regs_using_mov (HOST_WIDE_INT cfa_offset,
 	else
 	  ix86_add_cfa_restore_note (NULL, reg, cfa_offset);
 
-	cfa_offset -= UNITS_PER_WORD;
+	cfa_offset -= GET_MODE_SIZE (reg_mode);
       }
 }
 
@@ -12116,21 +12302,39 @@ ix86_emit_restore_sse_regs_using_mov (HOST_WIDE_INT cfa_offset,
 				      bool maybe_eh_return)
 {
   unsigned int regno;
+  enum machine_mode vector_reg_mode;
+
+  if (cfun->machine->is_interrupt)
+    {
+      /* We must restore full vector registers in interrupt handler.  */
+      if (TARGET_AVX512F)
+	vector_reg_mode = V16SFmode;
+      else if (TARGET_AVX)
+	vector_reg_mode = V8SFmode;
+      else
+	vector_reg_mode = V4SFmode;
+    }
+  else
+    vector_reg_mode = V4SFmode;
+
 
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, maybe_eh_return))
       {
-	rtx reg = gen_rtx_REG (V4SFmode, regno);
+	rtx reg = gen_rtx_REG (vector_reg_mode, regno);
 	rtx mem;
 
 	mem = choose_baseaddr (cfa_offset);
-	mem = gen_rtx_MEM (V4SFmode, mem);
-	set_mem_align (mem, 128);
+	mem = gen_rtx_MEM (vector_reg_mode, mem);
+	/* In interrupt handler, stack is only aligned to word_mode.  */
+	set_mem_align (mem, (cfun->machine->is_interrupt
+			     ? GET_MODE_ALIGNMENT (word_mode)
+			     : 128));
 	emit_move_insn (reg, mem);
 
 	ix86_add_cfa_restore_note (NULL, reg, cfa_offset);
 
-	cfa_offset -= 16;
+	cfa_offset -= GET_MODE_SIZE (vector_reg_mode);
       }
 }
 
@@ -12194,8 +12398,9 @@ ix86_expand_epilogue (int style)
   if (crtl->calls_eh_return && style != 2)
     frame.reg_save_offset -= 2 * UNITS_PER_WORD;
 
-  /* EH_RETURN requires the use of moves to function properly.  */
-  if (crtl->calls_eh_return)
+  /* EH_RETURN requires the use of moves to function properly.  We must
+     use move to restore bound and mask registers.  */
+  if (crtl->calls_eh_return || frame.nbndregs > 0 || frame.nmaskregs > 0)
     restore_regs_via_mov = true;
   /* SEH requires the use of pops to identify the epilogue.  */
   else if (TARGET_SEH)
@@ -12436,7 +12641,46 @@ ix86_expand_epilogue (int style)
       return;
     }
 
-  if (crtl->args.pops_args && crtl->args.size)
+  if (cfun->machine->is_interrupt)
+    {
+      /* Return with the "IRET" instruction from interrupt handler.
+	 Pop the 'ERROR_CODE' off the stack before the 'IRET'
+	 instruction in exception handler.  */
+      if (cfun->machine->is_exception)
+	{
+	  rtx r = plus_constant (Pmode, stack_pointer_rtx,
+				 UNITS_PER_WORD);
+	  emit_insn (gen_rtx_SET (stack_pointer_rtx, r));
+	}
+
+      rtx pat;
+      unsigned int i, n;
+
+      pat = gen_rtx_PARALLEL (VOIDmode,
+			      rtvec_alloc (ix86_interrupt_return_nregs + 2));
+      n = 0;
+      XVECEXP (pat, 0, n++) = simple_return_rtx;
+      XVECEXP (pat, 0, n++) = gen_rtx_UNSPEC (VOIDmode,
+					      gen_rtvec (1, const0_rtx),
+					      UNSPEC_INTERRUPT_RETURN);
+
+      /* Mark all preserved registers with USE for interrupt return so
+	 that they will be restored even if they are caller-saved.  */
+      for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+	if (reg_names[i][0]
+	    && !STACK_REGNO_P (i)
+	    && !MMX_REGNO_P (i)
+	    && i != BP_REG
+	    && i != SP_REG
+	    && (i <= ST7_REG || i >= XMM0_REG))
+	  {
+	    rtx reg = gen_rtx_REG (GET_MODE (regno_reg_rtx[i]), i);
+	    XVECEXP (pat, 0, n++) = gen_rtx_USE (VOIDmode, reg);
+	  }
+
+      emit_jump_insn (pat);
+    }
+  else if (crtl->args.pops_args && crtl->args.size)
     {
       rtx popc = GEN_INT (crtl->args.pops_args);
 
@@ -17409,6 +17653,13 @@ ix86_expand_move (machine_mode mode, rtx operands[])
   rtx op0, op1;
   enum tls_model model;
 
+  if (cfun->machine->is_interrupt && IS_STACK_MODE (mode))
+    {
+      error ("80387 instructions aren't allowed in %s service routine",
+	     (cfun->machine->is_exception ? "exception" : "interrupt"));
+      return;
+    }
+
   op0 = operands[0];
   op1 = operands[1];
 
@@ -17553,6 +17804,13 @@ ix86_expand_vector_move (machine_mode mode, rtx operands[])
   rtx op0 = operands[0], op1 = operands[1];
   unsigned int align = GET_MODE_ALIGNMENT (mode);
 
+  if (cfun->machine->is_interrupt && VALID_MMX_REG_MODE (mode))
+    {
+      error ("MMX/3Dnow instructions aren't allowed in %s service routine",
+	     (cfun->machine->is_exception ? "exception" : "interrupt"));
+      return;
+    }
+
   if (push_operand (op0, VOIDmode))
     op0 = emit_move_resolve_push (mode, op0);
 
@@ -25639,6 +25897,12 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
   rtx use = NULL, call;
   unsigned int vec_len = 0;
 
+  if (GET_CODE (XEXP (fnaddr, 0)) == SYMBOL_REF
+      && SYMBOL_REF_DECL ((XEXP (fnaddr, 0))) != NULL_TREE
+      && lookup_attribute ("interrupt",
+			   DECL_ATTRIBUTES (SYMBOL_REF_DECL (XEXP (fnaddr, 0)))))
+    error ("interrupt service routine can't be called directly");
+
   if (pop == const0_rtx)
     pop = NULL;
   gcc_assert (!TARGET_64BIT || !pop);
@@ -25757,6 +26021,8 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
   if (use)
     CALL_INSN_FUNCTION_USAGE (call) = use;
 
+  cfun->machine->make_calls = true;
+
   return call;
 }
 
@@ -43117,6 +43383,56 @@ ix86_handle_fndecl_attribute (tree *node, tree name, tree, int,
   return NULL_TREE;
 }
 
+
+static tree
+ix86_handle_interrupt_attribute (tree *node, tree name, tree, int,
+				 bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+	       name);
+      *no_add_attrs = true;
+    }
+
+  /* DECL_RESULT and DECL_ARGUMENTS do not exist there yet,
+     but the function type contains args and return type data.  */
+  tree func_type = TREE_TYPE (*node);
+  tree return_type = TREE_TYPE (func_type);
+
+  int nargs = 0;
+  tree current_arg_type = TYPE_ARG_TYPES (func_type);
+  while (current_arg_type
+	 && ! VOID_TYPE_P (TREE_VALUE (current_arg_type)))
+    {
+      if (nargs == 0)
+	{
+	  if (! POINTER_TYPE_P (TREE_VALUE (current_arg_type)))
+	    error ("interrupt service routine should have a pointer "
+		   "as the first argument");
+	}
+      else if (nargs == 1)
+	{
+	  if (TREE_CODE (TREE_VALUE (current_arg_type)) != INTEGER_TYPE
+	      || TYPE_MODE (TREE_VALUE (current_arg_type)) != word_mode)
+	    error ("interrupt service routine should have unsigned %s"
+		   "int as the second argument",
+		   TARGET_64BIT
+		   ? (TARGET_X32 ? "long long " : "long ")
+		   : "");
+	}
+      nargs++;
+      current_arg_type = TREE_CHAIN (current_arg_type);
+    }
+  if (!nargs || nargs > 2)
+    error ("interrupt service routine can only have a pointer argument "
+	   "and an optional integer argument");
+  if (! VOID_TYPE_P (return_type))
+    error ("interrupt service routine can't have non-void return value");
+
+  return NULL_TREE;
+}
+
 static bool
 ix86_ms_bitfield_layout_p (const_tree record_type)
 {
@@ -47118,6 +47434,9 @@ static const struct attribute_spec ix86_attribute_table[] =
     false },
   { "callee_pop_aggregate_return", 1, 1, false, true, true,
     ix86_handle_callee_pop_aggregate_return, true },
+  { "interrupt", 0, 0, true, false, false,
+    ix86_handle_interrupt_attribute, false },
+
   /* End element.  */
   { NULL,        0, 0, false, false, false, NULL, false }
 };
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 96ba90f..fb2701c 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -2494,6 +2494,17 @@ struct GTY(()) machine_function {
   /* If true, it is safe to not save/restore DRAP register.  */
   BOOL_BITFIELD no_drap_save_restore : 1;
 
+  /* If true, the current function is an interrupt function as
+     specified by the "interrupt" or "exception" attribute.  */
+  BOOL_BITFIELD is_interrupt : 1;
+
+  /* If true, the current function is an interrupt function as
+     specified by the "exception" attribute.  */
+  BOOL_BITFIELD is_exception : 1;
+
+  /* If true, the current function makes any function calls.  */
+  BOOL_BITFIELD make_calls : 1;
+
   /* If true, there is register available for argument passing.  This
      is used only in ix86_function_ok_for_sibcall by 32-bit to determine
      if there is scratch register available for indirect sibcall.  In
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index ba5ab32..9e3033f 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -193,6 +193,9 @@
   UNSPEC_BNDCU
   UNSPEC_BNDCN
   UNSPEC_MPX_FENCE
+
+  ;; IRET support
+  UNSPEC_INTERRUPT_RETURN
 ])
 
 (define_c_enum "unspecv" [
@@ -12107,6 +12110,13 @@
    (set_attr "modrm" "0")
    (set_attr "maybe_prefix_bnd" "1")])
 
+(define_insn "*interrupt_return"
+  [(match_parallel 0 "interrupt_return_operation"
+    [(simple_return)
+     (unspec [(const_int 0)] UNSPEC_INTERRUPT_RETURN)])]
+  "reload_completed"
+  "iret")
+
 ;; Used by x86_machine_dependent_reorg to avoid penalty on single byte RET
 ;; instruction Athlon and K8 have.
 
diff --git a/gcc/config/i386/predicates.md b/gcc/config/i386/predicates.md
index 042b949..e2e91f6 100644
--- a/gcc/config/i386/predicates.md
+++ b/gcc/config/i386/predicates.md
@@ -1394,6 +1394,36 @@
   return true;
 })
 
+;; Return true if OP is an interrupt_return operation, known to be a
+;; PARALLEL.
+(define_predicate "interrupt_return_operation"
+  (match_code "parallel")
+{
+  unsigned i, n = ix86_interrupt_return_nregs + 2;
+
+  if ((unsigned) XVECLEN (op, 0) != n)
+    return false;
+
+  n = 2;
+  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+    if (reg_names[i][0]
+	&& !STACK_REGNO_P (i)
+	&& !MMX_REGNO_P (i)
+	&& i != BP_REG
+	&& i != SP_REG
+	&& (i <= ST7_REG || i >= XMM0_REG))
+    {
+      rtx elt = XVECEXP (op, 0, n);
+      if (GET_CODE (elt) != USE
+	  || GET_CODE (XEXP (elt, 0)) != REG
+	  || REGNO (XEXP (elt, 0)) != i)
+	return false;
+      n++;
+    }
+
+  return true;
+})
+
 ;; Return true if OP is a vzeroall operation, known to be a PARALLEL.
 (define_predicate "vzeroall_operation"
   (match_code "parallel")
diff --git a/gcc/config/i386/sse.md b/gcc/config/i386/sse.md
index 4eefb45..3f932a7 100644
--- a/gcc/config/i386/sse.md
+++ b/gcc/config/i386/sse.md
@@ -875,10 +875,14 @@
 	case MODE_V16SF:
 	case MODE_V8SF:
 	case MODE_V4SF:
-	  if (TARGET_AVX
+	  /* We must supprt misaligned SSE load and store in interrupt
+	     handler since ix86_emit_save_reg_using_mov generates the
+	     normal *mov<mode>_internal pattern for interrupt handler
+	     with misaligned stack.  */
+	  if ((TARGET_AVX || cfun->machine->is_interrupt)
 	      && (misaligned_operand (operands[0], <MODE>mode)
 		  || misaligned_operand (operands[1], <MODE>mode)))
-	    return "vmovups\t{%1, %0|%0, %1}";
+	    return "%vmovups\t{%1, %0|%0, %1}";
 	  else
 	    return "%vmovaps\t{%1, %0|%0, %1}";
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 23e6a76..ee61af9 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -5061,6 +5061,62 @@ On x86-32 targets, the @code{stdcall} attribute causes the compiler to
 assume that the called function pops off the stack space used to
 pass arguments, unless it takes a variable number of arguments.
 
+@item interrupt
+@cindex @code{interrupt} function attribute, x86
+Use this attribute to indicate that the specified function is an
+interrupt handler.  The compiler generates function
+entry and exit sequences suitable for use in an interrupt handler when
+this attribute is present.  The @code{IRET} instruction, instead of the
+@code{RET} instruction, is used to return from interrupt handlers.  All
+registers, except for the EFLAGS register which is restored by the
+@code{IRET} instruction, are preserved by the compiler.
+
+Any interruptible-without-stack-switch code must be compiled with
+@option{-mno-red-zone} since interrupt handlers can and will, because
+of the hardware design, touch the red zone.
+
+An interrupt handler must be declared with a mandatory pointer
+argument:
+
+@smallexample
+struct interrupt_frame;
+
+__attribute__ ((interrupt))
+void
+f (struct interrupt_frame *frame)
+@{
+@}
+@end smallexample
+
+and user must properly define the structure the pointer pointing to.
+
+The exception handler is very similar to the interrupt handler with
+a different mandatory function signature:
+
+@smallexample
+#ifdef __x86_64__
+typedef unsigned long long int uword_t;
+#else
+typedef unsigned int uword_t;
+#endif
+
+struct interrupt_frame;
+
+__attribute__ ((interrupt))
+void
+f (struct interrupt_frame *frame, uword_t error_code)
+@{
+  ...
+@}
+@end smallexample
+
+and compiler pops the error code off the stack before the @code{IRET}
+instruction.
+
+The exception handler should only be used for exceptions which push an
+error code and all other exceptions must use the interrupt handler.
+The system will crash if the wrong handler is used.
+
 @item target (@var{options})
 @cindex @code{target} function attribute
 As discussed in @ref{Common Function Attributes}, this attribute 
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-1.c b/gcc/testsuite/gcc.target/i386/interrupt-1.c
new file mode 100644
index 0000000..54493f7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-1.c
@@ -0,0 +1,35 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern int bar (int);
+
+void foo (void *frame)
+{
+  int a,b,c,d,e,f,i;
+  a = bar (5);
+  b = bar (a);
+  c = bar (b);
+  d = bar (c);
+  e = bar (d);
+  f = bar (e);
+  for (i = 1; i < 10; i++)
+  {
+    a += bar (a + i) + bar (b + i) +
+	 bar (c + i) + bar (d + i) +
+	 bar (e + i) + bar (f + i);
+  }
+}
+/* { dg-final { scan-assembler "push.\t%.ax" } }*/
+/* { dg-final { scan-assembler "pop.\t%.ax" } }*/
+/* { dg-final { scan-assembler "push.\t%.dx" } }*/
+/* { dg-final { scan-assembler "pop.\t%.dx" } }*/
+/* { dg-final { scan-assembler "push.\t%.cx" } }*/
+/* { dg-final { scan-assembler "pop.\t%.cx" } }*/
+/* { dg-final { scan-assembler "push.\t%.bx" } }*/
+/* { dg-final { scan-assembler "pop.\t%.bx" } }*/
+/* { dg-final { scan-assembler "push.\t%.si" } }*/
+/* { dg-final { scan-assembler "pop.\t%.si" } }*/
+/* { dg-final { scan-assembler "push.\t%.di" } }*/
+/* { dg-final { scan-assembler "pop.\t%.di" } }*/
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-10.c b/gcc/testsuite/gcc.target/i386/interrupt-10.c
new file mode 100644
index 0000000..a439d6f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-10.c
@@ -0,0 +1,24 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mavx512bw -mno-iamcu" } */
+
+extern void bar (void);
+
+struct interrupt_frame;
+
+void
+ __attribute__ ((interrupt))
+foo (struct interrupt_frame *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm\[0-9\]+" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "kmovq\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" 8 } } */
+/* { dg-final { scan-assembler-times "kmovq\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" 8 } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-11.c b/gcc/testsuite/gcc.target/i386/interrupt-11.c
new file mode 100644
index 0000000..6556c44
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-11.c
@@ -0,0 +1,38 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-sse -mno-iamcu" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-12.c b/gcc/testsuite/gcc.target/i386/interrupt-12.c
new file mode 100644
index 0000000..5c9eb74
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-12.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int check_int (int *i, void *, int align);
+typedef int aligned __attribute__((aligned(64)));
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+__attribute__((interrupt))
+void
+foo (void *frame, uword_t error_code)
+{
+  aligned j;
+  if (check_int (frame, &j, __alignof__(j)))
+    __builtin_abort ();
+}
+
+/* { dg-final { scan-assembler-times "and\[lq\]?\[^\\n\]*-64,\[^\\n\]*sp" 1 } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-13.c b/gcc/testsuite/gcc.target/i386/interrupt-13.c
new file mode 100644
index 0000000..8cf0b25
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-13.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int check_int (int *i, void *, int align);
+typedef int aligned __attribute__((aligned(64)));
+
+__attribute__((interrupt))
+void
+foo (void *frame)
+{
+  aligned j;
+  if (check_int (frame, &j, __alignof__(j)))
+    __builtin_abort ();
+}
+
+/* { dg-final { scan-assembler-times "and\[lq\]?\[^\\n\]*-64,\[^\\n\]*sp" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-2.c b/gcc/testsuite/gcc.target/i386/interrupt-2.c
new file mode 100644
index 0000000..2003fbb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-2.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wall -Wunused-parameter" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+}
+
+/* { dg-final { scan-assembler-not "add(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-3.c b/gcc/testsuite/gcc.target/i386/interrupt-3.c
new file mode 100644
index 0000000..a87e594
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-3.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+void
+__attribute__((interrupt))
+fn (void* frame, uword_t error)
+{
+}
+
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-387-err.c b/gcc/testsuite/gcc.target/i386/interrupt-387-err.c
new file mode 100644
index 0000000..3a4a5cf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-387-err.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -m80387 -mlong-double-80 -mno-iamcu" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+extern long double y, x;
+
+void
+fn0 (void)
+{
+  x = y;
+  y = 0;
+}
+
+void
+__attribute__((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  x = 0;
+} /* { dg-error "80387 instructions aren't allowed in exception service routine" } */
+
+void
+__attribute__((interrupt))
+fn2 (void *frame)
+{
+  x = y; /* { dg-error "80387 instructions aren't allowed in interrupt service routine" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-4.c b/gcc/testsuite/gcc.target/i386/interrupt-4.c
new file mode 100644
index 0000000..94230cd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-4.c
@@ -0,0 +1,32 @@
+/* { dg-do link } */
+/* { dg-options "-O" } */
+
+#include <stdint.h>
+
+extern void link_error (void);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+struct interrupt_frame
+{
+  uword_t ip;
+  uword_t cs;
+  uword_t flags;
+  uword_t sp;
+  uword_t ss;
+};
+
+__attribute__ ((used, interrupt))
+void
+foo (struct interrupt_frame *frame)
+{
+  void *ra = __builtin_return_address (0);
+  if ((uintptr_t) ra != (uintptr_t) frame->ip)
+    link_error ();
+}
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-5.c b/gcc/testsuite/gcc.target/i386/interrupt-5.c
new file mode 100644
index 0000000..53d6656
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-5.c
@@ -0,0 +1,23 @@
+/* { dg-do link } */
+/* { dg-options "-O" } */
+
+#include <stdint.h>
+
+extern void link_error (void);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+__attribute__ ((used, interrupt))
+void
+foo (void *frame, uword_t error)
+{
+  void *ra = __builtin_return_address (0);
+  if ((uintptr_t) ra != (uintptr_t) error)
+    link_error ();
+}
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-6.c b/gcc/testsuite/gcc.target/i386/interrupt-6.c
new file mode 100644
index 0000000..a1a3d0e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-6.c
@@ -0,0 +1,40 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+extern int error;
+
+__attribute__((interrupt))
+void
+fn1 (void *p, short error_code)
+{ /* { dg-error "interrupt service routine should have unsigned \(long long |long |\)int as the second argument" } */
+}
+
+__attribute__((interrupt))
+void
+fn2 (void)
+{ /* { dg-error "interrupt service routine can only have a pointer argument and an optional integer argument" } */
+}
+
+__attribute__((interrupt))
+void
+fn3 (uword_t error_code)
+{ /* { dg-error "interrupt service routine should have a pointer as the first argument" } */
+  error = error_code;
+}
+
+__attribute__((interrupt))
+void
+fn4 (uword_t error_code, void *frame)
+{ /* { dg-error "interrupt service routine should have .* the .* argument" } */
+  error = error_code;
+}
+
+extern int fn5 (void *) __attribute__ ((interrupt)); /* { dg-error "interrupt service routine can't have non-void return value" } */
+
+int
+fn5 (void *frame)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-7.c b/gcc/testsuite/gcc.target/i386/interrupt-7.c
new file mode 100644
index 0000000..e7441a2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-7.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int error;
+
+extern void fn (void *) __attribute__((interrupt));
+
+void
+foo (void)
+{
+  fn (&error); /* { dg-error "interrupt service routine can't be called directly" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-8.c b/gcc/testsuite/gcc.target/i386/interrupt-8.c
new file mode 100644
index 0000000..0a83473
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-8.c
@@ -0,0 +1,20 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 16 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm\[0-9\]+" 16 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-9.c b/gcc/testsuite/gcc.target/i386/interrupt-9.c
new file mode 100644
index 0000000..9d0c776
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-9.c
@@ -0,0 +1,22 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512bw -mno-iamcu -mavx512f" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm\[0-9\]+" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "kmovw\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" 8 } } */
+/* { dg-final { scan-assembler-times "kmovw\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" 8 } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-bnd.c b/gcc/testsuite/gcc.target/i386/interrupt-bnd.c
new file mode 100644
index 0000000..9f50661
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-bnd.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mno-iamcu -mmpx" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "bnd3");
+}
+
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*%bnd3,\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%bnd3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
new file mode 100644
index 0000000..163a981
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
@@ -0,0 +1,33 @@
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -miamcu" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern int bar (int);
+
+void foo (void *frame)
+{
+  int a,b,c,d,e,f,i;
+  a = bar (5);
+  b = bar (a);
+  c = bar (b);
+  d = bar (c);
+  e = bar (d);
+  f = bar (e);
+  for (i = 1; i < 10; i++)
+    a += bar (a + i) + bar (b + i) +
+	 bar (c + i) + bar (d + i) +
+	 bar (e + i) + bar (f+i);
+}
+/* { dg-final { scan-assembler "push.\t%.ax" } }*/
+/* { dg-final { scan-assembler "pop.\t%.ax" } }*/
+/* { dg-final { scan-assembler "push.\t%.dx" } }*/
+/* { dg-final { scan-assembler "pop.\t%.dx" } }*/
+/* { dg-final { scan-assembler "push.\t%.cx" } }*/
+/* { dg-final { scan-assembler "pop.\t%.cx" } }*/
+/* { dg-final { scan-assembler "push.\t%.bx" } }*/
+/* { dg-final { scan-assembler "pop.\t%.bx" } }*/
+/* { dg-final { scan-assembler "push.\t%.si" } }*/
+/* { dg-final { scan-assembler "pop.\t%.si" } }*/
+/* { dg-final { scan-assembler "push.\t%.di" } }*/
+/* { dg-final { scan-assembler "pop.\t%.di" } }*/
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c
new file mode 100644
index 0000000..fc4f367
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mmmx -mno-iamcu" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+typedef short __v4hi __attribute__ ((__vector_size__ (8)));
+typedef int __m64 __attribute__ ((__vector_size__ (8), __may_alias__));
+
+extern __m64 y, x;
+
+void
+fn0 (void)
+{
+  x = __extension__ (__m64){ 0 };
+  x = (__m64) __builtin_ia32_packsswb ((__v4hi) x, (__v4hi) y);
+  y = x;
+}
+
+void
+__attribute__((interrupt))
+fn1 (void *frame)
+{
+  x = (__m64) __builtin_ia32_packsswb ((__v4hi) x, (__v4hi) y); /* { dg-error "MMX/3Dnow (instructions|intrinsics) aren't allowed in interrupt service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn2 (void *frame)
+{
+  x = y; /* { dg-error "MMX/3Dnow instructions aren't allowed in interrupt service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn3 (void *frame, uword_t error)
+{
+  x = __extension__ (__m64){ 0 }; /* { dg-error "MMX/3Dnow instructions aren't allowed in exception service routine" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c b/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c
new file mode 100644
index 0000000..796b81b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mred-zone" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  /* No need to adjust stack if less than 128 bytes are used on stack
+     with a 128-byte red zone.  */
+  char array[112];
+  asm ("# %0 "
+       :
+       : "r" (array));
+}
+
+/* { dg-final { scan-assembler-not "(sub|add)(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c b/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c
new file mode 100644
index 0000000..c6a2822
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mred-zone" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  /* Need to adjust stack if more than 128 bytes are used on stack
+     with a 128-byte red zone.  */
+  char array[113];
+  asm ("# %0 "
+       :
+       : "r" (array));
+}
+
+/* { dg-final { scan-assembler-times "(?:sub|add)(?:l|q)\[\\t \]*\\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c b/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c
new file mode 100644
index 0000000..e222acf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O3" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern void bar (void);
+
+void foo (void *frame)
+{
+  bar ();
+}
+/* { dg-final { scan-assembler-not "jmp" } }*/
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c
new file mode 100644
index 0000000..b1b0abe
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern void bar (int);
+
+void f1 (void){  bar (1); }
+__attribute__((interrupt))
+void f2 (void *frame){  bar (2); }
+void f3 (void){  bar (3); }
+__attribute__((interrupt))
+void f4 (void *frame){  bar (4); }
+void f5 (void){  bar (5); }
+
+/* { dg-final { scan-assembler-times "push.\t%.ax" 2 } } */
+/* { dg-final { scan-assembler-times "pop.\t%.ax" 2 } } */
+/* { dg-final { scan-assembler-times "iret" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-xmm.c b/gcc/testsuite/gcc.target/i386/interrupt-xmm.c
new file mode 100644
index 0000000..0eafb10
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-xmm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx -mno-iamcu -msse" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%xmm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%xmm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-ymm.c b/gcc/testsuite/gcc.target/i386/interrupt-ymm.c
new file mode 100644
index 0000000..acb5fc7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-ymm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*%ymm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-zmm.c b/gcc/testsuite/gcc.target/i386/interrupt-zmm.c
new file mode 100644
index 0000000..6d23b5d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-zmm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mavx512f -mno-iamcu" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*%zmm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-09-29 14:21 [PATCH] x86 interrupt attribute Yulia Koval
@ 2015-09-29 19:49 ` Mike Stump
  2015-09-29 20:30   ` H.J. Lu
  0 siblings, 1 reply; 45+ messages in thread
From: Mike Stump @ 2015-09-29 19:49 UTC (permalink / raw)
  To: Yulia Koval; +Cc: gcc-patches, ubizjak, hongjiu.lu

To be feature complete, it would be nice to have two styles of interrupt functions, one that returns with iret, and one that returns with ret.  The point is that the user might want to call functions from a interrupt handler and not save and restore all call clobbered registers.  By allowing a ret style interrupt handler, calls to a ret style interrupt routine can avoid saving and restoring all call clobbered registers.

Oh, and I wish that all the port independent code for interrupt functions was shared across all ports, as redoing all this code for each port is silly (sad).  And example of this would be the sibcall code, the fact that all call saved registers need to be saved is another.  The EPILOGUE_USES or the gen_rtx_USE is yet another.  Type checking the return type to ensure the return type is void, likely another.

One last comment, most folks use EPILOGUE_USES and mark up the registers as used.  You don’t.  I’m not sure if both ways work equally well, or if there is a reason to prefer one over the other.  Maybe someone could comment on this, as in my port I use EPILOGUE_USES and it seems to work just fine.

On Sep 29, 2015, at 6:49 AM, Yulia Koval <vaalfreja@gmail.com> wrote:
> +  /* Always need to save SSE registrers in interrupt handler.  */

Spelling registrers -> registers.

Not in your code, but I noticed it:

>    /* All pointer bounds argumntas are handled separately here.  */

Spelling argumntas -> arguments?

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-09-29 19:49 ` Mike Stump
@ 2015-09-29 20:30   ` H.J. Lu
  2015-09-29 21:49     ` H.J. Lu
  2015-09-29 22:10     ` Mike Stump
  0 siblings, 2 replies; 45+ messages in thread
From: H.J. Lu @ 2015-09-29 20:30 UTC (permalink / raw)
  To: Mike Stump; +Cc: Yulia Koval, GCC Patches, Uros Bizjak

On Tue, Sep 29, 2015 at 11:49 AM, Mike Stump <mikestump@comcast.net> wrote:
> To be feature complete, it would be nice to have two styles of interrupt functions, one that returns with iret, and one that returns with ret.  The point is that the user might want to call functions from a interrupt handler and not save and restore all call clobbered registers.  By allowing a ret style interrupt handler, calls to a ret style interrupt routine can avoid saving and restoring all call clobbered registers.

Do you have a testcase for this?  I think the current implementation
covers most use cases.

> Oh, and I wish that all the port independent code for interrupt functions was shared across all ports, as redoing all this code for each port is silly (sad).  And example of this would be the sibcall code, the fact that all call saved registers need to be saved is another.  The EPILOGUE_USES or the gen_rtx_USE is yet another.  Type checking the return type to ensure the return type is void, likely another.

A very good point, but beyond this implementation :-(.

> One last comment, most folks use EPILOGUE_USES and mark up the registers as used.  You don’t.  I’m not sure if both ways work equally well, or if there is a reason to prefer one over the other.  Maybe someone could comment on this, as in my port I use EPILOGUE_USES and it seems to work just fine.

We will take a look.

> On Sep 29, 2015, at 6:49 AM, Yulia Koval <vaalfreja@gmail.com> wrote:
>> +  /* Always need to save SSE registrers in interrupt handler.  */
>
> Spelling registrers -> registers.
>
> Not in your code, but I noticed it:
>
>>    /* All pointer bounds argumntas are handled separately here.  */
>
> Spelling argumntas -> arguments?

I checked in an obvious patch to fix those typos.

Thanks.


-- 
H.J.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-09-29 20:30   ` H.J. Lu
@ 2015-09-29 21:49     ` H.J. Lu
  2015-09-29 22:10       ` Mike Stump
  2015-09-29 22:10     ` Mike Stump
  1 sibling, 1 reply; 45+ messages in thread
From: H.J. Lu @ 2015-09-29 21:49 UTC (permalink / raw)
  To: Mike Stump; +Cc: Yulia Koval, GCC Patches, Uros Bizjak

On Tue, Sep 29, 2015 at 1:16 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Tue, Sep 29, 2015 at 11:49 AM, Mike Stump <mikestump@comcast.net> wrote:
>> To be feature complete, it would be nice to have two styles of interrupt functions, one that returns with iret, and one that returns with ret.  The point is that the user might want to call functions from a interrupt handler and not save and restore all call clobbered registers.  By allowing a ret style interrupt handler, calls to a ret style interrupt routine can avoid saving and restoring all call clobbered registers.
>
> Do you have a testcase for this?  I think the current implementation
> covers most use cases.
>
>> Oh, and I wish that all the port independent code for interrupt functions was shared across all ports, as redoing all this code for each port is silly (sad).  And example of this would be the sibcall code, the fact that all call saved registers need to be saved is another.  The EPILOGUE_USES or the gen_rtx_USE is yet another.  Type checking the return type to ensure the return type is void, likely another.
>
> A very good point, but beyond this implementation :-(.
>
>> One last comment, most folks use EPILOGUE_USES and mark up the registers as used.  You don’t.  I’m not sure if both ways work equally well, or if there is a reason to prefer one over the other.  Maybe someone could comment on this, as in my port I use EPILOGUE_USES and it seems to work just fine.
>
> We will take a look.

Julia, I checked a patch into hjl/interrupt/master branch to
define EPILOGUE_USES in i386:

commit f3a6675a8d69d810d2cad0c090a762094a0a8622
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Tue Sep 29 13:47:18 2015 -0700

    Define EPILOGUE_USES in i386

    Define EPILOGUE_USES in i386 so that all preserved registers are used
    by the epilogue of interrupt handler.  Don't explicitly mark BP and SP
    registers as used since they are always used in epilogue.

Please take a look.


-- 
H.J.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-09-29 20:30   ` H.J. Lu
  2015-09-29 21:49     ` H.J. Lu
@ 2015-09-29 22:10     ` Mike Stump
  2015-09-29 23:08       ` H.J. Lu
  1 sibling, 1 reply; 45+ messages in thread
From: Mike Stump @ 2015-09-29 22:10 UTC (permalink / raw)
  To: H.J. Lu; +Cc: Yulia Koval, GCC Patches, Uros Bizjak

On Sep 29, 2015, at 1:16 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Tue, Sep 29, 2015 at 11:49 AM, Mike Stump <mikestump@comcast.net> wrote:
>> To be feature complete, it would be nice to have two styles of interrupt functions, one that returns with iret, and one that returns with ret.  The point is that the user might want to call functions from a interrupt handler and not save and restore all call clobbered registers.  By allowing a ret style interrupt handler, calls to a ret style interrupt routine can avoid saving and restoring all call clobbered registers.
> 
> Do you have a testcase for this?  I think the current implementation
> covers most use cases.

When I wrote my interrupt support for my cpu, I ran these through the code generator…  I have many registers, and noticed saving and restoring them all just because two interrupt handlers used the same routine was silly.  Test case is trivial:

interrupt void foo2() {
  bar();
}

interrupt void foo1() {
  bar();
}

if more than 1-2 registers are saved, then likely it is saving all call used registers.  Saving all means that one cannot use functions to compose semantics and attain performance.  Performance of ISR routines I think is useful to shoot for, given that it is easy enough to attain, I don’t see the harm in doing that.  Even if in the first implementation you don’t bother with performance, if you spec the other function, the user code need never change; and when performance does matter, it is then a mere matter of enhancing the code gen to do the right thing.  It is pretty easy to get most of the benefit without much work.  i call the main interrupt function interrupt, and the recursive (ret style), I call interruptr.  The r is for recursive.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-09-29 21:49     ` H.J. Lu
@ 2015-09-29 22:10       ` Mike Stump
  2015-09-29 22:11         ` Mike Stump
  2015-09-29 22:13         ` H.J. Lu
  0 siblings, 2 replies; 45+ messages in thread
From: Mike Stump @ 2015-09-29 22:10 UTC (permalink / raw)
  To: H.J. Lu; +Cc: Yulia Koval, GCC Patches, Uros Bizjak

On Sep 29, 2015, at 1:59 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
> commit f3a6675a8d69d810d2cad0c090a762094a0a8622
> Author: H.J. Lu <hjl.tools@gmail.com>
> Date:   Tue Sep 29 13:47:18 2015 -0700
> 
>    Define EPILOGUE_USES in i386 so that all preserved registers are used
>    by the epilogue of interrupt handler.  Don't explicitly mark BP and SP
>    registers as used since they are always used in epilogue.
> 
> Please take a look.

Oh, too bad you didn’t copy it here.  The easiest thing to blow is the addition of reload_completed && on the condition:

  /* An interrupt handler must preserve some registers that are                                                                                         
     ordinarily call-clobbered.  */
  if (reload_completed
      && myarch_interrupt_func (current_function_decl)
      && save_reg_p (regno))
    return true;

without it, the optimizer will blow chunks all over the place and code-gen will not be very good, if it doesn’t.  I’d love this to be shared across all ports, it it is cryptic and usually test cases are not elaborate enough to find the problem.  When we ported a large library to our system that made extensive uses of complex interrupt routines, the compiler blew chunks.  With lessor code, we never even noticed a problem.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-09-29 22:10       ` Mike Stump
@ 2015-09-29 22:11         ` Mike Stump
  2015-09-29 22:13         ` H.J. Lu
  1 sibling, 0 replies; 45+ messages in thread
From: Mike Stump @ 2015-09-29 22:11 UTC (permalink / raw)
  To: H.J. Lu; +Cc: Yulia Koval, GCC Patches, Uros Bizjak

On Sep 29, 2015, at 2:23 PM, Mike Stump <mikestump@comcast.net> wrote:
> On Sep 29, 2015, at 1:59 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>> commit f3a6675a8d69d810d2cad0c090a762094a0a8622
>> Author: H.J. Lu <hjl.tools@gmail.com>
>> Date:   Tue Sep 29 13:47:18 2015 -0700
>> 
>>   Define EPILOGUE_USES in i386

>> Please take a look.

Oh, and with that, I don’t think one needs the generated USEs anymore.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-09-29 22:10       ` Mike Stump
  2015-09-29 22:11         ` Mike Stump
@ 2015-09-29 22:13         ` H.J. Lu
  2015-09-30  0:47           ` Mike Stump
  1 sibling, 1 reply; 45+ messages in thread
From: H.J. Lu @ 2015-09-29 22:13 UTC (permalink / raw)
  To: Mike Stump; +Cc: Yulia Koval, GCC Patches, Uros Bizjak

On Tue, Sep 29, 2015 at 2:23 PM, Mike Stump <mikestump@comcast.net> wrote:
> On Sep 29, 2015, at 1:59 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>> commit f3a6675a8d69d810d2cad0c090a762094a0a8622
>> Author: H.J. Lu <hjl.tools@gmail.com>
>> Date:   Tue Sep 29 13:47:18 2015 -0700
>>
>>    Define EPILOGUE_USES in i386 so that all preserved registers are used
>>    by the epilogue of interrupt handler.  Don't explicitly mark BP and SP
>>    registers as used since they are always used in epilogue.
>>
>> Please take a look.
>
> Oh, too bad you didn’t copy it here.  The easiest thing to blow is the addition of reload_completed && on the condition:
>
>   /* An interrupt handler must preserve some registers that are
>      ordinarily call-clobbered.  */
>   if (reload_completed
>       && myarch_interrupt_func (current_function_decl)
>       && save_reg_p (regno))
>     return true;
>
> without it, the optimizer will blow chunks all over the place and code-gen will not be very good, if it doesn’t.  I’d love this to be shared across all ports, it it is cryptic and usually test cases are not elaborate enough to find the problem.  When we ported a large library to our system that made extensive uses of complex interrupt routines, the compiler blew chunks.  With lessor code, we never even noticed a problem.

We have

static bool
ix86_save_reg (unsigned int regno, bool maybe_eh_return)
{
  /* In interrupt handler, we don't preserve MMX and x87 registers
     which aren't supported when saving and restoring registers.  No
     need to preserve callee-saved registers unless they are modified.
     We also preserve all caller-saved registers if a function call
     is made in interrupt handler since the called function may change
     them.  Don't explicitly save BP and SP registers since they are
     always preserved.  */
  if (cfun->machine->is_interrupt)
    return ((df_regs_ever_live_p (regno)
             || (call_used_regs[regno] && cfun->machine->make_calls))
            && !fixed_regs[regno]
            && !STACK_REGNO_P (regno)
            && !MMX_REGNO_P (regno)
            && regno != BP_REG
            && regno != SP_REG
            && (regno <= ST7_REG || regno >= XMM0_REG));

Is this sufficient?



-- 
H.J.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-09-29 22:10     ` Mike Stump
@ 2015-09-29 23:08       ` H.J. Lu
  2015-09-29 23:54         ` H.J. Lu
  0 siblings, 1 reply; 45+ messages in thread
From: H.J. Lu @ 2015-09-29 23:08 UTC (permalink / raw)
  To: Mike Stump; +Cc: Yulia Koval, GCC Patches, Uros Bizjak

On Tue, Sep 29, 2015 at 2:12 PM, Mike Stump <mikestump@comcast.net> wrote:
> On Sep 29, 2015, at 1:16 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>> On Tue, Sep 29, 2015 at 11:49 AM, Mike Stump <mikestump@comcast.net> wrote:
>>> To be feature complete, it would be nice to have two styles of interrupt functions, one that returns with iret, and one that returns with ret.  The point is that the user might want to call functions from a interrupt handler and not save and restore all call clobbered registers.  By allowing a ret style interrupt handler, calls to a ret style interrupt routine can avoid saving and restoring all call clobbered registers.
>>
>> Do you have a testcase for this?  I think the current implementation
>> covers most use cases.
>
> When I wrote my interrupt support for my cpu, I ran these through the code generator…  I have many registers, and noticed saving and restoring them all just because two interrupt handlers used the same routine was silly.  Test case is trivial:
>
> interrupt void foo2() {
>   bar();
> }
>
> interrupt void foo1() {
>   bar();
> }
>
> if more than 1-2 registers are saved, then likely it is saving all call used registers.  Saving all means that one cannot use functions to compose semantics and attain performance.  Performance of ISR routines I think is useful to shoot for, given that it is easy enough to attain, I don’t see the harm in doing that.  Even if in the first implementation you don’t bother with performance, if you spec the other function, the user code need never change; and when performance does matter, it is then a mere matter of enhancing the code gen to do the right thing.  It is pretty easy to get most of the benefit without much work.  i call the main interrupt function interrupt, and the recursive (ret style), I call interruptr.  The r is for recursive.

I added:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66960#c3

Thanks.

-- 
H.J.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-09-29 23:08       ` H.J. Lu
@ 2015-09-29 23:54         ` H.J. Lu
  2015-09-30  2:45           ` Mike Stump
  0 siblings, 1 reply; 45+ messages in thread
From: H.J. Lu @ 2015-09-29 23:54 UTC (permalink / raw)
  To: Mike Stump; +Cc: Yulia Koval, GCC Patches, Uros Bizjak

On Tue, Sep 29, 2015 at 3:19 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Tue, Sep 29, 2015 at 2:12 PM, Mike Stump <mikestump@comcast.net> wrote:
>> On Sep 29, 2015, at 1:16 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>> On Tue, Sep 29, 2015 at 11:49 AM, Mike Stump <mikestump@comcast.net> wrote:
>>>> To be feature complete, it would be nice to have two styles of interrupt functions, one that returns with iret, and one that returns with ret.  The point is that the user might want to call functions from a interrupt handler and not save and restore all call clobbered registers.  By allowing a ret style interrupt handler, calls to a ret style interrupt routine can avoid saving and restoring all call clobbered registers.
>>>
>>> Do you have a testcase for this?  I think the current implementation
>>> covers most use cases.
>>
>> When I wrote my interrupt support for my cpu, I ran these through the code generator…  I have many registers, and noticed saving and restoring them all just because two interrupt handlers used the same routine was silly.  Test case is trivial:
>>
>> interrupt void foo2() {
>>   bar();
>> }
>>
>> interrupt void foo1() {
>>   bar();
>> }
>>
>> if more than 1-2 registers are saved, then likely it is saving all call used registers.  Saving all means that one cannot use functions to compose semantics and attain performance.  Performance of ISR routines I think is useful to shoot for, given that it is easy enough to attain, I don’t see the harm in doing that.  Even if in the first implementation you don’t bother with performance, if you spec the other function, the user code need never change; and when performance does matter, it is then a mere matter of enhancing the code gen to do the right thing.  It is pretty easy to get most of the benefit without much work.  i call the main interrupt function interrupt, and the recursive (ret style), I call interruptr.  The r is for recursive.
>
> I added:
>
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66960#c3
>

How about adding a "no_caller_saved_registers" attribute?

-- 
H.J.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-09-29 22:13         ` H.J. Lu
@ 2015-09-30  0:47           ` Mike Stump
  2015-09-30  1:34             ` H.J. Lu
  0 siblings, 1 reply; 45+ messages in thread
From: Mike Stump @ 2015-09-30  0:47 UTC (permalink / raw)
  To: H.J. Lu; +Cc: Yulia Koval, GCC Patches, Uros Bizjak

On Sep 29, 2015, at 3:10 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Tue, Sep 29, 2015 at 2:23 PM, Mike Stump <mikestump@comcast.net> wrote:
>> On Sep 29, 2015, at 1:59 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>> commit f3a6675a8d69d810d2cad0c090a762094a0a8622
>>> Author: H.J. Lu <hjl.tools@gmail.com>
>>> Date:   Tue Sep 29 13:47:18 2015 -0700
>>> 
>>>   Define EPILOGUE_USES in i386 so that all preserved registers are used
>>>   by the epilogue of interrupt handler.  Don't explicitly mark BP and SP
>>>   registers as used since they are always used in epilogue.
>>> 
>>> Please take a look.
>> 
>> Oh, too bad you didn’t copy it here.  The easiest thing to blow is the addition of reload_completed && on the condition


> static bool
> ix86_save_reg (unsigned int regno, bool maybe_eh_return)
> {
>  /* In interrupt handler, we don't preserve MMX and x87 registers
>     which aren't supported when saving and restoring registers.  No
>     need to preserve callee-saved registers unless they are modified.
>     We also preserve all caller-saved registers if a function call
>     is made in interrupt handler since the called function may change
>     them.  Don't explicitly save BP and SP registers since they are
>     always preserved.  */
>  if (cfun->machine->is_interrupt)
>    return ((df_regs_ever_live_p (regno)
>             || (call_used_regs[regno] && cfun->machine->make_calls))
>            && !fixed_regs[regno]
>            && !STACK_REGNO_P (regno)
>            && !MMX_REGNO_P (regno)
>            && regno != BP_REG
>            && regno != SP_REG
>            && (regno <= ST7_REG || regno >= XMM0_REG));
> 
> Is this sufficient?

I see no string "reload_completed &&”.  Either, you need it here, or, you need it in the caller.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-09-30  0:47           ` Mike Stump
@ 2015-09-30  1:34             ` H.J. Lu
  2015-09-30  5:50               ` H.J. Lu
  0 siblings, 1 reply; 45+ messages in thread
From: H.J. Lu @ 2015-09-30  1:34 UTC (permalink / raw)
  To: Mike Stump; +Cc: Yulia Koval, GCC Patches, Uros Bizjak

On Tue, Sep 29, 2015 at 4:53 PM, Mike Stump <mikestump@comcast.net> wrote:
> On Sep 29, 2015, at 3:10 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>> On Tue, Sep 29, 2015 at 2:23 PM, Mike Stump <mikestump@comcast.net> wrote:
>>> On Sep 29, 2015, at 1:59 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>>> commit f3a6675a8d69d810d2cad0c090a762094a0a8622
>>>> Author: H.J. Lu <hjl.tools@gmail.com>
>>>> Date:   Tue Sep 29 13:47:18 2015 -0700
>>>>
>>>>   Define EPILOGUE_USES in i386 so that all preserved registers are used
>>>>   by the epilogue of interrupt handler.  Don't explicitly mark BP and SP
>>>>   registers as used since they are always used in epilogue.
>>>>
>>>> Please take a look.
>>>
>>> Oh, too bad you didn’t copy it here.  The easiest thing to blow is the addition of reload_completed && on the condition
>
>
>> static bool
>> ix86_save_reg (unsigned int regno, bool maybe_eh_return)
>> {
>>  /* In interrupt handler, we don't preserve MMX and x87 registers
>>     which aren't supported when saving and restoring registers.  No
>>     need to preserve callee-saved registers unless they are modified.
>>     We also preserve all caller-saved registers if a function call
>>     is made in interrupt handler since the called function may change
>>     them.  Don't explicitly save BP and SP registers since they are
>>     always preserved.  */
>>  if (cfun->machine->is_interrupt)
>>    return ((df_regs_ever_live_p (regno)
>>             || (call_used_regs[regno] && cfun->machine->make_calls))
>>            && !fixed_regs[regno]
>>            && !STACK_REGNO_P (regno)
>>            && !MMX_REGNO_P (regno)
>>            && regno != BP_REG
>>            && regno != SP_REG
>>            && (regno <= ST7_REG || regno >= XMM0_REG));
>>
>> Is this sufficient?
>
> I see no string "reload_completed &&”.  Either, you need it here, or, you need it in the caller.

Do you have a testcase to show its impact?

Thanks.

-- 
H.J.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-09-29 23:54         ` H.J. Lu
@ 2015-09-30  2:45           ` Mike Stump
  2015-09-30  5:21             ` H.J. Lu
  0 siblings, 1 reply; 45+ messages in thread
From: Mike Stump @ 2015-09-30  2:45 UTC (permalink / raw)
  To: H.J. Lu; +Cc: Yulia Koval, GCC Patches, Uros Bizjak

On Sep 29, 2015, at 3:40 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
> How about adding a "no_caller_saved_registers" attribute?

You can save all call clobbered registers with 3 instructions?  Really?  I’m skeptical.  Anyway, if you do this by turning off great swaths of registers, then, I guess that doesn’t surprise me.  I try and not turn any off in mine.

Now, if you turn off great swaths of registers, then you can’t actually make any calls to an abi that supports those registers as call clobbered:

> +  if (cfun->machine->is_interrupt)
> +    return ((df_regs_ever_live_p (regno)
> +	     || (call_used_regs[regno] && cfun->machine->make_calls))
> +	    && !fixed_regs[regno]
> +	    && !STACK_REGNO_P (regno)
> +	    && !MMX_REGNO_P (regno)
> +	    && regno != BP_REG
> +	    && regno != SP_REG
> +	    && (regno <= ST7_REG || regno >= XMM0_REG));

So, any calls to a function that uses any excluded register won’t work, if that register is not fixed and is call clobbered.  If you call such a function, you _must_ save and restore all those registers.  Code like:

> +  if (cfun->machine->is_interrupt && VALID_MMX_REG_MODE (mode))
> +    {
> +      error ("MMX/3Dnow instructions aren't allowed in %s service routine",
> +	     (cfun->machine->is_exception ? "exception" : "interrupt"));
> +      return;
> +    }

Does not save your from the obligation of saving such registers, if you support function calls inside an interrupt routine.  Once you add that support to make function calls work, then, you might as well lift this restriction, cause it would already just work?

If you really can save everything in 3 instructions, then there is no point to trying to enhance it more on your port.  3 instructions execute so fast as to not matter.

Now, if you ask me how I know all this, I had to debug a failure to save 1 register class in the prologue from large multi core instruction trace, and that class was the second most important class for general code gen right after the gprs.  Turns out that I killed a live variable in that class from a packet handler interrupt routine cause it failed to save/restore.  After that, I tested every register class and fixed all the issues.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-09-30  2:45           ` Mike Stump
@ 2015-09-30  5:21             ` H.J. Lu
  0 siblings, 0 replies; 45+ messages in thread
From: H.J. Lu @ 2015-09-30  5:21 UTC (permalink / raw)
  To: Mike Stump; +Cc: Yulia Koval, GCC Patches, Uros Bizjak

On Tue, Sep 29, 2015 at 5:23 PM, Mike Stump <mikestump@comcast.net> wrote:
> On Sep 29, 2015, at 3:40 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>> How about adding a "no_caller_saved_registers" attribute?
>
> You can save all call clobbered registers with 3 instructions?  Really?  I’m skeptical.  Anyway, if you do this by turning off great swaths of registers, then, I guess that doesn’t surprise me.  I try and not turn any off in mine.

We need one instruction to save one register.

> Now, if you turn off great swaths of registers, then you can’t actually make any calls to an abi that supports those registers as call clobbered:

We don't need to save and restore any registers when calling a
function marked with
"no_caller_saved_registers" attribute except for registers used to
pass parameters.
Maybe "no_caller_saved_registers" isn't a good name.

>> +  if (cfun->machine->is_interrupt)
>> +    return ((df_regs_ever_live_p (regno)
>> +          || (call_used_regs[regno] && cfun->machine->make_calls))
>> +         && !fixed_regs[regno]
>> +         && !STACK_REGNO_P (regno)
>> +         && !MMX_REGNO_P (regno)
>> +         && regno != BP_REG
>> +         && regno != SP_REG
>> +         && (regno <= ST7_REG || regno >= XMM0_REG));
>
> So, any calls to a function that uses any excluded register won’t work, if that register is not fixed and is call clobbered.  If you call such a function, you _must_ save and restore all those registers.  Code like:

We save all changed registers as well as all caller-saved registers if we make
any function call.  Shouldn't it be sufficient?

>> +  if (cfun->machine->is_interrupt && VALID_MMX_REG_MODE (mode))
>> +    {
>> +      error ("MMX/3Dnow instructions aren't allowed in %s service routine",
>> +          (cfun->machine->is_exception ? "exception" : "interrupt"));
>> +      return;
>> +    }
>
> Does not save your from the obligation of saving such registers, if you support function calls inside an interrupt routine.  Once you add that support to make function calls work, then, you might as well lift this restriction, cause it would already just work?

Due to the way how MMX/x87 registers work, we don't support using
MMX/x87 registers in
interrupt handler.  People have to write assembly codes if they have
to use them.

> If you really can save everything in 3 instructions, then there is no point to trying to enhance it more on your port.  3 instructions execute so fast as to not matter.
>
> Now, if you ask me how I know all this, I had to debug a failure to save 1 register class in the prologue from large multi core instruction trace, and that class was the second most important class for general code gen right after the gprs.  Turns out that I killed a live variable in that class from a packet handler interrupt routine cause it failed to save/restore.  After that, I tested every register class and fixed all the issues.

We include one testcase for each register class in x86, including MMX
and x87, which
gives an error.


-- 
H.J.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-09-30  1:34             ` H.J. Lu
@ 2015-09-30  5:50               ` H.J. Lu
  2015-09-30 12:41                 ` Yulia Koval
  0 siblings, 1 reply; 45+ messages in thread
From: H.J. Lu @ 2015-09-30  5:50 UTC (permalink / raw)
  To: Mike Stump; +Cc: Yulia Koval, GCC Patches, Uros Bizjak

On Tue, Sep 29, 2015 at 5:02 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Tue, Sep 29, 2015 at 4:53 PM, Mike Stump <mikestump@comcast.net> wrote:
>> On Sep 29, 2015, at 3:10 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>> On Tue, Sep 29, 2015 at 2:23 PM, Mike Stump <mikestump@comcast.net> wrote:
>>>> On Sep 29, 2015, at 1:59 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>>>> commit f3a6675a8d69d810d2cad0c090a762094a0a8622
>>>>> Author: H.J. Lu <hjl.tools@gmail.com>
>>>>> Date:   Tue Sep 29 13:47:18 2015 -0700
>>>>>
>>>>>   Define EPILOGUE_USES in i386 so that all preserved registers are used
>>>>>   by the epilogue of interrupt handler.  Don't explicitly mark BP and SP
>>>>>   registers as used since they are always used in epilogue.
>>>>>
>>>>> Please take a look.
>>>>
>>>> Oh, too bad you didn’t copy it here.  The easiest thing to blow is the addition of reload_completed && on the condition
>>
>>
>>> static bool
>>> ix86_save_reg (unsigned int regno, bool maybe_eh_return)
>>> {
>>>  /* In interrupt handler, we don't preserve MMX and x87 registers
>>>     which aren't supported when saving and restoring registers.  No
>>>     need to preserve callee-saved registers unless they are modified.
>>>     We also preserve all caller-saved registers if a function call
>>>     is made in interrupt handler since the called function may change
>>>     them.  Don't explicitly save BP and SP registers since they are
>>>     always preserved.  */
>>>  if (cfun->machine->is_interrupt)
>>>    return ((df_regs_ever_live_p (regno)
>>>             || (call_used_regs[regno] && cfun->machine->make_calls))
>>>            && !fixed_regs[regno]
>>>            && !STACK_REGNO_P (regno)
>>>            && !MMX_REGNO_P (regno)
>>>            && regno != BP_REG
>>>            && regno != SP_REG
>>>            && (regno <= ST7_REG || regno >= XMM0_REG));
>>>
>>> Is this sufficient?
>>
>> I see no string "reload_completed &&”.  Either, you need it here, or, you need it in the caller.
>
> Do you have a testcase to show its impact?

I checked this patch into hjl/interrupt/master branch.

Thanks.


-- 
H.J.
--
commit 8c75718a0f590cb02e7cb88e36fe12e90db62bc1
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Tue Sep 29 19:42:16 2015 -0700

    Preserve registers in interrupt handler after reload

    * config/i386/i386.c (ix86_save_reg): Preserve callee-saved and
    caller-saved registers in interrupt handler only after reload.

diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 6b14471..d5c7e07 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -11119,7 +11119,7 @@ ix86_save_reg (unsigned int regno, bool maybe_eh_return)
      is made in interrupt handler since the called function may change
      them.  Don't explicitly save BP and SP registers since they are
      always preserved.  */
-  if (cfun->machine->is_interrupt)
+  if (cfun->machine->is_interrupt && reload_completed)
     return ((df_regs_ever_live_p (regno)
      || (call_used_regs[regno]
  && cfun->machine->call_with_caller_saved_registers))

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-09-30  5:50               ` H.J. Lu
@ 2015-09-30 12:41                 ` Yulia Koval
  2015-09-30 13:02                   ` H.J. Lu
  2015-09-30 19:08                   ` H.J. Lu
  0 siblings, 2 replies; 45+ messages in thread
From: Yulia Koval @ 2015-09-30 12:41 UTC (permalink / raw)
  To: H.J. Lu; +Cc: Mike Stump, GCC Patches, Uros Bizjak

[-- Attachment #1: Type: text/plain, Size: 3470 bytes --]

Hi,

Thanks. I added all fixes to the patch, bootstrapped/regtested it on
Linux/x86_64. Linux/i686 in progress. Ok for trunk if testing passes
successfully?

Julia

On Wed, Sep 30, 2015 at 5:50 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Tue, Sep 29, 2015 at 5:02 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>> On Tue, Sep 29, 2015 at 4:53 PM, Mike Stump <mikestump@comcast.net> wrote:
>>> On Sep 29, 2015, at 3:10 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>>> On Tue, Sep 29, 2015 at 2:23 PM, Mike Stump <mikestump@comcast.net> wrote:
>>>>> On Sep 29, 2015, at 1:59 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>>>>> commit f3a6675a8d69d810d2cad0c090a762094a0a8622
>>>>>> Author: H.J. Lu <hjl.tools@gmail.com>
>>>>>> Date:   Tue Sep 29 13:47:18 2015 -0700
>>>>>>
>>>>>>   Define EPILOGUE_USES in i386 so that all preserved registers are used
>>>>>>   by the epilogue of interrupt handler.  Don't explicitly mark BP and SP
>>>>>>   registers as used since they are always used in epilogue.
>>>>>>
>>>>>> Please take a look.
>>>>>
>>>>> Oh, too bad you didn’t copy it here.  The easiest thing to blow is the addition of reload_completed && on the condition
>>>
>>>
>>>> static bool
>>>> ix86_save_reg (unsigned int regno, bool maybe_eh_return)
>>>> {
>>>>  /* In interrupt handler, we don't preserve MMX and x87 registers
>>>>     which aren't supported when saving and restoring registers.  No
>>>>     need to preserve callee-saved registers unless they are modified.
>>>>     We also preserve all caller-saved registers if a function call
>>>>     is made in interrupt handler since the called function may change
>>>>     them.  Don't explicitly save BP and SP registers since they are
>>>>     always preserved.  */
>>>>  if (cfun->machine->is_interrupt)
>>>>    return ((df_regs_ever_live_p (regno)
>>>>             || (call_used_regs[regno] && cfun->machine->make_calls))
>>>>            && !fixed_regs[regno]
>>>>            && !STACK_REGNO_P (regno)
>>>>            && !MMX_REGNO_P (regno)
>>>>            && regno != BP_REG
>>>>            && regno != SP_REG
>>>>            && (regno <= ST7_REG || regno >= XMM0_REG));
>>>>
>>>> Is this sufficient?
>>>
>>> I see no string "reload_completed &&”.  Either, you need it here, or, you need it in the caller.
>>
>> Do you have a testcase to show its impact?
>
> I checked this patch into hjl/interrupt/master branch.
>
> Thanks.
>
>
> --
> H.J.
> --
> commit 8c75718a0f590cb02e7cb88e36fe12e90db62bc1
> Author: H.J. Lu <hjl.tools@gmail.com>
> Date:   Tue Sep 29 19:42:16 2015 -0700
>
>     Preserve registers in interrupt handler after reload
>
>     * config/i386/i386.c (ix86_save_reg): Preserve callee-saved and
>     caller-saved registers in interrupt handler only after reload.
>
> diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
> index 6b14471..d5c7e07 100644
> --- a/gcc/config/i386/i386.c
> +++ b/gcc/config/i386/i386.c
> @@ -11119,7 +11119,7 @@ ix86_save_reg (unsigned int regno, bool maybe_eh_return)
>       is made in interrupt handler since the called function may change
>       them.  Don't explicitly save BP and SP registers since they are
>       always preserved.  */
> -  if (cfun->machine->is_interrupt)
> +  if (cfun->machine->is_interrupt && reload_completed)
>      return ((df_regs_ever_live_p (regno)
>       || (call_used_regs[regno]
>   && cfun->machine->call_with_caller_saved_registers))

[-- Attachment #2: interrupt_patch --]
[-- Type: application/octet-stream, Size: 52581 bytes --]

diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index 6a17ef4..c7ff393 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -278,6 +278,8 @@ extern rtx maybe_get_pool_constant (rtx);
 extern char internal_label_prefix[16];
 extern int internal_label_prefix_len;
 
+extern bool ix86_epilogue_uses (int);
+
 enum ix86_address_seg { SEG_DEFAULT, SEG_FS, SEG_GS };
 struct ix86_address
 {
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index be639e0..d5c7e07 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -2381,6 +2381,8 @@ struct ix86_frame
 {
   int nsseregs;
   int nregs;
+  int nbndregs;
+  int nmaskregs;
   int va_arg_size;
   int red_zone_size;
   int outgoing_arguments_size;
@@ -6241,6 +6243,24 @@ ix86_set_current_function (tree fndecl)
       return;
     }
 
+  if (lookup_attribute ("interrupt", DECL_ATTRIBUTES (fndecl)))
+    {
+      cfun->machine->is_interrupt = true;
+      int nargs = 0;
+      for (tree arg = DECL_ARGUMENTS (fndecl);
+	   arg;
+	   arg = TREE_CHAIN (arg))
+	{
+	  /* Mark arguments in interrupt handler as used to silence
+	     compiler warning since arguments in interrupt handler
+	     are mandatory even if they aren't used.  */
+	  TREE_USED (arg) = 1;
+	  nargs++;
+	}
+      if (nargs == 2)
+        cfun->machine->is_exception = true;
+    }
+
   tree new_tree = DECL_FUNCTION_SPECIFIC_TARGET (fndecl);
   if (new_tree == NULL_TREE)
     new_tree = target_option_default_node;
@@ -6519,6 +6539,11 @@ ix86_function_ok_for_sibcall (tree decl, tree exp)
   tree type, decl_or_type;
   rtx a, b;
 
+  /* Sibling call isn't OK in interrupt handler since it must return
+     with the "IRET" instruction.  */
+  if (cfun->machine->is_interrupt)
+    return false;
+
   /* If we are generating position-independent code, we cannot sibcall
      optimize direct calls to global functions, as the PLT requires
      %ebx be live. (Darwin does not have a PLT.)  */
@@ -7703,6 +7728,7 @@ type_natural_mode (const_tree type, const CUMULATIVE_ARGS *cum,
 		      }
 		  }
 		else if ((size == 8 && !TARGET_64BIT)
+			 && (!cfun || !cfun->machine->is_interrupt)
 			 && !TARGET_MMX
 			 && !TARGET_IAMCU)
 		  {
@@ -8682,6 +8708,11 @@ ix86_function_arg_advance (cumulative_args_t cum_v, machine_mode mode,
   HOST_WIDE_INT bytes, words;
   int nregs;
 
+  /* The argument of interrupt handler is a special case and is
+     handled in ix86_function_arg.  */
+  if (!cum->caller && cfun->machine->is_interrupt)
+    return;
+
   if (mode == BLKmode)
     bytes = int_size_in_bytes (type);
   else
@@ -8998,6 +9029,39 @@ ix86_function_arg (cumulative_args_t cum_v, machine_mode omode,
   HOST_WIDE_INT bytes, words;
   rtx arg;
 
+  if (!cum->caller && cfun->machine->is_interrupt)
+    {
+      /* The first argument of interrupt handler is a pointer and
+	 points to the return address on stack.  The optional second
+	 argument is an integer for error code on stack.  */
+      gcc_assert (type != NULL_TREE);
+      if (POINTER_TYPE_P (type))
+	{
+	  if (cfun->machine->is_exception)
+	    /* (AP) in the current frame in exception handler.  */
+	    arg = arg_pointer_rtx;
+	  else
+	    /* -WORD(AP) in the current frame in interrupt handler.  */
+	    arg = plus_constant (Pmode, arg_pointer_rtx,
+				 -UNITS_PER_WORD);
+	  if (mode != Pmode)
+	    arg = convert_to_mode (mode, arg, 1);
+	}
+      else
+	{
+	  gcc_assert (TREE_CODE (type) == INTEGER_TYPE
+		      && cfun->machine->is_exception
+		      && mode == word_mode);
+	  /* The error code is at -WORD(AP) in the current frame in
+	     exception handler.  */
+	  arg = gen_rtx_MEM (word_mode,
+			     plus_constant (Pmode, arg_pointer_rtx,
+					    -UNITS_PER_WORD));
+	}
+
+      return arg;
+    }
+
   /* All pointer bounds arguments are handled separately here.  */
   if ((type && POINTER_BOUNDS_TYPE_P (type))
       || POINTER_BOUNDS_MODE_P (mode))
@@ -10713,7 +10777,10 @@ ix86_can_use_return_insn_p (void)
 {
   struct ix86_frame frame;
 
-  if (! reload_completed || frame_pointer_needed)
+  /* Don't use `ret' instruction in interrupt handler.  */
+  if (! reload_completed
+      || frame_pointer_needed
+      || cfun->machine->is_interrupt)
     return 0;
 
   /* Don't allow more than 32k pop, since that's all we can do
@@ -11023,11 +11090,46 @@ ix86_select_alt_pic_regnum (void)
   return INVALID_REGNUM;
 }
 
+/* Return true if REGNO is used by the epilogue.  */
+
+bool
+ix86_epilogue_uses (int regno)
+{
+  /* All preserved registers are used by the epilogue of interrupt
+     handler.  Don't explicitly mark BP and SP registers as used
+     since they are always used in epilogue.  */
+  return (cfun->machine->is_interrupt
+	  && reg_names[regno][0]
+	  && !STACK_REGNO_P (regno)
+	  && !MMX_REGNO_P (regno)
+	  && regno != BP_REG
+	  && regno != SP_REG
+	  && (regno <= ST7_REG || regno >= XMM0_REG));
+}
+
 /* Return TRUE if we need to save REGNO.  */
 
 static bool
 ix86_save_reg (unsigned int regno, bool maybe_eh_return)
 {
+  /* In interrupt handler, we don't preserve MMX and x87 registers
+     which aren't supported when saving and restoring registers.  No
+     need to preserve callee-saved registers unless they are modified.
+     We also preserve all caller-saved registers if a function call
+     is made in interrupt handler since the called function may change
+     them.  Don't explicitly save BP and SP registers since they are
+     always preserved.  */
+  if (cfun->machine->is_interrupt && reload_completed)
+    return ((df_regs_ever_live_p (regno)
+	     || (call_used_regs[regno]
+		 && cfun->machine->call_with_caller_saved_registers))
+	    && !fixed_regs[regno]
+	    && !STACK_REGNO_P (regno)
+	    && !MMX_REGNO_P (regno)
+	    && regno != BP_REG
+	    && regno != SP_REG
+	    && (regno <= ST7_REG || regno >= XMM0_REG));
+
   if (regno == REAL_PIC_OFFSET_TABLE_REGNUM
       && pic_offset_table_rtx)
     {
@@ -11084,6 +11186,34 @@ ix86_nsaved_regs (void)
   return nregs;
 }
 
+/* Return number of saved bound registers.  */
+
+static int
+ix86_nsaved_bndregs (void)
+{
+  int nregs = 0;
+  int regno;
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (BND_REGNO_P (regno) && ix86_save_reg (regno, true))
+      nregs ++;
+  return nregs;
+}
+
+/* Return number of saved mask registers.  */
+
+static int
+ix86_nsaved_maskregs (void)
+{
+  int nregs = 0;
+  int regno;
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (MASK_REGNO_P (regno) && ix86_save_reg (regno, true))
+      nregs ++;
+  return nregs;
+}
+
 /* Return number of saved SSE registers.  */
 
 static int
@@ -11092,7 +11222,8 @@ ix86_nsaved_sseregs (void)
   int nregs = 0;
   int regno;
 
-  if (!TARGET_64BIT_MS_ABI)
+  /* Always need to save SSE registers in interrupt handler.  */
+  if (!TARGET_64BIT_MS_ABI && !cfun->machine->is_interrupt)
     return 0;
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
@@ -11161,6 +11292,18 @@ ix86_builtin_setjmp_frame_value (void)
 
 #define SPLIT_STACK_AVAILABLE 256
 
+/* Register save area size.  */
+
+static unsigned int
+ix86_reg_save_area_size (struct ix86_frame *frame)
+{
+  unsigned int size = ((frame->nregs - frame->nbndregs
+			- frame->nmaskregs) * UNITS_PER_WORD);
+  size += frame->nbndregs * (ix86_pmode == PMODE_DI ? 16 : 8);
+  size += frame->nmaskregs * (TARGET_AVX512BW ? 8 : 2);
+  return size;
+}
+
 /* Fill structure ix86_frame about frame of currently computed function.  */
 
 static void
@@ -11173,6 +11316,8 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   HOST_WIDE_INT to_allocate;
 
   frame->nregs = ix86_nsaved_regs ();
+  frame->nbndregs = ix86_nsaved_bndregs ();
+  frame->nmaskregs = ix86_nsaved_maskregs ();
   frame->nsseregs = ix86_nsaved_sseregs ();
 
   /* 64-bit MS ABI seem to require stack alignment to be always 16 except for
@@ -11244,11 +11389,16 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
 	   = !expensive_function_p (count);
     }
 
+  /* We must use move to save bound and mask registers.  */
   frame->save_regs_using_mov
-    = (TARGET_PROLOGUE_USING_MOVE && cfun->machine->use_fast_prologue_epilogue
-       /* If static stack checking is enabled and done with probes,
-	  the registers need to be saved before allocating the frame.  */
-       && flag_stack_check != STATIC_BUILTIN_STACK_CHECK);
+    = (frame->nbndregs > 0
+       || frame->nmaskregs > 0
+       || (TARGET_PROLOGUE_USING_MOVE
+	   && cfun->machine->use_fast_prologue_epilogue
+	   /* If static stack checking is enabled and done with probes,
+	      the registers need to be saved before allocating the
+	      frame.  */
+	   && flag_stack_check != STATIC_BUILTIN_STACK_CHECK));
 
   /* Skip return address.  */
   offset = UNITS_PER_WORD;
@@ -11266,7 +11416,7 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   frame->hard_frame_pointer_offset = offset;
 
   /* Register save area */
-  offset += frame->nregs * UNITS_PER_WORD;
+  offset += ix86_reg_save_area_size (frame);
   frame->reg_save_offset = offset;
 
   /* On SEH target, registers are pushed just before the frame pointer
@@ -11280,9 +11430,17 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
       /* The only ABI that has saved SSE registers (Win64) also has a
          16-byte aligned default stack, and thus we don't need to be
 	 within the re-aligned local stack frame to save them.  */
-      gcc_assert (INCOMING_STACK_BOUNDARY >= 128);
-      offset = (offset + 16 - 1) & -16;
-      offset += frame->nsseregs * 16;
+      if (cfun->machine->is_interrupt)
+	/* We must save full vector registers in interrupt handler.  */
+	offset += frame->nsseregs * (TARGET_AVX512F
+				     ? 64
+				     : (TARGET_AVX ? 32 : 16));
+      else
+	{
+	  gcc_assert (INCOMING_STACK_BOUNDARY >= 128);
+	  offset = (offset + 16 - 1) & -16;
+	  offset += frame->nsseregs * 16;
+	}
     }
   frame->sse_reg_save_offset = offset;
 
@@ -11338,8 +11496,12 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   /* Size prologue needs to allocate.  */
   to_allocate = offset - frame->sse_reg_save_offset;
 
-  if ((!to_allocate && frame->nregs <= 1)
-      || (TARGET_64BIT && to_allocate >= (HOST_WIDE_INT) 0x80000000))
+  /* We must use move to save bound and mask registers.  */
+  if (frame->nbndregs == 0
+      && frame->nmaskregs == 0
+      && ((!to_allocate && frame->nregs <= 1)
+	   || (TARGET_64BIT
+	       && to_allocate >= (HOST_WIDE_INT) 0x80000000)))
     frame->save_regs_using_mov = false;
 
   if (ix86_using_red_zone ()
@@ -11349,7 +11511,7 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
     {
       frame->red_zone_size = to_allocate;
       if (frame->save_regs_using_mov)
-	frame->red_zone_size += frame->nregs * UNITS_PER_WORD;
+	frame->red_zone_size += ix86_reg_save_area_size (frame);
       if (frame->red_zone_size > RED_ZONE_SIZE - RED_ZONE_RESERVE)
 	frame->red_zone_size = RED_ZONE_SIZE - RED_ZONE_RESERVE;
     }
@@ -11509,8 +11671,14 @@ ix86_emit_save_reg_using_mov (machine_mode mode, unsigned int regno,
   addr = choose_baseaddr (cfa_offset);
   mem = gen_frame_mem (mode, addr);
 
-  /* For SSE saves, we need to indicate the 128-bit alignment.  */
-  set_mem_align (mem, GET_MODE_ALIGNMENT (mode));
+  /* For SSE saves, we need to indicate the 128-bit alignment.  In
+     interrupt handler, stack is only aligned to word_mode.  We can't
+     use gen_sse_storeups since RTX_FRAME_RELATED_P is set and
+     dwarf2out_flush_queued_reg_saves doesn't like UNSPEC_STOREU.
+     Also gen_sse_storeups doesn't cover AVX nor AVX512.  */
+  set_mem_align (mem,
+		 GET_MODE_ALIGNMENT (cfun->machine->is_interrupt
+				     ? word_mode : mode));
 
   insn = emit_move_insn (mem, reg);
   RTX_FRAME_RELATED_P (insn) = 1;
@@ -11571,8 +11739,9 @@ ix86_emit_save_regs_using_mov (HOST_WIDE_INT cfa_offset)
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (!SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
       {
-        ix86_emit_save_reg_using_mov (word_mode, regno, cfa_offset);
-	cfa_offset -= UNITS_PER_WORD;
+	enum machine_mode reg_mode = GET_MODE (regno_reg_rtx[regno]);
+	ix86_emit_save_reg_using_mov (reg_mode, regno, cfa_offset);
+	cfa_offset -= GET_MODE_SIZE (reg_mode);
       }
 }
 
@@ -11582,12 +11751,26 @@ static void
 ix86_emit_save_sse_regs_using_mov (HOST_WIDE_INT cfa_offset)
 {
   unsigned int regno;
+  enum machine_mode vector_reg_mode;
+
+  if (cfun->machine->is_interrupt)
+    {
+      /* We must save full vector registers in interrupt handler.  */
+      if (TARGET_AVX512F)
+	vector_reg_mode = V16SFmode;
+      else if (TARGET_AVX)
+	vector_reg_mode = V8SFmode;
+      else
+	vector_reg_mode = V4SFmode;
+    }
+  else
+    vector_reg_mode = V4SFmode;
 
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
       {
-	ix86_emit_save_reg_using_mov (V4SFmode, regno, cfa_offset);
-	cfa_offset -= 16;
+	ix86_emit_save_reg_using_mov (vector_reg_mode, regno, cfa_offset);
+	cfa_offset -= GET_MODE_SIZE (vector_reg_mode);
       }
 }
 
@@ -13032,12 +13215,13 @@ ix86_emit_restore_regs_using_mov (HOST_WIDE_INT cfa_offset,
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (!SSE_REGNO_P (regno) && ix86_save_reg (regno, maybe_eh_return))
       {
-	rtx reg = gen_rtx_REG (word_mode, regno);
+	enum machine_mode reg_mode = GET_MODE (regno_reg_rtx[regno]);
+	rtx reg = gen_rtx_REG (reg_mode, regno);
 	rtx mem;
 	rtx_insn *insn;
 
 	mem = choose_baseaddr (cfa_offset);
-	mem = gen_frame_mem (word_mode, mem);
+	mem = gen_frame_mem (reg_mode, mem);
 	insn = emit_move_insn (reg, mem);
 
         if (m->fs.cfa_reg == crtl->drap_reg && regno == REGNO (crtl->drap_reg))
@@ -13056,7 +13240,7 @@ ix86_emit_restore_regs_using_mov (HOST_WIDE_INT cfa_offset,
 	else
 	  ix86_add_cfa_restore_note (NULL, reg, cfa_offset);
 
-	cfa_offset -= UNITS_PER_WORD;
+	cfa_offset -= GET_MODE_SIZE (reg_mode);
       }
 }
 
@@ -13067,21 +13251,39 @@ ix86_emit_restore_sse_regs_using_mov (HOST_WIDE_INT cfa_offset,
 				      bool maybe_eh_return)
 {
   unsigned int regno;
+  enum machine_mode vector_reg_mode;
+
+  if (cfun->machine->is_interrupt)
+    {
+      /* We must restore full vector registers in interrupt handler.  */
+      if (TARGET_AVX512F)
+	vector_reg_mode = V16SFmode;
+      else if (TARGET_AVX)
+	vector_reg_mode = V8SFmode;
+      else
+	vector_reg_mode = V4SFmode;
+    }
+  else
+    vector_reg_mode = V4SFmode;
+
 
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, maybe_eh_return))
       {
-	rtx reg = gen_rtx_REG (V4SFmode, regno);
+	rtx reg = gen_rtx_REG (vector_reg_mode, regno);
 	rtx mem;
 
 	mem = choose_baseaddr (cfa_offset);
-	mem = gen_rtx_MEM (V4SFmode, mem);
-	set_mem_align (mem, 128);
+	mem = gen_rtx_MEM (vector_reg_mode, mem);
+	/* In interrupt handler, stack is only aligned to word_mode.  */
+	set_mem_align (mem, (cfun->machine->is_interrupt
+			     ? GET_MODE_ALIGNMENT (word_mode)
+			     : 128));
 	emit_move_insn (reg, mem);
 
 	ix86_add_cfa_restore_note (NULL, reg, cfa_offset);
 
-	cfa_offset -= 16;
+	cfa_offset -= GET_MODE_SIZE (vector_reg_mode);
       }
 }
 
@@ -13145,8 +13347,9 @@ ix86_expand_epilogue (int style)
   if (crtl->calls_eh_return && style != 2)
     frame.reg_save_offset -= 2 * UNITS_PER_WORD;
 
-  /* EH_RETURN requires the use of moves to function properly.  */
-  if (crtl->calls_eh_return)
+  /* EH_RETURN requires the use of moves to function properly.  We must
+     use move to restore bound and mask registers.  */
+  if (crtl->calls_eh_return || frame.nbndregs > 0 || frame.nmaskregs > 0)
     restore_regs_via_mov = true;
   /* SEH requires the use of pops to identify the epilogue.  */
   else if (TARGET_SEH)
@@ -13387,7 +13590,20 @@ ix86_expand_epilogue (int style)
       return;
     }
 
-  if (crtl->args.pops_args && crtl->args.size)
+  if (cfun->machine->is_interrupt)
+    {
+      /* Return with the "IRET" instruction from interrupt handler.
+	 Pop the 'ERROR_CODE' off the stack before the 'IRET'
+	 instruction in exception handler.  */
+      if (cfun->machine->is_exception)
+	{
+	  rtx r = plus_constant (Pmode, stack_pointer_rtx,
+				 UNITS_PER_WORD);
+	  emit_insn (gen_rtx_SET (stack_pointer_rtx, r));
+	}
+      emit_jump_insn (gen_interrupt_return ());
+    }
+  else if (crtl->args.pops_args && crtl->args.size)
     {
       rtx popc = GEN_INT (crtl->args.pops_args);
 
@@ -18360,6 +18576,13 @@ ix86_expand_move (machine_mode mode, rtx operands[])
   rtx op0, op1;
   enum tls_model model;
 
+  if (cfun->machine->is_interrupt && IS_STACK_MODE (mode))
+    {
+      error ("80387 instructions aren't allowed in %s service routine",
+	     (cfun->machine->is_exception ? "exception" : "interrupt"));
+      return;
+    }
+
   op0 = operands[0];
   op1 = operands[1];
 
@@ -18504,6 +18727,13 @@ ix86_expand_vector_move (machine_mode mode, rtx operands[])
   rtx op0 = operands[0], op1 = operands[1];
   unsigned int align = GET_MODE_ALIGNMENT (mode);
 
+  if (cfun->machine->is_interrupt && VALID_MMX_REG_MODE (mode))
+    {
+      error ("MMX/3Dnow instructions aren't allowed in %s service routine",
+	     (cfun->machine->is_exception ? "exception" : "interrupt"));
+      return;
+    }
+
   if (push_operand (op0, VOIDmode))
     op0 = emit_move_resolve_push (mode, op0);
 
@@ -26590,6 +26820,12 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
   rtx use = NULL, call;
   unsigned int vec_len = 0;
 
+  if (GET_CODE (XEXP (fnaddr, 0)) == SYMBOL_REF
+      && SYMBOL_REF_DECL ((XEXP (fnaddr, 0))) != NULL_TREE
+      && lookup_attribute ("interrupt",
+			   DECL_ATTRIBUTES (SYMBOL_REF_DECL (XEXP (fnaddr, 0)))))
+    error ("interrupt service routine can't be called directly");
+
   if (pop == const0_rtx)
     pop = NULL;
   gcc_assert (!TARGET_64BIT || !pop);
@@ -26708,6 +26944,8 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
   if (use)
     CALL_INSN_FUNCTION_USAGE (call) = use;
 
+  cfun->machine->call_with_caller_saved_registers = true;
+
   return call;
 }
 
@@ -44068,6 +44306,56 @@ ix86_handle_fndecl_attribute (tree *node, tree name, tree, int,
   return NULL_TREE;
 }
 
+
+static tree
+ix86_handle_interrupt_attribute (tree *node, tree name, tree, int,
+				 bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+	       name);
+      *no_add_attrs = true;
+    }
+
+  /* DECL_RESULT and DECL_ARGUMENTS do not exist there yet,
+     but the function type contains args and return type data.  */
+  tree func_type = TREE_TYPE (*node);
+  tree return_type = TREE_TYPE (func_type);
+
+  int nargs = 0;
+  tree current_arg_type = TYPE_ARG_TYPES (func_type);
+  while (current_arg_type
+	 && ! VOID_TYPE_P (TREE_VALUE (current_arg_type)))
+    {
+      if (nargs == 0)
+	{
+	  if (! POINTER_TYPE_P (TREE_VALUE (current_arg_type)))
+	    error ("interrupt service routine should have a pointer "
+		   "as the first argument");
+	}
+      else if (nargs == 1)
+	{
+	  if (TREE_CODE (TREE_VALUE (current_arg_type)) != INTEGER_TYPE
+	      || TYPE_MODE (TREE_VALUE (current_arg_type)) != word_mode)
+	    error ("interrupt service routine should have unsigned %s"
+		   "int as the second argument",
+		   TARGET_64BIT
+		   ? (TARGET_X32 ? "long long " : "long ")
+		   : "");
+	}
+      nargs++;
+      current_arg_type = TREE_CHAIN (current_arg_type);
+    }
+  if (!nargs || nargs > 2)
+    error ("interrupt service routine can only have a pointer argument "
+	   "and an optional integer argument");
+  if (! VOID_TYPE_P (return_type))
+    error ("interrupt service routine can't have non-void return value");
+
+  return NULL_TREE;
+}
+
 static bool
 ix86_ms_bitfield_layout_p (const_tree record_type)
 {
@@ -48069,6 +48357,9 @@ static const struct attribute_spec ix86_attribute_table[] =
     false },
   { "callee_pop_aggregate_return", 1, 1, false, true, true,
     ix86_handle_callee_pop_aggregate_return, true },
+  { "interrupt", 0, 0, true, false, false,
+    ix86_handle_interrupt_attribute, false },
+
   /* End element.  */
   { NULL,        0, 0, false, false, false, NULL, false }
 };
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 96ba90f..237bd88 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -1730,6 +1730,11 @@ typedef struct ix86_args {
 
 #define EXIT_IGNORE_STACK 1
 
+/* Define this macro as a C expression that is nonzero for registers
+   used by the epilogue or the `return' pattern.  */
+
+#define EPILOGUE_USES(REGNO) ix86_epilogue_uses (REGNO)
+
 /* Output assembler code for a block containing the constant parts
    of a trampoline, leaving space for the variable parts.  */
 
@@ -2494,6 +2499,18 @@ struct GTY(()) machine_function {
   /* If true, it is safe to not save/restore DRAP register.  */
   BOOL_BITFIELD no_drap_save_restore : 1;
 
+  /* If true, the current function is an interrupt function as
+     specified by the "interrupt" or "exception" attribute.  */
+  BOOL_BITFIELD is_interrupt : 1;
+
+  /* If true, the current function is an interrupt function as
+     specified by the "exception" attribute.  */
+  BOOL_BITFIELD is_exception : 1;
+
+  /* If true, the current function makes any function calls with
+     caller-saved registers.  */
+  BOOL_BITFIELD call_with_caller_saved_registers : 1;
+
   /* If true, there is register available for argument passing.  This
      is used only in ix86_function_ok_for_sibcall by 32-bit to determine
      if there is scratch register available for indirect sibcall.  In
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 8c2ed60..8b22be4 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -193,6 +193,9 @@
   UNSPEC_BNDCU
   UNSPEC_BNDCN
   UNSPEC_MPX_FENCE
+
+  ;; IRET support
+  UNSPEC_INTERRUPT_RETURN
 ])
 
 (define_c_enum "unspecv" [
@@ -12179,6 +12182,12 @@
    (set_attr "modrm" "0")
    (set_attr "maybe_prefix_bnd" "1")])
 
+(define_insn "interrupt_return"
+  [(simple_return)
+   (unspec [(const_int 0)] UNSPEC_INTERRUPT_RETURN)]
+  "reload_completed"
+  "iret")
+
 ;; Used by x86_machine_dependent_reorg to avoid penalty on single byte RET
 ;; instruction Athlon and K8 have.
 
diff --git a/gcc/config/i386/sse.md b/gcc/config/i386/sse.md
index 4eefb45..3f932a7 100644
--- a/gcc/config/i386/sse.md
+++ b/gcc/config/i386/sse.md
@@ -875,10 +875,14 @@
 	case MODE_V16SF:
 	case MODE_V8SF:
 	case MODE_V4SF:
-	  if (TARGET_AVX
+	  /* We must supprt misaligned SSE load and store in interrupt
+	     handler since ix86_emit_save_reg_using_mov generates the
+	     normal *mov<mode>_internal pattern for interrupt handler
+	     with misaligned stack.  */
+	  if ((TARGET_AVX || cfun->machine->is_interrupt)
 	      && (misaligned_operand (operands[0], <MODE>mode)
 		  || misaligned_operand (operands[1], <MODE>mode)))
-	    return "vmovups\t{%1, %0|%0, %1}";
+	    return "%vmovups\t{%1, %0|%0, %1}";
 	  else
 	    return "%vmovaps\t{%1, %0|%0, %1}";
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 8406945..9f89e99 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -5061,6 +5061,62 @@ On x86-32 targets, the @code{stdcall} attribute causes the compiler to
 assume that the called function pops off the stack space used to
 pass arguments, unless it takes a variable number of arguments.
 
+@item interrupt
+@cindex @code{interrupt} function attribute, x86
+Use this attribute to indicate that the specified function is an
+interrupt handler.  The compiler generates function
+entry and exit sequences suitable for use in an interrupt handler when
+this attribute is present.  The @code{IRET} instruction, instead of the
+@code{RET} instruction, is used to return from interrupt handlers.  All
+registers, except for the EFLAGS register which is restored by the
+@code{IRET} instruction, are preserved by the compiler.
+
+Any interruptible-without-stack-switch code must be compiled with
+@option{-mno-red-zone} since interrupt handlers can and will, because
+of the hardware design, touch the red zone.
+
+An interrupt handler must be declared with a mandatory pointer
+argument:
+
+@smallexample
+struct interrupt_frame;
+
+__attribute__ ((interrupt))
+void
+f (struct interrupt_frame *frame)
+@{
+@}
+@end smallexample
+
+and user must properly define the structure the pointer pointing to.
+
+The exception handler is very similar to the interrupt handler with
+a different mandatory function signature:
+
+@smallexample
+#ifdef __x86_64__
+typedef unsigned long long int uword_t;
+#else
+typedef unsigned int uword_t;
+#endif
+
+struct interrupt_frame;
+
+__attribute__ ((interrupt))
+void
+f (struct interrupt_frame *frame, uword_t error_code)
+@{
+  ...
+@}
+@end smallexample
+
+and compiler pops the error code off the stack before the @code{IRET}
+instruction.
+
+The exception handler should only be used for exceptions which push an
+error code and all other exceptions must use the interrupt handler.
+The system will crash if the wrong handler is used.
+
 @item target (@var{options})
 @cindex @code{target} function attribute
 As discussed in @ref{Common Function Attributes}, this attribute 
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-1.c b/gcc/testsuite/gcc.target/i386/interrupt-1.c
new file mode 100644
index 0000000..ea2f1e4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-1.c
@@ -0,0 +1,46 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-push-args" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern int bar (int);
+
+void foo (void *frame)
+{
+  int a,b,c,d,e,f,i;
+  a = bar (5);
+  b = bar (a);
+  c = bar (b);
+  d = bar (c);
+  e = bar (d);
+  f = bar (e);
+  for (i = 1; i < 10; i++)
+  {
+    a += bar (a + i) + bar (b + i) +
+	 bar (c + i) + bar (d + i) +
+	 bar (e + i) + bar (f + i);
+  }
+}
+
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-10.c b/gcc/testsuite/gcc.target/i386/interrupt-10.c
new file mode 100644
index 0000000..a439d6f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-10.c
@@ -0,0 +1,24 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mavx512bw -mno-iamcu" } */
+
+extern void bar (void);
+
+struct interrupt_frame;
+
+void
+ __attribute__ ((interrupt))
+foo (struct interrupt_frame *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm\[0-9\]+" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "kmovq\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" 8 } } */
+/* { dg-final { scan-assembler-times "kmovq\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" 8 } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-11.c b/gcc/testsuite/gcc.target/i386/interrupt-11.c
new file mode 100644
index 0000000..6556c44
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-11.c
@@ -0,0 +1,38 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-sse -mno-iamcu" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-12.c b/gcc/testsuite/gcc.target/i386/interrupt-12.c
new file mode 100644
index 0000000..5c9eb74
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-12.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int check_int (int *i, void *, int align);
+typedef int aligned __attribute__((aligned(64)));
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+__attribute__((interrupt))
+void
+foo (void *frame, uword_t error_code)
+{
+  aligned j;
+  if (check_int (frame, &j, __alignof__(j)))
+    __builtin_abort ();
+}
+
+/* { dg-final { scan-assembler-times "and\[lq\]?\[^\\n\]*-64,\[^\\n\]*sp" 1 } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-13.c b/gcc/testsuite/gcc.target/i386/interrupt-13.c
new file mode 100644
index 0000000..8cf0b25
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-13.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int check_int (int *i, void *, int align);
+typedef int aligned __attribute__((aligned(64)));
+
+__attribute__((interrupt))
+void
+foo (void *frame)
+{
+  aligned j;
+  if (check_int (frame, &j, __alignof__(j)))
+    __builtin_abort ();
+}
+
+/* { dg-final { scan-assembler-times "and\[lq\]?\[^\\n\]*-64,\[^\\n\]*sp" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-14.c b/gcc/testsuite/gcc.target/i386/interrupt-14.c
new file mode 100644
index 0000000..7394207
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-14.c
@@ -0,0 +1,39 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-sse -mno-iamcu -mno-sse4 -mno-popcnt" } */
+
+extern int i, cnt;
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  cnt = __builtin_popcount (i);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 1 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-2.c b/gcc/testsuite/gcc.target/i386/interrupt-2.c
new file mode 100644
index 0000000..2003fbb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-2.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wall -Wunused-parameter" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+}
+
+/* { dg-final { scan-assembler-not "add(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-3.c b/gcc/testsuite/gcc.target/i386/interrupt-3.c
new file mode 100644
index 0000000..a87e594
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-3.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+void
+__attribute__((interrupt))
+fn (void* frame, uword_t error)
+{
+}
+
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-387-err.c b/gcc/testsuite/gcc.target/i386/interrupt-387-err.c
new file mode 100644
index 0000000..3a4a5cf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-387-err.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -m80387 -mlong-double-80 -mno-iamcu" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+extern long double y, x;
+
+void
+fn0 (void)
+{
+  x = y;
+  y = 0;
+}
+
+void
+__attribute__((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  x = 0;
+} /* { dg-error "80387 instructions aren't allowed in exception service routine" } */
+
+void
+__attribute__((interrupt))
+fn2 (void *frame)
+{
+  x = y; /* { dg-error "80387 instructions aren't allowed in interrupt service routine" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-4.c b/gcc/testsuite/gcc.target/i386/interrupt-4.c
new file mode 100644
index 0000000..94230cd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-4.c
@@ -0,0 +1,32 @@
+/* { dg-do link } */
+/* { dg-options "-O" } */
+
+#include <stdint.h>
+
+extern void link_error (void);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+struct interrupt_frame
+{
+  uword_t ip;
+  uword_t cs;
+  uword_t flags;
+  uword_t sp;
+  uword_t ss;
+};
+
+__attribute__ ((used, interrupt))
+void
+foo (struct interrupt_frame *frame)
+{
+  void *ra = __builtin_return_address (0);
+  if ((uintptr_t) ra != (uintptr_t) frame->ip)
+    link_error ();
+}
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-5.c b/gcc/testsuite/gcc.target/i386/interrupt-5.c
new file mode 100644
index 0000000..53d6656
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-5.c
@@ -0,0 +1,23 @@
+/* { dg-do link } */
+/* { dg-options "-O" } */
+
+#include <stdint.h>
+
+extern void link_error (void);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+__attribute__ ((used, interrupt))
+void
+foo (void *frame, uword_t error)
+{
+  void *ra = __builtin_return_address (0);
+  if ((uintptr_t) ra != (uintptr_t) error)
+    link_error ();
+}
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-6.c b/gcc/testsuite/gcc.target/i386/interrupt-6.c
new file mode 100644
index 0000000..a1a3d0e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-6.c
@@ -0,0 +1,40 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+extern int error;
+
+__attribute__((interrupt))
+void
+fn1 (void *p, short error_code)
+{ /* { dg-error "interrupt service routine should have unsigned \(long long |long |\)int as the second argument" } */
+}
+
+__attribute__((interrupt))
+void
+fn2 (void)
+{ /* { dg-error "interrupt service routine can only have a pointer argument and an optional integer argument" } */
+}
+
+__attribute__((interrupt))
+void
+fn3 (uword_t error_code)
+{ /* { dg-error "interrupt service routine should have a pointer as the first argument" } */
+  error = error_code;
+}
+
+__attribute__((interrupt))
+void
+fn4 (uword_t error_code, void *frame)
+{ /* { dg-error "interrupt service routine should have .* the .* argument" } */
+  error = error_code;
+}
+
+extern int fn5 (void *) __attribute__ ((interrupt)); /* { dg-error "interrupt service routine can't have non-void return value" } */
+
+int
+fn5 (void *frame)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-7.c b/gcc/testsuite/gcc.target/i386/interrupt-7.c
new file mode 100644
index 0000000..e7441a2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-7.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int error;
+
+extern void fn (void *) __attribute__((interrupt));
+
+void
+foo (void)
+{
+  fn (&error); /* { dg-error "interrupt service routine can't be called directly" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-8.c b/gcc/testsuite/gcc.target/i386/interrupt-8.c
new file mode 100644
index 0000000..0a83473
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-8.c
@@ -0,0 +1,20 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 16 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm\[0-9\]+" 16 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-9.c b/gcc/testsuite/gcc.target/i386/interrupt-9.c
new file mode 100644
index 0000000..9d0c776
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-9.c
@@ -0,0 +1,22 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512bw -mno-iamcu -mavx512f" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm\[0-9\]+" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "kmovw\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" 8 } } */
+/* { dg-final { scan-assembler-times "kmovw\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" 8 } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-bnd.c b/gcc/testsuite/gcc.target/i386/interrupt-bnd.c
new file mode 100644
index 0000000..9f50661
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-bnd.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mno-iamcu -mmpx" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "bnd3");
+}
+
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*%bnd3,\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%bnd3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
new file mode 100644
index 0000000..77a9463
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
@@ -0,0 +1,30 @@
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -miamcu" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern int bar (int);
+
+void foo (void *frame)
+{
+  int a,b,c,d,e,f,i;
+  a = bar (5);
+  b = bar (a);
+  c = bar (b);
+  d = bar (c);
+  e = bar (d);
+  f = bar (e);
+  for (i = 1; i < 10; i++)
+    a += bar (a + i) + bar (b + i) +
+	 bar (c + i) + bar (d + i) +
+	 bar (e + i) + bar (f+i);
+}
+
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%eax" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%ecx" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%edx" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%eax" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%ecx" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%edx" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c
new file mode 100644
index 0000000..fc4f367
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mmmx -mno-iamcu" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+typedef short __v4hi __attribute__ ((__vector_size__ (8)));
+typedef int __m64 __attribute__ ((__vector_size__ (8), __may_alias__));
+
+extern __m64 y, x;
+
+void
+fn0 (void)
+{
+  x = __extension__ (__m64){ 0 };
+  x = (__m64) __builtin_ia32_packsswb ((__v4hi) x, (__v4hi) y);
+  y = x;
+}
+
+void
+__attribute__((interrupt))
+fn1 (void *frame)
+{
+  x = (__m64) __builtin_ia32_packsswb ((__v4hi) x, (__v4hi) y); /* { dg-error "MMX/3Dnow (instructions|intrinsics) aren't allowed in interrupt service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn2 (void *frame)
+{
+  x = y; /* { dg-error "MMX/3Dnow instructions aren't allowed in interrupt service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn3 (void *frame, uword_t error)
+{
+  x = __extension__ (__m64){ 0 }; /* { dg-error "MMX/3Dnow instructions aren't allowed in exception service routine" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c b/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c
new file mode 100644
index 0000000..796b81b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mred-zone" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  /* No need to adjust stack if less than 128 bytes are used on stack
+     with a 128-byte red zone.  */
+  char array[112];
+  asm ("# %0 "
+       :
+       : "r" (array));
+}
+
+/* { dg-final { scan-assembler-not "(sub|add)(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c b/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c
new file mode 100644
index 0000000..c6a2822
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mred-zone" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  /* Need to adjust stack if more than 128 bytes are used on stack
+     with a 128-byte red zone.  */
+  char array[113];
+  asm ("# %0 "
+       :
+       : "r" (array));
+}
+
+/* { dg-final { scan-assembler-times "(?:sub|add)(?:l|q)\[\\t \]*\\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c b/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c
new file mode 100644
index 0000000..e222acf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O3" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern void bar (void);
+
+void foo (void *frame)
+{
+  bar ();
+}
+/* { dg-final { scan-assembler-not "jmp" } }*/
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c
new file mode 100644
index 0000000..b1b0abe
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern void bar (int);
+
+void f1 (void){  bar (1); }
+__attribute__((interrupt))
+void f2 (void *frame){  bar (2); }
+void f3 (void){  bar (3); }
+__attribute__((interrupt))
+void f4 (void *frame){  bar (4); }
+void f5 (void){  bar (5); }
+
+/* { dg-final { scan-assembler-times "push.\t%.ax" 2 } } */
+/* { dg-final { scan-assembler-times "pop.\t%.ax" 2 } } */
+/* { dg-final { scan-assembler-times "iret" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-xmm.c b/gcc/testsuite/gcc.target/i386/interrupt-xmm.c
new file mode 100644
index 0000000..0eafb10
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-xmm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx -mno-iamcu -msse" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%xmm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%xmm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-ymm.c b/gcc/testsuite/gcc.target/i386/interrupt-ymm.c
new file mode 100644
index 0000000..acb5fc7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-ymm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*%ymm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-zmm.c b/gcc/testsuite/gcc.target/i386/interrupt-zmm.c
new file mode 100644
index 0000000..6d23b5d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-zmm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mavx512f -mno-iamcu" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*%zmm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-09-30 12:41                 ` Yulia Koval
@ 2015-09-30 13:02                   ` H.J. Lu
  2015-09-30 18:48                     ` Yulia Koval
  2015-09-30 19:08                   ` H.J. Lu
  1 sibling, 1 reply; 45+ messages in thread
From: H.J. Lu @ 2015-09-30 13:02 UTC (permalink / raw)
  To: Yulia Koval; +Cc: Mike Stump, GCC Patches, Uros Bizjak

On Wed, Sep 30, 2015 at 5:36 AM, Yulia Koval <vaalfreja@gmail.com> wrote:
> Hi,
>
> Thanks. I added all fixes to the patch, bootstrapped/regtested it on
> Linux/x86_64. Linux/i686 in progress. Ok for trunk if testing passes
> successfully?
>

We will work on the "no_caller_saved_registers" attribute, or
something like that, later as an optimization.

Thanks.

-- 
H.J.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-09-30 13:02                   ` H.J. Lu
@ 2015-09-30 18:48                     ` Yulia Koval
  0 siblings, 0 replies; 45+ messages in thread
From: Yulia Koval @ 2015-09-30 18:48 UTC (permalink / raw)
  To: H.J. Lu; +Cc: Mike Stump, GCC Patches, Uros Bizjak

Tests for Linux/i686 passed successfully.

Julia

On Wed, Sep 30, 2015 at 3:48 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Wed, Sep 30, 2015 at 5:36 AM, Yulia Koval <vaalfreja@gmail.com> wrote:
>> Hi,
>>
>> Thanks. I added all fixes to the patch, bootstrapped/regtested it on
>> Linux/x86_64. Linux/i686 in progress. Ok for trunk if testing passes
>> successfully?
>>
>
> We will work on the "no_caller_saved_registers" attribute, or
> something like that, later as an optimization.
>
> Thanks.
>
> --
> H.J.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-09-30 12:41                 ` Yulia Koval
  2015-09-30 13:02                   ` H.J. Lu
@ 2015-09-30 19:08                   ` H.J. Lu
  2015-09-30 21:31                     ` Yulia Koval
  1 sibling, 1 reply; 45+ messages in thread
From: H.J. Lu @ 2015-09-30 19:08 UTC (permalink / raw)
  To: Yulia Koval; +Cc: Mike Stump, GCC Patches, Uros Bizjak

On Wed, Sep 30, 2015 at 5:36 AM, Yulia Koval <vaalfreja@gmail.com> wrote:
> Hi,
>
> Thanks. I added all fixes to the patch, bootstrapped/regtested it on
> Linux/x86_64. Linux/i686 in progress. Ok for trunk if testing passes
> successfully?

+  /* If true, the current function is an interrupt function as
+     specified by the "interrupt" or "exception" attribute.  */
+  BOOL_BITFIELD is_interrupt : 1;
+
+  /* If true, the current function is an interrupt function as
+     specified by the "exception" attribute.  */
+  BOOL_BITFIELD is_exception : 1;

Please update comments.  There is no "exception" attribute.

-- 
H.J.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-09-30 19:08                   ` H.J. Lu
@ 2015-09-30 21:31                     ` Yulia Koval
  2015-09-30 21:39                       ` H.J. Lu
  2015-10-01  0:24                       ` H.J. Lu
  0 siblings, 2 replies; 45+ messages in thread
From: Yulia Koval @ 2015-09-30 21:31 UTC (permalink / raw)
  To: H.J. Lu; +Cc: Mike Stump, GCC Patches, Uros Bizjak

[-- Attachment #1: Type: text/plain, Size: 740 bytes --]

Done.

Julia

On Wed, Sep 30, 2015 at 9:59 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Wed, Sep 30, 2015 at 5:36 AM, Yulia Koval <vaalfreja@gmail.com> wrote:
>> Hi,
>>
>> Thanks. I added all fixes to the patch, bootstrapped/regtested it on
>> Linux/x86_64. Linux/i686 in progress. Ok for trunk if testing passes
>> successfully?
>
> +  /* If true, the current function is an interrupt function as
> +     specified by the "interrupt" or "exception" attribute.  */
> +  BOOL_BITFIELD is_interrupt : 1;
> +
> +  /* If true, the current function is an interrupt function as
> +     specified by the "exception" attribute.  */
> +  BOOL_BITFIELD is_exception : 1;
>
> Please update comments.  There is no "exception" attribute.
>
> --
> H.J.

[-- Attachment #2: patch_interrupt --]
[-- Type: application/octet-stream, Size: 52580 bytes --]

diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index 6a17ef4..c7ff393 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -278,6 +278,8 @@ extern rtx maybe_get_pool_constant (rtx);
 extern char internal_label_prefix[16];
 extern int internal_label_prefix_len;
 
+extern bool ix86_epilogue_uses (int);
+
 enum ix86_address_seg { SEG_DEFAULT, SEG_FS, SEG_GS };
 struct ix86_address
 {
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index be639e0..d5c7e07 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -2381,6 +2381,8 @@ struct ix86_frame
 {
   int nsseregs;
   int nregs;
+  int nbndregs;
+  int nmaskregs;
   int va_arg_size;
   int red_zone_size;
   int outgoing_arguments_size;
@@ -6241,6 +6243,24 @@ ix86_set_current_function (tree fndecl)
       return;
     }
 
+  if (lookup_attribute ("interrupt", DECL_ATTRIBUTES (fndecl)))
+    {
+      cfun->machine->is_interrupt = true;
+      int nargs = 0;
+      for (tree arg = DECL_ARGUMENTS (fndecl);
+	   arg;
+	   arg = TREE_CHAIN (arg))
+	{
+	  /* Mark arguments in interrupt handler as used to silence
+	     compiler warning since arguments in interrupt handler
+	     are mandatory even if they aren't used.  */
+	  TREE_USED (arg) = 1;
+	  nargs++;
+	}
+      if (nargs == 2)
+        cfun->machine->is_exception = true;
+    }
+
   tree new_tree = DECL_FUNCTION_SPECIFIC_TARGET (fndecl);
   if (new_tree == NULL_TREE)
     new_tree = target_option_default_node;
@@ -6519,6 +6539,11 @@ ix86_function_ok_for_sibcall (tree decl, tree exp)
   tree type, decl_or_type;
   rtx a, b;
 
+  /* Sibling call isn't OK in interrupt handler since it must return
+     with the "IRET" instruction.  */
+  if (cfun->machine->is_interrupt)
+    return false;
+
   /* If we are generating position-independent code, we cannot sibcall
      optimize direct calls to global functions, as the PLT requires
      %ebx be live. (Darwin does not have a PLT.)  */
@@ -7703,6 +7728,7 @@ type_natural_mode (const_tree type, const CUMULATIVE_ARGS *cum,
 		      }
 		  }
 		else if ((size == 8 && !TARGET_64BIT)
+			 && (!cfun || !cfun->machine->is_interrupt)
 			 && !TARGET_MMX
 			 && !TARGET_IAMCU)
 		  {
@@ -8682,6 +8708,11 @@ ix86_function_arg_advance (cumulative_args_t cum_v, machine_mode mode,
   HOST_WIDE_INT bytes, words;
   int nregs;
 
+  /* The argument of interrupt handler is a special case and is
+     handled in ix86_function_arg.  */
+  if (!cum->caller && cfun->machine->is_interrupt)
+    return;
+
   if (mode == BLKmode)
     bytes = int_size_in_bytes (type);
   else
@@ -8998,6 +9029,39 @@ ix86_function_arg (cumulative_args_t cum_v, machine_mode omode,
   HOST_WIDE_INT bytes, words;
   rtx arg;
 
+  if (!cum->caller && cfun->machine->is_interrupt)
+    {
+      /* The first argument of interrupt handler is a pointer and
+	 points to the return address on stack.  The optional second
+	 argument is an integer for error code on stack.  */
+      gcc_assert (type != NULL_TREE);
+      if (POINTER_TYPE_P (type))
+	{
+	  if (cfun->machine->is_exception)
+	    /* (AP) in the current frame in exception handler.  */
+	    arg = arg_pointer_rtx;
+	  else
+	    /* -WORD(AP) in the current frame in interrupt handler.  */
+	    arg = plus_constant (Pmode, arg_pointer_rtx,
+				 -UNITS_PER_WORD);
+	  if (mode != Pmode)
+	    arg = convert_to_mode (mode, arg, 1);
+	}
+      else
+	{
+	  gcc_assert (TREE_CODE (type) == INTEGER_TYPE
+		      && cfun->machine->is_exception
+		      && mode == word_mode);
+	  /* The error code is at -WORD(AP) in the current frame in
+	     exception handler.  */
+	  arg = gen_rtx_MEM (word_mode,
+			     plus_constant (Pmode, arg_pointer_rtx,
+					    -UNITS_PER_WORD));
+	}
+
+      return arg;
+    }
+
   /* All pointer bounds arguments are handled separately here.  */
   if ((type && POINTER_BOUNDS_TYPE_P (type))
       || POINTER_BOUNDS_MODE_P (mode))
@@ -10713,7 +10777,10 @@ ix86_can_use_return_insn_p (void)
 {
   struct ix86_frame frame;
 
-  if (! reload_completed || frame_pointer_needed)
+  /* Don't use `ret' instruction in interrupt handler.  */
+  if (! reload_completed
+      || frame_pointer_needed
+      || cfun->machine->is_interrupt)
     return 0;
 
   /* Don't allow more than 32k pop, since that's all we can do
@@ -11023,11 +11090,46 @@ ix86_select_alt_pic_regnum (void)
   return INVALID_REGNUM;
 }
 
+/* Return true if REGNO is used by the epilogue.  */
+
+bool
+ix86_epilogue_uses (int regno)
+{
+  /* All preserved registers are used by the epilogue of interrupt
+     handler.  Don't explicitly mark BP and SP registers as used
+     since they are always used in epilogue.  */
+  return (cfun->machine->is_interrupt
+	  && reg_names[regno][0]
+	  && !STACK_REGNO_P (regno)
+	  && !MMX_REGNO_P (regno)
+	  && regno != BP_REG
+	  && regno != SP_REG
+	  && (regno <= ST7_REG || regno >= XMM0_REG));
+}
+
 /* Return TRUE if we need to save REGNO.  */
 
 static bool
 ix86_save_reg (unsigned int regno, bool maybe_eh_return)
 {
+  /* In interrupt handler, we don't preserve MMX and x87 registers
+     which aren't supported when saving and restoring registers.  No
+     need to preserve callee-saved registers unless they are modified.
+     We also preserve all caller-saved registers if a function call
+     is made in interrupt handler since the called function may change
+     them.  Don't explicitly save BP and SP registers since they are
+     always preserved.  */
+  if (cfun->machine->is_interrupt && reload_completed)
+    return ((df_regs_ever_live_p (regno)
+	     || (call_used_regs[regno]
+		 && cfun->machine->call_with_caller_saved_registers))
+	    && !fixed_regs[regno]
+	    && !STACK_REGNO_P (regno)
+	    && !MMX_REGNO_P (regno)
+	    && regno != BP_REG
+	    && regno != SP_REG
+	    && (regno <= ST7_REG || regno >= XMM0_REG));
+
   if (regno == REAL_PIC_OFFSET_TABLE_REGNUM
       && pic_offset_table_rtx)
     {
@@ -11084,6 +11186,34 @@ ix86_nsaved_regs (void)
   return nregs;
 }
 
+/* Return number of saved bound registers.  */
+
+static int
+ix86_nsaved_bndregs (void)
+{
+  int nregs = 0;
+  int regno;
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (BND_REGNO_P (regno) && ix86_save_reg (regno, true))
+      nregs ++;
+  return nregs;
+}
+
+/* Return number of saved mask registers.  */
+
+static int
+ix86_nsaved_maskregs (void)
+{
+  int nregs = 0;
+  int regno;
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (MASK_REGNO_P (regno) && ix86_save_reg (regno, true))
+      nregs ++;
+  return nregs;
+}
+
 /* Return number of saved SSE registers.  */
 
 static int
@@ -11092,7 +11222,8 @@ ix86_nsaved_sseregs (void)
   int nregs = 0;
   int regno;
 
-  if (!TARGET_64BIT_MS_ABI)
+  /* Always need to save SSE registers in interrupt handler.  */
+  if (!TARGET_64BIT_MS_ABI && !cfun->machine->is_interrupt)
     return 0;
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
@@ -11161,6 +11292,18 @@ ix86_builtin_setjmp_frame_value (void)
 
 #define SPLIT_STACK_AVAILABLE 256
 
+/* Register save area size.  */
+
+static unsigned int
+ix86_reg_save_area_size (struct ix86_frame *frame)
+{
+  unsigned int size = ((frame->nregs - frame->nbndregs
+			- frame->nmaskregs) * UNITS_PER_WORD);
+  size += frame->nbndregs * (ix86_pmode == PMODE_DI ? 16 : 8);
+  size += frame->nmaskregs * (TARGET_AVX512BW ? 8 : 2);
+  return size;
+}
+
 /* Fill structure ix86_frame about frame of currently computed function.  */
 
 static void
@@ -11173,6 +11316,8 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   HOST_WIDE_INT to_allocate;
 
   frame->nregs = ix86_nsaved_regs ();
+  frame->nbndregs = ix86_nsaved_bndregs ();
+  frame->nmaskregs = ix86_nsaved_maskregs ();
   frame->nsseregs = ix86_nsaved_sseregs ();
 
   /* 64-bit MS ABI seem to require stack alignment to be always 16 except for
@@ -11244,11 +11389,16 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
 	   = !expensive_function_p (count);
     }
 
+  /* We must use move to save bound and mask registers.  */
   frame->save_regs_using_mov
-    = (TARGET_PROLOGUE_USING_MOVE && cfun->machine->use_fast_prologue_epilogue
-       /* If static stack checking is enabled and done with probes,
-	  the registers need to be saved before allocating the frame.  */
-       && flag_stack_check != STATIC_BUILTIN_STACK_CHECK);
+    = (frame->nbndregs > 0
+       || frame->nmaskregs > 0
+       || (TARGET_PROLOGUE_USING_MOVE
+	   && cfun->machine->use_fast_prologue_epilogue
+	   /* If static stack checking is enabled and done with probes,
+	      the registers need to be saved before allocating the
+	      frame.  */
+	   && flag_stack_check != STATIC_BUILTIN_STACK_CHECK));
 
   /* Skip return address.  */
   offset = UNITS_PER_WORD;
@@ -11266,7 +11416,7 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   frame->hard_frame_pointer_offset = offset;
 
   /* Register save area */
-  offset += frame->nregs * UNITS_PER_WORD;
+  offset += ix86_reg_save_area_size (frame);
   frame->reg_save_offset = offset;
 
   /* On SEH target, registers are pushed just before the frame pointer
@@ -11280,9 +11430,17 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
       /* The only ABI that has saved SSE registers (Win64) also has a
          16-byte aligned default stack, and thus we don't need to be
 	 within the re-aligned local stack frame to save them.  */
-      gcc_assert (INCOMING_STACK_BOUNDARY >= 128);
-      offset = (offset + 16 - 1) & -16;
-      offset += frame->nsseregs * 16;
+      if (cfun->machine->is_interrupt)
+	/* We must save full vector registers in interrupt handler.  */
+	offset += frame->nsseregs * (TARGET_AVX512F
+				     ? 64
+				     : (TARGET_AVX ? 32 : 16));
+      else
+	{
+	  gcc_assert (INCOMING_STACK_BOUNDARY >= 128);
+	  offset = (offset + 16 - 1) & -16;
+	  offset += frame->nsseregs * 16;
+	}
     }
   frame->sse_reg_save_offset = offset;
 
@@ -11338,8 +11496,12 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   /* Size prologue needs to allocate.  */
   to_allocate = offset - frame->sse_reg_save_offset;
 
-  if ((!to_allocate && frame->nregs <= 1)
-      || (TARGET_64BIT && to_allocate >= (HOST_WIDE_INT) 0x80000000))
+  /* We must use move to save bound and mask registers.  */
+  if (frame->nbndregs == 0
+      && frame->nmaskregs == 0
+      && ((!to_allocate && frame->nregs <= 1)
+	   || (TARGET_64BIT
+	       && to_allocate >= (HOST_WIDE_INT) 0x80000000)))
     frame->save_regs_using_mov = false;
 
   if (ix86_using_red_zone ()
@@ -11349,7 +11511,7 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
     {
       frame->red_zone_size = to_allocate;
       if (frame->save_regs_using_mov)
-	frame->red_zone_size += frame->nregs * UNITS_PER_WORD;
+	frame->red_zone_size += ix86_reg_save_area_size (frame);
       if (frame->red_zone_size > RED_ZONE_SIZE - RED_ZONE_RESERVE)
 	frame->red_zone_size = RED_ZONE_SIZE - RED_ZONE_RESERVE;
     }
@@ -11509,8 +11671,14 @@ ix86_emit_save_reg_using_mov (machine_mode mode, unsigned int regno,
   addr = choose_baseaddr (cfa_offset);
   mem = gen_frame_mem (mode, addr);
 
-  /* For SSE saves, we need to indicate the 128-bit alignment.  */
-  set_mem_align (mem, GET_MODE_ALIGNMENT (mode));
+  /* For SSE saves, we need to indicate the 128-bit alignment.  In
+     interrupt handler, stack is only aligned to word_mode.  We can't
+     use gen_sse_storeups since RTX_FRAME_RELATED_P is set and
+     dwarf2out_flush_queued_reg_saves doesn't like UNSPEC_STOREU.
+     Also gen_sse_storeups doesn't cover AVX nor AVX512.  */
+  set_mem_align (mem,
+		 GET_MODE_ALIGNMENT (cfun->machine->is_interrupt
+				     ? word_mode : mode));
 
   insn = emit_move_insn (mem, reg);
   RTX_FRAME_RELATED_P (insn) = 1;
@@ -11571,8 +11739,9 @@ ix86_emit_save_regs_using_mov (HOST_WIDE_INT cfa_offset)
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (!SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
       {
-        ix86_emit_save_reg_using_mov (word_mode, regno, cfa_offset);
-	cfa_offset -= UNITS_PER_WORD;
+	enum machine_mode reg_mode = GET_MODE (regno_reg_rtx[regno]);
+	ix86_emit_save_reg_using_mov (reg_mode, regno, cfa_offset);
+	cfa_offset -= GET_MODE_SIZE (reg_mode);
       }
 }
 
@@ -11582,12 +11751,26 @@ static void
 ix86_emit_save_sse_regs_using_mov (HOST_WIDE_INT cfa_offset)
 {
   unsigned int regno;
+  enum machine_mode vector_reg_mode;
+
+  if (cfun->machine->is_interrupt)
+    {
+      /* We must save full vector registers in interrupt handler.  */
+      if (TARGET_AVX512F)
+	vector_reg_mode = V16SFmode;
+      else if (TARGET_AVX)
+	vector_reg_mode = V8SFmode;
+      else
+	vector_reg_mode = V4SFmode;
+    }
+  else
+    vector_reg_mode = V4SFmode;
 
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
       {
-	ix86_emit_save_reg_using_mov (V4SFmode, regno, cfa_offset);
-	cfa_offset -= 16;
+	ix86_emit_save_reg_using_mov (vector_reg_mode, regno, cfa_offset);
+	cfa_offset -= GET_MODE_SIZE (vector_reg_mode);
       }
 }
 
@@ -13032,12 +13215,13 @@ ix86_emit_restore_regs_using_mov (HOST_WIDE_INT cfa_offset,
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (!SSE_REGNO_P (regno) && ix86_save_reg (regno, maybe_eh_return))
       {
-	rtx reg = gen_rtx_REG (word_mode, regno);
+	enum machine_mode reg_mode = GET_MODE (regno_reg_rtx[regno]);
+	rtx reg = gen_rtx_REG (reg_mode, regno);
 	rtx mem;
 	rtx_insn *insn;
 
 	mem = choose_baseaddr (cfa_offset);
-	mem = gen_frame_mem (word_mode, mem);
+	mem = gen_frame_mem (reg_mode, mem);
 	insn = emit_move_insn (reg, mem);
 
         if (m->fs.cfa_reg == crtl->drap_reg && regno == REGNO (crtl->drap_reg))
@@ -13056,7 +13240,7 @@ ix86_emit_restore_regs_using_mov (HOST_WIDE_INT cfa_offset,
 	else
 	  ix86_add_cfa_restore_note (NULL, reg, cfa_offset);
 
-	cfa_offset -= UNITS_PER_WORD;
+	cfa_offset -= GET_MODE_SIZE (reg_mode);
       }
 }
 
@@ -13067,21 +13251,39 @@ ix86_emit_restore_sse_regs_using_mov (HOST_WIDE_INT cfa_offset,
 				      bool maybe_eh_return)
 {
   unsigned int regno;
+  enum machine_mode vector_reg_mode;
+
+  if (cfun->machine->is_interrupt)
+    {
+      /* We must restore full vector registers in interrupt handler.  */
+      if (TARGET_AVX512F)
+	vector_reg_mode = V16SFmode;
+      else if (TARGET_AVX)
+	vector_reg_mode = V8SFmode;
+      else
+	vector_reg_mode = V4SFmode;
+    }
+  else
+    vector_reg_mode = V4SFmode;
+
 
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, maybe_eh_return))
       {
-	rtx reg = gen_rtx_REG (V4SFmode, regno);
+	rtx reg = gen_rtx_REG (vector_reg_mode, regno);
 	rtx mem;
 
 	mem = choose_baseaddr (cfa_offset);
-	mem = gen_rtx_MEM (V4SFmode, mem);
-	set_mem_align (mem, 128);
+	mem = gen_rtx_MEM (vector_reg_mode, mem);
+	/* In interrupt handler, stack is only aligned to word_mode.  */
+	set_mem_align (mem, (cfun->machine->is_interrupt
+			     ? GET_MODE_ALIGNMENT (word_mode)
+			     : 128));
 	emit_move_insn (reg, mem);
 
 	ix86_add_cfa_restore_note (NULL, reg, cfa_offset);
 
-	cfa_offset -= 16;
+	cfa_offset -= GET_MODE_SIZE (vector_reg_mode);
       }
 }
 
@@ -13145,8 +13347,9 @@ ix86_expand_epilogue (int style)
   if (crtl->calls_eh_return && style != 2)
     frame.reg_save_offset -= 2 * UNITS_PER_WORD;
 
-  /* EH_RETURN requires the use of moves to function properly.  */
-  if (crtl->calls_eh_return)
+  /* EH_RETURN requires the use of moves to function properly.  We must
+     use move to restore bound and mask registers.  */
+  if (crtl->calls_eh_return || frame.nbndregs > 0 || frame.nmaskregs > 0)
     restore_regs_via_mov = true;
   /* SEH requires the use of pops to identify the epilogue.  */
   else if (TARGET_SEH)
@@ -13387,7 +13590,20 @@ ix86_expand_epilogue (int style)
       return;
     }
 
-  if (crtl->args.pops_args && crtl->args.size)
+  if (cfun->machine->is_interrupt)
+    {
+      /* Return with the "IRET" instruction from interrupt handler.
+	 Pop the 'ERROR_CODE' off the stack before the 'IRET'
+	 instruction in exception handler.  */
+      if (cfun->machine->is_exception)
+	{
+	  rtx r = plus_constant (Pmode, stack_pointer_rtx,
+				 UNITS_PER_WORD);
+	  emit_insn (gen_rtx_SET (stack_pointer_rtx, r));
+	}
+      emit_jump_insn (gen_interrupt_return ());
+    }
+  else if (crtl->args.pops_args && crtl->args.size)
     {
       rtx popc = GEN_INT (crtl->args.pops_args);
 
@@ -18360,6 +18576,13 @@ ix86_expand_move (machine_mode mode, rtx operands[])
   rtx op0, op1;
   enum tls_model model;
 
+  if (cfun->machine->is_interrupt && IS_STACK_MODE (mode))
+    {
+      error ("80387 instructions aren't allowed in %s service routine",
+	     (cfun->machine->is_exception ? "exception" : "interrupt"));
+      return;
+    }
+
   op0 = operands[0];
   op1 = operands[1];
 
@@ -18504,6 +18727,13 @@ ix86_expand_vector_move (machine_mode mode, rtx operands[])
   rtx op0 = operands[0], op1 = operands[1];
   unsigned int align = GET_MODE_ALIGNMENT (mode);
 
+  if (cfun->machine->is_interrupt && VALID_MMX_REG_MODE (mode))
+    {
+      error ("MMX/3Dnow instructions aren't allowed in %s service routine",
+	     (cfun->machine->is_exception ? "exception" : "interrupt"));
+      return;
+    }
+
   if (push_operand (op0, VOIDmode))
     op0 = emit_move_resolve_push (mode, op0);
 
@@ -26590,6 +26820,12 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
   rtx use = NULL, call;
   unsigned int vec_len = 0;
 
+  if (GET_CODE (XEXP (fnaddr, 0)) == SYMBOL_REF
+      && SYMBOL_REF_DECL ((XEXP (fnaddr, 0))) != NULL_TREE
+      && lookup_attribute ("interrupt",
+			   DECL_ATTRIBUTES (SYMBOL_REF_DECL (XEXP (fnaddr, 0)))))
+    error ("interrupt service routine can't be called directly");
+
   if (pop == const0_rtx)
     pop = NULL;
   gcc_assert (!TARGET_64BIT || !pop);
@@ -26708,6 +26944,8 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
   if (use)
     CALL_INSN_FUNCTION_USAGE (call) = use;
 
+  cfun->machine->call_with_caller_saved_registers = true;
+
   return call;
 }
 
@@ -44068,6 +44306,56 @@ ix86_handle_fndecl_attribute (tree *node, tree name, tree, int,
   return NULL_TREE;
 }
 
+
+static tree
+ix86_handle_interrupt_attribute (tree *node, tree name, tree, int,
+				 bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+	       name);
+      *no_add_attrs = true;
+    }
+
+  /* DECL_RESULT and DECL_ARGUMENTS do not exist there yet,
+     but the function type contains args and return type data.  */
+  tree func_type = TREE_TYPE (*node);
+  tree return_type = TREE_TYPE (func_type);
+
+  int nargs = 0;
+  tree current_arg_type = TYPE_ARG_TYPES (func_type);
+  while (current_arg_type
+	 && ! VOID_TYPE_P (TREE_VALUE (current_arg_type)))
+    {
+      if (nargs == 0)
+	{
+	  if (! POINTER_TYPE_P (TREE_VALUE (current_arg_type)))
+	    error ("interrupt service routine should have a pointer "
+		   "as the first argument");
+	}
+      else if (nargs == 1)
+	{
+	  if (TREE_CODE (TREE_VALUE (current_arg_type)) != INTEGER_TYPE
+	      || TYPE_MODE (TREE_VALUE (current_arg_type)) != word_mode)
+	    error ("interrupt service routine should have unsigned %s"
+		   "int as the second argument",
+		   TARGET_64BIT
+		   ? (TARGET_X32 ? "long long " : "long ")
+		   : "");
+	}
+      nargs++;
+      current_arg_type = TREE_CHAIN (current_arg_type);
+    }
+  if (!nargs || nargs > 2)
+    error ("interrupt service routine can only have a pointer argument "
+	   "and an optional integer argument");
+  if (! VOID_TYPE_P (return_type))
+    error ("interrupt service routine can't have non-void return value");
+
+  return NULL_TREE;
+}
+
 static bool
 ix86_ms_bitfield_layout_p (const_tree record_type)
 {
@@ -48069,6 +48357,9 @@ static const struct attribute_spec ix86_attribute_table[] =
     false },
   { "callee_pop_aggregate_return", 1, 1, false, true, true,
     ix86_handle_callee_pop_aggregate_return, true },
+  { "interrupt", 0, 0, true, false, false,
+    ix86_handle_interrupt_attribute, false },
+
   /* End element.  */
   { NULL,        0, 0, false, false, false, NULL, false }
 };
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 96ba90f..9892c64 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -1730,6 +1730,11 @@ typedef struct ix86_args {
 
 #define EXIT_IGNORE_STACK 1
 
+/* Define this macro as a C expression that is nonzero for registers
+   used by the epilogue or the `return' pattern.  */
+
+#define EPILOGUE_USES(REGNO) ix86_epilogue_uses (REGNO)
+
 /* Output assembler code for a block containing the constant parts
    of a trampoline, leaving space for the variable parts.  */
 
@@ -2494,6 +2499,18 @@ struct GTY(()) machine_function {
   /* If true, it is safe to not save/restore DRAP register.  */
   BOOL_BITFIELD no_drap_save_restore : 1;
 
+  /* If true, the current function is an interrupt service
+     routine as specified by the "interrupt" attribute.  */
+  BOOL_BITFIELD is_interrupt : 1;
+
+  /* If true, the current function is an exception service
+     routine as specified by the "interrupt" attribute.  */
+  BOOL_BITFIELD is_exception : 1;
+
+  /* If true, the current function makes any function calls with
+     caller-saved registers.  */
+  BOOL_BITFIELD call_with_caller_saved_registers : 1;
+
   /* If true, there is register available for argument passing.  This
      is used only in ix86_function_ok_for_sibcall by 32-bit to determine
      if there is scratch register available for indirect sibcall.  In
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 8c2ed60..8b22be4 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -193,6 +193,9 @@
   UNSPEC_BNDCU
   UNSPEC_BNDCN
   UNSPEC_MPX_FENCE
+
+  ;; IRET support
+  UNSPEC_INTERRUPT_RETURN
 ])
 
 (define_c_enum "unspecv" [
@@ -12179,6 +12182,12 @@
    (set_attr "modrm" "0")
    (set_attr "maybe_prefix_bnd" "1")])
 
+(define_insn "interrupt_return"
+  [(simple_return)
+   (unspec [(const_int 0)] UNSPEC_INTERRUPT_RETURN)]
+  "reload_completed"
+  "iret")
+
 ;; Used by x86_machine_dependent_reorg to avoid penalty on single byte RET
 ;; instruction Athlon and K8 have.
 
diff --git a/gcc/config/i386/sse.md b/gcc/config/i386/sse.md
index 4eefb45..3f932a7 100644
--- a/gcc/config/i386/sse.md
+++ b/gcc/config/i386/sse.md
@@ -875,10 +875,14 @@
 	case MODE_V16SF:
 	case MODE_V8SF:
 	case MODE_V4SF:
-	  if (TARGET_AVX
+	  /* We must supprt misaligned SSE load and store in interrupt
+	     handler since ix86_emit_save_reg_using_mov generates the
+	     normal *mov<mode>_internal pattern for interrupt handler
+	     with misaligned stack.  */
+	  if ((TARGET_AVX || cfun->machine->is_interrupt)
 	      && (misaligned_operand (operands[0], <MODE>mode)
 		  || misaligned_operand (operands[1], <MODE>mode)))
-	    return "vmovups\t{%1, %0|%0, %1}";
+	    return "%vmovups\t{%1, %0|%0, %1}";
 	  else
 	    return "%vmovaps\t{%1, %0|%0, %1}";
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 8406945..9f89e99 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -5061,6 +5061,62 @@ On x86-32 targets, the @code{stdcall} attribute causes the compiler to
 assume that the called function pops off the stack space used to
 pass arguments, unless it takes a variable number of arguments.
 
+@item interrupt
+@cindex @code{interrupt} function attribute, x86
+Use this attribute to indicate that the specified function is an
+interrupt handler.  The compiler generates function
+entry and exit sequences suitable for use in an interrupt handler when
+this attribute is present.  The @code{IRET} instruction, instead of the
+@code{RET} instruction, is used to return from interrupt handlers.  All
+registers, except for the EFLAGS register which is restored by the
+@code{IRET} instruction, are preserved by the compiler.
+
+Any interruptible-without-stack-switch code must be compiled with
+@option{-mno-red-zone} since interrupt handlers can and will, because
+of the hardware design, touch the red zone.
+
+An interrupt handler must be declared with a mandatory pointer
+argument:
+
+@smallexample
+struct interrupt_frame;
+
+__attribute__ ((interrupt))
+void
+f (struct interrupt_frame *frame)
+@{
+@}
+@end smallexample
+
+and user must properly define the structure the pointer pointing to.
+
+The exception handler is very similar to the interrupt handler with
+a different mandatory function signature:
+
+@smallexample
+#ifdef __x86_64__
+typedef unsigned long long int uword_t;
+#else
+typedef unsigned int uword_t;
+#endif
+
+struct interrupt_frame;
+
+__attribute__ ((interrupt))
+void
+f (struct interrupt_frame *frame, uword_t error_code)
+@{
+  ...
+@}
+@end smallexample
+
+and compiler pops the error code off the stack before the @code{IRET}
+instruction.
+
+The exception handler should only be used for exceptions which push an
+error code and all other exceptions must use the interrupt handler.
+The system will crash if the wrong handler is used.
+
 @item target (@var{options})
 @cindex @code{target} function attribute
 As discussed in @ref{Common Function Attributes}, this attribute 
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-1.c b/gcc/testsuite/gcc.target/i386/interrupt-1.c
new file mode 100644
index 0000000..ea2f1e4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-1.c
@@ -0,0 +1,46 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-push-args" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern int bar (int);
+
+void foo (void *frame)
+{
+  int a,b,c,d,e,f,i;
+  a = bar (5);
+  b = bar (a);
+  c = bar (b);
+  d = bar (c);
+  e = bar (d);
+  f = bar (e);
+  for (i = 1; i < 10; i++)
+  {
+    a += bar (a + i) + bar (b + i) +
+	 bar (c + i) + bar (d + i) +
+	 bar (e + i) + bar (f + i);
+  }
+}
+
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-10.c b/gcc/testsuite/gcc.target/i386/interrupt-10.c
new file mode 100644
index 0000000..a439d6f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-10.c
@@ -0,0 +1,24 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mavx512bw -mno-iamcu" } */
+
+extern void bar (void);
+
+struct interrupt_frame;
+
+void
+ __attribute__ ((interrupt))
+foo (struct interrupt_frame *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm\[0-9\]+" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "kmovq\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" 8 } } */
+/* { dg-final { scan-assembler-times "kmovq\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" 8 } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-11.c b/gcc/testsuite/gcc.target/i386/interrupt-11.c
new file mode 100644
index 0000000..6556c44
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-11.c
@@ -0,0 +1,38 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-sse -mno-iamcu" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-12.c b/gcc/testsuite/gcc.target/i386/interrupt-12.c
new file mode 100644
index 0000000..5c9eb74
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-12.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int check_int (int *i, void *, int align);
+typedef int aligned __attribute__((aligned(64)));
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+__attribute__((interrupt))
+void
+foo (void *frame, uword_t error_code)
+{
+  aligned j;
+  if (check_int (frame, &j, __alignof__(j)))
+    __builtin_abort ();
+}
+
+/* { dg-final { scan-assembler-times "and\[lq\]?\[^\\n\]*-64,\[^\\n\]*sp" 1 } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-13.c b/gcc/testsuite/gcc.target/i386/interrupt-13.c
new file mode 100644
index 0000000..8cf0b25
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-13.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int check_int (int *i, void *, int align);
+typedef int aligned __attribute__((aligned(64)));
+
+__attribute__((interrupt))
+void
+foo (void *frame)
+{
+  aligned j;
+  if (check_int (frame, &j, __alignof__(j)))
+    __builtin_abort ();
+}
+
+/* { dg-final { scan-assembler-times "and\[lq\]?\[^\\n\]*-64,\[^\\n\]*sp" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-14.c b/gcc/testsuite/gcc.target/i386/interrupt-14.c
new file mode 100644
index 0000000..7394207
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-14.c
@@ -0,0 +1,39 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-sse -mno-iamcu -mno-sse4 -mno-popcnt" } */
+
+extern int i, cnt;
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  cnt = __builtin_popcount (i);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 1 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-2.c b/gcc/testsuite/gcc.target/i386/interrupt-2.c
new file mode 100644
index 0000000..2003fbb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-2.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wall -Wunused-parameter" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+}
+
+/* { dg-final { scan-assembler-not "add(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-3.c b/gcc/testsuite/gcc.target/i386/interrupt-3.c
new file mode 100644
index 0000000..a87e594
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-3.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+void
+__attribute__((interrupt))
+fn (void* frame, uword_t error)
+{
+}
+
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-387-err.c b/gcc/testsuite/gcc.target/i386/interrupt-387-err.c
new file mode 100644
index 0000000..3a4a5cf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-387-err.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -m80387 -mlong-double-80 -mno-iamcu" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+extern long double y, x;
+
+void
+fn0 (void)
+{
+  x = y;
+  y = 0;
+}
+
+void
+__attribute__((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  x = 0;
+} /* { dg-error "80387 instructions aren't allowed in exception service routine" } */
+
+void
+__attribute__((interrupt))
+fn2 (void *frame)
+{
+  x = y; /* { dg-error "80387 instructions aren't allowed in interrupt service routine" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-4.c b/gcc/testsuite/gcc.target/i386/interrupt-4.c
new file mode 100644
index 0000000..94230cd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-4.c
@@ -0,0 +1,32 @@
+/* { dg-do link } */
+/* { dg-options "-O" } */
+
+#include <stdint.h>
+
+extern void link_error (void);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+struct interrupt_frame
+{
+  uword_t ip;
+  uword_t cs;
+  uword_t flags;
+  uword_t sp;
+  uword_t ss;
+};
+
+__attribute__ ((used, interrupt))
+void
+foo (struct interrupt_frame *frame)
+{
+  void *ra = __builtin_return_address (0);
+  if ((uintptr_t) ra != (uintptr_t) frame->ip)
+    link_error ();
+}
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-5.c b/gcc/testsuite/gcc.target/i386/interrupt-5.c
new file mode 100644
index 0000000..53d6656
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-5.c
@@ -0,0 +1,23 @@
+/* { dg-do link } */
+/* { dg-options "-O" } */
+
+#include <stdint.h>
+
+extern void link_error (void);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+__attribute__ ((used, interrupt))
+void
+foo (void *frame, uword_t error)
+{
+  void *ra = __builtin_return_address (0);
+  if ((uintptr_t) ra != (uintptr_t) error)
+    link_error ();
+}
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-6.c b/gcc/testsuite/gcc.target/i386/interrupt-6.c
new file mode 100644
index 0000000..a1a3d0e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-6.c
@@ -0,0 +1,40 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+extern int error;
+
+__attribute__((interrupt))
+void
+fn1 (void *p, short error_code)
+{ /* { dg-error "interrupt service routine should have unsigned \(long long |long |\)int as the second argument" } */
+}
+
+__attribute__((interrupt))
+void
+fn2 (void)
+{ /* { dg-error "interrupt service routine can only have a pointer argument and an optional integer argument" } */
+}
+
+__attribute__((interrupt))
+void
+fn3 (uword_t error_code)
+{ /* { dg-error "interrupt service routine should have a pointer as the first argument" } */
+  error = error_code;
+}
+
+__attribute__((interrupt))
+void
+fn4 (uword_t error_code, void *frame)
+{ /* { dg-error "interrupt service routine should have .* the .* argument" } */
+  error = error_code;
+}
+
+extern int fn5 (void *) __attribute__ ((interrupt)); /* { dg-error "interrupt service routine can't have non-void return value" } */
+
+int
+fn5 (void *frame)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-7.c b/gcc/testsuite/gcc.target/i386/interrupt-7.c
new file mode 100644
index 0000000..e7441a2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-7.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int error;
+
+extern void fn (void *) __attribute__((interrupt));
+
+void
+foo (void)
+{
+  fn (&error); /* { dg-error "interrupt service routine can't be called directly" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-8.c b/gcc/testsuite/gcc.target/i386/interrupt-8.c
new file mode 100644
index 0000000..0a83473
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-8.c
@@ -0,0 +1,20 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 16 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm\[0-9\]+" 16 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-9.c b/gcc/testsuite/gcc.target/i386/interrupt-9.c
new file mode 100644
index 0000000..9d0c776
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-9.c
@@ -0,0 +1,22 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512bw -mno-iamcu -mavx512f" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm\[0-9\]+" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "kmovw\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" 8 } } */
+/* { dg-final { scan-assembler-times "kmovw\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" 8 } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-bnd.c b/gcc/testsuite/gcc.target/i386/interrupt-bnd.c
new file mode 100644
index 0000000..9f50661
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-bnd.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mno-iamcu -mmpx" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "bnd3");
+}
+
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*%bnd3,\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%bnd3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
new file mode 100644
index 0000000..77a9463
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
@@ -0,0 +1,30 @@
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -miamcu" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern int bar (int);
+
+void foo (void *frame)
+{
+  int a,b,c,d,e,f,i;
+  a = bar (5);
+  b = bar (a);
+  c = bar (b);
+  d = bar (c);
+  e = bar (d);
+  f = bar (e);
+  for (i = 1; i < 10; i++)
+    a += bar (a + i) + bar (b + i) +
+	 bar (c + i) + bar (d + i) +
+	 bar (e + i) + bar (f+i);
+}
+
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%eax" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%ecx" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%edx" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%eax" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%ecx" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%edx" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c
new file mode 100644
index 0000000..fc4f367
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mmmx -mno-iamcu" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+typedef short __v4hi __attribute__ ((__vector_size__ (8)));
+typedef int __m64 __attribute__ ((__vector_size__ (8), __may_alias__));
+
+extern __m64 y, x;
+
+void
+fn0 (void)
+{
+  x = __extension__ (__m64){ 0 };
+  x = (__m64) __builtin_ia32_packsswb ((__v4hi) x, (__v4hi) y);
+  y = x;
+}
+
+void
+__attribute__((interrupt))
+fn1 (void *frame)
+{
+  x = (__m64) __builtin_ia32_packsswb ((__v4hi) x, (__v4hi) y); /* { dg-error "MMX/3Dnow (instructions|intrinsics) aren't allowed in interrupt service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn2 (void *frame)
+{
+  x = y; /* { dg-error "MMX/3Dnow instructions aren't allowed in interrupt service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn3 (void *frame, uword_t error)
+{
+  x = __extension__ (__m64){ 0 }; /* { dg-error "MMX/3Dnow instructions aren't allowed in exception service routine" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c b/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c
new file mode 100644
index 0000000..796b81b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mred-zone" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  /* No need to adjust stack if less than 128 bytes are used on stack
+     with a 128-byte red zone.  */
+  char array[112];
+  asm ("# %0 "
+       :
+       : "r" (array));
+}
+
+/* { dg-final { scan-assembler-not "(sub|add)(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c b/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c
new file mode 100644
index 0000000..c6a2822
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mred-zone" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  /* Need to adjust stack if more than 128 bytes are used on stack
+     with a 128-byte red zone.  */
+  char array[113];
+  asm ("# %0 "
+       :
+       : "r" (array));
+}
+
+/* { dg-final { scan-assembler-times "(?:sub|add)(?:l|q)\[\\t \]*\\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c b/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c
new file mode 100644
index 0000000..e222acf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O3" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern void bar (void);
+
+void foo (void *frame)
+{
+  bar ();
+}
+/* { dg-final { scan-assembler-not "jmp" } }*/
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c
new file mode 100644
index 0000000..b1b0abe
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern void bar (int);
+
+void f1 (void){  bar (1); }
+__attribute__((interrupt))
+void f2 (void *frame){  bar (2); }
+void f3 (void){  bar (3); }
+__attribute__((interrupt))
+void f4 (void *frame){  bar (4); }
+void f5 (void){  bar (5); }
+
+/* { dg-final { scan-assembler-times "push.\t%.ax" 2 } } */
+/* { dg-final { scan-assembler-times "pop.\t%.ax" 2 } } */
+/* { dg-final { scan-assembler-times "iret" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-xmm.c b/gcc/testsuite/gcc.target/i386/interrupt-xmm.c
new file mode 100644
index 0000000..0eafb10
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-xmm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx -mno-iamcu -msse" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%xmm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%xmm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-ymm.c b/gcc/testsuite/gcc.target/i386/interrupt-ymm.c
new file mode 100644
index 0000000..acb5fc7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-ymm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*%ymm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-zmm.c b/gcc/testsuite/gcc.target/i386/interrupt-zmm.c
new file mode 100644
index 0000000..6d23b5d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-zmm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mavx512f -mno-iamcu" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*%zmm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-09-30 21:31                     ` Yulia Koval
@ 2015-09-30 21:39                       ` H.J. Lu
  2015-10-01  0:24                       ` H.J. Lu
  1 sibling, 0 replies; 45+ messages in thread
From: H.J. Lu @ 2015-09-30 21:39 UTC (permalink / raw)
  To: Yulia Koval; +Cc: Mike Stump, GCC Patches, Uros Bizjak

On Wed, Sep 30, 2015 at 12:53 PM, Yulia Koval <vaalfreja@gmail.com> wrote:
> Done.
>

Please provide new ChangeLog entries since we removed and
added codes.


-- 
H.J.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-09-30 21:31                     ` Yulia Koval
  2015-09-30 21:39                       ` H.J. Lu
@ 2015-10-01  0:24                       ` H.J. Lu
  2015-10-01 12:34                         ` Yulia Koval
  2015-10-01 15:59                         ` Uros Bizjak
  1 sibling, 2 replies; 45+ messages in thread
From: H.J. Lu @ 2015-10-01  0:24 UTC (permalink / raw)
  To: Yulia Koval; +Cc: Mike Stump, GCC Patches, Uros Bizjak

On Wed, Sep 30, 2015 at 12:53 PM, Yulia Koval <vaalfreja@gmail.com> wrote:
> Done.
>

+  /* If true, the current function is an interrupt service
+     routine as specified by the "interrupt" attribute.  */
+  BOOL_BITFIELD is_interrupt : 1;
+
+  /* If true, the current function is an exception service
+     routine as specified by the "interrupt" attribute.  */
+  BOOL_BITFIELD is_exception : 1;


It is not very clear what is the difference between is_interrupt
and is_exception.  How about

  /* If true, the current function is an interrupt service routine with
     a pointer argument and an optional integer argument as specified by
     the "interrupt" attribute.  */
  BOOL_BITFIELD is_interrupt : 1;

  /* If true, the current function is an interrupt service routine with
     a pointer argument and an integer argument as specified by the
     "interrupt" attribute.  */
  BOOL_BITFIELD is_exception : 1;

-- 
H.J.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-10-01  0:24                       ` H.J. Lu
@ 2015-10-01 12:34                         ` Yulia Koval
  2015-10-01 15:59                         ` Uros Bizjak
  1 sibling, 0 replies; 45+ messages in thread
From: Yulia Koval @ 2015-10-01 12:34 UTC (permalink / raw)
  To: H.J. Lu; +Cc: Mike Stump, GCC Patches, Uros Bizjak

[-- Attachment #1: Type: text/plain, Size: 8670 bytes --]

Ok, here is the patch.

The interrupt and exception handlers are called by x86 processors.
X86 hardware pushes information onto stack and calls the handler.  The
requirements are

1. Both interrupt and exception handlers must use the 'IRET'
instruction, instead of the 'RET' instruction, to return from the
handlers.

2. All registers are callee-saved in interrupt and exception handlers.

3. The difference between interrupt and exception handlers is the
exception handler must pop 'ERROR_CODE' off the stack before the
'IRET' instruction.

The design goals of interrupt and exception handlers for x86 processors
are:

1. Support both 32-bit and 64-bit modes.
2. Flexible for compilers to optimize.
3. Easy to use by programmers.

To implement interrupt and exception handlers for x86 processors, a
compiler should support:

'interrupt' attribute

Use this attribute to indicate that the specified function with
mandatory arguments is an interrupt or exception handler.  The
compiler generates function entry and exit sequences suitable for use
in an interrupt handler when this attribute is present.  The 'IRET'
instruction, instead of the 'RET' instruction, is used to return from
interrupt or exception handlers.  All registers, except for the EFLAGS
register which is restored by the 'IRET' instruction, are preserved by
the compiler.

Any interruptible-without-stack-switch code must be compiled with
-mno-red-zone since interrupt handlers can and will, because of the
hardware design, touch the red zone.

1. interrupt handler must be declared with a mandatory pointer argument:

struct interrupt_frame;

__attribute__ ((interrupt))

void
f (struct interrupt_frame *frame)
{
...
}

and user must properly define the structure the pointer pointing to.

2. exception handler:

The exception handler is very similar to the interrupt handler with a
different mandatory function signature:

typedef unsigned long long int uword_t;
typedef unsigned int uword_t;

struct interrupt_frame;

__attribute__ ((interrupt))
void
f (struct interrupt_frame *frame, uword_t error_code)
{
 ...
}

and compiler pops the error code off stack before the 'IRET' instruction.

The exception handler should only be used for exceptions which push an
error code and all other exceptions must use the interrupt handler.

The system will crash if the wrong handler is used.

Bootstrapped/regtested on Linux/x86_64 and Linux/i686.

Ok for trunk?

2015-09-29  Julia Koval <jkoval@intel.com>
                  H.J. Lu <hongjiu.lu@intel.com>

gcc/
                PR target/67630
                PR target/67634
                * config/i386/i386.c (ix86_frame): Add nbndregs and nmaskregs.
                (ix86_nsaved_bndregs): New function.
                (ix86_nsaved_maskregs): Likewise.
                (ix86_reg_save_area_size): Likewise.
                (ix86_nsaved_sseregs): Don't return 0 in interrupt handler.
                (ix86_compute_frame_layout): Set nbndregs and nmaskregs.  Set
                save_regs_using_mov to true to save bound and mask registers.
                Call ix86_reg_save_area_size to get register save area size.
                Allocate space to save full vector registers in
interrupt handler.
                (ix86_emit_save_reg_using_mov): Set alignment to word_mode
                alignment when saving full vector registers in
interrupt handler.
                (ix86_emit_save_regs_using_mov): Use regno_reg_rtx to get
                register size.
                (ix86_emit_restore_regs_using_mov): Likewise.
                (ix86_emit_save_sse_regs_using_mov): Save full vector
registers in
                interrupt handler.
                (ix86_emit_restore_sse_regs_using_mov): Restore full vector
                registers in interrupt handler.
                (ix86_expand_epilogue): Use move to restore bound registers.
                * config/i386/sse.md (*mov<mode>_internal): Handle misaligned
                SSE load and store in interrupt handler.

                PR target/66960
                * config/i386/i386-protos.h (ix86_epilogue_uses) New
function decl.
                * config/i386/i386.c (ix86_epilogue_uses) New function.
                (ix86_set_current_function): Set is_interrupt and is_exception.
                Mark arguments in interrupt handler as used.
                (ix86_function_ok_for_sibcall): Return false if in interrupt
                handler.
                (type_natural_mode): Don't warn ABI change for MMX in interrupt
                handler.
                (ix86_function_arg_advance): Skip for callee in interrupt
                handler.
                (ix86_function_arg): Handle arguments for callee in interrupt
                handler.
                (ix86_can_use_return_insn_p): Don't use `ret' instruction in
                interrupt handler.
                (ix86_save_reg): Preserve callee-saved and
caller-saved registers
                in interrupt handler if needed.
                (ix86_expand_epilogue): Generate interrupt return for
                interrupt handler and pop the 'ERROR_CODE' off the stack before
                interrupt return in exception handler.
                (ix86_expand_move): Disallow 80387 instructions in exception
                handler.
                (ix86_expand_vector_move): Disallow MMX/3Dnow instructions in
                exception handler.
                (ix86_expand_call): Set make_calls.
                (ix86_handle_interrupt_attribute): New function.
                (ix86_attribute_table): Add interrupt attribute.
                * config/i386/i386.h (EPILOGUE_USES) Set hook to
ix86_epilogue_uses.
                (machine_function): Add is_interrupt, is_exception and
                call_with_caller_saved_registers.
                * config/i386/i386.md (UNSPEC_INTERRUPT_RETURN): New.
                (interrupt_return): New pattern.
                * doc/extend.texi: Document x86 interrupt attribute.

gcc/testsuite/

                PR target/66960
                PR target/67630
                PR target/67634
                * gcc.target/i386/interrupt-1.c: New test.
                * gcc.target/i386/interrupt-2.c: Likewise.
                * gcc.target/i386/interrupt-3.c: Likewise.
                * gcc.target/i386/interrupt-4.c: Likewise.
                * gcc.target/i386/interrupt-5.c: Likewise.
                * gcc.target/i386/interrupt-6.c: Likewise.
                * gcc.target/i386/interrupt-7.c: Likewise.
                * gcc.target/i386/interrupt-8.c: Likewise.
                * gcc.target/i386/interrupt-9.c: Likewise.
                * gcc.target/i386/interrupt-10.c: Likewise.
                * gcc.target/i386/interrupt-11.c: Likewise.
                * gcc.target/i386/interrupt-12.c: Likewise.
                * gcc.target/i386/interrupt-13.c: Likewise.
                * gcc.target/i386/interrupt-14.c: Likewise.
                * gcc.target/i386/interrupt-387-err.c: Likewise.
                * gcc.target/i386/interrupt-bnd.c: Likewise.
                * gcc.target/i386/interrupt-iamcu.c: Likewise.
                * gcc.target/i386/interrupt-mmx-err.c: Likewise.
                * gcc.target/i386/interrupt-redzone-1.c: Likewise.
                * gcc.target/i386/interrupt-redzone-2.c: Likewise.
                * gcc.target/i386/interrupt-sibcall.c: Likewise.
                * gcc.target/i386/interrupt-switch-abi.c: Likewise.
                * gcc.target/i386/interrupt-xmm.c: Likewise.
                * gcc.target/i386/interrupt-ymm.c: Likewise.
                * gcc.target/i386/interrupt-zmm.c: Likewise.

Julia

On Thu, Oct 1, 2015 at 3:24 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Wed, Sep 30, 2015 at 12:53 PM, Yulia Koval <vaalfreja@gmail.com> wrote:
>> Done.
>>
>
> +  /* If true, the current function is an interrupt service
> +     routine as specified by the "interrupt" attribute.  */
> +  BOOL_BITFIELD is_interrupt : 1;
> +
> +  /* If true, the current function is an exception service
> +     routine as specified by the "interrupt" attribute.  */
> +  BOOL_BITFIELD is_exception : 1;
>
>
> It is not very clear what is the difference between is_interrupt
> and is_exception.  How about
>
>   /* If true, the current function is an interrupt service routine with
>      a pointer argument and an optional integer argument as specified by
>      the "interrupt" attribute.  */
>   BOOL_BITFIELD is_interrupt : 1;
>
>   /* If true, the current function is an interrupt service routine with
>      a pointer argument and an integer argument as specified by the
>      "interrupt" attribute.  */
>   BOOL_BITFIELD is_exception : 1;
>
> --
> H.J.

[-- Attachment #2: patch_01.10_interrupt --]
[-- Type: application/octet-stream, Size: 52697 bytes --]

diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index 6a17ef4..c7ff393 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -278,6 +278,8 @@ extern rtx maybe_get_pool_constant (rtx);
 extern char internal_label_prefix[16];
 extern int internal_label_prefix_len;
 
+extern bool ix86_epilogue_uses (int);
+
 enum ix86_address_seg { SEG_DEFAULT, SEG_FS, SEG_GS };
 struct ix86_address
 {
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index fe9c756..5cb48a6 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -2381,6 +2381,8 @@ struct ix86_frame
 {
   int nsseregs;
   int nregs;
+  int nbndregs;
+  int nmaskregs;
   int va_arg_size;
   int red_zone_size;
   int outgoing_arguments_size;
@@ -6245,6 +6247,24 @@ ix86_set_current_function (tree fndecl)
       return;
     }
 
+  if (lookup_attribute ("interrupt", DECL_ATTRIBUTES (fndecl)))
+    {
+      cfun->machine->is_interrupt = true;
+      int nargs = 0;
+      for (tree arg = DECL_ARGUMENTS (fndecl);
+	   arg;
+	   arg = TREE_CHAIN (arg))
+	{
+	  /* Mark arguments in interrupt handler as used to silence
+	     compiler warning since arguments in interrupt handler
+	     are mandatory even if they aren't used.  */
+	  TREE_USED (arg) = 1;
+	  nargs++;
+	}
+      if (nargs == 2)
+        cfun->machine->is_exception = true;
+    }
+
   tree new_tree = DECL_FUNCTION_SPECIFIC_TARGET (fndecl);
   if (new_tree == NULL_TREE)
     new_tree = target_option_default_node;
@@ -6523,6 +6543,11 @@ ix86_function_ok_for_sibcall (tree decl, tree exp)
   tree type, decl_or_type;
   rtx a, b;
 
+  /* Sibling call isn't OK in interrupt handler since it must return
+     with the "IRET" instruction.  */
+  if (cfun->machine->is_interrupt)
+    return false;
+
   /* If we are generating position-independent code, we cannot sibcall
      optimize direct calls to global functions, as the PLT requires
      %ebx be live. (Darwin does not have a PLT.)  */
@@ -7707,6 +7732,7 @@ type_natural_mode (const_tree type, const CUMULATIVE_ARGS *cum,
 		      }
 		  }
 		else if ((size == 8 && !TARGET_64BIT)
+			 && (!cfun || !cfun->machine->is_interrupt)
 			 && !TARGET_MMX
 			 && !TARGET_IAMCU)
 		  {
@@ -8686,6 +8712,11 @@ ix86_function_arg_advance (cumulative_args_t cum_v, machine_mode mode,
   HOST_WIDE_INT bytes, words;
   int nregs;
 
+  /* The argument of interrupt handler is a special case and is
+     handled in ix86_function_arg.  */
+  if (!cum->caller && cfun->machine->is_interrupt)
+    return;
+
   if (mode == BLKmode)
     bytes = int_size_in_bytes (type);
   else
@@ -9002,6 +9033,39 @@ ix86_function_arg (cumulative_args_t cum_v, machine_mode omode,
   HOST_WIDE_INT bytes, words;
   rtx arg;
 
+  if (!cum->caller && cfun->machine->is_interrupt)
+    {
+      /* The first argument of interrupt handler is a pointer and
+	 points to the return address on stack.  The optional second
+	 argument is an integer for error code on stack.  */
+      gcc_assert (type != NULL_TREE);
+      if (POINTER_TYPE_P (type))
+	{
+	  if (cfun->machine->is_exception)
+	    /* (AP) in the current frame in exception handler.  */
+	    arg = arg_pointer_rtx;
+	  else
+	    /* -WORD(AP) in the current frame in interrupt handler.  */
+	    arg = plus_constant (Pmode, arg_pointer_rtx,
+				 -UNITS_PER_WORD);
+	  if (mode != Pmode)
+	    arg = convert_to_mode (mode, arg, 1);
+	}
+      else
+	{
+	  gcc_assert (TREE_CODE (type) == INTEGER_TYPE
+		      && cfun->machine->is_exception
+		      && mode == word_mode);
+	  /* The error code is at -WORD(AP) in the current frame in
+	     exception handler.  */
+	  arg = gen_rtx_MEM (word_mode,
+			     plus_constant (Pmode, arg_pointer_rtx,
+					    -UNITS_PER_WORD));
+	}
+
+      return arg;
+    }
+
   /* All pointer bounds arguments are handled separately here.  */
   if ((type && POINTER_BOUNDS_TYPE_P (type))
       || POINTER_BOUNDS_MODE_P (mode))
@@ -10717,7 +10781,10 @@ ix86_can_use_return_insn_p (void)
 {
   struct ix86_frame frame;
 
-  if (! reload_completed || frame_pointer_needed)
+  /* Don't use `ret' instruction in interrupt handler.  */
+  if (! reload_completed
+      || frame_pointer_needed
+      || cfun->machine->is_interrupt)
     return 0;
 
   /* Don't allow more than 32k pop, since that's all we can do
@@ -11027,11 +11094,46 @@ ix86_select_alt_pic_regnum (void)
   return INVALID_REGNUM;
 }
 
+/* Return true if REGNO is used by the epilogue.  */
+
+bool
+ix86_epilogue_uses (int regno)
+{
+  /* All preserved registers are used by the epilogue of interrupt
+     handler.  Don't explicitly mark BP and SP registers as used
+     since they are always used in epilogue.  */
+  return (cfun->machine->is_interrupt
+	  && reg_names[regno][0]
+	  && !STACK_REGNO_P (regno)
+	  && !MMX_REGNO_P (regno)
+	  && regno != BP_REG
+	  && regno != SP_REG
+	  && (regno <= ST7_REG || regno >= XMM0_REG));
+}
+
 /* Return TRUE if we need to save REGNO.  */
 
 static bool
 ix86_save_reg (unsigned int regno, bool maybe_eh_return)
 {
+  /* In interrupt handler, we don't preserve MMX and x87 registers
+     which aren't supported when saving and restoring registers.  No
+     need to preserve callee-saved registers unless they are modified.
+     We also preserve all caller-saved registers if a function call
+     is made in interrupt handler since the called function may change
+     them.  Don't explicitly save BP and SP registers since they are
+     always preserved.  */
+  if (cfun->machine->is_interrupt && reload_completed)
+    return ((df_regs_ever_live_p (regno)
+	     || (call_used_regs[regno]
+		 && cfun->machine->call_with_caller_saved_registers))
+	    && !fixed_regs[regno]
+	    && !STACK_REGNO_P (regno)
+	    && !MMX_REGNO_P (regno)
+	    && regno != BP_REG
+	    && regno != SP_REG
+	    && (regno <= ST7_REG || regno >= XMM0_REG));
+
   if (regno == REAL_PIC_OFFSET_TABLE_REGNUM
       && pic_offset_table_rtx)
     {
@@ -11088,6 +11190,34 @@ ix86_nsaved_regs (void)
   return nregs;
 }
 
+/* Return number of saved bound registers.  */
+
+static int
+ix86_nsaved_bndregs (void)
+{
+  int nregs = 0;
+  int regno;
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (BND_REGNO_P (regno) && ix86_save_reg (regno, true))
+      nregs ++;
+  return nregs;
+}
+
+/* Return number of saved mask registers.  */
+
+static int
+ix86_nsaved_maskregs (void)
+{
+  int nregs = 0;
+  int regno;
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (MASK_REGNO_P (regno) && ix86_save_reg (regno, true))
+      nregs ++;
+  return nregs;
+}
+
 /* Return number of saved SSE registers.  */
 
 static int
@@ -11096,7 +11226,8 @@ ix86_nsaved_sseregs (void)
   int nregs = 0;
   int regno;
 
-  if (!TARGET_64BIT_MS_ABI)
+  /* Always need to save SSE registers in interrupt handler.  */
+  if (!TARGET_64BIT_MS_ABI && !cfun->machine->is_interrupt)
     return 0;
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
@@ -11165,6 +11296,18 @@ ix86_builtin_setjmp_frame_value (void)
 
 #define SPLIT_STACK_AVAILABLE 256
 
+/* Register save area size.  */
+
+static unsigned int
+ix86_reg_save_area_size (struct ix86_frame *frame)
+{
+  unsigned int size = ((frame->nregs - frame->nbndregs
+			- frame->nmaskregs) * UNITS_PER_WORD);
+  size += frame->nbndregs * (ix86_pmode == PMODE_DI ? 16 : 8);
+  size += frame->nmaskregs * (TARGET_AVX512BW ? 8 : 2);
+  return size;
+}
+
 /* Fill structure ix86_frame about frame of currently computed function.  */
 
 static void
@@ -11177,6 +11320,8 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   HOST_WIDE_INT to_allocate;
 
   frame->nregs = ix86_nsaved_regs ();
+  frame->nbndregs = ix86_nsaved_bndregs ();
+  frame->nmaskregs = ix86_nsaved_maskregs ();
   frame->nsseregs = ix86_nsaved_sseregs ();
 
   /* 64-bit MS ABI seem to require stack alignment to be always 16 except for
@@ -11248,11 +11393,16 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
 	   = !expensive_function_p (count);
     }
 
+  /* We must use move to save bound and mask registers.  */
   frame->save_regs_using_mov
-    = (TARGET_PROLOGUE_USING_MOVE && cfun->machine->use_fast_prologue_epilogue
-       /* If static stack checking is enabled and done with probes,
-	  the registers need to be saved before allocating the frame.  */
-       && flag_stack_check != STATIC_BUILTIN_STACK_CHECK);
+    = (frame->nbndregs > 0
+       || frame->nmaskregs > 0
+       || (TARGET_PROLOGUE_USING_MOVE
+	   && cfun->machine->use_fast_prologue_epilogue
+	   /* If static stack checking is enabled and done with probes,
+	      the registers need to be saved before allocating the
+	      frame.  */
+	   && flag_stack_check != STATIC_BUILTIN_STACK_CHECK));
 
   /* Skip return address.  */
   offset = UNITS_PER_WORD;
@@ -11270,7 +11420,7 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   frame->hard_frame_pointer_offset = offset;
 
   /* Register save area */
-  offset += frame->nregs * UNITS_PER_WORD;
+  offset += ix86_reg_save_area_size (frame);
   frame->reg_save_offset = offset;
 
   /* On SEH target, registers are pushed just before the frame pointer
@@ -11284,9 +11434,17 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
       /* The only ABI that has saved SSE registers (Win64) also has a
          16-byte aligned default stack, and thus we don't need to be
 	 within the re-aligned local stack frame to save them.  */
-      gcc_assert (INCOMING_STACK_BOUNDARY >= 128);
-      offset = (offset + 16 - 1) & -16;
-      offset += frame->nsseregs * 16;
+      if (cfun->machine->is_interrupt)
+	/* We must save full vector registers in interrupt handler.  */
+	offset += frame->nsseregs * (TARGET_AVX512F
+				     ? 64
+				     : (TARGET_AVX ? 32 : 16));
+      else
+	{
+	  gcc_assert (INCOMING_STACK_BOUNDARY >= 128);
+	  offset = (offset + 16 - 1) & -16;
+	  offset += frame->nsseregs * 16;
+	}
     }
   frame->sse_reg_save_offset = offset;
 
@@ -11342,8 +11500,12 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   /* Size prologue needs to allocate.  */
   to_allocate = offset - frame->sse_reg_save_offset;
 
-  if ((!to_allocate && frame->nregs <= 1)
-      || (TARGET_64BIT && to_allocate >= (HOST_WIDE_INT) 0x80000000))
+  /* We must use move to save bound and mask registers.  */
+  if (frame->nbndregs == 0
+      && frame->nmaskregs == 0
+      && ((!to_allocate && frame->nregs <= 1)
+	   || (TARGET_64BIT
+	       && to_allocate >= (HOST_WIDE_INT) 0x80000000)))
     frame->save_regs_using_mov = false;
 
   if (ix86_using_red_zone ()
@@ -11353,7 +11515,7 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
     {
       frame->red_zone_size = to_allocate;
       if (frame->save_regs_using_mov)
-	frame->red_zone_size += frame->nregs * UNITS_PER_WORD;
+	frame->red_zone_size += ix86_reg_save_area_size (frame);
       if (frame->red_zone_size > RED_ZONE_SIZE - RED_ZONE_RESERVE)
 	frame->red_zone_size = RED_ZONE_SIZE - RED_ZONE_RESERVE;
     }
@@ -11513,8 +11675,14 @@ ix86_emit_save_reg_using_mov (machine_mode mode, unsigned int regno,
   addr = choose_baseaddr (cfa_offset);
   mem = gen_frame_mem (mode, addr);
 
-  /* For SSE saves, we need to indicate the 128-bit alignment.  */
-  set_mem_align (mem, GET_MODE_ALIGNMENT (mode));
+  /* For SSE saves, we need to indicate the 128-bit alignment.  In
+     interrupt handler, stack is only aligned to word_mode.  We can't
+     use gen_sse_storeups since RTX_FRAME_RELATED_P is set and
+     dwarf2out_flush_queued_reg_saves doesn't like UNSPEC_STOREU.
+     Also gen_sse_storeups doesn't cover AVX nor AVX512.  */
+  set_mem_align (mem,
+		 GET_MODE_ALIGNMENT (cfun->machine->is_interrupt
+				     ? word_mode : mode));
 
   insn = emit_move_insn (mem, reg);
   RTX_FRAME_RELATED_P (insn) = 1;
@@ -11575,8 +11743,9 @@ ix86_emit_save_regs_using_mov (HOST_WIDE_INT cfa_offset)
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (!SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
       {
-        ix86_emit_save_reg_using_mov (word_mode, regno, cfa_offset);
-	cfa_offset -= UNITS_PER_WORD;
+	enum machine_mode reg_mode = GET_MODE (regno_reg_rtx[regno]);
+	ix86_emit_save_reg_using_mov (reg_mode, regno, cfa_offset);
+	cfa_offset -= GET_MODE_SIZE (reg_mode);
       }
 }
 
@@ -11586,12 +11755,26 @@ static void
 ix86_emit_save_sse_regs_using_mov (HOST_WIDE_INT cfa_offset)
 {
   unsigned int regno;
+  enum machine_mode vector_reg_mode;
+
+  if (cfun->machine->is_interrupt)
+    {
+      /* We must save full vector registers in interrupt handler.  */
+      if (TARGET_AVX512F)
+	vector_reg_mode = V16SFmode;
+      else if (TARGET_AVX)
+	vector_reg_mode = V8SFmode;
+      else
+	vector_reg_mode = V4SFmode;
+    }
+  else
+    vector_reg_mode = V4SFmode;
 
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
       {
-	ix86_emit_save_reg_using_mov (V4SFmode, regno, cfa_offset);
-	cfa_offset -= 16;
+	ix86_emit_save_reg_using_mov (vector_reg_mode, regno, cfa_offset);
+	cfa_offset -= GET_MODE_SIZE (vector_reg_mode);
       }
 }
 
@@ -13036,12 +13219,13 @@ ix86_emit_restore_regs_using_mov (HOST_WIDE_INT cfa_offset,
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (!SSE_REGNO_P (regno) && ix86_save_reg (regno, maybe_eh_return))
       {
-	rtx reg = gen_rtx_REG (word_mode, regno);
+	enum machine_mode reg_mode = GET_MODE (regno_reg_rtx[regno]);
+	rtx reg = gen_rtx_REG (reg_mode, regno);
 	rtx mem;
 	rtx_insn *insn;
 
 	mem = choose_baseaddr (cfa_offset);
-	mem = gen_frame_mem (word_mode, mem);
+	mem = gen_frame_mem (reg_mode, mem);
 	insn = emit_move_insn (reg, mem);
 
         if (m->fs.cfa_reg == crtl->drap_reg && regno == REGNO (crtl->drap_reg))
@@ -13060,7 +13244,7 @@ ix86_emit_restore_regs_using_mov (HOST_WIDE_INT cfa_offset,
 	else
 	  ix86_add_cfa_restore_note (NULL, reg, cfa_offset);
 
-	cfa_offset -= UNITS_PER_WORD;
+	cfa_offset -= GET_MODE_SIZE (reg_mode);
       }
 }
 
@@ -13071,21 +13255,39 @@ ix86_emit_restore_sse_regs_using_mov (HOST_WIDE_INT cfa_offset,
 				      bool maybe_eh_return)
 {
   unsigned int regno;
+  enum machine_mode vector_reg_mode;
+
+  if (cfun->machine->is_interrupt)
+    {
+      /* We must restore full vector registers in interrupt handler.  */
+      if (TARGET_AVX512F)
+	vector_reg_mode = V16SFmode;
+      else if (TARGET_AVX)
+	vector_reg_mode = V8SFmode;
+      else
+	vector_reg_mode = V4SFmode;
+    }
+  else
+    vector_reg_mode = V4SFmode;
+
 
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, maybe_eh_return))
       {
-	rtx reg = gen_rtx_REG (V4SFmode, regno);
+	rtx reg = gen_rtx_REG (vector_reg_mode, regno);
 	rtx mem;
 
 	mem = choose_baseaddr (cfa_offset);
-	mem = gen_rtx_MEM (V4SFmode, mem);
-	set_mem_align (mem, 128);
+	mem = gen_rtx_MEM (vector_reg_mode, mem);
+	/* In interrupt handler, stack is only aligned to word_mode.  */
+	set_mem_align (mem, (cfun->machine->is_interrupt
+			     ? GET_MODE_ALIGNMENT (word_mode)
+			     : 128));
 	emit_move_insn (reg, mem);
 
 	ix86_add_cfa_restore_note (NULL, reg, cfa_offset);
 
-	cfa_offset -= 16;
+	cfa_offset -= GET_MODE_SIZE (vector_reg_mode);
       }
 }
 
@@ -13149,8 +13351,9 @@ ix86_expand_epilogue (int style)
   if (crtl->calls_eh_return && style != 2)
     frame.reg_save_offset -= 2 * UNITS_PER_WORD;
 
-  /* EH_RETURN requires the use of moves to function properly.  */
-  if (crtl->calls_eh_return)
+  /* EH_RETURN requires the use of moves to function properly.  We must
+     use move to restore bound and mask registers.  */
+  if (crtl->calls_eh_return || frame.nbndregs > 0 || frame.nmaskregs > 0)
     restore_regs_via_mov = true;
   /* SEH requires the use of pops to identify the epilogue.  */
   else if (TARGET_SEH)
@@ -13391,7 +13594,20 @@ ix86_expand_epilogue (int style)
       return;
     }
 
-  if (crtl->args.pops_args && crtl->args.size)
+  if (cfun->machine->is_interrupt)
+    {
+      /* Return with the "IRET" instruction from interrupt handler.
+	 Pop the 'ERROR_CODE' off the stack before the 'IRET'
+	 instruction in exception handler.  */
+      if (cfun->machine->is_exception)
+	{
+	  rtx r = plus_constant (Pmode, stack_pointer_rtx,
+				 UNITS_PER_WORD);
+	  emit_insn (gen_rtx_SET (stack_pointer_rtx, r));
+	}
+      emit_jump_insn (gen_interrupt_return ());
+    }
+  else if (crtl->args.pops_args && crtl->args.size)
     {
       rtx popc = GEN_INT (crtl->args.pops_args);
 
@@ -18364,6 +18580,13 @@ ix86_expand_move (machine_mode mode, rtx operands[])
   rtx op0, op1;
   enum tls_model model;
 
+  if (cfun->machine->is_interrupt && IS_STACK_MODE (mode))
+    {
+      error ("80387 instructions aren't allowed in %s service routine",
+	     (cfun->machine->is_exception ? "exception" : "interrupt"));
+      return;
+    }
+
   op0 = operands[0];
   op1 = operands[1];
 
@@ -18508,6 +18731,13 @@ ix86_expand_vector_move (machine_mode mode, rtx operands[])
   rtx op0 = operands[0], op1 = operands[1];
   unsigned int align = GET_MODE_ALIGNMENT (mode);
 
+  if (cfun->machine->is_interrupt && VALID_MMX_REG_MODE (mode))
+    {
+      error ("MMX/3Dnow instructions aren't allowed in %s service routine",
+	     (cfun->machine->is_exception ? "exception" : "interrupt"));
+      return;
+    }
+
   if (push_operand (op0, VOIDmode))
     op0 = emit_move_resolve_push (mode, op0);
 
@@ -26594,6 +26824,12 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
   rtx use = NULL, call;
   unsigned int vec_len = 0;
 
+  if (GET_CODE (XEXP (fnaddr, 0)) == SYMBOL_REF
+      && SYMBOL_REF_DECL ((XEXP (fnaddr, 0))) != NULL_TREE
+      && lookup_attribute ("interrupt",
+			   DECL_ATTRIBUTES (SYMBOL_REF_DECL (XEXP (fnaddr, 0)))))
+    error ("interrupt service routine can't be called directly");
+
   if (pop == const0_rtx)
     pop = NULL;
   gcc_assert (!TARGET_64BIT || !pop);
@@ -26712,6 +26948,8 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
   if (use)
     CALL_INSN_FUNCTION_USAGE (call) = use;
 
+  cfun->machine->call_with_caller_saved_registers = true;
+
   return call;
 }
 
@@ -44072,6 +44310,56 @@ ix86_handle_fndecl_attribute (tree *node, tree name, tree, int,
   return NULL_TREE;
 }
 
+
+static tree
+ix86_handle_interrupt_attribute (tree *node, tree name, tree, int,
+				 bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+	       name);
+      *no_add_attrs = true;
+    }
+
+  /* DECL_RESULT and DECL_ARGUMENTS do not exist there yet,
+     but the function type contains args and return type data.  */
+  tree func_type = TREE_TYPE (*node);
+  tree return_type = TREE_TYPE (func_type);
+
+  int nargs = 0;
+  tree current_arg_type = TYPE_ARG_TYPES (func_type);
+  while (current_arg_type
+	 && ! VOID_TYPE_P (TREE_VALUE (current_arg_type)))
+    {
+      if (nargs == 0)
+	{
+	  if (! POINTER_TYPE_P (TREE_VALUE (current_arg_type)))
+	    error ("interrupt service routine should have a pointer "
+		   "as the first argument");
+	}
+      else if (nargs == 1)
+	{
+	  if (TREE_CODE (TREE_VALUE (current_arg_type)) != INTEGER_TYPE
+	      || TYPE_MODE (TREE_VALUE (current_arg_type)) != word_mode)
+	    error ("interrupt service routine should have unsigned %s"
+		   "int as the second argument",
+		   TARGET_64BIT
+		   ? (TARGET_X32 ? "long long " : "long ")
+		   : "");
+	}
+      nargs++;
+      current_arg_type = TREE_CHAIN (current_arg_type);
+    }
+  if (!nargs || nargs > 2)
+    error ("interrupt service routine can only have a pointer argument "
+	   "and an optional integer argument");
+  if (! VOID_TYPE_P (return_type))
+    error ("interrupt service routine can't have non-void return value");
+
+  return NULL_TREE;
+}
+
 static bool
 ix86_ms_bitfield_layout_p (const_tree record_type)
 {
@@ -48073,6 +48361,9 @@ static const struct attribute_spec ix86_attribute_table[] =
     false },
   { "callee_pop_aggregate_return", 1, 1, false, true, true,
     ix86_handle_callee_pop_aggregate_return, true },
+  { "interrupt", 0, 0, true, false, false,
+    ix86_handle_interrupt_attribute, false },
+
   /* End element.  */
   { NULL,        0, 0, false, false, false, NULL, false }
 };
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 96ba90f..45fb6b8 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -1730,6 +1730,11 @@ typedef struct ix86_args {
 
 #define EXIT_IGNORE_STACK 1
 
+/* Define this macro as a C expression that is nonzero for registers
+   used by the epilogue or the `return' pattern.  */
+
+#define EPILOGUE_USES(REGNO) ix86_epilogue_uses (REGNO)
+
 /* Output assembler code for a block containing the constant parts
    of a trampoline, leaving space for the variable parts.  */
 
@@ -2494,6 +2499,20 @@ struct GTY(()) machine_function {
   /* If true, it is safe to not save/restore DRAP register.  */
   BOOL_BITFIELD no_drap_save_restore : 1;
 
+  /* If true, the current function is an interrupt service routine with
+     a pointer argument and an optional integer argument as specified by
+     the "interrupt" attribute.  */
+  BOOL_BITFIELD is_interrupt : 1;
+
+  /* If true, the current function is an interrupt service routine with
+     a pointer argument and an integer argument as specified by the
+     "interrupt" attribute.  */
+  BOOL_BITFIELD is_exception : 1;
+
+  /* If true, the current function makes any function calls with
+     caller-saved registers.  */
+  BOOL_BITFIELD call_with_caller_saved_registers : 1;
+
   /* If true, there is register available for argument passing.  This
      is used only in ix86_function_ok_for_sibcall by 32-bit to determine
      if there is scratch register available for indirect sibcall.  In
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 8c2ed60..8b22be4 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -193,6 +193,9 @@
   UNSPEC_BNDCU
   UNSPEC_BNDCN
   UNSPEC_MPX_FENCE
+
+  ;; IRET support
+  UNSPEC_INTERRUPT_RETURN
 ])
 
 (define_c_enum "unspecv" [
@@ -12179,6 +12182,12 @@
    (set_attr "modrm" "0")
    (set_attr "maybe_prefix_bnd" "1")])
 
+(define_insn "interrupt_return"
+  [(simple_return)
+   (unspec [(const_int 0)] UNSPEC_INTERRUPT_RETURN)]
+  "reload_completed"
+  "iret")
+
 ;; Used by x86_machine_dependent_reorg to avoid penalty on single byte RET
 ;; instruction Athlon and K8 have.
 
diff --git a/gcc/config/i386/sse.md b/gcc/config/i386/sse.md
index 4eefb45..3f932a7 100644
--- a/gcc/config/i386/sse.md
+++ b/gcc/config/i386/sse.md
@@ -875,10 +875,14 @@
 	case MODE_V16SF:
 	case MODE_V8SF:
 	case MODE_V4SF:
-	  if (TARGET_AVX
+	  /* We must supprt misaligned SSE load and store in interrupt
+	     handler since ix86_emit_save_reg_using_mov generates the
+	     normal *mov<mode>_internal pattern for interrupt handler
+	     with misaligned stack.  */
+	  if ((TARGET_AVX || cfun->machine->is_interrupt)
 	      && (misaligned_operand (operands[0], <MODE>mode)
 		  || misaligned_operand (operands[1], <MODE>mode)))
-	    return "vmovups\t{%1, %0|%0, %1}";
+	    return "%vmovups\t{%1, %0|%0, %1}";
 	  else
 	    return "%vmovaps\t{%1, %0|%0, %1}";
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 8406945..9f89e99 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -5061,6 +5061,62 @@ On x86-32 targets, the @code{stdcall} attribute causes the compiler to
 assume that the called function pops off the stack space used to
 pass arguments, unless it takes a variable number of arguments.
 
+@item interrupt
+@cindex @code{interrupt} function attribute, x86
+Use this attribute to indicate that the specified function is an
+interrupt handler.  The compiler generates function
+entry and exit sequences suitable for use in an interrupt handler when
+this attribute is present.  The @code{IRET} instruction, instead of the
+@code{RET} instruction, is used to return from interrupt handlers.  All
+registers, except for the EFLAGS register which is restored by the
+@code{IRET} instruction, are preserved by the compiler.
+
+Any interruptible-without-stack-switch code must be compiled with
+@option{-mno-red-zone} since interrupt handlers can and will, because
+of the hardware design, touch the red zone.
+
+An interrupt handler must be declared with a mandatory pointer
+argument:
+
+@smallexample
+struct interrupt_frame;
+
+__attribute__ ((interrupt))
+void
+f (struct interrupt_frame *frame)
+@{
+@}
+@end smallexample
+
+and user must properly define the structure the pointer pointing to.
+
+The exception handler is very similar to the interrupt handler with
+a different mandatory function signature:
+
+@smallexample
+#ifdef __x86_64__
+typedef unsigned long long int uword_t;
+#else
+typedef unsigned int uword_t;
+#endif
+
+struct interrupt_frame;
+
+__attribute__ ((interrupt))
+void
+f (struct interrupt_frame *frame, uword_t error_code)
+@{
+  ...
+@}
+@end smallexample
+
+and compiler pops the error code off the stack before the @code{IRET}
+instruction.
+
+The exception handler should only be used for exceptions which push an
+error code and all other exceptions must use the interrupt handler.
+The system will crash if the wrong handler is used.
+
 @item target (@var{options})
 @cindex @code{target} function attribute
 As discussed in @ref{Common Function Attributes}, this attribute 
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-1.c b/gcc/testsuite/gcc.target/i386/interrupt-1.c
new file mode 100644
index 0000000..ea2f1e4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-1.c
@@ -0,0 +1,46 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-push-args" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern int bar (int);
+
+void foo (void *frame)
+{
+  int a,b,c,d,e,f,i;
+  a = bar (5);
+  b = bar (a);
+  c = bar (b);
+  d = bar (c);
+  e = bar (d);
+  f = bar (e);
+  for (i = 1; i < 10; i++)
+  {
+    a += bar (a + i) + bar (b + i) +
+	 bar (c + i) + bar (d + i) +
+	 bar (e + i) + bar (f + i);
+  }
+}
+
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-10.c b/gcc/testsuite/gcc.target/i386/interrupt-10.c
new file mode 100644
index 0000000..a439d6f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-10.c
@@ -0,0 +1,24 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mavx512bw -mno-iamcu" } */
+
+extern void bar (void);
+
+struct interrupt_frame;
+
+void
+ __attribute__ ((interrupt))
+foo (struct interrupt_frame *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm\[0-9\]+" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "kmovq\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" 8 } } */
+/* { dg-final { scan-assembler-times "kmovq\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" 8 } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-11.c b/gcc/testsuite/gcc.target/i386/interrupt-11.c
new file mode 100644
index 0000000..6556c44
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-11.c
@@ -0,0 +1,38 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-sse -mno-iamcu" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-12.c b/gcc/testsuite/gcc.target/i386/interrupt-12.c
new file mode 100644
index 0000000..5c9eb74
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-12.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int check_int (int *i, void *, int align);
+typedef int aligned __attribute__((aligned(64)));
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+__attribute__((interrupt))
+void
+foo (void *frame, uword_t error_code)
+{
+  aligned j;
+  if (check_int (frame, &j, __alignof__(j)))
+    __builtin_abort ();
+}
+
+/* { dg-final { scan-assembler-times "and\[lq\]?\[^\\n\]*-64,\[^\\n\]*sp" 1 } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-13.c b/gcc/testsuite/gcc.target/i386/interrupt-13.c
new file mode 100644
index 0000000..8cf0b25
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-13.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int check_int (int *i, void *, int align);
+typedef int aligned __attribute__((aligned(64)));
+
+__attribute__((interrupt))
+void
+foo (void *frame)
+{
+  aligned j;
+  if (check_int (frame, &j, __alignof__(j)))
+    __builtin_abort ();
+}
+
+/* { dg-final { scan-assembler-times "and\[lq\]?\[^\\n\]*-64,\[^\\n\]*sp" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-14.c b/gcc/testsuite/gcc.target/i386/interrupt-14.c
new file mode 100644
index 0000000..7394207
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-14.c
@@ -0,0 +1,39 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-sse -mno-iamcu -mno-sse4 -mno-popcnt" } */
+
+extern int i, cnt;
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  cnt = __builtin_popcount (i);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 1 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-2.c b/gcc/testsuite/gcc.target/i386/interrupt-2.c
new file mode 100644
index 0000000..2003fbb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-2.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wall -Wunused-parameter" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+}
+
+/* { dg-final { scan-assembler-not "add(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-3.c b/gcc/testsuite/gcc.target/i386/interrupt-3.c
new file mode 100644
index 0000000..a87e594
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-3.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+void
+__attribute__((interrupt))
+fn (void* frame, uword_t error)
+{
+}
+
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-387-err.c b/gcc/testsuite/gcc.target/i386/interrupt-387-err.c
new file mode 100644
index 0000000..3a4a5cf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-387-err.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -m80387 -mlong-double-80 -mno-iamcu" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+extern long double y, x;
+
+void
+fn0 (void)
+{
+  x = y;
+  y = 0;
+}
+
+void
+__attribute__((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  x = 0;
+} /* { dg-error "80387 instructions aren't allowed in exception service routine" } */
+
+void
+__attribute__((interrupt))
+fn2 (void *frame)
+{
+  x = y; /* { dg-error "80387 instructions aren't allowed in interrupt service routine" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-4.c b/gcc/testsuite/gcc.target/i386/interrupt-4.c
new file mode 100644
index 0000000..94230cd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-4.c
@@ -0,0 +1,32 @@
+/* { dg-do link } */
+/* { dg-options "-O" } */
+
+#include <stdint.h>
+
+extern void link_error (void);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+struct interrupt_frame
+{
+  uword_t ip;
+  uword_t cs;
+  uword_t flags;
+  uword_t sp;
+  uword_t ss;
+};
+
+__attribute__ ((used, interrupt))
+void
+foo (struct interrupt_frame *frame)
+{
+  void *ra = __builtin_return_address (0);
+  if ((uintptr_t) ra != (uintptr_t) frame->ip)
+    link_error ();
+}
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-5.c b/gcc/testsuite/gcc.target/i386/interrupt-5.c
new file mode 100644
index 0000000..53d6656
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-5.c
@@ -0,0 +1,23 @@
+/* { dg-do link } */
+/* { dg-options "-O" } */
+
+#include <stdint.h>
+
+extern void link_error (void);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+__attribute__ ((used, interrupt))
+void
+foo (void *frame, uword_t error)
+{
+  void *ra = __builtin_return_address (0);
+  if ((uintptr_t) ra != (uintptr_t) error)
+    link_error ();
+}
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-6.c b/gcc/testsuite/gcc.target/i386/interrupt-6.c
new file mode 100644
index 0000000..a1a3d0e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-6.c
@@ -0,0 +1,40 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+extern int error;
+
+__attribute__((interrupt))
+void
+fn1 (void *p, short error_code)
+{ /* { dg-error "interrupt service routine should have unsigned \(long long |long |\)int as the second argument" } */
+}
+
+__attribute__((interrupt))
+void
+fn2 (void)
+{ /* { dg-error "interrupt service routine can only have a pointer argument and an optional integer argument" } */
+}
+
+__attribute__((interrupt))
+void
+fn3 (uword_t error_code)
+{ /* { dg-error "interrupt service routine should have a pointer as the first argument" } */
+  error = error_code;
+}
+
+__attribute__((interrupt))
+void
+fn4 (uword_t error_code, void *frame)
+{ /* { dg-error "interrupt service routine should have .* the .* argument" } */
+  error = error_code;
+}
+
+extern int fn5 (void *) __attribute__ ((interrupt)); /* { dg-error "interrupt service routine can't have non-void return value" } */
+
+int
+fn5 (void *frame)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-7.c b/gcc/testsuite/gcc.target/i386/interrupt-7.c
new file mode 100644
index 0000000..e7441a2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-7.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int error;
+
+extern void fn (void *) __attribute__((interrupt));
+
+void
+foo (void)
+{
+  fn (&error); /* { dg-error "interrupt service routine can't be called directly" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-8.c b/gcc/testsuite/gcc.target/i386/interrupt-8.c
new file mode 100644
index 0000000..0a83473
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-8.c
@@ -0,0 +1,20 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 16 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm\[0-9\]+" 16 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-9.c b/gcc/testsuite/gcc.target/i386/interrupt-9.c
new file mode 100644
index 0000000..9d0c776
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-9.c
@@ -0,0 +1,22 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512bw -mno-iamcu -mavx512f" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm\[0-9\]+" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "kmovw\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" 8 } } */
+/* { dg-final { scan-assembler-times "kmovw\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" 8 } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-bnd.c b/gcc/testsuite/gcc.target/i386/interrupt-bnd.c
new file mode 100644
index 0000000..9f50661
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-bnd.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mno-iamcu -mmpx" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "bnd3");
+}
+
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*%bnd3,\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%bnd3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
new file mode 100644
index 0000000..77a9463
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
@@ -0,0 +1,30 @@
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -miamcu" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern int bar (int);
+
+void foo (void *frame)
+{
+  int a,b,c,d,e,f,i;
+  a = bar (5);
+  b = bar (a);
+  c = bar (b);
+  d = bar (c);
+  e = bar (d);
+  f = bar (e);
+  for (i = 1; i < 10; i++)
+    a += bar (a + i) + bar (b + i) +
+	 bar (c + i) + bar (d + i) +
+	 bar (e + i) + bar (f+i);
+}
+
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%eax" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%ecx" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%edx" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%eax" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%ecx" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%edx" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c
new file mode 100644
index 0000000..fc4f367
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mmmx -mno-iamcu" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+typedef short __v4hi __attribute__ ((__vector_size__ (8)));
+typedef int __m64 __attribute__ ((__vector_size__ (8), __may_alias__));
+
+extern __m64 y, x;
+
+void
+fn0 (void)
+{
+  x = __extension__ (__m64){ 0 };
+  x = (__m64) __builtin_ia32_packsswb ((__v4hi) x, (__v4hi) y);
+  y = x;
+}
+
+void
+__attribute__((interrupt))
+fn1 (void *frame)
+{
+  x = (__m64) __builtin_ia32_packsswb ((__v4hi) x, (__v4hi) y); /* { dg-error "MMX/3Dnow (instructions|intrinsics) aren't allowed in interrupt service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn2 (void *frame)
+{
+  x = y; /* { dg-error "MMX/3Dnow instructions aren't allowed in interrupt service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn3 (void *frame, uword_t error)
+{
+  x = __extension__ (__m64){ 0 }; /* { dg-error "MMX/3Dnow instructions aren't allowed in exception service routine" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c b/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c
new file mode 100644
index 0000000..796b81b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mred-zone" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  /* No need to adjust stack if less than 128 bytes are used on stack
+     with a 128-byte red zone.  */
+  char array[112];
+  asm ("# %0 "
+       :
+       : "r" (array));
+}
+
+/* { dg-final { scan-assembler-not "(sub|add)(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c b/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c
new file mode 100644
index 0000000..c6a2822
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mred-zone" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  /* Need to adjust stack if more than 128 bytes are used on stack
+     with a 128-byte red zone.  */
+  char array[113];
+  asm ("# %0 "
+       :
+       : "r" (array));
+}
+
+/* { dg-final { scan-assembler-times "(?:sub|add)(?:l|q)\[\\t \]*\\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c b/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c
new file mode 100644
index 0000000..e222acf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O3" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern void bar (void);
+
+void foo (void *frame)
+{
+  bar ();
+}
+/* { dg-final { scan-assembler-not "jmp" } }*/
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c
new file mode 100644
index 0000000..b1b0abe
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern void bar (int);
+
+void f1 (void){  bar (1); }
+__attribute__((interrupt))
+void f2 (void *frame){  bar (2); }
+void f3 (void){  bar (3); }
+__attribute__((interrupt))
+void f4 (void *frame){  bar (4); }
+void f5 (void){  bar (5); }
+
+/* { dg-final { scan-assembler-times "push.\t%.ax" 2 } } */
+/* { dg-final { scan-assembler-times "pop.\t%.ax" 2 } } */
+/* { dg-final { scan-assembler-times "iret" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-xmm.c b/gcc/testsuite/gcc.target/i386/interrupt-xmm.c
new file mode 100644
index 0000000..0eafb10
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-xmm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx -mno-iamcu -msse" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%xmm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%xmm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-ymm.c b/gcc/testsuite/gcc.target/i386/interrupt-ymm.c
new file mode 100644
index 0000000..acb5fc7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-ymm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*%ymm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-zmm.c b/gcc/testsuite/gcc.target/i386/interrupt-zmm.c
new file mode 100644
index 0000000..6d23b5d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-zmm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mavx512f -mno-iamcu" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*%zmm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-10-01  0:24                       ` H.J. Lu
  2015-10-01 12:34                         ` Yulia Koval
@ 2015-10-01 15:59                         ` Uros Bizjak
  2015-10-01 16:08                           ` H.J. Lu
  1 sibling, 1 reply; 45+ messages in thread
From: Uros Bizjak @ 2015-10-01 15:59 UTC (permalink / raw)
  To: H.J. Lu; +Cc: Yulia Koval, Mike Stump, GCC Patches

On Thu, Oct 1, 2015 at 2:24 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Wed, Sep 30, 2015 at 12:53 PM, Yulia Koval <vaalfreja@gmail.com> wrote:
>> Done.
>>
>
> +  /* If true, the current function is an interrupt service
> +     routine as specified by the "interrupt" attribute.  */
> +  BOOL_BITFIELD is_interrupt : 1;
> +
> +  /* If true, the current function is an exception service
> +     routine as specified by the "interrupt" attribute.  */
> +  BOOL_BITFIELD is_exception : 1;
>
>
> It is not very clear what is the difference between is_interrupt
> and is_exception.  How about
>
>   /* If true, the current function is an interrupt service routine with
>      a pointer argument and an optional integer argument as specified by
>      the "interrupt" attribute.  */
>   BOOL_BITFIELD is_interrupt : 1;
>
>   /* If true, the current function is an interrupt service routine with
>      a pointer argument and an integer argument as specified by the
>      "interrupt" attribute.  */
>   BOOL_BITFIELD is_exception : 1;

Actually, both BOOL_BITFIELD flags should be rewritten as 2-bit
ENUM_BITFIELD using descriptive enum, e.g.

  ENUM_BITFIELD(function_type) func_type : 2;

with

TYPE_NORMAL = 0,
TYPE_INTERRUPT,
TYPE_EXCEPTION

This will simplify checking of function types, and make everything
more readable and maintainable.

Uros.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-10-01 15:59                         ` Uros Bizjak
@ 2015-10-01 16:08                           ` H.J. Lu
  2015-10-01 16:18                             ` Uros Bizjak
  0 siblings, 1 reply; 45+ messages in thread
From: H.J. Lu @ 2015-10-01 16:08 UTC (permalink / raw)
  To: Uros Bizjak; +Cc: Yulia Koval, Mike Stump, GCC Patches

On Thu, Oct 1, 2015 at 8:59 AM, Uros Bizjak <ubizjak@gmail.com> wrote:
> On Thu, Oct 1, 2015 at 2:24 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
>> On Wed, Sep 30, 2015 at 12:53 PM, Yulia Koval <vaalfreja@gmail.com> wrote:
>>> Done.
>>>
>>
>> +  /* If true, the current function is an interrupt service
>> +     routine as specified by the "interrupt" attribute.  */
>> +  BOOL_BITFIELD is_interrupt : 1;
>> +
>> +  /* If true, the current function is an exception service
>> +     routine as specified by the "interrupt" attribute.  */
>> +  BOOL_BITFIELD is_exception : 1;
>>
>>
>> It is not very clear what is the difference between is_interrupt
>> and is_exception.  How about
>>
>>   /* If true, the current function is an interrupt service routine with
>>      a pointer argument and an optional integer argument as specified by
>>      the "interrupt" attribute.  */
>>   BOOL_BITFIELD is_interrupt : 1;
>>
>>   /* If true, the current function is an interrupt service routine with
>>      a pointer argument and an integer argument as specified by the
>>      "interrupt" attribute.  */
>>   BOOL_BITFIELD is_exception : 1;
>
> Actually, both BOOL_BITFIELD flags should be rewritten as 2-bit
> ENUM_BITFIELD using descriptive enum, e.g.
>
>   ENUM_BITFIELD(function_type) func_type : 2;
>
> with
>
> TYPE_NORMAL = 0,
> TYPE_INTERRUPT,
> TYPE_EXCEPTION
>
> This will simplify checking of function types, and make everything
> more readable and maintainable.
>

Since an exception handler is a subset of interrupt handlers,
we need to check 2 bits separately.  Make the field 2 bits
doesn't make code more maintainable.

-- 
H.J.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-10-01 16:08                           ` H.J. Lu
@ 2015-10-01 16:18                             ` Uros Bizjak
  2015-10-02 12:51                               ` Yulia Koval
  0 siblings, 1 reply; 45+ messages in thread
From: Uros Bizjak @ 2015-10-01 16:18 UTC (permalink / raw)
  To: H.J. Lu; +Cc: Yulia Koval, Mike Stump, GCC Patches

On Thu, Oct 1, 2015 at 6:08 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Thu, Oct 1, 2015 at 8:59 AM, Uros Bizjak <ubizjak@gmail.com> wrote:
>> On Thu, Oct 1, 2015 at 2:24 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>> On Wed, Sep 30, 2015 at 12:53 PM, Yulia Koval <vaalfreja@gmail.com> wrote:
>>>> Done.
>>>>
>>>
>>> +  /* If true, the current function is an interrupt service
>>> +     routine as specified by the "interrupt" attribute.  */
>>> +  BOOL_BITFIELD is_interrupt : 1;
>>> +
>>> +  /* If true, the current function is an exception service
>>> +     routine as specified by the "interrupt" attribute.  */
>>> +  BOOL_BITFIELD is_exception : 1;
>>>
>>>
>>> It is not very clear what is the difference between is_interrupt
>>> and is_exception.  How about
>>>
>>>   /* If true, the current function is an interrupt service routine with
>>>      a pointer argument and an optional integer argument as specified by
>>>      the "interrupt" attribute.  */
>>>   BOOL_BITFIELD is_interrupt : 1;
>>>
>>>   /* If true, the current function is an interrupt service routine with
>>>      a pointer argument and an integer argument as specified by the
>>>      "interrupt" attribute.  */
>>>   BOOL_BITFIELD is_exception : 1;
>>
>> Actually, both BOOL_BITFIELD flags should be rewritten as 2-bit
>> ENUM_BITFIELD using descriptive enum, e.g.
>>
>>   ENUM_BITFIELD(function_type) func_type : 2;
>>
>> with
>>
>> TYPE_NORMAL = 0,
>> TYPE_INTERRUPT,
>> TYPE_EXCEPTION
>>
>> This will simplify checking of function types, and make everything
>> more readable and maintainable.
>>
>
> Since an exception handler is a subset of interrupt handlers,
> we need to check 2 bits separately.  Make the field 2 bits
> doesn't make code more maintainable.

For example, the following code:

+  if (!cum->caller && cfun->machine->is_interrupt)
+    {
+      /* The first argument of interrupt handler is a pointer and
+ points to the return address on stack.  The optional second
+ argument is an integer for error code on stack.  */
+      gcc_assert (type != NULL_TREE);
+      if (POINTER_TYPE_P (type))
+ {
+  if (cfun->machine->is_exception)

would become:

if (!cum->caller && cfun->machine->func_type != TYPE_NORMAL)
  {
    [...]
    if (cfun->machine->func_type == TYPE_EXCEPTION)
        [...]

It is kind of unintuitive if the function is an interrupt and an
exception at the same time, as is implied in the original code. In
proposed improvement, it is clear that it is not normal, and is later
refined to an exception.

Uros.

> --
> H.J.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-10-01 16:18                             ` Uros Bizjak
@ 2015-10-02 12:51                               ` Yulia Koval
  2015-10-02 15:45                                 ` Uros Bizjak
  0 siblings, 1 reply; 45+ messages in thread
From: Yulia Koval @ 2015-10-02 12:51 UTC (permalink / raw)
  To: Uros Bizjak; +Cc: H.J. Lu, Mike Stump, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 10460 bytes --]

Hi,
Here is a new patch. Added HJ's changes and review changes.

Implement x86 interrupt attribute

The interrupt and exception handlers are called by x86 processors.  X86
hardware pushes information onto stack and calls the handler.  The
requirements are

1. Both interrupt and exception handlers must use the 'IRET' instruction,
instead of the 'RET' instruction, to return from the handlers.
2. All registers are callee-saved in interrupt and exception handlers.
3. The difference between interrupt and exception handlers is the
exception handler must pop 'ERROR_CODE' off the stack before the 'IRET'
instruction.

The design goals of interrupt and exception handlers for x86 processors
are:

1. Support both 32-bit and 64-bit modes.
2. Flexible for compilers to optimize.
3. Easy to use by programmers.

To implement interrupt and exception handlers for x86 processors, a
compiler should support:

'interrupt' attribute

Use this attribute to indicate that the specified function with
mandatory arguments is an interrupt or exception handler.  The compiler
generates function entry and exit sequences suitable for use in an
interrupt handler when this attribute is present.  The 'IRET' instruction,
instead of the 'RET' instruction, is used to return from interrupt or
exception handlers.  All registers, except for the EFLAGS register which
is restored by the 'IRET' instruction, are preserved by the compiler.

Any interruptible-without-stack-switch code must be compiled with
-mno-red-zone since interrupt handlers can and will, because of the
hardware design, touch the red zone.

1. interrupt handler must be declared with a mandatory pointer argument:

struct interrupt_frame;

__attribute__ ((interrupt))
void
f (struct interrupt_frame *frame)
{
...
}

and user must properly define the structure the pointer pointing to.

2. exception handler:

The exception handler is very similar to the interrupt handler with
a different mandatory function signature:

typedef unsigned long long int uword_t;
typedef unsigned int uword_t;

struct interrupt_frame;

__attribute__ ((interrupt))
void
f (struct interrupt_frame *frame, uword_t error_code)
{
...
}

and compiler pops the error code off stack before the 'IRET' instruction.

The exception handler should only be used for exceptions which push an
error code and all other exceptions must use the interrupt handler.
The system will crash if the wrong handler is used.

To be feature complete, compiler may implement the optional
'no_caller_saved_registers' attribute:

Use this attribute to indicate that the specified function has no
caller-saved registers.  That is, all registers are callee-saved.
The compiler generates proper function entry and exit sequences to
save and restore any modified registers.

The user can call functions specified with 'no_caller_saved_registers'
attribute from an interrupt handler without saving and restoring all
call clobbered registers.

PR target/66960
PR target/67630
PR target/67634
* config/i386/i386-protos.h (ix86_epilogue_uses): New prototype.
* config/i386/i386.c (ix86_frame): Add nbndregs and nmaskregs.
(ix86_conditional_register_usage): Preserve all registers if
there are no caller-saved registers.
(ix86_set_current_function): Set no_caller_saved_registers and
func_type.  Mark arguments in interrupt handler as used.
(ix86_function_ok_for_sibcall): Return false if there are no
caller-saved registers.
(ix86_maybe_switch_abi): Call reinit_regs if AX register usage
isn't consistent.
(type_natural_mode): Don't warn ABI change for MMX in interrupt
handler.
(ix86_function_arg_advance): Skip for callee in interrupt
handler.
(ix86_function_arg): Handle arguments for callee in interrupt
handler.
(ix86_can_use_return_insn_p): Don't use `ret' instruction in
interrupt handler.
(ix86_hard_regno_scratch_ok): New function.
(ix86_epilogue_uses): Likewise.
(ix86_reg_ever_defined_p): Likewise.
(ix86_nsaved_bndregs): Likewise.
(ix86_nsaved_maskregs): Likewise.
(ix86_reg_save_area_size): Likewise.
(ix86_handle_no_caller_saved_registers_attribute): Likewise.
(ix86_handle_interrupt_attribute): Likewise.
(ix86_save_reg): Preserve all registers if there are no
caller-saved registers after reload.
(ix86_nsaved_sseregs): Don't return 0 if there are no
caller-saved registers.
(ix86_compute_frame_layout): Set nbndregs and nmaskregs.  Set
save_regs_using_mov to true to save bound and mask registers.
Call ix86_reg_save_area_size to get register save area size.
Allocate space to save full vector registers if there are
no caller-saved registers.
(ix86_emit_save_reg_using_mov): Set alignment to word_mode
alignment when saving full vector registers if there are no
caller-saved registers.
(ix86_emit_save_regs_using_mov): Use regno_reg_rtx to get
register size.
(ix86_emit_restore_regs_using_mov): Likewise.
(ix86_emit_save_sse_regs_using_mov): Save full vector registers
if there are no caller-saved registers.
(ix86_emit_restore_sse_regs_using_mov): Restore full vector
registers if there are no caller-saved registers.
(ix86_expand_epilogue): Use move to restore bound registers.
Generate interrupt return for interrupt handler and pop the
'ERROR_CODE' off the stack before interrupt return in exception
handler.
(ix86_expand_move): Disallow 80387 instructions in exception
handler.
(ix86_expand_vector_move): Disallow MMX/3Dnow instructions in
exception handler.
(ix86_expand_call): Disallow calling interrupt handler directly.
If there are no caller-saved registers, mark all registers that
are clobbered by the call as clobbered.
(ix86_attribute_table): Add interrupt and no_caller_saved_registers
attributes.
(TARGET_HARD_REGNO_SCRATCH_OK): New.
* config/i386/i386.h (EPILOGUE_USES): New.
(function_type): New enum.
* config/i386/i386.h (machine_function): Add func_type and
no_caller_saved_registers.
* config/i386/i386.md (UNSPEC_INTERRUPT_RETURN): New.
(interrupt_return): New pattern.
* config/i386/sse.md (*mov<mode>_internal): Handle misaligned
SSE load and store if there are no caller-saved registers.
* doc/extend.texi: Document x86 interrupt and
no_caller_saved_registers attributes.

gcc/testsuite/

PR target/66960
PR target/67630
PR target/67634
* gcc.target/i386/interrupt-1.c: New test.
* gcc.target/i386/interrupt-2.c: Likewise.
* gcc.target/i386/interrupt-3.c: Likewise.
* gcc.target/i386/interrupt-4.c: Likewise.
* gcc.target/i386/interrupt-5.c: Likewise.
* gcc.target/i386/interrupt-6.c: Likewise.
* gcc.target/i386/interrupt-7.c: Likewise.
* gcc.target/i386/interrupt-8.c: Likewise.
* gcc.target/i386/interrupt-9.c: Likewise.
* gcc.target/i386/interrupt-10.c: Likewise.
* gcc.target/i386/interrupt-11.c: Likewise.
* gcc.target/i386/interrupt-12.c: Likewise.
* gcc.target/i386/interrupt-13.c: Likewise.
* gcc.target/i386/interrupt-14.c: Likewise.
* gcc.target/i386/interrupt-15.c: Likewise.
* gcc.target/i386/interrupt-16.c: Likewise.
* gcc.target/i386/interrupt-17.c: Likewise.
* gcc.target/i386/interrupt-18.c: Likewise.
* gcc.target/i386/interrupt-19.c: Likewise.
* gcc.target/i386/interrupt-20.c: Likewise.
* gcc.target/i386/interrupt-21.c: Likewise.
* gcc.target/i386/interrupt-22.c: Likewise.
* gcc.target/i386/interrupt-23.c: Likewise.
* gcc.target/i386/interrupt-24.c: Likewise.
* gcc.target/i386/interrupt-387-err.c: Likewise.
* gcc.target/i386/interrupt-bnd.c: Likewise.
* gcc.target/i386/interrupt-iamcu.c: Likewise.
* gcc.target/i386/interrupt-mmx-err.c: Likewise.
* gcc.target/i386/interrupt-redzone-1.c: Likewise.
* gcc.target/i386/interrupt-redzone-2.c: Likewise.
* gcc.target/i386/interrupt-sibcall.c: Likewise.
* gcc.target/i386/interrupt-switch-abi.c: Likewise.
* gcc.target/i386/interrupt-xmm.c: Likewise.
* gcc.target/i386/interrupt-ymm.c: Likewise.
* gcc.target/i386/interrupt-zmm.c: Likewise.

Julia


On Thu, Oct 1, 2015 at 7:18 PM, Uros Bizjak <ubizjak@gmail.com> wrote:
> On Thu, Oct 1, 2015 at 6:08 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>> On Thu, Oct 1, 2015 at 8:59 AM, Uros Bizjak <ubizjak@gmail.com> wrote:
>>> On Thu, Oct 1, 2015 at 2:24 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>>> On Wed, Sep 30, 2015 at 12:53 PM, Yulia Koval <vaalfreja@gmail.com> wrote:
>>>>> Done.
>>>>>
>>>>
>>>> +  /* If true, the current function is an interrupt service
>>>> +     routine as specified by the "interrupt" attribute.  */
>>>> +  BOOL_BITFIELD is_interrupt : 1;
>>>> +
>>>> +  /* If true, the current function is an exception service
>>>> +     routine as specified by the "interrupt" attribute.  */
>>>> +  BOOL_BITFIELD is_exception : 1;
>>>>
>>>>
>>>> It is not very clear what is the difference between is_interrupt
>>>> and is_exception.  How about
>>>>
>>>>   /* If true, the current function is an interrupt service routine with
>>>>      a pointer argument and an optional integer argument as specified by
>>>>      the "interrupt" attribute.  */
>>>>   BOOL_BITFIELD is_interrupt : 1;
>>>>
>>>>   /* If true, the current function is an interrupt service routine with
>>>>      a pointer argument and an integer argument as specified by the
>>>>      "interrupt" attribute.  */
>>>>   BOOL_BITFIELD is_exception : 1;
>>>
>>> Actually, both BOOL_BITFIELD flags should be rewritten as 2-bit
>>> ENUM_BITFIELD using descriptive enum, e.g.
>>>
>>>   ENUM_BITFIELD(function_type) func_type : 2;
>>>
>>> with
>>>
>>> TYPE_NORMAL = 0,
>>> TYPE_INTERRUPT,
>>> TYPE_EXCEPTION
>>>
>>> This will simplify checking of function types, and make everything
>>> more readable and maintainable.
>>>
>>
>> Since an exception handler is a subset of interrupt handlers,
>> we need to check 2 bits separately.  Make the field 2 bits
>> doesn't make code more maintainable.
>
> For example, the following code:
>
> +  if (!cum->caller && cfun->machine->is_interrupt)
> +    {
> +      /* The first argument of interrupt handler is a pointer and
> + points to the return address on stack.  The optional second
> + argument is an integer for error code on stack.  */
> +      gcc_assert (type != NULL_TREE);
> +      if (POINTER_TYPE_P (type))
> + {
> +  if (cfun->machine->is_exception)
>
> would become:
>
> if (!cum->caller && cfun->machine->func_type != TYPE_NORMAL)
>   {
>     [...]
>     if (cfun->machine->func_type == TYPE_EXCEPTION)
>         [...]
>
> It is kind of unintuitive if the function is an interrupt and an
> exception at the same time, as is implied in the original code. In
> proposed improvement, it is clear that it is not normal, and is later
> refined to an exception.
>
> Uros.
>
>> --
>> H.J.

[-- Attachment #2: patch --]
[-- Type: application/octet-stream, Size: 73931 bytes --]

diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index 6a17ef4..c7ff393 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -278,6 +278,8 @@ extern rtx maybe_get_pool_constant (rtx);
 extern char internal_label_prefix[16];
 extern int internal_label_prefix_len;
 
+extern bool ix86_epilogue_uses (int);
+
 enum ix86_address_seg { SEG_DEFAULT, SEG_FS, SEG_GS };
 struct ix86_address
 {
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index cfeba76..458d0b8 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -2381,6 +2381,8 @@ struct ix86_frame
 {
   int nsseregs;
   int nregs;
+  int nbndregs;
+  int nmaskregs;
   int va_arg_size;
   int red_zone_size;
   int outgoing_arguments_size;
@@ -5464,6 +5466,13 @@ ix86_conditional_register_usage (void)
 {
   int i, c_mask;
 
+  /* If there are no caller-saved registers, preserve all registers.
+     except fixed_regs.  */
+  if (cfun && cfun->machine->no_caller_saved_registers)
+    for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+      if (!fixed_regs[i])
+	call_used_regs[i] = 0;
+
   /* For 32-bit targets, squash the REX registers.  */
   if (! TARGET_64BIT)
     {
@@ -6245,6 +6254,27 @@ ix86_set_current_function (tree fndecl)
       return;
     }
 
+  if (lookup_attribute ("interrupt", DECL_ATTRIBUTES (fndecl)))
+    {
+      int nargs = 0;
+      for (tree arg = DECL_ARGUMENTS (fndecl);
+	   arg;
+	   arg = TREE_CHAIN (arg))
+	{
+	  /* Mark arguments in interrupt handler as used to silence
+	     compiler warning since arguments in interrupt handler
+	     are mandatory even if they aren't used.  */
+	  TREE_USED (arg) = 1;
+	  nargs++;
+	}
+      cfun->machine->no_caller_saved_registers = true;
+      cfun->machine->func_type
+	= nargs == 2 ? TYPE_EXCEPTION : TYPE_INTERRUPT;
+    }
+  else if (lookup_attribute ("no_caller_saved_registers",
+			     DECL_ATTRIBUTES (fndecl)))
+    cfun->machine->no_caller_saved_registers = true;
+
   tree new_tree = DECL_FUNCTION_SPECIFIC_TARGET (fndecl);
   if (new_tree == NULL_TREE)
     new_tree = target_option_default_node;
@@ -6523,6 +6553,10 @@ ix86_function_ok_for_sibcall (tree decl, tree exp)
   tree type, decl_or_type;
   rtx a, b;
 
+  /* Sibling call isn't OK if there are no caller-saved registers.  */
+  if (cfun->machine->no_caller_saved_registers)
+    return false;
+
   /* If we are generating position-independent code, we cannot sibcall
      optimize direct calls to global functions, as the PLT requires
      %ebx be live. (Darwin does not have a PLT.)  */
@@ -7401,8 +7435,17 @@ ix86_call_abi_override (const_tree fndecl)
 static void
 ix86_maybe_switch_abi (void)
 {
-  if (TARGET_64BIT &&
-      call_used_regs[SI_REG] == (cfun->machine->call_abi == MS_ABI))
+  if (TARGET_64BIT
+	&& (call_used_regs[SI_REG]
+	  == (cfun->machine->call_abi == MS_ABI)))
+    {
+      reinit_regs ();
+      return;
+    }
+  /* AX register is only preserved if there are no caller-saved
+     registers.  */
+  if (cfun->machine->no_caller_saved_registers
+       != call_used_regs[AX_REG])
     reinit_regs ();
 }
 
@@ -7707,6 +7750,8 @@ type_natural_mode (const_tree type, const CUMULATIVE_ARGS *cum,
 		      }
 		  }
 		else if ((size == 8 && !TARGET_64BIT)
+			 && (!cfun
+			     || cfun->machine->func_type == TYPE_NORMAL)
 			 && !TARGET_MMX
 			 && !TARGET_IAMCU)
 		  {
@@ -8686,6 +8731,11 @@ ix86_function_arg_advance (cumulative_args_t cum_v, machine_mode mode,
   HOST_WIDE_INT bytes, words;
   int nregs;
 
+  /* The argument of interrupt handler is a special case and is
+     handled in ix86_function_arg.  */
+  if (!cum->caller && cfun->machine->func_type != TYPE_NORMAL)
+    return;
+
   if (mode == BLKmode)
     bytes = int_size_in_bytes (type);
   else
@@ -9002,6 +9052,39 @@ ix86_function_arg (cumulative_args_t cum_v, machine_mode omode,
   HOST_WIDE_INT bytes, words;
   rtx arg;
 
+  if (!cum->caller && cfun->machine->func_type != TYPE_NORMAL)
+    {
+      /* The first argument of interrupt handler is a pointer and
+	 points to the return address on stack.  The optional second
+	 argument is an integer for error code on stack.  */
+      gcc_assert (type != NULL_TREE);
+      if (POINTER_TYPE_P (type))
+	{
+	  if (cfun->machine->func_type == TYPE_EXCEPTION)
+	    /* (AP) in the current frame in exception handler.  */
+	    arg = arg_pointer_rtx;
+	  else
+	    /* -WORD(AP) in the current frame in interrupt handler.  */
+	    arg = plus_constant (Pmode, arg_pointer_rtx,
+				 -UNITS_PER_WORD);
+	  if (mode != Pmode)
+	    arg = convert_to_mode (mode, arg, 1);
+	}
+      else
+	{
+	  gcc_assert (TREE_CODE (type) == INTEGER_TYPE
+		      && cfun->machine->func_type == TYPE_EXCEPTION
+		      && mode == word_mode);
+	  /* The error code is at -WORD(AP) in the current frame in
+	     exception handler.  */
+	  arg = gen_rtx_MEM (word_mode,
+			     plus_constant (Pmode, arg_pointer_rtx,
+					    -UNITS_PER_WORD));
+	}
+
+      return arg;
+    }
+
   /* All pointer bounds arguments are handled separately here.  */
   if ((type && POINTER_BOUNDS_TYPE_P (type))
       || POINTER_BOUNDS_MODE_P (mode))
@@ -10717,7 +10800,10 @@ ix86_can_use_return_insn_p (void)
 {
   struct ix86_frame frame;
 
-  if (! reload_completed || frame_pointer_needed)
+  /* Don't use `ret' instruction in interrupt handler.  */
+  if (! reload_completed
+      || frame_pointer_needed
+      || cfun->machine->func_type != TYPE_NORMAL)
     return 0;
 
   /* Don't allow more than 32k pop, since that's all we can do
@@ -11027,11 +11113,66 @@ ix86_select_alt_pic_regnum (void)
   return INVALID_REGNUM;
 }
 
+/* Return nonzero if register REGNO can be used as a scratch register
+   in peephole2.  */
+
+bool
+ix86_hard_regno_scratch_ok (unsigned int regno ATTRIBUTE_UNUSED)
+{
+  /* No scratch registers if there are no caller-saved registers.  */
+  return !cfun->machine->no_caller_saved_registers;
+}
+
+/* Return true if REGNO is used by the epilogue.  */
+
+bool
+ix86_epilogue_uses (int regno)
+{
+  /* If there are no caller-saved registers, we preserve all registers,
+     except for MMX and x87 registers which aren't supported when saving
+     and restoring registers.  Don't explicitly save SP register since
+     it is always preserved.  */
+  return (cfun->machine->no_caller_saved_registers
+	  && reg_names[regno][0]
+	  && !fixed_regs[regno]
+	  && !STACK_REGNO_P (regno)
+	  && !MMX_REGNO_P (regno));
+}
+
+/* Return TRUE if register REGNO is ever defined..  */
+
+static bool
+ix86_reg_ever_defined_p (unsigned int regno)
+{
+  df_ref def;
+
+  for (def = DF_REG_DEF_CHAIN (regno); def; def = DF_REF_NEXT_REG (def))
+    if (!DF_REF_IS_ARTIFICIAL (def))
+      return true;
+
+  return false;
+}
+
 /* Return TRUE if we need to save REGNO.  */
 
 static bool
 ix86_save_reg (unsigned int regno, bool maybe_eh_return)
 {
+  /* If there are no caller-saved registers, we preserve all registers,
+     except for MMX and x87 registers which aren't supported when saving
+     and restoring registers.  Don't explicitly save SP register since
+     it is always preserved.  */
+  if (cfun->machine->no_caller_saved_registers && reload_completed)
+    return (df_regs_ever_live_p (regno)
+	    && ix86_reg_ever_defined_p (regno)
+	    && !fixed_regs[regno]
+	    && (!crtl->return_rtx
+		|| !refers_to_regno_p (regno, crtl->return_rtx))
+	    && (!crtl->return_bnd
+		|| !refers_to_regno_p (regno, crtl->return_bnd))
+	    && !STACK_REGNO_P (regno)
+	    && !MMX_REGNO_P (regno));
+
   if (regno == REAL_PIC_OFFSET_TABLE_REGNUM
       && pic_offset_table_rtx)
     {
@@ -11088,6 +11229,34 @@ ix86_nsaved_regs (void)
   return nregs;
 }
 
+/* Return number of saved bound registers.  */
+
+static int
+ix86_nsaved_bndregs (void)
+{
+  int nregs = 0;
+  int regno;
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (BND_REGNO_P (regno) && ix86_save_reg (regno, true))
+      nregs ++;
+  return nregs;
+}
+
+/* Return number of saved mask registers.  */
+
+static int
+ix86_nsaved_maskregs (void)
+{
+  int nregs = 0;
+  int regno;
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (MASK_REGNO_P (regno) && ix86_save_reg (regno, true))
+      nregs ++;
+  return nregs;
+}
+
 /* Return number of saved SSE registers.  */
 
 static int
@@ -11096,7 +11265,10 @@ ix86_nsaved_sseregs (void)
   int nregs = 0;
   int regno;
 
-  if (!TARGET_64BIT_MS_ABI)
+  /* Always need to save SSE registers if there are no caller-saved
+     registers.  */
+  if (!TARGET_64BIT_MS_ABI
+      && !cfun->machine->no_caller_saved_registers)
     return 0;
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
@@ -11165,6 +11337,18 @@ ix86_builtin_setjmp_frame_value (void)
 
 #define SPLIT_STACK_AVAILABLE 256
 
+/* Register save area size.  */
+
+static unsigned int
+ix86_reg_save_area_size (struct ix86_frame *frame)
+{
+  unsigned int size = ((frame->nregs - frame->nbndregs
+			- frame->nmaskregs) * UNITS_PER_WORD);
+  size += frame->nbndregs * (ix86_pmode == PMODE_DI ? 16 : 8);
+  size += frame->nmaskregs * (TARGET_AVX512BW ? 8 : 2);
+  return size;
+}
+
 /* Fill structure ix86_frame about frame of currently computed function.  */
 
 static void
@@ -11177,6 +11361,8 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   HOST_WIDE_INT to_allocate;
 
   frame->nregs = ix86_nsaved_regs ();
+  frame->nbndregs = ix86_nsaved_bndregs ();
+  frame->nmaskregs = ix86_nsaved_maskregs ();
   frame->nsseregs = ix86_nsaved_sseregs ();
 
   /* 64-bit MS ABI seem to require stack alignment to be always 16 except for
@@ -11248,11 +11434,16 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
 	   = !expensive_function_p (count);
     }
 
+  /* We must use move to save bound and mask registers.  */
   frame->save_regs_using_mov
-    = (TARGET_PROLOGUE_USING_MOVE && cfun->machine->use_fast_prologue_epilogue
-       /* If static stack checking is enabled and done with probes,
-	  the registers need to be saved before allocating the frame.  */
-       && flag_stack_check != STATIC_BUILTIN_STACK_CHECK);
+    = (frame->nbndregs > 0
+       || frame->nmaskregs > 0
+       || (TARGET_PROLOGUE_USING_MOVE
+	   && cfun->machine->use_fast_prologue_epilogue
+	   /* If static stack checking is enabled and done with probes,
+	      the registers need to be saved before allocating the
+	      frame.  */
+	   && flag_stack_check != STATIC_BUILTIN_STACK_CHECK));
 
   /* Skip return address.  */
   offset = UNITS_PER_WORD;
@@ -11270,7 +11461,7 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   frame->hard_frame_pointer_offset = offset;
 
   /* Register save area */
-  offset += frame->nregs * UNITS_PER_WORD;
+  offset += ix86_reg_save_area_size (frame);
   frame->reg_save_offset = offset;
 
   /* On SEH target, registers are pushed just before the frame pointer
@@ -11284,9 +11475,18 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
       /* The only ABI that has saved SSE registers (Win64) also has a
          16-byte aligned default stack, and thus we don't need to be
 	 within the re-aligned local stack frame to save them.  */
-      gcc_assert (INCOMING_STACK_BOUNDARY >= 128);
-      offset = (offset + 16 - 1) & -16;
-      offset += frame->nsseregs * 16;
+      if (cfun->machine->no_caller_saved_registers)
+	/* We must save full vector registers if there are no
+	   caller-saved registers.  */
+	offset += frame->nsseregs * (TARGET_AVX512F
+				     ? 64
+				     : (TARGET_AVX ? 32 : 16));
+      else
+	{
+	  gcc_assert (INCOMING_STACK_BOUNDARY >= 128);
+	  offset = (offset + 16 - 1) & -16;
+	  offset += frame->nsseregs * 16;
+	}
     }
   frame->sse_reg_save_offset = offset;
 
@@ -11342,8 +11542,12 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   /* Size prologue needs to allocate.  */
   to_allocate = offset - frame->sse_reg_save_offset;
 
-  if ((!to_allocate && frame->nregs <= 1)
-      || (TARGET_64BIT && to_allocate >= (HOST_WIDE_INT) 0x80000000))
+  /* We must use move to save bound and mask registers.  */
+  if (frame->nbndregs == 0
+      && frame->nmaskregs == 0
+      && ((!to_allocate && frame->nregs <= 1)
+	   || (TARGET_64BIT
+	       && to_allocate >= (HOST_WIDE_INT) 0x80000000)))
     frame->save_regs_using_mov = false;
 
   if (ix86_using_red_zone ()
@@ -11353,7 +11557,7 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
     {
       frame->red_zone_size = to_allocate;
       if (frame->save_regs_using_mov)
-	frame->red_zone_size += frame->nregs * UNITS_PER_WORD;
+	frame->red_zone_size += ix86_reg_save_area_size (frame);
       if (frame->red_zone_size > RED_ZONE_SIZE - RED_ZONE_RESERVE)
 	frame->red_zone_size = RED_ZONE_SIZE - RED_ZONE_RESERVE;
     }
@@ -11513,8 +11717,22 @@ ix86_emit_save_reg_using_mov (machine_mode mode, unsigned int regno,
   addr = choose_baseaddr (cfa_offset);
   mem = gen_frame_mem (mode, addr);
 
-  /* For SSE saves, we need to indicate the 128-bit alignment.  */
-  set_mem_align (mem, GET_MODE_ALIGNMENT (mode));
+  /* For SSE saves, we need to indicate the 128-bit alignment.  In
+     interrupt handler, stack is aligned to word_mode.  We can't use
+     gen_sse_storeups since RTX_FRAME_RELATED_P is set and
+     dwarf2out_flush_queued_reg_saves doesn't like UNSPEC_STOREU.
+     Also gen_sse_storeups doesn't cover AVX nor AVX512.  */
+  unsigned int incoming_stack_boundary;
+  if (cfun->machine->func_type == TYPE_NORMAL)
+    {
+      incoming_stack_boundary
+	= (crtl->parm_stack_boundary > ix86_incoming_stack_boundary
+	   ? crtl->parm_stack_boundary : ix86_incoming_stack_boundary);
+      gcc_assert (incoming_stack_boundary >= 128);
+    }
+  else
+    incoming_stack_boundary = GET_MODE_ALIGNMENT (word_mode);
+  set_mem_align (mem, incoming_stack_boundary);
 
   insn = emit_move_insn (mem, reg);
   RTX_FRAME_RELATED_P (insn) = 1;
@@ -11575,8 +11793,9 @@ ix86_emit_save_regs_using_mov (HOST_WIDE_INT cfa_offset)
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (!SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
       {
-        ix86_emit_save_reg_using_mov (word_mode, regno, cfa_offset);
-	cfa_offset -= UNITS_PER_WORD;
+	enum machine_mode reg_mode = GET_MODE (regno_reg_rtx[regno]);
+	ix86_emit_save_reg_using_mov (reg_mode, regno, cfa_offset);
+	cfa_offset -= GET_MODE_SIZE (reg_mode);
       }
 }
 
@@ -11586,12 +11805,27 @@ static void
 ix86_emit_save_sse_regs_using_mov (HOST_WIDE_INT cfa_offset)
 {
   unsigned int regno;
+  enum machine_mode vector_reg_mode;
+
+  if (cfun->machine->no_caller_saved_registers)
+    {
+      /* We must save full vector registers if there are no
+	 caller-saved registers.  */
+      if (TARGET_AVX512F)
+	vector_reg_mode = V16SFmode;
+      else if (TARGET_AVX)
+	vector_reg_mode = V8SFmode;
+      else
+	vector_reg_mode = V4SFmode;
+    }
+  else
+    vector_reg_mode = V4SFmode;
 
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
       {
-	ix86_emit_save_reg_using_mov (V4SFmode, regno, cfa_offset);
-	cfa_offset -= 16;
+	ix86_emit_save_reg_using_mov (vector_reg_mode, regno, cfa_offset);
+	cfa_offset -= GET_MODE_SIZE (vector_reg_mode);
       }
 }
 
@@ -13036,12 +13270,13 @@ ix86_emit_restore_regs_using_mov (HOST_WIDE_INT cfa_offset,
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (!SSE_REGNO_P (regno) && ix86_save_reg (regno, maybe_eh_return))
       {
-	rtx reg = gen_rtx_REG (word_mode, regno);
+	enum machine_mode reg_mode = GET_MODE (regno_reg_rtx[regno]);
+	rtx reg = gen_rtx_REG (reg_mode, regno);
 	rtx mem;
 	rtx_insn *insn;
 
 	mem = choose_baseaddr (cfa_offset);
-	mem = gen_frame_mem (word_mode, mem);
+	mem = gen_frame_mem (reg_mode, mem);
 	insn = emit_move_insn (reg, mem);
 
         if (m->fs.cfa_reg == crtl->drap_reg && regno == REGNO (crtl->drap_reg))
@@ -13060,7 +13295,7 @@ ix86_emit_restore_regs_using_mov (HOST_WIDE_INT cfa_offset,
 	else
 	  ix86_add_cfa_restore_note (NULL, reg, cfa_offset);
 
-	cfa_offset -= UNITS_PER_WORD;
+	cfa_offset -= GET_MODE_SIZE (reg_mode);
       }
 }
 
@@ -13071,21 +13306,48 @@ ix86_emit_restore_sse_regs_using_mov (HOST_WIDE_INT cfa_offset,
 				      bool maybe_eh_return)
 {
   unsigned int regno;
+  enum machine_mode vector_reg_mode;
+
+  if (cfun->machine->no_caller_saved_registers)
+    {
+      /* We must restore full vector registers if there are no
+	 caller-saved registers.  */
+      if (TARGET_AVX512F)
+	vector_reg_mode = V16SFmode;
+      else if (TARGET_AVX)
+	vector_reg_mode = V8SFmode;
+      else
+	vector_reg_mode = V4SFmode;
+    }
+  else
+    vector_reg_mode = V4SFmode;
+
+  /* In interrupt handler, stack is aligned to word_mode.  */
+  unsigned int incoming_stack_boundary;
+  if (cfun->machine->func_type == TYPE_NORMAL)
+    {
+      incoming_stack_boundary
+	= (crtl->parm_stack_boundary > ix86_incoming_stack_boundary
+	   ? crtl->parm_stack_boundary : ix86_incoming_stack_boundary);
+      gcc_assert (incoming_stack_boundary >= 128);
+    }
+  else
+    incoming_stack_boundary = GET_MODE_ALIGNMENT (word_mode);
 
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, maybe_eh_return))
       {
-	rtx reg = gen_rtx_REG (V4SFmode, regno);
+	rtx reg = gen_rtx_REG (vector_reg_mode, regno);
 	rtx mem;
 
 	mem = choose_baseaddr (cfa_offset);
-	mem = gen_rtx_MEM (V4SFmode, mem);
-	set_mem_align (mem, 128);
+	mem = gen_rtx_MEM (vector_reg_mode, mem);
+	set_mem_align (mem, incoming_stack_boundary);
 	emit_move_insn (reg, mem);
 
 	ix86_add_cfa_restore_note (NULL, reg, cfa_offset);
 
-	cfa_offset -= 16;
+	cfa_offset -= GET_MODE_SIZE (vector_reg_mode);
       }
 }
 
@@ -13149,8 +13411,9 @@ ix86_expand_epilogue (int style)
   if (crtl->calls_eh_return && style != 2)
     frame.reg_save_offset -= 2 * UNITS_PER_WORD;
 
-  /* EH_RETURN requires the use of moves to function properly.  */
-  if (crtl->calls_eh_return)
+  /* EH_RETURN requires the use of moves to function properly.  We must
+     use move to restore bound and mask registers.  */
+  if (crtl->calls_eh_return || frame.nbndregs > 0 || frame.nmaskregs > 0)
     restore_regs_via_mov = true;
   /* SEH requires the use of pops to identify the epilogue.  */
   else if (TARGET_SEH)
@@ -13391,7 +13654,20 @@ ix86_expand_epilogue (int style)
       return;
     }
 
-  if (crtl->args.pops_args && crtl->args.size)
+  if (cfun->machine->func_type != TYPE_NORMAL)
+    {
+      /* Return with the "IRET" instruction from interrupt handler.
+	 Pop the 'ERROR_CODE' off the stack before the 'IRET'
+	 instruction in exception handler.  */
+      if (cfun->machine->func_type == TYPE_EXCEPTION)
+	{
+	  rtx r = plus_constant (Pmode, stack_pointer_rtx,
+				 UNITS_PER_WORD);
+	  emit_insn (gen_rtx_SET (stack_pointer_rtx, r));
+	}
+      emit_jump_insn (gen_interrupt_return ());
+    }
+  else if (crtl->args.pops_args && crtl->args.size)
     {
       rtx popc = GEN_INT (crtl->args.pops_args);
 
@@ -18364,6 +18640,14 @@ ix86_expand_move (machine_mode mode, rtx operands[])
   rtx op0, op1;
   enum tls_model model;
 
+  if (cfun->machine->func_type != TYPE_NORMAL && IS_STACK_MODE (mode))
+    {
+      error ("80387 instructions aren't allowed in %s service routine",
+	     (cfun->machine->func_type == TYPE_EXCEPTION
+	      ? "exception" : "interrupt"));
+      return;
+    }
+
   op0 = operands[0];
   op1 = operands[1];
 
@@ -18508,6 +18792,15 @@ ix86_expand_vector_move (machine_mode mode, rtx operands[])
   rtx op0 = operands[0], op1 = operands[1];
   unsigned int align = GET_MODE_ALIGNMENT (mode);
 
+  if (cfun->machine->func_type != TYPE_NORMAL
+      && VALID_MMX_REG_MODE (mode))
+    {
+      error ("MMX/3Dnow instructions aren't allowed in %s service routine",
+	     (cfun->machine->func_type == TYPE_EXCEPTION
+	      ? "exception" : "interrupt"));
+      return;
+    }
+
   if (push_operand (op0, VOIDmode))
     op0 = emit_move_resolve_push (mode, op0);
 
@@ -26593,6 +26886,17 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
   rtx vec[3];
   rtx use = NULL, call;
   unsigned int vec_len = 0;
+  tree fndecl;
+
+  if (GET_CODE (XEXP (fnaddr, 0)) == SYMBOL_REF)
+    {
+      fndecl = SYMBOL_REF_DECL (XEXP (fnaddr, 0));
+      if (fndecl
+	  && (lookup_attribute ("interrupt", DECL_ATTRIBUTES (fndecl))))
+	error ("interrupt service routine can't be called directly");
+    }
+  else
+    fndecl = NULL_TREE;
 
   if (pop == const0_rtx)
     pop = NULL;
@@ -26690,8 +26994,31 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
       vec[vec_len++] = pop;
     }
 
-  if (TARGET_64BIT_MS_ABI
-      && (!callarg2 || INTVAL (callarg2) != -2))
+  if (cfun->machine->no_caller_saved_registers
+      && (!fndecl
+	  || !lookup_attribute ("no_caller_saved_registers",
+				DECL_ATTRIBUTES (fndecl))))
+    {
+      static const char ix86_call_used_regs[] = CALL_USED_REGISTERS;
+      char c_mask = ((TARGET_64BIT
+		      && ix86_function_abi (fndecl) == MS_ABI)
+		     ? (1 << 3)
+		     : TARGET_64BIT ? (1 << 2) : (1 << 1));
+
+      /* If there are no caller-saved registers, add all registers
+	 that are clobbered by the call.  */
+      for (int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+	if (reg_names[i][0]
+	    && !fixed_regs[i]
+	    && (ix86_call_used_regs[i] == 1
+		|| (ix86_call_used_regs[i] & c_mask))
+	    && !STACK_REGNO_P (i)
+	    && !MMX_REGNO_P (i))
+	  clobber_reg (&use,
+		       gen_rtx_REG (GET_MODE (regno_reg_rtx[i]), i));
+    }
+  else if (TARGET_64BIT_MS_ABI
+	   && (!callarg2 || INTVAL (callarg2) != -2))
     {
       int const cregs_size
 	= ARRAY_SIZE (x86_64_ms_sysv_extra_clobbered_registers);
@@ -44072,6 +44399,69 @@ ix86_handle_fndecl_attribute (tree *node, tree name, tree, int,
   return NULL_TREE;
 }
 
+static tree
+ix86_handle_no_caller_saved_registers_attribute (tree *node,
+						 tree name, tree, int,
+						 bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+	       name);
+      *no_add_attrs = true;
+    }
+  return NULL_TREE;
+}
+
+static tree
+ix86_handle_interrupt_attribute (tree *node, tree name, tree, int,
+				 bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+	       name);
+      *no_add_attrs = true;
+    }
+
+  /* DECL_RESULT and DECL_ARGUMENTS do not exist there yet,
+     but the function type contains args and return type data.  */
+  tree func_type = TREE_TYPE (*node);
+  tree return_type = TREE_TYPE (func_type);
+
+  int nargs = 0;
+  tree current_arg_type = TYPE_ARG_TYPES (func_type);
+  while (current_arg_type
+	 && ! VOID_TYPE_P (TREE_VALUE (current_arg_type)))
+    {
+      if (nargs == 0)
+	{
+	  if (! POINTER_TYPE_P (TREE_VALUE (current_arg_type)))
+	    error ("interrupt service routine should have a pointer "
+		   "as the first argument");
+	}
+      else if (nargs == 1)
+	{
+	  if (TREE_CODE (TREE_VALUE (current_arg_type)) != INTEGER_TYPE
+	      || TYPE_MODE (TREE_VALUE (current_arg_type)) != word_mode)
+	    error ("interrupt service routine should have unsigned %s"
+		   "int as the second argument",
+		   TARGET_64BIT
+		   ? (TARGET_X32 ? "long long " : "long ")
+		   : "");
+	}
+      nargs++;
+      current_arg_type = TREE_CHAIN (current_arg_type);
+    }
+  if (!nargs || nargs > 2)
+    error ("interrupt service routine can only have a pointer argument "
+	   "and an optional integer argument");
+  if (! VOID_TYPE_P (return_type))
+    error ("interrupt service routine can't have non-void return value");
+
+  return NULL_TREE;
+}
+
 static bool
 ix86_ms_bitfield_layout_p (const_tree record_type)
 {
@@ -48073,6 +48463,11 @@ static const struct attribute_spec ix86_attribute_table[] =
     false },
   { "callee_pop_aggregate_return", 1, 1, false, true, true,
     ix86_handle_callee_pop_aggregate_return, true },
+  { "interrupt", 0, 0, true, false, false,
+    ix86_handle_interrupt_attribute, false },
+  { "no_caller_saved_registers", 0, 0, true, false, false,
+    ix86_handle_no_caller_saved_registers_attribute, false },
+
   /* End element.  */
   { NULL,        0, 0, false, false, false, NULL, false }
 };
@@ -53861,6 +54256,9 @@ ix86_operands_ok_for_move_multiple (rtx *operands, bool load,
 #undef TARGET_ABSOLUTE_BIGGEST_ALIGNMENT
 #define TARGET_ABSOLUTE_BIGGEST_ALIGNMENT 512
 
+#undef TARGET_HARD_REGNO_SCRATCH_OK
+#define TARGET_HARD_REGNO_SCRATCH_OK ix86_hard_regno_scratch_ok
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 #include "gt-i386.h"
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 96ba90f..b9d3fc7 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -1730,6 +1730,11 @@ typedef struct ix86_args {
 
 #define EXIT_IGNORE_STACK 1
 
+/* Define this macro as a C expression that is nonzero for registers
+   used by the epilogue or the `return' pattern.  */
+
+#define EPILOGUE_USES(REGNO) ix86_epilogue_uses (REGNO)
+
 /* Output assembler code for a block containing the constant parts
    of a trampoline, leaving space for the variable parts.  */
 
@@ -2444,6 +2449,18 @@ struct GTY(()) machine_frame_state
 /* Private to winnt.c.  */
 struct seh_frame_state;
 
+enum function_type
+{
+  TYPE_NORMAL = 0,
+  /* The current function is an interrupt service routine with a
+     pointer argument as specified by the "interrupt" attribute.  */
+  TYPE_INTERRUPT,
+  /* The current function is an interrupt service routine with a
+     pointer argument and an integer argument as specified by the
+     "interrupt" attribute.  */
+  TYPE_EXCEPTION
+};
+
 struct GTY(()) machine_function {
   struct stack_local_entry *stack_locals;
   const char *some_ld_name;
@@ -2494,6 +2511,13 @@ struct GTY(()) machine_function {
   /* If true, it is safe to not save/restore DRAP register.  */
   BOOL_BITFIELD no_drap_save_restore : 1;
 
+  /* Function type.  */
+  ENUM_BITFIELD(function_type) func_type : 2;
+
+  /* If true, the current function is a function specified with
+     the "interrupt" or "no_caller_saved_registers" attribute.  */
+  BOOL_BITFIELD no_caller_saved_registers : 1;
+
   /* If true, there is register available for argument passing.  This
      is used only in ix86_function_ok_for_sibcall by 32-bit to determine
      if there is scratch register available for indirect sibcall.  In
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 8c2ed60..8b22be4 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -193,6 +193,9 @@
   UNSPEC_BNDCU
   UNSPEC_BNDCN
   UNSPEC_MPX_FENCE
+
+  ;; IRET support
+  UNSPEC_INTERRUPT_RETURN
 ])
 
 (define_c_enum "unspecv" [
@@ -12179,6 +12182,12 @@
    (set_attr "modrm" "0")
    (set_attr "maybe_prefix_bnd" "1")])
 
+(define_insn "interrupt_return"
+  [(simple_return)
+   (unspec [(const_int 0)] UNSPEC_INTERRUPT_RETURN)]
+  "reload_completed"
+  "iret")
+
 ;; Used by x86_machine_dependent_reorg to avoid penalty on single byte RET
 ;; instruction Athlon and K8 have.
 
diff --git a/gcc/config/i386/sse.md b/gcc/config/i386/sse.md
index 4eefb45..3f28899 100644
--- a/gcc/config/i386/sse.md
+++ b/gcc/config/i386/sse.md
@@ -875,10 +875,14 @@
 	case MODE_V16SF:
 	case MODE_V8SF:
 	case MODE_V4SF:
-	  if (TARGET_AVX
+	  /* We must support misaligned SSE load and store in interrupt
+	     handler, since ix86_emit_save_reg_using_mov generates the
+	     normal *mov<mode>_internal pattern to save and restore
+	     SSE registers with misaligned stack.  */
+	  if ((TARGET_AVX || cfun->machine->func_type != TYPE_NORMAL)
 	      && (misaligned_operand (operands[0], <MODE>mode)
 		  || misaligned_operand (operands[1], <MODE>mode)))
-	    return "vmovups\t{%1, %0|%0, %1}";
+	    return "%vmovups\t{%1, %0|%0, %1}";
 	  else
 	    return "%vmovaps\t{%1, %0|%0, %1}";
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 8406945..8c62218 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -5061,6 +5061,69 @@ On x86-32 targets, the @code{stdcall} attribute causes the compiler to
 assume that the called function pops off the stack space used to
 pass arguments, unless it takes a variable number of arguments.
 
+@item no_caller_saved_registers
+@cindex @code{no_caller_saved_registers} function attribute, x86
+Use this attribute to indicate that the specified function has no
+caller-saved registers.  That is, all registers are callee-saved.
+The compiler generates proper function entry and exit sequences to
+save and restore any modified registers.
+
+@item interrupt
+@cindex @code{interrupt} function attribute, x86
+Use this attribute to indicate that the specified function is an
+interrupt handler.  The compiler generates function
+entry and exit sequences suitable for use in an interrupt handler when
+this attribute is present.  The @code{IRET} instruction, instead of the
+@code{RET} instruction, is used to return from interrupt handlers.  All
+registers, except for the EFLAGS register which is restored by the
+@code{IRET} instruction, are preserved by the compiler.
+
+Any interruptible-without-stack-switch code must be compiled with
+@option{-mno-red-zone} since interrupt handlers can and will, because
+of the hardware design, touch the red zone.
+
+An interrupt handler must be declared with a mandatory pointer
+argument:
+
+@smallexample
+struct interrupt_frame;
+
+__attribute__ ((interrupt))
+void
+f (struct interrupt_frame *frame)
+@{
+@}
+@end smallexample
+
+and user must properly define the structure the pointer pointing to.
+
+The exception handler is very similar to the interrupt handler with
+a different mandatory function signature:
+
+@smallexample
+#ifdef __x86_64__
+typedef unsigned long long int uword_t;
+#else
+typedef unsigned int uword_t;
+#endif
+
+struct interrupt_frame;
+
+__attribute__ ((interrupt))
+void
+f (struct interrupt_frame *frame, uword_t error_code)
+@{
+  ...
+@}
+@end smallexample
+
+and compiler pops the error code off the stack before the @code{IRET}
+instruction.
+
+The exception handler should only be used for exceptions which push an
+error code and all other exceptions must use the interrupt handler.
+The system will crash if the wrong handler is used.
+
 @item target (@var{options})
 @cindex @code{target} function attribute
 As discussed in @ref{Common Function Attributes}, this attribute 
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-1.c b/gcc/testsuite/gcc.target/i386/interrupt-1.c
new file mode 100644
index 0000000..ea2f1e4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-1.c
@@ -0,0 +1,46 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-push-args" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern int bar (int);
+
+void foo (void *frame)
+{
+  int a,b,c,d,e,f,i;
+  a = bar (5);
+  b = bar (a);
+  c = bar (b);
+  d = bar (c);
+  e = bar (d);
+  f = bar (e);
+  for (i = 1; i < 10; i++)
+  {
+    a += bar (a + i) + bar (b + i) +
+	 bar (c + i) + bar (d + i) +
+	 bar (e + i) + bar (f + i);
+  }
+}
+
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-10.c b/gcc/testsuite/gcc.target/i386/interrupt-10.c
new file mode 100644
index 0000000..a439d6f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-10.c
@@ -0,0 +1,24 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mavx512bw -mno-iamcu" } */
+
+extern void bar (void);
+
+struct interrupt_frame;
+
+void
+ __attribute__ ((interrupt))
+foo (struct interrupt_frame *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm\[0-9\]+" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "kmovq\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" 8 } } */
+/* { dg-final { scan-assembler-times "kmovq\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" 8 } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-11.c b/gcc/testsuite/gcc.target/i386/interrupt-11.c
new file mode 100644
index 0000000..6556c44
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-11.c
@@ -0,0 +1,38 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-sse -mno-iamcu" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-12.c b/gcc/testsuite/gcc.target/i386/interrupt-12.c
new file mode 100644
index 0000000..5c9eb74
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-12.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int check_int (int *i, void *, int align);
+typedef int aligned __attribute__((aligned(64)));
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+__attribute__((interrupt))
+void
+foo (void *frame, uword_t error_code)
+{
+  aligned j;
+  if (check_int (frame, &j, __alignof__(j)))
+    __builtin_abort ();
+}
+
+/* { dg-final { scan-assembler-times "and\[lq\]?\[^\\n\]*-64,\[^\\n\]*sp" 1 } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-13.c b/gcc/testsuite/gcc.target/i386/interrupt-13.c
new file mode 100644
index 0000000..8cf0b25
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-13.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int check_int (int *i, void *, int align);
+typedef int aligned __attribute__((aligned(64)));
+
+__attribute__((interrupt))
+void
+foo (void *frame)
+{
+  aligned j;
+  if (check_int (frame, &j, __alignof__(j)))
+    __builtin_abort ();
+}
+
+/* { dg-final { scan-assembler-times "and\[lq\]?\[^\\n\]*-64,\[^\\n\]*sp" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-14.c b/gcc/testsuite/gcc.target/i386/interrupt-14.c
new file mode 100644
index 0000000..7394207
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-14.c
@@ -0,0 +1,39 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-sse -mno-iamcu -mno-sse4 -mno-popcnt" } */
+
+extern int i, cnt;
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  cnt = __builtin_popcount (i);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 1 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-15.c b/gcc/testsuite/gcc.target/i386/interrupt-15.c
new file mode 100644
index 0000000..1bb9cde
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-15.c
@@ -0,0 +1,24 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mpush-args" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  bar (error);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 1 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-16.c b/gcc/testsuite/gcc.target/i386/interrupt-16.c
new file mode 100644
index 0000000..794a846
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-16.c
@@ -0,0 +1,27 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mno-push-args" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  bar (error);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%rbp" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%ebp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%ebp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 1 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-17.c b/gcc/testsuite/gcc.target/i386/interrupt-17.c
new file mode 100644
index 0000000..6596a68
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-17.c
@@ -0,0 +1,29 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mpush-args" } */
+
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame)
+{
+  bar (3);
+}
+
+void
+ __attribute__ ((interrupt))
+fn2 (void *frame)
+{
+  bar (3);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 2 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-18.c b/gcc/testsuite/gcc.target/i386/interrupt-18.c
new file mode 100644
index 0000000..7767aee
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-18.c
@@ -0,0 +1,31 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mpush-args" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  bar (error);
+}
+
+void
+ __attribute__ ((interrupt))
+fn2 (void *frame, uword_t error)
+{
+  bar (error);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 2 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-19.c b/gcc/testsuite/gcc.target/i386/interrupt-19.c
new file mode 100644
index 0000000..76ca077
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-19.c
@@ -0,0 +1,24 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mno-push-args" } */
+
+extern int foo (int) __attribute__ ((no_caller_saved_registers));
+extern int bar (int) __attribute__ ((no_caller_saved_registers));
+
+int
+foo (int i)
+{
+  return bar (i) + i;
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%ebp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%ebp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rbp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rbp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-2.c b/gcc/testsuite/gcc.target/i386/interrupt-2.c
new file mode 100644
index 0000000..2003fbb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-2.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wall -Wunused-parameter" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+}
+
+/* { dg-final { scan-assembler-not "add(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-20.c b/gcc/testsuite/gcc.target/i386/interrupt-20.c
new file mode 100644
index 0000000..c1b6d32
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-20.c
@@ -0,0 +1,22 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mno-push-args" } */
+
+extern int foo (int) __attribute__ ((no_caller_saved_registers));
+extern int bar (int) __attribute__ ((no_caller_saved_registers));
+
+int
+foo (int i)
+{
+  return bar (i + i);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "jmp" } }*/
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-21.c b/gcc/testsuite/gcc.target/i386/interrupt-21.c
new file mode 100644
index 0000000..2655d90
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-21.c
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx -mno-iamcu -msse" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movaps\[\\t \]*%xmm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movaps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%xmm3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-2\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-2\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[4-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[4-9\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm1\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm1\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-22.c b/gcc/testsuite/gcc.target/i386/interrupt-22.c
new file mode 100644
index 0000000..81cf47a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-22.c
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-2\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-2\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[4-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[4-9\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm1\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm1\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-23.c b/gcc/testsuite/gcc.target/i386/interrupt-23.c
new file mode 100644
index 0000000..c92c263
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-23.c
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mavx512f -mno-iamcu" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-2\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-2\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[4-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[4-9\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm1\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm1\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-24.c b/gcc/testsuite/gcc.target/i386/interrupt-24.c
new file mode 100644
index 0000000..e6b1b13
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-24.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-iamcu -mmpx" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "bnd3");
+}
+
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*%bnd3,\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%bnd3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-3.c b/gcc/testsuite/gcc.target/i386/interrupt-3.c
new file mode 100644
index 0000000..a87e594
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-3.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+void
+__attribute__((interrupt))
+fn (void* frame, uword_t error)
+{
+}
+
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-387-err.c b/gcc/testsuite/gcc.target/i386/interrupt-387-err.c
new file mode 100644
index 0000000..3a4a5cf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-387-err.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -m80387 -mlong-double-80 -mno-iamcu" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+extern long double y, x;
+
+void
+fn0 (void)
+{
+  x = y;
+  y = 0;
+}
+
+void
+__attribute__((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  x = 0;
+} /* { dg-error "80387 instructions aren't allowed in exception service routine" } */
+
+void
+__attribute__((interrupt))
+fn2 (void *frame)
+{
+  x = y; /* { dg-error "80387 instructions aren't allowed in interrupt service routine" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-4.c b/gcc/testsuite/gcc.target/i386/interrupt-4.c
new file mode 100644
index 0000000..94230cd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-4.c
@@ -0,0 +1,32 @@
+/* { dg-do link } */
+/* { dg-options "-O" } */
+
+#include <stdint.h>
+
+extern void link_error (void);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+struct interrupt_frame
+{
+  uword_t ip;
+  uword_t cs;
+  uword_t flags;
+  uword_t sp;
+  uword_t ss;
+};
+
+__attribute__ ((used, interrupt))
+void
+foo (struct interrupt_frame *frame)
+{
+  void *ra = __builtin_return_address (0);
+  if ((uintptr_t) ra != (uintptr_t) frame->ip)
+    link_error ();
+}
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-5.c b/gcc/testsuite/gcc.target/i386/interrupt-5.c
new file mode 100644
index 0000000..53d6656
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-5.c
@@ -0,0 +1,23 @@
+/* { dg-do link } */
+/* { dg-options "-O" } */
+
+#include <stdint.h>
+
+extern void link_error (void);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+__attribute__ ((used, interrupt))
+void
+foo (void *frame, uword_t error)
+{
+  void *ra = __builtin_return_address (0);
+  if ((uintptr_t) ra != (uintptr_t) error)
+    link_error ();
+}
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-6.c b/gcc/testsuite/gcc.target/i386/interrupt-6.c
new file mode 100644
index 0000000..a1a3d0e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-6.c
@@ -0,0 +1,40 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+extern int error;
+
+__attribute__((interrupt))
+void
+fn1 (void *p, short error_code)
+{ /* { dg-error "interrupt service routine should have unsigned \(long long |long |\)int as the second argument" } */
+}
+
+__attribute__((interrupt))
+void
+fn2 (void)
+{ /* { dg-error "interrupt service routine can only have a pointer argument and an optional integer argument" } */
+}
+
+__attribute__((interrupt))
+void
+fn3 (uword_t error_code)
+{ /* { dg-error "interrupt service routine should have a pointer as the first argument" } */
+  error = error_code;
+}
+
+__attribute__((interrupt))
+void
+fn4 (uword_t error_code, void *frame)
+{ /* { dg-error "interrupt service routine should have .* the .* argument" } */
+  error = error_code;
+}
+
+extern int fn5 (void *) __attribute__ ((interrupt)); /* { dg-error "interrupt service routine can't have non-void return value" } */
+
+int
+fn5 (void *frame)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-7.c b/gcc/testsuite/gcc.target/i386/interrupt-7.c
new file mode 100644
index 0000000..e7441a2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-7.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int error;
+
+extern void fn (void *) __attribute__((interrupt));
+
+void
+foo (void)
+{
+  fn (&error); /* { dg-error "interrupt service routine can't be called directly" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-8.c b/gcc/testsuite/gcc.target/i386/interrupt-8.c
new file mode 100644
index 0000000..0a83473
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-8.c
@@ -0,0 +1,20 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 16 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm\[0-9\]+" 16 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-9.c b/gcc/testsuite/gcc.target/i386/interrupt-9.c
new file mode 100644
index 0000000..9d0c776
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-9.c
@@ -0,0 +1,22 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512bw -mno-iamcu -mavx512f" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm\[0-9\]+" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "kmovw\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" 8 } } */
+/* { dg-final { scan-assembler-times "kmovw\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" 8 } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-bnd.c b/gcc/testsuite/gcc.target/i386/interrupt-bnd.c
new file mode 100644
index 0000000..9f50661
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-bnd.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mno-iamcu -mmpx" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "bnd3");
+}
+
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*%bnd3,\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%bnd3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
new file mode 100644
index 0000000..77a9463
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
@@ -0,0 +1,30 @@
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -miamcu" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern int bar (int);
+
+void foo (void *frame)
+{
+  int a,b,c,d,e,f,i;
+  a = bar (5);
+  b = bar (a);
+  c = bar (b);
+  d = bar (c);
+  e = bar (d);
+  f = bar (e);
+  for (i = 1; i < 10; i++)
+    a += bar (a + i) + bar (b + i) +
+	 bar (c + i) + bar (d + i) +
+	 bar (e + i) + bar (f+i);
+}
+
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%eax" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%ecx" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%edx" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%eax" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%ecx" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%edx" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c
new file mode 100644
index 0000000..fc4f367
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mmmx -mno-iamcu" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+typedef short __v4hi __attribute__ ((__vector_size__ (8)));
+typedef int __m64 __attribute__ ((__vector_size__ (8), __may_alias__));
+
+extern __m64 y, x;
+
+void
+fn0 (void)
+{
+  x = __extension__ (__m64){ 0 };
+  x = (__m64) __builtin_ia32_packsswb ((__v4hi) x, (__v4hi) y);
+  y = x;
+}
+
+void
+__attribute__((interrupt))
+fn1 (void *frame)
+{
+  x = (__m64) __builtin_ia32_packsswb ((__v4hi) x, (__v4hi) y); /* { dg-error "MMX/3Dnow (instructions|intrinsics) aren't allowed in interrupt service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn2 (void *frame)
+{
+  x = y; /* { dg-error "MMX/3Dnow instructions aren't allowed in interrupt service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn3 (void *frame, uword_t error)
+{
+  x = __extension__ (__m64){ 0 }; /* { dg-error "MMX/3Dnow instructions aren't allowed in exception service routine" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c b/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c
new file mode 100644
index 0000000..796b81b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mred-zone" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  /* No need to adjust stack if less than 128 bytes are used on stack
+     with a 128-byte red zone.  */
+  char array[112];
+  asm ("# %0 "
+       :
+       : "r" (array));
+}
+
+/* { dg-final { scan-assembler-not "(sub|add)(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c b/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c
new file mode 100644
index 0000000..c6a2822
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mred-zone" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  /* Need to adjust stack if more than 128 bytes are used on stack
+     with a 128-byte red zone.  */
+  char array[113];
+  asm ("# %0 "
+       :
+       : "r" (array));
+}
+
+/* { dg-final { scan-assembler-times "(?:sub|add)(?:l|q)\[\\t \]*\\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c b/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c
new file mode 100644
index 0000000..e222acf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O3" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern void bar (void);
+
+void foo (void *frame)
+{
+  bar ();
+}
+/* { dg-final { scan-assembler-not "jmp" } }*/
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c
new file mode 100644
index 0000000..b1b0abe
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern void bar (int);
+
+void f1 (void){  bar (1); }
+__attribute__((interrupt))
+void f2 (void *frame){  bar (2); }
+void f3 (void){  bar (3); }
+__attribute__((interrupt))
+void f4 (void *frame){  bar (4); }
+void f5 (void){  bar (5); }
+
+/* { dg-final { scan-assembler-times "push.\t%.ax" 2 } } */
+/* { dg-final { scan-assembler-times "pop.\t%.ax" 2 } } */
+/* { dg-final { scan-assembler-times "iret" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-xmm.c b/gcc/testsuite/gcc.target/i386/interrupt-xmm.c
new file mode 100644
index 0000000..0eafb10
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-xmm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx -mno-iamcu -msse" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%xmm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%xmm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-ymm.c b/gcc/testsuite/gcc.target/i386/interrupt-ymm.c
new file mode 100644
index 0000000..acb5fc7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-ymm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*%ymm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-zmm.c b/gcc/testsuite/gcc.target/i386/interrupt-zmm.c
new file mode 100644
index 0000000..6d23b5d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-zmm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mavx512f -mno-iamcu" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*%zmm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-10-02 12:51                               ` Yulia Koval
@ 2015-10-02 15:45                                 ` Uros Bizjak
  2015-10-02 17:48                                   ` Yulia Koval
  0 siblings, 1 reply; 45+ messages in thread
From: Uros Bizjak @ 2015-10-02 15:45 UTC (permalink / raw)
  To: Yulia Koval; +Cc: H.J. Lu, Mike Stump, GCC Patches

On Fri, Oct 2, 2015 at 2:51 PM, Yulia Koval <vaalfreja@gmail.com> wrote:
> Hi,
> Here is a new patch. Added HJ's changes and review changes.
>
> Implement x86 interrupt attribute

+      incoming_stack_boundary
+ = (crtl->parm_stack_boundary > ix86_incoming_stack_boundary
+   ? crtl->parm_stack_boundary : ix86_incoming_stack_boundary);

MAX (crtl->parm_stack_boundary, ix86_incoming_stack_boundary);

+bool
+ix86_epilogue_uses (int regno)
+{
+  /* If there are no caller-saved registers, we preserve all registers,
+     except for MMX and x87 registers which aren't supported when saving
+     and restoring registers.  Don't explicitly save SP register since
+     it is always preserved.  */
+  return (cfun->machine->no_caller_saved_registers
+  && reg_names[regno][0]
+  && !fixed_regs[regno]
+  && !STACK_REGNO_P (regno)
+  && !MMX_REGNO_P (regno));

There is still a redundant check for reg_names with !fixed_regs. When
member of fixed_regs is 0, we are sure that corresponding reg_names is
non-null. As can be seen in ix86_conditional_register_usage, register
names are squashed depending on target for non-existent registers in
order to prevent their usage in "asm" statements.

+      /* If there are no caller-saved registers, add all registers
+ that are clobbered by the call.  */
+      for (int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (reg_names[i][0]
+    && !fixed_regs[i]
+    && (ix86_call_used_regs[i] == 1
+ || (ix86_call_used_regs[i] & c_mask))
+    && !STACK_REGNO_P (i)
+    && !MMX_REGNO_P (i))

And here.

Uros.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-10-02 15:45                                 ` Uros Bizjak
@ 2015-10-02 17:48                                   ` Yulia Koval
  2015-10-04  5:24                                     ` Yulia Koval
  0 siblings, 1 reply; 45+ messages in thread
From: Yulia Koval @ 2015-10-02 17:48 UTC (permalink / raw)
  To: Uros Bizjak; +Cc: H.J. Lu, Mike Stump, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 1711 bytes --]

Fixed it. Thanks.

On Fri, Oct 2, 2015 at 6:45 PM, Uros Bizjak <ubizjak@gmail.com> wrote:
> On Fri, Oct 2, 2015 at 2:51 PM, Yulia Koval <vaalfreja@gmail.com> wrote:
>> Hi,
>> Here is a new patch. Added HJ's changes and review changes.
>>
>> Implement x86 interrupt attribute
>
> +      incoming_stack_boundary
> + = (crtl->parm_stack_boundary > ix86_incoming_stack_boundary
> +   ? crtl->parm_stack_boundary : ix86_incoming_stack_boundary);
>
> MAX (crtl->parm_stack_boundary, ix86_incoming_stack_boundary);
>
> +bool
> +ix86_epilogue_uses (int regno)
> +{
> +  /* If there are no caller-saved registers, we preserve all registers,
> +     except for MMX and x87 registers which aren't supported when saving
> +     and restoring registers.  Don't explicitly save SP register since
> +     it is always preserved.  */
> +  return (cfun->machine->no_caller_saved_registers
> +  && reg_names[regno][0]
> +  && !fixed_regs[regno]
> +  && !STACK_REGNO_P (regno)
> +  && !MMX_REGNO_P (regno));
>
> There is still a redundant check for reg_names with !fixed_regs. When
> member of fixed_regs is 0, we are sure that corresponding reg_names is
> non-null. As can be seen in ix86_conditional_register_usage, register
> names are squashed depending on target for non-existent registers in
> order to prevent their usage in "asm" statements.
>
> +      /* If there are no caller-saved registers, add all registers
> + that are clobbered by the call.  */
> +      for (int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
> + if (reg_names[i][0]
> +    && !fixed_regs[i]
> +    && (ix86_call_used_regs[i] == 1
> + || (ix86_call_used_regs[i] & c_mask))
> +    && !STACK_REGNO_P (i)
> +    && !MMX_REGNO_P (i))
>
> And here.
>
> Uros.

[-- Attachment #2: patch_fixed --]
[-- Type: application/octet-stream, Size: 73818 bytes --]

diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index 6a17ef4..c7ff393 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -278,6 +278,8 @@ extern rtx maybe_get_pool_constant (rtx);
 extern char internal_label_prefix[16];
 extern int internal_label_prefix_len;
 
+extern bool ix86_epilogue_uses (int);
+
 enum ix86_address_seg { SEG_DEFAULT, SEG_FS, SEG_GS };
 struct ix86_address
 {
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index cfeba76..cf13d58 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -2381,6 +2381,8 @@ struct ix86_frame
 {
   int nsseregs;
   int nregs;
+  int nbndregs;
+  int nmaskregs;
   int va_arg_size;
   int red_zone_size;
   int outgoing_arguments_size;
@@ -5464,6 +5466,13 @@ ix86_conditional_register_usage (void)
 {
   int i, c_mask;
 
+  /* If there are no caller-saved registers, preserve all registers.
+     except fixed_regs.  */
+  if (cfun && cfun->machine->no_caller_saved_registers)
+    for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+      if (!fixed_regs[i])
+	call_used_regs[i] = 0;
+
   /* For 32-bit targets, squash the REX registers.  */
   if (! TARGET_64BIT)
     {
@@ -6245,6 +6254,27 @@ ix86_set_current_function (tree fndecl)
       return;
     }
 
+  if (lookup_attribute ("interrupt", DECL_ATTRIBUTES (fndecl)))
+    {
+      int nargs = 0;
+      for (tree arg = DECL_ARGUMENTS (fndecl);
+	   arg;
+	   arg = TREE_CHAIN (arg))
+	{
+	  /* Mark arguments in interrupt handler as used to silence
+	     compiler warning since arguments in interrupt handler
+	     are mandatory even if they aren't used.  */
+	  TREE_USED (arg) = 1;
+	  nargs++;
+	}
+      cfun->machine->no_caller_saved_registers = true;
+      cfun->machine->func_type
+	= nargs == 2 ? TYPE_EXCEPTION : TYPE_INTERRUPT;
+    }
+  else if (lookup_attribute ("no_caller_saved_registers",
+			     DECL_ATTRIBUTES (fndecl)))
+    cfun->machine->no_caller_saved_registers = true;
+
   tree new_tree = DECL_FUNCTION_SPECIFIC_TARGET (fndecl);
   if (new_tree == NULL_TREE)
     new_tree = target_option_default_node;
@@ -6523,6 +6553,10 @@ ix86_function_ok_for_sibcall (tree decl, tree exp)
   tree type, decl_or_type;
   rtx a, b;
 
+  /* Sibling call isn't OK if there are no caller-saved registers.  */
+  if (cfun->machine->no_caller_saved_registers)
+    return false;
+
   /* If we are generating position-independent code, we cannot sibcall
      optimize direct calls to global functions, as the PLT requires
      %ebx be live. (Darwin does not have a PLT.)  */
@@ -7401,8 +7435,17 @@ ix86_call_abi_override (const_tree fndecl)
 static void
 ix86_maybe_switch_abi (void)
 {
-  if (TARGET_64BIT &&
-      call_used_regs[SI_REG] == (cfun->machine->call_abi == MS_ABI))
+  if (TARGET_64BIT
+	&& (call_used_regs[SI_REG]
+	  == (cfun->machine->call_abi == MS_ABI)))
+    {
+      reinit_regs ();
+      return;
+    }
+  /* AX register is only preserved if there are no caller-saved
+     registers.  */
+  if (cfun->machine->no_caller_saved_registers
+       != call_used_regs[AX_REG])
     reinit_regs ();
 }
 
@@ -7707,6 +7750,8 @@ type_natural_mode (const_tree type, const CUMULATIVE_ARGS *cum,
 		      }
 		  }
 		else if ((size == 8 && !TARGET_64BIT)
+			 && (!cfun
+			     || cfun->machine->func_type == TYPE_NORMAL)
 			 && !TARGET_MMX
 			 && !TARGET_IAMCU)
 		  {
@@ -8686,6 +8731,11 @@ ix86_function_arg_advance (cumulative_args_t cum_v, machine_mode mode,
   HOST_WIDE_INT bytes, words;
   int nregs;
 
+  /* The argument of interrupt handler is a special case and is
+     handled in ix86_function_arg.  */
+  if (!cum->caller && cfun->machine->func_type != TYPE_NORMAL)
+    return;
+
   if (mode == BLKmode)
     bytes = int_size_in_bytes (type);
   else
@@ -9002,6 +9052,39 @@ ix86_function_arg (cumulative_args_t cum_v, machine_mode omode,
   HOST_WIDE_INT bytes, words;
   rtx arg;
 
+  if (!cum->caller && cfun->machine->func_type != TYPE_NORMAL)
+    {
+      /* The first argument of interrupt handler is a pointer and
+	 points to the return address on stack.  The optional second
+	 argument is an integer for error code on stack.  */
+      gcc_assert (type != NULL_TREE);
+      if (POINTER_TYPE_P (type))
+	{
+	  if (cfun->machine->func_type == TYPE_EXCEPTION)
+	    /* (AP) in the current frame in exception handler.  */
+	    arg = arg_pointer_rtx;
+	  else
+	    /* -WORD(AP) in the current frame in interrupt handler.  */
+	    arg = plus_constant (Pmode, arg_pointer_rtx,
+				 -UNITS_PER_WORD);
+	  if (mode != Pmode)
+	    arg = convert_to_mode (mode, arg, 1);
+	}
+      else
+	{
+	  gcc_assert (TREE_CODE (type) == INTEGER_TYPE
+		      && cfun->machine->func_type == TYPE_EXCEPTION
+		      && mode == word_mode);
+	  /* The error code is at -WORD(AP) in the current frame in
+	     exception handler.  */
+	  arg = gen_rtx_MEM (word_mode,
+			     plus_constant (Pmode, arg_pointer_rtx,
+					    -UNITS_PER_WORD));
+	}
+
+      return arg;
+    }
+
   /* All pointer bounds arguments are handled separately here.  */
   if ((type && POINTER_BOUNDS_TYPE_P (type))
       || POINTER_BOUNDS_MODE_P (mode))
@@ -10717,7 +10800,10 @@ ix86_can_use_return_insn_p (void)
 {
   struct ix86_frame frame;
 
-  if (! reload_completed || frame_pointer_needed)
+  /* Don't use `ret' instruction in interrupt handler.  */
+  if (! reload_completed
+      || frame_pointer_needed
+      || cfun->machine->func_type != TYPE_NORMAL)
     return 0;
 
   /* Don't allow more than 32k pop, since that's all we can do
@@ -11027,11 +11113,65 @@ ix86_select_alt_pic_regnum (void)
   return INVALID_REGNUM;
 }
 
+/* Return nonzero if register REGNO can be used as a scratch register
+   in peephole2.  */
+
+bool
+ix86_hard_regno_scratch_ok (unsigned int regno ATTRIBUTE_UNUSED)
+{
+  /* No scratch registers if there are no caller-saved registers.  */
+  return !cfun->machine->no_caller_saved_registers;
+}
+
+/* Return true if REGNO is used by the epilogue.  */
+
+bool
+ix86_epilogue_uses (int regno)
+{
+  /* If there are no caller-saved registers, we preserve all registers,
+     except for MMX and x87 registers which aren't supported when saving
+     and restoring registers.  Don't explicitly save SP register since
+     it is always preserved.  */
+  return (cfun->machine->no_caller_saved_registers
+	  && !fixed_regs[regno]
+	  && !STACK_REGNO_P (regno)
+	  && !MMX_REGNO_P (regno));
+}
+
+/* Return TRUE if register REGNO is ever defined..  */
+
+static bool
+ix86_reg_ever_defined_p (unsigned int regno)
+{
+  df_ref def;
+
+  for (def = DF_REG_DEF_CHAIN (regno); def; def = DF_REF_NEXT_REG (def))
+    if (!DF_REF_IS_ARTIFICIAL (def))
+      return true;
+
+  return false;
+}
+
 /* Return TRUE if we need to save REGNO.  */
 
 static bool
 ix86_save_reg (unsigned int regno, bool maybe_eh_return)
 {
+  /* If there are no caller-saved registers, we preserve all registers,
+     except for MMX and x87 registers which aren't supported when saving
+     and restoring registers.  Don't explicitly save SP register since
+     it is always preserved.  */
+  if (cfun->machine->no_caller_saved_registers && reload_completed)
+    return (df_regs_ever_live_p (regno)
+	    && ix86_reg_ever_defined_p (regno)
+	    && !fixed_regs[regno]
+	    && (!crtl->return_rtx
+		|| !refers_to_regno_p (regno, crtl->return_rtx))
+	    && (!crtl->return_bnd
+		|| !refers_to_regno_p (regno, crtl->return_bnd))
+	    && !STACK_REGNO_P (regno)
+	    && !MMX_REGNO_P (regno));
+
   if (regno == REAL_PIC_OFFSET_TABLE_REGNUM
       && pic_offset_table_rtx)
     {
@@ -11088,6 +11228,34 @@ ix86_nsaved_regs (void)
   return nregs;
 }
 
+/* Return number of saved bound registers.  */
+
+static int
+ix86_nsaved_bndregs (void)
+{
+  int nregs = 0;
+  int regno;
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (BND_REGNO_P (regno) && ix86_save_reg (regno, true))
+      nregs ++;
+  return nregs;
+}
+
+/* Return number of saved mask registers.  */
+
+static int
+ix86_nsaved_maskregs (void)
+{
+  int nregs = 0;
+  int regno;
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (MASK_REGNO_P (regno) && ix86_save_reg (regno, true))
+      nregs ++;
+  return nregs;
+}
+
 /* Return number of saved SSE registers.  */
 
 static int
@@ -11096,7 +11264,10 @@ ix86_nsaved_sseregs (void)
   int nregs = 0;
   int regno;
 
-  if (!TARGET_64BIT_MS_ABI)
+  /* Always need to save SSE registers if there are no caller-saved
+     registers.  */
+  if (!TARGET_64BIT_MS_ABI
+      && !cfun->machine->no_caller_saved_registers)
     return 0;
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
@@ -11165,6 +11336,18 @@ ix86_builtin_setjmp_frame_value (void)
 
 #define SPLIT_STACK_AVAILABLE 256
 
+/* Register save area size.  */
+
+static unsigned int
+ix86_reg_save_area_size (struct ix86_frame *frame)
+{
+  unsigned int size = ((frame->nregs - frame->nbndregs
+			- frame->nmaskregs) * UNITS_PER_WORD);
+  size += frame->nbndregs * (ix86_pmode == PMODE_DI ? 16 : 8);
+  size += frame->nmaskregs * (TARGET_AVX512BW ? 8 : 2);
+  return size;
+}
+
 /* Fill structure ix86_frame about frame of currently computed function.  */
 
 static void
@@ -11177,6 +11360,8 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   HOST_WIDE_INT to_allocate;
 
   frame->nregs = ix86_nsaved_regs ();
+  frame->nbndregs = ix86_nsaved_bndregs ();
+  frame->nmaskregs = ix86_nsaved_maskregs ();
   frame->nsseregs = ix86_nsaved_sseregs ();
 
   /* 64-bit MS ABI seem to require stack alignment to be always 16 except for
@@ -11248,11 +11433,16 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
 	   = !expensive_function_p (count);
     }
 
+  /* We must use move to save bound and mask registers.  */
   frame->save_regs_using_mov
-    = (TARGET_PROLOGUE_USING_MOVE && cfun->machine->use_fast_prologue_epilogue
-       /* If static stack checking is enabled and done with probes,
-	  the registers need to be saved before allocating the frame.  */
-       && flag_stack_check != STATIC_BUILTIN_STACK_CHECK);
+    = (frame->nbndregs > 0
+       || frame->nmaskregs > 0
+       || (TARGET_PROLOGUE_USING_MOVE
+	   && cfun->machine->use_fast_prologue_epilogue
+	   /* If static stack checking is enabled and done with probes,
+	      the registers need to be saved before allocating the
+	      frame.  */
+	   && flag_stack_check != STATIC_BUILTIN_STACK_CHECK));
 
   /* Skip return address.  */
   offset = UNITS_PER_WORD;
@@ -11270,7 +11460,7 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   frame->hard_frame_pointer_offset = offset;
 
   /* Register save area */
-  offset += frame->nregs * UNITS_PER_WORD;
+  offset += ix86_reg_save_area_size (frame);
   frame->reg_save_offset = offset;
 
   /* On SEH target, registers are pushed just before the frame pointer
@@ -11284,9 +11474,18 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
       /* The only ABI that has saved SSE registers (Win64) also has a
          16-byte aligned default stack, and thus we don't need to be
 	 within the re-aligned local stack frame to save them.  */
-      gcc_assert (INCOMING_STACK_BOUNDARY >= 128);
-      offset = (offset + 16 - 1) & -16;
-      offset += frame->nsseregs * 16;
+      if (cfun->machine->no_caller_saved_registers)
+	/* We must save full vector registers if there are no
+	   caller-saved registers.  */
+	offset += frame->nsseregs * (TARGET_AVX512F
+				     ? 64
+				     : (TARGET_AVX ? 32 : 16));
+      else
+	{
+	  gcc_assert (INCOMING_STACK_BOUNDARY >= 128);
+	  offset = (offset + 16 - 1) & -16;
+	  offset += frame->nsseregs * 16;
+	}
     }
   frame->sse_reg_save_offset = offset;
 
@@ -11342,8 +11541,12 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   /* Size prologue needs to allocate.  */
   to_allocate = offset - frame->sse_reg_save_offset;
 
-  if ((!to_allocate && frame->nregs <= 1)
-      || (TARGET_64BIT && to_allocate >= (HOST_WIDE_INT) 0x80000000))
+  /* We must use move to save bound and mask registers.  */
+  if (frame->nbndregs == 0
+      && frame->nmaskregs == 0
+      && ((!to_allocate && frame->nregs <= 1)
+	   || (TARGET_64BIT
+	       && to_allocate >= (HOST_WIDE_INT) 0x80000000)))
     frame->save_regs_using_mov = false;
 
   if (ix86_using_red_zone ()
@@ -11353,7 +11556,7 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
     {
       frame->red_zone_size = to_allocate;
       if (frame->save_regs_using_mov)
-	frame->red_zone_size += frame->nregs * UNITS_PER_WORD;
+	frame->red_zone_size += ix86_reg_save_area_size (frame);
       if (frame->red_zone_size > RED_ZONE_SIZE - RED_ZONE_RESERVE)
 	frame->red_zone_size = RED_ZONE_SIZE - RED_ZONE_RESERVE;
     }
@@ -11513,8 +11716,21 @@ ix86_emit_save_reg_using_mov (machine_mode mode, unsigned int regno,
   addr = choose_baseaddr (cfa_offset);
   mem = gen_frame_mem (mode, addr);
 
-  /* For SSE saves, we need to indicate the 128-bit alignment.  */
-  set_mem_align (mem, GET_MODE_ALIGNMENT (mode));
+  /* For SSE saves, we need to indicate the 128-bit alignment.  In
+     interrupt handler, stack is aligned to word_mode.  We can't use
+     gen_sse_storeups since RTX_FRAME_RELATED_P is set and
+     dwarf2out_flush_queued_reg_saves doesn't like UNSPEC_STOREU.
+     Also gen_sse_storeups doesn't cover AVX nor AVX512.  */
+  unsigned int incoming_stack_boundary;
+  if (cfun->machine->func_type == TYPE_NORMAL)
+    {
+      incoming_stack_boundary
+	= MAX(crtl->parm_stack_boundary, ix86_incoming_stack_boundary);
+      gcc_assert (incoming_stack_boundary >= 128);
+    }
+  else
+    incoming_stack_boundary = GET_MODE_ALIGNMENT (word_mode);
+  set_mem_align (mem, incoming_stack_boundary);
 
   insn = emit_move_insn (mem, reg);
   RTX_FRAME_RELATED_P (insn) = 1;
@@ -11575,8 +11791,9 @@ ix86_emit_save_regs_using_mov (HOST_WIDE_INT cfa_offset)
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (!SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
       {
-        ix86_emit_save_reg_using_mov (word_mode, regno, cfa_offset);
-	cfa_offset -= UNITS_PER_WORD;
+	enum machine_mode reg_mode = GET_MODE (regno_reg_rtx[regno]);
+	ix86_emit_save_reg_using_mov (reg_mode, regno, cfa_offset);
+	cfa_offset -= GET_MODE_SIZE (reg_mode);
       }
 }
 
@@ -11586,12 +11803,27 @@ static void
 ix86_emit_save_sse_regs_using_mov (HOST_WIDE_INT cfa_offset)
 {
   unsigned int regno;
+  enum machine_mode vector_reg_mode;
+
+  if (cfun->machine->no_caller_saved_registers)
+    {
+      /* We must save full vector registers if there are no
+	 caller-saved registers.  */
+      if (TARGET_AVX512F)
+	vector_reg_mode = V16SFmode;
+      else if (TARGET_AVX)
+	vector_reg_mode = V8SFmode;
+      else
+	vector_reg_mode = V4SFmode;
+    }
+  else
+    vector_reg_mode = V4SFmode;
 
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
       {
-	ix86_emit_save_reg_using_mov (V4SFmode, regno, cfa_offset);
-	cfa_offset -= 16;
+	ix86_emit_save_reg_using_mov (vector_reg_mode, regno, cfa_offset);
+	cfa_offset -= GET_MODE_SIZE (vector_reg_mode);
       }
 }
 
@@ -13036,12 +13268,13 @@ ix86_emit_restore_regs_using_mov (HOST_WIDE_INT cfa_offset,
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (!SSE_REGNO_P (regno) && ix86_save_reg (regno, maybe_eh_return))
       {
-	rtx reg = gen_rtx_REG (word_mode, regno);
+	enum machine_mode reg_mode = GET_MODE (regno_reg_rtx[regno]);
+	rtx reg = gen_rtx_REG (reg_mode, regno);
 	rtx mem;
 	rtx_insn *insn;
 
 	mem = choose_baseaddr (cfa_offset);
-	mem = gen_frame_mem (word_mode, mem);
+	mem = gen_frame_mem (reg_mode, mem);
 	insn = emit_move_insn (reg, mem);
 
         if (m->fs.cfa_reg == crtl->drap_reg && regno == REGNO (crtl->drap_reg))
@@ -13060,7 +13293,7 @@ ix86_emit_restore_regs_using_mov (HOST_WIDE_INT cfa_offset,
 	else
 	  ix86_add_cfa_restore_note (NULL, reg, cfa_offset);
 
-	cfa_offset -= UNITS_PER_WORD;
+	cfa_offset -= GET_MODE_SIZE (reg_mode);
       }
 }
 
@@ -13071,21 +13304,48 @@ ix86_emit_restore_sse_regs_using_mov (HOST_WIDE_INT cfa_offset,
 				      bool maybe_eh_return)
 {
   unsigned int regno;
+  enum machine_mode vector_reg_mode;
+
+  if (cfun->machine->no_caller_saved_registers)
+    {
+      /* We must restore full vector registers if there are no
+	 caller-saved registers.  */
+      if (TARGET_AVX512F)
+	vector_reg_mode = V16SFmode;
+      else if (TARGET_AVX)
+	vector_reg_mode = V8SFmode;
+      else
+	vector_reg_mode = V4SFmode;
+    }
+  else
+    vector_reg_mode = V4SFmode;
+
+  /* In interrupt handler, stack is aligned to word_mode.  */
+  unsigned int incoming_stack_boundary;
+  if (cfun->machine->func_type == TYPE_NORMAL)
+    {
+      incoming_stack_boundary
+	= (crtl->parm_stack_boundary > ix86_incoming_stack_boundary
+	   ? crtl->parm_stack_boundary : ix86_incoming_stack_boundary);
+      gcc_assert (incoming_stack_boundary >= 128);
+    }
+  else
+    incoming_stack_boundary = GET_MODE_ALIGNMENT (word_mode);
 
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, maybe_eh_return))
       {
-	rtx reg = gen_rtx_REG (V4SFmode, regno);
+	rtx reg = gen_rtx_REG (vector_reg_mode, regno);
 	rtx mem;
 
 	mem = choose_baseaddr (cfa_offset);
-	mem = gen_rtx_MEM (V4SFmode, mem);
-	set_mem_align (mem, 128);
+	mem = gen_rtx_MEM (vector_reg_mode, mem);
+	set_mem_align (mem, incoming_stack_boundary);
 	emit_move_insn (reg, mem);
 
 	ix86_add_cfa_restore_note (NULL, reg, cfa_offset);
 
-	cfa_offset -= 16;
+	cfa_offset -= GET_MODE_SIZE (vector_reg_mode);
       }
 }
 
@@ -13149,8 +13409,9 @@ ix86_expand_epilogue (int style)
   if (crtl->calls_eh_return && style != 2)
     frame.reg_save_offset -= 2 * UNITS_PER_WORD;
 
-  /* EH_RETURN requires the use of moves to function properly.  */
-  if (crtl->calls_eh_return)
+  /* EH_RETURN requires the use of moves to function properly.  We must
+     use move to restore bound and mask registers.  */
+  if (crtl->calls_eh_return || frame.nbndregs > 0 || frame.nmaskregs > 0)
     restore_regs_via_mov = true;
   /* SEH requires the use of pops to identify the epilogue.  */
   else if (TARGET_SEH)
@@ -13391,7 +13652,20 @@ ix86_expand_epilogue (int style)
       return;
     }
 
-  if (crtl->args.pops_args && crtl->args.size)
+  if (cfun->machine->func_type != TYPE_NORMAL)
+    {
+      /* Return with the "IRET" instruction from interrupt handler.
+	 Pop the 'ERROR_CODE' off the stack before the 'IRET'
+	 instruction in exception handler.  */
+      if (cfun->machine->func_type == TYPE_EXCEPTION)
+	{
+	  rtx r = plus_constant (Pmode, stack_pointer_rtx,
+				 UNITS_PER_WORD);
+	  emit_insn (gen_rtx_SET (stack_pointer_rtx, r));
+	}
+      emit_jump_insn (gen_interrupt_return ());
+    }
+  else if (crtl->args.pops_args && crtl->args.size)
     {
       rtx popc = GEN_INT (crtl->args.pops_args);
 
@@ -18364,6 +18638,14 @@ ix86_expand_move (machine_mode mode, rtx operands[])
   rtx op0, op1;
   enum tls_model model;
 
+  if (cfun->machine->func_type != TYPE_NORMAL && IS_STACK_MODE (mode))
+    {
+      error ("80387 instructions aren't allowed in %s service routine",
+	     (cfun->machine->func_type == TYPE_EXCEPTION
+	      ? "exception" : "interrupt"));
+      return;
+    }
+
   op0 = operands[0];
   op1 = operands[1];
 
@@ -18508,6 +18790,15 @@ ix86_expand_vector_move (machine_mode mode, rtx operands[])
   rtx op0 = operands[0], op1 = operands[1];
   unsigned int align = GET_MODE_ALIGNMENT (mode);
 
+  if (cfun->machine->func_type != TYPE_NORMAL
+      && VALID_MMX_REG_MODE (mode))
+    {
+      error ("MMX/3Dnow instructions aren't allowed in %s service routine",
+	     (cfun->machine->func_type == TYPE_EXCEPTION
+	      ? "exception" : "interrupt"));
+      return;
+    }
+
   if (push_operand (op0, VOIDmode))
     op0 = emit_move_resolve_push (mode, op0);
 
@@ -26593,6 +26884,17 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
   rtx vec[3];
   rtx use = NULL, call;
   unsigned int vec_len = 0;
+  tree fndecl;
+
+  if (GET_CODE (XEXP (fnaddr, 0)) == SYMBOL_REF)
+    {
+      fndecl = SYMBOL_REF_DECL (XEXP (fnaddr, 0));
+      if (fndecl
+	  && (lookup_attribute ("interrupt", DECL_ATTRIBUTES (fndecl))))
+	error ("interrupt service routine can't be called directly");
+    }
+  else
+    fndecl = NULL_TREE;
 
   if (pop == const0_rtx)
     pop = NULL;
@@ -26690,8 +26992,30 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
       vec[vec_len++] = pop;
     }
 
-  if (TARGET_64BIT_MS_ABI
-      && (!callarg2 || INTVAL (callarg2) != -2))
+  if (cfun->machine->no_caller_saved_registers
+      && (!fndecl
+	  || !lookup_attribute ("no_caller_saved_registers",
+				DECL_ATTRIBUTES (fndecl))))
+    {
+      static const char ix86_call_used_regs[] = CALL_USED_REGISTERS;
+      char c_mask = ((TARGET_64BIT
+		      && ix86_function_abi (fndecl) == MS_ABI)
+		     ? (1 << 3)
+		     : TARGET_64BIT ? (1 << 2) : (1 << 1));
+
+      /* If there are no caller-saved registers, add all registers
+	 that are clobbered by the call.  */
+      for (int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+	if (!fixed_regs[i]
+	    && (ix86_call_used_regs[i] == 1
+		|| (ix86_call_used_regs[i] & c_mask))
+	    && !STACK_REGNO_P (i)
+	    && !MMX_REGNO_P (i))
+	  clobber_reg (&use,
+		       gen_rtx_REG (GET_MODE (regno_reg_rtx[i]), i));
+    }
+  else if (TARGET_64BIT_MS_ABI
+	   && (!callarg2 || INTVAL (callarg2) != -2))
     {
       int const cregs_size
 	= ARRAY_SIZE (x86_64_ms_sysv_extra_clobbered_registers);
@@ -44072,6 +44396,69 @@ ix86_handle_fndecl_attribute (tree *node, tree name, tree, int,
   return NULL_TREE;
 }
 
+static tree
+ix86_handle_no_caller_saved_registers_attribute (tree *node,
+						 tree name, tree, int,
+						 bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+	       name);
+      *no_add_attrs = true;
+    }
+  return NULL_TREE;
+}
+
+static tree
+ix86_handle_interrupt_attribute (tree *node, tree name, tree, int,
+				 bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+	       name);
+      *no_add_attrs = true;
+    }
+
+  /* DECL_RESULT and DECL_ARGUMENTS do not exist there yet,
+     but the function type contains args and return type data.  */
+  tree func_type = TREE_TYPE (*node);
+  tree return_type = TREE_TYPE (func_type);
+
+  int nargs = 0;
+  tree current_arg_type = TYPE_ARG_TYPES (func_type);
+  while (current_arg_type
+	 && ! VOID_TYPE_P (TREE_VALUE (current_arg_type)))
+    {
+      if (nargs == 0)
+	{
+	  if (! POINTER_TYPE_P (TREE_VALUE (current_arg_type)))
+	    error ("interrupt service routine should have a pointer "
+		   "as the first argument");
+	}
+      else if (nargs == 1)
+	{
+	  if (TREE_CODE (TREE_VALUE (current_arg_type)) != INTEGER_TYPE
+	      || TYPE_MODE (TREE_VALUE (current_arg_type)) != word_mode)
+	    error ("interrupt service routine should have unsigned %s"
+		   "int as the second argument",
+		   TARGET_64BIT
+		   ? (TARGET_X32 ? "long long " : "long ")
+		   : "");
+	}
+      nargs++;
+      current_arg_type = TREE_CHAIN (current_arg_type);
+    }
+  if (!nargs || nargs > 2)
+    error ("interrupt service routine can only have a pointer argument "
+	   "and an optional integer argument");
+  if (! VOID_TYPE_P (return_type))
+    error ("interrupt service routine can't have non-void return value");
+
+  return NULL_TREE;
+}
+
 static bool
 ix86_ms_bitfield_layout_p (const_tree record_type)
 {
@@ -48073,6 +48460,11 @@ static const struct attribute_spec ix86_attribute_table[] =
     false },
   { "callee_pop_aggregate_return", 1, 1, false, true, true,
     ix86_handle_callee_pop_aggregate_return, true },
+  { "interrupt", 0, 0, true, false, false,
+    ix86_handle_interrupt_attribute, false },
+  { "no_caller_saved_registers", 0, 0, true, false, false,
+    ix86_handle_no_caller_saved_registers_attribute, false },
+
   /* End element.  */
   { NULL,        0, 0, false, false, false, NULL, false }
 };
@@ -53861,6 +54253,9 @@ ix86_operands_ok_for_move_multiple (rtx *operands, bool load,
 #undef TARGET_ABSOLUTE_BIGGEST_ALIGNMENT
 #define TARGET_ABSOLUTE_BIGGEST_ALIGNMENT 512
 
+#undef TARGET_HARD_REGNO_SCRATCH_OK
+#define TARGET_HARD_REGNO_SCRATCH_OK ix86_hard_regno_scratch_ok
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 #include "gt-i386.h"
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 96ba90f..b9d3fc7 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -1730,6 +1730,11 @@ typedef struct ix86_args {
 
 #define EXIT_IGNORE_STACK 1
 
+/* Define this macro as a C expression that is nonzero for registers
+   used by the epilogue or the `return' pattern.  */
+
+#define EPILOGUE_USES(REGNO) ix86_epilogue_uses (REGNO)
+
 /* Output assembler code for a block containing the constant parts
    of a trampoline, leaving space for the variable parts.  */
 
@@ -2444,6 +2449,18 @@ struct GTY(()) machine_frame_state
 /* Private to winnt.c.  */
 struct seh_frame_state;
 
+enum function_type
+{
+  TYPE_NORMAL = 0,
+  /* The current function is an interrupt service routine with a
+     pointer argument as specified by the "interrupt" attribute.  */
+  TYPE_INTERRUPT,
+  /* The current function is an interrupt service routine with a
+     pointer argument and an integer argument as specified by the
+     "interrupt" attribute.  */
+  TYPE_EXCEPTION
+};
+
 struct GTY(()) machine_function {
   struct stack_local_entry *stack_locals;
   const char *some_ld_name;
@@ -2494,6 +2511,13 @@ struct GTY(()) machine_function {
   /* If true, it is safe to not save/restore DRAP register.  */
   BOOL_BITFIELD no_drap_save_restore : 1;
 
+  /* Function type.  */
+  ENUM_BITFIELD(function_type) func_type : 2;
+
+  /* If true, the current function is a function specified with
+     the "interrupt" or "no_caller_saved_registers" attribute.  */
+  BOOL_BITFIELD no_caller_saved_registers : 1;
+
   /* If true, there is register available for argument passing.  This
      is used only in ix86_function_ok_for_sibcall by 32-bit to determine
      if there is scratch register available for indirect sibcall.  In
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 8c2ed60..8b22be4 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -193,6 +193,9 @@
   UNSPEC_BNDCU
   UNSPEC_BNDCN
   UNSPEC_MPX_FENCE
+
+  ;; IRET support
+  UNSPEC_INTERRUPT_RETURN
 ])
 
 (define_c_enum "unspecv" [
@@ -12179,6 +12182,12 @@
    (set_attr "modrm" "0")
    (set_attr "maybe_prefix_bnd" "1")])
 
+(define_insn "interrupt_return"
+  [(simple_return)
+   (unspec [(const_int 0)] UNSPEC_INTERRUPT_RETURN)]
+  "reload_completed"
+  "iret")
+
 ;; Used by x86_machine_dependent_reorg to avoid penalty on single byte RET
 ;; instruction Athlon and K8 have.
 
diff --git a/gcc/config/i386/sse.md b/gcc/config/i386/sse.md
index 4eefb45..3f28899 100644
--- a/gcc/config/i386/sse.md
+++ b/gcc/config/i386/sse.md
@@ -875,10 +875,14 @@
 	case MODE_V16SF:
 	case MODE_V8SF:
 	case MODE_V4SF:
-	  if (TARGET_AVX
+	  /* We must support misaligned SSE load and store in interrupt
+	     handler, since ix86_emit_save_reg_using_mov generates the
+	     normal *mov<mode>_internal pattern to save and restore
+	     SSE registers with misaligned stack.  */
+	  if ((TARGET_AVX || cfun->machine->func_type != TYPE_NORMAL)
 	      && (misaligned_operand (operands[0], <MODE>mode)
 		  || misaligned_operand (operands[1], <MODE>mode)))
-	    return "vmovups\t{%1, %0|%0, %1}";
+	    return "%vmovups\t{%1, %0|%0, %1}";
 	  else
 	    return "%vmovaps\t{%1, %0|%0, %1}";
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 8406945..8c62218 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -5061,6 +5061,69 @@ On x86-32 targets, the @code{stdcall} attribute causes the compiler to
 assume that the called function pops off the stack space used to
 pass arguments, unless it takes a variable number of arguments.
 
+@item no_caller_saved_registers
+@cindex @code{no_caller_saved_registers} function attribute, x86
+Use this attribute to indicate that the specified function has no
+caller-saved registers.  That is, all registers are callee-saved.
+The compiler generates proper function entry and exit sequences to
+save and restore any modified registers.
+
+@item interrupt
+@cindex @code{interrupt} function attribute, x86
+Use this attribute to indicate that the specified function is an
+interrupt handler.  The compiler generates function
+entry and exit sequences suitable for use in an interrupt handler when
+this attribute is present.  The @code{IRET} instruction, instead of the
+@code{RET} instruction, is used to return from interrupt handlers.  All
+registers, except for the EFLAGS register which is restored by the
+@code{IRET} instruction, are preserved by the compiler.
+
+Any interruptible-without-stack-switch code must be compiled with
+@option{-mno-red-zone} since interrupt handlers can and will, because
+of the hardware design, touch the red zone.
+
+An interrupt handler must be declared with a mandatory pointer
+argument:
+
+@smallexample
+struct interrupt_frame;
+
+__attribute__ ((interrupt))
+void
+f (struct interrupt_frame *frame)
+@{
+@}
+@end smallexample
+
+and user must properly define the structure the pointer pointing to.
+
+The exception handler is very similar to the interrupt handler with
+a different mandatory function signature:
+
+@smallexample
+#ifdef __x86_64__
+typedef unsigned long long int uword_t;
+#else
+typedef unsigned int uword_t;
+#endif
+
+struct interrupt_frame;
+
+__attribute__ ((interrupt))
+void
+f (struct interrupt_frame *frame, uword_t error_code)
+@{
+  ...
+@}
+@end smallexample
+
+and compiler pops the error code off the stack before the @code{IRET}
+instruction.
+
+The exception handler should only be used for exceptions which push an
+error code and all other exceptions must use the interrupt handler.
+The system will crash if the wrong handler is used.
+
 @item target (@var{options})
 @cindex @code{target} function attribute
 As discussed in @ref{Common Function Attributes}, this attribute 
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-1.c b/gcc/testsuite/gcc.target/i386/interrupt-1.c
new file mode 100644
index 0000000..ea2f1e4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-1.c
@@ -0,0 +1,46 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-push-args" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern int bar (int);
+
+void foo (void *frame)
+{
+  int a,b,c,d,e,f,i;
+  a = bar (5);
+  b = bar (a);
+  c = bar (b);
+  d = bar (c);
+  e = bar (d);
+  f = bar (e);
+  for (i = 1; i < 10; i++)
+  {
+    a += bar (a + i) + bar (b + i) +
+	 bar (c + i) + bar (d + i) +
+	 bar (e + i) + bar (f + i);
+  }
+}
+
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-10.c b/gcc/testsuite/gcc.target/i386/interrupt-10.c
new file mode 100644
index 0000000..a439d6f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-10.c
@@ -0,0 +1,24 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mavx512bw -mno-iamcu" } */
+
+extern void bar (void);
+
+struct interrupt_frame;
+
+void
+ __attribute__ ((interrupt))
+foo (struct interrupt_frame *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm\[0-9\]+" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "kmovq\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" 8 } } */
+/* { dg-final { scan-assembler-times "kmovq\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" 8 } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-11.c b/gcc/testsuite/gcc.target/i386/interrupt-11.c
new file mode 100644
index 0000000..6556c44
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-11.c
@@ -0,0 +1,38 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-sse -mno-iamcu" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-12.c b/gcc/testsuite/gcc.target/i386/interrupt-12.c
new file mode 100644
index 0000000..5c9eb74
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-12.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int check_int (int *i, void *, int align);
+typedef int aligned __attribute__((aligned(64)));
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+__attribute__((interrupt))
+void
+foo (void *frame, uword_t error_code)
+{
+  aligned j;
+  if (check_int (frame, &j, __alignof__(j)))
+    __builtin_abort ();
+}
+
+/* { dg-final { scan-assembler-times "and\[lq\]?\[^\\n\]*-64,\[^\\n\]*sp" 1 } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-13.c b/gcc/testsuite/gcc.target/i386/interrupt-13.c
new file mode 100644
index 0000000..8cf0b25
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-13.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int check_int (int *i, void *, int align);
+typedef int aligned __attribute__((aligned(64)));
+
+__attribute__((interrupt))
+void
+foo (void *frame)
+{
+  aligned j;
+  if (check_int (frame, &j, __alignof__(j)))
+    __builtin_abort ();
+}
+
+/* { dg-final { scan-assembler-times "and\[lq\]?\[^\\n\]*-64,\[^\\n\]*sp" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-14.c b/gcc/testsuite/gcc.target/i386/interrupt-14.c
new file mode 100644
index 0000000..7394207
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-14.c
@@ -0,0 +1,39 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-sse -mno-iamcu -mno-sse4 -mno-popcnt" } */
+
+extern int i, cnt;
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  cnt = __builtin_popcount (i);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 1 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-15.c b/gcc/testsuite/gcc.target/i386/interrupt-15.c
new file mode 100644
index 0000000..1bb9cde
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-15.c
@@ -0,0 +1,24 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mpush-args" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  bar (error);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 1 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-16.c b/gcc/testsuite/gcc.target/i386/interrupt-16.c
new file mode 100644
index 0000000..794a846
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-16.c
@@ -0,0 +1,27 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mno-push-args" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  bar (error);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%rbp" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%ebp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%ebp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 1 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-17.c b/gcc/testsuite/gcc.target/i386/interrupt-17.c
new file mode 100644
index 0000000..6596a68
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-17.c
@@ -0,0 +1,29 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mpush-args" } */
+
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame)
+{
+  bar (3);
+}
+
+void
+ __attribute__ ((interrupt))
+fn2 (void *frame)
+{
+  bar (3);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 2 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-18.c b/gcc/testsuite/gcc.target/i386/interrupt-18.c
new file mode 100644
index 0000000..7767aee
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-18.c
@@ -0,0 +1,31 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mpush-args" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  bar (error);
+}
+
+void
+ __attribute__ ((interrupt))
+fn2 (void *frame, uword_t error)
+{
+  bar (error);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 2 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-19.c b/gcc/testsuite/gcc.target/i386/interrupt-19.c
new file mode 100644
index 0000000..76ca077
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-19.c
@@ -0,0 +1,24 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mno-push-args" } */
+
+extern int foo (int) __attribute__ ((no_caller_saved_registers));
+extern int bar (int) __attribute__ ((no_caller_saved_registers));
+
+int
+foo (int i)
+{
+  return bar (i) + i;
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%ebp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%ebp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rbp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rbp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-2.c b/gcc/testsuite/gcc.target/i386/interrupt-2.c
new file mode 100644
index 0000000..2003fbb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-2.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wall -Wunused-parameter" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+}
+
+/* { dg-final { scan-assembler-not "add(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-20.c b/gcc/testsuite/gcc.target/i386/interrupt-20.c
new file mode 100644
index 0000000..c1b6d32
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-20.c
@@ -0,0 +1,22 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mno-push-args" } */
+
+extern int foo (int) __attribute__ ((no_caller_saved_registers));
+extern int bar (int) __attribute__ ((no_caller_saved_registers));
+
+int
+foo (int i)
+{
+  return bar (i + i);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "jmp" } }*/
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-21.c b/gcc/testsuite/gcc.target/i386/interrupt-21.c
new file mode 100644
index 0000000..2655d90
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-21.c
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx -mno-iamcu -msse" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movaps\[\\t \]*%xmm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movaps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%xmm3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-2\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-2\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[4-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[4-9\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm1\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm1\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-22.c b/gcc/testsuite/gcc.target/i386/interrupt-22.c
new file mode 100644
index 0000000..81cf47a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-22.c
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-2\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-2\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[4-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[4-9\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm1\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm1\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-23.c b/gcc/testsuite/gcc.target/i386/interrupt-23.c
new file mode 100644
index 0000000..c92c263
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-23.c
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mavx512f -mno-iamcu" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-2\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-2\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[4-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[4-9\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm1\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm1\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-24.c b/gcc/testsuite/gcc.target/i386/interrupt-24.c
new file mode 100644
index 0000000..e6b1b13
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-24.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-iamcu -mmpx" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "bnd3");
+}
+
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*%bnd3,\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%bnd3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-3.c b/gcc/testsuite/gcc.target/i386/interrupt-3.c
new file mode 100644
index 0000000..a87e594
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-3.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+void
+__attribute__((interrupt))
+fn (void* frame, uword_t error)
+{
+}
+
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-387-err.c b/gcc/testsuite/gcc.target/i386/interrupt-387-err.c
new file mode 100644
index 0000000..42c979e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-387-err.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -m80387 -mlong-double-80 -mno-iamcu" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+extern long double y, x;
+
+void
+fn0 (void)
+{
+  x = y;
+  y = 0;
+}
+
+void
+__attribute__((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  x = 0;  /* { dg-error "80387 instructions aren't allowed in exception service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn2 (void *frame)
+{
+  x = y; /* { dg-error "80387 instructions aren't allowed in interrupt service routine" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-4.c b/gcc/testsuite/gcc.target/i386/interrupt-4.c
new file mode 100644
index 0000000..94230cd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-4.c
@@ -0,0 +1,32 @@
+/* { dg-do link } */
+/* { dg-options "-O" } */
+
+#include <stdint.h>
+
+extern void link_error (void);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+struct interrupt_frame
+{
+  uword_t ip;
+  uword_t cs;
+  uword_t flags;
+  uword_t sp;
+  uword_t ss;
+};
+
+__attribute__ ((used, interrupt))
+void
+foo (struct interrupt_frame *frame)
+{
+  void *ra = __builtin_return_address (0);
+  if ((uintptr_t) ra != (uintptr_t) frame->ip)
+    link_error ();
+}
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-5.c b/gcc/testsuite/gcc.target/i386/interrupt-5.c
new file mode 100644
index 0000000..53d6656
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-5.c
@@ -0,0 +1,23 @@
+/* { dg-do link } */
+/* { dg-options "-O" } */
+
+#include <stdint.h>
+
+extern void link_error (void);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+__attribute__ ((used, interrupt))
+void
+foo (void *frame, uword_t error)
+{
+  void *ra = __builtin_return_address (0);
+  if ((uintptr_t) ra != (uintptr_t) error)
+    link_error ();
+}
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-6.c b/gcc/testsuite/gcc.target/i386/interrupt-6.c
new file mode 100644
index 0000000..a1a3d0e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-6.c
@@ -0,0 +1,40 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+extern int error;
+
+__attribute__((interrupt))
+void
+fn1 (void *p, short error_code)
+{ /* { dg-error "interrupt service routine should have unsigned \(long long |long |\)int as the second argument" } */
+}
+
+__attribute__((interrupt))
+void
+fn2 (void)
+{ /* { dg-error "interrupt service routine can only have a pointer argument and an optional integer argument" } */
+}
+
+__attribute__((interrupt))
+void
+fn3 (uword_t error_code)
+{ /* { dg-error "interrupt service routine should have a pointer as the first argument" } */
+  error = error_code;
+}
+
+__attribute__((interrupt))
+void
+fn4 (uword_t error_code, void *frame)
+{ /* { dg-error "interrupt service routine should have .* the .* argument" } */
+  error = error_code;
+}
+
+extern int fn5 (void *) __attribute__ ((interrupt)); /* { dg-error "interrupt service routine can't have non-void return value" } */
+
+int
+fn5 (void *frame)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-7.c b/gcc/testsuite/gcc.target/i386/interrupt-7.c
new file mode 100644
index 0000000..e7441a2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-7.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int error;
+
+extern void fn (void *) __attribute__((interrupt));
+
+void
+foo (void)
+{
+  fn (&error); /* { dg-error "interrupt service routine can't be called directly" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-8.c b/gcc/testsuite/gcc.target/i386/interrupt-8.c
new file mode 100644
index 0000000..0a83473
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-8.c
@@ -0,0 +1,20 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 16 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm\[0-9\]+" 16 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-9.c b/gcc/testsuite/gcc.target/i386/interrupt-9.c
new file mode 100644
index 0000000..9d0c776
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-9.c
@@ -0,0 +1,22 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512bw -mno-iamcu -mavx512f" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm\[0-9\]+" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "kmovw\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" 8 } } */
+/* { dg-final { scan-assembler-times "kmovw\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" 8 } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-bnd.c b/gcc/testsuite/gcc.target/i386/interrupt-bnd.c
new file mode 100644
index 0000000..9f50661
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-bnd.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mno-iamcu -mmpx" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "bnd3");
+}
+
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*%bnd3,\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%bnd3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
new file mode 100644
index 0000000..77a9463
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
@@ -0,0 +1,30 @@
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -miamcu" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern int bar (int);
+
+void foo (void *frame)
+{
+  int a,b,c,d,e,f,i;
+  a = bar (5);
+  b = bar (a);
+  c = bar (b);
+  d = bar (c);
+  e = bar (d);
+  f = bar (e);
+  for (i = 1; i < 10; i++)
+    a += bar (a + i) + bar (b + i) +
+	 bar (c + i) + bar (d + i) +
+	 bar (e + i) + bar (f+i);
+}
+
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%eax" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%ecx" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%edx" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%eax" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%ecx" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%edx" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c
new file mode 100644
index 0000000..fc4f367
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mmmx -mno-iamcu" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+typedef short __v4hi __attribute__ ((__vector_size__ (8)));
+typedef int __m64 __attribute__ ((__vector_size__ (8), __may_alias__));
+
+extern __m64 y, x;
+
+void
+fn0 (void)
+{
+  x = __extension__ (__m64){ 0 };
+  x = (__m64) __builtin_ia32_packsswb ((__v4hi) x, (__v4hi) y);
+  y = x;
+}
+
+void
+__attribute__((interrupt))
+fn1 (void *frame)
+{
+  x = (__m64) __builtin_ia32_packsswb ((__v4hi) x, (__v4hi) y); /* { dg-error "MMX/3Dnow (instructions|intrinsics) aren't allowed in interrupt service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn2 (void *frame)
+{
+  x = y; /* { dg-error "MMX/3Dnow instructions aren't allowed in interrupt service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn3 (void *frame, uword_t error)
+{
+  x = __extension__ (__m64){ 0 }; /* { dg-error "MMX/3Dnow instructions aren't allowed in exception service routine" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c b/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c
new file mode 100644
index 0000000..796b81b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mred-zone" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  /* No need to adjust stack if less than 128 bytes are used on stack
+     with a 128-byte red zone.  */
+  char array[112];
+  asm ("# %0 "
+       :
+       : "r" (array));
+}
+
+/* { dg-final { scan-assembler-not "(sub|add)(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c b/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c
new file mode 100644
index 0000000..c6a2822
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mred-zone" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  /* Need to adjust stack if more than 128 bytes are used on stack
+     with a 128-byte red zone.  */
+  char array[113];
+  asm ("# %0 "
+       :
+       : "r" (array));
+}
+
+/* { dg-final { scan-assembler-times "(?:sub|add)(?:l|q)\[\\t \]*\\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c b/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c
new file mode 100644
index 0000000..e222acf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O3" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern void bar (void);
+
+void foo (void *frame)
+{
+  bar ();
+}
+/* { dg-final { scan-assembler-not "jmp" } }*/
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c
new file mode 100644
index 0000000..b1b0abe
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern void bar (int);
+
+void f1 (void){  bar (1); }
+__attribute__((interrupt))
+void f2 (void *frame){  bar (2); }
+void f3 (void){  bar (3); }
+__attribute__((interrupt))
+void f4 (void *frame){  bar (4); }
+void f5 (void){  bar (5); }
+
+/* { dg-final { scan-assembler-times "push.\t%.ax" 2 } } */
+/* { dg-final { scan-assembler-times "pop.\t%.ax" 2 } } */
+/* { dg-final { scan-assembler-times "iret" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-xmm.c b/gcc/testsuite/gcc.target/i386/interrupt-xmm.c
new file mode 100644
index 0000000..0eafb10
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-xmm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx -mno-iamcu -msse" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%xmm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%xmm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-ymm.c b/gcc/testsuite/gcc.target/i386/interrupt-ymm.c
new file mode 100644
index 0000000..acb5fc7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-ymm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*%ymm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-zmm.c b/gcc/testsuite/gcc.target/i386/interrupt-zmm.c
new file mode 100644
index 0000000..6d23b5d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-zmm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mavx512f -mno-iamcu" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*%zmm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-10-02 17:48                                   ` Yulia Koval
@ 2015-10-04  5:24                                     ` Yulia Koval
  2015-10-04 10:29                                       ` Uros Bizjak
  0 siblings, 1 reply; 45+ messages in thread
From: Yulia Koval @ 2015-10-04  5:24 UTC (permalink / raw)
  To: Uros Bizjak; +Cc: H.J. Lu, Mike Stump, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 9711 bytes --]

Hi,

Here is the last version of the patch. Regtested/bootstraped for
Linux/i686 and Linux/x86_64.

Date: Fri, 4 Sep 2015 08:53:23 -0700
Subject: [PATCH] Implement x86 interrupt attribute

The interrupt and exception handlers are called by x86 processors.  X86
hardware pushes information onto stack and calls the handler.  The
requirements are

1. Both interrupt and exception handlers must use the 'IRET' instruction,
instead of the 'RET' instruction, to return from the handlers.
2. All registers are callee-saved in interrupt and exception handlers.
3. The difference between interrupt and exception handlers is the
exception handler must pop 'ERROR_CODE' off the stack before the 'IRET'
instruction.

The design goals of interrupt and exception handlers for x86 processors
are:

1. Support both 32-bit and 64-bit modes.
2. Flexible for compilers to optimize.
3. Easy to use by programmers.

To implement interrupt and exception handlers for x86 processors, a
compiler should support:

'interrupt' attribute

Use this attribute to indicate that the specified function with
mandatory arguments is an interrupt or exception handler.  The compiler
generates function entry and exit sequences suitable for use in an
interrupt handler when this attribute is present.  The 'IRET' instruction,
instead of the 'RET' instruction, is used to return from interrupt or
exception handlers.  All registers, except for the EFLAGS register which
is restored by the 'IRET' instruction, are preserved by the compiler.

Any interruptible-without-stack-switch code must be compiled with
-mno-red-zone since interrupt handlers can and will, because of the
hardware design, touch the red zone.

1. interrupt handler must be declared with a mandatory pointer argument:

struct interrupt_frame;

__attribute__ ((interrupt))
void
f (struct interrupt_frame *frame)
{
...
}

and user must properly define the structure the pointer pointing to.

2. exception handler:

The exception handler is very similar to the interrupt handler with
a different mandatory function signature:

typedef unsigned long long int uword_t;
typedef unsigned int uword_t;

struct interrupt_frame;

__attribute__ ((interrupt))
void
f (struct interrupt_frame *frame, uword_t error_code)
{
...
}

and compiler pops the error code off stack before the 'IRET' instruction.

The exception handler should only be used for exceptions which push an
error code and all other exceptions must use the interrupt handler.
The system will crash if the wrong handler is used.

To be feature complete, compiler may implement the optional
'no_caller_saved_registers' attribute:

Use this attribute to indicate that the specified function has no
caller-saved registers.  That is, all registers are callee-saved.
The compiler generates proper function entry and exit sequences to
save and restore any modified registers.

The user can call functions specified with 'no_caller_saved_registers'
attribute from an interrupt handler without saving and restoring all
call clobbered registers.

PR target/66960
PR target/67630
PR target/67634
* config/i386/i386-protos.h (ix86_epilogue_uses): New prototype.
* config/i386/i386.c (ix86_frame): Add nbndregs and nmaskregs.
(ix86_conditional_register_usage): Preserve all registers if
there are no caller-saved registers.
(ix86_set_current_function): Set no_caller_saved_registers and
func_type.  Mark arguments in interrupt handler as used.
(ix86_function_ok_for_sibcall): Return false if there are no
caller-saved registers.
(ix86_maybe_switch_abi): Call reinit_regs if AX register usage
isn't consistent.
(type_natural_mode): Don't warn ABI change for MMX in interrupt
handler.
(ix86_function_arg_advance): Skip for callee in interrupt
handler.
(ix86_function_arg): Handle arguments for callee in interrupt
handler.
(ix86_can_use_return_insn_p): Don't use `ret' instruction in
interrupt handler.
(ix86_hard_regno_scratch_ok): New function.
(ix86_epilogue_uses): Likewise.
(ix86_reg_ever_defined_p): Likewise.
(ix86_nsaved_bndregs): Likewise.
(ix86_nsaved_maskregs): Likewise.
(ix86_reg_save_area_size): Likewise.
(ix86_handle_no_caller_saved_registers_attribute): Likewise.
(ix86_handle_interrupt_attribute): Likewise.
(ix86_save_reg): Preserve all registers if there are no
caller-saved registers after reload.
(ix86_nsaved_sseregs): Don't return 0 if there are no
caller-saved registers.
(ix86_compute_frame_layout): Set nbndregs and nmaskregs.  Set
save_regs_using_mov to true to save bound and mask registers.
Call ix86_reg_save_area_size to get register save area size.
Allocate space to save full vector registers if there are
no caller-saved registers.
(ix86_emit_save_reg_using_mov): Set alignment to word_mode
alignment when saving full vector registers if there are no
caller-saved registers.
(ix86_emit_save_regs_using_mov): Use regno_reg_rtx to get
register size.
(ix86_emit_restore_regs_using_mov): Likewise.
(ix86_emit_save_sse_regs_using_mov): Save full vector registers
if there are no caller-saved registers.
(ix86_emit_restore_sse_regs_using_mov): Restore full vector
registers if there are no caller-saved registers.
(ix86_expand_epilogue): Use move to restore bound registers.
Generate interrupt return for interrupt handler and pop the
'ERROR_CODE' off the stack before interrupt return in exception
handler.
(ix86_expand_move): Disallow 80387 instructions in exception
handler.
(ix86_expand_vector_move): Disallow MMX/3Dnow instructions in
exception handler.
(ix86_expand_call): Disallow calling interrupt handler directly.
If there are no caller-saved registers, mark all registers that
are clobbered by the call as clobbered.
(ix86_attribute_table): Add interrupt and no_caller_saved_registers
attributes.
(TARGET_HARD_REGNO_SCRATCH_OK): New.
* config/i386/i386.h (EPILOGUE_USES): New.
(function_type): New enum.
* config/i386/i386.h (machine_function): Add func_type and
no_caller_saved_registers.
* config/i386/i386.md (UNSPEC_INTERRUPT_RETURN): New.
(interrupt_return): New pattern.
* config/i386/sse.md (*mov<mode>_internal): Handle misaligned
SSE load and store if there are no caller-saved registers.
* doc/extend.texi: Document x86 interrupt and
no_caller_saved_registers attributes.

gcc/testsuite/

PR target/66960
PR target/67630
PR target/67634
* gcc.target/i386/interrupt-1.c: New test.
* gcc.target/i386/interrupt-2.c: Likewise.
* gcc.target/i386/interrupt-3.c: Likewise.
* gcc.target/i386/interrupt-4.c: Likewise.
* gcc.target/i386/interrupt-5.c: Likewise.
* gcc.target/i386/interrupt-6.c: Likewise.
* gcc.target/i386/interrupt-7.c: Likewise.
* gcc.target/i386/interrupt-8.c: Likewise.
* gcc.target/i386/interrupt-9.c: Likewise.
* gcc.target/i386/interrupt-10.c: Likewise.
* gcc.target/i386/interrupt-11.c: Likewise.
* gcc.target/i386/interrupt-12.c: Likewise.
* gcc.target/i386/interrupt-13.c: Likewise.
* gcc.target/i386/interrupt-14.c: Likewise.
* gcc.target/i386/interrupt-15.c: Likewise.
* gcc.target/i386/interrupt-16.c: Likewise.
* gcc.target/i386/interrupt-17.c: Likewise.
* gcc.target/i386/interrupt-18.c: Likewise.
* gcc.target/i386/interrupt-19.c: Likewise.
* gcc.target/i386/interrupt-20.c: Likewise.
* gcc.target/i386/interrupt-21.c: Likewise.
* gcc.target/i386/interrupt-22.c: Likewise.
* gcc.target/i386/interrupt-23.c: Likewise.
* gcc.target/i386/interrupt-24.c: Likewise.
* gcc.target/i386/interrupt-25.c: Likewise.
* gcc.target/i386/interrupt-387-err.c: Likewise.
* gcc.target/i386/interrupt-bnd.c: Likewise.
* gcc.target/i386/interrupt-iamcu.c: Likewise.
* gcc.target/i386/interrupt-mmx-err.c: Likewise.
* gcc.target/i386/interrupt-redzone-1.c: Likewise.
* gcc.target/i386/interrupt-redzone-2.c: Likewise.
* gcc.target/i386/interrupt-sibcall.c: Likewise.
* gcc.target/i386/interrupt-switch-abi.c: Likewise.
* gcc.target/i386/interrupt-xmm.c: Likewise.
* gcc.target/i386/interrupt-ymm.c: Likewise.
* gcc.target/i386/interrupt-zmm.c: Likewise.

On Fri, Oct 2, 2015 at 8:48 PM, Yulia Koval <vaalfreja@gmail.com> wrote:
> Fixed it. Thanks.
>
> On Fri, Oct 2, 2015 at 6:45 PM, Uros Bizjak <ubizjak@gmail.com> wrote:
>> On Fri, Oct 2, 2015 at 2:51 PM, Yulia Koval <vaalfreja@gmail.com> wrote:
>>> Hi,
>>> Here is a new patch. Added HJ's changes and review changes.
>>>
>>> Implement x86 interrupt attribute
>>
>> +      incoming_stack_boundary
>> + = (crtl->parm_stack_boundary > ix86_incoming_stack_boundary
>> +   ? crtl->parm_stack_boundary : ix86_incoming_stack_boundary);
>>
>> MAX (crtl->parm_stack_boundary, ix86_incoming_stack_boundary);
>>
>> +bool
>> +ix86_epilogue_uses (int regno)
>> +{
>> +  /* If there are no caller-saved registers, we preserve all registers,
>> +     except for MMX and x87 registers which aren't supported when saving
>> +     and restoring registers.  Don't explicitly save SP register since
>> +     it is always preserved.  */
>> +  return (cfun->machine->no_caller_saved_registers
>> +  && reg_names[regno][0]
>> +  && !fixed_regs[regno]
>> +  && !STACK_REGNO_P (regno)
>> +  && !MMX_REGNO_P (regno));
>>
>> There is still a redundant check for reg_names with !fixed_regs. When
>> member of fixed_regs is 0, we are sure that corresponding reg_names is
>> non-null. As can be seen in ix86_conditional_register_usage, register
>> names are squashed depending on target for non-existent registers in
>> order to prevent their usage in "asm" statements.
>>
>> +      /* If there are no caller-saved registers, add all registers
>> + that are clobbered by the call.  */
>> +      for (int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
>> + if (reg_names[i][0]
>> +    && !fixed_regs[i]
>> +    && (ix86_call_used_regs[i] == 1
>> + || (ix86_call_used_regs[i] & c_mask))
>> +    && !STACK_REGNO_P (i)
>> +    && !MMX_REGNO_P (i))
>>
>> And here.
>>
>> Uros.

[-- Attachment #2: patch_04.10 --]
[-- Type: application/octet-stream, Size: 76172 bytes --]

diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index 6a17ef4..c7ff393 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -278,6 +278,8 @@ extern rtx maybe_get_pool_constant (rtx);
 extern char internal_label_prefix[16];
 extern int internal_label_prefix_len;
 
+extern bool ix86_epilogue_uses (int);
+
 enum ix86_address_seg { SEG_DEFAULT, SEG_FS, SEG_GS };
 struct ix86_address
 {
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index f5c9657..0aab211 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -2381,6 +2381,8 @@ struct ix86_frame
 {
   int nsseregs;
   int nregs;
+  int nbndregs;
+  int nmaskregs;
   int va_arg_size;
   int red_zone_size;
   int outgoing_arguments_size;
@@ -5464,6 +5466,13 @@ ix86_conditional_register_usage (void)
 {
   int i, c_mask;
 
+  /* If there are no caller-saved registers, preserve all registers.
+     except fixed_regs.  */
+  if (cfun && cfun->machine->no_caller_saved_registers)
+    for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+      if (!fixed_regs[i])
+	call_used_regs[i] = 0;
+
   /* For 32-bit targets, squash the REX registers.  */
   if (! TARGET_64BIT)
     {
@@ -6245,6 +6254,27 @@ ix86_set_current_function (tree fndecl)
       return;
     }
 
+  if (lookup_attribute ("interrupt", DECL_ATTRIBUTES (fndecl)))
+    {
+      int nargs = 0;
+      for (tree arg = DECL_ARGUMENTS (fndecl);
+	   arg;
+	   arg = TREE_CHAIN (arg))
+	{
+	  /* Mark arguments in interrupt handler as used to silence
+	     compiler warning since arguments in interrupt handler
+	     are mandatory even if they aren't used.  */
+	  TREE_USED (arg) = 1;
+	  nargs++;
+	}
+      cfun->machine->no_caller_saved_registers = true;
+      cfun->machine->func_type
+	= nargs == 2 ? TYPE_EXCEPTION : TYPE_INTERRUPT;
+    }
+  else if (lookup_attribute ("no_caller_saved_registers",
+			     DECL_ATTRIBUTES (fndecl)))
+    cfun->machine->no_caller_saved_registers = true;
+
   tree new_tree = DECL_FUNCTION_SPECIFIC_TARGET (fndecl);
   if (new_tree == NULL_TREE)
     new_tree = target_option_default_node;
@@ -6523,6 +6553,10 @@ ix86_function_ok_for_sibcall (tree decl, tree exp)
   tree type, decl_or_type;
   rtx a, b;
 
+  /* Sibling call isn't OK if there are no caller-saved registers.  */
+  if (cfun->machine->no_caller_saved_registers)
+    return false;
+
   /* If we are generating position-independent code, we cannot sibcall
      optimize direct calls to global functions, as the PLT requires
      %ebx be live. (Darwin does not have a PLT.)  */
@@ -7401,8 +7435,17 @@ ix86_call_abi_override (const_tree fndecl)
 static void
 ix86_maybe_switch_abi (void)
 {
-  if (TARGET_64BIT &&
-      call_used_regs[SI_REG] == (cfun->machine->call_abi == MS_ABI))
+  if (TARGET_64BIT
+	&& (call_used_regs[SI_REG]
+	  == (cfun->machine->call_abi == MS_ABI)))
+    {
+      reinit_regs ();
+      return;
+    }
+  /* AX register is only preserved if there are no caller-saved
+     registers.  */
+  if (cfun->machine->no_caller_saved_registers
+       != call_used_regs[AX_REG])
     reinit_regs ();
 }
 
@@ -7707,6 +7750,8 @@ type_natural_mode (const_tree type, const CUMULATIVE_ARGS *cum,
 		      }
 		  }
 		else if ((size == 8 && !TARGET_64BIT)
+			 && (!cfun
+			     || cfun->machine->func_type == TYPE_NORMAL)
 			 && !TARGET_MMX
 			 && !TARGET_IAMCU)
 		  {
@@ -8686,6 +8731,11 @@ ix86_function_arg_advance (cumulative_args_t cum_v, machine_mode mode,
   HOST_WIDE_INT bytes, words;
   int nregs;
 
+  /* The argument of interrupt handler is a special case and is
+     handled in ix86_function_arg.  */
+  if (!cum->caller && cfun->machine->func_type != TYPE_NORMAL)
+    return;
+
   if (mode == BLKmode)
     bytes = int_size_in_bytes (type);
   else
@@ -9002,6 +9052,39 @@ ix86_function_arg (cumulative_args_t cum_v, machine_mode omode,
   HOST_WIDE_INT bytes, words;
   rtx arg;
 
+  if (!cum->caller && cfun->machine->func_type != TYPE_NORMAL)
+    {
+      /* The first argument of interrupt handler is a pointer and
+	 points to the return address on stack.  The optional second
+	 argument is an integer for error code on stack.  */
+      gcc_assert (type != NULL_TREE);
+      if (POINTER_TYPE_P (type))
+	{
+	  if (cfun->machine->func_type == TYPE_EXCEPTION)
+	    /* (AP) in the current frame in exception handler.  */
+	    arg = arg_pointer_rtx;
+	  else
+	    /* -WORD(AP) in the current frame in interrupt handler.  */
+	    arg = plus_constant (Pmode, arg_pointer_rtx,
+				 -UNITS_PER_WORD);
+	  if (mode != Pmode)
+	    arg = convert_to_mode (mode, arg, 1);
+	}
+      else
+	{
+	  gcc_assert (TREE_CODE (type) == INTEGER_TYPE
+		      && cfun->machine->func_type == TYPE_EXCEPTION
+		      && mode == word_mode);
+	  /* The error code is at -WORD(AP) in the current frame in
+	     exception handler.  */
+	  arg = gen_rtx_MEM (word_mode,
+			     plus_constant (Pmode, arg_pointer_rtx,
+					    -UNITS_PER_WORD));
+	}
+
+      return arg;
+    }
+
   /* All pointer bounds arguments are handled separately here.  */
   if ((type && POINTER_BOUNDS_TYPE_P (type))
       || POINTER_BOUNDS_MODE_P (mode))
@@ -10717,7 +10800,10 @@ ix86_can_use_return_insn_p (void)
 {
   struct ix86_frame frame;
 
-  if (! reload_completed || frame_pointer_needed)
+  /* Don't use `ret' instruction in interrupt handler.  */
+  if (! reload_completed
+      || frame_pointer_needed
+      || cfun->machine->func_type != TYPE_NORMAL)
     return 0;
 
   /* Don't allow more than 32k pop, since that's all we can do
@@ -11027,11 +11113,65 @@ ix86_select_alt_pic_regnum (void)
   return INVALID_REGNUM;
 }
 
+/* Return nonzero if register REGNO can be used as a scratch register
+   in peephole2.  */
+
+bool
+ix86_hard_regno_scratch_ok (unsigned int regno ATTRIBUTE_UNUSED)
+{
+  /* No scratch registers if there are no caller-saved registers.  */
+  return !cfun->machine->no_caller_saved_registers;
+}
+
+/* Return true if REGNO is used by the epilogue.  */
+
+bool
+ix86_epilogue_uses (int regno)
+{
+  /* If there are no caller-saved registers, we preserve all registers,
+     except for MMX and x87 registers which aren't supported when saving
+     and restoring registers.  Don't explicitly save SP register since
+     it is always preserved.  */
+  return (cfun->machine->no_caller_saved_registers
+	  && !fixed_regs[regno]
+	  && !STACK_REGNO_P (regno)
+	  && !MMX_REGNO_P (regno));
+}
+
+/* Return TRUE if register REGNO is ever defined..  */
+
+static bool
+ix86_reg_ever_defined_p (unsigned int regno)
+{
+  df_ref def;
+
+  for (def = DF_REG_DEF_CHAIN (regno); def; def = DF_REF_NEXT_REG (def))
+    if (!DF_REF_IS_ARTIFICIAL (def))
+      return true;
+
+  return false;
+}
+
 /* Return TRUE if we need to save REGNO.  */
 
 static bool
 ix86_save_reg (unsigned int regno, bool maybe_eh_return)
 {
+  /* If there are no caller-saved registers, we preserve all registers,
+     except for MMX and x87 registers which aren't supported when saving
+     and restoring registers.  Don't explicitly save SP register since
+     it is always preserved.  */
+  if (cfun->machine->no_caller_saved_registers && reload_completed)
+    return (df_regs_ever_live_p (regno)
+	    && ix86_reg_ever_defined_p (regno)
+	    && !fixed_regs[regno]
+	    && (!crtl->return_rtx
+		|| !refers_to_regno_p (regno, crtl->return_rtx))
+	    && (!crtl->return_bnd
+		|| !refers_to_regno_p (regno, crtl->return_bnd))
+	    && !STACK_REGNO_P (regno)
+	    && !MMX_REGNO_P (regno));
+
   if (regno == REAL_PIC_OFFSET_TABLE_REGNUM
       && pic_offset_table_rtx)
     {
@@ -11088,6 +11228,34 @@ ix86_nsaved_regs (void)
   return nregs;
 }
 
+/* Return number of saved bound registers.  */
+
+static int
+ix86_nsaved_bndregs (void)
+{
+  int nregs = 0;
+  int regno;
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (BND_REGNO_P (regno) && ix86_save_reg (regno, true))
+      nregs ++;
+  return nregs;
+}
+
+/* Return number of saved mask registers.  */
+
+static int
+ix86_nsaved_maskregs (void)
+{
+  int nregs = 0;
+  int regno;
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (MASK_REGNO_P (regno) && ix86_save_reg (regno, true))
+      nregs ++;
+  return nregs;
+}
+
 /* Return number of saved SSE registers.  */
 
 static int
@@ -11096,7 +11264,10 @@ ix86_nsaved_sseregs (void)
   int nregs = 0;
   int regno;
 
-  if (!TARGET_64BIT_MS_ABI)
+  /* Always need to save SSE registers if there are no caller-saved
+     registers.  */
+  if (!TARGET_64BIT_MS_ABI
+      && !cfun->machine->no_caller_saved_registers)
     return 0;
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
@@ -11165,6 +11336,18 @@ ix86_builtin_setjmp_frame_value (void)
 
 #define SPLIT_STACK_AVAILABLE 256
 
+/* Register save area size.  */
+
+static unsigned int
+ix86_reg_save_area_size (struct ix86_frame *frame)
+{
+  unsigned int size = ((frame->nregs - frame->nbndregs
+			- frame->nmaskregs) * UNITS_PER_WORD);
+  size += frame->nbndregs * (ix86_pmode == PMODE_DI ? 16 : 8);
+  size += frame->nmaskregs * (TARGET_AVX512BW ? 8 : 2);
+  return size;
+}
+
 /* Fill structure ix86_frame about frame of currently computed function.  */
 
 static void
@@ -11177,6 +11360,8 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   HOST_WIDE_INT to_allocate;
 
   frame->nregs = ix86_nsaved_regs ();
+  frame->nbndregs = ix86_nsaved_bndregs ();
+  frame->nmaskregs = ix86_nsaved_maskregs ();
   frame->nsseregs = ix86_nsaved_sseregs ();
 
   /* 64-bit MS ABI seem to require stack alignment to be always 16 except for
@@ -11248,11 +11433,16 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
 	   = !expensive_function_p (count);
     }
 
+  /* We must use move to save bound and mask registers.  */
   frame->save_regs_using_mov
-    = (TARGET_PROLOGUE_USING_MOVE && cfun->machine->use_fast_prologue_epilogue
-       /* If static stack checking is enabled and done with probes,
-	  the registers need to be saved before allocating the frame.  */
-       && flag_stack_check != STATIC_BUILTIN_STACK_CHECK);
+    = (frame->nbndregs > 0
+       || frame->nmaskregs > 0
+       || (TARGET_PROLOGUE_USING_MOVE
+	   && cfun->machine->use_fast_prologue_epilogue
+	   /* If static stack checking is enabled and done with probes,
+	      the registers need to be saved before allocating the
+	      frame.  */
+	   && flag_stack_check != STATIC_BUILTIN_STACK_CHECK));
 
   /* Skip return address.  */
   offset = UNITS_PER_WORD;
@@ -11270,7 +11460,7 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   frame->hard_frame_pointer_offset = offset;
 
   /* Register save area */
-  offset += frame->nregs * UNITS_PER_WORD;
+  offset += ix86_reg_save_area_size (frame);
   frame->reg_save_offset = offset;
 
   /* On SEH target, registers are pushed just before the frame pointer
@@ -11284,9 +11474,29 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
       /* The only ABI that has saved SSE registers (Win64) also has a
          16-byte aligned default stack, and thus we don't need to be
 	 within the re-aligned local stack frame to save them.  */
-      gcc_assert (INCOMING_STACK_BOUNDARY >= 128);
-      offset = ROUND_UP (offset, 16);
-      offset += frame->nsseregs * 16;
+      unsigned int incoming_stack_boundary
+	= MAX (crtl->parm_stack_boundary, ix86_incoming_stack_boundary);
+      unsigned int vec_regsize;
+
+      /* We must save full vector registers if there are no
+	 caller-saved registers.  */
+      if (cfun->machine->no_caller_saved_registers)
+	vec_regsize = (TARGET_AVX512F ? 64 : (TARGET_AVX ? 32 : 16));
+      else
+	vec_regsize = 16;
+
+      if (cfun->machine->func_type == TYPE_NORMAL)
+	{
+	  /* Incoming stack may be 32-bit aligned in 32-bit mode.  */
+	  gcc_assert (!TARGET_64BIT || incoming_stack_boundary >= 128);
+	  /* Don't over-align vector register save area.  */
+	  incoming_stack_boundary /= BITS_PER_UNIT;
+	  if (incoming_stack_boundary > vec_regsize)
+	    incoming_stack_boundary = vec_regsize;
+	  offset = ROUND_UP (offset, incoming_stack_boundary);
+	}
+
+      offset += frame->nsseregs * vec_regsize;
     }
   frame->sse_reg_save_offset = offset;
 
@@ -11342,8 +11552,12 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   /* Size prologue needs to allocate.  */
   to_allocate = offset - frame->sse_reg_save_offset;
 
-  if ((!to_allocate && frame->nregs <= 1)
-      || (TARGET_64BIT && to_allocate >= (HOST_WIDE_INT) 0x80000000))
+  /* We must use move to save bound and mask registers.  */
+  if (frame->nbndregs == 0
+      && frame->nmaskregs == 0
+      && ((!to_allocate && frame->nregs <= 1)
+	   || (TARGET_64BIT
+	       && to_allocate >= (HOST_WIDE_INT) 0x80000000)))
     frame->save_regs_using_mov = false;
 
   if (ix86_using_red_zone ()
@@ -11353,7 +11567,7 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
     {
       frame->red_zone_size = to_allocate;
       if (frame->save_regs_using_mov)
-	frame->red_zone_size += frame->nregs * UNITS_PER_WORD;
+	frame->red_zone_size += ix86_reg_save_area_size (frame);
       if (frame->red_zone_size > RED_ZONE_SIZE - RED_ZONE_RESERVE)
 	frame->red_zone_size = RED_ZONE_SIZE - RED_ZONE_RESERVE;
     }
@@ -11513,8 +11727,28 @@ ix86_emit_save_reg_using_mov (machine_mode mode, unsigned int regno,
   addr = choose_baseaddr (cfa_offset);
   mem = gen_frame_mem (mode, addr);
 
-  /* For SSE saves, we need to indicate the 128-bit alignment.  */
-  set_mem_align (mem, GET_MODE_ALIGNMENT (mode));
+  /* For SSE saves, we need to indicate the 128-bit alignment.  In
+     interrupt handler, stack is aligned to word_mode.  We can't use
+     gen_sse_storeups since RTX_FRAME_RELATED_P is set and
+     dwarf2out_flush_queued_reg_saves doesn't like UNSPEC_STOREU.
+     Also gen_sse_storeups doesn't cover AVX nor AVX512.  */
+  unsigned int incoming_stack_boundary;
+  if (cfun->machine->func_type == TYPE_NORMAL)
+    {
+      incoming_stack_boundary
+	= MAX (crtl->parm_stack_boundary, ix86_incoming_stack_boundary);
+      if (!TARGET_64BIT || incoming_stack_boundary >= 128)
+	{
+	  /* Don't over-align vector register save slot.  */
+	  if (incoming_stack_boundary > GET_MODE_ALIGNMENT (mode))
+	    incoming_stack_boundary = GET_MODE_ALIGNMENT (mode);
+	}
+      else
+	gcc_unreachable ();
+    }
+  else
+    incoming_stack_boundary = GET_MODE_ALIGNMENT (word_mode);
+  set_mem_align (mem, incoming_stack_boundary);
 
   insn = emit_move_insn (mem, reg);
   RTX_FRAME_RELATED_P (insn) = 1;
@@ -11575,8 +11809,9 @@ ix86_emit_save_regs_using_mov (HOST_WIDE_INT cfa_offset)
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (!SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
       {
-        ix86_emit_save_reg_using_mov (word_mode, regno, cfa_offset);
-	cfa_offset -= UNITS_PER_WORD;
+	enum machine_mode reg_mode = GET_MODE (regno_reg_rtx[regno]);
+	ix86_emit_save_reg_using_mov (reg_mode, regno, cfa_offset);
+	cfa_offset -= GET_MODE_SIZE (reg_mode);
       }
 }
 
@@ -11586,12 +11821,27 @@ static void
 ix86_emit_save_sse_regs_using_mov (HOST_WIDE_INT cfa_offset)
 {
   unsigned int regno;
+  enum machine_mode vector_reg_mode;
+
+  if (cfun->machine->no_caller_saved_registers)
+    {
+      /* We must save full vector registers if there are no
+	 caller-saved registers.  */
+      if (TARGET_AVX512F)
+	vector_reg_mode = V16SFmode;
+      else if (TARGET_AVX)
+	vector_reg_mode = V8SFmode;
+      else
+	vector_reg_mode = V4SFmode;
+    }
+  else
+    vector_reg_mode = V4SFmode;
 
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
       {
-	ix86_emit_save_reg_using_mov (V4SFmode, regno, cfa_offset);
-	cfa_offset -= 16;
+	ix86_emit_save_reg_using_mov (vector_reg_mode, regno, cfa_offset);
+	cfa_offset -= GET_MODE_SIZE (vector_reg_mode);
       }
 }
 
@@ -13036,12 +13286,13 @@ ix86_emit_restore_regs_using_mov (HOST_WIDE_INT cfa_offset,
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (!SSE_REGNO_P (regno) && ix86_save_reg (regno, maybe_eh_return))
       {
-	rtx reg = gen_rtx_REG (word_mode, regno);
+	enum machine_mode reg_mode = GET_MODE (regno_reg_rtx[regno]);
+	rtx reg = gen_rtx_REG (reg_mode, regno);
 	rtx mem;
 	rtx_insn *insn;
 
 	mem = choose_baseaddr (cfa_offset);
-	mem = gen_frame_mem (word_mode, mem);
+	mem = gen_frame_mem (reg_mode, mem);
 	insn = emit_move_insn (reg, mem);
 
         if (m->fs.cfa_reg == crtl->drap_reg && regno == REGNO (crtl->drap_reg))
@@ -13060,7 +13311,7 @@ ix86_emit_restore_regs_using_mov (HOST_WIDE_INT cfa_offset,
 	else
 	  ix86_add_cfa_restore_note (NULL, reg, cfa_offset);
 
-	cfa_offset -= UNITS_PER_WORD;
+	cfa_offset -= GET_MODE_SIZE (reg_mode);
       }
 }
 
@@ -13071,21 +13322,56 @@ ix86_emit_restore_sse_regs_using_mov (HOST_WIDE_INT cfa_offset,
 				      bool maybe_eh_return)
 {
   unsigned int regno;
+  enum machine_mode vector_reg_mode;
+
+  if (cfun->machine->no_caller_saved_registers)
+    {
+      /* We must restore full vector registers if there are no
+	 caller-saved registers.  */
+      if (TARGET_AVX512F)
+	vector_reg_mode = V16SFmode;
+      else if (TARGET_AVX)
+	vector_reg_mode = V8SFmode;
+      else
+	vector_reg_mode = V4SFmode;
+    }
+  else
+    vector_reg_mode = V4SFmode;
+
+  /* In interrupt handler, stack is aligned to word_mode.  */
+  unsigned int incoming_stack_boundary;
+  if (cfun->machine->func_type == TYPE_NORMAL)
+    {
+      incoming_stack_boundary
+	= MAX (crtl->parm_stack_boundary, ix86_incoming_stack_boundary);
+      if (!TARGET_64BIT || incoming_stack_boundary >= 128)
+	{
+	  /* Don't over-align vector register save slot.  */
+	  if (incoming_stack_boundary
+	      > GET_MODE_ALIGNMENT (vector_reg_mode))
+	    incoming_stack_boundary
+	      = GET_MODE_ALIGNMENT (vector_reg_mode);
+	}
+      else
+	gcc_unreachable ();
+    }
+  else
+    incoming_stack_boundary = GET_MODE_ALIGNMENT (word_mode);
 
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, maybe_eh_return))
       {
-	rtx reg = gen_rtx_REG (V4SFmode, regno);
+	rtx reg = gen_rtx_REG (vector_reg_mode, regno);
 	rtx mem;
 
 	mem = choose_baseaddr (cfa_offset);
-	mem = gen_rtx_MEM (V4SFmode, mem);
-	set_mem_align (mem, 128);
+	mem = gen_rtx_MEM (vector_reg_mode, mem);
+	set_mem_align (mem, incoming_stack_boundary);
 	emit_move_insn (reg, mem);
 
 	ix86_add_cfa_restore_note (NULL, reg, cfa_offset);
 
-	cfa_offset -= 16;
+	cfa_offset -= GET_MODE_SIZE (vector_reg_mode);
       }
 }
 
@@ -13149,8 +13435,9 @@ ix86_expand_epilogue (int style)
   if (crtl->calls_eh_return && style != 2)
     frame.reg_save_offset -= 2 * UNITS_PER_WORD;
 
-  /* EH_RETURN requires the use of moves to function properly.  */
-  if (crtl->calls_eh_return)
+  /* EH_RETURN requires the use of moves to function properly.  We must
+     use move to restore bound and mask registers.  */
+  if (crtl->calls_eh_return || frame.nbndregs > 0 || frame.nmaskregs > 0)
     restore_regs_via_mov = true;
   /* SEH requires the use of pops to identify the epilogue.  */
   else if (TARGET_SEH)
@@ -13391,7 +13678,20 @@ ix86_expand_epilogue (int style)
       return;
     }
 
-  if (crtl->args.pops_args && crtl->args.size)
+  if (cfun->machine->func_type != TYPE_NORMAL)
+    {
+      /* Return with the "IRET" instruction from interrupt handler.
+	 Pop the 'ERROR_CODE' off the stack before the 'IRET'
+	 instruction in exception handler.  */
+      if (cfun->machine->func_type == TYPE_EXCEPTION)
+	{
+	  rtx r = plus_constant (Pmode, stack_pointer_rtx,
+				 UNITS_PER_WORD);
+	  emit_insn (gen_rtx_SET (stack_pointer_rtx, r));
+	}
+      emit_jump_insn (gen_interrupt_return ());
+    }
+  else if (crtl->args.pops_args && crtl->args.size)
     {
       rtx popc = GEN_INT (crtl->args.pops_args);
 
@@ -18364,6 +18664,14 @@ ix86_expand_move (machine_mode mode, rtx operands[])
   rtx op0, op1;
   enum tls_model model;
 
+  if (cfun->machine->func_type != TYPE_NORMAL && IS_STACK_MODE (mode))
+    {
+      error ("80387 instructions aren't allowed in %s service routine",
+	     (cfun->machine->func_type == TYPE_EXCEPTION
+	      ? "exception" : "interrupt"));
+      return;
+    }
+
   op0 = operands[0];
   op1 = operands[1];
 
@@ -18508,6 +18816,15 @@ ix86_expand_vector_move (machine_mode mode, rtx operands[])
   rtx op0 = operands[0], op1 = operands[1];
   unsigned int align = GET_MODE_ALIGNMENT (mode);
 
+  if (cfun->machine->func_type != TYPE_NORMAL
+      && VALID_MMX_REG_MODE (mode))
+    {
+      error ("MMX/3Dnow instructions aren't allowed in %s service routine",
+	     (cfun->machine->func_type == TYPE_EXCEPTION
+	      ? "exception" : "interrupt"));
+      return;
+    }
+
   if (push_operand (op0, VOIDmode))
     op0 = emit_move_resolve_push (mode, op0);
 
@@ -26593,6 +26910,17 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
   rtx vec[3];
   rtx use = NULL, call;
   unsigned int vec_len = 0;
+  tree fndecl;
+
+  if (GET_CODE (XEXP (fnaddr, 0)) == SYMBOL_REF)
+    {
+      fndecl = SYMBOL_REF_DECL (XEXP (fnaddr, 0));
+      if (fndecl
+	  && (lookup_attribute ("interrupt", DECL_ATTRIBUTES (fndecl))))
+	error ("interrupt service routine can't be called directly");
+    }
+  else
+    fndecl = NULL_TREE;
 
   if (pop == const0_rtx)
     pop = NULL;
@@ -26690,8 +27018,30 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
       vec[vec_len++] = pop;
     }
 
-  if (TARGET_64BIT_MS_ABI
-      && (!callarg2 || INTVAL (callarg2) != -2))
+  if (cfun->machine->no_caller_saved_registers
+      && (!fndecl
+	  || !lookup_attribute ("no_caller_saved_registers",
+				DECL_ATTRIBUTES (fndecl))))
+    {
+      static const char ix86_call_used_regs[] = CALL_USED_REGISTERS;
+      char c_mask = ((TARGET_64BIT
+		      && ix86_function_abi (fndecl) == MS_ABI)
+		     ? (1 << 3)
+		     : TARGET_64BIT ? (1 << 2) : (1 << 1));
+
+      /* If there are no caller-saved registers, add all registers
+	 that are clobbered by the call.  */
+      for (int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+	if (!fixed_regs[i]
+	    && (ix86_call_used_regs[i] == 1
+		|| (ix86_call_used_regs[i] & c_mask))
+	    && !STACK_REGNO_P (i)
+	    && !MMX_REGNO_P (i))
+	  clobber_reg (&use,
+		       gen_rtx_REG (GET_MODE (regno_reg_rtx[i]), i));
+    }
+  else if (TARGET_64BIT_MS_ABI
+	   && (!callarg2 || INTVAL (callarg2) != -2))
     {
       int const cregs_size
 	= ARRAY_SIZE (x86_64_ms_sysv_extra_clobbered_registers);
@@ -44076,6 +44426,69 @@ ix86_handle_fndecl_attribute (tree *node, tree name, tree, int,
   return NULL_TREE;
 }
 
+static tree
+ix86_handle_no_caller_saved_registers_attribute (tree *node,
+						 tree name, tree, int,
+						 bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+	       name);
+      *no_add_attrs = true;
+    }
+  return NULL_TREE;
+}
+
+static tree
+ix86_handle_interrupt_attribute (tree *node, tree name, tree, int,
+				 bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+	       name);
+      *no_add_attrs = true;
+    }
+
+  /* DECL_RESULT and DECL_ARGUMENTS do not exist there yet,
+     but the function type contains args and return type data.  */
+  tree func_type = TREE_TYPE (*node);
+  tree return_type = TREE_TYPE (func_type);
+
+  int nargs = 0;
+  tree current_arg_type = TYPE_ARG_TYPES (func_type);
+  while (current_arg_type
+	 && ! VOID_TYPE_P (TREE_VALUE (current_arg_type)))
+    {
+      if (nargs == 0)
+	{
+	  if (! POINTER_TYPE_P (TREE_VALUE (current_arg_type)))
+	    error ("interrupt service routine should have a pointer "
+		   "as the first argument");
+	}
+      else if (nargs == 1)
+	{
+	  if (TREE_CODE (TREE_VALUE (current_arg_type)) != INTEGER_TYPE
+	      || TYPE_MODE (TREE_VALUE (current_arg_type)) != word_mode)
+	    error ("interrupt service routine should have unsigned %s"
+		   "int as the second argument",
+		   TARGET_64BIT
+		   ? (TARGET_X32 ? "long long " : "long ")
+		   : "");
+	}
+      nargs++;
+      current_arg_type = TREE_CHAIN (current_arg_type);
+    }
+  if (!nargs || nargs > 2)
+    error ("interrupt service routine can only have a pointer argument "
+	   "and an optional integer argument");
+  if (! VOID_TYPE_P (return_type))
+    error ("interrupt service routine can't have non-void return value");
+
+  return NULL_TREE;
+}
+
 static bool
 ix86_ms_bitfield_layout_p (const_tree record_type)
 {
@@ -48077,6 +48490,11 @@ static const struct attribute_spec ix86_attribute_table[] =
     false },
   { "callee_pop_aggregate_return", 1, 1, false, true, true,
     ix86_handle_callee_pop_aggregate_return, true },
+  { "interrupt", 0, 0, true, false, false,
+    ix86_handle_interrupt_attribute, false },
+  { "no_caller_saved_registers", 0, 0, true, false, false,
+    ix86_handle_no_caller_saved_registers_attribute, false },
+
   /* End element.  */
   { NULL,        0, 0, false, false, false, NULL, false }
 };
@@ -53926,6 +54344,9 @@ ix86_operands_ok_for_move_multiple (rtx *operands, bool load,
 #undef TARGET_ABSOLUTE_BIGGEST_ALIGNMENT
 #define TARGET_ABSOLUTE_BIGGEST_ALIGNMENT 512
 
+#undef TARGET_HARD_REGNO_SCRATCH_OK
+#define TARGET_HARD_REGNO_SCRATCH_OK ix86_hard_regno_scratch_ok
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 #include "gt-i386.h"
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index c59e3cc..7a18634 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -1729,6 +1729,11 @@ typedef struct ix86_args {
 
 #define EXIT_IGNORE_STACK 1
 
+/* Define this macro as a C expression that is nonzero for registers
+   used by the epilogue or the `return' pattern.  */
+
+#define EPILOGUE_USES(REGNO) ix86_epilogue_uses (REGNO)
+
 /* Output assembler code for a block containing the constant parts
    of a trampoline, leaving space for the variable parts.  */
 
@@ -2443,6 +2448,18 @@ struct GTY(()) machine_frame_state
 /* Private to winnt.c.  */
 struct seh_frame_state;
 
+enum function_type
+{
+  TYPE_NORMAL = 0,
+  /* The current function is an interrupt service routine with a
+     pointer argument as specified by the "interrupt" attribute.  */
+  TYPE_INTERRUPT,
+  /* The current function is an interrupt service routine with a
+     pointer argument and an integer argument as specified by the
+     "interrupt" attribute.  */
+  TYPE_EXCEPTION
+};
+
 struct GTY(()) machine_function {
   struct stack_local_entry *stack_locals;
   const char *some_ld_name;
@@ -2493,6 +2510,13 @@ struct GTY(()) machine_function {
   /* If true, it is safe to not save/restore DRAP register.  */
   BOOL_BITFIELD no_drap_save_restore : 1;
 
+  /* Function type.  */
+  ENUM_BITFIELD(function_type) func_type : 2;
+
+  /* If true, the current function is a function specified with
+     the "interrupt" or "no_caller_saved_registers" attribute.  */
+  BOOL_BITFIELD no_caller_saved_registers : 1;
+
   /* If true, there is register available for argument passing.  This
      is used only in ix86_function_ok_for_sibcall by 32-bit to determine
      if there is scratch register available for indirect sibcall.  In
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 8c2ed60..8b22be4 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -193,6 +193,9 @@
   UNSPEC_BNDCU
   UNSPEC_BNDCN
   UNSPEC_MPX_FENCE
+
+  ;; IRET support
+  UNSPEC_INTERRUPT_RETURN
 ])
 
 (define_c_enum "unspecv" [
@@ -12179,6 +12182,12 @@
    (set_attr "modrm" "0")
    (set_attr "maybe_prefix_bnd" "1")])
 
+(define_insn "interrupt_return"
+  [(simple_return)
+   (unspec [(const_int 0)] UNSPEC_INTERRUPT_RETURN)]
+  "reload_completed"
+  "iret")
+
 ;; Used by x86_machine_dependent_reorg to avoid penalty on single byte RET
 ;; instruction Athlon and K8 have.
 
diff --git a/gcc/config/i386/sse.md b/gcc/config/i386/sse.md
index 013681c..ae9bfea 100644
--- a/gcc/config/i386/sse.md
+++ b/gcc/config/i386/sse.md
@@ -875,10 +875,18 @@
 	case MODE_V16SF:
 	case MODE_V8SF:
 	case MODE_V4SF:
-	  if (TARGET_AVX
+	  /* We must support misaligned SSE load and store in interrupt
+	     handler or there are no caller-saved registers and we are
+	     in 32-bit mode since ix86_emit_save_reg_using_mov generates
+	     the normal *mov<mode>_internal pattern to save and restore
+	     SSE registers with misaligned stack.  */
+	  if ((TARGET_AVX
+	       || cfun->machine->func_type != TYPE_NORMAL
+	       || (!TARGET_64BIT
+		   && cfun->machine->no_caller_saved_registers))
 	      && (misaligned_operand (operands[0], <MODE>mode)
 		  || misaligned_operand (operands[1], <MODE>mode)))
-	    return "vmovups\t{%1, %0|%0, %1}";
+	    return "%vmovups\t{%1, %0|%0, %1}";
 	  else
 	    return "%vmovaps\t{%1, %0|%0, %1}";
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 8406945..8c62218 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -5061,6 +5061,69 @@ On x86-32 targets, the @code{stdcall} attribute causes the compiler to
 assume that the called function pops off the stack space used to
 pass arguments, unless it takes a variable number of arguments.
 
+@item no_caller_saved_registers
+@cindex @code{no_caller_saved_registers} function attribute, x86
+Use this attribute to indicate that the specified function has no
+caller-saved registers.  That is, all registers are callee-saved.
+The compiler generates proper function entry and exit sequences to
+save and restore any modified registers.
+
+@item interrupt
+@cindex @code{interrupt} function attribute, x86
+Use this attribute to indicate that the specified function is an
+interrupt handler.  The compiler generates function
+entry and exit sequences suitable for use in an interrupt handler when
+this attribute is present.  The @code{IRET} instruction, instead of the
+@code{RET} instruction, is used to return from interrupt handlers.  All
+registers, except for the EFLAGS register which is restored by the
+@code{IRET} instruction, are preserved by the compiler.
+
+Any interruptible-without-stack-switch code must be compiled with
+@option{-mno-red-zone} since interrupt handlers can and will, because
+of the hardware design, touch the red zone.
+
+An interrupt handler must be declared with a mandatory pointer
+argument:
+
+@smallexample
+struct interrupt_frame;
+
+__attribute__ ((interrupt))
+void
+f (struct interrupt_frame *frame)
+@{
+@}
+@end smallexample
+
+and user must properly define the structure the pointer pointing to.
+
+The exception handler is very similar to the interrupt handler with
+a different mandatory function signature:
+
+@smallexample
+#ifdef __x86_64__
+typedef unsigned long long int uword_t;
+#else
+typedef unsigned int uword_t;
+#endif
+
+struct interrupt_frame;
+
+__attribute__ ((interrupt))
+void
+f (struct interrupt_frame *frame, uword_t error_code)
+@{
+  ...
+@}
+@end smallexample
+
+and compiler pops the error code off the stack before the @code{IRET}
+instruction.
+
+The exception handler should only be used for exceptions which push an
+error code and all other exceptions must use the interrupt handler.
+The system will crash if the wrong handler is used.
+
 @item target (@var{options})
 @cindex @code{target} function attribute
 As discussed in @ref{Common Function Attributes}, this attribute 
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-1.c b/gcc/testsuite/gcc.target/i386/interrupt-1.c
new file mode 100644
index 0000000..ea2f1e4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-1.c
@@ -0,0 +1,46 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-push-args" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern int bar (int);
+
+void foo (void *frame)
+{
+  int a,b,c,d,e,f,i;
+  a = bar (5);
+  b = bar (a);
+  c = bar (b);
+  d = bar (c);
+  e = bar (d);
+  f = bar (e);
+  for (i = 1; i < 10; i++)
+  {
+    a += bar (a + i) + bar (b + i) +
+	 bar (c + i) + bar (d + i) +
+	 bar (e + i) + bar (f + i);
+  }
+}
+
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-10.c b/gcc/testsuite/gcc.target/i386/interrupt-10.c
new file mode 100644
index 0000000..a439d6f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-10.c
@@ -0,0 +1,24 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mavx512bw -mno-iamcu" } */
+
+extern void bar (void);
+
+struct interrupt_frame;
+
+void
+ __attribute__ ((interrupt))
+foo (struct interrupt_frame *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm\[0-9\]+" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "kmovq\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" 8 } } */
+/* { dg-final { scan-assembler-times "kmovq\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" 8 } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-11.c b/gcc/testsuite/gcc.target/i386/interrupt-11.c
new file mode 100644
index 0000000..6556c44
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-11.c
@@ -0,0 +1,38 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-sse -mno-iamcu" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-12.c b/gcc/testsuite/gcc.target/i386/interrupt-12.c
new file mode 100644
index 0000000..5c9eb74
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-12.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int check_int (int *i, void *, int align);
+typedef int aligned __attribute__((aligned(64)));
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+__attribute__((interrupt))
+void
+foo (void *frame, uword_t error_code)
+{
+  aligned j;
+  if (check_int (frame, &j, __alignof__(j)))
+    __builtin_abort ();
+}
+
+/* { dg-final { scan-assembler-times "and\[lq\]?\[^\\n\]*-64,\[^\\n\]*sp" 1 } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-13.c b/gcc/testsuite/gcc.target/i386/interrupt-13.c
new file mode 100644
index 0000000..8cf0b25
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-13.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int check_int (int *i, void *, int align);
+typedef int aligned __attribute__((aligned(64)));
+
+__attribute__((interrupt))
+void
+foo (void *frame)
+{
+  aligned j;
+  if (check_int (frame, &j, __alignof__(j)))
+    __builtin_abort ();
+}
+
+/* { dg-final { scan-assembler-times "and\[lq\]?\[^\\n\]*-64,\[^\\n\]*sp" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-14.c b/gcc/testsuite/gcc.target/i386/interrupt-14.c
new file mode 100644
index 0000000..7394207
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-14.c
@@ -0,0 +1,39 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-sse -mno-iamcu -mno-sse4 -mno-popcnt" } */
+
+extern int i, cnt;
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  cnt = __builtin_popcount (i);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 1 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-15.c b/gcc/testsuite/gcc.target/i386/interrupt-15.c
new file mode 100644
index 0000000..1bb9cde
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-15.c
@@ -0,0 +1,24 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mpush-args" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  bar (error);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 1 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-16.c b/gcc/testsuite/gcc.target/i386/interrupt-16.c
new file mode 100644
index 0000000..794a846
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-16.c
@@ -0,0 +1,27 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mno-push-args" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  bar (error);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%rbp" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%ebp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%ebp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 1 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-17.c b/gcc/testsuite/gcc.target/i386/interrupt-17.c
new file mode 100644
index 0000000..6596a68
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-17.c
@@ -0,0 +1,29 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mpush-args" } */
+
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame)
+{
+  bar (3);
+}
+
+void
+ __attribute__ ((interrupt))
+fn2 (void *frame)
+{
+  bar (3);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 2 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-18.c b/gcc/testsuite/gcc.target/i386/interrupt-18.c
new file mode 100644
index 0000000..7767aee
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-18.c
@@ -0,0 +1,31 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mpush-args" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  bar (error);
+}
+
+void
+ __attribute__ ((interrupt))
+fn2 (void *frame, uword_t error)
+{
+  bar (error);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 2 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-19.c b/gcc/testsuite/gcc.target/i386/interrupt-19.c
new file mode 100644
index 0000000..76ca077
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-19.c
@@ -0,0 +1,24 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mno-push-args" } */
+
+extern int foo (int) __attribute__ ((no_caller_saved_registers));
+extern int bar (int) __attribute__ ((no_caller_saved_registers));
+
+int
+foo (int i)
+{
+  return bar (i) + i;
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%ebp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%ebp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rbp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rbp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-2.c b/gcc/testsuite/gcc.target/i386/interrupt-2.c
new file mode 100644
index 0000000..2003fbb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-2.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wall -Wunused-parameter" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+}
+
+/* { dg-final { scan-assembler-not "add(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-20.c b/gcc/testsuite/gcc.target/i386/interrupt-20.c
new file mode 100644
index 0000000..c1b6d32
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-20.c
@@ -0,0 +1,22 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mno-push-args" } */
+
+extern int foo (int) __attribute__ ((no_caller_saved_registers));
+extern int bar (int) __attribute__ ((no_caller_saved_registers));
+
+int
+foo (int i)
+{
+  return bar (i + i);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "jmp" } }*/
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-21.c b/gcc/testsuite/gcc.target/i386/interrupt-21.c
new file mode 100644
index 0000000..30c3893
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-21.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx -mno-iamcu -msse" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movaps\[\\t \]*%xmm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movaps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%xmm3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-2\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-2\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[4-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[4-9\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm1\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm1\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-22.c b/gcc/testsuite/gcc.target/i386/interrupt-22.c
new file mode 100644
index 0000000..7b1547d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-22.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-2\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-2\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[4-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[4-9\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm1\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm1\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-23.c b/gcc/testsuite/gcc.target/i386/interrupt-23.c
new file mode 100644
index 0000000..c53fcf7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-23.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mavx512f -mno-iamcu" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-2\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-2\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[4-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[4-9\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm1\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm1\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-24.c b/gcc/testsuite/gcc.target/i386/interrupt-24.c
new file mode 100644
index 0000000..1ab5bfc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-24.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-iamcu -mmpx" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "bnd3");
+}
+
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*%bnd3,\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%bnd3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-25.c b/gcc/testsuite/gcc.target/i386/interrupt-25.c
new file mode 100644
index 0000000..849fd3a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-25.c
@@ -0,0 +1,24 @@
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -mno-avx -mno-iamcu -msse -mincoming-stack-boundary=2" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%xmm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%xmm3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-2\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-2\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[4-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[4-9\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm1\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm1\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%e(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%e(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%ebp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-3.c b/gcc/testsuite/gcc.target/i386/interrupt-3.c
new file mode 100644
index 0000000..a87e594
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-3.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+void
+__attribute__((interrupt))
+fn (void* frame, uword_t error)
+{
+}
+
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-387-err.c b/gcc/testsuite/gcc.target/i386/interrupt-387-err.c
new file mode 100644
index 0000000..36094f2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-387-err.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -m80387 -mlong-double-80 -mno-iamcu -mno-sse" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+extern long double y, x;
+
+void
+fn0 (void)
+{
+  x = y;
+  y = 0;
+}
+
+void
+__attribute__((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  x = 0; /* { dg-error "80387 instructions aren't allowed in exception service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn2 (void *frame)
+{
+  x = y; /* { dg-error "80387 instructions aren't allowed in interrupt service routine" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-4.c b/gcc/testsuite/gcc.target/i386/interrupt-4.c
new file mode 100644
index 0000000..94230cd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-4.c
@@ -0,0 +1,32 @@
+/* { dg-do link } */
+/* { dg-options "-O" } */
+
+#include <stdint.h>
+
+extern void link_error (void);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+struct interrupt_frame
+{
+  uword_t ip;
+  uword_t cs;
+  uword_t flags;
+  uword_t sp;
+  uword_t ss;
+};
+
+__attribute__ ((used, interrupt))
+void
+foo (struct interrupt_frame *frame)
+{
+  void *ra = __builtin_return_address (0);
+  if ((uintptr_t) ra != (uintptr_t) frame->ip)
+    link_error ();
+}
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-5.c b/gcc/testsuite/gcc.target/i386/interrupt-5.c
new file mode 100644
index 0000000..53d6656
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-5.c
@@ -0,0 +1,23 @@
+/* { dg-do link } */
+/* { dg-options "-O" } */
+
+#include <stdint.h>
+
+extern void link_error (void);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+__attribute__ ((used, interrupt))
+void
+foo (void *frame, uword_t error)
+{
+  void *ra = __builtin_return_address (0);
+  if ((uintptr_t) ra != (uintptr_t) error)
+    link_error ();
+}
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-6.c b/gcc/testsuite/gcc.target/i386/interrupt-6.c
new file mode 100644
index 0000000..a1a3d0e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-6.c
@@ -0,0 +1,40 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+extern int error;
+
+__attribute__((interrupt))
+void
+fn1 (void *p, short error_code)
+{ /* { dg-error "interrupt service routine should have unsigned \(long long |long |\)int as the second argument" } */
+}
+
+__attribute__((interrupt))
+void
+fn2 (void)
+{ /* { dg-error "interrupt service routine can only have a pointer argument and an optional integer argument" } */
+}
+
+__attribute__((interrupt))
+void
+fn3 (uword_t error_code)
+{ /* { dg-error "interrupt service routine should have a pointer as the first argument" } */
+  error = error_code;
+}
+
+__attribute__((interrupt))
+void
+fn4 (uword_t error_code, void *frame)
+{ /* { dg-error "interrupt service routine should have .* the .* argument" } */
+  error = error_code;
+}
+
+extern int fn5 (void *) __attribute__ ((interrupt)); /* { dg-error "interrupt service routine can't have non-void return value" } */
+
+int
+fn5 (void *frame)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-7.c b/gcc/testsuite/gcc.target/i386/interrupt-7.c
new file mode 100644
index 0000000..e7441a2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-7.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int error;
+
+extern void fn (void *) __attribute__((interrupt));
+
+void
+foo (void)
+{
+  fn (&error); /* { dg-error "interrupt service routine can't be called directly" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-8.c b/gcc/testsuite/gcc.target/i386/interrupt-8.c
new file mode 100644
index 0000000..0a83473
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-8.c
@@ -0,0 +1,20 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 16 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm\[0-9\]+" 16 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-9.c b/gcc/testsuite/gcc.target/i386/interrupt-9.c
new file mode 100644
index 0000000..9d0c776
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-9.c
@@ -0,0 +1,22 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512bw -mno-iamcu -mavx512f" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm\[0-9\]+" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "kmovw\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" 8 } } */
+/* { dg-final { scan-assembler-times "kmovw\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" 8 } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-bnd.c b/gcc/testsuite/gcc.target/i386/interrupt-bnd.c
new file mode 100644
index 0000000..9f50661
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-bnd.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mno-iamcu -mmpx" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "bnd3");
+}
+
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*%bnd3,\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%bnd3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
new file mode 100644
index 0000000..77a9463
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
@@ -0,0 +1,30 @@
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -miamcu" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern int bar (int);
+
+void foo (void *frame)
+{
+  int a,b,c,d,e,f,i;
+  a = bar (5);
+  b = bar (a);
+  c = bar (b);
+  d = bar (c);
+  e = bar (d);
+  f = bar (e);
+  for (i = 1; i < 10; i++)
+    a += bar (a + i) + bar (b + i) +
+	 bar (c + i) + bar (d + i) +
+	 bar (e + i) + bar (f+i);
+}
+
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%eax" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%ecx" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%edx" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%eax" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%ecx" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%edx" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c
new file mode 100644
index 0000000..fc4f367
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mmmx -mno-iamcu" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+typedef short __v4hi __attribute__ ((__vector_size__ (8)));
+typedef int __m64 __attribute__ ((__vector_size__ (8), __may_alias__));
+
+extern __m64 y, x;
+
+void
+fn0 (void)
+{
+  x = __extension__ (__m64){ 0 };
+  x = (__m64) __builtin_ia32_packsswb ((__v4hi) x, (__v4hi) y);
+  y = x;
+}
+
+void
+__attribute__((interrupt))
+fn1 (void *frame)
+{
+  x = (__m64) __builtin_ia32_packsswb ((__v4hi) x, (__v4hi) y); /* { dg-error "MMX/3Dnow (instructions|intrinsics) aren't allowed in interrupt service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn2 (void *frame)
+{
+  x = y; /* { dg-error "MMX/3Dnow instructions aren't allowed in interrupt service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn3 (void *frame, uword_t error)
+{
+  x = __extension__ (__m64){ 0 }; /* { dg-error "MMX/3Dnow instructions aren't allowed in exception service routine" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c b/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c
new file mode 100644
index 0000000..796b81b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mred-zone" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  /* No need to adjust stack if less than 128 bytes are used on stack
+     with a 128-byte red zone.  */
+  char array[112];
+  asm ("# %0 "
+       :
+       : "r" (array));
+}
+
+/* { dg-final { scan-assembler-not "(sub|add)(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c b/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c
new file mode 100644
index 0000000..c6a2822
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mred-zone" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  /* Need to adjust stack if more than 128 bytes are used on stack
+     with a 128-byte red zone.  */
+  char array[113];
+  asm ("# %0 "
+       :
+       : "r" (array));
+}
+
+/* { dg-final { scan-assembler-times "(?:sub|add)(?:l|q)\[\\t \]*\\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c b/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c
new file mode 100644
index 0000000..e222acf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O3" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern void bar (void);
+
+void foo (void *frame)
+{
+  bar ();
+}
+/* { dg-final { scan-assembler-not "jmp" } }*/
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c
new file mode 100644
index 0000000..b1b0abe
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern void bar (int);
+
+void f1 (void){  bar (1); }
+__attribute__((interrupt))
+void f2 (void *frame){  bar (2); }
+void f3 (void){  bar (3); }
+__attribute__((interrupt))
+void f4 (void *frame){  bar (4); }
+void f5 (void){  bar (5); }
+
+/* { dg-final { scan-assembler-times "push.\t%.ax" 2 } } */
+/* { dg-final { scan-assembler-times "pop.\t%.ax" 2 } } */
+/* { dg-final { scan-assembler-times "iret" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-xmm.c b/gcc/testsuite/gcc.target/i386/interrupt-xmm.c
new file mode 100644
index 0000000..0eafb10
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-xmm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx -mno-iamcu -msse" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%xmm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%xmm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-ymm.c b/gcc/testsuite/gcc.target/i386/interrupt-ymm.c
new file mode 100644
index 0000000..acb5fc7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-ymm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*%ymm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-zmm.c b/gcc/testsuite/gcc.target/i386/interrupt-zmm.c
new file mode 100644
index 0000000..6d23b5d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-zmm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mavx512f -mno-iamcu" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*%zmm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-10-04  5:24                                     ` Yulia Koval
@ 2015-10-04 10:29                                       ` Uros Bizjak
  2015-10-04 18:15                                         ` H.J. Lu
  0 siblings, 1 reply; 45+ messages in thread
From: Uros Bizjak @ 2015-10-04 10:29 UTC (permalink / raw)
  To: Yulia Koval; +Cc: H.J. Lu, Mike Stump, GCC Patches

On Sun, Oct 4, 2015 at 7:23 AM, Yulia Koval <vaalfreja@gmail.com> wrote:
> Hi,
>
> Here is the last version of the patch. Regtested/bootstraped for
> Linux/i686 and Linux/x86_64.
>
> Date: Fri, 4 Sep 2015 08:53:23 -0700
> Subject: [PATCH] Implement x86 interrupt attribute
>
> The interrupt and exception handlers are called by x86 processors.  X86
> hardware pushes information onto stack and calls the handler.  The
> requirements are
>
> 1. Both interrupt and exception handlers must use the 'IRET' instruction,
> instead of the 'RET' instruction, to return from the handlers.
> 2. All registers are callee-saved in interrupt and exception handlers.
> 3. The difference between interrupt and exception handlers is the
> exception handler must pop 'ERROR_CODE' off the stack before the 'IRET'
> instruction.
>
> The design goals of interrupt and exception handlers for x86 processors
> are:
>
> 1. Support both 32-bit and 64-bit modes.
> 2. Flexible for compilers to optimize.
> 3. Easy to use by programmers.
>
> To implement interrupt and exception handlers for x86 processors, a
> compiler should support:
>
> 'interrupt' attribute
>
> Use this attribute to indicate that the specified function with
> mandatory arguments is an interrupt or exception handler.  The compiler
> generates function entry and exit sequences suitable for use in an
> interrupt handler when this attribute is present.  The 'IRET' instruction,
> instead of the 'RET' instruction, is used to return from interrupt or
> exception handlers.  All registers, except for the EFLAGS register which
> is restored by the 'IRET' instruction, are preserved by the compiler.
>
> Any interruptible-without-stack-switch code must be compiled with
> -mno-red-zone since interrupt handlers can and will, because of the
> hardware design, touch the red zone.
>
> 1. interrupt handler must be declared with a mandatory pointer argument:
>
> struct interrupt_frame;
>
> __attribute__ ((interrupt))
> void
> f (struct interrupt_frame *frame)
> {
> ...
> }
>
> and user must properly define the structure the pointer pointing to.
>
> 2. exception handler:
>
> The exception handler is very similar to the interrupt handler with
> a different mandatory function signature:
>
> typedef unsigned long long int uword_t;
> typedef unsigned int uword_t;
>
> struct interrupt_frame;
>
> __attribute__ ((interrupt))
> void
> f (struct interrupt_frame *frame, uword_t error_code)
> {
> ...
> }
>
> and compiler pops the error code off stack before the 'IRET' instruction.
>
> The exception handler should only be used for exceptions which push an
> error code and all other exceptions must use the interrupt handler.
> The system will crash if the wrong handler is used.
>
> To be feature complete, compiler may implement the optional
> 'no_caller_saved_registers' attribute:
>
> Use this attribute to indicate that the specified function has no
> caller-saved registers.  That is, all registers are callee-saved.
> The compiler generates proper function entry and exit sequences to
> save and restore any modified registers.
>
> The user can call functions specified with 'no_caller_saved_registers'
> attribute from an interrupt handler without saving and restoring all
> call clobbered registers.

Looking a bit deeper into the code, it looks that we want to realign
the stack in the interrupt handler. Let's assume that interrupt
handler is calling some other function that saves SSE vector regs to
the stack. According to the x86 ABI, incoming stack of the called
function is assumed to be aligned to 16 bytes. But, interrupt handler
violates this assumption, since the stack could be aligned to only 4
bytes for 32bit and 8 bytes for 64bit targets. Entering the called
function with stack, aligned to less than 16 bytes will certainly
violate ABI.

So, it looks to me that we need to realign the stack in the interrupt
handler unconditionally to 16bytes. In this case, we also won't need
the following changes:

@@ -11284,9 +11474,29 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
       /* The only ABI that has saved SSE registers (Win64) also has a
          16-byte aligned default stack, and thus we don't need to be
      within the re-aligned local stack frame to save them.  */
-      gcc_assert (INCOMING_STACK_BOUNDARY >= 128);
-      offset = ROUND_UP (offset, 16);
-      offset += frame->nsseregs * 16;
+      unsigned int incoming_stack_boundary
+    = MAX (crtl->parm_stack_boundary, ix86_incoming_stack_boundary);
+      unsigned int vec_regsize;
+
+      /* We must save full vector registers if there are no
+     caller-saved registers.  */
+      if (cfun->machine->no_caller_saved_registers)
+    vec_regsize = (TARGET_AVX512F ? 64 : (TARGET_AVX ? 32 : 16));
+      else
+    vec_regsize = 16;
+
+      if (cfun->machine->func_type == TYPE_NORMAL)
+    {
+      /* Incoming stack may be 32-bit aligned in 32-bit mode.  */
+      gcc_assert (!TARGET_64BIT || incoming_stack_boundary >= 128);
+      /* Don't over-align vector register save area.  */
+      incoming_stack_boundary /= BITS_PER_UNIT;
+      if (incoming_stack_boundary > vec_regsize)
+        incoming_stack_boundary = vec_regsize;
+      offset = ROUND_UP (offset, incoming_stack_boundary);
+    }
+
+      offset += frame->nsseregs * vec_regsize;
     }
   frame->sse_reg_save_offset = offset;

...

@@ -11513,8 +11727,28 @@ ix86_emit_save_reg_using_mov (machine_mode
mode, unsigned int regno,
   addr = choose_baseaddr (cfa_offset);
   mem = gen_frame_mem (mode, addr);

-  /* For SSE saves, we need to indicate the 128-bit alignment.  */
-  set_mem_align (mem, GET_MODE_ALIGNMENT (mode));
+  /* For SSE saves, we need to indicate the 128-bit alignment.  In
+     interrupt handler, stack is aligned to word_mode.  We can't use
+     gen_sse_storeups since RTX_FRAME_RELATED_P is set and
+     dwarf2out_flush_queued_reg_saves doesn't like UNSPEC_STOREU.
+     Also gen_sse_storeups doesn't cover AVX nor AVX512.  */
+  unsigned int incoming_stack_boundary;
+  if (cfun->machine->func_type == TYPE_NORMAL)
+    {
+      incoming_stack_boundary
+    = MAX (crtl->parm_stack_boundary, ix86_incoming_stack_boundary);
+      if (!TARGET_64BIT || incoming_stack_boundary >= 128)
+    {
+      /* Don't over-align vector register save slot.  */
+      if (incoming_stack_boundary > GET_MODE_ALIGNMENT (mode))
+        incoming_stack_boundary = GET_MODE_ALIGNMENT (mode);
+    }
+      else
+    gcc_unreachable ();
+    }
+  else
+    incoming_stack_boundary = GET_MODE_ALIGNMENT (word_mode);
+  set_mem_align (mem, incoming_stack_boundary);

   insn = emit_move_insn (mem, reg);
   RTX_FRAME_RELATED_P (insn) = 1;

and

--- a/gcc/config/i386/sse.md
+++ b/gcc/config/i386/sse.md
@@ -875,10 +875,18 @@
     case MODE_V16SF:
     case MODE_V8SF:
     case MODE_V4SF:
-      if (TARGET_AVX
+      /* We must support misaligned SSE load and store in interrupt
+         handler or there are no caller-saved registers and we are
+         in 32-bit mode since ix86_emit_save_reg_using_mov generates
+         the normal *mov<mode>_internal pattern to save and restore
+         SSE registers with misaligned stack.  */
+      if ((TARGET_AVX
+           || cfun->machine->func_type != TYPE_NORMAL
+           || (!TARGET_64BIT
+           && cfun->machine->no_caller_saved_registers))
           && (misaligned_operand (operands[0], <MODE>mode)
           || misaligned_operand (operands[1], <MODE>mode)))
-        return "vmovups\t{%1, %0|%0, %1}";
+        return "%vmovups\t{%1, %0|%0, %1}";
       else
         return "%vmovaps\t{%1, %0|%0, %1}";

If we want realignment to work for x86_64, we can also enable
-mstack-realign also for x86_64, as was recently requested from
Windows people. (I can't find the relevant PR ATM...)


What also bothers me is the following change:

@@ -11248,11 +11433,16 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
        = !expensive_function_p (count);
     }

+  /* We must use move to save bound and mask registers.  */
   frame->save_regs_using_mov
-    = (TARGET_PROLOGUE_USING_MOVE && cfun->machine->use_fast_prologue_epilogue
-       /* If static stack checking is enabled and done with probes,
-      the registers need to be saved before allocating the frame.  */
-       && flag_stack_check != STATIC_BUILTIN_STACK_CHECK);
+    = (frame->nbndregs > 0
+       || frame->nmaskregs > 0
+       || (TARGET_PROLOGUE_USING_MOVE
+       && cfun->machine->use_fast_prologue_epilogue
+       /* If static stack checking is enabled and done with probes,
+          the registers need to be saved before allocating the
+          frame.  */
+       && flag_stack_check != STATIC_BUILTIN_STACK_CHECK));

In the above code, we force the whole frame to save_regs_using_mov,
when we have to save BND and MASK registers. However, AFAICS,
cygwin/mingw is able to save SSE registers (that also don't have PUSH
insns) without this requirement. We should use the same approach to
save BND and MASK registers.

Uros.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-10-04 10:29                                       ` Uros Bizjak
@ 2015-10-04 18:15                                         ` H.J. Lu
  2015-10-04 20:01                                           ` Uros Bizjak
  2015-10-05  9:00                                           ` Mike Stump
  0 siblings, 2 replies; 45+ messages in thread
From: H.J. Lu @ 2015-10-04 18:15 UTC (permalink / raw)
  To: Uros Bizjak; +Cc: Yulia Koval, Mike Stump, GCC Patches

On Sun, Oct 4, 2015 at 3:29 AM, Uros Bizjak <ubizjak@gmail.com> wrote:
> On Sun, Oct 4, 2015 at 7:23 AM, Yulia Koval <vaalfreja@gmail.com> wrote:
>> Hi,
>>
>> Here is the last version of the patch. Regtested/bootstraped for
>> Linux/i686 and Linux/x86_64.
>>
>> Date: Fri, 4 Sep 2015 08:53:23 -0700
>> Subject: [PATCH] Implement x86 interrupt attribute
>>
>> The interrupt and exception handlers are called by x86 processors.  X86
>> hardware pushes information onto stack and calls the handler.  The
>> requirements are
>>
>> 1. Both interrupt and exception handlers must use the 'IRET' instruction,
>> instead of the 'RET' instruction, to return from the handlers.
>> 2. All registers are callee-saved in interrupt and exception handlers.
>> 3. The difference between interrupt and exception handlers is the
>> exception handler must pop 'ERROR_CODE' off the stack before the 'IRET'
>> instruction.
>>
>> The design goals of interrupt and exception handlers for x86 processors
>> are:
>>
>> 1. Support both 32-bit and 64-bit modes.
>> 2. Flexible for compilers to optimize.
>> 3. Easy to use by programmers.
>>
>> To implement interrupt and exception handlers for x86 processors, a
>> compiler should support:
>>
>> 'interrupt' attribute
>>
>> Use this attribute to indicate that the specified function with
>> mandatory arguments is an interrupt or exception handler.  The compiler
>> generates function entry and exit sequences suitable for use in an
>> interrupt handler when this attribute is present.  The 'IRET' instruction,
>> instead of the 'RET' instruction, is used to return from interrupt or
>> exception handlers.  All registers, except for the EFLAGS register which
>> is restored by the 'IRET' instruction, are preserved by the compiler.
>>
>> Any interruptible-without-stack-switch code must be compiled with
>> -mno-red-zone since interrupt handlers can and will, because of the
>> hardware design, touch the red zone.
>>
>> 1. interrupt handler must be declared with a mandatory pointer argument:
>>
>> struct interrupt_frame;
>>
>> __attribute__ ((interrupt))
>> void
>> f (struct interrupt_frame *frame)
>> {
>> ...
>> }
>>
>> and user must properly define the structure the pointer pointing to.
>>
>> 2. exception handler:
>>
>> The exception handler is very similar to the interrupt handler with
>> a different mandatory function signature:
>>
>> typedef unsigned long long int uword_t;
>> typedef unsigned int uword_t;
>>
>> struct interrupt_frame;
>>
>> __attribute__ ((interrupt))
>> void
>> f (struct interrupt_frame *frame, uword_t error_code)
>> {
>> ...
>> }
>>
>> and compiler pops the error code off stack before the 'IRET' instruction.
>>
>> The exception handler should only be used for exceptions which push an
>> error code and all other exceptions must use the interrupt handler.
>> The system will crash if the wrong handler is used.
>>
>> To be feature complete, compiler may implement the optional
>> 'no_caller_saved_registers' attribute:
>>
>> Use this attribute to indicate that the specified function has no
>> caller-saved registers.  That is, all registers are callee-saved.
>> The compiler generates proper function entry and exit sequences to
>> save and restore any modified registers.
>>
>> The user can call functions specified with 'no_caller_saved_registers'
>> attribute from an interrupt handler without saving and restoring all
>> call clobbered registers.
>
> Looking a bit deeper into the code, it looks that we want to realign
> the stack in the interrupt handler. Let's assume that interrupt
> handler is calling some other function that saves SSE vector regs to
> the stack. According to the x86 ABI, incoming stack of the called
> function is assumed to be aligned to 16 bytes. But, interrupt handler
> violates this assumption, since the stack could be aligned to only 4
> bytes for 32bit and 8 bytes for 64bit targets. Entering the called
> function with stack, aligned to less than 16 bytes will certainly
> violate ABI.
>
> So, it looks to me that we need to realign the stack in the interrupt
> handler unconditionally to 16bytes. In this case, we also won't need
> the following changes:
>

Current stack alignment implementation requires at least
one, maybe two, scratch registers:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67841

Extend it to the interrupt handler, which doesn't have any scratch
registers may require significant changes in backend as well as
register allocator.

-- 
H.J.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-10-04 18:15                                         ` H.J. Lu
@ 2015-10-04 20:01                                           ` Uros Bizjak
  2015-10-04 20:51                                             ` H.J. Lu
  2015-10-05  9:00                                           ` Mike Stump
  1 sibling, 1 reply; 45+ messages in thread
From: Uros Bizjak @ 2015-10-04 20:01 UTC (permalink / raw)
  To: H.J. Lu; +Cc: Yulia Koval, Mike Stump, GCC Patches

On Sun, Oct 4, 2015 at 8:15 PM, H.J. Lu <hjl.tools@gmail.com> wrote:

>> Looking a bit deeper into the code, it looks that we want to realign
>> the stack in the interrupt handler. Let's assume that interrupt
>> handler is calling some other function that saves SSE vector regs to
>> the stack. According to the x86 ABI, incoming stack of the called
>> function is assumed to be aligned to 16 bytes. But, interrupt handler
>> violates this assumption, since the stack could be aligned to only 4
>> bytes for 32bit and 8 bytes for 64bit targets. Entering the called
>> function with stack, aligned to less than 16 bytes will certainly
>> violate ABI.
>>
>> So, it looks to me that we need to realign the stack in the interrupt
>> handler unconditionally to 16bytes. In this case, we also won't need
>> the following changes:
>>
>
> Current stack alignment implementation requires at least
> one, maybe two, scratch registers:
>
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67841
>
> Extend it to the interrupt handler, which doesn't have any scratch
> registers may require significant changes in backend as well as
> register allocator.

But without realignment, the handler is unusable for anything but
simple functions. The handler will crash when called function will try
to save vector reg to stack.

Uros.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-10-04 20:01                                           ` Uros Bizjak
@ 2015-10-04 20:51                                             ` H.J. Lu
  2015-10-04 21:07                                               ` Uros Bizjak
  0 siblings, 1 reply; 45+ messages in thread
From: H.J. Lu @ 2015-10-04 20:51 UTC (permalink / raw)
  To: Uros Bizjak; +Cc: Yulia Koval, Mike Stump, GCC Patches

On Sun, Oct 4, 2015 at 1:00 PM, Uros Bizjak <ubizjak@gmail.com> wrote:
> On Sun, Oct 4, 2015 at 8:15 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>
>>> Looking a bit deeper into the code, it looks that we want to realign
>>> the stack in the interrupt handler. Let's assume that interrupt
>>> handler is calling some other function that saves SSE vector regs to
>>> the stack. According to the x86 ABI, incoming stack of the called
>>> function is assumed to be aligned to 16 bytes. But, interrupt handler
>>> violates this assumption, since the stack could be aligned to only 4
>>> bytes for 32bit and 8 bytes for 64bit targets. Entering the called
>>> function with stack, aligned to less than 16 bytes will certainly
>>> violate ABI.
>>>
>>> So, it looks to me that we need to realign the stack in the interrupt
>>> handler unconditionally to 16bytes. In this case, we also won't need
>>> the following changes:
>>>
>>
>> Current stack alignment implementation requires at least
>> one, maybe two, scratch registers:
>>
>> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67841
>>
>> Extend it to the interrupt handler, which doesn't have any scratch
>> registers may require significant changes in backend as well as
>> register allocator.
>
> But without realignment, the handler is unusable for anything but
> simple functions. The handler will crash when called function will try
> to save vector reg to stack.
>

We can use unaligned load and store to avoid crash.

-- 
H.J.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-10-04 20:51                                             ` H.J. Lu
@ 2015-10-04 21:07                                               ` Uros Bizjak
  2015-10-04 23:17                                                 ` H.J. Lu
  0 siblings, 1 reply; 45+ messages in thread
From: Uros Bizjak @ 2015-10-04 21:07 UTC (permalink / raw)
  To: H.J. Lu; +Cc: Yulia Koval, Mike Stump, GCC Patches

On Sun, Oct 4, 2015 at 10:51 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Sun, Oct 4, 2015 at 1:00 PM, Uros Bizjak <ubizjak@gmail.com> wrote:
>> On Sun, Oct 4, 2015 at 8:15 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>
>>>> Looking a bit deeper into the code, it looks that we want to realign
>>>> the stack in the interrupt handler. Let's assume that interrupt
>>>> handler is calling some other function that saves SSE vector regs to
>>>> the stack. According to the x86 ABI, incoming stack of the called
>>>> function is assumed to be aligned to 16 bytes. But, interrupt handler
>>>> violates this assumption, since the stack could be aligned to only 4
>>>> bytes for 32bit and 8 bytes for 64bit targets. Entering the called
>>>> function with stack, aligned to less than 16 bytes will certainly
>>>> violate ABI.
>>>>
>>>> So, it looks to me that we need to realign the stack in the interrupt
>>>> handler unconditionally to 16bytes. In this case, we also won't need
>>>> the following changes:
>>>>
>>>
>>> Current stack alignment implementation requires at least
>>> one, maybe two, scratch registers:
>>>
>>> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67841
>>>
>>> Extend it to the interrupt handler, which doesn't have any scratch
>>> registers may require significant changes in backend as well as
>>> register allocator.
>>
>> But without realignment, the handler is unusable for anything but
>> simple functions. The handler will crash when called function will try
>> to save vector reg to stack.
>>
>
> We can use unaligned load and store to avoid crash.

Oh, sorry, I meant "called function will crash", like:

-> interrupt when %rsp = 0x...8 ->
-> interrupt handler ->
-> calls some function that tries to save xmm reg to stack
-> crash in the called function

Uros.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-10-04 21:07                                               ` Uros Bizjak
@ 2015-10-04 23:17                                                 ` H.J. Lu
  2015-10-05  9:29                                                   ` Uros Bizjak
  0 siblings, 1 reply; 45+ messages in thread
From: H.J. Lu @ 2015-10-04 23:17 UTC (permalink / raw)
  To: Uros Bizjak; +Cc: Yulia Koval, Mike Stump, GCC Patches

On Sun, Oct 4, 2015 at 2:07 PM, Uros Bizjak <ubizjak@gmail.com> wrote:
> On Sun, Oct 4, 2015 at 10:51 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>> On Sun, Oct 4, 2015 at 1:00 PM, Uros Bizjak <ubizjak@gmail.com> wrote:
>>> On Sun, Oct 4, 2015 at 8:15 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>>
>>>>> Looking a bit deeper into the code, it looks that we want to realign
>>>>> the stack in the interrupt handler. Let's assume that interrupt
>>>>> handler is calling some other function that saves SSE vector regs to
>>>>> the stack. According to the x86 ABI, incoming stack of the called
>>>>> function is assumed to be aligned to 16 bytes. But, interrupt handler
>>>>> violates this assumption, since the stack could be aligned to only 4
>>>>> bytes for 32bit and 8 bytes for 64bit targets. Entering the called
>>>>> function with stack, aligned to less than 16 bytes will certainly
>>>>> violate ABI.
>>>>>
>>>>> So, it looks to me that we need to realign the stack in the interrupt
>>>>> handler unconditionally to 16bytes. In this case, we also won't need
>>>>> the following changes:
>>>>>
>>>>
>>>> Current stack alignment implementation requires at least
>>>> one, maybe two, scratch registers:
>>>>
>>>> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67841
>>>>
>>>> Extend it to the interrupt handler, which doesn't have any scratch
>>>> registers may require significant changes in backend as well as
>>>> register allocator.
>>>
>>> But without realignment, the handler is unusable for anything but
>>> simple functions. The handler will crash when called function will try
>>> to save vector reg to stack.
>>>
>>
>> We can use unaligned load and store to avoid crash.
>
> Oh, sorry, I meant "called function will crash", like:
>
> -> interrupt when %rsp = 0x...8 ->
> -> interrupt handler ->
> -> calls some function that tries to save xmm reg to stack
> -> crash in the called function
>

It should be fixed by this patch.   But we need to fix stack
alignment in interrupt handler to avoid scratch register.


-- 
H.J.
---
commit 15f48be1dc7ff48207927d0b835e593d058f695b
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Sun Oct 4 16:14:03 2015 -0700

    Correctly set incoming stack boundary for interrupt handler

diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 7ebdcd9..0f0cc3c 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -12037,8 +12037,11 @@ ix86_minimum_incoming_stack_boundary (bool sibcall)
 {
   unsigned int incoming_stack_boundary;

+  /* Stack of interrupt handler is always aligned to word_mode.  */
+  if (cfun->machine->func_type != TYPE_NORMAL)
+    incoming_stack_boundary = TARGET_64BIT ? 64 : 32;
   /* Prefer the one specified at command line. */
-  if (ix86_user_incoming_stack_boundary)
+  else if (ix86_user_incoming_stack_boundary)
     incoming_stack_boundary = ix86_user_incoming_stack_boundary;
   /* In 32bit, use MIN_STACK_BOUNDARY for incoming stack boundary
      if -mstackrealign is used, it isn't used for sibcall check and

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-10-04 18:15                                         ` H.J. Lu
  2015-10-04 20:01                                           ` Uros Bizjak
@ 2015-10-05  9:00                                           ` Mike Stump
  1 sibling, 0 replies; 45+ messages in thread
From: Mike Stump @ 2015-10-05  9:00 UTC (permalink / raw)
  To: H.J. Lu; +Cc: Uros Bizjak, Yulia Koval, GCC Patches

On Oct 4, 2015, at 11:15 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
> Current stack alignment implementation requires at least
> one, maybe two, scratch registers:

So, I have some cases where I need scratch registers as well.  I always save 2 registers and they go first (and restore last), so I can always use them.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-10-04 23:17                                                 ` H.J. Lu
@ 2015-10-05  9:29                                                   ` Uros Bizjak
  2015-10-13 12:18                                                     ` Yulia Koval
  0 siblings, 1 reply; 45+ messages in thread
From: Uros Bizjak @ 2015-10-05  9:29 UTC (permalink / raw)
  To: H.J. Lu; +Cc: Yulia Koval, Mike Stump, GCC Patches

On Mon, Oct 5, 2015 at 1:17 AM, H.J. Lu <hjl.tools@gmail.com> wrote:

>>>>>> Looking a bit deeper into the code, it looks that we want to realign
>>>>>> the stack in the interrupt handler. Let's assume that interrupt
>>>>>> handler is calling some other function that saves SSE vector regs to
>>>>>> the stack. According to the x86 ABI, incoming stack of the called
>>>>>> function is assumed to be aligned to 16 bytes. But, interrupt handler
>>>>>> violates this assumption, since the stack could be aligned to only 4
>>>>>> bytes for 32bit and 8 bytes for 64bit targets. Entering the called
>>>>>> function with stack, aligned to less than 16 bytes will certainly
>>>>>> violate ABI.
>>>>>>
>>>>>> So, it looks to me that we need to realign the stack in the interrupt
>>>>>> handler unconditionally to 16bytes. In this case, we also won't need
>>>>>> the following changes:
>>>>>>
>>>>>
>>>>> Current stack alignment implementation requires at least
>>>>> one, maybe two, scratch registers:
>>>>>
>>>>> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67841
>>>>>
>>>>> Extend it to the interrupt handler, which doesn't have any scratch
>>>>> registers may require significant changes in backend as well as
>>>>> register allocator.
>>>>
>>>> But without realignment, the handler is unusable for anything but
>>>> simple functions. The handler will crash when called function will try
>>>> to save vector reg to stack.
>>>>
>>>
>>> We can use unaligned load and store to avoid crash.
>>
>> Oh, sorry, I meant "called function will crash", like:
>>
>> -> interrupt when %rsp = 0x...8 ->
>> -> interrupt handler ->
>> -> calls some function that tries to save xmm reg to stack
>> -> crash in the called function
>>
>
> It should be fixed by this patch.   But we need to fix stack
> alignment in interrupt handler to avoid scratch register.
>
>
> --
> H.J.
> ---
> commit 15f48be1dc7ff48207927d0b835e593d058f695b
> Author: H.J. Lu <hjl.tools@gmail.com>
> Date:   Sun Oct 4 16:14:03 2015 -0700
>
>     Correctly set incoming stack boundary for interrupt handler
>
> diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
> index 7ebdcd9..0f0cc3c 100644
> --- a/gcc/config/i386/i386.c
> +++ b/gcc/config/i386/i386.c
> @@ -12037,8 +12037,11 @@ ix86_minimum_incoming_stack_boundary (bool sibcall)
>  {
>    unsigned int incoming_stack_boundary;
>
> +  /* Stack of interrupt handler is always aligned to word_mode.  */
> +  if (cfun->machine->func_type != TYPE_NORMAL)
> +    incoming_stack_boundary = TARGET_64BIT ? 64 : 32;

Just a heads up that in order to support stack realignmnent on x86_64,
MIN_STACK_BOUNDARY will soon be changed to BITS_PER_WORD, so you can
use it in the line above. Please see comment #5 and #6 of PR 66697
[1].

>    /* Prefer the one specified at command line. */
> -  if (ix86_user_incoming_stack_boundary)
> +  else if (ix86_user_incoming_stack_boundary)
>      incoming_stack_boundary = ix86_user_incoming_stack_boundary;
>    /* In 32bit, use MIN_STACK_BOUNDARY for incoming stack boundary
>       if -mstackrealign is used, it isn't used for sibcall check and

[1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66697

Uros.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-10-05  9:29                                                   ` Uros Bizjak
@ 2015-10-13 12:18                                                     ` Yulia Koval
  2015-10-20 13:25                                                       ` Yulia Koval
  0 siblings, 1 reply; 45+ messages in thread
From: Yulia Koval @ 2015-10-13 12:18 UTC (permalink / raw)
  To: Uros Bizjak; +Cc: H.J. Lu, Mike Stump, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 6216 bytes --]

Here is the current version of the patch with all the fixes.
Regtested\bootstraped it on 64 bit.

We need a pointer since interrupt handler will update data pointing
to by frame.  Since error_code isn't at the normal location where the
parameter is passed on stack and frame isn't in a hard register, we
changed ix86_function_arg:

+  if (!cum->caller && cfun->machine->func_type != TYPE_NORMAL)
+    {
+      /* The first argument of interrupt handler is a pointer and
+        points to the return address slot on stack.  The optional
+        second argument is an integer for error code on stack.  */
+      gcc_assert (type != NULL_TREE);
+      if (POINTER_TYPE_P (type))
+       {
+         if (cfun->machine->func_type == TYPE_EXCEPTION)
+           /* (AP) in the current frame in exception handler.  */
+           arg = arg_pointer_rtx;
+         else
+           /* -WORD(AP) in the current frame in interrupt handler.  */
+           arg = force_reg (Pmode,
+                            plus_constant (Pmode, arg_pointer_rtx,
+                                           -UNITS_PER_WORD));
+         if (mode != Pmode)
+           arg = convert_to_mode (mode, arg, 1);
+       }
+      else
+       {
+         gcc_assert (TREE_CODE (type) == INTEGER_TYPE
+                     && cfun->machine->func_type == TYPE_EXCEPTION
+                     && mode == word_mode);
+         /* The error code is at -WORD(AP) in the current frame in
+            exception handler.  */
+         arg = gen_rtx_MEM (word_mode,
+                            plus_constant (Pmode, arg_pointer_rtx,
+                                           -UNITS_PER_WORD));
+       }
+
+      return arg;
+    }
+

to return a pseudo register.  It violates

   Return where to put the arguments to a function.
   Return zero to push the argument on the stack, or a hard register in
   which to store the argument.

Register allocator has no problem with parameters in pseudo registers.
But GCC crashes when it tries to access DECL_INCOMING_RTL as a hard
register when generating debug information.  We worked around it by
doing

+
+  if (cfun->machine->func_type != TYPE_NORMAL)
+    {
+      /* Since the pointer argument of interrupt handler isn't a real
+         argument, adjust DECL_INCOMING_RTL for debug output.  */
+      tree arg = DECL_ARGUMENTS (current_function_decl);
+      gcc_assert (arg != NULL_TREE
+                 && POINTER_TYPE_P (TREE_TYPE (arg)));
+      if (cfun->machine->func_type == TYPE_EXCEPTION)
+       /* (AP) in the current frame in exception handler.  */
+       DECL_INCOMING_RTL (arg) = arg_pointer_rtx;
+      else
+       /* -WORD(AP) in the current frame in interrupt handler.  */
+       DECL_INCOMING_RTL (arg) = plus_constant (Pmode,
+                                                arg_pointer_rtx,
+                                                -UNITS_PER_WORD);
+    }


On Mon, Oct 5, 2015 at 12:29 PM, Uros Bizjak <ubizjak@gmail.com> wrote:
> On Mon, Oct 5, 2015 at 1:17 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
>
>>>>>>> Looking a bit deeper into the code, it looks that we want to realign
>>>>>>> the stack in the interrupt handler. Let's assume that interrupt
>>>>>>> handler is calling some other function that saves SSE vector regs to
>>>>>>> the stack. According to the x86 ABI, incoming stack of the called
>>>>>>> function is assumed to be aligned to 16 bytes. But, interrupt handler
>>>>>>> violates this assumption, since the stack could be aligned to only 4
>>>>>>> bytes for 32bit and 8 bytes for 64bit targets. Entering the called
>>>>>>> function with stack, aligned to less than 16 bytes will certainly
>>>>>>> violate ABI.
>>>>>>>
>>>>>>> So, it looks to me that we need to realign the stack in the interrupt
>>>>>>> handler unconditionally to 16bytes. In this case, we also won't need
>>>>>>> the following changes:
>>>>>>>
>>>>>>
>>>>>> Current stack alignment implementation requires at least
>>>>>> one, maybe two, scratch registers:
>>>>>>
>>>>>> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67841
>>>>>>
>>>>>> Extend it to the interrupt handler, which doesn't have any scratch
>>>>>> registers may require significant changes in backend as well as
>>>>>> register allocator.
>>>>>
>>>>> But without realignment, the handler is unusable for anything but
>>>>> simple functions. The handler will crash when called function will try
>>>>> to save vector reg to stack.
>>>>>
>>>>
>>>> We can use unaligned load and store to avoid crash.
>>>
>>> Oh, sorry, I meant "called function will crash", like:
>>>
>>> -> interrupt when %rsp = 0x...8 ->
>>> -> interrupt handler ->
>>> -> calls some function that tries to save xmm reg to stack
>>> -> crash in the called function
>>>
>>
>> It should be fixed by this patch.   But we need to fix stack
>> alignment in interrupt handler to avoid scratch register.
>>
>>
>> --
>> H.J.
>> ---
>> commit 15f48be1dc7ff48207927d0b835e593d058f695b
>> Author: H.J. Lu <hjl.tools@gmail.com>
>> Date:   Sun Oct 4 16:14:03 2015 -0700
>>
>>     Correctly set incoming stack boundary for interrupt handler
>>
>> diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
>> index 7ebdcd9..0f0cc3c 100644
>> --- a/gcc/config/i386/i386.c
>> +++ b/gcc/config/i386/i386.c
>> @@ -12037,8 +12037,11 @@ ix86_minimum_incoming_stack_boundary (bool sibcall)
>>  {
>>    unsigned int incoming_stack_boundary;
>>
>> +  /* Stack of interrupt handler is always aligned to word_mode.  */
>> +  if (cfun->machine->func_type != TYPE_NORMAL)
>> +    incoming_stack_boundary = TARGET_64BIT ? 64 : 32;
>
> Just a heads up that in order to support stack realignmnent on x86_64,
> MIN_STACK_BOUNDARY will soon be changed to BITS_PER_WORD, so you can
> use it in the line above. Please see comment #5 and #6 of PR 66697
> [1].
>
>>    /* Prefer the one specified at command line. */
>> -  if (ix86_user_incoming_stack_boundary)
>> +  else if (ix86_user_incoming_stack_boundary)
>>      incoming_stack_boundary = ix86_user_incoming_stack_boundary;
>>    /* In 32bit, use MIN_STACK_BOUNDARY for incoming stack boundary
>>       if -mstackrealign is used, it isn't used for sibcall check and
>
> [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66697
>
> Uros.

[-- Attachment #2: patch --]
[-- Type: application/octet-stream, Size: 82418 bytes --]

diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index 6a17ef4..c7ff393 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -278,6 +278,8 @@ extern rtx maybe_get_pool_constant (rtx);
 extern char internal_label_prefix[16];
 extern int internal_label_prefix_len;
 
+extern bool ix86_epilogue_uses (int);
+
 enum ix86_address_seg { SEG_DEFAULT, SEG_FS, SEG_GS };
 struct ix86_address
 {
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 419966d..4c25c9e 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -2473,6 +2473,8 @@ struct ix86_frame
 {
   int nsseregs;
   int nregs;
+  int nbndregs;
+  int nmaskregs;
   int va_arg_size;
   int red_zone_size;
   int outgoing_arguments_size;
@@ -2483,6 +2485,8 @@ struct ix86_frame
   HOST_WIDE_INT stack_pointer_offset;
   HOST_WIDE_INT hfp_save_offset;
   HOST_WIDE_INT reg_save_offset;
+  HOST_WIDE_INT bnd_reg_save_offset;
+  HOST_WIDE_INT mask_reg_save_offset;
   HOST_WIDE_INT sse_reg_save_offset;
 
   /* When save_regs_using_mov is set, emit prologue using
@@ -5570,6 +5574,13 @@ ix86_conditional_register_usage (void)
 {
   int i, c_mask;
 
+  /* If there are no caller-saved registers, preserve all registers.
+     except fixed_regs.  */
+  if (cfun && cfun->machine->no_caller_saved_registers)
+    for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+      if (!fixed_regs[i])
+	call_used_regs[i] = 0;
+
   /* For 32-bit targets, squash the REX registers.  */
   if (! TARGET_64BIT)
     {
@@ -6349,6 +6360,21 @@ ix86_set_current_function (tree fndecl)
       return;
     }
 
+  if (lookup_attribute ("interrupt", DECL_ATTRIBUTES (fndecl)))
+    {
+      int nargs = 0;
+      for (tree arg = DECL_ARGUMENTS (fndecl);
+	   arg;
+	   arg = TREE_CHAIN (arg))
+	nargs++;
+      cfun->machine->no_caller_saved_registers = true;
+      cfun->machine->func_type
+	= nargs == 2 ? TYPE_EXCEPTION : TYPE_INTERRUPT;
+    }
+  else if (lookup_attribute ("no_caller_saved_registers",
+			     DECL_ATTRIBUTES (fndecl)))
+    cfun->machine->no_caller_saved_registers = true;
+
   tree new_tree = DECL_FUNCTION_SPECIFIC_TARGET (fndecl);
   if (new_tree == NULL_TREE)
     new_tree = target_option_default_node;
@@ -6372,6 +6398,11 @@ ix86_set_current_function (tree fndecl)
       && (call_used_regs[SI_REG]
 	  == (cfun->machine->call_abi == MS_ABI)))
     reinit_regs ();
+  /* AX register is only preserved if there are no caller-saved
+     registers.  */
+  else if (cfun->machine->no_caller_saved_registers
+	   == call_used_regs[AX_REG])
+    reinit_regs ();
 }
 
 \f
@@ -6635,6 +6666,10 @@ ix86_function_ok_for_sibcall (tree decl, tree exp)
   tree type, decl_or_type;
   rtx a, b;
 
+  /* Sibling call isn't OK if there are no caller-saved registers.  */
+  if (cfun->machine->no_caller_saved_registers)
+    return false;
+
   /* If we are generating position-independent code, we cannot sibcall
      optimize direct calls to global functions, as the PLT requires
      %ebx be live. (Darwin does not have a PLT.)  */
@@ -7808,6 +7843,8 @@ type_natural_mode (const_tree type, const CUMULATIVE_ARGS *cum,
 		      }
 		  }
 		else if ((size == 8 && !TARGET_64BIT)
+			 && (!cfun
+			     || cfun->machine->func_type == TYPE_NORMAL)
 			 && !TARGET_MMX
 			 && !TARGET_IAMCU)
 		  {
@@ -8787,6 +8824,11 @@ ix86_function_arg_advance (cumulative_args_t cum_v, machine_mode mode,
   HOST_WIDE_INT bytes, words;
   int nregs;
 
+  /* The argument of interrupt handler is a special case and is
+     handled in ix86_function_arg.  */
+  if (!cum->caller && cfun->machine->func_type != TYPE_NORMAL)
+    return;
+
   if (mode == BLKmode)
     bytes = int_size_in_bytes (type);
   else
@@ -9103,6 +9145,40 @@ ix86_function_arg (cumulative_args_t cum_v, machine_mode omode,
   HOST_WIDE_INT bytes, words;
   rtx arg;
 
+  if (!cum->caller && cfun->machine->func_type != TYPE_NORMAL)
+    {
+      /* The first argument of interrupt handler is a pointer and
+	 points to the return address slot on stack.  The optional
+	 second argument is an integer for error code on stack.  */
+      gcc_assert (type != NULL_TREE);
+      if (POINTER_TYPE_P (type))
+	{
+	  if (cfun->machine->func_type == TYPE_EXCEPTION)
+	    /* (AP) in the current frame in exception handler.  */
+	    arg = arg_pointer_rtx;
+	  else
+	    /* -WORD(AP) in the current frame in interrupt handler.  */
+	    arg = force_reg (Pmode,
+			     plus_constant (Pmode, arg_pointer_rtx,
+					    -UNITS_PER_WORD));
+	  if (mode != Pmode)
+	    arg = convert_to_mode (mode, arg, 1);
+	}
+      else
+	{
+	  gcc_assert (TREE_CODE (type) == INTEGER_TYPE
+		      && cfun->machine->func_type == TYPE_EXCEPTION
+		      && mode == word_mode);
+	  /* The error code is at -WORD(AP) in the current frame in
+	     exception handler.  */
+	  arg = gen_rtx_MEM (word_mode,
+			     plus_constant (Pmode, arg_pointer_rtx,
+					    -UNITS_PER_WORD));
+	}
+
+      return arg;
+    }
+
   /* All pointer bounds arguments are handled separately here.  */
   if ((type && POINTER_BOUNDS_TYPE_P (type))
       || POINTER_BOUNDS_MODE_P (mode))
@@ -10818,7 +10894,10 @@ ix86_can_use_return_insn_p (void)
 {
   struct ix86_frame frame;
 
-  if (! reload_completed || frame_pointer_needed)
+  /* Don't use `ret' instruction in interrupt handler.  */
+  if (! reload_completed
+      || frame_pointer_needed
+      || cfun->machine->func_type != TYPE_NORMAL)
     return 0;
 
   /* Don't allow more than 32k pop, since that's all we can do
@@ -11128,11 +11207,52 @@ ix86_select_alt_pic_regnum (void)
   return INVALID_REGNUM;
 }
 
+/* Return true if REGNO is used by the epilogue.  */
+
+bool
+ix86_epilogue_uses (int regno)
+{
+  /* If there are no caller-saved registers, we preserve all registers,
+     except for MMX and x87 registers which aren't supported when saving
+     and restoring registers.  Don't explicitly save SP register since
+     it is always preserved.  */
+  return (epilogue_completed
+	  && cfun->machine->no_caller_saved_registers
+	  && !fixed_regs[regno]
+	  && !STACK_REGNO_P (regno)
+	  && !MMX_REGNO_P (regno));
+}
+
+/* Return TRUE if register REGNO is ever defined..  */
+
+static bool
+ix86_reg_ever_defined_p (unsigned int regno)
+{
+  df_ref def;
+
+  for (def = DF_REG_DEF_CHAIN (regno); def; def = DF_REF_NEXT_REG (def))
+    if (!DF_REF_IS_ARTIFICIAL (def))
+      return true;
+
+  return false;
+}
+
 /* Return TRUE if we need to save REGNO.  */
 
 static bool
 ix86_save_reg (unsigned int regno, bool maybe_eh_return)
 {
+  /* If there are no caller-saved registers, we preserve all registers,
+     except for MMX and x87 registers which aren't supported when saving
+     and restoring registers.  Don't explicitly save SP register since
+     it is always preserved.  */
+  if (reload_completed && cfun->machine->no_caller_saved_registers)
+    return (df_regs_ever_live_p (regno)
+	    && ix86_reg_ever_defined_p (regno)
+	    && !fixed_regs[regno]
+	    && !STACK_REGNO_P (regno)
+	    && !MMX_REGNO_P (regno));
+
   if (regno == REAL_PIC_OFFSET_TABLE_REGNUM
       && pic_offset_table_rtx)
     {
@@ -11189,6 +11309,34 @@ ix86_nsaved_regs (void)
   return nregs;
 }
 
+/* Return number of saved bound registers.  */
+
+static int
+ix86_nsaved_bndregs (void)
+{
+  int nregs = 0;
+  int regno;
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (BND_REGNO_P (regno) && ix86_save_reg (regno, true))
+      nregs ++;
+  return nregs;
+}
+
+/* Return number of saved mask registers.  */
+
+static int
+ix86_nsaved_maskregs (void)
+{
+  int nregs = 0;
+  int regno;
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (MASK_REGNO_P (regno) && ix86_save_reg (regno, true))
+      nregs ++;
+  return nregs;
+}
+
 /* Return number of saved SSE registers.  */
 
 static int
@@ -11197,7 +11345,10 @@ ix86_nsaved_sseregs (void)
   int nregs = 0;
   int regno;
 
-  if (!TARGET_64BIT_MS_ABI)
+  /* Always need to save SSE registers if there are no caller-saved
+     registers.  */
+  if (!TARGET_64BIT_MS_ABI
+      && !cfun->machine->no_caller_saved_registers)
     return 0;
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
@@ -11278,6 +11429,8 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   HOST_WIDE_INT to_allocate;
 
   frame->nregs = ix86_nsaved_regs ();
+  frame->nbndregs = ix86_nsaved_bndregs ();
+  frame->nmaskregs = ix86_nsaved_maskregs ();
   frame->nsseregs = ix86_nsaved_sseregs ();
 
   /* 64-bit MS ABI seem to require stack alignment to be always 16 except for
@@ -11379,19 +11532,39 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   if (TARGET_SEH)
     frame->hard_frame_pointer_offset = offset;
 
+  /* Set BND register save area.  */
+  if (frame->nbndregs)
+    offset += frame->nbndregs * (ix86_pmode == PMODE_DI ? 16 : 8);
+  frame->bnd_reg_save_offset = offset;
+
+  /* Set MASK register save area.  */
+  if (frame->nmaskregs)
+    offset += frame->nmaskregs * (TARGET_AVX512BW ? 8 : 2);
+  frame->mask_reg_save_offset = offset;
+
   /* Align and set SSE register save area.  */
   if (frame->nsseregs)
     {
+      /* We must save full vector registers if there are no
+	 caller-saved registers.  */
+      unsigned int vec_regsize;
+      if (cfun->machine->no_caller_saved_registers)
+	vec_regsize = (TARGET_AVX512F ? 64 : (TARGET_AVX ? 32 : 16));
+      else
+	vec_regsize = 16;
+
       /* The only ABI that has saved SSE registers (Win64) also has a
 	 16-byte aligned default stack, and thus we don't need to be
 	 within the re-aligned local stack frame to save them.  In case
 	 incoming stack boundary is aligned to less than 16 bytes,
 	 unaligned move of SSE register will be emitted, so there is
 	 no point to round up the SSE register save area outside the
-	 re-aligned local stack frame to 16 bytes.  */
-      if (ix86_incoming_stack_boundary >= 128)
-	offset = ROUND_UP (offset, 16);
-      offset += frame->nsseregs * 16;
+	 re-aligned local stack frame to 16 bytes.  When full vector
+	 registers are saved, we check incoming stack boundary against
+	 the size of vector registers.  */
+      if (ix86_incoming_stack_boundary >= (vec_regsize * BITS_PER_UNIT))
+	offset = ROUND_UP (offset, vec_regsize);
+      offset += frame->nsseregs * vec_regsize;
     }
   frame->sse_reg_save_offset = offset;
 
@@ -11445,7 +11618,7 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   frame->stack_pointer_offset = offset;
 
   /* Size prologue needs to allocate.  */
-  to_allocate = offset - frame->sse_reg_save_offset;
+  to_allocate = offset - frame->bnd_reg_save_offset;
 
   if ((!to_allocate && frame->nregs <= 1)
       || (TARGET_64BIT && to_allocate >= (HOST_WIDE_INT) 0x80000000))
@@ -11699,18 +11872,67 @@ ix86_emit_save_regs_using_mov (HOST_WIDE_INT cfa_offset)
       }
 }
 
+/* Emit code to save BND registers using MOV insns.
+   First register is stored at CFA - CFA_OFFSET.  */
+static void
+ix86_emit_save_bnd_regs_using_mov (HOST_WIDE_INT cfa_offset)
+{
+  unsigned int regno;
+  enum machine_mode mode = BNDmode;
+  unsigned int size = GET_MODE_SIZE (mode);
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (BND_REGNO_P (regno) && ix86_save_reg (regno, true))
+      {
+	ix86_emit_save_reg_using_mov (mode, regno, cfa_offset);
+	cfa_offset -= size;
+      }
+}
+
+/* Emit code to save MASK registers using MOV insns.
+   First register is stored at CFA - CFA_OFFSET.  */
+static void
+ix86_emit_save_mask_regs_using_mov (HOST_WIDE_INT cfa_offset)
+{
+  unsigned int regno;
+  enum machine_mode mode = TARGET_AVX512BW ? DImode : HImode;
+  unsigned int size = GET_MODE_SIZE (mode);
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (MASK_REGNO_P (regno) && ix86_save_reg (regno, true))
+      {
+	ix86_emit_save_reg_using_mov (mode, regno, cfa_offset);
+	cfa_offset -= size;
+      }
+}
+
 /* Emit code to save SSE registers using MOV insns.
    First register is stored at CFA - CFA_OFFSET.  */
 static void
 ix86_emit_save_sse_regs_using_mov (HOST_WIDE_INT cfa_offset)
 {
   unsigned int regno;
+  enum machine_mode vector_reg_mode;
+
+  if (cfun->machine->no_caller_saved_registers)
+    {
+      /* We must save full vector registers if there are no
+	 caller-saved registers.  */
+      if (TARGET_AVX512F)
+	vector_reg_mode = V16SFmode;
+      else if (TARGET_AVX)
+	vector_reg_mode = V8SFmode;
+      else
+	vector_reg_mode = V4SFmode;
+    }
+  else
+    vector_reg_mode = V4SFmode;
 
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
       {
-	ix86_emit_save_reg_using_mov (V4SFmode, regno, cfa_offset);
-	cfa_offset -= GET_MODE_SIZE (V4SFmode);
+	ix86_emit_save_reg_using_mov (vector_reg_mode, regno, cfa_offset);
+	cfa_offset -= GET_MODE_SIZE (vector_reg_mode);
       }
 }
 
@@ -11866,13 +12088,17 @@ find_drap_reg (void)
 {
   tree decl = cfun->decl;
 
+  /* Always use callee-saved register if there are no caller-saved
+     registers.  */
   if (TARGET_64BIT)
     {
       /* Use R13 for nested function or function need static chain.
 	 Since function with tail call may use any caller-saved
 	 registers in epilogue, DRAP must not use caller-saved
 	 register in such case.  */
-      if (DECL_STATIC_CHAIN (decl) || crtl->tail_call_emit)
+      if (DECL_STATIC_CHAIN (decl)
+	  || cfun->machine->no_caller_saved_registers
+	  || crtl->tail_call_emit)
 	return R13_REG;
 
       return R10_REG;
@@ -11883,7 +12109,9 @@ find_drap_reg (void)
 	 Since function with tail call may use any caller-saved
 	 registers in epilogue, DRAP must not use caller-saved
 	 register in such case.  */
-      if (DECL_STATIC_CHAIN (decl) || crtl->tail_call_emit)
+      if (DECL_STATIC_CHAIN (decl)
+	  || cfun->machine->no_caller_saved_registers
+	  || crtl->tail_call_emit)
 	return DI_REG;
 
       /* Reuse static chain register if it isn't used for parameter
@@ -11924,8 +12152,12 @@ ix86_minimum_incoming_stack_boundary (bool sibcall)
 {
   unsigned int incoming_stack_boundary;
 
+  /* Stack of interrupt handler is always aligned to MIN_STACK_BOUNDARY.
+   */
+  if (cfun->machine->func_type != TYPE_NORMAL)
+    incoming_stack_boundary = MIN_STACK_BOUNDARY;
   /* Prefer the one specified at command line. */
-  if (ix86_user_incoming_stack_boundary)
+  else if (ix86_user_incoming_stack_boundary)
     incoming_stack_boundary = ix86_user_incoming_stack_boundary;
   /* In 32bit, use MIN_STACK_BOUNDARY for incoming stack boundary
      if -mstackrealign is used, it isn't used for sibcall check and
@@ -12556,6 +12788,8 @@ ix86_expand_prologue (void)
   struct ix86_frame frame;
   HOST_WIDE_INT allocate;
   bool int_registers_saved;
+  bool bnd_registers_saved;
+  bool mask_registers_saved;
   bool sse_registers_saved;
   rtx static_chain = NULL_RTX;
 
@@ -12721,6 +12955,8 @@ ix86_expand_prologue (void)
     }
 
   int_registers_saved = (frame.nregs == 0);
+  bnd_registers_saved = (frame.nbndregs == 0);
+  mask_registers_saved = (frame.nmaskregs == 0);
   sse_registers_saved = (frame.nsseregs == 0);
 
   if (frame_pointer_needed && !m->fs.fp_valid)
@@ -12785,10 +13021,10 @@ ix86_expand_prologue (void)
 	 that we must allocate the size of the register save area before
 	 performing the actual alignment.  Otherwise we cannot guarantee
 	 that there's enough storage above the realignment point.  */
-      if (m->fs.sp_offset != frame.sse_reg_save_offset)
+      if (m->fs.sp_offset != frame.bnd_reg_save_offset)
         pro_epilogue_adjust_stack (stack_pointer_rtx, stack_pointer_rtx,
 				   GEN_INT (m->fs.sp_offset
-					    - frame.sse_reg_save_offset),
+					    - frame.bnd_reg_save_offset),
 				   -1, false);
 
       /* Align the stack.  */
@@ -13015,6 +13251,10 @@ ix86_expand_prologue (void)
 
   if (!int_registers_saved)
     ix86_emit_save_regs_using_mov (frame.reg_save_offset);
+  if (!bnd_registers_saved)
+    ix86_emit_save_bnd_regs_using_mov (frame.bnd_reg_save_offset);
+  if (!mask_registers_saved)
+    ix86_emit_save_mask_regs_using_mov (frame.mask_reg_save_offset);
   if (!sse_registers_saved)
     ix86_emit_save_sse_regs_using_mov (frame.sse_reg_save_offset);
 
@@ -13060,6 +13300,23 @@ ix86_expand_prologue (void)
      combined with prologue modifications.  */
   if (TARGET_SEH)
     emit_insn (gen_prologue_use (stack_pointer_rtx));
+
+  if (cfun->machine->func_type != TYPE_NORMAL)
+    {
+      /* Since the pointer argument of interrupt handler isn't a real
+	 argument, adjust DECL_INCOMING_RTL for debug output.  */
+      tree arg = DECL_ARGUMENTS (current_function_decl);
+      gcc_assert (arg != NULL_TREE
+		  && POINTER_TYPE_P (TREE_TYPE (arg)));
+      if (cfun->machine->func_type == TYPE_EXCEPTION)
+	/* (AP) in the current frame in exception handler.  */
+	DECL_INCOMING_RTL (arg) = arg_pointer_rtx;
+      else
+	/* -WORD(AP) in the current frame in interrupt handler.  */
+	DECL_INCOMING_RTL (arg) = plus_constant (Pmode,
+						 arg_pointer_rtx,
+						 -UNITS_PER_WORD);
+    }
 }
 
 /* Emit code to restore REG using a POP insn.  */
@@ -13201,6 +13458,90 @@ ix86_emit_restore_regs_using_mov (HOST_WIDE_INT cfa_offset,
       }
 }
 
+/* Emit code to restore saved BND registers using MOV insns.
+   First register is restored from CFA - CFA_OFFSET.  */
+static void
+ix86_emit_restore_bnd_regs_using_mov (HOST_WIDE_INT cfa_offset,
+				      bool maybe_eh_return)
+{
+  struct machine_function *m = cfun->machine;
+  unsigned int regno;
+  enum machine_mode mode = BNDmode;
+  unsigned int size = GET_MODE_SIZE (mode);
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (BND_REGNO_P (regno) && ix86_save_reg (regno, maybe_eh_return))
+      {
+	rtx reg = gen_rtx_REG (mode, regno);
+	rtx mem;
+	rtx_insn *insn;
+
+	mem = choose_baseaddr (cfa_offset);
+	mem = gen_frame_mem (mode, mem);
+	insn = emit_move_insn (reg, mem);
+
+	if (m->fs.cfa_reg == crtl->drap_reg && regno == REGNO (crtl->drap_reg))
+	  {
+	    /* Previously we'd represented the CFA as an expression
+	       like *(%ebp - 8).  We've just popped that value from
+	       the stack, which means we need to reset the CFA to
+	       the drap register.  This will remain until we restore
+	       the stack pointer.  */
+	    add_reg_note (insn, REG_CFA_DEF_CFA, reg);
+	    RTX_FRAME_RELATED_P (insn) = 1;
+
+	    /* This means that the DRAP register is valid for addressing.  */
+	    m->fs.drap_valid = true;
+	  }
+	else
+	  ix86_add_cfa_restore_note (NULL, reg, cfa_offset);
+
+	cfa_offset -= size;
+      }
+}
+
+/* Emit code to restore saved MASK registers using MOV insns.
+   First register is restored from CFA - CFA_OFFSET.  */
+static void
+ix86_emit_restore_mask_regs_using_mov (HOST_WIDE_INT cfa_offset,
+				       bool maybe_eh_return)
+{
+  struct machine_function *m = cfun->machine;
+  unsigned int regno;
+  enum machine_mode mode = TARGET_AVX512BW ? DImode : HImode;
+  unsigned int size = GET_MODE_SIZE (mode);
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (MASK_REGNO_P (regno) && ix86_save_reg (regno, maybe_eh_return))
+      {
+	rtx reg = gen_rtx_REG (mode, regno);
+	rtx mem;
+	rtx_insn *insn;
+
+	mem = choose_baseaddr (cfa_offset);
+	mem = gen_frame_mem (mode, mem);
+	insn = emit_move_insn (reg, mem);
+
+	if (m->fs.cfa_reg == crtl->drap_reg && regno == REGNO (crtl->drap_reg))
+	  {
+	    /* Previously we'd represented the CFA as an expression
+	       like *(%ebp - 8).  We've just popped that value from
+	       the stack, which means we need to reset the CFA to
+	       the drap register.  This will remain until we restore
+	       the stack pointer.  */
+	    add_reg_note (insn, REG_CFA_DEF_CFA, reg);
+	    RTX_FRAME_RELATED_P (insn) = 1;
+
+	    /* This means that the DRAP register is valid for addressing.  */
+	    m->fs.drap_valid = true;
+	  }
+	else
+	  ix86_add_cfa_restore_note (NULL, reg, cfa_offset);
+
+	cfa_offset -= size;
+      }
+}
+
 /* Emit code to restore saved registers using MOV insns.
    First register is restored from CFA - CFA_OFFSET.  */
 static void
@@ -13208,27 +13549,44 @@ ix86_emit_restore_sse_regs_using_mov (HOST_WIDE_INT cfa_offset,
 				      bool maybe_eh_return)
 {
   unsigned int regno;
+  enum machine_mode vector_reg_mode;
+
+  if (cfun->machine->no_caller_saved_registers)
+    {
+      /* We must restore full vector registers if there are no
+	 caller-saved registers.  */
+      if (TARGET_AVX512F)
+	vector_reg_mode = V16SFmode;
+      else if (TARGET_AVX)
+	vector_reg_mode = V8SFmode;
+      else
+	vector_reg_mode = V4SFmode;
+    }
+  else
+    vector_reg_mode = V4SFmode;
 
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, maybe_eh_return))
       {
-	rtx reg = gen_rtx_REG (V4SFmode, regno);
+	rtx reg = gen_rtx_REG (vector_reg_mode, regno);
 	rtx mem;
 	unsigned int align;
 
 	mem = choose_baseaddr (cfa_offset);
-	mem = gen_rtx_MEM (V4SFmode, mem);
+	mem = gen_rtx_MEM (vector_reg_mode, mem);
 
 	/* The location is aligned up to INCOMING_STACK_BOUNDARY.  */
-	align = MIN (GET_MODE_ALIGNMENT (V4SFmode), INCOMING_STACK_BOUNDARY);
+	align = MIN (GET_MODE_ALIGNMENT (vector_reg_mode),
+		     INCOMING_STACK_BOUNDARY);
 	set_mem_align (mem, align);
 
 	/* SSE saves are not within re-aligned local stack frame.
 	   In case INCOMING_STACK_BOUNDARY is misaligned, we have
 	   to emit unaligned load.  */
-	if (align < 128)
+	if (vector_reg_mode == V4SFmode && align < 128)
 	  {
-	    rtx unspec = gen_rtx_UNSPEC (V4SFmode, gen_rtvec (1, mem),
+	    rtx unspec = gen_rtx_UNSPEC (vector_reg_mode,
+					 gen_rtvec (1, mem),
 					 UNSPEC_LOADU);
 	    emit_insn (gen_rtx_SET (reg, unspec));
 	  }
@@ -13237,7 +13595,7 @@ ix86_emit_restore_sse_regs_using_mov (HOST_WIDE_INT cfa_offset,
 
 	ix86_add_cfa_restore_note (NULL, reg, cfa_offset);
 
-	cfa_offset -= GET_MODE_SIZE (V4SFmode);
+	cfa_offset -= GET_MODE_SIZE (vector_reg_mode);
       }
 }
 
@@ -13336,16 +13694,25 @@ ix86_expand_epilogue (int style)
       if (TARGET_64BIT
 	  && m->fs.sp_offset > 0x7fffffff
 	  && !(m->fs.fp_valid || m->fs.drap_valid)
-	  && (frame.nsseregs + frame.nregs) != 0)
+	  && (frame.nsseregs + frame.nmaskregs + frame.nbndregs
+	      + frame.nregs) != 0)
 	{
 	  pro_epilogue_adjust_stack (stack_pointer_rtx, stack_pointer_rtx,
 				     GEN_INT (m->fs.sp_offset
-					      - frame.sse_reg_save_offset),
+					      - frame.bnd_reg_save_offset),
 				     style,
 				     m->fs.cfa_reg == stack_pointer_rtx);
 	}
     }
 
+  if (frame.nbndregs)
+    ix86_emit_restore_bnd_regs_using_mov (frame.bnd_reg_save_offset,
+					  style == 2);
+
+  if (frame.nmaskregs)
+    ix86_emit_restore_mask_regs_using_mov (frame.mask_reg_save_offset,
+					   style == 2);
+
   /* If there are any SSE registers to restore, then we have to do it
      via moves, since there's obviously no pop for SSE regs.  */
   if (frame.nsseregs)
@@ -13543,7 +13910,20 @@ ix86_expand_epilogue (int style)
       return;
     }
 
-  if (crtl->args.pops_args && crtl->args.size)
+  if (cfun->machine->func_type != TYPE_NORMAL)
+    {
+      /* Return with the "IRET" instruction from interrupt handler.
+	 Pop the 'ERROR_CODE' off the stack before the 'IRET'
+	 instruction in exception handler.  */
+      if (cfun->machine->func_type == TYPE_EXCEPTION)
+	{
+	  rtx r = plus_constant (Pmode, stack_pointer_rtx,
+				 UNITS_PER_WORD);
+	  emit_insn (gen_rtx_SET (stack_pointer_rtx, r));
+	}
+      emit_jump_insn (gen_interrupt_return ());
+    }
+  else if (crtl->args.pops_args && crtl->args.size)
     {
       rtx popc = GEN_INT (crtl->args.pops_args);
 
@@ -18512,6 +18892,14 @@ ix86_expand_move (machine_mode mode, rtx operands[])
   rtx op0, op1;
   enum tls_model model;
 
+  if (cfun->machine->func_type != TYPE_NORMAL && IS_STACK_MODE (mode))
+    {
+      error ("80387 instructions aren't allowed in %s service routine",
+	     (cfun->machine->func_type == TYPE_EXCEPTION
+	      ? "exception" : "interrupt"));
+      return;
+    }
+
   op0 = operands[0];
   op1 = operands[1];
 
@@ -18656,6 +19044,15 @@ ix86_expand_vector_move (machine_mode mode, rtx operands[])
   rtx op0 = operands[0], op1 = operands[1];
   unsigned int align = GET_MODE_ALIGNMENT (mode);
 
+  if (cfun->machine->func_type != TYPE_NORMAL
+      && VALID_MMX_REG_MODE (mode))
+    {
+      error ("MMX/3Dnow instructions aren't allowed in %s service routine",
+	     (cfun->machine->func_type == TYPE_EXCEPTION
+	      ? "exception" : "interrupt"));
+      return;
+    }
+
   if (push_operand (op0, VOIDmode))
     op0 = emit_move_resolve_push (mode, op0);
 
@@ -26741,6 +27138,17 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
   rtx vec[3];
   rtx use = NULL, call;
   unsigned int vec_len = 0;
+  tree fndecl;
+
+  if (GET_CODE (XEXP (fnaddr, 0)) == SYMBOL_REF)
+    {
+      fndecl = SYMBOL_REF_DECL (XEXP (fnaddr, 0));
+      if (fndecl
+	  && (lookup_attribute ("interrupt", DECL_ATTRIBUTES (fndecl))))
+	error ("interrupt service routine can't be called directly");
+    }
+  else
+    fndecl = NULL_TREE;
 
   if (pop == const0_rtx)
     pop = NULL;
@@ -26838,8 +27246,29 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
       vec[vec_len++] = pop;
     }
 
-  if (TARGET_64BIT_MS_ABI
-      && (!callarg2 || INTVAL (callarg2) != -2))
+  if (cfun->machine->no_caller_saved_registers
+      && (!fndecl
+	  || !lookup_attribute ("no_caller_saved_registers",
+				DECL_ATTRIBUTES (fndecl))))
+    {
+      static const char ix86_call_used_regs[] = CALL_USED_REGISTERS;
+      bool is_64bit_ms_abi = (TARGET_64BIT
+			      && ix86_function_abi (fndecl) == MS_ABI);
+      char c_mask = CALL_USED_REGISTERS_MASK (is_64bit_ms_abi);
+
+      /* If there are no caller-saved registers, add all registers
+	 that are clobbered by the call.  */
+      for (int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+	if (!fixed_regs[i]
+	    && (ix86_call_used_regs[i] == 1
+		|| (ix86_call_used_regs[i] & c_mask))
+	    && !STACK_REGNO_P (i)
+	    && !MMX_REGNO_P (i))
+	  clobber_reg (&use,
+		       gen_rtx_REG (GET_MODE (regno_reg_rtx[i]), i));
+    }
+  else if (TARGET_64BIT_MS_ABI
+	   && (!callarg2 || INTVAL (callarg2) != -2))
     {
       int const cregs_size
 	= ARRAY_SIZE (x86_64_ms_sysv_extra_clobbered_registers);
@@ -44226,6 +44655,75 @@ ix86_handle_fndecl_attribute (tree *node, tree name, tree, int,
   return NULL_TREE;
 }
 
+static tree
+ix86_handle_no_caller_saved_registers_attribute (tree *node,
+						 tree name, tree, int,
+						 bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+	       name);
+      *no_add_attrs = true;
+    }
+  /* DECL_RESULT does not exist there yet, but the function type
+     contains return type data.  */
+  tree func_type = TREE_TYPE (*node);
+  tree return_type = TREE_TYPE (func_type);
+  if (! VOID_TYPE_P (return_type))
+    error ("function without caller-saved registers can't have non-void return value");
+  return NULL_TREE;
+}
+
+static tree
+ix86_handle_interrupt_attribute (tree *node, tree name, tree, int,
+				 bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+	       name);
+      *no_add_attrs = true;
+    }
+
+  /* DECL_RESULT and DECL_ARGUMENTS do not exist there yet,
+     but the function type contains args and return type data.  */
+  tree func_type = TREE_TYPE (*node);
+  tree return_type = TREE_TYPE (func_type);
+
+  int nargs = 0;
+  tree current_arg_type = TYPE_ARG_TYPES (func_type);
+  while (current_arg_type
+	 && ! VOID_TYPE_P (TREE_VALUE (current_arg_type)))
+    {
+      if (nargs == 0)
+	{
+	  if (! POINTER_TYPE_P (TREE_VALUE (current_arg_type)))
+	    error ("interrupt service routine should have a pointer "
+		   "as the first argument");
+	}
+      else if (nargs == 1)
+	{
+	  if (TREE_CODE (TREE_VALUE (current_arg_type)) != INTEGER_TYPE
+	      || TYPE_MODE (TREE_VALUE (current_arg_type)) != word_mode)
+	    error ("interrupt service routine should have unsigned %s"
+		   "int as the second argument",
+		   TARGET_64BIT
+		   ? (TARGET_X32 ? "long long " : "long ")
+		   : "");
+	}
+      nargs++;
+      current_arg_type = TREE_CHAIN (current_arg_type);
+    }
+  if (!nargs || nargs > 2)
+    error ("interrupt service routine can only have a pointer argument "
+	   "and an optional integer argument");
+  if (! VOID_TYPE_P (return_type))
+    error ("interrupt service routine can't have non-void return value");
+
+  return NULL_TREE;
+}
+
 static bool
 ix86_ms_bitfield_layout_p (const_tree record_type)
 {
@@ -48227,6 +48725,11 @@ static const struct attribute_spec ix86_attribute_table[] =
     false },
   { "callee_pop_aggregate_return", 1, 1, false, true, true,
     ix86_handle_callee_pop_aggregate_return, true },
+  { "interrupt", 0, 0, true, false, false,
+    ix86_handle_interrupt_attribute, false },
+  { "no_caller_saved_registers", 0, 0, true, false, false,
+    ix86_handle_no_caller_saved_registers_attribute, false },
+
   /* End element.  */
   { NULL,        0, 0, false, false, false, NULL, false }
 };
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 4a84fb9..3bfe604 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -1735,6 +1735,11 @@ typedef struct ix86_args {
 
 #define EXIT_IGNORE_STACK 1
 
+/* Define this macro as a C expression that is nonzero for registers
+   used by the epilogue or the `return' pattern.  */
+
+#define EPILOGUE_USES(REGNO) ix86_epilogue_uses (REGNO)
+
 /* Output assembler code for a block containing the constant parts
    of a trampoline, leaving space for the variable parts.  */
 
@@ -2450,6 +2455,18 @@ struct GTY(()) machine_frame_state
 /* Private to winnt.c.  */
 struct seh_frame_state;
 
+enum function_type
+{
+  TYPE_NORMAL = 0,
+  /* The current function is an interrupt service routine with a
+     pointer argument as specified by the "interrupt" attribute.  */
+  TYPE_INTERRUPT,
+  /* The current function is an interrupt service routine with a
+     pointer argument and an integer argument as specified by the
+     "interrupt" attribute.  */
+  TYPE_EXCEPTION
+};
+
 struct GTY(()) machine_function {
   struct stack_local_entry *stack_locals;
   const char *some_ld_name;
@@ -2500,6 +2517,13 @@ struct GTY(()) machine_function {
   /* If true, it is safe to not save/restore DRAP register.  */
   BOOL_BITFIELD no_drap_save_restore : 1;
 
+  /* Function type.  */
+  ENUM_BITFIELD(function_type) func_type : 2;
+
+  /* If true, the current function is a function specified with
+     the "interrupt" or "no_caller_saved_registers" attribute.  */
+  BOOL_BITFIELD no_caller_saved_registers : 1;
+
   /* If true, there is register available for argument passing.  This
      is used only in ix86_function_ok_for_sibcall by 32-bit to determine
      if there is scratch register available for indirect sibcall.  In
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index d0c0d23..810833c 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -193,6 +193,9 @@
   UNSPEC_BNDCU
   UNSPEC_BNDCN
   UNSPEC_MPX_FENCE
+
+  ;; IRET support
+  UNSPEC_INTERRUPT_RETURN
 ])
 
 (define_c_enum "unspecv" [
@@ -12190,6 +12193,12 @@
    (set_attr "modrm" "0")
    (set_attr "maybe_prefix_bnd" "1")])
 
+(define_insn "interrupt_return"
+  [(simple_return)
+   (unspec [(const_int 0)] UNSPEC_INTERRUPT_RETURN)]
+  "reload_completed"
+  "iret")
+
 ;; Used by x86_machine_dependent_reorg to avoid penalty on single byte RET
 ;; instruction Athlon and K8 have.
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 79440d3..b70e40c 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -5079,6 +5079,69 @@ On x86-32 targets, the @code{stdcall} attribute causes the compiler to
 assume that the called function pops off the stack space used to
 pass arguments, unless it takes a variable number of arguments.
 
+@item no_caller_saved_registers
+@cindex @code{no_caller_saved_registers} function attribute, x86
+Use this attribute to indicate that the specified function has no
+caller-saved registers.  That is, all registers are callee-saved.
+The compiler generates proper function entry and exit sequences to
+save and restore any modified registers.
+
+@item interrupt
+@cindex @code{interrupt} function attribute, x86
+Use this attribute to indicate that the specified function is an
+interrupt handler.  The compiler generates function
+entry and exit sequences suitable for use in an interrupt handler when
+this attribute is present.  The @code{IRET} instruction, instead of the
+@code{RET} instruction, is used to return from interrupt handlers.  All
+registers, except for the EFLAGS register which is restored by the
+@code{IRET} instruction, are preserved by the compiler.
+
+Any interruptible-without-stack-switch code must be compiled with
+@option{-mno-red-zone} since interrupt handlers can and will, because
+of the hardware design, touch the red zone.
+
+An interrupt handler must be declared with a mandatory pointer
+argument:
+
+@smallexample
+struct interrupt_frame;
+
+__attribute__ ((interrupt))
+void
+f (struct interrupt_frame *frame)
+@{
+@}
+@end smallexample
+
+and user must properly define the structure the pointer pointing to.
+
+The exception handler is very similar to the interrupt handler with
+a different mandatory function signature:
+
+@smallexample
+#ifdef __x86_64__
+typedef unsigned long long int uword_t;
+#else
+typedef unsigned int uword_t;
+#endif
+
+struct interrupt_frame;
+
+__attribute__ ((interrupt))
+void
+f (struct interrupt_frame *frame, uword_t error_code)
+@{
+  ...
+@}
+@end smallexample
+
+and compiler pops the error code off the stack before the @code{IRET}
+instruction.
+
+The exception handler should only be used for exceptions which push an
+error code and all other exceptions must use the interrupt handler.
+The system will crash if the wrong handler is used.
+
 @item target (@var{options})
 @cindex @code{target} function attribute
 As discussed in @ref{Common Function Attributes}, this attribute 
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-1.c b/gcc/testsuite/gcc.target/i386/interrupt-1.c
new file mode 100644
index 0000000..39d6b20
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-1.c
@@ -0,0 +1,53 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-push-args -maccumulate-outgoing-args" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern int bar (int);
+
+void foo (void *frame)
+{
+  int a,b,c,d,e,f,i;
+  a = bar (5);
+  b = bar (a);
+  c = bar (b);
+  d = bar (c);
+  e = bar (d);
+  f = bar (e);
+  for (i = 1; i < 10; i++)
+  {
+    a += bar (a + i) + bar (b + i) +
+	 bar (c + i) + bar (d + i) +
+	 bar (e + i) + bar (f + i);
+  }
+}
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)bx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)si" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%edi" 2 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r12" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r13" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r14" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r15" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)bx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)si" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%edi" 2 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r12" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r13" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r14" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r15" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-10.c b/gcc/testsuite/gcc.target/i386/interrupt-10.c
new file mode 100644
index 0000000..64e621c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-10.c
@@ -0,0 +1,24 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mavx512bw -mno-iamcu -maccumulate-outgoing-args" } */
+
+extern void bar (void);
+
+struct interrupt_frame;
+
+void
+ __attribute__ ((interrupt))
+foo (struct interrupt_frame *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\)" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%zmm\[0-9\]+" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%zmm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "kmovq\[\\t \]*%k\[0-7\]+,\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\)" 8 } } */
+/* { dg-final { scan-assembler-times "kmovq\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%k\[0-7\]+" 8 } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-11.c b/gcc/testsuite/gcc.target/i386/interrupt-11.c
new file mode 100644
index 0000000..ba47019
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-11.c
@@ -0,0 +1,37 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-sse -mno-iamcu -maccumulate-outgoing-args" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-12.c b/gcc/testsuite/gcc.target/i386/interrupt-12.c
new file mode 100644
index 0000000..5c9eb74
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-12.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int check_int (int *i, void *, int align);
+typedef int aligned __attribute__((aligned(64)));
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+__attribute__((interrupt))
+void
+foo (void *frame, uword_t error_code)
+{
+  aligned j;
+  if (check_int (frame, &j, __alignof__(j)))
+    __builtin_abort ();
+}
+
+/* { dg-final { scan-assembler-times "and\[lq\]?\[^\\n\]*-64,\[^\\n\]*sp" 1 } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-13.c b/gcc/testsuite/gcc.target/i386/interrupt-13.c
new file mode 100644
index 0000000..8cf0b25
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-13.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int check_int (int *i, void *, int align);
+typedef int aligned __attribute__((aligned(64)));
+
+__attribute__((interrupt))
+void
+foo (void *frame)
+{
+  aligned j;
+  if (check_int (frame, &j, __alignof__(j)))
+    __builtin_abort ();
+}
+
+/* { dg-final { scan-assembler-times "and\[lq\]?\[^\\n\]*-64,\[^\\n\]*sp" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-14.c b/gcc/testsuite/gcc.target/i386/interrupt-14.c
new file mode 100644
index 0000000..3772b01
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-14.c
@@ -0,0 +1,38 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-sse -mno-iamcu -mno-sse4 -mno-popcnt -maccumulate-outgoing-args" } */
+
+extern int i, cnt;
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  cnt = __builtin_popcount (i);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%esi" { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)di" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)di" 1 } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 1 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-15.c b/gcc/testsuite/gcc.target/i386/interrupt-15.c
new file mode 100644
index 0000000..16d82c2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-15.c
@@ -0,0 +1,28 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mpush-args -maccumulate-outgoing-args" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  bar (error);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%rax" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:r|e)bp" 1 } } */
+/* { dg-final { scan-assembler-times "leave" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%eax" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movl\[\\t \]*-4\\(%ebp\\),\[\\t \]*%eax" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movq\[\\t \]*-8\\(%(?:r|e)bp\\),\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 1 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-16.c b/gcc/testsuite/gcc.target/i386/interrupt-16.c
new file mode 100644
index 0000000..f20f117
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-16.c
@@ -0,0 +1,28 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mno-push-args -maccumulate-outgoing-args" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  bar (error);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%rax" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:r|e)bp" 1 } } */
+/* { dg-final { scan-assembler-times "leave" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%eax" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movl\[\\t \]*-4\\(%ebp\\),\[\\t \]*%eax" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movq\[\\t \]*-8\\(%(?:r|e)bp\\),\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 1 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-17.c b/gcc/testsuite/gcc.target/i386/interrupt-17.c
new file mode 100644
index 0000000..486a2eb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-17.c
@@ -0,0 +1,33 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mpush-args -mno-accumulate-outgoing-args" } */
+
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame)
+{
+  bar (3);
+}
+
+void
+ __attribute__ ((interrupt))
+fn2 (void *frame)
+{
+  bar (3);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[8-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r1\[0-2\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r1\[4-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:r|e)bp" 2 } } */
+/* { dg-final { scan-assembler-times "leave" 2 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%edi" 2 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r13" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r13" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 2 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-18.c b/gcc/testsuite/gcc.target/i386/interrupt-18.c
new file mode 100644
index 0000000..af32851
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-18.c
@@ -0,0 +1,35 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mpush-args -maccumulate-outgoing-args" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  bar (error);
+}
+
+void
+ __attribute__ ((interrupt))
+fn2 (void *frame, uword_t error)
+{
+  bar (error);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%rax" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:r|e)bp" 2 } } */
+/* { dg-final { scan-assembler-times "leave" 2 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%eax" 2 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movl\[\\t \]*-4\\(%ebp\\),\[\\t \]*%eax" 2 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movq\[\\t \]*-8\\(%(?:r|e)bp\\),\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 2 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 2 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-19.c b/gcc/testsuite/gcc.target/i386/interrupt-19.c
new file mode 100644
index 0000000..67e2fbd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-19.c
@@ -0,0 +1,25 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mno-push-args -maccumulate-outgoing-args" } */
+
+extern void foo (int) __attribute__ ((no_caller_saved_registers));
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+extern int x;
+
+void
+foo (int i)
+{
+  bar (i);
+  x = i;
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]-*\[0-9\]*\\(%\[re\]?bp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:r|e)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:r|e)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-2.c b/gcc/testsuite/gcc.target/i386/interrupt-2.c
new file mode 100644
index 0000000..27b40b0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-2.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wall -g" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+}
+
+/* { dg-final { scan-assembler-not "add(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-20.c b/gcc/testsuite/gcc.target/i386/interrupt-20.c
new file mode 100644
index 0000000..779addb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-20.c
@@ -0,0 +1,27 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mno-push-args" } */
+
+extern void foo (int) __attribute__ ((no_caller_saved_registers));
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+extern int x;
+
+void
+foo (int i)
+{
+  x = i;
+  bar (i + 1);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]-*\[0-9\]*\\(%\[re\]?bp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%rdx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%eax" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%eax" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "jmp" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-21.c b/gcc/testsuite/gcc.target/i386/interrupt-21.c
new file mode 100644
index 0000000..7cc5181
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-21.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx -mno-iamcu -msse" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movaps\[\\t \]*%xmm3,\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movaps\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%xmm3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-2\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-2\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[4-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[4-9\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm1\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm1\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-22.c b/gcc/testsuite/gcc.target/i386/interrupt-22.c
new file mode 100644
index 0000000..5dd6290
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-22.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm3,\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-2\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-2\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[4-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[4-9\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm1\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm1\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-23.c b/gcc/testsuite/gcc.target/i386/interrupt-23.c
new file mode 100644
index 0000000..e0cf550
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-23.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mavx512f -mno-iamcu" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm3,\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-2\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-2\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[4-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[4-9\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm1\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm1\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-24.c b/gcc/testsuite/gcc.target/i386/interrupt-24.c
new file mode 100644
index 0000000..9b92c00
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-24.c
@@ -0,0 +1,21 @@
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mno-iamcu -mmpx" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "bnd3");
+}
+
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*%bnd3,\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%bnd3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-25.c b/gcc/testsuite/gcc.target/i386/interrupt-25.c
new file mode 100644
index 0000000..849fd3a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-25.c
@@ -0,0 +1,24 @@
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -mno-avx -mno-iamcu -msse -mincoming-stack-boundary=2" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%xmm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%xmm3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-2\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-2\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[4-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[4-9\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm1\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm1\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%e(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%e(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%ebp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-26.c b/gcc/testsuite/gcc.target/i386/interrupt-26.c
new file mode 100644
index 0000000..27b40b0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-26.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wall -g" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+}
+
+/* { dg-final { scan-assembler-not "add(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-27.c b/gcc/testsuite/gcc.target/i386/interrupt-27.c
new file mode 100644
index 0000000..774bb4b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-27.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O0 -g -mno-avx -mno-iamcu -msse" } */
+
+typedef float __v4sf __attribute__ ((__vector_size__ (16)));
+__v4sf x, y;
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  x = y;
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%xmm0,\[\\t \]*-?\[0-9\]*\\(%\[re\]?bp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-?\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%xmm0" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-3.c b/gcc/testsuite/gcc.target/i386/interrupt-3.c
new file mode 100644
index 0000000..b09f56c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-3.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -g" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+void
+__attribute__((interrupt))
+fn (void* frame, uword_t error)
+{
+}
+
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-387-err.c b/gcc/testsuite/gcc.target/i386/interrupt-387-err.c
new file mode 100644
index 0000000..36094f2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-387-err.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -m80387 -mlong-double-80 -mno-iamcu -mno-sse" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+extern long double y, x;
+
+void
+fn0 (void)
+{
+  x = y;
+  y = 0;
+}
+
+void
+__attribute__((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  x = 0; /* { dg-error "80387 instructions aren't allowed in exception service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn2 (void *frame)
+{
+  x = y; /* { dg-error "80387 instructions aren't allowed in interrupt service routine" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-4.c b/gcc/testsuite/gcc.target/i386/interrupt-4.c
new file mode 100644
index 0000000..c34dbe0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-4.c
@@ -0,0 +1,32 @@
+/* { dg-do link } */
+/* { dg-options "-O -g" } */
+
+#include <stdint.h>
+
+extern void link_error (void);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+struct interrupt_frame
+{
+  uword_t ip;
+  uword_t cs;
+  uword_t flags;
+  uword_t sp;
+  uword_t ss;
+};
+
+__attribute__ ((used, interrupt))
+void
+foo (struct interrupt_frame *frame)
+{
+  void *ra = __builtin_return_address (0);
+  if ((uintptr_t) ra != (uintptr_t) frame->ip)
+    link_error ();
+}
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-5.c b/gcc/testsuite/gcc.target/i386/interrupt-5.c
new file mode 100644
index 0000000..3c4bc2d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-5.c
@@ -0,0 +1,23 @@
+/* { dg-do link } */
+/* { dg-options "-O -g" } */
+
+#include <stdint.h>
+
+extern void link_error (void);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+__attribute__ ((used, interrupt))
+void
+foo (void *frame, uword_t error)
+{
+  void *ra = __builtin_return_address (0);
+  if ((uintptr_t) ra != (uintptr_t) error)
+    link_error ();
+}
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-6.c b/gcc/testsuite/gcc.target/i386/interrupt-6.c
new file mode 100644
index 0000000..a1a3d0e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-6.c
@@ -0,0 +1,40 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+extern int error;
+
+__attribute__((interrupt))
+void
+fn1 (void *p, short error_code)
+{ /* { dg-error "interrupt service routine should have unsigned \(long long |long |\)int as the second argument" } */
+}
+
+__attribute__((interrupt))
+void
+fn2 (void)
+{ /* { dg-error "interrupt service routine can only have a pointer argument and an optional integer argument" } */
+}
+
+__attribute__((interrupt))
+void
+fn3 (uword_t error_code)
+{ /* { dg-error "interrupt service routine should have a pointer as the first argument" } */
+  error = error_code;
+}
+
+__attribute__((interrupt))
+void
+fn4 (uword_t error_code, void *frame)
+{ /* { dg-error "interrupt service routine should have .* the .* argument" } */
+  error = error_code;
+}
+
+extern int fn5 (void *) __attribute__ ((interrupt)); /* { dg-error "interrupt service routine can't have non-void return value" } */
+
+int
+fn5 (void *frame)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-7.c b/gcc/testsuite/gcc.target/i386/interrupt-7.c
new file mode 100644
index 0000000..e7441a2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-7.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int error;
+
+extern void fn (void *) __attribute__((interrupt));
+
+void
+foo (void)
+{
+  fn (&error); /* { dg-error "interrupt service routine can't be called directly" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-8.c b/gcc/testsuite/gcc.target/i386/interrupt-8.c
new file mode 100644
index 0000000..defec2e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-8.c
@@ -0,0 +1,20 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -maccumulate-outgoing-args" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm\[0-9\]+,\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\)" 16 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%ymm\[0-9\]+" 16 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm\[0-9\]+,\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%ymm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-9.c b/gcc/testsuite/gcc.target/i386/interrupt-9.c
new file mode 100644
index 0000000..56bd8dd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-9.c
@@ -0,0 +1,22 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512bw -mno-iamcu -mavx512f -maccumulate-outgoing-args" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]-*\[0-9\]*\\(%\[re\]?bp\\)" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%zmm\[0-9\]+" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%zmm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "kmovw\[\\t \]*%k\[0-7\]+,\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\)" 8 } } */
+/* { dg-final { scan-assembler-times "kmovw\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%k\[0-7\]+" 8 } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-bnd.c b/gcc/testsuite/gcc.target/i386/interrupt-bnd.c
new file mode 100644
index 0000000..9f50661
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-bnd.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mno-iamcu -mmpx" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "bnd3");
+}
+
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*%bnd3,\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%bnd3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
new file mode 100644
index 0000000..712b85f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
@@ -0,0 +1,35 @@
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -miamcu -maccumulate-outgoing-args" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern int bar (int);
+
+void foo (void *frame)
+{
+  int a,b,c,d,e,f,i;
+  a = bar (5);
+  b = bar (a);
+  c = bar (b);
+  d = bar (c);
+  e = bar (d);
+  f = bar (e);
+  for (i = 1; i < 10; i++)
+    a += bar (a + i) + bar (b + i) +
+	 bar (c + i) + bar (d + i) +
+	 bar (e + i) + bar (f+i);
+}
+
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%eax" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%ebx" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%ecx" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%edx" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%edi" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%esi" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%ebp" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%eax" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%ecx" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%edx" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%edi" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%esi" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%ebp" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c
new file mode 100644
index 0000000..fc4f367
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mmmx -mno-iamcu" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+typedef short __v4hi __attribute__ ((__vector_size__ (8)));
+typedef int __m64 __attribute__ ((__vector_size__ (8), __may_alias__));
+
+extern __m64 y, x;
+
+void
+fn0 (void)
+{
+  x = __extension__ (__m64){ 0 };
+  x = (__m64) __builtin_ia32_packsswb ((__v4hi) x, (__v4hi) y);
+  y = x;
+}
+
+void
+__attribute__((interrupt))
+fn1 (void *frame)
+{
+  x = (__m64) __builtin_ia32_packsswb ((__v4hi) x, (__v4hi) y); /* { dg-error "MMX/3Dnow (instructions|intrinsics) aren't allowed in interrupt service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn2 (void *frame)
+{
+  x = y; /* { dg-error "MMX/3Dnow instructions aren't allowed in interrupt service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn3 (void *frame, uword_t error)
+{
+  x = __extension__ (__m64){ 0 }; /* { dg-error "MMX/3Dnow instructions aren't allowed in exception service routine" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c b/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c
new file mode 100644
index 0000000..796b81b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mred-zone" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  /* No need to adjust stack if less than 128 bytes are used on stack
+     with a 128-byte red zone.  */
+  char array[112];
+  asm ("# %0 "
+       :
+       : "r" (array));
+}
+
+/* { dg-final { scan-assembler-not "(sub|add)(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c b/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c
new file mode 100644
index 0000000..c6a2822
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mred-zone" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  /* Need to adjust stack if more than 128 bytes are used on stack
+     with a 128-byte red zone.  */
+  char array[113];
+  asm ("# %0 "
+       :
+       : "r" (array));
+}
+
+/* { dg-final { scan-assembler-times "(?:sub|add)(?:l|q)\[\\t \]*\\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c b/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c
new file mode 100644
index 0000000..e222acf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O3" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern void bar (void);
+
+void foo (void *frame)
+{
+  bar ();
+}
+/* { dg-final { scan-assembler-not "jmp" } }*/
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c
new file mode 100644
index 0000000..b1b0abe
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern void bar (int);
+
+void f1 (void){  bar (1); }
+__attribute__((interrupt))
+void f2 (void *frame){  bar (2); }
+void f3 (void){  bar (3); }
+__attribute__((interrupt))
+void f4 (void *frame){  bar (4); }
+void f5 (void){  bar (5); }
+
+/* { dg-final { scan-assembler-times "push.\t%.ax" 2 } } */
+/* { dg-final { scan-assembler-times "pop.\t%.ax" 2 } } */
+/* { dg-final { scan-assembler-times "iret" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-xmm.c b/gcc/testsuite/gcc.target/i386/interrupt-xmm.c
new file mode 100644
index 0000000..6ffdd96
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-xmm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx -mno-iamcu -msse" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%xmm3,\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%xmm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-ymm.c b/gcc/testsuite/gcc.target/i386/interrupt-ymm.c
new file mode 100644
index 0000000..0754ae0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-ymm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*%ymm3,\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-zmm.c b/gcc/testsuite/gcc.target/i386/interrupt-zmm.c
new file mode 100644
index 0000000..f4d2fa1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-zmm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mavx512f -mno-iamcu" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*%zmm3,\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-10-13 12:18                                                     ` Yulia Koval
@ 2015-10-20 13:25                                                       ` Yulia Koval
  2015-10-23 13:13                                                         ` Yulia Koval
  0 siblings, 1 reply; 45+ messages in thread
From: Yulia Koval @ 2015-10-20 13:25 UTC (permalink / raw)
  To: Uros Bizjak; +Cc: H.J. Lu, Mike Stump, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 7322 bytes --]

The debug_info section for the interrupt function looks ok.

I tried to call it from assembler code to check it in gdb.

        pushl   $0x333 ;eflags
        pushl   $0x111 ;cs
        pushl $0x222   ;eip
        jmp  foo           ;interrupt function

#define uword_t unsigned int
struct interrupt_frame
{
     uword_t ip;
     uword_t cs;
     uword_t flags;
};

I get this inside the interrupt function:

Breakpoint 1, foo (frame=0xffffd560) at interrupt-1.c:7
7               a = (struct interrupt_frame*) frame;
(gdb) p/x ((struct interrupt_frame*)frame)->ip
$1 = 0x222
(gdb) p/x ((struct interrupt_frame*)frame)->cs
$3 = 0x111
(gdb) p/x ((struct interrupt_frame*)frame)->flags
$4 = 0x333

Frame pointer info looks ok.


On Tue, Oct 13, 2015 at 3:18 PM, Yulia Koval <vaalfreja@gmail.com> wrote:
>
> Here is the current version of the patch with all the fixes.
> Regtested\bootstraped it on 64 bit.
>
> We need a pointer since interrupt handler will update data pointing
> to by frame.  Since error_code isn't at the normal location where the
> parameter is passed on stack and frame isn't in a hard register, we
> changed ix86_function_arg:
>
> +  if (!cum->caller && cfun->machine->func_type != TYPE_NORMAL)
> +    {
> +      /* The first argument of interrupt handler is a pointer and
> +        points to the return address slot on stack.  The optional
> +        second argument is an integer for error code on stack.  */
> +      gcc_assert (type != NULL_TREE);
> +      if (POINTER_TYPE_P (type))
> +       {
> +         if (cfun->machine->func_type == TYPE_EXCEPTION)
> +           /* (AP) in the current frame in exception handler.  */
> +           arg = arg_pointer_rtx;
> +         else
> +           /* -WORD(AP) in the current frame in interrupt handler.  */
> +           arg = force_reg (Pmode,
> +                            plus_constant (Pmode, arg_pointer_rtx,
> +                                           -UNITS_PER_WORD));
> +         if (mode != Pmode)
> +           arg = convert_to_mode (mode, arg, 1);
> +       }
> +      else
> +       {
> +         gcc_assert (TREE_CODE (type) == INTEGER_TYPE
> +                     && cfun->machine->func_type == TYPE_EXCEPTION
> +                     && mode == word_mode);
> +         /* The error code is at -WORD(AP) in the current frame in
> +            exception handler.  */
> +         arg = gen_rtx_MEM (word_mode,
> +                            plus_constant (Pmode, arg_pointer_rtx,
> +                                           -UNITS_PER_WORD));
> +       }
> +
> +      return arg;
> +    }
> +
>
> to return a pseudo register.  It violates
>
>    Return where to put the arguments to a function.
>    Return zero to push the argument on the stack, or a hard register in
>    which to store the argument.
>
> Register allocator has no problem with parameters in pseudo registers.
> But GCC crashes when it tries to access DECL_INCOMING_RTL as a hard
> register when generating debug information.  We worked around it by
> doing
>
> +
> +  if (cfun->machine->func_type != TYPE_NORMAL)
> +    {
> +      /* Since the pointer argument of interrupt handler isn't a real
> +         argument, adjust DECL_INCOMING_RTL for debug output.  */
> +      tree arg = DECL_ARGUMENTS (current_function_decl);
> +      gcc_assert (arg != NULL_TREE
> +                 && POINTER_TYPE_P (TREE_TYPE (arg)));
> +      if (cfun->machine->func_type == TYPE_EXCEPTION)
> +       /* (AP) in the current frame in exception handler.  */
> +       DECL_INCOMING_RTL (arg) = arg_pointer_rtx;
> +      else
> +       /* -WORD(AP) in the current frame in interrupt handler.  */
> +       DECL_INCOMING_RTL (arg) = plus_constant (Pmode,
> +                                                arg_pointer_rtx,
> +                                                -UNITS_PER_WORD);
> +    }
>
>
> On Mon, Oct 5, 2015 at 12:29 PM, Uros Bizjak <ubizjak@gmail.com> wrote:
> > On Mon, Oct 5, 2015 at 1:17 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
> >
> >>>>>>> Looking a bit deeper into the code, it looks that we want to realign
> >>>>>>> the stack in the interrupt handler. Let's assume that interrupt
> >>>>>>> handler is calling some other function that saves SSE vector regs to
> >>>>>>> the stack. According to the x86 ABI, incoming stack of the called
> >>>>>>> function is assumed to be aligned to 16 bytes. But, interrupt handler
> >>>>>>> violates this assumption, since the stack could be aligned to only 4
> >>>>>>> bytes for 32bit and 8 bytes for 64bit targets. Entering the called
> >>>>>>> function with stack, aligned to less than 16 bytes will certainly
> >>>>>>> violate ABI.
> >>>>>>>
> >>>>>>> So, it looks to me that we need to realign the stack in the interrupt
> >>>>>>> handler unconditionally to 16bytes. In this case, we also won't need
> >>>>>>> the following changes:
> >>>>>>>
> >>>>>>
> >>>>>> Current stack alignment implementation requires at least
> >>>>>> one, maybe two, scratch registers:
> >>>>>>
> >>>>>> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67841
> >>>>>>
> >>>>>> Extend it to the interrupt handler, which doesn't have any scratch
> >>>>>> registers may require significant changes in backend as well as
> >>>>>> register allocator.
> >>>>>
> >>>>> But without realignment, the handler is unusable for anything but
> >>>>> simple functions. The handler will crash when called function will try
> >>>>> to save vector reg to stack.
> >>>>>
> >>>>
> >>>> We can use unaligned load and store to avoid crash.
> >>>
> >>> Oh, sorry, I meant "called function will crash", like:
> >>>
> >>> -> interrupt when %rsp = 0x...8 ->
> >>> -> interrupt handler ->
> >>> -> calls some function that tries to save xmm reg to stack
> >>> -> crash in the called function
> >>>
> >>
> >> It should be fixed by this patch.   But we need to fix stack
> >> alignment in interrupt handler to avoid scratch register.
> >>
> >>
> >> --
> >> H.J.
> >> ---
> >> commit 15f48be1dc7ff48207927d0b835e593d058f695b
> >> Author: H.J. Lu <hjl.tools@gmail.com>
> >> Date:   Sun Oct 4 16:14:03 2015 -0700
> >>
> >>     Correctly set incoming stack boundary for interrupt handler
> >>
> >> diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
> >> index 7ebdcd9..0f0cc3c 100644
> >> --- a/gcc/config/i386/i386.c
> >> +++ b/gcc/config/i386/i386.c
> >> @@ -12037,8 +12037,11 @@ ix86_minimum_incoming_stack_boundary (bool sibcall)
> >>  {
> >>    unsigned int incoming_stack_boundary;
> >>
> >> +  /* Stack of interrupt handler is always aligned to word_mode.  */
> >> +  if (cfun->machine->func_type != TYPE_NORMAL)
> >> +    incoming_stack_boundary = TARGET_64BIT ? 64 : 32;
> >
> > Just a heads up that in order to support stack realignmnent on x86_64,
> > MIN_STACK_BOUNDARY will soon be changed to BITS_PER_WORD, so you can
> > use it in the line above. Please see comment #5 and #6 of PR 66697
> > [1].
> >
> >>    /* Prefer the one specified at command line. */
> >> -  if (ix86_user_incoming_stack_boundary)
> >> +  else if (ix86_user_incoming_stack_boundary)
> >>      incoming_stack_boundary = ix86_user_incoming_stack_boundary;
> >>    /* In 32bit, use MIN_STACK_BOUNDARY for incoming stack boundary
> >>       if -mstackrealign is used, it isn't used for sibcall check and
> >
> > [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66697
> >
> > Uros.

[-- Attachment #2: patch_repost --]
[-- Type: application/octet-stream, Size: 82418 bytes --]

diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index 6a17ef4..c7ff393 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -278,6 +278,8 @@ extern rtx maybe_get_pool_constant (rtx);
 extern char internal_label_prefix[16];
 extern int internal_label_prefix_len;
 
+extern bool ix86_epilogue_uses (int);
+
 enum ix86_address_seg { SEG_DEFAULT, SEG_FS, SEG_GS };
 struct ix86_address
 {
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 419966d..4c25c9e 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -2473,6 +2473,8 @@ struct ix86_frame
 {
   int nsseregs;
   int nregs;
+  int nbndregs;
+  int nmaskregs;
   int va_arg_size;
   int red_zone_size;
   int outgoing_arguments_size;
@@ -2483,6 +2485,8 @@ struct ix86_frame
   HOST_WIDE_INT stack_pointer_offset;
   HOST_WIDE_INT hfp_save_offset;
   HOST_WIDE_INT reg_save_offset;
+  HOST_WIDE_INT bnd_reg_save_offset;
+  HOST_WIDE_INT mask_reg_save_offset;
   HOST_WIDE_INT sse_reg_save_offset;
 
   /* When save_regs_using_mov is set, emit prologue using
@@ -5570,6 +5574,13 @@ ix86_conditional_register_usage (void)
 {
   int i, c_mask;
 
+  /* If there are no caller-saved registers, preserve all registers.
+     except fixed_regs.  */
+  if (cfun && cfun->machine->no_caller_saved_registers)
+    for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+      if (!fixed_regs[i])
+	call_used_regs[i] = 0;
+
   /* For 32-bit targets, squash the REX registers.  */
   if (! TARGET_64BIT)
     {
@@ -6349,6 +6360,21 @@ ix86_set_current_function (tree fndecl)
       return;
     }
 
+  if (lookup_attribute ("interrupt", DECL_ATTRIBUTES (fndecl)))
+    {
+      int nargs = 0;
+      for (tree arg = DECL_ARGUMENTS (fndecl);
+	   arg;
+	   arg = TREE_CHAIN (arg))
+	nargs++;
+      cfun->machine->no_caller_saved_registers = true;
+      cfun->machine->func_type
+	= nargs == 2 ? TYPE_EXCEPTION : TYPE_INTERRUPT;
+    }
+  else if (lookup_attribute ("no_caller_saved_registers",
+			     DECL_ATTRIBUTES (fndecl)))
+    cfun->machine->no_caller_saved_registers = true;
+
   tree new_tree = DECL_FUNCTION_SPECIFIC_TARGET (fndecl);
   if (new_tree == NULL_TREE)
     new_tree = target_option_default_node;
@@ -6372,6 +6398,11 @@ ix86_set_current_function (tree fndecl)
       && (call_used_regs[SI_REG]
 	  == (cfun->machine->call_abi == MS_ABI)))
     reinit_regs ();
+  /* AX register is only preserved if there are no caller-saved
+     registers.  */
+  else if (cfun->machine->no_caller_saved_registers
+	   == call_used_regs[AX_REG])
+    reinit_regs ();
 }
 
 \f
@@ -6635,6 +6666,10 @@ ix86_function_ok_for_sibcall (tree decl, tree exp)
   tree type, decl_or_type;
   rtx a, b;
 
+  /* Sibling call isn't OK if there are no caller-saved registers.  */
+  if (cfun->machine->no_caller_saved_registers)
+    return false;
+
   /* If we are generating position-independent code, we cannot sibcall
      optimize direct calls to global functions, as the PLT requires
      %ebx be live. (Darwin does not have a PLT.)  */
@@ -7808,6 +7843,8 @@ type_natural_mode (const_tree type, const CUMULATIVE_ARGS *cum,
 		      }
 		  }
 		else if ((size == 8 && !TARGET_64BIT)
+			 && (!cfun
+			     || cfun->machine->func_type == TYPE_NORMAL)
 			 && !TARGET_MMX
 			 && !TARGET_IAMCU)
 		  {
@@ -8787,6 +8824,11 @@ ix86_function_arg_advance (cumulative_args_t cum_v, machine_mode mode,
   HOST_WIDE_INT bytes, words;
   int nregs;
 
+  /* The argument of interrupt handler is a special case and is
+     handled in ix86_function_arg.  */
+  if (!cum->caller && cfun->machine->func_type != TYPE_NORMAL)
+    return;
+
   if (mode == BLKmode)
     bytes = int_size_in_bytes (type);
   else
@@ -9103,6 +9145,40 @@ ix86_function_arg (cumulative_args_t cum_v, machine_mode omode,
   HOST_WIDE_INT bytes, words;
   rtx arg;
 
+  if (!cum->caller && cfun->machine->func_type != TYPE_NORMAL)
+    {
+      /* The first argument of interrupt handler is a pointer and
+	 points to the return address slot on stack.  The optional
+	 second argument is an integer for error code on stack.  */
+      gcc_assert (type != NULL_TREE);
+      if (POINTER_TYPE_P (type))
+	{
+	  if (cfun->machine->func_type == TYPE_EXCEPTION)
+	    /* (AP) in the current frame in exception handler.  */
+	    arg = arg_pointer_rtx;
+	  else
+	    /* -WORD(AP) in the current frame in interrupt handler.  */
+	    arg = force_reg (Pmode,
+			     plus_constant (Pmode, arg_pointer_rtx,
+					    -UNITS_PER_WORD));
+	  if (mode != Pmode)
+	    arg = convert_to_mode (mode, arg, 1);
+	}
+      else
+	{
+	  gcc_assert (TREE_CODE (type) == INTEGER_TYPE
+		      && cfun->machine->func_type == TYPE_EXCEPTION
+		      && mode == word_mode);
+	  /* The error code is at -WORD(AP) in the current frame in
+	     exception handler.  */
+	  arg = gen_rtx_MEM (word_mode,
+			     plus_constant (Pmode, arg_pointer_rtx,
+					    -UNITS_PER_WORD));
+	}
+
+      return arg;
+    }
+
   /* All pointer bounds arguments are handled separately here.  */
   if ((type && POINTER_BOUNDS_TYPE_P (type))
       || POINTER_BOUNDS_MODE_P (mode))
@@ -10818,7 +10894,10 @@ ix86_can_use_return_insn_p (void)
 {
   struct ix86_frame frame;
 
-  if (! reload_completed || frame_pointer_needed)
+  /* Don't use `ret' instruction in interrupt handler.  */
+  if (! reload_completed
+      || frame_pointer_needed
+      || cfun->machine->func_type != TYPE_NORMAL)
     return 0;
 
   /* Don't allow more than 32k pop, since that's all we can do
@@ -11128,11 +11207,52 @@ ix86_select_alt_pic_regnum (void)
   return INVALID_REGNUM;
 }
 
+/* Return true if REGNO is used by the epilogue.  */
+
+bool
+ix86_epilogue_uses (int regno)
+{
+  /* If there are no caller-saved registers, we preserve all registers,
+     except for MMX and x87 registers which aren't supported when saving
+     and restoring registers.  Don't explicitly save SP register since
+     it is always preserved.  */
+  return (epilogue_completed
+	  && cfun->machine->no_caller_saved_registers
+	  && !fixed_regs[regno]
+	  && !STACK_REGNO_P (regno)
+	  && !MMX_REGNO_P (regno));
+}
+
+/* Return TRUE if register REGNO is ever defined..  */
+
+static bool
+ix86_reg_ever_defined_p (unsigned int regno)
+{
+  df_ref def;
+
+  for (def = DF_REG_DEF_CHAIN (regno); def; def = DF_REF_NEXT_REG (def))
+    if (!DF_REF_IS_ARTIFICIAL (def))
+      return true;
+
+  return false;
+}
+
 /* Return TRUE if we need to save REGNO.  */
 
 static bool
 ix86_save_reg (unsigned int regno, bool maybe_eh_return)
 {
+  /* If there are no caller-saved registers, we preserve all registers,
+     except for MMX and x87 registers which aren't supported when saving
+     and restoring registers.  Don't explicitly save SP register since
+     it is always preserved.  */
+  if (reload_completed && cfun->machine->no_caller_saved_registers)
+    return (df_regs_ever_live_p (regno)
+	    && ix86_reg_ever_defined_p (regno)
+	    && !fixed_regs[regno]
+	    && !STACK_REGNO_P (regno)
+	    && !MMX_REGNO_P (regno));
+
   if (regno == REAL_PIC_OFFSET_TABLE_REGNUM
       && pic_offset_table_rtx)
     {
@@ -11189,6 +11309,34 @@ ix86_nsaved_regs (void)
   return nregs;
 }
 
+/* Return number of saved bound registers.  */
+
+static int
+ix86_nsaved_bndregs (void)
+{
+  int nregs = 0;
+  int regno;
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (BND_REGNO_P (regno) && ix86_save_reg (regno, true))
+      nregs ++;
+  return nregs;
+}
+
+/* Return number of saved mask registers.  */
+
+static int
+ix86_nsaved_maskregs (void)
+{
+  int nregs = 0;
+  int regno;
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (MASK_REGNO_P (regno) && ix86_save_reg (regno, true))
+      nregs ++;
+  return nregs;
+}
+
 /* Return number of saved SSE registers.  */
 
 static int
@@ -11197,7 +11345,10 @@ ix86_nsaved_sseregs (void)
   int nregs = 0;
   int regno;
 
-  if (!TARGET_64BIT_MS_ABI)
+  /* Always need to save SSE registers if there are no caller-saved
+     registers.  */
+  if (!TARGET_64BIT_MS_ABI
+      && !cfun->machine->no_caller_saved_registers)
     return 0;
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
@@ -11278,6 +11429,8 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   HOST_WIDE_INT to_allocate;
 
   frame->nregs = ix86_nsaved_regs ();
+  frame->nbndregs = ix86_nsaved_bndregs ();
+  frame->nmaskregs = ix86_nsaved_maskregs ();
   frame->nsseregs = ix86_nsaved_sseregs ();
 
   /* 64-bit MS ABI seem to require stack alignment to be always 16 except for
@@ -11379,19 +11532,39 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   if (TARGET_SEH)
     frame->hard_frame_pointer_offset = offset;
 
+  /* Set BND register save area.  */
+  if (frame->nbndregs)
+    offset += frame->nbndregs * (ix86_pmode == PMODE_DI ? 16 : 8);
+  frame->bnd_reg_save_offset = offset;
+
+  /* Set MASK register save area.  */
+  if (frame->nmaskregs)
+    offset += frame->nmaskregs * (TARGET_AVX512BW ? 8 : 2);
+  frame->mask_reg_save_offset = offset;
+
   /* Align and set SSE register save area.  */
   if (frame->nsseregs)
     {
+      /* We must save full vector registers if there are no
+	 caller-saved registers.  */
+      unsigned int vec_regsize;
+      if (cfun->machine->no_caller_saved_registers)
+	vec_regsize = (TARGET_AVX512F ? 64 : (TARGET_AVX ? 32 : 16));
+      else
+	vec_regsize = 16;
+
       /* The only ABI that has saved SSE registers (Win64) also has a
 	 16-byte aligned default stack, and thus we don't need to be
 	 within the re-aligned local stack frame to save them.  In case
 	 incoming stack boundary is aligned to less than 16 bytes,
 	 unaligned move of SSE register will be emitted, so there is
 	 no point to round up the SSE register save area outside the
-	 re-aligned local stack frame to 16 bytes.  */
-      if (ix86_incoming_stack_boundary >= 128)
-	offset = ROUND_UP (offset, 16);
-      offset += frame->nsseregs * 16;
+	 re-aligned local stack frame to 16 bytes.  When full vector
+	 registers are saved, we check incoming stack boundary against
+	 the size of vector registers.  */
+      if (ix86_incoming_stack_boundary >= (vec_regsize * BITS_PER_UNIT))
+	offset = ROUND_UP (offset, vec_regsize);
+      offset += frame->nsseregs * vec_regsize;
     }
   frame->sse_reg_save_offset = offset;
 
@@ -11445,7 +11618,7 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   frame->stack_pointer_offset = offset;
 
   /* Size prologue needs to allocate.  */
-  to_allocate = offset - frame->sse_reg_save_offset;
+  to_allocate = offset - frame->bnd_reg_save_offset;
 
   if ((!to_allocate && frame->nregs <= 1)
       || (TARGET_64BIT && to_allocate >= (HOST_WIDE_INT) 0x80000000))
@@ -11699,18 +11872,67 @@ ix86_emit_save_regs_using_mov (HOST_WIDE_INT cfa_offset)
       }
 }
 
+/* Emit code to save BND registers using MOV insns.
+   First register is stored at CFA - CFA_OFFSET.  */
+static void
+ix86_emit_save_bnd_regs_using_mov (HOST_WIDE_INT cfa_offset)
+{
+  unsigned int regno;
+  enum machine_mode mode = BNDmode;
+  unsigned int size = GET_MODE_SIZE (mode);
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (BND_REGNO_P (regno) && ix86_save_reg (regno, true))
+      {
+	ix86_emit_save_reg_using_mov (mode, regno, cfa_offset);
+	cfa_offset -= size;
+      }
+}
+
+/* Emit code to save MASK registers using MOV insns.
+   First register is stored at CFA - CFA_OFFSET.  */
+static void
+ix86_emit_save_mask_regs_using_mov (HOST_WIDE_INT cfa_offset)
+{
+  unsigned int regno;
+  enum machine_mode mode = TARGET_AVX512BW ? DImode : HImode;
+  unsigned int size = GET_MODE_SIZE (mode);
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (MASK_REGNO_P (regno) && ix86_save_reg (regno, true))
+      {
+	ix86_emit_save_reg_using_mov (mode, regno, cfa_offset);
+	cfa_offset -= size;
+      }
+}
+
 /* Emit code to save SSE registers using MOV insns.
    First register is stored at CFA - CFA_OFFSET.  */
 static void
 ix86_emit_save_sse_regs_using_mov (HOST_WIDE_INT cfa_offset)
 {
   unsigned int regno;
+  enum machine_mode vector_reg_mode;
+
+  if (cfun->machine->no_caller_saved_registers)
+    {
+      /* We must save full vector registers if there are no
+	 caller-saved registers.  */
+      if (TARGET_AVX512F)
+	vector_reg_mode = V16SFmode;
+      else if (TARGET_AVX)
+	vector_reg_mode = V8SFmode;
+      else
+	vector_reg_mode = V4SFmode;
+    }
+  else
+    vector_reg_mode = V4SFmode;
 
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
       {
-	ix86_emit_save_reg_using_mov (V4SFmode, regno, cfa_offset);
-	cfa_offset -= GET_MODE_SIZE (V4SFmode);
+	ix86_emit_save_reg_using_mov (vector_reg_mode, regno, cfa_offset);
+	cfa_offset -= GET_MODE_SIZE (vector_reg_mode);
       }
 }
 
@@ -11866,13 +12088,17 @@ find_drap_reg (void)
 {
   tree decl = cfun->decl;
 
+  /* Always use callee-saved register if there are no caller-saved
+     registers.  */
   if (TARGET_64BIT)
     {
       /* Use R13 for nested function or function need static chain.
 	 Since function with tail call may use any caller-saved
 	 registers in epilogue, DRAP must not use caller-saved
 	 register in such case.  */
-      if (DECL_STATIC_CHAIN (decl) || crtl->tail_call_emit)
+      if (DECL_STATIC_CHAIN (decl)
+	  || cfun->machine->no_caller_saved_registers
+	  || crtl->tail_call_emit)
 	return R13_REG;
 
       return R10_REG;
@@ -11883,7 +12109,9 @@ find_drap_reg (void)
 	 Since function with tail call may use any caller-saved
 	 registers in epilogue, DRAP must not use caller-saved
 	 register in such case.  */
-      if (DECL_STATIC_CHAIN (decl) || crtl->tail_call_emit)
+      if (DECL_STATIC_CHAIN (decl)
+	  || cfun->machine->no_caller_saved_registers
+	  || crtl->tail_call_emit)
 	return DI_REG;
 
       /* Reuse static chain register if it isn't used for parameter
@@ -11924,8 +12152,12 @@ ix86_minimum_incoming_stack_boundary (bool sibcall)
 {
   unsigned int incoming_stack_boundary;
 
+  /* Stack of interrupt handler is always aligned to MIN_STACK_BOUNDARY.
+   */
+  if (cfun->machine->func_type != TYPE_NORMAL)
+    incoming_stack_boundary = MIN_STACK_BOUNDARY;
   /* Prefer the one specified at command line. */
-  if (ix86_user_incoming_stack_boundary)
+  else if (ix86_user_incoming_stack_boundary)
     incoming_stack_boundary = ix86_user_incoming_stack_boundary;
   /* In 32bit, use MIN_STACK_BOUNDARY for incoming stack boundary
      if -mstackrealign is used, it isn't used for sibcall check and
@@ -12556,6 +12788,8 @@ ix86_expand_prologue (void)
   struct ix86_frame frame;
   HOST_WIDE_INT allocate;
   bool int_registers_saved;
+  bool bnd_registers_saved;
+  bool mask_registers_saved;
   bool sse_registers_saved;
   rtx static_chain = NULL_RTX;
 
@@ -12721,6 +12955,8 @@ ix86_expand_prologue (void)
     }
 
   int_registers_saved = (frame.nregs == 0);
+  bnd_registers_saved = (frame.nbndregs == 0);
+  mask_registers_saved = (frame.nmaskregs == 0);
   sse_registers_saved = (frame.nsseregs == 0);
 
   if (frame_pointer_needed && !m->fs.fp_valid)
@@ -12785,10 +13021,10 @@ ix86_expand_prologue (void)
 	 that we must allocate the size of the register save area before
 	 performing the actual alignment.  Otherwise we cannot guarantee
 	 that there's enough storage above the realignment point.  */
-      if (m->fs.sp_offset != frame.sse_reg_save_offset)
+      if (m->fs.sp_offset != frame.bnd_reg_save_offset)
         pro_epilogue_adjust_stack (stack_pointer_rtx, stack_pointer_rtx,
 				   GEN_INT (m->fs.sp_offset
-					    - frame.sse_reg_save_offset),
+					    - frame.bnd_reg_save_offset),
 				   -1, false);
 
       /* Align the stack.  */
@@ -13015,6 +13251,10 @@ ix86_expand_prologue (void)
 
   if (!int_registers_saved)
     ix86_emit_save_regs_using_mov (frame.reg_save_offset);
+  if (!bnd_registers_saved)
+    ix86_emit_save_bnd_regs_using_mov (frame.bnd_reg_save_offset);
+  if (!mask_registers_saved)
+    ix86_emit_save_mask_regs_using_mov (frame.mask_reg_save_offset);
   if (!sse_registers_saved)
     ix86_emit_save_sse_regs_using_mov (frame.sse_reg_save_offset);
 
@@ -13060,6 +13300,23 @@ ix86_expand_prologue (void)
      combined with prologue modifications.  */
   if (TARGET_SEH)
     emit_insn (gen_prologue_use (stack_pointer_rtx));
+
+  if (cfun->machine->func_type != TYPE_NORMAL)
+    {
+      /* Since the pointer argument of interrupt handler isn't a real
+	 argument, adjust DECL_INCOMING_RTL for debug output.  */
+      tree arg = DECL_ARGUMENTS (current_function_decl);
+      gcc_assert (arg != NULL_TREE
+		  && POINTER_TYPE_P (TREE_TYPE (arg)));
+      if (cfun->machine->func_type == TYPE_EXCEPTION)
+	/* (AP) in the current frame in exception handler.  */
+	DECL_INCOMING_RTL (arg) = arg_pointer_rtx;
+      else
+	/* -WORD(AP) in the current frame in interrupt handler.  */
+	DECL_INCOMING_RTL (arg) = plus_constant (Pmode,
+						 arg_pointer_rtx,
+						 -UNITS_PER_WORD);
+    }
 }
 
 /* Emit code to restore REG using a POP insn.  */
@@ -13201,6 +13458,90 @@ ix86_emit_restore_regs_using_mov (HOST_WIDE_INT cfa_offset,
       }
 }
 
+/* Emit code to restore saved BND registers using MOV insns.
+   First register is restored from CFA - CFA_OFFSET.  */
+static void
+ix86_emit_restore_bnd_regs_using_mov (HOST_WIDE_INT cfa_offset,
+				      bool maybe_eh_return)
+{
+  struct machine_function *m = cfun->machine;
+  unsigned int regno;
+  enum machine_mode mode = BNDmode;
+  unsigned int size = GET_MODE_SIZE (mode);
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (BND_REGNO_P (regno) && ix86_save_reg (regno, maybe_eh_return))
+      {
+	rtx reg = gen_rtx_REG (mode, regno);
+	rtx mem;
+	rtx_insn *insn;
+
+	mem = choose_baseaddr (cfa_offset);
+	mem = gen_frame_mem (mode, mem);
+	insn = emit_move_insn (reg, mem);
+
+	if (m->fs.cfa_reg == crtl->drap_reg && regno == REGNO (crtl->drap_reg))
+	  {
+	    /* Previously we'd represented the CFA as an expression
+	       like *(%ebp - 8).  We've just popped that value from
+	       the stack, which means we need to reset the CFA to
+	       the drap register.  This will remain until we restore
+	       the stack pointer.  */
+	    add_reg_note (insn, REG_CFA_DEF_CFA, reg);
+	    RTX_FRAME_RELATED_P (insn) = 1;
+
+	    /* This means that the DRAP register is valid for addressing.  */
+	    m->fs.drap_valid = true;
+	  }
+	else
+	  ix86_add_cfa_restore_note (NULL, reg, cfa_offset);
+
+	cfa_offset -= size;
+      }
+}
+
+/* Emit code to restore saved MASK registers using MOV insns.
+   First register is restored from CFA - CFA_OFFSET.  */
+static void
+ix86_emit_restore_mask_regs_using_mov (HOST_WIDE_INT cfa_offset,
+				       bool maybe_eh_return)
+{
+  struct machine_function *m = cfun->machine;
+  unsigned int regno;
+  enum machine_mode mode = TARGET_AVX512BW ? DImode : HImode;
+  unsigned int size = GET_MODE_SIZE (mode);
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (MASK_REGNO_P (regno) && ix86_save_reg (regno, maybe_eh_return))
+      {
+	rtx reg = gen_rtx_REG (mode, regno);
+	rtx mem;
+	rtx_insn *insn;
+
+	mem = choose_baseaddr (cfa_offset);
+	mem = gen_frame_mem (mode, mem);
+	insn = emit_move_insn (reg, mem);
+
+	if (m->fs.cfa_reg == crtl->drap_reg && regno == REGNO (crtl->drap_reg))
+	  {
+	    /* Previously we'd represented the CFA as an expression
+	       like *(%ebp - 8).  We've just popped that value from
+	       the stack, which means we need to reset the CFA to
+	       the drap register.  This will remain until we restore
+	       the stack pointer.  */
+	    add_reg_note (insn, REG_CFA_DEF_CFA, reg);
+	    RTX_FRAME_RELATED_P (insn) = 1;
+
+	    /* This means that the DRAP register is valid for addressing.  */
+	    m->fs.drap_valid = true;
+	  }
+	else
+	  ix86_add_cfa_restore_note (NULL, reg, cfa_offset);
+
+	cfa_offset -= size;
+      }
+}
+
 /* Emit code to restore saved registers using MOV insns.
    First register is restored from CFA - CFA_OFFSET.  */
 static void
@@ -13208,27 +13549,44 @@ ix86_emit_restore_sse_regs_using_mov (HOST_WIDE_INT cfa_offset,
 				      bool maybe_eh_return)
 {
   unsigned int regno;
+  enum machine_mode vector_reg_mode;
+
+  if (cfun->machine->no_caller_saved_registers)
+    {
+      /* We must restore full vector registers if there are no
+	 caller-saved registers.  */
+      if (TARGET_AVX512F)
+	vector_reg_mode = V16SFmode;
+      else if (TARGET_AVX)
+	vector_reg_mode = V8SFmode;
+      else
+	vector_reg_mode = V4SFmode;
+    }
+  else
+    vector_reg_mode = V4SFmode;
 
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, maybe_eh_return))
       {
-	rtx reg = gen_rtx_REG (V4SFmode, regno);
+	rtx reg = gen_rtx_REG (vector_reg_mode, regno);
 	rtx mem;
 	unsigned int align;
 
 	mem = choose_baseaddr (cfa_offset);
-	mem = gen_rtx_MEM (V4SFmode, mem);
+	mem = gen_rtx_MEM (vector_reg_mode, mem);
 
 	/* The location is aligned up to INCOMING_STACK_BOUNDARY.  */
-	align = MIN (GET_MODE_ALIGNMENT (V4SFmode), INCOMING_STACK_BOUNDARY);
+	align = MIN (GET_MODE_ALIGNMENT (vector_reg_mode),
+		     INCOMING_STACK_BOUNDARY);
 	set_mem_align (mem, align);
 
 	/* SSE saves are not within re-aligned local stack frame.
 	   In case INCOMING_STACK_BOUNDARY is misaligned, we have
 	   to emit unaligned load.  */
-	if (align < 128)
+	if (vector_reg_mode == V4SFmode && align < 128)
 	  {
-	    rtx unspec = gen_rtx_UNSPEC (V4SFmode, gen_rtvec (1, mem),
+	    rtx unspec = gen_rtx_UNSPEC (vector_reg_mode,
+					 gen_rtvec (1, mem),
 					 UNSPEC_LOADU);
 	    emit_insn (gen_rtx_SET (reg, unspec));
 	  }
@@ -13237,7 +13595,7 @@ ix86_emit_restore_sse_regs_using_mov (HOST_WIDE_INT cfa_offset,
 
 	ix86_add_cfa_restore_note (NULL, reg, cfa_offset);
 
-	cfa_offset -= GET_MODE_SIZE (V4SFmode);
+	cfa_offset -= GET_MODE_SIZE (vector_reg_mode);
       }
 }
 
@@ -13336,16 +13694,25 @@ ix86_expand_epilogue (int style)
       if (TARGET_64BIT
 	  && m->fs.sp_offset > 0x7fffffff
 	  && !(m->fs.fp_valid || m->fs.drap_valid)
-	  && (frame.nsseregs + frame.nregs) != 0)
+	  && (frame.nsseregs + frame.nmaskregs + frame.nbndregs
+	      + frame.nregs) != 0)
 	{
 	  pro_epilogue_adjust_stack (stack_pointer_rtx, stack_pointer_rtx,
 				     GEN_INT (m->fs.sp_offset
-					      - frame.sse_reg_save_offset),
+					      - frame.bnd_reg_save_offset),
 				     style,
 				     m->fs.cfa_reg == stack_pointer_rtx);
 	}
     }
 
+  if (frame.nbndregs)
+    ix86_emit_restore_bnd_regs_using_mov (frame.bnd_reg_save_offset,
+					  style == 2);
+
+  if (frame.nmaskregs)
+    ix86_emit_restore_mask_regs_using_mov (frame.mask_reg_save_offset,
+					   style == 2);
+
   /* If there are any SSE registers to restore, then we have to do it
      via moves, since there's obviously no pop for SSE regs.  */
   if (frame.nsseregs)
@@ -13543,7 +13910,20 @@ ix86_expand_epilogue (int style)
       return;
     }
 
-  if (crtl->args.pops_args && crtl->args.size)
+  if (cfun->machine->func_type != TYPE_NORMAL)
+    {
+      /* Return with the "IRET" instruction from interrupt handler.
+	 Pop the 'ERROR_CODE' off the stack before the 'IRET'
+	 instruction in exception handler.  */
+      if (cfun->machine->func_type == TYPE_EXCEPTION)
+	{
+	  rtx r = plus_constant (Pmode, stack_pointer_rtx,
+				 UNITS_PER_WORD);
+	  emit_insn (gen_rtx_SET (stack_pointer_rtx, r));
+	}
+      emit_jump_insn (gen_interrupt_return ());
+    }
+  else if (crtl->args.pops_args && crtl->args.size)
     {
       rtx popc = GEN_INT (crtl->args.pops_args);
 
@@ -18512,6 +18892,14 @@ ix86_expand_move (machine_mode mode, rtx operands[])
   rtx op0, op1;
   enum tls_model model;
 
+  if (cfun->machine->func_type != TYPE_NORMAL && IS_STACK_MODE (mode))
+    {
+      error ("80387 instructions aren't allowed in %s service routine",
+	     (cfun->machine->func_type == TYPE_EXCEPTION
+	      ? "exception" : "interrupt"));
+      return;
+    }
+
   op0 = operands[0];
   op1 = operands[1];
 
@@ -18656,6 +19044,15 @@ ix86_expand_vector_move (machine_mode mode, rtx operands[])
   rtx op0 = operands[0], op1 = operands[1];
   unsigned int align = GET_MODE_ALIGNMENT (mode);
 
+  if (cfun->machine->func_type != TYPE_NORMAL
+      && VALID_MMX_REG_MODE (mode))
+    {
+      error ("MMX/3Dnow instructions aren't allowed in %s service routine",
+	     (cfun->machine->func_type == TYPE_EXCEPTION
+	      ? "exception" : "interrupt"));
+      return;
+    }
+
   if (push_operand (op0, VOIDmode))
     op0 = emit_move_resolve_push (mode, op0);
 
@@ -26741,6 +27138,17 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
   rtx vec[3];
   rtx use = NULL, call;
   unsigned int vec_len = 0;
+  tree fndecl;
+
+  if (GET_CODE (XEXP (fnaddr, 0)) == SYMBOL_REF)
+    {
+      fndecl = SYMBOL_REF_DECL (XEXP (fnaddr, 0));
+      if (fndecl
+	  && (lookup_attribute ("interrupt", DECL_ATTRIBUTES (fndecl))))
+	error ("interrupt service routine can't be called directly");
+    }
+  else
+    fndecl = NULL_TREE;
 
   if (pop == const0_rtx)
     pop = NULL;
@@ -26838,8 +27246,29 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
       vec[vec_len++] = pop;
     }
 
-  if (TARGET_64BIT_MS_ABI
-      && (!callarg2 || INTVAL (callarg2) != -2))
+  if (cfun->machine->no_caller_saved_registers
+      && (!fndecl
+	  || !lookup_attribute ("no_caller_saved_registers",
+				DECL_ATTRIBUTES (fndecl))))
+    {
+      static const char ix86_call_used_regs[] = CALL_USED_REGISTERS;
+      bool is_64bit_ms_abi = (TARGET_64BIT
+			      && ix86_function_abi (fndecl) == MS_ABI);
+      char c_mask = CALL_USED_REGISTERS_MASK (is_64bit_ms_abi);
+
+      /* If there are no caller-saved registers, add all registers
+	 that are clobbered by the call.  */
+      for (int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+	if (!fixed_regs[i]
+	    && (ix86_call_used_regs[i] == 1
+		|| (ix86_call_used_regs[i] & c_mask))
+	    && !STACK_REGNO_P (i)
+	    && !MMX_REGNO_P (i))
+	  clobber_reg (&use,
+		       gen_rtx_REG (GET_MODE (regno_reg_rtx[i]), i));
+    }
+  else if (TARGET_64BIT_MS_ABI
+	   && (!callarg2 || INTVAL (callarg2) != -2))
     {
       int const cregs_size
 	= ARRAY_SIZE (x86_64_ms_sysv_extra_clobbered_registers);
@@ -44226,6 +44655,75 @@ ix86_handle_fndecl_attribute (tree *node, tree name, tree, int,
   return NULL_TREE;
 }
 
+static tree
+ix86_handle_no_caller_saved_registers_attribute (tree *node,
+						 tree name, tree, int,
+						 bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+	       name);
+      *no_add_attrs = true;
+    }
+  /* DECL_RESULT does not exist there yet, but the function type
+     contains return type data.  */
+  tree func_type = TREE_TYPE (*node);
+  tree return_type = TREE_TYPE (func_type);
+  if (! VOID_TYPE_P (return_type))
+    error ("function without caller-saved registers can't have non-void return value");
+  return NULL_TREE;
+}
+
+static tree
+ix86_handle_interrupt_attribute (tree *node, tree name, tree, int,
+				 bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+	       name);
+      *no_add_attrs = true;
+    }
+
+  /* DECL_RESULT and DECL_ARGUMENTS do not exist there yet,
+     but the function type contains args and return type data.  */
+  tree func_type = TREE_TYPE (*node);
+  tree return_type = TREE_TYPE (func_type);
+
+  int nargs = 0;
+  tree current_arg_type = TYPE_ARG_TYPES (func_type);
+  while (current_arg_type
+	 && ! VOID_TYPE_P (TREE_VALUE (current_arg_type)))
+    {
+      if (nargs == 0)
+	{
+	  if (! POINTER_TYPE_P (TREE_VALUE (current_arg_type)))
+	    error ("interrupt service routine should have a pointer "
+		   "as the first argument");
+	}
+      else if (nargs == 1)
+	{
+	  if (TREE_CODE (TREE_VALUE (current_arg_type)) != INTEGER_TYPE
+	      || TYPE_MODE (TREE_VALUE (current_arg_type)) != word_mode)
+	    error ("interrupt service routine should have unsigned %s"
+		   "int as the second argument",
+		   TARGET_64BIT
+		   ? (TARGET_X32 ? "long long " : "long ")
+		   : "");
+	}
+      nargs++;
+      current_arg_type = TREE_CHAIN (current_arg_type);
+    }
+  if (!nargs || nargs > 2)
+    error ("interrupt service routine can only have a pointer argument "
+	   "and an optional integer argument");
+  if (! VOID_TYPE_P (return_type))
+    error ("interrupt service routine can't have non-void return value");
+
+  return NULL_TREE;
+}
+
 static bool
 ix86_ms_bitfield_layout_p (const_tree record_type)
 {
@@ -48227,6 +48725,11 @@ static const struct attribute_spec ix86_attribute_table[] =
     false },
   { "callee_pop_aggregate_return", 1, 1, false, true, true,
     ix86_handle_callee_pop_aggregate_return, true },
+  { "interrupt", 0, 0, true, false, false,
+    ix86_handle_interrupt_attribute, false },
+  { "no_caller_saved_registers", 0, 0, true, false, false,
+    ix86_handle_no_caller_saved_registers_attribute, false },
+
   /* End element.  */
   { NULL,        0, 0, false, false, false, NULL, false }
 };
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 4a84fb9..3bfe604 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -1735,6 +1735,11 @@ typedef struct ix86_args {
 
 #define EXIT_IGNORE_STACK 1
 
+/* Define this macro as a C expression that is nonzero for registers
+   used by the epilogue or the `return' pattern.  */
+
+#define EPILOGUE_USES(REGNO) ix86_epilogue_uses (REGNO)
+
 /* Output assembler code for a block containing the constant parts
    of a trampoline, leaving space for the variable parts.  */
 
@@ -2450,6 +2455,18 @@ struct GTY(()) machine_frame_state
 /* Private to winnt.c.  */
 struct seh_frame_state;
 
+enum function_type
+{
+  TYPE_NORMAL = 0,
+  /* The current function is an interrupt service routine with a
+     pointer argument as specified by the "interrupt" attribute.  */
+  TYPE_INTERRUPT,
+  /* The current function is an interrupt service routine with a
+     pointer argument and an integer argument as specified by the
+     "interrupt" attribute.  */
+  TYPE_EXCEPTION
+};
+
 struct GTY(()) machine_function {
   struct stack_local_entry *stack_locals;
   const char *some_ld_name;
@@ -2500,6 +2517,13 @@ struct GTY(()) machine_function {
   /* If true, it is safe to not save/restore DRAP register.  */
   BOOL_BITFIELD no_drap_save_restore : 1;
 
+  /* Function type.  */
+  ENUM_BITFIELD(function_type) func_type : 2;
+
+  /* If true, the current function is a function specified with
+     the "interrupt" or "no_caller_saved_registers" attribute.  */
+  BOOL_BITFIELD no_caller_saved_registers : 1;
+
   /* If true, there is register available for argument passing.  This
      is used only in ix86_function_ok_for_sibcall by 32-bit to determine
      if there is scratch register available for indirect sibcall.  In
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index d0c0d23..810833c 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -193,6 +193,9 @@
   UNSPEC_BNDCU
   UNSPEC_BNDCN
   UNSPEC_MPX_FENCE
+
+  ;; IRET support
+  UNSPEC_INTERRUPT_RETURN
 ])
 
 (define_c_enum "unspecv" [
@@ -12190,6 +12193,12 @@
    (set_attr "modrm" "0")
    (set_attr "maybe_prefix_bnd" "1")])
 
+(define_insn "interrupt_return"
+  [(simple_return)
+   (unspec [(const_int 0)] UNSPEC_INTERRUPT_RETURN)]
+  "reload_completed"
+  "iret")
+
 ;; Used by x86_machine_dependent_reorg to avoid penalty on single byte RET
 ;; instruction Athlon and K8 have.
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 79440d3..b70e40c 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -5079,6 +5079,69 @@ On x86-32 targets, the @code{stdcall} attribute causes the compiler to
 assume that the called function pops off the stack space used to
 pass arguments, unless it takes a variable number of arguments.
 
+@item no_caller_saved_registers
+@cindex @code{no_caller_saved_registers} function attribute, x86
+Use this attribute to indicate that the specified function has no
+caller-saved registers.  That is, all registers are callee-saved.
+The compiler generates proper function entry and exit sequences to
+save and restore any modified registers.
+
+@item interrupt
+@cindex @code{interrupt} function attribute, x86
+Use this attribute to indicate that the specified function is an
+interrupt handler.  The compiler generates function
+entry and exit sequences suitable for use in an interrupt handler when
+this attribute is present.  The @code{IRET} instruction, instead of the
+@code{RET} instruction, is used to return from interrupt handlers.  All
+registers, except for the EFLAGS register which is restored by the
+@code{IRET} instruction, are preserved by the compiler.
+
+Any interruptible-without-stack-switch code must be compiled with
+@option{-mno-red-zone} since interrupt handlers can and will, because
+of the hardware design, touch the red zone.
+
+An interrupt handler must be declared with a mandatory pointer
+argument:
+
+@smallexample
+struct interrupt_frame;
+
+__attribute__ ((interrupt))
+void
+f (struct interrupt_frame *frame)
+@{
+@}
+@end smallexample
+
+and user must properly define the structure the pointer pointing to.
+
+The exception handler is very similar to the interrupt handler with
+a different mandatory function signature:
+
+@smallexample
+#ifdef __x86_64__
+typedef unsigned long long int uword_t;
+#else
+typedef unsigned int uword_t;
+#endif
+
+struct interrupt_frame;
+
+__attribute__ ((interrupt))
+void
+f (struct interrupt_frame *frame, uword_t error_code)
+@{
+  ...
+@}
+@end smallexample
+
+and compiler pops the error code off the stack before the @code{IRET}
+instruction.
+
+The exception handler should only be used for exceptions which push an
+error code and all other exceptions must use the interrupt handler.
+The system will crash if the wrong handler is used.
+
 @item target (@var{options})
 @cindex @code{target} function attribute
 As discussed in @ref{Common Function Attributes}, this attribute 
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-1.c b/gcc/testsuite/gcc.target/i386/interrupt-1.c
new file mode 100644
index 0000000..39d6b20
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-1.c
@@ -0,0 +1,53 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-push-args -maccumulate-outgoing-args" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern int bar (int);
+
+void foo (void *frame)
+{
+  int a,b,c,d,e,f,i;
+  a = bar (5);
+  b = bar (a);
+  c = bar (b);
+  d = bar (c);
+  e = bar (d);
+  f = bar (e);
+  for (i = 1; i < 10; i++)
+  {
+    a += bar (a + i) + bar (b + i) +
+	 bar (c + i) + bar (d + i) +
+	 bar (e + i) + bar (f + i);
+  }
+}
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)bx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)si" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%edi" 2 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r12" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r13" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r14" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r15" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)bx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)si" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%edi" 2 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r12" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r13" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r14" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r15" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-10.c b/gcc/testsuite/gcc.target/i386/interrupt-10.c
new file mode 100644
index 0000000..64e621c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-10.c
@@ -0,0 +1,24 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mavx512bw -mno-iamcu -maccumulate-outgoing-args" } */
+
+extern void bar (void);
+
+struct interrupt_frame;
+
+void
+ __attribute__ ((interrupt))
+foo (struct interrupt_frame *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\)" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%zmm\[0-9\]+" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%zmm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "kmovq\[\\t \]*%k\[0-7\]+,\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\)" 8 } } */
+/* { dg-final { scan-assembler-times "kmovq\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%k\[0-7\]+" 8 } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-11.c b/gcc/testsuite/gcc.target/i386/interrupt-11.c
new file mode 100644
index 0000000..ba47019
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-11.c
@@ -0,0 +1,37 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-sse -mno-iamcu -maccumulate-outgoing-args" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-12.c b/gcc/testsuite/gcc.target/i386/interrupt-12.c
new file mode 100644
index 0000000..5c9eb74
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-12.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int check_int (int *i, void *, int align);
+typedef int aligned __attribute__((aligned(64)));
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+__attribute__((interrupt))
+void
+foo (void *frame, uword_t error_code)
+{
+  aligned j;
+  if (check_int (frame, &j, __alignof__(j)))
+    __builtin_abort ();
+}
+
+/* { dg-final { scan-assembler-times "and\[lq\]?\[^\\n\]*-64,\[^\\n\]*sp" 1 } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-13.c b/gcc/testsuite/gcc.target/i386/interrupt-13.c
new file mode 100644
index 0000000..8cf0b25
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-13.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int check_int (int *i, void *, int align);
+typedef int aligned __attribute__((aligned(64)));
+
+__attribute__((interrupt))
+void
+foo (void *frame)
+{
+  aligned j;
+  if (check_int (frame, &j, __alignof__(j)))
+    __builtin_abort ();
+}
+
+/* { dg-final { scan-assembler-times "and\[lq\]?\[^\\n\]*-64,\[^\\n\]*sp" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-14.c b/gcc/testsuite/gcc.target/i386/interrupt-14.c
new file mode 100644
index 0000000..3772b01
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-14.c
@@ -0,0 +1,38 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-sse -mno-iamcu -mno-sse4 -mno-popcnt -maccumulate-outgoing-args" } */
+
+extern int i, cnt;
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  cnt = __builtin_popcount (i);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%esi" { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)di" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)di" 1 } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 1 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-15.c b/gcc/testsuite/gcc.target/i386/interrupt-15.c
new file mode 100644
index 0000000..16d82c2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-15.c
@@ -0,0 +1,28 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mpush-args -maccumulate-outgoing-args" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  bar (error);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%rax" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:r|e)bp" 1 } } */
+/* { dg-final { scan-assembler-times "leave" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%eax" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movl\[\\t \]*-4\\(%ebp\\),\[\\t \]*%eax" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movq\[\\t \]*-8\\(%(?:r|e)bp\\),\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 1 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-16.c b/gcc/testsuite/gcc.target/i386/interrupt-16.c
new file mode 100644
index 0000000..f20f117
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-16.c
@@ -0,0 +1,28 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mno-push-args -maccumulate-outgoing-args" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  bar (error);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%rax" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:r|e)bp" 1 } } */
+/* { dg-final { scan-assembler-times "leave" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%eax" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movl\[\\t \]*-4\\(%ebp\\),\[\\t \]*%eax" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movq\[\\t \]*-8\\(%(?:r|e)bp\\),\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 1 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-17.c b/gcc/testsuite/gcc.target/i386/interrupt-17.c
new file mode 100644
index 0000000..486a2eb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-17.c
@@ -0,0 +1,33 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mpush-args -mno-accumulate-outgoing-args" } */
+
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame)
+{
+  bar (3);
+}
+
+void
+ __attribute__ ((interrupt))
+fn2 (void *frame)
+{
+  bar (3);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[8-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r1\[0-2\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r1\[4-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:r|e)bp" 2 } } */
+/* { dg-final { scan-assembler-times "leave" 2 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%edi" 2 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r13" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r13" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 2 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-18.c b/gcc/testsuite/gcc.target/i386/interrupt-18.c
new file mode 100644
index 0000000..af32851
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-18.c
@@ -0,0 +1,35 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mpush-args -maccumulate-outgoing-args" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  bar (error);
+}
+
+void
+ __attribute__ ((interrupt))
+fn2 (void *frame, uword_t error)
+{
+  bar (error);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%rax" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:r|e)bp" 2 } } */
+/* { dg-final { scan-assembler-times "leave" 2 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%eax" 2 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movl\[\\t \]*-4\\(%ebp\\),\[\\t \]*%eax" 2 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movq\[\\t \]*-8\\(%(?:r|e)bp\\),\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 2 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 2 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-19.c b/gcc/testsuite/gcc.target/i386/interrupt-19.c
new file mode 100644
index 0000000..67e2fbd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-19.c
@@ -0,0 +1,25 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mno-push-args -maccumulate-outgoing-args" } */
+
+extern void foo (int) __attribute__ ((no_caller_saved_registers));
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+extern int x;
+
+void
+foo (int i)
+{
+  bar (i);
+  x = i;
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]-*\[0-9\]*\\(%\[re\]?bp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:r|e)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:r|e)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-2.c b/gcc/testsuite/gcc.target/i386/interrupt-2.c
new file mode 100644
index 0000000..27b40b0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-2.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wall -g" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+}
+
+/* { dg-final { scan-assembler-not "add(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-20.c b/gcc/testsuite/gcc.target/i386/interrupt-20.c
new file mode 100644
index 0000000..779addb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-20.c
@@ -0,0 +1,27 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mno-push-args" } */
+
+extern void foo (int) __attribute__ ((no_caller_saved_registers));
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+extern int x;
+
+void
+foo (int i)
+{
+  x = i;
+  bar (i + 1);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]-*\[0-9\]*\\(%\[re\]?bp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%rdx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%eax" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%eax" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "jmp" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-21.c b/gcc/testsuite/gcc.target/i386/interrupt-21.c
new file mode 100644
index 0000000..7cc5181
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-21.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx -mno-iamcu -msse" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movaps\[\\t \]*%xmm3,\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movaps\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%xmm3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-2\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-2\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[4-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[4-9\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm1\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm1\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-22.c b/gcc/testsuite/gcc.target/i386/interrupt-22.c
new file mode 100644
index 0000000..5dd6290
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-22.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm3,\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-2\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-2\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[4-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[4-9\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm1\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm1\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-23.c b/gcc/testsuite/gcc.target/i386/interrupt-23.c
new file mode 100644
index 0000000..e0cf550
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-23.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mavx512f -mno-iamcu" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm3,\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-2\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-2\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[4-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[4-9\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm1\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm1\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-24.c b/gcc/testsuite/gcc.target/i386/interrupt-24.c
new file mode 100644
index 0000000..9b92c00
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-24.c
@@ -0,0 +1,21 @@
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mno-iamcu -mmpx" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "bnd3");
+}
+
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*%bnd3,\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%bnd3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-25.c b/gcc/testsuite/gcc.target/i386/interrupt-25.c
new file mode 100644
index 0000000..849fd3a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-25.c
@@ -0,0 +1,24 @@
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -mno-avx -mno-iamcu -msse -mincoming-stack-boundary=2" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%xmm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%xmm3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-2\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-2\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[4-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[4-9\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm1\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm1\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%e(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%e(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%ebp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-26.c b/gcc/testsuite/gcc.target/i386/interrupt-26.c
new file mode 100644
index 0000000..27b40b0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-26.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wall -g" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+}
+
+/* { dg-final { scan-assembler-not "add(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-27.c b/gcc/testsuite/gcc.target/i386/interrupt-27.c
new file mode 100644
index 0000000..774bb4b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-27.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O0 -g -mno-avx -mno-iamcu -msse" } */
+
+typedef float __v4sf __attribute__ ((__vector_size__ (16)));
+__v4sf x, y;
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  x = y;
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%xmm0,\[\\t \]*-?\[0-9\]*\\(%\[re\]?bp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-?\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%xmm0" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-3.c b/gcc/testsuite/gcc.target/i386/interrupt-3.c
new file mode 100644
index 0000000..b09f56c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-3.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -g" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+void
+__attribute__((interrupt))
+fn (void* frame, uword_t error)
+{
+}
+
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-387-err.c b/gcc/testsuite/gcc.target/i386/interrupt-387-err.c
new file mode 100644
index 0000000..36094f2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-387-err.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -m80387 -mlong-double-80 -mno-iamcu -mno-sse" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+extern long double y, x;
+
+void
+fn0 (void)
+{
+  x = y;
+  y = 0;
+}
+
+void
+__attribute__((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  x = 0; /* { dg-error "80387 instructions aren't allowed in exception service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn2 (void *frame)
+{
+  x = y; /* { dg-error "80387 instructions aren't allowed in interrupt service routine" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-4.c b/gcc/testsuite/gcc.target/i386/interrupt-4.c
new file mode 100644
index 0000000..c34dbe0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-4.c
@@ -0,0 +1,32 @@
+/* { dg-do link } */
+/* { dg-options "-O -g" } */
+
+#include <stdint.h>
+
+extern void link_error (void);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+struct interrupt_frame
+{
+  uword_t ip;
+  uword_t cs;
+  uword_t flags;
+  uword_t sp;
+  uword_t ss;
+};
+
+__attribute__ ((used, interrupt))
+void
+foo (struct interrupt_frame *frame)
+{
+  void *ra = __builtin_return_address (0);
+  if ((uintptr_t) ra != (uintptr_t) frame->ip)
+    link_error ();
+}
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-5.c b/gcc/testsuite/gcc.target/i386/interrupt-5.c
new file mode 100644
index 0000000..3c4bc2d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-5.c
@@ -0,0 +1,23 @@
+/* { dg-do link } */
+/* { dg-options "-O -g" } */
+
+#include <stdint.h>
+
+extern void link_error (void);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+__attribute__ ((used, interrupt))
+void
+foo (void *frame, uword_t error)
+{
+  void *ra = __builtin_return_address (0);
+  if ((uintptr_t) ra != (uintptr_t) error)
+    link_error ();
+}
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-6.c b/gcc/testsuite/gcc.target/i386/interrupt-6.c
new file mode 100644
index 0000000..a1a3d0e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-6.c
@@ -0,0 +1,40 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+extern int error;
+
+__attribute__((interrupt))
+void
+fn1 (void *p, short error_code)
+{ /* { dg-error "interrupt service routine should have unsigned \(long long |long |\)int as the second argument" } */
+}
+
+__attribute__((interrupt))
+void
+fn2 (void)
+{ /* { dg-error "interrupt service routine can only have a pointer argument and an optional integer argument" } */
+}
+
+__attribute__((interrupt))
+void
+fn3 (uword_t error_code)
+{ /* { dg-error "interrupt service routine should have a pointer as the first argument" } */
+  error = error_code;
+}
+
+__attribute__((interrupt))
+void
+fn4 (uword_t error_code, void *frame)
+{ /* { dg-error "interrupt service routine should have .* the .* argument" } */
+  error = error_code;
+}
+
+extern int fn5 (void *) __attribute__ ((interrupt)); /* { dg-error "interrupt service routine can't have non-void return value" } */
+
+int
+fn5 (void *frame)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-7.c b/gcc/testsuite/gcc.target/i386/interrupt-7.c
new file mode 100644
index 0000000..e7441a2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-7.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int error;
+
+extern void fn (void *) __attribute__((interrupt));
+
+void
+foo (void)
+{
+  fn (&error); /* { dg-error "interrupt service routine can't be called directly" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-8.c b/gcc/testsuite/gcc.target/i386/interrupt-8.c
new file mode 100644
index 0000000..defec2e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-8.c
@@ -0,0 +1,20 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -maccumulate-outgoing-args" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm\[0-9\]+,\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\)" 16 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%ymm\[0-9\]+" 16 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm\[0-9\]+,\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%ymm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-9.c b/gcc/testsuite/gcc.target/i386/interrupt-9.c
new file mode 100644
index 0000000..56bd8dd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-9.c
@@ -0,0 +1,22 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512bw -mno-iamcu -mavx512f -maccumulate-outgoing-args" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]-*\[0-9\]*\\(%\[re\]?bp\\)" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%zmm\[0-9\]+" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%zmm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "kmovw\[\\t \]*%k\[0-7\]+,\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\)" 8 } } */
+/* { dg-final { scan-assembler-times "kmovw\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%k\[0-7\]+" 8 } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-bnd.c b/gcc/testsuite/gcc.target/i386/interrupt-bnd.c
new file mode 100644
index 0000000..9f50661
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-bnd.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mno-iamcu -mmpx" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "bnd3");
+}
+
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*%bnd3,\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%bnd3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
new file mode 100644
index 0000000..712b85f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
@@ -0,0 +1,35 @@
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -miamcu -maccumulate-outgoing-args" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern int bar (int);
+
+void foo (void *frame)
+{
+  int a,b,c,d,e,f,i;
+  a = bar (5);
+  b = bar (a);
+  c = bar (b);
+  d = bar (c);
+  e = bar (d);
+  f = bar (e);
+  for (i = 1; i < 10; i++)
+    a += bar (a + i) + bar (b + i) +
+	 bar (c + i) + bar (d + i) +
+	 bar (e + i) + bar (f+i);
+}
+
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%eax" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%ebx" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%ecx" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%edx" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%edi" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%esi" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%ebp" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%eax" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%ecx" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%edx" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%edi" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%esi" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%ebp" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c
new file mode 100644
index 0000000..fc4f367
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mmmx -mno-iamcu" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+typedef short __v4hi __attribute__ ((__vector_size__ (8)));
+typedef int __m64 __attribute__ ((__vector_size__ (8), __may_alias__));
+
+extern __m64 y, x;
+
+void
+fn0 (void)
+{
+  x = __extension__ (__m64){ 0 };
+  x = (__m64) __builtin_ia32_packsswb ((__v4hi) x, (__v4hi) y);
+  y = x;
+}
+
+void
+__attribute__((interrupt))
+fn1 (void *frame)
+{
+  x = (__m64) __builtin_ia32_packsswb ((__v4hi) x, (__v4hi) y); /* { dg-error "MMX/3Dnow (instructions|intrinsics) aren't allowed in interrupt service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn2 (void *frame)
+{
+  x = y; /* { dg-error "MMX/3Dnow instructions aren't allowed in interrupt service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn3 (void *frame, uword_t error)
+{
+  x = __extension__ (__m64){ 0 }; /* { dg-error "MMX/3Dnow instructions aren't allowed in exception service routine" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c b/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c
new file mode 100644
index 0000000..796b81b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mred-zone" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  /* No need to adjust stack if less than 128 bytes are used on stack
+     with a 128-byte red zone.  */
+  char array[112];
+  asm ("# %0 "
+       :
+       : "r" (array));
+}
+
+/* { dg-final { scan-assembler-not "(sub|add)(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c b/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c
new file mode 100644
index 0000000..c6a2822
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mred-zone" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  /* Need to adjust stack if more than 128 bytes are used on stack
+     with a 128-byte red zone.  */
+  char array[113];
+  asm ("# %0 "
+       :
+       : "r" (array));
+}
+
+/* { dg-final { scan-assembler-times "(?:sub|add)(?:l|q)\[\\t \]*\\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c b/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c
new file mode 100644
index 0000000..e222acf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O3" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern void bar (void);
+
+void foo (void *frame)
+{
+  bar ();
+}
+/* { dg-final { scan-assembler-not "jmp" } }*/
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c
new file mode 100644
index 0000000..b1b0abe
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern void bar (int);
+
+void f1 (void){  bar (1); }
+__attribute__((interrupt))
+void f2 (void *frame){  bar (2); }
+void f3 (void){  bar (3); }
+__attribute__((interrupt))
+void f4 (void *frame){  bar (4); }
+void f5 (void){  bar (5); }
+
+/* { dg-final { scan-assembler-times "push.\t%.ax" 2 } } */
+/* { dg-final { scan-assembler-times "pop.\t%.ax" 2 } } */
+/* { dg-final { scan-assembler-times "iret" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-xmm.c b/gcc/testsuite/gcc.target/i386/interrupt-xmm.c
new file mode 100644
index 0000000..6ffdd96
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-xmm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx -mno-iamcu -msse" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%xmm3,\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%xmm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-ymm.c b/gcc/testsuite/gcc.target/i386/interrupt-ymm.c
new file mode 100644
index 0000000..0754ae0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-ymm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*%ymm3,\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-zmm.c b/gcc/testsuite/gcc.target/i386/interrupt-zmm.c
new file mode 100644
index 0000000..f4d2fa1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-zmm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mavx512f -mno-iamcu" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*%zmm3,\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-10-20 13:25                                                       ` Yulia Koval
@ 2015-10-23 13:13                                                         ` Yulia Koval
  2015-11-06 14:08                                                           ` Yulia Koval
  0 siblings, 1 reply; 45+ messages in thread
From: Yulia Koval @ 2015-10-23 13:13 UTC (permalink / raw)
  To: Uros Bizjak; +Cc: H.J. Lu, Mike Stump, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 16769 bytes --]

Added fix for PR68037. Bootstraped/regtested on Linux/x86_64.

Implement x86 interrupt attribute

    The interrupt and exception handlers are called by x86 processors.  X86
    hardware pushes information onto stack and calls the handler.  The
    requirements are

    1. Both interrupt and exception handlers must use the 'IRET' instruction,
    instead of the 'RET' instruction, to return from the handlers.
    2. All registers are callee-saved in interrupt and exception handlers.
    3. The difference between interrupt and exception handlers is the
    exception handler must pop 'ERROR_CODE' off the stack before the 'IRET'
    instruction.

    The design goals of interrupt and exception handlers for x86 processors
    are:

    1. Support both 32-bit and 64-bit modes.
    2. Flexible for compilers to optimize.
    3. Easy to use by programmers.

    To implement interrupt and exception handlers for x86 processors, a
    compiler should support:

    'interrupt' attribute

    Use this attribute to indicate that the specified function with
    mandatory arguments is an interrupt or exception handler.  The compiler
    generates function entry and exit sequences suitable for use in an
    interrupt handler when this attribute is present.  The 'IRET' instruction,
    instead of the 'RET' instruction, is used to return from interrupt or
    exception handlers.  All registers, except for the EFLAGS register which
    is restored by the 'IRET' instruction, are preserved by the compiler.

    Any interruptible-without-stack-switch code must be compiled with
    -mno-red-zone since interrupt handlers can and will, because of the
    hardware design, touch the red zone.

    1. interrupt handler must be declared with a mandatory pointer argument:

    struct interrupt_frame;

    __attribute__ ((interrupt))
    void
    f (struct interrupt_frame *frame)
    {
    ...
    }

    and user must properly define the structure the pointer pointing to.

    2. exception handler:

    The exception handler is very similar to the interrupt handler with
    a different mandatory function signature:

    typedef unsigned int uword_t __attribute__ ((mode (__word__)));

    struct interrupt_frame;

    __attribute__ ((interrupt))
    void
    f (struct interrupt_frame *frame, uword_t error_code)
    {
    ...
    }

    and compiler pops the error code off stack before the 'IRET' instruction.

    The exception handler should only be used for exceptions which push an
    error code and all other exceptions must use the interrupt handler.
    The system will crash if the wrong handler is used.

    To be feature complete, compiler may implement the optional
    'no_caller_saved_registers' attribute:

    Use this attribute to indicate that the specified function has no
    caller-saved registers.  That is, all registers are callee-saved.
    The compiler generates proper function entry and exit sequences to
    save and restore any modified registers.

    The user can call functions specified with 'no_caller_saved_registers'
    attribute from an interrupt handler without saving and restoring all
    call clobbered registers.

    gcc/

    PR target/66960
    PR target/67630
    PR target/67634
    PR target/68037
    * config/i386/i386-protos.h (ix86_epilogue_uses): New prototype.
    * config/i386/i386.c (ix86_frame): Add nbndregs, nmaskregs,
    bnd_reg_save_offset and mask_reg_save_offset.
    (ix86_conditional_register_usage): Preserve all registers,
    except for function return registers if there are no caller-saved
    registers.
    (ix86_set_current_function): Set no_caller_saved_registers and
    func_type.  Call reinit_regs if AX register usage isn't
    consistent.
    (ix86_function_ok_for_sibcall): Return false if there are no
    caller-saved registers.
    (type_natural_mode): Don't warn ABI change for MMX in interrupt
    handler.
    (ix86_function_arg_advance): Skip for callee in interrupt
    handler.
    (ix86_function_arg): Handle arguments for callee in interrupt
    handler.
    (ix86_can_use_return_insn_p): Don't use `ret' instruction in
    interrupt handler.
    (ix86_epilogue_uses): New function.
    (ix86_hard_regno_scratch_ok): Likewise.
    (ix86_reg_ever_defined_p): Likewise.
    (ix86_nsaved_bndregs): Likewise.
    (ix86_nsaved_maskregs): Likewise.
    (ix86_emit_save_bnd_regs_using_mov): Likewise.
    (ix86_emit_save_mask_regs_using_mov): Likewise.
    (ix86_emit_restore_bnd_regs_using_mov): Likewise.
    (ix86_emit_restore_mask_regs_using_mov): Likewise.
    (ix86_handle_no_caller_saved_registers_attribute): Likewise.
    (ix86_handle_interrupt_attribute): Likewise.
    (ix86_save_reg): Preserve all registers in interrupt function
    after reload.  Preserve all registers, except for function
    return registers, if there are no caller-saved registers after
    reload.
    (ix86_nsaved_sseregs): Don't return 0 if there are no
    caller-saved registers.
    (ix86_compute_frame_layout): Set nbndregs and nmaskregs.  Set
    and allocate BND and MASK register save areas.  Allocate space to
    save full vector registers if there are no caller-saved registers.
    (ix86_emit_save_reg_using_mov): Don't use UNSPEC_STOREU to
    SSE registers.
    (ix86_emit_save_sse_regs_using_mov): Save full vector registers
    if there are no caller-saved registers.
    (find_drap_reg): Always use callee-saved register if there are
    no caller-saved registers.
    (ix86_minimum_incoming_stack_boundary): Return MIN_STACK_BOUNDARY
    for interrupt handler.
    (ix86_expand_prologue): Save BND and MASK registers.  Adjust
    DECL_INCOMING_RTL of the pointer argument for interrupt handler.
    (ix86_emit_restore_sse_regs_using_mov): Restore full vector
    registers if there are no caller-saved registers.
    (ix86_expand_epilogue): Restore BND and MASK registers.  Generate
    interrupt return for interrupt handler and pop the 'ERROR_CODE'
    off the stack before interrupt return in exception handler.
    (ix86_expand_move): Disallow 80387 instructions in exception
    handler.
    (ix86_expand_vector_move): Disallow MMX/3Dnow instructions in
    exception handler.
    (ix86_expand_call): Disallow calling interrupt handler directly.
    If there are no caller-saved registers, mark all registers that
    are clobbered by the call as clobbered.
    (ix86_attribute_table): Add interrupt and no_caller_saved_registers
    attributes.
    (TARGET_HARD_REGNO_SCRATCH_OK): New.
    * config/i386/i386.h (ACCUMULATE_OUTGOING_ARGS): Always use
    argument accumulation in interrupt function if stack may be
    realigned to avoid DRAP.
    * config/i386/i386.h (EPILOGUE_USES): New.
    (function_type): New enum.
    (machine_function): Add func_type and no_caller_saved_registers.
    * config/i386/i386.md (UNSPEC_INTERRUPT_RETURN): New.
    (interrupt_return): New pattern.
    * doc/extend.texi: Document x86 interrupt and
    no_caller_saved_registers attributes.

    gcc/testsuite/

    PR target/66960
    PR target/67630
    PR target/67634
    PR target/68037
    * gcc.dg/guality/pr68037-1.c: New test.
    * gcc.dg/guality/pr68037-2.c: Likewise.
    * gcc.dg/guality/pr68037-3.c: Likewise.
    * gcc.dg/torture/pr68037-1.c: Likewise.
    * gcc.dg/torture/pr68037-2.c: Likewise.
    * gcc.dg/torture/pr68037-3.c: Likewise.
    * gcc.target/i386/interrupt-1.c: Likewise.
    * gcc.target/i386/interrupt-2.c: Likewise.
    * gcc.target/i386/interrupt-3.c: Likewise.
    * gcc.target/i386/interrupt-4.c: Likewise.
    * gcc.target/i386/interrupt-5.c: Likewise.
    * gcc.target/i386/interrupt-6.c: Likewise.
    * gcc.target/i386/interrupt-7.c: Likewise.
    * gcc.target/i386/interrupt-8.c: Likewise.
    * gcc.target/i386/interrupt-9.c: Likewise.
    * gcc.target/i386/interrupt-10.c: Likewise.
    * gcc.target/i386/interrupt-11.c: Likewise.
    * gcc.target/i386/interrupt-12.c: Likewise.
    * gcc.target/i386/interrupt-13.c: Likewise.
    * gcc.target/i386/interrupt-14.c: Likewise.
    * gcc.target/i386/interrupt-15.c: Likewise.
    * gcc.target/i386/interrupt-16.c: Likewise.
    * gcc.target/i386/interrupt-17.c: Likewise.
    * gcc.target/i386/interrupt-18.c: Likewise.
    * gcc.target/i386/interrupt-19.c: Likewise.
    * gcc.target/i386/interrupt-20.c: Likewise.
    * gcc.target/i386/interrupt-21.c: Likewise.
    * gcc.target/i386/interrupt-22.c: Likewise.
    * gcc.target/i386/interrupt-23.c: Likewise.
    * gcc.target/i386/interrupt-24.c: Likewise.
    * gcc.target/i386/interrupt-25.c: Likewise.
    * gcc.target/i386/interrupt-26.c: Likewise.
    * gcc.target/i386/interrupt-387-err.c: Likewise.
    * gcc.target/i386/interrupt-bnd.c: Likewise.
    * gcc.target/i386/interrupt-iamcu.c: Likewise.
    * gcc.target/i386/interrupt-mmx-err.c: Likewise.
    * gcc.target/i386/interrupt-redzone-1.c: Likewise.
    * gcc.target/i386/interrupt-redzone-2.c: Likewise.
    * gcc.target/i386/interrupt-sibcall.c: Likewise.
    * gcc.target/i386/interrupt-switch-abi.c: Likewise.
    * gcc.target/i386/interrupt-xmm.c: Likewise.
    * gcc.target/i386/interrupt-ymm.c: Likewise.
    * gcc.target/i386/interrupt-zmm.c: Likewise.

On Tue, Oct 20, 2015 at 4:21 PM, Yulia Koval <vaalfreja@gmail.com> wrote:
> The debug_info section for the interrupt function looks ok.
>
> I tried to call it from assembler code to check it in gdb.
>
>         pushl   $0x333 ;eflags
>         pushl   $0x111 ;cs
>         pushl $0x222   ;eip
>         jmp  foo           ;interrupt function
>
> #define uword_t unsigned int
> struct interrupt_frame
> {
>      uword_t ip;
>      uword_t cs;
>      uword_t flags;
> };
>
> I get this inside the interrupt function:
>
> Breakpoint 1, foo (frame=0xffffd560) at interrupt-1.c:7
> 7               a = (struct interrupt_frame*) frame;
> (gdb) p/x ((struct interrupt_frame*)frame)->ip
> $1 = 0x222
> (gdb) p/x ((struct interrupt_frame*)frame)->cs
> $3 = 0x111
> (gdb) p/x ((struct interrupt_frame*)frame)->flags
> $4 = 0x333
>
> Frame pointer info looks ok.
>
>
> On Tue, Oct 13, 2015 at 3:18 PM, Yulia Koval <vaalfreja@gmail.com> wrote:
>>
>> Here is the current version of the patch with all the fixes.
>> Regtested\bootstraped it on 64 bit.
>>
>> We need a pointer since interrupt handler will update data pointing
>> to by frame.  Since error_code isn't at the normal location where the
>> parameter is passed on stack and frame isn't in a hard register, we
>> changed ix86_function_arg:
>>
>> +  if (!cum->caller && cfun->machine->func_type != TYPE_NORMAL)
>> +    {
>> +      /* The first argument of interrupt handler is a pointer and
>> +        points to the return address slot on stack.  The optional
>> +        second argument is an integer for error code on stack.  */
>> +      gcc_assert (type != NULL_TREE);
>> +      if (POINTER_TYPE_P (type))
>> +       {
>> +         if (cfun->machine->func_type == TYPE_EXCEPTION)
>> +           /* (AP) in the current frame in exception handler.  */
>> +           arg = arg_pointer_rtx;
>> +         else
>> +           /* -WORD(AP) in the current frame in interrupt handler.  */
>> +           arg = force_reg (Pmode,
>> +                            plus_constant (Pmode, arg_pointer_rtx,
>> +                                           -UNITS_PER_WORD));
>> +         if (mode != Pmode)
>> +           arg = convert_to_mode (mode, arg, 1);
>> +       }
>> +      else
>> +       {
>> +         gcc_assert (TREE_CODE (type) == INTEGER_TYPE
>> +                     && cfun->machine->func_type == TYPE_EXCEPTION
>> +                     && mode == word_mode);
>> +         /* The error code is at -WORD(AP) in the current frame in
>> +            exception handler.  */
>> +         arg = gen_rtx_MEM (word_mode,
>> +                            plus_constant (Pmode, arg_pointer_rtx,
>> +                                           -UNITS_PER_WORD));
>> +       }
>> +
>> +      return arg;
>> +    }
>> +
>>
>> to return a pseudo register.  It violates
>>
>>    Return where to put the arguments to a function.
>>    Return zero to push the argument on the stack, or a hard register in
>>    which to store the argument.
>>
>> Register allocator has no problem with parameters in pseudo registers.
>> But GCC crashes when it tries to access DECL_INCOMING_RTL as a hard
>> register when generating debug information.  We worked around it by
>> doing
>>
>> +
>> +  if (cfun->machine->func_type != TYPE_NORMAL)
>> +    {
>> +      /* Since the pointer argument of interrupt handler isn't a real
>> +         argument, adjust DECL_INCOMING_RTL for debug output.  */
>> +      tree arg = DECL_ARGUMENTS (current_function_decl);
>> +      gcc_assert (arg != NULL_TREE
>> +                 && POINTER_TYPE_P (TREE_TYPE (arg)));
>> +      if (cfun->machine->func_type == TYPE_EXCEPTION)
>> +       /* (AP) in the current frame in exception handler.  */
>> +       DECL_INCOMING_RTL (arg) = arg_pointer_rtx;
>> +      else
>> +       /* -WORD(AP) in the current frame in interrupt handler.  */
>> +       DECL_INCOMING_RTL (arg) = plus_constant (Pmode,
>> +                                                arg_pointer_rtx,
>> +                                                -UNITS_PER_WORD);
>> +    }
>>
>>
>> On Mon, Oct 5, 2015 at 12:29 PM, Uros Bizjak <ubizjak@gmail.com> wrote:
>> > On Mon, Oct 5, 2015 at 1:17 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
>> >
>> >>>>>>> Looking a bit deeper into the code, it looks that we want to realign
>> >>>>>>> the stack in the interrupt handler. Let's assume that interrupt
>> >>>>>>> handler is calling some other function that saves SSE vector regs to
>> >>>>>>> the stack. According to the x86 ABI, incoming stack of the called
>> >>>>>>> function is assumed to be aligned to 16 bytes. But, interrupt handler
>> >>>>>>> violates this assumption, since the stack could be aligned to only 4
>> >>>>>>> bytes for 32bit and 8 bytes for 64bit targets. Entering the called
>> >>>>>>> function with stack, aligned to less than 16 bytes will certainly
>> >>>>>>> violate ABI.
>> >>>>>>>
>> >>>>>>> So, it looks to me that we need to realign the stack in the interrupt
>> >>>>>>> handler unconditionally to 16bytes. In this case, we also won't need
>> >>>>>>> the following changes:
>> >>>>>>>
>> >>>>>>
>> >>>>>> Current stack alignment implementation requires at least
>> >>>>>> one, maybe two, scratch registers:
>> >>>>>>
>> >>>>>> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67841
>> >>>>>>
>> >>>>>> Extend it to the interrupt handler, which doesn't have any scratch
>> >>>>>> registers may require significant changes in backend as well as
>> >>>>>> register allocator.
>> >>>>>
>> >>>>> But without realignment, the handler is unusable for anything but
>> >>>>> simple functions. The handler will crash when called function will try
>> >>>>> to save vector reg to stack.
>> >>>>>
>> >>>>
>> >>>> We can use unaligned load and store to avoid crash.
>> >>>
>> >>> Oh, sorry, I meant "called function will crash", like:
>> >>>
>> >>> -> interrupt when %rsp = 0x...8 ->
>> >>> -> interrupt handler ->
>> >>> -> calls some function that tries to save xmm reg to stack
>> >>> -> crash in the called function
>> >>>
>> >>
>> >> It should be fixed by this patch.   But we need to fix stack
>> >> alignment in interrupt handler to avoid scratch register.
>> >>
>> >>
>> >> --
>> >> H.J.
>> >> ---
>> >> commit 15f48be1dc7ff48207927d0b835e593d058f695b
>> >> Author: H.J. Lu <hjl.tools@gmail.com>
>> >> Date:   Sun Oct 4 16:14:03 2015 -0700
>> >>
>> >>     Correctly set incoming stack boundary for interrupt handler
>> >>
>> >> diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
>> >> index 7ebdcd9..0f0cc3c 100644
>> >> --- a/gcc/config/i386/i386.c
>> >> +++ b/gcc/config/i386/i386.c
>> >> @@ -12037,8 +12037,11 @@ ix86_minimum_incoming_stack_boundary (bool sibcall)
>> >>  {
>> >>    unsigned int incoming_stack_boundary;
>> >>
>> >> +  /* Stack of interrupt handler is always aligned to word_mode.  */
>> >> +  if (cfun->machine->func_type != TYPE_NORMAL)
>> >> +    incoming_stack_boundary = TARGET_64BIT ? 64 : 32;
>> >
>> > Just a heads up that in order to support stack realignmnent on x86_64,
>> > MIN_STACK_BOUNDARY will soon be changed to BITS_PER_WORD, so you can
>> > use it in the line above. Please see comment #5 and #6 of PR 66697
>> > [1].
>> >
>> >>    /* Prefer the one specified at command line. */
>> >> -  if (ix86_user_incoming_stack_boundary)
>> >> +  else if (ix86_user_incoming_stack_boundary)
>> >>      incoming_stack_boundary = ix86_user_incoming_stack_boundary;
>> >>    /* In 32bit, use MIN_STACK_BOUNDARY for incoming stack boundary
>> >>       if -mstackrealign is used, it isn't used for sibcall check and
>> >
>> > [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66697
>> >
>> > Uros.

[-- Attachment #2: patch --]
[-- Type: application/octet-stream, Size: 95366 bytes --]

diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index 6a17ef4..c7ff393 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -278,6 +278,8 @@ extern rtx maybe_get_pool_constant (rtx);
 extern char internal_label_prefix[16];
 extern int internal_label_prefix_len;
 
+extern bool ix86_epilogue_uses (int);
+
 enum ix86_address_seg { SEG_DEFAULT, SEG_FS, SEG_GS };
 struct ix86_address
 {
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 6fd4a56..a19a85f 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -2457,6 +2457,8 @@ struct ix86_frame
 {
   int nsseregs;
   int nregs;
+  int nbndregs;
+  int nmaskregs;
   int va_arg_size;
   int red_zone_size;
   int outgoing_arguments_size;
@@ -2467,6 +2469,8 @@ struct ix86_frame
   HOST_WIDE_INT stack_pointer_offset;
   HOST_WIDE_INT hfp_save_offset;
   HOST_WIDE_INT reg_save_offset;
+  HOST_WIDE_INT bnd_reg_save_offset;
+  HOST_WIDE_INT mask_reg_save_offset;
   HOST_WIDE_INT sse_reg_save_offset;
 
   /* When save_regs_using_mov is set, emit prologue using
@@ -5547,6 +5551,15 @@ ix86_conditional_register_usage (void)
 {
   int i, c_mask;
 
+  /* If there are no caller-saved registers, preserve all registers.
+     except fixed_regs and registers used for function return value
+     since aggregate_value_p checks call_used_regs[regno] on return
+     value.  */
+  if (cfun && cfun->machine->no_caller_saved_registers)
+    for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+      if (!fixed_regs[i] && !ix86_function_value_regno_p (i))
+	call_used_regs[i] = 0;
+
   /* For 32-bit targets, squash the REX registers.  */
   if (! TARGET_64BIT)
     {
@@ -6341,6 +6354,21 @@ ix86_set_current_function (tree fndecl)
       return;
     }
 
+  if (lookup_attribute ("interrupt", DECL_ATTRIBUTES (fndecl)))
+    {
+      int nargs = 0;
+      for (tree arg = DECL_ARGUMENTS (fndecl);
+	   arg;
+	   arg = TREE_CHAIN (arg))
+	nargs++;
+      cfun->machine->no_caller_saved_registers = true;
+      cfun->machine->func_type
+	= nargs == 2 ? TYPE_EXCEPTION : TYPE_INTERRUPT;
+    }
+  else if (lookup_attribute ("no_caller_saved_registers",
+			     DECL_ATTRIBUTES (fndecl)))
+    cfun->machine->no_caller_saved_registers = true;
+
   tree new_tree = DECL_FUNCTION_SPECIFIC_TARGET (fndecl);
   if (new_tree == NULL_TREE)
     new_tree = target_option_default_node;
@@ -6364,6 +6392,11 @@ ix86_set_current_function (tree fndecl)
       && (call_used_regs[SI_REG]
 	  == (cfun->machine->call_abi == MS_ABI)))
     reinit_regs ();
+  /* AX register is only preserved if there are no caller-saved
+     registers.  */
+  else if (cfun->machine->no_caller_saved_registers
+	   == call_used_regs[AX_REG])
+    reinit_regs ();
 }
 
 \f
@@ -6627,6 +6660,10 @@ ix86_function_ok_for_sibcall (tree decl, tree exp)
   tree type, decl_or_type;
   rtx a, b;
 
+  /* Sibling call isn't OK if there are no caller-saved registers.  */
+  if (cfun->machine->no_caller_saved_registers)
+    return false;
+
   /* If we are generating position-independent code, we cannot sibcall
      optimize direct calls to global functions, as the PLT requires
      %ebx be live. (Darwin does not have a PLT.)  */
@@ -7800,6 +7837,8 @@ type_natural_mode (const_tree type, const CUMULATIVE_ARGS *cum,
 		      }
 		  }
 		else if ((size == 8 && !TARGET_64BIT)
+			 && (!cfun
+			     || cfun->machine->func_type == TYPE_NORMAL)
 			 && !TARGET_MMX
 			 && !TARGET_IAMCU)
 		  {
@@ -8778,6 +8817,11 @@ ix86_function_arg_advance (cumulative_args_t cum_v, machine_mode mode,
   HOST_WIDE_INT bytes, words;
   int nregs;
 
+  /* The argument of interrupt handler is a special case and is
+     handled in ix86_function_arg.  */
+  if (!cum->caller && cfun->machine->func_type != TYPE_NORMAL)
+    return;
+
   if (mode == BLKmode)
     bytes = int_size_in_bytes (type);
   else
@@ -9094,6 +9138,40 @@ ix86_function_arg (cumulative_args_t cum_v, machine_mode omode,
   HOST_WIDE_INT bytes, words;
   rtx arg;
 
+  if (!cum->caller && cfun->machine->func_type != TYPE_NORMAL)
+    {
+      /* The first argument of interrupt handler is a pointer and
+	 points to the return address slot on stack.  The optional
+	 second argument is an integer for error code on stack.  */
+      gcc_assert (type != NULL_TREE);
+      if (POINTER_TYPE_P (type))
+	{
+	  if (cfun->machine->func_type == TYPE_EXCEPTION)
+	    /* (AP) in the current frame in exception handler.  */
+	    arg = arg_pointer_rtx;
+	  else
+	    /* -WORD(AP) in the current frame in interrupt handler.  */
+	    arg = force_reg (Pmode,
+			     plus_constant (Pmode, arg_pointer_rtx,
+					    -UNITS_PER_WORD));
+	  if (mode != Pmode)
+	    arg = convert_to_mode (mode, arg, 1);
+	}
+      else
+	{
+	  gcc_assert (TREE_CODE (type) == INTEGER_TYPE
+		      && cfun->machine->func_type == TYPE_EXCEPTION
+		      && mode == word_mode);
+	  /* The error code is at -WORD(AP) in the current frame in
+	     exception handler.  */
+	  arg = gen_rtx_MEM (word_mode,
+			     plus_constant (Pmode, arg_pointer_rtx,
+					    -UNITS_PER_WORD));
+	}
+
+      return arg;
+    }
+
   /* All pointer bounds arguments are handled separately here.  */
   if ((type && POINTER_BOUNDS_TYPE_P (type))
       || POINTER_BOUNDS_MODE_P (mode))
@@ -10809,7 +10887,10 @@ ix86_can_use_return_insn_p (void)
 {
   struct ix86_frame frame;
 
-  if (! reload_completed || frame_pointer_needed)
+  /* Don't use `ret' instruction in interrupt handler.  */
+  if (! reload_completed
+      || frame_pointer_needed
+      || cfun->machine->func_type != TYPE_NORMAL)
     return 0;
 
   /* Don't allow more than 32k pop, since that's all we can do
@@ -11119,11 +11200,97 @@ ix86_select_alt_pic_regnum (void)
   return INVALID_REGNUM;
 }
 
+/* Return true if REGNO is used by the epilogue.  */
+
+bool
+ix86_epilogue_uses (int regno)
+{
+  /* If there are no caller-saved registers, we preserve all registers,
+     except for MMX and x87 registers which aren't supported when saving
+     and restoring registers.  Don't explicitly save SP register since
+     it is always preserved.  */
+  return (epilogue_completed
+	  && cfun->machine->no_caller_saved_registers
+	  && !fixed_regs[regno]
+	  && !STACK_REGNO_P (regno)
+	  && !MMX_REGNO_P (regno));
+}
+
+/* Return nonzero if register REGNO can be used as a scratch register
+   in peephole2.  */
+
+static bool
+ix86_hard_regno_scratch_ok (unsigned int regno ATTRIBUTE_UNUSED)
+{
+  /* If there are no caller-saved registers, we can't use any register
+     as a scratch register.  */
+  return !cfun->machine->no_caller_saved_registers;
+}
+
+/* Return TRUE if register REGNO is ever defined..  */
+
+static bool
+ix86_reg_ever_defined_p (unsigned int regno)
+{
+  df_ref def;
+
+  for (def = DF_REG_DEF_CHAIN (regno); def; def = DF_REF_NEXT_REG (def))
+    if (!DF_REF_IS_ARTIFICIAL (def))
+      return true;
+
+  return false;
+}
+
 /* Return TRUE if we need to save REGNO.  */
 
 static bool
 ix86_save_reg (unsigned int regno, bool maybe_eh_return)
 {
+  /* If there are no caller-saved registers, we preserve all registers,
+     except for MMX and x87 registers which aren't supported when saving
+     and restoring registers.  Don't explicitly save SP register since
+     it is always preserved.  */
+  if (reload_completed)
+    {
+      if (cfun->machine->func_type != TYPE_NORMAL)
+	return (df_regs_ever_live_p (regno)
+		&& ix86_reg_ever_defined_p (regno)
+		&& !fixed_regs[regno]
+		&& !STACK_REGNO_P (regno)
+		&& !MMX_REGNO_P (regno));
+
+      if (cfun->machine->no_caller_saved_registers)
+	{
+	  /* Don't preserve registers used for function return
+	     value.  */
+	  rtx reg = crtl->return_rtx;
+	  if (reg)
+	    {
+	      unsigned int i = REGNO (reg);
+	      unsigned nregs = hard_regno_nregs[i][GET_MODE (reg)];
+	      while (nregs-- > 0)
+		if ((i + nregs) == regno)
+		  return false;
+	    }
+
+	  reg = crtl->return_bnd;
+	  if (reg)
+	    {
+	      unsigned int i = REGNO (reg);
+	      unsigned nregs = hard_regno_nregs[i][GET_MODE (reg)];
+	      while (nregs-- > 0)
+		if ((i + nregs) == regno)
+		  return false;
+	    }
+
+	  return (df_regs_ever_live_p (regno)
+		  && ix86_reg_ever_defined_p (regno)
+		  && !fixed_regs[regno]
+		  && !STACK_REGNO_P (regno)
+		  && !MMX_REGNO_P (regno));
+	}
+    }
+
   if (regno == REAL_PIC_OFFSET_TABLE_REGNUM
       && pic_offset_table_rtx)
     {
@@ -11180,6 +11347,34 @@ ix86_nsaved_regs (void)
   return nregs;
 }
 
+/* Return number of saved bound registers.  */
+
+static int
+ix86_nsaved_bndregs (void)
+{
+  int nregs = 0;
+  int regno;
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (BND_REGNO_P (regno) && ix86_save_reg (regno, true))
+      nregs ++;
+  return nregs;
+}
+
+/* Return number of saved mask registers.  */
+
+static int
+ix86_nsaved_maskregs (void)
+{
+  int nregs = 0;
+  int regno;
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (MASK_REGNO_P (regno) && ix86_save_reg (regno, true))
+      nregs ++;
+  return nregs;
+}
+
 /* Return number of saved SSE registers.  */
 
 static int
@@ -11188,7 +11383,10 @@ ix86_nsaved_sseregs (void)
   int nregs = 0;
   int regno;
 
-  if (!TARGET_64BIT_MS_ABI)
+  /* Always need to save SSE registers if there are no caller-saved
+     registers.  */
+  if (!TARGET_64BIT_MS_ABI
+      && !cfun->machine->no_caller_saved_registers)
     return 0;
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
@@ -11269,6 +11467,8 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   HOST_WIDE_INT to_allocate;
 
   frame->nregs = ix86_nsaved_regs ();
+  frame->nbndregs = ix86_nsaved_bndregs ();
+  frame->nmaskregs = ix86_nsaved_maskregs ();
   frame->nsseregs = ix86_nsaved_sseregs ();
 
   /* 64-bit MS ABI seem to require stack alignment to be always 16,
@@ -11373,19 +11573,39 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   if (TARGET_SEH)
     frame->hard_frame_pointer_offset = offset;
 
+  /* Set BND register save area.  */
+  if (frame->nbndregs)
+    offset += frame->nbndregs * (ix86_pmode == PMODE_DI ? 16 : 8);
+  frame->bnd_reg_save_offset = offset;
+
+  /* Set MASK register save area.  */
+  if (frame->nmaskregs)
+    offset += frame->nmaskregs * (TARGET_AVX512BW ? 8 : 2);
+  frame->mask_reg_save_offset = offset;
+
   /* Align and set SSE register save area.  */
   if (frame->nsseregs)
     {
+      /* We must save full vector registers if there are no
+	 caller-saved registers.  */
+      unsigned int vec_regsize;
+      if (cfun->machine->no_caller_saved_registers)
+	vec_regsize = (TARGET_AVX512F ? 64 : (TARGET_AVX ? 32 : 16));
+      else
+	vec_regsize = 16;
+
       /* The only ABI that has saved SSE registers (Win64) also has a
 	 16-byte aligned default stack, and thus we don't need to be
 	 within the re-aligned local stack frame to save them.  In case
 	 incoming stack boundary is aligned to less than 16 bytes,
 	 unaligned move of SSE register will be emitted, so there is
 	 no point to round up the SSE register save area outside the
-	 re-aligned local stack frame to 16 bytes.  */
-      if (ix86_incoming_stack_boundary >= 128)
-	offset = ROUND_UP (offset, 16);
-      offset += frame->nsseregs * 16;
+	 re-aligned local stack frame to 16 bytes.  When full vector
+	 registers are saved, we check incoming stack boundary against
+	 the size of vector registers.  */
+      if (ix86_incoming_stack_boundary >= (vec_regsize * BITS_PER_UNIT))
+	offset = ROUND_UP (offset, vec_regsize);
+      offset += frame->nsseregs * vec_regsize;
     }
   frame->sse_reg_save_offset = offset;
 
@@ -11439,7 +11659,7 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   frame->stack_pointer_offset = offset;
 
   /* Size prologue needs to allocate.  */
-  to_allocate = offset - frame->sse_reg_save_offset;
+  to_allocate = offset - frame->bnd_reg_save_offset;
 
   if ((!to_allocate && frame->nregs <= 1)
       || (TARGET_64BIT && to_allocate >= (HOST_WIDE_INT) 0x80000000))
@@ -11690,18 +11910,67 @@ ix86_emit_save_regs_using_mov (HOST_WIDE_INT cfa_offset)
       }
 }
 
+/* Emit code to save BND registers using MOV insns.
+   First register is stored at CFA - CFA_OFFSET.  */
+static void
+ix86_emit_save_bnd_regs_using_mov (HOST_WIDE_INT cfa_offset)
+{
+  unsigned int regno;
+  enum machine_mode mode = BNDmode;
+  unsigned int size = GET_MODE_SIZE (mode);
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (BND_REGNO_P (regno) && ix86_save_reg (regno, true))
+      {
+	ix86_emit_save_reg_using_mov (mode, regno, cfa_offset);
+	cfa_offset -= size;
+      }
+}
+
+/* Emit code to save MASK registers using MOV insns.
+   First register is stored at CFA - CFA_OFFSET.  */
+static void
+ix86_emit_save_mask_regs_using_mov (HOST_WIDE_INT cfa_offset)
+{
+  unsigned int regno;
+  enum machine_mode mode = TARGET_AVX512BW ? DImode : HImode;
+  unsigned int size = GET_MODE_SIZE (mode);
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (MASK_REGNO_P (regno) && ix86_save_reg (regno, true))
+      {
+	ix86_emit_save_reg_using_mov (mode, regno, cfa_offset);
+	cfa_offset -= size;
+      }
+}
+
 /* Emit code to save SSE registers using MOV insns.
    First register is stored at CFA - CFA_OFFSET.  */
 static void
 ix86_emit_save_sse_regs_using_mov (HOST_WIDE_INT cfa_offset)
 {
   unsigned int regno;
+  enum machine_mode vector_reg_mode;
+
+  if (cfun->machine->no_caller_saved_registers)
+    {
+      /* We must save full vector registers if there are no
+	 caller-saved registers.  */
+      if (TARGET_AVX512F)
+	vector_reg_mode = V16SFmode;
+      else if (TARGET_AVX)
+	vector_reg_mode = V8SFmode;
+      else
+	vector_reg_mode = V4SFmode;
+    }
+  else
+    vector_reg_mode = V4SFmode;
 
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
       {
-	ix86_emit_save_reg_using_mov (V4SFmode, regno, cfa_offset);
-	cfa_offset -= GET_MODE_SIZE (V4SFmode);
+	ix86_emit_save_reg_using_mov (vector_reg_mode, regno, cfa_offset);
+	cfa_offset -= GET_MODE_SIZE (vector_reg_mode);
       }
 }
 
@@ -11857,13 +12126,17 @@ find_drap_reg (void)
 {
   tree decl = cfun->decl;
 
+  /* Always use callee-saved register if there are no caller-saved
+     registers.  */
   if (TARGET_64BIT)
     {
       /* Use R13 for nested function or function need static chain.
 	 Since function with tail call may use any caller-saved
 	 registers in epilogue, DRAP must not use caller-saved
 	 register in such case.  */
-      if (DECL_STATIC_CHAIN (decl) || crtl->tail_call_emit)
+      if (DECL_STATIC_CHAIN (decl)
+	  || cfun->machine->no_caller_saved_registers
+	  || crtl->tail_call_emit)
 	return R13_REG;
 
       return R10_REG;
@@ -11874,7 +12147,9 @@ find_drap_reg (void)
 	 Since function with tail call may use any caller-saved
 	 registers in epilogue, DRAP must not use caller-saved
 	 register in such case.  */
-      if (DECL_STATIC_CHAIN (decl) || crtl->tail_call_emit)
+      if (DECL_STATIC_CHAIN (decl)
+	  || cfun->machine->no_caller_saved_registers
+	  || crtl->tail_call_emit)
 	return DI_REG;
 
       /* Reuse static chain register if it isn't used for parameter
@@ -11915,8 +12190,12 @@ ix86_minimum_incoming_stack_boundary (bool sibcall)
 {
   unsigned int incoming_stack_boundary;
 
+  /* Stack of interrupt handler is always aligned to MIN_STACK_BOUNDARY.
+   */
+  if (cfun->machine->func_type != TYPE_NORMAL)
+    incoming_stack_boundary = MIN_STACK_BOUNDARY;
   /* Prefer the one specified at command line. */
-  if (ix86_user_incoming_stack_boundary)
+  else if (ix86_user_incoming_stack_boundary)
     incoming_stack_boundary = ix86_user_incoming_stack_boundary;
   /* In 32bit, use MIN_STACK_BOUNDARY for incoming stack boundary
      if -mstackrealign is used, it isn't used for sibcall check and
@@ -12547,6 +12826,8 @@ ix86_expand_prologue (void)
   struct ix86_frame frame;
   HOST_WIDE_INT allocate;
   bool int_registers_saved;
+  bool bnd_registers_saved;
+  bool mask_registers_saved;
   bool sse_registers_saved;
   rtx static_chain = NULL_RTX;
 
@@ -12666,6 +12947,12 @@ ix86_expand_prologue (void)
     {
       int align_bytes = crtl->stack_alignment_needed / BITS_PER_UNIT;
 
+      /* Can't use DRAP in interrupt function.  */
+      if (cfun->machine->func_type != TYPE_NORMAL)
+	sorry ("Dynamic Realign Argument Pointer (DRAP) not supported "
+	       "in interrupt service routine.  This may be worked "
+	       "around by avoiding functions with aggregate return.");
+
       /* Only need to push parameter pointer reg if it is caller saved.  */
       if (!call_used_regs[REGNO (crtl->drap_reg)])
 	{
@@ -12712,6 +12999,8 @@ ix86_expand_prologue (void)
     }
 
   int_registers_saved = (frame.nregs == 0);
+  bnd_registers_saved = (frame.nbndregs == 0);
+  mask_registers_saved = (frame.nmaskregs == 0);
   sse_registers_saved = (frame.nsseregs == 0);
 
   if (frame_pointer_needed && !m->fs.fp_valid)
@@ -12776,10 +13065,10 @@ ix86_expand_prologue (void)
 	 that we must allocate the size of the register save area before
 	 performing the actual alignment.  Otherwise we cannot guarantee
 	 that there's enough storage above the realignment point.  */
-      if (m->fs.sp_offset != frame.sse_reg_save_offset)
+      if (m->fs.sp_offset != frame.bnd_reg_save_offset)
         pro_epilogue_adjust_stack (stack_pointer_rtx, stack_pointer_rtx,
 				   GEN_INT (m->fs.sp_offset
-					    - frame.sse_reg_save_offset),
+					    - frame.bnd_reg_save_offset),
 				   -1, false);
 
       /* Align the stack.  */
@@ -13006,6 +13295,10 @@ ix86_expand_prologue (void)
 
   if (!int_registers_saved)
     ix86_emit_save_regs_using_mov (frame.reg_save_offset);
+  if (!bnd_registers_saved)
+    ix86_emit_save_bnd_regs_using_mov (frame.bnd_reg_save_offset);
+  if (!mask_registers_saved)
+    ix86_emit_save_mask_regs_using_mov (frame.mask_reg_save_offset);
   if (!sse_registers_saved)
     ix86_emit_save_sse_regs_using_mov (frame.sse_reg_save_offset);
 
@@ -13051,6 +13344,23 @@ ix86_expand_prologue (void)
      combined with prologue modifications.  */
   if (TARGET_SEH)
     emit_insn (gen_prologue_use (stack_pointer_rtx));
+
+  if (cfun->machine->func_type != TYPE_NORMAL)
+    {
+      /* Since the pointer argument of interrupt handler isn't a real
+         argument, adjust DECL_INCOMING_RTL for debug output.  */
+      tree arg = DECL_ARGUMENTS (current_function_decl);
+      gcc_assert (arg != NULL_TREE
+		  && POINTER_TYPE_P (TREE_TYPE (arg)));
+      if (cfun->machine->func_type == TYPE_EXCEPTION)
+	/* (AP) in the current frame in exception handler.  */
+	DECL_INCOMING_RTL (arg) = arg_pointer_rtx;
+      else
+	/* -WORD(AP) in the current frame in interrupt handler.  */
+	DECL_INCOMING_RTL (arg) = plus_constant (Pmode,
+						 arg_pointer_rtx,
+						 -UNITS_PER_WORD);
+    }
 }
 
 /* Emit code to restore REG using a POP insn.  */
@@ -13192,6 +13502,90 @@ ix86_emit_restore_regs_using_mov (HOST_WIDE_INT cfa_offset,
       }
 }
 
+/* Emit code to restore saved BND registers using MOV insns.
+   First register is restored from CFA - CFA_OFFSET.  */
+static void
+ix86_emit_restore_bnd_regs_using_mov (HOST_WIDE_INT cfa_offset,
+				      bool maybe_eh_return)
+{
+  struct machine_function *m = cfun->machine;
+  unsigned int regno;
+  enum machine_mode mode = BNDmode;
+  unsigned int size = GET_MODE_SIZE (mode);
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (BND_REGNO_P (regno) && ix86_save_reg (regno, maybe_eh_return))
+      {
+	rtx reg = gen_rtx_REG (mode, regno);
+	rtx mem;
+	rtx_insn *insn;
+
+	mem = choose_baseaddr (cfa_offset);
+	mem = gen_frame_mem (mode, mem);
+	insn = emit_move_insn (reg, mem);
+
+        if (m->fs.cfa_reg == crtl->drap_reg && regno == REGNO (crtl->drap_reg))
+	  {
+	    /* Previously we'd represented the CFA as an expression
+	       like *(%ebp - 8).  We've just popped that value from
+	       the stack, which means we need to reset the CFA to
+	       the drap register.  This will remain until we restore
+	       the stack pointer.  */
+	    add_reg_note (insn, REG_CFA_DEF_CFA, reg);
+	    RTX_FRAME_RELATED_P (insn) = 1;
+
+	    /* This means that the DRAP register is valid for addressing.  */
+	    m->fs.drap_valid = true;
+	  }
+	else
+	  ix86_add_cfa_restore_note (NULL, reg, cfa_offset);
+
+	cfa_offset -= size;
+      }
+}
+
+/* Emit code to restore saved MASK registers using MOV insns.
+   First register is restored from CFA - CFA_OFFSET.  */
+static void
+ix86_emit_restore_mask_regs_using_mov (HOST_WIDE_INT cfa_offset,
+				       bool maybe_eh_return)
+{
+  struct machine_function *m = cfun->machine;
+  unsigned int regno;
+  enum machine_mode mode = TARGET_AVX512BW ? DImode : HImode;
+  unsigned int size = GET_MODE_SIZE (mode);
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (MASK_REGNO_P (regno) && ix86_save_reg (regno, maybe_eh_return))
+      {
+	rtx reg = gen_rtx_REG (mode, regno);
+	rtx mem;
+	rtx_insn *insn;
+
+	mem = choose_baseaddr (cfa_offset);
+	mem = gen_frame_mem (mode, mem);
+	insn = emit_move_insn (reg, mem);
+
+        if (m->fs.cfa_reg == crtl->drap_reg && regno == REGNO (crtl->drap_reg))
+	  {
+	    /* Previously we'd represented the CFA as an expression
+	       like *(%ebp - 8).  We've just popped that value from
+	       the stack, which means we need to reset the CFA to
+	       the drap register.  This will remain until we restore
+	       the stack pointer.  */
+	    add_reg_note (insn, REG_CFA_DEF_CFA, reg);
+	    RTX_FRAME_RELATED_P (insn) = 1;
+
+	    /* This means that the DRAP register is valid for addressing.  */
+	    m->fs.drap_valid = true;
+	  }
+	else
+	  ix86_add_cfa_restore_note (NULL, reg, cfa_offset);
+
+	cfa_offset -= size;
+      }
+}
+
 /* Emit code to restore saved registers using MOV insns.
    First register is restored from CFA - CFA_OFFSET.  */
 static void
@@ -13199,27 +13593,44 @@ ix86_emit_restore_sse_regs_using_mov (HOST_WIDE_INT cfa_offset,
 				      bool maybe_eh_return)
 {
   unsigned int regno;
+  enum machine_mode vector_reg_mode;
+
+  if (cfun->machine->no_caller_saved_registers)
+    {
+      /* We must restore full vector registers if there are no
+	 caller-saved registers.  */
+      if (TARGET_AVX512F)
+	vector_reg_mode = V16SFmode;
+      else if (TARGET_AVX)
+	vector_reg_mode = V8SFmode;
+      else
+	vector_reg_mode = V4SFmode;
+    }
+  else
+    vector_reg_mode = V4SFmode;
 
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, maybe_eh_return))
       {
-	rtx reg = gen_rtx_REG (V4SFmode, regno);
+	rtx reg = gen_rtx_REG (vector_reg_mode, regno);
 	rtx mem;
 	unsigned int align;
 
 	mem = choose_baseaddr (cfa_offset);
-	mem = gen_rtx_MEM (V4SFmode, mem);
+	mem = gen_rtx_MEM (vector_reg_mode, mem);
 
 	/* The location is aligned up to INCOMING_STACK_BOUNDARY.  */
-	align = MIN (GET_MODE_ALIGNMENT (V4SFmode), INCOMING_STACK_BOUNDARY);
+	align = MIN (GET_MODE_ALIGNMENT (vector_reg_mode),
+		     INCOMING_STACK_BOUNDARY);
 	set_mem_align (mem, align);
 
 	/* SSE saves are not within re-aligned local stack frame.
 	   In case INCOMING_STACK_BOUNDARY is misaligned, we have
 	   to emit unaligned load.  */
-	if (align < 128)
+	if (vector_reg_mode == V4SFmode && align < 128)
 	  {
-	    rtx unspec = gen_rtx_UNSPEC (V4SFmode, gen_rtvec (1, mem),
+	    rtx unspec = gen_rtx_UNSPEC (vector_reg_mode,
+					 gen_rtvec (1, mem),
 					 UNSPEC_LOADU);
 	    emit_insn (gen_rtx_SET (reg, unspec));
 	  }
@@ -13228,7 +13639,7 @@ ix86_emit_restore_sse_regs_using_mov (HOST_WIDE_INT cfa_offset,
 
 	ix86_add_cfa_restore_note (NULL, reg, cfa_offset);
 
-	cfa_offset -= GET_MODE_SIZE (V4SFmode);
+	cfa_offset -= GET_MODE_SIZE (vector_reg_mode);
       }
 }
 
@@ -13327,16 +13738,25 @@ ix86_expand_epilogue (int style)
       if (TARGET_64BIT
 	  && m->fs.sp_offset > 0x7fffffff
 	  && !(m->fs.fp_valid || m->fs.drap_valid)
-	  && (frame.nsseregs + frame.nregs) != 0)
+	  && (frame.nsseregs + frame.nmaskregs + frame.nbndregs
+	      + frame.nregs) != 0)
 	{
 	  pro_epilogue_adjust_stack (stack_pointer_rtx, stack_pointer_rtx,
 				     GEN_INT (m->fs.sp_offset
-					      - frame.sse_reg_save_offset),
+					      - frame.bnd_reg_save_offset),
 				     style,
 				     m->fs.cfa_reg == stack_pointer_rtx);
 	}
     }
 
+  if (frame.nbndregs)
+    ix86_emit_restore_bnd_regs_using_mov (frame.bnd_reg_save_offset,
+					  style == 2);
+
+  if (frame.nmaskregs)
+    ix86_emit_restore_mask_regs_using_mov (frame.mask_reg_save_offset,
+					   style == 2);
+
   /* If there are any SSE registers to restore, then we have to do it
      via moves, since there's obviously no pop for SSE regs.  */
   if (frame.nsseregs)
@@ -13534,7 +13954,20 @@ ix86_expand_epilogue (int style)
       return;
     }
 
-  if (crtl->args.pops_args && crtl->args.size)
+  if (cfun->machine->func_type != TYPE_NORMAL)
+    {
+      /* Return with the "IRET" instruction from interrupt handler.
+	 Pop the 'ERROR_CODE' off the stack before the 'IRET'
+	 instruction in exception handler.  */
+      if (cfun->machine->func_type == TYPE_EXCEPTION)
+	{
+	  rtx r = plus_constant (Pmode, stack_pointer_rtx,
+				 UNITS_PER_WORD);
+	  emit_insn (gen_rtx_SET (stack_pointer_rtx, r));
+	}
+      emit_jump_insn (gen_interrupt_return ());
+    }
+  else if (crtl->args.pops_args && crtl->args.size)
     {
       rtx popc = GEN_INT (crtl->args.pops_args);
 
@@ -18503,6 +18936,14 @@ ix86_expand_move (machine_mode mode, rtx operands[])
   rtx op0, op1;
   enum tls_model model;
 
+  if (cfun->machine->func_type != TYPE_NORMAL && IS_STACK_MODE (mode))
+    {
+      error ("80387 instructions aren't allowed in %s service routine",
+	     (cfun->machine->func_type == TYPE_EXCEPTION
+	      ? "exception" : "interrupt"));
+      return;
+    }
+
   op0 = operands[0];
   op1 = operands[1];
 
@@ -18651,6 +19092,15 @@ ix86_expand_vector_move (machine_mode mode, rtx operands[])
 			? GET_MODE_BITSIZE (mode)
 			: GET_MODE_ALIGNMENT (mode));
 
+  if (cfun->machine->func_type != TYPE_NORMAL
+      && VALID_MMX_REG_MODE (mode))
+    {
+      error ("MMX/3Dnow instructions aren't allowed in %s service routine",
+	     (cfun->machine->func_type == TYPE_EXCEPTION
+	      ? "exception" : "interrupt"));
+      return;
+    }
+
   if (push_operand (op0, VOIDmode))
     op0 = emit_move_resolve_push (mode, op0);
 
@@ -26736,6 +27186,17 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
   rtx vec[3];
   rtx use = NULL, call;
   unsigned int vec_len = 0;
+  tree fndecl;
+
+  if (GET_CODE (XEXP (fnaddr, 0)) == SYMBOL_REF)
+    {
+      fndecl = SYMBOL_REF_DECL (XEXP (fnaddr, 0));
+      if (fndecl
+	  && (lookup_attribute ("interrupt", DECL_ATTRIBUTES (fndecl))))
+	error ("interrupt service routine can't be called directly");
+    }
+  else
+    fndecl = NULL_TREE;
 
   if (pop == const0_rtx)
     pop = NULL;
@@ -26833,8 +27294,29 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
       vec[vec_len++] = pop;
     }
 
-  if (TARGET_64BIT_MS_ABI
-      && (!callarg2 || INTVAL (callarg2) != -2))
+  if (cfun->machine->no_caller_saved_registers
+      && (!fndecl
+	  || !lookup_attribute ("no_caller_saved_registers",
+				DECL_ATTRIBUTES (fndecl))))
+    {
+      static const char ix86_call_used_regs[] = CALL_USED_REGISTERS;
+      bool is_64bit_ms_abi = (TARGET_64BIT
+			      && ix86_function_abi (fndecl) == MS_ABI);
+      char c_mask = CALL_USED_REGISTERS_MASK (is_64bit_ms_abi);
+
+      /* If there are no caller-saved registers, add all registers
+	 that are clobbered by the call.  */
+      for (int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+	if (!fixed_regs[i]
+	    && (ix86_call_used_regs[i] == 1
+		|| (ix86_call_used_regs[i] & c_mask))
+	    && !STACK_REGNO_P (i)
+	    && !MMX_REGNO_P (i))
+	  clobber_reg (&use,
+		       gen_rtx_REG (GET_MODE (regno_reg_rtx[i]), i));
+    }
+  else if (TARGET_64BIT_MS_ABI
+	   && (!callarg2 || INTVAL (callarg2) != -2))
     {
       int const cregs_size
 	= ARRAY_SIZE (x86_64_ms_sysv_extra_clobbered_registers);
@@ -44220,6 +44702,69 @@ ix86_handle_fndecl_attribute (tree *node, tree name, tree, int,
   return NULL_TREE;
 }
 
+static tree
+ix86_handle_no_caller_saved_registers_attribute (tree *node,
+						 tree name, tree, int,
+						 bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+	       name);
+      *no_add_attrs = true;
+    }
+  return NULL_TREE;
+}
+
+static tree
+ix86_handle_interrupt_attribute (tree *node, tree name, tree, int,
+				 bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+	       name);
+      *no_add_attrs = true;
+    }
+
+  /* DECL_RESULT and DECL_ARGUMENTS do not exist there yet,
+     but the function type contains args and return type data.  */
+  tree func_type = TREE_TYPE (*node);
+  tree return_type = TREE_TYPE (func_type);
+
+  int nargs = 0;
+  tree current_arg_type = TYPE_ARG_TYPES (func_type);
+  while (current_arg_type
+	 && ! VOID_TYPE_P (TREE_VALUE (current_arg_type)))
+    {
+      if (nargs == 0)
+	{
+	  if (! POINTER_TYPE_P (TREE_VALUE (current_arg_type)))
+	    error ("interrupt service routine should have a pointer "
+		   "as the first argument");
+	}
+      else if (nargs == 1)
+	{
+	  if (TREE_CODE (TREE_VALUE (current_arg_type)) != INTEGER_TYPE
+	      || TYPE_MODE (TREE_VALUE (current_arg_type)) != word_mode)
+	    error ("interrupt service routine should have unsigned %s"
+		   "int as the second argument",
+		   TARGET_64BIT
+		   ? (TARGET_X32 ? "long long " : "long ")
+		   : "");
+	}
+      nargs++;
+      current_arg_type = TREE_CHAIN (current_arg_type);
+    }
+  if (!nargs || nargs > 2)
+    error ("interrupt service routine can only have a pointer argument "
+	   "and an optional integer argument");
+  if (! VOID_TYPE_P (return_type))
+    error ("interrupt service routine can't have non-void return value");
+
+  return NULL_TREE;
+}
+
 static bool
 ix86_ms_bitfield_layout_p (const_tree record_type)
 {
@@ -48221,6 +48766,11 @@ static const struct attribute_spec ix86_attribute_table[] =
     false },
   { "callee_pop_aggregate_return", 1, 1, false, true, true,
     ix86_handle_callee_pop_aggregate_return, true },
+  { "interrupt", 0, 0, true, false, false,
+    ix86_handle_interrupt_attribute, false },
+  { "no_caller_saved_registers", 0, 0, true, false, false,
+    ix86_handle_no_caller_saved_registers_attribute, false },
+
   /* End element.  */
   { NULL,        0, 0, false, false, false, NULL, false }
 };
@@ -54067,6 +54617,9 @@ ix86_operands_ok_for_move_multiple (rtx *operands, bool load,
 #undef TARGET_ABSOLUTE_BIGGEST_ALIGNMENT
 #define TARGET_ABSOLUTE_BIGGEST_ALIGNMENT 512
 
+#undef TARGET_HARD_REGNO_SCRATCH_OK
+#define TARGET_HARD_REGNO_SCRATCH_OK ix86_hard_regno_scratch_ok
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 #include "gt-i386.h"
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index be96c75..cdaef10 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -1628,11 +1628,18 @@ enum reg_class
 
    If stack probes are required, the space used for large function
    arguments on the stack must also be probed, so enable
-   -maccumulate-outgoing-args so this happens in the prologue.  */
+   -maccumulate-outgoing-args so this happens in the prologue.
+
+   We must use argument accumulation in interrupt function if stack
+   may be realigned to avoid DRAP.  */
 
 #define ACCUMULATE_OUTGOING_ARGS \
-  ((TARGET_ACCUMULATE_OUTGOING_ARGS && optimize_function_for_speed_p (cfun)) \
-   || TARGET_STACK_PROBE || TARGET_64BIT_MS_ABI)
+  ((TARGET_ACCUMULATE_OUTGOING_ARGS \
+    && optimize_function_for_speed_p (cfun)) \
+   || (cfun->machine->func_type != TYPE_NORMAL \
+       && crtl->stack_realign_needed) \
+   || TARGET_STACK_PROBE \
+   || TARGET_64BIT_MS_ABI)
 
 /* If defined, a C expression whose value is nonzero when we want to use PUSH
    instructions to pass outgoing arguments.  */
@@ -1741,6 +1748,11 @@ typedef struct ix86_args {
 
 #define EXIT_IGNORE_STACK 1
 
+/* Define this macro as a C expression that is nonzero for registers
+   used by the epilogue or the `return' pattern.  */
+
+#define EPILOGUE_USES(REGNO) ix86_epilogue_uses (REGNO)
+
 /* Output assembler code for a block containing the constant parts
    of a trampoline, leaving space for the variable parts.  */
 
@@ -2456,6 +2468,18 @@ struct GTY(()) machine_frame_state
 /* Private to winnt.c.  */
 struct seh_frame_state;
 
+enum function_type
+{
+  TYPE_NORMAL = 0,
+  /* The current function is an interrupt service routine with a
+     pointer argument as specified by the "interrupt" attribute.  */
+  TYPE_INTERRUPT,
+  /* The current function is an interrupt service routine with a
+     pointer argument and an integer argument as specified by the
+     "interrupt" attribute.  */
+  TYPE_EXCEPTION
+};
+
 struct GTY(()) machine_function {
   struct stack_local_entry *stack_locals;
   const char *some_ld_name;
@@ -2506,6 +2530,13 @@ struct GTY(()) machine_function {
   /* If true, it is safe to not save/restore DRAP register.  */
   BOOL_BITFIELD no_drap_save_restore : 1;
 
+  /* Function type.  */
+  ENUM_BITFIELD(function_type) func_type : 2;
+
+  /* If true, the current function is a function specified with
+     the "interrupt" or "no_caller_saved_registers" attribute.  */
+  BOOL_BITFIELD no_caller_saved_registers : 1;
+
   /* If true, there is register available for argument passing.  This
      is used only in ix86_function_ok_for_sibcall by 32-bit to determine
      if there is scratch register available for indirect sibcall.  In
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index d0c0d23..810833c 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -193,6 +193,9 @@
   UNSPEC_BNDCU
   UNSPEC_BNDCN
   UNSPEC_MPX_FENCE
+
+  ;; IRET support
+  UNSPEC_INTERRUPT_RETURN
 ])
 
 (define_c_enum "unspecv" [
@@ -12190,6 +12193,12 @@
    (set_attr "modrm" "0")
    (set_attr "maybe_prefix_bnd" "1")])
 
+(define_insn "interrupt_return"
+  [(simple_return)
+   (unspec [(const_int 0)] UNSPEC_INTERRUPT_RETURN)]
+  "reload_completed"
+  "iret")
+
 ;; Used by x86_machine_dependent_reorg to avoid penalty on single byte RET
 ;; instruction Athlon and K8 have.
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index e54fe67..ac2aa42 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -5079,6 +5079,69 @@ On x86-32 targets, the @code{stdcall} attribute causes the compiler to
 assume that the called function pops off the stack space used to
 pass arguments, unless it takes a variable number of arguments.
 
+@item no_caller_saved_registers
+@cindex @code{no_caller_saved_registers} function attribute, x86
+Use this attribute to indicate that the specified function has no
+caller-saved registers.  That is, all registers are callee-saved.
+The compiler generates proper function entry and exit sequences to
+save and restore any modified registers.
+
+@item interrupt
+@cindex @code{interrupt} function attribute, x86
+Use this attribute to indicate that the specified function is an
+interrupt handler.  The compiler generates function
+entry and exit sequences suitable for use in an interrupt handler when
+this attribute is present.  The @code{IRET} instruction, instead of the
+@code{RET} instruction, is used to return from interrupt handlers.  All
+registers, except for the EFLAGS register which is restored by the
+@code{IRET} instruction, are preserved by the compiler.
+
+Any interruptible-without-stack-switch code must be compiled with
+@option{-mno-red-zone} since interrupt handlers can and will, because
+of the hardware design, touch the red zone.
+
+An interrupt handler must be declared with a mandatory pointer
+argument:
+
+@smallexample
+struct interrupt_frame;
+
+__attribute__ ((interrupt))
+void
+f (struct interrupt_frame *frame)
+@{
+@}
+@end smallexample
+
+and user must properly define the structure the pointer pointing to.
+
+The exception handler is very similar to the interrupt handler with
+a different mandatory function signature:
+
+@smallexample
+#ifdef __x86_64__
+typedef unsigned long long int uword_t;
+#else
+typedef unsigned int uword_t;
+#endif
+
+struct interrupt_frame;
+
+__attribute__ ((interrupt))
+void
+f (struct interrupt_frame *frame, uword_t error_code)
+@{
+  ...
+@}
+@end smallexample
+
+and compiler pops the error code off the stack before the @code{IRET}
+instruction.
+
+The exception handler should only be used for exceptions which push an
+error code and all other exceptions must use the interrupt handler.
+The system will crash if the wrong handler is used.
+
 @item target (@var{options})
 @cindex @code{target} function attribute
 As discussed in @ref{Common Function Attributes}, this attribute 
diff --git a/gcc/testsuite/gcc.dg/guality/pr68037-1.c b/gcc/testsuite/gcc.dg/guality/pr68037-1.c
new file mode 100644
index 0000000..ac3431c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/guality/pr68037-1.c
@@ -0,0 +1,65 @@
+/* { dg-do run { target i?86-*-* x86_64-*-* } } */
+/* { dg-options "-g" } */
+
+extern void exit (int);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+#define ERROR		0x12345670
+#define IP		0x12345671
+#define CS		0x12345672
+#define FLAGS		0x12345673
+#define SP		0x12345674
+#define SS		0x12345675
+
+#define STRING(x)	XSTRING(x)
+#define XSTRING(x)	#x
+
+struct interrupt_frame
+{
+  uword_t ip;
+  uword_t cs;
+  uword_t flags;
+  uword_t sp;
+  uword_t ss;
+};
+
+__attribute__((interrupt, used))
+void
+fn (struct interrupt_frame *frame, uword_t error)
+{
+  if (ERROR != error)		/* BREAK */
+    __builtin_abort ();
+  if (IP != frame->ip)
+    __builtin_abort ();
+  if (CS != frame->cs)
+    __builtin_abort ();
+  if (FLAGS != frame->flags)
+    __builtin_abort ();
+  if (SP != frame->sp)
+    __builtin_abort ();
+  if (SS != frame->ss)
+    __builtin_abort ();
+
+  exit (0);
+}
+
+int
+main ()
+{
+  asm ("push	$" STRING (SS) ";		\
+	push	$" STRING (SP) ";		\
+	push	$" STRING (FLAGS) ";		\
+	push	$" STRING (CS) ";		\
+	push	$" STRING (IP) ";		\
+	push	$" STRING (ERROR) ";		\
+	jmp	fn");
+  return 0;
+}
+
+/* { dg-final { gdb-test 31 "error" "0x12345670" } } */
+/* { dg-final { gdb-test 31 "frame->ip" "0x12345671" } } */
+/* { dg-final { gdb-test 31 "frame->cs" "0x12345672" } } */
+/* { dg-final { gdb-test 31 "frame->flags" "0x12345673" } } */
+/* { dg-final { gdb-test 31 "frame->sp" "0x12345674" } } */
+/* { dg-final { gdb-test 31 "frame->ss" "0x12345675" } } */
diff --git a/gcc/testsuite/gcc.dg/guality/pr68037-2.c b/gcc/testsuite/gcc.dg/guality/pr68037-2.c
new file mode 100644
index 0000000..2010384
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/guality/pr68037-2.c
@@ -0,0 +1,60 @@
+/* { dg-do run { target i?86-*-* x86_64-*-* } } */
+/* { dg-options "-g" } */
+
+extern void exit (int);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+#define IP		0x12345671
+#define CS		0x12345672
+#define FLAGS		0x12345673
+#define SP		0x12345674
+#define SS		0x12345675
+
+#define STRING(x)	XSTRING(x)
+#define XSTRING(x)	#x
+
+struct interrupt_frame
+{
+  uword_t ip;
+  uword_t cs;
+  uword_t flags;
+  uword_t sp;
+  uword_t ss;
+};
+
+__attribute__((interrupt, used))
+void
+fn (struct interrupt_frame *frame)
+{
+  if (IP != frame->ip)		/* BREAK */
+    __builtin_abort ();
+  if (CS != frame->cs)
+    __builtin_abort ();
+  if (FLAGS != frame->flags)
+    __builtin_abort ();
+  if (SP != frame->sp)
+    __builtin_abort ();
+  if (SS != frame->ss)
+    __builtin_abort ();
+
+  exit (0);
+}
+
+int
+main ()
+{
+  asm ("push	$" STRING (SS) ";		\
+	push	$" STRING (SP) ";		\
+	push	$" STRING (FLAGS) ";		\
+	push	$" STRING (CS) ";		\
+	push	$" STRING (IP) ";		\
+	jmp	fn");
+  return 0;
+}
+
+/* { dg-final { gdb-test 30 "frame->ip" "0x12345671" } } */
+/* { dg-final { gdb-test 30 "frame->cs" "0x12345672" } } */
+/* { dg-final { gdb-test 30 "frame->flags" "0x12345673" } } */
+/* { dg-final { gdb-test 30 "frame->sp" "0x12345674" } } */
+/* { dg-final { gdb-test 30 "frame->ss" "0x12345675" } } */
diff --git a/gcc/testsuite/gcc.dg/guality/pr68037-3.c b/gcc/testsuite/gcc.dg/guality/pr68037-3.c
new file mode 100644
index 0000000..10854f5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/guality/pr68037-3.c
@@ -0,0 +1,76 @@
+/* { dg-do run { target i?86-*-* x86_64-*-* } } */
+/* { dg-options "-g" } */
+
+#include <stddef.h>
+
+extern void exit (int);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+typedef int aligned __attribute__((aligned(64)));
+
+#define IP		0x12345671
+#define CS		0x12345672
+#define FLAGS		0x12345673
+#define SP		0x12345674
+#define SS		0x12345675
+
+#define STRING(x)	XSTRING(x)
+#define XSTRING(x)	#x
+
+struct interrupt_frame
+{
+  uword_t ip;
+  uword_t cs;
+  uword_t flags;
+  uword_t sp;
+  uword_t ss;
+};
+
+int
+check_int (int *i, int align)
+{
+  *i = 20;
+  if ((((ptrdiff_t) i) & (align - 1)) != 0)
+    __builtin_abort ();
+  return *i;
+}
+
+__attribute__((interrupt, used))
+void
+fn (struct interrupt_frame *frame)
+{
+  aligned i;
+  if (check_int (&i, __alignof__(i)) != i)
+    __builtin_abort ();
+
+  if (IP != frame->ip)		/* BREAK */
+    __builtin_abort ();
+  if (CS != frame->cs)
+    __builtin_abort ();
+  if (FLAGS != frame->flags)
+    __builtin_abort ();
+  if (SP != frame->sp)
+    __builtin_abort ();
+  if (SS != frame->ss)
+    __builtin_abort ();
+
+  exit (0);
+}
+
+int
+main ()
+{
+  asm ("push	$" STRING (SS) ";		\
+	push	$" STRING (SP) ";		\
+	push	$" STRING (FLAGS) ";		\
+	push	$" STRING (CS) ";		\
+	push	$" STRING (IP) ";		\
+	jmp	fn");
+  return 0;
+}
+
+/* { dg-final { gdb-test 46 "frame->ip" "0x12345671" } } */
+/* { dg-final { gdb-test 46 "frame->cs" "0x12345672" } } */
+/* { dg-final { gdb-test 46 "frame->flags" "0x12345673" } } */
+/* { dg-final { gdb-test 46 "frame->sp" "0x12345674" } } */
+/* { dg-final { gdb-test 46 "frame->ss" "0x12345675" } } */
diff --git a/gcc/testsuite/gcc.dg/torture/pr68037-1.c b/gcc/testsuite/gcc.dg/torture/pr68037-1.c
new file mode 100644
index 0000000..0730c41
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/pr68037-1.c
@@ -0,0 +1,57 @@
+/* { dg-do run { target i?86-*-* x86_64-*-* } } */
+
+extern void exit (int);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+#define ERROR		0x12345670
+#define IP		0x12345671
+#define CS		0x12345672
+#define FLAGS		0x12345673
+#define SP		0x12345674
+#define SS		0x12345675
+
+#define STRING(x)	XSTRING(x)
+#define XSTRING(x)	#x
+
+struct interrupt_frame
+{
+  uword_t ip;
+  uword_t cs;
+  uword_t flags;
+  uword_t sp;
+  uword_t ss;
+};
+
+__attribute__((interrupt, used))
+void
+fn (struct interrupt_frame *frame, uword_t error)
+{
+  if (ERROR != error)
+    __builtin_abort ();
+  if (IP != frame->ip)
+    __builtin_abort ();
+  if (CS != frame->cs)
+    __builtin_abort ();
+  if (FLAGS != frame->flags)
+    __builtin_abort ();
+  if (SP != frame->sp)
+    __builtin_abort ();
+  if (SS != frame->ss)
+    __builtin_abort ();
+
+  exit (0);
+}
+
+int
+main ()
+{
+  asm ("push	$" STRING (SS) ";		\
+	push	$" STRING (SP) ";		\
+	push	$" STRING (FLAGS) ";		\
+	push	$" STRING (CS) ";		\
+	push	$" STRING (IP) ";		\
+	push	$" STRING (ERROR) ";		\
+	jmp	fn");
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/torture/pr68037-2.c b/gcc/testsuite/gcc.dg/torture/pr68037-2.c
new file mode 100644
index 0000000..197e8a4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/pr68037-2.c
@@ -0,0 +1,53 @@
+/* { dg-do run { target i?86-*-* x86_64-*-* } } */
+
+extern void exit (int);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+#define IP		0x12345671
+#define CS		0x12345672
+#define FLAGS		0x12345673
+#define SP		0x12345674
+#define SS		0x12345675
+
+#define STRING(x)	XSTRING(x)
+#define XSTRING(x)	#x
+
+struct interrupt_frame
+{
+  uword_t ip;
+  uword_t cs;
+  uword_t flags;
+  uword_t sp;
+  uword_t ss;
+};
+
+__attribute__((interrupt, used))
+void
+fn (struct interrupt_frame *frame)
+{
+  if (IP != frame->ip)
+    __builtin_abort ();
+  if (CS != frame->cs)
+    __builtin_abort ();
+  if (FLAGS != frame->flags)
+    __builtin_abort ();
+  if (SP != frame->sp)
+    __builtin_abort ();
+  if (SS != frame->ss)
+    __builtin_abort ();
+
+  exit (0);
+}
+
+int
+main ()
+{
+  asm ("push	$" STRING (SS) ";		\
+	push	$" STRING (SP) ";		\
+	push	$" STRING (FLAGS) ";		\
+	push	$" STRING (CS) ";		\
+	push	$" STRING (IP) ";		\
+	jmp	fn");
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/torture/pr68037-3.c b/gcc/testsuite/gcc.dg/torture/pr68037-3.c
new file mode 100644
index 0000000..6bcc317
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/pr68037-3.c
@@ -0,0 +1,69 @@
+/* { dg-do run { target i?86-*-* x86_64-*-* } } */
+
+#include <stddef.h>
+
+extern void exit (int);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+typedef int aligned __attribute__((aligned(64)));
+
+#define IP		0x12345671
+#define CS		0x12345672
+#define FLAGS		0x12345673
+#define SP		0x12345674
+#define SS		0x12345675
+
+#define STRING(x)	XSTRING(x)
+#define XSTRING(x)	#x
+
+struct interrupt_frame
+{
+  uword_t ip;
+  uword_t cs;
+  uword_t flags;
+  uword_t sp;
+  uword_t ss;
+};
+
+int
+check_int (int *i, int align)
+{
+  *i = 20;
+  if ((((ptrdiff_t) i) & (align - 1)) != 0)
+    __builtin_abort ();
+  return *i;
+}
+
+__attribute__((interrupt, used))
+void
+fn (struct interrupt_frame *frame)
+{
+  aligned i;
+  if (check_int (&i, __alignof__(i)) != i)
+    __builtin_abort ();
+
+  if (IP != frame->ip)
+    __builtin_abort ();
+  if (CS != frame->cs)
+    __builtin_abort ();
+  if (FLAGS != frame->flags)
+    __builtin_abort ();
+  if (SP != frame->sp)
+    __builtin_abort ();
+  if (SS != frame->ss)
+    __builtin_abort ();
+
+  exit (0);
+}
+
+int
+main ()
+{
+  asm ("push	$" STRING (SS) ";		\
+	push	$" STRING (SP) ";		\
+	push	$" STRING (FLAGS) ";		\
+	push	$" STRING (CS) ";		\
+	push	$" STRING (IP) ";		\
+	jmp	fn");
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-1.c b/gcc/testsuite/gcc.target/i386/interrupt-1.c
new file mode 100644
index 0000000..70b8f3f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-1.c
@@ -0,0 +1,53 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-push-args -maccumulate-outgoing-args" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern int bar (int);
+
+void foo (void *frame)
+{
+  int a,b,c,d,e,f,i;
+  a = bar (5);
+  b = bar (a);
+  c = bar (b);
+  d = bar (c);
+  e = bar (d);
+  f = bar (e);
+  for (i = 1; i < 10; i++)
+  {
+    a += bar (a + i) + bar (b + i) +
+	 bar (c + i) + bar (d + i) +
+	 bar (e + i) + bar (f + i);
+  }
+}
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)bx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)si" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%edi" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r12" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r13" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r14" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r15" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)bx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)si" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%edi" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r12" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r13" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r14" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r15" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-10.c b/gcc/testsuite/gcc.target/i386/interrupt-10.c
new file mode 100644
index 0000000..64e621c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-10.c
@@ -0,0 +1,24 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mavx512bw -mno-iamcu -maccumulate-outgoing-args" } */
+
+extern void bar (void);
+
+struct interrupt_frame;
+
+void
+ __attribute__ ((interrupt))
+foo (struct interrupt_frame *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\)" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%zmm\[0-9\]+" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%zmm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "kmovq\[\\t \]*%k\[0-7\]+,\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\)" 8 } } */
+/* { dg-final { scan-assembler-times "kmovq\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%k\[0-7\]+" 8 } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-11.c b/gcc/testsuite/gcc.target/i386/interrupt-11.c
new file mode 100644
index 0000000..ba47019
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-11.c
@@ -0,0 +1,37 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-sse -mno-iamcu -maccumulate-outgoing-args" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-12.c b/gcc/testsuite/gcc.target/i386/interrupt-12.c
new file mode 100644
index 0000000..5c9eb74
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-12.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int check_int (int *i, void *, int align);
+typedef int aligned __attribute__((aligned(64)));
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+__attribute__((interrupt))
+void
+foo (void *frame, uword_t error_code)
+{
+  aligned j;
+  if (check_int (frame, &j, __alignof__(j)))
+    __builtin_abort ();
+}
+
+/* { dg-final { scan-assembler-times "and\[lq\]?\[^\\n\]*-64,\[^\\n\]*sp" 1 } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-13.c b/gcc/testsuite/gcc.target/i386/interrupt-13.c
new file mode 100644
index 0000000..8cf0b25
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-13.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int check_int (int *i, void *, int align);
+typedef int aligned __attribute__((aligned(64)));
+
+__attribute__((interrupt))
+void
+foo (void *frame)
+{
+  aligned j;
+  if (check_int (frame, &j, __alignof__(j)))
+    __builtin_abort ();
+}
+
+/* { dg-final { scan-assembler-times "and\[lq\]?\[^\\n\]*-64,\[^\\n\]*sp" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-14.c b/gcc/testsuite/gcc.target/i386/interrupt-14.c
new file mode 100644
index 0000000..751c328
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-14.c
@@ -0,0 +1,39 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-sse -mno-iamcu -mno-sse4 -mno-popcnt -maccumulate-outgoing-args" } */
+
+extern int i, cnt;
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  cnt = __builtin_popcount (i);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%esi" { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 1 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-15.c b/gcc/testsuite/gcc.target/i386/interrupt-15.c
new file mode 100644
index 0000000..16d82c2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-15.c
@@ -0,0 +1,28 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mpush-args -maccumulate-outgoing-args" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  bar (error);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%rax" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:r|e)bp" 1 } } */
+/* { dg-final { scan-assembler-times "leave" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%eax" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movl\[\\t \]*-4\\(%ebp\\),\[\\t \]*%eax" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movq\[\\t \]*-8\\(%(?:r|e)bp\\),\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 1 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-16.c b/gcc/testsuite/gcc.target/i386/interrupt-16.c
new file mode 100644
index 0000000..f20f117
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-16.c
@@ -0,0 +1,28 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mno-push-args -maccumulate-outgoing-args" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  bar (error);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%rax" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:r|e)bp" 1 } } */
+/* { dg-final { scan-assembler-times "leave" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%eax" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movl\[\\t \]*-4\\(%ebp\\),\[\\t \]*%eax" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movq\[\\t \]*-8\\(%(?:r|e)bp\\),\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 1 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-17.c b/gcc/testsuite/gcc.target/i386/interrupt-17.c
new file mode 100644
index 0000000..4bad7dc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-17.c
@@ -0,0 +1,30 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mpush-args -mno-accumulate-outgoing-args" } */
+
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame)
+{
+  bar (3);
+}
+
+void
+ __attribute__ ((interrupt))
+fn2 (void *frame)
+{
+  bar (3);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[8-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r1\[0-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:r|e)bp" 2 } } */
+/* { dg-final { scan-assembler-times "leave" 2 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movq\[\\t \]*-8\\(%(?:r|e)bp\\),\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 2 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-18.c b/gcc/testsuite/gcc.target/i386/interrupt-18.c
new file mode 100644
index 0000000..af32851
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-18.c
@@ -0,0 +1,35 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mpush-args -maccumulate-outgoing-args" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  bar (error);
+}
+
+void
+ __attribute__ ((interrupt))
+fn2 (void *frame, uword_t error)
+{
+  bar (error);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%rax" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:r|e)bp" 2 } } */
+/* { dg-final { scan-assembler-times "leave" 2 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%eax" 2 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movl\[\\t \]*-4\\(%ebp\\),\[\\t \]*%eax" 2 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movq\[\\t \]*-8\\(%(?:r|e)bp\\),\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 2 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 2 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-19.c b/gcc/testsuite/gcc.target/i386/interrupt-19.c
new file mode 100644
index 0000000..e1aaaec
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-19.c
@@ -0,0 +1,21 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mno-push-args -maccumulate-outgoing-args" } */
+
+extern int foo (int) __attribute__ ((no_caller_saved_registers));
+extern int bar (int) __attribute__ ((no_caller_saved_registers));
+
+int
+foo (int i)
+{
+  return bar (i);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]-*\[0-9\]*\\(%\[re\]?bp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-2.c b/gcc/testsuite/gcc.target/i386/interrupt-2.c
new file mode 100644
index 0000000..27b40b0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-2.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wall -g" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+}
+
+/* { dg-final { scan-assembler-not "add(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-20.c b/gcc/testsuite/gcc.target/i386/interrupt-20.c
new file mode 100644
index 0000000..2a2210d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-20.c
@@ -0,0 +1,23 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mno-push-args" } */
+
+extern int foo (int) __attribute__ ((no_caller_saved_registers));
+extern int bar (int) __attribute__ ((no_caller_saved_registers));
+
+int
+foo (int i)
+{
+  return bar (i + 1);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]-*\[0-9\]*\\(%\[re\]?bp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%rdx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "jmp" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-21.c b/gcc/testsuite/gcc.target/i386/interrupt-21.c
new file mode 100644
index 0000000..7cc5181
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-21.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx -mno-iamcu -msse" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movaps\[\\t \]*%xmm3,\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movaps\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%xmm3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-2\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-2\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[4-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[4-9\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm1\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm1\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-22.c b/gcc/testsuite/gcc.target/i386/interrupt-22.c
new file mode 100644
index 0000000..5dd6290
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-22.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm3,\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-2\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-2\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[4-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[4-9\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm1\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm1\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-23.c b/gcc/testsuite/gcc.target/i386/interrupt-23.c
new file mode 100644
index 0000000..e0cf550
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-23.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mavx512f -mno-iamcu" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm3,\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-2\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-2\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[4-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[4-9\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm1\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm1\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-24.c b/gcc/testsuite/gcc.target/i386/interrupt-24.c
new file mode 100644
index 0000000..9b92c00
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-24.c
@@ -0,0 +1,21 @@
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mno-iamcu -mmpx" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "bnd3");
+}
+
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*%bnd3,\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%bnd3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-25.c b/gcc/testsuite/gcc.target/i386/interrupt-25.c
new file mode 100644
index 0000000..849fd3a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-25.c
@@ -0,0 +1,24 @@
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -mno-avx -mno-iamcu -msse -mincoming-stack-boundary=2" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%xmm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%xmm3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-2\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-2\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[4-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[4-9\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm1\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm1\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%e(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%e(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%ebp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-26.c b/gcc/testsuite/gcc.target/i386/interrupt-26.c
new file mode 100644
index 0000000..27b40b0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-26.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wall -g" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+}
+
+/* { dg-final { scan-assembler-not "add(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-27.c b/gcc/testsuite/gcc.target/i386/interrupt-27.c
new file mode 100644
index 0000000..774bb4b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-27.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O0 -g -mno-avx -mno-iamcu -msse" } */
+
+typedef float __v4sf __attribute__ ((__vector_size__ (16)));
+__v4sf x, y;
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  x = y;
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%xmm0,\[\\t \]*-?\[0-9\]*\\(%\[re\]?bp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-?\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%xmm0" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-28.c b/gcc/testsuite/gcc.target/i386/interrupt-28.c
new file mode 100644
index 0000000..0768a46
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-28.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -mno-iamcu" } */
+
+struct ret
+{
+  int i[8];
+};
+
+extern struct ret bar (void);
+
+void
+ __attribute__ ((interrupt))
+fn (void *frame)
+{
+  bar ();
+} /* { dg-message "sorry, unimplemented: Dynamic Realign Argument Pointer" } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-3.c b/gcc/testsuite/gcc.target/i386/interrupt-3.c
new file mode 100644
index 0000000..b09f56c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-3.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -g" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+void
+__attribute__((interrupt))
+fn (void* frame, uword_t error)
+{
+}
+
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-387-err.c b/gcc/testsuite/gcc.target/i386/interrupt-387-err.c
new file mode 100644
index 0000000..36094f2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-387-err.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -m80387 -mlong-double-80 -mno-iamcu -mno-sse" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+extern long double y, x;
+
+void
+fn0 (void)
+{
+  x = y;
+  y = 0;
+}
+
+void
+__attribute__((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  x = 0; /* { dg-error "80387 instructions aren't allowed in exception service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn2 (void *frame)
+{
+  x = y; /* { dg-error "80387 instructions aren't allowed in interrupt service routine" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-4.c b/gcc/testsuite/gcc.target/i386/interrupt-4.c
new file mode 100644
index 0000000..c34dbe0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-4.c
@@ -0,0 +1,32 @@
+/* { dg-do link } */
+/* { dg-options "-O -g" } */
+
+#include <stdint.h>
+
+extern void link_error (void);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+struct interrupt_frame
+{
+  uword_t ip;
+  uword_t cs;
+  uword_t flags;
+  uword_t sp;
+  uword_t ss;
+};
+
+__attribute__ ((used, interrupt))
+void
+foo (struct interrupt_frame *frame)
+{
+  void *ra = __builtin_return_address (0);
+  if ((uintptr_t) ra != (uintptr_t) frame->ip)
+    link_error ();
+}
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-5.c b/gcc/testsuite/gcc.target/i386/interrupt-5.c
new file mode 100644
index 0000000..3c4bc2d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-5.c
@@ -0,0 +1,23 @@
+/* { dg-do link } */
+/* { dg-options "-O -g" } */
+
+#include <stdint.h>
+
+extern void link_error (void);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+__attribute__ ((used, interrupt))
+void
+foo (void *frame, uword_t error)
+{
+  void *ra = __builtin_return_address (0);
+  if ((uintptr_t) ra != (uintptr_t) error)
+    link_error ();
+}
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-6.c b/gcc/testsuite/gcc.target/i386/interrupt-6.c
new file mode 100644
index 0000000..a1a3d0e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-6.c
@@ -0,0 +1,40 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+extern int error;
+
+__attribute__((interrupt))
+void
+fn1 (void *p, short error_code)
+{ /* { dg-error "interrupt service routine should have unsigned \(long long |long |\)int as the second argument" } */
+}
+
+__attribute__((interrupt))
+void
+fn2 (void)
+{ /* { dg-error "interrupt service routine can only have a pointer argument and an optional integer argument" } */
+}
+
+__attribute__((interrupt))
+void
+fn3 (uword_t error_code)
+{ /* { dg-error "interrupt service routine should have a pointer as the first argument" } */
+  error = error_code;
+}
+
+__attribute__((interrupt))
+void
+fn4 (uword_t error_code, void *frame)
+{ /* { dg-error "interrupt service routine should have .* the .* argument" } */
+  error = error_code;
+}
+
+extern int fn5 (void *) __attribute__ ((interrupt)); /* { dg-error "interrupt service routine can't have non-void return value" } */
+
+int
+fn5 (void *frame)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-7.c b/gcc/testsuite/gcc.target/i386/interrupt-7.c
new file mode 100644
index 0000000..e7441a2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-7.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int error;
+
+extern void fn (void *) __attribute__((interrupt));
+
+void
+foo (void)
+{
+  fn (&error); /* { dg-error "interrupt service routine can't be called directly" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-8.c b/gcc/testsuite/gcc.target/i386/interrupt-8.c
new file mode 100644
index 0000000..defec2e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-8.c
@@ -0,0 +1,20 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -maccumulate-outgoing-args" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm\[0-9\]+,\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\)" 16 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%ymm\[0-9\]+" 16 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm\[0-9\]+,\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%ymm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-9.c b/gcc/testsuite/gcc.target/i386/interrupt-9.c
new file mode 100644
index 0000000..56bd8dd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-9.c
@@ -0,0 +1,22 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512bw -mno-iamcu -mavx512f -maccumulate-outgoing-args" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]-*\[0-9\]*\\(%\[re\]?bp\\)" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%zmm\[0-9\]+" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%zmm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "kmovw\[\\t \]*%k\[0-7\]+,\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\)" 8 } } */
+/* { dg-final { scan-assembler-times "kmovw\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%k\[0-7\]+" 8 } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-bnd.c b/gcc/testsuite/gcc.target/i386/interrupt-bnd.c
new file mode 100644
index 0000000..9f50661
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-bnd.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mno-iamcu -mmpx" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "bnd3");
+}
+
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*%bnd3,\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%bnd3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
new file mode 100644
index 0000000..712b85f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
@@ -0,0 +1,35 @@
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -miamcu -maccumulate-outgoing-args" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern int bar (int);
+
+void foo (void *frame)
+{
+  int a,b,c,d,e,f,i;
+  a = bar (5);
+  b = bar (a);
+  c = bar (b);
+  d = bar (c);
+  e = bar (d);
+  f = bar (e);
+  for (i = 1; i < 10; i++)
+    a += bar (a + i) + bar (b + i) +
+	 bar (c + i) + bar (d + i) +
+	 bar (e + i) + bar (f+i);
+}
+
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%eax" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%ebx" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%ecx" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%edx" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%edi" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%esi" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%ebp" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%eax" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%ecx" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%edx" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%edi" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%esi" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%ebp" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c
new file mode 100644
index 0000000..fc4f367
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mmmx -mno-iamcu" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+typedef short __v4hi __attribute__ ((__vector_size__ (8)));
+typedef int __m64 __attribute__ ((__vector_size__ (8), __may_alias__));
+
+extern __m64 y, x;
+
+void
+fn0 (void)
+{
+  x = __extension__ (__m64){ 0 };
+  x = (__m64) __builtin_ia32_packsswb ((__v4hi) x, (__v4hi) y);
+  y = x;
+}
+
+void
+__attribute__((interrupt))
+fn1 (void *frame)
+{
+  x = (__m64) __builtin_ia32_packsswb ((__v4hi) x, (__v4hi) y); /* { dg-error "MMX/3Dnow (instructions|intrinsics) aren't allowed in interrupt service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn2 (void *frame)
+{
+  x = y; /* { dg-error "MMX/3Dnow instructions aren't allowed in interrupt service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn3 (void *frame, uword_t error)
+{
+  x = __extension__ (__m64){ 0 }; /* { dg-error "MMX/3Dnow instructions aren't allowed in exception service routine" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c b/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c
new file mode 100644
index 0000000..78d3f27
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c
@@ -0,0 +1,31 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mred-zone" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  /* No need to adjust stack if less than 128 bytes are used on stack
+     with a 128-byte red zone.  */
+  long long int i0;
+  long long int i1;
+  long long int i2;
+  long long int i3;
+  long long int i4;
+  long long int i5;
+  long long int i6;
+  long long int i7;
+  long long int i8;
+  long long int i9;
+  long long int i10;
+  long long int i11;
+  long long int i12;
+  long long int i13;
+  asm ("# %0, %1, %2, %3, %4, %5, %6, %7"
+       : "=m" (i0), "=m" (i1), "=m" (i2), "=m" (i3),
+         "=m" (i4), "=m" (i5), "=m" (i6), "=m" (i7),
+         "=m" (i8), "=m" (i9), "=m" (i10), "=m" (i11),
+	 "=m" (i12), "=m" (i13));
+}
+
+/* { dg-final { scan-assembler-not "(sub|add)(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c b/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c
new file mode 100644
index 0000000..1ab7041
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c
@@ -0,0 +1,32 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mred-zone" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  /* Need to adjust stack if more than 128 bytes are used on stack
+     with a 128-byte red zone.  */
+  long long int i0;
+  long long int i1;
+  long long int i2;
+  long long int i3;
+  long long int i4;
+  long long int i5;
+  long long int i6;
+  long long int i7;
+  long long int i8;
+  long long int i9;
+  long long int i10;
+  long long int i11;
+  long long int i12;
+  long long int i13;
+  char c;
+  asm ("# %0, %1, %2, %3, %4, %5, %6, %7"
+       : "=m" (i0), "=m" (i1), "=m" (i2), "=m" (i3),
+         "=m" (i4), "=m" (i5), "=m" (i6), "=m" (i7),
+         "=m" (i8), "=m" (i9), "=m" (i10), "=m" (i11),
+	 "=m" (i12), "=m" (i13), "=m" (c));
+}
+
+/* { dg-final { scan-assembler-times "(?:sub|add)(?:l|q)\[\\t \]*\\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c b/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c
new file mode 100644
index 0000000..e222acf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O3" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern void bar (void);
+
+void foo (void *frame)
+{
+  bar ();
+}
+/* { dg-final { scan-assembler-not "jmp" } }*/
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c
new file mode 100644
index 0000000..b1b0abe
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern void bar (int);
+
+void f1 (void){  bar (1); }
+__attribute__((interrupt))
+void f2 (void *frame){  bar (2); }
+void f3 (void){  bar (3); }
+__attribute__((interrupt))
+void f4 (void *frame){  bar (4); }
+void f5 (void){  bar (5); }
+
+/* { dg-final { scan-assembler-times "push.\t%.ax" 2 } } */
+/* { dg-final { scan-assembler-times "pop.\t%.ax" 2 } } */
+/* { dg-final { scan-assembler-times "iret" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-xmm.c b/gcc/testsuite/gcc.target/i386/interrupt-xmm.c
new file mode 100644
index 0000000..6ffdd96
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-xmm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx -mno-iamcu -msse" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%xmm3,\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%xmm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-ymm.c b/gcc/testsuite/gcc.target/i386/interrupt-ymm.c
new file mode 100644
index 0000000..0754ae0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-ymm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*%ymm3,\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-zmm.c b/gcc/testsuite/gcc.target/i386/interrupt-zmm.c
new file mode 100644
index 0000000..f4d2fa1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-zmm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mavx512f -mno-iamcu" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*%zmm3,\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-10-23 13:13                                                         ` Yulia Koval
@ 2015-11-06 14:08                                                           ` Yulia Koval
  2015-11-06 15:31                                                             ` Uros Bizjak
  0 siblings, 1 reply; 45+ messages in thread
From: Yulia Koval @ 2015-11-06 14:08 UTC (permalink / raw)
  To: GCC Patches; +Cc: H.J. Lu, Mike Stump, Uros Bizjak

[-- Attachment #1: Type: text/plain, Size: 8545 bytes --]

Hi,

I updated and reposted the patch. Regtested/bootstraped on
x86_64/Linux and i686/Linux. Ok for trunk?

Implement x86 interrupt attribute

The interrupt and exception handlers are called by x86 processors.  X86
hardware pushes information onto stack and calls the handler.  The
requirements are

1. Both interrupt and exception handlers must use the 'IRET' instruction,
instead of the 'RET' instruction, to return from the handlers.
2. All registers are callee-saved in interrupt and exception handlers.
3. The difference between interrupt and exception handlers is the
exception handler must pop 'ERROR_CODE' off the stack before the 'IRET'
instruction.

The design goals of interrupt and exception handlers for x86 processors
are:

1. Support both 32-bit and 64-bit modes.
2. Flexible for compilers to optimize.
3. Easy to use by programmers.

To implement interrupt and exception handlers for x86 processors, a
compiler should support:

'interrupt' attribute

Use this attribute to indicate that the specified function with
mandatory arguments is an interrupt or exception handler.  The compiler
generates function entry and exit sequences suitable for use in an
interrupt handler when this attribute is present.  The 'IRET' instruction,
instead of the 'RET' instruction, is used to return from interrupt or
exception handlers.  All registers, except for the EFLAGS register which
is restored by the 'IRET' instruction, are preserved by the compiler.

Any interruptible-without-stack-switch code must be compiled with
-mno-red-zone since interrupt handlers can and will, because of the
hardware design, touch the red zone.

1. interrupt handler must be declared with a mandatory pointer argument:

struct interrupt_frame;

__attribute__ ((interrupt))
void
f (struct interrupt_frame *frame)
{
...
}

and user must properly define the structure the pointer pointing to.

2. exception handler:

The exception handler is very similar to the interrupt handler with
a different mandatory function signature:

typedef unsigned int uword_t __attribute__ ((mode (__word__)));

struct interrupt_frame;

__attribute__ ((interrupt))
void
f (struct interrupt_frame *frame, uword_t error_code)
{
...
}

and compiler pops the error code off stack before the 'IRET' instruction.

The exception handler should only be used for exceptions which push an
error code and all other exceptions must use the interrupt handler.
The system will crash if the wrong handler is used.

To be feature complete, compiler may implement the optional
'no_caller_saved_registers' attribute:

Use this attribute to indicate that the specified function has no
caller-saved registers.  That is, all registers are callee-saved.
The compiler generates proper function entry and exit sequences to
save and restore any modified registers.

The user can call functions specified with 'no_caller_saved_registers'
attribute from an interrupt handler without saving and restoring all
call clobbered registers.

gcc/

PR target/66960
PR target/67630
PR target/67634
PR target/68037
* config/i386/i386-protos.h (ix86_epilogue_uses): New prototype.
* config/i386/i386.c (ix86_frame): Add nbndregs, nmaskregs,
bnd_reg_save_offset and mask_reg_save_offset.
(ix86_conditional_register_usage): Preserve all registers,
except for function return registers if there are no caller-saved
registers.
(ix86_set_current_function): Set no_caller_saved_registers and
func_type.  Call reinit_regs if AX register usage isn't
consistent.
(ix86_function_ok_for_sibcall): Return false if there are no
caller-saved registers.
(type_natural_mode): Don't warn ABI change for MMX in interrupt
handler.
(ix86_function_arg_advance): Skip for callee in interrupt
handler.
(ix86_function_arg): Handle arguments for callee in interrupt
handler.
(ix86_can_use_return_insn_p): Don't use `ret' instruction in
interrupt handler.
(ix86_epilogue_uses): New function.
(ix86_hard_regno_scratch_ok): Likewise.
(ix86_reg_ever_defined_p): Likewise.
(ix86_nsaved_bndregs): Likewise.
(ix86_nsaved_maskregs): Likewise.
(ix86_emit_save_bnd_regs_using_mov): Likewise.
(ix86_emit_save_mask_regs_using_mov): Likewise.
(ix86_emit_restore_bnd_regs_using_mov): Likewise.
(ix86_emit_restore_mask_regs_using_mov): Likewise.
(ix86_handle_no_caller_saved_registers_attribute): Likewise.
(ix86_handle_interrupt_attribute): Likewise.
(ix86_save_reg): Preserve all registers in interrupt function
after reload.  Preserve all registers, except for function
return registers, if there are no caller-saved registers after
reload.
(ix86_nsaved_sseregs): Don't return 0 if there are no
caller-saved registers.
(ix86_compute_frame_layout): Set nbndregs and nmaskregs.  Set
and allocate BND and MASK register save areas.  Allocate space to
save full vector registers if there are no caller-saved registers.
(ix86_emit_save_reg_using_mov): Don't use UNSPEC_STOREU to
SSE registers.
(ix86_emit_save_sse_regs_using_mov): Save full vector registers
if there are no caller-saved registers.
(find_drap_reg): Always use callee-saved register if there are
no caller-saved registers.
(ix86_minimum_incoming_stack_boundary): Return MIN_STACK_BOUNDARY
for interrupt handler.
(ix86_expand_prologue): Save BND and MASK registers.  Adjust
DECL_INCOMING_RTL of the pointer argument for interrupt handler.
(ix86_emit_restore_sse_regs_using_mov): Restore full vector
registers if there are no caller-saved registers.
(ix86_expand_epilogue): Restore BND and MASK registers.  Generate
interrupt return for interrupt handler and pop the 'ERROR_CODE'
off the stack before interrupt return in exception handler.
(ix86_expand_move): Disallow 80387 instructions in exception
handler.
(ix86_expand_vector_move): Disallow MMX/3Dnow instructions in
exception handler.
(ix86_expand_call): Disallow calling interrupt handler directly.
If there are no caller-saved registers, mark all registers that
are clobbered by the call as clobbered.
(ix86_attribute_table): Add interrupt and no_caller_saved_registers
attributes.
(TARGET_HARD_REGNO_SCRATCH_OK): New.
* config/i386/i386.h (ACCUMULATE_OUTGOING_ARGS): Always use
argument accumulation in interrupt function if stack may be
realigned to avoid DRAP.
* config/i386/i386.h (EPILOGUE_USES): New.
(function_type): New enum.
(machine_function): Add func_type and no_caller_saved_registers.
* config/i386/i386.md (UNSPEC_INTERRUPT_RETURN): New.
(interrupt_return): New pattern.
* doc/extend.texi: Document x86 interrupt and
no_caller_saved_registers attributes.

gcc/testsuite/

PR target/66960
PR target/67630
PR target/67634
PR target/68037
* gcc.dg/guality/pr68037-1.c: New test.
* gcc.dg/guality/pr68037-2.c: Likewise.
* gcc.dg/guality/pr68037-3.c: Likewise.
* gcc.dg/torture/pr68037-1.c: Likewise.
* gcc.dg/torture/pr68037-2.c: Likewise.
* gcc.dg/torture/pr68037-3.c: Likewise.
* gcc.target/i386/interrupt-1.c: Likewise.
* gcc.target/i386/interrupt-2.c: Likewise.
* gcc.target/i386/interrupt-3.c: Likewise.
* gcc.target/i386/interrupt-4.c: Likewise.
* gcc.target/i386/interrupt-5.c: Likewise.
* gcc.target/i386/interrupt-6.c: Likewise.
* gcc.target/i386/interrupt-7.c: Likewise.
* gcc.target/i386/interrupt-8.c: Likewise.
* gcc.target/i386/interrupt-9.c: Likewise.
* gcc.target/i386/interrupt-10.c: Likewise.
* gcc.target/i386/interrupt-11.c: Likewise.
* gcc.target/i386/interrupt-12.c: Likewise.
* gcc.target/i386/interrupt-13.c: Likewise.
* gcc.target/i386/interrupt-14.c: Likewise.
* gcc.target/i386/interrupt-15.c: Likewise.
* gcc.target/i386/interrupt-16.c: Likewise.
* gcc.target/i386/interrupt-17.c: Likewise.
* gcc.target/i386/interrupt-18.c: Likewise.
* gcc.target/i386/interrupt-19.c: Likewise.
* gcc.target/i386/interrupt-20.c: Likewise.
* gcc.target/i386/interrupt-21.c: Likewise.
* gcc.target/i386/interrupt-22.c: Likewise.
* gcc.target/i386/interrupt-23.c: Likewise.
* gcc.target/i386/interrupt-24.c: Likewise.
* gcc.target/i386/interrupt-25.c: Likewise.
* gcc.target/i386/interrupt-26.c: Likewise.
* gcc.target/i386/interrupt-27.c: Likewise.
* gcc.target/i386/interrupt-28.c: Likewise.
* gcc.target/i386/interrupt-387-err.c: Likewise.
* gcc.target/i386/interrupt-bnd.c: Likewise.
* gcc.target/i386/interrupt-iamcu.c: Likewise.
* gcc.target/i386/interrupt-mmx-err.c: Likewise.
* gcc.target/i386/interrupt-redzone-1.c: Likewise.
* gcc.target/i386/interrupt-redzone-2.c: Likewise.
* gcc.target/i386/interrupt-sibcall.c: Likewise.
* gcc.target/i386/interrupt-switch-abi.c: Likewise.
* gcc.target/i386/interrupt-xmm.c: Likewise.
* gcc.target/i386/interrupt-ymm.c: Likewise.
* gcc.target/i386/interrupt-zmm.c: Likewise.

[-- Attachment #2: patch_06_11 --]
[-- Type: application/octet-stream, Size: 95589 bytes --]

diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index 6a17ef4..c7ff393 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -278,6 +278,8 @@ extern rtx maybe_get_pool_constant (rtx);
 extern char internal_label_prefix[16];
 extern int internal_label_prefix_len;
 
+extern bool ix86_epilogue_uses (int);
+
 enum ix86_address_seg { SEG_DEFAULT, SEG_FS, SEG_GS };
 struct ix86_address
 {
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 2a965f6..8c71ea8 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -2457,6 +2457,8 @@ struct ix86_frame
 {
   int nsseregs;
   int nregs;
+  int nbndregs;
+  int nmaskregs;
   int va_arg_size;
   int red_zone_size;
   int outgoing_arguments_size;
@@ -2467,6 +2469,8 @@ struct ix86_frame
   HOST_WIDE_INT stack_pointer_offset;
   HOST_WIDE_INT hfp_save_offset;
   HOST_WIDE_INT reg_save_offset;
+  HOST_WIDE_INT bnd_reg_save_offset;
+  HOST_WIDE_INT mask_reg_save_offset;
   HOST_WIDE_INT sse_reg_save_offset;
 
   /* When save_regs_using_mov is set, emit prologue using
@@ -5547,6 +5551,15 @@ ix86_conditional_register_usage (void)
 {
   int i, c_mask;
 
+  /* If there are no caller-saved registers, preserve all registers.
+     except fixed_regs and registers used for function return value
+     since aggregate_value_p checks call_used_regs[regno] on return
+     value.  */
+  if (cfun && cfun->machine->no_caller_saved_registers)
+    for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+      if (!fixed_regs[i] && !ix86_function_value_regno_p (i))
+	call_used_regs[i] = 0;
+
   /* For 32-bit targets, squash the REX registers.  */
   if (! TARGET_64BIT)
     {
@@ -6341,6 +6354,21 @@ ix86_set_current_function (tree fndecl)
       return;
     }
 
+  if (lookup_attribute ("interrupt", DECL_ATTRIBUTES (fndecl)))
+    {
+      int nargs = 0;
+      for (tree arg = DECL_ARGUMENTS (fndecl);
+	   arg;
+	   arg = TREE_CHAIN (arg))
+	nargs++;
+      cfun->machine->no_caller_saved_registers = true;
+      cfun->machine->func_type
+	= nargs == 2 ? TYPE_EXCEPTION : TYPE_INTERRUPT;
+    }
+  else if (lookup_attribute ("no_caller_saved_registers",
+			     DECL_ATTRIBUTES (fndecl)))
+    cfun->machine->no_caller_saved_registers = true;
+
   tree new_tree = DECL_FUNCTION_SPECIFIC_TARGET (fndecl);
   if (new_tree == NULL_TREE)
     new_tree = target_option_default_node;
@@ -6364,6 +6392,11 @@ ix86_set_current_function (tree fndecl)
       && (call_used_regs[SI_REG]
 	  == (cfun->machine->call_abi == MS_ABI)))
     reinit_regs ();
+  /* AX register is only preserved if there are no caller-saved
+     registers.  */
+  else if (cfun->machine->no_caller_saved_registers
+	   == call_used_regs[AX_REG])
+    reinit_regs ();
 }
 
 \f
@@ -6627,6 +6660,10 @@ ix86_function_ok_for_sibcall (tree decl, tree exp)
   tree type, decl_or_type;
   rtx a, b;
 
+  /* Sibling call isn't OK if there are no caller-saved registers.  */
+  if (cfun->machine->no_caller_saved_registers)
+    return false;
+
   /* If we are generating position-independent code, we cannot sibcall
      optimize direct calls to global functions, as the PLT requires
      %ebx be live. (Darwin does not have a PLT.)  */
@@ -7800,6 +7837,8 @@ type_natural_mode (const_tree type, const CUMULATIVE_ARGS *cum,
 		      }
 		  }
 		else if ((size == 8 && !TARGET_64BIT)
+			 && (!cfun
+			     || cfun->machine->func_type == TYPE_NORMAL)
 			 && !TARGET_MMX
 			 && !TARGET_IAMCU)
 		  {
@@ -8778,6 +8817,11 @@ ix86_function_arg_advance (cumulative_args_t cum_v, machine_mode mode,
   HOST_WIDE_INT bytes, words;
   int nregs;
 
+  /* The argument of interrupt handler is a special case and is
+     handled in ix86_function_arg.  */
+  if (!cum->caller && cfun->machine->func_type != TYPE_NORMAL)
+    return;
+
   if (mode == BLKmode)
     bytes = int_size_in_bytes (type);
   else
@@ -9094,6 +9138,40 @@ ix86_function_arg (cumulative_args_t cum_v, machine_mode omode,
   HOST_WIDE_INT bytes, words;
   rtx arg;
 
+  if (!cum->caller && cfun->machine->func_type != TYPE_NORMAL)
+    {
+      /* The first argument of interrupt handler is a pointer and
+	 points to the return address slot on stack.  The optional
+	 second argument is an integer for error code on stack.  */
+      gcc_assert (type != NULL_TREE);
+      if (POINTER_TYPE_P (type))
+	{
+	  if (cfun->machine->func_type == TYPE_EXCEPTION)
+	    /* (AP) in the current frame in exception handler.  */
+	    arg = arg_pointer_rtx;
+	  else
+	    /* -WORD(AP) in the current frame in interrupt handler.  */
+	    arg = force_reg (Pmode,
+			     plus_constant (Pmode, arg_pointer_rtx,
+					    -UNITS_PER_WORD));
+	  if (mode != Pmode)
+	    arg = convert_to_mode (mode, arg, 1);
+	}
+      else
+	{
+	  gcc_assert (TREE_CODE (type) == INTEGER_TYPE
+		      && cfun->machine->func_type == TYPE_EXCEPTION
+		      && mode == word_mode);
+	  /* The error code is at -WORD(AP) in the current frame in
+	     exception handler.  */
+	  arg = gen_rtx_MEM (word_mode,
+			     plus_constant (Pmode, arg_pointer_rtx,
+					    -UNITS_PER_WORD));
+	}
+
+      return arg;
+    }
+
   /* All pointer bounds arguments are handled separately here.  */
   if ((type && POINTER_BOUNDS_TYPE_P (type))
       || POINTER_BOUNDS_MODE_P (mode))
@@ -10809,7 +10887,10 @@ ix86_can_use_return_insn_p (void)
 {
   struct ix86_frame frame;
 
-  if (! reload_completed || frame_pointer_needed)
+  /* Don't use `ret' instruction in interrupt handler.  */
+  if (! reload_completed
+      || frame_pointer_needed
+      || cfun->machine->func_type != TYPE_NORMAL)
     return 0;
 
   /* Don't allow more than 32k pop, since that's all we can do
@@ -11120,11 +11201,102 @@ ix86_select_alt_pic_regnum (void)
   return INVALID_REGNUM;
 }
 
+/* Return true if REGNO is used by the epilogue.  */
+
+bool
+ix86_epilogue_uses (int regno)
+{
+  /* If there are no caller-saved registers, we preserve all registers,
+     except for MMX and x87 registers which aren't supported when saving
+     and restoring registers.  Don't explicitly save SP register since
+     it is always preserved.  */
+  return (epilogue_completed
+	  && cfun->machine->no_caller_saved_registers
+	  && !fixed_regs[regno]
+	  && !STACK_REGNO_P (regno)
+	  && !MMX_REGNO_P (regno));
+}
+
+/* Return TRUE if register REGNO is ever defined..  */
+
+static bool
+ix86_reg_ever_defined_p (unsigned int regno)
+{
+  df_ref def;
+
+  for (def = DF_REG_DEF_CHAIN (regno); def; def = DF_REF_NEXT_REG (def))
+    if (!DF_REF_IS_ARTIFICIAL (def))
+      return true;
+
+  return false;
+}
+
+/* Return nonzero if register REGNO can be used as a scratch register
+   in peephole2.  */
+
+static bool
+ix86_hard_regno_scratch_ok (unsigned int regno)
+{
+  /* If there are no caller-saved registers, we can't use any register
+     as a scratch register after epilogue and use REGNO as scratch
+     register only if it has been used before to avoid saving and
+     restoring it.  */
+  return (!cfun->machine->no_caller_saved_registers
+	  || (!epilogue_completed
+	      && df_regs_ever_live_p (regno)
+	      && ix86_reg_ever_defined_p (regno)));
+}
+
 /* Return TRUE if we need to save REGNO.  */
 
 static bool
 ix86_save_reg (unsigned int regno, bool maybe_eh_return)
 {
+  /* If there are no caller-saved registers, we preserve all registers,
+     except for MMX and x87 registers which aren't supported when saving
+     and restoring registers.  Don't explicitly save SP register since
+     it is always preserved.  */
+  if (reload_completed)
+    {
+      if (cfun->machine->func_type != TYPE_NORMAL)
+	return (df_regs_ever_live_p (regno)
+		&& ix86_reg_ever_defined_p (regno)
+		&& !fixed_regs[regno]
+		&& !STACK_REGNO_P (regno)
+		&& !MMX_REGNO_P (regno));
+
+      if (cfun->machine->no_caller_saved_registers)
+	{
+	  /* Don't preserve registers used for function return
+	     value.  */
+	  rtx reg = crtl->return_rtx;
+	  if (reg)
+	    {
+	      unsigned int i = REGNO (reg);
+	      unsigned nregs = hard_regno_nregs[i][GET_MODE (reg)];
+	      while (nregs-- > 0)
+		if ((i + nregs) == regno)
+		  return false;
+	    }
+
+	  reg = crtl->return_bnd;
+	  if (reg)
+	    {
+	      unsigned int i = REGNO (reg);
+	      unsigned nregs = hard_regno_nregs[i][GET_MODE (reg)];
+	      while (nregs-- > 0)
+		if ((i + nregs) == regno)
+		  return false;
+	    }
+
+	  return (df_regs_ever_live_p (regno)
+		  && ix86_reg_ever_defined_p (regno)
+		  && !fixed_regs[regno]
+		  && !STACK_REGNO_P (regno)
+		  && !MMX_REGNO_P (regno));
+	}
+    }
+
   if (regno == REAL_PIC_OFFSET_TABLE_REGNUM
       && pic_offset_table_rtx)
     {
@@ -11181,6 +11353,34 @@ ix86_nsaved_regs (void)
   return nregs;
 }
 
+/* Return number of saved bound registers.  */
+
+static int
+ix86_nsaved_bndregs (void)
+{
+  int nregs = 0;
+  int regno;
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (BND_REGNO_P (regno) && ix86_save_reg (regno, true))
+      nregs ++;
+  return nregs;
+}
+
+/* Return number of saved mask registers.  */
+
+static int
+ix86_nsaved_maskregs (void)
+{
+  int nregs = 0;
+  int regno;
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (MASK_REGNO_P (regno) && ix86_save_reg (regno, true))
+      nregs ++;
+  return nregs;
+}
+
 /* Return number of saved SSE registers.  */
 
 static int
@@ -11189,7 +11389,10 @@ ix86_nsaved_sseregs (void)
   int nregs = 0;
   int regno;
 
-  if (!TARGET_64BIT_MS_ABI)
+  /* Always need to save SSE registers if there are no caller-saved
+     registers.  */
+  if (!TARGET_64BIT_MS_ABI
+      && !cfun->machine->no_caller_saved_registers)
     return 0;
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
@@ -11270,6 +11473,8 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   HOST_WIDE_INT to_allocate;
 
   frame->nregs = ix86_nsaved_regs ();
+  frame->nbndregs = ix86_nsaved_bndregs ();
+  frame->nmaskregs = ix86_nsaved_maskregs ();
   frame->nsseregs = ix86_nsaved_sseregs ();
 
   /* 64-bit MS ABI seem to require stack alignment to be always 16,
@@ -11374,19 +11579,39 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   if (TARGET_SEH)
     frame->hard_frame_pointer_offset = offset;
 
+  /* Set BND register save area.  */
+  if (frame->nbndregs)
+    offset += frame->nbndregs * (ix86_pmode == PMODE_DI ? 16 : 8);
+  frame->bnd_reg_save_offset = offset;
+
+  /* Set MASK register save area.  */
+  if (frame->nmaskregs)
+    offset += frame->nmaskregs * (TARGET_AVX512BW ? 8 : 2);
+  frame->mask_reg_save_offset = offset;
+
   /* Align and set SSE register save area.  */
   if (frame->nsseregs)
     {
+      /* We must save full vector registers if there are no
+	 caller-saved registers.  */
+      unsigned int vec_regsize;
+      if (cfun->machine->no_caller_saved_registers)
+	vec_regsize = (TARGET_AVX512F ? 64 : (TARGET_AVX ? 32 : 16));
+      else
+	vec_regsize = 16;
+
       /* The only ABI that has saved SSE registers (Win64) also has a
 	 16-byte aligned default stack, and thus we don't need to be
 	 within the re-aligned local stack frame to save them.  In case
 	 incoming stack boundary is aligned to less than 16 bytes,
 	 unaligned move of SSE register will be emitted, so there is
 	 no point to round up the SSE register save area outside the
-	 re-aligned local stack frame to 16 bytes.  */
-      if (ix86_incoming_stack_boundary >= 128)
-	offset = ROUND_UP (offset, 16);
-      offset += frame->nsseregs * 16;
+	 re-aligned local stack frame to 16 bytes.  When full vector
+	 registers are saved, we check incoming stack boundary against
+	 the size of vector registers.  */
+      if (ix86_incoming_stack_boundary >= (vec_regsize * BITS_PER_UNIT))
+	offset = ROUND_UP (offset, vec_regsize);
+      offset += frame->nsseregs * vec_regsize;
     }
   frame->sse_reg_save_offset = offset;
 
@@ -11440,7 +11665,7 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   frame->stack_pointer_offset = offset;
 
   /* Size prologue needs to allocate.  */
-  to_allocate = offset - frame->sse_reg_save_offset;
+  to_allocate = offset - frame->bnd_reg_save_offset;
 
   if ((!to_allocate && frame->nregs <= 1)
       || (TARGET_64BIT && to_allocate >= (HOST_WIDE_INT) 0x80000000))
@@ -11691,18 +11916,67 @@ ix86_emit_save_regs_using_mov (HOST_WIDE_INT cfa_offset)
       }
 }
 
+/* Emit code to save BND registers using MOV insns.
+   First register is stored at CFA - CFA_OFFSET.  */
+static void
+ix86_emit_save_bnd_regs_using_mov (HOST_WIDE_INT cfa_offset)
+{
+  unsigned int regno;
+  enum machine_mode mode = BNDmode;
+  unsigned int size = GET_MODE_SIZE (mode);
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (BND_REGNO_P (regno) && ix86_save_reg (regno, true))
+      {
+	ix86_emit_save_reg_using_mov (mode, regno, cfa_offset);
+	cfa_offset -= size;
+      }
+}
+
+/* Emit code to save MASK registers using MOV insns.
+   First register is stored at CFA - CFA_OFFSET.  */
+static void
+ix86_emit_save_mask_regs_using_mov (HOST_WIDE_INT cfa_offset)
+{
+  unsigned int regno;
+  enum machine_mode mode = TARGET_AVX512BW ? DImode : HImode;
+  unsigned int size = GET_MODE_SIZE (mode);
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (MASK_REGNO_P (regno) && ix86_save_reg (regno, true))
+      {
+	ix86_emit_save_reg_using_mov (mode, regno, cfa_offset);
+	cfa_offset -= size;
+      }
+}
+
 /* Emit code to save SSE registers using MOV insns.
    First register is stored at CFA - CFA_OFFSET.  */
 static void
 ix86_emit_save_sse_regs_using_mov (HOST_WIDE_INT cfa_offset)
 {
   unsigned int regno;
+  enum machine_mode vector_reg_mode;
+
+  if (cfun->machine->no_caller_saved_registers)
+    {
+      /* We must save full vector registers if there are no
+	 caller-saved registers.  */
+      if (TARGET_AVX512F)
+	vector_reg_mode = V16SFmode;
+      else if (TARGET_AVX)
+	vector_reg_mode = V8SFmode;
+      else
+	vector_reg_mode = V4SFmode;
+    }
+  else
+    vector_reg_mode = V4SFmode;
 
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, true))
       {
-	ix86_emit_save_reg_using_mov (V4SFmode, regno, cfa_offset);
-	cfa_offset -= GET_MODE_SIZE (V4SFmode);
+	ix86_emit_save_reg_using_mov (vector_reg_mode, regno, cfa_offset);
+	cfa_offset -= GET_MODE_SIZE (vector_reg_mode);
       }
 }
 
@@ -11858,13 +12132,17 @@ find_drap_reg (void)
 {
   tree decl = cfun->decl;
 
+  /* Always use callee-saved register if there are no caller-saved
+     registers.  */
   if (TARGET_64BIT)
     {
       /* Use R13 for nested function or function need static chain.
 	 Since function with tail call may use any caller-saved
 	 registers in epilogue, DRAP must not use caller-saved
 	 register in such case.  */
-      if (DECL_STATIC_CHAIN (decl) || crtl->tail_call_emit)
+      if (DECL_STATIC_CHAIN (decl)
+	  || cfun->machine->no_caller_saved_registers
+	  || crtl->tail_call_emit)
 	return R13_REG;
 
       return R10_REG;
@@ -11875,7 +12153,9 @@ find_drap_reg (void)
 	 Since function with tail call may use any caller-saved
 	 registers in epilogue, DRAP must not use caller-saved
 	 register in such case.  */
-      if (DECL_STATIC_CHAIN (decl) || crtl->tail_call_emit)
+      if (DECL_STATIC_CHAIN (decl)
+	  || cfun->machine->no_caller_saved_registers
+	  || crtl->tail_call_emit)
 	return DI_REG;
 
       /* Reuse static chain register if it isn't used for parameter
@@ -11916,8 +12196,12 @@ ix86_minimum_incoming_stack_boundary (bool sibcall)
 {
   unsigned int incoming_stack_boundary;
 
+  /* Stack of interrupt handler is always aligned to MIN_STACK_BOUNDARY.
+   */
+  if (cfun->machine->func_type != TYPE_NORMAL)
+    incoming_stack_boundary = MIN_STACK_BOUNDARY;
   /* Prefer the one specified at command line. */
-  if (ix86_user_incoming_stack_boundary)
+  else if (ix86_user_incoming_stack_boundary)
     incoming_stack_boundary = ix86_user_incoming_stack_boundary;
   /* In 32bit, use MIN_STACK_BOUNDARY for incoming stack boundary
      if -mstackrealign is used, it isn't used for sibcall check and
@@ -12548,6 +12832,8 @@ ix86_expand_prologue (void)
   struct ix86_frame frame;
   HOST_WIDE_INT allocate;
   bool int_registers_saved;
+  bool bnd_registers_saved;
+  bool mask_registers_saved;
   bool sse_registers_saved;
   rtx static_chain = NULL_RTX;
 
@@ -12667,6 +12953,12 @@ ix86_expand_prologue (void)
     {
       int align_bytes = crtl->stack_alignment_needed / BITS_PER_UNIT;
 
+      /* Can't use DRAP in interrupt function.  */
+      if (cfun->machine->func_type != TYPE_NORMAL)
+	sorry ("Dynamic Realign Argument Pointer (DRAP) not supported "
+	       "in interrupt service routine.  This may be worked "
+	       "around by avoiding functions with aggregate return.");
+
       /* Only need to push parameter pointer reg if it is caller saved.  */
       if (!call_used_regs[REGNO (crtl->drap_reg)])
 	{
@@ -12713,6 +13005,8 @@ ix86_expand_prologue (void)
     }
 
   int_registers_saved = (frame.nregs == 0);
+  bnd_registers_saved = (frame.nbndregs == 0);
+  mask_registers_saved = (frame.nmaskregs == 0);
   sse_registers_saved = (frame.nsseregs == 0);
 
   if (frame_pointer_needed && !m->fs.fp_valid)
@@ -12777,10 +13071,10 @@ ix86_expand_prologue (void)
 	 that we must allocate the size of the register save area before
 	 performing the actual alignment.  Otherwise we cannot guarantee
 	 that there's enough storage above the realignment point.  */
-      if (m->fs.sp_offset != frame.sse_reg_save_offset)
+      if (m->fs.sp_offset != frame.bnd_reg_save_offset)
         pro_epilogue_adjust_stack (stack_pointer_rtx, stack_pointer_rtx,
 				   GEN_INT (m->fs.sp_offset
-					    - frame.sse_reg_save_offset),
+					    - frame.bnd_reg_save_offset),
 				   -1, false);
 
       /* Align the stack.  */
@@ -13007,6 +13301,10 @@ ix86_expand_prologue (void)
 
   if (!int_registers_saved)
     ix86_emit_save_regs_using_mov (frame.reg_save_offset);
+  if (!bnd_registers_saved)
+    ix86_emit_save_bnd_regs_using_mov (frame.bnd_reg_save_offset);
+  if (!mask_registers_saved)
+    ix86_emit_save_mask_regs_using_mov (frame.mask_reg_save_offset);
   if (!sse_registers_saved)
     ix86_emit_save_sse_regs_using_mov (frame.sse_reg_save_offset);
 
@@ -13052,6 +13350,23 @@ ix86_expand_prologue (void)
      combined with prologue modifications.  */
   if (TARGET_SEH)
     emit_insn (gen_prologue_use (stack_pointer_rtx));
+
+  if (cfun->machine->func_type != TYPE_NORMAL)
+    {
+      /* Since the pointer argument of interrupt handler isn't a real
+         argument, adjust DECL_INCOMING_RTL for debug output.  */
+      tree arg = DECL_ARGUMENTS (current_function_decl);
+      gcc_assert (arg != NULL_TREE
+		  && POINTER_TYPE_P (TREE_TYPE (arg)));
+      if (cfun->machine->func_type == TYPE_EXCEPTION)
+	/* (AP) in the current frame in exception handler.  */
+	DECL_INCOMING_RTL (arg) = arg_pointer_rtx;
+      else
+	/* -WORD(AP) in the current frame in interrupt handler.  */
+	DECL_INCOMING_RTL (arg) = plus_constant (Pmode,
+						 arg_pointer_rtx,
+						 -UNITS_PER_WORD);
+    }
 }
 
 /* Emit code to restore REG using a POP insn.  */
@@ -13193,6 +13508,90 @@ ix86_emit_restore_regs_using_mov (HOST_WIDE_INT cfa_offset,
       }
 }
 
+/* Emit code to restore saved BND registers using MOV insns.
+   First register is restored from CFA - CFA_OFFSET.  */
+static void
+ix86_emit_restore_bnd_regs_using_mov (HOST_WIDE_INT cfa_offset,
+				      bool maybe_eh_return)
+{
+  struct machine_function *m = cfun->machine;
+  unsigned int regno;
+  enum machine_mode mode = BNDmode;
+  unsigned int size = GET_MODE_SIZE (mode);
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (BND_REGNO_P (regno) && ix86_save_reg (regno, maybe_eh_return))
+      {
+	rtx reg = gen_rtx_REG (mode, regno);
+	rtx mem;
+	rtx_insn *insn;
+
+	mem = choose_baseaddr (cfa_offset);
+	mem = gen_frame_mem (mode, mem);
+	insn = emit_move_insn (reg, mem);
+
+        if (m->fs.cfa_reg == crtl->drap_reg && regno == REGNO (crtl->drap_reg))
+	  {
+	    /* Previously we'd represented the CFA as an expression
+	       like *(%ebp - 8).  We've just popped that value from
+	       the stack, which means we need to reset the CFA to
+	       the drap register.  This will remain until we restore
+	       the stack pointer.  */
+	    add_reg_note (insn, REG_CFA_DEF_CFA, reg);
+	    RTX_FRAME_RELATED_P (insn) = 1;
+
+	    /* This means that the DRAP register is valid for addressing.  */
+	    m->fs.drap_valid = true;
+	  }
+	else
+	  ix86_add_cfa_restore_note (NULL, reg, cfa_offset);
+
+	cfa_offset -= size;
+      }
+}
+
+/* Emit code to restore saved MASK registers using MOV insns.
+   First register is restored from CFA - CFA_OFFSET.  */
+static void
+ix86_emit_restore_mask_regs_using_mov (HOST_WIDE_INT cfa_offset,
+				       bool maybe_eh_return)
+{
+  struct machine_function *m = cfun->machine;
+  unsigned int regno;
+  enum machine_mode mode = TARGET_AVX512BW ? DImode : HImode;
+  unsigned int size = GET_MODE_SIZE (mode);
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (MASK_REGNO_P (regno) && ix86_save_reg (regno, maybe_eh_return))
+      {
+	rtx reg = gen_rtx_REG (mode, regno);
+	rtx mem;
+	rtx_insn *insn;
+
+	mem = choose_baseaddr (cfa_offset);
+	mem = gen_frame_mem (mode, mem);
+	insn = emit_move_insn (reg, mem);
+
+        if (m->fs.cfa_reg == crtl->drap_reg && regno == REGNO (crtl->drap_reg))
+	  {
+	    /* Previously we'd represented the CFA as an expression
+	       like *(%ebp - 8).  We've just popped that value from
+	       the stack, which means we need to reset the CFA to
+	       the drap register.  This will remain until we restore
+	       the stack pointer.  */
+	    add_reg_note (insn, REG_CFA_DEF_CFA, reg);
+	    RTX_FRAME_RELATED_P (insn) = 1;
+
+	    /* This means that the DRAP register is valid for addressing.  */
+	    m->fs.drap_valid = true;
+	  }
+	else
+	  ix86_add_cfa_restore_note (NULL, reg, cfa_offset);
+
+	cfa_offset -= size;
+      }
+}
+
 /* Emit code to restore saved registers using MOV insns.
    First register is restored from CFA - CFA_OFFSET.  */
 static void
@@ -13200,27 +13599,44 @@ ix86_emit_restore_sse_regs_using_mov (HOST_WIDE_INT cfa_offset,
 				      bool maybe_eh_return)
 {
   unsigned int regno;
+  enum machine_mode vector_reg_mode;
+
+  if (cfun->machine->no_caller_saved_registers)
+    {
+      /* We must restore full vector registers if there are no
+	 caller-saved registers.  */
+      if (TARGET_AVX512F)
+	vector_reg_mode = V16SFmode;
+      else if (TARGET_AVX)
+	vector_reg_mode = V8SFmode;
+      else
+	vector_reg_mode = V4SFmode;
+    }
+  else
+    vector_reg_mode = V4SFmode;
 
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if (SSE_REGNO_P (regno) && ix86_save_reg (regno, maybe_eh_return))
       {
-	rtx reg = gen_rtx_REG (V4SFmode, regno);
+	rtx reg = gen_rtx_REG (vector_reg_mode, regno);
 	rtx mem;
 	unsigned int align;
 
 	mem = choose_baseaddr (cfa_offset);
-	mem = gen_rtx_MEM (V4SFmode, mem);
+	mem = gen_rtx_MEM (vector_reg_mode, mem);
 
 	/* The location is aligned up to INCOMING_STACK_BOUNDARY.  */
-	align = MIN (GET_MODE_ALIGNMENT (V4SFmode), INCOMING_STACK_BOUNDARY);
+	align = MIN (GET_MODE_ALIGNMENT (vector_reg_mode),
+		     INCOMING_STACK_BOUNDARY);
 	set_mem_align (mem, align);
 
 	/* SSE saves are not within re-aligned local stack frame.
 	   In case INCOMING_STACK_BOUNDARY is misaligned, we have
 	   to emit unaligned load.  */
-	if (align < 128)
+	if (vector_reg_mode == V4SFmode && align < 128)
 	  {
-	    rtx unspec = gen_rtx_UNSPEC (V4SFmode, gen_rtvec (1, mem),
+	    rtx unspec = gen_rtx_UNSPEC (vector_reg_mode,
+					 gen_rtvec (1, mem),
 					 UNSPEC_LOADU);
 	    emit_insn (gen_rtx_SET (reg, unspec));
 	  }
@@ -13229,7 +13645,7 @@ ix86_emit_restore_sse_regs_using_mov (HOST_WIDE_INT cfa_offset,
 
 	ix86_add_cfa_restore_note (NULL, reg, cfa_offset);
 
-	cfa_offset -= GET_MODE_SIZE (V4SFmode);
+	cfa_offset -= GET_MODE_SIZE (vector_reg_mode);
       }
 }
 
@@ -13328,16 +13744,25 @@ ix86_expand_epilogue (int style)
       if (TARGET_64BIT
 	  && m->fs.sp_offset > 0x7fffffff
 	  && !(m->fs.fp_valid || m->fs.drap_valid)
-	  && (frame.nsseregs + frame.nregs) != 0)
+	  && (frame.nsseregs + frame.nmaskregs + frame.nbndregs
+	      + frame.nregs) != 0)
 	{
 	  pro_epilogue_adjust_stack (stack_pointer_rtx, stack_pointer_rtx,
 				     GEN_INT (m->fs.sp_offset
-					      - frame.sse_reg_save_offset),
+					      - frame.bnd_reg_save_offset),
 				     style,
 				     m->fs.cfa_reg == stack_pointer_rtx);
 	}
     }
 
+  if (frame.nbndregs)
+    ix86_emit_restore_bnd_regs_using_mov (frame.bnd_reg_save_offset,
+					  style == 2);
+
+  if (frame.nmaskregs)
+    ix86_emit_restore_mask_regs_using_mov (frame.mask_reg_save_offset,
+					   style == 2);
+
   /* If there are any SSE registers to restore, then we have to do it
      via moves, since there's obviously no pop for SSE regs.  */
   if (frame.nsseregs)
@@ -13535,7 +13960,20 @@ ix86_expand_epilogue (int style)
       return;
     }
 
-  if (crtl->args.pops_args && crtl->args.size)
+  if (cfun->machine->func_type != TYPE_NORMAL)
+    {
+      /* Return with the "IRET" instruction from interrupt handler.
+	 Pop the 'ERROR_CODE' off the stack before the 'IRET'
+	 instruction in exception handler.  */
+      if (cfun->machine->func_type == TYPE_EXCEPTION)
+	{
+	  rtx r = plus_constant (Pmode, stack_pointer_rtx,
+				 UNITS_PER_WORD);
+	  emit_insn (gen_rtx_SET (stack_pointer_rtx, r));
+	}
+      emit_jump_insn (gen_interrupt_return ());
+    }
+  else if (crtl->args.pops_args && crtl->args.size)
     {
       rtx popc = GEN_INT (crtl->args.pops_args);
 
@@ -18504,6 +18942,14 @@ ix86_expand_move (machine_mode mode, rtx operands[])
   rtx op0, op1;
   enum tls_model model;
 
+  if (cfun->machine->func_type != TYPE_NORMAL && IS_STACK_MODE (mode))
+    {
+      error ("80387 instructions aren't allowed in %s service routine",
+	     (cfun->machine->func_type == TYPE_EXCEPTION
+	      ? "exception" : "interrupt"));
+      return;
+    }
+
   op0 = operands[0];
   op1 = operands[1];
 
@@ -18652,6 +19098,15 @@ ix86_expand_vector_move (machine_mode mode, rtx operands[])
 			? GET_MODE_BITSIZE (mode)
 			: GET_MODE_ALIGNMENT (mode));
 
+  if (cfun->machine->func_type != TYPE_NORMAL
+      && VALID_MMX_REG_MODE (mode))
+    {
+      error ("MMX/3Dnow instructions aren't allowed in %s service routine",
+	     (cfun->machine->func_type == TYPE_EXCEPTION
+	      ? "exception" : "interrupt"));
+      return;
+    }
+
   if (push_operand (op0, VOIDmode))
     op0 = emit_move_resolve_push (mode, op0);
 
@@ -26737,6 +27192,17 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
   rtx vec[3];
   rtx use = NULL, call;
   unsigned int vec_len = 0;
+  tree fndecl;
+
+  if (GET_CODE (XEXP (fnaddr, 0)) == SYMBOL_REF)
+    {
+      fndecl = SYMBOL_REF_DECL (XEXP (fnaddr, 0));
+      if (fndecl
+	  && (lookup_attribute ("interrupt", DECL_ATTRIBUTES (fndecl))))
+	error ("interrupt service routine can't be called directly");
+    }
+  else
+    fndecl = NULL_TREE;
 
   if (pop == const0_rtx)
     pop = NULL;
@@ -26873,8 +27339,29 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
       vec[vec_len++] = pop;
     }
 
-  if (TARGET_64BIT_MS_ABI
-      && (!callarg2 || INTVAL (callarg2) != -2))
+  if (cfun->machine->no_caller_saved_registers
+      && (!fndecl
+	  || !lookup_attribute ("no_caller_saved_registers",
+				DECL_ATTRIBUTES (fndecl))))
+    {
+      static const char ix86_call_used_regs[] = CALL_USED_REGISTERS;
+      bool is_64bit_ms_abi = (TARGET_64BIT
+			      && ix86_function_abi (fndecl) == MS_ABI);
+      char c_mask = CALL_USED_REGISTERS_MASK (is_64bit_ms_abi);
+
+      /* If there are no caller-saved registers, add all registers
+	 that are clobbered by the call.  */
+      for (int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+	if (!fixed_regs[i]
+	    && (ix86_call_used_regs[i] == 1
+		|| (ix86_call_used_regs[i] & c_mask))
+	    && !STACK_REGNO_P (i)
+	    && !MMX_REGNO_P (i))
+	  clobber_reg (&use,
+		       gen_rtx_REG (GET_MODE (regno_reg_rtx[i]), i));
+    }
+  else if (TARGET_64BIT_MS_ABI
+	   && (!callarg2 || INTVAL (callarg2) != -2))
     {
       int const cregs_size
 	= ARRAY_SIZE (x86_64_ms_sysv_extra_clobbered_registers);
@@ -44261,6 +44748,69 @@ ix86_handle_fndecl_attribute (tree *node, tree name, tree, int,
   return NULL_TREE;
 }
 
+static tree
+ix86_handle_no_caller_saved_registers_attribute (tree *node,
+						 tree name, tree, int,
+						 bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+	       name);
+      *no_add_attrs = true;
+    }
+  return NULL_TREE;
+}
+
+static tree
+ix86_handle_interrupt_attribute (tree *node, tree name, tree, int,
+				 bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+	       name);
+      *no_add_attrs = true;
+    }
+
+  /* DECL_RESULT and DECL_ARGUMENTS do not exist there yet,
+     but the function type contains args and return type data.  */
+  tree func_type = TREE_TYPE (*node);
+  tree return_type = TREE_TYPE (func_type);
+
+  int nargs = 0;
+  tree current_arg_type = TYPE_ARG_TYPES (func_type);
+  while (current_arg_type
+	 && ! VOID_TYPE_P (TREE_VALUE (current_arg_type)))
+    {
+      if (nargs == 0)
+	{
+	  if (! POINTER_TYPE_P (TREE_VALUE (current_arg_type)))
+	    error ("interrupt service routine should have a pointer "
+		   "as the first argument");
+	}
+      else if (nargs == 1)
+	{
+	  if (TREE_CODE (TREE_VALUE (current_arg_type)) != INTEGER_TYPE
+	      || TYPE_MODE (TREE_VALUE (current_arg_type)) != word_mode)
+	    error ("interrupt service routine should have unsigned %s"
+		   "int as the second argument",
+		   TARGET_64BIT
+		   ? (TARGET_X32 ? "long long " : "long ")
+		   : "");
+	}
+      nargs++;
+      current_arg_type = TREE_CHAIN (current_arg_type);
+    }
+  if (!nargs || nargs > 2)
+    error ("interrupt service routine can only have a pointer argument "
+	   "and an optional integer argument");
+  if (! VOID_TYPE_P (return_type))
+    error ("interrupt service routine can't have non-void return value");
+
+  return NULL_TREE;
+}
+
 static bool
 ix86_ms_bitfield_layout_p (const_tree record_type)
 {
@@ -48262,6 +48812,11 @@ static const struct attribute_spec ix86_attribute_table[] =
     false },
   { "callee_pop_aggregate_return", 1, 1, false, true, true,
     ix86_handle_callee_pop_aggregate_return, true },
+  { "interrupt", 0, 0, true, false, false,
+    ix86_handle_interrupt_attribute, false },
+  { "no_caller_saved_registers", 0, 0, true, false, false,
+    ix86_handle_no_caller_saved_registers_attribute, false },
+
   /* End element.  */
   { NULL,        0, 0, false, false, false, NULL, false }
 };
@@ -54108,6 +54663,9 @@ ix86_operands_ok_for_move_multiple (rtx *operands, bool load,
 #undef TARGET_ABSOLUTE_BIGGEST_ALIGNMENT
 #define TARGET_ABSOLUTE_BIGGEST_ALIGNMENT 512
 
+#undef TARGET_HARD_REGNO_SCRATCH_OK
+#define TARGET_HARD_REGNO_SCRATCH_OK ix86_hard_regno_scratch_ok
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 #include "gt-i386.h"
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index be96c75..cdaef10 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -1628,11 +1628,18 @@ enum reg_class
 
    If stack probes are required, the space used for large function
    arguments on the stack must also be probed, so enable
-   -maccumulate-outgoing-args so this happens in the prologue.  */
+   -maccumulate-outgoing-args so this happens in the prologue.
+
+   We must use argument accumulation in interrupt function if stack
+   may be realigned to avoid DRAP.  */
 
 #define ACCUMULATE_OUTGOING_ARGS \
-  ((TARGET_ACCUMULATE_OUTGOING_ARGS && optimize_function_for_speed_p (cfun)) \
-   || TARGET_STACK_PROBE || TARGET_64BIT_MS_ABI)
+  ((TARGET_ACCUMULATE_OUTGOING_ARGS \
+    && optimize_function_for_speed_p (cfun)) \
+   || (cfun->machine->func_type != TYPE_NORMAL \
+       && crtl->stack_realign_needed) \
+   || TARGET_STACK_PROBE \
+   || TARGET_64BIT_MS_ABI)
 
 /* If defined, a C expression whose value is nonzero when we want to use PUSH
    instructions to pass outgoing arguments.  */
@@ -1741,6 +1748,11 @@ typedef struct ix86_args {
 
 #define EXIT_IGNORE_STACK 1
 
+/* Define this macro as a C expression that is nonzero for registers
+   used by the epilogue or the `return' pattern.  */
+
+#define EPILOGUE_USES(REGNO) ix86_epilogue_uses (REGNO)
+
 /* Output assembler code for a block containing the constant parts
    of a trampoline, leaving space for the variable parts.  */
 
@@ -2456,6 +2468,18 @@ struct GTY(()) machine_frame_state
 /* Private to winnt.c.  */
 struct seh_frame_state;
 
+enum function_type
+{
+  TYPE_NORMAL = 0,
+  /* The current function is an interrupt service routine with a
+     pointer argument as specified by the "interrupt" attribute.  */
+  TYPE_INTERRUPT,
+  /* The current function is an interrupt service routine with a
+     pointer argument and an integer argument as specified by the
+     "interrupt" attribute.  */
+  TYPE_EXCEPTION
+};
+
 struct GTY(()) machine_function {
   struct stack_local_entry *stack_locals;
   const char *some_ld_name;
@@ -2506,6 +2530,13 @@ struct GTY(()) machine_function {
   /* If true, it is safe to not save/restore DRAP register.  */
   BOOL_BITFIELD no_drap_save_restore : 1;
 
+  /* Function type.  */
+  ENUM_BITFIELD(function_type) func_type : 2;
+
+  /* If true, the current function is a function specified with
+     the "interrupt" or "no_caller_saved_registers" attribute.  */
+  BOOL_BITFIELD no_caller_saved_registers : 1;
+
   /* If true, there is register available for argument passing.  This
      is used only in ix86_function_ok_for_sibcall by 32-bit to determine
      if there is scratch register available for indirect sibcall.  In
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index d0c0d23..810833c 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -193,6 +193,9 @@
   UNSPEC_BNDCU
   UNSPEC_BNDCN
   UNSPEC_MPX_FENCE
+
+  ;; IRET support
+  UNSPEC_INTERRUPT_RETURN
 ])
 
 (define_c_enum "unspecv" [
@@ -12190,6 +12193,12 @@
    (set_attr "modrm" "0")
    (set_attr "maybe_prefix_bnd" "1")])
 
+(define_insn "interrupt_return"
+  [(simple_return)
+   (unspec [(const_int 0)] UNSPEC_INTERRUPT_RETURN)]
+  "reload_completed"
+  "iret")
+
 ;; Used by x86_machine_dependent_reorg to avoid penalty on single byte RET
 ;; instruction Athlon and K8 have.
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 26afc00..0ee7159 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -5113,6 +5113,69 @@ On x86-32 targets, the @code{stdcall} attribute causes the compiler to
 assume that the called function pops off the stack space used to
 pass arguments, unless it takes a variable number of arguments.
 
+@item no_caller_saved_registers
+@cindex @code{no_caller_saved_registers} function attribute, x86
+Use this attribute to indicate that the specified function has no
+caller-saved registers.  That is, all registers are callee-saved.
+The compiler generates proper function entry and exit sequences to
+save and restore any modified registers.
+
+@item interrupt
+@cindex @code{interrupt} function attribute, x86
+Use this attribute to indicate that the specified function is an
+interrupt handler.  The compiler generates function
+entry and exit sequences suitable for use in an interrupt handler when
+this attribute is present.  The @code{IRET} instruction, instead of the
+@code{RET} instruction, is used to return from interrupt handlers.  All
+registers, except for the EFLAGS register which is restored by the
+@code{IRET} instruction, are preserved by the compiler.
+
+Any interruptible-without-stack-switch code must be compiled with
+@option{-mno-red-zone} since interrupt handlers can and will, because
+of the hardware design, touch the red zone.
+
+An interrupt handler must be declared with a mandatory pointer
+argument:
+
+@smallexample
+struct interrupt_frame;
+
+__attribute__ ((interrupt))
+void
+f (struct interrupt_frame *frame)
+@{
+@}
+@end smallexample
+
+and user must properly define the structure the pointer pointing to.
+
+The exception handler is very similar to the interrupt handler with
+a different mandatory function signature:
+
+@smallexample
+#ifdef __x86_64__
+typedef unsigned long long int uword_t;
+#else
+typedef unsigned int uword_t;
+#endif
+
+struct interrupt_frame;
+
+__attribute__ ((interrupt))
+void
+f (struct interrupt_frame *frame, uword_t error_code)
+@{
+  ...
+@}
+@end smallexample
+
+and compiler pops the error code off the stack before the @code{IRET}
+instruction.
+
+The exception handler should only be used for exceptions which push an
+error code and all other exceptions must use the interrupt handler.
+The system will crash if the wrong handler is used.
+
 @item target (@var{options})
 @cindex @code{target} function attribute
 As discussed in @ref{Common Function Attributes}, this attribute 
diff --git a/gcc/testsuite/gcc.dg/guality/pr68037-1.c b/gcc/testsuite/gcc.dg/guality/pr68037-1.c
new file mode 100644
index 0000000..ac3431c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/guality/pr68037-1.c
@@ -0,0 +1,65 @@
+/* { dg-do run { target i?86-*-* x86_64-*-* } } */
+/* { dg-options "-g" } */
+
+extern void exit (int);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+#define ERROR		0x12345670
+#define IP		0x12345671
+#define CS		0x12345672
+#define FLAGS		0x12345673
+#define SP		0x12345674
+#define SS		0x12345675
+
+#define STRING(x)	XSTRING(x)
+#define XSTRING(x)	#x
+
+struct interrupt_frame
+{
+  uword_t ip;
+  uword_t cs;
+  uword_t flags;
+  uword_t sp;
+  uword_t ss;
+};
+
+__attribute__((interrupt, used))
+void
+fn (struct interrupt_frame *frame, uword_t error)
+{
+  if (ERROR != error)		/* BREAK */
+    __builtin_abort ();
+  if (IP != frame->ip)
+    __builtin_abort ();
+  if (CS != frame->cs)
+    __builtin_abort ();
+  if (FLAGS != frame->flags)
+    __builtin_abort ();
+  if (SP != frame->sp)
+    __builtin_abort ();
+  if (SS != frame->ss)
+    __builtin_abort ();
+
+  exit (0);
+}
+
+int
+main ()
+{
+  asm ("push	$" STRING (SS) ";		\
+	push	$" STRING (SP) ";		\
+	push	$" STRING (FLAGS) ";		\
+	push	$" STRING (CS) ";		\
+	push	$" STRING (IP) ";		\
+	push	$" STRING (ERROR) ";		\
+	jmp	fn");
+  return 0;
+}
+
+/* { dg-final { gdb-test 31 "error" "0x12345670" } } */
+/* { dg-final { gdb-test 31 "frame->ip" "0x12345671" } } */
+/* { dg-final { gdb-test 31 "frame->cs" "0x12345672" } } */
+/* { dg-final { gdb-test 31 "frame->flags" "0x12345673" } } */
+/* { dg-final { gdb-test 31 "frame->sp" "0x12345674" } } */
+/* { dg-final { gdb-test 31 "frame->ss" "0x12345675" } } */
diff --git a/gcc/testsuite/gcc.dg/guality/pr68037-2.c b/gcc/testsuite/gcc.dg/guality/pr68037-2.c
new file mode 100644
index 0000000..2010384
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/guality/pr68037-2.c
@@ -0,0 +1,60 @@
+/* { dg-do run { target i?86-*-* x86_64-*-* } } */
+/* { dg-options "-g" } */
+
+extern void exit (int);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+#define IP		0x12345671
+#define CS		0x12345672
+#define FLAGS		0x12345673
+#define SP		0x12345674
+#define SS		0x12345675
+
+#define STRING(x)	XSTRING(x)
+#define XSTRING(x)	#x
+
+struct interrupt_frame
+{
+  uword_t ip;
+  uword_t cs;
+  uword_t flags;
+  uword_t sp;
+  uword_t ss;
+};
+
+__attribute__((interrupt, used))
+void
+fn (struct interrupt_frame *frame)
+{
+  if (IP != frame->ip)		/* BREAK */
+    __builtin_abort ();
+  if (CS != frame->cs)
+    __builtin_abort ();
+  if (FLAGS != frame->flags)
+    __builtin_abort ();
+  if (SP != frame->sp)
+    __builtin_abort ();
+  if (SS != frame->ss)
+    __builtin_abort ();
+
+  exit (0);
+}
+
+int
+main ()
+{
+  asm ("push	$" STRING (SS) ";		\
+	push	$" STRING (SP) ";		\
+	push	$" STRING (FLAGS) ";		\
+	push	$" STRING (CS) ";		\
+	push	$" STRING (IP) ";		\
+	jmp	fn");
+  return 0;
+}
+
+/* { dg-final { gdb-test 30 "frame->ip" "0x12345671" } } */
+/* { dg-final { gdb-test 30 "frame->cs" "0x12345672" } } */
+/* { dg-final { gdb-test 30 "frame->flags" "0x12345673" } } */
+/* { dg-final { gdb-test 30 "frame->sp" "0x12345674" } } */
+/* { dg-final { gdb-test 30 "frame->ss" "0x12345675" } } */
diff --git a/gcc/testsuite/gcc.dg/guality/pr68037-3.c b/gcc/testsuite/gcc.dg/guality/pr68037-3.c
new file mode 100644
index 0000000..10854f5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/guality/pr68037-3.c
@@ -0,0 +1,76 @@
+/* { dg-do run { target i?86-*-* x86_64-*-* } } */
+/* { dg-options "-g" } */
+
+#include <stddef.h>
+
+extern void exit (int);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+typedef int aligned __attribute__((aligned(64)));
+
+#define IP		0x12345671
+#define CS		0x12345672
+#define FLAGS		0x12345673
+#define SP		0x12345674
+#define SS		0x12345675
+
+#define STRING(x)	XSTRING(x)
+#define XSTRING(x)	#x
+
+struct interrupt_frame
+{
+  uword_t ip;
+  uword_t cs;
+  uword_t flags;
+  uword_t sp;
+  uword_t ss;
+};
+
+int
+check_int (int *i, int align)
+{
+  *i = 20;
+  if ((((ptrdiff_t) i) & (align - 1)) != 0)
+    __builtin_abort ();
+  return *i;
+}
+
+__attribute__((interrupt, used))
+void
+fn (struct interrupt_frame *frame)
+{
+  aligned i;
+  if (check_int (&i, __alignof__(i)) != i)
+    __builtin_abort ();
+
+  if (IP != frame->ip)		/* BREAK */
+    __builtin_abort ();
+  if (CS != frame->cs)
+    __builtin_abort ();
+  if (FLAGS != frame->flags)
+    __builtin_abort ();
+  if (SP != frame->sp)
+    __builtin_abort ();
+  if (SS != frame->ss)
+    __builtin_abort ();
+
+  exit (0);
+}
+
+int
+main ()
+{
+  asm ("push	$" STRING (SS) ";		\
+	push	$" STRING (SP) ";		\
+	push	$" STRING (FLAGS) ";		\
+	push	$" STRING (CS) ";		\
+	push	$" STRING (IP) ";		\
+	jmp	fn");
+  return 0;
+}
+
+/* { dg-final { gdb-test 46 "frame->ip" "0x12345671" } } */
+/* { dg-final { gdb-test 46 "frame->cs" "0x12345672" } } */
+/* { dg-final { gdb-test 46 "frame->flags" "0x12345673" } } */
+/* { dg-final { gdb-test 46 "frame->sp" "0x12345674" } } */
+/* { dg-final { gdb-test 46 "frame->ss" "0x12345675" } } */
diff --git a/gcc/testsuite/gcc.dg/torture/pr68037-1.c b/gcc/testsuite/gcc.dg/torture/pr68037-1.c
new file mode 100644
index 0000000..0730c41
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/pr68037-1.c
@@ -0,0 +1,57 @@
+/* { dg-do run { target i?86-*-* x86_64-*-* } } */
+
+extern void exit (int);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+#define ERROR		0x12345670
+#define IP		0x12345671
+#define CS		0x12345672
+#define FLAGS		0x12345673
+#define SP		0x12345674
+#define SS		0x12345675
+
+#define STRING(x)	XSTRING(x)
+#define XSTRING(x)	#x
+
+struct interrupt_frame
+{
+  uword_t ip;
+  uword_t cs;
+  uword_t flags;
+  uword_t sp;
+  uword_t ss;
+};
+
+__attribute__((interrupt, used))
+void
+fn (struct interrupt_frame *frame, uword_t error)
+{
+  if (ERROR != error)
+    __builtin_abort ();
+  if (IP != frame->ip)
+    __builtin_abort ();
+  if (CS != frame->cs)
+    __builtin_abort ();
+  if (FLAGS != frame->flags)
+    __builtin_abort ();
+  if (SP != frame->sp)
+    __builtin_abort ();
+  if (SS != frame->ss)
+    __builtin_abort ();
+
+  exit (0);
+}
+
+int
+main ()
+{
+  asm ("push	$" STRING (SS) ";		\
+	push	$" STRING (SP) ";		\
+	push	$" STRING (FLAGS) ";		\
+	push	$" STRING (CS) ";		\
+	push	$" STRING (IP) ";		\
+	push	$" STRING (ERROR) ";		\
+	jmp	fn");
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/torture/pr68037-2.c b/gcc/testsuite/gcc.dg/torture/pr68037-2.c
new file mode 100644
index 0000000..197e8a4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/pr68037-2.c
@@ -0,0 +1,53 @@
+/* { dg-do run { target i?86-*-* x86_64-*-* } } */
+
+extern void exit (int);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+#define IP		0x12345671
+#define CS		0x12345672
+#define FLAGS		0x12345673
+#define SP		0x12345674
+#define SS		0x12345675
+
+#define STRING(x)	XSTRING(x)
+#define XSTRING(x)	#x
+
+struct interrupt_frame
+{
+  uword_t ip;
+  uword_t cs;
+  uword_t flags;
+  uword_t sp;
+  uword_t ss;
+};
+
+__attribute__((interrupt, used))
+void
+fn (struct interrupt_frame *frame)
+{
+  if (IP != frame->ip)
+    __builtin_abort ();
+  if (CS != frame->cs)
+    __builtin_abort ();
+  if (FLAGS != frame->flags)
+    __builtin_abort ();
+  if (SP != frame->sp)
+    __builtin_abort ();
+  if (SS != frame->ss)
+    __builtin_abort ();
+
+  exit (0);
+}
+
+int
+main ()
+{
+  asm ("push	$" STRING (SS) ";		\
+	push	$" STRING (SP) ";		\
+	push	$" STRING (FLAGS) ";		\
+	push	$" STRING (CS) ";		\
+	push	$" STRING (IP) ";		\
+	jmp	fn");
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/torture/pr68037-3.c b/gcc/testsuite/gcc.dg/torture/pr68037-3.c
new file mode 100644
index 0000000..6bcc317
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/pr68037-3.c
@@ -0,0 +1,69 @@
+/* { dg-do run { target i?86-*-* x86_64-*-* } } */
+
+#include <stddef.h>
+
+extern void exit (int);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+typedef int aligned __attribute__((aligned(64)));
+
+#define IP		0x12345671
+#define CS		0x12345672
+#define FLAGS		0x12345673
+#define SP		0x12345674
+#define SS		0x12345675
+
+#define STRING(x)	XSTRING(x)
+#define XSTRING(x)	#x
+
+struct interrupt_frame
+{
+  uword_t ip;
+  uword_t cs;
+  uword_t flags;
+  uword_t sp;
+  uword_t ss;
+};
+
+int
+check_int (int *i, int align)
+{
+  *i = 20;
+  if ((((ptrdiff_t) i) & (align - 1)) != 0)
+    __builtin_abort ();
+  return *i;
+}
+
+__attribute__((interrupt, used))
+void
+fn (struct interrupt_frame *frame)
+{
+  aligned i;
+  if (check_int (&i, __alignof__(i)) != i)
+    __builtin_abort ();
+
+  if (IP != frame->ip)
+    __builtin_abort ();
+  if (CS != frame->cs)
+    __builtin_abort ();
+  if (FLAGS != frame->flags)
+    __builtin_abort ();
+  if (SP != frame->sp)
+    __builtin_abort ();
+  if (SS != frame->ss)
+    __builtin_abort ();
+
+  exit (0);
+}
+
+int
+main ()
+{
+  asm ("push	$" STRING (SS) ";		\
+	push	$" STRING (SP) ";		\
+	push	$" STRING (FLAGS) ";		\
+	push	$" STRING (CS) ";		\
+	push	$" STRING (IP) ";		\
+	jmp	fn");
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-1.c b/gcc/testsuite/gcc.target/i386/interrupt-1.c
new file mode 100644
index 0000000..70b8f3f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-1.c
@@ -0,0 +1,53 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-push-args -maccumulate-outgoing-args" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern int bar (int);
+
+void foo (void *frame)
+{
+  int a,b,c,d,e,f,i;
+  a = bar (5);
+  b = bar (a);
+  c = bar (b);
+  d = bar (c);
+  e = bar (d);
+  f = bar (e);
+  for (i = 1; i < 10; i++)
+  {
+    a += bar (a + i) + bar (b + i) +
+	 bar (c + i) + bar (d + i) +
+	 bar (e + i) + bar (f + i);
+  }
+}
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)bx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)si" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%edi" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r12" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r13" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r14" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r15" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)bx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)si" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%edi" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r12" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r13" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r14" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r15" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-10.c b/gcc/testsuite/gcc.target/i386/interrupt-10.c
new file mode 100644
index 0000000..64e621c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-10.c
@@ -0,0 +1,24 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mavx512bw -mno-iamcu -maccumulate-outgoing-args" } */
+
+extern void bar (void);
+
+struct interrupt_frame;
+
+void
+ __attribute__ ((interrupt))
+foo (struct interrupt_frame *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\)" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%zmm\[0-9\]+" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%zmm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "kmovq\[\\t \]*%k\[0-7\]+,\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\)" 8 } } */
+/* { dg-final { scan-assembler-times "kmovq\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%k\[0-7\]+" 8 } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-11.c b/gcc/testsuite/gcc.target/i386/interrupt-11.c
new file mode 100644
index 0000000..ba47019
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-11.c
@@ -0,0 +1,37 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-sse -mno-iamcu -maccumulate-outgoing-args" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-12.c b/gcc/testsuite/gcc.target/i386/interrupt-12.c
new file mode 100644
index 0000000..5c9eb74
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-12.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int check_int (int *i, void *, int align);
+typedef int aligned __attribute__((aligned(64)));
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+__attribute__((interrupt))
+void
+foo (void *frame, uword_t error_code)
+{
+  aligned j;
+  if (check_int (frame, &j, __alignof__(j)))
+    __builtin_abort ();
+}
+
+/* { dg-final { scan-assembler-times "and\[lq\]?\[^\\n\]*-64,\[^\\n\]*sp" 1 } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-13.c b/gcc/testsuite/gcc.target/i386/interrupt-13.c
new file mode 100644
index 0000000..8cf0b25
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-13.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int check_int (int *i, void *, int align);
+typedef int aligned __attribute__((aligned(64)));
+
+__attribute__((interrupt))
+void
+foo (void *frame)
+{
+  aligned j;
+  if (check_int (frame, &j, __alignof__(j)))
+    __builtin_abort ();
+}
+
+/* { dg-final { scan-assembler-times "and\[lq\]?\[^\\n\]*-64,\[^\\n\]*sp" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-14.c b/gcc/testsuite/gcc.target/i386/interrupt-14.c
new file mode 100644
index 0000000..751c328
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-14.c
@@ -0,0 +1,39 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-sse -mno-iamcu -mno-sse4 -mno-popcnt -maccumulate-outgoing-args" } */
+
+extern int i, cnt;
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  cnt = __builtin_popcount (i);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "kmov.\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%esi" { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 1 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-15.c b/gcc/testsuite/gcc.target/i386/interrupt-15.c
new file mode 100644
index 0000000..16d82c2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-15.c
@@ -0,0 +1,28 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mpush-args -maccumulate-outgoing-args" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  bar (error);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%rax" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:r|e)bp" 1 } } */
+/* { dg-final { scan-assembler-times "leave" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%eax" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movl\[\\t \]*-4\\(%ebp\\),\[\\t \]*%eax" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movq\[\\t \]*-8\\(%(?:r|e)bp\\),\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 1 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-16.c b/gcc/testsuite/gcc.target/i386/interrupt-16.c
new file mode 100644
index 0000000..f20f117
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-16.c
@@ -0,0 +1,28 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mno-push-args -maccumulate-outgoing-args" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  bar (error);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%rax" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:r|e)bp" 1 } } */
+/* { dg-final { scan-assembler-times "leave" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%eax" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movl\[\\t \]*-4\\(%ebp\\),\[\\t \]*%eax" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movq\[\\t \]*-8\\(%(?:r|e)bp\\),\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 1 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-17.c b/gcc/testsuite/gcc.target/i386/interrupt-17.c
new file mode 100644
index 0000000..4bad7dc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-17.c
@@ -0,0 +1,30 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mpush-args -mno-accumulate-outgoing-args" } */
+
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame)
+{
+  bar (3);
+}
+
+void
+ __attribute__ ((interrupt))
+fn2 (void *frame)
+{
+  bar (3);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[8-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r1\[0-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:r|e)bp" 2 } } */
+/* { dg-final { scan-assembler-times "leave" 2 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movq\[\\t \]*-8\\(%(?:r|e)bp\\),\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 2 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-18.c b/gcc/testsuite/gcc.target/i386/interrupt-18.c
new file mode 100644
index 0000000..af32851
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-18.c
@@ -0,0 +1,35 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mpush-args -maccumulate-outgoing-args" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+extern void bar (int) __attribute__ ((no_caller_saved_registers));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  bar (error);
+}
+
+void
+ __attribute__ ((interrupt))
+fn2 (void *frame, uword_t error)
+{
+  bar (error);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%rax" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:r|e)bp" 2 } } */
+/* { dg-final { scan-assembler-times "leave" 2 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%eax" 2 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movl\[\\t \]*-4\\(%ebp\\),\[\\t \]*%eax" 2 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movq\[\\t \]*-8\\(%(?:r|e)bp\\),\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 2 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "iret" 2 } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-19.c b/gcc/testsuite/gcc.target/i386/interrupt-19.c
new file mode 100644
index 0000000..e1aaaec
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-19.c
@@ -0,0 +1,21 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mno-push-args -maccumulate-outgoing-args" } */
+
+extern int foo (int) __attribute__ ((no_caller_saved_registers));
+extern int bar (int) __attribute__ ((no_caller_saved_registers));
+
+int
+foo (int i)
+{
+  return bar (i);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]-*\[0-9\]*\\(%\[re\]?bp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-2.c b/gcc/testsuite/gcc.target/i386/interrupt-2.c
new file mode 100644
index 0000000..27b40b0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-2.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wall -g" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+}
+
+/* { dg-final { scan-assembler-not "add(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-20.c b/gcc/testsuite/gcc.target/i386/interrupt-20.c
new file mode 100644
index 0000000..2a2210d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-20.c
@@ -0,0 +1,23 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -mno-push-args" } */
+
+extern int foo (int) __attribute__ ((no_caller_saved_registers));
+extern int bar (int) __attribute__ ((no_caller_saved_registers));
+
+int
+foo (int i)
+{
+  return bar (i + 1);
+}
+
+/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]-*\[0-9\]*\\(%\[re\]?bp\\)" } } */
+/* { dg-final { scan-assembler-not "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%rdx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "jmp" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-21.c b/gcc/testsuite/gcc.target/i386/interrupt-21.c
new file mode 100644
index 0000000..7cc5181
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-21.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx -mno-iamcu -msse" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movaps\[\\t \]*%xmm3,\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movaps\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%xmm3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-2\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-2\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[4-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[4-9\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm1\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm1\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-22.c b/gcc/testsuite/gcc.target/i386/interrupt-22.c
new file mode 100644
index 0000000..5dd6290
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-22.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm3,\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-2\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-2\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[4-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[4-9\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm1\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm1\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-23.c b/gcc/testsuite/gcc.target/i386/interrupt-23.c
new file mode 100644
index 0000000..e0cf550
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-23.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mavx512f -mno-iamcu" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm3,\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-2\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-2\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[4-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[4-9\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm1\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm1\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-24.c b/gcc/testsuite/gcc.target/i386/interrupt-24.c
new file mode 100644
index 0000000..9b92c00
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-24.c
@@ -0,0 +1,21 @@
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mno-iamcu -mmpx" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "bnd3");
+}
+
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*%bnd3,\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%bnd3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */
+/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-25.c b/gcc/testsuite/gcc.target/i386/interrupt-25.c
new file mode 100644
index 0000000..849fd3a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-25.c
@@ -0,0 +1,24 @@
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -mno-avx -mno-iamcu -msse -mincoming-stack-boundary=2" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%xmm3,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%xmm3" 1 } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[0-2\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[0-2\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm\[4-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm\[4-9\]+" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*%(x|y|z)mm1\[0-9\]+,\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(a|u)ps\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%(x|y|z)mm1\[0-9\]+" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%e(a|b|c|d)x" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%e(s|d)i" } } */
+/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%ebp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-26.c b/gcc/testsuite/gcc.target/i386/interrupt-26.c
new file mode 100644
index 0000000..27b40b0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-26.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wall -g" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+}
+
+/* { dg-final { scan-assembler-not "add(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-27.c b/gcc/testsuite/gcc.target/i386/interrupt-27.c
new file mode 100644
index 0000000..774bb4b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-27.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O0 -g -mno-avx -mno-iamcu -msse" } */
+
+typedef float __v4sf __attribute__ ((__vector_size__ (16)));
+__v4sf x, y;
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  x = y;
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%xmm0,\[\\t \]*-?\[0-9\]*\\(%\[re\]?bp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-?\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%xmm0" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-28.c b/gcc/testsuite/gcc.target/i386/interrupt-28.c
new file mode 100644
index 0000000..0768a46
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-28.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -mno-iamcu" } */
+
+struct ret
+{
+  int i[8];
+};
+
+extern struct ret bar (void);
+
+void
+ __attribute__ ((interrupt))
+fn (void *frame)
+{
+  bar ();
+} /* { dg-message "sorry, unimplemented: Dynamic Realign Argument Pointer" } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-3.c b/gcc/testsuite/gcc.target/i386/interrupt-3.c
new file mode 100644
index 0000000..b09f56c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-3.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -g" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+void
+__attribute__((interrupt))
+fn (void* frame, uword_t error)
+{
+}
+
+/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-387-err.c b/gcc/testsuite/gcc.target/i386/interrupt-387-err.c
new file mode 100644
index 0000000..36094f2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-387-err.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -m80387 -mlong-double-80 -mno-iamcu -mno-sse" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+extern long double y, x;
+
+void
+fn0 (void)
+{
+  x = y;
+  y = 0;
+}
+
+void
+__attribute__((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  x = 0; /* { dg-error "80387 instructions aren't allowed in exception service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn2 (void *frame)
+{
+  x = y; /* { dg-error "80387 instructions aren't allowed in interrupt service routine" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-4.c b/gcc/testsuite/gcc.target/i386/interrupt-4.c
new file mode 100644
index 0000000..c34dbe0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-4.c
@@ -0,0 +1,32 @@
+/* { dg-do link } */
+/* { dg-options "-O -g" } */
+
+#include <stdint.h>
+
+extern void link_error (void);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+struct interrupt_frame
+{
+  uword_t ip;
+  uword_t cs;
+  uword_t flags;
+  uword_t sp;
+  uword_t ss;
+};
+
+__attribute__ ((used, interrupt))
+void
+foo (struct interrupt_frame *frame)
+{
+  void *ra = __builtin_return_address (0);
+  if ((uintptr_t) ra != (uintptr_t) frame->ip)
+    link_error ();
+}
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-5.c b/gcc/testsuite/gcc.target/i386/interrupt-5.c
new file mode 100644
index 0000000..3c4bc2d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-5.c
@@ -0,0 +1,23 @@
+/* { dg-do link } */
+/* { dg-options "-O -g" } */
+
+#include <stdint.h>
+
+extern void link_error (void);
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+__attribute__ ((used, interrupt))
+void
+foo (void *frame, uword_t error)
+{
+  void *ra = __builtin_return_address (0);
+  if ((uintptr_t) ra != (uintptr_t) error)
+    link_error ();
+}
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-6.c b/gcc/testsuite/gcc.target/i386/interrupt-6.c
new file mode 100644
index 0000000..a1a3d0e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-6.c
@@ -0,0 +1,40 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+extern int error;
+
+__attribute__((interrupt))
+void
+fn1 (void *p, short error_code)
+{ /* { dg-error "interrupt service routine should have unsigned \(long long |long |\)int as the second argument" } */
+}
+
+__attribute__((interrupt))
+void
+fn2 (void)
+{ /* { dg-error "interrupt service routine can only have a pointer argument and an optional integer argument" } */
+}
+
+__attribute__((interrupt))
+void
+fn3 (uword_t error_code)
+{ /* { dg-error "interrupt service routine should have a pointer as the first argument" } */
+  error = error_code;
+}
+
+__attribute__((interrupt))
+void
+fn4 (uword_t error_code, void *frame)
+{ /* { dg-error "interrupt service routine should have .* the .* argument" } */
+  error = error_code;
+}
+
+extern int fn5 (void *) __attribute__ ((interrupt)); /* { dg-error "interrupt service routine can't have non-void return value" } */
+
+int
+fn5 (void *frame)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-7.c b/gcc/testsuite/gcc.target/i386/interrupt-7.c
new file mode 100644
index 0000000..e7441a2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-7.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int error;
+
+extern void fn (void *) __attribute__((interrupt));
+
+void
+foo (void)
+{
+  fn (&error); /* { dg-error "interrupt service routine can't be called directly" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-8.c b/gcc/testsuite/gcc.target/i386/interrupt-8.c
new file mode 100644
index 0000000..defec2e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-8.c
@@ -0,0 +1,20 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -maccumulate-outgoing-args" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm\[0-9\]+,\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\)" 16 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%ymm\[0-9\]+" 16 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%ymm\[0-9\]+,\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%ymm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-9.c b/gcc/testsuite/gcc.target/i386/interrupt-9.c
new file mode 100644
index 0000000..56bd8dd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-9.c
@@ -0,0 +1,22 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512bw -mno-iamcu -mavx512f -maccumulate-outgoing-args" } */
+
+extern void bar (void);
+
+void
+ __attribute__ ((interrupt))
+foo (void *frame)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]-*\[0-9\]*\\(%\[re\]?bp\\)" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%zmm\[0-9\]+" 32 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%zmm\[0-9\]+,\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\)" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%zmm\[0-9\]+" 8 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "kmovw\[\\t \]*%k\[0-7\]+,\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\)" 8 } } */
+/* { dg-final { scan-assembler-times "kmovw\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%k\[0-7\]+" 8 } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-bnd.c b/gcc/testsuite/gcc.target/i386/interrupt-bnd.c
new file mode 100644
index 0000000..9f50661
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-bnd.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mno-iamcu -mmpx" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "bnd3");
+}
+
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*%bnd3,\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "bndmov\[\\t \]*\[\\-\]?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%bnd3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
new file mode 100644
index 0000000..712b85f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
@@ -0,0 +1,35 @@
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -miamcu -maccumulate-outgoing-args" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern int bar (int);
+
+void foo (void *frame)
+{
+  int a,b,c,d,e,f,i;
+  a = bar (5);
+  b = bar (a);
+  c = bar (b);
+  d = bar (c);
+  e = bar (d);
+  f = bar (e);
+  for (i = 1; i < 10; i++)
+    a += bar (a + i) + bar (b + i) +
+	 bar (c + i) + bar (d + i) +
+	 bar (e + i) + bar (f+i);
+}
+
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%eax" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%ebx" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%ecx" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%edx" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%edi" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%esi" 1 } } */
+/* { dg-final { scan-assembler-times "pushl\[\\t \]*%ebp" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%eax" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%ecx" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%edx" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%edi" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%esi" 1 } } */
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%ebp" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c
new file mode 100644
index 0000000..fc4f367
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mmmx -mno-iamcu" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+typedef short __v4hi __attribute__ ((__vector_size__ (8)));
+typedef int __m64 __attribute__ ((__vector_size__ (8), __may_alias__));
+
+extern __m64 y, x;
+
+void
+fn0 (void)
+{
+  x = __extension__ (__m64){ 0 };
+  x = (__m64) __builtin_ia32_packsswb ((__v4hi) x, (__v4hi) y);
+  y = x;
+}
+
+void
+__attribute__((interrupt))
+fn1 (void *frame)
+{
+  x = (__m64) __builtin_ia32_packsswb ((__v4hi) x, (__v4hi) y); /* { dg-error "MMX/3Dnow (instructions|intrinsics) aren't allowed in interrupt service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn2 (void *frame)
+{
+  x = y; /* { dg-error "MMX/3Dnow instructions aren't allowed in interrupt service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn3 (void *frame, uword_t error)
+{
+  x = __extension__ (__m64){ 0 }; /* { dg-error "MMX/3Dnow instructions aren't allowed in exception service routine" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c b/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c
new file mode 100644
index 0000000..78d3f27
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c
@@ -0,0 +1,31 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mred-zone" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  /* No need to adjust stack if less than 128 bytes are used on stack
+     with a 128-byte red zone.  */
+  long long int i0;
+  long long int i1;
+  long long int i2;
+  long long int i3;
+  long long int i4;
+  long long int i5;
+  long long int i6;
+  long long int i7;
+  long long int i8;
+  long long int i9;
+  long long int i10;
+  long long int i11;
+  long long int i12;
+  long long int i13;
+  asm ("# %0, %1, %2, %3, %4, %5, %6, %7"
+       : "=m" (i0), "=m" (i1), "=m" (i2), "=m" (i3),
+         "=m" (i4), "=m" (i5), "=m" (i6), "=m" (i7),
+         "=m" (i8), "=m" (i9), "=m" (i10), "=m" (i11),
+	 "=m" (i12), "=m" (i13));
+}
+
+/* { dg-final { scan-assembler-not "(sub|add)(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c b/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c
new file mode 100644
index 0000000..1ab7041
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c
@@ -0,0 +1,32 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mred-zone" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  /* Need to adjust stack if more than 128 bytes are used on stack
+     with a 128-byte red zone.  */
+  long long int i0;
+  long long int i1;
+  long long int i2;
+  long long int i3;
+  long long int i4;
+  long long int i5;
+  long long int i6;
+  long long int i7;
+  long long int i8;
+  long long int i9;
+  long long int i10;
+  long long int i11;
+  long long int i12;
+  long long int i13;
+  char c;
+  asm ("# %0, %1, %2, %3, %4, %5, %6, %7"
+       : "=m" (i0), "=m" (i1), "=m" (i2), "=m" (i3),
+         "=m" (i4), "=m" (i5), "=m" (i6), "=m" (i7),
+         "=m" (i8), "=m" (i9), "=m" (i10), "=m" (i11),
+	 "=m" (i12), "=m" (i13), "=m" (c));
+}
+
+/* { dg-final { scan-assembler-times "(?:sub|add)(?:l|q)\[\\t \]*\\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c b/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c
new file mode 100644
index 0000000..e222acf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-sibcall.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O3" } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern void bar (void);
+
+void foo (void *frame)
+{
+  bar ();
+}
+/* { dg-final { scan-assembler-not "jmp" } }*/
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c
new file mode 100644
index 0000000..b1b0abe
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern void bar (int);
+
+void f1 (void){  bar (1); }
+__attribute__((interrupt))
+void f2 (void *frame){  bar (2); }
+void f3 (void){  bar (3); }
+__attribute__((interrupt))
+void f4 (void *frame){  bar (4); }
+void f5 (void){  bar (5); }
+
+/* { dg-final { scan-assembler-times "push.\t%.ax" 2 } } */
+/* { dg-final { scan-assembler-times "pop.\t%.ax" 2 } } */
+/* { dg-final { scan-assembler-times "iret" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-xmm.c b/gcc/testsuite/gcc.target/i386/interrupt-xmm.c
new file mode 100644
index 0000000..6ffdd96
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-xmm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx -mno-iamcu -msse" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "movups\[\\t \]*%xmm3,\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "movups\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%xmm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-ymm.c b/gcc/testsuite/gcc.target/i386/interrupt-ymm.c
new file mode 100644
index 0000000..0754ae0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-ymm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*%ymm3,\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%ymm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-zmm.c b/gcc/testsuite/gcc.target/i386/interrupt-zmm.c
new file mode 100644
index 0000000..f4d2fa1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-zmm.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mavx512f -mno-iamcu" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+  asm ("#"
+       :
+       :
+       : "xmm3");
+}
+
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*%zmm3,\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "vmovups\[\\t \]*-?\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%zmm3" 1 } } */
+/* { dg-final { scan-assembler "iret" } }*/

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-11-06 14:08                                                           ` Yulia Koval
@ 2015-11-06 15:31                                                             ` Uros Bizjak
  2015-11-06 22:19                                                               ` H.J. Lu
  0 siblings, 1 reply; 45+ messages in thread
From: Uros Bizjak @ 2015-11-06 15:31 UTC (permalink / raw)
  To: Yulia Koval; +Cc: GCC Patches, H.J. Lu, Mike Stump

On Fri, Nov 6, 2015 at 3:07 PM, Yulia Koval <vaalfreja@gmail.com> wrote:
> Hi,
>
> I updated and reposted the patch. Regtested/bootstraped on
> x86_64/Linux and i686/Linux. Ok for trunk?

This version still emits insns from ix86_function_arg, so NAK.

Uros.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-11-06 15:31                                                             ` Uros Bizjak
@ 2015-11-06 22:19                                                               ` H.J. Lu
  2015-11-07  4:50                                                                 ` Jeff Law
  0 siblings, 1 reply; 45+ messages in thread
From: H.J. Lu @ 2015-11-06 22:19 UTC (permalink / raw)
  To: Uros Bizjak; +Cc: Yulia Koval, GCC Patches, Mike Stump

On Fri, Nov 6, 2015 at 7:31 AM, Uros Bizjak <ubizjak@gmail.com> wrote:
> On Fri, Nov 6, 2015 at 3:07 PM, Yulia Koval <vaalfreja@gmail.com> wrote:
>> Hi,
>>
>> I updated and reposted the patch. Regtested/bootstraped on
>> x86_64/Linux and i686/Linux. Ok for trunk?
>
> This version still emits insns from ix86_function_arg, so NAK.
>
> Uros.

Hi Uros,

The ix86_function_arg change only applies the interrupt function body,
which has fixed parameters and won't be called by any functions directly.
We have verified that this approach works with all optimization levels.
This approach has no impact on normal functions. I don't think we should
change the middle-end for x86 interrupt functions which are never called
by GCC nor follows the normal psABI. We will address any issues which
may come up later.

If this isn't the right approach, do you have any suggestions?

Thanks.


H.J.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] x86 interrupt attribute
  2015-11-06 22:19                                                               ` H.J. Lu
@ 2015-11-07  4:50                                                                 ` Jeff Law
  0 siblings, 0 replies; 45+ messages in thread
From: Jeff Law @ 2015-11-07  4:50 UTC (permalink / raw)
  To: H.J. Lu, Uros Bizjak; +Cc: Yulia Koval, GCC Patches, Mike Stump

On 11/06/2015 03:19 PM, H.J. Lu wrote:
> On Fri, Nov 6, 2015 at 7:31 AM, Uros Bizjak <ubizjak@gmail.com> wrote:
>> On Fri, Nov 6, 2015 at 3:07 PM, Yulia Koval <vaalfreja@gmail.com> wrote:
>>> Hi,
>>>
>>> I updated and reposted the patch. Regtested/bootstraped on
>>> x86_64/Linux and i686/Linux. Ok for trunk?
>>
>> This version still emits insns from ix86_function_arg, so NAK.
>>
>> Uros.
>
> Hi Uros,
>
> The ix86_function_arg change only applies the interrupt function body,
> which has fixed parameters and won't be called by any functions directly.
Uros was pretty clear that generating code from within ix86_function_arg 
was a NAK from his side (and I'd agree with that assessment from Uros). 
  Generating code like that from within FUNCTION_ARG is pretty bad.

Can you arrange to load up the arguments from within the prologue so 
that they look like they were passed in?  Alternately you have to make 
FUNCTION_ARG return 0 for those cases and arrange to define all the 
other argument passing offsets appropriately so those arguments can be 
found in the stack.

Jeff

^ permalink raw reply	[flat|nested] 45+ messages in thread

end of thread, other threads:[~2015-11-07  4:50 UTC | newest]

Thread overview: 45+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-09-29 14:21 [PATCH] x86 interrupt attribute Yulia Koval
2015-09-29 19:49 ` Mike Stump
2015-09-29 20:30   ` H.J. Lu
2015-09-29 21:49     ` H.J. Lu
2015-09-29 22:10       ` Mike Stump
2015-09-29 22:11         ` Mike Stump
2015-09-29 22:13         ` H.J. Lu
2015-09-30  0:47           ` Mike Stump
2015-09-30  1:34             ` H.J. Lu
2015-09-30  5:50               ` H.J. Lu
2015-09-30 12:41                 ` Yulia Koval
2015-09-30 13:02                   ` H.J. Lu
2015-09-30 18:48                     ` Yulia Koval
2015-09-30 19:08                   ` H.J. Lu
2015-09-30 21:31                     ` Yulia Koval
2015-09-30 21:39                       ` H.J. Lu
2015-10-01  0:24                       ` H.J. Lu
2015-10-01 12:34                         ` Yulia Koval
2015-10-01 15:59                         ` Uros Bizjak
2015-10-01 16:08                           ` H.J. Lu
2015-10-01 16:18                             ` Uros Bizjak
2015-10-02 12:51                               ` Yulia Koval
2015-10-02 15:45                                 ` Uros Bizjak
2015-10-02 17:48                                   ` Yulia Koval
2015-10-04  5:24                                     ` Yulia Koval
2015-10-04 10:29                                       ` Uros Bizjak
2015-10-04 18:15                                         ` H.J. Lu
2015-10-04 20:01                                           ` Uros Bizjak
2015-10-04 20:51                                             ` H.J. Lu
2015-10-04 21:07                                               ` Uros Bizjak
2015-10-04 23:17                                                 ` H.J. Lu
2015-10-05  9:29                                                   ` Uros Bizjak
2015-10-13 12:18                                                     ` Yulia Koval
2015-10-20 13:25                                                       ` Yulia Koval
2015-10-23 13:13                                                         ` Yulia Koval
2015-11-06 14:08                                                           ` Yulia Koval
2015-11-06 15:31                                                             ` Uros Bizjak
2015-11-06 22:19                                                               ` H.J. Lu
2015-11-07  4:50                                                                 ` Jeff Law
2015-10-05  9:00                                           ` Mike Stump
2015-09-29 22:10     ` Mike Stump
2015-09-29 23:08       ` H.J. Lu
2015-09-29 23:54         ` H.J. Lu
2015-09-30  2:45           ` Mike Stump
2015-09-30  5:21             ` H.J. Lu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).