(index<- ) ./libgreen/context.rs
git branch: * master 5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
modified: Fri May 9 13:02:28 2014
1 // Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use stack::Stack;
12 use std::uint;
13 use std::cast::{transmute, transmute_mut_unsafe};
14 use std::rt::stack;
15 use std::raw;
16
17 // FIXME #7761: Registers is boxed so that it is 16-byte aligned, for storing
18 // SSE regs. It would be marginally better not to do this. In C++ we
19 // use an attribute on a struct.
20 // FIXME #7761: It would be nice to define regs as `Box<Option<Registers>>`
21 // since the registers are sometimes empty, but the discriminant would
22 // then misalign the regs again.
23 pub struct Context {
24 /// Hold the registers while the task or scheduler is suspended
25 regs: Box<Registers>,
26 /// Lower bound and upper bound for the stack
27 stack_bounds: Option<(uint, uint)>,
28 }
29
30 pub type InitFn = extern "C" fn(uint, *(), *()) -> !;
31
32 impl Context {
33 pub fn empty() -> Context {
34 Context {
35 regs: new_regs(),
36 stack_bounds: None,
37 }
38 }
39
40 /// Create a new context that will resume execution by running proc()
41 ///
42 /// The `init` function will be run with `arg` and the `start` procedure
43 /// split up into code and env pointers. It is required that the `init`
44 /// function never return.
45 ///
46 /// FIXME: this is basically an awful the interface. The main reason for
47 /// this is to reduce the number of allocations made when a green
48 /// task is spawned as much as possible
49 pub fn new(init: InitFn, arg: uint, start: proc():Send,
50 stack: &mut Stack) -> Context {
51
52 let sp: *uint = stack.end();
53 let sp: *mut uint = unsafe { transmute_mut_unsafe(sp) };
54 // Save and then immediately load the current context,
55 // which we will then modify to call the given function when restored
56 let mut regs = new_regs();
57
58 initialize_call_frame(&mut *regs,
59 init,
60 arg,
61 unsafe { transmute(start) },
62 sp);
63
64 // Scheduler tasks don't have a stack in the "we allocated it" sense,
65 // but rather they run on pthreads stacks. We have complete control over
66 // them in terms of the code running on them (and hopefully they don't
67 // overflow). Additionally, their coroutine stacks are listed as being
68 // zero-length, so that's how we detect what's what here.
69 let stack_base: *uint = stack.start();
70 let bounds = if sp as uint == stack_base as uint {
71 None
72 } else {
73 Some((stack_base as uint, sp as uint))
74 };
75 return Context {
76 regs: regs,
77 stack_bounds: bounds,
78 }
79 }
80
81 /* Switch contexts
82
83 Suspend the current execution context and resume another by
84 saving the registers values of the executing thread to a Context
85 then loading the registers from a previously saved Context.
86 */
87 pub fn swap(out_context: &mut Context, in_context: &Context) {
88 rtdebug!("swapping contexts");
89 let out_regs: &mut Registers = match out_context {
90 &Context { regs: box ref mut r, .. } => r
91 };
92 let in_regs: &Registers = match in_context {
93 &Context { regs: box ref r, .. } => r
94 };
95
96 rtdebug!("noting the stack limit and doing raw swap");
97
98 unsafe {
99 // Right before we switch to the new context, set the new context's
100 // stack limit in the OS-specified TLS slot. This also means that
101 // we cannot call any more rust functions after record_stack_bounds
102 // returns because they would all likely fail due to the limit being
103 // invalid for the current task. Lucky for us `rust_swap_registers`
104 // is a C function so we don't have to worry about that!
105 match in_context.stack_bounds {
106 Some((lo, hi)) => stack::record_stack_bounds(lo, hi),
107 // If we're going back to one of the original contexts or
108 // something that's possibly not a "normal task", then reset
109 // the stack limit to 0 to make morestack never fail
110 None => stack::record_stack_bounds(0, uint::MAX),
111 }
112 rust_swap_registers(out_regs, in_regs)
113 }
114 }
115 }
116
117 #[link(name = "context_switch", kind = "static")]
118 extern {
119 fn rust_swap_registers(out_regs: *mut Registers, in_regs: *Registers);
120 }
121
122 // Register contexts used in various architectures
123 //
124 // These structures all represent a context of one task throughout its
125 // execution. Each struct is a representation of the architecture's register
126 // set. When swapping between tasks, these register sets are used to save off
127 // the current registers into one struct, and load them all from another.
128 //
129 // Note that this is only used for context switching, which means that some of
130 // the registers may go unused. For example, for architectures with
131 // callee/caller saved registers, the context will only reflect the callee-saved
132 // registers. This is because the caller saved registers are already stored
133 // elsewhere on the stack (if it was necessary anyway).
134 //
135 // Additionally, there may be fields on various architectures which are unused
136 // entirely because they only reflect what is theoretically possible for a
137 // "complete register set" to show, but user-space cannot alter these registers.
138 // An example of this would be the segment selectors for x86.
139 //
140 // These structures/functions are roughly in-sync with the source files inside
141 // of src/rt/arch/$arch. The only currently used function from those folders is
142 // the `rust_swap_registers` function, but that's only because for now segmented
143 // stacks are disabled.
144
145 #[cfg(target_arch = "x86")]
146 struct Registers {
147 eax: u32, ebx: u32, ecx: u32, edx: u32,
148 ebp: u32, esi: u32, edi: u32, esp: u32,
149 cs: u16, ds: u16, ss: u16, es: u16, fs: u16, gs: u16,
150 eflags: u32, eip: u32
151 }
152
153 #[cfg(target_arch = "x86")]
154 fn new_regs() -> Box<Registers> {
155 box Registers {
156 eax: 0, ebx: 0, ecx: 0, edx: 0,
157 ebp: 0, esi: 0, edi: 0, esp: 0,
158 cs: 0, ds: 0, ss: 0, es: 0, fs: 0, gs: 0,
159 eflags: 0, eip: 0
160 }
161 }
162
163 #[cfg(target_arch = "x86")]
164 fn initialize_call_frame(regs: &mut Registers, fptr: InitFn, arg: uint,
165 procedure: raw::Procedure, sp: *mut uint) {
166
167 // x86 has interesting stack alignment requirements, so do some alignment
168 // plus some offsetting to figure out what the actual stack should be.
169 let sp = align_down(sp);
170 let sp = mut_offset(sp, -4);
171
172 unsafe { *mut_offset(sp, 2) = procedure.env as uint };
173 unsafe { *mut_offset(sp, 1) = procedure.code as uint };
174 unsafe { *mut_offset(sp, 0) = arg as uint };
175 let sp = mut_offset(sp, -1);
176 unsafe { *sp = 0 }; // The final return address
177
178 regs.esp = sp as u32;
179 regs.eip = fptr as u32;
180
181 // Last base pointer on the stack is 0
182 regs.ebp = 0;
183 }
184
185 // windows requires saving more registers (both general and XMM), so the windows
186 // register context must be larger.
187 #[cfg(windows, target_arch = "x86_64")]
188 type Registers = [uint, ..34];
189 #[cfg(not(windows), target_arch = "x86_64")]
190 type Registers = [uint, ..22];
191
192 #[cfg(windows, target_arch = "x86_64")]
193 fn new_regs() -> Box<Registers> { box [0, .. 34] }
194 #[cfg(not(windows), target_arch = "x86_64")]
195 fn new_regs() -> Box<Registers> { box {let v = [0, .. 22]; v} }
196
197 #[cfg(target_arch = "x86_64")]
198 fn initialize_call_frame(regs: &mut Registers, fptr: InitFn, arg: uint,
199 procedure: raw::Procedure, sp: *mut uint) {
200 extern { fn rust_bootstrap_green_task(); }
201
202 // Redefinitions from rt/arch/x86_64/regs.h
203 static RUSTRT_RSP: uint = 1;
204 static RUSTRT_IP: uint = 8;
205 static RUSTRT_RBP: uint = 2;
206 static RUSTRT_R12: uint = 4;
207 static RUSTRT_R13: uint = 5;
208 static RUSTRT_R14: uint = 6;
209 static RUSTRT_R15: uint = 7;
210
211 let sp = align_down(sp);
212 let sp = mut_offset(sp, -1);
213
214 // The final return address. 0 indicates the bottom of the stack
215 unsafe { *sp = 0; }
216
217 rtdebug!("creating call frame");
218 rtdebug!("fptr {:#x}", fptr as uint);
219 rtdebug!("arg {:#x}", arg);
220 rtdebug!("sp {}", sp);
221
222 // These registers are frobbed by rust_bootstrap_green_task into the right
223 // location so we can invoke the "real init function", `fptr`.
224 regs[RUSTRT_R12] = arg as uint;
225 regs[RUSTRT_R13] = procedure.code as uint;
226 regs[RUSTRT_R14] = procedure.env as uint;
227 regs[RUSTRT_R15] = fptr as uint;
228
229 // These registers are picked up by the regulard context switch paths. These
230 // will put us in "mostly the right context" except for frobbing all the
231 // arguments to the right place. We have the small trampoline code inside of
232 // rust_bootstrap_green_task to do that.
233 regs[RUSTRT_RSP] = sp as uint;
234 regs[RUSTRT_IP] = rust_bootstrap_green_task as uint;
235
236 // Last base pointer on the stack should be 0
237 regs[RUSTRT_RBP] = 0;
238 }
239
240 #[cfg(target_arch = "arm")]
241 type Registers = [uint, ..32];
242
243 #[cfg(target_arch = "arm")]
244 fn new_regs() -> Box<Registers> { box {[0, .. 32]} }
245
246 #[cfg(target_arch = "arm")]
247 fn initialize_call_frame(regs: &mut Registers, fptr: InitFn, arg: uint,
248 procedure: raw::Procedure, sp: *mut uint) {
249 extern { fn rust_bootstrap_green_task(); }
250
251 let sp = align_down(sp);
252 // sp of arm eabi is 8-byte aligned
253 let sp = mut_offset(sp, -2);
254
255 // The final return address. 0 indicates the bottom of the stack
256 unsafe { *sp = 0; }
257
258 // ARM uses the same technique as x86_64 to have a landing pad for the start
259 // of all new green tasks. Neither r1/r2 are saved on a context switch, so
260 // the shim will copy r3/r4 into r1/r2 and then execute the function in r5
261 regs[0] = arg as uint; // r0
262 regs[3] = procedure.code as uint; // r3
263 regs[4] = procedure.env as uint; // r4
264 regs[5] = fptr as uint; // r5
265 regs[13] = sp as uint; // #52 sp, r13
266 regs[14] = rust_bootstrap_green_task as uint; // #56 pc, r14 --> lr
267 }
268
269 #[cfg(target_arch = "mips")]
270 type Registers = [uint, ..32];
271
272 #[cfg(target_arch = "mips")]
273 fn new_regs() -> Box<Registers> { box [0, .. 32] }
274
275 #[cfg(target_arch = "mips")]
276 fn initialize_call_frame(regs: &mut Registers, fptr: InitFn, arg: uint,
277 procedure: raw::Procedure, sp: *mut uint) {
278 let sp = align_down(sp);
279 // sp of mips o32 is 8-byte aligned
280 let sp = mut_offset(sp, -2);
281
282 // The final return address. 0 indicates the bottom of the stack
283 unsafe { *sp = 0; }
284
285 regs[4] = arg as uint;
286 regs[5] = procedure.code as uint;
287 regs[6] = procedure.env as uint;
288 regs[29] = sp as uint;
289 regs[25] = fptr as uint;
290 regs[31] = fptr as uint;
291 }
292
293 fn align_down(sp: *mut uint) -> *mut uint {
294 let sp = (sp as uint) & !(16 - 1);
295 sp as *mut uint
296 }
297
298 // ptr::mut_offset is positive ints only
299 #[inline]
300 pub fn mut_offset<T>(ptr: *mut T, count: int) -> *mut T {
301 use std::mem::size_of;
302 (ptr as int + count * (size_of::<T>() as int)) as *mut T
303 }
libgreen/context.rs:194:45-194:45 -fn- definition:
fn new_regs() -> Box<Registers> { box {let v = [0, .. 22]; v} }
fn initialize_call_frame(regs: &mut Registers, fptr: InitFn, arg: uint,
procedure: raw::Procedure, sp: *mut uint) {
references:- 255: // which we will then modify to call the given function when restored
56: let mut regs = new_regs();
libgreen/context.rs:22:33-22:33 -struct- definition:
// then misalign the regs again.
pub struct Context {
/// Hold the registers while the task or scheduler is suspended
references:- 1274: };
75: return Context {
76: regs: regs,
--
86: */
87: pub fn swap(out_context: &mut Context, in_context: &Context) {
88: rtdebug!("swapping contexts");
89: let out_regs: &mut Registers = match out_context {
90: &Context { regs: box ref mut r, .. } => r
91: };
92: let in_regs: &Registers = match in_context {
93: &Context { regs: box ref r, .. } => r
94: };
libgreen/coroutine.rs:
27: /// Always valid if the task is alive and not running.
28: pub saved_context: Context
29: }
libgreen/sched.rs:
680: pub fn get_contexts<'a>(current_task: &mut GreenTask, next_task: &mut GreenTask) ->
681: (&'a mut Context, &'a mut Context) {
682: let current_task_context =
libgreen/context.rs:
32: impl Context {
33: pub fn empty() -> Context {
libgreen/context.rs:189:45-189:45 -NK_AS_STR_TODO- definition:
type Registers = [uint, ..22];
fn new_regs() -> Box<Registers> { box [0, .. 34] }
fn new_regs() -> Box<Registers> { box {let v = [0, .. 22]; v} }
references:- 7198: fn initialize_call_frame(regs: &mut Registers, fptr: InitFn, arg: uint,
199: procedure: raw::Procedure, sp: *mut uint) {
libgreen/context.rs:29:1-29:1 -NK_AS_STR_TODO- definition:
pub type InitFn = extern "C" fn(uint, *(), *()) -> !;
impl Context {
pub fn empty() -> Context {
references:- 2198: fn initialize_call_frame(regs: &mut Registers, fptr: InitFn, arg: uint,
199: procedure: raw::Procedure, sp: *mut uint) {