public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] x86 interrupt attribute patch [2/2]
@ 2016-04-20 13:42 Koval, Julia
  2016-05-10 14:54 ` Koval, Julia
  2016-05-10 20:02 ` Sandra Loosemore
  0 siblings, 2 replies; 8+ messages in thread
From: Koval, Julia @ 2016-04-20 13:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: Lu, Hongjiu, vaalfreja, ubizjak, law, Zamyatin, Igor

[-- Attachment #1: Type: text/plain, Size: 9468 bytes --]

Hi,
Here is the new version of interrupt attribute patch. Bootstraped/regtested for Linux/x86_64. Ok for trunk?
    
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.
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.
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.

[-- Attachment #2: 0001-Implement-x86-interrupt-attribute.patch --]
[-- Type: application/octet-stream, Size: 101572 bytes --]

From c63e1f238ddcf1c932e57b96cfcdafceec3f3966 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Fri, 5 Feb 2016 04:43:16 -0800
Subject: [PATCH] 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.
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.
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.
---
 gcc/config/i386/i386-protos.h                      |   2 +
 gcc/config/i386/i386.c                             | 355 ++++++++++++++++++++-
 gcc/config/i386/i386.h                             |  38 ++-
 gcc/config/i386/i386.md                            |  11 +
 gcc/doc/extend.texi                                |  77 +++++
 gcc/testsuite/gcc.dg/guality/pr68037-1.c           |  65 ++++
 gcc/testsuite/gcc.dg/guality/pr68037-2.c           |  60 ++++
 gcc/testsuite/gcc.dg/guality/pr68037-3.c           |  76 +++++
 gcc/testsuite/gcc.dg/torture/pr68037-1.c           |  58 ++++
 gcc/testsuite/gcc.dg/torture/pr68037-2.c           |  54 ++++
 gcc/testsuite/gcc.dg/torture/pr68037-3.c           |  70 ++++
 gcc/testsuite/gcc.dg/torture/pr68661-1a.c          |  18 ++
 gcc/testsuite/gcc.dg/torture/pr68661-1b.c          |  45 +++
 gcc/testsuite/gcc.target/i386/interrupt-1.c        |  55 ++++
 gcc/testsuite/gcc.target/i386/interrupt-10.c       |  19 ++
 gcc/testsuite/gcc.target/i386/interrupt-11.c       |  41 +++
 gcc/testsuite/gcc.target/i386/interrupt-12.c       |  30 ++
 gcc/testsuite/gcc.target/i386/interrupt-13.c       |  30 ++
 gcc/testsuite/gcc.target/i386/interrupt-14.c       |  32 ++
 gcc/testsuite/gcc.target/i386/interrupt-15.c       |  37 +++
 gcc/testsuite/gcc.target/i386/interrupt-16.c       |  21 ++
 gcc/testsuite/gcc.target/i386/interrupt-17.c       |  22 ++
 gcc/testsuite/gcc.target/i386/interrupt-18.c       |  13 +
 gcc/testsuite/gcc.target/i386/interrupt-19.c       |  16 +
 gcc/testsuite/gcc.target/i386/interrupt-2.c        |  20 ++
 gcc/testsuite/gcc.target/i386/interrupt-20.c       |  29 ++
 gcc/testsuite/gcc.target/i386/interrupt-21.c       |  30 ++
 gcc/testsuite/gcc.target/i386/interrupt-22.c       |  29 ++
 gcc/testsuite/gcc.target/i386/interrupt-23.c       |  46 +++
 gcc/testsuite/gcc.target/i386/interrupt-24.c       |  19 ++
 gcc/testsuite/gcc.target/i386/interrupt-25.c       |  54 ++++
 gcc/testsuite/gcc.target/i386/interrupt-26.c       |  16 +
 gcc/testsuite/gcc.target/i386/interrupt-27.c       |  15 +
 gcc/testsuite/gcc.target/i386/interrupt-28.c       |  12 +
 gcc/testsuite/gcc.target/i386/interrupt-3.c        |  16 +
 .../gcc.target/i386/interrupt-387-err-1.c          |  16 +
 .../gcc.target/i386/interrupt-387-err-2.c          |   8 +
 gcc/testsuite/gcc.target/i386/interrupt-4.c        |  32 ++
 gcc/testsuite/gcc.target/i386/interrupt-5.c        |  23 ++
 gcc/testsuite/gcc.target/i386/interrupt-6.c        |  40 +++
 gcc/testsuite/gcc.target/i386/interrupt-7.c        |  12 +
 gcc/testsuite/gcc.target/i386/interrupt-8.c        |  38 +++
 gcc/testsuite/gcc.target/i386/interrupt-9.c        |  22 ++
 .../gcc.target/i386/interrupt-bnd-err-1.c          |  16 +
 .../gcc.target/i386/interrupt-bnd-err-2.c          |   8 +
 gcc/testsuite/gcc.target/i386/interrupt-iamcu.c    |  36 +++
 .../gcc.target/i386/interrupt-mmx-err-1.c          |  16 +
 .../gcc.target/i386/interrupt-mmx-err-2.c          |   8 +
 .../gcc.target/i386/interrupt-redzone-1.c          |  32 ++
 .../gcc.target/i386/interrupt-redzone-2.c          |  33 ++
 .../gcc.target/i386/interrupt-sibcall-1.c          |  14 +
 .../gcc.target/i386/interrupt-sibcall-2.c          |  15 +
 .../gcc.target/i386/interrupt-switch-abi.c         |  18 ++
 53 files changed, 1903 insertions(+), 15 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/guality/pr68037-1.c
 create mode 100644 gcc/testsuite/gcc.dg/guality/pr68037-2.c
 create mode 100644 gcc/testsuite/gcc.dg/guality/pr68037-3.c
 create mode 100644 gcc/testsuite/gcc.dg/torture/pr68037-1.c
 create mode 100644 gcc/testsuite/gcc.dg/torture/pr68037-2.c
 create mode 100644 gcc/testsuite/gcc.dg/torture/pr68037-3.c
 create mode 100644 gcc/testsuite/gcc.dg/torture/pr68661-1a.c
 create mode 100644 gcc/testsuite/gcc.dg/torture/pr68661-1b.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-10.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-11.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-12.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-13.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-14.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-15.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-16.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-17.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-18.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-19.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-2.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-20.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-21.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-22.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-23.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-24.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-25.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-26.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-27.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-28.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-3.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-387-err-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-387-err-2.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-4.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-5.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-6.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-7.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-8.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-9.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-bnd-err-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-bnd-err-2.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-mmx-err-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-mmx-err-2.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-sibcall-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-sibcall-2.c
 create mode 100644 gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c

diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index ff47bc1..0159901 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -285,6 +285,8 @@ extern rtx maybe_get_pool_constant (rtx);
 extern char internal_label_prefix[16];
 extern int internal_label_prefix_len;
 
+extern bool ix86_epilogue_uses (int);
+
 struct ix86_address
 {
   rtx base, index, disp;
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 3d044e8..2147f36 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -5625,6 +5625,15 @@ ix86_conditional_register_usage (void)
 {
   int i, c_mask;
 
+  /* If there are no caller-saved registers, preserve all registers.
+     except fixed_regs and registers used for function return value
+     since aggregate_value_p checks call_used_regs[regno] on return
+     value.  */
+  if (cfun && cfun->machine->no_caller_saved_registers)
+    for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+      if (!fixed_regs[i] && !ix86_function_value_regno_p (i))
+	call_used_regs[i] = 0;
+
   /* For 32-bit targets, squash the REX registers.  */
   if (! TARGET_64BIT)
     {
@@ -6397,6 +6406,40 @@ ix86_reset_previous_fndecl (void)
   ix86_previous_fndecl = NULL_TREE;
 }
 
+/* Set the func_type field from the function FNDECL.  */
+
+static void
+ix86_set_func_type (tree fndecl)
+{
+  if (cfun->machine->func_type == TYPE_UNKNOWN)
+    {
+      if (lookup_attribute ("interrupt",
+			    TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))
+	{
+	  int nargs = 0;
+	  for (tree arg = DECL_ARGUMENTS (fndecl);
+	       arg;
+	       arg = TREE_CHAIN (arg))
+	    nargs++;
+	  cfun->machine->no_caller_saved_registers = true;
+	  cfun->machine->func_type
+	    = nargs == 2 ? TYPE_EXCEPTION : TYPE_INTERRUPT;
+
+	  /* Only dwarf2out.c can handle -WORD(AP) as a pointer argument.  */
+	  if (write_symbols != NO_DEBUG && write_symbols != DWARF2_DEBUG)
+	    sorry ("Only DWARF debug format is supported for interrupt "
+		   "service routine.");
+	}
+      else
+	{
+	  cfun->machine->func_type = TYPE_NORMAL;
+	  if (lookup_attribute ("no_caller_saved_registers",
+				TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))
+	    cfun->machine->no_caller_saved_registers = true;
+	}
+    }
+}
+
 /* Establish appropriate back-end context for processing the function
    FNDECL.  The argument might be NULL to indicate processing at top
    level, outside of any function scope.  */
@@ -6407,7 +6450,14 @@ ix86_set_current_function (tree fndecl)
      several times in the course of compiling a function, and we don't want to
      slow things down too much or call target_reinit when it isn't safe.  */
   if (fndecl == ix86_previous_fndecl)
-    return;
+    {
+      /* There may be 2 function bodies for the same function FNDECL,
+	 one is extern inline and one isn't.  Call ix86_set_func_type
+	 to set the func_type field.  */
+      if (fndecl != NULL_TREE)
+	ix86_set_func_type (fndecl);
+      return;
+    }
 
   tree old_tree;
   if (ix86_previous_fndecl == NULL_TREE)
@@ -6424,6 +6474,8 @@ ix86_set_current_function (tree fndecl)
       return;
     }
 
+  ix86_set_func_type (fndecl);
+
   tree new_tree = DECL_FUNCTION_SPECIFIC_TARGET (fndecl);
   if (new_tree == NULL_TREE)
     new_tree = target_option_default_node;
@@ -6440,6 +6492,8 @@ ix86_set_current_function (tree fndecl)
     }
   ix86_previous_fndecl = fndecl;
 
+  static bool prev_no_caller_saved_registers;
+
   /* 64-bit MS and SYSV ABI have different set of call used registers.
      Avoid expensive re-initialization of init_regs each time we switch
      function context.  */
@@ -6447,6 +6501,45 @@ ix86_set_current_function (tree fndecl)
       && (call_used_regs[SI_REG]
 	  == (cfun->machine->call_abi == MS_ABI)))
     reinit_regs ();
+  /* Need to re-initialize init_regs if caller-saved registers are
+     changed.  */
+  else if (prev_no_caller_saved_registers
+	   != cfun->machine->no_caller_saved_registers)
+    reinit_regs ();
+
+  if (cfun->machine->func_type != TYPE_NORMAL
+      || cfun->machine->no_caller_saved_registers)
+    {
+      /* Don't allow MPX, SSE, MMX nor x87 instructions since they
+	 may change processor state.  */
+      const char *isa;
+      if (TARGET_MPX)
+	isa = "MPX";
+      else if (TARGET_SSE)
+	isa = "SSE";
+      else if (TARGET_MMX)
+	isa = "MMX/3Dnow";
+      else if (TARGET_80387)
+	isa = "80387";
+      else
+	isa = NULL;
+      if (isa != NULL)
+	{
+	  if (cfun->machine->func_type != TYPE_NORMAL)
+	    sorry ("%s instructions aren't allowed in %s service routine",
+		   isa, (cfun->machine->func_type == TYPE_EXCEPTION
+			 ? "exception" : "interrupt"));
+	  else
+	    sorry ("%s instructions aren't allowed in function with "
+		   "no_caller_saved_registers attribute", isa);
+	  /* Don't issue the same error twice.  */
+	  cfun->machine->func_type = TYPE_NORMAL;
+	  cfun->machine->no_caller_saved_registers = false;
+	}
+    }
+
+  prev_no_caller_saved_registers
+    = cfun->machine->no_caller_saved_registers;
 }
 
 \f
@@ -6711,6 +6804,11 @@ ix86_function_ok_for_sibcall (tree decl, tree exp)
   rtx a, b;
   bool bind_global = decl && !targetm.binds_local_p (decl);
 
+  /* Sibling call isn't OK if there are no caller-saved registers
+     since all registers must be preserved before return.  */
+  if (cfun->machine->no_caller_saved_registers)
+    return false;
+
   /* If we are generating position-independent code, we cannot sibcall
      optimize direct calls to global functions, as the PLT requires
      %ebx be live. (Darwin does not have a PLT.)  */
@@ -7891,6 +7989,8 @@ type_natural_mode (const_tree type, const CUMULATIVE_ARGS *cum,
 		      }
 		  }
 		else if ((size == 8 && !TARGET_64BIT)
+			 && (!cfun
+			     || cfun->machine->func_type == TYPE_NORMAL)
 			 && !TARGET_MMX
 			 && !TARGET_IAMCU)
 		  {
@@ -8869,6 +8969,11 @@ ix86_function_arg_advance (cumulative_args_t cum_v, machine_mode mode,
   HOST_WIDE_INT bytes, words;
   int nregs;
 
+  /* The argument of interrupt handler is a special case and is
+     handled in ix86_function_arg.  */
+  if (!cum->caller && cfun->machine->func_type != TYPE_NORMAL)
+    return;
+
   if (mode == BLKmode)
     bytes = int_size_in_bytes (type);
   else
@@ -9185,6 +9290,36 @@ ix86_function_arg (cumulative_args_t cum_v, machine_mode omode,
   HOST_WIDE_INT bytes, words;
   rtx arg;
 
+  if (!cum->caller && cfun->machine->func_type != TYPE_NORMAL)
+    {
+      gcc_assert (type != NULL_TREE);
+      if (POINTER_TYPE_P (type))
+	{
+	  /* This is the pointer argument.  */
+	  gcc_assert (TYPE_MODE (type) == Pmode);
+	  if (cfun->machine->func_type == TYPE_INTERRUPT)
+	    /* -WORD(AP) in the current frame in interrupt handler.  */
+	    arg = plus_constant (Pmode, arg_pointer_rtx,
+				 -UNITS_PER_WORD);
+	  else
+	    /* (AP) in the current frame in exception handler.  */
+	    arg = arg_pointer_rtx;
+	}
+      else
+	{
+	  gcc_assert (cfun->machine->func_type == TYPE_EXCEPTION
+		      && TREE_CODE (type) == INTEGER_TYPE
+		      && TYPE_MODE (type) == word_mode);
+	  /* The integer argument is the error code at -WORD(AP) in
+	     the current frame in exception handler.  */
+	  arg = gen_rtx_MEM (word_mode,
+			     plus_constant (Pmode,
+					    arg_pointer_rtx,
+					    -UNITS_PER_WORD));
+	}
+      return arg;
+    }
+
   /* All pointer bounds arguments are handled separately here.  */
   if ((type && POINTER_BOUNDS_TYPE_P (type))
       || POINTER_BOUNDS_MODE_P (mode))
@@ -9746,14 +9881,16 @@ ix86_function_value_bounds (const_tree valtype,
 }
 
 /* Pointer function arguments and return values are promoted to
-   word_mode.  */
+   word_mode for normal functions.  */
 
 static machine_mode
 ix86_promote_function_mode (const_tree type, machine_mode mode,
 			    int *punsignedp, const_tree fntype,
 			    int for_return)
 {
-  if (type != NULL_TREE && POINTER_TYPE_P (type))
+  if (cfun->machine->func_type == TYPE_NORMAL
+      && type != NULL_TREE
+      && POINTER_TYPE_P (type))
     {
       *punsignedp = POINTERS_EXTEND_UNSIGNED;
       return word_mode;
@@ -10914,7 +11051,10 @@ ix86_can_use_return_insn_p (void)
 {
   struct ix86_frame frame;
 
-  if (! reload_completed || frame_pointer_needed)
+  /* Don't use `ret' instruction in interrupt handler.  */
+  if (! reload_completed
+      || frame_pointer_needed
+      || cfun->machine->func_type != TYPE_NORMAL)
     return 0;
 
   /* Don't allow more than 32k pop, since that's all we can do
@@ -11229,11 +11369,77 @@ ix86_select_alt_pic_regnum (void)
   return INVALID_REGNUM;
 }
 
+/* Return true if REGNO is used by the epilogue.  */
+
+bool
+ix86_epilogue_uses (int regno)
+{
+  /* If there are no caller-saved registers, we preserve all registers,
+     except for MMX and x87 registers which aren't supported when saving
+     and restoring registers.  Don't explicitly save SP register since
+     it is always preserved.  */
+  return (epilogue_completed
+	  && cfun->machine->no_caller_saved_registers
+	  && !fixed_regs[regno]
+	  && !STACK_REGNO_P (regno)
+	  && !MMX_REGNO_P (regno));
+}
+
+/* Return nonzero if register REGNO can be used as a scratch register
+   in peephole2.  */
+
+static bool
+ix86_hard_regno_scratch_ok (unsigned int regno)
+{
+  /* If there are no caller-saved registers, we can't use any register
+     as a scratch register after epilogue and use REGNO as scratch
+     register only if it has been used before to avoid saving and
+     restoring it.  */
+  return (!cfun->machine->no_caller_saved_registers
+	  || (!epilogue_completed
+	      && df_regs_ever_live_p (regno)));
+}
+
 /* Return TRUE if we need to save REGNO.  */
 
 static bool
 ix86_save_reg (unsigned int regno, bool maybe_eh_return)
 {
+  /* If there are no caller-saved registers, we preserve all registers,
+     except for MMX and x87 registers which aren't supported when saving
+     and restoring registers.  Don't explicitly save SP register since
+     it is always preserved.  */
+  if (cfun->machine->no_caller_saved_registers)
+    {
+      /* Don't preserve registers used for function return value.  */
+      rtx reg = crtl->return_rtx;
+      if (reg)
+	{
+	  unsigned int i = REGNO (reg);
+	  unsigned int nregs = hard_regno_nregs[i][GET_MODE (reg)];
+	  while (nregs-- > 0)
+	    if ((i + nregs) == regno)
+	      return false;
+
+	  reg = crtl->return_bnd;
+	  if (reg)
+	    {
+	      i = REGNO (reg);
+	      nregs = hard_regno_nregs[i][GET_MODE (reg)];
+	      while (nregs-- > 0)
+		if ((i + nregs) == regno)
+		  return false;
+	    }
+	}
+
+      return (df_regs_ever_live_p (regno)
+	      && !fixed_regs[regno]
+	      && !STACK_REGNO_P (regno)
+	      && !MMX_REGNO_P (regno)
+	      && (regno != HARD_FRAME_POINTER_REGNUM
+		  || !frame_pointer_needed));
+    }
+
   if (regno == REAL_PIC_OFFSET_TABLE_REGNUM
       && pic_offset_table_rtx)
     {
@@ -11956,13 +12162,17 @@ find_drap_reg (void)
 {
   tree decl = cfun->decl;
 
+  /* Always use callee-saved register if there are no caller-saved
+     registers.  */
   if (TARGET_64BIT)
     {
       /* Use R13 for nested function or function need static chain.
 	 Since function with tail call may use any caller-saved
 	 registers in epilogue, DRAP must not use caller-saved
 	 register in such case.  */
-      if (DECL_STATIC_CHAIN (decl) || crtl->tail_call_emit)
+      if (DECL_STATIC_CHAIN (decl)
+	  || cfun->machine->no_caller_saved_registers
+	  || crtl->tail_call_emit)
 	return R13_REG;
 
       return R10_REG;
@@ -11973,7 +12183,9 @@ find_drap_reg (void)
 	 Since function with tail call may use any caller-saved
 	 registers in epilogue, DRAP must not use caller-saved
 	 register in such case.  */
-      if (DECL_STATIC_CHAIN (decl) || crtl->tail_call_emit)
+      if (DECL_STATIC_CHAIN (decl)
+	  || cfun->machine->no_caller_saved_registers
+	  || crtl->tail_call_emit)
 	return DI_REG;
 
       /* Reuse static chain register if it isn't used for parameter
@@ -12014,8 +12226,12 @@ ix86_minimum_incoming_stack_boundary (bool sibcall)
 {
   unsigned int incoming_stack_boundary;
 
+  /* Stack of interrupt handler is always aligned to MIN_STACK_BOUNDARY.
+   */
+  if (cfun->machine->func_type != TYPE_NORMAL)
+    incoming_stack_boundary = MIN_STACK_BOUNDARY;
   /* Prefer the one specified at command line. */
-  if (ix86_user_incoming_stack_boundary)
+  else if (ix86_user_incoming_stack_boundary)
     incoming_stack_boundary = ix86_user_incoming_stack_boundary;
   /* In 32bit, use MIN_STACK_BOUNDARY for incoming stack boundary
      if -mstackrealign is used, it isn't used for sibcall check and
@@ -12776,6 +12992,12 @@ ix86_expand_prologue (void)
     {
       int align_bytes = crtl->stack_alignment_needed / BITS_PER_UNIT;
 
+      /* Can't use DRAP in interrupt function.  */
+      if (cfun->machine->func_type != TYPE_NORMAL)
+	sorry ("Dynamic Realign Argument Pointer (DRAP) not supported "
+	       "in interrupt service routine.  This may be worked "
+	       "around by avoiding functions with aggregate return.");
+
       /* Only need to push parameter pointer reg if it is caller saved.  */
       if (!call_used_regs[REGNO (crtl->drap_reg)])
 	{
@@ -13151,8 +13373,14 @@ ix86_expand_prologue (void)
   if (frame_pointer_needed && frame.red_zone_size)
     emit_insn (gen_memory_blockage ());
 
-  /* Emit cld instruction if stringops are used in the function.  */
-  if (TARGET_CLD && ix86_current_function_needs_cld)
+  /* Emit cld instruction if stringops are used in the function.  Since
+     we can't assume the direction flag in interrupt handler, we must
+     emit cld instruction if stringops are used in interrupt handler or
+     interrupt handler isn't a leaf function.  */
+  if ((TARGET_CLD && ix86_current_function_needs_cld)
+      || (!TARGET_CLD
+	  && cfun->machine->func_type != TYPE_NORMAL
+	  && (ix86_current_function_needs_cld || !crtl->is_leaf)))
     emit_insn (gen_cld ());
 
   /* SEH requires that the prologue end within 256 bytes of the start of
@@ -13646,7 +13874,20 @@ ix86_expand_epilogue (int style)
       return;
     }
 
-  if (crtl->args.pops_args && crtl->args.size)
+  if (cfun->machine->func_type != TYPE_NORMAL)
+    {
+      /* Return with the "IRET" instruction from interrupt handler.
+	 Pop the 'ERROR_CODE' off the stack before the 'IRET'
+	 instruction in exception handler.  */
+      if (cfun->machine->func_type == TYPE_EXCEPTION)
+	{
+	  rtx r = plus_constant (Pmode, stack_pointer_rtx,
+				 UNITS_PER_WORD);
+	  emit_insn (gen_rtx_SET (stack_pointer_rtx, r));
+	}
+      emit_jump_insn (gen_interrupt_return ());
+    }
+  else if (crtl->args.pops_args && crtl->args.size)
     {
       rtx popc = GEN_INT (crtl->args.pops_args);
 
@@ -27123,6 +27364,18 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
   rtx vec[3];
   rtx use = NULL, call;
   unsigned int vec_len = 0;
+  tree fndecl;
+
+  if (GET_CODE (XEXP (fnaddr, 0)) == SYMBOL_REF)
+    {
+      fndecl = SYMBOL_REF_DECL (XEXP (fnaddr, 0));
+      if (fndecl
+	  && (lookup_attribute ("interrupt",
+				TYPE_ATTRIBUTES (TREE_TYPE (fndecl)))))
+	error ("interrupt service routine can't be called directly");
+    }
+  else
+    fndecl = NULL_TREE;
 
   if (pop == const0_rtx)
     pop = NULL;
@@ -27259,8 +27512,30 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
       vec[vec_len++] = pop;
     }
 
-  if (TARGET_64BIT_MS_ABI
-      && (!callarg2 || INTVAL (callarg2) != -2))
+  if (cfun->machine->no_caller_saved_registers
+      && (!fndecl
+	  || (!TREE_THIS_VOLATILE (fndecl)
+	      && !lookup_attribute ("no_caller_saved_registers",
+				    TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))))
+    {
+      static const char ix86_call_used_regs[] = CALL_USED_REGISTERS;
+      bool is_64bit_ms_abi = (TARGET_64BIT
+			      && ix86_function_abi (fndecl) == MS_ABI);
+      char c_mask = CALL_USED_REGISTERS_MASK (is_64bit_ms_abi);
+
+      /* If there are no caller-saved registers, add all registers
+	 that are clobbered by the call which returns.  */
+      for (int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+	if (!fixed_regs[i]
+	    && (ix86_call_used_regs[i] == 1
+		|| (ix86_call_used_regs[i] & c_mask))
+	    && !STACK_REGNO_P (i)
+	    && !MMX_REGNO_P (i))
+	  clobber_reg (&use,
+		       gen_rtx_REG (GET_MODE (regno_reg_rtx[i]), i));
+    }
+  else if (TARGET_64BIT_MS_ABI
+	   && (!callarg2 || INTVAL (callarg2) != -2))
     {
       int const cregs_size
 	= ARRAY_SIZE (x86_64_ms_sysv_extra_clobbered_registers);
@@ -44559,6 +44834,54 @@ ix86_handle_fndecl_attribute (tree *node, tree name, tree, int,
   return NULL_TREE;
 }
 
+static tree
+ix86_handle_no_caller_saved_registers_attribute (tree *, tree, tree,
+						 int, bool *)
+{
+  return NULL_TREE;
+}
+
+static tree
+ix86_handle_interrupt_attribute (tree *node, tree, tree, int, bool *)
+{
+  /* DECL_RESULT and DECL_ARGUMENTS do not exist there yet,
+     but the function type contains args and return type data.  */
+  tree func_type = *node;
+  tree return_type = TREE_TYPE (func_type);
+
+  int nargs = 0;
+  tree current_arg_type = TYPE_ARG_TYPES (func_type);
+  while (current_arg_type
+	 && ! VOID_TYPE_P (TREE_VALUE (current_arg_type)))
+    {
+      if (nargs == 0)
+	{
+	  if (! POINTER_TYPE_P (TREE_VALUE (current_arg_type)))
+	    error ("interrupt service routine should have a pointer "
+		   "as the first argument");
+	}
+      else if (nargs == 1)
+	{
+	  if (TREE_CODE (TREE_VALUE (current_arg_type)) != INTEGER_TYPE
+	      || TYPE_MODE (TREE_VALUE (current_arg_type)) != word_mode)
+	    error ("interrupt service routine should have unsigned %s"
+		   "int as the second argument",
+		   TARGET_64BIT
+		   ? (TARGET_X32 ? "long long " : "long ")
+		   : "");
+	}
+      nargs++;
+      current_arg_type = TREE_CHAIN (current_arg_type);
+    }
+  if (!nargs || nargs > 2)
+    error ("interrupt service routine can only have a pointer argument "
+	   "and an optional integer argument");
+  if (! VOID_TYPE_P (return_type))
+    error ("interrupt service routine can't have non-void return value");
+
+  return NULL_TREE;
+}
+
 static bool
 ix86_ms_bitfield_layout_p (const_tree record_type)
 {
@@ -48774,6 +49097,11 @@ static const struct attribute_spec ix86_attribute_table[] =
     false },
   { "callee_pop_aggregate_return", 1, 1, false, true, true,
     ix86_handle_callee_pop_aggregate_return, true },
+  { "interrupt", 0, 0, false, true, true,
+    ix86_handle_interrupt_attribute, false },
+  { "no_caller_saved_registers", 0, 0, false, true, true,
+    ix86_handle_no_caller_saved_registers_attribute, false },
+
   /* End element.  */
   { NULL,        0, 0, false, false, false, NULL, false }
 };
@@ -54832,6 +55160,9 @@ ix86_addr_space_zero_address_valid (addr_space_t as)
 #undef TARGET_OPTAB_SUPPORTED_P
 #define TARGET_OPTAB_SUPPORTED_P ix86_optab_supported_p
 
+#undef TARGET_HARD_REGNO_SCRATCH_OK
+#define TARGET_HARD_REGNO_SCRATCH_OK ix86_hard_regno_scratch_ok
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 #include "gt-i386.h"
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index d0b418b..ed9394d 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -1634,11 +1634,18 @@ enum reg_class
 
    If stack probes are required, the space used for large function
    arguments on the stack must also be probed, so enable
-   -maccumulate-outgoing-args so this happens in the prologue.  */
+   -maccumulate-outgoing-args so this happens in the prologue.
+
+   We must use argument accumulation in interrupt function if stack
+   may be realigned to avoid DRAP.  */
 
 #define ACCUMULATE_OUTGOING_ARGS \
-  ((TARGET_ACCUMULATE_OUTGOING_ARGS && optimize_function_for_speed_p (cfun)) \
-   || TARGET_STACK_PROBE || TARGET_64BIT_MS_ABI \
+  ((TARGET_ACCUMULATE_OUTGOING_ARGS \
+    && optimize_function_for_speed_p (cfun)) \
+   || (cfun->machine->func_type != TYPE_NORMAL \
+       && crtl->stack_realign_needed) \
+   || TARGET_STACK_PROBE \
+   || TARGET_64BIT_MS_ABI \
    || (TARGET_MACHO && crtl->profile))
 
 /* If defined, a C expression whose value is nonzero when we want to use PUSH
@@ -1748,6 +1755,11 @@ typedef struct ix86_args {
 
 #define EXIT_IGNORE_STACK 1
 
+/* Define this macro as a C expression that is nonzero for registers
+   used by the epilogue or the `return' pattern.  */
+
+#define EPILOGUE_USES(REGNO) ix86_epilogue_uses (REGNO)
+
 /* Output assembler code for a block containing the constant parts
    of a trampoline, leaving space for the variable parts.  */
 
@@ -2463,6 +2475,19 @@ struct GTY(()) machine_frame_state
 /* Private to winnt.c.  */
 struct seh_frame_state;
 
+enum function_type
+{
+  TYPE_UNKNOWN = 0,
+  TYPE_NORMAL,
+  /* The current function is an interrupt service routine with a
+     pointer argument as specified by the "interrupt" attribute.  */
+  TYPE_INTERRUPT,
+  /* The current function is an interrupt service routine with a
+     pointer argument and an integer argument as specified by the
+     "interrupt" attribute.  */
+  TYPE_EXCEPTION
+};
+
 struct GTY(()) machine_function {
   struct stack_local_entry *stack_locals;
   const char *some_ld_name;
@@ -2517,6 +2542,13 @@ struct GTY(()) machine_function {
   /* If true, it is safe to not save/restore DRAP register.  */
   BOOL_BITFIELD no_drap_save_restore : 1;
 
+  /* Function type.  */
+  ENUM_BITFIELD(function_type) func_type : 2;
+
+  /* If true, the current function is a function specified with
+     the "interrupt" or "no_caller_saved_registers" attribute.  */
+  BOOL_BITFIELD no_caller_saved_registers : 1;
+
   /* If true, there is register available for argument passing.  This
      is used only in ix86_function_ok_for_sibcall by 32-bit to determine
      if there is scratch register available for indirect sibcall.  In
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index cf29e5d..5b1a813 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -193,6 +193,9 @@
   UNSPEC_BNDCU
   UNSPEC_BNDCN
   UNSPEC_MPX_FENCE
+
+  ;; IRET support
+  UNSPEC_INTERRUPT_RETURN
 ])
 
 (define_c_enum "unspecv" [
@@ -12444,6 +12447,14 @@
    (set_attr "modrm" "0")
    (set_attr "maybe_prefix_bnd" "1")])
 
+(define_insn "interrupt_return"
+  [(simple_return)
+   (unspec [(const_int 0)] UNSPEC_INTERRUPT_RETURN)]
+  "reload_completed"
+{
+  return TARGET_64BIT ? "iretq" : "iret";
+})
+
 ;; Used by x86_machine_dependent_reorg to avoid penalty on single byte RET
 ;; instruction Athlon and K8 have.
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index a5a8b23..82de5bf 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -5263,6 +5263,83 @@ On x86-32 targets, the @code{stdcall} attribute causes the compiler to
 assume that the called function pops off the stack space used to
 pass arguments, unless it takes a variable number of arguments.
 
+@item no_caller_saved_registers
+@cindex @code{no_caller_saved_registers} function attribute, x86
+Use this attribute to indicate that the specified function has no
+caller-saved registers.  That is, all registers are callee-saved.
+The compiler generates proper function entry and exit sequences to
+save and restore any modified registers, except for the EFLAGS
+register.  If the compiler generates MPX, SSE, MMX or x87 instructions
+in a function with @code{no_caller_saved_registers} attribute or
+functions called from a function with @code{no_caller_saved_registers}
+attribute may contain MPX, SSE, MMX or x87 instructions, the compiler
+must save and restore the corresponding state.
+
+@item interrupt
+@cindex @code{interrupt} function attribute, x86
+Use this attribute to indicate that the specified function is an
+interrupt handler.  The compiler generates function
+entry and exit sequences suitable for use in an interrupt handler when
+this attribute is present.  The @code{IRET} instruction, instead of the
+@code{RET} instruction, is used to return from interrupt handlers.  All
+registers, except for the EFLAGS register which is restored by the
+@code{IRET} instruction, are preserved by the compiler.  If the
+compiler generates MPX, SSE, MMX or x87 instructions in an interrupt
+handler, or functions called from an interrupt handler may contain MPX,
+SSE, MMX or x87 instructions, the compiler must save and restore the
+corresponding state.
+
+Since the direction flag in the FLAGS register in interrupt handlers
+is undetermined, cld instruction must be emitted in function prologue
+if rep string instructions are used in interrupt handler or interrupt
+handler isn't a leaf function.
+
+Any interruptible-without-stack-switch code must be compiled with
+@option{-mno-red-zone} since interrupt handlers can and will, because
+of the hardware design, touch the red zone.
+
+An interrupt handler must be declared with a mandatory pointer
+argument:
+
+@smallexample
+struct interrupt_frame;
+
+__attribute__ ((interrupt))
+void
+f (struct interrupt_frame *frame)
+@{
+@}
+@end smallexample
+
+and user must properly define the structure the pointer pointing to.
+
+The exception handler is very similar to the interrupt handler with
+a different mandatory function signature:
+
+@smallexample
+#ifdef __x86_64__
+typedef unsigned long long int uword_t;
+#else
+typedef unsigned int uword_t;
+#endif
+
+struct interrupt_frame;
+
+__attribute__ ((interrupt))
+void
+f (struct interrupt_frame *frame, uword_t error_code)
+@{
+  ...
+@}
+@end smallexample
+
+and compiler pops the error code off the stack before the @code{IRET}
+instruction.
+
+The exception handler should only be used for exceptions which push an
+error code and all other exceptions must use the interrupt handler.
+The system will crash if the wrong handler is used.
+
 @item target (@var{options})
 @cindex @code{target} function attribute
 As discussed in @ref{Common Function Attributes}, this attribute 
diff --git a/gcc/testsuite/gcc.dg/guality/pr68037-1.c b/gcc/testsuite/gcc.dg/guality/pr68037-1.c
new file mode 100644
index 0000000..069d3e9
--- /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 0000000..da25028
--- /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 0000000..7f9af3d
--- /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 0000000..32336b0
--- /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 0000000..02d7ab9
--- /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 0000000..ea1952e
--- /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 0000000..50b2c7b
--- /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 0000000..d86c0f0
--- /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 0000000..56f070f
--- /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 0000000..b025a61
--- /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 0000000..4918e76
--- /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 0000000..4da8121
--- /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 0000000..7e35c29
--- /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 0000000..fe0966d
--- /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 0000000..754733a
--- /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 0000000..28e3f43
--- /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 0000000..0125f41
--- /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 0000000..77a554b
--- /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 0000000..4492f4b
--- /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 0000000..06b9a36
--- /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 0000000..6ef13b6
--- /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 0000000..ddbaf61
--- /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 0000000..5de2018
--- /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 0000000..c42049c
--- /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 0000000..1669cda
--- /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 0000000..30e3e7f
--- /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 0000000..14e4355
--- /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 0000000..5bed18f
--- /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 0000000..58c68c7
--- /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 0000000..ee7351e
--- /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 0000000..3dff034
--- /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 0000000..d7a54c8
--- /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 0000000..eb1195d
--- /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 0000000..21ffea3
--- /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 0000000..3bbe0da
--- /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 0000000..23c7488
--- /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 0000000..f0e1637
--- /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 0000000..827196e
--- /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 0000000..6fd8117
--- /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 0000000..1bad58d
--- /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 0000000..28c50fc
--- /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 0000000..d46b21d
--- /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 0000000..e1d34e5
--- /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 0000000..e0ff182
--- /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 0000000..df916ca
--- /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 0000000..7e20d58
--- /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 0000000..51c0478
--- /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 0000000..f23e6f9
--- /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 } } */
-- 
2.1.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* RE: [PATCH] x86 interrupt attribute patch [2/2]
  2016-04-20 13:42 [PATCH] x86 interrupt attribute patch [2/2] Koval, Julia
@ 2016-05-10 14:54 ` Koval, Julia
  2016-05-10 15:29   ` Uros Bizjak
  2016-05-10 20:02 ` Sandra Loosemore
  1 sibling, 1 reply; 8+ messages in thread
From: Koval, Julia @ 2016-05-10 14:54 UTC (permalink / raw)
  To: 'gcc-patches@gcc.gnu.org'
  Cc: Lu, Hongjiu, 'vaalfreja@gmail.com',
	'ubizjak@gmail.com', 'law@redhat.com',
	Zamyatin, Igor

Gentle ping.

-----Original Message-----
From: Koval, Julia 
Sent: Wednesday, April 20, 2016 4:42 PM
To: gcc-patches@gcc.gnu.org
Cc: Lu, Hongjiu <hongjiu.lu@intel.com>; vaalfreja@gmail.com; ubizjak@gmail.com; law@redhat.com; Zamyatin, Igor <igor.zamyatin@intel.com>
Subject: [PATCH] x86 interrupt attribute patch [2/2]

Hi,
Here is the new version of interrupt attribute patch. Bootstraped/regtested for Linux/x86_64. Ok for trunk?
    
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.
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.
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.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] x86 interrupt attribute patch [2/2]
  2016-05-10 14:54 ` Koval, Julia
@ 2016-05-10 15:29   ` Uros Bizjak
  0 siblings, 0 replies; 8+ messages in thread
From: Uros Bizjak @ 2016-05-10 15:29 UTC (permalink / raw)
  To: Koval, Julia; +Cc: gcc-patches, Lu, Hongjiu, vaalfreja, law, Zamyatin, Igor

On Tue, May 10, 2016 at 4:54 PM, Koval, Julia <julia.koval@intel.com> wrote:
> Gentle ping.
>
> -----Original Message-----
> From: Koval, Julia
> Sent: Wednesday, April 20, 2016 4:42 PM
> To: gcc-patches@gcc.gnu.org
> Cc: Lu, Hongjiu <hongjiu.lu@intel.com>; vaalfreja@gmail.com; ubizjak@gmail.com; law@redhat.com; Zamyatin, Igor <igor.zamyatin@intel.com>
> Subject: [PATCH] x86 interrupt attribute patch [2/2]
>
> Hi,
> Here is the new version of interrupt attribute patch. Bootstraped/regtested for Linux/x86_64. Ok for trunk?
>
> 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.
> 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.
> 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.

OK for mainline, but please commit patch [1/2] first.

Thanks,
Uros.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] x86 interrupt attribute patch [2/2]
  2016-04-20 13:42 [PATCH] x86 interrupt attribute patch [2/2] Koval, Julia
  2016-05-10 14:54 ` Koval, Julia
@ 2016-05-10 20:02 ` Sandra Loosemore
  2016-05-30 16:05   ` Koval, Julia
  1 sibling, 1 reply; 8+ messages in thread
From: Sandra Loosemore @ 2016-05-10 20:02 UTC (permalink / raw)
  To: Koval, Julia, gcc-patches
  Cc: Lu, Hongjiu, vaalfreja, ubizjak, law, Zamyatin, Igor

On 04/20/2016 07:42 AM, Koval, Julia wrote:
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index a5a8b23..82de5bf 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -5263,6 +5263,83 @@ On x86-32 targets, the @code{stdcall} attribute causes the compiler to
>  assume that the called function pops off the stack space used to
>  pass arguments, unless it takes a variable number of arguments.
>
> +@item no_caller_saved_registers
> +@cindex @code{no_caller_saved_registers} function attribute, x86
> +Use this attribute to indicate that the specified function has no
> +caller-saved registers.  That is, all registers are callee-saved.
> +The compiler generates proper function entry and exit sequences to
> +save and restore any modified registers, except for the EFLAGS
> +register.  If the compiler generates MPX, SSE, MMX or x87 instructions
> +in a function with @code{no_caller_saved_registers} attribute or
> +functions called from a function with @code{no_caller_saved_registers}
> +attribute may contain MPX, SSE, MMX or x87 instructions, the compiler
> +must save and restore the corresponding state.

I cannot parse the last sentence in this paragraph.  How can the 
compiler know whether called functions may contain those instructions? 
Plus, talking about what the compiler must do seems too 
implementor-speaky for user documentation.  Maybe you mean something 
like "The compiler also saves and restores state associated with MPX, 
SSE, MMX, and x87 instructions." ?

I also think the documentation needs to give some hint about why a user 
would want to put this attribute on a function.

> +
> +@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.

Similar problems here.

 From the further discussion that follows, it appears that you can use 
the "interrupt" attribute on exception handlers as well, but the 
paragraph above only mentions interrupt handlers.

> +
> +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.

Again, this sounds like implementor-speak, and there are grammatical 
errors (noun/verb disagreement, missing articles).  Do users of this 
attribute need to know what instructions the compiler is emitting?  We 
already say above that it causes GCC to generate suitable entry and exit 
sequences.

> +
> +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.

"user" == "you" in the GCC user manual.

How do you "properly define" this structure, or is that a stupid 
question?  (I know very little about x86, maybe this is obvious to experts.)

> +
> +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.

I think you need to move this information above the examples, and 
explain from the start that there are two flavors of handlers that have 
different information pushed on the stack by the hardware.

> +
>  @item target (@var{options})
>  @cindex @code{target} function attribute
>  As discussed in @ref{Common Function Attributes}, this attribute

-Sandra

^ permalink raw reply	[flat|nested] 8+ messages in thread

* RE: [PATCH] x86 interrupt attribute patch [2/2]
  2016-05-10 20:02 ` Sandra Loosemore
@ 2016-05-30 16:05   ` Koval, Julia
  2016-05-31  7:28     ` Sandra Loosemore
  0 siblings, 1 reply; 8+ messages in thread
From: Koval, Julia @ 2016-05-30 16:05 UTC (permalink / raw)
  To: gcc-patches
  Cc: Lu, Hongjiu, vaalfreja, ubizjak, law, Zamyatin, Igor, Sandra Loosemore

[-- Attachment #1: Type: text/plain, Size: 5573 bytes --]

Hi,
Here is the fixed version of the patch. Ok for trunk?


-----Original Message-----
From: Sandra Loosemore [mailto:sandra@codesourcery.com] 
Sent: Tuesday, May 10, 2016 11:02 PM
To: Koval, Julia <julia.koval@intel.com>; gcc-patches@gcc.gnu.org
Cc: Lu, Hongjiu <hongjiu.lu@intel.com>; vaalfreja@gmail.com; ubizjak@gmail.com; law@redhat.com; Zamyatin, Igor <igor.zamyatin@intel.com>
Subject: Re: [PATCH] x86 interrupt attribute patch [2/2]

On 04/20/2016 07:42 AM, Koval, Julia wrote:
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 
> a5a8b23..82de5bf 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -5263,6 +5263,83 @@ On x86-32 targets, the @code{stdcall} attribute 
> causes the compiler to  assume that the called function pops off the 
> stack space used to  pass arguments, unless it takes a variable number of arguments.
>
> +@item no_caller_saved_registers
> +@cindex @code{no_caller_saved_registers} function attribute, x86 Use 
> +this attribute to indicate that the specified function has no 
> +caller-saved registers.  That is, all registers are callee-saved.
> +The compiler generates proper function entry and exit sequences to 
> +save and restore any modified registers, except for the EFLAGS 
> +register.  If the compiler generates MPX, SSE, MMX or x87 
> +instructions in a function with @code{no_caller_saved_registers} 
> +attribute or functions called from a function with 
> +@code{no_caller_saved_registers} attribute may contain MPX, SSE, MMX 
> +or x87 instructions, the compiler must save and restore the corresponding state.

I cannot parse the last sentence in this paragraph.  How can the compiler know whether called functions may contain those instructions? 
Plus, talking about what the compiler must do seems too implementor-speaky for user documentation.  Maybe you mean something like "The compiler also saves and restores state associated with MPX, SSE, MMX, and x87 instructions." ?

I also think the documentation needs to give some hint about why a user would want to put this attribute on a function.

> +
> +@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.

Similar problems here.

 From the further discussion that follows, it appears that you can use the "interrupt" attribute on exception handlers as well, but the paragraph above only mentions interrupt handlers.

> +
> +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.

Again, this sounds like implementor-speak, and there are grammatical errors (noun/verb disagreement, missing articles).  Do users of this attribute need to know what instructions the compiler is emitting?  We already say above that it causes GCC to generate suitable entry and exit sequences.

> +
> +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.

"user" == "you" in the GCC user manual.

How do you "properly define" this structure, or is that a stupid 
question?  (I know very little about x86, maybe this is obvious to experts.)

> +
> +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.

I think you need to move this information above the examples, and 
explain from the start that there are two flavors of handlers that have 
different information pushed on the stack by the hardware.

> +
>  @item target (@var{options})
>  @cindex @code{target} function attribute
>  As discussed in @ref{Common Function Attributes}, this attribute

-Sandra


[-- Attachment #2: 30_patch --]
[-- Type: application/octet-stream, Size: 96268 bytes --]

commit dea2de68ec18ed1e584a4f1227efdb4f350b7411
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Fri Feb 5 04:43:16 2016 -0800

    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.

diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index 447f67e..9fd14f6 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -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;
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 7e9f511..0873ac7 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -6024,6 +6024,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)
     {
@@ -6795,6 +6804,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.  */
@@ -6805,7 +6848,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)
@@ -6822,6 +6872,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;
@@ -6838,6 +6890,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.  */
@@ -6845,6 +6899,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;
 }
 
 \f
@@ -7126,6 +7219,11 @@ ix86_function_ok_for_sibcall (tree decl, tree exp)
   rtx a, b;
   bool bind_global = decl && !targetm.binds_local_p (decl);
 
+  /* Sibling call isn't OK if there are no caller-saved registers
+     since all registers must be preserved before return.  */
+  if (cfun->machine->no_caller_saved_registers)
+    return false;
+
   /* If we are generating position-independent code, we cannot sibcall
      optimize direct calls to global functions, as the PLT requires
      %ebx be live. (Darwin does not have a PLT.)  */
@@ -8294,6 +8392,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)
 		  {
@@ -9272,6 +9372,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
@@ -9588,6 +9693,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))
@@ -10149,14 +10284,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;
@@ -11367,7 +11504,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
@@ -11682,11 +11822,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)
     {
@@ -12400,13 +12606,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;
@@ -12417,7 +12627,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
@@ -12458,8 +12670,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
@@ -13220,6 +13436,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)])
 	{
@@ -13595,8 +13817,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
@@ -14079,7 +14307,20 @@ ix86_expand_epilogue (int style)
       return;
     }
 
-  if (crtl->args.pops_args && crtl->args.size)
+  if (cfun->machine->func_type != TYPE_NORMAL)
+    {
+      /* Return with the "IRET" instruction from interrupt handler.
+	 Pop the 'ERROR_CODE' off the stack before the 'IRET'
+	 instruction in exception handler.  */
+      if (cfun->machine->func_type == TYPE_EXCEPTION)
+	{
+	  rtx r = plus_constant (Pmode, stack_pointer_rtx,
+				 UNITS_PER_WORD);
+	  emit_insn (gen_rtx_SET (stack_pointer_rtx, r));
+	}
+      emit_jump_insn (gen_interrupt_return ());
+    }
+  else if (crtl->args.pops_args && crtl->args.size)
     {
       rtx popc = GEN_INT (crtl->args.pops_args);
 
@@ -27399,6 +27640,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;
@@ -27535,8 +27788,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);
@@ -44906,6 +45181,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)
 {
@@ -49121,6 +49444,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 }
 };
@@ -55182,6 +55510,9 @@ ix86_addr_space_zero_address_valid (addr_space_t as)
 #undef TARGET_OPTAB_SUPPORTED_P
 #define TARGET_OPTAB_SUPPORTED_P ix86_optab_supported_p
 
+#undef TARGET_HARD_REGNO_SCRATCH_OK
+#define TARGET_HARD_REGNO_SCRATCH_OK ix86_hard_regno_scratch_ok
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 #include "gt-i386.h"
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 47427f5..f1fa02c 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -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
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index d20bbe4..a256d2c 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -194,6 +194,9 @@
   UNSPEC_BNDCU
   UNSPEC_BNDCN
   UNSPEC_MPX_FENCE
+
+  ;; IRET support
+  UNSPEC_INTERRUPT_RETURN
 ])
 
 (define_c_enum "unspecv" [
@@ -12434,6 +12437,14 @@
    (set_attr "modrm" "0")
    (set_attr "maybe_prefix_bnd" "1")])
 
+(define_insn "interrupt_return"
+  [(simple_return)
+   (unspec [(const_int 0)] UNSPEC_INTERRUPT_RETURN)]
+  "reload_completed"
+{
+  return TARGET_64BIT ? "iretq" : "iret";
+})
+
 ;; Used by x86_machine_dependent_reorg to avoid penalty on single byte RET
 ;; instruction Athlon and K8 have.
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 2d4f028..f4bd7dd 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -5266,6 +5266,96 @@ 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, for example for a function, called from an
+interrupt handler.  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, @option{-mgeneral-regs-only}, should be used to
+compile functions with @code{no_caller_saved_registers} attribute.
+
+@emph{Note for compiler implementers:} 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 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.
+
+@emph{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 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 you must define the structure the pointer pointing to, depending
+on the proper x86 interrupt frame, described in the processor's manual.
+
+The exception handler is very similar to the interrupt handler with
+a different mandatory function signature:
+
+@smallexample
+#ifdef __x86_64__
+typedef unsigned long long int uword_t;
+#else
+typedef unsigned int uword_t;
+#endif
+
+struct interrupt_frame;
+
+__attribute__ ((interrupt))
+void
+f (struct interrupt_frame *frame, uword_t error_code)
+@{
+  ...
+@}
+@end smallexample
+
+and compiler pops the error code off the stack before the @code{IRET}
+instruction.
+
+The exception handler should only be used for exceptions which push an
+error code and all other exceptions must use the interrupt handler.
+The system will crash if the wrong handler is used.
+
 @item target (@var{options})
 @cindex @code{target} function attribute
 As discussed in @ref{Common Function Attributes}, this attribute 
diff --git a/gcc/testsuite/gcc.dg/guality/pr68037-1.c b/gcc/testsuite/gcc.dg/guality/pr68037-1.c
new file mode 100644
index 0000000..c3ea645
--- /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 -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" } } */
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 0000000..6f7e920
--- /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 -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" } } */
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 0000000..504a931
--- /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 -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" } } */
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 0000000..15fe08c
--- /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 "-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;
+}
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 0000000..00ba7d4
--- /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 "-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;
+}
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 0000000..abf8adb
--- /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 "-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;
+}
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 0000000..b343844
--- /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 "-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 ();
+}
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 0000000..863c16a
--- /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 "-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;
+}
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 0000000..3dfe3d8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-1.c
@@ -0,0 +1,55 @@
+/* { 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 } } */
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 0000000..a88e948
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-10.c
@@ -0,0 +1,19 @@
+/* { 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 } } */
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 0000000..ded589e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-11.c
@@ -0,0 +1,41 @@
+/* { 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 } } */
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 0000000..b04de5f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-12.c
@@ -0,0 +1,30 @@
+/* { 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 } } */
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 0000000..018602f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-13.c
@@ -0,0 +1,30 @@
+/* { 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 } } */
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 0000000..6ed2cf2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-14.c
@@ -0,0 +1,32 @@
+/* { 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 } } */
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 0000000..9b53abe
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-15.c
@@ -0,0 +1,37 @@
+/* { 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 } } */
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 0000000..bc929c6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-16.c
@@ -0,0 +1,21 @@
+/* { 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 } } } } */
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 0000000..5d5b59e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-17.c
@@ -0,0 +1,22 @@
+/* { 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" } }*/
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 0000000..9f21a23
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-18.c
@@ -0,0 +1,13 @@
+/* { 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" } } */
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 0000000..c3b2097
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-19.c
@@ -0,0 +1,16 @@
+/* { 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" } */
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 0000000..3899d52
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-2.c
@@ -0,0 +1,20 @@
+/* { 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" } } */
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 0000000..0861ffe
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-20.c
@@ -0,0 +1,29 @@
+/* { 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 } } */
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 0000000..5bb397e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-21.c
@@ -0,0 +1,30 @@
+/* { 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 } } */
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 0000000..a51ba63
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-22.c
@@ -0,0 +1,29 @@
+/* { 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 } } */
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 0000000..95c211a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-23.c
@@ -0,0 +1,46 @@
+/* { 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 } } */
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 0000000..0b95660
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-24.c
@@ -0,0 +1,19 @@
+/* { 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 } } } } */
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 0000000..9c25fed
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-25.c
@@ -0,0 +1,54 @@
+/* { 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 } } } } */
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 0000000..10cc3e1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-26.c
@@ -0,0 +1,16 @@
+/* { 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 } } */
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 0000000..4e1643a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-27.c
@@ -0,0 +1,15 @@
+/* { 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" } } */
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 0000000..8690535
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-28.c
@@ -0,0 +1,12 @@
+/* { 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" } } */
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 0000000..ec7dc71
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-3.c
@@ -0,0 +1,16 @@
+/* { 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" } } */
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 0000000..3fbdc88
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-387-err-1.c
@@ -0,0 +1,16 @@
+/* { 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" } */
+}
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 0000000..3203d64
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-387-err-2.c
@@ -0,0 +1,8 @@
+/* { 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" } */
+}
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 0000000..0cb8d3c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-4.c
@@ -0,0 +1,32 @@
+/* { 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;
+}
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 0000000..803c063
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-5.c
@@ -0,0 +1,23 @@
+/* { 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;
+}
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 0000000..2a0266a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-6.c
@@ -0,0 +1,40 @@
+/* { 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;
+}
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 0000000..506f61a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-7.c
@@ -0,0 +1,12 @@
+/* { 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" } */
+}
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 0000000..34536d1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-8.c
@@ -0,0 +1,38 @@
+/* { 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 } } */
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 0000000..490fb9c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-9.c
@@ -0,0 +1,22 @@
+/* { 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 } } */
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 0000000..e07688e
--- /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 -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" } */
+}
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 0000000..641ca63
--- /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 -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" } */
+}
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 0000000..c2256ef
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
@@ -0,0 +1,36 @@
+/* { 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 } } */
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 0000000..cd1367b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err-1.c
@@ -0,0 +1,16 @@
+/* { 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" } */
+}
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 0000000..3e9f70c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err-2.c
@@ -0,0 +1,8 @@
+/* { 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" } */
+}
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 0000000..1009884
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c
@@ -0,0 +1,32 @@
+/* { 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" } } */
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 0000000..a3fa776
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c
@@ -0,0 +1,33 @@
+/* { 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" } } */
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 0000000..e2cda61
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-sibcall-1.c
@@ -0,0 +1,14 @@
+/* { 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 } } */
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 0000000..f59253a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-sibcall-2.c
@@ -0,0 +1,15 @@
+/* { 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 } } */
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 0000000..a7aa0a8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c
@@ -0,0 +1,18 @@
+/* { 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 } } */

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] x86 interrupt attribute patch [2/2]
  2016-05-30 16:05   ` Koval, Julia
@ 2016-05-31  7:28     ` Sandra Loosemore
  2016-06-01 14:20       ` Koval, Julia
  0 siblings, 1 reply; 8+ messages in thread
From: Sandra Loosemore @ 2016-05-31  7:28 UTC (permalink / raw)
  To: Koval, Julia, gcc-patches
  Cc: Lu, Hongjiu, vaalfreja, ubizjak, law, Zamyatin, Igor

On 05/30/2016 07:31 AM, Koval, Julia wrote:
> Hi,
> Here is the fixed version of the patch. Ok for trunk?
>
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index 2d4f028..f4bd7dd 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -5266,6 +5266,96 @@ On x86-32 targets, the @code{stdcall} attribute causes th
> e 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, for example for a function, called from an
> +interrupt handler.  That is, all registers are callee-saved.

I think the "for example" information should come after the second 
sentence to improve the flow here.  I'm also confused; I can see how an 
interrupt handler might have different register usage conventions, but 
do regular functions called from inside an interrupt handler function 
really use a non-standard call/return sequence?

> +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

Please delete both commas in the line above.

> +compile functions with @code{no_caller_saved_registers} attribute.
> +
> +@emph{Note for compiler implementers:} 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.

A "Note for compiler implementers" has no place in user documentation. 
You should just document what GCC does, if it is relevant to how a user 
would use this feature.

It also seems like the admonition in this note that the compiler must 
save/restore the state contradicts the previous paragraph, where you say 
GCC doesn't preserve the state.

> +
> +@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

Delete the comma.

> +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

Delete the two previous commas.

> +exception handlers.
> +
> +@emph{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.

Again, this is user documentation.  Just explain what GCC does if it is 
relevant to how a user would use the feature you are documenting.

> +
> +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.

This paragraph seems totally implementor-speaky and irrelevant to how a 
user would use the feature.

> +
> +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 you must define the structure the pointer pointing to, depending
> +on the proper x86 interrupt frame, described in the processor's manual.

How about

@noindent
and you must define @code{struct interrupt_frame} as described in the 
processor's manual.


> +
> +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.

I'm still not happy with the organization of material in this part.  How 
about something like this instead:

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.

[example here]

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

-Sandra

^ permalink raw reply	[flat|nested] 8+ messages in thread

* RE: [PATCH] x86 interrupt attribute patch [2/2]
  2016-05-31  7:28     ` Sandra Loosemore
@ 2016-06-01 14:20       ` Koval, Julia
  2016-06-02 18:33         ` Sandra Loosemore
  0 siblings, 1 reply; 8+ messages in thread
From: Koval, Julia @ 2016-06-01 14:20 UTC (permalink / raw)
  To: Sandra Loosemore, gcc-patches
  Cc: Lu, Hongjiu, vaalfreja, ubizjak, law, Zamyatin, Igor

[-- Attachment #1: Type: text/plain, Size: 7560 bytes --]

> I'm also confused; I can see how an interrupt handler might have different register usage conventions, but do regular functions called from inside an interrupt handler function really use a non-standard call/return sequence?
Usually, they don't. But because of this, the interrupt handler should save all the context, because any register can be used in the called function and we have no information about it. This is bad for performance. That's why no_caller_saved_registers attribute was proposed - the function with this attribute saves/restores all modified registers itself, and an interrupt function can call it without saving full context. Here is the discussion: https://gcc.gnu.org/ml/gcc-patches/2015-09/msg02277.html

Here is the patch with the fixes. Ok for trunk?

Thanks,
Julia

-----Original Message-----
From: Sandra Loosemore [mailto:sandra@codesourcery.com] 
Sent: Monday, May 30, 2016 10:37 PM
To: Koval, Julia <julia.koval@intel.com>; gcc-patches@gcc.gnu.org
Cc: Lu, Hongjiu <hongjiu.lu@intel.com>; vaalfreja@gmail.com; ubizjak@gmail.com; law@redhat.com; Zamyatin, Igor <igor.zamyatin@intel.com>
Subject: Re: [PATCH] x86 interrupt attribute patch [2/2]

On 05/30/2016 07:31 AM, Koval, Julia wrote:
> Hi,
> Here is the fixed version of the patch. Ok for trunk?
>
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 
> 2d4f028..f4bd7dd 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -5266,6 +5266,96 @@ On x86-32 targets, the @code{stdcall} attribute 
> causes th e 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, for example for a function, called from an 
> +interrupt handler.  That is, all registers are callee-saved.

I think the "for example" information should come after the second sentence to improve the flow here.  I'm also confused; I can see how an interrupt handler might have different register usage conventions, but do regular functions called from inside an interrupt handler function really use a non-standard call/return sequence?

> +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

Please delete both commas in the line above.

> +compile functions with @code{no_caller_saved_registers} attribute.
> +
> +@emph{Note for compiler implementers:} 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.

A "Note for compiler implementers" has no place in user documentation. 
You should just document what GCC does, if it is relevant to how a user would use this feature.

It also seems like the admonition in this note that the compiler must save/restore the state contradicts the previous paragraph, where you say GCC doesn't preserve the state.

> +
> +@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

Delete the comma.

> +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

Delete the two previous commas.

> +exception handlers.
> +
> +@emph{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.

Again, this is user documentation.  Just explain what GCC does if it is relevant to how a user would use the feature you are documenting.

> +
> +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.

This paragraph seems totally implementor-speaky and irrelevant to how a user would use the feature.

> +
> +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 you must define the structure the pointer pointing to, depending 
> +on the proper x86 interrupt frame, described in the processor's manual.

How about

@noindent
and you must define @code{struct interrupt_frame} as described in the processor's manual.


> +
> +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.

I'm still not happy with the organization of material in this part.  How 
about something like this instead:

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.

[example here]

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

-Sandra


[-- Attachment #2: patch_01.06.patch --]
[-- Type: application/octet-stream, Size: 95470 bytes --]

commit 57c7f5bac61ac87d033ca7f83dc9c09bcc4463d4
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Fri Feb 5 04:43:16 2016 -0800

    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.

diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index 447f67e..9fd14f6 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -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;
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 7e9f511..0873ac7 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -6024,6 +6024,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)
     {
@@ -6795,6 +6804,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.  */
@@ -6805,7 +6848,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)
@@ -6822,6 +6872,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;
@@ -6838,6 +6890,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.  */
@@ -6845,6 +6899,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;
 }
 
 \f
@@ -7126,6 +7219,11 @@ ix86_function_ok_for_sibcall (tree decl, tree exp)
   rtx a, b;
   bool bind_global = decl && !targetm.binds_local_p (decl);
 
+  /* Sibling call isn't OK if there are no caller-saved registers
+     since all registers must be preserved before return.  */
+  if (cfun->machine->no_caller_saved_registers)
+    return false;
+
   /* If we are generating position-independent code, we cannot sibcall
      optimize direct calls to global functions, as the PLT requires
      %ebx be live. (Darwin does not have a PLT.)  */
@@ -8294,6 +8392,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)
 		  {
@@ -9272,6 +9372,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
@@ -9588,6 +9693,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))
@@ -10149,14 +10284,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;
@@ -11367,7 +11504,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
@@ -11682,11 +11822,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)
     {
@@ -12400,13 +12606,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;
@@ -12417,7 +12627,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
@@ -12458,8 +12670,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
@@ -13220,6 +13436,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)])
 	{
@@ -13595,8 +13817,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
@@ -14079,7 +14307,20 @@ ix86_expand_epilogue (int style)
       return;
     }
 
-  if (crtl->args.pops_args && crtl->args.size)
+  if (cfun->machine->func_type != TYPE_NORMAL)
+    {
+      /* Return with the "IRET" instruction from interrupt handler.
+	 Pop the 'ERROR_CODE' off the stack before the 'IRET'
+	 instruction in exception handler.  */
+      if (cfun->machine->func_type == TYPE_EXCEPTION)
+	{
+	  rtx r = plus_constant (Pmode, stack_pointer_rtx,
+				 UNITS_PER_WORD);
+	  emit_insn (gen_rtx_SET (stack_pointer_rtx, r));
+	}
+      emit_jump_insn (gen_interrupt_return ());
+    }
+  else if (crtl->args.pops_args && crtl->args.size)
     {
       rtx popc = GEN_INT (crtl->args.pops_args);
 
@@ -27399,6 +27640,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;
@@ -27535,8 +27788,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);
@@ -44906,6 +45181,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)
 {
@@ -49121,6 +49444,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 }
 };
@@ -55182,6 +55510,9 @@ ix86_addr_space_zero_address_valid (addr_space_t as)
 #undef TARGET_OPTAB_SUPPORTED_P
 #define TARGET_OPTAB_SUPPORTED_P ix86_optab_supported_p
 
+#undef TARGET_HARD_REGNO_SCRATCH_OK
+#define TARGET_HARD_REGNO_SCRATCH_OK ix86_hard_regno_scratch_ok
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 #include "gt-i386.h"
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 47427f5..f1fa02c 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -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
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index d20bbe4..a256d2c 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -194,6 +194,9 @@
   UNSPEC_BNDCU
   UNSPEC_BNDCN
   UNSPEC_MPX_FENCE
+
+  ;; IRET support
+  UNSPEC_INTERRUPT_RETURN
 ])
 
 (define_c_enum "unspecv" [
@@ -12434,6 +12437,14 @@
    (set_attr "modrm" "0")
    (set_attr "maybe_prefix_bnd" "1")])
 
+(define_insn "interrupt_return"
+  [(simple_return)
+   (unspec [(const_int 0)] UNSPEC_INTERRUPT_RETURN)]
+  "reload_completed"
+{
+  return TARGET_64BIT ? "iretq" : "iret";
+})
+
 ;; Used by x86_machine_dependent_reorg to avoid penalty on single byte RET
 ;; instruction Athlon and K8 have.
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 2d4f028..3e6a796 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -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 
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 0000000..c3ea645
--- /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 -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" } } */
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 0000000..6f7e920
--- /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 -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" } } */
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 0000000..504a931
--- /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 -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" } } */
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 0000000..15fe08c
--- /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 "-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;
+}
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 0000000..00ba7d4
--- /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 "-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;
+}
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 0000000..abf8adb
--- /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 "-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;
+}
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 0000000..b343844
--- /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 "-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 ();
+}
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 0000000..863c16a
--- /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 "-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;
+}
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 0000000..3dfe3d8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-1.c
@@ -0,0 +1,55 @@
+/* { 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 } } */
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 0000000..a88e948
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-10.c
@@ -0,0 +1,19 @@
+/* { 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 } } */
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 0000000..ded589e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-11.c
@@ -0,0 +1,41 @@
+/* { 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 } } */
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 0000000..b04de5f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-12.c
@@ -0,0 +1,30 @@
+/* { 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 } } */
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 0000000..018602f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-13.c
@@ -0,0 +1,30 @@
+/* { 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 } } */
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 0000000..6ed2cf2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-14.c
@@ -0,0 +1,32 @@
+/* { 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 } } */
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 0000000..9b53abe
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-15.c
@@ -0,0 +1,37 @@
+/* { 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 } } */
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 0000000..bc929c6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-16.c
@@ -0,0 +1,21 @@
+/* { 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 } } } } */
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 0000000..5d5b59e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-17.c
@@ -0,0 +1,22 @@
+/* { 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" } }*/
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 0000000..9f21a23
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-18.c
@@ -0,0 +1,13 @@
+/* { 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" } } */
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 0000000..c3b2097
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-19.c
@@ -0,0 +1,16 @@
+/* { 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" } */
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 0000000..3899d52
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-2.c
@@ -0,0 +1,20 @@
+/* { 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" } } */
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 0000000..0861ffe
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-20.c
@@ -0,0 +1,29 @@
+/* { 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 } } */
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 0000000..5bb397e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-21.c
@@ -0,0 +1,30 @@
+/* { 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 } } */
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 0000000..a51ba63
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-22.c
@@ -0,0 +1,29 @@
+/* { 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 } } */
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 0000000..95c211a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-23.c
@@ -0,0 +1,46 @@
+/* { 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 } } */
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 0000000..0b95660
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-24.c
@@ -0,0 +1,19 @@
+/* { 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 } } } } */
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 0000000..9c25fed
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-25.c
@@ -0,0 +1,54 @@
+/* { 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 } } } } */
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 0000000..10cc3e1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-26.c
@@ -0,0 +1,16 @@
+/* { 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 } } */
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 0000000..4e1643a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-27.c
@@ -0,0 +1,15 @@
+/* { 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" } } */
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 0000000..8690535
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-28.c
@@ -0,0 +1,12 @@
+/* { 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" } } */
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 0000000..ec7dc71
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-3.c
@@ -0,0 +1,16 @@
+/* { 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" } } */
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 0000000..3fbdc88
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-387-err-1.c
@@ -0,0 +1,16 @@
+/* { 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" } */
+}
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 0000000..3203d64
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-387-err-2.c
@@ -0,0 +1,8 @@
+/* { 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" } */
+}
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 0000000..0cb8d3c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-4.c
@@ -0,0 +1,32 @@
+/* { 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;
+}
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 0000000..803c063
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-5.c
@@ -0,0 +1,23 @@
+/* { 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;
+}
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 0000000..2a0266a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-6.c
@@ -0,0 +1,40 @@
+/* { 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;
+}
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 0000000..506f61a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-7.c
@@ -0,0 +1,12 @@
+/* { 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" } */
+}
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 0000000..34536d1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-8.c
@@ -0,0 +1,38 @@
+/* { 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 } } */
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 0000000..490fb9c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-9.c
@@ -0,0 +1,22 @@
+/* { 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 } } */
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 0000000..e07688e
--- /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 -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" } */
+}
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 0000000..641ca63
--- /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 -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" } */
+}
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 0000000..c2256ef
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
@@ -0,0 +1,36 @@
+/* { 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 } } */
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 0000000..cd1367b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err-1.c
@@ -0,0 +1,16 @@
+/* { 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" } */
+}
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 0000000..3e9f70c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-mmx-err-2.c
@@ -0,0 +1,8 @@
+/* { 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" } */
+}
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 0000000..1009884
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c
@@ -0,0 +1,32 @@
+/* { 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" } } */
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 0000000..a3fa776
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c
@@ -0,0 +1,33 @@
+/* { 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" } } */
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 0000000..e2cda61
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-sibcall-1.c
@@ -0,0 +1,14 @@
+/* { 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 } } */
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 0000000..f59253a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-sibcall-2.c
@@ -0,0 +1,15 @@
+/* { 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 } } */
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 0000000..a7aa0a8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c
@@ -0,0 +1,18 @@
+/* { 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 } } */

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] x86 interrupt attribute patch [2/2]
  2016-06-01 14:20       ` Koval, Julia
@ 2016-06-02 18:33         ` Sandra Loosemore
  0 siblings, 0 replies; 8+ messages in thread
From: Sandra Loosemore @ 2016-06-02 18:33 UTC (permalink / raw)
  To: Koval, Julia, gcc-patches
  Cc: Lu, Hongjiu, vaalfreja, ubizjak, law, Zamyatin, Igor

On 06/01/2016 08:19 AM, Koval, Julia wrote:

> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index 2d4f028..3e6a796 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -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

s/function,/function/

The documentation parts are OK with that change.

-Sandra

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2016-06-02 18:33 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-04-20 13:42 [PATCH] x86 interrupt attribute patch [2/2] Koval, Julia
2016-05-10 14:54 ` Koval, Julia
2016-05-10 15:29   ` Uros Bizjak
2016-05-10 20:02 ` Sandra Loosemore
2016-05-30 16:05   ` Koval, Julia
2016-05-31  7:28     ` Sandra Loosemore
2016-06-01 14:20       ` Koval, Julia
2016-06-02 18:33         ` Sandra Loosemore

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).