Commit 5ed3cc7b authored by hjl's avatar hjl

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.


git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@237073 138bc75d-0d04-0410-961f-82ee72b054a4
parent f27c52fb
2016-06-03 H.J. Lu <hongjiu.lu@intel.com>
Julia Koval <julia.koval@intel.com>
PR target/66960
PR target/67630
PR target/67634
PR target/67841
PR target/68037
PR target/68618
PR target/68661
PR target/69575
PR target/69596
PR target/69734
* config/i386/i386-protos.h (ix86_epilogue_uses): New prototype.
* config/i386/i386.c (ix86_conditional_register_usage): Preserve
all registers, except for function return registers if there are
no caller-saved registers.
(ix86_set_func_type): New function.
(ix86_set_current_function): Call ix86_set_func_type to set
no_caller_saved_registers and func_type. Call reinit_regs if
caller-saved registers are changed. Don't allow MPX, SSE, MMX
nor x87 instructions in interrupt handler nor function with
no_caller_saved_registers attribute.
(ix86_function_ok_for_sibcall): Return false if there are no
caller-saved registers.
(type_natural_mode): Don't warn ABI change for MMX in interrupt
handler.
(ix86_function_arg_advance): Skip for callee in interrupt
handler.
(ix86_function_arg): Return special arguments in interrupt
handler.
(ix86_promote_function_mode): Promote pointer to word_mode only
for normal functions.
(ix86_can_use_return_insn_p): Don't use `ret' instruction in
interrupt handler.
(ix86_epilogue_uses): New function.
(ix86_hard_regno_scratch_ok): Likewise.
(ix86_save_reg): Preserve all registers in interrupt handler
after reload. Preserve all registers, except for function
return registers, if there are no caller-saved registers after
reload.
(find_drap_reg): Always use callee-saved register if there are
no caller-saved registers.
(ix86_minimum_incoming_stack_boundary): Return MIN_STACK_BOUNDARY
for interrupt handler.
(ix86_expand_prologue): Don't allow DRAP in interrupt handler.
Emit cld instruction if stringops are used in interrupt handler
or interrupt handler isn't a leaf function.
(ix86_expand_epilogue): Generate interrupt return for interrupt
handler and pop the 'ERROR_CODE' off the stack before interrupt
return in exception handler.
(ix86_expand_call): Disallow calling interrupt handler directly.
If there are no caller-saved registers, mark all registers that
are clobbered by the call which returns as clobbered.
(ix86_handle_no_caller_saved_registers_attribute): New function.
(ix86_handle_interrupt_attribute): Likewise.
(ix86_attribute_table): Add interrupt and no_caller_saved_registers
attributes.
(TARGET_HARD_REGNO_SCRATCH_OK): Likewise.
* config/i386/i386.h (ACCUMULATE_OUTGOING_ARGS): Use argument
accumulation in interrupt function if stack may be realigned to
avoid DRAP.
(EPILOGUE_USES): New.
(function_type): New enum.
(machine_function): Add func_type and no_caller_saved_registers.
* config/i386/i386.md (UNSPEC_INTERRUPT_RETURN): New.
(interrupt_return): New pattern.
* doc/extend.texi: Document x86 interrupt and
no_caller_saved_registers attributes.
2016-06-03 Bernd Schmidt <bschmidt@redhat.com>
PR tree-optimization/52171
......
......@@ -286,6 +286,8 @@ extern rtx maybe_get_pool_constant (rtx);
extern char internal_label_prefix[16];
extern int internal_label_prefix_len;
extern bool ix86_epilogue_uses (int);
struct ix86_address
{
rtx base, index, disp;
......
......@@ -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;
}
......@@ -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;
#include "gt-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
......
......@@ -194,6 +194,9 @@
UNSPEC_BNDCU
UNSPEC_BNDCN
UNSPEC_MPX_FENCE
;; IRET support
UNSPEC_INTERRUPT_RETURN
])
(define_c_enum "unspecv" [
......@@ -12437,6 +12440,14 @@
(set_attr "modrm" "0")
(set_attr "maybe_prefix_bnd" "1")])
(define_insn "interrupt_return"
[(simple_return)
(unspec [(const_int 0)] UNSPEC_INTERRUPT_RETURN)]
"reload_completed"
{
return TARGET_64BIT ? "iretq" : "iret";