Commit | Line | Data |
---|---|---|
24a647d7 MD |
1 | /* |
2 | * QuickThreads -- Threads-building toolkit. | |
3 | * Copyright (c) 1993 by David Keppel | |
4 | * | |
5 | * Permission to use, copy, modify and distribute this software and | |
6 | * its documentation for any purpose and without fee is hereby | |
7 | * granted, provided that the above copyright notice and this notice | |
8 | * appear in all copies. This software is provided as a | |
9 | * proof-of-concept and for demonstration purposes; there is no | |
10 | * representation about the suitability of this software for any | |
11 | * purpose. | |
12 | */ | |
13 | ||
14 | #ifndef QT_KSR1_H | |
15 | #define QT_KSR1_H | |
16 | ||
17 | /* | |
18 | Stack layout: | |
19 | ||
20 | Registers are saved in strictly low to high order, FPU regs first | |
21 | (only if qt_block is called), CEU regs second, IPU regs next, with no | |
22 | padding between the groups. | |
23 | ||
24 | Callee-save: f16..f63; c15..c30; i12..i30. | |
25 | Args passed in i2..i5. | |
26 | ||
27 | Note: c31 is a private data pointer. It is not changed on thread | |
28 | swaps with the assumption that it represents per-processor rather | |
29 | than per-thread state. | |
30 | ||
31 | Note: i31 is an instruction count register that is updated by the | |
32 | context switch routines. Like c31, it is not changed on context | |
33 | switches. | |
34 | ||
35 | This is what we want on startup: | |
36 | ||
37 | ||
38 | +------ <-- BOS: Bottom of stack (grows down) | |
39 | | 80 (128 - 48) bytes of padding to a 128-byte boundary | |
40 | +--- | |
41 | | only | |
42 | | userf | |
43 | | t | |
44 | | u | |
45 | | qt_start$TXT | |
46 | | (empty) <-- qt.sp | |
47 | +------ <-- (BOS - 128) | |
48 | ||
49 | This is why we want this on startup: | |
50 | ||
51 | A thread begins running when the restore procedure switches thread stacks | |
52 | and pops a return address off of the top of the new stack (see below | |
53 | for the reason why we explicitly store qt_start$TXT). The | |
54 | block procedure pushes two jump addresses on a thread's stack before | |
55 | it switches stacks. The first is the return address for the block | |
56 | procedure, and the second is a restore address. The return address | |
57 | is used to jump back to the thread that has been switched to; the | |
58 | restore address is a jump within the block code to restore the registers. | |
59 | Normally, this is just a jump to the next address. However, on thread | |
60 | startup, this is a jump to qt_start$TXT. (The block procedure stores | |
61 | the restore address at an offset of 8 bytes from the top of the stack, | |
62 | which is also the offset at which qt_start$TXT is stored on the stacks | |
63 | of new threads. Hence, when the block procedure switches to a new | |
64 | thread stack, it will initially jump to qt_start$TXT; thereafter, | |
65 | it jumps to the restore code.) | |
66 | ||
67 | qt_start$TXT, after it has read the initial data on the new thread's | |
68 | stack and placed it in registers, pops the initial stack frame | |
69 | and gives the thread the entire stack to use for execution. | |
70 | ||
71 | The KSR runtime system has an unusual treatment of pointers to | |
72 | functions. From C, taking the `name' of a function yields a | |
73 | pointer to a _constant block_ and *not* the address of the | |
74 | function. The zero'th entry in the constant block is a pointer to | |
75 | the function. | |
76 | ||
77 | We have to be careful: the restore procedure expects a return | |
78 | address on the top of the stack (pointed to by qt.sp). This is not | |
79 | a problem when restoring a thread that has run before, since the | |
80 | block routine would have stored the return address on top of the | |
81 | stack. However, when ``faking up'' a thread start (bootstrapping a | |
82 | thread stack frame), the top of the stack needs to contain a | |
83 | pointer to the code that will start the thread running. | |
84 | ||
85 | The pointer to the startup code is *not* `qt_start'. It is the | |
86 | word *pointed to* by `qt_start'. Thus, we dereference `qt_start', | |
87 | see QT_ARGS_MD below. | |
88 | ||
89 | On varargs startup (still unimplemented): | |
90 | ||
91 | | padding to 128 byte boundary | |
92 | | varargs <-- padded to a 128-byte-boundary | |
93 | +--- | |
94 | | caller's frame, 16 bytes | |
95 | | 80 bytes of padding (frame padded to a 128-byte boundary) | |
96 | +--- | |
97 | | cleanup | |
98 | | vuserf | |
99 | | startup | |
100 | | t | |
101 | +--- | |
102 | | qt_start <-- qt.sp | |
103 | +--- | |
104 | ||
105 | Of a suspended thread: | |
106 | ||
107 | +--- | |
108 | | caller's frame, 16 bytes | |
109 | | fpu registers 47 regs * 8 bytes/reg 376 bytes | |
110 | | ceu registers 16 regs * 8 bytes/reg 128 bytes | |
111 | | ipu registers 19 regs * 8 bytes/reg 152 bytes | |
112 | | : | |
113 | | 80 bytes of padding | |
114 | | : | |
115 | | qt_restore <-- qt.sp | |
116 | +--- | |
117 | ||
118 | */ | |
119 | ||
120 | ||
121 | #define QT_STKALIGN 128 | |
122 | #define QT_GROW_DOWN | |
123 | typedef unsigned long qt_word_t; | |
124 | ||
125 | #define QT_STKBASE QT_STKALIGN | |
126 | #define QT_VSTKBASE QT_STKBASE | |
127 | ||
128 | extern void qt_start(void); | |
129 | /* | |
130 | * See the discussion above for what indexing into a procedure ptr | |
131 | * does for us (it's lovely, though, isn't it?). | |
132 | * | |
133 | * This assumes that the address of a procedure's code is the | |
134 | * first word in a procedure's constant block. That's how the manual | |
135 | * says it will be arranged. | |
136 | */ | |
137 | #define QT_ARGS_MD(sp) (QT_SPUT (sp, 1, ((qt_word_t *)qt_start)[0])) | |
138 | ||
139 | /* | |
140 | * The *index* (positive offset) of where to put each value. | |
141 | * See the picture of the stack above that explains the offsets. | |
142 | */ | |
143 | #define QT_ONLY_INDEX (5) | |
144 | #define QT_USER_INDEX (4) | |
145 | #define QT_ARGT_INDEX (3) | |
146 | #define QT_ARGU_INDEX (2) | |
147 | ||
148 | #define QT_VARGS_DEFAULT | |
149 | #define QT_VARGS(sp, nb, vargs, pt, startup, vuserf, cleanup) \ | |
150 | (qt_vargs (sp, nbytes, &vargs, pt, startup, vuserf, cleanup)) | |
151 | ||
152 | ||
153 | #define QT_VARGS_MD0(sp, vabytes) \ | |
154 | ((qt_t *)(((char *)(sp)) - 4*8 - QT_STKROUNDUP(vabytes))) | |
155 | ||
156 | extern void qt_vstart(void); | |
157 | #define QT_VARGS_MD1(sp) (QT_SPUT (sp, 0, ((qt_word_t *)qt_vstart)[0])) | |
158 | ||
159 | #define QT_VCLEANUP_INDEX (4) | |
160 | #define QT_VUSERF_INDEX (3) | |
161 | #define QT_VSTARTUP_INDEX (2) | |
162 | #define QT_VARGT_INDEX (1) | |
163 | ||
164 | #endif /* def QT_KSR1_H */ |