mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-04-30 20:00:19 +02:00
164 lines
5.3 KiB
C
164 lines
5.3 KiB
C
/*
|
|
* 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 QT_KSR1_H
|
|
#define QT_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 QT_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 QT_STKALIGN 128
|
|
#define QT_GROW_DOWN
|
|
typedef unsigned long qt_word_t;
|
|
|
|
#define QT_STKBASE QT_STKALIGN
|
|
#define QT_VSTKBASE QT_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 QT_ARGS_MD(sp) (QT_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 QT_ONLY_INDEX (5)
|
|
#define QT_USER_INDEX (4)
|
|
#define QT_ARGT_INDEX (3)
|
|
#define QT_ARGU_INDEX (2)
|
|
|
|
#define QT_VARGS_DEFAULT
|
|
#define QT_VARGS(sp, nb, vargs, pt, startup, vuserf, cleanup) \
|
|
(qt_vargs (sp, nbytes, &vargs, pt, startup, vuserf, cleanup))
|
|
|
|
|
|
#define QT_VARGS_MD0(sp, vabytes) \
|
|
((qt_t *)(((char *)(sp)) - 4*8 - QT_STKROUNDUP(vabytes)))
|
|
|
|
extern void qt_vstart(void);
|
|
#define QT_VARGS_MD1(sp) (QT_SPUT (sp, 0, ((qt_word_t *)qt_vstart)[0]))
|
|
|
|
#define QT_VCLEANUP_INDEX (4)
|
|
#define QT_VUSERF_INDEX (3)
|
|
#define QT_VSTARTUP_INDEX (2)
|
|
#define QT_VARGT_INDEX (1)
|
|
|
|
#endif /* def QT_KSR1_H */
|