This is an archive of the discontinued LLVM Phabricator instance.

X86: add an interrupt calling convention
AbandonedPublic

Authored by tari on Aug 31 2015, 12:16 PM.

Details

Reviewers
nadav
Summary

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.

Diff Detail

Repository
rL LLVM

Event Timeline

tari updated this revision to Diff 33609.Aug 31 2015, 12:16 PM
tari retitled this revision from to X86: add an interrupt calling convention.
tari updated this object.
tari set the repository for this revision to rL LLVM.
tari added a subscriber: llvm-commits.
tari added a reviewer: nadav.Aug 31 2015, 2:26 PM
delena added a subscriber: delena.Aug 31 2015, 11:45 PM

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.

tari added a comment.Sep 1 2015, 7:43 AM

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..).

tari added a comment.Sep 3 2015, 3:52 PM

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.

aaboud added a subscriber: aaboud.Nov 18 2015, 7:46 AM

According to the final proposal by H.J.

#ifdef x86_64
typedef unsigned long long int uword_t;
#else
typedef unsigned int uword_t;
#endif

struct 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.
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?

tari added a comment.Nov 18 2015, 11:50 AM

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.

tari added inline comments.Nov 18 2015, 11:53 AM
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)
But I still think that in x86_32 configuration the alignment should be 4. There is no need to consume more memory than actually needed, right?

X86-32: the arguments need to have size 4 and alignment 4
X86-64: the arguments need to have size 4 and alignment 8

tari added a comment.Dec 6 2015, 11:37 AM

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.

tari abandoned this revision.Dec 21 2015, 11:51 AM

Superceded by D15567.