summaryrefslogtreecommitdiff
path: root/gcc/config/i386/i386.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/i386/i386.c')
-rw-r--r--gcc/config/i386/i386.c377
1 files changed, 348 insertions, 29 deletions
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 28d2fcf7f22..9567fb66b81 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -2404,6 +2404,8 @@ struct ix86_frame
{
int nsseregs;
int nregs;
+ int nbndregs;
+ int nmaskregs;
int va_arg_size;
int red_zone_size;
int outgoing_arguments_size;
@@ -4500,6 +4502,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
@@ -4571,6 +4577,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;
}
@@ -5288,6 +5310,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;
@@ -5566,6 +5606,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.) */
@@ -6762,6 +6807,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)
{
@@ -7741,6 +7787,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
@@ -8052,6 +8103,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))
@@ -9782,7 +9866,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
@@ -10099,6 +10186,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)
{
@@ -10155,6 +10259,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
@@ -10163,7 +10295,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))
@@ -10232,6 +10365,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
@@ -10244,6 +10389,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
@@ -10315,11 +10462,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;
@@ -10337,7 +10489,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
@@ -10351,9 +10503,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;
@@ -10409,8 +10569,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 ()
@@ -10420,7 +10584,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;
}
@@ -10580,8 +10744,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;
@@ -10643,8 +10813,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);
}
}
@@ -10654,12 +10825,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);
}
}
@@ -12104,11 +12289,12 @@ 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 insn, mem;
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))
@@ -12127,7 +12313,7 @@ ix86_emit_restore_regs_using_mov (HOST_WIDE_INT cfa_offset,
else
ix86_add_cfa_restore_note (NULL_RTX, reg, cfa_offset);
- cfa_offset -= UNITS_PER_WORD;
+ cfa_offset -= GET_MODE_SIZE (reg_mode);
}
}
@@ -12138,21 +12324,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_RTX, reg, cfa_offset);
- cfa_offset -= 16;
+ cfa_offset -= GET_MODE_SIZE (vector_reg_mode);
}
}
@@ -12216,8 +12420,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)
@@ -12457,7 +12662,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);
@@ -17529,6 +17773,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];
@@ -17676,6 +17927,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);
@@ -25813,6 +26071,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);
@@ -25925,6 +26189,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;
}
@@ -43167,6 +43433,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)
{
@@ -47052,6 +47368,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 }
};