0.9.7.25:
[sbcl.git] / src / pcl / fngen.lisp
1 ;;;; This software is part of the SBCL system. See the README file for
2 ;;;; more information.
3
4 ;;;; This software is derived from software originally released by Xerox
5 ;;;; Corporation. Copyright and release statements follow. Later modifications
6 ;;;; to the software are in the public domain and are provided with
7 ;;;; absolutely no warranty. See the COPYING and CREDITS files for more
8 ;;;; information.
9
10 ;;;; copyright information from original PCL sources:
11 ;;;;
12 ;;;; Copyright (c) 1985, 1986, 1987, 1988, 1989, 1990 Xerox Corporation.
13 ;;;; All rights reserved.
14 ;;;;
15 ;;;; Use and copying of this software and preparation of derivative works based
16 ;;;; upon this software are permitted. Any distribution of this software or
17 ;;;; derivative works must comply with all applicable United States export
18 ;;;; control laws.
19 ;;;;
20 ;;;; This software is made available AS IS, and Xerox Corporation makes no
21 ;;;; warranty about the software, its performance or its conformity to any
22 ;;;; specification.
23
24 (in-package "SB-PCL")
25 \f
26 ;;; GET-FUN is the main user interface to this code. It is like
27 ;;; COMPILE, only more efficient. It achieves this efficiency by
28 ;;; reducing the number of times that the compiler needs to be called.
29 ;;; Calls to GET-FUN in which the lambda forms differ only by
30 ;;; constants can use the same piece of compiled code. (For example,
31 ;;; dispatch dfuns and combined method functions can often be shared,
32 ;;; if they differ only by referring to different methods.)
33 ;;;
34 ;;; If GET-FUN is called with a lambda expression only, it will return
35 ;;; a corresponding function. The optional constant-converter argument
36 ;;; can be a function which will be called to convert each constant appearing
37 ;;; in the lambda to whatever value should appear in the function.
38 ;;;
39 ;;; There are three internal functions which operate on the lambda argument
40 ;;; to GET-FUN:
41 ;;;   COMPUTE-TEST converts the lambda into a key to be used for lookup,
42 ;;;   COMPUTE-CODE is used by GET-NEW-FUN-GENERATOR-INTERNAL to
43 ;;;             generate the actual lambda to be compiled, and
44 ;;;   COMPUTE-CONSTANTS is used to generate the argument list that is
45 ;;;             to be passed to the compiled function.
46 ;;;
47 (defun get-fun (lambda &optional
48                  (test-converter #'default-test-converter)
49                  (code-converter #'default-code-converter)
50                  (constant-converter #'default-constant-converter))
51   (function-apply (get-fun-generator lambda test-converter code-converter)
52                   (compute-constants      lambda constant-converter)))
53
54 (defun get-fun1 (lambda &optional
55                   (test-converter #'default-test-converter)
56                   (code-converter #'default-code-converter)
57                   (constant-converter #'default-constant-converter))
58   (values (the function
59             (get-fun-generator lambda test-converter code-converter))
60           (compute-constants lambda constant-converter)))
61
62 (defun default-constantp (form)
63   (and (constantp form)
64        (not (typep (eval form) '(or symbol fixnum)))))
65
66 (defun default-test-converter (form)
67   (if (default-constantp form)
68       '.constant.
69       form))
70
71 (defun default-code-converter  (form)
72   (if (default-constantp form)
73       (let ((gensym (gensym))) (values gensym (list gensym)))
74       form))
75
76 (defun default-constant-converter (form)
77   (if (default-constantp form)
78       (list (eval form))
79       nil))
80 \f
81 ;;; *FGENS* is a list of all the function generators we have so far. Each
82 ;;; element is a FGEN structure as implemented below. Don't ever touch this
83 ;;; list by hand, use STORE-FGEN.
84 (defvar *fgens* ())
85
86 (defun store-fgen (fgen)
87   (let ((old (lookup-fgen (fgen-test fgen))))
88     (if old
89         (setf (svref old 2) (fgen-generator fgen)
90               (svref old 4) (or (svref old 4)
91                                 (fgen-system fgen)))
92         (setq *fgens* (nconc *fgens* (list fgen))))))
93
94 (defun lookup-fgen (test)
95   (find test (the list *fgens*) :key #'fgen-test :test #'equal))
96
97 (defun make-fgen (test gensyms generator generator-lambda system)
98   (let ((new (make-array 6)))
99     (setf (svref new 0) test
100           (svref new 1) gensyms
101           (svref new 2) generator
102           (svref new 3) generator-lambda
103           (svref new 4) system)
104     new))
105
106 (defun fgen-test             (fgen) (svref fgen 0))
107 (defun fgen-gensyms          (fgen) (svref fgen 1))
108 (defun fgen-generator        (fgen) (svref fgen 2))
109 (defun fgen-generator-lambda (fgen) (svref fgen 3))
110 (defun fgen-system           (fgen) (svref fgen 4))
111 \f
112 (defun get-fun-generator (lambda test-converter code-converter)
113   (let* ((test (compute-test lambda test-converter))
114          (fgen (lookup-fgen test)))
115     (if fgen
116         (fgen-generator fgen)
117         (get-new-fun-generator lambda test code-converter))))
118
119 (defun get-new-fun-generator (lambda test code-converter)
120   (multiple-value-bind (gensyms generator-lambda)
121       (get-new-fun-generator-internal lambda code-converter)
122     (let* ((generator (compile nil generator-lambda))
123            (fgen (make-fgen test gensyms generator generator-lambda nil)))
124       (store-fgen fgen)
125       generator)))
126
127 (defun get-new-fun-generator-internal (lambda code-converter)
128   (multiple-value-bind (code gensyms)
129       (compute-code lambda code-converter)
130     (values gensyms `(lambda ,gensyms (function ,code)))))
131
132 (defun compute-test (lambda test-converter)
133   (let ((*walk-form-expand-macros-p* t))
134     (walk-form lambda
135                nil
136                (lambda (f c e)
137                  (declare (ignore e))
138                  (if (neq c :eval)
139                      f
140                      (let ((converted (funcall test-converter f)))
141                        (values converted (neq converted f))))))))
142
143 (defun compute-code (lambda code-converter)
144   (let ((*walk-form-expand-macros-p* t)
145         (gensyms ()))
146     (values (walk-form lambda
147                        nil
148                        (lambda (f c e)
149                          (declare (ignore e))
150                          (if (neq c :eval)
151                              f
152                              (multiple-value-bind (converted gens)
153                                  (funcall code-converter f)
154                                (when gens (setq gensyms (append gensyms gens)))
155                                (values converted (neq converted f))))))
156             gensyms)))
157
158 (defun compute-constants (lambda constant-converter)
159   (let ((*walk-form-expand-macros-p* t) ; doesn't matter here.
160         collect)
161     (walk-form lambda
162                nil
163                (lambda (f c e)
164                  (declare (ignore e))
165                  (if (neq c :eval)
166                      f
167                      (let ((consts (funcall constant-converter f)))
168                        (if consts
169                            (progn
170                              (setq collect (append collect consts))
171                              (values f t))
172                            f)))))
173     collect))
174 \f
175 (defmacro precompile-function-generators (&optional system)
176   `(progn
177     ,@(let (collect)
178         (dolist (fgen *fgens*)
179           (when (or (null (fgen-system fgen))
180                     (eq (fgen-system fgen) system))
181             (when system (setf (svref fgen 4) system))
182             (push `(load-function-generator
183                     ',(fgen-test fgen)
184                     ',(fgen-gensyms fgen)
185                     (function ,(fgen-generator-lambda fgen))
186                     ',(fgen-generator-lambda fgen)
187                     ',system)
188                   collect)))
189         (nreverse collect))))
190
191 (defun load-function-generator (test gensyms generator generator-lambda system)
192   (store-fgen (make-fgen test gensyms generator generator-lambda system)))