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