summaryrefslogtreecommitdiff
path: root/gcc/testsuite/gcc.target/i386/interrupt-20.c
diff options
context:
space:
mode:
authorH.J. Lu <hjl.tools@gmail.com>2015-10-06 04:47:06 -0700
committerH.J. Lu <hjl.tools@gmail.com>2015-11-25 15:53:01 -0800
commit0428f4715d1bcbf175d1a8fa2079e51b4b9069d1 (patch)
tree767bccc6f0f2d2e43b54de23917bafafa86baee9 /gcc/testsuite/gcc.target/i386/interrupt-20.c
parent3b5af4184d0cd72f696dbe6d2711f68954b5cb88 (diff)
downloadgcc-hjl/interrupt/middle.tar.gz
Implement x86 interrupt attributehjl/interrupt/middle
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. 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. To be feature complete, compiler may implement the optional '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. 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/68037 * config/i386/i386-protos.h (ix86_epilogue_uses): New prototype. * config/i386/i386.c (ix86_frame): Add nbndregs, nmaskregs, bnd_reg_save_offset and mask_reg_save_offset. (ix86_conditional_register_usage): Preserve all registers, except for function return registers if there are no caller-saved registers. (ix86_set_current_function): Set no_caller_saved_registers and func_type. Sorry if non-DWARF debug format is used. Call reinit_regs if AX register usage isn't consistent. (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_reg_ever_defined_p): Likewise. (ix86_hard_regno_scratch_ok): Likewise. (ix86_nsaved_bndregs): Likewise. (ix86_nsaved_maskregs): Likewise. (ix86_emit_save_bnd_regs_using_mov): Likewise. (ix86_emit_save_mask_regs_using_mov): Likewise. (ix86_emit_restore_bnd_regs_using_mov): Likewise. (ix86_emit_restore_mask_regs_using_mov): Likewise. (ix86_handle_no_caller_saved_registers_attribute): Likewise. (ix86_handle_interrupt_attribute): Likewise. (ix86_save_reg): Preserve all registers in interrupt function after reload. Preserve all registers, except for function return registers, if there are no caller-saved registers after reload. (ix86_nsaved_sseregs): Don't return 0 if there are no caller-saved registers. (ix86_compute_frame_layout): Set nbndregs and nmaskregs. Set and allocate BND and MASK register save areas. Allocate space to save full vector registers if there are no caller-saved registers. (ix86_emit_save_reg_using_mov): Don't use UNSPEC_STOREU to SSE registers. (ix86_emit_save_sse_regs_using_mov): Save full vector registers if there are no caller-saved registers. (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): Save BND and MASK registers. (ix86_emit_restore_sse_regs_using_mov): Restore full vector registers if there are no caller-saved registers. (ix86_expand_epilogue): Restore BND and MASK registers. Generate interrupt return for interrupt handler and pop the 'ERROR_CODE' off the stack before interrupt return in exception handler. (ix86_expand_move): Disallow 80387 instructions in exception handler. Force PLUS source operand in store operation into a register. (ix86_expand_vector_move): Disallow MMX/3Dnow instructions 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 as clobbered. (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/68037 * 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.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-387-err.c: Likewise. * gcc.target/i386/interrupt-bnd.c: Likewise. * gcc.target/i386/interrupt-iamcu.c: Likewise. * gcc.target/i386/interrupt-mmx-err.c: Likewise. * gcc.target/i386/interrupt-redzone-1.c: Likewise. * gcc.target/i386/interrupt-redzone-2.c: Likewise. * gcc.target/i386/interrupt-sibcall.c: Likewise. * gcc.target/i386/interrupt-switch-abi.c: Likewise. * gcc.target/i386/interrupt-xmm.c: Likewise. * gcc.target/i386/interrupt-ymm.c: Likewise. * gcc.target/i386/interrupt-zmm.c: Likewise.
Diffstat (limited to 'gcc/testsuite/gcc.target/i386/interrupt-20.c')
-rw-r--r--gcc/testsuite/gcc.target/i386/interrupt-20.c23
1 files changed, 23 insertions, 0 deletions
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-20.c b/gcc/testsuite/gcc.target/i386/interrupt-20.c
new file mode 100644
index 00000000000..2a2210dc976
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/interrupt-20.c
@@ -0,0 +1,23 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-avx512f -mno-iamcu -mavx -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\]+,\[\\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 \]*%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" } }*/