Add support for X86 hardware interrupt contexts. These require that all registers be saved (with a few minor exceptions), and sometimes take one parameter. The parameter value (if present) is placed on the stack below the function return address, so it must be popped before returning. Return must be performed with an iret instruction.
Details
Diff Detail
- Repository
- rL LLVM
Event Timeline
Is it something private that you need or general? X86 interrupts receive up to 6 parameters.
lib/Target/X86/X86ISelLowering.cpp | ||
---|---|---|
2517 | Why? X86 interrupts receive up to 6 parameters. |
The hardware provides only up to one parameter, unless I've missed some major point. I think software interrupt calling conventions would be best left to some other work (if even they're well-defined enough to be worth trying to support).
lib/Target/X86/X86ISelLowering.cpp | ||
---|---|---|
2517 | I believe you're thinking of BIOS interrupts or such, which have their own (different) calling convention? I only handle the error code pushed by hardware, which is placed on the stack (and is only sometimes present- see Figure 6-4 in Volume 3 of the Intel Software Developer's Manual). Software interrupts typically take more parameters in registers, but then the calling convention is completely different (there's no error code, some registers become caller-saved..). |
I proposed a different approach:
http://lists.llvm.org/pipermail/cfe-dev/2015-September/044924.html
It's worth noting that I opted to implement this as a calling convention because it's relatively easy to support in the Rust compiler, where I really want this feature. While something similar might be useful in a C frontend as well, historically there has been no great need.
The native calling convention in Rust is deliberately unspecified, which has the result that there is no reasonably efficient way to pass exception codes into a Rust ISR- the entry and exit can be implemented in assembly (with relatively inefficient double dispatch to Rust code), but it is impossible for the programmer writing assembly to pass a parameter to a function using the Rust ABI (such as an error code placed on the stack by hardware) because it is not predictable.
According to the final proposal by H.J.
#ifdef x86_64
typedef unsigned long long int uword_t;
#else
typedef unsigned int uword_t;
#endifstruct interrupt_frame
{uword_t ip; uword_t cs; uword_t flags; uword_t sp; uword_t ss;};
attribute ((interrupt))
void
f (struct interrupt_frame *frame)
{
...
}
the frame parameter is passed in C as a pointer argument.
However, in the assembly it should be passed by value on the stack rather than a pointer on the stack.
Do we assume that this will be handled by the clang-FE ?
lib/Target/X86/X86CallingConv.td | ||
---|---|---|
711 | I think we need to define argument convention for x86-32 and another for x86-64. Also, why do we need to promote types to i32? Do we expect to have smaller types? Should not that be considered as illegal interrupt? |
the frame parameter is passed in C as a pointer argument.
However, in the assembly it should be passed by value on the stack rather than a pointer on the stack.
Do we assume that this will be handled by the clang-FE ?
This code predates H.J's proposal, so the frame parameter doesn't even exist here. It needs to be added.
lib/Target/X86/X86CallingConv.td | ||
---|---|---|
711 | According to the AMD documentation, the error code in long mode (x86-64) is still 32 bits wide, but is padded to 64 bits when placed on the stack. So the handling of the error code from a user's perspective is the same in both 32- and 64-bit code. Arguments that are not 32 bits wide should probably be made illegal, yes. |
The current interrupt spec is
https://gcc.gnu.org/ml/gcc-patches/2015-11/msg00641.html
We added:
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.
H.J.
I understand that this implementation have a gap with respect to the final proposal.
However, we can close this gap, right?
Peter, how do you suggest to go further from here? Do you want to make the needed changes and upload a new patch?
lib/Target/X86/X86CallingConv.td | ||
---|---|---|
711 | OK, I understand why error code in x86_64 configuration should be also with size 4 (and with alignment 8) X86-32: the arguments need to have size 4 and alignment 4 |
Peter, how do you suggest to go further from here? Do you want to make the needed changes and upload a new patch?
I can revise this patch further, but it's rather low on my priority list right now.
lib/Target/X86/X86CallingConv.td | ||
---|---|---|
711 | Yes, that sounds correct. |
I think we need to define argument convention for x86-32 and another for x86-64.
X86-32: the arguments need to have size 4 and alignment 4
X86-64: the arguments need to have size 8 and alignment 8
Also, why do we need to promote types to i32? Do we expect to have smaller types? Should not that be considered as illegal interrupt?