Update of /project/movitz/cvsroot/movitz In directory common-lisp.net:/tmp/cvs-serv705
Modified Files: compiler.lisp Log Message: These changes are all about making the compiler smarter about functions whose lambda-list look like (x &optional y). Most such functions become about 20 bytes shorter. More importantly, they become branch-less, reducing the CPU-cycle-cost of this abstraction essentially zero.
Date: Thu Feb 5 09:46:02 2004 Author: ffjeld
Index: movitz/compiler.lisp diff -u movitz/compiler.lisp:1.16 movitz/compiler.lisp:1.17 --- movitz/compiler.lisp:1.16 Thu Feb 5 06:02:39 2004 +++ movitz/compiler.lisp Thu Feb 5 09:46:02 2004 @@ -8,7 +8,7 @@ ;;;; Created at: Wed Oct 25 12:30:49 2000 ;;;; Distribution: See the accompanying file COPYING. ;;;; -;;;; $Id: compiler.lisp,v 1.16 2004/02/05 11:02:39 ffjeld Exp $ +;;;; $Id: compiler.lisp,v 1.17 2004/02/05 14:46:02 ffjeld Exp $ ;;;; ;;;;------------------------------------------------------------------
@@ -119,14 +119,15 @@ :accessor function-envs) (funobj-env :initarg :funobj-env - :accessor funobj-env)) + :accessor funobj-env) + (entry-protocol + :initform :default + :initarg :entry-protocol + :reader funobj-entry-protocol)) (:documentation "This class is used for funobjs during the first compiler pass. Before the second pass, such objects will be change-class-ed to proper movitz-funobjs. This way, we ensure that no undue side-effects on the funobj occur during pass 1."))
-(defclass movitz-funobj-pass1-numargs-case (movitz-funobj-pass1) ()) -(defclass movitz-funobj-pass1-1req1opt (movitz-funobj-pass1) ()) - (defmethod print-object ((object movitz-funobj-pass1) stream) (print-unreadable-object (object stream :type t :identity t) (when (slot-boundp object 'name) @@ -165,8 +166,10 @@ ;; mutually recursive (lexically bound) functions. (with-retries-until-true (retry-pass1 "Retry first-pass compilation of ~S." name) ;; First-pass is mostly functional, so it can safely be restarted. - (multiple-value-bind (required-vars optional-vars rest-var key-vars) + (multiple-value-bind (required-vars optional-vars rest-var key-vars + aux-vars allow-p min max edx-var) (decode-normal-lambda-list lambda-list) + (declare (ignore aux-vars allow-p min max)) ;; There are several main branches through the function ;; compiler, and this is where we decide which one to take. (funcall (cond @@ -176,8 +179,11 @@ 'make-compiled-function-pass1-numarg-case) ((and (= 1 (length required-vars)) ; (x &optional y) (= 1 (length optional-vars)) + (movitz-constantp (nth-value 1 (decode-optional-formal (first optional-vars))) + env) (null key-vars) - (not rest-var)) + (not rest-var) + (not edx-var)) 'make-compiled-function-pass1-1req1opt) (t 'make-compiled-function-pass1)) name lambda-list declarations form env top-level-p funobj)))) @@ -192,7 +198,8 @@ init-args))
(defun make-compiled-function-pass1-numarg-case (name lambda-list declarations form env top-level-p funobj) - (let* ((funobj (ensure-pass1-funobj funobj 'movitz-funobj-pass1-numargs-case + (let* ((funobj (ensure-pass1-funobj funobj 'movitz-funobj-pass1 + :entry-protocol :numargs-case :name name :lambda-list (movitz-read (lambda-list-simplify lambda-list)))) (funobj-env (make-local-movitz-environment env funobj :type 'funobj-env))) @@ -230,6 +237,46 @@ (function-envs funobj))))) funobj))
+(defun make-compiled-function-pass1-1req1opt (name lambda-list declarations form env top-level-p funobj) + "Returns funobj." + (when (duplicatesp lambda-list) + (error "There are duplicates in lambda-list ~S." lambda-list)) + (let* ((funobj (ensure-pass1-funobj funobj 'movitz-funobj-pass1 + :entry-protocol :1req1opt + :name name + :lambda-list (movitz-read (lambda-list-simplify lambda-list)))) + (funobj-env (make-local-movitz-environment env funobj :type 'funobj-env)) + (function-env (add-bindings-from-lambda-list + lambda-list + (make-local-movitz-environment funobj-env funobj + :type 'function-env + :need-normalized-ecx-p nil + :declaration-context :funobj + :declarations declarations))) + (optional-env (make-local-movitz-environment function-env funobj + :type 'function-env))) + (setf (funobj-env funobj) funobj-env) + ;; (print-code 'arg-init-code arg-init-code) + (setf (extended-code optional-env) + (compiler-call #'compile-form + :form (optional-function-argument-init-form + (movitz-binding (first (optional-vars function-env)) function-env nil)) + :funobj funobj + :env optional-env + :result-mode :ebx)) + (setf (extended-code function-env) + (append #+ignore arg-init-code + (compiler-call #'compile-form + :form (make-special-funarg-shadowing function-env form) + :funobj funobj + :env function-env + :top-level-p top-level-p + :result-mode :function))) + (setf (function-envs funobj) + (list (cons 'muerte.cl::t function-env) + (cons :optional optional-env))) + funobj)) + (defun make-compiled-function-pass1 (name lambda-list declarations form env top-level-p funobj) "Returns funobj." (when (duplicatesp lambda-list) @@ -261,7 +308,7 @@
(defun make-compiled-funobj-pass2 (toplevel-funobj-pass1) - "This is where second pass compilation for each top-level funobj begins." + "This is the entry-poing for second pass compilation for each top-level funobj." (check-type toplevel-funobj-pass1 movitz-funobj-pass1) (let ((toplevel-funobj (change-class toplevel-funobj-pass1 'movitz-funobj))) (multiple-value-bind (toplevel-funobj function-binding-usage) @@ -476,6 +523,75 @@ funobj)
(defun complete-funobj (funobj) + (case (funobj-entry-protocol funobj) + (:1req1opt + (complete-funobj-1req1opt funobj)) + (t (complete-funobj-default funobj))) + (loop for (sub-function-binding) on (sub-function-binding-usage funobj) by #'cddr + do (complete-funobj (function-binding-funobj sub-function-binding))) + funobj) + +(defun complete-funobj-1req1opt (funobj) + (assert (= 2 (length (function-envs funobj)))) + (let* ((function-env (cdr (assoc 'muerte.cl::t (function-envs funobj)))) + (optional-env (cdr (assoc :optional (function-envs funobj)))) + (frame-map (frame-map function-env)) + (resolved-code (finalize-code (extended-code function-env) funobj frame-map)) + (resolved-optional-code (finalize-code (extended-code optional-env) funobj frame-map)) + (stack-frame-size (frame-map-size (frame-map function-env))) + (use-stack-frame-p (or (plusp stack-frame-size) + (tree-search resolved-code + '(:ebp :esp :call :leave)))) + (optional-stack-frame-p (tree-search resolved-optional-code + '(:ebp :esp :call :leave)))) + (assert (not optional-stack-frame-p)) + (let* ((stack-setup-size stack-frame-size) + (function-code + (let* ((req-binding (movitz-binding (first (required-vars function-env)) + function-env nil)) + (req-location (cdr (assoc req-binding frame-map))) + (opt-binding (movitz-binding (first (optional-vars function-env)) + function-env nil)) + (opt-location (cdr (assoc opt-binding frame-map)))) + (append `((:jmp (:edi ,(global-constant-offset 'trampoline-cl-dispatch-1or2)))) + '(entry%1op) + (unless (eql nil opt-location) + resolved-optional-code) + '(entry%2op) + (when use-stack-frame-p + +enter-stack-frame-code+) + '(start-stack-frame-setup) + (cond + ((and (eql 1 req-location) + (eql 2 opt-location)) + (decf stack-setup-size 2) + `((:pushl :eax) + (:pushl :ebx))) + ((and (eql 1 req-location) + (eql nil opt-location)) + (decf stack-setup-size 1) + `((:pushl :eax))) + ((and (member req-location '(nil :eax)) + (eql 1 opt-location)) + (decf stack-setup-size 1) + `((:pushl :ebx))) + ((and (member req-location '(nil :eax)) + (member opt-location '(nil :ebx))) + nil) + (t (error "Can't deal with req ~S opt ~S." + req-location opt-location))) + (make-stack-setup-code stack-setup-size) + resolved-code + (make-compiled-function-postlude funobj function-env + use-stack-frame-p))))) + (let ((optimized-function-code + (optimize-code function-code + :keep-labels (nconc (subseq (movitz-funobj-const-list funobj) + 0 (movitz-funobj-num-jumpers funobj)) + '(entry%1op entry%2op))))) + (assemble-funobj funobj optimized-function-code))))) + +(defun complete-funobj-default (funobj) (let ((code-specs (loop for (numargs . function-env) in (function-envs funobj) collecting @@ -506,7 +622,7 @@ (code2 (cdr (assoc 2 code-specs))) (code3 (cdr (assoc 3 code-specs))) (codet (cdr (assoc 'muerte.cl::t code-specs)))) - (assert codet () "A default numargs-case is required.") + (assert codet () "A default numargs-case is required.") ;; (format t "codet:~{~&~A~}" codet) (let ((combined-code (delete 'start-stack-frame-setup @@ -539,8 +655,6 @@ codet))))) ;; (warn "opt code: ~{~&~A~}" optimized-function-code) (assemble-funobj funobj combined-code)))) - (loop for (sub-function-binding) on (sub-function-binding-usage funobj) by #'cddr - do (complete-funobj (function-binding-funobj sub-function-binding))) funobj)
@@ -598,7 +712,8 @@ :initial-contents code-vector :flags '(:code-vector-p) :alignment 16 - :alignment-offset 8))))) + :alignment-offset 8)))) + funobj)
#+ignore (defun make-compiled-function-body-default (form funobj env top-level-p) @@ -985,7 +1100,7 @@
(defun print-code (x code) (let ((*print-level* 3)) - (format t "~A code:~{~& ~A~}" x code)) + (format t "~&~A code:~{~& ~A~}" x code)) code)
(defun layout-program (pc) @@ -3128,6 +3243,14 @@ (t (error "Don't know how to compile checking for ~A to ~A arguments." min-args max-args)))))
+(defun make-stack-setup-code (stack-setup-size) + (case stack-setup-size + (0 nil) + (1 '((:pushl :edi))) + (2 '((:pushl :edi) (:pushl :edi))) + (3 '((:pushl :edi) (:pushl :edi) (:pushl :edi))) + (t `((:subl ,(* 4 stack-setup-size) :esp))))) + (defun make-compiled-function-prelude (stack-frame-size env use-stack-frame-p need-normalized-ecx-p frame-map &key do-check-stack-p) @@ -3258,7 +3381,7 @@ (append (when (and do-check-stack-p *compiler-auto-stack-checks-p* (not (without-check-stack-limit-p env))) - `(((:fs-override) + `((,*compiler-global-segment-prefix* :bound (:edi ,(global-constant-offset 'stack-bottom)) :esp))) (when use-stack-frame-p `((:pushl :ebp) @@ -3286,12 +3409,7 @@ (make-compiled-function-prelude-numarg-check min-args max-args)))) '(start-stack-frame-setup) eax-ebx-code - (case stack-setup-size - (0 nil) - (1 '((:pushl :edi))) - (2 '((:pushl :edi) (:pushl :edi))) - (3 '((:pushl :edi) (:pushl :edi) (:pushl :edi))) - (t `((:subl ,(* 4 stack-setup-size) :esp)))) + (make-stack-setup-code stack-setup-size) (when need-normalized-ecx-p (cond ;; normalize arg-count in ecx..