Bug 68661 - __attribute__ ((no_caller_saved_registers)) trashes stack
Summary: __attribute__ ((no_caller_saved_registers)) trashes stack
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: target (show other bugs)
Version: 6.0
: P3 normal
Target Milestone: 6.0
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2015-12-02 18:09 UTC by H.J. Lu
Modified: 2016-06-03 15:08 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2015-12-03 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description H.J. Lu 2015-12-02 18:09:18 UTC
[hjl@gnu-6 no-caller-saved-2]$ cat x.i 
extern void bar0 (int, int) __attribute__ ((no_caller_saved_registers));

void
foo (void)
{
  bar0 (1, 2);
}
[hjl@gnu-6 no-caller-saved-2]$ cat main.i
void 
__attribute__ ((no_caller_saved_registers))
bar0 (int i, int j)
{
  if (i != 1)
    __builtin_abort ();

  if (j != 2)
    __builtin_abort ();
}

extern void foo (void);

int
main ()
{
  foo ();
  return 0;
}
[hjl@gnu-6 no-caller-saved-2]$ make
/export/build/gnu/gcc-x32/build-x86_64-linux/gcc/xgcc -B/export/build/gnu/gcc-x32/build-x86_64-linux/gcc/ -Og -g -m32 -S -o x.s x.i
/export/build/gnu/gcc-x32/build-x86_64-linux/gcc/xgcc -B/export/build/gnu/gcc-x32/build-x86_64-linux/gcc/ -Og -g -m32 -S -o main.s main.i
/export/build/gnu/gcc-x32/build-x86_64-linux/gcc/xgcc -B/export/build/gnu/gcc-x32/build-x86_64-linux/gcc/ -Og -g -m32 -o x x.s main.s
./x
Makefile:12: recipe for target 'all' failed
make: *** [all] Aborted
[hjl@gnu-6 no-caller-saved-2]$
Comment 1 H.J. Lu 2015-12-03 04:36:09 UTC
It is fixed on hjl/interrupt/stage1 branch at

https://github.com/hjl-tools/gcc
Comment 2 H.J. Lu 2016-01-15 17:43:12 UTC
Fixed.
Comment 3 H.J. Lu 2016-01-15 17:43:55 UTC
Fixed.
Comment 4 hjl@gcc.gnu.org 2016-06-03 15:08:34 UTC
Author: hjl
Date: Fri Jun  3 15:08:00 2016
New Revision: 237073

URL: https://gcc.gnu.org/viewcvs?rev=237073&root=gcc&view=rev
Log:
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.

Added:
    trunk/gcc/testsuite/gcc.dg/guality/pr68037-1.c
    trunk/gcc/testsuite/gcc.dg/guality/pr68037-2.c
    trunk/gcc/testsuite/gcc.dg/guality/pr68037-3.c
    trunk/gcc/testsuite/gcc.dg/torture/pr68037-1.c
    trunk/gcc/testsuite/gcc.dg/torture/pr68037-2.c
    trunk/gcc/testsuite/gcc.dg/torture/pr68037-3.c
    trunk/gcc/testsuite/gcc.dg/torture/pr68661-1a.c
    trunk/gcc/testsuite/gcc.dg/torture/pr68661-1b.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-1.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-10.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-11.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-12.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-13.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-14.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-15.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-16.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-17.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-18.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-19.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-2.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-20.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-21.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-22.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-23.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-24.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-25.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-26.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-27.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-28.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-3.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-387-err-1.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-387-err-2.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-4.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-5.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-6.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-7.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-8.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-9.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-bnd-err-1.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-bnd-err-2.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-mmx-err-1.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-mmx-err-2.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-redzone-1.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-redzone-2.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-sibcall-1.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-sibcall-2.c
    trunk/gcc/testsuite/gcc.target/i386/interrupt-switch-abi.c
Modified:
    trunk/gcc/ChangeLog
    trunk/gcc/config/i386/i386-protos.h
    trunk/gcc/config/i386/i386.c
    trunk/gcc/config/i386/i386.h
    trunk/gcc/config/i386/i386.md
    trunk/gcc/doc/extend.texi
    trunk/gcc/testsuite/ChangeLog