This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH] x86 interrupt attribute


Hi,

Here is the last version of the patch. Regtested/bootstraped for
Linux/i686 and Linux/x86_64.

Date: Fri, 4 Sep 2015 08:53:23 -0700
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.

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 long long int uword_t;
typedef unsigned int uword_t;

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.

PR target/66960
PR target/67630
PR target/67634
* config/i386/i386-protos.h (ix86_epilogue_uses): New prototype.
* config/i386/i386.c (ix86_frame): Add nbndregs and nmaskregs.
(ix86_conditional_register_usage): Preserve all registers if
there are no caller-saved registers.
(ix86_set_current_function): Set no_caller_saved_registers and
func_type.  Mark arguments in interrupt handler as used.
(ix86_function_ok_for_sibcall): Return false if there are no
caller-saved registers.
(ix86_maybe_switch_abi): Call reinit_regs if AX register usage
isn't consistent.
(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): Handle arguments for callee in interrupt
handler.
(ix86_can_use_return_insn_p): Don't use `ret' instruction in
interrupt handler.
(ix86_hard_regno_scratch_ok): New function.
(ix86_epilogue_uses): Likewise.
(ix86_reg_ever_defined_p): Likewise.
(ix86_nsaved_bndregs): Likewise.
(ix86_nsaved_maskregs): Likewise.
(ix86_reg_save_area_size): Likewise.
(ix86_handle_no_caller_saved_registers_attribute): Likewise.
(ix86_handle_interrupt_attribute): Likewise.
(ix86_save_reg): Preserve all 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
save_regs_using_mov to true to save bound and mask registers.
Call ix86_reg_save_area_size to get register save area size.
Allocate space to save full vector registers if there are
no caller-saved registers.
(ix86_emit_save_reg_using_mov): Set alignment to word_mode
alignment when saving full vector registers if there are no
caller-saved registers.
(ix86_emit_save_regs_using_mov): Use regno_reg_rtx to get
register size.
(ix86_emit_restore_regs_using_mov): Likewise.
(ix86_emit_save_sse_regs_using_mov): Save full vector registers
if there are no caller-saved registers.
(ix86_emit_restore_sse_regs_using_mov): Restore full vector
registers if there are no caller-saved registers.
(ix86_expand_epilogue): Use move to restore bound 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.
(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): New.
* config/i386/i386.h (EPILOGUE_USES): New.
(function_type): New enum.
* config/i386/i386.h (machine_function): Add func_type and
no_caller_saved_registers.
* config/i386/i386.md (UNSPEC_INTERRUPT_RETURN): New.
(interrupt_return): New pattern.
* config/i386/sse.md (*mov<mode>_internal): Handle misaligned
SSE load and store if there are no caller-saved registers.
* doc/extend.texi: Document x86 interrupt and
no_caller_saved_registers attributes.

gcc/testsuite/

PR target/66960
PR target/67630
PR target/67634
* gcc.target/i386/interrupt-1.c: New test.
* 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-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.

On Fri, Oct 2, 2015 at 8:48 PM, Yulia Koval <vaalfreja@gmail.com> wrote:
> Fixed it. Thanks.
>
> On Fri, Oct 2, 2015 at 6:45 PM, Uros Bizjak <ubizjak@gmail.com> wrote:
>> On Fri, Oct 2, 2015 at 2:51 PM, Yulia Koval <vaalfreja@gmail.com> wrote:
>>> Hi,
>>> Here is a new patch. Added HJ's changes and review changes.
>>>
>>> Implement x86 interrupt attribute
>>
>> +      incoming_stack_boundary
>> + = (crtl->parm_stack_boundary > ix86_incoming_stack_boundary
>> +   ? crtl->parm_stack_boundary : ix86_incoming_stack_boundary);
>>
>> MAX (crtl->parm_stack_boundary, ix86_incoming_stack_boundary);
>>
>> +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 (cfun->machine->no_caller_saved_registers
>> +  && reg_names[regno][0]
>> +  && !fixed_regs[regno]
>> +  && !STACK_REGNO_P (regno)
>> +  && !MMX_REGNO_P (regno));
>>
>> There is still a redundant check for reg_names with !fixed_regs. When
>> member of fixed_regs is 0, we are sure that corresponding reg_names is
>> non-null. As can be seen in ix86_conditional_register_usage, register
>> names are squashed depending on target for non-existent registers in
>> order to prevent their usage in "asm" statements.
>>
>> +      /* If there are no caller-saved registers, add all registers
>> + that are clobbered by the call.  */
>> +      for (int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
>> + if (reg_names[i][0]
>> +    && !fixed_regs[i]
>> +    && (ix86_call_used_regs[i] == 1
>> + || (ix86_call_used_regs[i] & c_mask))
>> +    && !STACK_REGNO_P (i)
>> +    && !MMX_REGNO_P (i))
>>
>> And here.
>>
>> Uros.

Attachment: patch_04.10
Description: Binary data


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]