mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-04-30 11:50:28 +02:00
112 lines
4.8 KiB
Text
112 lines
4.8 KiB
Text
Date: Tue, 11 Jan 94 13:23:11 -0800
|
|
From: "pardo@cs.washington.edu" <pardo@meitner.cs.washington.edu>
|
|
|
|
>[What's needed to get `qt' on an i860-based machine?]
|
|
|
|
Almost certainly "some assembly required" (pun accepted).
|
|
|
|
To write a cswap port, you need to understand the context switching
|
|
model. Turn to figure 2 in the QT TR. Here's about what the assembly
|
|
code looks like to implement that:
|
|
|
|
qt_cswap:
|
|
adjust stack pointer
|
|
save callee-save registers on to old's stack
|
|
argument register <- old sp
|
|
sp <- new sp
|
|
(*helper)(args...)
|
|
restore callee-save registers from new's stack
|
|
unadjust stack pointer
|
|
return
|
|
|
|
Once more in slow motion:
|
|
|
|
- `old' thread calls context switch routine (new, a0, a1, h)
|
|
- cswap routine saves registers that have useful values
|
|
- cswap routine switches to new stack
|
|
- cswap routine calls helper function (*h)(old, a0, a1)
|
|
- when helper returns, cswap routine restores registers
|
|
that were saved the last time `new' was suspended
|
|
- cswap routine returns to whatever `new' routine called the
|
|
context switch routine
|
|
|
|
There's a few tricks here. First, how do you start a thread running
|
|
for the very first time? Answer is: fake some stuff on the stack
|
|
so it *looks* like it was called from the middle of some routine.
|
|
When the new thread is restarted, it is treated like any other
|
|
thread. It just so happens that it's never really run before, but
|
|
you can't tell that because the saved state makes it look like like
|
|
it's been run. The return pc is set to point at a little stub of
|
|
assembly code that loads up registers with the right values and
|
|
then calls `only'.
|
|
|
|
Second, I advise you to forget about varargs routines (at least
|
|
until you get single-arg routines up and running).
|
|
|
|
Third, on most machines `qt_abort' is the same as `qt_cswap' except
|
|
that it need not save any callee-save registers.
|
|
|
|
Fourth, `qt_cswap' needs to save and restore any floating-point
|
|
registers that are callee-save (see your processor handbook). On
|
|
some machines, *no* floating-point registers are callee-save, so
|
|
`qt_cswap' is exactly the same as the integer-only cswap routine.
|
|
|
|
I suggest staring at the MIPS code for a few minutes. It's "mostly"
|
|
generic RISC code, so it gets a lot of the flavor across without
|
|
getting too bogged down in little nitty details.
|
|
|
|
|
|
|
|
Now for a bit more detail: The stack is laid out to hold callee-save
|
|
registers. On many machines, I implemented fp cswap as save fp
|
|
regs, call integer cswap, and when integer cswap returns (when the
|
|
thread wakes up again), restore fp regs.
|
|
|
|
For thread startup, I figure out some callee-save registers that
|
|
I use to hold parameters to the startup routine (`only'). When
|
|
the thread is being started it doesn't have any saved registers
|
|
that need to be restored, but I go ahead and let the integer context
|
|
switch routine restore some registers then "return" to the stub
|
|
code. The stub code then copies the "callee save" registers to
|
|
argument registers and calls the startup routine. That keeps the
|
|
stub code pretty darn simple.
|
|
|
|
For each machine I need to know the machine's procedure calling
|
|
convention before I write a port. I figure out how many callee-save
|
|
registers are there and allocate enough stack space for those
|
|
registers. I also figure out how parameters are passed, since I
|
|
will need to call the helper function. On most RISC machines, I
|
|
just need to put the old sp in the 0'th arg register and then call
|
|
indirect through the 3rd arg register; the 1st and 2nd arg registers
|
|
are already set up correctly. Likewise, I don't touch the return
|
|
value register between the helper's return and the context switch
|
|
routine's return.
|
|
|
|
I have a bunch of macros set up to do the stack initialization.
|
|
The easiest way to debug this stuff is to go ahead and write a C
|
|
routine to do stack initialization. Once you're happy with it you
|
|
can turn it in to a macro.
|
|
|
|
In general there's a lot of ugly macros, but most of them do simple
|
|
things like return constants, etc. Any time you're looking at it
|
|
and it looks confusing you just need to remember "this is actually
|
|
simple code, the only tricky thing is calling the helper between
|
|
the stack switch and the new thread's register restore."
|
|
|
|
|
|
You will almost certainly need to write the assembly code fragment
|
|
that starts a thread. You might be able to do a lot of the context
|
|
switch code with `setjmp' and `longjmp', if they *happen* to have
|
|
the "right" implementation. But getting all the details right (the
|
|
helper can return a value to the new thread's cswap routine caller)
|
|
is probaby trickier than writing code that does the minimum and
|
|
thus doesn't have any extra instructions (or generality) to cause
|
|
problems.
|
|
|
|
I don't know of any ports besides those included with the source
|
|
code distribution. If you send me a port I will hapily add it to
|
|
the distribution.
|
|
|
|
Let me know as you have questions and/or comments.
|
|
|
|
;-D on ( Now *that*'s a switch... ) Pardo
|