diff --git a/src/rt/arch/x86_64/_context.S b/src/rt/arch/x86_64/_context.S index 2eba7296df2..5632df932f3 100644 --- a/src/rt/arch/x86_64/_context.S +++ b/src/rt/arch/x86_64/_context.S @@ -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) diff --git a/src/rt/arch/x86_64/context.cpp b/src/rt/arch/x86_64/context.cpp index edfae486b4f..09577dbd151 100644 --- a/src/rt/arch/x86_64/context.cpp +++ b/src/rt/arch/x86_64/context.cpp @@ -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; } diff --git a/src/rt/arch/x86_64/context.h b/src/rt/arch/x86_64/context.h index 305298093cc..5d764cfdb0a 100644 --- a/src/rt/arch/x86_64/context.h +++ b/src/rt/arch/x86_64/context.h @@ -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 : +#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(top); - } + return reinterpret_cast(top); + } }; #endif diff --git a/src/rt/arch/x86_64/x86_64_regs.h b/src/rt/arch/x86_64/x86_64_regs.h new file mode 100644 index 00000000000..084ab03fdc4 --- /dev/null +++ b/src/rt/arch/x86_64/x86_64_regs.h @@ -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 + +