mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-05-22 12:30:32 +02:00
New file: From the Cygnus r0.3-release.
This commit is contained in:
parent
ec349f39cc
commit
24a647d7fe
49 changed files with 4499 additions and 0 deletions
142
qt/md/_sparc.s
Normal file
142
qt/md/_sparc.s
Normal file
|
@ -0,0 +1,142 @@
|
|||
/* sparc.s -- assembly support for the `qt' thread building kit. */
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* #include <machine/trap.h> */
|
||||
|
||||
.text
|
||||
.align 4
|
||||
.global _qt_blocki
|
||||
.global _qt_block
|
||||
.global _qt_abort
|
||||
.global _qt_start
|
||||
.global _qt_vstart
|
||||
|
||||
/* Register assignment:
|
||||
// %o0: incoming `helper' function to call after cswap
|
||||
// also used as outgoing sp of old thread (qt_t *)
|
||||
// %o1, %o2:
|
||||
// parameters to `helper' function called after cswap
|
||||
// %o3: sp of new thread
|
||||
// %o5: tmp used to save old thread sp, while using %o0
|
||||
// to call `helper' f() after cswap.
|
||||
//
|
||||
//
|
||||
// Aborting a thread is easy if there are no cached register window
|
||||
// frames: just switch to the new stack and away we go. If there are
|
||||
// cached register window frames they must all be written back to the
|
||||
// old stack before we move to the new stack. If we fail to do the
|
||||
// writeback then the old stack memory can be written with register
|
||||
// window contents e.g., after the stack memory has been freed and
|
||||
// reused.
|
||||
//
|
||||
// If you don't believe this, try setting the frame pointer to zero
|
||||
// once we're on the new stack. This will not affect correctnes
|
||||
// otherwise because the frame pointer will eventually get reloaded w/
|
||||
// the new thread's frame pointer. But it will be zero briefly before
|
||||
// the reload. You will eventually (100,000 cswaps later on a small
|
||||
// SPARC machine that I tried) get an illegal instruction trap from
|
||||
// the kernel trying to flush a cached window to location 0x0.
|
||||
//
|
||||
// Solution: flush windows before switching stacks, which invalidates
|
||||
// all the other register windows. We could do the trap
|
||||
// conditionally: if we're in the lowest frame of a thread, the fp is
|
||||
// zero already so we know there's nothing cached. But we expect most
|
||||
// aborts will be done from a first function that does a `save', so we
|
||||
// will rarely save anything and always pay the cost of testing to see
|
||||
// if we should flush.
|
||||
//
|
||||
// All floating-point registers are caller-save, so this routine
|
||||
// doesn't need to do anything to save and restore them.
|
||||
//
|
||||
// `qt_block' and `qt_blocki' return the same value as the value
|
||||
// returned by the helper function. We get this ``for free''
|
||||
// since we don't touch the return value register between the
|
||||
// return from the helper function and return from qt_block{,i}.
|
||||
*/
|
||||
|
||||
_qt_block:
|
||||
_qt_blocki:
|
||||
sub %sp, 8, %sp /* Allocate save area for return pc. */
|
||||
st %o7, [%sp+64] /* Save return pc. */
|
||||
_qt_abort:
|
||||
ta 0x03 /* Save locals and ins. */
|
||||
mov %sp, %o5 /* Remember old sp w/o chng ins/locals. */
|
||||
sub %o3, 64, %sp /* Allocate kwsa, switch stacks. */
|
||||
call %o0, 0 /* Call `helper' routine. */
|
||||
mov %o5, %o0 /* Pass old thread to qt_after_t() */
|
||||
/* .. along w/ args in %o1 & %o2. */
|
||||
|
||||
/* Restore callee-save regs. The kwsa
|
||||
// is on this stack, so offset all
|
||||
// loads by sizeof(kwsa), 64 bytes.
|
||||
*/
|
||||
ldd [%sp+ 0+64], %l0
|
||||
ldd [%sp+ 8+64], %l2
|
||||
ldd [%sp+16+64], %l4
|
||||
ldd [%sp+24+64], %l6
|
||||
ldd [%sp+32+64], %i0
|
||||
ldd [%sp+40+64], %i2
|
||||
ldd [%sp+48+64], %i4
|
||||
ldd [%sp+56+64], %i6
|
||||
ld [%sp+64+64], %o7 /* Restore return pc. */
|
||||
|
||||
retl /* Return to address in %o7. */
|
||||
add %sp, 72, %sp /* Deallocate kwsa, ret pc area. */
|
||||
|
||||
|
||||
/* The function calling conventions say there has to be a 1-word area
|
||||
// in the caller's stack to hold a pointer to space for aggregate
|
||||
// return values. It also says there should be a 6-word area to hold
|
||||
// %o0..%o5 if the callee wants to save them (why? I don't know...)
|
||||
// Round up to 8 words to maintain alignment.
|
||||
//
|
||||
// Parameter values were stored in callee-save regs and are moved to
|
||||
// the parameter registers.
|
||||
*/
|
||||
_qt_start:
|
||||
mov %i1, %o0 /* `pu': Set up args to `only'. */
|
||||
mov %i2, %o1 /* `pt'. */
|
||||
mov %i4, %o2 /* `userf'. */
|
||||
call %i5, 0 /* Call client function. */
|
||||
sub %sp, 32, %sp /* Allocate 6-word callee space. */
|
||||
|
||||
call _qt_error, 0 /* `only' erroniously returned. */
|
||||
nop
|
||||
|
||||
|
||||
/* Same comments as `_qt_start' about allocating rounded-up 7-word
|
||||
// save areas. */
|
||||
|
||||
_qt_vstart:
|
||||
sub %sp, 32, %sp /* Allocate 7-word callee space. */
|
||||
call %i5, 0 /* call `startup'. */
|
||||
mov %i2, %o0 /* .. with argument `pt'. */
|
||||
|
||||
add %sp, 32, %sp /* Use 7-word space in varargs. */
|
||||
ld [%sp+ 4+64], %o0 /* Load arg0 ... */
|
||||
ld [%sp+ 8+64], %o1
|
||||
ld [%sp+12+64], %o2
|
||||
ld [%sp+16+64], %o3
|
||||
ld [%sp+20+64], %o4
|
||||
call %i4, 0 /* Call `userf'. */
|
||||
ld [%sp+24+64], %o5
|
||||
|
||||
/* Use 6-word space in varargs. */
|
||||
mov %o0, %o1 /* Pass return value from userf */
|
||||
call %i3, 0 /* .. when call `cleanup. */
|
||||
mov %i2, %o0 /* .. along with argument `pt'. */
|
||||
|
||||
call _qt_error, 0 /* `cleanup' erroniously returned. */
|
||||
nop
|
106
qt/md/_sparc_b.s
Normal file
106
qt/md/_sparc_b.s
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.globl _b_call_reg
|
||||
.globl _b_call_imm
|
||||
.globl _b_add
|
||||
.globl _b_load
|
||||
|
||||
_b_null:
|
||||
retl
|
||||
nop
|
||||
|
||||
_b_call_reg:
|
||||
sethi %hi(_b_null),%o4
|
||||
or %o4,%lo(_b_null),%o4
|
||||
add %o7,%g0, %o3
|
||||
L0:
|
||||
call %o4
|
||||
nop
|
||||
call %o4
|
||||
nop
|
||||
call %o4
|
||||
nop
|
||||
call %o4
|
||||
nop
|
||||
call %o4
|
||||
nop
|
||||
|
||||
subcc %o0,1,%o0
|
||||
bg L0
|
||||
nop
|
||||
add %o3,%g0, %o7
|
||||
retl
|
||||
nop
|
||||
|
||||
_b_call_imm:
|
||||
sethi %hi(_b_null),%o4
|
||||
or %o4,%lo(_b_null),%o4
|
||||
add %o7,%g0, %o3
|
||||
L1:
|
||||
call _b_null
|
||||
call _b_null
|
||||
call _b_null
|
||||
call _b_null
|
||||
call _b_null
|
||||
|
||||
subcc %o0,1,%o0
|
||||
bg L0
|
||||
nop
|
||||
add %o3,%g0, %o7
|
||||
retl
|
||||
nop
|
||||
|
||||
|
||||
_b_add:
|
||||
add %o0,%g0,%o1
|
||||
add %o0,%g0,%o2
|
||||
add %o0,%g0,%o3
|
||||
add %o0,%g0,%o4
|
||||
L2:
|
||||
sub %o0,5,%o0
|
||||
sub %o1,5,%o1
|
||||
sub %o2,5,%o2
|
||||
sub %o3,5,%o3
|
||||
sub %o4,5,%o4
|
||||
|
||||
subcc %o0,5,%o0
|
||||
sub %o1,5,%o1
|
||||
sub %o2,5,%o2
|
||||
sub %o3,5,%o3
|
||||
sub %o4,5,%o4
|
||||
|
||||
bg L2
|
||||
nop
|
||||
retl
|
||||
nop
|
||||
|
||||
|
||||
_b_load:
|
||||
ld [%sp+ 0], %g0
|
||||
L3:
|
||||
ld [%sp+ 4],%g0
|
||||
ld [%sp+ 8],%g0
|
||||
ld [%sp+12],%g0
|
||||
ld [%sp+16],%g0
|
||||
ld [%sp+20],%g0
|
||||
ld [%sp+24],%g0
|
||||
ld [%sp+28],%g0
|
||||
ld [%sp+32],%g0
|
||||
ld [%sp+36],%g0
|
||||
|
||||
subcc %o0,10,%o0
|
||||
bg L3
|
||||
ld [%sp+ 0],%g0
|
||||
retl
|
||||
nop
|
5
qt/md/axp.1.Makefile
Normal file
5
qt/md/axp.1.Makefile
Normal file
|
@ -0,0 +1,5 @@
|
|||
|
||||
#
|
||||
# Compiling for the DEC AXP (alpha) with GNU CC or version 1.x of OSF.
|
||||
#
|
||||
CC = cc -std1 -D__AXP__ -D__OSF1__
|
5
qt/md/axp.2.Makefile
Normal file
5
qt/md/axp.2.Makefile
Normal file
|
@ -0,0 +1,5 @@
|
|||
|
||||
#
|
||||
# Compiling for the DEC AXP (alpha) with GNU CC or version 2.x of OSF.
|
||||
#
|
||||
CC = cc -std1 -D__AXP__ -D__OSF2__
|
5
qt/md/axp.Makefile
Normal file
5
qt/md/axp.Makefile
Normal file
|
@ -0,0 +1,5 @@
|
|||
|
||||
#
|
||||
# GNU CC
|
||||
#
|
||||
CC = gcc -D__AXP__
|
10
qt/md/axp.README
Normal file
10
qt/md/axp.README
Normal file
|
@ -0,0 +1,10 @@
|
|||
The handling of varargs is platform-dependent. Assar Westerlund
|
||||
stared at the problem for a while and deduces the following table:
|
||||
|
||||
vers / compiler cc gcc
|
||||
----------------------------------------------------------------------
|
||||
1.3 a0, offset __base, __offset
|
||||
2.0 _a0, _offset __base, __offset
|
||||
|
||||
The current code should handle both cc and gcc versions, provided
|
||||
you configure for the correct compiler.
|
133
qt/md/axp.c
Normal file
133
qt/md/axp.c
Normal file
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include "qt.h"
|
||||
|
||||
|
||||
/* Varargs is harder on the AXP. Parameters are saved on the stack as
|
||||
something like (stack grows down to low memory; low at bottom of
|
||||
picture):
|
||||
|
||||
| :
|
||||
| arg6
|
||||
+---
|
||||
| iarg5
|
||||
| :
|
||||
| iarg3 <-- va_list._a0 + va_list._offset
|
||||
| :
|
||||
| iarg0 <-- va_list._a0
|
||||
+---
|
||||
| farg5
|
||||
| :
|
||||
| farg0
|
||||
+---
|
||||
|
||||
When some of the arguments have known type, there is no need to
|
||||
save all of them in the struct. So, for example, if the routine is
|
||||
called
|
||||
|
||||
zork (int a0, float a1, int a2, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start (ap, a2);
|
||||
qt_vargs (... &ap ...);
|
||||
}
|
||||
|
||||
then offset is set to 3 * 8 (8 === sizeof machine word) = 24.
|
||||
|
||||
What this means for us is that the user's routine needs to be
|
||||
called with an arg list where some of the words in the `any type'
|
||||
parameter list have to be split and moved up in to the int/fp
|
||||
region.
|
||||
|
||||
Ways in which this can fail:
|
||||
- The user might not know the size of the pushed arguments anyway.
|
||||
- Structures have funny promotion rules.
|
||||
- Probably lots of other things.
|
||||
|
||||
All in all, we never promised varargs would work reliably. */
|
||||
|
||||
|
||||
|
||||
#define QT_VADJ(sp) (((char *)sp) - QT_VSTKBASE)
|
||||
|
||||
#define QT_VARGS_MD0(sp, vabytes) \
|
||||
((qt_t *)(((char *)(sp)) - 6*2*8 - QT_STKROUNDUP(vabytes)))
|
||||
|
||||
extern void qt_vstart(void);
|
||||
#define QT_VARGS_MD1(sp) (QT_SPUT (sp, QT_R26, qt_vstart))
|
||||
|
||||
|
||||
/* Different machines use different implementations for varargs.
|
||||
Unfortunately, the code below ``looks in to'' the varargs
|
||||
structure, `va_list', and thus depends on the conventions.
|
||||
The following #defines try to deal with it but don't catch
|
||||
everything. */
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define _a0 __base
|
||||
#define _offset __offset
|
||||
#else
|
||||
#ifdef __OSF1__
|
||||
#define _a0 a0
|
||||
#define _offset offset
|
||||
#endif
|
||||
#endif /* def __GNUC__ */
|
||||
|
||||
|
||||
struct qt_t *
|
||||
qt_vargs (struct qt_t *qsp, int nbytes, struct va_list *vargs,
|
||||
void *pt, qt_function_t *startup,
|
||||
qt_function_t *vuserf, qt_function_t *cleanup)
|
||||
{
|
||||
va_list ap;
|
||||
int i;
|
||||
int max; /* Maximum *words* of args to copy. */
|
||||
int tmove; /* *Words* of args moved typed->typed. */
|
||||
qt_word_t *sp;
|
||||
|
||||
ap = *(va_list *)vargs;
|
||||
qsp = QT_VARGS_MD0 (qsp, nbytes);
|
||||
sp = (qt_word_t *)qsp;
|
||||
|
||||
tmove = 6 - ap._offset/sizeof(qt_word_t);
|
||||
|
||||
/* Copy from one typed area to the other. */
|
||||
for (i=0; i<tmove; ++i) {
|
||||
/* Integer args: */
|
||||
sp[i+6] = ((qt_word_t *)(ap._a0 + ap._offset))[i];
|
||||
/* Fp args: */
|
||||
sp[i] = ((qt_word_t *)(ap._a0 + ap._offset))[i-6];
|
||||
}
|
||||
|
||||
max = nbytes/sizeof(qt_word_t);
|
||||
|
||||
/* Copy from the untyped area to the typed area. Split each arg.
|
||||
in to integer and floating-point save areas. */
|
||||
for (; i<6 && i<max; ++i) {
|
||||
sp[i] = sp[i+6] = ((qt_word_t *)(ap._a0 + ap._offset))[i];
|
||||
}
|
||||
|
||||
/* Copy from the untyped area to the other untyped area. */
|
||||
for (; i<max; ++i) {
|
||||
sp[i+6] = ((qt_word_t *)(ap._a0 + ap._offset))[i];
|
||||
}
|
||||
|
||||
QT_VARGS_MD1 (QT_VADJ(sp));
|
||||
QT_SPUT (QT_VADJ(sp), QT_VARGT_INDEX, pt);
|
||||
QT_SPUT (QT_VADJ(sp), QT_VSTARTUP_INDEX, startup);
|
||||
QT_SPUT (QT_VADJ(sp), QT_VUSERF_INDEX, vuserf);
|
||||
QT_SPUT (QT_VADJ(sp), QT_VCLEANUP_INDEX, cleanup);
|
||||
return ((qt_t *)QT_VADJ(sp));
|
||||
}
|
160
qt/md/axp.h
Normal file
160
qt/md/axp.h
Normal file
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* 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_AXP_H
|
||||
#define QT_AXP_H
|
||||
|
||||
#define QT_GROW_DOWN
|
||||
|
||||
typedef unsigned long qt_word_t;
|
||||
|
||||
|
||||
/* Stack layout on the Alpha:
|
||||
|
||||
Integer:
|
||||
|
||||
Caller-save: r0..r8, r22..r25, r27..r29
|
||||
argument/caller-save: r16..r21
|
||||
callee-save: r9..r15
|
||||
return pc *callee-save*: r26
|
||||
stack pointer: r30
|
||||
zero: r31
|
||||
|
||||
Floating-point:
|
||||
|
||||
Caller-save: f0..f1, f10..f15
|
||||
argument/caller-save: f16..f21, f22..f30
|
||||
callee-save: f2..f9
|
||||
zero: f31
|
||||
|
||||
Non-varargs:
|
||||
|
||||
+---
|
||||
| padding
|
||||
| f9
|
||||
| f8
|
||||
| f7
|
||||
| f6
|
||||
| f5
|
||||
| f4
|
||||
| f3
|
||||
| f2
|
||||
| r26
|
||||
+---
|
||||
| padding
|
||||
| r29
|
||||
| r15
|
||||
| r14
|
||||
| r13
|
||||
| r12 on startup === `only'
|
||||
| r11 on startup === `userf'
|
||||
| r10 on startup === `qt'
|
||||
| r9 on startup === `qu'
|
||||
| r26 on startup === qt_start <--- qt.sp
|
||||
+---
|
||||
|
||||
Conventions for varargs startup:
|
||||
|
||||
| :
|
||||
| arg6
|
||||
| iarg5
|
||||
| :
|
||||
| iarg0
|
||||
| farg5
|
||||
| :
|
||||
| farg0
|
||||
+---
|
||||
| padding
|
||||
| r29
|
||||
| r15
|
||||
| r14
|
||||
| r13
|
||||
| r12 on startup === `startup'
|
||||
| r11 on startup === `vuserf'
|
||||
| r10 on startup === `cleanup'
|
||||
| r9 on startup === `qt'
|
||||
| r26 on startup === qt_vstart <--- qt.sp
|
||||
+---
|
||||
|
||||
Note: this is a pretty cheap/sleazy way to get things going,
|
||||
but ``there must be a better way.'' For instance, some varargs
|
||||
parameters could be loaded in to integer registers, or the return
|
||||
address could be stored on top of the stack. */
|
||||
|
||||
|
||||
/* Stack must be 16-byte aligned. */
|
||||
#define QT_STKALIGN (16)
|
||||
|
||||
/* How much space is allocated to hold all the crud for
|
||||
initialization: 7 registers times 8 bytes/register. */
|
||||
|
||||
#define QT_STKBASE (10 * 8)
|
||||
#define QT_VSTKBASE QT_STKBASE
|
||||
|
||||
|
||||
/* Offsets of various registers. */
|
||||
#define QT_R26 0
|
||||
#define QT_R9 1
|
||||
#define QT_R10 2
|
||||
#define QT_R11 3
|
||||
#define QT_R12 4
|
||||
|
||||
|
||||
/* When a never-before-run thread is restored, the return pc points
|
||||
to a fragment of code that starts the thread running. For
|
||||
non-vargs functions, it just calls the client's `only' function.
|
||||
For varargs functions, it calls the startup, user, and cleanup
|
||||
functions.
|
||||
|
||||
The varargs startup routine always reads 12 8-byte arguments from
|
||||
the stack. If fewer argumets were pushed, the startup routine
|
||||
would read off the top of the stack. To prevent errors we always
|
||||
allocate enough space. When there are fewer args, the preallocated
|
||||
words are simply wasted. */
|
||||
|
||||
extern void qt_start(void);
|
||||
#define QT_ARGS_MD(sp) (QT_SPUT (sp, QT_R26, qt_start))
|
||||
|
||||
|
||||
/* The AXP uses a struct for `va_list', so pass a pointer to the
|
||||
struct. This may break some uses of `QT_VARGS', but then we never
|
||||
claimed it was totally portable. */
|
||||
|
||||
typedef void (qt_function_t)(void);
|
||||
|
||||
struct qt_t;
|
||||
struct va_list;
|
||||
extern struct qt_t *qt_vargs (struct qt_t *sp, int nbytes,
|
||||
struct va_list *vargs, void *pt,
|
||||
qt_function_t *startup,
|
||||
qt_function_t *vuserf,
|
||||
qt_function_t *cleanup);
|
||||
|
||||
#define QT_VARGS(sp, nbytes, vargs, pt, startup, vuserf, cleanup) \
|
||||
(qt_vargs (sp, nbytes, (struct va_list *)(&(vargs)), pt, \
|
||||
(qt_function_t *) startup, (qt_function_t *)vuserf, \
|
||||
(qt_function_t *)cleanup));
|
||||
|
||||
|
||||
/* The *index* (positive offset) of where to put each value. */
|
||||
#define QT_ONLY_INDEX (QT_R12)
|
||||
#define QT_USER_INDEX (QT_R11)
|
||||
#define QT_ARGT_INDEX (QT_R10)
|
||||
#define QT_ARGU_INDEX (QT_R9)
|
||||
|
||||
#define QT_VCLEANUP_INDEX (QT_R10)
|
||||
#define QT_VUSERF_INDEX (QT_R11)
|
||||
#define QT_VSTARTUP_INDEX (QT_R12)
|
||||
#define QT_VARGT_INDEX (QT_R9)
|
||||
|
||||
#endif /* ndef QT_AXP_H */
|
160
qt/md/axp.s
Normal file
160
qt/md/axp.s
Normal file
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* axp.s -- assembly support. */
|
||||
|
||||
.text
|
||||
.align 4
|
||||
.file 2 "axp.s"
|
||||
|
||||
.globl qt_block
|
||||
.globl qt_blocki
|
||||
.globl qt_abort
|
||||
.globl qt_start
|
||||
.globl qt_vstart
|
||||
|
||||
/*
|
||||
** $16: ptr to function to call once curr is suspended
|
||||
** and control is on r19's stack.
|
||||
** $17: 1'th arg to (*$16)(...).
|
||||
** $18: 2'th arg to (*$16)(...).
|
||||
** $19: sp of thread to resume.
|
||||
**
|
||||
** The helper routine returns a value that is passed on as the
|
||||
** return value from the blocking routine. Since we don't
|
||||
** touch r0 between the helper's return and the end of
|
||||
** function, we get this behavior for free.
|
||||
*/
|
||||
|
||||
.ent qt_blocki
|
||||
qt_blocki:
|
||||
subq $30,80, $30 /* Allocate save area. */
|
||||
stq $26, 0($30) /* Save registers. */
|
||||
stq $9, 8($30)
|
||||
stq $10,16($30)
|
||||
stq $11,24($30)
|
||||
stq $12,32($30)
|
||||
stq $13,40($30)
|
||||
stq $14,48($30)
|
||||
stq $15,56($30)
|
||||
stq $29,64($30)
|
||||
.end qt_blocki
|
||||
.ent qt_abort
|
||||
qt_abort:
|
||||
addq $16,$31, $27 /* Put argument function in PV. */
|
||||
addq $30,$31, $16 /* Save stack ptr in outgoing arg. */
|
||||
addq $19,$31, $30 /* Set new stack pointer. */
|
||||
jsr $26,($27),0 /* Call helper function. */
|
||||
|
||||
ldq $26, 0($30) /* Restore registers. */
|
||||
ldq $9, 8($30)
|
||||
ldq $10,16($30)
|
||||
ldq $11,24($30)
|
||||
ldq $12,32($30)
|
||||
ldq $13,40($30)
|
||||
ldq $14,48($30)
|
||||
ldq $15,56($30)
|
||||
ldq $29,64($30)
|
||||
|
||||
addq $30,80, $30 /* Deallocate save area. */
|
||||
ret $31,($26),1 /* Return, predict===RET. */
|
||||
.end qt_abort
|
||||
|
||||
|
||||
/*
|
||||
** Non-varargs thread startup.
|
||||
*/
|
||||
.ent qt_start
|
||||
qt_start:
|
||||
addq $9,$31, $16 /* Load up `qu'. */
|
||||
addq $10,$31, $17 /* ... user function's `pt'. */
|
||||
addq $11,$31, $18 /* ... user function's `userf'. */
|
||||
addq $12,$31, $27 /* ... set procedure value to `only'. */
|
||||
jsr $26,($27),0 /* Call `only'. */
|
||||
|
||||
jsr $26,qt_error /* `only' erroniously returned. */
|
||||
.end qt_start
|
||||
|
||||
|
||||
.ent qt_vstart:
|
||||
qt_vstart:
|
||||
/* Call startup function. */
|
||||
addq $9,$31, $16 /* Arg0 to `startup'. */
|
||||
addq $12,$31, $27 /* Set procedure value. */
|
||||
jsr $26,($27),0 /* Call `startup'. */
|
||||
|
||||
/* Call user function. */
|
||||
ldt $f16, 0($30) /* Load fp arg regs. */
|
||||
ldt $f17, 8($30)
|
||||
ldt $f18,16($30)
|
||||
ldt $f19,24($30)
|
||||
ldt $f20,32($30)
|
||||
ldt $f21,40($30)
|
||||
ldq $16,48($30) /* And integer arg regs. */
|
||||
ldq $17,56($30)
|
||||
ldq $18,64($30)
|
||||
ldq $19,72($30)
|
||||
ldq $20,80($30)
|
||||
ldq $21,88($30)
|
||||
addq $30,96 $30 /* Pop 6*2*8 saved arg regs. */
|
||||
addq $11,$31, $27 /* Set procedure value. */
|
||||
jsr $26,($27),0 /* Call `vuserf'. */
|
||||
|
||||
/* Call cleanup. */
|
||||
addq $9,$31, $16 /* Arg0 to `cleanup'. */
|
||||
addq $0,$31, $17 /* Users's return value is arg1. */
|
||||
addq $10,$31, $27 /* Set procedure value. */
|
||||
jsr $26,($27),0 /* Call `cleanup'. */
|
||||
|
||||
jsr $26,qt_error /* Cleanup erroniously returned. */
|
||||
.end qt_start
|
||||
|
||||
|
||||
/*
|
||||
** Save calle-save floating-point regs $f2..$f9.
|
||||
** Also save return pc from whomever called us.
|
||||
**
|
||||
** Return value from `qt_block' is the same as the return from
|
||||
** `qt_blocki'. We get that for free since we don't touch $0
|
||||
** between the return from `qt_blocki' and the return from
|
||||
** `qt_block'.
|
||||
*/
|
||||
.ent qt_block
|
||||
qt_block:
|
||||
subq $30,80, $30 /* Allocate a save space. */
|
||||
stq $26, 0($30) /* Save registers. */
|
||||
stt $f2, 8($30)
|
||||
stt $f3,16($30)
|
||||
stt $f4,24($30)
|
||||
stt $f5,32($30)
|
||||
stt $f6,40($30)
|
||||
stt $f7,48($30)
|
||||
stt $f8,56($30)
|
||||
stt $f9,64($30)
|
||||
|
||||
jsr $26,qt_blocki /* Call helper. */
|
||||
/* .. who will also restore $gp. */
|
||||
|
||||
ldq $26, 0($30) /* restore registers. */
|
||||
ldt $f2, 8($30)
|
||||
ldt $f3,16($30)
|
||||
ldt $f4,24($30)
|
||||
ldt $f5,32($30)
|
||||
ldt $f6,40($30)
|
||||
ldt $f7,48($30)
|
||||
ldt $f8,56($30)
|
||||
ldt $f9,64($30)
|
||||
|
||||
addq $30,80, $30 /* Deallcate save space. */
|
||||
ret $31,($26),1 /* Return, predict===RET. */
|
||||
.end qt_block
|
111
qt/md/axp_b.s
Normal file
111
qt/md/axp_b.s
Normal file
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.text
|
||||
.globl b_call_reg
|
||||
.globl b_call_imm
|
||||
.globl b_add
|
||||
.globl b_load
|
||||
|
||||
.ent b_null
|
||||
b_null:
|
||||
ret $31,($18),1
|
||||
.end b_null
|
||||
|
||||
.ent b_call_reg
|
||||
b_call_reg:
|
||||
lda $27,b_null
|
||||
$L0:
|
||||
jsr $18,($27)
|
||||
jsr $18,($27)
|
||||
jsr $18,($27)
|
||||
jsr $18,($27)
|
||||
jsr $18,($27)
|
||||
|
||||
jsr $18,($27)
|
||||
jsr $18,($27)
|
||||
jsr $18,($27)
|
||||
jsr $18,($27)
|
||||
jsr $18,($27)
|
||||
|
||||
subq $16,1,$16
|
||||
bgt $16,$L0
|
||||
|
||||
ret $31,($26),1
|
||||
.end
|
||||
|
||||
|
||||
.ent b_call_imm
|
||||
b_call_imm:
|
||||
$L1:
|
||||
jsr $18,b_null
|
||||
jsr $18,b_null
|
||||
jsr $18,b_null
|
||||
jsr $18,b_null
|
||||
jsr $18,b_null
|
||||
|
||||
jsr $18,b_null
|
||||
jsr $18,b_null
|
||||
jsr $18,b_null
|
||||
jsr $18,b_null
|
||||
jsr $18,b_null
|
||||
|
||||
subq $16,1,$16
|
||||
bgt $16,$L1
|
||||
|
||||
ret $31,($26),1
|
||||
.end
|
||||
|
||||
|
||||
.ent b_add
|
||||
b_add:
|
||||
$L2:
|
||||
addq $31,$31,$31
|
||||
addq $31,$31,$31
|
||||
addq $31,$31,$31
|
||||
addq $31,$31,$31
|
||||
addq $31,$31,$31
|
||||
|
||||
addq $31,$31,$31
|
||||
addq $31,$31,$31
|
||||
addq $31,$31,$31
|
||||
addq $31,$31,$31
|
||||
addq $31,$31,$31
|
||||
|
||||
subq $16,1,$16
|
||||
bgt $16,$L2
|
||||
|
||||
ret $31,($26),1
|
||||
.end
|
||||
|
||||
|
||||
.ent b_load
|
||||
b_load:
|
||||
$L3:
|
||||
ldq $31,0($30)
|
||||
ldq $31,8($30)
|
||||
ldq $31,16($30)
|
||||
ldq $31,24($30)
|
||||
ldq $31,32($30)
|
||||
|
||||
ldq $31,0($30)
|
||||
ldq $31,8($30)
|
||||
ldq $31,16($30)
|
||||
ldq $31,24($30)
|
||||
ldq $31,32($30)
|
||||
|
||||
subq $16,1,$16
|
||||
bgt $16,$L3
|
||||
|
||||
ret $31,($26),1
|
||||
.end
|
6
qt/md/default.Makefile
Normal file
6
qt/md/default.Makefile
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
#
|
||||
# `Normal' configuration.
|
||||
#
|
||||
CC = gcc -ansi -Wall -pedantic
|
||||
|
9
qt/md/hppa-cnx.Makefile
Normal file
9
qt/md/hppa-cnx.Makefile
Normal file
|
@ -0,0 +1,9 @@
|
|||
# This file (cnx_spp.Makefile) is part of the port of QuickThreads for
|
||||
# PA-RISC 1.1 architecture on a Convex SPP. This file is a machine dependent
|
||||
# makefile for QuickThreads. It was written in 1994 by Uwe Reder
|
||||
# (`uereder@cip.informatik.uni-erlangen.de') for the Operating Systems
|
||||
# Department (IMMD4) at the University of Erlangen/Nuernberg Germany.
|
||||
|
||||
# `Normal' configuration.
|
||||
|
||||
CC = /usr/convex/bin/cc
|
9
qt/md/hppa.Makefile
Normal file
9
qt/md/hppa.Makefile
Normal file
|
@ -0,0 +1,9 @@
|
|||
# This file (pa-risc.Makefile) is part of the port of QuickThreads for
|
||||
# PA-RISC 1.1 architecture. This file is a machine dependent makefile
|
||||
# for QuickThreads. It was written in 1994 by Uwe Reder
|
||||
# (`uereder@cip.informatik.uni-erlangen.de') for the Operating Systems
|
||||
# Department (IMMD4) at the University of Erlangen/Nuernberg Germany.
|
||||
|
||||
# `Normal' configuration.
|
||||
|
||||
CC = cc -Aa
|
194
qt/md/hppa.h
Normal file
194
qt/md/hppa.h
Normal file
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file (pa-risc.h) is part of the port of QuickThreads for the
|
||||
* PA-RISC 1.1 architecture. This file is a machine dependent header
|
||||
* file. It was written in 1994 by Uwe Reder
|
||||
* (`uereder@cip.informatik.uni-erlangen.de') for the Operating Systems
|
||||
* Department (IMMD4) at the University of Erlangen/Nuernberg Germany.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef QT_PA_RISC_H
|
||||
#define QT_PA_RISC_H
|
||||
|
||||
#include <qt.h>
|
||||
|
||||
/* size of an integer-register (32 bit) */
|
||||
typedef unsigned long qt_word_t;
|
||||
|
||||
/* PA-RISC's stack grows up */
|
||||
#define QT_GROW_UP
|
||||
|
||||
/* Stack layout on PA-RISC according to PA-RISC Procedure Calling Conventions:
|
||||
|
||||
Callee-save registers are: gr3-gr18, fr12-fr21.
|
||||
Also save gr2, return pointer.
|
||||
|
||||
+---
|
||||
| fr12 Each floating register is a double word (8 bytes).
|
||||
| fr13 Floating registers are only saved if `qt_block' is
|
||||
| fr14 called, in which case it saves the floating-point
|
||||
| fr15 registers then calls `qt_blocki' to save the integer
|
||||
| fr16 registers.
|
||||
| fr17
|
||||
| fr18
|
||||
| fr19
|
||||
| fr20
|
||||
| fr21
|
||||
| <arg word 3> fixed arguments (must be allocated; may remain unused)
|
||||
| <arg word 2>
|
||||
| <arg word 1>
|
||||
| <arg word 0>
|
||||
| <LPT> frame marker
|
||||
| <LPT'>
|
||||
| <RP'>
|
||||
| <Current RP>
|
||||
| <Static Link>
|
||||
| <Clean Up>
|
||||
| <RP''>
|
||||
| <Previous SP>
|
||||
+---
|
||||
| gr3 word each (4 bytes)
|
||||
| gr4
|
||||
| gr5
|
||||
| gr6
|
||||
| gr7
|
||||
| gr8
|
||||
| gr9
|
||||
| gr10
|
||||
| gr11
|
||||
| gr12
|
||||
| gr13
|
||||
| gr14
|
||||
| gr15
|
||||
| gr16
|
||||
| gr17
|
||||
| gr18
|
||||
| <16 bytes filled in (sp has to be 64-bytes aligned)>
|
||||
| <arg word 3> fixed arguments (must be allocated; may remain unused)
|
||||
| <arg word 2>
|
||||
| <arg word 1>
|
||||
| <arg word 0>
|
||||
| <LPT> frame marker
|
||||
| <LPT'>
|
||||
| <RP'>
|
||||
| <Current RP>
|
||||
| <Static Link>
|
||||
| <Clean Up>
|
||||
| <RP''>
|
||||
| <Previous SP>
|
||||
+--- <--- sp
|
||||
*/
|
||||
|
||||
/* When a never-before-run thread is restored, the return pc points
|
||||
to a fragment of code that starts the thread running. For
|
||||
non-vargs functions, it just calls the client's `only' function.
|
||||
For varargs functions, it calls the startup, user, and cleanup
|
||||
functions. */
|
||||
|
||||
/* Note: Procedue Labels on PA-RISC
|
||||
|
||||
<--2--><-------28---------><1-><1->
|
||||
-----------------------------------
|
||||
| SID | Adress Part | L | X |
|
||||
-----------------------------------
|
||||
|
||||
On HP-UX the L field is used to flag wheather the procedure
|
||||
label (plabel) is a pointer to an LT entry or to the entry point
|
||||
of the procedure (PA-RISC Procedure Calling Conventions Reference
|
||||
Manual, 5.3.2 Procedure Labels and Dynamic Calls). */
|
||||
|
||||
#define QT_PA_RISC_READ_PLABEL(plabel) \
|
||||
( (((int)plabel) & 2) ? \
|
||||
( (*((int *)(((int)plabel) & 0xfffffffc)))) : ((int)plabel) )
|
||||
|
||||
/* Stack must be 64 bytes aligned. */
|
||||
#define QT_STKALIGN (64)
|
||||
|
||||
/* Internal helper for putting stuff on stack (negative index!). */
|
||||
#define QT_SPUT(top, at, val) \
|
||||
(((qt_word_t *)(top))[-(at)] = (qt_word_t)(val))
|
||||
|
||||
/* Offsets of various registers which are modified on the stack.
|
||||
rp (return-pointer) has to be stored in the frame-marker-area
|
||||
of the "older" stack-segment. */
|
||||
|
||||
#define QT_crp (12+4+16+5)
|
||||
#define QT_15 (12+4+4)
|
||||
#define QT_16 (12+4+3)
|
||||
#define QT_17 (12+4+2)
|
||||
#define QT_18 (12+4+1)
|
||||
|
||||
|
||||
/** This stuff is for NON-VARARGS. **/
|
||||
|
||||
/* Stack looks like this (2 stack frames):
|
||||
|
||||
<--- 64-bytes aligned --><------- 64-bytes aligned ------------>
|
||||
| || |
|
||||
<--16--><------48-------><----16*4-----><--16-><------48------->
|
||||
|| | || | | ||
|
||||
||filler|arg|frame-marker||register-save|filler|arg|frame-marker||
|
||||
------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#define QT_STKBASE (16+48+(16*sizeof(qt_word_t))+16+48)
|
||||
|
||||
/* The index, relative to sp, of where to put each value. */
|
||||
#define QT_ONLY_INDEX (QT_15)
|
||||
#define QT_USER_INDEX (QT_16)
|
||||
#define QT_ARGT_INDEX (QT_17)
|
||||
#define QT_ARGU_INDEX (QT_18)
|
||||
|
||||
extern void qt_start(void);
|
||||
#define QT_ARGS_MD(sp) \
|
||||
(QT_SPUT (sp, QT_crp, QT_PA_RISC_READ_PLABEL(qt_start)))
|
||||
|
||||
|
||||
/** This is for VARARGS. **/
|
||||
|
||||
#define QT_VARGS_DEFAULT
|
||||
|
||||
/* Stack looks like this (2 stack frames):
|
||||
|
||||
<------ 64-bytes aligned -------><--------- 64-bytes aligned ---------->
|
||||
| || |
|
||||
<---?--><--?---><16><----32-----><----16*4-----><-16--><16><----32----->
|
||||
|| | | | || | | | ||
|
||||
||filler|varargs|arg|frame-marker||register-save|filler|arg|frame-marker||
|
||||
--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/* Sp is moved to the end of the first stack frame. */
|
||||
#define QT_VARGS_MD0(sp, vasize) \
|
||||
((qt_t *)(((char *)sp) + QT_STKROUNDUP(vasize + 4*4 + 32)))
|
||||
|
||||
/* To reach the arguments from the end of the first stack frame use 32
|
||||
as a negative adjustment. */
|
||||
#define QT_VARGS_ADJUST(sp) ((qt_t *)(((char *)sp) - 32))
|
||||
|
||||
/* Offset to reach the end of the second stack frame. */
|
||||
#define QT_VSTKBASE ((16*sizeof(qt_word_t)) + 16 + 4*4 + 32)
|
||||
|
||||
extern void qt_vstart(void);
|
||||
#define QT_VARGS_MD1(sp) \
|
||||
(QT_SPUT (sp, QT_crp, QT_PA_RISC_READ_PLABEL(qt_vstart)))
|
||||
|
||||
#define QT_VARGT_INDEX (QT_15)
|
||||
#define QT_VSTARTUP_INDEX (QT_16)
|
||||
#define QT_VUSERF_INDEX (QT_17)
|
||||
#define QT_VCLEANUP_INDEX (QT_18)
|
||||
|
||||
#endif /* ndef QT_PA_RISC_H */
|
237
qt/md/hppa.s
Normal file
237
qt/md/hppa.s
Normal file
|
@ -0,0 +1,237 @@
|
|||
; pa-risc.s -- assembly support.
|
||||
|
||||
; 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.
|
||||
|
||||
; This file (pa-risc.s) is part of the port of QuickThreads for
|
||||
; PA-RISC 1.1 architecture. This file implements context switches
|
||||
; and thread startup. It was written in 1994 by Uwe Reder
|
||||
; (`uereder@cip.informatik.uni-erlangen.de') for the Operating
|
||||
; Systems Department (IMMD4) at the University of Erlangen/Nuernberg
|
||||
; Germany.
|
||||
|
||||
|
||||
; Callee saves general registers gr3..gr18,
|
||||
; floating-point registers fr12..fr21.
|
||||
|
||||
.CODE
|
||||
|
||||
.IMPORT $$dyncall, MILLICODE
|
||||
.IMPORT qt_error, CODE
|
||||
|
||||
.EXPORT qt_blocki, ENTRY
|
||||
.EXPORT qt_block, ENTRY
|
||||
.EXPORT qt_abort, ENTRY
|
||||
.EXPORT qt_start, ENTRY
|
||||
.EXPORT qt_vstart, ENTRY
|
||||
|
||||
|
||||
; arg0: ptr to function (helper) to call once curr is suspended
|
||||
; and control is on arg3's stack.
|
||||
; arg1: 1'th arg to *arg0.
|
||||
; arg2: 2'th arg to *arg0.
|
||||
; arg3: sp of new thread.
|
||||
|
||||
qt_blocki
|
||||
.PROC
|
||||
.CALLINFO CALLER, FRAME=0, SAVE_RP, ENTRY_GR=18
|
||||
.ENTRY
|
||||
|
||||
stw %rp,-20(%sp) ; save rp to old frame-marker
|
||||
|
||||
stwm %r3,128(%sp) ; save callee-saves general registers
|
||||
stw %r4,-124(%sp)
|
||||
stw %r5,-120(%sp)
|
||||
stw %r6,-116(%sp)
|
||||
stw %r7,-112(%sp)
|
||||
stw %r8,-108(%sp)
|
||||
stw %r9,-104(%sp)
|
||||
stw %r10,-100(%sp)
|
||||
stw %r11,-96(%sp)
|
||||
stw %r12,-92(%sp)
|
||||
stw %r13,-88(%sp)
|
||||
stw %r14,-84(%sp)
|
||||
stw %r15,-80(%sp)
|
||||
stw %r16,-76(%sp)
|
||||
stw %r17,-72(%sp)
|
||||
stw %r18,-68(%sp)
|
||||
|
||||
qt_abort
|
||||
copy %arg0,%r22 ; helper to be called by $$dyncall
|
||||
copy %sp,%arg0 ; pass current sp as arg0 to helper
|
||||
copy %arg3,%sp ; set new sp
|
||||
|
||||
.CALL
|
||||
bl $$dyncall,%mrp ; call helper
|
||||
copy %mrp,%rp
|
||||
|
||||
ldw -68(%sp),%r18 ; restore general registers
|
||||
ldw -72(%sp),%r17
|
||||
ldw -76(%sp),%r16
|
||||
ldw -80(%sp),%r15
|
||||
ldw -84(%sp),%r14
|
||||
ldw -88(%sp),%r13
|
||||
ldw -92(%sp),%r12
|
||||
ldw -96(%sp),%r11
|
||||
ldw -100(%sp),%r10
|
||||
ldw -104(%sp),%r9
|
||||
ldw -108(%sp),%r8
|
||||
ldw -112(%sp),%r7
|
||||
ldw -116(%sp),%r6
|
||||
ldw -120(%sp),%r5
|
||||
ldw -124(%sp),%r4
|
||||
|
||||
ldw -148(%sp),%rp ; restore return-pointer
|
||||
|
||||
bv %r0(%rp) ; return to caller
|
||||
ldwm -128(%sp),%r3
|
||||
|
||||
.EXIT
|
||||
.PROCEND
|
||||
|
||||
|
||||
qt_block
|
||||
.PROC
|
||||
.CALLINFO CALLER, FRAME=0, SAVE_RP, ENTRY_FR=21
|
||||
.ENTRY
|
||||
|
||||
stw %rp,-20(%sp) ; save rp to old frame-marker
|
||||
|
||||
fstds,ma %fr12,8(%sp) ; save callee-saves float registers
|
||||
fstds,ma %fr13,8(%sp)
|
||||
fstds,ma %fr14,8(%sp)
|
||||
fstds,ma %fr15,8(%sp)
|
||||
fstds,ma %fr16,8(%sp)
|
||||
fstds,ma %fr17,8(%sp)
|
||||
fstds,ma %fr18,8(%sp)
|
||||
fstds,ma %fr19,8(%sp)
|
||||
fstds,ma %fr20,8(%sp)
|
||||
fstds,ma %fr21,8(%sp)
|
||||
|
||||
.CALL
|
||||
bl qt_blocki,%rp
|
||||
ldo 48(%sp),%sp
|
||||
|
||||
ldo -48(%sp),%sp
|
||||
|
||||
fldds,mb -8(%sp),%fr21 ; restore callee-saves float registers
|
||||
fldds,mb -8(%sp),%fr20
|
||||
fldds,mb -8(%sp),%fr19
|
||||
fldds,mb -8(%sp),%fr18
|
||||
fldds,mb -8(%sp),%fr17
|
||||
fldds,mb -8(%sp),%fr16
|
||||
fldds,mb -8(%sp),%fr15
|
||||
fldds,mb -8(%sp),%fr14
|
||||
fldds,mb -8(%sp),%fr13
|
||||
|
||||
ldw -28(%sp),%rp ; restore return-pointer
|
||||
|
||||
bv %r0(%rp) ; return to caller.
|
||||
fldds,mb -8(%sp),%fr12
|
||||
|
||||
.EXIT
|
||||
.PROCEND
|
||||
|
||||
|
||||
qt_start
|
||||
.PROC
|
||||
.CALLINFO CALLER, FRAME=0
|
||||
.ENTRY
|
||||
|
||||
copy %r18,%arg0 ; set user arg `pu'.
|
||||
copy %r17,%arg1 ; ... user function pt.
|
||||
copy %r16,%arg2 ; ... user function userf.
|
||||
; %r22 is a caller-saves register
|
||||
copy %r15,%r22 ; function to be called by $$dyncall
|
||||
|
||||
.CALL ; in=%r22
|
||||
bl $$dyncall,%mrp ; call `only'.
|
||||
copy %mrp,%rp
|
||||
|
||||
bl,n qt_error,%r0 ; `only' erroniously returned.
|
||||
|
||||
.EXIT
|
||||
.PROCEND
|
||||
|
||||
|
||||
; Varargs
|
||||
;
|
||||
; First, call `startup' with the `pt' argument.
|
||||
;
|
||||
; Next, call the user's function with all arguments.
|
||||
; We don't know whether arguments are integers, 32-bit floating-points or
|
||||
; even 64-bit floating-points, so we reload all the registers, possibly
|
||||
; with garbage arguments. The thread creator provided non-garbage for
|
||||
; the arguments that the callee actually uses, so the callee never gets
|
||||
; garbage.
|
||||
;
|
||||
; -48 -44 -40 -36 -32
|
||||
; | arg3 | arg2 | arg1 | arg0 |
|
||||
; -----------------------------
|
||||
; integers: arg3 arg2 arg1 arg0
|
||||
; 32-bit fps: farg3 farg2 farg1 farg0
|
||||
; 64-bit fps: <---farg3--> <---farg1-->
|
||||
;
|
||||
; Finally, call `cleanup' with the `pt' argument and with the return value
|
||||
; from the user's function. It is an error for `cleanup' to return.
|
||||
|
||||
qt_vstart
|
||||
.PROC
|
||||
.CALLINFO CALLER, FRAME=0
|
||||
.ENTRY
|
||||
|
||||
; Because the startup function may damage the fixed arguments
|
||||
; on the stack (PA-RISC Procedure Calling Conventions Reference
|
||||
; Manual, 2.4 Fixed Arguments Area), we allocate a seperate
|
||||
; stack frame for it.
|
||||
ldo 64(%sp),%sp
|
||||
|
||||
; call: void startup(void *pt)
|
||||
|
||||
copy %r15,%arg0 ; `pt' is arg0 to `startup'.
|
||||
copy %r16,%r22
|
||||
.CALL
|
||||
bl $$dyncall,%mrp ; Call `startup'.
|
||||
copy %mrp,%rp
|
||||
|
||||
ldo -64(%sp),%sp
|
||||
|
||||
; call: void *qt_vuserf_t(...)
|
||||
|
||||
ldw -36(%sp),%arg0 ; Load args to integer registers.
|
||||
ldw -40(%sp),%arg1
|
||||
ldw -44(%sp),%arg2
|
||||
ldw -48(%sp),%arg3
|
||||
; Index of fld[w|d]s only ranges from -16 to 15, so we
|
||||
; take r22 to be our new base register.
|
||||
ldo -32(%sp),%r22
|
||||
fldws -4(%r22),%farg0 ; Load args to floating-point registers.
|
||||
fldds -8(%r22),%farg1
|
||||
fldws -12(%r22),%farg2
|
||||
fldds -16(%r22),%farg3
|
||||
copy %r17,%r22
|
||||
.CALL
|
||||
bl $$dyncall,%mrp ; Call `userf'.
|
||||
copy %mrp,%rp
|
||||
|
||||
; call: void cleanup(void *pt, void *vuserf_return)
|
||||
|
||||
copy %r15,%arg0 ; `pt' is arg0 to `cleanup'.
|
||||
copy %ret0,%arg1 ; Return-value is arg1 to `cleanup'.
|
||||
copy %r18,%r22
|
||||
.CALL
|
||||
bl $$dyncall,%mrp ; Call `cleanup'.
|
||||
copy %mrp,%rp
|
||||
|
||||
bl,n qt_error,%r0
|
||||
|
||||
.EXIT
|
||||
.PROCEND
|
203
qt/md/hppa_b.s
Normal file
203
qt/md/hppa_b.s
Normal file
|
@ -0,0 +1,203 @@
|
|||
; 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.
|
||||
|
||||
; This file (pa-risc_b.s) is part of the port of QuickThreads for
|
||||
; PA-RISC 1.1 architecture. It contains assembly-level support for
|
||||
; raw processor performance measurement. It was written in 1994 by
|
||||
; Uwe Reder (`uereder@cip.informatik.uni-erlangen.de')
|
||||
; for the Operating Systems Department (IMMD4) at the
|
||||
; University of Erlangen/Nuernberg Germany.
|
||||
|
||||
|
||||
; Note that the number of instructions in the measurement-loops, differ
|
||||
; from implementation to implementation. I took eight instructions in a loop
|
||||
; for every test (execute eight instructions and loop to the start).
|
||||
|
||||
.CODE
|
||||
|
||||
.IMPORT $global$,DATA
|
||||
.IMPORT $$dyncall,MILLICODE
|
||||
.EXPORT b_call_reg
|
||||
.EXPORT b_call_imm
|
||||
.EXPORT b_add
|
||||
.EXPORT b_load
|
||||
|
||||
; Just do nothing, only return to caller. This procedure is called by
|
||||
; `b_call_reg' and `b_call_imm'.
|
||||
|
||||
b_null
|
||||
.PROC
|
||||
.CALLINFO NO_CALLS, FRAME=0
|
||||
.ENTRY
|
||||
|
||||
bv,n %r0(%rp) ; just return
|
||||
|
||||
.EXIT
|
||||
.PROCEND
|
||||
|
||||
; Call the procedure `b_null' with function pointer in a register.
|
||||
|
||||
b_call_reg
|
||||
.PROC
|
||||
.CALLINFO CALLER, FRAME=0
|
||||
.ENTRY
|
||||
|
||||
stwm %r3,64(%sp) ; store r3 (may be used by caller)
|
||||
stw %rp,-20(%sp) ; save return-pointer to frame-marker
|
||||
|
||||
addil LR'to_call-$global$,%r27
|
||||
ldw RR'to_call-$global$(%r1),%r3
|
||||
|
||||
_loop0
|
||||
copy %r3,%r22 ; copy the procedure label to r22, ...
|
||||
.CALL ; ...this is the input to $$dyncall
|
||||
bl $$dyncall,%mrp ; call $$dyncall (millicode function)
|
||||
copy %mrp,%rp ; remember the return-pointer
|
||||
|
||||
copy %r3,%r22
|
||||
.CALL
|
||||
bl $$dyncall,%mrp
|
||||
copy %mrp,%rp
|
||||
|
||||
copy %r3,%r22
|
||||
.CALL
|
||||
bl $$dyncall,%mrp
|
||||
copy %mrp,%rp
|
||||
|
||||
copy %r3,%r22
|
||||
.CALL
|
||||
bl $$dyncall,%mrp
|
||||
copy %mrp,%rp
|
||||
|
||||
copy %r3,%r22
|
||||
.CALL
|
||||
bl $$dyncall,%mrp
|
||||
copy %mrp,%rp
|
||||
|
||||
copy %r3,%r22
|
||||
.CALL
|
||||
bl $$dyncall,%mrp
|
||||
copy %mrp,%rp
|
||||
|
||||
copy %r3,%r22
|
||||
.CALL
|
||||
bl $$dyncall,%mrp
|
||||
copy %mrp,%rp
|
||||
|
||||
copy %r3,%r22
|
||||
.CALL
|
||||
bl $$dyncall,%mrp
|
||||
copy %mrp,%rp
|
||||
|
||||
addibf,<= -8,%arg0,_loop0 ; decrement counter by 8 and loop
|
||||
nop
|
||||
|
||||
ldw -20(%sp),%rp ; restore return-pointer
|
||||
bv %r0(%rp) ; return to caller
|
||||
ldwm -64(%sp),%r3 ; resore r3 and remove stack frame
|
||||
|
||||
.EXIT
|
||||
.PROCEND
|
||||
|
||||
; Call the procedure `b_null' immediate.
|
||||
|
||||
b_call_imm
|
||||
.PROC
|
||||
.CALLINFO CALLER, FRAME=0, SAVE_RP
|
||||
.ENTRY
|
||||
|
||||
ldo 64(%sp),%sp ; caller needs a stack-frame
|
||||
stw %rp,-20(%sp) ; save return-pointer to frame-marker
|
||||
|
||||
_loop1
|
||||
bl b_null,%rp ; call `b_null' immediate (8 times)
|
||||
nop
|
||||
bl b_null,%rp
|
||||
nop
|
||||
bl b_null,%rp
|
||||
nop
|
||||
bl b_null,%rp
|
||||
nop
|
||||
bl b_null,%rp
|
||||
nop
|
||||
bl b_null,%rp
|
||||
nop
|
||||
bl b_null,%rp
|
||||
nop
|
||||
bl b_null,%rp
|
||||
nop
|
||||
|
||||
addibf,<= -8,%arg0,_loop1 ; decrement counter by 8 and loop
|
||||
nop
|
||||
|
||||
ldw -20(%sp),%rp ; restore return-pointer
|
||||
bv %r0(%rp) ; return to caller
|
||||
ldo -64(%sp),%sp ; remove stack-frame
|
||||
|
||||
.EXIT
|
||||
.PROCEND
|
||||
|
||||
; Copy register-to-register.
|
||||
; On PA-RISC this is implemented with an `or'.
|
||||
; The `or' is hidden by a pseudo-operation called `copy'.
|
||||
|
||||
b_add
|
||||
.PROC
|
||||
.CALLINFO NO_CALLS, FRAME=0
|
||||
.ENTRY
|
||||
|
||||
_loop2
|
||||
copy %r19,%r20 ; copy register-to-register
|
||||
copy %r20,%r21 ; use caller-saves registers
|
||||
copy %r21,%r22
|
||||
copy %r22,%r21
|
||||
copy %r21,%r20
|
||||
copy %r20,%r19
|
||||
copy %r19,%r20
|
||||
copy %r20,%r21
|
||||
|
||||
addibf,<= -8,%arg0,_loop2 ; decrement counter by 8 and loop
|
||||
nop
|
||||
|
||||
bv,n %r0(%rp)
|
||||
|
||||
.EXIT
|
||||
.PROCEND
|
||||
|
||||
; Load memory to a register.
|
||||
|
||||
b_load
|
||||
.PROC
|
||||
.CALLINFO NO_CALLS, FRAME=0
|
||||
.ENTRY
|
||||
|
||||
_loop3
|
||||
ldw -4(%sp),%r22 ; load data from frame-marker
|
||||
ldw -8(%sp),%r22 ; use a caller-saves register
|
||||
ldw -12(%sp),%r22
|
||||
ldw -16(%sp),%r22
|
||||
ldw -20(%sp),%r22
|
||||
ldw -24(%sp),%r22
|
||||
ldw -28(%sp),%r22
|
||||
ldw -32(%sp),%r22
|
||||
|
||||
addibf,<= -8,%arg0,_loop3 ; decrement counter by 8 and loop
|
||||
nop
|
||||
|
||||
bv,n %r0(%rp)
|
||||
|
||||
.EXIT
|
||||
.PROCEND
|
||||
|
||||
|
||||
.ALIGN 8
|
||||
to_call
|
||||
.WORD b_null
|
7
qt/md/i386.README
Normal file
7
qt/md/i386.README
Normal file
|
@ -0,0 +1,7 @@
|
|||
Note that some machines want labels to have leading underscores,
|
||||
while others (e.g. System V) do not. Thus, several labels appear
|
||||
duplicated except for the leading underscore, e.g.
|
||||
|
||||
_qt_cswap:
|
||||
qt_cswap:
|
||||
|
120
qt/md/i386.h
Normal file
120
qt/md/i386.h
Normal file
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* 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_386_H
|
||||
#define QT_386_H
|
||||
|
||||
typedef unsigned long qt_word_t;
|
||||
|
||||
/* Thread's initial stack layout on the i386:
|
||||
|
||||
non-varargs:
|
||||
|
||||
+---
|
||||
| arg[2] === `userf' on startup
|
||||
| arg[1] === `pt' on startup
|
||||
| arg[0] === `pu' on startup
|
||||
+---
|
||||
| ret pc === qt_error
|
||||
+---
|
||||
| ret pc === `only' on startup
|
||||
+---
|
||||
| %ebp
|
||||
| %esi
|
||||
| %edi
|
||||
| %ebx <--- qt_t.sp
|
||||
+---
|
||||
|
||||
When a non-varargs thread is started, it ``returns'' directly to
|
||||
the client's `only' function.
|
||||
|
||||
varargs:
|
||||
|
||||
+---
|
||||
| arg[n-1]
|
||||
| ..
|
||||
| arg[0]
|
||||
+---
|
||||
| ret pc === `qt_vstart'
|
||||
+---
|
||||
| %ebp === `startup'
|
||||
| %esi === `cleanup'
|
||||
| %edi === `pt'
|
||||
| %ebx === `vuserf' <--- qt_t.sp
|
||||
+---
|
||||
|
||||
When a varargs thread is started, it ``returns'' to the `qt_vstart'
|
||||
startup code. The startup code calls the appropriate functions. */
|
||||
|
||||
|
||||
/* What to do to start a varargs thread running. */
|
||||
extern void qt_vstart (void);
|
||||
|
||||
|
||||
/* Hold 4 saved regs plus two return pcs (qt_error, qt_start) plus
|
||||
three args. */
|
||||
#define QT_STKBASE (9 * 4)
|
||||
|
||||
/* Hold 4 saved regs plus one return pc (qt_vstart). */
|
||||
#define QT_VSTKBASE (5 * 4)
|
||||
|
||||
|
||||
/* Stack must be 4-byte aligned. */
|
||||
#define QT_STKALIGN (4)
|
||||
|
||||
|
||||
/* Where to place various arguments. */
|
||||
#define QT_ONLY_INDEX (QT_PC)
|
||||
#define QT_USER_INDEX (QT_ARG2)
|
||||
#define QT_ARGT_INDEX (QT_ARG1)
|
||||
#define QT_ARGU_INDEX (QT_ARG0)
|
||||
|
||||
#define QT_VSTARTUP_INDEX (QT_EBP)
|
||||
#define QT_VUSERF_INDEX (QT_EBX)
|
||||
#define QT_VCLEANUP_INDEX (QT_ESI)
|
||||
#define QT_VARGT_INDEX (QT_EDI)
|
||||
|
||||
|
||||
#define QT_EBX 0
|
||||
#define QT_EDI 1
|
||||
#define QT_ESI 2
|
||||
#define QT_EBP 3
|
||||
#define QT_PC 4
|
||||
/* The following are defined only for non-varargs. */
|
||||
#define QT_RPC 5
|
||||
#define QT_ARG0 6
|
||||
#define QT_ARG1 7
|
||||
#define QT_ARG2 8
|
||||
|
||||
|
||||
/* Stack grows down. The top of the stack is the first thing to
|
||||
pop off (preincrement, postdecrement). */
|
||||
#define QT_GROW_DOWN
|
||||
|
||||
extern void qt_error (void);
|
||||
|
||||
/* Push on the error return address. */
|
||||
#define QT_ARGS_MD(sto) \
|
||||
(QT_SPUT (sto, QT_RPC, qt_error))
|
||||
|
||||
|
||||
/* When varargs are pushed, allocate space for all the args. */
|
||||
#define QT_VARGS_MD0(sto, nbytes) \
|
||||
((qt_t *)(((char *)(sto)) - QT_STKROUNDUP(nbytes)))
|
||||
|
||||
#define QT_VARGS_MD1(sto) \
|
||||
(QT_SPUT (sto, QT_PC, qt_vstart))
|
||||
|
||||
#define QT_VARGS_DEFAULT
|
||||
|
||||
#endif /* QT_386_H */
|
108
qt/md/i386.s
Normal file
108
qt/md/i386.s
Normal file
|
@ -0,0 +1,108 @@
|
|||
/* i386.s -- assembly support. */
|
||||
|
||||
/*
|
||||
// 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. */
|
||||
|
||||
/* NOTE: double-labeled `_name' and `name' for System V compatability. */
|
||||
/* NOTE: Comment lines start with '/*' and '//' ONLY. Sorry! */
|
||||
|
||||
/* Callee-save: %esi, %edi, %ebx, %ebp
|
||||
// Caller-save: %eax, %ecx
|
||||
// Can't tell: %edx (seems to work w/o saving it.)
|
||||
//
|
||||
// Assignment:
|
||||
//
|
||||
// See ``i386.h'' for the somewhat unconventional stack layout. */
|
||||
|
||||
|
||||
.text
|
||||
.align 2
|
||||
|
||||
.globl _qt_abort
|
||||
.globl qt_abort
|
||||
.globl _qt_block
|
||||
.globl qt_block
|
||||
.globl _qt_blocki
|
||||
.globl qt_blocki
|
||||
|
||||
/* These all have the type signature
|
||||
//
|
||||
// void *blocking (helper, arg0, arg1, new)
|
||||
//
|
||||
// On procedure entry, the helper is at 4(sp), args at 8(sp) and
|
||||
// 12(sp) and the new thread's sp at 16(sp). It *appears* that the
|
||||
// calling convention for the 8X86 requires the caller to save all
|
||||
// floating-point registers, this makes our life easy. */
|
||||
|
||||
/* Halt the currently-running thread. Save it's callee-save regs on
|
||||
// to the stack, 32 bytes. Switch to the new stack (next == 16+32(sp))
|
||||
// and call the user function (f == 4+32(sp) with arguments: old sp
|
||||
// arg1 (8+32(sp)) and arg2 (12+32(sp)). When the user function is
|
||||
// done, restore the new thread's state and return.
|
||||
//
|
||||
// `qt_abort' is (currently) an alias for `qt_block' because most of
|
||||
// the work is shared. We could save the insns up to `qt_common' by
|
||||
// replicating, but w/o replicating we need an inital subtract (to
|
||||
// offset the stack as if it had been a qt_block) and then a jump
|
||||
// to qt_common. For the cost of a jump, we might as well just do
|
||||
// all the work.
|
||||
//
|
||||
// The helper function (4(sp)) can return a void* that is returned
|
||||
// by the call to `qt_blockk{,i}'. Since we don't touch %eax in
|
||||
// between, we get that ``for free''. */
|
||||
|
||||
_qt_abort:
|
||||
qt_abort:
|
||||
_qt_block:
|
||||
qt_block:
|
||||
_qt_blocki:
|
||||
qt_blocki:
|
||||
pushl %ebp /* Save callee-save, sp-=4. */
|
||||
pushl %esi /* Save callee-save, sp-=4. */
|
||||
pushl %edi /* Save callee-save, sp-=4. */
|
||||
pushl %ebx /* Save callee-save, sp-=4. */
|
||||
movl %esp, %eax /* Remember old stack pointer. */
|
||||
|
||||
qt_common:
|
||||
movl 32(%esp), %esp /* Move to new thread. */
|
||||
pushl 28(%eax) /* Push arg 2. */
|
||||
pushl 24(%eax) /* Push arg 1. */
|
||||
pushl %eax /* Push arg 0. */
|
||||
movl 20(%eax), %ebx /* Get function to call. */
|
||||
call *%ebx /* Call f. */
|
||||
addl $12, %esp /* Pop args. */
|
||||
|
||||
popl %ebx /* Restore callee-save, sp+=4. */
|
||||
popl %edi /* Restore callee-save, sp+=4. */
|
||||
popl %esi /* Restore callee-save, sp+=4. */
|
||||
popl %ebp /* Restore callee-save, sp+=4. */
|
||||
ret /* Resume the stopped function. */
|
||||
hlt
|
||||
|
||||
|
||||
/* Start a varargs thread. */
|
||||
|
||||
.globl _qt_vstart
|
||||
.globl qt_vstart
|
||||
_qt_vstart:
|
||||
qt_vstart:
|
||||
pushl %edi /* Push `pt' arg to `startup'. */
|
||||
call *%ebp /* Call `startup'. */
|
||||
popl %eax /* Clean up the stack. */
|
||||
|
||||
call *%ebx /* Call the user's function. */
|
||||
|
||||
pushl %eax /* Push return from user's. */
|
||||
pushl %edi /* Push `pt' arg to `cleanup'. */
|
||||
call *%esi /* Call `cleanup'. */
|
||||
|
||||
hlt /* `cleanup' never returns. */
|
30
qt/md/i386_b.s
Normal file
30
qt/md/i386_b.s
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
// 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. */
|
||||
|
||||
.globl _b_call_reg
|
||||
.globl b_call_reg
|
||||
.globl _b_call_imm
|
||||
.globl b_call_imm
|
||||
.globl _b_add
|
||||
.globl b_add
|
||||
.globl _b_load
|
||||
.globl b_load
|
||||
|
||||
_b_call_reg:
|
||||
b_call_reg:
|
||||
_b_call_imm:
|
||||
b_call_imm:
|
||||
_b_add:
|
||||
b_add:
|
||||
_b_load:
|
||||
b_load:
|
||||
hlt
|
6
qt/md/ksr1.Makefile
Normal file
6
qt/md/ksr1.Makefile
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
#
|
||||
# KSR1 configuration.
|
||||
#
|
||||
CC = cc -ansi
|
||||
|
164
qt/md/ksr1.h
Normal file
164
qt/md/ksr1.h
Normal file
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* 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 */
|
424
qt/md/ksr1.s
Normal file
424
qt/md/ksr1.s
Normal file
|
@ -0,0 +1,424 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.file "ksr1.s"
|
||||
.def .debug; .endef
|
||||
|
||||
.align 128
|
||||
.globl qt_blocki
|
||||
.globl qt_blocki$TXT
|
||||
.globl qt_block
|
||||
.globl qt_block$TXT
|
||||
.globl qt_start$TXT
|
||||
.globl qt_start
|
||||
.globl qt_abort$TXT
|
||||
.globl qt_abort
|
||||
.globl qt_vstart
|
||||
.globl qt_vstart$TXT
|
||||
|
||||
#
|
||||
# KSR convention: on procedure calls, load both the procedure address
|
||||
# and a pointer to a constant block. The address of function `f' is
|
||||
# `f$TXT', and the constant block address is `f'. The constant block
|
||||
# has several reserved values:
|
||||
#
|
||||
# 8 bytes fpu register save mask
|
||||
# 4 bytes ipu register save mask
|
||||
# 4 bytes ceu register save mask
|
||||
# f: f$TXT
|
||||
# ... whatever you want ... (not quite...read on)
|
||||
#
|
||||
# Note, by the way, that a pointer to a function is passed as a
|
||||
# pointer to the constant area, and the constant area has the text
|
||||
# address.
|
||||
#
|
||||
|
||||
#
|
||||
# Procedures that do not return structures prefix their code with
|
||||
#
|
||||
# proc$TXT:
|
||||
# finop; cxnop
|
||||
# finop; cxnop
|
||||
# <proc code>
|
||||
#
|
||||
# Calls to those procedures branch to a 16 byte offset (4 instrs) in
|
||||
# to the procedure to skip those instructions.
|
||||
#
|
||||
# Procedures that return structures use a different code prefix:
|
||||
#
|
||||
# proc$TXT:
|
||||
# finop; beq.qt %rc, %rc, 24 # return value entry
|
||||
# finop; cxnop
|
||||
# finop; movi8 0, %rc # no return value entry
|
||||
# <proc code>
|
||||
#
|
||||
# Calls that want the returned structure branch directly to the
|
||||
# procedure address. Callers that don't want (or aren't expecting) a
|
||||
# return value branche 16 bytes in to the procedure, which will zero
|
||||
# %rc, telling the called procedure not to return a structure.
|
||||
#
|
||||
|
||||
#
|
||||
# On entry:
|
||||
# %i2 -- control block of helper function to run
|
||||
# (dereference to get helper)
|
||||
# %i3 -- a1
|
||||
# %i4 -- a2
|
||||
# %i5 -- sp of new to run
|
||||
#
|
||||
|
||||
.data
|
||||
.half 0x0, 0x0, 0x7ffff000, 0x7fff8000
|
||||
qt_blocki:
|
||||
qt_abort:
|
||||
.word qt_blocki$TXT
|
||||
.word qt_restore$TXT
|
||||
|
||||
.text
|
||||
qt_abort$TXT:
|
||||
qt_blocki$TXT:
|
||||
finop ; cxnop # entry prefix
|
||||
finop ; cxnop # entry prefix
|
||||
add8.ntr 75,%i31,%i31 ; movi8 512,%c5 # ICR; stk adjust
|
||||
finop ; ssub8.ntr 0,%sp,%c5,%sp
|
||||
finop ; st8 %fp,504(%sp) # Save caller's fp
|
||||
finop ; st8 %cp,496(%sp) # Save caller's cp
|
||||
finop ; ld8 8(%c10),%c5 # ld qt_restore$TXT
|
||||
finop ; st8 %c14,0(%sp) # Save special ret addr
|
||||
finop ; mov8_8 %c10, %cp # Our cp
|
||||
finop ; sadd8.ntr 0,%sp,%c5,%fp # Our frame ptr
|
||||
finop ; st8 %c5,8(%sp) # st qt_restore$TXT
|
||||
#
|
||||
# CEU registers %c15-%c24, %c26-%c30 (%c14 we restore later)
|
||||
#
|
||||
finop ; st8 %c15,456(%sp)
|
||||
finop ; st8 %c16,448(%sp)
|
||||
finop ; st8 %c17,440(%sp)
|
||||
finop ; st8 %c18,432(%sp)
|
||||
finop ; st8 %c19,424(%sp)
|
||||
finop ; st8 %c20,416(%sp)
|
||||
finop ; st8 %c21,408(%sp)
|
||||
finop ; st8 %c22,400(%sp)
|
||||
finop ; st8 %c23,392(%sp)
|
||||
finop ; st8 %c24,384(%sp)
|
||||
#
|
||||
# %c25 is the Enclosing Frame Pointer (EFP) -- since C doesn't
|
||||
# use nested procedures, we ignore it (leaving a gap, though)
|
||||
#
|
||||
finop ; st8 %c26,368(%sp)
|
||||
finop ; st8 %c27,360(%sp)
|
||||
finop ; st8 %c28,352(%sp)
|
||||
finop ; st8 %c29,344(%sp)
|
||||
finop ; st8 %c30,336(%sp)
|
||||
#
|
||||
# IPU registers %i12-%i30
|
||||
#
|
||||
finop ; st8 %i12,328(%sp)
|
||||
finop ; st8 %i13,320(%sp)
|
||||
finop ; st8 %i14,312(%sp)
|
||||
finop ; st8 %i15,304(%sp)
|
||||
# (gap to get alignment for st64)
|
||||
# -- Doesn't work on version 1.1.3 of the OS
|
||||
# finop ; st64 %i16,256(%sp)
|
||||
|
||||
finop ; st8 %i16,256(%sp)
|
||||
finop ; st8 %i17,248(%sp)
|
||||
finop ; st8 %i18,240(%sp)
|
||||
finop ; st8 %i19,232(%sp)
|
||||
finop ; st8 %i20,224(%sp)
|
||||
finop ; st8 %i21,216(%sp)
|
||||
finop ; st8 %i22,208(%sp)
|
||||
finop ; st8 %i23,200(%sp)
|
||||
finop ; st8 %i24,192(%sp)
|
||||
finop ; st8 %i25,184(%sp)
|
||||
finop ; st8 %i26,176(%sp)
|
||||
finop ; st8 %i27,168(%sp)
|
||||
finop ; st8 %i28,160(%sp)
|
||||
finop ; st8 %i29,152(%sp)
|
||||
finop ; st8 %i30,144(%sp)
|
||||
#
|
||||
# FPU already saved, or saving not necessary
|
||||
#
|
||||
|
||||
#
|
||||
# Switch to the stack passed in as fourth argument to the block
|
||||
# routine (%i5) and call the helper routine passed in as the first
|
||||
# argument (%i2). Note that the address of the helper's constant
|
||||
# block is passed in, so we must derefence it to get the helper's text
|
||||
# address.
|
||||
#
|
||||
finop ; movb8_8 %i2,%c10 # helper's ConstBlock
|
||||
finop ; cxnop # Delay slot, fill w/
|
||||
finop ; cxnop # .. 2 st8 from above
|
||||
finop ; ld8 0(%c10),%c4 # load addr of helper
|
||||
finop ; movb8_8 %sp, %i2 # 1st arg to helper
|
||||
# is this stack; other
|
||||
# args remain in regs
|
||||
finop ; movb8_8 %i5,%sp # switch stacks
|
||||
finop ; jsr %c14,16(%c4) # call helper
|
||||
movi8 3, %i0 ; movi8 0,%c8 # nargs brain dmg
|
||||
finop ; cxnop
|
||||
finop ; cxnop
|
||||
#
|
||||
# Here is where behavior differs for threads being restored and threads
|
||||
# being started. Blocked threads have a pointer to qt_restore$TXT on
|
||||
# the top of their stacks; manufactured stacks have a pointer to qt_start$TXT
|
||||
# on the top of their stacks. With this setup, starting threads
|
||||
# skip the (unecessary) restore operations.
|
||||
#
|
||||
# We jump to an offset of 16 to either (1) skip past the two noop pairs
|
||||
# at the start of qt_start$TXT, or (2) skip past the two noop pairs
|
||||
# after qt_restore$TXT.
|
||||
#
|
||||
finop ; ld8 8(%sp),%c4
|
||||
finop ; cxnop
|
||||
finop ; cxnop
|
||||
finop ; jmp 16(%c4)
|
||||
qt_restore$TXT:
|
||||
finop ; cxnop
|
||||
finop ; cxnop
|
||||
#
|
||||
# Point of Restore:
|
||||
#
|
||||
# The helper funtion will return here. Any result it has placed in
|
||||
# a return register (most likely %i0) will not get overwritten below
|
||||
# and will consequently be the return value of the blocking routine.
|
||||
#
|
||||
|
||||
#
|
||||
# CEU registers %c15-%c24, %c26-%c30 (%c14 we restore later)
|
||||
#
|
||||
finop ; ld8 456(%sp),%c15
|
||||
finop ; ld8 448(%sp),%c16
|
||||
finop ; ld8 440(%sp),%c17
|
||||
finop ; ld8 432(%sp),%c18
|
||||
finop ; ld8 424(%sp),%c19
|
||||
finop ; ld8 416(%sp),%c20
|
||||
finop ; ld8 408(%sp),%c21
|
||||
finop ; ld8 400(%sp),%c22
|
||||
finop ; ld8 392(%sp),%c23
|
||||
finop ; ld8 384(%sp),%c24
|
||||
#
|
||||
# %c25 is the Enclosing Frame Pointer (EFP) -- since C doesn't
|
||||
# use nested procedures, we ignore it (leaving a gap, though)
|
||||
#
|
||||
finop ; ld8 368(%sp),%c26
|
||||
finop ; ld8 360(%sp),%c27
|
||||
finop ; ld8 352(%sp),%c28
|
||||
finop ; ld8 344(%sp),%c29
|
||||
finop ; ld8 336(%sp),%c30
|
||||
#
|
||||
# IPU registers %i12-%i30
|
||||
#
|
||||
finop ; ld8 328(%sp),%i12
|
||||
finop ; ld8 320(%sp),%i13
|
||||
finop ; ld8 312(%sp),%i14
|
||||
finop ; ld8 304(%sp),%i15
|
||||
# (gap to get alignment for ld64)
|
||||
# -- Doesn't work on version 1.1.3 of the OS
|
||||
# finop ; ld64 256(%sp),%i16
|
||||
|
||||
finop ; ld8 256(%sp),%i16
|
||||
finop ; ld8 248(%sp),%i17
|
||||
finop ; ld8 240(%sp),%i18
|
||||
finop ; ld8 232(%sp),%i19
|
||||
finop ; ld8 224(%sp),%i20
|
||||
finop ; ld8 216(%sp),%i21
|
||||
finop ; ld8 208(%sp),%i22
|
||||
finop ; ld8 200(%sp),%i23
|
||||
finop ; ld8 192(%sp),%i24
|
||||
finop ; ld8 184(%sp),%i25
|
||||
finop ; ld8 176(%sp),%i26
|
||||
finop ; ld8 168(%sp),%i27
|
||||
finop ; ld8 160(%sp),%i28
|
||||
finop ; ld8 152(%sp),%i29
|
||||
finop ; ld8 144(%sp),%i30
|
||||
|
||||
#
|
||||
# FPU registers don't need to be loaded, or will be loaded by an
|
||||
# enclosing scope (e.g., if this is called by qt_block).
|
||||
#
|
||||
|
||||
#
|
||||
# Load the special registers. We don't load the stack ptr because
|
||||
# the new stack is passed in as an argument, we don't load the EFP
|
||||
# because we don't use it, and we load the return address specially
|
||||
# off the top of the stack.
|
||||
#
|
||||
finop ; ld8 0(%sp),%c14 # return addr
|
||||
finop ; ld8 496(%sp),%cp
|
||||
finop ; ld8 504(%sp),%fp
|
||||
|
||||
finop ; jmp 32(%c14) # jump back to thread
|
||||
finop ; movi8 512,%c5 # stack adjust
|
||||
finop ; sadd8.ntr 0,%sp,%c5,%sp
|
||||
|
||||
.data
|
||||
.half 0x0, 0x0, 0x7ffff000, 0x7fff8000
|
||||
qt_block:
|
||||
.word qt_block$TXT
|
||||
.word qt_error
|
||||
.word qt_error$TXT
|
||||
.word qt_blocki
|
||||
#
|
||||
# Handle saving and restoring the FPU regs, relying on qt_blocki
|
||||
# to save and restore the remaining registers.
|
||||
#
|
||||
.text
|
||||
qt_block$TXT:
|
||||
finop ; cxnop # entry prefix
|
||||
finop ; cxnop # entry prefix
|
||||
|
||||
add8.ntr 29,%i31,%i31 ; movi8 512,%c5 # ICR; stk adjust
|
||||
finop ; ssub8.ntr 0,%sp,%c5,%sp
|
||||
finop ; st8 %fp,504(%sp) # Save caller's fp
|
||||
finop ; st8 %cp,496(%sp) # Save caller's cp
|
||||
finop ; st8 %c14,488(%sp) # store ret addr
|
||||
finop ; sadd8.ntr 0,%sp,%c5,%fp # Our frame ptr
|
||||
finop ; mov8_8 %c10, %cp # Our cp
|
||||
|
||||
#
|
||||
# Store 8 registers at once...destination must be a multiple of 64
|
||||
#
|
||||
finop ; st64 %f16,384(%sp)
|
||||
finop ; st64 %f24,320(%sp)
|
||||
finop ; st64 %f32,256(%sp)
|
||||
finop ; st64 %f40,192(%sp)
|
||||
finop ; st64 %f48,128(%sp)
|
||||
finop ; st64 %f56,64(%sp)
|
||||
|
||||
#
|
||||
# Call the integer blocking routine, passing the arguments passed to us
|
||||
#
|
||||
finop ; ld8 24(%cp), %c10
|
||||
finop ; cxnop
|
||||
finop ; jsr %c14, qt_blocki$TXT
|
||||
finop ; cxnop
|
||||
finop ; cxnop
|
||||
movi8 4,%i0 ; movi8 0,%c8 # nargs brain dmg
|
||||
|
||||
#
|
||||
# Load 8 registers at once...source must be a multiple of 64
|
||||
#
|
||||
finop ; ld64 64(%sp),%f56
|
||||
finop ; ld64 128(%sp),%f48
|
||||
finop ; ld64 192(%sp),%f40
|
||||
finop ; ld64 256(%sp),%f32
|
||||
finop ; ld64 320(%sp),%f24
|
||||
finop ; ld64 384(%sp),%f16
|
||||
|
||||
finop ; ld8 488(%sp),%c14
|
||||
finop ; ld8 496(%sp),%cp
|
||||
finop ; ld8 504(%sp),%fp
|
||||
finop ; jmp 32(%c14) # jump back to thread
|
||||
finop ; movi8 512,%c5 # stack adjust
|
||||
finop ; sadd8.ntr 0,%sp,%c5,%sp
|
||||
|
||||
|
||||
.data
|
||||
.half 0x0, 0x0, 0x7ffff000, 0x7fff8000
|
||||
qt_start:
|
||||
.word qt_start$TXT
|
||||
#
|
||||
# A new thread is set up to "appear" as if it were executing code at
|
||||
# the beginning of qt_start and then it called a blocking routine
|
||||
# (qt_blocki). So when a new thread starts to run, it gets unblocked
|
||||
# by the code above and "returns" to `qt_start$TXT' in the
|
||||
# restore step of the switch. Blocked threads jump to 16(qt_restore$TXT),
|
||||
# and starting threads jump to 16(qt_start$TXT).
|
||||
#
|
||||
.text
|
||||
qt_start$TXT:
|
||||
finop ; cxnop #
|
||||
finop ; cxnop #
|
||||
finop ; ld8 40(%sp),%c10 # `only' constant block
|
||||
finop ; ld8 32(%sp),%i4 # `userf' arg.
|
||||
finop ; ld8 24(%sp),%i3 # `t' arg.
|
||||
finop ; ld8 0(%c10),%c4 # `only' text location
|
||||
finop ; ld8 16(%sp),%i2 # `u' arg.
|
||||
finop ; cxnop
|
||||
finop ; jsr %c14,16(%c4) # call `only'
|
||||
#
|
||||
# Pop the frame used to store the thread's initial data
|
||||
#
|
||||
finop ; sadd8.ntr 0,%sp,128,%sp
|
||||
finop ; cxnop
|
||||
movi8 2,%i0 ; movi8 0,%c8 # nargs brain dmg
|
||||
#
|
||||
# If we ever return, it's an error.
|
||||
#
|
||||
finop ; jmp qt_error$TXT
|
||||
finop ; cxnop
|
||||
finop ; cxnop
|
||||
movi8 0,%i0 ; movi8 0,%c8 # nargs brain dmg
|
||||
|
||||
|
||||
#
|
||||
# This stuff is broken
|
||||
#
|
||||
.data
|
||||
.half 0x0, 0x0, 0x7ffff000, 0x7fff8000
|
||||
qt_vstart:
|
||||
.word qt_vstart$TXT
|
||||
|
||||
.text
|
||||
qt_vstart$TXT:
|
||||
finop ; cxnop # entry prefix
|
||||
finop ; cxnop # entry prefix
|
||||
finop ; cxnop
|
||||
finop ; cxnop
|
||||
add8.ntr 11,%i31,%i31 ; movi8 512,%c5
|
||||
finop ; ssub8.ntr 0,%sp,%c5,%sp # fix stack
|
||||
finop ; ld8 8(%sp),%i2 # load `t' as arg to
|
||||
finop ; cxnop # `startup'
|
||||
finop ; cxnop
|
||||
finop ; ld8 16(%sp),%c10 # `startup' const block
|
||||
finop ; cxnop
|
||||
finop ; cxnop
|
||||
finop ; ld8 0(%c10),%c4 # `startup' text loc.
|
||||
finop ; cxnop
|
||||
finop ; cxnop
|
||||
finop ; jsr %c14,16(%c4) # call `startup'
|
||||
finop ; cxnop
|
||||
finop ; cxnop
|
||||
movi8 1, %i0 ; movi8 0,%c8 # nargs brain dmg
|
||||
#
|
||||
# finop ; sadd 0,%sp,128,%sp # alter stack
|
||||
#
|
||||
finop ; ld8 8(%sp),%i2 # load `t' as arg to
|
||||
finop ; ld8 8(%sp),%i2 # load `t' as arg to
|
||||
finop ; ld8 8(%sp),%i2 # load `t' as arg to
|
||||
finop ; ld8 8(%sp),%i2 # load `t' as arg to
|
||||
|
||||
finop ; ld8 32(%sp),%c10 # `only' constant block
|
||||
finop ; ld8 8(%sp),%i2 # `u' arg.
|
||||
finop ; ld8 16(%sp),%i3 # `t' arg.
|
||||
finop ; ld8 0(%c10),%c4 # `only' text location
|
||||
finop ; ld8 24(%sp),%i4 # `userf' arg.
|
||||
finop ; cxnop
|
||||
finop ; jsr %c4,16(%c4) # call `only'
|
||||
finop ; cxnop
|
||||
finop ; cxnop
|
||||
#
|
||||
# If the callee ever calls `nargs', the following instruction (pair)
|
||||
# will be executed. However, we don't know when we compile this code
|
||||
# how many args are being passed. So we give our best guess: 0.
|
||||
#
|
||||
movi8 0,%i0 ; movi8 0,%c8 # nargs brain dmg
|
||||
#
|
||||
# If we ever return, it's an error.
|
||||
#
|
||||
finop ; jmp qt_error$TXT
|
||||
finop ; cxnop
|
||||
finop ; cxnop
|
||||
movi8 0,%i0 ; movi8 0,%c8 # nargs brain dmg
|
49
qt/md/ksr1_b.s
Normal file
49
qt/md/ksr1_b.s
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.file "ksr1_b.s"
|
||||
.def .debug; .endef
|
||||
|
||||
.globl b_call_reg$TXT
|
||||
.globl b_call_reg
|
||||
.globl b_call_imm$TXT
|
||||
.globl b_call_imm
|
||||
.globl b_add$TXT
|
||||
.globl b_add
|
||||
.globl b_load$TXT
|
||||
.globl b_load
|
||||
|
||||
|
||||
b_call_reg:
|
||||
b_call_imm:
|
||||
b_add:
|
||||
b_load:
|
||||
.word b_call_reg$TXT
|
||||
.word qt_error
|
||||
.word qt_error$TXT
|
||||
|
||||
|
||||
b_call_reg$TXT:
|
||||
b_call_imm$TXT:
|
||||
b_add$TXT:
|
||||
b_load$TXT:
|
||||
finop ; cxnop
|
||||
finop ; cxnop
|
||||
finop ; ld8 16(%cp),%c4
|
||||
finop ; ld8 8(%cp),%cp
|
||||
finop ; cxnop
|
||||
finop ; cxnop
|
||||
finop ; jsr %c4,0(%c4)
|
||||
finop ; cxnop
|
||||
finop ; cxnop
|
||||
|
6
qt/md/m88k.Makefile
Normal file
6
qt/md/m88k.Makefile
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
#
|
||||
# Hosted compilers for 88k for Meerkat.
|
||||
#
|
||||
CC = gcc88 -Dm88k -ansi -pedantic -Wall -fno-builtin
|
||||
AS = as88
|
111
qt/md/m88k.c
Normal file
111
qt/md/m88k.c
Normal file
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include "qt.h"
|
||||
|
||||
/* Varargs is harder on the m88k. Parameters are saved on the stack as
|
||||
something like (stack grows down to low memory; low at bottom of
|
||||
picture):
|
||||
|
||||
| :
|
||||
| arg8 <-- va_list.__va_stk
|
||||
+---
|
||||
| :
|
||||
+---
|
||||
| arg7
|
||||
| :
|
||||
| iarg0 <-- va_list.__va_reg
|
||||
+---
|
||||
| :
|
||||
| va_list { __va_arg, __va_stk, __va_reg }
|
||||
| :
|
||||
+---
|
||||
|
||||
Here, `va_list.__va_arg' is the number of word-size arguments
|
||||
that have already been skipped. Doubles must be double-arligned.
|
||||
|
||||
What this means for us is that the user's routine needs to be
|
||||
called with an arg list where some of the words in the `__va_stk'
|
||||
part of the parameter list have to be promoted to registers.
|
||||
|
||||
BUG: doubleword register arguments must be double-aligned. If
|
||||
something is passed as an even # arg and used as an odd # arg or
|
||||
vice-versa, the code in the called routine (in the new thread) that
|
||||
decides how to adjust the index will get it wrong, because it will
|
||||
be expect it to be, say, doubleword aligned and it will really be
|
||||
singleword aligned.
|
||||
|
||||
I'm not sure you can solve this without knowing the types of all
|
||||
the arguments. All in all, we never promised varargs would work
|
||||
reliably. */
|
||||
|
||||
|
||||
|
||||
#define QT_VADJ(sp) (((char *)sp) - QT_VSTKBASE)
|
||||
|
||||
/* Always allocate at least enough space for 8 args; waste some space
|
||||
at the base of the stack to ensure the startup routine doesn't read
|
||||
off the end of the stack. */
|
||||
|
||||
#define QT_VARGS_MD0(sp, vabytes) \
|
||||
((qt_t *)(((char *)(sp)) - 8*4 - QT_STKROUNDUP(vabytes)))
|
||||
|
||||
extern void qt_vstart(void);
|
||||
#define QT_VARGS_MD1(sp) (QT_SPUT (sp, QT_1, qt_vstart))
|
||||
|
||||
|
||||
struct qt_t *
|
||||
qt_vargs (struct qt_t *qsp, int nbytes, void *vargs,
|
||||
void *pt, qt_function_t *startup,
|
||||
qt_function_t *vuserf, qt_function_t *cleanup)
|
||||
{
|
||||
va_list ap;
|
||||
int i;
|
||||
int n; /* Number of words into original arg list. */
|
||||
qt_word_t *sp;
|
||||
int *reg; /* Where to read passed-in-reg args. */
|
||||
int *stk; /* Where to read passed-on-stk args. */
|
||||
|
||||
ap = *(va_list *)vargs;
|
||||
qsp = QT_VARGS_MD0 (qsp, nbytes);
|
||||
sp = (qt_word_t *)qsp;
|
||||
|
||||
reg = (ap.__va_arg < 8)
|
||||
? &ap.__va_reg[ap.__va_arg]
|
||||
: 0;
|
||||
stk = &ap.__va_stk[8];
|
||||
n = ap.__va_arg;
|
||||
for (i=0; i<nbytes/sizeof(qt_word_t) && n<8; ++i,++n) {
|
||||
sp[i] = *reg++;
|
||||
}
|
||||
for (; i<nbytes/sizeof(qt_word_t); ++i) {
|
||||
sp[i] = *stk++;
|
||||
}
|
||||
|
||||
#ifdef QT_NDEF
|
||||
for (i=0; i<nbytes/sizeof(qt_word_t); ++i) {
|
||||
sp[i] = (n < 8)
|
||||
? *reg++
|
||||
: *stk++;
|
||||
++n;
|
||||
}
|
||||
#endif
|
||||
|
||||
QT_VARGS_MD1 (QT_VADJ(sp));
|
||||
QT_SPUT (QT_VADJ(sp), QT_VARGT_INDEX, pt);
|
||||
QT_SPUT (QT_VADJ(sp), QT_VSTARTUP_INDEX, startup);
|
||||
QT_SPUT (QT_VADJ(sp), QT_VUSERF_INDEX, vuserf);
|
||||
QT_SPUT (QT_VADJ(sp), QT_VCLEANUP_INDEX, cleanup);
|
||||
return ((qt_t *)QT_VADJ(sp));
|
||||
}
|
159
qt/md/m88k.h
Normal file
159
qt/md/m88k.h
Normal file
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* 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_M88K_H
|
||||
#define QT_M88K_H
|
||||
|
||||
typedef unsigned long qt_word_t;
|
||||
|
||||
#define QT_GROW_DOWN
|
||||
|
||||
/* Stack layout on the mips:
|
||||
|
||||
Callee-save registers are: $16-$23, $30; $f20-$f30.
|
||||
Also save $31, return pc.
|
||||
|
||||
Non-varargs:
|
||||
|
||||
+---
|
||||
| r30 (fp) on startup === 0
|
||||
| r25
|
||||
| r24
|
||||
| r23
|
||||
| r22
|
||||
| r21
|
||||
| r20
|
||||
| r19
|
||||
| r18
|
||||
| r17 on startup === `only'
|
||||
| r16 on startup === `userf'
|
||||
| r15 on startup === `pt'
|
||||
| r14 on startup === `pu'
|
||||
| r1 on startup === `qt_start'
|
||||
| 0
|
||||
| 0
|
||||
+---
|
||||
| 0
|
||||
| ... (8 regs worth === 32 bytes of homing area)
|
||||
| 0 <--- sp
|
||||
+---
|
||||
|
||||
Conventions for varargs:
|
||||
|
||||
| :
|
||||
| arg8
|
||||
+---
|
||||
| r30 (fp) arg7
|
||||
| r25 arg6
|
||||
| r24 arg5
|
||||
| r23 arg4
|
||||
| r22 arg3
|
||||
| r21 arg2
|
||||
| r20 arg1
|
||||
| r19 arg0
|
||||
| r18
|
||||
| r17 on startup === `startup'
|
||||
| r16 on startup === `vuserf'
|
||||
| r15 on startup === `pt'
|
||||
| r14 on startup === `cleanup'
|
||||
| r1 on startup === `qt_vstart'
|
||||
| 0
|
||||
| 0
|
||||
+---
|
||||
| 0
|
||||
| ... (8 regs worth === 32 bytes of homing area)
|
||||
| 0 <--- sp
|
||||
+---
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/* Stack must be doubleword aligned. */
|
||||
#define QT_STKALIGN (16) /* Doubleword aligned. */
|
||||
|
||||
/* How much space is allocated to hold all the crud for
|
||||
initialization: saved registers plus padding to keep the stack
|
||||
aligned plus 8 words of padding to use as a `homing area' (for
|
||||
r2-r9) when calling helper functions on the stack of the (not yet
|
||||
started) thread. The varargs save area is small because it gets
|
||||
overlapped with the top of the parameter list. In case the
|
||||
parameter list is less than 8 args, QT_ARGS_MD0 adds some dead
|
||||
space at the top of the stack. */
|
||||
|
||||
#define QT_STKBASE (16*4 + 8*4)
|
||||
#define QT_VSTKBASE (8*4 + 8*4)
|
||||
|
||||
|
||||
/* Index of various registers. */
|
||||
#define QT_1 (8+2)
|
||||
#define QT_14 (8+3)
|
||||
#define QT_15 (8+4)
|
||||
#define QT_16 (8+5)
|
||||
#define QT_17 (8+6)
|
||||
#define QT_30 (8+15)
|
||||
|
||||
|
||||
/* When a never-before-run thread is restored, the return pc points
|
||||
to a fragment of code that starts the thread running. For
|
||||
non-vargs functions, it sets up arguments and calls the client's
|
||||
`only' function. For varargs functions, the startup code calls the
|
||||
startup, user, and cleanup functions.
|
||||
|
||||
For non-varargs functions, we set the frame pointer to 0 to
|
||||
null-terminate the call chain.
|
||||
|
||||
For varargs functions, the frame pointer register is used to hold
|
||||
one of the arguments, so that all arguments can be laid out in
|
||||
memory by the conventional `qt_vargs' varargs initialization
|
||||
routine.
|
||||
|
||||
The varargs startup routine always reads 8 words of arguments from
|
||||
the stack. If there are less than 8 words of arguments, then the
|
||||
arg list could call off the top of the stack. To prevent fall-off,
|
||||
always allocate 8 words. */
|
||||
|
||||
extern void qt_start(void);
|
||||
#define QT_ARGS_MD(sp) \
|
||||
(QT_SPUT (sp, QT_1, qt_start), \
|
||||
QT_SPUT (sp, QT_30, 0))
|
||||
|
||||
|
||||
/* The m88k uses a struct for `va_list', so pass a pointer to the
|
||||
struct. */
|
||||
|
||||
typedef void (qt_function_t)(void);
|
||||
|
||||
struct qt_t;
|
||||
extern struct qt_t *qt_vargs (struct qt_t *sp, int nbytes,
|
||||
void *vargs, void *pt,
|
||||
qt_function_t *startup,
|
||||
qt_function_t *vuserf,
|
||||
qt_function_t *cleanup);
|
||||
|
||||
#define QT_VARGS(sp, nbytes, vargs, pt, startup, vuserf, cleanup) \
|
||||
(qt_vargs (sp, nbytes, &(vargs), pt, (qt_function_t *)startup, \
|
||||
(qt_function_t *)vuserf, (qt_function_t *)cleanup))
|
||||
|
||||
|
||||
/* The *index* (positive offset) of where to put each value. */
|
||||
#define QT_ONLY_INDEX (QT_17)
|
||||
#define QT_USER_INDEX (QT_16)
|
||||
#define QT_ARGT_INDEX (QT_15)
|
||||
#define QT_ARGU_INDEX (QT_14)
|
||||
|
||||
#define QT_VCLEANUP_INDEX (QT_14)
|
||||
#define QT_VUSERF_INDEX (QT_16)
|
||||
#define QT_VSTARTUP_INDEX (QT_17)
|
||||
#define QT_VARGT_INDEX (QT_15)
|
||||
|
||||
#endif /* ndef QT_M88K_H */
|
132
qt/md/m88k.s
Normal file
132
qt/md/m88k.s
Normal file
|
@ -0,0 +1,132 @@
|
|||
/* m88k.s -- assembly support. */
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* Callee-save r14..r25, r31(sp), r30(fp). r1 === return pc.
|
||||
* Argument registers r2..r9, return value r2..r3.
|
||||
*
|
||||
* On startup, restore regs so retpc === call to a function to start.
|
||||
*
|
||||
* We're going to call a function (r2) from within the context switch
|
||||
* routine. Call it on the new thread's stack on behalf of the old
|
||||
* thread.
|
||||
*/
|
||||
|
||||
.globl _qt_block
|
||||
.globl _qt_blocki
|
||||
.globl _qt_abort
|
||||
.globl _qt_start
|
||||
.globl _qt_vstart
|
||||
|
||||
/*
|
||||
** r2: ptr to function to call once curr is suspended
|
||||
** and control is on r5's stack.
|
||||
** r3: 1'th arg to *r2.
|
||||
** r4: 2'th arg to *r2.
|
||||
** r5: sp of thread to suspend.
|
||||
**
|
||||
** The helper routine returns a value that is passed on as the
|
||||
** return value from the blocking routine. Since we don't
|
||||
** touch r2 between the helper's return and the end of
|
||||
** function, we get this behavior for free.
|
||||
**
|
||||
** Same entry for integer-only and floating-point, since there
|
||||
** are no separate integer and floating-point registers.
|
||||
**
|
||||
** Each procedure call sets aside a ``home region'' of 8 regs
|
||||
** for r2-r9 for varargs. For context switches we don't use
|
||||
** the ``home region'' for varargs so use it to save regs.
|
||||
** Allocate 64 bytes of save space -- use 32 bytes of register
|
||||
** save area passed in to us plus 32 bytes we allcated, use
|
||||
** the other 32 bytes for save area for a save area to call
|
||||
** the helper function.
|
||||
*/
|
||||
_qt_block:
|
||||
_qt_blocki:
|
||||
sub r31, r31,64 /* Allocate reg save space. */
|
||||
st r1, r31,8+32 /* Save callee-save registers. */
|
||||
st r14, r31,12+32
|
||||
st.d r15, r31,16+32
|
||||
st.d r17, r31,24+32
|
||||
st.d r19, r31,32+32
|
||||
st.d r21, r31,40+32
|
||||
st.d r23, r31,48+32
|
||||
st r25, r31,56+32
|
||||
st r30, r31,60+32
|
||||
|
||||
_qt_abort:
|
||||
addu r14, r31,0 /* Remember old sp. */
|
||||
addu r31, r5,0 /* Set new sp. */
|
||||
jsr.n r2 /* Call helper. */
|
||||
addu r2, r14,0 /* Pass old sp as an arg0 to helper. */
|
||||
|
||||
ld r1, r31,8+32 /* Restore callee-save registers. */
|
||||
ld r14, r31,12+32
|
||||
ld.d r15, r31,16+32
|
||||
ld.d r17, r31,24+32
|
||||
ld.d r19, r31,32+32
|
||||
ld.d r21, r31,40+32
|
||||
ld.d r23, r31,48+32
|
||||
ld r25, r31,56+32
|
||||
ld r30, r31,60+32
|
||||
|
||||
jmp.n r1 /* Return to new thread's caller. */
|
||||
addu r31, r31,64 /* Free register save space. */
|
||||
|
||||
|
||||
/*
|
||||
** Non-varargs thread startup.
|
||||
** See `m88k.h' for register use conventions.
|
||||
*/
|
||||
_qt_start:
|
||||
addu r2, r14,0 /* Set user arg `pu'. */
|
||||
addu r3, r15,0 /* ... user function pt. */
|
||||
jsr.n r17 /* Call `only'. */
|
||||
addu r4, r16,0 /* ... user function userf. */
|
||||
|
||||
bsr _qt_error /* `only' erroniously returned. */
|
||||
|
||||
|
||||
/*
|
||||
** Varargs thread startup.
|
||||
** See `m88k.h' for register use conventions.
|
||||
**
|
||||
** Call the `startup' function with just argument `pt'.
|
||||
** Then call `vuserf' with 8 register args plus any
|
||||
** stack args.
|
||||
** Then call `cleanup' with `pt' and the return value
|
||||
** from `vuserf'.
|
||||
*/
|
||||
_qt_vstart:
|
||||
addu r18, r30,0 /* Remember arg7 to `vuserf'. */
|
||||
addu r30, r0,0 /* Null-terminate call chain. */
|
||||
|
||||
jsr.n r17 /* Call `startup'. */
|
||||
addu r2, r15,0 /* `pt' is arg0 to `startup'. */
|
||||
|
||||
addu r2, r19,0 /* Set arg0. */
|
||||
addu r3, r20,0 /* Set arg1. */
|
||||
addu r4, r21,0 /* Set arg2. */
|
||||
addu r5, r22,0 /* Set arg3. */
|
||||
addu r6, r23,0 /* Set arg4. */
|
||||
addu r7, r24,0 /* Set arg5. */
|
||||
addu r8, r25,0 /* Set arg6. */
|
||||
jsr.n r16 /* Call `vuserf'. */
|
||||
addu r9, r18,0 /* Set arg7. */
|
||||
|
||||
addu r3, r2,0 /* Ret. value is arg1 to `cleanup'. */
|
||||
jsr.n r14 /* Call `cleanup'. */
|
||||
addu r2, r15,0 /* `pt' is arg0 to `cleanup'. */
|
||||
|
||||
bsr _qt_error /* `cleanup' erroniously returned. */
|
117
qt/md/m88k_b.s
Normal file
117
qt/md/m88k_b.s
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.text
|
||||
.globl _b_call_reg
|
||||
.globl _b_call_imm
|
||||
.globl _b_add
|
||||
.globl _b_load
|
||||
|
||||
_b_null:
|
||||
jmp r1
|
||||
|
||||
_b_call_reg:
|
||||
subu r31, r31,8 /* Alloc ret pc save space. */
|
||||
st r1, r31,32 /* Save ret pc. */
|
||||
or.u r3, r0,hi16(_b_null) /* Put call addr in a reg. */
|
||||
or r3, r3,lo16(_b_null)
|
||||
jsr r3
|
||||
L0:
|
||||
jsr r3
|
||||
jsr r3
|
||||
jsr r3
|
||||
jsr.n r3
|
||||
subu r2, r2,5 /* Decrement #of iter to go. */
|
||||
bcnd.n gt0,r2,L0
|
||||
jsr r3
|
||||
|
||||
ld r1, r31,32
|
||||
jmp r1
|
||||
|
||||
|
||||
_b_call_imm:
|
||||
subu r31, r31,8 /* Alloc ret pc save space. */
|
||||
st r1, r31,32 /* Save ret pc. */
|
||||
bsr _b_null
|
||||
L1:
|
||||
bsr _b_null
|
||||
bsr _b_null
|
||||
bsr _b_null
|
||||
bsr.n _b_null
|
||||
subu r2, r2,5 /* Decrement #of iter to go. */
|
||||
bcnd.n gt0,r2,L1
|
||||
bsr _b_null
|
||||
|
||||
ld r1, r31,32
|
||||
jmp r1
|
||||
|
||||
_b_add:
|
||||
add r0, r3,r4
|
||||
L2:
|
||||
add r3, r4,r5
|
||||
add r4, r5,r6
|
||||
add r5, r6,r7
|
||||
add r8, r9,r0
|
||||
add r0, r3,r4
|
||||
add r3, r4,r5
|
||||
add r4, r5,r6
|
||||
add r5, r6,r7
|
||||
add r8, r9,r0
|
||||
|
||||
add r0, r3,r4
|
||||
add r3, r4,r5
|
||||
add r4, r5,r6
|
||||
add r5, r6,r7
|
||||
add r8, r9,r0
|
||||
add r0, r3,r4
|
||||
add r3, r4,r5
|
||||
add r4, r5,r6
|
||||
add r5, r6,r7
|
||||
add r8, r9,r0
|
||||
|
||||
subu r2, r2,20 /* Decrement #of iter to go. */
|
||||
bcnd.n gt0,r2,L2
|
||||
add r0, r3,r4
|
||||
|
||||
jmp r1
|
||||
|
||||
|
||||
_b_load:
|
||||
ld r0, r31,0
|
||||
L3:
|
||||
ld r3, r31,4
|
||||
ld r4, r31,8
|
||||
ld r5, r31,12
|
||||
ld r6, r31,16
|
||||
ld r0, r31,0
|
||||
ld r3, r31,4
|
||||
ld r4, r31,8
|
||||
ld r5, r31,12
|
||||
ld r6, r31,16
|
||||
|
||||
ld r0, r31,0
|
||||
ld r3, r31,4
|
||||
ld r4, r31,8
|
||||
ld r5, r31,12
|
||||
ld r6, r31,16
|
||||
ld r0, r31,0
|
||||
ld r3, r31,4
|
||||
ld r4, r31,8
|
||||
ld r5, r31,12
|
||||
ld r6, r31,16
|
||||
|
||||
subu r2, r2,20 /* Decrement #of iter to go. */
|
||||
bcnd.n gt0,r2,L3
|
||||
ld r0, r31,0
|
||||
|
||||
jmp r1
|
182
qt/md/mips-irix5.s
Normal file
182
qt/md/mips-irix5.s
Normal file
|
@ -0,0 +1,182 @@
|
|||
/* mips.s -- assembly support. */
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* Callee-save $16-$23, $30-$31.
|
||||
*
|
||||
* $25 is used as a procedure value pointer, used to discover constants
|
||||
* in a callee. Thus, each caller here sets $25 before the call.
|
||||
*
|
||||
* On startup, restore regs so retpc === call to a function to start.
|
||||
* We're going to call a function ($4) from within this routine.
|
||||
* We're passing 3 args, therefore need to allocate 12 extra bytes on
|
||||
* the stack for a save area. The start routine needs a like 16-byte
|
||||
* save area. Must be doubleword aligned (_mips r3000 risc
|
||||
* architecture_, gerry kane, pg d-23).
|
||||
*/
|
||||
|
||||
/*
|
||||
* Modified by Assar Westerlund <assar@sics.se> to support Irix 5.x
|
||||
* calling conventions for dynamically-linked code.
|
||||
*/
|
||||
|
||||
/* Make this position-independent code. */
|
||||
.option pic2
|
||||
|
||||
.globl qt_block
|
||||
.globl qt_blocki
|
||||
.globl qt_abort
|
||||
.globl qt_start
|
||||
.globl qt_vstart
|
||||
|
||||
/*
|
||||
** $4: ptr to function to call once curr is suspended
|
||||
** and control is on $7's stack.
|
||||
** $5: 1'th arg to $4.
|
||||
** $6: 2'th arg to $4
|
||||
** $7: sp of thread to suspend.
|
||||
**
|
||||
** Totally gross hack: The MIPS calling convention reserves
|
||||
** 4 words on the stack for a0..a3. This routine "ought" to
|
||||
** allocate space for callee-save registers plus 4 words for
|
||||
** the helper function, but instead we use the 4 words
|
||||
** provided by the function that called us (we don't need to
|
||||
** save our argument registers). So what *appears* to be
|
||||
** allocating only 40 bytes is actually allocating 56, by
|
||||
** using the caller's 16 bytes.
|
||||
**
|
||||
** The helper routine returns a value that is passed on as the
|
||||
** return value from the blocking routine. Since we don't
|
||||
** touch $2 between the helper's return and the end of
|
||||
** function, we get this behavior for free.
|
||||
*/
|
||||
qt_blocki:
|
||||
sub $sp,$sp,40 /* Allocate reg save space. */
|
||||
sw $16, 0+16($sp)
|
||||
sw $17, 4+16($sp)
|
||||
sw $18, 8+16($sp)
|
||||
sw $19,12+16($sp)
|
||||
sw $20,16+16($sp)
|
||||
sw $21,20+16($sp)
|
||||
sw $22,24+16($sp)
|
||||
sw $23,28+16($sp)
|
||||
sw $30,32+16($sp)
|
||||
sw $31,36+16($sp)
|
||||
add $2, $sp,$0 /* $2 <= old sp to pass to func@$4. */
|
||||
qt_abort:
|
||||
add $sp, $7,$0 /* $sp <= new sp. */
|
||||
.set noreorder
|
||||
add $25, $4,$0 /* Set helper function procedure value. */
|
||||
jal $31,$25 /* Call helper func@$4 . */
|
||||
add $4, $2,$0 /* $a0 <= pass old sp as a parameter. */
|
||||
.set reorder
|
||||
lw $31,36+16($sp) /* Restore callee-save regs... */
|
||||
lw $30,32+16($sp)
|
||||
lw $23,28+16($sp)
|
||||
lw $22,24+16($sp)
|
||||
lw $21,20+16($sp)
|
||||
lw $20,16+16($sp)
|
||||
lw $19,12+16($sp)
|
||||
lw $18, 8+16($sp)
|
||||
lw $17, 4+16($sp)
|
||||
lw $16, 0+16($sp) /* Restore callee-save */
|
||||
|
||||
add $sp,$sp,40 /* Deallocate reg save space. */
|
||||
j $31 /* Return to caller. */
|
||||
|
||||
/*
|
||||
** Non-varargs thread startup.
|
||||
** Note: originally, 56 bytes were allocated on the stack.
|
||||
** The thread restore routine (_blocki/_abort) removed 40
|
||||
** of them, which means there is still 16 bytes for the
|
||||
** argument area required by the MIPS calling convention.
|
||||
*/
|
||||
qt_start:
|
||||
add $4, $16,$0 /* Load up user function pu. */
|
||||
add $5, $17,$0 /* ... user function pt. */
|
||||
add $6, $18,$0 /* ... user function userf. */
|
||||
add $25, $19,$0 /* Set `only' procedure value. */
|
||||
jal $31,$25 /* Call `only'. */
|
||||
la $25,qt_error /* Set `qt_error' procedure value. */
|
||||
j $25
|
||||
|
||||
|
||||
/*
|
||||
** Save calle-save floating-point regs $f20-$f30
|
||||
** See comment in `qt_block' about calling conventinos and
|
||||
** reserved space. Use the same trick here, but here we
|
||||
** actually have to allocate all the bytes since we have to
|
||||
** leave 4 words leftover for `qt_blocki'.
|
||||
**
|
||||
** Return value from `qt_block' is the same as the return from
|
||||
** `qt_blocki'. We get that for free since we don't touch $2
|
||||
** between the return from `qt_blocki' and the return from
|
||||
** `qt_block'.
|
||||
*/
|
||||
qt_block:
|
||||
sub $sp, $sp,56 /* 6 8-byte regs, saved ret pc, aligned. */
|
||||
swc1 $f20, 0+16($sp)
|
||||
swc1 $f22, 8+16($sp)
|
||||
swc1 $f24, 16+16($sp)
|
||||
swc1 $f26, 24+16($sp)
|
||||
swc1 $f28, 32+16($sp)
|
||||
swc1 $f30, 40+16($sp)
|
||||
sw $31, 48+16($sp)
|
||||
jal qt_blocki
|
||||
lwc1 $f20, 0+16($sp)
|
||||
lwc1 $f22, 8+16($sp)
|
||||
lwc1 $f24, 16+16($sp)
|
||||
lwc1 $f26, 24+16($sp)
|
||||
lwc1 $f28, 32+16($sp)
|
||||
lwc1 $f30, 40+16($sp)
|
||||
lw $31, 48+16($sp)
|
||||
add $sp, $sp,56
|
||||
j $31
|
||||
|
||||
|
||||
/*
|
||||
** First, call `startup' with the `pt' argument.
|
||||
**
|
||||
** Next, call the user's function with all arguments.
|
||||
** Note that we don't know whether args were passed in
|
||||
** integer regs, fp regs, or on the stack (See Gerry Kane
|
||||
** "MIPS R2000 RISC Architecture" pg D-22), so we reload
|
||||
** all the registers, possibly with garbage arguments.
|
||||
**
|
||||
** Finally, call `cleanup' with the `pt' argument and with
|
||||
** the return value from the user's function. It is an error
|
||||
** for `cleanup' to return.
|
||||
*/
|
||||
qt_vstart:
|
||||
add $4, $17,$0 /* `pt' is arg0 to `startup'. */
|
||||
add $25, $18,$0 /* Set `startup' procedure value. */
|
||||
jal $31, $25 /* Call `startup'. */
|
||||
|
||||
add $sp, $sp,16 /* Free extra save space. */
|
||||
lw $4, 0($sp) /* Load up args. */
|
||||
lw $5, 4($sp)
|
||||
lw $6, 8($sp)
|
||||
lw $7, 12($sp)
|
||||
lwc1 $f12, 0($sp) /* Load up fp args. */
|
||||
lwc1 $f14, 8($sp)
|
||||
add $25, $19,$0 /* Set `userf' procedure value. */
|
||||
jal $31,$25 /* Call `userf'. */
|
||||
|
||||
add $4, $17,$0 /* `pt' is arg0 to `cleanup'. */
|
||||
add $5, $2,$0 /* Ret. val is arg1 to `cleanup'. */
|
||||
add $25, $16,$0 /* Set `cleanup' procedure value. */
|
||||
jal $31, $25 /* Call `cleanup'. */
|
||||
|
||||
la $25,qt_error /* Set `qt_error' procedure value. */
|
||||
j $25
|
134
qt/md/mips.h
Normal file
134
qt/md/mips.h
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* 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_MIPS_H
|
||||
#define QT_MIPS_H
|
||||
|
||||
typedef unsigned long qt_word_t;
|
||||
|
||||
#define QT_GROW_DOWN
|
||||
|
||||
/* Stack layout on the mips:
|
||||
|
||||
Callee-save registers are: $16-$23, $30; $f20-$f30.
|
||||
Also save $31, return pc.
|
||||
|
||||
Non-varargs:
|
||||
|
||||
+---
|
||||
| $f30 The first clump is only saved if `qt_block'
|
||||
| $f28 is called, in which case it saves the fp regs
|
||||
| $f26 then calls `qt_blocki' to save the int regs.
|
||||
| $f24
|
||||
| $f22
|
||||
| $f20
|
||||
| $31 === return pc in `qt_block'
|
||||
+---
|
||||
| $31 === return pc; on startup == qt_start
|
||||
| $30
|
||||
| $23
|
||||
| $22
|
||||
| $21
|
||||
| $20
|
||||
| $19 on startup === only
|
||||
| $18 on startup === $a2 === userf
|
||||
| $17 on startup === $a1 === pt
|
||||
| $16 on startup === $a0 === pu
|
||||
| <a3> save area req'd by MIPS calling convention
|
||||
| <a2> save area req'd by MIPS calling convention
|
||||
| <a1> save area req'd by MIPS calling convention
|
||||
| <a0> save area req'd by MIPS calling convention <--- sp
|
||||
+---
|
||||
|
||||
Conventions for varargs:
|
||||
|
||||
| args ...
|
||||
+---
|
||||
| :
|
||||
| :
|
||||
| $21
|
||||
| $20
|
||||
| $19 on startup === `userf'
|
||||
| $18 on startup === `startup'
|
||||
| $17 on startup === `pt'
|
||||
| $16 on startup === `cleanup'
|
||||
| <a3>
|
||||
| <a2>
|
||||
| <a1>
|
||||
| <a0> <--- sp
|
||||
+---
|
||||
|
||||
Note: if we wanted to, we could muck about and try to get the 4
|
||||
argument registers loaded in to, e.g., $22, $23, $30, and $31,
|
||||
and the return pc in, say, $20. Then, the first 4 args would
|
||||
not need to be loaded from memory, they could just use
|
||||
register-to-register copies. */
|
||||
|
||||
|
||||
/* Stack must be doubleword aligned. */
|
||||
#define QT_STKALIGN (8) /* Doubleword aligned. */
|
||||
|
||||
/* How much space is allocated to hold all the crud for
|
||||
initialization: $16-$23, $30, $31. Just do an integer restore,
|
||||
no need to restore floating-point. Four words are needed for the
|
||||
argument save area for the helper function that will be called for
|
||||
the old thread, just before the new thread starts to run. */
|
||||
|
||||
#define QT_STKBASE (14 * 4)
|
||||
#define QT_VSTKBASE QT_STKBASE
|
||||
|
||||
|
||||
/* Offsets of various registers. */
|
||||
#define QT_31 (9+4)
|
||||
#define QT_19 (3+4)
|
||||
#define QT_18 (2+4)
|
||||
#define QT_17 (1+4)
|
||||
#define QT_16 (0+4)
|
||||
|
||||
|
||||
/* When a never-before-run thread is restored, the return pc points
|
||||
to a fragment of code that starts the thread running. For
|
||||
non-vargs functions, it just calls the client's `only' function.
|
||||
For varargs functions, it calls the startup, user, and cleanup
|
||||
functions.
|
||||
|
||||
The varargs startup routine always reads 4 words of arguments from
|
||||
the stack. If there are less than 4 words of arguments, then the
|
||||
startup routine can read off the top of the stack. To prevent
|
||||
errors we always allocate 4 words. If there are more than 3 words
|
||||
of arguments, the 4 preallocated words are simply wasted. */
|
||||
|
||||
extern void qt_start(void);
|
||||
#define QT_ARGS_MD(sp) (QT_SPUT (sp, QT_31, qt_start))
|
||||
|
||||
#define QT_VARGS_MD0(sp, vabytes) \
|
||||
((qt_t *)(((char *)(sp)) - 4*4 - QT_STKROUNDUP(vabytes)))
|
||||
|
||||
extern void qt_vstart(void);
|
||||
#define QT_VARGS_MD1(sp) (QT_SPUT (sp, QT_31, qt_vstart))
|
||||
|
||||
#define QT_VARGS_DEFAULT
|
||||
|
||||
|
||||
/* The *index* (positive offset) of where to put each value. */
|
||||
#define QT_ONLY_INDEX (QT_19)
|
||||
#define QT_USER_INDEX (QT_18)
|
||||
#define QT_ARGT_INDEX (QT_17)
|
||||
#define QT_ARGU_INDEX (QT_16)
|
||||
|
||||
#define QT_VCLEANUP_INDEX (QT_16)
|
||||
#define QT_VUSERF_INDEX (QT_19)
|
||||
#define QT_VSTARTUP_INDEX (QT_18)
|
||||
#define QT_VARGT_INDEX (QT_17)
|
||||
|
||||
#endif /* ndef QT_MIPS_H */
|
164
qt/md/mips.s
Normal file
164
qt/md/mips.s
Normal file
|
@ -0,0 +1,164 @@
|
|||
/* mips.s -- assembly support. */
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* Callee-save $16-$23, $30-$31.
|
||||
*
|
||||
* On startup, restore regs so retpc === call to a function to start.
|
||||
* We're going to call a function ($4) from within this routine.
|
||||
* We're passing 3 args, therefore need to allocate 12 extra bytes on
|
||||
* the stack for a save area. The start routine needs a like 16-byte
|
||||
* save area. Must be doubleword aligned (_mips r3000 risc
|
||||
* architecture_, gerry kane, pg d-23).
|
||||
*/
|
||||
|
||||
.globl qt_block
|
||||
.globl qt_blocki
|
||||
.globl qt_abort
|
||||
.globl qt_start
|
||||
.globl qt_vstart
|
||||
|
||||
/*
|
||||
** $4: ptr to function to call once curr is suspended
|
||||
** and control is on $7's stack.
|
||||
** $5: 1'th arg to $4.
|
||||
** $6: 2'th arg to $4
|
||||
** $7: sp of thread to suspend.
|
||||
**
|
||||
** Totally gross hack: The MIPS calling convention reserves
|
||||
** 4 words on the stack for a0..a3. This routine "ought" to
|
||||
** allocate space for callee-save registers plus 4 words for
|
||||
** the helper function, but instead we use the 4 words
|
||||
** provided by the function that called us (we don't need to
|
||||
** save our argument registers). So what *appears* to be
|
||||
** allocating only 40 bytes is actually allocating 56, by
|
||||
** using the caller's 16 bytes.
|
||||
**
|
||||
** The helper routine returns a value that is passed on as the
|
||||
** return value from the blocking routine. Since we don't
|
||||
** touch $2 between the helper's return and the end of
|
||||
** function, we get this behavior for free.
|
||||
*/
|
||||
qt_blocki:
|
||||
sub $sp,$sp,40 /* Allocate reg save space. */
|
||||
sw $16, 0+16($sp)
|
||||
sw $17, 4+16($sp)
|
||||
sw $18, 8+16($sp)
|
||||
sw $19,12+16($sp)
|
||||
sw $20,16+16($sp)
|
||||
sw $21,20+16($sp)
|
||||
sw $22,24+16($sp)
|
||||
sw $23,28+16($sp)
|
||||
sw $30,32+16($sp)
|
||||
sw $31,36+16($sp)
|
||||
add $2, $sp,$0 /* $2 <= old sp to pass to func@$4. */
|
||||
qt_abort:
|
||||
add $sp, $7,$0 /* $sp <= new sp. */
|
||||
.set noreorder
|
||||
jal $31,$4 /* Call helper func@$4 . */
|
||||
add $4, $2,$0 /* $a0 <= pass old sp as a parameter. */
|
||||
.set reorder
|
||||
lw $31,36+16($sp) /* Restore callee-save regs... */
|
||||
lw $30,32+16($sp)
|
||||
lw $23,28+16($sp)
|
||||
lw $22,24+16($sp)
|
||||
lw $21,20+16($sp)
|
||||
lw $20,16+16($sp)
|
||||
lw $19,12+16($sp)
|
||||
lw $18, 8+16($sp)
|
||||
lw $17, 4+16($sp)
|
||||
lw $16, 0+16($sp) /* Restore callee-save */
|
||||
|
||||
add $sp,$sp,40 /* Deallocate reg save space. */
|
||||
j $31 /* Return to caller. */
|
||||
|
||||
/*
|
||||
** Non-varargs thread startup.
|
||||
** Note: originally, 56 bytes were allocated on the stack.
|
||||
** The thread restore routine (_blocki/_abort) removed 40
|
||||
** of them, which means there is still 16 bytes for the
|
||||
** argument area required by the MIPS calling convention.
|
||||
*/
|
||||
qt_start:
|
||||
add $4, $16,$0 /* Load up user function pu. */
|
||||
add $5, $17,$0 /* ... user function pt. */
|
||||
add $6, $18,$0 /* ... user function userf. */
|
||||
jal $31,$19 /* Call `only'. */
|
||||
j qt_error
|
||||
|
||||
|
||||
/*
|
||||
** Save calle-save floating-point regs $f20-$f30
|
||||
** See comment in `qt_block' about calling conventinos and
|
||||
** reserved space. Use the same trick here, but here we
|
||||
** actually have to allocate all the bytes since we have to
|
||||
** leave 4 words leftover for `qt_blocki'.
|
||||
**
|
||||
** Return value from `qt_block' is the same as the return from
|
||||
** `qt_blocki'. We get that for free since we don't touch $2
|
||||
** between the return from `qt_blocki' and the return from
|
||||
** `qt_block'.
|
||||
*/
|
||||
qt_block:
|
||||
sub $sp, $sp,56 /* 6 8-byte regs, saved ret pc, aligned. */
|
||||
swc1 $f20, 0+16($sp)
|
||||
swc1 $f22, 8+16($sp)
|
||||
swc1 $f24, 16+16($sp)
|
||||
swc1 $f26, 24+16($sp)
|
||||
swc1 $f28, 32+16($sp)
|
||||
swc1 $f30, 40+16($sp)
|
||||
sw $31, 48+16($sp)
|
||||
jal qt_blocki
|
||||
lwc1 $f20, 0+16($sp)
|
||||
lwc1 $f22, 8+16($sp)
|
||||
lwc1 $f24, 16+16($sp)
|
||||
lwc1 $f26, 24+16($sp)
|
||||
lwc1 $f28, 32+16($sp)
|
||||
lwc1 $f30, 40+16($sp)
|
||||
lw $31, 48+16($sp)
|
||||
add $sp, $sp,56
|
||||
j $31
|
||||
|
||||
|
||||
/*
|
||||
** First, call `startup' with the `pt' argument.
|
||||
**
|
||||
** Next, call the user's function with all arguments.
|
||||
** Note that we don't know whether args were passed in
|
||||
** integer regs, fp regs, or on the stack (See Gerry Kane
|
||||
** "MIPS R2000 RISC Architecture" pg D-22), so we reload
|
||||
** all the registers, possibly with garbage arguments.
|
||||
**
|
||||
** Finally, call `cleanup' with the `pt' argument and with
|
||||
** the return value from the user's function. It is an error
|
||||
** for `cleanup' to return.
|
||||
*/
|
||||
qt_vstart:
|
||||
add $4, $17,$0 /* `pt' is arg0 to `startup'. */
|
||||
jal $31, $18 /* Call `startup'. */
|
||||
|
||||
add $sp, $sp,16 /* Free extra save space. */
|
||||
lw $4, 0($sp) /* Load up args. */
|
||||
lw $5, 4($sp)
|
||||
lw $6, 8($sp)
|
||||
lw $7, 12($sp)
|
||||
lwc1 $f12, 0($sp) /* Load up fp args. */
|
||||
lwc1 $f14, 8($sp)
|
||||
jal $31,$19 /* Call `userf'. */
|
||||
|
||||
add $4, $17,$0 /* `pt' is arg0 to `cleanup'. */
|
||||
add $5, $2,$0 /* Ret. val is arg1 to `cleanup'. */
|
||||
jal $31, $16 /* Call `cleanup'. */
|
||||
|
||||
j qt_error
|
99
qt/md/mips_b.s
Normal file
99
qt/md/mips_b.s
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.globl b_call_reg
|
||||
.globl b_call_imm
|
||||
.globl b_add
|
||||
.globl b_load
|
||||
|
||||
.ent b_null
|
||||
b_null:
|
||||
j $31
|
||||
.end b_null
|
||||
|
||||
.ent b_call_reg
|
||||
b_call_reg:
|
||||
la $5,b_null
|
||||
add $6, $31,0
|
||||
$L0:
|
||||
jal $5
|
||||
jal $5
|
||||
jal $5
|
||||
jal $5
|
||||
jal $5
|
||||
|
||||
sub $4, $4,5
|
||||
bgtz $4,$L0
|
||||
j $6
|
||||
.end
|
||||
|
||||
|
||||
.ent b_call_imm
|
||||
b_call_imm:
|
||||
add $6, $31,0
|
||||
$L1:
|
||||
jal b_null
|
||||
jal b_null
|
||||
jal b_null
|
||||
jal b_null
|
||||
jal b_null
|
||||
|
||||
sub $4, $4,5
|
||||
bgtz $4,$L1
|
||||
j $6
|
||||
.end
|
||||
|
||||
|
||||
.ent b_add
|
||||
b_add:
|
||||
add $5, $0,$4
|
||||
add $6, $0,$4
|
||||
add $7, $0,$4
|
||||
add $8, $0,$4
|
||||
$L2:
|
||||
sub $4, $4,5
|
||||
sub $5, $5,5
|
||||
sub $6, $6,5
|
||||
sub $7, $7,5
|
||||
sub $8, $8,5
|
||||
|
||||
sub $4, $4,5
|
||||
sub $5, $5,5
|
||||
sub $6, $6,5
|
||||
sub $7, $7,5
|
||||
sub $8, $8,5
|
||||
|
||||
bgtz $4,$L2
|
||||
j $31
|
||||
.end
|
||||
|
||||
|
||||
.ent b_load
|
||||
b_load:
|
||||
$L3:
|
||||
ld $0, 0($sp)
|
||||
ld $0, 4($sp)
|
||||
ld $0, 8($sp)
|
||||
ld $0, 12($sp)
|
||||
ld $0, 16($sp)
|
||||
|
||||
ld $0, 20($sp)
|
||||
ld $0, 24($sp)
|
||||
ld $0, 28($sp)
|
||||
ld $0, 32($sp)
|
||||
ld $0, 36($sp)
|
||||
|
||||
sub $4, $4,10
|
||||
bgtz $4,$L3
|
||||
j $31
|
||||
.end
|
0
qt/md/null.README
Normal file
0
qt/md/null.README
Normal file
14
qt/md/null.c
Normal file
14
qt/md/null.c
Normal file
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
char const qtmd_rcsid[] = "$Header: /home/ludo/src/guile.cvs/gitification/guile-cvs/guile/guile-core/qt/md/null.c,v 1.1 1996-10-01 03:34:16 mdj Exp $";
|
19
qt/md/solaris.README
Normal file
19
qt/md/solaris.README
Normal file
|
@ -0,0 +1,19 @@
|
|||
Solaris 2.x is like System V (maybe it *is* System V?) and is different
|
||||
from older versions in that it uses no leading underscore for variable
|
||||
and function names. That is, the old convention was:
|
||||
|
||||
foo(){}
|
||||
|
||||
got compiled as
|
||||
|
||||
.globl _foo
|
||||
_foo:
|
||||
|
||||
and is now compiled as
|
||||
|
||||
.globl foo
|
||||
foo:
|
||||
|
||||
The `config' script should fix up the older (leading underscore) versions
|
||||
of the machine-dependent files to use the newer (no leading underscore)
|
||||
calling conventions.
|
140
qt/md/sparc.h
Normal file
140
qt/md/sparc.h
Normal file
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* 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_SPARC_H
|
||||
#define QT_SPARC_H
|
||||
|
||||
typedef unsigned long qt_word_t;
|
||||
|
||||
/* Stack layout on the sparc:
|
||||
|
||||
non-varargs:
|
||||
|
||||
+---
|
||||
| <blank space for alignment>
|
||||
| %o7 == return address -> qt_start
|
||||
| %i7
|
||||
| %i6 == frame pointer -> 0 (NULL-terminated stack frame chain)
|
||||
| %i5 -> only
|
||||
| %i4 -> userf
|
||||
| %i3
|
||||
| %i2 -> pt
|
||||
| %i1 -> pu
|
||||
| %i0
|
||||
| %l7
|
||||
| %l6
|
||||
| %l5
|
||||
| %l4
|
||||
| %l3
|
||||
| %l2
|
||||
| %l1
|
||||
| %l0 <--- qt_t.sp
|
||||
+---
|
||||
|
||||
varargs:
|
||||
|
||||
| :
|
||||
| :
|
||||
| argument list
|
||||
| one-word aggregate return pointer
|
||||
+---
|
||||
| <blank space for alignment>
|
||||
| %o7 == return address -> qt_vstart
|
||||
| %i7
|
||||
| %i6 == frame pointer -> 0 (NULL-terminated stack frame chain)
|
||||
| %i5 -> startup
|
||||
| %i4 -> userf
|
||||
| %i3 -> cleanup
|
||||
| %i2 -> pt
|
||||
| %i1
|
||||
| %i0
|
||||
| %l7
|
||||
| %l6
|
||||
| %l5
|
||||
| %l4
|
||||
| %l3
|
||||
| %l2
|
||||
| %l1
|
||||
| %l0 <--- qt_t.sp
|
||||
+---
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/* What to do to start a thread running. */
|
||||
extern void qt_start (void);
|
||||
extern void qt_vstart (void);
|
||||
|
||||
|
||||
/* Hold 17 saved registers + 1 word for alignment. */
|
||||
#define QT_STKBASE (18 * 4)
|
||||
#define QT_VSTKBASE QT_STKBASE
|
||||
|
||||
|
||||
/* Stack must be doubleword aligned. */
|
||||
#define QT_STKALIGN (8) /* Doubleword aligned. */
|
||||
|
||||
#define QT_ONLY_INDEX (QT_I5)
|
||||
#define QT_USER_INDEX (QT_I4)
|
||||
#define QT_ARGT_INDEX (QT_I2)
|
||||
#define QT_ARGU_INDEX (QT_I1)
|
||||
|
||||
#define QT_VSTARTUP_INDEX (QT_I5)
|
||||
#define QT_VUSERF_INDEX (QT_I4)
|
||||
#define QT_VCLEANUP_INDEX (QT_I3)
|
||||
#define QT_VARGT_INDEX (QT_I2)
|
||||
|
||||
#define QT_O7 (16)
|
||||
#define QT_I6 (14)
|
||||
#define QT_I5 (13)
|
||||
#define QT_I4 (12)
|
||||
#define QT_I3 (11)
|
||||
#define QT_I2 (10)
|
||||
#define QT_I1 ( 9)
|
||||
|
||||
|
||||
/* The thread will ``return'' to the `qt_start' routine to get things
|
||||
going. The normal return sequence takes us to QT_O7+8, so we
|
||||
pre-subtract 8. The frame pointer chain is 0-terminated to prevent
|
||||
the trap handler from chasing off in to random memory when flushing
|
||||
stack windows. */
|
||||
|
||||
#define QT_ARGS_MD(top) \
|
||||
(QT_SPUT ((top), QT_O7, ((void *)(((int)qt_start)-8))), \
|
||||
QT_SPUT ((top), QT_I6, 0))
|
||||
|
||||
|
||||
/* The varargs startup routine always reads 6 words of arguments
|
||||
(6 argument registers) from the stack, offset by one word to
|
||||
allow for an aggregate return area pointer. If the varargs
|
||||
routine actually pushed fewer words than that, qt_vstart could read
|
||||
off the top of the stack. To prevent errors, we always allocate 8
|
||||
words. The space is often just wasted. */
|
||||
|
||||
#define QT_VARGS_MD0(sp, vabytes) \
|
||||
((qt_t *)(((char *)(sp)) - 8*4 - QT_STKROUNDUP(vabytes)))
|
||||
|
||||
#define QT_VARGS_MD1(sp) \
|
||||
(QT_SPUT (sp, QT_O7, ((void *)(((int)qt_vstart)-8))))
|
||||
|
||||
/* The SPARC has wierdo calling conventions which stores a hidden
|
||||
parameter for returning aggregate values, so the rest of the
|
||||
parameters are shoved up the stack by one place. */
|
||||
#define QT_VARGS_ADJUST(sp) (((char *)sp)+4)
|
||||
|
||||
#define QT_VARGS_DEFAULT
|
||||
|
||||
|
||||
#define QT_GROW_DOWN
|
||||
|
||||
#endif /* ndef QT_SPARC_H */
|
142
qt/md/sparc.s
Normal file
142
qt/md/sparc.s
Normal file
|
@ -0,0 +1,142 @@
|
|||
/* sparc.s -- assembly support for the `qt' thread building kit. */
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* #include <machine/trap.h> */
|
||||
|
||||
.text
|
||||
.align 4
|
||||
.global qt_blocki
|
||||
.global qt_block
|
||||
.global qt_abort
|
||||
.global qt_start
|
||||
.global qt_vstart
|
||||
|
||||
/* Register assignment:
|
||||
// %o0: incoming `helper' function to call after cswap
|
||||
// also used as outgoing sp of old thread (qt_t *)
|
||||
// %o1, %o2:
|
||||
// parameters to `helper' function called after cswap
|
||||
// %o3: sp of new thread
|
||||
// %o5: tmp used to save old thread sp, while using %o0
|
||||
// to call `helper' f() after cswap.
|
||||
//
|
||||
//
|
||||
// Aborting a thread is easy if there are no cached register window
|
||||
// frames: just switch to the new stack and away we go. If there are
|
||||
// cached register window frames they must all be written back to the
|
||||
// old stack before we move to the new stack. If we fail to do the
|
||||
// writeback then the old stack memory can be written with register
|
||||
// window contents e.g., after the stack memory has been freed and
|
||||
// reused.
|
||||
//
|
||||
// If you don't believe this, try setting the frame pointer to zero
|
||||
// once we're on the new stack. This will not affect correctnes
|
||||
// otherwise because the frame pointer will eventually get reloaded w/
|
||||
// the new thread's frame pointer. But it will be zero briefly before
|
||||
// the reload. You will eventually (100,000 cswaps later on a small
|
||||
// SPARC machine that I tried) get an illegal instruction trap from
|
||||
// the kernel trying to flush a cached window to location 0x0.
|
||||
//
|
||||
// Solution: flush windows before switching stacks, which invalidates
|
||||
// all the other register windows. We could do the trap
|
||||
// conditionally: if we're in the lowest frame of a thread, the fp is
|
||||
// zero already so we know there's nothing cached. But we expect most
|
||||
// aborts will be done from a first function that does a `save', so we
|
||||
// will rarely save anything and always pay the cost of testing to see
|
||||
// if we should flush.
|
||||
//
|
||||
// All floating-point registers are caller-save, so this routine
|
||||
// doesn't need to do anything to save and restore them.
|
||||
//
|
||||
// `qt_block' and `qt_blocki' return the same value as the value
|
||||
// returned by the helper function. We get this ``for free''
|
||||
// since we don't touch the return value register between the
|
||||
// return from the helper function and return from qt_block{,i}.
|
||||
*/
|
||||
|
||||
qt_block:
|
||||
qt_blocki:
|
||||
sub %sp, 8, %sp /* Allocate save area for return pc. */
|
||||
st %o7, [%sp+64] /* Save return pc. */
|
||||
qt_abort:
|
||||
ta 0x03 /* Save locals and ins. */
|
||||
mov %sp, %o5 /* Remember old sp w/o chng ins/locals. */
|
||||
sub %o3, 64, %sp /* Allocate kwsa, switch stacks. */
|
||||
call %o0, 0 /* Call `helper' routine. */
|
||||
mov %o5, %o0 /* Pass old thread to qt_after_t() */
|
||||
/* .. along w/ args in %o1 & %o2. */
|
||||
|
||||
/* Restore callee-save regs. The kwsa
|
||||
// is on this stack, so offset all
|
||||
// loads by sizeof(kwsa), 64 bytes.
|
||||
*/
|
||||
ldd [%sp+ 0+64], %l0
|
||||
ldd [%sp+ 8+64], %l2
|
||||
ldd [%sp+16+64], %l4
|
||||
ldd [%sp+24+64], %l6
|
||||
ldd [%sp+32+64], %i0
|
||||
ldd [%sp+40+64], %i2
|
||||
ldd [%sp+48+64], %i4
|
||||
ldd [%sp+56+64], %i6
|
||||
ld [%sp+64+64], %o7 /* Restore return pc. */
|
||||
|
||||
retl /* Return to address in %o7. */
|
||||
add %sp, 72, %sp /* Deallocate kwsa, ret pc area. */
|
||||
|
||||
|
||||
/* The function calling conventions say there has to be a 1-word area
|
||||
// in the caller's stack to hold a pointer to space for aggregate
|
||||
// return values. It also says there should be a 6-word area to hold
|
||||
// %o0..%o5 if the callee wants to save them (why? I don't know...)
|
||||
// Round up to 8 words to maintain alignment.
|
||||
//
|
||||
// Parameter values were stored in callee-save regs and are moved to
|
||||
// the parameter registers.
|
||||
*/
|
||||
qt_start:
|
||||
mov %i1, %o0 /* `pu': Set up args to `only'. */
|
||||
mov %i2, %o1 /* `pt'. */
|
||||
mov %i4, %o2 /* `userf'. */
|
||||
call %i5, 0 /* Call client function. */
|
||||
sub %sp, 32, %sp /* Allocate 6-word callee space. */
|
||||
|
||||
call qt_error, 0 /* `only' erroniously returned. */
|
||||
nop
|
||||
|
||||
|
||||
/* Same comments as `qt_start' about allocating rounded-up 7-word
|
||||
// save areas. */
|
||||
|
||||
qt_vstart:
|
||||
sub %sp, 32, %sp /* Allocate 7-word callee space. */
|
||||
call %i5, 0 /* call `startup'. */
|
||||
mov %i2, %o0 /* .. with argument `pt'. */
|
||||
|
||||
add %sp, 32, %sp /* Use 7-word space in varargs. */
|
||||
ld [%sp+ 4+64], %o0 /* Load arg0 ... */
|
||||
ld [%sp+ 8+64], %o1
|
||||
ld [%sp+12+64], %o2
|
||||
ld [%sp+16+64], %o3
|
||||
ld [%sp+20+64], %o4
|
||||
call %i4, 0 /* Call `userf'. */
|
||||
ld [%sp+24+64], %o5
|
||||
|
||||
/* Use 6-word space in varargs. */
|
||||
mov %o0, %o1 /* Pass return value from userf */
|
||||
call %i3, 0 /* .. when call `cleanup. */
|
||||
mov %i2, %o0 /* .. along with argument `pt'. */
|
||||
|
||||
call qt_error, 0 /* `cleanup' erroniously returned. */
|
||||
nop
|
106
qt/md/sparc_b.s
Normal file
106
qt/md/sparc_b.s
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.globl b_call_reg
|
||||
.globl b_call_imm
|
||||
.globl b_add
|
||||
.globl b_load
|
||||
|
||||
b_null:
|
||||
retl
|
||||
nop
|
||||
|
||||
b_call_reg:
|
||||
sethi %hi(b_null),%o4
|
||||
or %o4,%lo(b_null),%o4
|
||||
add %o7,%g0, %o3
|
||||
L0:
|
||||
call %o4
|
||||
nop
|
||||
call %o4
|
||||
nop
|
||||
call %o4
|
||||
nop
|
||||
call %o4
|
||||
nop
|
||||
call %o4
|
||||
nop
|
||||
|
||||
subcc %o0,1,%o0
|
||||
bg L0
|
||||
nop
|
||||
add %o3,%g0, %o7
|
||||
retl
|
||||
nop
|
||||
|
||||
b_call_imm:
|
||||
sethi %hi(b_null),%o4
|
||||
or %o4,%lo(b_null),%o4
|
||||
add %o7,%g0, %o3
|
||||
L1:
|
||||
call b_null
|
||||
call b_null
|
||||
call b_null
|
||||
call b_null
|
||||
call b_null
|
||||
|
||||
subcc %o0,1,%o0
|
||||
bg L0
|
||||
nop
|
||||
add %o3,%g0, %o7
|
||||
retl
|
||||
nop
|
||||
|
||||
|
||||
b_add:
|
||||
add %o0,%g0,%o1
|
||||
add %o0,%g0,%o2
|
||||
add %o0,%g0,%o3
|
||||
add %o0,%g0,%o4
|
||||
L2:
|
||||
sub %o0,5,%o0
|
||||
sub %o1,5,%o1
|
||||
sub %o2,5,%o2
|
||||
sub %o3,5,%o3
|
||||
sub %o4,5,%o4
|
||||
|
||||
subcc %o0,5,%o0
|
||||
sub %o1,5,%o1
|
||||
sub %o2,5,%o2
|
||||
sub %o3,5,%o3
|
||||
sub %o4,5,%o4
|
||||
|
||||
bg L2
|
||||
nop
|
||||
retl
|
||||
nop
|
||||
|
||||
|
||||
b_load:
|
||||
ld [%sp+ 0], %g0
|
||||
L3:
|
||||
ld [%sp+ 4],%g0
|
||||
ld [%sp+ 8],%g0
|
||||
ld [%sp+12],%g0
|
||||
ld [%sp+16],%g0
|
||||
ld [%sp+20],%g0
|
||||
ld [%sp+24],%g0
|
||||
ld [%sp+28],%g0
|
||||
ld [%sp+32],%g0
|
||||
ld [%sp+36],%g0
|
||||
|
||||
subcc %o0,10,%o0
|
||||
bg L3
|
||||
ld [%sp+ 0],%g0
|
||||
retl
|
||||
nop
|
130
qt/md/vax.h
Normal file
130
qt/md/vax.h
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* 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_VAX_H
|
||||
#define QT_VAX_H
|
||||
|
||||
typedef unsigned long qt_word_t;
|
||||
|
||||
/* Thread's initial stack layout on the VAX:
|
||||
|
||||
non-varargs:
|
||||
|
||||
+---
|
||||
| arg[2] === `userf' on startup
|
||||
| arg[1] === `pt' on startup
|
||||
| arg[0] === `pu' on startup
|
||||
| ... === `only' on startup.
|
||||
+---
|
||||
| ret pc === `qt_start' on startup
|
||||
| fp === 0 on startup
|
||||
| ap === 0 on startup
|
||||
| <mask>
|
||||
| 0 (handler) <--- qt_t.sp
|
||||
+---
|
||||
|
||||
When a non-varargs thread is started, it ``returns'' to the start
|
||||
routine, which calls the client's `only' function.
|
||||
|
||||
The varargs case is clearly bad code. The various values should be
|
||||
stored in a save area and snarfed in to callee-save registers on
|
||||
startup. However, it's too painful to figure out the register
|
||||
mask (right now), so do it the slow way.
|
||||
|
||||
+---
|
||||
| arg[n-1]
|
||||
| ..
|
||||
| arg[0]
|
||||
| nargs
|
||||
+---
|
||||
| === `cleanup'
|
||||
| === `vuserf'
|
||||
| === `startup'
|
||||
| === `pt'
|
||||
+---
|
||||
| ret pc === `qt_start' on startup
|
||||
| fp === 0 on startup
|
||||
| ap === 0 on startup
|
||||
| <mask>
|
||||
| 0 (handler) <--- qt_t.sp
|
||||
+---
|
||||
|
||||
When a varargs thread is started, it ``returns'' to the `qt_vstart'
|
||||
startup code. The startup code pops all the extra arguments, then
|
||||
calls the appropriate functions. */
|
||||
|
||||
|
||||
/* What to do to start a thread running. */
|
||||
extern void qt_start (void);
|
||||
extern void qt_vstart (void);
|
||||
|
||||
|
||||
/* Initial call frame for non-varargs and varargs cases. */
|
||||
#define QT_STKBASE (10 * 4)
|
||||
#define QT_VSTKBASE (9 * 4)
|
||||
|
||||
|
||||
/* Stack "must be" 4-byte aligned. (Actually, no, but it's
|
||||
easiest and probably fastest to do so.) */
|
||||
|
||||
#define QT_STKALIGN (4)
|
||||
|
||||
|
||||
/* Where to place various arguments. */
|
||||
#define QT_ONLY_INDEX (5)
|
||||
#define QT_USER_INDEX (8)
|
||||
#define QT_ARGT_INDEX (7)
|
||||
#define QT_ARGU_INDEX (6)
|
||||
|
||||
#define QT_VSTARTUP_INDEX (6)
|
||||
#define QT_VUSERF_INDEX (7)
|
||||
#define QT_VCLEANUP_INDEX (8)
|
||||
#define QT_VARGT_INDEX (5)
|
||||
|
||||
|
||||
/* Stack grows down. The top of the stack is the first thing to
|
||||
pop off (predecrement, postincrement). */
|
||||
#define QT_GROW_DOWN
|
||||
|
||||
|
||||
extern void qt_error (void);
|
||||
|
||||
#define QT_VAX_GMASK_NOREGS (0)
|
||||
|
||||
/* Push on the error return address, null termination to call chains,
|
||||
number of arguments to `only', register save mask (save no
|
||||
registers). */
|
||||
|
||||
#define QT_ARGS_MD(sto) \
|
||||
(QT_SPUT (sto, 0, 0), \
|
||||
QT_SPUT (sto, 1, QT_VAX_GMASK_NOREGS), \
|
||||
QT_SPUT (sto, 2, 0), \
|
||||
QT_SPUT (sto, 3, 0), \
|
||||
QT_SPUT (sto, 4, qt_start))
|
||||
|
||||
#define QT_VARGS_MD0(sto, nbytes) \
|
||||
(QT_SPUT (sto, (-(nbytes)/4)-1, (nbytes)/4), \
|
||||
((char *)(((sto)-4) - QT_STKROUNDUP(nbytes))))
|
||||
|
||||
#define QT_VARGS_ADJUST(sp) ((char *)sp + 4)
|
||||
|
||||
#define QT_VARGS_MD1(sto) \
|
||||
(QT_SPUT (sto, 0, 0), \
|
||||
QT_SPUT (sto, 1, QT_VAX_GMASK_NOREGS), \
|
||||
QT_SPUT (sto, 2, 0), \
|
||||
QT_SPUT (sto, 3, 0), \
|
||||
QT_SPUT (sto, 4, qt_vstart))
|
||||
|
||||
#define QT_VARGS_DEFAULT
|
||||
|
||||
#endif /* QT_VAX_H */
|
69
qt/md/vax.s
Normal file
69
qt/md/vax.s
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.text
|
||||
|
||||
.globl _qt_abort
|
||||
.globl _qt_block
|
||||
.globl _qt_blocki
|
||||
.globl _qt_start
|
||||
.globl _qt_vstart
|
||||
|
||||
|
||||
/*
|
||||
// Calls to these routines have the signature
|
||||
//
|
||||
// void *block (func, arg1, arg2, newsp)
|
||||
//
|
||||
// Since the prologue saves 5 registers, nargs, pc, fp, ap, mask, and
|
||||
// a condition handler (at sp+0), the first argument is 40=4*10 bytes
|
||||
// offset from the stack pointer.
|
||||
*/
|
||||
_qt_block:
|
||||
_qt_blocki:
|
||||
_qt_abort:
|
||||
.word 0x7c0 /* Callee-save mask: 5 registers. */
|
||||
movl 56(sp),r1 /* Get stack pointer of new thread. */
|
||||
movl 52(sp),-(r1) /* Push arg2 */
|
||||
movl 48(sp),-(r1) /* Push arg1 */
|
||||
movl sp,-(r1) /* Push arg0 */
|
||||
|
||||
movl 44(sp),r0 /* Get helper to call. */
|
||||
movl r1,sp /* Move to new thread's stack. */
|
||||
addl3 sp,$12,fp /* .. including the frame pointer. */
|
||||
calls $3,(r0) /* Call helper. */
|
||||
|
||||
ret
|
||||
|
||||
_qt_start:
|
||||
movl (sp)+,r0 /* Get `only'. */
|
||||
calls $3,(r0) /* Call `only'. */
|
||||
calls $0,_qt_error /* `only' erroniously returned. */
|
||||
|
||||
|
||||
_qt_vstart:
|
||||
movl (sp)+,r10 /* Get `pt'. */
|
||||
movl (sp)+,r9 /* Get `startup'. */
|
||||
movl (sp)+,r8 /* Get `vuserf'. */
|
||||
movl (sp)+,r7 /* Get `cleanup'. */
|
||||
|
||||
pushl r10 /* Push `qt'. */
|
||||
calls $1,(r9) /* Call `startup', pop `qt' on return. */
|
||||
|
||||
calls (sp)+,(r8) /* Call user's function. */
|
||||
|
||||
pushl r0 /* Push `vuserf_retval'. */
|
||||
pushl r10 /* Push `qt'. */
|
||||
calls $2,(r7) /* Call `cleanup', never return. */
|
||||
|
||||
calls $0,_qt_error /* `cleanup' erroniously returned. */
|
92
qt/md/vax_b.s
Normal file
92
qt/md/vax_b.s
Normal file
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.text
|
||||
.globl _b_call_reg
|
||||
.globl _b_call_imm
|
||||
.globl _b_add
|
||||
.globl _b_load
|
||||
|
||||
_b_null:
|
||||
.word 0x0
|
||||
ret
|
||||
|
||||
_b_call_reg:
|
||||
.word 0x0
|
||||
movl 4(ap),r0
|
||||
moval _b_null,r1
|
||||
L0:
|
||||
calls $0,(r1)
|
||||
calls $0,(r1)
|
||||
calls $0,(r1)
|
||||
calls $0,(r1)
|
||||
calls $0,(r1)
|
||||
|
||||
subl2 $5,r0
|
||||
bgtr L0
|
||||
ret
|
||||
|
||||
|
||||
_b_call_imm:
|
||||
.word 0x0
|
||||
movl 4(ap),r0
|
||||
L1:
|
||||
calls $0,_b_null
|
||||
calls $0,_b_null
|
||||
calls $0,_b_null
|
||||
calls $0,_b_null
|
||||
calls $0,_b_null
|
||||
|
||||
subl2 $5,r0
|
||||
bgtr L1
|
||||
ret
|
||||
|
||||
|
||||
_b_add:
|
||||
.word 0x0
|
||||
movl 4(ap),r0
|
||||
L2:
|
||||
subl2 $1,r0
|
||||
subl2 $1,r0
|
||||
subl2 $1,r0
|
||||
subl2 $1,r0
|
||||
subl2 $1,r0
|
||||
|
||||
subl2 $1,r0
|
||||
subl2 $1,r0
|
||||
subl2 $1,r0
|
||||
subl2 $1,r0
|
||||
subl2 $1,r0
|
||||
|
||||
bgtr L2
|
||||
ret
|
||||
|
||||
|
||||
_b_load:
|
||||
.word 0x0
|
||||
movl 4(ap),r0
|
||||
L3:
|
||||
movl 0(sp),r1
|
||||
movl 4(sp),r1
|
||||
movl 8(sp),r1
|
||||
movl 12(sp),r1
|
||||
movl 16(sp),r1
|
||||
movl 20(sp),r1
|
||||
movl 24(sp),r1
|
||||
movl 28(sp),r1
|
||||
movl 32(sp),r1
|
||||
movl 36(sp),r1
|
||||
|
||||
subl2 $1,r0
|
||||
bgtr L3
|
||||
ret
|
17
qt/time/README.time
Normal file
17
qt/time/README.time
Normal file
|
@ -0,0 +1,17 @@
|
|||
The program `raw', when run in `..' runs the program `run' produced
|
||||
from `meas.c'. It produces a raw output file (see `../tmp/*.raw').
|
||||
`raw' will die with an error if run in the current directory. Note
|
||||
that some versions of `time' produce output in an unexpected format;
|
||||
edit them by hand.
|
||||
|
||||
`prim', `init', `cswap' and `go' produce formatted table entries used
|
||||
in the documentation (in `../doc'). For example, from `..',
|
||||
|
||||
foreach i (tmp/*.raw)
|
||||
time/prim $i
|
||||
end
|
||||
|
||||
See notes in the QuickThreads document about the applicability of
|
||||
these microbenchmark measurements -- in general, you can expect all
|
||||
QuickThreads operations to be a bit slower when used in a real
|
||||
application.
|
42
qt/time/assim
Executable file
42
qt/time/assim
Executable file
|
@ -0,0 +1,42 @@
|
|||
#! /bin/awk -f
|
||||
|
||||
BEGIN {
|
||||
nmach = 0;
|
||||
|
||||
init_test = "1";
|
||||
abort_test = "6";
|
||||
blocki_test = "7";
|
||||
block_test = "8";
|
||||
}
|
||||
|
||||
{
|
||||
mach = $1
|
||||
test = $2
|
||||
iter = $3
|
||||
time = $6 + $8
|
||||
|
||||
if (machi[mach] == 0) {
|
||||
machn[nmach] = mach;
|
||||
machi[mach] = 1;
|
||||
++nmach;
|
||||
}
|
||||
|
||||
us_per_op = time / iter * 1000000
|
||||
times[mach "_" test] = us_per_op;
|
||||
}
|
||||
|
||||
|
||||
END {
|
||||
for (i=0; i<nmach; ++i) {
|
||||
m = machn[i];
|
||||
init = times[m "_" init_test];
|
||||
printf ("init %s | %f\n", m, init);
|
||||
|
||||
init_abort_blocki = times[m "_" abort_test];
|
||||
abort_blocki = init_abort_blocki - init;
|
||||
blocki = times[m "_" blocki_test];
|
||||
abort = abort_blocki - blocki;
|
||||
blockf = times[m "_" block_test];
|
||||
printf ("swap %s | %f | %f | %f\n", m, abort, blocki, blockf);
|
||||
}
|
||||
}
|
37
qt/time/cswap
Executable file
37
qt/time/cswap
Executable file
|
@ -0,0 +1,37 @@
|
|||
#! /bin/awk -f
|
||||
|
||||
BEGIN {
|
||||
purpose = "report time used by int only and int+fp cswaps";
|
||||
|
||||
nmach = 0;
|
||||
|
||||
test_int = "7";
|
||||
test_fp = "8";
|
||||
}
|
||||
|
||||
{
|
||||
mach = $1
|
||||
test = $2
|
||||
iter = $3
|
||||
time = $6 + $8
|
||||
|
||||
if (machi[mach] == 0) {
|
||||
machn[nmach] = mach;
|
||||
machi[mach] = 1;
|
||||
++nmach;
|
||||
}
|
||||
|
||||
us_per_op = time / iter * 1000000
|
||||
times[mach "_" test] = us_per_op;
|
||||
}
|
||||
|
||||
|
||||
END {
|
||||
for (i=0; i<nmach; ++i) {
|
||||
m = machn[i];
|
||||
|
||||
integer = times[m "_" test_int];
|
||||
fp = times[m "_" test_fp];
|
||||
printf ("%s|%3.1f|%3.1f\n", m, integer, fp);
|
||||
}
|
||||
}
|
43
qt/time/go
Executable file
43
qt/time/go
Executable file
|
@ -0,0 +1,43 @@
|
|||
#! /bin/awk -f
|
||||
|
||||
BEGIN {
|
||||
purpose = "report times used for init/start/stop";
|
||||
|
||||
nmach = 0;
|
||||
|
||||
test_single = "6";
|
||||
test_v0 = "10";
|
||||
test_v2 = "11";
|
||||
test_v4 = "12";
|
||||
test_v8 = "13";
|
||||
}
|
||||
|
||||
{
|
||||
mach = $1
|
||||
test = $2
|
||||
iter = $3
|
||||
time = $6 + $8
|
||||
|
||||
if (machi[mach] == 0) {
|
||||
machn[nmach] = mach;
|
||||
machi[mach] = 1;
|
||||
++nmach;
|
||||
}
|
||||
|
||||
us_per_op = time / iter * 1000000
|
||||
times[mach "_" test] = us_per_op;
|
||||
}
|
||||
|
||||
|
||||
END {
|
||||
for (i=0; i<nmach; ++i) {
|
||||
m = machn[i];
|
||||
|
||||
single = times[m "_" test_single];
|
||||
v0 = times[m "_" test_v0];
|
||||
v2 = times[m "_" test_v2];
|
||||
v4 = times[m "_" test_v4];
|
||||
v8 = times[m "_" test_v8];
|
||||
printf ("%s|%3.1f|%3.1f|%3.1f|%3.1f|%3.1f\n", m, single, v0, v2, v4, v8);
|
||||
}
|
||||
}
|
42
qt/time/init
Executable file
42
qt/time/init
Executable file
|
@ -0,0 +1,42 @@
|
|||
#! /bin/awk -f
|
||||
|
||||
BEGIN {
|
||||
purpose = "Report time used to initialize a thread."
|
||||
nmach = 0;
|
||||
|
||||
test_single = "1";
|
||||
test_v0 = "14";
|
||||
test_v2 = "15";
|
||||
test_v4 = "16";
|
||||
test_v8 = "17";
|
||||
}
|
||||
|
||||
{
|
||||
mach = $1
|
||||
test = $2
|
||||
iter = $3
|
||||
time = $6 + $8
|
||||
|
||||
if (machi[mach] == 0) {
|
||||
machn[nmach] = mach;
|
||||
machi[mach] = 1;
|
||||
++nmach;
|
||||
}
|
||||
|
||||
us_per_op = time / iter * 1000000
|
||||
times[mach "_" test] = us_per_op;
|
||||
}
|
||||
|
||||
|
||||
END {
|
||||
for (i=0; i<nmach; ++i) {
|
||||
m = machn[i];
|
||||
|
||||
single = times[m "_" test_single];
|
||||
v0 = times[m "_" test_v0];
|
||||
v2 = times[m "_" test_v2];
|
||||
v4 = times[m "_" test_v4];
|
||||
v8 = times[m "_" test_v8];
|
||||
printf ("%s|%3.1f|%3.1f|%3.1f|%3.1f|%3.1f\n", m, single, v0, v2, v4, v8);
|
||||
}
|
||||
}
|
41
qt/time/prim
Executable file
41
qt/time/prim
Executable file
|
@ -0,0 +1,41 @@
|
|||
#! /bin/awk -f
|
||||
|
||||
BEGIN {
|
||||
purpose = "report times for microbenchmarks"
|
||||
|
||||
nmach = 0;
|
||||
|
||||
test_callind = "18";
|
||||
test_callimm = "18";
|
||||
test_addreg = "20";
|
||||
test_loadreg = "21";
|
||||
}
|
||||
|
||||
{
|
||||
mach = $1
|
||||
test = $2
|
||||
iter = $3
|
||||
time = $6 + $8
|
||||
|
||||
if (machi[mach] == 0) {
|
||||
machn[nmach] = mach;
|
||||
machi[mach] = 1;
|
||||
++nmach;
|
||||
}
|
||||
|
||||
ns_per_op = time / iter * 1000000
|
||||
times[mach "_" test] = ns_per_op;
|
||||
}
|
||||
|
||||
|
||||
END {
|
||||
for (i=0; i<nmach; ++i) {
|
||||
m = machn[i];
|
||||
|
||||
ind = times[m "_" test_callind];
|
||||
imm = times[m "_" test_callimm];
|
||||
add = times[m "_" test_addreg];
|
||||
load = times[m "_" test_loadreg];
|
||||
printf ("%s|%1.3f|%1.3f|%1.3f|%1.3f\n", m, ind, imm, add, load);
|
||||
}
|
||||
}
|
58
qt/time/raw
Executable file
58
qt/time/raw
Executable file
|
@ -0,0 +1,58 @@
|
|||
#! /bin/csh
|
||||
|
||||
rm -f timed
|
||||
|
||||
set init=1
|
||||
set runone=6
|
||||
set blockint=7
|
||||
set blockfloat=8
|
||||
set vainit0=14
|
||||
set vainit2=15
|
||||
set vainit4=16
|
||||
set vainit8=17
|
||||
set vastart0=10
|
||||
set vastart2=11
|
||||
set vastart4=12
|
||||
set vastart8=13
|
||||
set bench_regcall=18
|
||||
set bench_immcall=19
|
||||
set bench_add=20
|
||||
set bench_load=21
|
||||
|
||||
source configuration
|
||||
|
||||
echo -n $config_machine $init $config_init
|
||||
/bin/time run $init $config_init
|
||||
echo -n $config_machine $runone $config_runone
|
||||
/bin/time run $runone $config_runone
|
||||
echo -n $config_machine $blockint $config_blockint
|
||||
/bin/time run $blockint $config_blockint
|
||||
echo -n $config_machine $blockfloat $config_blockfloat
|
||||
/bin/time run $blockfloat $config_blockfloat
|
||||
|
||||
echo -n $config_machine $vainit0 $config_vainit0
|
||||
/bin/time run $vainit0 $config_vainit0
|
||||
echo -n $config_machine $vainit2 $config_vainit2
|
||||
/bin/time run $vainit2 $config_vainit2
|
||||
echo -n $config_machine $vainit4 $config_vainit4
|
||||
/bin/time run $vainit4 $config_vainit4
|
||||
echo -n $config_machine $vainit8 $config_vainit8
|
||||
/bin/time run $vainit8 $config_vainit8
|
||||
|
||||
echo -n $config_machine $vastart0 $config_vastart0
|
||||
/bin/time run $vastart0 $config_vastart0
|
||||
echo -n $config_machine $vastart2 $config_vastart2
|
||||
/bin/time run $vastart2 $config_vastart2
|
||||
echo -n $config_machine $vastart4 $config_vastart4
|
||||
/bin/time run $vastart4 $config_vastart4
|
||||
echo -n $config_machine $vastart8 $config_vastart8
|
||||
/bin/time run $vastart8 $config_vastart8
|
||||
|
||||
echo -n $config_machine $bench_regcall $config_bcall_reg
|
||||
/bin/time run $bench_regcall $config_bcall_reg
|
||||
echo -n $config_machine $bench_immcall $config_bcall_imm
|
||||
/bin/time run $bench_immcall $config_bcall_imm
|
||||
echo -n $config_machine $bench_add $config_b_add
|
||||
/bin/time run $bench_add $config_b_add
|
||||
echo -n $config_machine $bench_load $config_b_load
|
||||
/bin/time run $bench_load $config_b_load
|
Loading…
Add table
Add a link
Reference in a new issue