wiki:InternalsMiscellany

Version 6 (modified by lpolzer, 6 years ago) (diff)

More stuff

TOC?

Assorted internals tidbits

Compiler backend

VINSN   Virtual instruction (probably akin to SBCL's VOPs)
  LAP   Lisp Assembly Parser; compiles SEXPs into opcodes

VINSN macros

<-    copy register
@     label
@=    label (aligned)
->    jump to label
^     branch
?     register (unwired)
$     register (wired)

(from compiler/X86/x862.lisp)

Operand syntax

(% 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

(from compiler/X86/x86-lap.lisp)

Kernel

  • x86: the FS register contains a pointer to the current TCR.
  • x86: the TCR is defined in lisp-kernel/x86-constants32.h (struct tcr)

Memory allocation

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:

   [21]    (subl ($ 7) (@ (% fs) 132))  ; subtract the size of a cons - the tag of a
cons
                                        ; from the per-thread pointer
   [30]    (movl (@ (% fs) 132) (% temp0)) ; load the per-thread pointer into a temp
reg
   [38]    (cmpl (@ (% fs) 136) (% temp0)) ; compare to limit
   [46]    (ja L50)                        ; continue if above limit
   [48]    (uuo-alloc)                     ; trap, get a new chunk
L50
   [50]    (andb ($ 248) (@ (% fs) 132))   ; clear tag bits in thread-local storage
   [59]    (movl (% arg_y) (@ 3 (% temp0))) ; store car
   [62]    (movl (% arg_z) (@ -1 (% temp0))) ; store cdr
   [65]    (movl (% temp0) (% arg_z))      ; return cons cell

Exception handling

The manual has some information on this. Here's some more.

UUOs (by gb)

Origin

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."

Implementation details

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.