APPROVE COMMIT
NOTE: This patch has been committed.
# HG changeset patch
# User Aidan Kehoe <kehoea(a)parhasard.net>
# Date 1317565936 -3600
# Node ID d4f334808463b4785db56315e79b893d7a42b5e9
# Parent f0f1fd0d8486dc10d88245ca1f1f83f672557da5
Support inlining labels, bytecomp.el.
lisp/ChangeLog addition:
2011-10-02 Aidan Kehoe <kehoea(a)parhasard.net>
* bytecomp.el (byte-compile-initial-macro-environment):
Add #'declare to this, so it doesn't need to rely on
#'cl-compiling file to determine when we're byte-compiling.
Update #'labels to support declaring labels inline, as Common Lisp
requires.
* bytecomp.el (byte-compile-function-form):
Don't error if FUNCTION is quoting a non-lambda, non-symbol, just
return it.
* cl-extra.el (cl-macroexpand-all):
If a label name has been quoted, expand to the label placeholder
quoted with 'function. This allows the byte compiler to
distinguish between uses of the placeholder as data and uses in
contexts where it should be inlined.
* cl-macs.el:
* cl-macs.el (cl-do-proclaim):
When proclaming something as inline, if it is bound as a label,
don't modify the symbol's plist; instead, treat the first element
of its placeholder constant vector as a place to store compile
information.
* cl-macs.el (declare):
Leave processing declarations while compiling to the
implementation of #'declare in
byte-compile-initial-macro-environment.
tests/ChangeLog addition:
2011-10-02 Aidan Kehoe <kehoea(a)parhasard.net>
* automated/lisp-tests.el:
* automated/lisp-tests.el (+):
Test #'labels and inlining.
diff -r f0f1fd0d8486 -r d4f334808463 lisp/ChangeLog
--- a/lisp/ChangeLog Sun Sep 25 16:12:07 2011 +0100
+++ b/lisp/ChangeLog Sun Oct 02 15:32:16 2011 +0100
@@ -1,3 +1,29 @@
+2011-10-02 Aidan Kehoe <kehoea(a)parhasard.net>
+
+ * bytecomp.el (byte-compile-initial-macro-environment):
+ Add #'declare to this, so it doesn't need to rely on
+ #'cl-compiling file to determine when we're byte-compiling.
+ Update #'labels to support declaring labels inline, as Common Lisp
+ requires.
+ * bytecomp.el (byte-compile-function-form):
+ Don't error if FUNCTION is quoting a non-lambda, non-symbol, just
+ return it.
+ * cl-extra.el (cl-macroexpand-all):
+ If a label name has been quoted, expand to the label placeholder
+ quoted with 'function. This allows the byte compiler to
+ distinguish between uses of the placeholder as data and uses in
+ contexts where it should be inlined.
+ * cl-macs.el:
+ * cl-macs.el (cl-do-proclaim):
+ When proclaming something as inline, if it is bound as a label,
+ don't modify the symbol's plist; instead, treat the first element
+ of its placeholder constant vector as a place to store compile
+ information.
+ * cl-macs.el (declare):
+ Leave processing declarations while compiling to the
+ implementation of #'declare in
+ byte-compile-initial-macro-environment.
+
2011-09-25 Aidan Kehoe <kehoea(a)parhasard.net>
* files.el (binary-file-regexps):
diff -r f0f1fd0d8486 -r d4f334808463 lisp/bytecomp.el
--- a/lisp/bytecomp.el Sun Sep 25 16:12:07 2011 +0100
+++ b/lisp/bytecomp.el Sun Oct 02 15:32:16 2011 +0100
@@ -494,6 +494,11 @@
(if byte-compile-delete-errors
form
(funcall (cdr (symbol-function 'the)) type form))))
+ (declare
+ . ,#'(lambda (&rest specs)
+ (while specs
+ (if (listp cl-declare-stack) (push (car specs) cl-declare-stack))
+ (cl-do-proclaim (pop specs) nil))))
(load-time-value
. ,#'(lambda (form &optional read-only)
(let* ((gensym (gensym))
@@ -517,37 +522,116 @@
(placeholders
(mapcar #'(lambda (lambda)
(make-byte-code (second lambda) "\xc0\x87"
- [42] 1))
+ ;; This list is used for
+ ;; the byte-optimize
+ ;; property, if the
+ ;; function is to be
+ ;; inlined. See
+ ;; cl-do-proclaim.
+ (vector nil) 1))
lambdas))
(byte-compile-macro-environment
(pairlis names (mapcar
#'(lambda (placeholder)
`(lambda (&rest cl-labels-args)
+ ;; Be careful not to quote
+ ;; PLACEHOLDER, otherwise
+ ;; byte-optimize-funcall inlines
+ ;; it.
(list* 'funcall ,placeholder
cl-labels-args)))
placeholders)
byte-compile-macro-environment))
(gensym (gensym)))
- (put gensym 'byte-compile-label-alist
- (pairlis placeholders
- (mapcar 'second (mapcar 'cl-macroexpand-all
- lambdas))))
- (put gensym 'byte-compile
- #'(lambda (form)
- (let* ((byte-compile-label-alist
- (get (car form) 'byte-compile-label-alist)))
- (dolist (acons byte-compile-label-alist)
- (setf (cdr acons)
- (byte-compile-lambda (cdr acons))))
- (byte-compile-body-do-effect
- (sublis byte-compile-label-alist (cdr form)
-:test #'eq))
- (dolist (acons byte-compile-label-alist)
- (nsubst (cdr acons) (car acons)
- byte-compile-label-alist :test #'eq
-:descend-structures t)))))
- (cl-macroexpand-all (cons gensym body)
- byte-compile-macro-environment))))
+ (labels
+ ((byte-compile-transform-labels (form names lambdas
+ placeholders)
+ (let* ((inline
+ (mapcan
+ #'(lambda (name placeholder lambda)
+ (and
+ (eq
+ (getf (aref
+ (compiled-function-constants
+ placeholder) 0)
+ 'byte-optimizer)
+ 'byte-compile-inline-expand)
+ `(((function ,placeholder)
+ ,(byte-compile-lambda lambda)
+ (function ,lambda)))))
+ names placeholders lambdas))
+ (compiled
+ (mapcar #'byte-compile-lambda
+ (if (not inline)
+ lambdas
+ ;; See further down for the
+ ;; rationale of the sublis calls.
+ (sublis (pairlis
+ (mapcar #'cadar inline)
+ (mapcar #'third inline))
+ (sublis
+ (pairlis
+ (mapcar #'car inline)
+ (mapcar #'second inline))
+ lambdas :test #'equal)
+:test #'eq))))
+ elt)
+ (mapc #'(lambda (placeholder function)
+ (nsubst function placeholder compiled
+:test #'eq
+:descend-structures t))
+ placeholders compiled)
+ (when inline
+ (dolist (triad inline)
+ (nsubst (setq elt (elt compiled
+ (position (cadar triad)
+ placeholders)))
+ (second triad) compiled :test #'eq
+:descend-structures t)
+ (setf (second triad) elt))
+ ;; For inlined labels: first, replace uses of
+ ;; the placeholder in places where it's not an
+ ;; evident, explicit funcall (that is, where
+ ;; it is not to be inlined) with the compiled
+ ;; function:
+ (setq form (sublis
+ (pairlis (mapcar #'car inline)
+ (mapcar #'second inline))
+ form :test #'equal)
+ ;; Now replace uses of the placeholder
+ ;; where it is an evident funcall with the
+ ;; lambda, quoted as a function, to allow
+ ;; byte-optimize-funcall to do its
+ ;; thing. Note that the lambdas still have
+ ;; the placeholders, so there's no risk
+ ;; of recursive inlining.
+ form (sublis (pairlis
+ (mapcar #'cadar inline)
+ (mapcar #'third inline))
+ form :test #'eq)))
+ (sublis (pairlis placeholders compiled) form
+:test #'eq))))
+ (put gensym 'byte-compile
+ #'(lambda (form)
+ (let* ((names (cadr (cl-pop2 form)))
+ (lambdas (mapcar #'cadr (cdr (pop form))))
+ (placeholders (cadr (pop form))))
+ (byte-compile-body-do-effect
+ (byte-compile-transform-labels form names
+ lambdas
+ placeholders)))))
+ (put gensym 'byte-hunk-handler
+ #'(lambda (form)
+ (let* ((names (cadr (cl-pop2 form)))
+ (lambdas (mapcar #'cadr (cdr (pop form))))
+ (placeholders (cadr (pop form))))
+ (byte-compile-file-form
+ (cons 'progn
+ (byte-compile-transform-labels
+ form names lambdas placeholders))))))
+ (cl-macroexpand-all `(,gensym ',names (list ,@lambdas)
+ ',placeholders ,@body)
+ byte-compile-macro-environment)))))
(flet .
,#'(lambda (bindings &rest body)
(let* ((names (mapcar 'car bindings))
@@ -3699,10 +3783,9 @@
(if (cddr form)
(byte-compile-normal-call
`(signal 'wrong-number-of-arguments '(function ,(length (cdr form)))))
- (byte-compile-constant
- (cond ((symbolp (nth 1 form))
- (nth 1 form))
- ((byte-compile-lambda (nth 1 form)))))))
+ (byte-compile-constant (if (eq 'lambda (car-safe (nth 1 form)))
+ (byte-compile-lambda (nth 1 form))
+ (nth 1 form)))))
(defun byte-compile-insert (form)
(cond ((null (cdr form))
diff -r f0f1fd0d8486 -r d4f334808463 lisp/cl-extra.el
--- a/lisp/cl-extra.el Sun Sep 25 16:12:07 2011 +0100
+++ b/lisp/cl-extra.el Sun Oct 02 15:32:16 2011 +0100
@@ -619,8 +619,11 @@
(cl-macroexpand-all (nth 1 (nth 2 (nth 2 found))) env)
;; It's an atom, almost certainly a compiled function;
;; we're using the implementation of labels in
- ;; bytecomp.el.
- (nth 2 (nth 2 found)))
+ ;; bytecomp.el. Quote it with FUNCTION so that code can
+ ;; tell uses as data apart from the uses with funcall,
+ ;; where it's unquoted. #### We should warn if (car form)
+ ;; above is quote, rather than function.
+ (list 'function (nth 2 (nth 2 found))))
form))))
((memq (car form) '(defun defmacro))
(list* (car form) (nth 1 form) (cl-macroexpand-body (cddr form) env)))
diff -r f0f1fd0d8486 -r d4f334808463 lisp/cl-macs.el
--- a/lisp/cl-macs.el Sun Sep 25 16:12:07 2011 +0100
+++ b/lisp/cl-macs.el Sun Oct 02 15:32:16 2011 +0100
@@ -1969,18 +1969,38 @@
((eq (car-safe spec) 'inline)
(while (setq spec (cdr spec))
- (or (memq (get (car spec) 'byte-optimizer)
- '(nil byte-compile-inline-expand))
- (error "%s already has a byte-optimizer, can't make it inline"
- (car spec)))
- (put (car spec) 'byte-optimizer 'byte-compile-inline-expand)))
-
+ (let ((assq (cdr (assq (car spec) byte-compile-macro-environment))))
+ (if (and (consp assq) (eq (nth 1 (nth 1 assq)) 'cl-labels-args)
+ (atom (setq assq (nth 2 (nth 2 assq)))))
+ ;; It's a label, and we're using the labels
+ ;; implementation in bytecomp.el. Tell the compiler
+ ;; to inline it, don't mark the symbol to be inlined
+ ;; globally.
+ (setf (getf (aref (compiled-function-constants assq) 0)
+ 'byte-optimizer)
+ 'byte-compile-inline-expand)
+ (or (memq (get (car spec) 'byte-optimizer)
+ '(nil byte-compile-inline-expand))
+ (error
+ "%s already has a byte-optimizer, can't make it inline"
+ (car spec)))
+ (put (car spec) 'byte-optimizer 'byte-compile-inline-expand)))))
((eq (car-safe spec) 'notinline)
(while (setq spec (cdr spec))
- (if (eq (get (car spec) 'byte-optimizer)
- 'byte-compile-inline-expand)
- (put (car spec) 'byte-optimizer nil))))
-
+ (let ((assq (cdr (assq (car spec) byte-compile-macro-environment))))
+ (if (and (consp assq) (eq (nth 1 (nth 1 assq)) 'cl-labels-args)
+ (atom (setq assq (nth 2 (nth 2 assq)))))
+ ;; It's a label, and we're using the labels
+ ;; implementation in bytecomp.el. Tell the compiler
+ ;; not to inline it.
+ (if (eq 'byte-compile-inline-expand
+ (getf (aref (compiled-function-constants assq) 0)
+ 'byte-optimizer))
+ (remf (aref (compiled-function-constants assq) 0)
+ 'byte-optimizer))
+ (if (eq (get (car spec) 'byte-optimizer)
+ 'byte-compile-inline-expand)
+ (put (car spec) 'byte-optimizer nil))))))
((eq (car-safe spec) 'optimize)
(let ((speed (assq (nth 1 (assq 'speed (cdr spec)))
'((0 . nil) (1 . t) (2 . t) (3 . t))))
@@ -2014,14 +2034,8 @@
;;;###autoload
(defmacro declare (&rest specs)
- (if (cl-compiling-file)
- (while specs
- (if (listp cl-declare-stack) (push (car specs) cl-declare-stack))
- (cl-do-proclaim (pop specs) nil)))
nil)
-
-
;;; Generalized variables.
;;;###autoload
diff -r f0f1fd0d8486 -r d4f334808463 tests/ChangeLog
--- a/tests/ChangeLog Sun Sep 25 16:12:07 2011 +0100
+++ b/tests/ChangeLog Sun Oct 02 15:32:16 2011 +0100
@@ -1,3 +1,9 @@
+2011-10-02 Aidan Kehoe <kehoea(a)parhasard.net>
+
+ * automated/lisp-tests.el:
+ * automated/lisp-tests.el (+):
+ Test #'labels and inlining.
+
2011-09-04 Aidan Kehoe <kehoea(a)parhasard.net>
* automated/lisp-reader-tests.el:
diff -r f0f1fd0d8486 -r d4f334808463 tests/automated/lisp-tests.el
--- a/tests/automated/lisp-tests.el Sun Sep 25 16:12:07 2011 +0100
+++ b/tests/automated/lisp-tests.el Sun Oct 02 15:32:16 2011 +0100
@@ -2939,4 +2939,50 @@
(Check-Error wrong-number-of-arguments (apply-partially))
(Assert (equal (funcall construct-list) '(5 6 7))))
+;; Test labels and inlining.
+(labels
+ ((+ (&rest arguments)
+ ;; Shades of Java, hah.
+ (mapconcat #'prin1-to-string arguments ", "))
+ (print-with-commas (stream one two three four five)
+ (princ (+ one two three four five) stream))
+ (bookend (open close &rest arguments)
+ (refer-to-bookend (concat open (apply #'+ arguments) close)))
+ (refer-to-bookend (string)
+ (bookend "[" "]" string "hello"
"there")))
+ (declare (inline + print-with-commas bookend refer-to-bookend))
+ (macrolet
+ ((with-first-arguments (&optional form)
+ (append form (list 1 [hi there] 40 "this is a string" pi)))
+ (with-second-arguments (&optional form)
+ (append form (list pi e ''hello ''there [40 50 60])))
+ (with-both-arguments (&optional form)
+ (append form
+ (macroexpand '(with-first-arguments))
+ (macroexpand '(with-second-arguments)))))
+
+ (with-temp-buffer
+ (Assert
+ (equal
+ (mapconcat #'prin1-to-string (with-first-arguments (list)) ", ")
+ (with-first-arguments (print-with-commas (current-buffer))))
+ "checking print-with-commas gives the expected result")
+ (Assert
+ (or
+ (not (compiled-function-p (indirect-function #'print-with-commas)))
+ (notany #'compiled-function-p
+ (compiled-function-constants
+ (indirect-function #'print-with-commas))))
+ "checking the label + was inlined correctly")
+ (insert ", ")
+ ;; This call to + will be inline in compiled code, but there's
+ ;; no easy way for us to check that:
+ (Assert (null (insert (with-second-arguments (+)))))
+ (Assert (equal
+ (mapconcat #'prin1-to-string (with-both-arguments (list)) ",
")
+ (buffer-string))
+ "checking the buffer contents are as expected at the end.")
+ (Assert (not (funcall (intern "eq") #'bookend
#'refer-to-bookend))
+ "checking two mutually recursive functions compiled OK"))))
+
;;; end of lisp-tests.el
--
‘Iodine deficiency was endemic in parts of the UK until, through what has been
described as “an unplanned and accidental public health triumph”, iodine was
added to cattle feed to improve milk production in the 1930s.’
(EN Pearce, Lancet, June 2011)
_______________________________________________
XEmacs-Patches mailing list
XEmacs-Patches(a)xemacs.org
http://lists.xemacs.org/mailman/listinfo/xemacs-patches