From mboxrd@z Thu Jan 1 00:00:00 1970 Path: main.gmane.org!not-for-mail From: Zack Weinberg Newsgroups: gmane.emacs.devel Subject: byte-opt.el addition - optimize list of compile-time constants Date: Wed, 08 Dec 2004 01:21:54 -0800 Message-ID: <87u0qxgn65.fsf@codesourcery.com> NNTP-Posting-Host: deer.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: sea.gmane.org 1102497804 5375 80.91.229.6 (8 Dec 2004 09:23:24 GMT) X-Complaints-To: usenet@sea.gmane.org NNTP-Posting-Date: Wed, 8 Dec 2004 09:23:24 +0000 (UTC) Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Wed Dec 08 10:23:16 2004 Return-path: Original-Received: from lists.gnu.org ([199.232.76.165]) by deer.gmane.org with esmtp (Exim 3.35 #1 (Debian)) id 1Cby2V-0003yL-00 for ; Wed, 08 Dec 2004 10:23:15 +0100 Original-Received: from localhost ([127.0.0.1] helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.33) id 1CbyCL-0002YW-9Y for ged-emacs-devel@m.gmane.org; Wed, 08 Dec 2004 04:33:25 -0500 Original-Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.33) id 1CbyBY-0002U4-Lb for emacs-devel@gnu.org; Wed, 08 Dec 2004 04:32:37 -0500 Original-Received: from exim by lists.gnu.org with spam-scanned (Exim 4.33) id 1CbyBX-0002TN-Bq for emacs-devel@gnu.org; Wed, 08 Dec 2004 04:32:35 -0500 Original-Received: from [199.232.76.173] (helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.33) id 1CbyBW-0002Sl-HN for emacs-devel@gnu.org; Wed, 08 Dec 2004 04:32:34 -0500 Original-Received: from [65.74.133.9] (helo=mail.codesourcery.com) by monty-python.gnu.org with esmtp (TLSv1:DES-CBC3-SHA:168) (Exim 4.34) id 1Cby1F-0001X1-1r for emacs-devel@gnu.org; Wed, 08 Dec 2004 04:21:57 -0500 Original-Received: (qmail 9210 invoked from network); 8 Dec 2004 09:21:54 -0000 Original-Received: from localhost (HELO taltos.codesourcery.com) (zack@127.0.0.1) by mail.codesourcery.com with SMTP; 8 Dec 2004 09:21:54 -0000 Original-Received: by taltos.codesourcery.com (sSMTP sendmail emulation); Wed, 8 Dec 2004 01:21:54 -0800 Original-To: emacs-devel@gnu.org User-Agent: Gnus/5.110003 (No Gnus v0.3) Emacs/21.3 (gnu/linux) X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: main.gmane.org gmane.emacs.devel:30856 X-Report-Spam: http://spam.gmane.org/gmane.emacs.devel:30856 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