Skip to content

Commit a3ff02f

Browse files
committed
Split start into rustc and rustboot versions. This introduces a bit of
duplication, but we will hopefully drop the rustboot one soon. This is also a preparation for changing the rustc one to have the activate glue return to the exit glue which will then call the main function. This (returning to the function that calls main) matches what happens when loader stats a program or a new thread. It lets gdb produce good backtraces and should help with EH too.
1 parent a919a30 commit a3ff02f

File tree

2 files changed

+122
-27
lines changed

2 files changed

+122
-27
lines changed

src/rt/rust_task.cpp

Lines changed: 114 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,18 @@ rust_task::start(uintptr_t exit_task_glue,
141141
uintptr_t spawnee_fn,
142142
uintptr_t args,
143143
size_t callsz)
144+
{
145+
if (spawnee_abi == ABI_X86_RUSTBOOT_CDECL)
146+
start_rustboot(exit_task_glue, spawnee_fn, args, callsz);
147+
else
148+
start_rustc(exit_task_glue, spawnee_fn, args, callsz);
149+
}
150+
151+
void
152+
rust_task::start_rustboot(uintptr_t exit_task_glue,
153+
uintptr_t spawnee_fn,
154+
uintptr_t args,
155+
size_t callsz)
144156
{
145157
LOGPTR(dom, "exit-task glue", exit_task_glue);
146158
LOGPTR(dom, "from spawnee", spawnee_fn);
@@ -176,30 +188,25 @@ rust_task::start(uintptr_t exit_task_glue,
176188

177189
uintptr_t exit_task_frame_base = 0;
178190

179-
if (spawnee_abi == ABI_X86_RUSTBOOT_CDECL) {
180-
for (size_t j = 0; j < n_callee_saves; ++j) {
181-
182-
// We want 'frame_base' to point to the old fp in this (exit-task)
183-
// frame, because we're going to inject this frame-pointer into
184-
// the callee-save frame pointer value in the *next* (spawnee)
185-
// frame. A cheap trick, but this means the spawnee frame will
186-
// restore the proper frame pointer of the glue frame as it runs
187-
// its epilogue.
188-
if (j == callee_save_fp)
189-
exit_task_frame_base = (uintptr_t)spp;
191+
for (size_t j = 0; j < n_callee_saves; ++j) {
190192

191-
*spp-- = 0;
192-
}
193+
// We want 'frame_base' to point to the old fp in this (exit-task)
194+
// frame, because we're going to inject this frame-pointer into
195+
// the callee-save frame pointer value in the *next* (spawnee)
196+
// frame. A cheap trick, but this means the spawnee frame will
197+
// restore the proper frame pointer of the glue frame as it runs
198+
// its epilogue.
199+
if (j == callee_save_fp)
200+
exit_task_frame_base = (uintptr_t)spp;
193201

194-
*spp-- = (uintptr_t) dom->root_crate; // crate ptr
195-
*spp-- = (uintptr_t) 0; // frame_glue_fns
202+
*spp-- = 0;
196203
}
197204

205+
*spp-- = (uintptr_t) dom->root_crate; // crate ptr
206+
*spp-- = (uintptr_t) 0; // frame_glue_fns
207+
198208
I(dom, args);
199-
if (spawnee_abi == ABI_X86_RUSTBOOT_CDECL)
200-
make_aligned_room_for_bytes(spp, callsz - sizeof(uintptr_t));
201-
else
202-
make_aligned_room_for_bytes(spp, callsz - 3 * sizeof(uintptr_t));
209+
make_aligned_room_for_bytes(spp, callsz - sizeof(uintptr_t));
203210

204211
// Copy args from spawner to spawnee.
205212
uintptr_t *src = (uintptr_t *)args;
@@ -222,16 +229,96 @@ rust_task::start(uintptr_t exit_task_glue,
222229
// activating:
223230
*spp-- = (uintptr_t) 0x0; // closure-or-obj
224231

225-
if (spawnee_abi == ABI_X86_RUSTBOOT_CDECL) {
226-
// in CDECL mode we write the task + outptr to the spawnee stack.
227-
*spp-- = (uintptr_t) this; // task
228-
*spp-- = (uintptr_t) 0; // output addr
229-
} else {
230-
// in FASTCALL mode we don't, the outptr will be in ecx and the task
231-
// in edx, and the activate_glue will make sure to set that up.
232-
I(dom, spawnee_abi == ABI_X86_RUSTC_FASTCALL);
232+
// in CDECL mode we write the task + outptr to the spawnee stack.
233+
*spp-- = (uintptr_t) this; // task
234+
*spp-- = (uintptr_t) 0; // output addr
235+
236+
I(dom, spp+1 == align_down(spp+1));
237+
*spp-- = (uintptr_t) exit_task_glue; // retpc
238+
239+
// The context the activate_glue needs to switch stack.
240+
*spp-- = (uintptr_t) spawnee_fn; // instruction to start at
241+
for (size_t j = 0; j < n_callee_saves; ++j) {
242+
// callee-saves to carry in when we activate
243+
if (j == callee_save_fp)
244+
*spp-- = exit_task_frame_base;
245+
else
246+
*spp-- = (uintptr_t)NULL;
233247
}
234248

249+
// Back up one, we overshot where sp should be.
250+
rust_sp = (uintptr_t) (spp+1);
251+
252+
transition(&dom->newborn_tasks, &dom->running_tasks);
253+
}
254+
255+
void
256+
rust_task::start_rustc(uintptr_t exit_task_glue,
257+
uintptr_t spawnee_fn,
258+
uintptr_t args,
259+
size_t callsz)
260+
{
261+
LOGPTR(dom, "exit-task glue", exit_task_glue);
262+
LOGPTR(dom, "from spawnee", spawnee_fn);
263+
264+
// Set sp to last uintptr_t-sized cell of segment
265+
rust_sp -= sizeof(uintptr_t);
266+
267+
// NB: Darwin needs "16-byte aligned" stacks *at the point of the call
268+
// instruction in the caller*. This means that the address at which the
269+
// word before retpc is pushed must always be 16-byte aligned.
270+
//
271+
// see: "Mac OS X ABI Function Call Guide"
272+
273+
274+
// Begin synthesizing frames. There are two: a "fully formed"
275+
// exit-task frame at the top of the stack -- that pretends to be
276+
// mid-execution -- and a just-starting frame beneath it that
277+
// starts executing the first instruction of the spawnee. The
278+
// spawnee *thinks* it was called by the exit-task frame above
279+
// it. It wasn't; we put that fake frame in place here, but the
280+
// illusion is enough for the spawnee to return to the exit-task
281+
// frame when it's done, and exit.
282+
uintptr_t *spp = (uintptr_t *)rust_sp;
283+
284+
285+
// The exit_task_glue frame we synthesize above the frame we activate:
286+
make_aligned_room_for_bytes(spp, 2 * sizeof(uintptr_t));
287+
*spp-- = (uintptr_t) 0; // closure-or-obj
288+
*spp-- = (uintptr_t) this; // task
289+
I(dom, spp == align_down(spp));
290+
*spp-- = (uintptr_t) 0x0; // output
291+
*spp-- = (uintptr_t) 0x0; // retpc
292+
293+
uintptr_t exit_task_frame_base = 0;
294+
295+
I(dom, args);
296+
make_aligned_room_for_bytes(spp, callsz - 3 * sizeof(uintptr_t));
297+
298+
// Copy args from spawner to spawnee.
299+
uintptr_t *src = (uintptr_t *)args;
300+
src += 1; // spawn-call output slot
301+
src += 1; // spawn-call task slot
302+
src += 1; // spawn-call closure-or-obj slot
303+
304+
// Undo previous sp-- so we're pointing at the last word pushed.
305+
++spp;
306+
307+
// Memcpy all but the task, output and env pointers
308+
callsz -= (3 * sizeof(uintptr_t));
309+
spp = (uintptr_t*) (((uintptr_t)spp) - callsz);
310+
memcpy(spp, src, callsz);
311+
312+
// Move sp down to point to last implicit-arg cell (env).
313+
spp--;
314+
315+
// The *implicit* incoming args to the spawnee frame we're
316+
// activating:
317+
*spp-- = (uintptr_t) 0x0; // closure-or-obj
318+
319+
// in FASTCALL mode we don't, the outptr will be in ecx and the task
320+
// in edx, and the activate_glue will make sure to set that up.
321+
235322
I(dom, spp+1 == align_down(spp+1));
236323
*spp-- = (uintptr_t) exit_task_glue; // retpc
237324

src/rt/rust_task.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ rust_task : public maybe_proxy<rust_task>,
6060
uintptr_t spawnee_fn,
6161
uintptr_t args,
6262
size_t callsz);
63+
void start_rustboot(uintptr_t exit_task_glue,
64+
uintptr_t spawnee_fn,
65+
uintptr_t args,
66+
size_t callsz);
67+
void start_rustc(uintptr_t exit_task_glue,
68+
uintptr_t spawnee_fn,
69+
uintptr_t args,
70+
size_t callsz);
6371
void grow(size_t n_frame_bytes);
6472
bool running();
6573
bool blocked();

0 commit comments

Comments
 (0)