summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/config/i386/i386-protos.h2
-rw-r--r--gcc/config/i386/i386.c355
-rw-r--r--gcc/config/i386/i386.h38
-rw-r--r--gcc/config/i386/i386.md11
-rw-r--r--gcc/doc/extend.texi77
-rw-r--r--gcc/testsuite/gcc.dg/guality/pr68037-1.c65
-rw-r--r--gcc/testsuite/gcc.dg/guality/pr68037-2.c60
-rw-r--r--gcc/testsuite/gcc.dg/guality/pr68037-3.c76
-rw-r--r--gcc/testsuite/gcc.dg/torture/pr68037-1.c58
-rw-r--r--gcc/testsuite/gcc.dg/torture/pr68037-2.c54
-rw-r--r--gcc/testsuite/gcc.dg/torture/pr68037-3.c70
-rw-r--r--gcc/testsuite/gcc.dg/torture/pr68661-1a.c18
-rw-r--r--gcc/testsuite/gcc.dg/torture/pr68661-1b.c45
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-1.c55
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-10.c19
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-11.c41
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-12.c30
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-13.c30
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-14.c32
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-15.c37
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-16.c21
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-17.c22
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-18.c13
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-19.c16
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-2.c20
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-20.c29
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-21.c30
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-22.c29
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-23.c46
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-24.c19
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-25.c54
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-26.c16
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-27.c15
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-28.c12
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-3.c16
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-387-err-1.c16
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-387-err-2.c8
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-4.c32
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-5.c23
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-6.c40
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-7.c12
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-8.c38
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-9.c22
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-bnd-err-1.c16
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-bnd-err-2.c8
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-iamcu.c36
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-mmx-err-1.c16
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-mmx-err-2.c8
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c32
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c33
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-sibcall-1.c14
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-sibcall-2.c15
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c18
53 files changed, 1903 insertions, 15 deletions
diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index ff47bc15600..01599015040 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -285,6 +285,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);
+
struct ix86_address
{
rtx base, index, disp;
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 3d044e8bd68..2147f363164 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -5625,6 +5625,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)
{
@@ -6397,6 +6406,40 @@ ix86_reset_previous_fndecl (void)
ix86_previous_fndecl = NULL_TREE;
}
+/* Set the func_type field from the function FNDECL. */
+
+static void
+ix86_set_func_type (tree fndecl)
+{
+ if (cfun->machine->func_type == TYPE_UNKNOWN)
+ {
+ if (lookup_attribute ("interrupt",
+ TYPE_ATTRIBUTES (TREE_TYPE (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;
+
+ /* Only dwarf2out.c can handle -WORD(AP) as a pointer argument. */
+ if (write_symbols != NO_DEBUG && write_symbols != DWARF2_DEBUG)
+ sorry ("Only DWARF debug format is supported for interrupt "
+ "service routine.");
+ }
+ else
+ {
+ cfun->machine->func_type = TYPE_NORMAL;
+ if (lookup_attribute ("no_caller_saved_registers",
+ TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))
+ cfun->machine->no_caller_saved_registers = true;
+ }
+ }
+}
+
/* Establish appropriate back-end context for processing the function
FNDECL. The argument might be NULL to indicate processing at top
level, outside of any function scope. */
@@ -6407,7 +6450,14 @@ ix86_set_current_function (tree fndecl)
several times in the course of compiling a function, and we don't want to
slow things down too much or call target_reinit when it isn't safe. */
if (fndecl == ix86_previous_fndecl)
- return;
+ {
+ /* There may be 2 function bodies for the same function FNDECL,
+ one is extern inline and one isn't. Call ix86_set_func_type
+ to set the func_type field. */
+ if (fndecl != NULL_TREE)
+ ix86_set_func_type (fndecl);
+ return;
+ }
tree old_tree;
if (ix86_previous_fndecl == NULL_TREE)
@@ -6424,6 +6474,8 @@ ix86_set_current_function (tree fndecl)
return;
}
+ ix86_set_func_type (fndecl);
+
tree new_tree = DECL_FUNCTION_SPECIFIC_TARGET (fndecl);
if (new_tree == NULL_TREE)
new_tree = target_option_default_node;
@@ -6440,6 +6492,8 @@ ix86_set_current_function (tree fndecl)
}
ix86_previous_fndecl = fndecl;
+ static bool prev_no_caller_saved_registers;
+
/* 64-bit MS and SYSV ABI have different set of call used registers.
Avoid expensive re-initialization of init_regs each time we switch
function context. */
@@ -6447,6 +6501,45 @@ ix86_set_current_function (tree fndecl)
&& (call_used_regs[SI_REG]
== (cfun->machine->call_abi == MS_ABI)))
reinit_regs ();
+ /* Need to re-initialize init_regs if caller-saved registers are
+ changed. */
+ else if (prev_no_caller_saved_registers
+ != cfun->machine->no_caller_saved_registers)
+ reinit_regs ();
+
+ if (cfun->machine->func_type != TYPE_NORMAL
+ || cfun->machine->no_caller_saved_registers)
+ {
+ /* Don't allow MPX, SSE, MMX nor x87 instructions since they
+ may change processor state. */
+ const char *isa;
+ if (TARGET_MPX)
+ isa = "MPX";
+ else if (TARGET_SSE)
+ isa = "SSE";
+ else if (TARGET_MMX)
+ isa = "MMX/3Dnow";
+ else if (TARGET_80387)
+ isa = "80387";
+ else
+ isa = NULL;
+ if (isa != NULL)
+ {
+ if (cfun->machine->func_type != TYPE_NORMAL)
+ sorry ("%s instructions aren't allowed in %s service routine",
+ isa, (cfun->machine->func_type == TYPE_EXCEPTION
+ ? "exception" : "interrupt"));
+ else
+ sorry ("%s instructions aren't allowed in function with "
+ "no_caller_saved_registers attribute", isa);
+ /* Don't issue the same error twice. */
+ cfun->machine->func_type = TYPE_NORMAL;
+ cfun->machine->no_caller_saved_registers = false;
+ }
+ }
+
+ prev_no_caller_saved_registers
+ = cfun->machine->no_caller_saved_registers;
}
@@ -6711,6 +6804,11 @@ ix86_function_ok_for_sibcall (tree decl, tree exp)
rtx a, b;
bool bind_global = decl && !targetm.binds_local_p (decl);
+ /* Sibling call isn't OK if there are no caller-saved registers
+ since all registers must be preserved before return. */
+ 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.) */
@@ -7891,6 +7989,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)
{
@@ -8869,6 +8969,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
@@ -9185,6 +9290,36 @@ 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)
+ {
+ gcc_assert (type != NULL_TREE);
+ if (POINTER_TYPE_P (type))
+ {
+ /* This is the pointer argument. */
+ gcc_assert (TYPE_MODE (type) == Pmode);
+ if (cfun->machine->func_type == TYPE_INTERRUPT)
+ /* -WORD(AP) in the current frame in interrupt handler. */
+ arg = plus_constant (Pmode, arg_pointer_rtx,
+ -UNITS_PER_WORD);
+ else
+ /* (AP) in the current frame in exception handler. */
+ arg = arg_pointer_rtx;
+ }
+ else
+ {
+ gcc_assert (cfun->machine->func_type == TYPE_EXCEPTION
+ && TREE_CODE (type) == INTEGER_TYPE
+ && TYPE_MODE (type) == word_mode);
+ /* The integer argument is the error code 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))
@@ -9746,14 +9881,16 @@ ix86_function_value_bounds (const_tree valtype,
}
/* Pointer function arguments and return values are promoted to
- word_mode. */
+ word_mode for normal functions. */
static machine_mode
ix86_promote_function_mode (const_tree type, machine_mode mode,
int *punsignedp, const_tree fntype,
int for_return)
{
- if (type != NULL_TREE && POINTER_TYPE_P (type))
+ if (cfun->machine->func_type == TYPE_NORMAL
+ && type != NULL_TREE
+ && POINTER_TYPE_P (type))
{
*punsignedp = POINTERS_EXTEND_UNSIGNED;
return word_mode;
@@ -10914,7 +11051,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
@@ -11229,11 +11369,77 @@ 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)
+{
+ /* 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)));
+}
+
/* 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)
+ {
+ /* Don't preserve registers used for function return value. */
+ rtx reg = crtl->return_rtx;
+ if (reg)
+ {
+ unsigned int i = REGNO (reg);
+ unsigned int nregs = hard_regno_nregs[i][GET_MODE (reg)];
+ while (nregs-- > 0)
+ if ((i + nregs) == regno)
+ return false;
+
+ reg = crtl->return_bnd;
+ if (reg)
+ {
+ i = REGNO (reg);
+ nregs = hard_regno_nregs[i][GET_MODE (reg)];
+ while (nregs-- > 0)
+ if ((i + nregs) == regno)
+ return false;
+ }
+ }
+
+ return (df_regs_ever_live_p (regno)
+ && !fixed_regs[regno]
+ && !STACK_REGNO_P (regno)
+ && !MMX_REGNO_P (regno)
+ && (regno != HARD_FRAME_POINTER_REGNUM
+ || !frame_pointer_needed));
+ }
+
if (regno == REAL_PIC_OFFSET_TABLE_REGNUM
&& pic_offset_table_rtx)
{
@@ -11956,13 +12162,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;
@@ -11973,7 +12183,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
@@ -12014,8 +12226,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
@@ -12776,6 +12992,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)])
{
@@ -13151,8 +13373,14 @@ ix86_expand_prologue (void)
if (frame_pointer_needed && frame.red_zone_size)
emit_insn (gen_memory_blockage ());
- /* Emit cld instruction if stringops are used in the function. */
- if (TARGET_CLD && ix86_current_function_needs_cld)
+ /* Emit cld instruction if stringops are used in the function. Since
+ we can't assume the direction flag in interrupt handler, we must
+ emit cld instruction if stringops are used in interrupt handler or
+ interrupt handler isn't a leaf function. */
+ if ((TARGET_CLD && ix86_current_function_needs_cld)
+ || (!TARGET_CLD
+ && cfun->machine->func_type != TYPE_NORMAL
+ && (ix86_current_function_needs_cld || !crtl->is_leaf)))
emit_insn (gen_cld ());
/* SEH requires that the prologue end within 256 bytes of the start of
@@ -13646,7 +13874,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);
@@ -27123,6 +27364,18 @@ 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",
+ TYPE_ATTRIBUTES (TREE_TYPE (fndecl)))))
+ error ("interrupt service routine can't be called directly");
+ }
+ else
+ fndecl = NULL_TREE;
if (pop == const0_rtx)
pop = NULL;
@@ -27259,8 +27512,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
+ || (!TREE_THIS_VOLATILE (fndecl)
+ && !lookup_attribute ("no_caller_saved_registers",
+ TYPE_ATTRIBUTES (TREE_TYPE (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 which returns. */
+ 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);
@@ -44559,6 +44834,54 @@ ix86_handle_fndecl_attribute (tree *node, tree name, tree, int,
return NULL_TREE;
}
+static tree
+ix86_handle_no_caller_saved_registers_attribute (tree *, tree, tree,
+ int, bool *)
+{
+ return NULL_TREE;
+}
+
+static tree
+ix86_handle_interrupt_attribute (tree *node, tree, tree, int, bool *)
+{
+ /* DECL_RESULT and DECL_ARGUMENTS do not exist there yet,
+ but the function type contains args and return type data. */
+ tree func_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)
{
@@ -48774,6 +49097,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, false, true, true,
+ ix86_handle_interrupt_attribute, false },
+ { "no_caller_saved_registers", 0, 0, false, true, true,
+ ix86_handle_no_caller_saved_registers_attribute, false },
+
/* End element. */
{ NULL, 0, 0, false, false, false, NULL, false }
};
@@ -54832,6 +55160,9 @@ ix86_addr_space_zero_address_valid (addr_space_t as)
#undef TARGET_OPTAB_SUPPORTED_P
#define TARGET_OPTAB_SUPPORTED_P ix86_optab_supported_p
+#undef TARGET_HARD_REGNO_SCRATCH_OK
+#define TARGET_HARD_REGNO_SCRATCH_OK ix86_hard_regno_scratch_ok
+
struct gcc_target targetm = TARGET_INITIALIZER;
#include "gt-i386.h"
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index d0b418b0fd9..ed9394db39d 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -1634,11 +1634,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 \
|| (TARGET_MACHO && crtl->profile))
/* If defined, a C expression whose value is nonzero when we want to use PUSH
@@ -1748,6 +1755,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. */
@@ -2463,6 +2475,19 @@ struct GTY(()) machine_frame_state
/* Private to winnt.c. */
struct seh_frame_state;
+enum function_type
+{
+ TYPE_UNKNOWN = 0,
+ TYPE_NORMAL,
+ /* 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;
@@ -2517,6 +2542,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 cf29e5d39d5..5b1a813e17e 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" [
@@ -12444,6 +12447,14 @@
(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"
+{
+ return TARGET_64BIT ? "iretq" : "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 a5a8b23df27..82de5bf9fd8 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -5263,6 +5263,83 @@ 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, except for the EFLAGS
+register. If the compiler generates MPX, SSE, MMX or x87 instructions
+in a function with @code{no_caller_saved_registers} attribute or
+functions called from a function with @code{no_caller_saved_registers}
+attribute may contain MPX, SSE, MMX or x87 instructions, the compiler
+must save and restore the corresponding state.
+
+@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. If the
+compiler generates MPX, SSE, MMX or x87 instructions in an interrupt
+handler, or functions called from an interrupt handler may contain MPX,
+SSE, MMX or x87 instructions, the compiler must save and restore the
+corresponding state.
+
+Since the direction flag in the FLAGS register in interrupt handlers
+is undetermined, cld instruction must be emitted in function prologue
+if rep string instructions are used in interrupt handler or interrupt
+handler isn't a leaf function.
+
+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 00000000000..069d3e980dd
--- /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 -mno-mpx -mno-sse -mno-mmx -mno-80387" } */
+
+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 00000000000..da250289816
--- /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 -mno-mpx -mno-sse -mno-mmx -mno-80387" } */
+
+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 00000000000..7f9af3dd152
--- /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 -mno-mpx -mno-sse -mno-mmx -mno-80387" } */
+
+#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 00000000000..32336b013ff
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/pr68037-1.c
@@ -0,0 +1,58 @@
+/* { dg-do run { target i?86-*-* x86_64-*-* } } */
+/* { dg-options "-mno-mpx -mno-sse -mno-mmx -mno-80387" } */
+
+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 00000000000..02d7ab93bc9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/pr68037-2.c
@@ -0,0 +1,54 @@
+/* { dg-do run { target i?86-*-* x86_64-*-* } } */
+/* { dg-options "-mno-mpx -mno-sse -mno-mmx -mno-80387" } */
+
+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 00000000000..ea1952eb095
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/pr68037-3.c
@@ -0,0 +1,70 @@
+/* { dg-do run { target i?86-*-* x86_64-*-* } } */
+/* { dg-options "-mno-mpx -mno-sse -mno-mmx -mno-80387" } */
+
+#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.dg/torture/pr68661-1a.c b/gcc/testsuite/gcc.dg/torture/pr68661-1a.c
new file mode 100644
index 00000000000..50b2c7b6719
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/pr68661-1a.c
@@ -0,0 +1,18 @@
+/* { dg-do run { target i?86-*-* x86_64-*-* } } */
+/* { dg-options "-mno-mpx -mno-sse -mno-mmx -mno-80387" } */
+/* { dg-additional-sources pr68661-1b.c } */
+
+extern void bar0 (int, int, int, int, int, int, int, int, int)
+ __attribute__ ((no_caller_saved_registers));
+
+void
+foo (void)
+{
+ bar0 (0, 1, 2, 3, 4, 5, 6, 7, 8);
+}
+
+void
+bad (void)
+{
+ __builtin_abort ();
+}
diff --git a/gcc/testsuite/gcc.dg/torture/pr68661-1b.c b/gcc/testsuite/gcc.dg/torture/pr68661-1b.c
new file mode 100644
index 00000000000..d86c0f082cd
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/pr68661-1b.c
@@ -0,0 +1,45 @@
+/* { dg-do compile { target i?86-*-* x86_64-*-* } } */
+/* { dg-options "-mno-mpx -mno-sse -mno-mmx -mno-80387" } */
+
+extern void foo (void);
+extern void bad (void);
+
+void
+__attribute__ ((no_caller_saved_registers))
+bar0 (int i0, int i1, int i2, int i3, int i4, int i5, int i6,
+ int i7, int i8)
+{
+ if (i0 != 0)
+ bad ();
+
+ if (i1 != 1)
+ bad ();
+
+ if (i2 != 2)
+ bad ();
+
+ if (i3 != 3)
+ bad ();
+
+ if (i4 != 4)
+ bad ();
+
+ if (i5 != 5)
+ bad ();
+
+ if (i6 != 6)
+ bad ();
+
+ if (i7 != 7)
+ bad ();
+
+ if (i8 != 8)
+ bad ();
+}
+
+int
+main ()
+{
+ foo ();
+ 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 00000000000..56f070f13da
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-1.c
@@ -0,0 +1,55 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -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-times "iret" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "iretq" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "\tcld" 1 } } */
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 00000000000..b025a614c11
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-10.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld" } */
+
+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-times "iret" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "iretq" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "\tcld" 1 } } */
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 00000000000..4918e76358f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-11.c
@@ -0,0 +1,41 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -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 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "iretq" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "\tcld" 1 } } */
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 00000000000..4da81219578
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-12.c
@@ -0,0 +1,30 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -mno-iamcu -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 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "iretq" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "\tcld" 1 } } */
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 00000000000..7e35c291442
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-13.c
@@ -0,0 +1,30 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -mno-iamcu -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 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "iretq" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "\tcld" 1 } } */
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 00000000000..fe0966d532b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-14.c
@@ -0,0 +1,32 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -mno-iamcu -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 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "iretq" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "\tcld" 2 } } */
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 00000000000..754733ae904
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-15.c
@@ -0,0 +1,37 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -mno-iamcu -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 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "iretq" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "\tcld" 2 } } */
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 00000000000..28e3f43a990
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-16.c
@@ -0,0 +1,21 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -mno-iamcu -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-17.c b/gcc/testsuite/gcc.target/i386/interrupt-17.c
new file mode 100644
index 00000000000..0125f41bb24
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-17.c
@@ -0,0 +1,22 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O3 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -mno-iamcu -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\]+" } } */
+/* { 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-18.c b/gcc/testsuite/gcc.target/i386/interrupt-18.c
new file mode 100644
index 00000000000..77a554bd236
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-18.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -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-times "iret" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "iretq" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "\tcld" } } */
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 00000000000..4492f4b296b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-19.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -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-2.c b/gcc/testsuite/gcc.target/i386/interrupt-2.c
new file mode 100644
index 00000000000..06b9a36f4c6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-2.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -Wall -g" } */
+
+void
+__attribute__((interrupt))
+fn (void *frame)
+{
+}
+
+typedef void (*fn_t) (void *) __attribute__((interrupt));
+
+fn_t fns[] =
+{
+ fn,
+};
+
+/* { dg-final { scan-assembler-not "add(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */
+/* { dg-final { scan-assembler-times "iret" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "iretq" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "\tcld" } } */
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 00000000000..6ef13b6be85
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-20.c
@@ -0,0 +1,29 @@
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -miamcu -maccumulate-outgoing-args" } */
+
+struct interrupt_frame;
+
+void (*callback[1])(unsigned int id, unsigned int len);
+unsigned int remaining;
+
+void
+handler(int uart)
+{
+ while (1) {
+ if (remaining) {
+ callback[uart](0, 0);
+ break;
+ }
+ }
+}
+
+int uart;
+
+void
+__attribute__((interrupt))
+my_isr(struct interrupt_frame *frame)
+{
+ handler(uart);
+}
+
+/* { dg-final { scan-assembler-times "\tcld" 1 } } */
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 00000000000..ddbaf61ca72
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-21.c
@@ -0,0 +1,30 @@
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -miamcu -maccumulate-outgoing-args" } */
+
+struct interrupt_frame;
+
+void (*callback[1])(unsigned int id, unsigned int len);
+unsigned int remaining;
+
+void
+__attribute__((no_caller_saved_registers))
+handler(int uart)
+{
+ while (1) {
+ if (remaining) {
+ callback[uart](0, 0);
+ break;
+ }
+ }
+}
+
+int uart;
+
+void
+__attribute__((interrupt))
+my_isr(struct interrupt_frame *frame)
+{
+ handler(uart);
+}
+
+/* { dg-final { scan-assembler-times "\tcld" 1 } } */
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 00000000000..5de2018961b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-22.c
@@ -0,0 +1,29 @@
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -miamcu -maccumulate-outgoing-args" } */
+
+struct interrupt_frame;
+
+void (*callback) (unsigned int id, unsigned int len)
+ __attribute__((no_caller_saved_registers));
+unsigned int remaining;
+
+void
+__attribute__((no_caller_saved_registers))
+handler(void)
+{
+ while (1) {
+ if (remaining) {
+ callback(0, 0);
+ break;
+ }
+ }
+}
+
+void
+__attribute__((interrupt))
+my_isr(struct interrupt_frame *frame)
+{
+ handler();
+}
+
+/* { dg-final { scan-assembler-times "\tcld" 1 } } */
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 00000000000..c42049c1280
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-23.c
@@ -0,0 +1,46 @@
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -miamcu -maccumulate-outgoing-args" } */
+
+struct interrupt_frame;
+
+extern void callback0 (unsigned int id, unsigned int len)
+ __attribute__((no_caller_saved_registers));
+extern void callback1 (unsigned int id, unsigned int len)
+ __attribute__((no_caller_saved_registers));
+extern void callback2 (unsigned int id, unsigned int len)
+ __attribute__((no_caller_saved_registers));
+
+typedef void (*callback_t) (unsigned int id, unsigned int len)
+ __attribute__((no_caller_saved_registers));
+
+callback_t callback[] =
+{
+ callback0,
+ callback1,
+ callback2,
+};
+
+unsigned int remaining;
+
+void
+__attribute__((no_caller_saved_registers))
+handler(int uart)
+{
+ while (1) {
+ if (remaining) {
+ callback[uart](0, 0);
+ break;
+ }
+ }
+}
+
+int uart;
+
+void
+__attribute__((interrupt))
+my_isr(struct interrupt_frame *frame)
+{
+ handler(uart);
+}
+
+/* { dg-final { scan-assembler-times "\tcld" 1 } } */
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 00000000000..1669cda1736
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-24.c
@@ -0,0 +1,19 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -mno-iamcu -mno-push-args" } */
+
+extern void bar (void) __attribute__ ((noreturn));
+
+void
+__attribute__ ((no_caller_saved_registers))
+foo (int i0, int i1, int i2, int i3, int i4, int i5, int i6,
+ int i7, int i8)
+{
+ if (i7)
+ bar ();
+}
+
+/* { 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)(d|s)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 00000000000..30e3e7f92a4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-25.c
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -mno-push-args -maccumulate-outgoing-args" } */
+
+extern int bar (int);
+
+__attribute__((no_caller_saved_registers))
+void
+foo (void)
+{
+ 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 } } } } */
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 00000000000..14e43558c93
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-26.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-Os -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld" } */
+
+
+extern void *a;
+extern int b;
+
+__attribute__ ((interrupt))
+void
+foo (void *frame)
+{
+ __builtin_memset (a, b, 40);
+}
+
+/* { dg-final { scan-assembler "stosb" } } */
+/* { dg-final { scan-assembler-times "\tcld" 1 } } */
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 00000000000..5bed18f24b7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-27.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mcld" } */
+
+extern void bar (void);
+
+void
+__attribute__ ((interrupt))
+foo (void *frame)
+{
+ bar ();
+}
+
+/* { dg-final { scan-assembler-times "iret" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "iretq" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "\tcld" } } */
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 00000000000..58c68c79673
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-28.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mcld" } */
+
+__attribute__ ((interrupt))
+void
+foo (void *frame)
+{
+}
+
+/* { dg-final { scan-assembler-times "iret" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "iretq" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "\tcld" } } */
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 00000000000..ee7351e32e8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-3.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -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-times "iret" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "iretq" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "\tcld" } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-387-err-1.c b/gcc/testsuite/gcc.target/i386/interrupt-387-err-1.c
new file mode 100644
index 00000000000..3dff0345a53
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-387-err-1.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-cld -mno-iamcu -m80387" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+void
+__attribute__((interrupt))
+fn1 (void *frame, uword_t error)
+{ /* { dg-message "80387 instructions aren't allowed in exception service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn2 (void *frame)
+{ /* { dg-message "80387 instructions aren't allowed in interrupt service routine" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-387-err-2.c b/gcc/testsuite/gcc.target/i386/interrupt-387-err-2.c
new file mode 100644
index 00000000000..d7a54c8ec74
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-387-err-2.c
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-cld -m80387 -mlong-double-80 -mno-iamcu" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn1 (void)
+{ /* { dg-message "80387 instructions aren't allowed in function with no_caller_saved_registers attribute" } */
+}
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 00000000000..eb1195defd8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-4.c
@@ -0,0 +1,32 @@
+/* { dg-do link } */
+/* { dg-options "-O -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -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 00000000000..21ffea3bc13
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-5.c
@@ -0,0 +1,23 @@
+/* { dg-do link } */
+/* { dg-options "-O -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -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 00000000000..3bbe0da7c39
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-6.c
@@ -0,0 +1,40 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld" } */
+
+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 00000000000..23c748871d8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-7.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld" } */
+
+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 00000000000..f0e16378f52
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-8.c
@@ -0,0 +1,38 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -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 } } } } */
+/* { dg-final { scan-assembler-times "\tcld" 1 } } */
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 00000000000..827196ed100
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-9.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld" } */
+
+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-times "iret" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "iretq" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "\tcld" 1 } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-bnd-err-1.c b/gcc/testsuite/gcc.target/i386/interrupt-bnd-err-1.c
new file mode 100644
index 00000000000..6fd811705fc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-bnd-err-1.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mno-sse -mno-mmx -mno-80387 -mno-cld -mno-iamcu -mmpx" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+void
+__attribute__((interrupt))
+fn1 (void *frame)
+{ /* { dg-message "MPX instructions aren't allowed in interrupt service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn2 (void *frame, uword_t error)
+{ /* { dg-message "MPX instructions aren't allowed in exception service routine" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-bnd-err-2.c b/gcc/testsuite/gcc.target/i386/interrupt-bnd-err-2.c
new file mode 100644
index 00000000000..1bad58d65f3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-bnd-err-2.c
@@ -0,0 +1,8 @@
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mno-sse -mno-mmx -mno-80387 -mno-cld -mno-iamcu -mmpx" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn (void *frame)
+{ /* { dg-message "MPX instructions aren't allowed in function with no_caller_saved_registers attribute" } */
+}
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 00000000000..28c50fc1c14
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
@@ -0,0 +1,36 @@
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -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-times "iret" 1 } } */
+/* { dg-final { scan-assembler-times "\tcld" 1 } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-mmx-err-1.c b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err-1.c
new file mode 100644
index 00000000000..d46b21d8653
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err-1.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-80387 -mmmx -mno-cld -mno-iamcu" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+void
+__attribute__((interrupt))
+fn1 (void *frame)
+{ /* { dg-message "MMX/3Dnow instructions aren't allowed in interrupt service routine" } */
+}
+
+void
+__attribute__((interrupt))
+fn2 (void *frame, uword_t error)
+{ /* { dg-message "MMX/3Dnow instructions aren't allowed in exception service routine" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-mmx-err-2.c b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err-2.c
new file mode 100644
index 00000000000..e1d34e523bc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err-2.c
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-80387 -mmmx -mno-cld -mno-iamcu" } */
+
+void
+__attribute__((no_caller_saved_registers))
+fn1 (void)
+{ /* { dg-message "MMX/3Dnow instructions aren't allowed in function with no_caller_saved_registers attribute" } */
+}
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 00000000000..e0ff18217be
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c
@@ -0,0 +1,32 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -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" } } */
+/* { dg-final { scan-assembler-not "\tcld" } } */
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 00000000000..df916cad0da
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c
@@ -0,0 +1,33 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -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 } } */
+/* { dg-final { scan-assembler-not "\tcld" } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-sibcall-1.c b/gcc/testsuite/gcc.target/i386/interrupt-sibcall-1.c
new file mode 100644
index 00000000000..7e20d5808c4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-sibcall-1.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld" } */
+
+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-times "iret" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "iretq" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "\tcld" 1 } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-sibcall-2.c b/gcc/testsuite/gcc.target/i386/interrupt-sibcall-2.c
new file mode 100644
index 00000000000..51c0478d2bc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-sibcall-2.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -mpreferred-stack-boundary=3" { target { ! { ia32 } } } } */
+/* { dg-options "-O3 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -mpreferred-stack-boundary=2" { target { ia32 } } } */
+
+extern void foo (void *) __attribute__ ((interrupt));
+extern void bar (void) __attribute__ ((no_caller_saved_registers));
+
+void foo (void *frame)
+{
+ bar ();
+}
+/* { dg-final { scan-assembler-not "jmp" } } */
+/* { dg-final { scan-assembler-times "iret" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "iretq" 1 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "\tcld" 1 } } */
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 00000000000..f23e6f9870c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld" } */
+
+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 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "iretq" 2 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "\tcld" 2 } } */