[Openmcl-devel] CCL LAP info...
Jon S. Anthony
j-anthony at comcast.net
Sun Feb 14 18:11:00 PST 2010
On Sun, 2010-02-14 at 12:58 -0700, Gary Byers wrote:
>
> On Sun, 14 Feb 2010, Jon S. Anthony wrote:
>
> > Thanks, that is some valuable info! BTW, I've seen TCR and this fn
> > being "established" and such, and have wondered what they are being used
> > for/as. Couple sentence description?
>
> TCR = "Thread Context Record" - lisp-specific thread-local data. If the
> OS allows us to, we try to keep a thread's TCR in an otherwise-unused segment
> register on x86.
>
> %fn = the current function.
Valuable input!
> rme did a blog post on how CCL implements
> closures a month or so ago:
>
> <http://ccl.clozure.com/blog/>
>
> which explains a lot about the physical structure of functions (closures
> and others) in x86 ccl.
Thanks a lot - this is very good stuff.
> The %fn register is mostly used to reference lisp objects as constants.
> A function that references the symbol X will probably contain an
> instruction that looks something like:
>
> [12] (movl (@ 'X (% fn)) (% arg_z))
>
> The disassembler recognizes that as a constant reference and displays
> the constant symbolically; the actual instruction looks more like:
>
> [12] (movl (@ n (% fn)) (% arg_z))
>
> where N is an integer whose value is "the number of bytes from the
> start of the current function to the place where the reference to X
> is stored in the function's constants". Unless you're debugging
> the assembler/LAP, the value of N isn't very interesting; if you're
> writing LAP code, you can use the (@ 'foo (% fn)) syntax to both
> ensure that a constant reference will be allocated for FOO and to
> determine the correct value of N to encode in the instruction.
>
> "the current function" changes every time a function is called or
> returned to. (You can think of this as meaning that %fn is an
> implicit argument to each function, or that %fn is saved and restored
> by the caller.) We don't actually save it, but we do need to restore
> it (as the first thing that a function does on entry and as the first
> thing that a function does after it returns from a function it calls.)
> The instruction that's used to (re-)establish the %fn register is a
> little weird and has to be handled specially on ia32; the disassembler
> shows it as (recover-fn), and ccl:compiler;X86;x86-lapmacros.lisp defines
> a LAP macro of the same name.
>
> An initial recover-fn pseudo-instruction gets generated automatically;
> other lap macros defined in the file mentioned above ensure that
> a RECOVER-FN happens after function calls. (Aside from allowing a
> function to reference constants, %fn is important to the GC, which
> uses the RECOVER-FN pseudo-intstructions to identify the function
> that contains them; the use of RECOVER-FN is not optional.)
All of this is quite enlightening and very valuable info. The
description of %fn and the RECOVER-FN clears up several fuzzy bits in my
understanding. Many thanks!
> > So, this just returns the stack arg (arg1) in standard return reg argz
> > and then pops off the arg1 and 2 extra reserved (alignment??) words.
>
> If the caller pushes any outgoing arguments (e.g., passes more than the
> 2 register args on ia32), it has to first reserve space on the stack
> that the callee can use to establish a stack frame. (This is a bit hard
> to concisely explain and may be a bit hard to understand, but it has to
> do with the fact that functions on X86 are always entered with the return
> address on top of the stack and that can be awkard (especially if the callee
> can be called with different numbers of arguments.)
So, the stack on entry is:
TOS ----> RA
... ----> Stack args if any (right most on top?)
Reserve1 ----> -21
Reserve2 ----> -21
> Reserving space for the callee's canonicalized stack frame involves
> pushing two words before any stack arguments are pushed. The value of
> each of those two words is conventionally the value of
> TARGET::RESERVED-FRAME-MARKER; you could actually push any legal value,
> but using that marker makes it easier to recognize outgoing function
> calls in backtrace.
>
> ? (defun foo () (abs (bar 1 2 3)))
> FOO ; and a warning about BAR, probably.
> ? (disassemble *)
>
> ;;; (bar 1 2 3)
> [12] (pushl ($ -21)) ; x8632::reserved-frame-marker
> [14] (pushl ($ -21)) ; x8632::reserved-frame-marker
Ha! I've been wondering what that -21 stuff was!
> [16] (pushl ($ 4)) ; 1
> [18] (movl ($ 8) (% arg_y)) ; 2
> [23] (movl ($ 12) (% arg_z)) ; 3
> [28] (movl ($ 12) (% temp1)) ; 3 args
> [33] (movl (@ 'BAR (% fn)) (% temp0))
>
>
> >
> >
> >> ? (defun 3-arg-caller ()
> >> (3-arg-taker 1 2 3)
> >> 'foo))
> >> 3-ARG-CALLER
> >> ? (df *)
> >
> > Simple, but handy!
> >
> >
> >> ;;; (defun 3-arg-caller () (3-arg-taker 1 2 3) 'foo)
> >> [0] (recover-fn)
> >> [5] (testl (% temp1) (% temp1))
> >> [7] (jne L66)
> >> [9] (pushl (% ebp))
> >> [10] (movl (% esp) (% ebp))
> >>
> >> ;;; (3-arg-taker 1 2 3)
> >>
> >> ;; here the caller reserves space for a frame
> >> [12] (pushl ($ -21))
> >> [14] (pushl ($ -21))
> >> ;; pass the first parameter on the stack
> >> ;; (the number 4 represents fixnum 1)
> >> [16] (pushl ($ 4))
> >
> > This is very interesting - this seems to indicate that CCL uses the
> > _low_ order bits as the tag bits? That would explain some things...
>
> I'm not aware of a native CL implementation that doesn't use the low
> bits for tagging. (MCL 1.0 didn't, but that was over 20 years ago.)
Which only goes to show how much I know :0. And after you mention it,
thinking about it for like 5 seconds reveals why this makes sense...
/Jon
More information about the Openmcl-devel
mailing list