|Version 6 (modified by lpolzer, 7 years ago) (diff)|
Assorted internals tidbits
VINSN Virtual instruction (probably akin to SBCL's VOPs) LAP Lisp Assembly Parser; compiles SEXPs into opcodes
<- copy register @ label @= label (aligned) -> jump to label ^ branch ? register (unwired) $ register (wired)
(% x) register ($ x) immediate (@ x) memory operand; x ~ ([seg] [disp] [base] [index] [scale]) x labelref (:rcontext x) memory operand, using segment register or gpr; see @ for syntax of x. (:self fn) self-reference
- x86: the FS register contains a pointer to the current TCR.
- x86: the TCR is defined in lisp-kernel/x86-constants32.h (struct tcr)
Explanation by gb:
Memory allocation is per-thread; a thread conses within a private "chunk" of memory by decrementing a per-thread pointer that starts at the high end of its current chunk and comparing that pointer to the low end of the chunk; if the pointer's above the limit, we use the memory, otherwise we need a new chunk:
 (subl ($ 7) (@ (% fs) 132)) ; subtract the size of a cons - the tag of a cons ; from the per-thread pointer  (movl (@ (% fs) 132) (% temp0)) ; load the per-thread pointer into a temp reg  (cmpl (@ (% fs) 136) (% temp0)) ; compare to limit  (ja L50) ; continue if above limit  (uuo-alloc) ; trap, get a new chunk L50  (andb ($ 248) (@ (% fs) 132)) ; clear tag bits in thread-local storage  (movl (% arg_y) (@ 3 (% temp0))) ; store car  (movl (% arg_z) (@ -1 (% temp0))) ; store cdr  (movl (% temp0) (% arg_z)) ; return cons cell
The manual has some information on this. Here's some more.
UUOs (by gb)
On the DEC PDP-10, the acronym UUO stood for "Unimplemented User Operation"; UUOs were used to invoke system services and/or to invoke what we'd think of as exception handlers. [...] As it's used in CCL, the term just means "an illegal instruction that'll ultimately cause a lisp-aware handler function to be called."
Executing an "int $n" (#xcd n) instruction transfers control to an entry insome (incredibly complex and baroque) table of handlers maintained by the chip. The OS might use a few entries in this table to implement system calls (though modern x86 CPUs often have faster ways of doing system calls), and some entries are reserved for hardware- related interrupts.
Trying to execute an "int $n" instruction that doesn't have a defined entry in this table causes a General Protection Fault, and this eventually causes (on Unix) a signal to be raised. Exactly what signal depends on the OS; there's a macro called SIGNUM_FOR_INTN_TRAP defined conditionally in ccl/lisp-kernel/x86-exceptions.h that defines this. As you can see, it's generally overloaded on SIGBUS or SIGSEGV, which usually indicates an addressing exception (yes, SIGILL would make more sense, but since it involves a GP fault an undefined "int $n" winds up taking the same path as a memory protection fault) and we need to look at other arguments to the signal handler (as defined by the IS_MAYBE_INT_TRAP macro: if IS_MAYBE_INT_TRAP is true, we believe that we can look at the the byte that the PC in the signal context is pointing to to see if it's #xcd; if not, the SIGSEGV or SIGBUS might have been caused by trying to execute code at an unmapped address.)
We also use "ud2a" instructions (followed by a byte or more of data) for similar purposes, but "int $n" is generally shorter; illegal instructions that use the ud2a opcode (which generally -does- raise SIGILL when executed) are sometimes called "xuuos" (eXtended UUOs).
Once in a while (when doing a port to a new OS), we've found that the OS uses an interrupt that we've previously considered free for our use, and have had to do things differently (reassign UUOs or avoid emitting conflicting ones.) I suppose that that could happen if we ported to some other OS (NetBSD ?), but it's unlikely that x8632 OSes will invoke new functionality via "int $n" (since other means of doing system calls are generally faster/better.) See the comment in x86-asm.lisp:
;;; DON'T use #xcd8x: doing so will make Mach angry and confused.