Commit f8071c05 by H.J. Lu Committed by H.J. Lu

Implement x86 interrupt attribute

The interrupt and exception handlers are called by x86 processors.  X86
hardware pushes information onto stack and calls the handler.  The
requirements are

1. Both interrupt and exception handlers must use the 'IRET' instruction,
instead of the 'RET' instruction, to return from the handlers.
2. All registers are callee-saved in interrupt and exception handlers.
3. The difference between interrupt and exception handlers is the
exception handler must pop 'ERROR_CODE' off the stack before the 'IRET'
instruction.

The design goals of interrupt and exception handlers for x86 processors
are:

1. Support both 32-bit and 64-bit modes.
2. Flexible for compilers to optimize.
3. Easy to use by programmers.

To implement interrupt and exception handlers for x86 processors, a
compiler should support:

'interrupt' attribute

Use this attribute to indicate that the specified function with
mandatory arguments is an interrupt or exception handler.  The compiler
generates function entry and exit sequences suitable for use in an
interrupt handler when this attribute is present.  The 'IRET' instruction,
instead of the 'RET' instruction, is used to return from interrupt or
exception handlers.  All registers, except for the EFLAGS register which
is restored by the 'IRET' instruction, are preserved by the compiler.
Since GCC doesn't preserve MPX, SSE, MMX nor x87 states, the GCC option,
-mgeneral-regs-only, should be used to compile interrupt and exception
handlers.

Note for compiler implementers: If the compiler generates MPX, SSE, MMX
or x87 instructions in an interrupt or exception handler, or functions
called from an interrupt or exception 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 (exception)
handlers is undetermined, cld instruction must be emitted in function
prologue if rep string instructions are used in interrupt (exception)
handler or interrupt (exception) handler isn't a leaf function.

Any interruptible-without-stack-switch code must be compiled with
-mno-red-zone since interrupt handlers can and will, because of the
hardware design, touch the red zone.

1. interrupt handler must be declared with a mandatory pointer argument:

struct interrupt_frame;

__attribute__ ((interrupt))
void
f (struct interrupt_frame *frame)
{
...
}

and user must properly define the structure the pointer pointing to.

2. exception handler:

The exception handler is very similar to the interrupt handler with
a different mandatory function signature:

typedef unsigned int uword_t __attribute__ ((mode (__word__)));

struct interrupt_frame;

__attribute__ ((interrupt))
void
f (struct interrupt_frame *frame, uword_t error_code)
{
...
}

and compiler pops the error code off stack before the 'IRET' instruction.

The exception handler should only be used for exceptions which push an
error code and all other exceptions must use the interrupt handler.
The system will crash if the wrong handler is used.

'no_caller_saved_registers' attribute

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.
Since GCC doesn't preserve MPX, SSE, MMX nor x87 states, the GCC option,
-mgeneral-regs-only, should be used to compile functions with
'no_caller_saved_registers'attribute.

Note for compiler implementers: If the compiler generates MPX, SSE,
MMX or x87 instructions in a function with 'no_caller_saved_registers'
attribute or functions called from a function with
'no_caller_saved_registers' attribute may contain MPX, SSE, MMX or x87
instructions, the compiler must save and restore the corresponding state.

The user can call functions specified with 'no_caller_saved_registers'
attribute from an interrupt handler without saving and restoring all
call clobbered registers.

On x86, interrupt handlers are only called by processors which push
interrupt data onto stack at the address where the normal return address
is.  Interrupt handlers must access interrupt data via pointers so that
they can update interrupt data.

gcc/

	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.

gcc/testsuite/

	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.

Co-Authored-By: Julia Koval <julia.koval@intel.com>

From-SVN: r237073
parent 9de2e795
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
......
......@@ -286,6 +286,8 @@ extern rtx maybe_get_pool_constant (rtx);
extern char internal_label_prefix[16];
extern int internal_label_prefix_len;
extern bool ix86_epilogue_uses (int);
struct ix86_address
{
rtx base, index, disp;
......
......@@ -1637,11 +1637,18 @@ enum reg_class
If stack probes are required, the space used for large function
arguments on the stack must also be probed, so enable
-maccumulate-outgoing-args so this happens in the prologue. */
-maccumulate-outgoing-args so this happens in the prologue.
We must use argument accumulation in interrupt function if stack
may be realigned to avoid DRAP. */
#define ACCUMULATE_OUTGOING_ARGS \
((TARGET_ACCUMULATE_OUTGOING_ARGS && optimize_function_for_speed_p (cfun)) \
|| TARGET_STACK_PROBE || TARGET_64BIT_MS_ABI \
((TARGET_ACCUMULATE_OUTGOING_ARGS \
&& optimize_function_for_speed_p (cfun)) \
|| (cfun->machine->func_type != TYPE_NORMAL \
&& crtl->stack_realign_needed) \
|| TARGET_STACK_PROBE \
|| TARGET_64BIT_MS_ABI \
|| (TARGET_MACHO && crtl->profile))
/* If defined, a C expression whose value is nonzero when we want to use PUSH
......@@ -1751,6 +1758,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. */
......@@ -2466,6 +2478,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;
......@@ -2520,6 +2545,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
......
......@@ -194,6 +194,9 @@
UNSPEC_BNDCU
UNSPEC_BNDCN
UNSPEC_MPX_FENCE
;; IRET support
UNSPEC_INTERRUPT_RETURN
])
(define_c_enum "unspecv" [
......@@ -12437,6 +12440,14 @@
(set_attr "modrm" "0")
(set_attr "maybe_prefix_bnd" "1")])
(define_insn "interrupt_return"
[(simple_return)
(unspec [(const_int 0)] UNSPEC_INTERRUPT_RETURN)]
"reload_completed"
{
return TARGET_64BIT ? "iretq" : "iret";
})
;; Used by x86_machine_dependent_reorg to avoid penalty on single byte RET
;; instruction Athlon and K8 have.
......
......@@ -5266,6 +5266,79 @@ 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. 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
......
/* { 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" } } */
/* { 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" } } */
/* { 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" } } */
/* { 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;
}
/* { 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;
}
/* { 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;
}
/* { 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 ();
}
/* { 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;
}
/* { 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 } } */
/* { 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 } } */
/* { 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 } } */
/* { 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 } } */
/* { 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 } } */
/* { 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 } } */
/* { 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 } } */
/* { 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 } } } } */
/* { 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" } }*/
/* { 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" } } */
/* { 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" } */
/* { 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" } } */
/* { 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 } } */
/* { 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 } } */
/* { 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 } } */
/* { 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 } } */
/* { 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 } } } } */
/* { 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 } } } } */
/* { 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 } } */
/* { 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" } } */
/* { 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" } } */
/* { 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" } } */
/* { 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" } */
}
/* { 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" } */
}
/* { 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;
}
/* { 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;
}
/* { 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;
}
/* { 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" } */
}
/* { 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 } } */
/* { 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 } } */
/* { 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" } */
}
/* { 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" } */
}
/* { 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 } } */
/* { 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" } */
}
/* { 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" } */
}
/* { 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" } } */
/* { 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" } } */
/* { 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 } } */
/* { 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 } } */
/* { 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 } } */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment