unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* byte-opt.el addition - optimize list of compile-time constants
@ 2004-12-08  9:21 Zack Weinberg
  2004-12-08 16:56 ` Stefan Monnier
  0 siblings, 1 reply; 33+ messages in thread
From: Zack Weinberg @ 2004-12-08  9:21 UTC (permalink / raw)



Consider a construct like the following, which might appear in a major
mode definition.  (This example is from my half-written major mode for
editing GCC machine descriptions.  These are mostly Lisp-like but
contain large blocks of embedded C, which it would be nice to apply
c-mode to.  The MMM library tries to make that possible.  It doesn't
work very well with c-mode right now, but that's irrelevant to this email.)

(when (require 'mmm-auto nil t)
  (mmm-add-classes
   '((md-embedded-c
      :submode c-mode
      :front "^{"
      :back "^}"
      :include-front t
      :include-back t
      ;; If the 'back' } is on a line by itself, include the carriage
      ;; return too; this makes the submode background highlight look
      ;; less strange.
      :back-offset #'(lambda () (when (eq (following-char) ?\n)
                                  (forward-char 1)))
      )))

One would like the anonymous :back-offset function to be
byte-compiled.  This does not happen (with 21.3.1) because the byte
compiler does not look inside a quoted form for lambda expressions.

I can make it get byte-compiled by using ` instead of ':

(when (require 'mmm-auto nil t)
  (mmm-add-classes
   `((md-embedded-c
      :submode c-mode
      :front "^{"
      :back "^}"
      :include-front t
      :include-back t
      ;; If the 'back' } is on a line by itself, include the carriage
      ;; return too; this makes the submode background highlight look
      ;; less strange.
      :back-offset #'(lambda () (when (eq (following-char) ?\n)
                                  (forward-char 1)))
      )))

The byte compiler then sees, after ` gets macroexpanded,

  (list (list
          'md-embedded-c
          :submode 'c-mode
          :front "^{"
          :back "^}"
          :include-front t
          :include-back t
          :back-offset (function (lambda () ...))))

and it *does* look into that for the nested lambda and compile it.
However, the cost of this is a substantially less efficient byte-code
sequence for the outer form.  Instead of having the entire
S-expression in the constant vector, each individual component goes
into the vector, and a rather lengthy byte-code sequence is generated
to execute the (list (list ...)) at runtime.  This does not matter
terribly much for *this* example, which is going to be executed once
when the file is loaded, but I can easily imagine circumstances where
it would matter.  I can work around this by writing (eval-when-compile
...) around the `(...) form, but that's just plain ugly.

I dug through the byte-compiler a bit and determined that it makes no
attempt whatsoever to optimize (list ...) expressions.  So I wrote a
basic byte-optimize-list function, and here it is, suitable for being
slapped into byte-opt.el.

(defun byte-optimize-list (form)
   ; (list) -> nil
  (if (null (cdr form))
      nil

    ; (list X Y Z ...) -> (quote (X Y Z)) when X Y Z are all
    ; compile-time constants.  In addition to the set of things for
    ; which byte-compile-constp is true, detect embedded
    ; (function ...) expressions and compile them, which turns them
    ; into compile-time constants.

    (catch :nonconstant
      (let ((new-form
	     (mapcar (lambda (elem)
		       (if (consp elem)
			 (cond 
					; (quote X) -> X
			  ((eq (car elem) 'quote)
			   (car (cdr elem)))
			                ; (function X) -> byte-compiled X
			  ((eq (car elem) 'function)
			   (byte-compile (car (cdr elem))))
					; ??? (lambda ...) -> byte-compile it
			  ((eq (car elem) 'lambda)
			   (byte-compile elem))
			  (t (throw :nonconstant form)))
			 (if (byte-compile-constp elem)
			     elem
			   (throw :nonconstant form))))
		     (cdr form))))

	; If we get here, new-form is a list of constants; turn it
	; into a constant.
	(list 'quote new-form)))))

(put 'list 'byte-optimizer 'byte-optimize-list)

This does have several issues - I'm not proposing it be put in as is.
First, I'm unsure I'm unsure whether it is appropriate to detect and
byte-compile a bare lambda.  The manual dithers on the subject.

Second, I'm not sure why I'm even getting uncompiled lambdas in the
list.  Shouldn't the master byte-opt loop, operating depth-first, have
already done it?  Having to do it here means that if we have a list
which has several lambda expressions followed by something that isn't
a compile-time constant, we'll compile the lambdas, discover the
non-constant, throw away the compiled functions, and have to do them
over again later.

Rather less important is the question of whether I should be
trying to optimize lists that aren't entirely constants.  It is
possible that, e.g. given 

(list 1 2 3 (+ var1 var2) 4 5 6)

it would be appropriate to translate it to

(nconc '(1 2 3) (+ var1 var2) '(4 5 6))

But I can see that being not a good idea, as well, so I didn't do it.

Finally, I'm not sure about coding style.  The catch/let/mapcar/nested
conditionals sequence is rather ugly, IMO, but I couldn't think of a
better way.

Thoughts?

(please cc:, I'm not subscribed to emacs-devel.)

zw

^ permalink raw reply	[flat|nested] 33+ messages in thread

end of thread, other threads:[~2004-12-10  5:50 UTC | newest]

Thread overview: 33+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-12-08  9:21 byte-opt.el addition - optimize list of compile-time constants Zack Weinberg
2004-12-08 16:56 ` Stefan Monnier
2004-12-08 18:59   ` Zack Weinberg
2004-12-08 19:27     ` Stefan Monnier
2004-12-08 19:45       ` Zack Weinberg
2004-12-08 19:56         ` Stefan Monnier
2004-12-08 20:14           ` Nick Roberts
2004-12-08 22:47       ` Zack Weinberg
2004-12-08 23:40         ` Stefan Monnier
2004-12-09  1:20           ` Zack Weinberg
2004-12-09  2:11             ` Stefan Monnier
2004-12-09  2:33               ` Zack Weinberg
2004-12-09  2:46                 ` Miles Bader
2004-12-09  3:08                   ` Zack Weinberg
2004-12-09  3:28                     ` Miles Bader
2004-12-09  3:48                       ` Zack Weinberg
2004-12-09  4:04                         ` Miles Bader
2004-12-09  4:41                           ` Zack Weinberg
2004-12-09  4:52                             ` Stefan Monnier
2004-12-09  5:33                               ` Zack Weinberg
2004-12-09  5:39                                 ` Miles Bader
2004-12-09  6:49                                   ` Zack Weinberg
2004-12-09 15:22                                     ` Thien-Thi Nguyen
2004-12-10  5:50                                       ` Richard Stallman
2004-12-09  9:22                                 ` David Kastrup
2004-12-09  4:54                             ` Miles Bader
2004-12-09  9:20                             ` David Kastrup
2004-12-09  4:35                 ` Stefan Monnier
2004-12-09  4:55                   ` Zack Weinberg
2004-12-09  5:13                     ` Stefan Monnier
2004-12-09  9:10           ` David Kastrup
2004-12-08 19:33     ` Paul Pogonyshev
2004-12-09 10:34       ` Andreas Schwab

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).