Skip to content

Commit 49ee448

Browse files
committed
---
yaml --- r: 1350 b: refs/heads/master c: 467a628 h: refs/heads/master v: v3
1 parent 1efd62a commit 49ee448

File tree

2 files changed

+100
-3
lines changed

2 files changed

+100
-3
lines changed

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
---
2-
refs/heads/master: 2f25d9c983c5730d79cc14278e666b8eb6531b10
2+
refs/heads/master: 467a628ffaf41844a90e6664c2ecd481eef1dc85

trunk/src/comp/back/x86.rs

Lines changed: 99 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,117 @@ fn store_esp_to_runtime_sp() -> vec[str] {
4141
ret vec("movl %esp, " + wstr(abi.task_field_runtime_sp) + "(%ecx)");
4242
}
4343

44+
/*
45+
* This is a bit of glue-code. It should be emitted once per
46+
* compilation unit.
47+
*
48+
* - save regs on C stack
49+
* - align sp on a 16-byte boundary
50+
* - save sp to task.runtime_sp (runtime_sp is thus always aligned)
51+
* - load saved task sp (switch stack)
52+
* - restore saved task regs
53+
* - return to saved task pc
54+
*
55+
* Our incoming stack looks like this:
56+
*
57+
* *esp+4 = [arg1 ] = task ptr
58+
* *esp = [retpc ]
59+
*/
60+
4461
fn rust_activate_glue() -> vec[str] {
4562
ret vec("movl 4(%esp), %ecx # ecx = rust_task")
4663
+ save_callee_saves()
4764
+ store_esp_to_runtime_sp()
4865
+ load_esp_from_rust_sp()
4966

50-
// This 'add' instruction is a bit surprising.
51-
// See lengthy comment in boot/be/x86.ml activate_glue.
67+
/*
68+
* There are two paths we can arrive at this code from:
69+
*
70+
*
71+
* 1. We are activating a task for the first time. When we switch
72+
* into the task stack and 'ret' to its first instruction, we'll
73+
* start doing whatever the first instruction says. Probably
74+
* saving registers and starting to establish a frame. Harmless
75+
* stuff, doesn't look at task->rust_sp again except when it
76+
* clobbers it during a later upcall.
77+
*
78+
*
79+
* 2. We are resuming a task that was descheduled by the yield glue
80+
* below. When we switch into the task stack and 'ret', we'll be
81+
* ret'ing to a very particular instruction:
82+
*
83+
* "esp <- task->rust_sp"
84+
*
85+
* this is the first instruction we 'ret' to after this glue,
86+
* because it is the first instruction following *any* upcall,
87+
* and the task we are activating was descheduled mid-upcall.
88+
*
89+
* Unfortunately for us, we have already restored esp from
90+
* task->rust_sp and are about to eat the 5 words off the top of
91+
* it.
92+
*
93+
*
94+
* | ... | <-- where esp will be once we restore + ret, below,
95+
* | retpc | and where we'd *like* task->rust_sp to wind up.
96+
* | ebp |
97+
* | edi |
98+
* | esi |
99+
* | ebx | <-- current task->rust_sp == current esp
100+
*
101+
*
102+
* This is a problem. If we return to "esp <- task->rust_sp" it
103+
* will push esp back down by 5 words. This manifests as a rust
104+
* stack that grows by 5 words on each yield/reactivate. Not
105+
* good.
106+
*
107+
* So what we do here is just adjust task->rust_sp up 5 words as
108+
* well, to mirror the movement in esp we're about to
109+
* perform. That way the "esp <- task->rust_sp" we 'ret' to below
110+
* will be a no-op. Esp won't move, and the task's stack won't
111+
* grow.
112+
*/
52113
+ vec("addl $20, " + wstr(abi.task_field_rust_sp) + "(%ecx)")
53114

115+
116+
/*
117+
* In most cases, the function we're returning to (activating)
118+
* will have saved any caller-saves before it yielded via upcalling,
119+
* so no work to do here. With one exception: when we're initially
120+
* activating, the task needs to be in the fastcall 2nd parameter
121+
* expected by the rust main function. That's edx.
122+
*/
123+
+ vec("mov %ecx, %edx")
124+
54125
+ restore_callee_saves()
55126
+ vec("ret");
56127
}
57128

129+
/* More glue code, this time the 'bottom half' of yielding.
130+
*
131+
* We arrived here because an upcall decided to deschedule the
132+
* running task. So the upcall's return address got patched to the
133+
* first instruction of this glue code.
134+
*
135+
* When the upcall does 'ret' it will come here, and its esp will be
136+
* pointing to the last argument pushed on the C stack before making
137+
* the upcall: the 0th argument to the upcall, which is always the
138+
* task ptr performing the upcall. That's where we take over.
139+
*
140+
* Our goal is to complete the descheduling
141+
*
142+
* - Switch over to the task stack temporarily.
143+
*
144+
* - Save the task's callee-saves onto the task stack.
145+
* (the task is now 'descheduled', safe to set aside)
146+
*
147+
* - Switch *back* to the C stack.
148+
*
149+
* - Restore the C-stack callee-saves.
150+
*
151+
* - Return to the caller on the C stack that activated the task.
152+
*
153+
*/
154+
58155
fn rust_yield_glue() -> vec[str] {
59156
ret vec("movl 0(%esp), %ecx # ecx = rust_task")
60157
+ load_esp_from_rust_sp()

0 commit comments

Comments
 (0)