Hi, I have something like the following in mind:
(defvar *x*) (defun foo () (let ((x (make-bar …))) (declare (dynamic-extent x)) (setq *x* x) (return-with-dynamic-extent x))) (defun test () (eql *x* (foo))) If the result of test is not guaranteed to be t, implementations should have more freedom to move stack-allocated values up and down the stack, which could potentially simplify things internally. For users, I don't think this would be a great restriction, because it seems to me it's unlikely that would want to rely on eq/eql in the first place here… [eql is not only defined on numbers and chars, but also other objects, and is the same as eq in most cases…] Pascal On 7 May 2012, at 01:41, Peter Seibel wrote: > This is verging on worrying about the color of the bikeshed but why should > EQL not necessarily work with these objects. I can see EQ not working, by > analogy with numbers and characters but EQL should perhaps do a value > comparison. (Hmmm, are you assuming that these objects are immutable? Sort of > seems like they'd better be.) > > -Peter > > On Sun, May 6, 2012 at 12:27 PM, Pascal Costanza <p...@p-cos.net> wrote: > Hi, > > Here is a description of what I think is a feature missing in Common Lisp, > both the standard and current implementations, namely a more complete way to > ensure stack allocation of objects. > > Based on some recent experience with translating some C++ benchmarks to > Common Lisp - more specifically the fluid animate benchmark from the PARSEC > benchmark suite - being able to allocate objects on the stack can be a major > performance win. Unfortunately, this is harder than necessary to achieve in > Common Lisp. > > Here is a simplified example. Assume you have the following class definition > in C++: > > struct vec { > int x; int y; int z; > vec(int x, int y, int z): x(x), y(y), z(z) {} > }; > > Then you can define the following operations on such a class: > > vec vplus(vec v1, vec v2) { > vec v(v1.x+v2.x, v1.y+v2.y, v1.z+v2.z); > return vec; > } > > In such a function definition, the local variable v is stack allocated, plus > the return value is returned by value, not by reference. This holds for any > intermediate values as well, so if you have other operations like vminus, > vmul and vdiv, etc., defined accordingly, you can write expressions like this: > > vplus(v1, vmul(v2, v3)); > > …and rest assured that all intermediate values are allocated on the stack as > well. (C++ loses some performance in some cases because it can happen that it > relies too much on copying of data structures, but this has been largely > resolved in C++11, where move operations are supported really well.) > > In Common Lisp, you can declare local variables with dynamic extent: > > (let ((v (make-vec :x 1 :y 2 :z 3))) > (declare (dynamic-extent v)) > …) > > This has the effect that (in Common Lisp that support this) the struct that v > refers to is stack allocated. Unfortunately, Common Lisp implementations can > only do this for data structure constructors that the implementation knows > about - there is no way for users to add additional operations to the set of > known dynamic extent 'allocators.' > > For example, if you say the following: > > (defun vplus (v1 v2) > (let ((v (make-vec :x (+ (vec-x v1) (vec-x v2)) > :y (+ (vec-y v1) (vec-y v2)) > :z (+ (vec-z v1) (vec-z v2))))) > (declare (dynamic-extent v)) > v)) > > …you can be very sure that the resulting program will fail, because the > reference to the stack allocated data structure is returned, but the data > will be gone afterwards. This had the consequence that in our attempts at > translating the fluid animate benchmark, we had to introduce all intermediate > results explicitly in separate variables, and be very explicit about all the > necessary computational steps. This was very tedious and the result is not > nice to look at. > > What would be nice is, if we could instead have some form of saying that we > want to return a data structure on the stack: > > (defun vplus (v1 v2) > (let ((v (make-vec :x (+ (vec-x v1) (vec-x v2)) > :y (+ (vec-y v1) (vec-y v2)) > :z (+ (vec-z v1) (vec-z v2))))) > (declare (dynamic-extent v)) > (return-with-dynamic-extent v)) > > The idea is that now, the data that v refers to is returned by value on the > stack. If the call site receives the value with a matching dynamic-extent > declaration, then v can just stay on the stack. Same (hopefully) for > intermediate results in more complex expressions. > > Just like dynamic-extent declarations, an implementation should be allowed to > ignore return-with-dynamic-extent and replace it with a mere return form. > > To be able to support move operations on the stack, it may be interesting to > specify that eq and eql are not reliable anymore for data that is return by > return-with-dynamic-extent. > > I presented the idea at ELS'12 in Zadar, and Christophe Rhodes commented that > it may also be necessary to have some form of global declaim that states that > a function will always return something with return-with-dynamic-extent, so > it's easier for the compiler to know what to do with a return value. > > I don't claim that I have completely thought this through, but I think this > should be doable and shouldn't have too many negative consequences for the > rest of a system that doesn't use dynamic extents. > > I won't be able to implement this, but I just wanted to make sure that the > idea is on the table, so someone who is interested may pick it up… > > > Pascal > > -- > Pascal Costanza > The views expressed in this email are my own, and not those of my employer. > > > > > _______________________________________________ > pro mailing list > pro@common-lisp.net > http://lists.common-lisp.net/cgi-bin/mailman/listinfo/pro > > > > -- > Peter Seibel > http://www.gigamonkeys.com/ -- Pascal Costanza
_______________________________________________ pro mailing list pro@common-lisp.net http://lists.common-lisp.net/cgi-bin/mailman/listinfo/pro