8
8
// option. This file may not be copied, modified, or distributed
9
9
// except according to those terms.
10
10
11
- use std:: libc:: c_void;
12
11
use std:: uint;
13
12
use std:: cast:: { transmute, transmute_mut_unsafe,
14
13
transmute_region, transmute_mut_region} ;
15
14
use stack:: Stack ;
16
15
use std:: unstable:: stack;
16
+ use std:: unstable:: raw;
17
17
18
18
// FIXME #7761: Registers is boxed so that it is 16-byte aligned, for storing
19
19
// SSE regs. It would be marginally better not to do this. In C++ we
@@ -22,47 +22,33 @@ use std::unstable::stack;
22
22
// the registers are sometimes empty, but the discriminant would
23
23
// then misalign the regs again.
24
24
pub struct Context {
25
- /// The context entry point, saved here for later destruction
26
- priv start : Option < ~proc ( ) > ,
27
25
/// Hold the registers while the task or scheduler is suspended
28
26
priv regs : ~Registers ,
29
27
/// Lower bound and upper bound for the stack
30
28
priv stack_bounds : Option < ( uint , uint ) > ,
31
29
}
32
30
31
+ pub type InitFn = extern "C" fn ( uint , * ( ) , * ( ) ) -> !;
32
+
33
33
impl Context {
34
34
pub fn empty ( ) -> Context {
35
35
Context {
36
- start : None ,
37
36
regs : new_regs ( ) ,
38
37
stack_bounds : None ,
39
38
}
40
39
}
41
40
42
41
/// Create a new context that will resume execution by running proc()
43
- pub fn new ( start : proc ( ) , stack : & mut Stack ) -> Context {
44
- // The C-ABI function that is the task entry point
45
- //
46
- // Note that this function is a little sketchy. We're taking a
47
- // procedure, transmuting it to a stack-closure, and then calling to
48
- // closure. This leverages the fact that the representation of these two
49
- // types is the same.
50
- //
51
- // The reason that we're doing this is that this procedure is expected
52
- // to never return. The codegen which frees the environment of the
53
- // procedure occurs *after* the procedure has completed, and this means
54
- // that we'll never actually free the procedure.
55
- //
56
- // To solve this, we use this transmute (to not trigger the procedure
57
- // deallocation here), and then store a copy of the procedure in the
58
- // `Context` structure returned. When the `Context` is deallocated, then
59
- // the entire procedure box will be deallocated as well.
60
- extern fn task_start_wrapper ( f : & proc ( ) ) {
61
- unsafe {
62
- let f: & || = transmute ( f ) ;
63
- ( * f) ( )
64
- }
65
- }
42
+ ///
43
+ /// The `init` function will be run with `arg` and the `start` procedure
44
+ /// split up into code and env pointers. It is required that the `init`
45
+ /// function never return.
46
+ ///
47
+ /// FIXME: this is basically an awful the interface. The main reason for
48
+ /// this is to reduce the number of allocations made when a green
49
+ /// task is spawned as much as possible
50
+ pub fn new ( init : InitFn , arg : uint , start : proc ( ) ,
51
+ stack : & mut Stack ) -> Context {
66
52
67
53
let sp: * uint = stack. end ( ) ;
68
54
let sp: * mut uint = unsafe { transmute_mut_unsafe ( sp) } ;
@@ -74,14 +60,10 @@ impl Context {
74
60
transmute_region ( & * regs) ) ;
75
61
} ;
76
62
77
- // FIXME #7767: Putting main into a ~ so it's a thin pointer and can
78
- // be passed to the spawn function. Another unfortunate
79
- // allocation
80
- let start = ~start;
81
-
82
63
initialize_call_frame ( & mut * regs,
83
- task_start_wrapper as * c_void ,
84
- unsafe { transmute ( & * start) } ,
64
+ init,
65
+ arg,
66
+ unsafe { transmute ( start) } ,
85
67
sp) ;
86
68
87
69
// Scheduler tasks don't have a stack in the "we allocated it" sense,
@@ -96,7 +78,6 @@ impl Context {
96
78
Some ( ( stack_base as uint , sp as uint ) )
97
79
} ;
98
80
return Context {
99
- start : Some ( start) ,
100
81
regs : regs,
101
82
stack_bounds : bounds,
102
83
}
@@ -138,7 +119,7 @@ impl Context {
138
119
}
139
120
}
140
121
141
- #[ link( name = "rustrt " , kind = "static" ) ]
122
+ #[ link( name = "context_switch " , kind = "static" ) ]
142
123
extern {
143
124
fn rust_swap_registers ( out_regs : * mut Registers , in_regs : * Registers ) ;
144
125
}
@@ -185,13 +166,17 @@ fn new_regs() -> ~Registers {
185
166
}
186
167
187
168
#[ cfg( target_arch = "x86" ) ]
188
- fn initialize_call_frame ( regs : & mut Registers , fptr : * c_void , arg : * c_void ,
189
- sp : * mut uint ) {
169
+ fn initialize_call_frame ( regs : & mut Registers , fptr : InitFn , arg : uint ,
170
+ procedure : raw :: Procedure , sp : * mut uint ) {
190
171
172
+ // x86 has interesting stack alignment requirements, so do some alignment
173
+ // plus some offsetting to figure out what the actual stack should be.
191
174
let sp = align_down ( sp) ;
192
175
let sp = mut_offset ( sp, -4 ) ;
193
176
194
- unsafe { * sp = arg as uint } ;
177
+ unsafe { * mut_offset ( sp, 2 ) = procedure. env as uint } ;
178
+ unsafe { * mut_offset ( sp, 1 ) = procedure. code as uint } ;
179
+ unsafe { * mut_offset ( sp, 0 ) = arg as uint } ;
195
180
let sp = mut_offset ( sp, -1 ) ;
196
181
unsafe { * sp = 0 } ; // The final return address
197
182
@@ -215,14 +200,18 @@ fn new_regs() -> ~Registers { ~([0, .. 34]) }
215
200
fn new_regs ( ) -> ~Registers { ~( [ 0 , .. 22 ] ) }
216
201
217
202
#[ cfg( target_arch = "x86_64" ) ]
218
- fn initialize_call_frame ( regs : & mut Registers , fptr : * c_void , arg : * c_void ,
219
- sp : * mut uint ) {
203
+ fn initialize_call_frame ( regs : & mut Registers , fptr : InitFn , arg : uint ,
204
+ procedure : raw:: Procedure , sp : * mut uint ) {
205
+ extern { fn rust_bootstrap_green_task ( ) ; }
220
206
221
207
// Redefinitions from rt/arch/x86_64/regs.h
222
- static RUSTRT_ARG0 : uint = 3 ;
223
208
static RUSTRT_RSP : uint = 1 ;
224
209
static RUSTRT_IP : uint = 8 ;
225
210
static RUSTRT_RBP : uint = 2 ;
211
+ static RUSTRT_R12 : uint = 4 ;
212
+ static RUSTRT_R13 : uint = 5 ;
213
+ static RUSTRT_R14 : uint = 6 ;
214
+ static RUSTRT_R15 : uint = 7 ;
226
215
227
216
let sp = align_down ( sp) ;
228
217
let sp = mut_offset ( sp, -1 ) ;
@@ -231,13 +220,23 @@ fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void,
231
220
unsafe { * sp = 0 ; }
232
221
233
222
rtdebug ! ( "creating call frame" ) ;
234
- rtdebug ! ( "fptr {}" , fptr) ;
235
- rtdebug ! ( "arg {}" , arg) ;
223
+ rtdebug ! ( "fptr {:#x }" , fptr as uint ) ;
224
+ rtdebug ! ( "arg {:#x }" , arg) ;
236
225
rtdebug ! ( "sp {}" , sp) ;
237
226
238
- regs[ RUSTRT_ARG0 ] = arg as uint ;
227
+ // These registers are frobbed by rust_bootstrap_green_task into the right
228
+ // location so we can invoke the "real init function", `fptr`.
229
+ regs[ RUSTRT_R12 ] = arg as uint ;
230
+ regs[ RUSTRT_R13 ] = procedure. code as uint ;
231
+ regs[ RUSTRT_R14 ] = procedure. env as uint ;
232
+ regs[ RUSTRT_R15 ] = fptr as uint ;
233
+
234
+ // These registers are picked up by the regulard context switch paths. These
235
+ // will put us in "mostly the right context" except for frobbing all the
236
+ // arguments to the right place. We have the small trampoline code inside of
237
+ // rust_bootstrap_green_task to do that.
239
238
regs[ RUSTRT_RSP ] = sp as uint ;
240
- regs[ RUSTRT_IP ] = fptr as uint ;
239
+ regs[ RUSTRT_IP ] = rust_bootstrap_green_task as uint ;
241
240
242
241
// Last base pointer on the stack should be 0
243
242
regs[ RUSTRT_RBP ] = 0 ;
@@ -250,18 +249,26 @@ type Registers = [uint, ..32];
250
249
fn new_regs ( ) -> ~Registers { ~( [ 0 , .. 32 ] ) }
251
250
252
251
#[ cfg( target_arch = "arm" ) ]
253
- fn initialize_call_frame ( regs : & mut Registers , fptr : * c_void , arg : * c_void ,
254
- sp : * mut uint ) {
252
+ fn initialize_call_frame ( regs : & mut Registers , fptr : InitFn , arg : uint ,
253
+ procedure : raw:: Procedure , sp : * mut uint ) {
254
+ extern { fn rust_bootstrap_green_task ( ) ; }
255
+
255
256
let sp = align_down ( sp) ;
256
257
// sp of arm eabi is 8-byte aligned
257
258
let sp = mut_offset ( sp, -2 ) ;
258
259
259
260
// The final return address. 0 indicates the bottom of the stack
260
261
unsafe { * sp = 0 ; }
261
262
262
- regs[ 0 ] = arg as uint ; // r0
263
- regs[ 13 ] = sp as uint ; // #53 sp, r13
264
- regs[ 14 ] = fptr as uint ; // #60 pc, r15 --> lr
263
+ // ARM uses the same technique as x86_64 to have a landing pad for the start
264
+ // of all new green tasks. Neither r1/r2 are saved on a context switch, so
265
+ // the shim will copy r3/r4 into r1/r2 and then execute the function in r5
266
+ regs[ 0 ] = arg as uint ; // r0
267
+ regs[ 3 ] = procedure. code as uint ; // r3
268
+ regs[ 4 ] = procedure. env as uint ; // r4
269
+ regs[ 5 ] = fptr as uint ; // r5
270
+ regs[ 13 ] = sp as uint ; // #52 sp, r13
271
+ regs[ 14 ] = rust_bootstrap_green_task as uint ; // #56 pc, r14 --> lr
265
272
}
266
273
267
274
#[ cfg( target_arch = "mips" ) ]
@@ -271,8 +278,8 @@ type Registers = [uint, ..32];
271
278
fn new_regs ( ) -> ~Registers { ~( [ 0 , .. 32 ] ) }
272
279
273
280
#[ cfg( target_arch = "mips" ) ]
274
- fn initialize_call_frame ( regs : & mut Registers , fptr : * c_void , arg : * c_void ,
275
- sp : * mut uint ) {
281
+ fn initialize_call_frame ( regs : & mut Registers , fptr : InitFn , arg : uint ,
282
+ procedure : raw :: Procedure , sp : * mut uint ) {
276
283
let sp = align_down ( sp) ;
277
284
// sp of mips o32 is 8-byte aligned
278
285
let sp = mut_offset ( sp, -2 ) ;
0 commit comments