Dynamic space relocation, part 1 of 2
[sbcl.git] / src / pcl / dlisp.lisp
blob30d34dd33cbed49e41e94869c1dce45fae7fe9c2
1 ;;;; This software is part of the SBCL system. See the README file for
2 ;;;; more information.
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.
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.
24 (in-package "SB-PCL")
27 ;;;; some support stuff for getting a hold of symbols that we need when
28 ;;;; building the discriminator codes. It's OK for these to be interned
29 ;;;; symbols because we don't capture any user code in the scope in which
30 ;;;; these symbols are bound.
32 (declaim (list *dfun-arg-symbols*))
33 (define-load-time-global *dfun-arg-symbols* '(.ARG0. .ARG1. .ARG2. .ARG3.))
35 (defun dfun-arg-symbol (arg-number)
36 (or (nth arg-number *dfun-arg-symbols*)
37 (format-symbol *pcl-package* ".ARG~A." arg-number)))
39 (declaim (list *slot-vector-symbols*))
40 (define-load-time-global *slot-vector-symbols* '(.SLOTS0. .SLOTS1. .SLOTS2. .SLOTS3.))
42 (defun slot-vector-symbol (arg-number)
43 (or (nth arg-number *slot-vector-symbols*)
44 (format-symbol *pcl-package* ".SLOTS~A." arg-number)))
46 (declaim (inline make-dfun-required-args))
47 (defun make-dfun-required-args (count)
48 (declare (type index count))
49 (let (result)
50 (dotimes (i count (nreverse result))
51 (push (dfun-arg-symbol i) result))))
53 (defun make-dfun-lambda-list (nargs applyp)
54 (let ((required (make-dfun-required-args nargs)))
55 (if applyp
56 (nconc required
57 ;; Use &MORE arguments to avoid consing up an &REST list
58 ;; that we might not need at all. See MAKE-EMF-CALL and
59 ;; INVOKE-EFFECTIVE-METHOD-FUNCTION for the other
60 ;; pieces.
61 '(&more .dfun-more-context. .dfun-more-count.))
62 required)))
64 (defun make-dlap-lambda-list (nargs applyp)
65 (let ((required (make-dfun-required-args nargs)))
66 ;; Return the full lambda list, the required arguments, a form
67 ;; that will generate a rest-list, and a list of the &MORE
68 ;; parameters used.
69 ;; Beware of deep voodoo! The DEFKNOWN for %LISTIFY-REST-ARGS says that its
70 ;; second argument is INDEX, but the THE form below is "weaker" on account
71 ;; of the vop operand restrictions or something that I don't understand.
72 ;; Which is to say, PCL compilation reliably broke when changed to INDEX.
73 (if applyp
74 (values (append required '(&more .more-context. .more-count.))
75 required
76 '((sb-c:%listify-rest-args
77 .more-context. (the (and unsigned-byte fixnum)
78 .more-count.)))
79 '(.more-context. .more-count.))
80 (values required required nil nil))))
82 (defun make-emf-call (nargs applyp fn-variable &optional emf-type)
83 (let ((required (make-dfun-required-args nargs)))
84 `(,(if (eq emf-type 'fast-method-call)
85 'invoke-effective-method-function-fast
86 'invoke-effective-method-function)
87 ,fn-variable
88 ,applyp
89 :required-args ,required
90 ;; INVOKE-EFFECTIVE-METHOD-FUNCTION will decide whether to use
91 ;; the :REST-ARG version or the :MORE-ARG version depending on
92 ;; the type of the EMF.
93 :rest-arg ,(if applyp
94 ;; Creates a list from the &MORE arguments.
95 '((sb-c:%listify-rest-args ; See above re. voodoo
96 .dfun-more-context.
97 (the (and unsigned-byte fixnum)
98 .dfun-more-count.)))
99 nil)
100 :more-arg ,(when applyp
101 '(.dfun-more-context. .dfun-more-count.)))))
103 (defun make-fast-method-call-lambda-list (nargs applyp)
104 (list* '.pv. '.next-method-call. (make-dfun-lambda-list nargs applyp)))
106 ;;; Emitting various accessors.
108 (defun emit-one-class-reader (class-slot-p)
109 (emit-reader/writer :reader 1 class-slot-p))
111 (defun emit-one-class-boundp (class-slot-p)
112 (emit-reader/writer :boundp 1 class-slot-p))
114 (defun emit-one-class-writer (class-slot-p)
115 (emit-reader/writer :writer 1 class-slot-p))
117 (defun emit-two-class-reader (class-slot-p)
118 (emit-reader/writer :reader 2 class-slot-p))
120 (defun emit-two-class-boundp (class-slot-p)
121 (emit-reader/writer :boundp 2 class-slot-p))
123 (defun emit-two-class-writer (class-slot-p)
124 (emit-reader/writer :writer 2 class-slot-p))
126 ;;; --------------------------------
128 (defun emit-one-index-readers (class-slot-p)
129 (emit-one-or-n-index-reader/writer :reader nil class-slot-p))
131 (defun emit-one-index-boundps (class-slot-p)
132 (emit-one-or-n-index-reader/writer :boundp nil class-slot-p))
134 (defun emit-one-index-writers (class-slot-p)
135 (emit-one-or-n-index-reader/writer :writer nil class-slot-p))
137 (defun emit-n-n-readers ()
138 (emit-one-or-n-index-reader/writer :reader t nil))
140 (defun emit-n-n-boundps ()
141 (emit-one-or-n-index-reader/writer :boundp t nil))
143 (defun emit-n-n-writers ()
144 (emit-one-or-n-index-reader/writer :writer t nil))
146 ;;; --------------------------------
148 (defun emit-checking (metatypes applyp)
149 (emit-checking-or-caching nil nil metatypes applyp))
151 (defun emit-caching (metatypes applyp)
152 (emit-checking-or-caching t nil metatypes applyp))
154 (defun emit-in-checking-cache-p (metatypes)
155 (emit-checking-or-caching nil t metatypes nil))
157 (defun emit-constant-value (metatypes)
158 (emit-checking-or-caching t t metatypes nil))
160 ;;; --------------------------------
162 ;;; FIXME: What do these variables mean?
163 (defvar *precompiling-lap* nil)
165 (defun emit-default-only (metatypes applyp)
166 (multiple-value-bind (lambda-list args rest-arg more-arg)
167 (make-dlap-lambda-list (length metatypes) applyp)
168 (generating-lisp '(emf)
169 lambda-list
170 `(invoke-effective-method-function emf
171 ,applyp
172 :required-args ,args
173 :more-arg ,more-arg
174 :rest-arg ,rest-arg))))
176 ;;; --------------------------------
178 (defun generating-lisp (closure-variables args form)
179 (let ((lambda `(lambda ,closure-variables
180 ,@(when (member 'miss-fn closure-variables)
181 `((declare (type function miss-fn))))
182 (declare (optimize (sb-c::eval-store-source-form 0)))
183 #'(lambda ,args
184 (let ()
185 (declare #.*optimize-speed*)
186 ,form)))))
187 (values (if *precompiling-lap*
188 `#',lambda
189 (compile nil lambda))
190 nil)))
192 ;;; note on implementation for CMU 17 and later (including SBCL):
193 ;;; Since STD-INSTANCE-P is weakened, that branch may run on non-PCL
194 ;;; instances (structures). The result will be the non-wrapper layout
195 ;;; for the structure, which will cause a miss. The "slots" will be
196 ;;; whatever the first slot is, but will be ignored. Similarly,
197 ;;; FSC-INSTANCE-P returns true on funcallable structures as well as
198 ;;; PCL fins.
199 (defun emit-reader/writer (reader/writer 1-or-2-class class-slot-p)
200 (let ((instance nil)
201 (arglist ())
202 (closure-variables ())
203 (read-form (emit-slot-read-form class-slot-p 'index 'slots)))
204 (ecase reader/writer
205 ((:reader :boundp)
206 (setq instance (dfun-arg-symbol 0)
207 arglist (list instance)))
208 (:writer (setq instance (dfun-arg-symbol 1)
209 arglist (list (dfun-arg-symbol 0) instance))))
210 (ecase 1-or-2-class
211 (1 (setq closure-variables '(wrapper-0 index miss-fn)))
212 (2 (setq closure-variables '(wrapper-0 wrapper-1 index miss-fn))))
213 (generating-lisp
214 closure-variables
215 arglist
216 `(let* (,@(unless class-slot-p `((slots nil)))
217 (wrapper (cond ((std-instance-p ,instance)
218 ,@(unless class-slot-p
219 `((setq slots
220 (std-instance-slots ,instance))))
221 (%instance-layout ,instance))
222 ((fsc-instance-p ,instance)
223 ,@(unless class-slot-p
224 `((setq slots
225 (fsc-instance-slots ,instance))))
226 (%funcallable-instance-layout ,instance)))))
227 (block access
228 (when (and wrapper
229 (not (zerop (layout-clos-hash wrapper)))
230 ,@(if (eql 1 1-or-2-class)
231 `((eq wrapper wrapper-0))
232 `((or (eq wrapper wrapper-0)
233 (eq wrapper wrapper-1)))))
234 ,@(ecase reader/writer
235 (:reader
236 `((let ((value ,read-form))
237 (unless (unbound-marker-p value)
238 (return-from access value)))))
239 (:boundp
240 `((let ((value ,read-form))
241 (return-from access (not (unbound-marker-p value))))))
242 (:writer
243 `((return-from access (setf ,read-form ,(car arglist)))))))
244 (funcall miss-fn ,@arglist))))))
246 (defun emit-slot-read-form (class-slot-p index slots)
247 (if class-slot-p
248 `(cdr ,index)
249 `(clos-slots-ref ,slots ,index)))
251 (defun emit-boundp-check (value-form miss-fn arglist)
252 `(let ((value ,value-form))
253 (if (unbound-marker-p value)
254 (funcall ,miss-fn ,@arglist)
255 value)))
257 (defun emit-slot-access (reader/writer class-slot-p slots
258 index miss-fn arglist)
259 (let ((read-form (emit-slot-read-form class-slot-p index slots)))
260 (ecase reader/writer
261 (:reader (emit-boundp-check read-form miss-fn arglist))
262 (:boundp `(not (unbound-marker-p ,read-form)))
263 (:writer `(setf ,read-form ,(car arglist))))))
265 (defmacro emit-reader/writer-macro (reader/writer 1-or-2-class class-slot-p)
266 (let ((*precompiling-lap* t))
267 (values
268 (emit-reader/writer reader/writer 1-or-2-class class-slot-p))))
270 ;; If CACHED-INDEX-P is false, then the slot location is a constant and
271 ;; the cache holds layouts eligible to use that index.
272 ;; If true, then the cache is a map of layout -> index.
273 (defun emit-one-or-n-index-reader/writer (reader/writer
274 cached-index-p
275 class-slot-p)
276 (multiple-value-bind (arglist metatypes)
277 (ecase reader/writer
278 ((:reader :boundp)
279 (values (list (dfun-arg-symbol 0))
280 '(standard-instance)))
281 (:writer (values (list (dfun-arg-symbol 0) (dfun-arg-symbol 1))
282 '(t standard-instance))))
283 (generating-lisp
284 `(cache ,@(unless cached-index-p '(index)) miss-fn)
285 arglist
286 `(let (,@(unless class-slot-p '(slots))
287 ,@(when cached-index-p '(index)))
288 ,(emit-dlap 'cache arglist metatypes
289 (emit-slot-access reader/writer class-slot-p
290 'slots 'index 'miss-fn arglist)
291 `(funcall miss-fn ,@arglist)
292 (when cached-index-p 'index)
293 (unless class-slot-p '(slots)))))))
295 (defmacro emit-one-or-n-index-reader/writer-macro
296 (reader/writer cached-index-p class-slot-p)
297 (let ((*precompiling-lap* t))
298 (values
299 (emit-one-or-n-index-reader/writer reader/writer
300 cached-index-p
301 class-slot-p))))
303 (defun emit-miss (miss-fn args applyp)
304 (if applyp
305 `(multiple-value-call ,miss-fn ,@args
306 (sb-c::%more-arg-values .more-context.
308 .more-count.))
309 `(funcall ,miss-fn ,@args)))
311 ;; (cache-emf, return-value):
312 ;; NIL / NIL : GF has a single EMF. Invoke it when layouts are in cache.
313 ;; NIL / T : GF has a single EMF. Return T when layouts are in cache.
314 ;; T / NIL : Look for the EMF for argument layouts. Invoke it when in cache.
315 ;; T / T : Look for the EMF for argument layouts. Return it when in cache.
317 ;; METATYPES must be acceptable to EMIT-FETCH-WRAPPER.
318 ;; APPLYP says whether there is a &MORE context.
319 (defun emit-checking-or-caching (cached-emf-p return-value-p metatypes applyp)
320 (multiple-value-bind (lambda-list args rest-arg more-arg)
321 (make-dlap-lambda-list (length metatypes) applyp)
322 (generating-lisp
323 `(cache ,@(unless cached-emf-p '(emf)) miss-fn)
324 lambda-list
325 `(let (,@(when cached-emf-p '(emf)))
326 ,(emit-dlap 'cache args metatypes
327 (if return-value-p
328 (if cached-emf-p 'emf t)
329 `(invoke-effective-method-function
330 emf ,applyp
331 :required-args ,args
332 :more-arg ,more-arg
333 :rest-arg ,rest-arg))
334 (emit-miss 'miss-fn args applyp)
335 (when cached-emf-p 'emf))))))
337 (defmacro emit-checking-or-caching-macro (cached-emf-p
338 return-value-p
339 metatypes
340 applyp)
341 (let ((*precompiling-lap* t))
342 (values
343 (emit-checking-or-caching cached-emf-p return-value-p metatypes applyp))))
345 (defun emit-dlap (cache-var args metatypes hit-form miss-form value-var
346 &optional slot-vars)
347 (let* ((index -1)
348 (miss-tag (gensym "MISSED"))
349 (wrapper-bindings (mapcan (lambda (arg mt)
350 (unless (eq mt t)
351 (incf index)
352 `((,(format-symbol *pcl-package*
353 "WRAPPER-~D"
354 index)
355 ,(emit-fetch-wrapper
356 mt arg miss-tag (pop slot-vars))))))
357 args metatypes))
358 (wrapper-vars (mapcar #'car wrapper-bindings)))
359 (declare (fixnum index))
360 (unless wrapper-vars
361 (error "Every metatype is T."))
362 `(prog ()
363 (return
364 (let ,wrapper-bindings
365 ,(emit-cache-lookup cache-var wrapper-vars miss-tag value-var)
366 ,hit-form))
367 ,miss-tag
368 (return ,miss-form))))
370 ;; SLOTS-VAR, if supplied, is the variable to update with instance-slots
371 ;; by side-effect of fetching the wrapper for ARGUMENT.
372 (defun emit-fetch-wrapper (metatype argument miss-tag &optional slots-var)
373 (ecase metatype
374 ((standard-instance)
375 ;; This branch may run on non-pcl instances (structures). The
376 ;; result will be the non-wrapper layout for the structure, which
377 ;; will cause a miss. Since refencing the structure is rather iffy
378 ;; if it should have no slots, or only raw slots, we use FOR-STD-CLASS-P
379 ;; to ensure that we have a wrapper.
381 ;; FIXME: If we unify layouts and wrappers we can use
382 ;; instance-slots-layout instead of for-std-class-p, as if there
383 ;; are no layouts there are no slots to worry about.
384 (with-unique-names (wrapper)
385 `(cond ((std-instance-p ,argument)
386 ,(if slots-var
387 `(let ((,wrapper (%instance-layout ,argument)))
388 (when (layout-for-std-class-p ,wrapper)
389 (setq ,slots-var (std-instance-slots ,argument)))
390 ,wrapper)
391 `(%instance-layout ,argument)))
392 ((fsc-instance-p ,argument)
393 ,(if slots-var
394 `(let ((,wrapper (%funcallable-instance-layout ,argument)))
395 (when (layout-for-std-class-p ,wrapper)
396 (setq ,slots-var (fsc-instance-slots ,argument)))
397 ,wrapper)
398 `(%funcallable-instance-layout ,argument)))
399 (t (go ,miss-tag)))))
400 ;; Sep92 PCL used to distinguish between some of these cases (and
401 ;; spuriously exclude others). Since in SBCL
402 ;; WRAPPER-OF/LAYOUT-OF/BUILT-IN-OR-STRUCTURE-WRAPPER are all
403 ;; equivalent and inlined to each other, we can collapse some
404 ;; spurious differences.
405 ((class system-instance structure-instance condition-instance)
406 (when slots-var
407 (bug "SLOT requested for metatype ~S, but it isn't going to happen."
408 metatype))
409 `(layout-of ,argument))
410 ;; a metatype of NIL should never be seen here, as NIL is only in
411 ;; the metatypes before a generic function is fully initialized.
412 ;; T should never be seen because we never need to get a wrapper
413 ;; to do dispatch if all methods have T as the respective
414 ;; specializer.
415 ((t nil)
416 (bug "~@<metatype ~S seen in ~S.~@:>" metatype 'emit-fetch-wrapper))))