| /* | |
| * QuickThreads -- Threads-building toolkit. | |
| * Copyright (c) 1993 by David Keppel | |
| * | |
| * Permission to use, copy, modify and distribute this software and | |
| * its documentation for any purpose and without fee is hereby | |
| * granted, provided that the above copyright notice and this notice | |
| * appear in all copies. This software is provided as a | |
| * proof-of-concept and for demonstration purposes; there is no | |
| * representation about the suitability of this software for any | |
| * purpose. | |
| */ | |
| #ifndef QUICKTHREADS_KSR1_H | |
| #define QUICKTHREADS_KSR1_H | |
| /* | |
| Stack layout: | |
| Registers are saved in strictly low to high order, FPU regs first | |
| (only if qt_block is called), CEU regs second, IPU regs next, with no | |
| padding between the groups. | |
| Callee-save: f16..f63; c15..c30; i12..i30. | |
| Args passed in i2..i5. | |
| Note: c31 is a private data pointer. It is not changed on thread | |
| swaps with the assumption that it represents per-processor rather | |
| than per-thread state. | |
| Note: i31 is an instruction count register that is updated by the | |
| context switch routines. Like c31, it is not changed on context | |
| switches. | |
| This is what we want on startup: | |
| +------ <-- BOS: Bottom of stack (grows down) | |
| | 80 (128 - 48) bytes of padding to a 128-byte boundary | |
| +--- | |
| | only | |
| | userf | |
| | t | |
| | u | |
| | qt_start$TXT | |
| | (empty) <-- qt.sp | |
| +------ <-- (BOS - 128) | |
| This is why we want this on startup: | |
| A thread begins running when the restore procedure switches thread stacks | |
| and pops a return address off of the top of the new stack (see below | |
| for the reason why we explicitly store qt_start$TXT). The | |
| block procedure pushes two jump addresses on a thread's stack before | |
| it switches stacks. The first is the return address for the block | |
| procedure, and the second is a restore address. The return address | |
| is used to jump back to the thread that has been switched to; the | |
| restore address is a jump within the block code to restore the registers. | |
| Normally, this is just a jump to the next address. However, on thread | |
| startup, this is a jump to qt_start$TXT. (The block procedure stores | |
| the restore address at an offset of 8 bytes from the top of the stack, | |
| which is also the offset at which qt_start$TXT is stored on the stacks | |
| of new threads. Hence, when the block procedure switches to a new | |
| thread stack, it will initially jump to qt_start$TXT; thereafter, | |
| it jumps to the restore code.) | |
| qt_start$TXT, after it has read the initial data on the new thread's | |
| stack and placed it in registers, pops the initial stack frame | |
| and gives the thread the entire stack to use for execution. | |
| The KSR runtime system has an unusual treatment of pointers to | |
| functions. From C, taking the `name' of a function yields a | |
| pointer to a _constant block_ and *not* the address of the | |
| function. The zero'th entry in the constant block is a pointer to | |
| the function. | |
| We have to be careful: the restore procedure expects a return | |
| address on the top of the stack (pointed to by qt.sp). This is not | |
| a problem when restoring a thread that has run before, since the | |
| block routine would have stored the return address on top of the | |
| stack. However, when ``faking up'' a thread start (bootstrapping a | |
| thread stack frame), the top of the stack needs to contain a | |
| pointer to the code that will start the thread running. | |
| The pointer to the startup code is *not* `qt_start'. It is the | |
| word *pointed to* by `qt_start'. Thus, we dereference `qt_start', | |
| see QUICKTHREADS_ARGS_MD below. | |
| On varargs startup (still unimplemented): | |
| | padding to 128 byte boundary | |
| | varargs <-- padded to a 128-byte-boundary | |
| +--- | |
| | caller's frame, 16 bytes | |
| | 80 bytes of padding (frame padded to a 128-byte boundary) | |
| +--- | |
| | cleanup | |
| | vuserf | |
| | startup | |
| | t | |
| +--- | |
| | qt_start <-- qt.sp | |
| +--- | |
| Of a suspended thread: | |
| +--- | |
| | caller's frame, 16 bytes | |
| | fpu registers 47 regs * 8 bytes/reg 376 bytes | |
| | ceu registers 16 regs * 8 bytes/reg 128 bytes | |
| | ipu registers 19 regs * 8 bytes/reg 152 bytes | |
| | : | |
| | 80 bytes of padding | |
| | : | |
| | qt_restore <-- qt.sp | |
| +--- | |
| */ | |
| #define QUICKTHREADS_STKALIGN 128 | |
| #define QUICKTHREADS_GROW_DOWN | |
| typedef unsigned long qt_word_t; | |
| #define QUICKTHREADS_STKBASE QUICKTHREADS_STKALIGN | |
| #define QUICKTHREADS_VSTKBASE QUICKTHREADS_STKBASE | |
| extern void qt_start(void); | |
| /* | |
| * See the discussion above for what indexing into a procedure ptr | |
| * does for us (it's lovely, though, isn't it?). | |
| * | |
| * This assumes that the address of a procedure's code is the | |
| * first word in a procedure's constant block. That's how the manual | |
| * says it will be arranged. | |
| */ | |
| #define QUICKTHREADS_ARGS_MD(sp) (QUICKTHREADS_SPUT (sp, 1, ((qt_word_t *)qt_start)[0])) | |
| /* | |
| * The *index* (positive offset) of where to put each value. | |
| * See the picture of the stack above that explains the offsets. | |
| */ | |
| #define QUICKTHREADS_ONLY_INDEX (5) | |
| #define QUICKTHREADS_USER_INDEX (4) | |
| #define QUICKTHREADS_ARGT_INDEX (3) | |
| #define QUICKTHREADS_ARGU_INDEX (2) | |
| #define QUICKTHREADS_VARGS_DEFAULT | |
| #define QUICKTHREADS_VARGS(sp, nb, vargs, pt, startup, vuserf, cleanup) \ | |
| (qt_vargs (sp, nbytes, &vargs, pt, startup, vuserf, cleanup)) | |
| #define QUICKTHREADS_VARGS_MD0(sp, vabytes) \ | |
| ((qt_t *)(((char *)(sp)) - 4*8 - QUICKTHREADS_STKROUNDUP(vabytes))) | |
| extern void qt_vstart(void); | |
| #define QUICKTHREADS_VARGS_MD1(sp) (QUICKTHREADS_SPUT (sp, 0, ((qt_word_t *)qt_vstart)[0])) | |
| #define QUICKTHREADS_VCLEANUP_INDEX (4) | |
| #define QUICKTHREADS_VUSERF_INDEX (3) | |
| #define QUICKTHREADS_VSTARTUP_INDEX (2) | |
| #define QUICKTHREADS_VARGT_INDEX (1) | |
| #endif /* def QUICKTHREADS_KSR1_H */ |