[movitz-cvs] CVS update: movitz/compiler.lisp

Frode Vatvedt Fjeld ffjeld at common-lisp.net
Thu Feb 5 14:46:03 UTC 2004


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..





More information about the Movitz-cvs mailing list