+2016-06-03 H.J. Lu <hongjiu.lu@intel.com>
+ Julia Koval <julia.koval@intel.com>
+
+ PR target/66960
+ PR target/67630
+ PR target/67634
+ PR target/67841
+ PR target/68037
+ PR target/68618
+ PR target/68661
+ PR target/69575
+ PR target/69596
+ PR target/69734
+ * config/i386/i386-protos.h (ix86_epilogue_uses): New prototype.
+ * config/i386/i386.c (ix86_conditional_register_usage): Preserve
+ all registers, except for function return registers if there are
+ no caller-saved registers.
+ (ix86_set_func_type): New function.
+ (ix86_set_current_function): Call ix86_set_func_type to set
+ no_caller_saved_registers and func_type. Call reinit_regs if
+ caller-saved registers are changed. Don't allow MPX, SSE, MMX
+ nor x87 instructions in interrupt handler nor function with
+ no_caller_saved_registers attribute.
+ (ix86_function_ok_for_sibcall): Return false if there are no
+ caller-saved registers.
+ (type_natural_mode): Don't warn ABI change for MMX in interrupt
+ handler.
+ (ix86_function_arg_advance): Skip for callee in interrupt
+ handler.
+ (ix86_function_arg): Return special arguments in interrupt
+ handler.
+ (ix86_promote_function_mode): Promote pointer to word_mode only
+ for normal functions.
+ (ix86_can_use_return_insn_p): Don't use `ret' instruction in
+ interrupt handler.
+ (ix86_epilogue_uses): New function.
+ (ix86_hard_regno_scratch_ok): Likewise.
+ (ix86_save_reg): Preserve all registers in interrupt handler
+ after reload. Preserve all registers, except for function
+ return registers, if there are no caller-saved registers after
+ reload.
+ (find_drap_reg): Always use callee-saved register if there are
+ no caller-saved registers.
+ (ix86_minimum_incoming_stack_boundary): Return MIN_STACK_BOUNDARY
+ for interrupt handler.
+ (ix86_expand_prologue): Don't allow DRAP in interrupt handler.
+ Emit cld instruction if stringops are used in interrupt handler
+ or interrupt handler isn't a leaf function.
+ (ix86_expand_epilogue): Generate interrupt return for interrupt
+ handler and pop the 'ERROR_CODE' off the stack before interrupt
+ return in exception handler.
+ (ix86_expand_call): Disallow calling interrupt handler directly.
+ If there are no caller-saved registers, mark all registers that
+ are clobbered by the call which returns as clobbered.
+ (ix86_handle_no_caller_saved_registers_attribute): New function.
+ (ix86_handle_interrupt_attribute): Likewise.
+ (ix86_attribute_table): Add interrupt and no_caller_saved_registers
+ attributes.
+ (TARGET_HARD_REGNO_SCRATCH_OK): Likewise.
+ * config/i386/i386.h (ACCUMULATE_OUTGOING_ARGS): Use argument
+ accumulation in interrupt function if stack may be realigned to
+ avoid DRAP.
+ (EPILOGUE_USES): New.
+ (function_type): New enum.
+ (machine_function): Add func_type and no_caller_saved_registers.
+ * config/i386/i386.md (UNSPEC_INTERRUPT_RETURN): New.
+ (interrupt_return): New pattern.
+ * doc/extend.texi: Document x86 interrupt and
+ no_caller_saved_registers attributes.
+
2016-06-03 Bernd Schmidt <bschmidt@redhat.com>
PR tree-optimization/52171
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;
{
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)
{
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. */
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)
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;
}
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. */
&& (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;
}
\f
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.) */
}
}
else if ((size == 8 && !TARGET_64BIT)
+ && (!cfun
+ || cfun->machine->func_type == TYPE_NORMAL)
&& !TARGET_MMX
&& !TARGET_IAMCU)
{
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
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))
}
/* 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;
{
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
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)
{
{
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;
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
{
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
{
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)])
{
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
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);
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;
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);
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)
{
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 }
};
#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;
\f
#include "gt-i386.h"
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
#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. */
/* 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;
/* 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
UNSPEC_BNDCU
UNSPEC_BNDCN
UNSPEC_MPX_FENCE
+
+ ;; IRET support
+ UNSPEC_INTERRUPT_RETURN
])
(define_c_enum "unspecv" [
(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.
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. For
+example, this attribute can be used for a function called from an
+interrupt handler. The compiler generates proper function entry and
+exit sequences to save and restore any modified registers, except for
+the EFLAGS register. Since GCC doesn't preserve MPX, SSE, MMX nor x87
+states, the GCC option @option{-mgeneral-regs-only} should be used to
+compile functions with @code{no_caller_saved_registers} attribute.
+
+@item interrupt
+@cindex @code{interrupt} function attribute, x86
+Use this attribute to indicate that the specified function is an
+interrupt handler or an exception handler (depending on parameters passed
+to the function, explained further). 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. Since GCC
+doesn't preserve MPX, SSE, MMX nor x87 states, the GCC option
+@option{-mgeneral-regs-only} should be used to compile interrupt and
+exception handlers.
+
+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
+
+@noindent
+and you must define @code{struct interrupt_frame} as described in the
+processor's manual.
+
+Exception handlers differ from interrupt handlers because the system
+pushes an error code on the stack. An exception handler declaration is
+similar to that for an interrupt handler, but with a different mandatory
+function signature. The compiler arranges to pop the error code off the
+stack before the @code{IRET} instruction.
+
+@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
+
+Exception handlers should only be used for exceptions that push an error
+code; you should use an interrupt handler in other cases. The system
+will crash if the wrong kind of handler is used.
+
@item target (@var{options})
@cindex @code{target} function attribute
As discussed in @ref{Common Function Attributes}, this attribute
+2016-06-03 H.J. Lu <hongjiu.lu@intel.com>
+ Julia Koval <julia.koval@intel.com>
+
+ PR target/66960
+ PR target/67630
+ PR target/67634
+ PR target/67841
+ PR target/68037
+ PR target/68618
+ PR target/68661
+ PR target/69575
+ PR target/69596
+ PR target/69734
+ * gcc.dg/guality/pr68037-1.c: New test.
+ * gcc.dg/guality/pr68037-2.c: Likewise.
+ * gcc.dg/guality/pr68037-3.c: Likewise.
+ * gcc.dg/torture/pr68037-1.c: Likewise.
+ * gcc.dg/torture/pr68037-2.c: Likewise.
+ * gcc.dg/torture/pr68037-3.c: Likewise.
+ * gcc.dg/torture/pr68661-1a.c: Likewise.
+ * gcc.dg/torture/pr68661-1b.c: Likewise.
+ * gcc.target/i386/interrupt-1.c: Likewise.
+ * gcc.target/i386/interrupt-2.c: Likewise.
+ * gcc.target/i386/interrupt-3.c: Likewise.
+ * gcc.target/i386/interrupt-4.c: Likewise.
+ * gcc.target/i386/interrupt-5.c: Likewise.
+ * gcc.target/i386/interrupt-6.c: Likewise.
+ * gcc.target/i386/interrupt-7.c: Likewise.
+ * gcc.target/i386/interrupt-8.c: Likewise.
+ * gcc.target/i386/interrupt-9.c: Likewise.
+ * gcc.target/i386/interrupt-10.c: Likewise.
+ * gcc.target/i386/interrupt-11.c: Likewise.
+ * gcc.target/i386/interrupt-12.c: Likewise.
+ * gcc.target/i386/interrupt-13.c: Likewise.
+ * gcc.target/i386/interrupt-14.c: Likewise.
+ * gcc.target/i386/interrupt-15.c: Likewise.
+ * gcc.target/i386/interrupt-16.c: Likewise.
+ * gcc.target/i386/interrupt-17.c: Likewise.
+ * gcc.target/i386/interrupt-18.c: Likewise.
+ * gcc.target/i386/interrupt-19.c: Likewise.
+ * gcc.target/i386/interrupt-20.c: Likewise.
+ * gcc.target/i386/interrupt-21.c: Likewise.
+ * gcc.target/i386/interrupt-22.c: Likewise.
+ * gcc.target/i386/interrupt-23.c: Likewise.
+ * gcc.target/i386/interrupt-24.c: Likewise.
+ * gcc.target/i386/interrupt-25.c: Likewise.
+ * gcc.target/i386/interrupt-26.c: Likewise.
+ * gcc.target/i386/interrupt-27.c: Likewise.
+ * gcc.target/i386/interrupt-28.c: Likewise.
+ * gcc.target/i386/interrupt-387-err-1.c: Likewise.
+ * gcc.target/i386/interrupt-387-err-2.c: Likewise.
+ * gcc.target/i386/interrupt-bnd-err-1.c: Likewise.
+ * gcc.target/i386/interrupt-bnd-err-2.c: Likewise.
+ * gcc.target/i386/interrupt-iamcu.c: Likewise.
+ * gcc.target/i386/interrupt-mmx-err-1.c: Likewise.
+ * gcc.target/i386/interrupt-mmx-err-2.c: Likewise.
+ * gcc.target/i386/interrupt-redzone-1.c: Likewise.
+ * gcc.target/i386/interrupt-redzone-2.c: Likewise.
+ * gcc.target/i386/interrupt-sibcall-1.c: Likewise.
+ * gcc.target/i386/interrupt-sibcall-2.c: Likewise.
+ * gcc.target/i386/interrupt-switch-abi.c: Likewise.
+
2016-06-03 Bernd Schmidt <bschmidt@redhat.com>
PR tree-optimization/52171
--- /dev/null
+/* { dg-do run { target i?86-*-* x86_64-*-* } } */
+/* { dg-options "-g -mgeneral-regs-only" } */
+
+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" } } */
--- /dev/null
+/* { dg-do run { target i?86-*-* x86_64-*-* } } */
+/* { dg-options "-g -mgeneral-regs-only" } */
+
+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" } } */
--- /dev/null
+/* { dg-do run { target i?86-*-* x86_64-*-* } } */
+/* { dg-options "-g -mgeneral-regs-only" } */
+
+#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" } } */
--- /dev/null
+/* { dg-do run { target i?86-*-* x86_64-*-* } } */
+/* { dg-options "-mgeneral-regs-only" } */
+
+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;
+}
--- /dev/null
+/* { dg-do run { target i?86-*-* x86_64-*-* } } */
+/* { dg-options "-mgeneral-regs-only" } */
+
+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;
+}
--- /dev/null
+/* { dg-do run { target i?86-*-* x86_64-*-* } } */
+/* { dg-options "-mgeneral-regs-only" } */
+
+#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;
+}
--- /dev/null
+/* { dg-do run { target i?86-*-* x86_64-*-* } } */
+/* { dg-options "-mgeneral-regs-only" } */
+/* { 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 ();
+}
--- /dev/null
+/* { dg-do compile { target i?86-*-* x86_64-*-* } } */
+/* { dg-options "-mgeneral-regs-only" } */
+
+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;
+}
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -mgeneral-regs-only -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 } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -mgeneral-regs-only -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 } } */
--- /dev/null
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mgeneral-regs-only -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 } } */
--- /dev/null
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mgeneral-regs-only -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 } } */
--- /dev/null
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mgeneral-regs-only -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 } } */
--- /dev/null
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mgeneral-regs-only -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 } } */
--- /dev/null
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mgeneral-regs-only -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 } } */
--- /dev/null
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mgeneral-regs-only -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 } } } } */
--- /dev/null
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O3 -mgeneral-regs-only -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" } }*/
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -mgeneral-regs-only -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" } } */
--- /dev/null
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -mgeneral-regs-only -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" } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -mgeneral-regs-only -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" } } */
--- /dev/null
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -mgeneral-regs-only -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 } } */
--- /dev/null
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -mgeneral-regs-only -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 } } */
--- /dev/null
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -mgeneral-regs-only -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 } } */
--- /dev/null
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -mgeneral-regs-only -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 } } */
--- /dev/null
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mgeneral-regs-only -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 } } } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -mgeneral-regs-only -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 } } } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-Os -mgeneral-regs-only -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 } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -mgeneral-regs-only -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" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -mgeneral-regs-only -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" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -mgeneral-regs-only -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" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -mgeneral-regs-only -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" } */
+}
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -mgeneral-regs-only -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" } */
+}
--- /dev/null
+/* { dg-do link } */
+/* { dg-options "-O -mgeneral-regs-only -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;
+}
--- /dev/null
+/* { dg-do link } */
+/* { dg-options "-O -mgeneral-regs-only -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;
+}
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -mgeneral-regs-only -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;
+}
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -mgeneral-regs-only -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" } */
+}
--- /dev/null
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mgeneral-regs-only -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 } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -mgeneral-regs-only -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 } } */
--- /dev/null
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mgeneral-regs-only -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" } */
+}
--- /dev/null
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mgeneral-regs-only -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" } */
+}
--- /dev/null
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -mgeneral-regs-only -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 } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -mgeneral-regs-only -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" } */
+}
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -mgeneral-regs-only -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" } */
+}
--- /dev/null
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mgeneral-regs-only -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" } } */
--- /dev/null
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mgeneral-regs-only -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" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O3 -mgeneral-regs-only -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 } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O3 -mgeneral-regs-only -mno-cld -mpreferred-stack-boundary=3" { target { ! { ia32 } } } } */
+/* { dg-options "-O3 -mgeneral-regs-only -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 } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -mgeneral-regs-only -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 } } */