From: Eugene Zaikonnikov
Subject: Question re closures compilation
Date: 
Message-ID: <921342161.907122@lxms.cit.org.by>
Suppose I have a function returning a closure, e.g.

(defun foo (a b)
    #'(lambda (x y)
            ......)

The question is: should any state-of-art lisp compiler compile closure body
(i.e. #'(lambda ....)), or should it be interpreted at runtime?
If first is true, may I add type declarations inside the closure body?
If latter, is it possible to force closure to compile?

Note: I did 'WALK' on such function under LWW4.1, but didn't noticed any
closure-specific code.

Thanks,
     Eugene Zaikonnikov

From: Hrvoje Niksic
Subject: Re: Question re closures compilation
Date: 
Message-ID: <87r9qtqpln.fsf@pc-hrvoje.srce.hr>
"Eugene Zaikonnikov" <······@removeme.cit.org.by> writes:

> Suppose I have a function returning a closure, e.g.
> 
> (defun foo (a b)
>     #'(lambda (x y)
>             ......)
> 
> The question is: should any state-of-art lisp compiler compile
> closure body (i.e. #'(lambda ....)), or should it be interpreted at
> runtime?  If first is true, may I add type declarations inside the
> closure body?  If latter, is it possible to force closure to
> compile?

CMU CL appears to return compiled closures in the case you described,
provided that FOO itself is compiled.
From: Kent M Pitman
Subject: Re: Question re closures compilation
Date: 
Message-ID: <sfwzp5hrx0s.fsf@world.std.com>
"Eugene Zaikonnikov" <······@removeme.cit.org.by> writes:

> Suppose I have a function returning a closure, e.g.
> 
> (defun foo (a b)
>     #'(lambda (x y)
>             ......)
> 
> The question is: should any state-of-art lisp compiler compile closure body
> (i.e. #'(lambda ....)), or should it be interpreted at runtime?

About 20 years ago, these started getting compiled correctly in most
commecial lisps.  The only situation where you should expect
interpretation is where you don't have a compiler at all.  Think of a
closure as a cons.  It has a static code part and some number of cells
for the closed-over data which must vary.  
 (defun foo (x)
   #'(lambda (a) (+ x a)))
is effectively the same as
 (defun foo (x)
   (closure-cons #'(lambda (a x) (+ x a)) x))
If you think of closure-cons as literally cons, you'll get a data type
of the wrong type, but conceptually you'll see what's going on.
This isn't rocket science, and it's done correctly by compilers 
except where there is an out-and-out bug that you should report.

> If first is true, may I add type declarations inside the closure body?

The language is specific about which forms can and cannot have type
declarations.  But yes, type declarations are appropriate in a lambda.
The closure part doesn't have anything to do with it.

> If latter, is it possible to force closure to compile?

N/A.
 
> Note: I did 'WALK' on such function under LWW4.1, but didn't noticed any
> closure-specific code.

I don't know what you mean by this, so can't comment.
From: Vassil Nikolov
Subject: Re: Question re closures compilation
Date: 
Message-ID: <7cfkmh$vmr$1@nnrp1.dejanews.com>
In article <···············@world.std.com>,
  Kent M Pitman <······@world.std.com> wrote:
> "Eugene Zaikonnikov" <······@removeme.cit.org.by> writes:
>
> > Suppose I have a function returning a closure, e.g.
> >
> > (defun foo (a b)
> >     #'(lambda (x y)
> >             ......)
> >
(...)

> > If latter, is it possible to force closure to compile?
>
> N/A.

Are there still Lisps in use that have compilers but don't compile
closures in cases such as above?  If one has such an implementation,
in a for-evaluation position one might say (COMPILE NIL #'(LAMBDA ...)).

> > Note: I did 'WALK' on such function under LWW4.1, but didn't noticed any
> > closure-specific code.
>
> I don't know what you mean by this, so can't comment.

`Walk' in the sense of source-level stepping, or in the sense of examining
object code produced by the compiler?

If the closure did not contain closed-over variables, a good compiler
could compile it as an ordinary function so in the latter sense of `walk'
nothing closure-related would show.  (Sorry I can't give LWW-specific
info.)

Vassil Nikolov <········@poboxes.com> www.poboxes.com/vnikolov
(You may want to cc your posting to me if I _have_ to see it.)
   LEGEMANVALEMFVTVTVM  (Ancient Roman programmers' adage.)

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own    
From: Barry Margolin
Subject: Re: Question re closures compilation
Date: 
Message-ID: <rEQG2.220$p4.61939@burlma1-snr2>
In article <············@nnrp1.dejanews.com>,
Vassil Nikolov  <········@poboxes.com> wrote:
>Are there still Lisps in use that have compilers but don't compile
>closures in cases such as above?  If one has such an implementation,
>in a for-evaluation position one might say (COMPILE NIL #'(LAMBDA ...)).

I don't think there are implementations like this.  Certainly none of the
commercial or high-quality free implementations.  And I think the explicit
call to COMPILE is not guaranteed to do the right thing if the lambda
expression has any closed-over variables.

-- 
Barry Margolin, ······@bbnplanet.com
GTE Internetworking, Powered by BBN, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Kent M Pitman
Subject: Re: Question re closures compilation
Date: 
Message-ID: <sfwvhg4doro.fsf@world.std.com>
As usual, Vassil says something interesting to which I want to reply
but I'm not really replying so much to him, who probably knows much of
this, as to others reading along...

Vassil Nikolov <········@poboxes.com> writes:

> Are there still Lisps in use that have compilers but don't compile
> closures in cases such as above?  If one has such an implementation,
> in a for-evaluation position one might say (COMPILE NIL #'(LAMBDA ...)).

There might be, but it seems unlikely.  Seems to me it's not that much
more work to do the compilation, unless you're just trying to avoid
having a compiler on board at all... Lexical interpreters are, IMO, hard
to write--especially if you want to close over just the right info and not
every possible piece of info you might need later.  You have to code-walk
to get this right, and if you're code-walking, you might as well just
do a fair piece of the compilation.

In Maclisp, FUNCTION didn't get lexical access to what was outside in
compiled code (only in interpreted code--yuck) and there was a special
*FUNCTION you could use if you wanted access to the stack in a
downward-closure-only way.  No first-class (upward) closures.  That
was cheap and easy to implement, and was the time in Lisp's history
when Lisp mostly didn't compile random stuff.  I think it didn't
compile various other constructs, too--PROGV maybe?  I can't remember.
Various things it just punted and stored the s-expression in compiled
code and gave you not only cruddy performance later but also confusing
semantics since the compiler and interpreter semantics of Maclisp were
different.  When the change was made in CL to require
interpreter/compiler semantics to be the same, that made it possible
to better compile these constructs, but it also made it almost
necessary...

IMO, as a rule, it just isn't worth writing your code for the low-end
implementation that doesn't compile things right if most others do.
If you have a low-end compiler or a need to go with something at
below-commercial prices, you can specially accomodate it on a
case-by-case basis, but

I think we are (or should be) talking here about how to write for one
commercial-quality implementation and to guard against reasonable
problems tha can be expected if one has to shift to a different
commercial-quality implementation.  Any fully universal quantification
I could make on what *all* implementations do could be broken tomorrow
by someone making a counter-example implementation anyway, so being
fully complete just isn't that useful.
 
> > > Note: I did 'WALK' on such function under LWW4.1, but didn't noticed any
> > > closure-specific code.
> >
> > I don't know what you mean by this, so can't comment.
> 
> `Walk' in the sense of source-level stepping, or in the sense of examining
> object code produced by the compiler?
> 
> If the closure did not contain closed-over variables, a good compiler
> could compile it as an ordinary function so in the latter sense of `walk'
> nothing closure-related would show.  (Sorry I can't give LWW-specific
> info.)

I'm going to assume that WALK might mean STEP in part of this...

I recall having observed (from execution behavior in both cases, I
never looked at the source code) that, for interpreted code, LWW
expands macros all at once in a pre-pass and Allegro lazily does it
when it hits them.  LWW may be doing other prepass steps for the
interpreter as well, for all I know, so you can easily confuse
yourself if you assume that any kind of STEP tool would naturally bump
into this because it may already have been done by time STEP does its
thing.  The fact that STEP is not a function is one reason this is
possible.  In compiled code, you have to also worry that local functions 
will have been constant-folded in some implementations if your whole test
case is "closed" so that the compiler can see what you're doing. e.g.,
 (defun test (x)
   (step
     (flet ((foo (y) #'(lambda (a) (+ a y))))
       (funcall (foo x) x))))
can be mechanically transformed by a compiler to
 (defun test (x)
   (step
     (funcall (funcall #'(lambda (y) #'(lambda (a) (+ a y))) x) x)))
to
 (defun test (x)
   (step
     (funcall #'(lambda (a) (+ a x)) x)))
to
 (defun test (x)
   (step (+ x x)))
before executing if the compiler wants to.  In fact, in MCL, there is no
interpreter and so eval (as I understand it) is just
 (defun eval (x)
   (funcall (compile nil '(lambda () ,x))))

None of this is intended to say what an implementation WILL do.  Just to
remind you that there is a broad space of legal things that an implementation
CAN do.  The reason there is this space is to allow commercial competition
both in the area of creative progress in compilation technique AND in the
area of figuring out when and where it's appropriate to waste time doing
lots of optimizations that might not matter.  The commercial marketplace
is intended to sort that out, and there is no "remedy" in the spec itself.

As a rule, I personally suggest people focus on writing programs they way
they want and ENTIRELY ignoring performance (other than algorithmic 
complexity) until/unless they get far enough to have an application that
would net them lots of money in its current form if only it were just a
little faster.  People waste extraordinary parts of their life fussing
over this, and it's this fussing that keeps them from climbing to the top
of the "computer food chain".  What's wrong with C and to some extent Java
is that there is an excessive concern about how something is implemented
and too little concern about what the something can do.  Lisp's framework
allows a change in focus, but only if you take advantage of this flexibility
and unlearn the practice of obsessing about minutia.

FWIW, John Mallery used to obsess about how to get one last machine
cycle out of some unimportant piece of code in some unimportant
program, and I used to "yell" at him a lot about this.  He's a smart
guy and it seemed to me he was being wasted on such things. Eventually
he unlearned the practice (or so he tells me) and seems to
periodically offer me a bit of verbal credit for nudging him out of
this rut.  Whatever the cause of this evolution, the change in
attitude/focus shows in the results he achieves because he has gone on
to do many cool things like CL-HTTP, which it seems to me were not
created by fussing over small performance details as a first task.
There are surely some performance details in there, but the tool as a
whole was not created by worrying FIRST about those details.  Indeed,
I'd say the entire C community is held back by a language that makes
it impossible not to think about storage layout, memory management,
and other such details so that your entire view of your goal becomes
warped by these matters.  First figure out what you want to do, then
figure out if you can do it at all, then figure out if you can
optimize it ... among other things, performance is a subtle business
and if you're not looking at close to the final product, you probably
don't know the right issues to take into account to be performance tuning
anyway.  Well, that's my opinion anyway...

I spend my time as a language designer only worrying about whether it's
possible to optimize something, not about whether anyone does.  If it
ever turns out to matter, I'll send a bug report or write the code to
optimize it myself.
From: Christopher R. Barry
Subject: Some CL-HTTP code sloooooow... (was Re: Question re closures compilation)
Date: 
Message-ID: <87iuc3gacm.fsf_-_@2xtreme.net>
Kent M Pitman <······@world.std.com> writes:

[...]

> FWIW, John Mallery used to obsess about how to get one last machine
> cycle out of some unimportant piece of code in some unimportant
> program, and I used to "yell" at him a lot about this.  He's a smart
> guy and it seemed to me he was being wasted on such things. Eventually
> he unlearned the practice (or so he tells me) [...]

Well, some of the over-CLOSified utilities really have less than
optimal performance, to the extent that I find them unusable. As an
example, here's a copy-file routine that Steven M. Haflich of Franz
posted awhile back - the only modification to the original being a
change of the :IF-EXISTS argument from :ERROR to :SUPERSEDE

  (defun copy-file (from to)
    (with-open-file (out to
       :element-type '(unsigned-byte 8)
       :direction :output
       :if-exists :supersede :if-does-not-exist :create)
      (let ((buf (make-array 2048 :element-type '(unsigned-byte 8))))
	(with-open-file (in from :element-type '(unsigned-byte 8))
   (loop as x = (read-sequence buf in)
       until (= x 0)
       do (write-sequence buf out :end x))))
      out))

And now for some benchmarking

  bash-2.01$ ls -l big-file
  -rw-r--r--   1 cbarry   cbarry   20462581 Mar 14 10:47 big-file
  bash-2.01$ time cp big-file big-file-copy

  real	0m6.072s
  user	0m0.020s
  sys	0m1.360s

  USER(20): (time (copy-file "home:big-file" "home:big-file-copy"))
  ; cpu time (non-gc) 170 msec user, 1,350 msec system
  ; cpu time (gc)     0 msec user, 0 msec system
  ; cpu time (total)  170 msec user, 1,350 msec system
  ; real time  5,451 msec
  ; space allocation:
  ;  104 cons cells, 0 symbols, 4,576 other bytes, 0 static bytes

And now HTTP:COPY-FILE for both Allegro CL and CMUCL

  USER(22): (time (http:copy-file "home:big-file" "home:big-file-copy"))
  ; cpu time (non-gc) 33,850 msec user, 2,450 msec system
  ; cpu time (gc)     660 msec user, 60 msec system
  ; cpu time (total)  34,510 msec user, 2,510 msec system
  ; real time  40,655 msec
  ; space allocation:
  ;  28 cons cells, 1 symbol, 24,278,168 other bytes, 0 static bytes

  * (time (http:copy-file "home:big-file" "home:big-file-copy"))
  Compiling LAMBDA NIL: 
  Compiling Top-Level Form: 

  Evaluation took:
    22.93 seconds of real time
    17.77 seconds of user run time
    2.52 seconds of system run time
    [Run times include 0.64 seconds GC run time]
    5010 page faults and
    44859024 bytes consed.

Another one of these cases where CMUCL is big-time outperforming
Allegro, but in both cases the CL-HTTP code is many times slower than
it should be - simply unusable because of its performance.

Christopher
From: Kent M Pitman
Subject: Re: Some CL-HTTP code sloooooow... (was Re: Question re closures compilation)
Date: 
Message-ID: <sfw3e37zozi.fsf@world.std.com>
······@2xtreme.net (Christopher R. Barry) writes:


> Well, some of the over-CLOSified utilities really have less than
> optimal performance, [...]

I didn't make a performance claim about CL-HTTP.

I made a claim about how a way to order things:
Ideas first, performance second.

My guess is that if you'd put a heavy performance constraint on
CL-HTTP, you'd not have gotten a tighter implementation, you'd have
gotten no implementation.  And we wouldn't be having this 20/20
hindsight discussion of how sad it is that isn't faster because we
wouldn't have anything for you to be grumbling about.

My further guess is that having seen a slow CL-HTTP, people can
figure out how to make a fast one.
From: Pekka P. Pirinen
Subject: Re: Question re closures compilation
Date: 
Message-ID: <ix4snltglb.fsf@gaspode.cam.harlequin.co.uk>
> for interpreted code, LWW
> expands macros all at once in a pre-pass and Allegro lazily does it
> when it hits them.  LWW may be doing other prepass steps for the
> interpreter as well, for all I know, so you can easily confuse

That's true, and in particular, LWW resolves all lexical scoping in
the preprocessing pass, including whether a lexical function _needs_
to be a closure.  So just going (let ((x 1)) #'(lambda ())) isn't
going to create a closure, you need to refer to a lexical name in an
outer scope: (let ((x 1)) #'(lambda () x)).

> `Walk' in the sense of source-level stepping, or in the sense of examining
> object code produced by the compiler?

There's a GUI command called Walk, that expands macros at all levels,
not just the top-level expression -- but it won't show you what the
system does for closures.

Yes, LWW does compile local functions, including closures -- and also
STEP bodies, so this won't show you anything:

 (defun test (x)
   (step
     (flet ((foo (y) #'(lambda (a) (+ a y))))
       (funcall (foo x) x))))
-- 
Pekka P. Pirinen   Harlequin Group plc, Cambridge, UK
          "Once a new technology rolls over you, if you're not part of
the steamroller, you're part of the road."
                    Stewart Brand 
From: Steve Gonedes
Subject: Re: Question re closures compilation
Date: 
Message-ID: <m2emmszgcb.fsf@KludgeUnix.com>
"Eugene Zaikonnikov" <······@removeme.cit.org.by> writes:

< Suppose I have a function returning a closure, e.g.
<
< (defun foo (a b)
<     #'(lambda (x y)
<             ......)
<
< The question is: should any state-of-art lisp compiler compile closure body
< (i.e. #'(lambda ....)), or should it be interpreted at runtime?

Although I don't know for certain, I'd venture to guess that the
closure is compiled or at least allocated at the time of it's
creation. You can always use the function `compiled-function-p' to
test if a function is compiled or not.

< If first is true, may I add type declarations inside the closure body?
< If latter, is it possible to force closure to compile?

I don't think you can compile a closure, but you can include
declarations withing a lambda form, a `locally' block, and some other
places too (most of which just make sence, like local functions, etc).

< Note: I did 'WALK' on such function under LWW4.1, but didn't noticed any
< closure-specific code.

(defun this (x)
   #'(lambda (y)
      (declare (fixnum y)
         (optimize speed (safety 0) (debug 0)))
      (* y x)))

(this 3) => #<Interpreted Closure (:INTERNAL THIS) @ #x204fe5fa>

(compile 'this) => THIS, NIL, NIL

(compiled-function-p (this 3)) => T

(disassemble (this 3))

;; disassembly of #<Closure (:INTERNAL THIS 0) @ #x2050796a>
;; formals: Y
;; closure contents:
;;   0: 3

;; code start: #x205077b4:
   0: 8b 5d ec    movl	ebx,[ebp-20]
   3: 8b 53 1a    movl	edx,[ebx+26]
   6: 8b 9f bf fd movl	ebx,[edi-577]   ; *_2OP
      ff ff
  12: ff 67 27    jmp	*[edi+39]       ; TRAMP-TWO
  15: 90          nop

It's like a `minature function' I suppose.

You can get similiar behavior with a macro and using a `flet'.

(defmacro make-named-function (name lambda-form)
  (destructuring-bind (lambda-symbol params &body forms)
       lambda-form
     (declare (ignore lambda-symbol))
   `(flet ((,name ,params ,@ forms))
        #',name)))

(make-named-function my-named-function (lambda (x) (* x x)))
=> #<Interpreted Function (FLET () MY-NAMED-FUNCTION) @ #x20519c1a>


If you're interested in trivia, most (?) common lisp implementations
have what they call `named-functions', which can be used to name a
lambda form.

(named-function my-function (lambda (x) (* x x)))
=> #<Interpreted Function MY-FUNCTION @ #x2050a7d2>

(compile nil (named-function my-function (lambda (x) (* x x))))
=> #<Function (:ANONYMOUS-LAMBDA 6) @ #x20509ad2>

[I don't think you can compile a named-function when it closes over
 the parent environment, i.e., when it becomes closed.]

Hope this was useful, have fun.
From: Vassil Nikolov
Subject: Re: Question re closures compilation
Date: 
Message-ID: <7cih6u$adi$1@nnrp1.dejanews.com>
In article <··············@KludgeUnix.com>,
  Steve Gonedes <········@worldnet.att.net> wrote:
(...)
> (defun this (x)
>    #'(lambda (y)
>       (declare (fixnum y)
>          (optimize speed (safety 0) (debug 0)))
>       (* y x)))
(...)
> ;; disassembly of #<Closure (:INTERNAL THIS 0) @ #x2050796a>
> ;; formals: Y
> ;; closure contents:
> ;;   0: 3
>
> ;; code start: #x205077b4:
>    0: 8b 5d ec    movl	ebx,[ebp-20]
>    3: 8b 53 1a    movl	edx,[ebx+26]
>    6: 8b 9f bf fd movl	ebx,[edi-577]   ; *_2OP
>       ff ff
>   12: ff 67 27    jmp	*[edi+39]       ; TRAMP-TWO
>   15: 90          nop
>
> It's like a `minature function' I suppose.
(...)

Is it the case then that Y (the argument to the closure) is loaded in EDX
from the stack (via EBP), and X (the closed-over variable) is loaded in
EBX with the closure data available via EDI?

If so, then the third instruction (at offset 6) is the closure-specific
part (access to closed-over variable) in this case.

(By the way, why declare the type of Y but not of X and of the result of
the multiplication?)

Vassil Nikolov <········@poboxes.com> www.poboxes.com/vnikolov
(You may want to cc your posting to me if I _have_ to see it.)
   LEGEMANVALEMFVTVTVM  (Ancient Roman programmers' adage.)

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own    
From: Duane Rettig
Subject: Re: Question re closures compilation
Date: 
Message-ID: <4aexe68rp.fsf@beta.franz.com>
Vassil Nikolov <········@poboxes.com> writes:

> In article <··············@KludgeUnix.com>,
>   Steve Gonedes <········@worldnet.att.net> wrote:
> (...)
> > (defun this (x)
> >    #'(lambda (y)
> >       (declare (fixnum y)
> >          (optimize speed (safety 0) (debug 0)))
> >       (* y x)))
> (...)
> > ;; disassembly of #<Closure (:INTERNAL THIS 0) @ #x2050796a>
> > ;; formals: Y
> > ;; closure contents:
> > ;;   0: 3
> >
> > ;; code start: #x205077b4:
> >    0: 8b 5d ec    movl	ebx,[ebp-20]
> >    3: 8b 53 1a    movl	edx,[ebx+26]
> >    6: 8b 9f bf fd movl	ebx,[edi-577]   ; *_2OP
> >       ff ff
> >   12: ff 67 27    jmp	*[edi+39]       ; TRAMP-TWO
> >   15: 90          nop
> >
> > It's like a `minature function' I suppose.
> (...)
> 
> Is it the case then that Y (the argument to the closure) is loaded in EDX
> from the stack (via EBP), and X (the closed-over variable) is loaded in
> EBX with the closure data available via EDI?

No, y is already in eax (the first argument register) and ebp-20 holds
the closure environment (shown as "closure contents" in the disassembler
output).  The instruction at offset 3 loads the second argument register
(edx) with y, which is a slot in the closure environment.

> If so, then the third instruction (at offset 6) is the closure-specific
> part (access to closed-over variable) in this case.

No, edi is nil (and a pointer to the global table).  The instruction at
6 is getting the symbol excl::*_2op (a two-operand simplification of *)
and tramp-two is the place to jump to make a two-operand call without
having to set the argument-count (ecx) register inline.

> (By the way, why declare the type of Y but not of X and of the result of
> the multiplication?)

I can't speak for the original poster, but if you do this, it does
give you much faster code, but it is much longer due to the special
register needs of the imul instruction, and so it obscures the
original point of the example.  For this non-optimized (but shorter)
demo, the declaration of y is unnecessary, as you also imply.

-- 
Duane Rettig          Franz Inc.            http://www.franz.com/ (www)
1995 University Ave Suite 275  Berkeley, CA 94704
Phone: (510) 548-3600; FAX: (510) 548-8253   ·····@Franz.COM (internet)
From: Duane Rettig
Subject: Re: Question re closures compilation
Date: 
Message-ID: <490cy68e4.fsf@beta.franz.com>
Duane Rettig <·····@franz.com> writes:

> No, y is already in eax (the first argument register) and ebp-20 holds
> the closure environment (shown as "closure contents" in the disassembler
> output).  The instruction at offset 3 loads the second argument register
> (edx) with y, which is a slot in the closure environment.
=============^

Sorry, this is a typo that I didn't see until after I posted.  It
should have said "x".

-- 
Duane Rettig          Franz Inc.            http://www.franz.com/ (www)
1995 University Ave Suite 275  Berkeley, CA 94704
Phone: (510) 548-3600; FAX: (510) 548-8253   ·····@Franz.COM (internet)
From: Vassil Nikolov
Subject: Re: Question re closures compilation
Date: 
Message-ID: <7ckmml$8er$1@nnrp1.dejanews.com>
In article <·············@beta.franz.com>,
  Duane Rettig <·····@franz.com> wrote:
> Vassil Nikolov <········@poboxes.com> writes:
>
> > In article <··············@KludgeUnix.com>,
> >   Steve Gonedes <········@worldnet.att.net> wrote:
> > (...)
> > > (defun this (x)
> > >    #'(lambda (y)
> > >       (declare (fixnum y)
> > >          (optimize speed (safety 0) (debug 0)))
> > >       (* y x)))
> > (...)
> > > ;; disassembly of #<Closure (:INTERNAL THIS 0) @ #x2050796a>
> > > ;; formals: Y
> > > ;; closure contents:
> > > ;;   0: 3
> > >
> > > ;; code start: #x205077b4:
> > >    0: 8b 5d ec    movl	ebx,[ebp-20]
> > >    3: 8b 53 1a    movl	edx,[ebx+26]
> > >    6: 8b 9f bf fd movl	ebx,[edi-577]   ; *_2OP
> > >       ff ff
> > >   12: ff 67 27    jmp	*[edi+39]       ; TRAMP-TWO
> > >   15: 90          nop
> > >
> > > It's like a `minature function' I suppose.
> > (...)
> >
> > Is it the case then that Y (the argument to the closure) is loaded in EDX
> > from the stack (via EBP), and X (the closed-over variable) is loaded in
> > EBX with the closure data available via EDI?
>
> No, y is already in eax (the first argument register) and ebp-20 holds
> the closure environment (shown as "closure contents" in the disassembler
> output).  The instruction at offset 3 loads the second argument register
> (edx) with y, which is a slot in the closure environment.
             ^ this is of course x (corrected by Duane Rettig in a followup)

So the first few arguments are always passed in registers in this
implementation, and the closure environment is passed on the stack.
My mistake came from thinking that the argument is passed on the
stack; of course passing an environment via EDI is unnatural.

So the instructions at offsets 0 and 3 are the closure-specific part
which I have been after.

> > If so, then the third instruction (at offset 6) is the closure-specific
> > part (access to closed-over variable) in this case.
>
> No, edi is nil (and a pointer to the global table).  The instruction at
> 6 is getting the symbol excl::*_2op (a two-operand simplification of *)
> and tramp-two is the place to jump to make a two-operand call without
> having to set the argument-count (ecx) register inline.
>
> > (By the way, why declare the type of Y but not of X and of the result of
> > the multiplication?)
>
> I can't speak for the original poster, but if you do this, it does
> give you much faster code, but it is much longer due to the special
> register needs of the imul instruction, and so it obscures the
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This is part of the pleasure in programming a x86...

> original point of the example.  For this non-optimized (but shorter)
> demo, the declaration of y is unnecessary, as you also imply.

Yes, either declare them all or not at all.  (Aut Caesar aut nihil...)

Thank you for correcting my misinterpretation and explaining this
disassembly.

Have a nice day or night,
Vassil.


Vassil Nikolov <········@poboxes.com> www.poboxes.com/vnikolov
(You may want to cc your posting to me if I _have_ to see it.)
   LEGEMANVALEMFVTVTVM  (Ancient Roman programmers' adage.)

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own    
From: Steve Gonedes
Subject: Re: Question re closures compilation
Date: 
Message-ID: <m290cxzy2s.fsf@KludgeUnix.com>
Vassil Nikolov <········@poboxes.com> writes:

< In article <··············@KludgeUnix.com>,
<   Steve Gonedes <········@worldnet.att.net> wrote:
< (...)
< > (defun this (x)
< >    #'(lambda (y)
< >       (declare (fixnum y)
< >          (optimize speed (safety 0) (debug 0)))
< >       (* y x)))
< (...)
< > ;; disassembly of #<Closure (:INTERNAL THIS 0) @ #x2050796a>
< > ;; formals: Y
< > ;; closure contents:
< > ;;   0: 3
< >
< > ;; code start: #x205077b4:
< >    0: 8b 5d ec    movl	ebx,[ebp-20]
< >    3: 8b 53 1a    movl	edx,[ebx+26]
< >    6: 8b 9f bf fd movl	ebx,[edi-577]   ; *_2OP
< >       ff ff
< >   12: ff 67 27    jmp	*[edi+39]       ; TRAMP-TWO
< >   15: 90          nop
< >
< > It's like a `minature function' I suppose.
< (...)
<
< Is it the case then that Y (the argument to the closure) is loaded in EDX
< from the stack (via EBP), and X (the closed-over variable) is loaded in
< EBX with the closure data available via EDI?

*_2OP is a function (or something similiar). I thought that eax was
already loaded with `Y' from the caller and edx has the old `X'. Not
sure about the closure environment; I think ebx has special memory
functionality - dunno, it gets too confusing for me - too much
functionality in too few registers on too many special occasions (?).

By `minature function' I just meant that it didn't have to do the
`C/unix/x86 standard function setup' ordeal with return.

I was expecting to see the typical

pushl %ebp
movl  %esp,%ebp
movl  8(%ebp),%eax
...
ret,

but didn't. Thought it was neat.

< If so, then the third instruction (at offset 6) is the closure-specific
< part (access to closed-over variable) in this case.
<
< (By the way, why declare the type of Y but not of X and of the result of
< the multiplication?)

Just wanted to show that a declaration could appear in a lambda form
without getting too verbose.
From: Vassil Nikolov
Subject: Re: Question re closures compilation
Date: 
Message-ID: <7cnikg$p8b$1@nnrp1.dejanews.com>
In article <··············@KludgeUnix.com>,
  Steve Gonedes <········@worldnet.att.net> wrote:
>
> Vassil Nikolov <········@poboxes.com> writes:
>
> < In article <··············@KludgeUnix.com>,
> <   Steve Gonedes <········@worldnet.att.net> wrote:
> < (...)
> < > (defun this (x)
> < >    #'(lambda (y)
> < >       (declare (fixnum y)
> < >          (optimize speed (safety 0) (debug 0)))
> < >       (* y x)))
> < (...)
> < > ;; disassembly of #<Closure (:INTERNAL THIS 0) @ #x2050796a>
> < > ;; formals: Y
> < > ;; closure contents:
> < > ;;   0: 3
> < >
> < > ;; code start: #x205077b4:
> < >    0: 8b 5d ec    movl	ebx,[ebp-20]
> < >    3: 8b 53 1a    movl	edx,[ebx+26]
> < >    6: 8b 9f bf fd movl	ebx,[edi-577]   ; *_2OP
> < >       ff ff
> < >   12: ff 67 27    jmp	*[edi+39]       ; TRAMP-TWO
> < >   15: 90          nop
> < >
> < > It's like a `minature function' I suppose.
> < (...)
> <
> < Is it the case then that Y (the argument to the closure) is loaded in EDX
> < from the stack (via EBP), and X (the closed-over variable) is loaded in
> < EBX with the closure data available via EDI?
>
> *_2OP is a function (or something similiar). I thought that eax was
> already loaded with `Y' from the caller and edx has the old `X'.
(...)

Yes, you are right and I was wrong, as Duane Rettig explained.

> By `minature function' I just meant that it didn't have to do the
> `C/unix/x86 standard function setup' ordeal with return.
>
> I was expecting to see the typical
>
> pushl %ebp
> movl  %esp,%ebp
> movl  8(%ebp),%eax
> ...
> ret,
>
> but didn't. Thought it was neat.
(...)

I suppose this (tail-call elimination) is the result of optimizing
for speed and against debuggability (in the lambda expression).
You could try the disassembly minus the declaration (or with
(OPTIMIZE DEBUG)) to see if you would get a different prologue and
epilogue.

And, of course, it depends on the circumstances if it is neat or not.

Have a nice day,
Vassil.

Vassil Nikolov <········@poboxes.com> www.poboxes.com/vnikolov
(You may want to cc your posting to me if I _have_ to see it.)
   LEGEMANVALEMFVTVTVM  (Ancient Roman programmers' adage.)

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own