Commit | Line | Data |
---|---|---|
24a647d7 MD |
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, 64, %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+64], %l0 | |
86 | ldd [%sp+ 8+64], %l2 | |
87 | ldd [%sp+16+64], %l4 | |
88 | ldd [%sp+24+64], %l6 | |
89 | ldd [%sp+32+64], %i0 | |
90 | ldd [%sp+40+64], %i2 | |
91 | ldd [%sp+48+64], %i4 | |
92 | ldd [%sp+56+64], %i6 | |
93 | ld [%sp+64+64], %o7 /* Restore return pc. */ | |
94 | ||
95 | retl /* Return to address in %o7. */ | |
96 | add %sp, 72, %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 |