From: Camm Maguire
Subject: gcc ~= lisp in C
Date: 
Message-ID: <54vf78fs18.fsf@intech19.enhanced.com>
Greetings!  Its amazing how much lisp one can get in C via gcc's
extensions to the C language.  Just discovered the following 'lexical
closure' over V:

  {
    static int f(int i,int j,int n,double *d) {
      
      double *de=d+n;
      
      for (;d<de;d++)
	*d=-1.0/V;
      
      return 0;
      
    }
    

    E(sl_pddistr_init,f,e,0,0,de);
    
  }
  
Ignore the E cpp macro, which just returns -1 with diagnostics on
function error.  But here we pass a locally defined function accessing
variable V defined in an outer scope as a 'first class object' to
sl_pddistr_init. 

Neat.

Take care,
-- 
Camm Maguire			     			····@enhanced.com
==========================================================================
"The earth is but one country, and mankind its citizens."  --  Baha'u'llah

From: Barry Margolin
Subject: Re: gcc ~= lisp in C
Date: 
Message-ID: <barmar-A75900.09101231032005@comcast.dca.giganews.com>
In article <··············@intech19.enhanced.com>,
 Camm Maguire <····@enhanced.com> wrote:

> Ignore the E cpp macro, which just returns -1 with diagnostics on
> function error.  But here we pass a locally defined function accessing
> variable V defined in an outer scope as a 'first class object' to
> sl_pddistr_init. 
> 
> Neat.
> 
> Take care,

That's something you could do in Algol, PL/I, and Pascal decades ago, 
it's hardly new technology.  In fact, you couldn't do it properly in 
most Lisp dialects until Scheme came along -- dynamic scoping of 
variable bindings could screw things up.

But can you return the locally defined function to an outer scope and 
still call it?  I doubt very much that GCC can do upward funargs.

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
From: Andru Luvisi
Subject: Re: gcc ~= lisp in C
Date: 
Message-ID: <87k6nn7kbm.fsf@andru.sonoma.edu>
>>>>> "Barry" == Barry Margolin <······@alum.mit.edu> writes:
    Barry> That's something you could do in Algol, PL/I, and Pascal
    Barry> decades ago, it's hardly new technology.  In fact, you
    Barry> couldn't do it properly in most Lisp dialects until Scheme
    Barry> came along -- dynamic scoping of variable bindings could
    Barry> screw things up.

Lisp 1.5 could do both downward and upward funargs.  They were just
closed over the dynamic environment instead of the lexical
environment.  See _Lisp 1.5 Programmer's Manual_.

I don't have a Lisp 1.5 system handy right now, but I believe the
following would work:

define ((
  (make-adder (lambda (x) (function (lambda (y) (+ x y)))))
  (test-adder (lambda ()
	(prog (a1 a2)
		(setq a1 (make-adder 5))
		(setq a2 (make-adder 10))
		(print (a1 1))
		(print (a2 1))
		(return nil))))
))

test-adder ()
6
11
NIL

Andru
-- 
Andru Luvisi

Quote Of The Moment:
  "If you give someone Fortran, he has Fortran. 
   If you give someone Lisp,    he has any language he pleases." 
  		-- Guy Steele
From: Maurice
Subject: Re: gcc ~= lisp in C
Date: 
Message-ID: <d2h5ph$1eh$05$1@news.t-online.com>
Ingvar <······@hexapodia.net> wrote:
> It seems as if there's an ability to
> call the returned local function, but it doesn't seem to be anywhere
> near perfect (it may, in fact, just be sheer luck that it works).
> 
> head$ cat gccdemo-1.c 
> #include <stdio.h>
> 
> int (*fun1(int x))()
> {
>   int local ()
>   {
>     return x;
>   }
> 
>   return local;
> }
> 
> int main (int argc, char **argv)
> {
> 
>   int (*foo)();
> 
>   foo=fun1(17);
> 
>   printf("%d\n", foo());
> 
>   return 0;
> }
> 
> head$ make gccdemo-1   
> gcc -Wall     gccdemo-1.c   -o gccdemo-1
> head$ ./gccdemo-1
> 17

on my computer:

   ~% make gccdemo1
   cc     gccdemo1.c   -o gccdemo1
   ~% ./gccdemo1
   127

oops :-)

no, thats realy very unusable...
From: Bruno Haible
Subject: Re: gcc ~= lisp in C
Date: 
Message-ID: <d2jrcc$ef1$1@laposte.ilog.fr>
Barry Margolin wrote:
> But can you return the locally defined function to an outer scope and 
> still call it?  I doubt very much that GCC can do upward funargs.

GCC cannot. But the 'trampoline' package [1] can do it. In fact, it
uses the same techniques as GCC does: a trampoline, mprotect and I
cache flushing.

               Bruno


[1] ftp://ftp.santafe.edu/pub/gnu/ffcall-1.10.tar.gz
From: Michael Ash
Subject: Re: gcc ~= lisp in C
Date: 
Message-ID: <1112284449.58714@nfs-db1.segnet.com>
Camm Maguire <····@enhanced.com> wrote:
> Greetings!  Its amazing how much lisp one can get in C via gcc's
> extensions to the C language.  Just discovered the following 'lexical
> closure' over V:
> 
>  {
>    static int f(int i,int j,int n,double *d) {
>      
>      double *de=d+n;
>      
>      for (;d<de;d++)
>        *d=-1.0/V;
>      
>      return 0;
>      
>    }
>    
> 
>    E(sl_pddistr_init,f,e,0,0,de);
>    
>  }
>  
> Ignore the E cpp macro, which just returns -1 with diagnostics on
> function error.  But here we pass a locally defined function accessing
> variable V defined in an outer scope as a 'first class object' to
> sl_pddistr_init. 
> 
> Neat.

Unfortunately, it's pretty useless. I discovered this a while ago and had 
fun with it until I realized how broken it is.

In order to remain compatible with C function pointers, which don't have 
any space for data other than the address of the function itself, gcc has 
to do some evil. Pointers to the inner function are taken by constructing 
a trampoline on the stack which sets up the enclosing lexical environment 
and then jumps to the function body. This is a bad idea for several 
reasons.

Both the trampoline and the enclosing lexical environment live on the 
stack, not in any global space. This means that they're both invalid once 
the function returns, so returning pointers to these inner functions is a 
big no-no.

The trampoline is executable code living on the stack. On machines with a 
non-executable stack, which are becoming more and more common these days 
to prevent buffer-overflow attacks and the like, they simply won't work at 
all.

The trampoline is constructed and then executed at some point in the 
future. Many processors have separate data and instruction caches which 
are not automatically synchronized. The instruction cache has to be 
flushed to ensure that stale data isn't executed. If gcc isn't flushing 
this cache, which seems likely, then these trampolines will randomly fail. 
If it is, it's a big performance loss.

This is not meant as a slight against gcc; doing "proper" closures in C is 
impossible without breaking compatibility with other code (and maybe more; 
forcing the programmer to manually "free" the closure when he's done is 
probably necessary as well). But sadly, their current implementation is 
just not practical.
From: Kalle Olavi Niemitalo
Subject: Re: gcc ~= lisp in C
Date: 
Message-ID: <87d5tfhfmo.fsf@Astalo.kon.iki.fi>
Michael Ash <····@mikeash.com> writes:

> Both the trampoline and the enclosing lexical environment live on the 
> stack, not in any global space. This means that they're both invalid once 
> the function returns, so returning pointers to these inner functions is a 
> big no-no.

Right, just like returning a pointer to an automatic variable.
This is documented in the manual.

> The trampoline is executable code living on the stack. On machines with a 
> non-executable stack, which are becoming more and more common these days 
> to prevent buffer-overflow attacks and the like, they simply won't work at 
> all.

GCC puts a note in the object file, so that the linker will know
to mark the stack as executable.  See file_end_indicate_exec_stack
in gcc/varasm.c.  On some platforms, GCC also generates a call to
an __enable_execute_stack function that then calls mprotect.

> The trampoline is constructed and then executed at some point in the 
> future. Many processors have separate data and instruction caches which 
> are not automatically synchronized. The instruction cache has to be 
> flushed to ensure that stale data isn't executed. If gcc isn't flushing 
> this cache, which seems likely, then these trampolines will randomly fail. 
> If it is, it's a big performance loss.

GCC apparently generates a cache flush on those architectures
where it is necessary.  See the end of alpha_initialize_trampoline
in gcc/config/alpha/alpha.c, and "call_pal 0x86" in
gcc/config/alpha/alpha.md.