diff options
Diffstat (limited to 'gcc')
53 files changed, 1915 insertions, 27 deletions
diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h index 39624270638..23152c37f0b 100644 --- a/gcc/config/i386/i386-protos.h +++ b/gcc/config/i386/i386-protos.h @@ -279,6 +279,8 @@ extern rtx maybe_get_pool_constant (rtx); extern char internal_label_prefix[16]; extern int internal_label_prefix_len; +extern bool ix86_epilogue_uses (int); + enum ix86_address_seg { SEG_DEFAULT, SEG_FS, SEG_GS }; struct ix86_address { diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index 8a1bf7ca5e8..2a39c475308 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -4512,6 +4512,15 @@ ix86_conditional_register_usage (void) { int i, c_mask; + /* If there are no caller-saved registers, preserve all registers. + except fixed_regs and registers used for function return value + since aggregate_value_p checks call_used_regs[regno] on return + value. */ + if (cfun && cfun->machine->no_caller_saved_registers) + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (!fixed_regs[i] && !ix86_function_value_regno_p (i)) + call_used_regs[i] = 0; + /* For 32-bit targets, squash the REX registers. */ if (! TARGET_64BIT) { @@ -5280,6 +5289,40 @@ ix86_reset_previous_fndecl (void) ix86_previous_fndecl = NULL_TREE; } +/* Set the func_type field from the function FNDECL. */ + +static void +ix86_set_func_type (tree fndecl) +{ + if (cfun->machine->func_type == TYPE_UNKNOWN) + { + if (lookup_attribute ("interrupt", + TYPE_ATTRIBUTES (TREE_TYPE (fndecl)))) + { + int nargs = 0; + for (tree arg = DECL_ARGUMENTS (fndecl); + arg; + arg = TREE_CHAIN (arg)) + nargs++; + cfun->machine->no_caller_saved_registers = true; + cfun->machine->func_type + = nargs == 2 ? TYPE_EXCEPTION : TYPE_INTERRUPT; + + /* Only dwarf2out.c can handle -WORD(AP) as a pointer argument. */ + if (write_symbols != NO_DEBUG && write_symbols != DWARF2_DEBUG) + sorry ("Only DWARF debug format is supported for interrupt " + "service routine."); + } + else + { + cfun->machine->func_type = TYPE_NORMAL; + if (lookup_attribute ("no_caller_saved_registers", + TYPE_ATTRIBUTES (TREE_TYPE (fndecl)))) + cfun->machine->no_caller_saved_registers = true; + } + } +} + /* Establish appropriate back-end context for processing the function FNDECL. The argument might be NULL to indicate processing at top level, outside of any function scope. */ @@ -5290,7 +5333,14 @@ ix86_set_current_function (tree fndecl) several times in the course of compiling a function, and we don't want to slow things down too much or call target_reinit when it isn't safe. */ if (fndecl == ix86_previous_fndecl) - return; + { + /* There may be 2 function bodies for the same function FNDECL, + one is extern inline and one isn't. Call ix86_set_func_type + to set the func_type field. */ + if (fndecl != NULL_TREE) + ix86_set_func_type (fndecl); + return; + } tree old_tree; if (ix86_previous_fndecl == NULL_TREE) @@ -5307,6 +5357,8 @@ ix86_set_current_function (tree fndecl) return; } + ix86_set_func_type (fndecl); + tree new_tree = DECL_FUNCTION_SPECIFIC_TARGET (fndecl); if (new_tree == NULL_TREE) new_tree = target_option_default_node; @@ -5323,6 +5375,8 @@ ix86_set_current_function (tree fndecl) } ix86_previous_fndecl = fndecl; + static bool prev_no_caller_saved_registers; + /* 64-bit MS and SYSV ABI have different set of call used registers. Avoid expensive re-initialization of init_regs each time we switch function context. */ @@ -5330,6 +5384,45 @@ ix86_set_current_function (tree fndecl) && (call_used_regs[SI_REG] == (cfun->machine->call_abi == MS_ABI))) reinit_regs (); + /* Need to re-initialize init_regs if caller-saved registers are + changed. */ + else if (prev_no_caller_saved_registers + != cfun->machine->no_caller_saved_registers) + reinit_regs (); + + if (cfun->machine->func_type != TYPE_NORMAL + || cfun->machine->no_caller_saved_registers) + { + /* Don't allow MPX, SSE, MMX nor x87 instructions since they + may change processor state. */ + const char *isa; + if (TARGET_MPX) + isa = "MPX"; + else if (TARGET_SSE) + isa = "SSE"; + else if (TARGET_MMX) + isa = "MMX/3Dnow"; + else if (TARGET_80387) + isa = "80387"; + else + isa = NULL; + if (isa != NULL) + { + if (cfun->machine->func_type != TYPE_NORMAL) + sorry ("%s instructions aren't allowed in %s service routine", + isa, (cfun->machine->func_type == TYPE_EXCEPTION + ? "exception" : "interrupt")); + else + sorry ("%s instructions aren't allowed in function with " + "no_caller_saved_registers attribute", isa); + /* Don't issue the same error twice. */ + cfun->machine->func_type = TYPE_NORMAL; + cfun->machine->no_caller_saved_registers = false; + } + } + + prev_no_caller_saved_registers + = cfun->machine->no_caller_saved_registers; } @@ -5593,6 +5686,11 @@ ix86_function_ok_for_sibcall (tree decl, tree exp) tree type, decl_or_type; rtx a, b; + /* Sibling call isn't OK if there are no caller-saved registers + 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.) */ @@ -6778,6 +6876,8 @@ type_natural_mode (const_tree type, const CUMULATIVE_ARGS *cum, } } else if ((size == 8 && !TARGET_64BIT) + && (!cfun + || cfun->machine->func_type == TYPE_NORMAL) && !TARGET_MMX && !TARGET_IAMCU) { @@ -7757,6 +7857,11 @@ ix86_function_arg_advance (cumulative_args_t cum_v, machine_mode mode, HOST_WIDE_INT bytes, words; int nregs; + /* The argument of interrupt handler is a special case and is + handled in ix86_function_arg. */ + if (!cum->caller && cfun->machine->func_type != TYPE_NORMAL) + return; + if (mode == BLKmode) bytes = int_size_in_bytes (type); else @@ -8068,6 +8173,36 @@ ix86_function_arg (cumulative_args_t cum_v, machine_mode omode, HOST_WIDE_INT bytes, words; rtx arg; + if (!cum->caller && cfun->machine->func_type != TYPE_NORMAL) + { + gcc_assert (type != NULL_TREE); + if (POINTER_TYPE_P (type)) + { + /* This is the pointer argument. */ + gcc_assert (TYPE_MODE (type) == Pmode); + if (cfun->machine->func_type == TYPE_INTERRUPT) + /* -WORD(AP) in the current frame in interrupt handler. */ + arg = plus_constant (Pmode, arg_pointer_rtx, + -UNITS_PER_WORD); + else + /* (AP) in the current frame in exception handler. */ + arg = arg_pointer_rtx; + } + else + { + gcc_assert (cfun->machine->func_type == TYPE_EXCEPTION + && TREE_CODE (type) == INTEGER_TYPE + && TYPE_MODE (type) == word_mode); + /* The integer argument is the error code at -WORD(AP) in + the current frame in exception handler. */ + arg = gen_rtx_MEM (word_mode, + plus_constant (Pmode, + arg_pointer_rtx, + -UNITS_PER_WORD)); + } + return arg; + } + /* All pointer bounds arguments are handled separately here. */ if ((type && POINTER_BOUNDS_TYPE_P (type)) || POINTER_BOUNDS_MODE_P (mode)) @@ -8623,14 +8758,16 @@ ix86_function_value_bounds (const_tree valtype, } /* Pointer function arguments and return values are promoted to - word_mode. */ + word_mode for normal functions. */ static machine_mode ix86_promote_function_mode (const_tree type, machine_mode mode, int *punsignedp, const_tree fntype, int for_return) { - if (type != NULL_TREE && POINTER_TYPE_P (type)) + if (cfun->machine->func_type == TYPE_NORMAL + && type != NULL_TREE + && POINTER_TYPE_P (type)) { *punsignedp = POINTERS_EXTEND_UNSIGNED; return word_mode; @@ -9798,7 +9935,10 @@ ix86_can_use_return_insn_p (void) { struct ix86_frame frame; - if (! reload_completed || frame_pointer_needed) + /* Don't use `ret' instruction in interrupt handler. */ + if (! reload_completed + || frame_pointer_needed + || cfun->machine->func_type != TYPE_NORMAL) return 0; /* Don't allow more than 32k pop, since that's all we can do @@ -10114,11 +10254,77 @@ ix86_select_alt_pic_regnum (void) return INVALID_REGNUM; } +/* Return true if REGNO is used by the epilogue. */ + +bool +ix86_epilogue_uses (int regno) +{ + /* If there are no caller-saved registers, we preserve all registers, + except for MMX and x87 registers which aren't supported when saving + and restoring registers. Don't explicitly save SP register since + it is always preserved. */ + return (epilogue_completed + && cfun->machine->no_caller_saved_registers + && !fixed_regs[regno] + && !STACK_REGNO_P (regno) + && !MMX_REGNO_P (regno)); +} + +/* Return nonzero if register REGNO can be used as a scratch register + in peephole2. */ + +static bool +ix86_hard_regno_scratch_ok (unsigned int regno) +{ + /* If there are no caller-saved registers, we can't use any register + as a scratch register after epilogue and use REGNO as scratch + register only if it has been used before to avoid saving and + restoring it. */ + return (!cfun->machine->no_caller_saved_registers + || (!epilogue_completed + && df_regs_ever_live_p (regno))); +} + /* Return TRUE if we need to save REGNO. */ static bool ix86_save_reg (unsigned int regno, bool maybe_eh_return) { + /* If there are no caller-saved registers, we preserve all registers, + except for MMX and x87 registers which aren't supported when saving + and restoring registers. Don't explicitly save SP register since + it is always preserved. */ + if (cfun->machine->no_caller_saved_registers) + { + /* Don't preserve registers used for function return value. */ + rtx reg = crtl->return_rtx; + if (reg) + { + unsigned int i = REGNO (reg); + unsigned int nregs = hard_regno_nregs[i][GET_MODE (reg)]; + while (nregs-- > 0) + if ((i + nregs) == regno) + return false; + + reg = crtl->return_bnd; + if (reg) + { + i = REGNO (reg); + nregs = hard_regno_nregs[i][GET_MODE (reg)]; + while (nregs-- > 0) + if ((i + nregs) == regno) + return false; + } + } + + return (df_regs_ever_live_p (regno) + && !fixed_regs[regno] + && !STACK_REGNO_P (regno) + && !MMX_REGNO_P (regno) + && (regno != HARD_FRAME_POINTER_REGNUM + || !frame_pointer_needed)); + } + if (regno == REAL_PIC_OFFSET_TABLE_REGNUM && pic_offset_table_rtx) { @@ -10842,13 +11048,17 @@ find_drap_reg (void) { tree decl = cfun->decl; + /* Always use callee-saved register if there are no caller-saved + registers. */ if (TARGET_64BIT) { /* Use R13 for nested function or function need static chain. Since function with tail call may use any caller-saved registers in epilogue, DRAP must not use caller-saved register in such case. */ - if (DECL_STATIC_CHAIN (decl) || crtl->tail_call_emit) + if (DECL_STATIC_CHAIN (decl) + || cfun->machine->no_caller_saved_registers + || crtl->tail_call_emit) return R13_REG; return R10_REG; @@ -10859,7 +11069,9 @@ find_drap_reg (void) Since function with tail call may use any caller-saved registers in epilogue, DRAP must not use caller-saved register in such case. */ - if (DECL_STATIC_CHAIN (decl) || crtl->tail_call_emit) + if (DECL_STATIC_CHAIN (decl) + || cfun->machine->no_caller_saved_registers + || crtl->tail_call_emit) return DI_REG; /* Reuse static chain register if it isn't used for parameter @@ -10900,8 +11112,12 @@ ix86_minimum_incoming_stack_boundary (bool sibcall) { unsigned int incoming_stack_boundary; + /* Stack of interrupt handler is always aligned to MIN_STACK_BOUNDARY. + */ + if (cfun->machine->func_type != TYPE_NORMAL) + incoming_stack_boundary = MIN_STACK_BOUNDARY; /* Prefer the one specified at command line. */ - if (ix86_user_incoming_stack_boundary) + else if (ix86_user_incoming_stack_boundary) incoming_stack_boundary = ix86_user_incoming_stack_boundary; /* In 32bit, use MIN_STACK_BOUNDARY for incoming stack boundary if -mstackrealign is used, it isn't used for sibcall check and @@ -11659,6 +11875,12 @@ ix86_expand_prologue (void) { int align_bytes = crtl->stack_alignment_needed / BITS_PER_UNIT; + /* Can't use DRAP in interrupt function. */ + if (cfun->machine->func_type != TYPE_NORMAL) + sorry ("Dynamic Realign Argument Pointer (DRAP) not supported " + "in interrupt service routine. This may be worked " + "around by avoiding functions with aggregate return."); + /* Only need to push parameter pointer reg if it is caller saved. */ if (!call_used_regs[REGNO (crtl->drap_reg)]) { @@ -12034,8 +12256,14 @@ ix86_expand_prologue (void) if (frame_pointer_needed && frame.red_zone_size) emit_insn (gen_memory_blockage ()); - /* Emit cld instruction if stringops are used in the function. */ - if (TARGET_CLD && ix86_current_function_needs_cld) + /* Emit cld instruction if stringops are used in the function. Since + we can't assume the direction flag in interrupt handler, we must + emit cld instruction if stringops are used in interrupt handler or + interrupt handler isn't a leaf function. */ + if ((TARGET_CLD && ix86_current_function_needs_cld) + || (!TARGET_CLD + && cfun->machine->func_type != TYPE_NORMAL + && (ix86_current_function_needs_cld || !crtl->is_leaf))) emit_insn (gen_cld ()); /* SEH requires that the prologue end within 256 bytes of the start of @@ -12202,20 +12430,20 @@ ix86_emit_restore_sse_regs_using_mov (HOST_WIDE_INT cfa_offset, mem = choose_baseaddr (cfa_offset); mem = gen_rtx_MEM (V4SFmode, mem); - /* The location is aligned up to INCOMING_STACK_BOUNDARY. */ + /* The location is aligned up to INCOMING_STACK_BOUNDARY. */ align = MIN (GET_MODE_ALIGNMENT (V4SFmode), INCOMING_STACK_BOUNDARY); - set_mem_align (mem, align); - - /* SSE saves are not within re-aligned local stack frame. - In case INCOMING_STACK_BOUNDARY is misaligned, we have - to emit unaligned load. */ - if (align < 128) - { - rtx unspec = gen_rtx_UNSPEC (V4SFmode, gen_rtvec (1, mem), - UNSPEC_LOADU); + set_mem_align (mem, align); + + /* SSE saves are not within re-aligned local stack frame. + In case INCOMING_STACK_BOUNDARY is misaligned, we have + to emit unaligned load. */ + if (align < 128) + { + rtx unspec = gen_rtx_UNSPEC (V4SFmode, gen_rtvec (1, mem), + UNSPEC_LOADU); emit_insn (gen_rtx_SET (VOIDmode, reg, unspec)); - } - else + } + else emit_insn (gen_rtx_SET (VOIDmode, reg, mem)); ix86_add_cfa_restore_note (NULL_RTX, reg, cfa_offset); @@ -12525,7 +12753,20 @@ ix86_expand_epilogue (int style) return; } - if (crtl->args.pops_args && crtl->args.size) + if (cfun->machine->func_type != TYPE_NORMAL) + { + /* Return with the "IRET" instruction from interrupt handler. + Pop the 'ERROR_CODE' off the stack before the 'IRET' + instruction in exception handler. */ + if (cfun->machine->func_type == TYPE_EXCEPTION) + { + rtx r = plus_constant (Pmode, stack_pointer_rtx, + UNITS_PER_WORD); + emit_insn (gen_rtx_SET (VOIDmode, 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); @@ -25891,6 +26132,18 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1, rtx vec[3]; rtx use = NULL, call; unsigned int vec_len = 0; + tree fndecl; + + if (GET_CODE (XEXP (fnaddr, 0)) == SYMBOL_REF) + { + fndecl = SYMBOL_REF_DECL (XEXP (fnaddr, 0)); + if (fndecl + && (lookup_attribute ("interrupt", + TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))) + error ("interrupt service routine can't be called directly"); + } + else + fndecl = NULL_TREE; if (pop == const0_rtx) pop = NULL; @@ -25982,8 +26235,30 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1, vec[vec_len++] = pop; } - if (TARGET_64BIT_MS_ABI - && (!callarg2 || INTVAL (callarg2) != -2)) + if (cfun->machine->no_caller_saved_registers + && (!fndecl + || (!TREE_THIS_VOLATILE (fndecl) + && !lookup_attribute ("no_caller_saved_registers", + TYPE_ATTRIBUTES (TREE_TYPE (fndecl)))))) + { + static const char ix86_call_used_regs[] = CALL_USED_REGISTERS; + bool is_64bit_ms_abi = (TARGET_64BIT + && ix86_function_abi (fndecl) == MS_ABI); + char c_mask = CALL_USED_REGISTERS_MASK (is_64bit_ms_abi); + + /* If there are no caller-saved registers, add all registers + that are clobbered by the call which returns. */ + for (int i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (!fixed_regs[i] + && (ix86_call_used_regs[i] == 1 + || (ix86_call_used_regs[i] & c_mask)) + && !STACK_REGNO_P (i) + && !MMX_REGNO_P (i)) + clobber_reg (&use, + gen_rtx_REG (GET_MODE (regno_reg_rtx[i]), i)); + } + else if (TARGET_64BIT_MS_ABI + && (!callarg2 || INTVAL (callarg2) != -2)) { int const cregs_size = ARRAY_SIZE (x86_64_ms_sysv_extra_clobbered_registers); @@ -43266,6 +43541,54 @@ ix86_handle_fndecl_attribute (tree *node, tree name, tree, int, return NULL_TREE; } +static tree +ix86_handle_no_caller_saved_registers_attribute (tree *, tree, tree, + int, bool *) +{ + return NULL_TREE; +} + +static tree +ix86_handle_interrupt_attribute (tree *node, tree, tree, int, bool *) +{ + /* DECL_RESULT and DECL_ARGUMENTS do not exist there yet, + but the function type contains args and return type data. */ + tree func_type = *node; + tree return_type = TREE_TYPE (func_type); + + int nargs = 0; + tree current_arg_type = TYPE_ARG_TYPES (func_type); + while (current_arg_type + && ! VOID_TYPE_P (TREE_VALUE (current_arg_type))) + { + if (nargs == 0) + { + if (! POINTER_TYPE_P (TREE_VALUE (current_arg_type))) + error ("interrupt service routine should have a pointer " + "as the first argument"); + } + else if (nargs == 1) + { + if (TREE_CODE (TREE_VALUE (current_arg_type)) != INTEGER_TYPE + || TYPE_MODE (TREE_VALUE (current_arg_type)) != word_mode) + error ("interrupt service routine should have unsigned %s" + "int as the second argument", + TARGET_64BIT + ? (TARGET_X32 ? "long long " : "long ") + : ""); + } + nargs++; + current_arg_type = TREE_CHAIN (current_arg_type); + } + if (!nargs || nargs > 2) + error ("interrupt service routine can only have a pointer argument " + "and an optional integer argument"); + if (! VOID_TYPE_P (return_type)) + error ("interrupt service routine can't have non-void return value"); + + return NULL_TREE; +} + static bool ix86_ms_bitfield_layout_p (const_tree record_type) { @@ -47157,6 +47480,11 @@ static const struct attribute_spec ix86_attribute_table[] = false }, { "callee_pop_aggregate_return", 1, 1, false, true, true, ix86_handle_callee_pop_aggregate_return, true }, + { "interrupt", 0, 0, false, true, true, + ix86_handle_interrupt_attribute, false }, + { "no_caller_saved_registers", 0, 0, false, true, true, + ix86_handle_no_caller_saved_registers_attribute, false }, + /* End element. */ { NULL, 0, 0, false, false, false, NULL, false } }; @@ -52800,6 +53128,9 @@ ix86_binds_local_p (const_tree exp) #undef TARGET_ABSOLUTE_BIGGEST_ALIGNMENT #define TARGET_ABSOLUTE_BIGGEST_ALIGNMENT 512 +#undef TARGET_HARD_REGNO_SCRATCH_OK +#define TARGET_HARD_REGNO_SCRATCH_OK ix86_hard_regno_scratch_ok + struct gcc_target targetm = TARGET_INITIALIZER; #include "gt-i386.h" diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h index 1a369c2c6c0..1b3abeba8e6 100644 --- a/gcc/config/i386/i386.h +++ b/gcc/config/i386/i386.h @@ -1619,11 +1619,18 @@ enum reg_class If stack probes are required, the space used for large function arguments on the stack must also be probed, so enable - -maccumulate-outgoing-args so this happens in the prologue. */ + -maccumulate-outgoing-args so this happens in the prologue. + + We must use argument accumulation in interrupt function if stack + may be realigned to avoid DRAP. */ #define ACCUMULATE_OUTGOING_ARGS \ - ((TARGET_ACCUMULATE_OUTGOING_ARGS && optimize_function_for_speed_p (cfun)) \ - || TARGET_STACK_PROBE || TARGET_64BIT_MS_ABI) + ((TARGET_ACCUMULATE_OUTGOING_ARGS \ + && optimize_function_for_speed_p (cfun)) \ + || (cfun->machine->func_type != TYPE_NORMAL \ + && crtl->stack_realign_needed) \ + || TARGET_STACK_PROBE \ + || TARGET_64BIT_MS_ABI) /* If defined, a C expression whose value is nonzero when we want to use PUSH instructions to pass outgoing arguments. */ @@ -1731,6 +1738,11 @@ typedef struct ix86_args { #define EXIT_IGNORE_STACK 1 +/* Define this macro as a C expression that is nonzero for registers + used by the epilogue or the `return' pattern. */ + +#define EPILOGUE_USES(REGNO) ix86_epilogue_uses (REGNO) + /* Output assembler code for a block containing the constant parts of a trampoline, leaving space for the variable parts. */ @@ -2446,6 +2458,19 @@ struct GTY(()) machine_frame_state /* Private to winnt.c. */ struct seh_frame_state; +enum function_type +{ + TYPE_UNKNOWN = 0, + TYPE_NORMAL, + /* The current function is an interrupt service routine with a + pointer argument as specified by the "interrupt" attribute. */ + TYPE_INTERRUPT, + /* The current function is an interrupt service routine with a + pointer argument and an integer argument as specified by the + "interrupt" attribute. */ + TYPE_EXCEPTION +}; + struct GTY(()) machine_function { struct stack_local_entry *stack_locals; const char *some_ld_name; @@ -2496,6 +2521,13 @@ struct GTY(()) machine_function { /* If true, it is safe to not save/restore DRAP register. */ BOOL_BITFIELD no_drap_save_restore : 1; + /* Function type. */ + ENUM_BITFIELD(function_type) func_type : 2; + + /* If true, the current function is a function specified with + the "interrupt" or "no_caller_saved_registers" attribute. */ + BOOL_BITFIELD no_caller_saved_registers : 1; + /* If true, there is register available for argument passing. This is used only in ix86_function_ok_for_sibcall by 32-bit to determine if there is scratch register available for indirect sibcall. In diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index e1fcc8d16d4..b1777f12c56 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -193,6 +193,9 @@ UNSPEC_BNDCU UNSPEC_BNDCN UNSPEC_MPX_FENCE + + ;; IRET support + UNSPEC_INTERRUPT_RETURN ]) (define_c_enum "unspecv" [ @@ -12218,6 +12221,14 @@ (set_attr "length_immediate" "0") (set_attr "modrm" "0")]) +(define_insn "interrupt_return" + [(simple_return) + (unspec [(const_int 0)] UNSPEC_INTERRUPT_RETURN)] + "reload_completed" +{ + return TARGET_64BIT ? "iretq" : "iret"; +}) + ;; Used by x86_machine_dependent_reorg to avoid penalty on single byte RET ;; instruction Athlon and K8 have. diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 70a09032d43..98afcd3a754 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -3929,6 +3929,83 @@ On x86-32 targets, the @code{stdcall} attribute causes the compiler to assume that the called function pops off the stack space used to pass arguments, unless it takes a variable number of arguments. +@item no_caller_saved_registers +@cindex @code{no_caller_saved_registers} function attribute, x86 +Use this attribute to indicate that the specified function has no +caller-saved registers. That is, all registers are callee-saved. +The compiler generates proper function entry and exit sequences to +save and restore any modified registers, except for the EFLAGS +register. If the compiler generates MPX, SSE, MMX or x87 instructions +in a function with @code{no_caller_saved_registers} attribute or +functions called from a function with @code{no_caller_saved_registers} +attribute may contain MPX, SSE, MMX or x87 instructions, the compiler +must save and restore the corresponding state. + +@item interrupt +@cindex @code{interrupt} function attribute, x86 +Use this attribute to indicate that the specified function is an +interrupt handler. The compiler generates function +entry and exit sequences suitable for use in an interrupt handler when +this attribute is present. The @code{IRET} instruction, instead of the +@code{RET} instruction, is used to return from interrupt handlers. All +registers, except for the EFLAGS register which is restored by the +@code{IRET} instruction, are preserved by the compiler. If the +compiler generates MPX, SSE, MMX or x87 instructions in an interrupt +handler, or functions called from an interrupt handler may contain MPX, +SSE, MMX or x87 instructions, the compiler must save and restore the +corresponding state. + +Since the direction flag in the FLAGS register in interrupt handlers +is undetermined, cld instruction must be emitted in function prologue +if rep string instructions are used in interrupt handler or interrupt +handler isn't a leaf function. + +Any interruptible-without-stack-switch code must be compiled with +@option{-mno-red-zone} since interrupt handlers can and will, because +of the hardware design, touch the red zone. + +An interrupt handler must be declared with a mandatory pointer +argument: + +@smallexample +struct interrupt_frame; + +__attribute__ ((interrupt)) +void +f (struct interrupt_frame *frame) +@{ +@} +@end smallexample + +and user must properly define the structure the pointer pointing to. + +The exception handler is very similar to the interrupt handler with +a different mandatory function signature: + +@smallexample +#ifdef __x86_64__ +typedef unsigned long long int uword_t; +#else +typedef unsigned int uword_t; +#endif + +struct interrupt_frame; + +__attribute__ ((interrupt)) +void +f (struct interrupt_frame *frame, uword_t error_code) +@{ + ... +@} +@end smallexample + +and compiler pops the error code off the stack before the @code{IRET} +instruction. + +The exception handler should only be used for exceptions which push an +error code and all other exceptions must use the interrupt handler. +The system will crash if the wrong handler is used. + @item syscall_linkage @cindex @code{syscall_linkage} function attribute, IA-64 This attribute is used to modify the IA-64 calling convention by marking diff --git a/gcc/testsuite/gcc.dg/guality/pr68037-1.c b/gcc/testsuite/gcc.dg/guality/pr68037-1.c new file mode 100644 index 00000000000..069d3e980dd --- /dev/null +++ b/gcc/testsuite/gcc.dg/guality/pr68037-1.c @@ -0,0 +1,65 @@ +/* { dg-do run { target i?86-*-* x86_64-*-* } } */ +/* { dg-options "-g -mno-mpx -mno-sse -mno-mmx -mno-80387" } */ + +extern void exit (int); + +typedef unsigned int uword_t __attribute__ ((mode (__word__))); + +#define ERROR 0x12345670 +#define IP 0x12345671 +#define CS 0x12345672 +#define FLAGS 0x12345673 +#define SP 0x12345674 +#define SS 0x12345675 + +#define STRING(x) XSTRING(x) +#define XSTRING(x) #x + +struct interrupt_frame +{ + uword_t ip; + uword_t cs; + uword_t flags; + uword_t sp; + uword_t ss; +}; + +__attribute__((interrupt, used)) +void +fn (struct interrupt_frame *frame, uword_t error) +{ + if (ERROR != error) /* BREAK */ + __builtin_abort (); + if (IP != frame->ip) + __builtin_abort (); + if (CS != frame->cs) + __builtin_abort (); + if (FLAGS != frame->flags) + __builtin_abort (); + if (SP != frame->sp) + __builtin_abort (); + if (SS != frame->ss) + __builtin_abort (); + + exit (0); +} + +int +main () +{ + asm ("push $" STRING (SS) "; \ + push $" STRING (SP) "; \ + push $" STRING (FLAGS) "; \ + push $" STRING (CS) "; \ + push $" STRING (IP) "; \ + push $" STRING (ERROR) "; \ + jmp fn"); + return 0; +} + +/* { dg-final { gdb-test 31 "error" "0x12345670" } } */ +/* { dg-final { gdb-test 31 "frame->ip" "0x12345671" } } */ +/* { dg-final { gdb-test 31 "frame->cs" "0x12345672" } } */ +/* { dg-final { gdb-test 31 "frame->flags" "0x12345673" } } */ +/* { dg-final { gdb-test 31 "frame->sp" "0x12345674" } } */ +/* { dg-final { gdb-test 31 "frame->ss" "0x12345675" } } */ diff --git a/gcc/testsuite/gcc.dg/guality/pr68037-2.c b/gcc/testsuite/gcc.dg/guality/pr68037-2.c new file mode 100644 index 00000000000..da250289816 --- /dev/null +++ b/gcc/testsuite/gcc.dg/guality/pr68037-2.c @@ -0,0 +1,60 @@ +/* { dg-do run { target i?86-*-* x86_64-*-* } } */ +/* { dg-options "-g -mno-mpx -mno-sse -mno-mmx -mno-80387" } */ + +extern void exit (int); + +typedef unsigned int uword_t __attribute__ ((mode (__word__))); + +#define IP 0x12345671 +#define CS 0x12345672 +#define FLAGS 0x12345673 +#define SP 0x12345674 +#define SS 0x12345675 + +#define STRING(x) XSTRING(x) +#define XSTRING(x) #x + +struct interrupt_frame +{ + uword_t ip; + uword_t cs; + uword_t flags; + uword_t sp; + uword_t ss; +}; + +__attribute__((interrupt, used)) +void +fn (struct interrupt_frame *frame) +{ + if (IP != frame->ip) /* BREAK */ + __builtin_abort (); + if (CS != frame->cs) + __builtin_abort (); + if (FLAGS != frame->flags) + __builtin_abort (); + if (SP != frame->sp) + __builtin_abort (); + if (SS != frame->ss) + __builtin_abort (); + + exit (0); +} + +int +main () +{ + asm ("push $" STRING (SS) "; \ + push $" STRING (SP) "; \ + push $" STRING (FLAGS) "; \ + push $" STRING (CS) "; \ + push $" STRING (IP) "; \ + jmp fn"); + return 0; +} + +/* { dg-final { gdb-test 30 "frame->ip" "0x12345671" } } */ +/* { dg-final { gdb-test 30 "frame->cs" "0x12345672" } } */ +/* { dg-final { gdb-test 30 "frame->flags" "0x12345673" } } */ +/* { dg-final { gdb-test 30 "frame->sp" "0x12345674" } } */ +/* { dg-final { gdb-test 30 "frame->ss" "0x12345675" } } */ diff --git a/gcc/testsuite/gcc.dg/guality/pr68037-3.c b/gcc/testsuite/gcc.dg/guality/pr68037-3.c new file mode 100644 index 00000000000..7f9af3dd152 --- /dev/null +++ b/gcc/testsuite/gcc.dg/guality/pr68037-3.c @@ -0,0 +1,76 @@ +/* { dg-do run { target i?86-*-* x86_64-*-* } } */ +/* { dg-options "-g -mno-mpx -mno-sse -mno-mmx -mno-80387" } */ + +#include <stddef.h> + +extern void exit (int); + +typedef unsigned int uword_t __attribute__ ((mode (__word__))); +typedef int aligned __attribute__((aligned(64))); + +#define IP 0x12345671 +#define CS 0x12345672 +#define FLAGS 0x12345673 +#define SP 0x12345674 +#define SS 0x12345675 + +#define STRING(x) XSTRING(x) +#define XSTRING(x) #x + +struct interrupt_frame +{ + uword_t ip; + uword_t cs; + uword_t flags; + uword_t sp; + uword_t ss; +}; + +int +check_int (int *i, int align) +{ + *i = 20; + if ((((ptrdiff_t) i) & (align - 1)) != 0) + __builtin_abort (); + return *i; +} + +__attribute__((interrupt, used)) +void +fn (struct interrupt_frame *frame) +{ + aligned i; + if (check_int (&i, __alignof__(i)) != i) + __builtin_abort (); + + if (IP != frame->ip) /* BREAK */ + __builtin_abort (); + if (CS != frame->cs) + __builtin_abort (); + if (FLAGS != frame->flags) + __builtin_abort (); + if (SP != frame->sp) + __builtin_abort (); + if (SS != frame->ss) + __builtin_abort (); + + exit (0); +} + +int +main () +{ + asm ("push $" STRING (SS) "; \ + push $" STRING (SP) "; \ + push $" STRING (FLAGS) "; \ + push $" STRING (CS) "; \ + push $" STRING (IP) "; \ + jmp fn"); + return 0; +} + +/* { dg-final { gdb-test 46 "frame->ip" "0x12345671" } } */ +/* { dg-final { gdb-test 46 "frame->cs" "0x12345672" } } */ +/* { dg-final { gdb-test 46 "frame->flags" "0x12345673" } } */ +/* { dg-final { gdb-test 46 "frame->sp" "0x12345674" } } */ +/* { dg-final { gdb-test 46 "frame->ss" "0x12345675" } } */ diff --git a/gcc/testsuite/gcc.dg/torture/pr68037-1.c b/gcc/testsuite/gcc.dg/torture/pr68037-1.c new file mode 100644 index 00000000000..32336b013ff --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/pr68037-1.c @@ -0,0 +1,58 @@ +/* { dg-do run { target i?86-*-* x86_64-*-* } } */ +/* { dg-options "-mno-mpx -mno-sse -mno-mmx -mno-80387" } */ + +extern void exit (int); + +typedef unsigned int uword_t __attribute__ ((mode (__word__))); + +#define ERROR 0x12345670 +#define IP 0x12345671 +#define CS 0x12345672 +#define FLAGS 0x12345673 +#define SP 0x12345674 +#define SS 0x12345675 + +#define STRING(x) XSTRING(x) +#define XSTRING(x) #x + +struct interrupt_frame +{ + uword_t ip; + uword_t cs; + uword_t flags; + uword_t sp; + uword_t ss; +}; + +__attribute__((interrupt, used)) +void +fn (struct interrupt_frame *frame, uword_t error) +{ + if (ERROR != error) + __builtin_abort (); + if (IP != frame->ip) + __builtin_abort (); + if (CS != frame->cs) + __builtin_abort (); + if (FLAGS != frame->flags) + __builtin_abort (); + if (SP != frame->sp) + __builtin_abort (); + if (SS != frame->ss) + __builtin_abort (); + + exit (0); +} + +int +main () +{ + asm ("push $" STRING (SS) "; \ + push $" STRING (SP) "; \ + push $" STRING (FLAGS) "; \ + push $" STRING (CS) "; \ + push $" STRING (IP) "; \ + push $" STRING (ERROR) "; \ + jmp fn"); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/torture/pr68037-2.c b/gcc/testsuite/gcc.dg/torture/pr68037-2.c new file mode 100644 index 00000000000..02d7ab93bc9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/pr68037-2.c @@ -0,0 +1,54 @@ +/* { dg-do run { target i?86-*-* x86_64-*-* } } */ +/* { dg-options "-mno-mpx -mno-sse -mno-mmx -mno-80387" } */ + +extern void exit (int); + +typedef unsigned int uword_t __attribute__ ((mode (__word__))); + +#define IP 0x12345671 +#define CS 0x12345672 +#define FLAGS 0x12345673 +#define SP 0x12345674 +#define SS 0x12345675 + +#define STRING(x) XSTRING(x) +#define XSTRING(x) #x + +struct interrupt_frame +{ + uword_t ip; + uword_t cs; + uword_t flags; + uword_t sp; + uword_t ss; +}; + +__attribute__((interrupt, used)) +void +fn (struct interrupt_frame *frame) +{ + if (IP != frame->ip) + __builtin_abort (); + if (CS != frame->cs) + __builtin_abort (); + if (FLAGS != frame->flags) + __builtin_abort (); + if (SP != frame->sp) + __builtin_abort (); + if (SS != frame->ss) + __builtin_abort (); + + exit (0); +} + +int +main () +{ + asm ("push $" STRING (SS) "; \ + push $" STRING (SP) "; \ + push $" STRING (FLAGS) "; \ + push $" STRING (CS) "; \ + push $" STRING (IP) "; \ + jmp fn"); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/torture/pr68037-3.c b/gcc/testsuite/gcc.dg/torture/pr68037-3.c new file mode 100644 index 00000000000..ea1952eb095 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/pr68037-3.c @@ -0,0 +1,70 @@ +/* { dg-do run { target i?86-*-* x86_64-*-* } } */ +/* { dg-options "-mno-mpx -mno-sse -mno-mmx -mno-80387" } */ + +#include <stddef.h> + +extern void exit (int); + +typedef unsigned int uword_t __attribute__ ((mode (__word__))); +typedef int aligned __attribute__((aligned(64))); + +#define IP 0x12345671 +#define CS 0x12345672 +#define FLAGS 0x12345673 +#define SP 0x12345674 +#define SS 0x12345675 + +#define STRING(x) XSTRING(x) +#define XSTRING(x) #x + +struct interrupt_frame +{ + uword_t ip; + uword_t cs; + uword_t flags; + uword_t sp; + uword_t ss; +}; + +int +check_int (int *i, int align) +{ + *i = 20; + if ((((ptrdiff_t) i) & (align - 1)) != 0) + __builtin_abort (); + return *i; +} + +__attribute__((interrupt, used)) +void +fn (struct interrupt_frame *frame) +{ + aligned i; + if (check_int (&i, __alignof__(i)) != i) + __builtin_abort (); + + if (IP != frame->ip) + __builtin_abort (); + if (CS != frame->cs) + __builtin_abort (); + if (FLAGS != frame->flags) + __builtin_abort (); + if (SP != frame->sp) + __builtin_abort (); + if (SS != frame->ss) + __builtin_abort (); + + exit (0); +} + +int +main () +{ + asm ("push $" STRING (SS) "; \ + push $" STRING (SP) "; \ + push $" STRING (FLAGS) "; \ + push $" STRING (CS) "; \ + push $" STRING (IP) "; \ + jmp fn"); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/torture/pr68661-1a.c b/gcc/testsuite/gcc.dg/torture/pr68661-1a.c new file mode 100644 index 00000000000..50b2c7b6719 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/pr68661-1a.c @@ -0,0 +1,18 @@ +/* { dg-do run { target i?86-*-* x86_64-*-* } } */ +/* { dg-options "-mno-mpx -mno-sse -mno-mmx -mno-80387" } */ +/* { dg-additional-sources pr68661-1b.c } */ + +extern void bar0 (int, int, int, int, int, int, int, int, int) + __attribute__ ((no_caller_saved_registers)); + +void +foo (void) +{ + bar0 (0, 1, 2, 3, 4, 5, 6, 7, 8); +} + +void +bad (void) +{ + __builtin_abort (); +} diff --git a/gcc/testsuite/gcc.dg/torture/pr68661-1b.c b/gcc/testsuite/gcc.dg/torture/pr68661-1b.c new file mode 100644 index 00000000000..d86c0f082cd --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/pr68661-1b.c @@ -0,0 +1,45 @@ +/* { dg-do compile { target i?86-*-* x86_64-*-* } } */ +/* { dg-options "-mno-mpx -mno-sse -mno-mmx -mno-80387" } */ + +extern void foo (void); +extern void bad (void); + +void +__attribute__ ((no_caller_saved_registers)) +bar0 (int i0, int i1, int i2, int i3, int i4, int i5, int i6, + int i7, int i8) +{ + if (i0 != 0) + bad (); + + if (i1 != 1) + bad (); + + if (i2 != 2) + bad (); + + if (i3 != 3) + bad (); + + if (i4 != 4) + bad (); + + if (i5 != 5) + bad (); + + if (i6 != 6) + bad (); + + if (i7 != 7) + bad (); + + if (i8 != 8) + bad (); +} + +int +main () +{ + foo (); + return 0; +} diff --git a/gcc/testsuite/gcc.target/i386/interrupt-1.c b/gcc/testsuite/gcc.target/i386/interrupt-1.c new file mode 100644 index 00000000000..56f070f13da --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-1.c @@ -0,0 +1,55 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -mno-push-args -maccumulate-outgoing-args" } */ + +extern void foo (void *) __attribute__ ((interrupt)); +extern int bar (int); + +void foo (void *frame) +{ + int a,b,c,d,e,f,i; + a = bar (5); + b = bar (a); + c = bar (b); + d = bar (c); + e = bar (d); + f = bar (e); + for (i = 1; i < 10; i++) + { + a += bar (a + i) + bar (b + i) + + bar (c + i) + bar (d + i) + + bar (e + i) + bar (f + i); + } +} +/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */ +/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)bx" 1 } } */ +/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */ +/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */ +/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)si" 1 } } */ +/* { dg-final { scan-assembler-times "pushl\[\\t \]*%edi" 1 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r12" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r13" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r14" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r15" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */ +/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)bx" 1 } } */ +/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */ +/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */ +/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)si" 1 } } */ +/* { dg-final { scan-assembler-times "popl\[\\t \]*%edi" 1 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%r12" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%r13" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%r14" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%r15" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "iret" 1 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "iretq" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "\tcld" 1 } } */ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-10.c b/gcc/testsuite/gcc.target/i386/interrupt-10.c new file mode 100644 index 00000000000..b025a614c11 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-10.c @@ -0,0 +1,19 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld" } */ + +extern int check_int (int *i, void *, int align); +typedef int aligned __attribute__((aligned(64))); + +__attribute__((interrupt)) +void +foo (void *frame) +{ + aligned j; + if (check_int (frame, &j, __alignof__(j))) + __builtin_abort (); +} + +/* { dg-final { scan-assembler-times "and\[lq\]?\[^\\n\]*-64,\[^\\n\]*sp" 1 } } */ +/* { dg-final { scan-assembler-times "iret" 1 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "iretq" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "\tcld" 1 } } */ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-11.c b/gcc/testsuite/gcc.target/i386/interrupt-11.c new file mode 100644 index 00000000000..4918e76358f --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-11.c @@ -0,0 +1,41 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -mno-iamcu -mno-sse4 -mno-popcnt -maccumulate-outgoing-args" } */ + +extern int i, cnt; + +void + __attribute__ ((interrupt)) +foo (void *frame) +{ + cnt = __builtin_popcount (i); +} + +/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */ +/* { dg-final { scan-assembler-not "kmov.\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" } } */ +/* { dg-final { scan-assembler-not "kmov.\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" } } */ +/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */ +/* { dg-final { scan-assembler-not "pushl\[\\t \]*%edi" { target ia32 } } } */ +/* { dg-final { scan-assembler-not "pushl\[\\t \]*%esi" { target ia32 } } } */ +/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */ +/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */ +/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */ +/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */ +/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "iret" 1 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "iretq" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "\tcld" 1 } } */ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-12.c b/gcc/testsuite/gcc.target/i386/interrupt-12.c new file mode 100644 index 00000000000..4da81219578 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-12.c @@ -0,0 +1,30 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -mno-iamcu -mpush-args -maccumulate-outgoing-args" } */ + +typedef unsigned int uword_t __attribute__ ((mode (__word__))); +extern void bar (int) __attribute__ ((no_caller_saved_registers)); + +void + __attribute__ ((interrupt)) +fn1 (void *frame, uword_t error) +{ + bar (error); +} + +/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */ +/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(b|c|d)x" } } */ +/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */ +/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */ +/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%rax" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:r|e)bp" 1 } } */ +/* { dg-final { scan-assembler-times "leave" 1 } } */ +/* { dg-final { scan-assembler-times "pushl\[\\t \]*%eax" 1 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "movl\[\\t \]*-4\\(%ebp\\),\[\\t \]*%eax" 1 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "movq\[\\t \]*-8\\(%(?:r|e)bp\\),\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "iret" 1 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "iretq" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "\tcld" 1 } } */ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-13.c b/gcc/testsuite/gcc.target/i386/interrupt-13.c new file mode 100644 index 00000000000..7e35c291442 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-13.c @@ -0,0 +1,30 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -mno-iamcu -mno-push-args -maccumulate-outgoing-args" } */ + +typedef unsigned int uword_t __attribute__ ((mode (__word__))); +extern void bar (int) __attribute__ ((no_caller_saved_registers)); + +void + __attribute__ ((interrupt)) +fn1 (void *frame, uword_t error) +{ + bar (error); +} + +/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */ +/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(b|c|d)x" } } */ +/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */ +/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */ +/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%rax" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:r|e)bp" 1 } } */ +/* { dg-final { scan-assembler-times "leave" 1 } } */ +/* { dg-final { scan-assembler-times "pushl\[\\t \]*%eax" 1 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "movl\[\\t \]*-4\\(%ebp\\),\[\\t \]*%eax" 1 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "movq\[\\t \]*-8\\(%(?:r|e)bp\\),\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "iret" 1 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "iretq" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "\tcld" 1 } } */ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-14.c b/gcc/testsuite/gcc.target/i386/interrupt-14.c new file mode 100644 index 00000000000..fe0966d532b --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-14.c @@ -0,0 +1,32 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -mno-iamcu -mpush-args -mno-accumulate-outgoing-args" } */ + +extern void bar (int) __attribute__ ((no_caller_saved_registers)); + +void + __attribute__ ((interrupt)) +fn1 (void *frame) +{ + bar (3); +} + +void + __attribute__ ((interrupt)) +fn2 (void *frame) +{ + bar (3); +} + +/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */ +/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */ +/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */ +/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */ +/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[8-9\]+" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r1\[0-5\]+" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:r|e)bp" 2 } } */ +/* { dg-final { scan-assembler-times "leave" 2 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "movq\[\\t \]*-8\\(%(?:r|e)bp\\),\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "iret" 2 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "iretq" 2 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "\tcld" 2 } } */ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-15.c b/gcc/testsuite/gcc.target/i386/interrupt-15.c new file mode 100644 index 00000000000..754733ae904 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-15.c @@ -0,0 +1,37 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -mno-iamcu -mpush-args -maccumulate-outgoing-args" } */ + +typedef unsigned int uword_t __attribute__ ((mode (__word__))); +extern void bar (int) __attribute__ ((no_caller_saved_registers)); + +void + __attribute__ ((interrupt)) +fn1 (void *frame, uword_t error) +{ + bar (error); +} + +void + __attribute__ ((interrupt)) +fn2 (void *frame, uword_t error) +{ + bar (error); +} + +/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */ +/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(b|c|d)x" } } */ +/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */ +/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */ +/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%rax" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:r|e)bp" 2 } } */ +/* { dg-final { scan-assembler-times "leave" 2 } } */ +/* { dg-final { scan-assembler-times "pushl\[\\t \]*%eax" 2 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "movl\[\\t \]*-4\\(%ebp\\),\[\\t \]*%eax" 2 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "movq\[\\t \]*-8\\(%(?:r|e)bp\\),\[\\t \]*%rdi" 2 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 2 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 2 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "iret" 2 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "iretq" 2 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "\tcld" 2 } } */ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-16.c b/gcc/testsuite/gcc.target/i386/interrupt-16.c new file mode 100644 index 00000000000..28e3f43a990 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-16.c @@ -0,0 +1,21 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -mno-iamcu -mno-push-args -maccumulate-outgoing-args" } */ + +extern int foo (int) __attribute__ ((no_caller_saved_registers)); +extern int bar (int) __attribute__ ((no_caller_saved_registers)); + +int +foo (int i) +{ + return bar (i); +} + +/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+,\[\\t \]-*\[0-9\]*\\(%\[re\]?bp\\)" } } */ +/* { dg-final { scan-assembler-not "movups\[\\t \]*-\[0-9\]*\\(%\[re\]?bp\\),\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */ +/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */ +/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */ +/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */ +/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */ +/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-17.c b/gcc/testsuite/gcc.target/i386/interrupt-17.c new file mode 100644 index 00000000000..0125f41bb24 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-17.c @@ -0,0 +1,22 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O3 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -mno-iamcu -mno-push-args" } */ + +extern int foo (int) __attribute__ ((no_caller_saved_registers)); +extern int bar (int) __attribute__ ((no_caller_saved_registers)); + +int +foo (int i) +{ + return bar (i + 1); +} + +/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */ +/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */ +/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)si" } } */ +/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */ +/* { dg-final { scan-assembler-not "(push|pop)l\[\\t \]*%edi" { target ia32 } } } */ +/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%rdx" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "jmp" } }*/ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-18.c b/gcc/testsuite/gcc.target/i386/interrupt-18.c new file mode 100644 index 00000000000..77a554bd236 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-18.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -Wall -g" } */ + +void +__attribute__((interrupt)) +fn (void *frame) +{ +} + +/* { dg-final { scan-assembler-not "add(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */ +/* { dg-final { scan-assembler-times "iret" 1 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "iretq" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "\tcld" } } */ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-19.c b/gcc/testsuite/gcc.target/i386/interrupt-19.c new file mode 100644 index 00000000000..4492f4b296b --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-19.c @@ -0,0 +1,16 @@ +/* { dg-do compile { target ia32 } } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -mno-iamcu" } */ + +struct ret +{ + int i[8]; +}; + +extern struct ret bar (void); + +void + __attribute__ ((interrupt)) +fn (void *frame) +{ + bar (); +} /* { dg-message "sorry, unimplemented: Dynamic Realign Argument Pointer" } */ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-2.c b/gcc/testsuite/gcc.target/i386/interrupt-2.c new file mode 100644 index 00000000000..06b9a36f4c6 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-2.c @@ -0,0 +1,20 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -Wall -g" } */ + +void +__attribute__((interrupt)) +fn (void *frame) +{ +} + +typedef void (*fn_t) (void *) __attribute__((interrupt)); + +fn_t fns[] = +{ + fn, +}; + +/* { dg-final { scan-assembler-not "add(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */ +/* { dg-final { scan-assembler-times "iret" 1 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "iretq" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "\tcld" } } */ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-20.c b/gcc/testsuite/gcc.target/i386/interrupt-20.c new file mode 100644 index 00000000000..6ef13b6be85 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-20.c @@ -0,0 +1,29 @@ +/* { dg-do compile { target ia32 } } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -miamcu -maccumulate-outgoing-args" } */ + +struct interrupt_frame; + +void (*callback[1])(unsigned int id, unsigned int len); +unsigned int remaining; + +void +handler(int uart) +{ + while (1) { + if (remaining) { + callback[uart](0, 0); + break; + } + } +} + +int uart; + +void +__attribute__((interrupt)) +my_isr(struct interrupt_frame *frame) +{ + handler(uart); +} + +/* { dg-final { scan-assembler-times "\tcld" 1 } } */ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-21.c b/gcc/testsuite/gcc.target/i386/interrupt-21.c new file mode 100644 index 00000000000..ddbaf61ca72 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-21.c @@ -0,0 +1,30 @@ +/* { dg-do compile { target ia32 } } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -miamcu -maccumulate-outgoing-args" } */ + +struct interrupt_frame; + +void (*callback[1])(unsigned int id, unsigned int len); +unsigned int remaining; + +void +__attribute__((no_caller_saved_registers)) +handler(int uart) +{ + while (1) { + if (remaining) { + callback[uart](0, 0); + break; + } + } +} + +int uart; + +void +__attribute__((interrupt)) +my_isr(struct interrupt_frame *frame) +{ + handler(uart); +} + +/* { dg-final { scan-assembler-times "\tcld" 1 } } */ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-22.c b/gcc/testsuite/gcc.target/i386/interrupt-22.c new file mode 100644 index 00000000000..5de2018961b --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-22.c @@ -0,0 +1,29 @@ +/* { dg-do compile { target ia32 } } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -miamcu -maccumulate-outgoing-args" } */ + +struct interrupt_frame; + +void (*callback) (unsigned int id, unsigned int len) + __attribute__((no_caller_saved_registers)); +unsigned int remaining; + +void +__attribute__((no_caller_saved_registers)) +handler(void) +{ + while (1) { + if (remaining) { + callback(0, 0); + break; + } + } +} + +void +__attribute__((interrupt)) +my_isr(struct interrupt_frame *frame) +{ + handler(); +} + +/* { dg-final { scan-assembler-times "\tcld" 1 } } */ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-23.c b/gcc/testsuite/gcc.target/i386/interrupt-23.c new file mode 100644 index 00000000000..c42049c1280 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-23.c @@ -0,0 +1,46 @@ +/* { dg-do compile { target ia32 } } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -miamcu -maccumulate-outgoing-args" } */ + +struct interrupt_frame; + +extern void callback0 (unsigned int id, unsigned int len) + __attribute__((no_caller_saved_registers)); +extern void callback1 (unsigned int id, unsigned int len) + __attribute__((no_caller_saved_registers)); +extern void callback2 (unsigned int id, unsigned int len) + __attribute__((no_caller_saved_registers)); + +typedef void (*callback_t) (unsigned int id, unsigned int len) + __attribute__((no_caller_saved_registers)); + +callback_t callback[] = +{ + callback0, + callback1, + callback2, +}; + +unsigned int remaining; + +void +__attribute__((no_caller_saved_registers)) +handler(int uart) +{ + while (1) { + if (remaining) { + callback[uart](0, 0); + break; + } + } +} + +int uart; + +void +__attribute__((interrupt)) +my_isr(struct interrupt_frame *frame) +{ + handler(uart); +} + +/* { dg-final { scan-assembler-times "\tcld" 1 } } */ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-24.c b/gcc/testsuite/gcc.target/i386/interrupt-24.c new file mode 100644 index 00000000000..1669cda1736 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-24.c @@ -0,0 +1,19 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -mno-iamcu -mno-push-args" } */ + +extern void bar (void) __attribute__ ((noreturn)); + +void +__attribute__ ((no_caller_saved_registers)) +foo (int i0, int i1, int i2, int i3, int i4, int i5, int i6, + int i7, int i8) +{ + if (i7) + bar (); +} + +/* { dg-final { scan-assembler-not "movups\[\\t \]*%(x|y|z)mm\[0-9\]+" } } */ +/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(a|b|c|d)x" } } */ +/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)(d|s)i" } } */ +/* { dg-final { scan-assembler-not "(push|pop)(l|q)\[\\t \]*%(r|e)bp" } } */ +/* { dg-final { scan-assembler-not "(push|pop)q\[\\t \]*%r\[0-9\]+" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-25.c b/gcc/testsuite/gcc.target/i386/interrupt-25.c new file mode 100644 index 00000000000..30e3e7f92a4 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-25.c @@ -0,0 +1,54 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -mno-push-args -maccumulate-outgoing-args" } */ + +extern int bar (int); + +__attribute__((no_caller_saved_registers)) +void +foo (void) +{ + int a,b,c,d,e,f,i; + a = bar (5); + b = bar (a); + c = bar (b); + d = bar (c); + e = bar (d); + f = bar (e); + for (i = 1; i < 10; i++) + { + a += bar (a + i) + bar (b + i) + + bar (c + i) + bar (d + i) + + bar (e + i) + bar (f + i); + } +} + +/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */ +/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)bx" 1 } } */ +/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */ +/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */ +/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)si" 1 } } */ +/* { dg-final { scan-assembler-times "pushl\[\\t \]*%edi" 1 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r12" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r13" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r14" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r15" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */ +/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)bx" 1 } } */ +/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */ +/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */ +/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)si" 1 } } */ +/* { dg-final { scan-assembler-times "popl\[\\t \]*%edi" 1 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%r12" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%r13" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%r14" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%r15" 1 { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-26.c b/gcc/testsuite/gcc.target/i386/interrupt-26.c new file mode 100644 index 00000000000..14e43558c93 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-26.c @@ -0,0 +1,16 @@ +/* { dg-do compile } */ +/* { dg-options "-Os -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld" } */ + + +extern void *a; +extern int b; + +__attribute__ ((interrupt)) +void +foo (void *frame) +{ + __builtin_memset (a, b, 40); +} + +/* { dg-final { scan-assembler "stosb" } } */ +/* { dg-final { scan-assembler-times "\tcld" 1 } } */ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-27.c b/gcc/testsuite/gcc.target/i386/interrupt-27.c new file mode 100644 index 00000000000..5bed18f24b7 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-27.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mcld" } */ + +extern void bar (void); + +void +__attribute__ ((interrupt)) +foo (void *frame) +{ + bar (); +} + +/* { dg-final { scan-assembler-times "iret" 1 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "iretq" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "\tcld" } } */ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-28.c b/gcc/testsuite/gcc.target/i386/interrupt-28.c new file mode 100644 index 00000000000..58c68c79673 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-28.c @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mcld" } */ + +__attribute__ ((interrupt)) +void +foo (void *frame) +{ +} + +/* { dg-final { scan-assembler-times "iret" 1 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "iretq" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "\tcld" } } */ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-3.c b/gcc/testsuite/gcc.target/i386/interrupt-3.c new file mode 100644 index 00000000000..ee7351e32e8 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-3.c @@ -0,0 +1,16 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -g" } */ + +typedef unsigned int uword_t __attribute__ ((mode (__word__))); + +void +__attribute__((interrupt)) +fn (void* frame, uword_t error) +{ +} + +/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "iret" 1 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "iretq" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "\tcld" } } */ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-387-err-1.c b/gcc/testsuite/gcc.target/i386/interrupt-387-err-1.c new file mode 100644 index 00000000000..3dff0345a53 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-387-err-1.c @@ -0,0 +1,16 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-cld -mno-iamcu -m80387" } */ + +typedef unsigned int uword_t __attribute__ ((mode (__word__))); + +void +__attribute__((interrupt)) +fn1 (void *frame, uword_t error) +{ /* { dg-message "80387 instructions aren't allowed in exception service routine" } */ +} + +void +__attribute__((interrupt)) +fn2 (void *frame) +{ /* { dg-message "80387 instructions aren't allowed in interrupt service routine" } */ +} diff --git a/gcc/testsuite/gcc.target/i386/interrupt-387-err-2.c b/gcc/testsuite/gcc.target/i386/interrupt-387-err-2.c new file mode 100644 index 00000000000..d7a54c8ec74 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-387-err-2.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-cld -m80387 -mlong-double-80 -mno-iamcu" } */ + +void +__attribute__((no_caller_saved_registers)) +fn1 (void) +{ /* { dg-message "80387 instructions aren't allowed in function with no_caller_saved_registers attribute" } */ +} diff --git a/gcc/testsuite/gcc.target/i386/interrupt-4.c b/gcc/testsuite/gcc.target/i386/interrupt-4.c new file mode 100644 index 00000000000..eb1195defd8 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-4.c @@ -0,0 +1,32 @@ +/* { dg-do link } */ +/* { dg-options "-O -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -g" } */ + +#include <stdint.h> + +extern void link_error (void); + +typedef unsigned int uword_t __attribute__ ((mode (__word__))); + +struct interrupt_frame +{ + uword_t ip; + uword_t cs; + uword_t flags; + uword_t sp; + uword_t ss; +}; + +__attribute__ ((used, interrupt)) +void +foo (struct interrupt_frame *frame) +{ + void *ra = __builtin_return_address (0); + if ((uintptr_t) ra != (uintptr_t) frame->ip) + link_error (); +} + +int +main (void) +{ + return 0; +} diff --git a/gcc/testsuite/gcc.target/i386/interrupt-5.c b/gcc/testsuite/gcc.target/i386/interrupt-5.c new file mode 100644 index 00000000000..21ffea3bc13 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-5.c @@ -0,0 +1,23 @@ +/* { dg-do link } */ +/* { dg-options "-O -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -g" } */ + +#include <stdint.h> + +extern void link_error (void); + +typedef unsigned int uword_t __attribute__ ((mode (__word__))); + +__attribute__ ((used, interrupt)) +void +foo (void *frame, uword_t error) +{ + void *ra = __builtin_return_address (0); + if ((uintptr_t) ra != (uintptr_t) error) + link_error (); +} + +int +main (void) +{ + return 0; +} diff --git a/gcc/testsuite/gcc.target/i386/interrupt-6.c b/gcc/testsuite/gcc.target/i386/interrupt-6.c new file mode 100644 index 00000000000..3bbe0da7c39 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-6.c @@ -0,0 +1,40 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld" } */ + +typedef unsigned int uword_t __attribute__ ((mode (__word__))); + +extern int error; + +__attribute__((interrupt)) +void +fn1 (void *p, short error_code) +{ /* { dg-error "interrupt service routine should have unsigned \(long long |long |\)int as the second argument" } */ +} + +__attribute__((interrupt)) +void +fn2 (void) +{ /* { dg-error "interrupt service routine can only have a pointer argument and an optional integer argument" } */ +} + +__attribute__((interrupt)) +void +fn3 (uword_t error_code) +{ /* { dg-error "interrupt service routine should have a pointer as the first argument" } */ + error = error_code; +} + +__attribute__((interrupt)) +void +fn4 (uword_t error_code, void *frame) +{ /* { dg-error "interrupt service routine should have .* the .* argument" } */ + error = error_code; +} + +extern int fn5 (void *) __attribute__ ((interrupt)); /* { dg-error "interrupt service routine can't have non-void return value" } */ + +int +fn5 (void *frame) +{ + return 0; +} diff --git a/gcc/testsuite/gcc.target/i386/interrupt-7.c b/gcc/testsuite/gcc.target/i386/interrupt-7.c new file mode 100644 index 00000000000..23c748871d8 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-7.c @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld" } */ + +extern int error; + +extern void fn (void *) __attribute__((interrupt)); + +void +foo (void) +{ + fn (&error); /* { dg-error "interrupt service routine can't be called directly" } */ +} diff --git a/gcc/testsuite/gcc.target/i386/interrupt-8.c b/gcc/testsuite/gcc.target/i386/interrupt-8.c new file mode 100644 index 00000000000..f0e16378f52 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-8.c @@ -0,0 +1,38 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -mno-iamcu -maccumulate-outgoing-args" } */ + +extern void bar (void); + +void + __attribute__ ((interrupt)) +foo (void *frame) +{ + bar (); +} + +/* { dg-final { scan-assembler-not "movups\[\\t .\]*%(x|y|z)mm\[0-9\]+" } } */ +/* { dg-final { scan-assembler-not "kmov.\[\\t \]*%k\[0-7\]+,\[\\t \]*\[\\-]?\[0-9\]*\\(%\[re\]?sp\\)" } } */ +/* { dg-final { scan-assembler-not "kmov.\[\\t \]*\[0-9\]*\\(%\[re\]?sp\\),\[\\t \]*%k\[0-7\]+" } } */ +/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rbx" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r1\[2-5\]+" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "pushl\[\\t \]*%ebx" { target ia32 } } } */ +/* { dg-final { scan-assembler-not "pushl\[\\t \]*%e(s|d)i" { target ia32 } } } */ +/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */ +/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */ +/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */ +/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */ +/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%rdi" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%rsi" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "\tcld" 1 } } */ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-9.c b/gcc/testsuite/gcc.target/i386/interrupt-9.c new file mode 100644 index 00000000000..827196ed100 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-9.c @@ -0,0 +1,22 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld" } */ + +extern int check_int (int *i, void *, int align); +typedef int aligned __attribute__((aligned(64))); +typedef unsigned int uword_t __attribute__ ((mode (__word__))); + +__attribute__((interrupt)) +void +foo (void *frame, uword_t error_code) +{ + aligned j; + if (check_int (frame, &j, __alignof__(j))) + __builtin_abort (); +} + +/* { dg-final { scan-assembler-times "and\[lq\]?\[^\\n\]*-64,\[^\\n\]*sp" 1 } } */ +/* { dg-final { scan-assembler-times "add(?:l|q)\[\\t \]*\\\$8,\[\\t \]*%\[re\]?sp" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "addl\[\\t \]*\\\$4,\[\\t \]*%esp" 1 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "iret" 1 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "iretq" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "\tcld" 1 } } */ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-bnd-err-1.c b/gcc/testsuite/gcc.target/i386/interrupt-bnd-err-1.c new file mode 100644 index 00000000000..6fd811705fc --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-bnd-err-1.c @@ -0,0 +1,16 @@ +/* { dg-do compile { target { ! x32 } } } */ +/* { dg-options "-O2 -mno-sse -mno-mmx -mno-80387 -mno-cld -mno-iamcu -mmpx" } */ + +typedef unsigned int uword_t __attribute__ ((mode (__word__))); + +void +__attribute__((interrupt)) +fn1 (void *frame) +{ /* { dg-message "MPX instructions aren't allowed in interrupt service routine" } */ +} + +void +__attribute__((interrupt)) +fn2 (void *frame, uword_t error) +{ /* { dg-message "MPX instructions aren't allowed in exception service routine" } */ +} diff --git a/gcc/testsuite/gcc.target/i386/interrupt-bnd-err-2.c b/gcc/testsuite/gcc.target/i386/interrupt-bnd-err-2.c new file mode 100644 index 00000000000..1bad58d65f3 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-bnd-err-2.c @@ -0,0 +1,8 @@ +/* { dg-do compile { target { ! x32 } } } */ +/* { dg-options "-O2 -mno-sse -mno-mmx -mno-80387 -mno-cld -mno-iamcu -mmpx" } */ + +void +__attribute__((no_caller_saved_registers)) +fn (void *frame) +{ /* { dg-message "MPX instructions aren't allowed in function with no_caller_saved_registers attribute" } */ +} diff --git a/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c new file mode 100644 index 00000000000..28c50fc1c14 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c @@ -0,0 +1,36 @@ +/* { dg-do compile { target ia32 } } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -miamcu -maccumulate-outgoing-args" } */ + +extern void foo (void *) __attribute__ ((interrupt)); +extern int bar (int); + +void foo (void *frame) +{ + int a,b,c,d,e,f,i; + a = bar (5); + b = bar (a); + c = bar (b); + d = bar (c); + e = bar (d); + f = bar (e); + for (i = 1; i < 10; i++) + a += bar (a + i) + bar (b + i) + + bar (c + i) + bar (d + i) + + bar (e + i) + bar (f+i); +} + +/* { dg-final { scan-assembler-times "pushl\[\\t \]*%eax" 1 } } */ +/* { dg-final { scan-assembler-times "pushl\[\\t \]*%ebx" 1 } } */ +/* { dg-final { scan-assembler-times "pushl\[\\t \]*%ecx" 1 } } */ +/* { dg-final { scan-assembler-times "pushl\[\\t \]*%edx" 1 } } */ +/* { dg-final { scan-assembler-times "pushl\[\\t \]*%edi" 1 } } */ +/* { dg-final { scan-assembler-times "pushl\[\\t \]*%esi" 1 } } */ +/* { dg-final { scan-assembler-times "pushl\[\\t \]*%ebp" 1 } } */ +/* { dg-final { scan-assembler-times "popl\[\\t \]*%eax" 1 } } */ +/* { dg-final { scan-assembler-times "popl\[\\t \]*%ecx" 1 } } */ +/* { dg-final { scan-assembler-times "popl\[\\t \]*%edx" 1 } } */ +/* { dg-final { scan-assembler-times "popl\[\\t \]*%edi" 1 } } */ +/* { dg-final { scan-assembler-times "popl\[\\t \]*%esi" 1 } } */ +/* { dg-final { scan-assembler-times "popl\[\\t \]*%ebp" 1 } } */ +/* { dg-final { scan-assembler-times "iret" 1 } } */ +/* { dg-final { scan-assembler-times "\tcld" 1 } } */ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-mmx-err-1.c b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err-1.c new file mode 100644 index 00000000000..d46b21d8653 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err-1.c @@ -0,0 +1,16 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-80387 -mmmx -mno-cld -mno-iamcu" } */ + +typedef unsigned int uword_t __attribute__ ((mode (__word__))); + +void +__attribute__((interrupt)) +fn1 (void *frame) +{ /* { dg-message "MMX/3Dnow instructions aren't allowed in interrupt service routine" } */ +} + +void +__attribute__((interrupt)) +fn2 (void *frame, uword_t error) +{ /* { dg-message "MMX/3Dnow instructions aren't allowed in exception service routine" } */ +} diff --git a/gcc/testsuite/gcc.target/i386/interrupt-mmx-err-2.c b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err-2.c new file mode 100644 index 00000000000..e1d34e523bc --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err-2.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-80387 -mmmx -mno-cld -mno-iamcu" } */ + +void +__attribute__((no_caller_saved_registers)) +fn1 (void) +{ /* { dg-message "MMX/3Dnow instructions aren't allowed in function with no_caller_saved_registers attribute" } */ +} diff --git a/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c b/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c new file mode 100644 index 00000000000..e0ff18217be --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c @@ -0,0 +1,32 @@ +/* { dg-do compile { target { ! ia32 } } } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -mred-zone" } */ + +void +__attribute__((interrupt)) +fn (void *frame) +{ + /* No need to adjust stack if less than 128 bytes are used on stack + with a 128-byte red zone. */ + long long int i0; + long long int i1; + long long int i2; + long long int i3; + long long int i4; + long long int i5; + long long int i6; + long long int i7; + long long int i8; + long long int i9; + long long int i10; + long long int i11; + long long int i12; + long long int i13; + asm ("# %0, %1, %2, %3, %4, %5, %6, %7" + : "=m" (i0), "=m" (i1), "=m" (i2), "=m" (i3), + "=m" (i4), "=m" (i5), "=m" (i6), "=m" (i7), + "=m" (i8), "=m" (i9), "=m" (i10), "=m" (i11), + "=m" (i12), "=m" (i13)); +} + +/* { dg-final { scan-assembler-not "(sub|add)(l|q)\[\\t \]*\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" } } */ +/* { dg-final { scan-assembler-not "\tcld" } } */ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c b/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c new file mode 100644 index 00000000000..df916cad0da --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c @@ -0,0 +1,33 @@ +/* { dg-do compile { target { ! ia32 } } } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -mred-zone" } */ + +void +__attribute__((interrupt)) +fn (void *frame) +{ + /* Need to adjust stack if more than 128 bytes are used on stack + with a 128-byte red zone. */ + long long int i0; + long long int i1; + long long int i2; + long long int i3; + long long int i4; + long long int i5; + long long int i6; + long long int i7; + long long int i8; + long long int i9; + long long int i10; + long long int i11; + long long int i12; + long long int i13; + char c; + asm ("# %0, %1, %2, %3, %4, %5, %6, %7" + : "=m" (i0), "=m" (i1), "=m" (i2), "=m" (i3), + "=m" (i4), "=m" (i5), "=m" (i6), "=m" (i7), + "=m" (i8), "=m" (i9), "=m" (i10), "=m" (i11), + "=m" (i12), "=m" (i13), "=m" (c)); +} + +/* { dg-final { scan-assembler-times "(?:sub|add)(?:l|q)\[\\t \]*\\\$\[0-9\]*,\[\\t \]*%\[re\]?sp" 2 } } */ +/* { dg-final { scan-assembler-not "\tcld" } } */ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-sibcall-1.c b/gcc/testsuite/gcc.target/i386/interrupt-sibcall-1.c new file mode 100644 index 00000000000..7e20d5808c4 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-sibcall-1.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld" } */ + +extern void foo (void *) __attribute__ ((interrupt)); +extern void bar (void); + +void foo (void *frame) +{ + bar (); +} +/* { dg-final { scan-assembler-not "jmp" } } */ +/* { dg-final { scan-assembler-times "iret" 1 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "iretq" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "\tcld" 1 } } */ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-sibcall-2.c b/gcc/testsuite/gcc.target/i386/interrupt-sibcall-2.c new file mode 100644 index 00000000000..51c0478d2bc --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-sibcall-2.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -mpreferred-stack-boundary=3" { target { ! { ia32 } } } } */ +/* { dg-options "-O3 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld -mpreferred-stack-boundary=2" { target { ia32 } } } */ + +extern void foo (void *) __attribute__ ((interrupt)); +extern void bar (void) __attribute__ ((no_caller_saved_registers)); + +void foo (void *frame) +{ + bar (); +} +/* { dg-final { scan-assembler-not "jmp" } } */ +/* { dg-final { scan-assembler-times "iret" 1 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "iretq" 1 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "\tcld" 1 } } */ diff --git a/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c new file mode 100644 index 00000000000..f23e6f9870c --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mno-mpx -mno-sse -mno-mmx -mno-80387 -mno-cld" } */ + +extern void bar (int); + +void f1 (void){ bar (1); } +__attribute__((interrupt)) +void f2 (void *frame){ bar (2); } +void f3 (void){ bar (3); } +__attribute__((interrupt)) +void f4 (void *frame){ bar (4); } +void f5 (void){ bar (5); } + +/* { dg-final { scan-assembler-times "push.\t%.ax" 2 } } */ +/* { dg-final { scan-assembler-times "pop.\t%.ax" 2 } } */ +/* { dg-final { scan-assembler-times "iret" 2 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "iretq" 2 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "\tcld" 2 } } */ |