copy over x86-specific code

This commit is contained in:
Niko Matsakis 2011-10-12 15:45:17 -07:00 committed by Brian Anderson
parent 5b3bddcd87
commit 6a0d86c754
4 changed files with 233 additions and 0 deletions

View File

@ -0,0 +1,76 @@
.text
/*
Callee save registers:
ebp, ebx, esi, edi
Caller save registers:
eax, ecx, edx
*/
/*
Stores current registers into arg0/RCX and restores
registers found in arg1/RDX. This is used by our
implementation of getcontext.
*/
// swap_registers(registers_t *oregs, registers_t *regs)
.globl swap_registers
swap_registers:
// save the old context
movl 4(%esp), %eax
//movl %eax, 0(%eax)
movl %ebx, 4(%eax)
movl %ecx, 8(%eax)
movl %edx, 12(%eax)
movl %ebp, 16(%eax)
movl %esi, 20(%eax)
movl %edi, 24(%eax)
//movl %cs, 32(%eax)
//movl %ds, 34(%eax)
//movl %ss, 36(%eax)
//movl %es, 38(%eax)
//movl %fs, 40(%eax)
//movl %gs, 42(%eax)
// save the flags
pushf
popl %ecx
movl %ecx, 44(%eax)
// save the return address as the instruction pointer
// and save the stack pointer of the caller
popl %ecx
movl %esp, 28(%eax)
movl %ecx, 48(%eax)
// restore the new context
movl 4(%esp), %eax
movl 4(%eax), %ebx
// save ecx for later...
movl 12(%eax), %edx
movl 16(%eax), %ebp
movl 20(%eax), %esi
movl 24(%eax), %edi
movl 28(%eax), %esp
// We can't actually change this...
//movl 32(%eax), %cs
//movl 34(%eax), %ds
//movl 36(%eax), %ss
//movl 38(%eax), %es
//movl 40(%eax), %fs
//movl 42(%eax), %gs
// restore the flags
movl 44(%eax), %ecx
push %ecx
popf
// ok, now we can restore ecx
movl 8(%eax), %ecx
// Return!
jmp *48(%eax)

22
src/rt/arch/x64/ccall.S Normal file
View File

@ -0,0 +1,22 @@
.text
// upcall_call_c_stack(void (*fn)(), void *new_esp)
//
// Note that we could use |enter| and |leave| but the manuals tell me they're
// slower.
#if defined(__APPLE__) || defined(_WIN32)
.globl _upcall_call_c_stack
_upcall_call_c_stack:
#else
.globl upcall_call_c_stack
upcall_call_c_stack:
#endif
pushl %ebp
movl %esp,%ebp // save esp
movl 8(%esp),%eax // eax = callee
movl 12(%esp),%esp // switch stack
calll *%eax
movl %ebp,%esp // would like to use "leave" but it's slower
popl %ebp
ret

View File

@ -0,0 +1,73 @@
#include "context.h"
#include "../../rust.h"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
extern "C" uint32_t CDECL swap_registers(registers_t *oregs,
registers_t *regs)
asm ("swap_registers");
context::context()
{
assert((void*)&regs == (void*)this);
}
void context::swap(context &out)
{
swap_registers(&out.regs, &regs);
}
void context::call(void *f, void *arg, void *stack) {
// Get the current context, which we will then modify to call the
// given function.
swap(*this);
// set up the trampoline frame
uint32_t *sp = (uint32_t *)stack;
// Shift the stack pointer so the alignment works out right.
sp = align_down(sp) - 3;
*--sp = (uint32_t)arg;
*--sp = 0xdeadbeef;
regs.esp = (uint32_t)sp;
regs.eip = (uint32_t)f;
}
#if 0
// This is some useful code to check how the registers struct got
// layed out in memory.
int main() {
registers_t regs;
printf("Register offsets\n");
#define REG(r) \
printf(" %6s: +%ld\n", #r, (intptr_t)&regs.r - (intptr_t)&regs);
REG(eax);
REG(ebx);
REG(ecx);
REG(edx);
REG(ebp);
REG(esi);
REG(edi);
REG(esp);
REG(cs);
REG(ds);
REG(ss);
REG(es);
REG(fs);
REG(gs);
REG(eflags);
REG(eip);
return 0;
}
#endif

62
src/rt/arch/x64/context.h Normal file
View File

@ -0,0 +1,62 @@
// -*- mode: c++ -*-
#ifndef CONTEXT_H
#define CONTEXT_H
#include <cstdlib>
#include <inttypes.h>
#include <stdint.h>
#ifdef HAVE_VALGRIND
#include <valgrind/memcheck.h>
#endif
template<typename T>
T align_down(T sp)
{
// There is no platform we care about that needs more than a
// 16-byte alignment.
return (T)((uint32_t)sp & ~(16 - 1));
}
struct registers_t {
// general purpose registers
uint32_t eax, ebx, ecx, edx, ebp, esi, edi, esp;
// segment registers
uint16_t cs, ds, ss, es, fs, gs;
uint32_t eflags;
uint32_t eip;
};
class context {
public:
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.esp;
uint32_t top = align_down(bot - nbytes);
#ifdef HAVE_VALGRIND
(void)VALGRIND_MAKE_MEM_UNDEFINED(top - 4, bot - top + 4);
#endif
return reinterpret_cast<void *>(top);
}
};
#endif