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