correct calling convention for x86_64
This commit is contained in:
parent
a8c0c2b871
commit
b30a76ed1b
|
@ -1,4 +1,6 @@
|
|||
.text
|
||||
#include "x86_64_regs.h"
|
||||
|
||||
.text
|
||||
|
||||
/*
|
||||
According to ABI documentation found at
|
||||
|
@ -46,54 +48,68 @@ First four arguments:
|
|||
.globl swap_registers
|
||||
swap_registers:
|
||||
// n.b. when we enter, the return address is at the top of
|
||||
// the stack (i.e., 0(%RSP)). We
|
||||
// the stack (i.e., 0(%RSP)) and the argument is in
|
||||
// RUSTRT_ARG0_S. We
|
||||
// simply save all NV registers into oregs.
|
||||
// We then restore all NV registers from regs. This restores
|
||||
// the old stack pointer, which should include the proper
|
||||
// return address. We can therefore just return normally to
|
||||
// jump back into the old code.
|
||||
|
||||
# if defined(RUSTRT_ARG0_S)
|
||||
mov RUSTRT_ARG0_S, %rdi
|
||||
mov RUSTRT_ARG1_S, %rsi
|
||||
# endif
|
||||
|
||||
// Save instruction pointer:
|
||||
pop %rax
|
||||
mov %rax, 56(%rdi)
|
||||
mov %rax, (RUSTRT_IP*8)(%rdi)
|
||||
|
||||
// Save non-volatile integer registers:
|
||||
// (including RSP)
|
||||
mov %rbx, 0(%rdi)
|
||||
mov %rsp, 8(%rdi)
|
||||
mov %rbp, 16(%rdi)
|
||||
mov %r12, 24(%rdi)
|
||||
mov %r13, 32(%rdi)
|
||||
mov %r14, 40(%rdi)
|
||||
mov %r15, 48(%rdi)
|
||||
mov %rbx, (RUSTRT_RBX*8)(%rdi)
|
||||
mov %rsp, (RUSTRT_RSP*8)(%rdi)
|
||||
mov %rbp, (RUSTRT_RBP*8)(%rdi)
|
||||
mov %r12, (RUSTRT_R12*8)(%rdi)
|
||||
mov %r13, (RUSTRT_R13*8)(%rdi)
|
||||
mov %r14, (RUSTRT_R14*8)(%rdi)
|
||||
mov %r15, (RUSTRT_R15*8)(%rdi)
|
||||
|
||||
// Save relevant argument registers:
|
||||
mov %rcx, (RUSTRT_RCX*8)(%rdi)
|
||||
mov %rdi, (RUSTRT_RDI*8)(%rdi)
|
||||
|
||||
// Save non-volatile XMM registers:
|
||||
movapd %xmm0, 64(%rdi)
|
||||
movapd %xmm1, 80(%rdi)
|
||||
movapd %xmm2, 96(%rdi)
|
||||
movapd %xmm3, 112(%rdi)
|
||||
movapd %xmm4, 128(%rdi)
|
||||
movapd %xmm5, 144(%rdi)
|
||||
movapd %xmm0, (RUSTRT_XMM0*8)(%rdi)
|
||||
movapd %xmm1, (RUSTRT_XMM1*8)(%rdi)
|
||||
movapd %xmm2, (RUSTRT_XMM2*8)(%rdi)
|
||||
movapd %xmm3, (RUSTRT_XMM3*8)(%rdi)
|
||||
movapd %xmm4, (RUSTRT_XMM4*8)(%rdi)
|
||||
movapd %xmm5, (RUSTRT_XMM5*8)(%rdi)
|
||||
|
||||
// Restore non-volatile integer registers:
|
||||
// (including RSP)
|
||||
mov 0(%rsi), %rbx
|
||||
mov 8(%rsi), %rsp
|
||||
mov 16(%rsi), %rbp
|
||||
mov 24(%rsi), %r12
|
||||
mov 32(%rsi), %r13
|
||||
mov 40(%rsi), %r14
|
||||
mov 48(%rsi), %r15
|
||||
mov (RUSTRT_RBX*8)(%rsi), %rbx
|
||||
mov (RUSTRT_RSP*8)(%rsi), %rsp
|
||||
mov (RUSTRT_RBP*8)(%rsi), %rbp
|
||||
mov (RUSTRT_R12*8)(%rsi), %r12
|
||||
mov (RUSTRT_R13*8)(%rsi), %r13
|
||||
mov (RUSTRT_R14*8)(%rsi), %r14
|
||||
mov (RUSTRT_R15*8)(%rsi), %r15
|
||||
|
||||
// Restore relevant argument registers:
|
||||
mov (RUSTRT_RCX*8)(%rdi), %rcx
|
||||
mov (RUSTRT_RDI*8)(%rdi), %rdi
|
||||
|
||||
// Restore non-volatile XMM registers:
|
||||
movapd 64(%rsi), %xmm0
|
||||
movapd 80(%rsi), %xmm1
|
||||
movapd 96(%rsi), %xmm2
|
||||
movapd 112(%rsi), %xmm3
|
||||
movapd 128(%rsi), %xmm4
|
||||
movapd 144(%rsi), %xmm5
|
||||
movapd (RUSTRT_XMM0*8)(%rsi), %xmm0
|
||||
movapd (RUSTRT_XMM1*8)(%rsi), %xmm1
|
||||
movapd (RUSTRT_XMM2*8)(%rsi), %xmm2
|
||||
movapd (RUSTRT_XMM3*8)(%rsi), %xmm3
|
||||
movapd (RUSTRT_XMM4*8)(%rsi), %xmm4
|
||||
movapd (RUSTRT_XMM5*8)(%rsi), %xmm5
|
||||
|
||||
// Jump to the instruction pointer
|
||||
// found in regs:
|
||||
jmp *56(%rsi)
|
||||
jmp *(RUSTRT_IP*8)(%rsi)
|
||||
|
||||
|
|
|
@ -25,14 +25,11 @@ void context::call(void *f, void *arg, void *stack) {
|
|||
// given function.
|
||||
swap(*this);
|
||||
|
||||
// set up the trampoline frame
|
||||
uint64_t *sp = (uint64_t *)stack;
|
||||
// set up the stack
|
||||
uint32_t *sp = (uint32_t *)stack;
|
||||
sp = align_down(sp);
|
||||
|
||||
// Shift the stack pointer so the alignment works out right.
|
||||
sp = align_down(sp) - 3;
|
||||
*--sp = (uint64_t)arg;
|
||||
*--sp = 0xdeadbeef;
|
||||
|
||||
regs.regs[RSP] = (uint64_t)sp;
|
||||
regs.ip = (uint64_t)f;
|
||||
regs.data[RUSTRT_ARG0] = (uint64_t)arg;
|
||||
regs.data[RUSTRT_RSP] = (uint64_t)sp;
|
||||
regs.data[RUSTRT_IP] = (uint64_t)f;
|
||||
}
|
||||
|
|
|
@ -20,42 +20,41 @@ T align_down(T sp)
|
|||
return (T)((uint64_t)sp & ~(16 - 1));
|
||||
}
|
||||
|
||||
// The struct in which we store the saved data. This is mostly the
|
||||
// volatile registers and instruction pointer, but it also includes
|
||||
// RCX/RDI which are used to pass arguments. The indices for each
|
||||
// register are found in <x86_64_regs.h>:
|
||||
#include "x86_64_regs.h"
|
||||
struct registers_t {
|
||||
uint64_t regs[7]; // Space for the volatile regs: rbx, rsp, rbp, r12:r15
|
||||
uint64_t ip;
|
||||
|
||||
// n.b.: These must be 16-byte aligned or movapd is unhappy.
|
||||
__m128 xmms[6]; // Space for the volatile regs: xmm0:xmm5
|
||||
uint64_t data[RUSTRT_MAX];
|
||||
};
|
||||
|
||||
class context {
|
||||
public:
|
||||
static const int RSP = 1;
|
||||
|
||||
registers_t regs;
|
||||
|
||||
context();
|
||||
|
||||
context *next;
|
||||
|
||||
void swap(context &out);
|
||||
void call(void *f, void *arg, void *sp);
|
||||
void call(void *f, void *sp);
|
||||
|
||||
// Note that this doesn't actually adjust esp. Instead, we adjust esp when
|
||||
// we actually do the call. This is needed for exception safety -- if the
|
||||
// function being called causes the task to fail, then we have to avoid
|
||||
// leaking space on the C stack.
|
||||
inline void *alloc_stack(size_t nbytes) {
|
||||
uint32_t bot = regs.regs[RSP];
|
||||
uint32_t top = align_down(bot - nbytes);
|
||||
registers_t regs;
|
||||
|
||||
context();
|
||||
|
||||
context *next;
|
||||
|
||||
void swap(context &out);
|
||||
void call(void *f, void *arg, void *sp);
|
||||
void call(void *f, void *sp);
|
||||
|
||||
// Note that this doesn't actually adjust esp. Instead, we adjust esp when
|
||||
// we actually do the call. This is needed for exception safety -- if the
|
||||
// function being called causes the task to fail, then we have to avoid
|
||||
// leaking space on the C stack.
|
||||
inline void *alloc_stack(size_t nbytes) {
|
||||
uint32_t bot = regs.data[RUSTRT_RSP];
|
||||
uint32_t top = align_down(bot - nbytes);
|
||||
|
||||
#ifdef HAVE_VALGRIND
|
||||
(void)VALGRIND_MAKE_MEM_UNDEFINED(top - 4, bot - top + 4);
|
||||
(void)VALGRIND_MAKE_MEM_UNDEFINED(top - 4, bot - top + 4);
|
||||
#endif
|
||||
|
||||
return reinterpret_cast<void *>(top);
|
||||
}
|
||||
return reinterpret_cast<void *>(top);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
#define RUSTRT_RBX 0
|
||||
#define RUSTRT_RSP 1
|
||||
#define RUSTRT_RBP 2
|
||||
#define RUSTRT_RDI 3
|
||||
#define RUSTRT_RCX 4
|
||||
#define RUSTRT_R12 5
|
||||
#define RUSTRT_R13 6
|
||||
#define RUSTRT_R14 7
|
||||
#define RUSTRT_R15 8
|
||||
#define RUSTRT_IP 9
|
||||
#define RUSTRT_XMM0 10
|
||||
#define RUSTRT_XMM1 12
|
||||
#define RUSTRT_XMM2 14
|
||||
#define RUSTRT_XMM3 16
|
||||
#define RUSTRT_XMM4 18
|
||||
#define RUSTRT_XMM5 20
|
||||
#define RUSTRT_MAX 22
|
||||
|
||||
// ARG0 is the register in which the first argument goes.
|
||||
// Naturally this depends on your operating system.
|
||||
#if defined(__MINGW32__) || defined(_WINDOWS)
|
||||
# define RUSTRT_ARG0 RUSTRT_RCX
|
||||
# define RUSTRT_ARG0_S %rcx
|
||||
# define RUSTRT_ARG1_S %rdx
|
||||
#else
|
||||
# define RUSTRT_ARG0 RUSTRT_RDI
|
||||
#endif
|
||||
|
||||
|
Loading…
Reference in New Issue