1/* sparc.s -- assembly support for the `qt' thread building kit. */
2
3/*
4 * QuickThreads -- Threads-building toolkit.
5 * Copyright (c) 1993 by David Keppel
6 *
7 * Permission to use, copy, modify and distribute this software and
8 * its documentation for any purpose and without fee is hereby
9 * granted, provided that the above copyright notice and this notice
10 * appear in all copies.  This software is provided as a
11 * proof-of-concept and for demonstration purposes; there is no
12 * representation about the suitability of this software for any
13 * purpose.
14 */
15
16/* #include <machine/trap.h> */
17
18	.text
19	.align 4
20	.global _qt_blocki
21	.global _qt_block
22	.global _qt_abort
23	.global _qt_start
24	.global _qt_vstart
25
26/* Register assignment:
27// %o0:	incoming `helper' function to call after cswap
28//	also used as outgoing sp of old thread (qt_t *)
29// %o1, %o2:
30//	parameters to `helper' function called after cswap
31// %o3:	sp of new thread
32// %o5: tmp used to save old thread sp, while using %o0
33//	to call `helper' f() after cswap.
34//
35//
36// Aborting a thread is easy if there are no cached register window
37// frames: just switch to the new stack and away we go.  If there are
38// cached register window frames they must all be written back to the
39// old stack before we move to the new stack.  If we fail to do the
40// writeback then the old stack memory can be written with register
41// window contents e.g., after the stack memory has been freed and
42// reused.
43//
44// If you don't believe this, try setting the frame pointer to zero
45// once we're on the new stack.  This will not affect correctnes
46// otherwise because the frame pointer will eventually get reloaded w/
47// the new thread's frame pointer.  But it will be zero briefly before
48// the reload.  You will eventually (100,000 cswaps later on a small
49// SPARC machine that I tried) get an illegal instruction trap  from
50// the kernel trying to flush a cached window to location 0x0.
51//
52// Solution: flush windows before switching stacks, which invalidates
53// all the other register windows.  We could do the trap
54// conditionally: if we're in the lowest frame of a thread, the fp is
55// zero already so we know there's nothing cached.  But we expect most
56// aborts will be done from a first function that does a `save', so we
57// will rarely save anything and always pay the cost of testing to see
58// if we should flush.
59//
60// All floating-point registers are caller-save, so this routine
61// doesn't need to do anything to save and restore them.
62//
63// `qt_block' and `qt_blocki' return the same value as the value
64// returned by the helper function.  We get this ``for free''
65// since we don't touch the return value register between the
66// return from the helper function and return from qt_block{,i}.
67*/
68
69_qt_block:
70_qt_blocki:
71	sub %sp, 8, %sp		/* Allocate save area for return pc. */
72	st %o7, [%sp+64]	/* Save return pc. */
73_qt_abort:
74	ta 0x03			/* Save locals and ins. */
75	mov %sp, %o5		/* Remember old sp w/o chng ins/locals. */
76	sub %o3, 96, %sp	/* Allocate kwsa, switch stacks. */
77	call %o0, 0		/* Call `helper' routine. */
78	mov %o5, %o0		/* Pass old thread to qt_after_t() */
79				/* .. along w/ args in %o1 & %o2. */
80
81	/* Restore callee-save regs.  The kwsa
82	// is on this stack, so offset all
83	// loads by sizeof(kwsa), 64 bytes.
84	*/
85	ldd [%sp+ 0+96], %l0
86	ldd [%sp+ 8+96], %l2
87	ldd [%sp+16+96], %l4
88	ldd [%sp+24+96], %l6
89	ldd [%sp+32+96], %i0
90	ldd [%sp+40+96], %i2
91	ldd [%sp+48+96], %i4
92	ldd [%sp+56+96], %i6
93	ld [%sp+64+96], %o7	/* Restore return pc. */
94
95	retl			/* Return to address in %o7. */
96	add %sp, 104, %sp	/* Deallocate kwsa, ret pc area. */
97
98
99/* The function calling conventions say there has to be a 1-word area
100// in the caller's stack to hold a pointer to space for aggregate
101// return values.  It also says there should be a 6-word area to hold
102// %o0..%o5 if the callee wants to save them (why?  I don't know...)
103// Round up to 8 words to maintain alignment.
104//
105// Parameter values were stored in callee-save regs and are moved to
106// the parameter registers.
107*/
108_qt_start:
109	mov %i1, %o0		/* `pu': Set up args to `only'. */
110	mov %i2, %o1		/* `pt'. */
111	mov %i4, %o2		/* `userf'. */
112	call %i5, 0		/* Call client function. */
113	sub %sp, 32, %sp	/* Allocate 6-word callee space. */
114
115	call _qt_error, 0	/* `only' erroniously returned. */
116	nop
117
118
119/* Same comments as `_qt_start' about allocating rounded-up 7-word
120// save areas. */
121
122_qt_vstart:
123	sub %sp, 32, %sp	/* Allocate 7-word callee space. */
124	call %i5, 0		/* call `startup'. */
125	mov %i2, %o0		/* .. with argument `pt'. */
126
127	add %sp, 32, %sp	/* Use 7-word space in varargs. */
128	ld [%sp+ 4+64], %o0	/* Load arg0 ... */
129	ld [%sp+ 8+64], %o1
130	ld [%sp+12+64], %o2
131	ld [%sp+16+64], %o3
132	ld [%sp+20+64], %o4
133	call %i4, 0		/* Call `userf'. */
134	ld [%sp+24+64], %o5
135
136				/* Use 6-word space in varargs. */
137	mov %o0, %o1		/* Pass return value from userf */
138	call %i3, 0		/* .. when call `cleanup. */
139	mov %i2, %o0		/* .. along with argument `pt'. */
140
141	call _qt_error, 0	/* `cleanup' erroniously returned. */
142	nop
143