Skip to content

Commit

Permalink
Support Win64 context switching
Browse files Browse the repository at this point in the history
This patch saves and restores win64's nonvolatile registers.
This patch also saves stack information of thread environment
block (TEB), which is at %gs:0x08 and %gs:0x10.
  • Loading branch information
klutzy committed Aug 26, 2013
1 parent 63e53b8 commit 442f4a5
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 16 deletions.
38 changes: 31 additions & 7 deletions src/libstd/rt/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ impl Context {

let fp: *c_void = task_start_wrapper as *c_void;
let argp: *c_void = unsafe { transmute::<&~fn(), *c_void>(&*start) };
let stack_base: *uint = stack.start();
let sp: *uint = stack.end();
let sp: *mut uint = unsafe { transmute_mut_unsafe(sp) };
// Save and then immediately load the current context,
Expand All @@ -56,7 +57,7 @@ impl Context {
swap_registers(transmute_mut_region(&mut *regs), transmute_region(&*regs));
};

initialize_call_frame(&mut *regs, fp, argp, sp);
initialize_call_frame(&mut *regs, fp, argp, sp, stack_base);

return Context {
start: Some(start),
Expand Down Expand Up @@ -107,7 +108,8 @@ fn new_regs() -> ~Registers {
}

#[cfg(target_arch = "x86")]
fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void, sp: *mut uint) {
fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void,
sp: *mut uint, _stack_base: *uint) {

let sp = align_down(sp);
let sp = mut_offset(sp, -4);
Expand All @@ -123,21 +125,41 @@ fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void, sp:
regs.ebp = 0;
}

#[cfg(target_arch = "x86_64")]
#[cfg(windows, target_arch = "x86_64")]
type Registers = [uint, ..34];
#[cfg(not(windows), target_arch = "x86_64")]
type Registers = [uint, ..22];

#[cfg(target_arch = "x86_64")]
#[cfg(windows, target_arch = "x86_64")]
fn new_regs() -> ~Registers { ~([0, .. 34]) }
#[cfg(not(windows), target_arch = "x86_64")]
fn new_regs() -> ~Registers { ~([0, .. 22]) }

#[cfg(target_arch = "x86_64")]
fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void, sp: *mut uint) {
fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void,
sp: *mut uint, stack_base: *uint) {

// Redefinitions from regs.h
static RUSTRT_ARG0: uint = 3;
static RUSTRT_RSP: uint = 1;
static RUSTRT_IP: uint = 8;
static RUSTRT_RBP: uint = 2;

#[cfg(windows)]
fn initialize_tib(regs: &mut Registers, sp: *mut uint, stack_base: *uint) {
// Redefinitions from regs.h
static RUSTRT_ST1: uint = 11; // stack bottom
static RUSTRT_ST2: uint = 12; // stack top
regs[RUSTRT_ST1] = sp as uint;
regs[RUSTRT_ST2] = stack_base as uint;
}
#[cfg(not(windows))]
fn initialize_tib(_: &mut Registers, _: *mut uint, _: *uint) {
}

// Win64 manages stack range at TIB: %gs:0x08 (top) and %gs:0x10 (bottom)
initialize_tib(regs, sp, stack_base);

let sp = align_down(sp);
let sp = mut_offset(sp, -1);

Expand All @@ -164,7 +186,8 @@ type Registers = [uint, ..32];
fn new_regs() -> ~Registers { ~([0, .. 32]) }

#[cfg(target_arch = "arm")]
fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void, sp: *mut uint) {
fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void,
sp: *mut uint, _stack_base: *uint) {
let sp = align_down(sp);
// sp of arm eabi is 8-byte aligned
let sp = mut_offset(sp, -2);
Expand All @@ -184,7 +207,8 @@ type Registers = [uint, ..32];
fn new_regs() -> ~Registers { ~([0, .. 32]) }

#[cfg(target_arch = "mips")]
fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void, sp: *mut uint) {
fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void,
sp: *mut uint, _stack_base: *uint) {
let sp = align_down(sp);
// sp of mips o32 is 8-byte aligned
let sp = mut_offset(sp, -2);
Expand Down
48 changes: 48 additions & 0 deletions src/rt/arch/x86_64/_context.S
Original file line number Diff line number Diff line change
Expand Up @@ -86,16 +86,40 @@ SWAP_REGISTERS:
mov %r14, (RUSTRT_R14*8)(ARG0)
mov %r15, (RUSTRT_R15*8)(ARG0)

#if defined(__MINGW32__) || defined(_WINDOWS)
mov %rdi, (RUSTRT_RDI*8)(ARG0)
mov %rsi, (RUSTRT_RSI*8)(ARG0)

// Save stack range
mov %gs:0x08, %r8
mov %r8, (RUSTRT_ST1*8)(ARG0)
mov %gs:0x10, %r9
mov %r9, (RUSTRT_ST2*8)(ARG0)
#endif

// Save 0th argument register:
mov ARG0, (RUSTRT_ARG0*8)(ARG0)

// Save non-volatile XMM registers:
#if defined(__MINGW32__) || defined(_WINDOWS)
movapd %xmm6, (RUSTRT_XMM6*8)(ARG0)
movapd %xmm7, (RUSTRT_XMM7*8)(ARG0)
movapd %xmm8, (RUSTRT_XMM8*8)(ARG0)
movapd %xmm9, (RUSTRT_XMM9*8)(ARG0)
movapd %xmm10, (RUSTRT_XMM10*8)(ARG0)
movapd %xmm11, (RUSTRT_XMM11*8)(ARG0)
movapd %xmm12, (RUSTRT_XMM12*8)(ARG0)
movapd %xmm13, (RUSTRT_XMM13*8)(ARG0)
movapd %xmm14, (RUSTRT_XMM14*8)(ARG0)
movapd %xmm15, (RUSTRT_XMM15*8)(ARG0)
#else
movapd %xmm0, (RUSTRT_XMM0*8)(ARG0)
movapd %xmm1, (RUSTRT_XMM1*8)(ARG0)
movapd %xmm2, (RUSTRT_XMM2*8)(ARG0)
movapd %xmm3, (RUSTRT_XMM3*8)(ARG0)
movapd %xmm4, (RUSTRT_XMM4*8)(ARG0)
movapd %xmm5, (RUSTRT_XMM5*8)(ARG0)
#endif

// Restore non-volatile integer registers:
// (including RSP)
Expand All @@ -107,16 +131,40 @@ SWAP_REGISTERS:
mov (RUSTRT_R14*8)(ARG1), %r14
mov (RUSTRT_R15*8)(ARG1), %r15

#if defined(__MINGW32__) || defined(_WINDOWS)
mov (RUSTRT_RDI*8)(ARG1), %rdi
mov (RUSTRT_RSI*8)(ARG1), %rsi

// Restore stack range
mov (RUSTRT_ST1*8)(ARG1), %r8
mov %r8, %gs:0x08
mov (RUSTRT_ST2*8)(ARG1), %r9
mov %r9, %gs:0x10
#endif

// Restore 0th argument register:
mov (RUSTRT_ARG0*8)(ARG1), ARG0

// Restore non-volatile XMM registers:
#if defined(__MINGW32__) || defined(_WINDOWS)
movapd (RUSTRT_XMM6*8)(ARG1), %xmm6
movapd (RUSTRT_XMM7*8)(ARG1), %xmm7
movapd (RUSTRT_XMM8*8)(ARG1), %xmm8
movapd (RUSTRT_XMM9*8)(ARG1), %xmm9
movapd (RUSTRT_XMM10*8)(ARG1), %xmm10
movapd (RUSTRT_XMM11*8)(ARG1), %xmm11
movapd (RUSTRT_XMM12*8)(ARG1), %xmm12
movapd (RUSTRT_XMM13*8)(ARG1), %xmm13
movapd (RUSTRT_XMM14*8)(ARG1), %xmm14
movapd (RUSTRT_XMM15*8)(ARG1), %xmm15
#else
movapd (RUSTRT_XMM0*8)(ARG1), %xmm0
movapd (RUSTRT_XMM1*8)(ARG1), %xmm1
movapd (RUSTRT_XMM2*8)(ARG1), %xmm2
movapd (RUSTRT_XMM3*8)(ARG1), %xmm3
movapd (RUSTRT_XMM4*8)(ARG1), %xmm4
movapd (RUSTRT_XMM5*8)(ARG1), %xmm5
#endif

// Jump to the instruction pointer
// found in regs:
Expand Down
36 changes: 27 additions & 9 deletions src/rt/arch/x86_64/regs.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,33 @@
#define RUSTRT_R14 6
#define RUSTRT_R15 7
#define RUSTRT_IP 8
// Not used, just padding
#define RUSTRT_XXX 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
#if defined(__MINGW32__) || defined(_WINDOWS)
#define RUSTRT_RDI 9
#define RUSTRT_RSI 10
#define RUSTRT_ST1 11
#define RUSTRT_ST2 12
#define RUSTRT_XMM6 14
#define RUSTRT_XMM7 16
#define RUSTRT_XMM8 18
#define RUSTRT_XMM9 20
#define RUSTRT_XMM10 22
#define RUSTRT_XMM11 24
#define RUSTRT_XMM12 26
#define RUSTRT_XMM13 28
#define RUSTRT_XMM14 30
#define RUSTRT_XMM15 32
#define RUSTRT_MAX 34
#else
// Not used, just padding
#define RUSTRT_XXX 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
#endif

// ARG0 is the register in which the first argument goes.
// Naturally this depends on your operating system.
Expand Down

1 comment on commit 442f4a5

@brson
Copy link

@brson brson commented on 442f4a5 Aug 26, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

r+

Please sign in to comment.