use detail::{initialize_call_frame, swap_registers, Registers};
use stack::Stack;
#[derive(Debug)]
pub struct RegContext {
regs: Registers,
}
pub type InitFn = fn(usize, *mut usize) -> !;
impl RegContext {
pub fn empty() -> RegContext {
RegContext {
regs: Registers::new(),
}
}
#[inline]
pub fn prefetch(&self) {
self.regs.prefetch();
}
#[allow(dead_code)]
pub fn new(init: InitFn, arg: usize, start: *mut usize, stack: &Stack) -> RegContext {
let mut ctx = RegContext::empty();
ctx.init_with(init, arg, start, stack);
ctx
}
#[inline]
pub fn init_with(&mut self, init: InitFn, arg: usize, start: *mut usize, stack: &Stack) {
initialize_call_frame(&mut self.regs, init, arg, start, stack);
}
#[inline]
pub fn swap(out_context: &mut RegContext, in_context: &RegContext) {
let out_regs: &mut Registers = match *out_context {
RegContext {
regs: ref mut r, ..
} => r,
};
let in_regs: &Registers = match *in_context {
RegContext { regs: ref r, .. } => r,
};
unsafe { swap_registers(out_regs, in_regs) }
}
#[inline]
#[allow(dead_code)]
pub fn load(to_context: &RegContext) {
let mut cur = Registers::new();
let regs: &Registers = &to_context.regs;
unsafe { swap_registers(&mut cur, regs) }
}
}
#[cfg(test)]
mod test {
use std::mem::transmute;
use stack::Stack;
use reg_context::RegContext;
const MIN_STACK: usize = 2 * 1024 * 1024;
fn init_fn(arg: usize, f: *mut usize) -> ! {
let func: fn() = unsafe { transmute(f) };
func();
let ctx: &RegContext = unsafe { transmute(arg) };
RegContext::load(ctx);
unreachable!("Should never comeback");
}
#[test]
fn test_swap_context() {
static mut VAL: bool = false;
let mut cur = RegContext::empty();
fn callback() {
unsafe {
VAL = true;
}
}
let stk = Stack::new(MIN_STACK);
let ctx = RegContext::new(
init_fn,
unsafe { transmute(&cur) },
unsafe { transmute(callback as usize) },
&stk,
);
RegContext::swap(&mut cur, &ctx);
unsafe {
assert!(VAL);
}
}
}