Subject: Re: Function object equality
From: rpw3@rpw3.org (Rob Warnock)
Date: Wed, 22 Mar 2006 19:23:38 -0600
Newsgroups: comp.lang.lisp
Message-ID: <DOadnbm3R9YHZbzZRVn-sQ@speakeasy.net>
Frode Vatvedt Fjeld  <frodef@cs.uit.no> wrote:
+---------------
| Marcin 'Qrczak' Kowalczyk <qrczak@knm.org.pl> writes:
| > But there should be only one foo, as mkfoo is called only once.
| 
| Right you are, I read the original post too quickly. I suspect it's a
| corner case in a compiler that is easy to miss.
+---------------

Actually, it would seem that in CMUCL (nothing just 19c) that the 
FUNCTION special operator result in "executable code" (if you will)
that creates a closure at the moment the FUNCTION form is "executed".
And that in the OP's test code, the optimizer is not *quite* smart
enough to realize that the original #'FOO can be re-used. Here's the
OP's case, with MKFOO compiled [simplifies printouts]:

    cmu> (defun mkfoo (x)
	   (labels ((foo ()
		      (values (function foo) x)))
	     (function foo)))

    MKFOO
    cmu> (compile 'mkfoo)
    ; Compiling LAMBDA (X): 
    ; Compiling Top-Level Form: 

    MKFOO
    NIL
    NIL
    cmu> (deflex foo (mkfoo "xyz"))	; Pardon my DEFLEX...  ;-}

    FOO
    cmu> (funcall foo)

    #<Closure Over Function (LABELS FOO
			      MKFOO)
      {5890B689}>
    "xyz"
    cmu> (funcall foo)

    #<Closure Over Function (LABELS FOO
			      MKFOO)
      {5890C001}>
    "xyz"
    cmu> 

And a (DISASSEMBLE FOO) reveals that the (FUNCTION FOO) in the VALUES
call *is* allocating a new closure object (size = 16 bytes) inline:

      ...
      87:     MOV     EDX, 16
      8C:     ADD     EDX, [#x2800057C] ; X86::*CURRENT-REGION-FREE-POINTER*
      92:     CMP     EDX, [#x28000594] ; X86::*CURRENT-REGION-END-ADDR*
      98:     JBE     L0
      9A:     CALL    #xBE000010        ; #xBE000010: alloc_overflow_edx
      9F: L0: XCHG    EDX, [#x2800057C] ; X86::*CURRENT-REGION-FREE-POINTER*
      A5:     LEA     EDX, [EDX+1]
      A8:     MOV     DWORD PTR [EDX-1], 642
      ...fill in rest of struct & return...

The "+1" offset gives it a function pointer low-tag. The "642" (#x282)
is a header word for a "type_ClosureHeader" (#x82) with two additional
data words, a function (#'FOO itself) and a single closed-over value
[the string "xyz", as it happens in this case]:

     cmu> (dump32 (1- (kernel:get-lisp-obj-address foo)) 12)
     #x58964E60: #x00000282 #x5895C960 #x58962127
     cmu> (dump32 (1- (kernel:get-lisp-obj-address (funcall foo))) 12)
     #x589AFD20: #x00000282 #x5895C960 #x58962127
     cmu> (dump32 (1- (kernel:get-lisp-obj-address (funcall foo))) 12)
     #x589B5B00: #x00000282 #x5895C960 #x58962127
     cmu> (kernel:make-lisp-obj #x58962127)

     "xyz"
     cmu> 

As you can see, the *contents* of all three closure objects are entirely
the same, but they are still very different objects (in the EQ sense).

A slight tweak to MKFOO to avoid run-time creation of a closure makes
the "problem"(?) go away:

    cmu> (defun mkfoo (x)
	   (let ((foo nil))
	     (labels ((foo ()
			(values foo x)))
	       (setf foo (function foo)))))
    cmu> (compile *)

    ; Compiling LAMBDA (X): 
    ; Compiling Top-Level Form: 

    MKFOO
    NIL
    NIL
    cmu> (deflex foo (mkfoo "abc"))

    FOO
    cmu> (funcall foo)

    #<Closure Over Function (LABELS FOO
			      MKFOO)
      {589D2F81}>
    "abc"
    cmu> (funcall foo)

    #<Closure Over Function (LABELS FOO
			      MKFOO)
      {589D2F81}>
    "abc"
    cmu> (dump32 (1- (kernel:get-lisp-obj-address foo)) 12)
    #x589D2F80: #x00000382 #x589CAB88 #x589D2F7F
    cmu> (dump32 (1- (kernel:get-lisp-obj-address (funcall foo))) 12)
    #x589D2F80: #x00000382 #x589CAB88 #x589D2F7F
    cmu> (dump32 (1- (kernel:get-lisp-obj-address (funcall foo))) 12)
    #x589D2F80: #x00000382 #x589CAB88 #x589D2F7F
    cmu> 

To reprise the OP's test [well, with EQ instead of EQL]:

    cmu> (let ((foo (mkfoo "defghi")))
	   (eq foo (funcall foo)))

    T
    cmu> 


-Rob

-----
Rob Warnock			<rpw3@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607