From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Lynn Winebarger Newsgroups: gmane.emacs.devel Subject: Re: native compilation units Date: Tue, 14 Jun 2022 23:03:29 -0400 Message-ID: References: Mime-Version: 1.0 Content-Type: multipart/alternative; boundary="000000000000b9ba2805e173c407" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="26371"; mail-complaints-to="usenet@ciao.gmane.io" Cc: Andrea Corallo , emacs-devel@gnu.org To: Stefan Monnier Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Wed Jun 15 05:06:48 2022 Return-path: Envelope-to: ged-emacs-devel@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1o1JMl-0006e4-MM for ged-emacs-devel@m.gmane-mx.org; Wed, 15 Jun 2022 05:06:47 +0200 Original-Received: from localhost ([::1]:34728 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1o1JMk-000633-Ip for ged-emacs-devel@m.gmane-mx.org; Tue, 14 Jun 2022 23:06:46 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:43466) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1o1JJq-0004OP-Hn for emacs-devel@gnu.org; Tue, 14 Jun 2022 23:03:46 -0400 Original-Received: from mail-vs1-xe2e.google.com ([2607:f8b0:4864:20::e2e]:46028) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1o1JJm-0003NP-Nb for emacs-devel@gnu.org; Tue, 14 Jun 2022 23:03:45 -0400 Original-Received: by mail-vs1-xe2e.google.com with SMTP id q14so10722368vsr.12 for ; Tue, 14 Jun 2022 20:03:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=0QoNGj5yUutpIMAhyDN+2NwIoQ1HM7tjSx8azSLv/ts=; b=e6Q2DuM1tQoT0vGwjup3byTKgfm3h1Nm4cf7/2w9vXB81SRXUPeb96Njuv1fx+rUwM HJ5JaUx44rD7r5Qef1Kw7Xp4QSG+hJre4hmEpQkr41YRX6H9kGHyvc3IeN69G41eb0db L12X5A9unG/3stejcaHj8T/JP5z8LAS7HtK5XGw9Omk8u/SPJYewyoJVYYtwAc7jLP8A mSrv4H7yzo6lbwiFNXEbe9AwHxZscIrsrJ3QtgdcQAgOO34FfmDIpIDmHJzuLd2scg4C 3pVxvDUTnPmQXZ9aDafPvgehsCk+RQU6HHbW5Q61wW7bDxfL+MtLro4RqkKIQ5a35ggn 9dYA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=0QoNGj5yUutpIMAhyDN+2NwIoQ1HM7tjSx8azSLv/ts=; b=rvBY0nxLWJ/qe0VUdGxnmBqTIvDcGiL8As+bjCK5DTekeA9689m1MSeoFIwej/EaPd vFQb77kKdT2wwKof9l8+95P0fwUn8amDyE/+vVD4cq4ecrcTtJT3fX/ntfiawMbs7ldn 9U7dxK9rsrCUtYWtuKK5fxejqd6y7RDZPFRW0nCVaG/mJf0jzGCPnPrL/EiqFM5j0xER QRcChAb89MWQzRa4fyhLos7hwbW3Wu7GYK3fo76z96EQ9OWNPJ2oLhpIMV8VKOKgjv8Z IYdd1+0eMXLUCOWz31oP9UmMN4/I7C+2o2HLZjXcc+x5DzHbUXyUf5JfCJDVNRWVaSN9 V3Lg== X-Gm-Message-State: AJIora+7Bbk1ToyqNo8+wl8rM0DRSJybj5Fj9LYP9BGPacgGx0ZDun44 CE+SIbHlE35qpHnnzxF152t0tKcQ6nLDQ/BjGPNCgRg8NZQ= X-Google-Smtp-Source: AGRyM1tyI2WYPcyxuL29BrVJbQ9MDUNscYAUoAbOIQkoxQI8WzQohMVeFbBzmqi3bL3FlWT4vVPtSziboYYeJxZewGE= X-Received: by 2002:a67:ec8d:0:b0:34e:1ece:9c01 with SMTP id h13-20020a67ec8d000000b0034e1ece9c01mr932193vsp.64.1655262221546; Tue, 14 Jun 2022 20:03:41 -0700 (PDT) In-Reply-To: Received-SPF: pass client-ip=2607:f8b0:4864:20::e2e; envelope-from=owinebar@gmail.com; helo=mail-vs1-xe2e.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.io gmane.emacs.devel:291188 Archived-At: --000000000000b9ba2805e173c407 Content-Type: text/plain; charset="UTF-8" On Mon, Jun 13, 2022 at 1:15 PM Stefan Monnier wrote: > > To be clear, I'm trying to first understand what Andrea means by "safe". > > I'm assuming it means the result agrees with whatever the byte > > compiler and VM would produce for the same code. > > Not directly. It means that it agrees with the intended semantics. > That semantics is sometimes accidentally defined by the actual > implementation in the Lisp interpreter or the bytecode compiler, but > that's secondary. > What I mean is, there's not really a spec defining the semantics to judge against. But every emacs has a working byte compiler, and only some have a native compiler. If the users with the byte compiler get a different result than the users that have the native compiler, my guess is that the code would be expected to be rewritten so that it produces the expected result from the byte compiler (at least until the byte compiler is revised). To the extent the byte compiler is judged to produce an incorrect result, it's probably an area of the language that was not considered well-defined enough (or useful enough) to have been used previously. Or it was known that the byte compiler's semantics weren't very useful for a particular family of expressions. > The semantic issue is that if you call > > (foo bar baz) > > it normally (when `foo` is a global function) means you're calling the > function contained in the `symbol-function` of the `foo` symbol *at the > time of the function call*. So compiling this to jump directly to the > code that happens to be contained there during compilation (or the code > which the compiler expects to be there at that point) is unsafe in > the sense that you don't know whether that symbol's `symbol-function` > will really have that value when we get to executing that function call. > > The use of `cl-flet` (or `cl-labels`) circumvents this problem since the > call to `foo` is now to a lexically-scoped function `foo`, so the > compiler knows that the code that is called is always that same one > (there is no way to modify it between the compilation time and the > runtime). > The fact that cl-flet (and cl-labels) are defined to provide immutable bindings is really a surprise to me. However, what I was trying to do originally was figure out if there was any situation where Andrea's statement (in another reply): the compiler can't take advantage of interprocedural optimizations (such > as inline etc) as every function in Lisp can be redefined in every > moment. Remember, I was asking whether concatenating a bunch of files together as a library would have the same meaning as compiling and linking the object files. There is one kind of expression where Andrea isn't quite correct, and that is with respect to (eval-when-compile ...). Those *can* be treated as constants, even without actually compiling them first. If I understand the CL-Hyperspec/Emacs Lisp manual, the following expression: ------------------------------------ (let () (eval-when-compile (defvar a (lambda (f) (lambda (x) (f (+ x 5)))))) (eval-when-compile (defvar b (lambda (y) (* y 3)))) (let ((f (eval-when-compile (a b)))) (lambda (z) (pow z (f 6))))) ------------------------------------ can be rewritten (using a new form "define-eval-time-constant") as ------------------------------------ (eval-when-compile (define-eval-time-constant ct-r1 (defvar a (lambda (f) (lambda (x) (f (+ x 5)))))) (define-eval-time-constant ct-r2 (defvar b (lambda (y) (* y 3)))) (define-eval-time-constant ct-r3 (a b))) (let () ct-r1 ct-r2 (let ((f ct-r3)) (lambda (z) (pow z (f 6))))) ------------------------------------ Now the optimizer can treat ct-r1,ct-r2, and ct-r3 as constants for the purpose of propagation, *without actually determining their value*. So this could be rewritten as ------------------------------------------- (eval-when-compile (define-eval-time-constant ct-r1 (defvar a (lambda (f) (lambda (x) (f (+ x 5)))))) (define-eval-time-constant ct-r2 (defvar b (lambda (y) (* y 3)))) (define-eval-time-constant ct-r3 (a b))) (let () (lambda (z) (pow z (ct-r3 6)))) ------------------------------------------------ If I wanted to "link" files A, B, and C together, with A exporting symbols a1,..., and b exporting symbols b1,...., I could do the following: (eval-when-compile (eval-when-compile ) ) I doubt I'm bringing up topics or ideas that are new to you. But if > > I do make use of semantic/wisent, I'd like to know the result can be > > fast (modulo garbage collection, anyway). > > It's also "modulo enough work on the compiler (and potentially some > primitive functions) to make the code fast". > Absolutely, it just doesn't look to me like a very big lift compared to, say, what Andrea did. > > I've been operating under the assumption that > > > > - Compiled code objects should be first class in the sense that > > they can be serialized just by using print and read. That seems to > > have been important historically, and was true for byte-code > > vectors for dynamically scoped functions. It's still true for > > byte-code vectors of top-level functions, but is not true for > > byte-code vectors for closures (and hasn't been for at least > > a decade, apparently). > > It's also true for byte-compiled closures, although, inevitably, this > holds only for closures that capture only serializable values. > > > But I see that closures are being implemented by calling an ordinary > > function that side-effects the "constants" vector. > > I don't think that's the case. Where do you see that? > My misreading, unfortunately. That does seem like a lot of copying for anyone relying on efficient closures. Does this mean the native compiled code can only produce closures in byte-code form? Assuming dlopen loads the shared object into read-only memory for execution. > > Wedging closures into the byte-code format that works for dynamic scoping > > could be made to work with shared structures, but you'd need to modify > > print to always capture shared structure (at least for byte-code > vectors), > > not just when there's a cycle. > > It already does. > > Ok, I must be missing it. I know eval_byte_code *creates* the result shown below with shared structure (the '(5)], but I don't see anything in the printed text to indicate it if read back in. (defvar z (byte-compile-sexp '(let ((lx 5)) (let ((f (lambda () lx)) (g (lambda (ly) (setq lx ly)))) `(,f ,g))))) (ppcb z) (byte-code "\300C\301\302 \"\301\303 \" D\207" [5 make-closure #[0 "\300\242\207" [V0] 1] #[257 "\300 \240\207" [V0] 3 "\n\n(fn LY)"]] 5) (defvar zv (eval z)) (ppcb zv) (#[0 "\300\242\207" [(5)] 1] #[257 "\300 \240\207" [(5)] 3 "\n\n(fn LY)"]) (defvar zvs (prin1-to-string zv)) (ppcb zvs) "(#[0 \"\\300\\242\\207\" [(5)] 1] #[257 \"\\300 \\240\\207\" [(5)] 3 \"\n\n(fn LY)\"])" (defvar zz (car (read-from-string zvs))) (ppcb zz) (#[0 "\300\242\207" [(5)] 1] #[257 "\300 \240\207" [(5)] 3 "\n\n(fn LY)"]) (let ((f (car zz)) (g (cadr zz))) (print (eq (aref (aref f 2) 0) (aref (aref g 2) 0)) (current-buffer))) nil Of course, those last bindings of f and g were just vectors, not byte-code vectors, but the (5) is no longer shared state. > > Then I think the current approach is suboptimal. The current > > byte-code representation is analogous to the a.out format. > > Because the .elc files run code on load you can put an arbitrary > > amount of infrastructure in there to support an implementation of > > compilation units with exported compile-time symbols, but it puts > > a lot more burden on the compiler and linker/loader writers than just > > being explicit would. > > I think the practical performance issues with ELisp code are very far > removed from these problems. Maybe some day we'll have to face them, > but we still have a long way to go. > I'm sure you're correct in terms of the current code base. But isn't the history of these kinds of improvements in compilers for functional languages that coding styles that had been avoided in the past can be adopted and produce faster code than the original? In this case, it would be enabling the pervasive use of recursion and less reliance on side-effects. Improvements in the gc wouldn't hurt, either. > >> You explicitly write `(require 'cl-lib)` but I don't see any > >> > >> -*- lexical-binding:t -*- > >> > >> anywhere, so I suspect you forgot to add those cookies that are needed > >> to get proper lexical scoping. > >> Ok, wow, I really misread the NEWS for 28.1 where it said > > The 'lexical-binding' local variable is always enabled. > > Are you sure? How do you do that? > Some of the errors you showed seem to point very squarely towards the > code being compiled as dyn-bound ELisp. > > My quoting wasn't very effective. That last line was actually line 2902 of NEWS.28: "** The 'lexical-binding' local variable is always enabled. Previously, if 'enable-local-variables' was nil, a 'lexical-binding' local variable would not be heeded. This has now changed, and a file with a 'lexical-binding' cookie is always heeded. To revert to the old behavior, set 'permanently-enabled-local-variables' to nil." I feel a little less silly about my optimistic misreading of the first line, at least. Lynn --000000000000b9ba2805e173c407 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
On Mon, Jun 13, 2022 at 1:15 PM Stefan Mo= nnier <mon= nier@iro.umontreal.ca> wrote:
> To be clear, I'm t= rying to first understand what Andrea means by "safe".
> I'm assuming it means the result agrees with whatever the byte
> compiler and VM would produce for the same code.

Not directly.=C2=A0 It means that it agrees with the intended semantics. That semantics is sometimes accidentally defined by the actual
implementation in the Lisp interpreter or the bytecode compiler, but
that's secondary.

What I mean is, t= here's not really a spec defining the semantics to
judge agai= nst.=C2=A0 But every emacs has a working byte compiler, and only
= some have a native compiler.=C2=A0 If the users with the byte compiler get = a=C2=A0
different result than the users that have the native comp= iler, my guess
is that the code would be expected to be rewritten= so that it produces the
expected result from the byte compiler (= at least until the byte compiler is=C2=A0
revised).=C2=A0 To the = extent the byte compiler is judged to produce an incorrect=C2=A0
= result, it's probably an area of the language that was not considered w= ell-defined
enough (or useful enough) to have been used previousl= y.=C2=A0 Or it was known
that the byte compiler's semantics w= eren't very useful for a particular family
of expressions.
=C2=A0
The semantic issue is that if you call

=C2=A0 =C2=A0 (foo bar baz)

it normally (when `foo` is a global function) means you're calling the<= br> function contained in the `symbol-function` of the `foo` symbol *at the
time of the function call*.=C2=A0 So compiling this to jump directly to the=
code that happens to be contained there during compilation (or the code
which the compiler expects to be there at that point) is unsafe in
the sense that you don't know whether that symbol's `symbol-functio= n`
will really have that value when we get to executing that function call.
The use of `cl-flet` (or `cl-labels`) circumvents this problem since the call to `foo` is now to a lexically-scoped function `foo`, so the
compiler knows that the code that is called is always that same one
(there is no way to modify it between the compilation time and the
runtime).

The fact that cl-flet (and cl= -labels) are defined to provide immutable=C2=A0
bindings is r= eally a surprise to me.=C2=A0=C2=A0
However, what I was trying to= do originally was figure out if there was
any situation where An= drea's statement (in another reply):

the compiler can't take advanta= ge of interprocedural optimizations (such
as inline etc) as every functi= on in Lisp can be redefined in every
moment.

=
Remember, I was asking whether concatenating a bunch of files
together as a library would have the same meaning as compiling and linkin= g
the object files.

There is one kind of= expression where Andrea isn't quite correct, and that
is wit= h respect to (eval-when-compile ...).=C2=A0 Those *can* be treated as const= ants,
even without actually compiling them first.=C2=A0 If I unde= rstand the=C2=A0
CL-Hyperspec/Emacs Lisp manual, the following ex= pression:
------------------------------------
(let ()<= br>=C2=A0 (eval-when-compile (defvar a (lambda (f) (lambda (x) (f (+ x 5)))= )))
=C2=A0 (eval-when-compile (defvar b (lambda (y) (* y 3))))
=C2=A0= (let ((f (eval-when-compile (a b))))
=C2=A0 =C2=A0 (lambda (z)
=C2= =A0 =C2=A0 =C2=A0 (pow z (f 6)))))
--------------------------= ----------

can be r= ewritten (using a new form "define-eval-time-constant") as
<= div>
------------------------------------
(eval-whe= n-compile
=C2=A0 (define-eval-time-constant ct-r1 (defvar a (lambda (f) = (lambda (x) (f (+ x 5))))))
=C2=A0 (define-eval-time-constant ct-r2 (def= var b (lambda (y) (* y 3))))
=C2=A0 (define-eval-time-constant ct-r3 (a = b)))
(let ()
=C2=A0 ct-r1
=C2=A0 ct-r2
=C2=A0 (let ((f ct-r3)) = =C2=A0
=C2=A0 =C2=A0 (lambda (z)
=C2=A0 =C2=A0 =C2=A0 (pow z (f 6))))= )
------------------------------------
Now the opti= mizer can treat ct-r1,ct-r2, and ct-r3 as constants for the purpose of prop= agation,
*without actually determining their value*.=C2=A0 So thi= s could be rewritten as
-----------------------------------------= --
(eval-when-compile
=C2=A0 (define-eval-time-constant ct-r1 = (defvar a (lambda (f) (lambda (x) (f (+ x 5))))))
=C2=A0 (define-eval-ti= me-constant ct-r2 (defvar b (lambda (y) (* y 3))))
=C2=A0 (define-eval-t= ime-constant ct-r3 (a b)))
(let ()
=C2=A0 (lambda (z)
=C2=A0 =C2= =A0 (pow z (ct-r3 6))))
-------------------------------------= -----------

If I wanted to "link" files = A, B, and C together, with A exporting symbols a1,..., and b exporting symb= ols b1,....,
I could do the following:
(eval-when-compi= le
=C2=A0 (eval-when-compile
=C2=A0 =C2=A0 =C2=A0<te= xt of A>
=C2=A0 =C2=A0 )
=C2=A0 <text of B with a= 1,...,and replaced by (eval-when-compile a1), ....>
)
<text of C with a1,... replaced by (eval-when-compile (eval-when-compi= le a1))... and b1,... replaced by (eval-when-compile b1),...

=
And now the (eval-when-compile) expressions can be freely propag= ated within the=C2=A0 code of each file,
as they are constant exp= ressions.=C2=A0=C2=A0

I don't know how the nat= ive compiler is handling "eval-when-compile" expressions now, but= this should
give that=C2=A0optimizer pass a class of expressions= where "-O3" is in fact safe to apply.
Then it's ju= st a matter of creating the macros to make producing those expressions in a= ppropriate contexts
convenient to do in practice.

<= /div>
> I doubt I'm= bringing up topics or ideas that are new to you.=C2=A0 But if
> I do make use of semantic/wisent, I'd like to know the result can = be
> fast (modulo garbage collection, anyway).

It's also "modulo enough work on the compiler (and potentially som= e
primitive functions) to make the code fast".

=
Absolutely, it just doesn't look to me like a very big lift = compared to, say, what Andrea did.
=C2=A0
> I've been operating under the ass= umption that
>
>=C2=A0 =C2=A0 - Compiled code objects should be first class in the sens= e that
>=C2=A0 =C2=A0 they can be serialized just by using print and read.=C2= =A0 That seems to
>=C2=A0 =C2=A0 have been important historically, and was true for byte-c= ode
>=C2=A0 =C2=A0 vectors for dynamically scoped functions.=C2=A0 It's = still true for
>=C2=A0 =C2=A0 byte-code vectors of top-level functions, but is not true= for
>=C2=A0 =C2=A0 byte-code vectors for closures (and hasn't been for a= t least
>=C2=A0 =C2=A0 a decade, apparently).

It's also true for byte-compiled closures, although, inevitably, this holds only for closures that capture only serializable values.
=C2=A0
>= But I see that closures are being implemented by calling an ordinary
> function that side-effects the "constants" vector.

I don't think that's the case.=C2=A0 Where do you see that?
My misreading, unfortunately.=C2=A0
That does seem = like a lot of copying for anyone relying on efficient closures.
D= oes this mean the native compiled code can only produce closures in byte-co= de
form?=C2=A0 Assuming dlopen loads the shared object into read-= only memory for=C2=A0
execution.
=C2=A0
> Wedging closures into the byte-code format that works for dynamic scop= ing
> could be made to work with shared structures, but you'd need to mo= dify
> print to always capture shared structure (at least for byte-code vecto= rs),
> not just when there's a cycle.

It already does.

Ok, I must be missing it.=C2=A0 I know eval_byte_code= *creates* the result shown
below with shared structure (the '= ;(5)], but I don't see anything in the printed=C2=A0
text to = indicate it if read back in.
(defvar z
=C2=A0 (byte-compile-sexp
=C2=A0 =C2=A0'(let ((lx 5))
=C2=A0 =C2=A0 =C2=A0 (let ((f (lambda () l= x))
=C2=A0 =C2=A0(g (lambda (l= y) (setq lx ly))))
`(,f ,g)))))=
(ppcb z)
(byte-code "\300C\301\302 \"\301\303 \"= ; D\207"
=C2=A0 [5 make-= closure
=C2=A0 =C2=A0 =C2=A0#[= 0 "\300\242\207"
= =C2=A0[V0]
=C2=A01]
=C2=A0 =C2=A0 =C2=A0#[257 "\300 \2= 40\207"
=C2=A0 =C2=A0[V0= ]
=C2=A0 =C2=A03 "\n\n(f= n LY)"]]
=C2=A0 5)
<= /div>
(defvar zv (eval z))
(ppcb zv)
<= div>(#[0 "\300\242\207"
=C2=A0 =C2=A0 =C2=A0[(5)]
=C2= =A0 =C2=A0 =C2=A01]
=C2=A0#[257 = "\300 \240\207"
=C2=A0= =C2=A0 =C2=A0 =C2=A0[(5)]
=C2= =A0 =C2=A0 =C2=A0 =C2=A03 "\n\n(fn LY)"])

(defv= ar zvs (prin1-to-string zv))
(pp= cb zvs)
"(#[0 \"\\300\= \242\\207\" [(5)] 1] #[257 \"\\300 \\240\\207\" [(5)] 3 \&qu= ot;\n\n(fn LY)\"])"

(defvar zz (car (rea= d-from-string zvs)))
(ppcb zz)
(#[0 "\300\242\207"
=C2= =A0 =C2=A0 =C2=A0[(5)]
=C2=A0 =C2=A0 =C2=A01]
=C2=A0#[257 "\300 = \240\207"
=C2=A0 =C2=A0 =C2=A0 =C2=A0[(5)]
=C2=A0 =C2=A0 =C2=A0 = =C2=A03 "\n\n(fn LY)"])
(let ((f (car zz)) (g (cadr zz)))
= =C2=A0 (print (eq (aref (aref f 2) 0) (aref (aref g 2) 0)) (current-buffer)= ))

nil

Of course, those last bindings of f and g were just vectors, = not byte-code vectors, but
the (5) is no longer shared state.
=C2=A0
&g= t; Then I think the current approach is suboptimal.=C2=A0 The current
> byte-code representation is analogous to the a.out format.
> Because the .elc files run code on load you can put an arbitrary
> amount of infrastructure in there to support an implementation of
> compilation units with exported compile-time symbols, but it puts
> a lot more burden on the compiler and linker/loader writers than just<= br> > being explicit would.

I think the practical performance issues with ELisp code are very far
removed from these problems.=C2=A0 Maybe some day we'll have to face th= em,
but we still have a long way to go.

I&#= 39;m sure you're correct in terms of the current code base.=C2=A0 But i= sn't the history
of these kinds of improvements in compilers = for functional languages that
coding styles that had been avoided= in the past can be adopted and produce
faster code than the orig= inal?=C2=A0 In this case, it would be enabling the pervasive
use = of recursion and less reliance on side-effects.=C2=A0 Improvements in the g= c
wouldn't hurt, either.
=C2=A0
>> You explicitly write `(require 'cl-lib)` but I don't see a= ny
>>
>>=C2=A0 =C2=A0 =C2=A0-*- lexical-binding:t -*-
>>
>> anywhere, so I suspect you forgot to add those cookies that are ne= eded
>> to get proper lexical scoping.
>> Ok, wow, I really misread the NEWS for 28.1 where it said
> The 'lexical-binding' local variable is always enabled.

Are you sure?=C2=A0 How do you do that?
Some of the errors you showed seem to point very squarely towards the
code being compiled as dyn-bound ELisp.

My quoting= wasn't very effective.=C2=A0 That last line was actually line 2902 of = NEWS.28:
=C2=A0 =C2=A0 =C2=A0"** The 'lexical-binding' local variable is a= lways enabled.
    Previously, if &=
#39;enable-local-variables' was nil, a 'lexical-binding'
    local variable would not be heeded.  This has now changed, and a file
    with a 'lexical-binding' cookie is always heeded.  To revert to=
 the=C2=A0
=C2=A0 =C2=A0 =C2=A0 =C2=A0 old behavior, set 'permanently-enable= d-local-variables' to nil."

I feel= a little less silly about my optimistic misreading of the first line, at l= east.

Lynn

=C2=A0
--000000000000b9ba2805e173c407--