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

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).