Moved ATA driver into its own package
[movitz-core.git] / compiler.lisp
blob0e04de0257844c15d18819dddef9c4c9c069a8e5
1 ;;;;------------------------------------------------------------------
2 ;;;;
3 ;;;; Copyright (C) 2001,2000, 2002-2005,
4 ;;;; Department of Computer Science, University of Tromso, Norway
5 ;;;;
6 ;;;; Description: A simple lisp compiler.
7 ;;;; Author: Frode Vatvedt Fjeld <frodef@acm.org>
8 ;;;; Created at: Wed Oct 25 12:30:49 2000
9 ;;;; Distribution: See the accompanying file COPYING.
10 ;;;;
11 ;;;; $Id: compiler.lisp,v 1.194 2008/03/06 21:14:22 ffjeld Exp $
12 ;;;;
13 ;;;;------------------------------------------------------------------
15 (in-package movitz)
17 (defvar *warn-function-change-p* t
18 "Emit a warning whenever a named function's code-vector changes size.")
20 (defvar *compiler-verbose-p* nil)
22 (defvar *compiler-do-optimize* t
23 "Apply the peephole optimizer to function code.")
25 (defvar *explain-peephole-optimizations* nil
26 "Emit some cryptic information about which peephole optimization
27 heuristics that fire. Used for debugging the optimizer.")
29 (defvar *compiler-use-cmov-p* nil
30 "Allow the compiler to emit CMOV instructions, making the code
31 incompatible with pre-pentium CPUs.")
33 (defvar *compiler-auto-stack-checks-p* t
34 "Make every compiled function check upon entry that the
35 stack-pointer is within bounds. Costs 3 code-bytes and a few cycles.")
37 (defvar *compiler-allow-transients* t
38 "Allow the compiler to keep function arguments solely in registers.
39 Hurst debugging, improves performance.")
41 (defvar *compiler-local-segment-prefix* '(:fs-override)
42 "Use these assembly-instruction prefixes when accessing the thread-local
43 run-time context.")
45 (defvar *compiler-global-segment-prefix* nil
46 "Use these assembly-instruction prefixes when accessing the global
47 run-time context.")
49 (defparameter *compiler-physical-segment-prefix* '(:gs-override)
50 "Use this instruction prefix when accessing a physical memory location (i.e. typically some memory-mapped hardware device).")
52 (defparameter *compiler-nonlocal-lispval-read-segment-prefix* '()
53 "Use this segment prefix when reading a lispval at (potentially)
54 non-local locations.")
56 (defparameter *compiler-nonlocal-lispval-write-segment-prefix* '(:es-override)
57 "Use this segment prefix when writing a lispval at (potentially)
58 non-local locations.")
60 (defparameter *compiler-use-cons-reader-segment-protocol-p* nil)
62 (defparameter *compiler-cons-read-segment-prefix* '(:gs-override)
63 "Use this segment prefix for CAR and CDR, when using cons-reader protocol.")
65 (defvar *compiler-allow-untagged-word-bits* 0
66 "Allow (temporary) untagged values of this bit-size to exist, because
67 the system ensures one way or another that there can be no pointers below
68 this size.")
70 (defvar *compiler-use-into-unbound-protocol* t
71 "Use #x7fffffff as the <unbound-value> and thereby the INTO
72 instruction for checking whether a value is the unbound value.")
74 (defvar *compiler-compile-eval-whens* t
75 "When encountering (eval-when (:compile-toplevel) <code>),
76 compile, using the host compiler, the code rather than just using eval.")
78 (defvar *compiler-compile-macro-expanders* t
79 "For macros of any kind, compile the macro-expanders using the host compiler.")
81 (defvar *compiler-do-type-inference* t
82 "Spend time and effort performing type inference and optimization.")
84 (defvar *compiler-produce-defensive-code* t
85 "Try to make code be extra cautious.")
87 (defvar *compiler-relink-recursive-funcall* t
88 "If true, also recursive function calls look up the function through the function name,
89 which enables tracing of recursive functions.")
91 (defvar *compiler-trust-user-type-declarations-p* t)
93 (defvar *compiling-function-name* nil)
94 (defvar muerte.cl:*compile-file-pathname* nil)
96 (defvar *extended-code-expanders*
97 (make-hash-table :test #'eq))
99 (defvar *extended-code-find-write-binding-and-type*
100 (make-hash-table :test #'eq))
103 (defparameter +enter-stack-frame-code+
104 '((:pushl :ebp)
105 (:movl :esp :ebp)
106 (:pushl :esi)))
108 (defun duplicatesp (list)
109 "Returns TRUE iff at least one object occurs more than once in LIST."
110 (if (null list)
112 (or (member (car list) (cdr list))
113 (duplicatesp (cdr list)))))
115 (defun compute-call-extra-prefix (pc size)
116 (let* ((return-pointer-tag (ldb (byte 3 0)
117 (+ pc size))))
118 (cond
119 ((or (= (tag :even-fixnum) return-pointer-tag)
120 (= (tag :odd-fixnum) return-pointer-tag))
121 ;; Insert a NOP
122 '(#x90))
123 ;;; ((= 3 return-pointer-tag)
124 ;;; ;; Insert two NOPs, 3 -> 5
125 ;;; '(#x90 #x90))
126 ((= (tag :character) return-pointer-tag)
127 ;; Insert three NOPs, 2 -> 5
128 '(#x90 #x90 #x90)
129 '(#x90)))))
131 (defun make-compiled-primitive (form environment top-level-p docstring)
132 "Primitive functions have no funobj, no stack-frame, and no implied
133 parameter/return value passing conventions."
134 (declare (ignore top-level-p docstring))
135 (let* ((env (make-local-movitz-environment environment nil))
136 (body-code (compiler-call #'compile-form
137 :form form
138 :funobj nil
139 :env env
140 :top-level-p nil
141 :result-mode :ignore))
142 ;; (ignmore (format t "~{~S~%~}" body-code))
143 (resolved-code (finalize-code body-code nil nil)))
145 (multiple-value-bind (code-vector symtab)
146 (let ((asm-x86:*cpu-mode* :32-bit)
147 (asm:*instruction-compute-extra-prefix-map*
148 '((:call . compute-call-extra-prefix))))
149 (asm:assemble-proglist (translate-program resolved-code :muerte.cl :cl)
150 :symtab (list (cons :nil-value (image-nil-word *image*)))))
151 (values (make-movitz-vector (length code-vector)
152 :element-type 'code
153 :initial-contents code-vector)
154 symtab))))
156 (defun register-function-code-size (funobj)
157 (let* ((name (movitz-print (movitz-funobj-name funobj)))
158 (hash-name name)
159 (new-size (length (movitz-vector-symbolic-data (movitz-funobj-code-vector funobj)))))
160 (assert name)
161 (let ((old-size (gethash hash-name (function-code-sizes *image*))))
162 (cond
163 ((not old-size))
164 ((not *warn-function-change-p*))
165 ((> new-size old-size)
166 (warn "~S grew from ~D to ~D bytes." name old-size new-size))
167 ((< new-size old-size)
168 (warn "~S shrunk from ~D to ~D bytes" name old-size new-size))))
169 (setf (gethash hash-name (function-code-sizes *image*)) new-size))
170 funobj)
172 (defclass movitz-funobj-pass1 ()
173 ((name
174 :initarg :name
175 :accessor movitz-funobj-name)
176 (lambda-list
177 :initarg :lambda-list
178 :accessor movitz-funobj-lambda-list)
179 (function-envs
180 :accessor function-envs)
181 (funobj-env
182 :initarg :funobj-env
183 :accessor funobj-env)
184 (extent
185 :initarg :extent
186 :initform :unused
187 :accessor movitz-funobj-extent)
188 (allocation
189 :initform nil
190 :accessor movitz-allocation)
191 (entry-protocol
192 :initform :default
193 :initarg :entry-protocol
194 :reader funobj-entry-protocol))
195 (:documentation "This class is used for funobjs during the first compiler pass.
196 Before the second pass, such objects will be change-class-ed to proper movitz-funobjs.
197 This way, we ensure that no undue side-effects on the funobj occur during pass 1."))
199 (defmethod print-object ((object movitz-funobj-pass1) stream)
200 (print-unreadable-object (object stream :type t :identity t)
201 (when (slot-boundp object 'name)
202 (write (movitz-funobj-name object) :stream stream)))
203 object)
205 (defun movitz-macro-expander-make-function (lambda-form &key name (type :unknown))
206 "Make a lambda-form that is a macro-expander into a proper function.
207 Gensym a name whose symbol-function is set to the macro-expander, and return that symbol."
208 (let ((function-name (gensym (format nil "~A-expander-~@[~A-~]" type name))))
209 (if *compiler-compile-macro-expanders*
210 (with-host-environment ()
211 (compile function-name lambda-form))
212 (setf (symbol-function function-name)
213 (coerce lambda-form 'function)))
214 function-name))
216 (defun make-compiled-funobj (name lambda-list declarations form env top-level-p &key funobj)
217 "Compiler entry-point for making a (lexically) top-level function."
218 (handler-bind (((or warning error)
219 (lambda (c)
220 (declare (ignore c))
221 (if (not (boundp 'muerte.cl:*compile-file-pathname*))
222 (format *error-output*
223 "~&;; While Movitz compiling ~S:" name)
224 (format *error-output*
225 "~&;; While Movitz compiling ~S in ~A:"
226 name muerte.cl:*compile-file-pathname*)))))
227 (with-retries-until-true (retry-funobj "Retry compilation of ~S." name)
228 (make-compiled-funobj-pass2
229 (make-compiled-funobj-pass1 name lambda-list declarations
230 form env top-level-p :funobj funobj)))))
232 (defun make-compiled-funobj-pass1 (name lambda-list declarations form env top-level-p
233 &key funobj)
234 "Per funobj (i.e. not necessarily top-level) entry-point for first-pass compilation.
235 If funobj is provided, its identity will be kept, but its type (and values) might change."
236 ;; The ability to provide funobj's identity is important when a
237 ;; function must be referenced before it can be compiled, e.g. for
238 ;; mutually recursive (lexically bound) functions.
239 (with-retries-until-true (retry-pass1 "Retry first-pass compilation of ~S." name)
240 ;; First-pass is mostly functional, so it can safely be restarted.
241 (multiple-value-bind (required-vars optional-vars rest-var key-vars aux-vars allow-p min max edx-var)
242 (decode-normal-lambda-list lambda-list)
243 (declare (ignore aux-vars allow-p min max))
244 ;; There are several main branches through the function
245 ;; compiler, and this is where we decide which one to take.
246 (funcall (cond
247 ((let ((sub-form (cddr form)))
248 (and (consp (car sub-form))
249 (eq 'muerte::numargs-case (caar sub-form))))
250 'make-compiled-function-pass1-numarg-case)
251 ((and (= 1 (length required-vars)) ; (x &optional y)
252 (= 1 (length optional-vars))
253 (movitz-constantp (nth-value 1 (decode-optional-formal (first optional-vars)))
254 env)
255 (null key-vars)
256 (not rest-var)
257 (not edx-var))
258 'make-compiled-function-pass1-1req1opt)
259 (t 'make-compiled-function-pass1))
260 name lambda-list declarations form env top-level-p funobj))))
262 (defun ensure-pass1-funobj (funobj class &rest init-args)
263 "If funobj is nil, return a fresh funobj of class.
264 Otherwise coerce funobj to class."
265 (apply #'reinitialize-instance
266 (if funobj
267 (change-class funobj class)
268 (make-instance class))
269 init-args))
271 (defun make-compiled-function-pass1-numarg-case (name lambda-list declarations form env top-level-p funobj)
272 (let* ((funobj (ensure-pass1-funobj funobj 'movitz-funobj-pass1
273 :entry-protocol :numargs-case
274 :name name
275 :lambda-list (movitz-read (lambda-list-simplify lambda-list))))
276 (funobj-env (make-local-movitz-environment env funobj :type 'funobj-env)))
277 (setf (funobj-env funobj) funobj-env
278 (function-envs funobj) nil)
279 (loop for (numargs lambda-list . clause-body) in (cdr (caddr form))
280 do (when (duplicatesp lambda-list)
281 (error "There are duplicates in lambda-list ~S." lambda-list))
282 (multiple-value-bind (clause-body clause-declarations)
283 (parse-declarations-and-body clause-body)
284 (let* ((function-env
285 (add-bindings-from-lambda-list lambda-list
286 (make-local-movitz-environment
287 funobj-env funobj
288 :type 'function-env
289 :declaration-context :funobj
290 :declarations
291 (append clause-declarations
292 declarations))))
293 (function-form (list* 'muerte.cl::block
294 (compute-function-block-name name)
295 clause-body)))
296 (multiple-value-bind (arg-init-code need-normalized-ecx-p)
297 (make-function-arguments-init funobj function-env)
298 (setf (extended-code function-env)
299 (append arg-init-code
300 (compiler-call #'compile-form
301 :form (make-special-funarg-shadowing function-env function-form)
302 :funobj funobj
303 :env function-env
304 :top-level-p top-level-p
305 :result-mode :function)))
306 (setf (need-normalized-ecx-p function-env) need-normalized-ecx-p))
307 (push (cons numargs function-env)
308 (function-envs funobj)))))
309 funobj))
311 (defun make-compiled-function-pass1-1req1opt (name lambda-list declarations form env top-level-p funobj)
312 "Returns funobj."
313 (when (duplicatesp lambda-list)
314 (error "There are duplicates in lambda-list ~S." lambda-list))
315 (let* ((funobj (ensure-pass1-funobj funobj 'movitz-funobj-pass1
316 :entry-protocol :1req1opt
317 :name name
318 :lambda-list (movitz-read (lambda-list-simplify lambda-list))))
319 (funobj-env (make-local-movitz-environment env funobj :type 'funobj-env))
320 (function-env (add-bindings-from-lambda-list
321 lambda-list
322 (make-local-movitz-environment funobj-env funobj
323 :type 'function-env
324 :need-normalized-ecx-p nil
325 :declaration-context :funobj
326 :declarations declarations)))
327 (optional-env (make-local-movitz-environment function-env funobj
328 :type 'function-env)))
329 (setf (funobj-env funobj) funobj-env)
330 ;; (print-code 'arg-init-code arg-init-code)
331 (setf (extended-code optional-env)
332 (compiler-call #'compile-form
333 :form (optional-function-argument-init-form
334 (movitz-binding (first (optional-vars function-env)) function-env nil))
335 :funobj funobj
336 :env optional-env
337 :result-mode :ebx))
338 (setf (extended-code function-env)
339 (append #+ignore arg-init-code
340 (compiler-call #'compile-form
341 :form (make-special-funarg-shadowing function-env form)
342 :funobj funobj
343 :env function-env
344 :top-level-p top-level-p
345 :result-mode :function)))
346 (setf (function-envs funobj)
347 (list (cons 'muerte.cl::t function-env)
348 (cons :optional optional-env)))
349 funobj))
351 (defun make-compiled-function-pass1 (name lambda-list declarations form env top-level-p funobj)
352 "Returns funobj."
353 (when (duplicatesp lambda-list)
354 (error "There are duplicates in lambda-list ~S." lambda-list))
355 (let* ((funobj (ensure-pass1-funobj funobj 'movitz-funobj-pass1
356 :name name
357 :lambda-list (movitz-read (lambda-list-simplify lambda-list))))
358 (funobj-env (make-local-movitz-environment env funobj :type 'funobj-env))
359 (function-env (add-bindings-from-lambda-list
360 lambda-list
361 (make-local-movitz-environment funobj-env funobj
362 :type 'function-env
363 :declaration-context :funobj
364 :declarations declarations))))
365 (setf (funobj-env funobj) funobj-env
366 (function-envs funobj) (list (cons 'muerte.cl::t function-env)))
367 (multiple-value-bind (arg-init-code need-normalized-ecx-p)
368 (make-function-arguments-init funobj function-env)
369 (setf (need-normalized-ecx-p function-env) need-normalized-ecx-p)
370 (setf (extended-code function-env)
371 (append arg-init-code
372 (compiler-call #'compile-form
373 :form (make-special-funarg-shadowing function-env form)
374 :funobj funobj
375 :env function-env
376 :top-level-p top-level-p
377 :result-mode :function))))
378 funobj))
381 (defun make-compiled-funobj-pass2 (toplevel-funobj-pass1)
382 "This is the entry-poing for second pass compilation for each top-level funobj."
383 (check-type toplevel-funobj-pass1 movitz-funobj-pass1)
384 (let ((toplevel-funobj (change-class toplevel-funobj-pass1 'movitz-funobj)))
385 (multiple-value-bind (toplevel-funobj function-binding-usage)
386 (resolve-borrowed-bindings toplevel-funobj)
387 (complete-funobj
388 (layout-stack-frames
389 (analyze-bindings
390 (resolve-sub-functions toplevel-funobj function-binding-usage)))))))
392 (defstruct (type-analysis (:type list))
393 (thunks)
394 (binding-types)
395 (encoded-type
396 (multiple-value-list (type-specifier-encode nil)))
397 (declared-encoded-type
398 (multiple-value-list (type-specifier-encode t))))
400 (defun make-type-analysis-with-declaration (binding)
401 (let ((declared-type
402 (if (not (and *compiler-trust-user-type-declarations-p*
403 (movitz-env-get (binding-name binding) :variable-type
404 nil (binding-env binding) nil)))
405 (multiple-value-list (type-specifier-encode t))
406 (multiple-value-list
407 (type-specifier-encode (movitz-env-get (binding-name binding) :variable-type
408 t (binding-env binding) nil))))))
409 ;; (warn "~S decl: ~A" binding (apply #'encoded-type-decode declared-type))
410 (make-type-analysis :declared-encoded-type declared-type)))
412 (defun analyze-bindings (toplevel-funobj)
413 "Figure out usage of bindings in a toplevel funobj.
414 Side-effects each binding's binding-store-type."
415 (if (not *compiler-do-type-inference*)
416 (labels
417 ((analyze-code (code)
418 (dolist (instruction code)
419 (when (listp instruction)
420 (let ((binding
421 (find-written-binding-and-type instruction)))
422 (when binding
423 (setf (binding-store-type binding)
424 (multiple-value-list (type-specifier-encode t)))))
425 (analyze-code (instruction-sub-program instruction)))))
426 (analyze-funobj (funobj)
427 (loop for (nil . function-env) in (function-envs funobj)
428 do (analyze-code (extended-code function-env)))
429 (loop for function-binding in (sub-function-binding-usage funobj) by #'cddr
430 do (analyze-funobj (function-binding-funobj function-binding)))
431 funobj))
432 (analyze-funobj toplevel-funobj))
433 (let ((binding-usage (make-hash-table :test 'eq)))
434 (labels ((binding-resolved-p (binding)
435 (or (typep binding 'constant-object-binding)
436 (typep binding 'function-argument)
437 (let ((analysis (gethash binding binding-usage)))
438 (and analysis
439 (null (type-analysis-thunks analysis))))))
440 (binding-resolve (binding)
441 (cond
442 ((not (bindingp binding))
443 binding)
444 ((typep binding 'constant-object-binding)
445 (apply #'encoded-type-decode
446 (binding-store-type binding)))
447 ((typep binding 'function-argument)
449 ((let ((analysis (gethash binding binding-usage)))
450 (assert (and (and analysis
451 (null (type-analysis-thunks analysis))))
452 (binding)
453 "Can't resolve unresolved binding ~S." binding)))
454 (*compiler-trust-user-type-declarations-p*
455 (let ((analysis (gethash binding binding-usage)))
456 (multiple-value-call #'encoded-type-decode
457 (apply #'encoded-types-and
458 (append (type-analysis-declared-encoded-type analysis)
459 (type-analysis-encoded-type analysis))))))
460 (t (let ((analysis (gethash binding binding-usage)))
461 (apply #'encoded-type-decode
462 (type-analysis-encoded-type analysis))))))
463 (type-is-t (type-specifier)
464 (or (eq type-specifier t)
465 (and (listp type-specifier)
466 (eq 'or (car type-specifier))
467 (some #'type-is-t (cdr type-specifier)))))
468 (analyze-store (binding type thunk thunk-args)
469 (assert (not (null type)) ()
470 "store-lexical with empty type.")
471 (assert (or (typep type 'binding)
472 (eql 1 (type-specifier-num-values type))) ()
473 "store-lexical with multiple-valued type: ~S for ~S" type binding)
474 #+ignore (warn "store ~S type ~S, thunk ~S" binding type thunk)
475 (let ((analysis (or (gethash binding binding-usage)
476 (setf (gethash binding binding-usage)
477 (make-type-analysis-with-declaration binding)))))
478 (cond
479 (thunk
480 (assert (some #'bindingp thunk-args))
481 (push (cons thunk thunk-args) (type-analysis-thunks analysis)))
482 ((and (bindingp type)
483 (binding-eql type binding))
484 (break "got binding type")
485 nil)
486 (t (setf (type-analysis-encoded-type analysis)
487 (multiple-value-list
488 (multiple-value-call
489 #'encoded-types-or
490 (values-list (type-analysis-encoded-type analysis))
491 (type-specifier-encode type))))))))
492 (analyze-code (code)
493 #+ignore (print-code 'analyze code)
494 (dolist (instruction code)
495 (when (listp instruction)
496 (multiple-value-bind (store-binding store-type thunk thunk-args)
497 (find-written-binding-and-type instruction)
498 (when store-binding
499 #+ignore (warn "store: ~S binding ~S type ~S thunk ~S"
500 instruction store-binding store-type thunk)
501 (analyze-store store-binding store-type thunk thunk-args)))
502 (analyze-code (instruction-sub-program instruction)))))
503 (analyze-funobj (funobj)
504 (loop for (nil . function-env) in (function-envs funobj)
505 do (analyze-code (extended-code function-env)))
506 (loop for function-binding in (sub-function-binding-usage funobj) by #'cddr
507 do (analyze-funobj (function-binding-funobj function-binding)))
508 funobj))
509 ;; 1. Examine each store to lexical bindings.
510 (analyze-funobj toplevel-funobj)
511 ;; 2.
512 (flet ((resolve-thunks ()
513 (loop with more-thunks-p = t
514 repeat 20
515 while more-thunks-p
516 do (setf more-thunks-p nil)
517 (maphash (lambda (binding analysis)
518 (declare (ignore binding))
519 (setf (type-analysis-thunks analysis)
520 (loop for (thunk . thunk-args) in (type-analysis-thunks analysis)
521 if (not (every #'binding-resolved-p thunk-args))
522 collect (cons thunk thunk-args)
523 else
524 do #+ignore
525 (warn "because ~S=>~S->~S completing ~S: ~S and ~S"
526 thunk thunk-args
527 (mapcar #'binding-resolve thunk-args)
528 binding
529 (type-analysis-declared-encoded-type analysis)
530 (multiple-value-list
531 (multiple-value-call
532 #'encoded-types-or
533 (values-list
534 (type-analysis-encoded-type analysis))
535 (type-specifier-encode
536 (apply thunk (mapcar #'binding-resolve
537 thunk-args))))))
538 (setf (type-analysis-encoded-type analysis)
539 (multiple-value-list
540 (multiple-value-call
541 #'encoded-types-and
542 (values-list
543 (type-analysis-declared-encoded-type analysis))
544 (multiple-value-call
545 #'encoded-types-or
546 (values-list
547 (type-analysis-encoded-type analysis))
548 (type-specifier-encode
549 (apply thunk (mapcar #'binding-resolve
550 thunk-args)))))))
551 (setf more-thunks-p t))))
552 binding-usage))))
553 (resolve-thunks)
554 (when *compiler-trust-user-type-declarations-p*
555 ;; For each unresolved binding, just use the declared type.
556 (maphash (lambda (binding analysis)
557 (declare (ignore binding))
558 (when (and (not (null (type-analysis-thunks analysis)))
559 (not (apply #'encoded-allp
560 (type-analysis-declared-encoded-type analysis))))
561 #+ignore
562 (warn "Trusting ~S, was ~S, because ~S [~S]"
563 binding
564 (type-analysis-encoded-type analysis)
565 (type-analysis-thunks analysis)
566 (loop for (thunk . thunk-args) in (type-analysis-thunks analysis)
567 collect (mapcar #'binding-resolved-p thunk-args)))
568 (setf (type-analysis-encoded-type analysis)
569 (type-analysis-declared-encoded-type analysis))
570 (setf (type-analysis-thunks analysis) nil))) ; Ignore remaining thunks.
571 binding-usage)
572 ;; Try one more time to resolve thunks.
573 (resolve-thunks)))
574 #+ignore
575 (maphash (lambda (binding analysis)
576 (when (type-analysis-thunks analysis)
577 (warn "Unable to infer type for ~S: ~S" binding
578 (type-analysis-thunks analysis))))
579 binding-usage)
580 ;; 3.
581 (maphash (lambda (binding analysis)
582 (setf (binding-store-type binding)
583 (cond
584 ((and (not (null (type-analysis-thunks analysis)))
585 *compiler-trust-user-type-declarations-p*
586 (movitz-env-get (binding-name binding) :variable-type nil
587 (binding-env binding) nil))
588 (multiple-value-list
589 (type-specifier-encode (movitz-env-get (binding-name binding) :variable-type
590 t (binding-env binding) nil))))
591 ((and *compiler-trust-user-type-declarations-p*
592 (movitz-env-get (binding-name binding) :variable-type nil
593 (binding-env binding) nil))
594 (multiple-value-list
595 (multiple-value-call #'encoded-types-and
596 (type-specifier-encode (movitz-env-get (binding-name binding) :variable-type
597 t (binding-env binding) nil))
598 (values-list (type-analysis-encoded-type analysis)))))
599 ((not (null (type-analysis-thunks analysis)))
600 (multiple-value-list (type-specifier-encode t)))
601 (t (type-analysis-encoded-type analysis))))
602 #+ignore (warn "Finally: ~S" binding))
603 binding-usage))))
604 toplevel-funobj)
606 (defun resolve-borrowed-bindings (toplevel-funobj)
607 "For <funobj>'s code, for every non-local binding used we create
608 a borrowing-binding in the funobj-env. This process must be done
609 recursively, depth-first wrt. sub-functions. Also, return a plist
610 of all function-bindings seen."
611 (check-type toplevel-funobj movitz-funobj)
612 (let ((function-binding-usage ()))
613 (labels ((process-binding (funobj binding usages)
614 (cond
615 ((typep binding 'constant-object-binding))
616 ((not (eq funobj (binding-funobj binding)))
617 (let ((borrowing-binding
618 (or (find binding (borrowed-bindings funobj)
619 :key #'borrowed-binding-target)
620 (car (push (movitz-env-add-binding (funobj-env funobj)
621 (make-instance 'borrowed-binding
622 :name (binding-name binding)
623 :target-binding binding))
624 (borrowed-bindings funobj))))))
625 ;; We don't want to borrow a forwarding-binding..
626 (when (typep (borrowed-binding-target borrowing-binding)
627 'forwarding-binding)
628 (change-class (borrowed-binding-target borrowing-binding)
629 'located-binding))
630 ;;; (warn "binding ~S of ~S is not local to ~S, replacing with ~S of ~S."
631 ;;; binding (binding-env binding) funobj
632 ;;; borrowing-binding (binding-env borrowing-binding))
633 ;;; (pushnew borrowing-binding
634 ;;; (getf (binding-lended-p binding) :lended-to))
635 (dolist (usage usages)
636 (pushnew usage (borrowed-binding-usage borrowing-binding)))
637 borrowing-binding))
638 (t ; Binding is local to this funobj
639 (typecase binding
640 (forwarding-binding
641 (process-binding funobj (forwarding-binding-target binding) usages)
642 #+ignore
643 (setf (forwarding-binding-target binding)
644 (process-binding funobj (forwarding-binding-target binding) usages)))
645 (function-binding
646 (dolist (usage usages)
647 (pushnew usage
648 (getf (sub-function-binding-usage (function-binding-parent binding))
649 binding))
650 (pushnew usage (getf function-binding-usage binding)))
651 binding)
652 (t binding)))))
653 (resolve-sub-funobj (funobj sub-funobj)
654 (dolist (binding-we-lend (borrowed-bindings (resolve-funobj-borrowing sub-funobj)))
655 #+ignore
656 (warn "Lending from ~S to ~S: ~S <= ~S"
657 funobj sub-funobj
658 (borrowed-binding-target binding-we-lend)
659 binding-we-lend)
660 (process-binding funobj
661 (borrowed-binding-target binding-we-lend)
662 (borrowed-binding-usage binding-we-lend))))
663 (resolve-code (funobj code)
664 (dolist (instruction code)
665 (when (listp instruction)
666 (let ((store-binding (find-written-binding-and-type instruction)))
667 (when store-binding
668 (process-binding funobj store-binding '(:write))))
669 (dolist (load-binding (find-read-bindings instruction))
670 (process-binding funobj load-binding '(:read)))
671 (case (car instruction)
672 (:call-lexical
673 (process-binding funobj (second instruction) '(:call)))
674 (:stack-cons
675 (destructuring-bind (proto-cons dynamic-scope)
676 (cdr instruction)
677 (push proto-cons (dynamic-extent-scope-members dynamic-scope))))
678 (:load-lambda
679 (destructuring-bind (lambda-binding lambda-result-mode capture-env)
680 (cdr instruction)
681 (declare (ignore lambda-result-mode))
682 (assert (eq funobj (binding-funobj lambda-binding)) ()
683 "A non-local lambda doesn't make sense. There must be a bug.")
684 (let ((lambda-funobj (function-binding-funobj lambda-binding)))
685 (let ((dynamic-scope (find-dynamic-extent-scope capture-env)))
686 (when dynamic-scope
687 ;; (warn "Adding ~S to ~S/~S" lambda-funobj dynamic-extent dynamic-scope)
688 (setf (movitz-funobj-extent lambda-funobj) :dynamic-extent
689 (movitz-allocation lambda-funobj) dynamic-scope)
690 (push lambda-funobj (dynamic-extent-scope-members dynamic-scope))
691 (process-binding funobj (base-binding dynamic-scope) '(:read))))
692 (resolve-sub-funobj funobj lambda-funobj)
693 (process-binding funobj lambda-binding '(:read))
694 ;; This funobj is effectively using every binding that the lambda
695 ;; is borrowing..
696 (map nil (lambda (borrowed-binding)
697 (process-binding funobj
698 (borrowed-binding-target borrowed-binding)
699 '(:read)))
700 (borrowed-bindings (function-binding-funobj lambda-binding))))))
701 (:local-function-init
702 (let ((function-binding (second instruction)))
703 (assert (eq funobj (binding-funobj function-binding)) ()
704 "Initialization of a non-local function doesn't make sense.")
705 (resolve-sub-funobj funobj (function-binding-funobj (second instruction)))
706 (map nil (lambda (borrowed-binding)
707 (process-binding funobj
708 (borrowed-binding-target borrowed-binding)
709 '(:read)))
710 (borrowed-bindings (function-binding-funobj (second instruction)))))))
711 (resolve-code funobj (instruction-sub-program instruction)))))
712 (resolve-funobj-borrowing (funobj)
713 (let ((funobj (change-class funobj 'movitz-funobj :borrowed-bindings nil)))
714 (loop for (nil . function-env) in (function-envs funobj)
715 do (resolve-code funobj (extended-code function-env)))
716 ;; (warn "~S borrows ~S." funobj (borrowed-bindings funobj))
717 funobj)))
718 (values (resolve-funobj-borrowing toplevel-funobj)
719 function-binding-usage))))
721 (defun resolve-sub-functions (toplevel-funobj function-binding-usage)
722 (assert (null (borrowed-bindings toplevel-funobj)) ()
723 "Can't deal with toplevel closures yet. Borrowed: ~S"
724 (borrowed-bindings toplevel-funobj))
725 (setf (movitz-funobj-extent toplevel-funobj) :indefinite-extent)
726 (let ((sub-funobj-index 0))
727 (loop for (function-binding usage) on function-binding-usage by #'cddr
728 do (let ((sub-funobj (function-binding-funobj function-binding)))
729 ;; (warn "USage: ~S => ~S" sub-funobj usage)
730 (case (car (movitz-funobj-name sub-funobj))
731 ((muerte.cl:lambda)
732 (setf (movitz-funobj-name sub-funobj)
733 (list 'muerte.cl:lambda
734 (movitz-funobj-name toplevel-funobj)
735 (post-incf sub-funobj-index)))))
736 (loop for borrowed-binding in (borrowed-bindings sub-funobj)
737 do (pushnew borrowed-binding
738 (getf (binding-lending (borrowed-binding-target borrowed-binding))
739 :lended-to)))
740 ;; (warn "old extent: ~S" (movitz-funobj-extent sub-funobj))
741 (cond
742 ((or (null usage)
743 (null (borrowed-bindings sub-funobj)))
744 (when (null usage)
745 (warn "null usage for ~S" sub-funobj))
746 (change-class function-binding 'funobj-binding)
747 (setf (movitz-funobj-extent sub-funobj)
748 :indefinite-extent))
749 ((equal usage '(:call))
750 (change-class function-binding 'closure-binding)
751 (setf (movitz-funobj-extent sub-funobj)
752 :lexical-extent))
753 ((eq :dynamic-extent (movitz-funobj-extent sub-funobj))
754 (change-class function-binding 'closure-binding))
755 (t (change-class function-binding 'closure-binding)
756 (setf (movitz-funobj-extent sub-funobj)
757 :indefinite-extent))))))
758 (loop for function-binding in function-binding-usage by #'cddr
759 do (finalize-funobj (function-binding-funobj function-binding)))
760 (finalize-funobj toplevel-funobj))
762 (defun finalize-funobj (funobj)
763 "Calculate funobj's constants, jumpers."
764 (loop with all-key-args-constants = nil
765 with all-constants-plist = () and all-jumper-sets = ()
766 for (nil . function-env) in (function-envs funobj)
767 ;; (borrowed-bindings body-code) in code-specs
768 as body-code = (extended-code function-env)
769 as (const-plist jumper-sets key-args-constants) =
770 (multiple-value-list (find-code-constants-and-jumpers body-code))
771 do (when key-args-constants
772 (assert (not all-key-args-constants) ()
773 "only one &key parsing allowed per funobj.")
774 (setf all-key-args-constants key-args-constants))
775 (loop for (constant usage) on const-plist by #'cddr
776 do (incf (getf all-constants-plist constant 0) usage))
777 (loop for (name set) on jumper-sets by #'cddr
778 do (assert (not (getf all-jumper-sets name)) ()
779 "Jumper-set ~S multiply defined." name)
780 (setf (getf all-jumper-sets name) set))
781 finally
782 (multiple-value-bind (const-list num-jumpers jumpers-map borrower-map)
783 (layout-funobj-vector all-constants-plist
784 all-key-args-constants
785 #+ignore (mapcar (lambda (x)
786 (cons (movitz-read x) 1))
787 '(:a :b :c :d))
788 all-jumper-sets
789 (borrowed-bindings funobj))
790 (setf (movitz-funobj-num-jumpers funobj) num-jumpers
791 (movitz-funobj-const-list funobj) const-list
792 (movitz-funobj-num-constants funobj) (length const-list)
793 (movitz-funobj-jumpers-map funobj) jumpers-map)
794 (loop for (binding . pos) in borrower-map
795 do (setf (borrowed-binding-reference-slot binding) pos))
796 (return funobj))))
798 (defun layout-stack-frames (funobj)
799 "Lay out the stack-frame (i.e. create a frame-map) for funobj
800 and all its local functions. This must be done breadth-first, because
801 a (lexical-extent) sub-function might care about its parent frame-map."
802 (loop for (nil . function-env) in (function-envs funobj)
803 do (assert (not (slot-boundp function-env 'frame-map)))
804 (setf (frame-map function-env)
805 (funobj-assign-bindings (extended-code function-env)
806 function-env)))
807 (loop for (sub-function-binding) on (sub-function-binding-usage funobj) by #'cddr
808 do (layout-stack-frames (function-binding-funobj sub-function-binding)))
809 funobj)
811 (defun complete-funobj (funobj)
812 (case (funobj-entry-protocol funobj)
813 (:1req1opt
814 (complete-funobj-1req1opt funobj))
815 (t (complete-funobj-default funobj)))
816 (loop for (sub-function-binding) on (sub-function-binding-usage funobj) by #'cddr
817 do (complete-funobj (function-binding-funobj sub-function-binding)))
818 (register-function-code-size funobj))
820 (defun complete-funobj-1req1opt (funobj)
821 (assert (= 2 (length (function-envs funobj))))
822 (let* ((function-env (cdr (assoc 'muerte.cl::t (function-envs funobj))))
823 (optional-env (cdr (assoc :optional (function-envs funobj))))
824 (frame-map (frame-map function-env))
825 (resolved-code (finalize-code (extended-code function-env) funobj frame-map))
826 (resolved-optional-code (finalize-code (extended-code optional-env) funobj frame-map))
827 (stack-frame-size (frame-map-size (frame-map function-env)))
828 (use-stack-frame-p (or (plusp stack-frame-size)
829 (tree-search resolved-code
830 '(:pushl :popl :ebp :esp :call :leave))
831 (some (lambda (x)
832 (and (not (equal '(:movl (:ebp -4) :esi) x))
833 (tree-search x ':esi)))
834 resolved-code))))
835 (let* ((function-code
836 (let* ((req-binding (movitz-binding (first (required-vars function-env))
837 function-env nil))
838 (req-location (cdr (assoc req-binding frame-map)))
839 (opt-binding (movitz-binding (first (optional-vars function-env))
840 function-env nil))
841 (opt-location (cdr (assoc opt-binding frame-map)))
842 (optp-binding (movitz-binding (optional-function-argument-supplied-p-var opt-binding)
843 function-env nil))
844 (optp-location (cdr (assoc optp-binding frame-map)))
845 (stack-setup-pre 0))
846 (append `((:jmp (:edi ,(global-constant-offset 'trampoline-cl-dispatch-1or2))))
847 '(entry%1op)
848 (unless (eql nil opt-location)
849 resolved-optional-code)
850 (when optp-location
851 `((:movl :edi :edx)
852 (:jmp 'optp-into-edx-ok)))
853 '(entry%2op)
854 (when optp-location
855 `((,*compiler-global-segment-prefix*
856 :movl (:edi ,(global-constant-offset 't-symbol)) :edx)
857 optp-into-edx-ok))
858 (when use-stack-frame-p
859 +enter-stack-frame-code+)
860 '(start-stack-frame-setup)
861 (cond
862 ((and (eql 1 req-location)
863 (eql 2 opt-location))
864 (incf stack-setup-pre 2)
865 `((:pushl :eax)
866 (:pushl :ebx)))
867 ((and (eql 1 req-location)
868 (eql nil opt-location))
869 (incf stack-setup-pre 1)
870 `((:pushl :eax)))
871 ((and (member req-location '(nil :eax))
872 (eql 1 opt-location))
873 (incf stack-setup-pre 1)
874 `((:pushl :ebx)))
875 ((and (member req-location '(nil :eax))
876 (member opt-location '(nil :ebx)))
877 nil)
878 (t (error "Can't deal with req ~S opt ~S."
879 req-location opt-location)))
880 (cond
881 ((not optp-location)
882 (make-stack-setup-code (- stack-frame-size stack-setup-pre)))
883 ((and (integerp optp-location)
884 (= optp-location (1+ stack-setup-pre)))
885 (append `((:pushl :edx))
886 (make-stack-setup-code (- stack-frame-size stack-setup-pre 1))))
887 ((integerp optp-location)
888 (append (make-stack-setup-code (- stack-frame-size stack-setup-pre))
889 `((:movl :edx (:ebp ,(stack-frame-offset optp-location))))))
890 (t (error "Can't deal with optional-p at ~S, after (~S ~S)."
891 optp-location req-location opt-location)))
892 (flet ((make-lending (location lended-cons-position)
893 (etypecase req-location
894 (integer
895 `((:movl (:ebp ,(stack-frame-offset location)) :edx)
896 (:movl :edi (:ebp ,(stack-frame-offset lended-cons-position))) ; cdr
897 (:movl :edx (:ebp ,(stack-frame-offset (1+ lended-cons-position)))) ; car
898 (:leal (:ebp 1 ,(stack-frame-offset (1+ lended-cons-position)))
899 :edx)
900 (:movl :edx (:ebp ,(stack-frame-offset location))))))))
901 (append
902 (when (binding-lended-p req-binding)
903 (make-lending req-location (getf (binding-lending req-binding)
904 :stack-cons-location)))
905 (when (binding-lended-p opt-binding)
906 (make-lending opt-location (getf (binding-lending opt-binding)
907 :stack-cons-location)))
908 (when (and optp-binding (binding-lended-p optp-binding))
909 (make-lending optp-location (getf (binding-lending optp-binding)
910 :stack-cons-location)))))
911 resolved-code
912 (make-compiled-function-postlude funobj function-env
913 use-stack-frame-p)))))
914 (let ((optimized-function-code
915 (optimize-code function-code
916 :keep-labels (append (subseq (movitz-funobj-const-list funobj)
917 0 (movitz-funobj-num-jumpers funobj))
918 '(entry%1op entry%2op)))))
919 (assemble-funobj funobj optimized-function-code)))))
921 (defun complete-funobj-default (funobj)
922 (let ((code-specs
923 (loop for (numargs . function-env) in (function-envs funobj)
924 collecting
925 (let* ((frame-map (frame-map function-env))
926 (resolved-code (finalize-code (extended-code function-env) funobj frame-map))
927 (stack-frame-size (frame-map-size (frame-map function-env)))
928 (use-stack-frame-p (or (plusp stack-frame-size)
929 (tree-search resolved-code
930 '(:push :pop :ebp :esp :call :leave))
931 (some (lambda (x)
932 (and (not (equal '(:movl (:ebp -4) :esi) x))
933 (tree-search x ':esi)))
934 resolved-code))))
935 (multiple-value-bind (prelude-code have-normalized-ecx-p)
936 (make-compiled-function-prelude stack-frame-size function-env use-stack-frame-p
937 (need-normalized-ecx-p function-env) frame-map
938 :do-check-stack-p (or (<= 32 stack-frame-size)
939 (tree-search resolved-code
940 '(:call))))
941 (let ((function-code
942 (install-arg-cmp (append prelude-code
943 resolved-code
944 (make-compiled-function-postlude funobj function-env
945 use-stack-frame-p))
946 have-normalized-ecx-p)))
947 (let ((optimized-function-code
948 (optimize-code function-code
949 :keep-labels (append
950 (subseq (movitz-funobj-const-list funobj)
951 0 (movitz-funobj-num-jumpers funobj))
952 '(entry%1op
953 entry%2op
954 entry%3op)))))
955 (cons numargs optimized-function-code))))))))
956 (let ((code1 (cdr (assoc 1 code-specs)))
957 (code2 (cdr (assoc 2 code-specs)))
958 (code3 (cdr (assoc 3 code-specs)))
959 (codet (cdr (assoc 'muerte.cl::t code-specs))))
960 (assert codet () "A default numargs-case is required.")
961 ;; (format t "codet:~{~&~A~}" codet)
962 (let ((combined-code
963 (delete 'start-stack-frame-setup
964 (append
965 (when code1
966 `((:cmpb 1 :cl)
967 (:jne 'not-one-arg)
968 ,@(unless (find 'entry%1op code1)
969 '(entry%1op (:movb 1 :cl)))
970 ,@code1
971 not-one-arg))
972 (when code2
973 `((:cmpb 2 :cl)
974 (:jne 'not-two-args)
975 ,@(unless (find 'entry%2op code2)
976 '(entry%2op (:movb 2 :cl)))
977 ,@code2
978 not-two-args))
979 (when code3
980 `((:cmpb 3 :cl)
981 (:jne 'not-three-args)
982 ,@(unless (find 'entry%3op code3)
983 '(entry%3op (:movb 3 :cl)))
984 ,@code3
985 not-three-args))
986 (delete-if (lambda (x)
987 (or (and code1 (eq x 'entry%1op))
988 (and code2 (eq x 'entry%2op))
989 (and code3 (eq x 'entry%3op))))
990 codet)))))
991 ;; (print-code funobj combined-code)
992 (assemble-funobj funobj combined-code))))
993 funobj)
995 (defun assemble-funobj (funobj combined-code)
996 (multiple-value-bind (code-vector code-symtab)
997 (let ((asm-x86:*cpu-mode* :32-bit)
998 (asm:*instruction-compute-extra-prefix-map*
999 '((:call . compute-call-extra-prefix))))
1000 (asm:assemble-proglist combined-code
1001 :symtab (list* (cons :nil-value (image-nil-word *image*))
1002 (loop for (label . set) in (movitz-funobj-jumpers-map funobj)
1003 collect (cons label
1004 (* 4 (or (search set (movitz-funobj-const-list funobj)
1005 :end2 (movitz-funobj-num-jumpers funobj))
1006 (error "Jumper for ~S missing." label))))))))
1007 (setf (movitz-funobj-symtab funobj) code-symtab)
1008 (let* ((code-length (- (length code-vector) 3 -3))
1009 (code-vector (make-array code-length
1010 :initial-contents code-vector
1011 :fill-pointer t)))
1012 (setf (fill-pointer code-vector) code-length)
1013 ;; debug info
1014 (setf (ldb (byte 1 5) (slot-value funobj 'debug-info))
1015 1 #+ignore (if use-stack-frame-p 1 0))
1016 (let ((x (cdr (assoc 'start-stack-frame-setup code-symtab))))
1017 (cond
1018 ((not x)
1019 #+ignore (warn "No start-stack-frame-setup label for ~S." name))
1020 ((<= 0 x 30)
1021 (setf (ldb (byte 5 0) (slot-value funobj 'debug-info)) x))
1022 (t (warn "Can't encode start-stack-frame-setup label ~D into debug-info for ~S."
1023 x (movitz-funobj-name funobj)))))
1024 (let* ((a (or (cdr (assoc 'entry%1op code-symtab)) 0))
1025 (b (or (cdr (assoc 'entry%2op code-symtab)) a))
1026 (c (or (cdr (assoc 'entry%3op code-symtab)) b)))
1027 (unless (<= a b c)
1028 (warn "Weird code-entries: ~D, ~D, ~D." a b c))
1029 (unless (<= 0 a 255)
1030 (break "entry%1: ~D" a))
1031 (unless (<= 0 b 2047)
1032 (break "entry%2: ~D" b))
1033 (unless (<= 0 c 4095)
1034 (break "entry%3: ~D" c)))
1035 (loop for (entry-label slot-name) in '((entry%1op code-vector%1op)
1036 (entry%2op code-vector%2op)
1037 (entry%3op code-vector%3op))
1038 do (when (assoc entry-label code-symtab)
1039 (let ((offset (cdr (assoc entry-label code-symtab))))
1040 (setf (slot-value funobj slot-name)
1041 (cons offset funobj)))))
1042 (check-locate-concistency code-vector)
1043 (setf (movitz-funobj-code-vector funobj)
1044 (make-movitz-vector (length code-vector)
1045 :fill-pointer code-length
1046 :element-type 'code
1047 :initial-contents code-vector))))
1048 funobj)
1050 (defun check-locate-concistency (code-vector)
1051 (loop for x from 0 below (length code-vector) by 8
1052 do (when (and (= (tag :basic-vector) (aref code-vector x))
1053 (= (enum-value 'movitz-vector-element-type :code) (aref code-vector (1+ x)))
1054 (or (<= #x4000 (length code-vector))
1055 (and (= (ldb (byte 8 0) (length code-vector))
1056 (aref code-vector (+ x 2)))
1057 (= (ldb (byte 8 8) (length code-vector))
1058 (aref code-vector (+ x 3))))))
1059 (break "Code-vector (length #x~X) can break %find-code-vector at ~D: #x~2,'0X~2,'0X ~2,'0X~2,'0X."
1060 (length code-vector) x
1061 (aref code-vector (+ x 0))
1062 (aref code-vector (+ x 1))
1063 (aref code-vector (+ x 2))
1064 (aref code-vector (+ x 3)))))
1065 (values))
1068 (defun make-2req (binding0 binding1 frame-map)
1069 (let ((location-0 (new-binding-location binding0 frame-map))
1070 (location-1 (new-binding-location binding1 frame-map)))
1071 (cond
1072 ((and (eq :eax location-0)
1073 (eq :ebx location-1))
1074 (values nil 0))
1075 ((and (eq :ebx location-0)
1076 (eq :eax location-1))
1077 (values '((:xchgl :eax :ebx)) 0))
1078 ((and (eql 1 location-0)
1079 (eql 2 location-1))
1080 (values '((:pushl :eax)
1081 (:pushl :ebx))
1083 ((and (eq :eax location-0)
1084 (eql 1 location-1))
1085 (values '((:pushl :ebx))
1087 (t (error "make-2req confused by loc0: ~W, loc1: ~W" location-0 location-1)))))
1090 (defun movitz-compile-file (path &key ((:image *image*) *image*)
1091 load-priority
1092 (delete-file-p nil))
1093 (handler-bind
1094 (#+sbcl (sb-ext:defconstant-uneql #'continue))
1095 (unwind-protect
1096 (let ((*movitz-host-features* *features*)
1097 (*features* (image-movitz-features *image*)))
1098 (multiple-value-prog1
1099 (movitz-compile-file-internal path load-priority)
1100 (unless (equalp *features* (image-movitz-features *image*))
1101 (warn "*features* changed from ~S to ~S." (image-movitz-features *image*) *features*)
1102 (setf (image-movitz-features *image*) *features*))))
1103 (when delete-file-p
1104 (assert (equal (pathname-directory "/tmp/")
1105 (pathname-directory path))
1106 (path)
1107 "Refusing to delete file not in /tmp.")
1108 (delete-file path)))))
1110 (defun movitz-compile-file-internal (path &optional (*default-load-priority*
1111 (and (boundp '*default-load-priority*)
1112 (symbol-value '*default-load-priority*)
1113 (1+ (symbol-value '*default-load-priority*)))))
1114 (declare (special *default-load-priority*))
1115 (with-simple-restart (continue "Skip Movitz compilation of ~S." path)
1116 (with-retries-until-true (retry "Restart Movitz compilation of ~S." path)
1117 (with-open-file (stream path :direction :input)
1118 (let ((*package* (find-package :muerte)))
1119 (movitz-compile-stream-internal stream :path path))))))
1121 (defun movitz-compile-stream (stream &key (path "unknown-toplevel.lisp") (package :muerte))
1122 (handler-bind
1123 (#+sbcl (sb-ext:defconstant-uneql #'continue))
1124 (unwind-protect
1125 (let ((*package* (find-package package))
1126 (*movitz-host-features* *features*)
1127 (*features* (image-movitz-features *image*)))
1128 (multiple-value-prog1
1129 (movitz-compile-stream-internal stream :path path)
1130 (unless (equalp *features* (image-movitz-features *image*))
1131 (warn "*features* changed from ~S to ~S." (image-movitz-features *image*) *features*)
1132 (setf (image-movitz-features *image*) *features*)))))))
1134 (defun movitz-compile-stream-internal (stream &key (path "unknown-toplevel.lisp"))
1135 (let* ((muerte.cl::*compile-file-pathname* path)
1136 (funobj (make-instance 'movitz-funobj-pass1
1137 :name (intern (format nil "~A" path) :muerte)
1138 :lambda-list (movitz-read nil)))
1139 (funobj-env (make-local-movitz-environment nil funobj
1140 :type 'funobj-env
1141 :declaration-context :funobj))
1142 (function-env (make-local-movitz-environment funobj-env funobj
1143 :type 'function-env
1144 :declaration-context :funobj))
1145 (file-code
1146 (with-compilation-unit ()
1147 (add-bindings-from-lambda-list () function-env)
1148 (setf (funobj-env funobj) funobj-env)
1149 (loop for form = (with-movitz-syntax ()
1150 (read stream nil '#0=#:eof))
1151 until (eq form '#0#)
1152 appending
1153 (with-simple-restart (skip-toplevel-form
1154 "Skip the compilation of top-level form~{ ~A~}."
1155 (cond
1156 ((symbolp form)
1157 (list form))
1158 ((symbolp (car form))
1159 (list (car form)
1160 (cadr form)))))
1161 (when *compiler-verbose-p*
1162 (format *query-io* "~&Movitz Compiling ~S..~%"
1163 (cond
1164 ((symbolp form) form)
1165 ((symbolp (car form))
1166 (xsubseq form 0 2)))))
1167 (compiler-call #'compile-form
1168 :form form
1169 :funobj funobj
1170 :env function-env
1171 :top-level-p t
1172 :result-mode :ignore))))))
1173 (cond
1174 ((null file-code)
1175 (setf (image-load-time-funobjs *image*)
1176 (delete funobj (image-load-time-funobjs *image*) :key #'first))
1177 'muerte::constantly-true)
1178 (t (setf (extended-code function-env) file-code
1179 (need-normalized-ecx-p function-env) nil
1180 (function-envs funobj) (list (cons 'muerte.cl::t function-env))
1181 (funobj-env funobj) funobj-env)
1182 (make-compiled-funobj-pass2 funobj)
1183 (let ((name (funobj-name funobj)))
1184 (setf (movitz-env-named-function name) funobj)
1185 name)))))
1187 ;;;;
1189 (defun print-code (x code)
1190 (let ((*print-level* 4))
1191 (format t "~&~A code:~{~& ~A~}" x code))
1192 code)
1194 (defun layout-program (pc)
1195 "For the program in pc, layout sub-programs at the top-level program."
1196 (do ((previous-subs nil)
1197 (pending-subs nil)
1198 (new-program nil))
1199 ((endp pc)
1200 (assert (not pending-subs) ()
1201 "pending sub-programs: ~S" pending-subs)
1202 (nreverse new-program))
1203 (let ((i (pop pc)))
1204 (multiple-value-bind (sub-prg sub-opts)
1205 (instruction-sub-program i)
1206 (if (null sub-prg)
1207 (push i new-program)
1208 (destructuring-bind (&optional (label (gensym "sub-prg-label-")))
1209 sub-opts
1210 (let ((x (cons label sub-prg)))
1211 (unless (find x previous-subs :test #'equal)
1212 (push x pending-subs)
1213 (push x previous-subs)))
1214 (unless (instruction-is i :jnever)
1215 (push `(,(car i) ',label)
1216 new-program))))
1217 (when (or (instruction-uncontinues-p i)
1218 (endp pc))
1219 (let* ((match-label (and (eq (car i) :jmp)
1220 (consp (second i))
1221 (eq (car (second i)) 'quote)
1222 (symbolp (second (second i)))
1223 (second (second i))))
1224 (matching-sub (assoc match-label pending-subs)))
1225 (unless (and match-label
1226 (or (eq match-label (first pc))
1227 (and (symbolp (first pc))
1228 (eq match-label (second pc)))))
1229 (if matching-sub
1230 (setf pc (append (cdr matching-sub) pc)
1231 pending-subs (delete matching-sub pending-subs))
1232 (setf pc (append (reduce #'append (nreverse pending-subs)) pc)
1233 pending-subs nil)))))))))
1236 (defun optimize-code (unoptimized-code &rest args)
1237 #+ignore (print-code 'to-optimize unoptimized-code)
1238 (if (not *compiler-do-optimize*)
1239 (layout-program (optimize-code-unfold-branches unoptimized-code))
1240 (apply #'optimize-code-internal
1241 (optimize-code-dirties
1242 (layout-program (optimize-code-unfold-branches unoptimized-code)))
1243 0 args)))
1245 (defun optimize-code-unfold-branches (unoptimized-code)
1246 "This particular optimization should be done before code layout:
1247 (:jcc 'label) (:jmp 'foo) label => (:jncc 'foo) label"
1248 (flet ((explain (always format &rest args)
1249 (when (or always *explain-peephole-optimizations*)
1250 (warn "Peephole: ~?~&----------------------------" format args)))
1251 (branch-instruction-label (i &optional jmp (branch-types '(:je :jne :jb :jnb :jbe :jz
1252 :jl :jnz :jle :ja :jae :jg
1253 :jge :jnc :jc :js :jns)))
1254 "If i is a branch, return the label."
1255 (when jmp (push :jmp branch-types))
1256 (let ((i (ignore-instruction-prefixes i)))
1257 (or (and (listp i) (member (car i) branch-types)
1258 (listp (second i)) (member (car (second i)) '(quote muerte.cl::quote))
1259 (second (second i))))))
1260 (negate-branch (branch-type)
1261 (ecase branch-type
1262 (:jb :jnb) (:jnb :jb)
1263 (:jbe :ja) (:ja :jbe)
1264 (:jz :jnz) (:jnz :jz)
1265 (:je :jne) (:jne :je)
1266 (:jc :jnc) (:jnc :jc)
1267 (:jl :jge) (:jge :jl)
1268 (:jle :jg) (:jg :jle))))
1269 (loop with next-pc = 'auto-next
1270 ;; initially (warn "opt: ~{ ~A~%~}" unoptimized-code)
1271 for pc = unoptimized-code then (prog1 (if (eq 'auto-next next-pc) auto-next-pc next-pc)
1272 (setq next-pc 'auto-next))
1273 as auto-next-pc = (cdr unoptimized-code) then (cdr pc)
1274 as p = (list (car pc)) ; will be appended.
1275 as i1 = (first pc) ; current instruction, collected by default.
1276 and i2 = (second pc) and i3 = (third pc)
1277 while pc
1278 do (when (and (branch-instruction-label i1)
1279 (branch-instruction-label i2 t nil)
1280 (symbolp i3)
1281 (eq i3 (branch-instruction-label i1)))
1282 (setf p (list `(,(negate-branch (car i1)) ',(branch-instruction-label i2 t nil))
1284 next-pc (nthcdr 3 pc))
1285 (explain nil "Got a sit: ~{~&~A~} => ~{~&~A~}" (subseq pc 0 3) p))
1286 nconc p)))
1288 (defun optimize-code-dirties (unoptimized-code)
1289 "These optimizations may rearrange register usage in a way that is incompatible
1290 with other optimizations that track register usage. So this is performed just once,
1291 initially."
1292 unoptimized-code
1293 #+ignore
1294 (labels ; This stuff doesn't work..
1295 ((explain (always format &rest args)
1296 (when (or always *explain-peephole-optimizations*)
1297 (warn "Peephole: ~?~&----------------------------" format args)))
1298 (twop-p (c &optional op)
1299 (let ((c (ignore-instruction-prefixes c)))
1300 (and (listp c) (= 3 (length c))
1301 (or (not op) (eq op (first c)))
1302 (cdr c))))
1303 (twop-dst (c &optional op src)
1304 (let ((c (ignore-instruction-prefixes c)))
1305 (and (or (not src)
1306 (equal src (first (twop-p c op))))
1307 (second (twop-p c op)))))
1308 (twop-src (c &optional op dest)
1309 (let ((c (ignore-instruction-prefixes c)))
1310 (and (or (not dest)
1311 (equal dest (second (twop-p c op))))
1312 (first (twop-p c op)))))
1313 (register-operand (op)
1314 (and (member op '(:eax :ebx :ecx :edx :edi))
1315 op)))
1316 (loop with next-pc = 'auto-next
1317 ;; initially (warn "opt: ~{ ~A~%~}" unoptimized-code)
1318 for pc = unoptimized-code then (prog1 (if (eq 'auto-next next-pc) auto-next-pc next-pc)
1319 (setq next-pc 'auto-next))
1320 as auto-next-pc = (cdr unoptimized-code) then (cdr pc)
1321 as p = (list (car pc)) ; will be appended.
1322 as i1 = (first pc) ; current instruction, collected by default.
1323 and i2 = (second pc) and i3 = (third pc)
1324 while pc
1325 do (let ((regx (register-operand (twop-src i1 :movl)))
1326 (regy (register-operand (twop-dst i1 :movl))))
1327 (when (and regx regy
1328 (eq regx (twop-dst i2 :movl))
1329 (eq regx (twop-src i3 :cmpl))
1330 (eq regy (twop-dst i3 :cmpl)))
1331 (setq p (list `(:cmpl ,(twop-src i2) ,regx) i1)
1332 next-pc (nthcdr 3 pc))
1333 (explain t "4: ~S for ~S [regx ~S, regy ~S]" p (subseq pc 0 5) regx regy)))
1334 nconc p)))
1336 (defun xsubseq (sequence start end)
1337 (subseq sequence start (min (length sequence) end)))
1339 (defun optimize-code-internal (unoptimized-code recursive-count &rest key-args
1340 &key keep-labels stack-frame-size)
1341 "Peephole optimizer. Based on a lot of rather random heuristics."
1342 (declare (ignore stack-frame-size))
1343 (when (<= 20 recursive-count)
1344 (error "Peephole-optimizer recursive count reached ~D.
1345 There is (propably) a bug in the peephole optimizer." recursive-count))
1346 ;; (warn "==================OPTIMIZE: ~{~&~A~}" unoptimized-code)
1347 (macrolet ((explain (always format &rest args)
1348 `(when (or *explain-peephole-optimizations* ,always)
1349 (warn "Peephole: ~@?~&----------------------------" ,format ,@args))))
1350 (labels
1351 (#+ignore
1352 (explain (always format &rest args)
1353 (when (or always *explain-peephole-optimizations*)
1354 (warn "Peephole: ~?~&----------------------------" format args)))
1355 (twop-p (c &optional op)
1356 (let ((c (ignore-instruction-prefixes c)))
1357 (and (listp c) (= 3 (length c))
1358 (or (not op) (eq op (first c)))
1359 (cdr c))))
1360 (twop-dst (c &optional op src)
1361 (let ((c (ignore-instruction-prefixes c)))
1362 (and (or (not src)
1363 (equal src (first (twop-p c op))))
1364 (second (twop-p c op)))))
1365 (twop-src (c &optional op dest)
1366 (let ((c (ignore-instruction-prefixes c)))
1367 (and (or (not dest)
1368 (equal dest (second (twop-p c op))))
1369 (first (twop-p c op)))))
1370 (isrc (c)
1371 (let ((c (ignore-instruction-prefixes c)))
1372 (ecase (length (cdr c))
1373 (0 nil)
1374 (1 (cadr c))
1375 (2 (twop-src c)))))
1376 (idst (c)
1377 (let ((c (ignore-instruction-prefixes c)))
1378 (ecase (length (cdr c))
1379 (0 nil)
1380 (1 (cadr c))
1381 (2 (twop-dst c)))))
1382 (non-destructive-p (c)
1383 (let ((c (ignore-instruction-prefixes c)))
1384 (and (consp c)
1385 (member (car c) '(:testl :testb :cmpl :cmpb :frame-map :std)))))
1386 (simple-instruction-p (c)
1387 (let ((c (ignore-instruction-prefixes c)))
1388 (and (listp c)
1389 (member (car c)
1390 '(:movl :xorl :popl :pushl :cmpl :leal :andl :addl :subl)))))
1391 (register-indirect-operand (op base)
1392 (multiple-value-bind (reg off)
1393 (when (listp op)
1394 (loop for x in op
1395 if (integerp x) sum x into off
1396 else collect x into reg
1397 finally (return (values reg off))))
1398 (and (eq base (car reg))
1399 (not (rest reg))
1400 off)))
1401 (stack-frame-operand (op)
1402 (register-indirect-operand op :ebp))
1403 (funobj-constant-operand (op)
1404 (register-indirect-operand op :esi))
1405 (global-constant-operand (op)
1406 (register-indirect-operand op :edi))
1407 (global-funcall-p (op &optional funs)
1408 (let ((op (ignore-instruction-prefixes op)))
1409 (when (instruction-is op :call)
1410 (let ((x (global-constant-operand (second op))))
1411 (flet ((try (name)
1412 (and (eql x (slot-offset 'movitz-run-time-context name))
1413 name)))
1414 (cond
1415 ((not x) nil)
1416 ((null funs) t)
1417 ((atom funs) (try funs))
1418 (t (some #'try funs))))))))
1419 (preserves-stack-location-p (i stack-location)
1420 (let ((i (ignore-instruction-prefixes i)))
1421 (and (not (atom i))
1422 (or (global-funcall-p i)
1423 (instruction-is i :frame-map)
1424 (branch-instruction-label i)
1425 (non-destructive-p i)
1426 (and (simple-instruction-p i)
1427 (not (eql stack-location (stack-frame-operand (idst i)))))))))
1428 (preserves-register-p (i register)
1429 (let ((i (ignore-instruction-prefixes i)))
1430 (and (not (atom i))
1431 (not (and (eq register :esp)
1432 (member (instruction-is i)
1433 '(:pushl :popl))))
1434 (or (and (simple-instruction-p i)
1435 (not (eq register (idst i))))
1436 (instruction-is i :frame-map)
1437 (branch-instruction-label i)
1438 (non-destructive-p i)
1439 (and (member register '(:edx))
1440 (member (global-funcall-p i)
1441 '(fast-car fast-cdr fast-car-ebx fast-cdr-ebx)))
1442 (and (not (eq register :esp))
1443 (instruction-is i :pushl))))))
1444 (operand-register-indirect-p (operand register)
1445 (and (consp operand)
1446 (tree-search operand register)))
1447 (doesnt-read-register-p (i register)
1448 (let ((i (ignore-instruction-prefixes i)))
1449 (or (symbolp i)
1450 (and (simple-instruction-p i)
1451 (if (member (instruction-is i) '(:movl))
1452 (and (not (eq register (twop-src i)))
1453 (not (operand-register-indirect-p (twop-src i) register))
1454 (not (operand-register-indirect-p (twop-dst i) register)))
1455 (not (or (eq register (isrc i))
1456 (operand-register-indirect-p (isrc i) register)
1457 (eq register (idst i))
1458 (operand-register-indirect-p (idst i) register)))))
1459 (instruction-is i :frame-map)
1460 (and (member register '(:edx))
1461 (member (global-funcall-p i)
1462 '(fast-car fast-cdr fast-car-ebx fast-cdr-ebx))))))
1463 (register-operand (op)
1464 (and (member op '(:eax :ebx :ecx :edx :edi))
1465 op))
1466 (true-and-equal (x &rest more)
1467 (declare (dynamic-extent more))
1468 (and x (dolist (y more t)
1469 (unless (equal x y)
1470 (return nil)))))
1471 (uses-stack-frame-p (c)
1472 (and (consp c)
1473 (some #'stack-frame-operand (cdr (ignore-instruction-prefixes c)))))
1474 (load-stack-frame-p (c &optional (op :movl))
1475 (stack-frame-operand (twop-src c op)))
1476 (store-stack-frame-p (c &optional (op :movl))
1477 (stack-frame-operand (twop-dst c op)))
1478 (read-stack-frame-p (c)
1479 (or (load-stack-frame-p c :movl)
1480 (load-stack-frame-p c :addl)
1481 (load-stack-frame-p c :subl)
1482 (load-stack-frame-p c :cmpl)
1483 (store-stack-frame-p c :cmpl)
1484 (and (consp c)
1485 (eq :pushl (car c))
1486 (stack-frame-operand (second c)))))
1487 (in-stack-frame-p (c reg)
1488 "Does c ensure that reg is in some particular stack-frame location?"
1489 (or (and (load-stack-frame-p c)
1490 (eq reg (twop-dst c))
1491 (stack-frame-operand (twop-src c)))
1492 (and (store-stack-frame-p c)
1493 (eq reg (twop-src c))
1494 (stack-frame-operand (twop-dst c)))))
1495 (load-funobj-constant-p (c)
1496 (funobj-constant-operand (twop-src c :movl)))
1497 #+ignore
1498 (sub-program-label-p (l)
1499 (and (consp l)
1500 (eq :sub-program (car l))))
1501 (local-load-p (c)
1502 (if (or (load-stack-frame-p c)
1503 (load-funobj-constant-p c))
1504 (twop-src c)
1505 nil))
1506 (label-here-p (label code)
1507 "Is <label> at this point in <code>?"
1508 (loop for i in code
1509 while (or (symbolp i)
1510 (instruction-is i :frame-map))
1511 thereis (eq label i)))
1512 (negate-branch (branch-type)
1513 (ecase branch-type
1514 (:jbe :ja) (:ja :jbe)
1515 (:jz :jnz) (:jnz :jz)
1516 (:je :jne) (:jne :je)
1517 (:jc :jnc) (:jnc :jc)
1518 (:jl :jge) (:jge :jl)
1519 (:jle :jg) (:jg :jle)))
1520 (branch-instruction-label (i &optional jmp (branch-types '(:je :jne :jb :jnb :jbe :jz :jl :jnz
1521 :jle :ja :jae :jg :jge :jnc :jc :js :jns)))
1522 "If i is a branch, return the label."
1523 (when jmp (push :jmp branch-types))
1524 (let ((i (ignore-instruction-prefixes i)))
1525 (or (and (listp i)
1526 (listp (second i))
1527 (member (car (second i)) '(quote muerte.cl::quote))
1528 (member (car i) branch-types)
1529 (second (second i)))
1530 #+ignore
1531 (and (listp i)
1532 branch-types
1533 (symbolp (car i))
1534 (not (member (car i) '(:jmp :jecxz)))
1535 (char= #\J (char (symbol-name (car i)) 0))
1536 (warn "Not a branch: ~A / ~A [~A]" i (symbol-package (caadr i)) branch-types)))))
1537 (find-branches-to-label (start-pc label &optional (context-size 0))
1538 "Context-size is the number of instructions _before_ the branch you want returned."
1539 (dotimes (i context-size)
1540 (push nil start-pc))
1541 (loop for pc on start-pc
1542 as i = (nth context-size pc)
1543 as i-label = (branch-instruction-label i t)
1544 if (or (eq label i-label)
1545 (and (consp i-label)
1546 (eq :label-plus-one (car i-label))))
1547 nconc (list pc)
1548 else if (let ((sub-program i-label))
1549 (and (consp sub-program)
1550 (eq :sub-program (car sub-program))))
1551 nconc (find-branches-to-label (cddr (branch-instruction-label i t))
1552 label context-size)
1553 else if (and (not (atom i))
1554 (tree-search i label))
1555 nconc (list 'unknown-label-usage)))
1556 (optimize-trim-stack-frame (unoptimized-code)
1557 "Any unused local variables on the stack-frame?"
1558 unoptimized-code
1559 ;; BUILD A MAP OF USED STACK-VARS AND REMAP THEM!
1560 #+ignore (if (not (and stack-frame-size
1561 (find 'start-stack-frame-setup unoptimized-code)))
1562 unoptimized-code
1563 (let ((old-code unoptimized-code)
1564 (new-code ()))
1565 ;; copy everything upto start-stack-frame-setup
1566 (loop for i = (pop old-code)
1567 do (push i new-code)
1568 while old-code
1569 until (eq i 'start-stack-frame-setup))
1570 (assert (eq (car new-code) 'start-stack-frame-setup) ()
1571 "no start-stack-frame-setup label, but we already checked!")
1572 (loop for pos downfrom -8 by 4
1573 as i = (pop old-code)
1574 if (and (consp i) (eq :pushl (car i)) (symbolp (cadr i)))
1575 collect (cons pos (cadr i))
1576 and do (unless (find pos old-code :key #'read-stack-frame-p)
1577 (cond
1578 ((find pos old-code :key #'store-stack-frame-p)
1579 (warn "Unused local but stored var: ~S" pos))
1580 ((find pos old-code :key #'uses-stack-frame-p)
1581 (warn "Unused BUT USED local var: ~S" pos))
1582 (t (warn "Unused local var: ~S" pos))))
1583 else do
1584 (push i old-code)
1585 (loop-finish))))
1586 unoptimized-code)
1587 (frame-map-code (unoptimized-code)
1588 "After each label in unoptimized-code, insert a (:frame-map <full-map> <branch-map> <sticky>)
1589 that says which registers are known to hold which stack-frame-locations.
1590 A branch-map is the map that is guaranteed after every branch to the label, i.e. not including
1591 falling below the label."
1592 #+ignore (warn "unmapped:~{~&~A~}" unoptimized-code)
1593 (flet ((rcode-map (code)
1594 #+ignore (when (instruction-is (car code) :testb)
1595 (warn "rcoding ~A" code))
1596 (loop with modifieds = nil
1597 with registers = (list :eax :ebx :ecx :edx)
1598 with local-map = nil
1599 for ii in code
1600 while registers
1601 do (flet ((add-map (stack reg)
1602 (when (and (not (member stack modifieds))
1603 (member reg registers))
1604 (push (cons stack reg)
1605 local-map))))
1606 (cond ((instruction-is ii :frame-map)
1607 (dolist (m (second ii))
1608 (add-map (car m) (cdr m))))
1609 ((load-stack-frame-p ii)
1610 (add-map (load-stack-frame-p ii)
1611 (twop-dst ii)))
1612 ((store-stack-frame-p ii)
1613 (add-map (store-stack-frame-p ii)
1614 (twop-src ii))
1615 (pushnew (store-stack-frame-p ii)
1616 modifieds))
1617 ((non-destructive-p ii))
1618 ((branch-instruction-label ii))
1619 ((simple-instruction-p ii)
1620 (let ((op (idst ii)))
1621 (cond
1622 ((stack-frame-operand op)
1623 (pushnew (stack-frame-operand op) modifieds))
1624 ((symbolp op)
1625 (setf registers (delete op registers))))))
1626 (t #+ignore (when (instruction-is (car code) :testb)
1627 (warn "stopped at ~A" ii))
1628 (loop-finish))))
1629 (setf registers
1630 (delete-if (lambda (r)
1631 (not (preserves-register-p ii r)))
1632 registers))
1633 finally
1634 #+ignore (when (instruction-is (car code) :testb)
1635 (warn "..map ~A" local-map))
1636 (return local-map))))
1637 (loop with next-pc = 'auto-next
1638 ;; initially (warn "opt: ~{ ~A~%~}" unoptimized-code)
1639 for pc = unoptimized-code then (prog1 (if (eq 'auto-next next-pc) auto-next-pc next-pc)
1640 (setq next-pc 'auto-next))
1641 as auto-next-pc = (cdr unoptimized-code) then (cdr pc)
1642 as p = (list (car pc)) ; will be appended.
1643 as i1 = (first pc) ; current instruction, collected by default.
1644 and i2 = (second pc)
1645 while pc
1646 do (when (and (symbolp i1)
1647 (not (and (instruction-is i2 :frame-map)
1648 (fourth i2))))
1649 (let* ((label i1)
1650 (branch-map (reduce (lambda (&optional x y)
1651 (intersection x y :test #'equal))
1652 (mapcar (lambda (lpc)
1653 (if (eq 'unknown-label-usage lpc)
1655 (rcode-map (nreverse (xsubseq lpc 0 9)))))
1656 (find-branches-to-label unoptimized-code label 9))))
1657 (full-map (let ((rcode (nreverse (let* ((pos (loop for x on unoptimized-code
1658 as pos upfrom 0
1659 until (eq x pc)
1660 finally (return pos)))
1661 (back9 (max 0 (- pos 9))))
1662 (subseq unoptimized-code
1663 back9 pos)))))
1664 (if (instruction-uncontinues-p (car rcode))
1665 branch-map
1666 (intersection branch-map (rcode-map rcode) :test #'equal)))))
1667 (when (or full-map branch-map nil)
1668 #+ignore
1669 (explain nil "Inserting at ~A frame-map ~S branch-map ~S."
1670 label full-map branch-map))
1671 (setq p (list label `(:frame-map ,full-map ,branch-map))
1672 next-pc (if (instruction-is i2 :frame-map)
1673 (cddr pc)
1674 (cdr pc)))))
1675 nconc p)))
1676 (optimize-stack-frame-init (unoptimized-code)
1677 "Look at the function's stack-frame initialization code, and see
1678 if we can optimize that, and/or immediately subsequent loads/stores."
1679 (if (not (find 'start-stack-frame-setup unoptimized-code))
1680 unoptimized-code
1681 (let ((old-code unoptimized-code)
1682 (new-code ()))
1683 ;; copy everything upto start-stack-frame-setup
1684 (loop for i = (pop old-code)
1685 do (push i new-code)
1686 while old-code
1687 until (eq i 'start-stack-frame-setup))
1688 (assert (eq (car new-code) 'start-stack-frame-setup) ()
1689 "no start-stack-frame-setup label, but we already checked!")
1690 (let* ((frame-map (loop with pos = -8
1691 as i = (pop old-code)
1692 if (instruction-is i :frame-map)
1693 do (progn :nothing)
1694 else if
1695 (and (consp i) (eq :pushl (car i)) (symbolp (cadr i)))
1696 collect
1697 (cons pos (cadr i))
1698 and do
1699 (decf pos 4)
1700 (push i new-code)
1701 else do
1702 (push i old-code)
1703 (loop-finish)))
1704 (mod-p (loop with mod-p = nil
1705 for i = `(:frame-map ,(copy-list frame-map) nil t)
1706 then (pop old-code)
1707 while i
1708 do (let ((new-i (cond
1709 ((let ((store-pos (store-stack-frame-p i)))
1710 (and store-pos
1711 (eq (cdr (assoc store-pos frame-map))
1712 (twop-src i))))
1713 (explain nil "removed stack-init store: ~S" i)
1714 nil)
1715 ((let ((load-pos (load-stack-frame-p i)))
1716 (and load-pos
1717 (eq (cdr (assoc load-pos frame-map))
1718 (twop-dst i))))
1719 (explain nil "removed stack-init load: ~S" i)
1720 nil)
1721 ((and (load-stack-frame-p i)
1722 (assoc (load-stack-frame-p i) frame-map))
1723 (let ((old-reg (cdr (assoc (load-stack-frame-p i)
1724 frame-map))))
1725 (explain nil "load ~S already in ~S."
1726 i old-reg)
1727 `(:movl ,old-reg ,(twop-dst i))))
1728 ((and (instruction-is i :pushl)
1729 (stack-frame-operand (idst i))
1730 (assoc (stack-frame-operand (idst i))
1731 frame-map))
1732 (let ((old-reg
1733 (cdr (assoc (stack-frame-operand (idst i))
1734 frame-map))))
1735 (explain nil "push ~S already in ~S."
1736 i old-reg)
1737 `(:pushl ,old-reg)))
1738 (t i))))
1739 (unless (eq new-i i)
1740 (setf mod-p t))
1741 (when (branch-instruction-label new-i t)
1742 (setf mod-p t)
1743 (push `(:frame-map ,(copy-list frame-map) nil t)
1744 new-code))
1745 (when new-i
1746 (push new-i new-code)
1747 ;; (warn "new-i: ~S, fm: ~S" new-i frame-map)
1748 (setf frame-map
1749 (delete-if (lambda (map)
1750 ;; (warn "considering: ~S" map)
1751 (not (and (preserves-register-p new-i (cdr map))
1752 (preserves-stack-location-p new-i
1753 (car map)))))
1754 frame-map))
1755 ;; (warn "Frame-map now: ~S" frame-map)
1756 (when (store-stack-frame-p new-i)
1757 (loop for map in frame-map
1758 do (when (= (store-stack-frame-p new-i)
1759 (car map))
1760 (setf (cdr map) (twop-src new-i)))))))
1761 while frame-map
1762 finally (return mod-p))))
1763 (if (not mod-p)
1764 unoptimized-code
1765 (append (nreverse new-code)
1766 old-code))))))
1767 (remove-frame-maps (code)
1768 (remove-if (lambda (x)
1769 (typep x '(cons (eql :frame-map) *)))
1770 code)))
1771 (let* ((unoptimized-code (frame-map-code (optimize-stack-frame-init unoptimized-code)))
1772 (code-modified-p nil)
1773 (stack-frame-used-map (loop with map = nil
1774 for i in unoptimized-code
1775 do (let ((x (read-stack-frame-p i)))
1776 (when x (pushnew x map)))
1777 (when (and (instruction-is i :leal)
1778 (stack-frame-operand (twop-src i)))
1779 (let ((x (stack-frame-operand (twop-src i))))
1780 (when (= (tag :cons) (ldb (byte 2 0) x))
1781 (pushnew (+ x -1) map)
1782 (pushnew (+ x 3) map))))
1783 finally (return map)))
1784 (optimized-code
1785 ;; This loop applies a set of (hard-coded) heuristics on unoptimized-code.
1786 (loop with next-pc = 'auto-next
1787 ;; initially (warn "opt: ~{ ~A~%~}" unoptimized-code)
1788 for pc = unoptimized-code then (prog1 (if (eq 'auto-next next-pc) auto-next-pc next-pc)
1789 (setq next-pc 'auto-next))
1790 as auto-next-pc = (cdr unoptimized-code) then (cdr pc)
1791 as p = (list (car pc)) ; will be appended.
1792 as original-p = p
1793 as i = (first pc) ; current instruction, collected by default.
1794 and i2 = (second pc) and i3 = (third pc) and i4 = (fourth pc) and i5 = (fifth pc)
1795 while pc
1796 do (cond
1797 ((and (instruction-is i :frame-map)
1798 (instruction-is i2 :frame-map)
1799 (not (fourth i))
1800 (not (fourth i2)))
1801 (let ((map (union (second i) (second i2) :test #'equal)))
1802 (explain nil "Merged maps:~%~A + ~A~% => ~A"
1803 (second i) (second i2) map)
1804 (setq p `((:frame-map ,map))
1805 next-pc (cddr pc))))
1806 ((let ((x (store-stack-frame-p i)))
1807 (and x (not (member x stack-frame-used-map))))
1808 (setq p nil)
1809 (explain nil "Removed store of unused local var: ~S" i))
1810 ((and (global-funcall-p i2 '(fast-car))
1811 (global-funcall-p i5 '(fast-cdr))
1812 (true-and-equal (in-stack-frame-p i :eax)
1813 (in-stack-frame-p i4 :eax)))
1814 (let ((call-prefix (if (consp (car i2)) (car i2) nil)))
1815 (cond
1816 ((equal i3 '(:pushl :eax))
1817 (explain nil "merge car,push,cdr to cdr-car,push")
1818 (setf p (list i
1819 `(,call-prefix :call
1820 (:edi ,(global-constant-offset 'fast-cdr-car)))
1821 `(:pushl :ebx))
1822 next-pc (nthcdr 5 pc)))
1823 ((and (store-stack-frame-p i3)
1824 (eq :eax (twop-src i3)))
1825 (explain nil "merge car,store,cdr to cdr-car,store")
1826 (setf p (list i
1827 `(,call-prefix :call
1828 (:edi ,(global-constant-offset 'fast-cdr-car)))
1829 `(:movl :ebx ,(twop-dst i3)))
1830 next-pc (nthcdr 5 pc)))
1831 (t (error "can't deal with cdr-car here: ~{~&~A~}" (subseq pc 0 8))))))
1832 ((flet ((try (place register &optional map reason)
1833 "See if we can remove a stack-frame load below current pc,
1834 given the knowledge that <register> is equal to <place>."
1835 (let ((next-load
1836 (and place
1837 (dolist (si (cdr pc))
1838 (when (and (twop-p si :cmpl)
1839 (equal place (twop-src si)))
1840 (warn "Reverse cmp not yet dealed with.."))
1841 (cond
1842 ((and (twop-p si :cmpl)
1843 (equal place (twop-dst si)))
1844 (return si))
1845 ((equal place (local-load-p si))
1846 (return si))
1847 ((or (not (consp si))
1848 (not (preserves-register-p si register))
1849 (equal place (twop-dst si)))
1850 (return nil)))
1851 (setf map
1852 (remove-if (lambda (m)
1853 (not (preserves-register-p si (cdr m))))
1854 map))))))
1855 (case (instruction-is next-load)
1856 (:movl
1857 (let ((pos (position next-load pc)))
1858 (setq p (nconc (subseq pc 0 pos)
1859 (if (or (eq register (twop-dst next-load))
1860 (find-if (lambda (m)
1861 (and (eq (twop-dst next-load) (cdr m))
1862 (= (car m) (stack-frame-operand place))))
1863 map))
1865 (list `(:movl ,register ,(twop-dst next-load)))))
1866 next-pc (nthcdr (1+ pos) pc))
1867 (explain nil "preserved load/store .. load ~S of place ~S because ~S."
1868 next-load place reason)))
1869 (:cmpl
1870 (let ((pos (position next-load pc)))
1871 (setq p (nconc (subseq pc 0 pos)
1872 (list `(:cmpl ,(twop-src next-load) ,register)))
1873 next-pc (nthcdr (1+ pos) pc))
1874 (explain nil "preserved load/store..cmp: ~S" p next-load))))
1875 (if next-load t nil))))
1876 (or (when (instruction-is i :frame-map)
1877 (loop for (place . register) in (second i)
1878 ;;; do (warn "map try ~S ~S: ~S" place register
1879 ;;; (try place register))
1880 thereis (try `(:ebp ,place) register (second i) :frame-map)))
1881 (try (or (local-load-p i)
1882 (and (store-stack-frame-p i)
1883 (twop-dst i)))
1884 (if (store-stack-frame-p i)
1885 (twop-src i)
1886 (twop-dst i))
1887 nil i))))
1888 ((and (symbolp i)
1889 (instruction-is i2 :frame-map)
1890 (load-stack-frame-p i3)
1891 (eq (twop-dst i3)
1892 (cdr (assoc (load-stack-frame-p i3) (third i2))))
1893 (not (assoc (load-stack-frame-p i3) (second i2))))
1894 (let ((reg (cdr (assoc (load-stack-frame-p i3) (third i2)))))
1895 (explain nil "factor out load from loop: ~S" i3)
1896 (assert (eq reg (twop-dst i3)))
1897 (setq p (if (eq reg (twop-dst i3))
1898 (list i3 i i2)
1899 (append (list i3 i i2)
1900 `((:movl ,reg ,(twop-dst i3)))))
1901 next-pc (cdddr pc))))
1902 ;; ((:movl <foo> <bar>) label (:movl <zot> <bar>))
1903 ;; => (label (:movl <zot> <bar>))
1904 ((and (instruction-is i :movl)
1905 (or (symbolp i2)
1906 (and (not (branch-instruction-label i2))
1907 (symbolp (twop-dst i))
1908 (doesnt-read-register-p i2 (twop-dst i))))
1909 (instruction-is i3 :frame-map)
1910 (instruction-is i4 :movl)
1911 (equal (twop-dst i) (twop-dst i4))
1912 (not (and (symbolp (twop-dst i))
1913 (operand-register-indirect-p (twop-src i4)
1914 (twop-dst i)))))
1915 (setq p (list i2 i3 i4)
1916 next-pc (nthcdr 4 pc))
1917 (explain nil "Removed redundant store before ~A: ~A"
1918 i2 (subseq pc 0 4)))
1919 ((and (instruction-is i :movl)
1920 (not (branch-instruction-label i2))
1921 (symbolp (twop-dst i))
1922 (doesnt-read-register-p i2 (twop-dst i))
1923 (instruction-is i3 :movl)
1924 (equal (twop-dst i) (twop-dst i3))
1925 (not (and (symbolp (twop-dst i))
1926 (operand-register-indirect-p (twop-src i3)
1927 (twop-dst i)))))
1928 (setq p (list i2 i3)
1929 next-pc (nthcdr 3 pc))
1930 (explain nil "Removed redundant store before ~A: ~A"
1931 i2 (subseq pc 0 3)))
1932 #+ignore
1933 ((let ((stack-pos (store-stack-frame-p i)))
1934 (and stack-pos
1935 (loop with search-pc = (cdr pc)
1936 while search-pc
1937 repeat 10
1938 for ii = (pop search-pc)
1939 thereis (eql stack-pos
1940 (store-stack-frame-p ii))
1941 while (or (global-funcall-p ii)
1942 (and (simple-instruction-p ii)
1943 (not (eql stack-pos
1944 (uses-stack-frame-p ii))))))
1945 #+ignore
1946 (eql stack-pos
1947 (store-stack-frame-p i4))
1948 #+ignore
1949 (every (lambda (ii)
1950 (or (global-funcall-p ii)
1951 (and (simple-instruction-p ii)
1952 (not (eql stack-pos
1953 (uses-stack-frame-p ii))))))
1954 (list i2 i3))))
1955 (setf p nil
1956 next-pc (cdr pc))
1957 (explain t "removing redundant store at ~A"
1958 (subseq pc 0 (min 10 (length pc)))))
1959 ((and (member (instruction-is i)
1960 '(:cmpl :cmpb :cmpw :testl :testb :testw))
1961 (member (instruction-is i2)
1962 '(:cmpl :cmpb :cmpw :testl :testb :testw)))
1963 (setq p (list i2)
1964 next-pc (nthcdr 2 pc))
1965 (explain nil "Trimmed double test: ~A" (subseq pc 0 4)))
1966 ;; ((:jmp x) ...(no labels).... x ..)
1967 ;; => (x ...)
1968 ((let ((x (branch-instruction-label i t nil)))
1969 (and (position x (cdr pc))
1970 (not (find-if #'symbolp (cdr pc) :end (position x (cdr pc))))))
1971 (explain nil "jmp x .. x: ~W"
1972 (subseq pc 0 (1+ (position (branch-instruction-label i t nil)
1973 pc))))
1974 (setq p nil
1975 next-pc (member (branch-instruction-label i t nil) pc)))
1976 ;; (:jcc 'x) .... x (:jmp 'y) ..
1977 ;; => (:jcc 'y) .... x (:jmp 'y) ..
1978 ((let* ((from (branch-instruction-label i t))
1979 (dest (member (branch-instruction-label i t)
1980 unoptimized-code))
1981 (to (branch-instruction-label (if (instruction-is (second dest) :frame-map)
1982 (third dest)
1983 (second dest))
1984 t nil)))
1985 (when (and from to (not (eq from to)))
1986 (setq p (list `(,(car i) ',to)))
1987 (explain nil "branch redirect from ~S to ~S" from to)
1988 t)))
1989 ;; remove back-to-back std/cld
1990 ((and (instruction-is i :cld)
1991 (instruction-is i2 :std))
1992 (explain nil "removing back-to-back cld, std.")
1993 (setq p nil next-pc (cddr pc)))
1994 ;; remove branch no-ops.
1995 ((and (branch-instruction-label i t)
1996 (label-here-p (branch-instruction-label i t)
1997 (cdr pc)))
1998 (explain nil "branch no-op: ~A" i)
1999 (setq p nil))
2000 ((and (symbolp i)
2001 (null (symbol-package i))
2002 (null (find-branches-to-label unoptimized-code i))
2003 (not (member i keep-labels)))
2004 (setq p nil
2005 next-pc (if (instruction-is i2 :frame-map)
2006 (cddr pc)
2007 (cdr pc)))
2008 (explain nil "unused label: ~S" i))
2009 ;; ((:jcc 'label) (:jmp 'y) label) => ((:jncc 'y) label)
2010 ((and (branch-instruction-label i)
2011 (branch-instruction-label i2 t nil)
2012 (symbolp i3)
2013 (eq (branch-instruction-label i) i3))
2014 (setq p (list `(,(negate-branch (first i))
2015 ',(branch-instruction-label i2 t nil)))
2016 next-pc (nthcdr 2 pc))
2017 (explain nil "collapsed double negative branch to ~S: ~A." i3 p))
2018 ((and (branch-instruction-label i)
2019 (instruction-is i2 :frame-map)
2020 (branch-instruction-label i3 t nil)
2021 (symbolp i4)
2022 (eq (branch-instruction-label i) i4))
2023 (setq p (list `(,(negate-branch (first i))
2024 ',(branch-instruction-label i3 t nil)))
2025 next-pc (nthcdr 3 pc))
2026 (explain nil "collapsed double negative branch to ~S: ~A." i4 p))
2027 ((and (twop-p i :movl)
2028 (register-operand (twop-src i))
2029 (register-operand (twop-dst i))
2030 (twop-p i2 :movl)
2031 (eq (twop-dst i) (twop-dst i2))
2032 (register-indirect-operand (twop-src i2) (twop-dst i)))
2033 (setq p (list `(:movl (,(twop-src i)
2034 ,(register-indirect-operand (twop-src i2)
2035 (twop-dst i)))
2036 ,(twop-dst i2)))
2037 next-pc (nthcdr 2 pc))
2038 (explain nil "(movl edx eax) (movl (eax <z>) eax) => (movl (edx <z>) eax: ~S"
2040 ((and (twop-p i :movl)
2041 (instruction-is i2 :pushl)
2042 (eq (twop-dst i) (second i2))
2043 (twop-p i3 :movl)
2044 (eq (twop-dst i) (twop-dst i3)))
2045 (setq p (list `(:pushl ,(twop-src i)))
2046 next-pc (nthcdr 2 pc))
2047 (explain nil "(movl <z> :eax) (pushl :eax) => (pushl <z>): ~S" p))
2048 ((and (instruction-uncontinues-p i)
2049 (not (or (symbolp i2)
2050 #+ignore (member (instruction-is i2) '(:foobar)))))
2051 (do ((x (cdr pc) (cdr x)))
2052 (nil)
2053 (cond
2054 ((not (or (symbolp (car x))
2055 #+ignore (member (instruction-is (car x)) '(:foobar))))
2056 (explain nil "Removing unreachable code ~A after ~A." (car x) i))
2057 (t (setf p (list i)
2058 next-pc x)
2059 (return)))))
2060 ((and (store-stack-frame-p i)
2061 (load-stack-frame-p i2)
2062 (load-stack-frame-p i3)
2063 (= (store-stack-frame-p i)
2064 (load-stack-frame-p i3))
2065 (not (eq (twop-dst i2) (twop-dst i3))))
2066 (setq p (list i `(:movl ,(twop-src i) ,(twop-dst i3)) i2)
2067 next-pc (nthcdr 3 pc))
2068 (explain nil "store, z, load => store, move, z: ~A" p))
2069 ((and (instruction-is i :movl)
2070 (member (twop-dst i) '(:eax :ebx :ecx :edx))
2071 (instruction-is i2 :pushl)
2072 (not (member (second i2) '(:eax :ebx :ecx :edx)))
2073 (equal (twop-src i) (second i2)))
2074 (setq p (list i `(:pushl ,(twop-dst i)))
2075 next-pc (nthcdr 2 pc))
2076 (explain t "load, push => load, push reg."))
2077 ((and (instruction-is i :movl)
2078 (member (twop-src i) '(:eax :ebx :ecx :edx))
2079 (instruction-is i2 :pushl)
2080 (not (member (second i2) '(:eax :ebx :ecx :edx)))
2081 (equal (twop-dst i) (second i2)))
2082 (setq p (list i `(:pushl ,(twop-src i)))
2083 next-pc (nthcdr 2 pc))
2084 (explain nil "store, push => store, push reg: ~S ~S" i i2))
2085 ;;; ((and (instruction-is i :cmpl)
2086 ;;; (true-and-equal (stack-frame-operand (twop-dst i))
2087 ;;; (load-stack-frame-p i3))
2088 ;;; (branch-instruction-label i2))
2089 ;;; (setf p (list i3
2090 ;;; `(:cmpl ,(twop-src i) ,(twop-dst i3))
2091 ;;; i2)
2092 ;;; next-pc (nthcdr 3 pc))
2093 ;;; (explain t "~S ~S ~S => ~S" i i2 i3 p))
2094 ((and (instruction-is i :pushl)
2095 (instruction-is i3 :popl)
2096 (store-stack-frame-p i2)
2097 (store-stack-frame-p i4)
2098 (eq (idst i3) (twop-src i4)))
2099 (setf p (list i2
2100 `(:movl ,(idst i) ,(twop-dst i4))
2101 `(:movl ,(idst i) ,(idst i3)))
2102 next-pc (nthcdr 4 pc))
2103 (explain nil "~S => ~S" (subseq pc 0 4) p))
2104 #+ignore
2105 ((let ((i6 (nth 6 pc)))
2106 (and (global-funcall-p i2 '(fast-car))
2107 (global-funcall-p i6 '(fast-cdr))
2108 (load-stack-frame-p i)
2109 (eq :eax (twop-dst i))
2110 (equal i i4))))
2111 ((and (equal i '(:movl :ebx :eax))
2112 (global-funcall-p i2 '(fast-car fast-cdr)))
2113 (let ((newf (ecase (global-funcall-p i2 '(fast-car fast-cdr))
2114 (fast-car 'fast-car-ebx)
2115 (fast-cdr 'fast-cdr-ebx))))
2116 (setq p `((:call (:edi ,(global-constant-offset newf))))
2117 next-pc (nthcdr 2 pc))
2118 (explain nil "Changed [~S ~S] to ~S" i i2 newf)))
2119 ((and (equal i '(:movl :eax :ebx))
2120 (global-funcall-p i2 '(fast-car-ebx fast-cdr-ebx)))
2121 (let ((newf (ecase (global-funcall-p i2 '(fast-car-ebx fast-cdr-ebx))
2122 (fast-car-ebx 'fast-car)
2123 (fast-cdr-ebx 'fast-cdr))))
2124 (setq p `((:call (:edi ,(global-constant-offset newf))))
2125 next-pc (nthcdr 2 pc))
2126 (explain nil "Changed [~S ~S] to ~S" i i2 newf)))
2127 #+ignore
2128 ((and (global-funcall-p i '(fast-cdr))
2129 (global-funcall-p i2 '(fast-cdr))
2130 (global-funcall-p i3 '(fast-cdr)))
2131 (setq p `((:call (:edi ,(global-constant-offset 'fast-cdddr))))
2132 next-pc (nthcdr 3 pc))
2133 (explain nil "Changed (cdr (cdr (cdr :eax))) to (cdddr :eax)."))
2134 ((and (global-funcall-p i '(fast-cdr))
2135 (global-funcall-p i2 '(fast-cdr)))
2136 (setq p `((:call (:edi ,(global-constant-offset 'fast-cddr))))
2137 next-pc (nthcdr 2 pc))
2138 (explain nil "Changed (cdr (cdr :eax)) to (cddr :eax)."))
2139 ((and (load-stack-frame-p i) (eq :eax (twop-dst i))
2140 (global-funcall-p i2 '(fast-car fast-cdr))
2141 (preserves-stack-location-p i3 (load-stack-frame-p i))
2142 (preserves-register-p i3 :ebx)
2143 (eql (load-stack-frame-p i)
2144 (load-stack-frame-p i4)))
2145 (let ((newf (ecase (global-funcall-p i2 '(fast-car fast-cdr))
2146 (fast-car 'fast-car-ebx)
2147 (fast-cdr 'fast-cdr-ebx))))
2148 (setq p `((:movl ,(twop-src i) :ebx)
2149 (:call (:edi ,(global-constant-offset newf)))
2151 ,@(unless (eq :ebx (twop-dst i4))
2152 `((:movl :ebx ,(twop-dst i4)))))
2153 next-pc (nthcdr 4 pc))
2154 (explain nil "load around ~A: ~{~&~A~}~%=>~% ~{~&~A~}"
2155 newf (subseq pc 0 5) p))))
2156 do (unless (eq p original-p) ; auto-detect whether any heuristic fired..
2157 #+ignore (warn "at ~A, ~A inserted ~A" i i2 p)
2158 #+ignore (warn "modified at ~S ~S ~S" i i2 i3)
2159 (setf code-modified-p t))
2160 nconc p)))
2161 (if code-modified-p
2162 (apply #'optimize-code-internal optimized-code (1+ recursive-count) key-args)
2163 (optimize-trim-stack-frame (remove-frame-maps unoptimized-code)))))))
2164 ;;;; Compiler internals
2166 (defclass binding ()
2167 ((name
2168 :initarg :name
2169 :accessor binding-name)
2170 (env
2171 :accessor binding-env)
2172 (declarations
2173 :initarg :declarations
2174 :accessor binding-declarations)
2175 (extent-env
2176 :accessor binding-extent-env
2177 :initform nil)))
2179 (defmethod (setf binding-env) :after (env (binding binding))
2180 (unless (binding-extent-env binding)
2181 (setf (binding-extent-env binding) env)))
2183 (defmethod print-object ((object binding) stream)
2184 (print-unreadable-object (object stream :type t :identity t)
2185 (when (slot-boundp object 'name)
2186 (format stream "name: ~S~@[->~S~]~@[ %~A~]"
2187 (and (slot-boundp object 'name)
2188 (binding-name object))
2189 (when (and (binding-target object)
2190 (not (eq object (binding-target object))))
2191 (binding-name (forwarding-binding-target object)))
2192 (when (and (slot-exists-p object 'store-type)
2193 (slot-boundp object 'store-type)
2194 (binding-store-type object))
2195 (or (apply #'encoded-type-decode
2196 (binding-store-type object))
2197 'empty))))))
2199 (defclass constant-object-binding (binding)
2200 ((object
2201 :initarg :object
2202 :reader constant-object)))
2204 (defmethod binding-lended-p ((binding constant-object-binding)) nil)
2205 (defmethod binding-store-type ((binding constant-object-binding))
2206 (multiple-value-list (type-specifier-encode `(eql ,(constant-object binding)))))
2209 (defclass operator-binding (binding) ())
2211 (defclass macro-binding (operator-binding)
2212 ((expander
2213 :initarg :expander
2214 :accessor macro-binding-expander)))
2216 (defclass symbol-macro-binding (binding)
2217 ((expander
2218 :initarg :expander
2219 :accessor macro-binding-expander)))
2221 (defclass variable-binding (binding)
2222 ((lending ; a property-list
2223 :initform nil
2224 :accessor binding-lending)
2225 (store-type ; union of all types ever stored here
2226 :initform nil
2227 ;; :initarg :store-type
2228 :accessor binding-store-type)))
2230 (defmethod binding-lended-p ((binding variable-binding))
2231 (and (getf (binding-lending binding) :lended-to)
2232 (not (eq :unused (getf (binding-lending binding) :lended-to)))))
2234 (defclass lexical-binding (variable-binding) ())
2235 (defclass located-binding (lexical-binding) ())
2237 (defclass function-binding (operator-binding located-binding)
2238 ((funobj
2239 :initarg :funobj
2240 :accessor function-binding-funobj)
2241 (parent-funobj
2242 :initarg :parent-funobj
2243 :reader function-binding-parent)))
2245 (defclass funobj-binding (function-binding) ())
2246 (defclass closure-binding (function-binding located-binding) ())
2247 (defclass lambda-binding (function-binding) ())
2249 (defclass temporary-name (located-binding)
2252 (defclass borrowed-binding (located-binding)
2253 ((reference-slot
2254 :initarg :reference-slot
2255 :accessor borrowed-binding-reference-slot)
2256 (target-binding
2257 :initarg :target-binding
2258 :reader borrowed-binding-target)
2259 (usage
2260 :initarg :usage
2261 :initform nil
2262 :accessor borrowed-binding-usage)))
2264 (defclass lexical-borrowed-binding (borrowed-binding)
2265 ((stack-frame-distance
2266 :initarg :stack-frame-distance
2267 :reader stack-frame-distance))
2268 (:documentation "A closure with lexical extent borrows bindings using this class."))
2270 (defclass indefinite-borrowed-binding (borrowed-binding)
2271 ((reference-slot
2272 :initarg :reference-slot
2273 :reader borrowed-binding-reference-slot)))
2275 #+ignore
2276 (defclass constant-reference-binding (lexical-binding)
2277 ((object
2278 :initarg :object
2279 :reader constant-reference-object)))
2281 #+ignore
2282 (defmethod print-object ((object constant-reference-binding) stream)
2283 (print-unreadable-object (object stream :type t :identity t)
2284 (format stream "object: ~S" (constant-reference-object object)))
2285 object)
2287 (defclass forwarding-binding (lexical-binding)
2288 ((target-binding
2289 :initarg :target-binding
2290 :accessor forwarding-binding-target)))
2292 (defmethod binding-funobj ((binding binding))
2293 (movitz-environment-funobj (binding-env binding)))
2295 (defmethod binding-funobj ((binding forwarding-binding))
2296 (movitz-environment-funobj (binding-env (forwarding-binding-target binding))))
2298 (defclass function-argument (located-binding) ())
2299 (defclass edx-function-argument (function-argument) ())
2301 (defclass positional-function-argument (function-argument)
2302 ((argnum
2303 :initarg :argnum
2304 :reader function-argument-argnum)))
2306 (defclass required-function-argument (positional-function-argument) ())
2308 (defclass register-required-function-argument (required-function-argument) ())
2309 (defclass fixed-required-function-argument (required-function-argument)
2310 ((numargs
2311 :initarg :numargs
2312 :reader binding-numargs)))
2313 (defclass floating-required-function-argument (required-function-argument) ())
2315 (defclass non-required-function-argument (function-argument)
2316 ((init-form
2317 :initarg init-form
2318 :reader optional-function-argument-init-form)
2319 (supplied-p-var
2320 :initarg supplied-p-var
2321 :reader optional-function-argument-supplied-p-var)))
2323 (defclass optional-function-argument (non-required-function-argument positional-function-argument) ())
2325 (defclass supplied-p-function-argument (function-argument) ())
2327 (defclass rest-function-argument (positional-function-argument) ())
2329 (defclass keyword-function-argument (non-required-function-argument)
2330 ((keyword-name
2331 :initarg :keyword-name
2332 :reader keyword-function-argument-keyword-name)))
2334 (defclass dynamic-binding (variable-binding) ())
2336 (defclass shadowing-binding (binding) ())
2338 (defclass shadowing-dynamic-binding (dynamic-binding shadowing-binding)
2339 ((shadowed-variable
2340 :initarg :shadowed-variable
2341 :reader shadowed-variable)
2342 (shadowing-variable
2343 :initarg :shadowing-variable
2344 :reader shadowing-variable)))
2346 (defmethod binding-store-type ((binding dynamic-binding))
2347 (multiple-value-list (type-specifier-encode t)))
2349 (defun stack-frame-offset (stack-frame-position)
2350 (* -4 (1+ stack-frame-position)))
2352 (defun argument-stack-offset (binding)
2353 (check-type binding fixed-required-function-argument)
2354 (argument-stack-offset-shortcut (binding-numargs binding)
2355 (function-argument-argnum binding)))
2357 (defun argument-stack-offset-shortcut (numargs argnum)
2358 "For a function of <numargs> arguments, locate the ebp-relative position
2359 of argument <argnum>."
2360 (* 4 (- numargs -1 argnum)))
2364 ;;; New style of locating bindings. The point is to not side-effect the binding objects.
2366 (defun new-binding-location (binding map &key (default nil default-p))
2367 (check-type binding (or binding (cons keyword binding)))
2368 (let ((x (assoc binding map)))
2369 (cond
2370 (x (cdr x))
2371 (default-p default)
2372 (t (error "No location for ~S." binding)))))
2374 (defun make-binding-map () nil)
2376 (defun new-binding-located-p (binding map)
2377 (check-type binding (or null binding (cons keyword binding)))
2378 (and (assoc binding map) t))
2380 (defun frame-map-size (map)
2381 (reduce #'max map
2382 :initial-value 0
2383 :key (lambda (x)
2384 (if (integerp (cdr x))
2385 (cdr x)
2386 0))))
2388 (defun frame-map-next-free-location (frame-map env &optional (size 1))
2389 (labels ((stack-location (binding)
2390 (if (typep binding 'forwarding-binding)
2391 (stack-location (forwarding-binding-target binding))
2392 (new-binding-location binding frame-map :default nil)))
2393 (env-extant (env1 env2)
2394 "Is env1 active whenever env2 is active?"
2395 (cond
2396 ((null env2)
2397 nil)
2398 ((eq env1 env2)
2399 ;; (warn "~S shadowed by ~S" env env2)
2401 (t (env-extant env1 (movitz-environment-extent-uplink env2))))))
2402 (let ((frame-size (frame-map-size frame-map)))
2403 (or (loop for location from 1 to frame-size
2404 when
2405 (loop for sub-location from location below (+ location size)
2406 never
2407 (find-if (lambda (b-loc)
2408 (destructuring-bind (binding . binding-location)
2409 b-loc
2410 (or (and (eq binding nil) ; nil means "back off!"
2411 (eql sub-location binding-location))
2412 (and (not (bindingp binding))
2413 (eql sub-location binding-location))
2414 (and (bindingp binding)
2415 (eql sub-location (stack-location binding))
2416 (labels
2417 ((z (b)
2418 (when b
2419 (or (env-extant (binding-env b) env)
2420 (env-extant env (binding-env b))
2421 (when (typep b 'forwarding-binding)
2422 (z (forwarding-binding-target b)))))))
2423 (z binding))))))
2424 frame-map))
2425 return location)
2426 (1+ frame-size))))) ; no free location found, so grow frame-size.
2428 (define-setf-expander new-binding-location (binding map-place &environment env)
2429 (multiple-value-bind (temps values stores setter getter)
2430 (get-setf-expansion map-place env)
2431 (let ((new-value (gensym))
2432 (binding-var (gensym)))
2433 (values (append temps (list binding-var))
2434 (append values (list binding))
2435 (list new-value)
2436 `(let ((,(car stores) (progn
2437 (assert (or (null binding)
2438 (not (new-binding-located-p ,binding-var ,getter))))
2439 (check-type ,new-value (or keyword
2440 binding
2441 (integer 0 *)
2442 (cons (eql :argument-stack) *)))
2443 (acons ,binding-var ,new-value ,getter))))
2444 ,setter
2445 ,new-value)
2446 `(new-binding-location ,binding-var ,getter)))))
2448 ;;; Objects with dynamic extent may be located on the stack-frame, which at
2449 ;;; compile-time is represented with this structure.
2451 ;;;(defclass stack-allocated-object ()
2452 ;;; ((size
2453 ;;; ;; Size in words (4 octets) this object occupies in the stack-frame.
2454 ;;; :initarg :size
2455 ;;; :accessor size)
2456 ;;; (location
2457 ;;; ;; Stack-frame offset (in words) this object is allocated to.
2458 ;;; :accessor location)))
2464 (defun ignore-instruction-prefixes (instruction)
2465 (if (and (consp instruction)
2466 (listp (car instruction)))
2467 (cdr instruction)
2468 instruction))
2470 (defun instruction-sub-program (instruction)
2471 "When an instruction contains a sub-program, return that program, and
2472 the sub-program options (&optional label) as secondary value."
2473 (let ((instruction (ignore-instruction-prefixes instruction)))
2474 (and (consp instruction)
2475 (consp (second instruction))
2476 (symbolp (car (second instruction)))
2477 (string= 'quote (car (second instruction)))
2478 (let ((x (second (second instruction))))
2479 (and (consp x)
2480 (eq :sub-program (car x))
2481 (values (cddr x)
2482 (second x)))))))
2484 (defun instruction-is (instruction &optional operator)
2485 (and (listp instruction)
2486 (if (member (car instruction) '(:globally :locally))
2487 (instruction-is (second instruction) operator)
2488 (let ((instruction (ignore-instruction-prefixes instruction)))
2489 (if operator
2490 (eq operator (car instruction))
2491 (car instruction))))))
2493 (defun instruction-uncontinues-p (instruction)
2494 "Is it impossible for control to return after instruction?"
2495 (or (member (instruction-is instruction)
2496 '(:jmp :ret))
2497 (member instruction
2498 '((:int 100))
2499 :test #'equalp)))
2501 #+ignore (defun sub-environment-p (env1 env2)
2502 (cond
2503 ((eq env1 env2) t)
2504 ((null env1) nil)
2505 (t (sub-environment-p (movitz-environment-uplink env1) env2))))
2507 (defun find-code-constants-and-jumpers (code &key include-programs)
2508 "Return code's constants (a plist of constants and their usage-counts) and jumper-sets."
2509 (let (jumper-sets constants key-args-set)
2510 (labels ((process-binding (binding)
2511 "Some bindings are really references to constants."
2512 (typecase binding
2513 (constant-object-binding
2514 (let ((object (movitz-read (constant-object binding))))
2515 (when (typep object 'movitz-heap-object)
2516 (incf (getf constants object 0)))))
2517 (forwarding-binding
2518 (process-binding (forwarding-binding-target binding)))
2519 (funobj-binding
2520 (let ((funobj (function-binding-funobj binding)))
2521 (incf (getf constants funobj 0))))
2522 (closure-binding)
2523 (function-binding
2524 (error "No function-binding now..: ~S" binding))))
2525 (process (sub-code)
2526 "This local function side-effects the variables jumper-sets and constants."
2527 (loop for instruction in sub-code
2528 do (case (instruction-is instruction)
2529 ((:local-function-init :load-lambda)
2530 (let* ((binding (second instruction))
2531 (funobj (function-binding-funobj binding)))
2532 (unless (eq :unused (movitz-funobj-extent funobj))
2533 (incf (getf constants funobj 0))
2534 (dolist (binding (borrowed-bindings funobj))
2535 (process-binding binding)))))
2536 ((:load-lexical :lend-lexical :call-lexical)
2537 (process-binding (second instruction)))
2538 (:load-constant
2539 (let ((object (movitz-read (second instruction))))
2540 (when (typep object 'movitz-heap-object)
2541 (incf (getf constants object 0)))))
2542 (:declare-label-set
2543 (destructuring-bind (name set)
2544 (cdr instruction)
2545 (assert (not (getf jumper-sets name)) ()
2546 "Duplicate jumper declaration for ~S." name)
2547 (setf (getf jumper-sets name) set)))
2548 (:declare-key-arg-set
2549 (setf key-args-set (cdr instruction)))
2550 (t (when (listp instruction)
2551 (dolist (binding (find-read-bindings instruction))
2552 (process-binding binding)))))
2553 do (let ((sub (instruction-sub-program instruction)))
2554 (when sub (process sub))))))
2555 (process code)
2556 (map nil #'process include-programs))
2557 (loop for key-arg in key-args-set
2558 do (remf constants key-arg))
2559 (values constants jumper-sets key-args-set)))
2561 (defun layout-funobj-vector (constants key-args-constants jumper-sets borrowing-bindings)
2562 (let* ((jumpers (loop with x
2563 for set in (cdr jumper-sets) by #'cddr
2564 unless (search set x)
2565 do (setf x (nconc x (copy-list set)))
2566 finally (return x)))
2567 (num-jumpers (length jumpers))
2568 (stuff (append (mapcar (lambda (c)
2569 (cons c 1))
2570 key-args-constants)
2571 (when key-args-constants
2572 (list (cons (movitz-read 0)
2573 1)))
2574 (sort (loop for (constant count) on constants by #'cddr
2575 unless (or (eq constant *movitz-nil*)
2576 (eq constant (image-t-symbol *image*)))
2577 collect (cons constant count))
2578 #'< :key #'cdr))))
2579 (values (append jumpers
2580 (mapcar (lambda (x)
2581 (movitz-read (car x)))
2582 stuff)
2583 (make-list (length borrowing-bindings)
2584 :initial-element *movitz-nil*))
2585 num-jumpers
2586 (loop for (name set) on jumper-sets by #'cddr
2587 collect (cons name set))
2588 (loop for borrowing-binding in borrowing-bindings
2589 as pos upfrom (+ num-jumpers (length stuff))
2590 collect (cons borrowing-binding pos)))))
2592 (defun movitz-funobj-intern-constant (funobj obj)
2593 ;; (error "XXXXX")
2594 (let ((cobj (movitz-read obj)))
2595 (+ (slot-offset 'movitz-funobj 'constant0)
2596 (* (sizeof 'word)
2597 (let* ((pos (position cobj (movitz-funobj-const-list funobj)
2598 :start (movitz-funobj-num-jumpers funobj))))
2599 (assert pos ()
2600 "Couldn't find constant ~S in ~S's set of constants ~S."
2601 obj funobj (movitz-funobj-const-list funobj))
2602 pos)))))
2604 (defun compute-free-registers (pc distance funobj frame-map
2605 &key (free-registers '(:ecx :eax :ebx :edx)))
2606 "Return set of free register, and whether there may be more registers
2607 free later, with a more specified frame-map."
2608 (loop with free-so-far = free-registers
2609 repeat distance for i in pc
2610 while (not (null free-so-far))
2611 doing
2612 (cond
2613 ((and (instruction-is i :init-lexvar)
2614 (typep (second i) 'required-function-argument)) ; XXX
2615 (destructuring-bind (binding &key init-with-register init-with-type
2616 protect-registers protect-carry)
2617 (cdr i)
2618 (declare (ignore protect-carry init-with-type))
2619 (when init-with-register
2620 (setf free-so-far (remove-if (lambda (x)
2621 (if (new-binding-located-p binding frame-map)
2622 (eq x (new-binding-location binding frame-map))
2623 (or (eq x init-with-register)
2624 (member x protect-registers))))
2625 free-so-far)))))
2626 (t (case (instruction-is i)
2627 ((nil)
2628 (return nil)) ; a label, most likely
2629 ((:declare-key-arg-set :declare-label-set)
2630 nil)
2631 ((:lexical-control-transfer :load-lambda)
2632 (return nil)) ; not sure about these.
2633 ((:call)
2634 (setf free-so-far
2635 (remove-if (lambda (r)
2636 (not (eq r :push)))
2637 free-so-far)))
2638 ((:arg-cmp)
2639 (setf free-so-far
2640 (remove :ecx free-so-far)))
2641 ((:cld :std)
2642 (setf free-so-far
2643 (set-difference free-so-far '(:eax :edx))))
2644 ((:into :clc :stc :int))
2645 ((:jmp :jnz :je :jne :jz :jge :jae :jnc :jbe)
2646 (setf free-so-far
2647 (remove :push free-so-far)))
2648 ((:pushl :popl)
2649 (setf free-so-far
2650 (remove-if (lambda (r)
2651 (or (eq r :push)
2652 (tree-search i r)))
2653 free-so-far)))
2654 ((:outb :inb)
2655 (setf free-so-far
2656 (set-difference free-so-far '(:eax :edx))))
2657 ((:movb :testb :andb :cmpb)
2658 (setf free-so-far
2659 (remove-if (lambda (r)
2660 (and (not (eq r :push))
2661 (or (tree-search i r)
2662 (tree-search i (register32-to-low8 r)))))
2663 free-so-far)))
2664 ((:sarl :shrl :shll :xorl :cmpl :leal :btl :sbbl :cdq
2665 :movl :movzxw :movzxb :testl :andl :addl :subl :imull :idivl)
2666 (setf free-so-far
2667 (remove-if (lambda (r)
2668 (tree-search i r))
2669 free-so-far)))
2670 ((:load-constant :load-lexical :store-lexical :cons-get :endp :incf-lexvar :init-lexvar)
2671 (assert (gethash (instruction-is i) *extended-code-expanders*))
2672 (cond
2673 ((and (instruction-is i :init-lexvar) ; special case..
2674 (typep (second i) 'forwarding-binding)))
2675 (t (unless (can-expand-extended-p i frame-map)
2676 ;; (warn "can't expand ~A from ~A" i frame-map)
2677 (return (values nil t)))
2678 (let ((exp (expand-extended-code i funobj frame-map)))
2679 (when (tree-search exp '(:call :local-function-init))
2680 (setf free-so-far
2681 (remove-if (lambda (r)
2682 (not (eq r :push)))
2683 free-so-far)))
2684 (setf free-so-far
2685 (remove-if (lambda (r)
2686 (and (not (eq r :push))
2687 (or (tree-search exp r)
2688 (tree-search exp (register32-to-low8 r)))))
2689 free-so-far))))))
2690 ((:local-function-init)
2691 (destructuring-bind (binding)
2692 (cdr i)
2693 (unless (typep binding 'funobj-binding)
2694 (return nil))))
2695 (t #+ignore (warn "Dist ~D stopped by ~A"
2696 distance i)
2697 (return nil)))))
2698 ;; do (warn "after ~A: ~A" i free-so-far)
2699 finally (return free-so-far)))
2701 (defun try-locate-in-register (binding var-counts funobj frame-map)
2702 "Try to locate binding in a register. Return a register, or
2703 nil and :not-now, or :never.
2704 This function is factored out from assign-bindings."
2705 (assert (not (typep binding 'forwarding-binding)))
2706 (let* ((count-init-pc (gethash binding var-counts))
2707 (count (car count-init-pc))
2708 (init-pc (second count-init-pc)))
2709 #+ignore (warn "b ~S: count: ~D, init-pc: ~{~&~A~}" binding count init-pc)
2710 (cond
2711 ((and (not *compiler-allow-transients*)
2712 (typep binding 'function-argument))
2713 (values nil :never))
2714 ((binding-lended-p binding)
2715 ;; We can't lend a register.
2716 (values nil :never))
2717 ((and (= 1 count)
2718 init-pc)
2719 (assert (instruction-is (first init-pc) :init-lexvar))
2720 (destructuring-bind (init-binding &key init-with-register init-with-type
2721 protect-registers protect-carry)
2722 (cdr (first init-pc))
2723 (declare (ignore protect-registers protect-carry init-with-type))
2724 (assert (eq binding init-binding))
2725 (multiple-value-bind (load-instruction binding-destination distance)
2726 (loop for i in (cdr init-pc) as distance upfrom 0
2727 do (when (not (instruction-is i :init-lexvar))
2728 (multiple-value-bind (read-bindings read-destinations)
2729 (find-read-bindings i)
2730 (let ((pos (position binding read-bindings :test #'binding-eql)))
2731 (when pos
2732 (return (values i (nth pos read-destinations) distance)))))))
2733 (declare (ignore load-instruction))
2734 (multiple-value-bind (free-registers more-later-p)
2735 (and distance (compute-free-registers (cdr init-pc) distance funobj frame-map))
2736 #+ignore
2737 (when (string= 'num-jumpers (binding-name binding))
2738 (warn "load: ~S, dist: ~S, dest: ~S" load-instruction distance binding-destination)
2739 (warn "free: ~S, more: ~S" free-registers more-later-p))
2740 (let ((free-registers-no-ecx (remove :ecx free-registers)))
2741 (cond
2742 ((member binding-destination free-registers-no-ecx)
2743 binding-destination)
2744 ((and (not (typep binding '(or fixed-required-function-argument
2745 register-required-function-argument)))
2746 (member binding-destination free-registers))
2747 binding-destination)
2748 ((member init-with-register free-registers)
2749 init-with-register)
2750 ((and (member :ecx free-registers)
2751 (not (typep binding 'function-argument))
2752 (or (eq :untagged-fixnum-ecx binding-destination)
2753 (eq :untagged-fixnum-ecx init-with-register)))
2754 :untagged-fixnum-ecx)
2755 ((and (binding-store-type binding)
2756 (member :ecx free-registers)
2757 (not (typep binding '(or fixed-required-function-argument
2758 register-required-function-argument)))
2759 (multiple-value-call #'encoded-subtypep
2760 (values-list (binding-store-type binding))
2761 (type-specifier-encode '(or integer character))))
2762 :ecx)
2763 ((not (null free-registers-no-ecx))
2764 (first free-registers-no-ecx))
2765 (more-later-p
2766 (values nil :not-now))
2767 ((and distance (typep binding 'temporary-name))
2768 ;; We might push/pop this variable
2769 (multiple-value-bind (push-available-p maybe-later)
2770 (compute-free-registers (cdr init-pc) distance funobj frame-map
2771 :free-registers '(:push))
2772 ;; (warn "pushing.. ~S ~A ~A" binding push-available-p maybe-later)
2773 (cond
2774 (push-available-p
2775 (values :push))
2776 (maybe-later
2777 (values nil :not-now))
2778 (t (values nil :never)))))
2779 (t (values nil :never))))))))
2780 (t (values nil :never)))))
2782 (defun discover-variables (code function-env)
2783 "Iterate over CODE, and take note in the hash-table VAR-COUNTER which ~
2784 variables CODE references that are lexically bound in ENV."
2785 (check-type function-env function-env)
2786 ;; (print-code 'discover code)
2787 (let ((var-counter (make-hash-table :test #'eq :size 40)))
2788 (labels ((record-binding-used (binding)
2789 (let ((count-init-pc (or (gethash binding var-counter)
2790 (setf (gethash binding var-counter)
2791 (list 0 nil t)))))
2792 (setf (third count-init-pc) t)
2793 (when (typep binding 'forwarding-binding)
2794 (record-binding-used (forwarding-binding-target binding)))))
2795 (take-note-of-binding (binding &optional storep init-pc)
2796 (let ((count-init-pc (or (gethash binding var-counter)
2797 (setf (gethash binding var-counter)
2798 (list 0 nil (not storep))))))
2799 (when init-pc
2800 (assert (not (second count-init-pc)))
2801 (setf (second count-init-pc) init-pc))
2802 (unless storep
2803 (unless (eq binding (binding-target binding))
2804 ;; (break "ewfew: ~S" (gethash (binding-target binding) var-counter))
2805 (take-note-of-binding (binding-target binding)))
2806 (setf (third count-init-pc) t)
2807 (incf (car count-init-pc))))
2808 #+ignore
2809 (when (typep binding 'forwarding-binding)
2810 (take-note-of-binding (forwarding-binding-target binding) storep)))
2811 (take-note-of-init (binding init-pc)
2812 (let ((count-init-pc (or (gethash binding var-counter)
2813 (setf (gethash binding var-counter)
2814 (list 0 nil nil)))))
2815 (assert (not (second count-init-pc)))
2816 (setf (second count-init-pc) init-pc)))
2817 (do-discover-variables (code env)
2818 (loop for pc on code as instruction in code
2819 when (listp instruction)
2820 do (flet ((lend-lexical (borrowing-binding dynamic-extent-p)
2821 (let ((lended-binding
2822 (borrowed-binding-target borrowing-binding)))
2823 (assert (not (typep lended-binding 'forwarding-binding)) ()
2824 "Can't lend a forwarding-binding.")
2825 (pushnew lended-binding
2826 (potentially-lended-bindings function-env))
2827 (take-note-of-binding lended-binding)
2828 (symbol-macrolet ((p (binding-lending lended-binding)))
2829 (incf (getf p :lended-count 0))
2830 (setf (getf p :dynamic-extent-p) (and (getf p :dynamic-extent-p t)
2831 dynamic-extent-p))))))
2832 (case (instruction-is instruction)
2833 ((:local-function-init :load-lambda)
2834 (let ((function-binding (second instruction)))
2835 (take-note-of-binding function-binding)
2836 (let ((sub-funobj (function-binding-funobj function-binding)))
2837 #+ignore
2838 (warn "fun-ext: ~S ~S ~S"
2839 sub-funobj
2840 (movitz-funobj-extent sub-funobj)
2841 (movitz-allocation sub-funobj))
2842 (when (typep (movitz-allocation sub-funobj)
2843 'with-dynamic-extent-scope-env)
2844 (take-note-of-binding (base-binding (movitz-allocation sub-funobj)))))
2845 (let ((closure-funobj (function-binding-funobj function-binding)))
2846 (dolist (borrowing-binding (borrowed-bindings closure-funobj))
2847 (lend-lexical borrowing-binding nil)))))
2848 (:call-lexical
2849 (destructuring-bind (binding num-args)
2850 (cdr instruction)
2851 (declare (ignore num-args))
2852 (etypecase binding
2853 (function-binding
2854 (take-note-of-binding binding))
2855 (funobj-binding))))
2856 (:init-lexvar
2857 (destructuring-bind (binding &key init-with-register init-with-type
2858 protect-registers protect-carry
2859 shared-reference-p)
2860 (cdr instruction)
2861 (declare (ignore protect-registers protect-carry init-with-type
2862 shared-reference-p))
2863 (cond
2864 ((not init-with-register)
2865 (take-note-of-init binding pc))
2866 (init-with-register
2867 (take-note-of-binding binding t pc)
2868 (when (and (typep init-with-register 'binding)
2869 (not (typep binding 'forwarding-binding))
2870 (not (typep binding 'keyword-function-argument))) ; XXX
2871 (take-note-of-binding init-with-register))))))
2872 (t (mapcar #'take-note-of-binding
2873 (find-read-bindings instruction))
2874 (mapcar #'record-binding-used ; This is just concerning "unused variable"
2875 (find-used-bindings instruction)) ; warnings!
2876 (let ((store-binding (find-written-binding-and-type instruction)))
2877 (when store-binding
2878 (take-note-of-binding store-binding t)))
2879 (do-discover-variables (instruction-sub-program instruction) env)))))))
2880 (do-discover-variables code function-env))
2881 (values var-counter)))
2883 (defun assign-bindings (code function-env &optional (initial-stack-frame-position 1)
2884 (frame-map (make-binding-map)))
2885 "Assign locations to all lexical variables in CODE. Recurses into any
2886 sub-environments found in CODE. A frame-map which is an assoc from
2887 bindings to stack-frame locations."
2888 ;; Then assign them to locations in the stack-frame.
2889 #+ignore (warn "assigning code:~%~{~& ~A~}" code)
2890 (check-type function-env function-env)
2891 (assert (= initial-stack-frame-position
2892 (1+ (frame-map-size frame-map))))
2893 (let* ((env-assigned-p nil) ; memoize result of assign-env-bindings
2894 (flat-program code)
2895 (var-counts (discover-variables flat-program function-env)))
2896 (labels
2897 ((assign-env-bindings (env)
2898 (unless (member env env-assigned-p)
2899 (unless (eq env function-env)
2900 (assign-env-bindings (movitz-environment-extent-uplink env)))
2901 (let* ((bindings-to-locate
2902 (loop for binding being the hash-keys of var-counts
2903 when
2904 (and (eq env (binding-extent-env binding))
2905 (not (let ((variable (binding-name binding)))
2906 (cond
2907 ((not (typep binding 'lexical-binding)))
2908 ((typep binding 'lambda-binding))
2909 ((typep binding 'constant-object-binding))
2910 ((typep binding 'forwarding-binding)
2911 (when (plusp (or (car (gethash binding var-counts)) 0))
2912 (assert (new-binding-located-p binding frame-map)))
2914 ((typep binding 'borrowed-binding))
2915 ((typep binding 'funobj-binding))
2916 ((and (typep binding 'fixed-required-function-argument)
2917 (plusp (or (car (gethash binding var-counts)) 0)))
2918 (prog1 nil ; may need lending-cons
2919 (setf (new-binding-location binding frame-map)
2920 `(:argument-stack ,(function-argument-argnum binding)))))
2921 ((unless (or (movitz-env-get variable 'ignore nil
2922 (binding-env binding) nil)
2923 (movitz-env-get variable 'ignorable nil
2924 (binding-env binding) nil)
2925 (third (gethash binding var-counts)))
2926 (warn "Unused variable: ~S"
2927 (binding-name binding))))
2928 ((not (plusp (or (car (gethash binding var-counts)) 0))))))))
2929 collect binding))
2930 (bindings-fun-arg-sorted
2931 (when (eq env function-env)
2932 (sort (copy-list bindings-to-locate) #'<
2933 :key (lambda (binding)
2934 (etypecase binding
2935 (edx-function-argument 3)
2936 (positional-function-argument
2937 (* 2 (function-argument-argnum binding)))
2938 (binding 100000))))))
2939 (bindings-register-goodness-sort
2940 (sort (copy-list bindings-to-locate) #'<
2941 ;; Sort so as to make the most likely
2942 ;; candidates for locating to registers
2943 ;; be assigned first (i.e. maps to
2944 ;; a smaller value).
2945 :key (lambda (b)
2946 (etypecase b
2947 ((or constant-object-binding
2948 forwarding-binding
2949 borrowed-binding)
2950 1000)
2951 (fixed-required-function-argument
2952 (+ 100 (function-argument-argnum b)))
2953 (located-binding
2954 (let* ((count-init (gethash b var-counts))
2955 (count (car count-init))
2956 (init-pc (second count-init)))
2957 (if (not (and count init-pc))
2959 (truncate
2960 (or (position-if (lambda (i)
2961 (member b (find-read-bindings i)))
2962 (cdr init-pc))
2964 count)))))))))
2965 ;; First, make several passes while trying to locate bindings
2966 ;; into registers.
2967 (loop repeat 100 with try-again = t and did-assign = t
2968 do (unless (and try-again did-assign)
2969 (return))
2970 do (setf try-again nil did-assign nil)
2971 (loop for binding in bindings-fun-arg-sorted
2972 while (or (typep binding 'register-required-function-argument)
2973 (typep binding 'floating-required-function-argument)
2974 (and (typep binding 'positional-function-argument)
2975 (< (function-argument-argnum binding)
2976 2)))
2977 do (unless (new-binding-located-p binding frame-map)
2978 (multiple-value-bind (register status)
2979 (try-locate-in-register binding var-counts
2980 (movitz-environment-funobj function-env)
2981 frame-map)
2982 (cond
2983 (register
2984 (setf (new-binding-location binding frame-map)
2985 register)
2986 (setf did-assign t))
2987 ((eq status :not-now)
2988 ;; (warn "Wait for ~S map ~A" binding frame-map)
2989 (setf try-again t))
2990 (t (assert (eq status :never)))))))
2991 (dolist (binding bindings-register-goodness-sort)
2992 (unless (and (binding-lended-p binding)
2993 (not (typep binding 'borrowed-binding))
2994 (not (getf (binding-lending binding) :stack-cons-location)))
2995 (unless (new-binding-located-p binding frame-map)
2996 (check-type binding located-binding)
2997 (multiple-value-bind (register status)
2998 (try-locate-in-register binding var-counts
2999 (movitz-environment-funobj function-env)
3000 frame-map)
3001 (cond
3002 (register
3003 (setf (new-binding-location binding frame-map)
3004 register)
3005 (setf did-assign t))
3006 ((eq status :not-now)
3007 (setf try-again t))
3008 (t (assert (eq status :never))))))))
3009 do (when (and try-again (not did-assign))
3010 (let ((binding (or (find-if (lambda (b)
3011 (and (typep b 'positional-function-argument)
3012 (= 0 (function-argument-argnum b))
3013 (not (new-binding-located-p b frame-map))))
3014 bindings-fun-arg-sorted)
3015 (find-if (lambda (b)
3016 (and (typep b 'positional-function-argument)
3017 (= 1 (function-argument-argnum b))
3018 (not (new-binding-located-p b frame-map))))
3019 bindings-fun-arg-sorted)
3020 (find-if (lambda (b)
3021 (and (not (new-binding-located-p b frame-map))
3022 (not (typep b 'function-argument))))
3023 bindings-register-goodness-sort
3024 :from-end t))))
3025 (when binding
3026 (setf (new-binding-location binding frame-map)
3027 (frame-map-next-free-location frame-map (binding-env binding)))
3028 (setf did-assign t))))
3029 finally (break "100 iterations didn't work"))
3030 ;; Then, make one pass assigning bindings to stack-frame.
3031 (loop for binding in bindings-fun-arg-sorted
3032 while (or (typep binding 'register-required-function-argument)
3033 (typep binding 'floating-required-function-argument)
3034 (and (typep binding 'positional-function-argument)
3035 (< (function-argument-argnum binding)
3036 2)))
3037 do (unless (new-binding-located-p binding frame-map)
3038 (setf (new-binding-location binding frame-map)
3039 (frame-map-next-free-location frame-map (binding-env binding)))))
3040 (dolist (binding bindings-register-goodness-sort)
3041 (when (and (binding-lended-p binding)
3042 (not (typep binding 'borrowed-binding))
3043 (not (getf (binding-lending binding) :stack-cons-location)))
3044 #+ignore
3045 (assert (not (typep binding 'keyword-function-argument)) ()
3046 "Can't lend keyword binding ~S." binding)
3047 ;; (warn "assigning lending-cons for ~W at ~D" binding stack-frame-position)
3048 (let ((cons-pos (frame-map-next-free-location frame-map function-env 2)))
3049 (setf (new-binding-location (cons :lended-cons binding) frame-map)
3050 cons-pos)
3051 (setf (new-binding-location (cons :lended-cons binding) frame-map)
3052 (1+ cons-pos))
3053 (setf (getf (binding-lending binding) :stack-cons-location)
3054 cons-pos)))
3055 (unless (new-binding-located-p binding frame-map)
3056 (etypecase binding
3057 (constant-object-binding) ; no location needed.
3058 (forwarding-binding) ; will use the location of target binding.
3059 (borrowed-binding) ; location is predetermined
3060 (fixed-required-function-argument
3061 (setf (new-binding-location binding frame-map)
3062 `(:argument-stack ,(function-argument-argnum binding))))
3063 (located-binding
3064 (setf (new-binding-location binding frame-map)
3065 (frame-map-next-free-location frame-map (binding-env binding)))))))
3066 (push env env-assigned-p)))))
3067 ;; First, "assign" each forwarding binding to their target.
3068 (loop for binding being the hash-keys of var-counts
3069 do (when (and (typep binding 'forwarding-binding)
3070 (plusp (car (gethash binding var-counts '(0)))))
3071 (setf (new-binding-location binding frame-map)
3072 (forwarding-binding-target binding))))
3073 ;; Keyword bindings
3074 (flet ((set-exclusive-location (binding location)
3075 (assert (not (rassoc location frame-map))
3076 () "Fixed location ~S for ~S is taken by ~S."
3077 location binding (rassoc location frame-map))
3078 (setf (new-binding-location binding frame-map) location)))
3079 (when (key-vars-p function-env)
3080 (when (= 0 (rest-args-position function-env))
3081 (set-exclusive-location (loop for var in (required-vars function-env)
3082 as binding = (movitz-binding var function-env nil)
3083 thereis (when (= 0 (function-argument-argnum binding))
3084 binding))
3086 (when (>= 1 (rest-args-position function-env))
3087 (set-exclusive-location (loop for var in (required-vars function-env)
3088 as binding = (movitz-binding var function-env nil)
3089 thereis (when (= 1 (function-argument-argnum binding))
3090 binding))
3091 2)))
3092 (loop for key-var in (key-vars function-env)
3093 as key-binding = (or (movitz-binding key-var function-env nil)
3094 (error "No binding for key-var ~S." key-var))
3095 as used-key-binding =
3096 (when (plusp (car (gethash key-binding var-counts '(0))))
3097 key-binding)
3098 as used-supplied-p-binding =
3099 (when (optional-function-argument-supplied-p-var key-binding)
3100 (let ((b (or (movitz-binding (optional-function-argument-supplied-p-var key-binding)
3101 function-env nil)
3102 (error "No binding for supplied-p-var ~S."
3103 (optional-function-argument-supplied-p-var key-binding)))))
3104 (when (plusp (car (gethash key-binding var-counts '(0))))
3105 b)))
3106 as location upfrom 3 by 2
3107 do (set-exclusive-location used-key-binding location)
3108 (set-exclusive-location used-supplied-p-binding (1+ location))))
3109 ;; Now, use assing-env-bindings on the remaining bindings.
3110 (loop for env in
3111 (loop with z = nil
3112 for b being the hash-keys of var-counts using (hash-value c)
3113 as env = (binding-env b)
3114 when (sub-env-p env function-env)
3115 do (incf (getf z env 0) (car c))
3116 finally
3117 (return (sort (loop for x in z by #'cddr
3118 collect x)
3120 :key (lambda (env)
3121 (getf z env)))))
3122 do (assign-env-bindings env))
3123 #+ignore (warn "Frame-map ~D:~{~&~A~}"
3124 (frame-map-size frame-map)
3125 (stable-sort (sort (loop for (b . l) in frame-map
3126 collect (list b l (car (gethash b var-counts nil))))
3127 #'string<
3128 :key (lambda (x)
3129 (and (bindingp (car x))
3130 (binding-name (car x)))))
3132 :key (lambda (x)
3133 (if (integerp (cadr x))
3134 (cadr x)
3135 1000))))
3136 frame-map)))
3139 (defun operators-present-in-code-p (code operators operands &key (operand-test #'eql)
3140 (test #'identity))
3141 "A simple tree search for `(<one of operators> ,operand) in CODE."
3142 ;; (break "Deprecated operators-present-in-code-p")
3143 (cond
3144 ((atom code)
3145 nil)
3146 ((and (member (first code) operators)
3147 (or (null operands)
3148 (if (atom operands)
3149 (funcall operand-test (second code) operands)
3150 (member (second code) operands :test operand-test)))
3151 (funcall test code)
3152 code))
3153 (t (or (operators-present-in-code-p (car code) operators operands
3154 :operand-test operand-test
3155 :test test)
3156 (operators-present-in-code-p (cdr code) operators operands
3157 :operand-test operand-test
3158 :test test)))))
3161 (defun code-uses-binding-p (code binding &key (load t) store call)
3162 "Does extended <code> potentially read/write/call <binding>?"
3163 (labels ((search-funobj (funobj binding load store call)
3164 ;; If this is a recursive lexical call (i.e. labels),
3165 ;; the function-envs might not be bound, but then this
3166 ;; code is searched already.
3167 (when (slot-boundp funobj 'function-envs)
3168 (some (lambda (function-env-spec)
3169 (code-search (extended-code (cdr function-env-spec)) binding
3170 load store call))
3171 (function-envs funobj))))
3172 (code-search (code binding load store call)
3173 (dolist (instruction code)
3174 (when (consp instruction)
3175 (let ((x (or (when load
3176 (some (lambda (read-binding)
3177 (binding-eql read-binding binding))
3178 (find-read-bindings instruction)))
3179 (when store
3180 (let ((store-binding (find-written-binding-and-type instruction)))
3181 (when store-binding
3182 (binding-eql binding store-binding))))
3183 (case (car instruction)
3184 (:local-function-init
3185 (search-funobj (function-binding-funobj (second instruction))
3186 binding load store call))
3187 (:load-lambda
3188 (or (when load
3189 (binding-eql binding (second instruction)))
3190 (let ((allocation (movitz-allocation
3191 (function-binding-funobj (second instruction)))))
3192 (when (and load
3193 (typep allocation 'with-dynamic-extent-scope-env))
3194 (binding-eql binding (base-binding allocation))))
3195 (search-funobj (function-binding-funobj (second instruction))
3196 binding load store call)))
3197 (:call-lexical
3198 (or (when call
3199 (binding-eql binding (second instruction)))
3200 (search-funobj (function-binding-funobj (second instruction))
3201 binding load store call))))
3202 (code-search (instruction-sub-program instruction)
3203 binding load store call))))
3204 (when x (return t)))))))
3205 (code-search code binding load store call)))
3207 (defun bindingp (x)
3208 (typep x 'binding))
3210 (defun binding-target (binding)
3211 "Resolve a binding in terms of forwarding."
3212 (etypecase binding
3213 (forwarding-binding
3214 (binding-target (forwarding-binding-target binding)))
3215 (binding
3216 binding)))
3218 (defun binding-eql (x y)
3219 (check-type x binding)
3220 (check-type y binding)
3221 (or (eql x y)
3222 (and (typep x 'forwarding-binding)
3223 (binding-eql (forwarding-binding-target x) y))
3224 (and (typep y 'forwarding-binding)
3225 (binding-eql x (forwarding-binding-target y)))))
3227 (defun tree-search (tree items)
3228 (if (and (atom items) ; make common case fast(er), hopefully.
3229 (not (numberp items)))
3230 (labels ((tree-search* (tree item)
3231 (etypecase tree
3232 (null nil)
3233 (cons
3234 (or (tree-search* (car tree) item)
3235 (tree-search* (cdr tree) item)))
3236 (t (eq tree item)))))
3237 (tree-search* tree items))
3238 (etypecase tree
3239 (atom
3240 (if (atom items)
3241 (eql tree items)
3242 (member tree items)))
3243 (cons
3244 (or (tree-search (car tree) items)
3245 (tree-search (cdr tree) items))))))
3247 (defun operator (x)
3248 (if (atom x) x (car x)))
3250 (defun result-mode-type (x)
3251 (etypecase x
3252 (symbol x)
3253 (cons (car x))
3254 (constant-object-binding :constant-binding)
3255 (lexical-binding :lexical-binding)
3256 (dynamic-binding :dynamic-binding)))
3258 (defun operands (x)
3259 (if (symbolp x) nil (cdr x)))
3261 (defun funobj-assign-bindings (code env &optional (stack-frame-position 1)
3262 (frame-map (make-binding-map)))
3263 "This wrapper around assign-bindings checks if the first instructions of CODE
3264 are load-lexicals of the first two function arguments, and if possible these
3265 bindings are located in the appropriate register, so no stack location is needed."
3266 (check-type env function-env)
3267 (assign-bindings (append (when (first (required-vars env))
3268 (let ((binding (movitz-binding (first (required-vars env))
3269 env nil)))
3270 (check-type binding required-function-argument)
3271 `((:init-lexvar ,binding :init-with-register :eax :init-with-type t))))
3272 (when (second (required-vars env))
3273 (let ((binding (movitz-binding (second (required-vars env))
3274 env nil)))
3275 (check-type binding required-function-argument)
3276 `((:init-lexvar ,binding :init-with-register :ebx :init-with-type t))))
3277 code)
3278 env stack-frame-position frame-map))
3280 (defun single-value-register (mode)
3281 (ecase mode
3282 ((:eax :single-value :multiple-values :function) :eax)
3283 ((:ebx :ecx :edx :esi :esp :ebp) mode)))
3285 (defun result-mode-register (mode)
3286 (case mode
3287 ((:eax :single-value) :eax)
3288 ((:ebx :ecx :edx :esi :esp) mode)
3289 (t mode)))
3291 (defun accept-register-mode (mode &optional (default-mode :eax))
3292 (case mode
3293 ((:eax :ebx :ecx :edx)
3294 mode)
3295 (t default-mode)))
3297 (defun chose-free-register (unfree-registers &optional (preferred-register :eax))
3298 (cond
3299 ((not (member preferred-register unfree-registers))
3300 preferred-register)
3301 ((find-if (lambda (r) (not (member r unfree-registers)))
3302 '(:eax :ebx :ecx :edx)))
3303 (t (error "Unable to find a free register."))))
3305 (defun make-indirect-reference (base-register offset)
3306 "Make the shortest possible assembly indirect reference, explointing the constant edi register."
3307 (if (<= #x-80 offset #x7f)
3308 (list base-register offset)
3309 (let ((edi (image-nil-word *image*)))
3310 (cond
3311 ((<= #x-80 (- offset edi) #x7f)
3312 `(,base-register :edi ,(- offset edi)))
3313 ((<= #x-80 (- offset (* 2 edi)) #x7f)
3314 `(,base-register (:edi 2) ,(- offset (* 2 edi))))
3315 ((<= #x-80 (- offset (* 4 edi)) #x7f)
3316 `(,base-register (:edi 4) ,(- offset (* 4 edi))))
3317 ((<= #x-80 (- offset (* 8 edi)) #x7f)
3318 `(,base-register (:edi 8) ,(- offset (* 8 edi))))
3319 (t (list base-register offset))))))
3321 (defun make-load-lexical (binding result-mode funobj shared-reference-p frame-map
3322 &key tmp-register protect-registers override-binding-type)
3323 "When tmp-register is provided, use that for intermediate storage required when
3324 loading borrowed bindings."
3325 #+ignore
3326 (when (eq :ecx result-mode)
3327 ;; (warn "loading to ecx: ~S" binding)
3328 (unless (or (null (binding-store-type binding))
3329 (movitz-subtypep (apply #'encoded-type-decode
3330 (binding-store-type binding))
3331 'integer))
3332 (warn "ecx from ~S" binding)))
3333 (when (movitz-env-get (binding-name binding) 'ignore nil (binding-env binding))
3334 (break "The variable ~S is used even if it was declared ignored."
3335 (binding-name binding)))
3336 (let ((binding (ensure-local-binding binding funobj))
3337 (protect-registers (cons :edx protect-registers)))
3338 (labels ((chose-tmp-register (&optional preferred)
3339 (or tmp-register
3340 (unless (member preferred protect-registers)
3341 preferred)
3342 (first (set-difference '(:eax :ebx :edx)
3343 protect-registers))
3344 (error "Unable to chose a temporary register.")))
3345 (install-for-single-value (lexb lexb-location result-mode indirect-p
3346 &optional binding-type)
3347 (let ((decoded-type (when binding-type
3348 (apply #'encoded-type-decode binding-type))))
3349 (cond
3350 ((and (eq result-mode :untagged-fixnum-ecx)
3351 (integerp lexb-location))
3352 (cond
3353 ((and binding-type
3354 (type-specifier-singleton decoded-type))
3355 #+ignore (warn "Immloadlex: ~S"
3356 (type-specifier-singleton decoded-type))
3357 (make-immediate-move (movitz-fixnum-value
3358 (car (type-specifier-singleton decoded-type)))
3359 :ecx))
3360 ((and binding-type
3361 (movitz-subtypep decoded-type '(and fixnum (unsigned-byte 32))))
3362 (assert (not indirect-p))
3363 (append (install-for-single-value lexb lexb-location :ecx nil)
3364 `((:shrl ,+movitz-fixnum-shift+ :ecx))))
3365 #+ignore ((warn "utecx ~S bt: ~S" lexb decoded-type))
3367 (assert (not indirect-p))
3368 (assert (not (member :eax protect-registers)))
3369 (append (install-for-single-value lexb lexb-location :eax nil)
3370 `((,*compiler-global-segment-prefix*
3371 :call (:edi ,(global-constant-offset 'unbox-u32))))))))
3372 ((integerp lexb-location)
3373 (append `((:movl ,(make-indirect-reference :ebp (stack-frame-offset lexb-location))
3374 ,(single-value-register result-mode)))
3375 (when indirect-p
3376 `((:movl (-1 ,(single-value-register result-mode))
3377 ,(single-value-register result-mode))))))
3378 ((eq lexb-location result-mode)
3380 (t (when (and (eq result-mode :untagged-fixnum-ecx)
3381 binding-type
3382 (type-specifier-singleton decoded-type))
3383 (break "xxx Immloadlex: ~S ~S"
3384 (operator lexb-location)
3385 (type-specifier-singleton decoded-type)))
3386 (ecase (operator lexb-location)
3387 (:push
3388 (assert (member result-mode '(:eax :ebx :ecx :edx)))
3389 (assert (not indirect-p))
3390 `((:popl ,result-mode)))
3391 (:eax
3392 (assert (not indirect-p))
3393 (ecase result-mode
3394 ((:ebx :ecx :edx :esi) `((:movl :eax ,result-mode)))
3395 ((:eax :single-value) nil)
3396 (:untagged-fixnum-ecx
3397 `((,*compiler-global-segment-prefix*
3398 :call (:edi ,(global-constant-offset 'unbox-u32)))))))
3399 ((:ebx :ecx :edx)
3400 (assert (not indirect-p))
3401 (unless (eq result-mode lexb-location)
3402 (ecase result-mode
3403 ((:eax :single-value) `((:movl ,lexb-location :eax)))
3404 ((:ebx :ecx :edx :esi) `((:movl ,lexb-location ,result-mode)))
3405 (:untagged-fixnum-ecx
3406 `((:movl ,lexb-location :ecx)
3407 (:sarl ,movitz:+movitz-fixnum-shift+ :ecx))))))
3408 (:argument-stack
3409 (assert (<= 2 (function-argument-argnum lexb)) ()
3410 "lexical :argument-stack argnum can't be ~A." (function-argument-argnum lexb))
3411 (cond
3412 ((eq result-mode :untagged-fixnum-ecx)
3413 (assert (not indirect-p))
3414 `((:movl (:ebp ,(argument-stack-offset lexb)) :ecx)
3415 (:sarl ,+movitz-fixnum-shift+ :ecx)))
3416 (t (append `((:movl (:ebp ,(argument-stack-offset lexb))
3417 ,(single-value-register result-mode)))
3418 (when indirect-p
3419 `((:movl (-1 ,(single-value-register result-mode))
3420 ,(single-value-register result-mode))))))))
3421 (:untagged-fixnum-ecx
3422 (ecase result-mode
3423 ((:eax :ebx :ecx :edx)
3424 `((:leal ((:ecx ,+movitz-fixnum-factor+)) ,result-mode)))
3425 (:untagged-fixnum-ecx
3426 nil)))))))))
3427 (etypecase binding
3428 (forwarding-binding
3429 (assert (not (binding-lended-p binding)) (binding)
3430 "Can't lend a forwarding-binding ~S." binding)
3431 (make-load-lexical (forwarding-binding-target binding)
3432 result-mode funobj shared-reference-p frame-map
3433 :override-binding-type (binding-store-type binding)))
3434 (constant-object-binding
3435 (assert (not (binding-lended-p binding)) (binding)
3436 "Can't lend a constant-reference-binding ~S." binding)
3437 (make-load-constant (constant-object binding)
3438 result-mode
3439 funobj frame-map))
3440 (funobj-binding
3441 (make-load-constant (function-binding-funobj binding)
3442 result-mode funobj frame-map))
3443 (borrowed-binding
3444 (let ((slot (borrowed-binding-reference-slot binding)))
3445 (cond
3446 (shared-reference-p
3447 (ecase (result-mode-type result-mode)
3448 ((:eax :ebx :ecx :edx)
3449 `((:movl (:esi ,(+ (slot-offset 'movitz-funobj 'constant0) (* 4 slot)))
3450 ,(result-mode-type result-mode))))))
3451 ((not shared-reference-p)
3452 (case result-mode
3453 ((:single-value :eax :ebx :ecx :edx :esi)
3454 (let ((tmp-register (chose-tmp-register (single-value-register result-mode))))
3455 `((:movl (:esi ,(+ (slot-offset 'movitz-funobj 'constant0) (* 4 slot)))
3456 ,tmp-register)
3457 (:movl (,tmp-register -1)
3458 ,(single-value-register result-mode)))))
3459 (:push
3460 (let ((tmp-register (chose-tmp-register :eax)))
3461 `((:movl (:esi ,(+ (slot-offset 'movitz-funobj 'constant0) (* 4 slot)))
3462 ,tmp-register)
3463 (:pushl (,tmp-register -1)))))
3464 (t (let ((tmp-register (chose-tmp-register :eax)))
3465 (make-result-and-returns-glue
3466 result-mode tmp-register
3467 `((:movl (:esi ,(+ (slot-offset 'movitz-funobj 'constant0) (* 4 slot)))
3468 ,tmp-register)
3469 (:movl (,tmp-register -1) ,tmp-register))))))))))
3470 (located-binding
3471 (let ((binding-type (or override-binding-type
3472 (binding-store-type binding)))
3473 (binding-location (new-binding-location binding frame-map)))
3474 #+ignore (warn "~S type: ~S ~:[~;lended~]"
3475 binding
3476 binding-type
3477 (binding-lended-p binding))
3478 (cond
3479 ((and (binding-lended-p binding)
3480 (not shared-reference-p))
3481 (case (result-mode-type result-mode)
3482 ((:single-value :eax :ebx :ecx :edx :esi :esp)
3483 (install-for-single-value binding binding-location
3484 (single-value-register result-mode) t))
3485 (:push
3486 (if (integerp binding-location)
3487 `((:movl (:ebp ,(stack-frame-offset binding-location)) :eax)
3488 (:pushl (:eax -1)))
3489 (ecase (operator binding-location)
3490 (:argument-stack
3491 (assert (<= 2 (function-argument-argnum binding)) ()
3492 ":load-lexical argnum can't be ~A." (function-argument-argnum binding))
3493 `((:movl (:ebp ,(argument-stack-offset binding)) :eax)
3494 (:pushl (:eax -1)))))))
3495 (t (make-result-and-returns-glue
3496 result-mode :eax
3497 (install-for-single-value binding binding-location :eax t)))))
3498 (t (when (integerp result-mode)
3499 (break "result-mode: ~S" result-mode))
3500 (case (result-mode-type result-mode)
3501 ((:single-value :eax :ebx :ecx :edx :esi :esp :ebp)
3502 (install-for-single-value binding binding-location
3503 (single-value-register result-mode) nil))
3504 (:push
3505 (if (integerp binding-location)
3506 `((:pushl (:ebp ,(stack-frame-offset binding-location))))
3507 (ecase (operator binding-location)
3508 ((:eax :ebx :ecx :edx)
3509 `((:pushl ,binding-location)))
3510 (:untagged-fixnum-ecx
3511 `((,*compiler-local-segment-prefix*
3512 :call (:edi ,(global-constant-offset 'box-u32-ecx)))
3513 (:pushl :eax)))
3514 (:argument-stack
3515 (assert (<= 2 (function-argument-argnum binding)) ()
3516 ":load-lexical argnum can't be ~A." (function-argument-argnum binding))
3517 `((:pushl (:ebp ,(argument-stack-offset binding))))))))
3518 (:boolean-branch-on-true
3519 (if (integerp binding-location)
3520 `((:cmpl :edi (:ebp ,(stack-frame-offset binding-location)))
3521 (:jne ',(operands result-mode)))
3522 (ecase (operator binding-location)
3523 ((:eax :ebx :edx)
3524 `((:cmpl :edi ,binding-location)
3525 (:jne ',(operands result-mode))))
3526 (:argument-stack
3527 `((:cmpl :edi (:ebp ,(argument-stack-offset binding)))
3528 (:jne ',(operands result-mode)))))))
3529 (:boolean-branch-on-false
3530 (if (integerp binding-location)
3531 `((:cmpl :edi (:ebp ,(stack-frame-offset binding-location)))
3532 (:je ',(operands result-mode)))
3533 (ecase (operator binding-location)
3534 ((:eax :ebx :edx)
3535 `((:cmpl :edi ,binding-location)
3536 (:je ',(operands result-mode))))
3537 (:argument-stack
3538 `((:cmpl :edi (:ebp ,(argument-stack-offset binding)))
3539 (:je ',(operands result-mode)))))))
3540 (:untagged-fixnum-ecx
3541 (install-for-single-value binding binding-location :untagged-fixnum-ecx nil
3542 binding-type))
3543 (:lexical-binding
3544 (let* ((destination result-mode)
3545 (dest-location (new-binding-location destination frame-map :default nil)))
3546 (cond
3547 ((not dest-location) ; unknown, e.g. a borrowed-binding.
3548 (append (install-for-single-value binding binding-location :edx nil)
3549 (make-store-lexical result-mode :edx nil funobj frame-map)))
3550 ((equal binding-location dest-location)
3551 nil)
3552 ((member binding-location '(:eax :ebx :ecx :edx))
3553 (make-store-lexical destination binding-location nil funobj frame-map))
3554 ((member dest-location '(:eax :ebx :ecx :edx))
3555 (install-for-single-value binding binding-location dest-location nil))
3556 (t #+ignore (warn "binding => binding: ~A => ~A~% => ~A ~A"
3557 binding-location
3558 dest-location
3559 binding
3560 destination)
3561 (append (install-for-single-value binding binding-location :eax nil)
3562 (make-store-lexical result-mode :eax nil funobj frame-map))))))
3563 (t (make-result-and-returns-glue
3564 result-mode :eax
3565 (install-for-single-value binding binding-location :eax nil)))
3566 )))))))))
3568 (defun make-store-lexical (binding source shared-reference-p funobj frame-map
3569 &key protect-registers)
3570 (let ((binding (ensure-local-binding binding funobj)))
3571 (assert (not (and shared-reference-p
3572 (not (binding-lended-p binding))))
3573 (binding)
3574 "funny binding: ~W" binding)
3575 (if (and nil (typep source 'constant-object-binding))
3576 (make-load-constant (constant-object source) binding funobj frame-map)
3577 (let ((protect-registers (cons source protect-registers)))
3578 (cond
3579 ((eq :untagged-fixnum-ecx source)
3580 (if (eq :untagged-fixnum-ecx
3581 (new-binding-location binding frame-map))
3583 (append (make-result-and-returns-glue :ecx :untagged-fixnum-ecx)
3584 (make-store-lexical binding :ecx shared-reference-p funobj frame-map
3585 :protect-registers protect-registers))))
3586 ((typep binding 'borrowed-binding)
3587 (let ((slot (borrowed-binding-reference-slot binding)))
3588 (if (not shared-reference-p)
3589 (let ((tmp-reg (chose-free-register protect-registers)
3590 #+ignore(if (eq source :eax) :ebx :eax)))
3591 (when (eq :ecx source)
3592 (break "loading a word from ECX?"))
3593 `((:movl (:esi ,(+ (slot-offset 'movitz-funobj 'constant0) (* 4 slot)))
3594 ,tmp-reg)
3595 (:movl ,source (-1 ,tmp-reg))))
3596 `((:movl ,source (:esi ,(+ (slot-offset 'movitz-funobj 'constant0) (* 4 slot))))))))
3597 ((typep binding 'forwarding-binding)
3598 (assert (not (binding-lended-p binding)) (binding))
3599 (make-store-lexical (forwarding-binding-target binding)
3600 source shared-reference-p funobj frame-map))
3601 ((not (new-binding-located-p binding frame-map))
3602 ;; (warn "Can't store to unlocated binding ~S." binding)
3603 nil)
3604 ((and (binding-lended-p binding)
3605 (not shared-reference-p))
3606 (let ((tmp-reg (chose-free-register protect-registers)
3607 #+ignore (if (eq source :eax) :ebx :eax))
3608 (location (new-binding-location binding frame-map)))
3609 (if (integerp location)
3610 `((:movl (:ebp ,(stack-frame-offset location)) ,tmp-reg)
3611 (:movl ,source (,tmp-reg -1)))
3612 (ecase (operator location)
3613 (:argument-stack
3614 (assert (<= 2 (function-argument-argnum binding)) ()
3615 "store-lexical argnum can't be ~A." (function-argument-argnum binding))
3616 `((:movl (:ebp ,(argument-stack-offset binding)) ,tmp-reg)
3617 (:movl ,source (,tmp-reg -1))))))))
3618 (t (let ((location (new-binding-location binding frame-map)))
3619 (cond
3620 ((member source '(:eax :ebx :ecx :edx :edi :esp))
3621 (if (integerp location)
3622 `((:movl ,source (:ebp ,(stack-frame-offset location))))
3623 (ecase (operator location)
3624 ((:push)
3625 `((:pushl ,source)))
3626 ((:eax :ebx :ecx :edx)
3627 (unless (eq source location)
3628 `((:movl ,source ,location))))
3629 (:argument-stack
3630 (assert (<= 2 (function-argument-argnum binding)) ()
3631 "store-lexical argnum can't be ~A." (function-argument-argnum binding))
3632 `((:movl ,source (:ebp ,(argument-stack-offset binding)))))
3633 (:untagged-fixnum-ecx
3634 (assert (not (eq source :edi)))
3635 (cond
3636 ((eq source :untagged-fixnum-ecx)
3637 nil)
3638 ((eq source :eax)
3639 `((,*compiler-global-segment-prefix*
3640 :call (:edi ,(global-constant-offset 'unbox-u32)))))
3641 (t `((:movl ,source :eax)
3642 (,*compiler-global-segment-prefix*
3643 :call (:edi ,(global-constant-offset 'unbox-u32))))))))))
3644 ((eq source :boolean-cf=1)
3645 (let ((tmp (chose-free-register protect-registers)))
3646 `((:sbbl :ecx :ecx)
3647 (,*compiler-local-segment-prefix*
3648 :movl (:edi (:ecx 4) ,(global-constant-offset 'not-not-nil)) ,tmp)
3649 ,@(make-store-lexical binding tmp shared-reference-p funobj frame-map
3650 :protect-registers protect-registers))))
3651 ((eq source :boolean-cf=0)
3652 (let ((tmp (chose-free-register protect-registers)))
3653 `((:sbbl :ecx :ecx)
3654 (,*compiler-local-segment-prefix*
3655 :movl (:edi (:ecx 4) ,(global-constant-offset 'boolean-zero)) ,tmp)
3656 ,@(make-store-lexical binding tmp shared-reference-p funobj frame-map
3657 :protect-registers protect-registers))))
3658 ((and *compiler-use-cmov-p*
3659 (member source +boolean-modes+))
3660 (let ((tmp (chose-free-register protect-registers)))
3661 (append `((:movl :edi ,tmp))
3662 (list (cons *compiler-local-segment-prefix*
3663 (make-cmov-on-boolean source
3664 `(:edi ,(global-constant-offset 't-symbol))
3665 tmp)))
3666 (make-store-lexical binding tmp shared-reference-p funobj frame-map
3667 :protect-registers protect-registers))))
3668 ((member source +boolean-modes+)
3669 (let ((tmp (chose-free-register protect-registers))
3670 (label (gensym "store-lexical-bool-")))
3671 (append `((:movl :edi ,tmp))
3672 (list (make-branch-on-boolean source label :invert t))
3673 `((,*compiler-local-segment-prefix*
3674 :movl (:edi ,(global-constant-offset 't-symbol)) ,tmp))
3675 (list label)
3676 (make-store-lexical binding tmp shared-reference-p funobj frame-map
3677 :protect-registers protect-registers))))
3678 ((not (bindingp source))
3679 (error "Unknown source for store-lexical: ~S" source))
3680 ((binding-singleton source)
3681 (assert (not shared-reference-p))
3682 (let ((value (car (binding-singleton source))))
3683 (etypecase value
3684 (movitz-fixnum
3685 (let ((immediate (movitz-immediate-value value)))
3686 (if (integerp location)
3687 (let ((tmp (chose-free-register protect-registers)))
3688 (append (make-immediate-move immediate tmp)
3689 `((:movl ,tmp (:ebp ,(stack-frame-offset location))))))
3690 #+ignore (if (= 0 immediate)
3691 (let ((tmp (chose-free-register protect-registers)))
3692 `((:xorl ,tmp ,tmp)
3693 (:movl ,tmp (:ebp ,(stack-frame-offset location)))))
3694 `((:movl ,immediate (:ebp ,(stack-frame-offset location)))))
3695 (ecase (operator location)
3696 ((:argument-stack)
3697 `((:movl ,immediate (:ebp ,(argument-stack-offset binding)))))
3698 ((:eax :ebx :ecx :edx)
3699 (make-immediate-move immediate location))
3700 ((:untagged-fixnum-ecx)
3701 (make-immediate-move (movitz-fixnum-value value) :ecx))))))
3702 (movitz-character
3703 (let ((immediate (movitz-immediate-value value)))
3704 (if (integerp location)
3705 (let ((tmp (chose-free-register protect-registers)))
3706 (append (make-immediate-move immediate tmp)
3707 `((:movl ,tmp (:ebp ,(stack-frame-offset location))))))
3708 (ecase (operator location)
3709 ((:argument-stack)
3710 `((:movl ,immediate (:ebp ,(argument-stack-offset binding)))))
3711 ((:eax :ebx :ecx :edx)
3712 (make-immediate-move immediate location))))))
3713 (movitz-heap-object
3714 (etypecase location
3715 ((member :eax :ebx :edx)
3716 (make-load-constant value location funobj frame-map))
3717 (integer
3718 (let ((tmp (chose-free-register protect-registers)))
3719 (append (make-load-constant value tmp funobj frame-map)
3720 (make-store-lexical binding tmp shared-reference-p
3721 funobj frame-map
3722 :protect-registers protect-registers))))
3723 ((eql :untagged-fixnum-ecx)
3724 (check-type value movitz-bignum)
3725 (let ((immediate (movitz-bignum-value value)))
3726 (check-type immediate (unsigned-byte 32))
3727 (make-immediate-move immediate :ecx)))
3728 )))))
3729 (t (error "Generalized lexb source for store-lexical not implemented: ~S" source))))))))))
3731 (defun finalize-code (code funobj frame-map)
3732 ;; (print-code 'to-be-finalized code)
3733 ;; (warn "frame-map: ~A" frame-map)
3734 (labels ((actual-binding (b)
3735 (if (typep b 'borrowed-binding)
3736 (borrowed-binding-target b)
3738 (make-lend-lexical (borrowing-binding funobj-register dynamic-extent-p)
3739 (let ((lended-binding (ensure-local-binding
3740 (borrowed-binding-target borrowing-binding))))
3741 #+ignore (warn "LB: in ~S ~S from ~S"
3742 funobj
3743 lended-binding borrowing-binding)
3744 (assert (eq funobj (binding-funobj lended-binding)))
3745 (assert (plusp (getf (binding-lending (actual-binding lended-binding))
3746 :lended-count 0)) ()
3747 "Asked to lend ~S of ~S to ~S of ~S with no lended-count."
3748 lended-binding (binding-env lended-binding)
3749 borrowing-binding (binding-env borrowing-binding))
3750 (assert (eq funobj-register :edx))
3751 (when (getf (binding-lending lended-binding) :dynamic-extent-p)
3752 (assert dynamic-extent-p))
3753 #+ignore
3754 (warn "lending: ~W: ~S"
3755 lended-binding
3756 (mapcar #'movitz-funobj-extent
3757 (mapcar #'binding-funobj
3758 (getf (binding-lending lended-binding) :lended-to))))
3759 (append (make-load-lexical lended-binding :eax funobj t frame-map)
3760 (unless (or (typep lended-binding 'borrowed-binding)
3761 (getf (binding-lending lended-binding) :dynamic-extent-p)
3762 (every (lambda (borrower)
3763 (member (movitz-funobj-extent (binding-funobj borrower))
3764 '(:lexical-extent :dynamic-extent)))
3765 (getf (binding-lending lended-binding) :lended-to)))
3766 (append `((:pushl :edx)
3767 (:globally (:call (:edi (:edi-offset ensure-heap-cons-variable))))
3768 (:popl :edx))
3769 (make-store-lexical lended-binding :eax t funobj frame-map)))
3770 `((:movl :eax
3771 (,funobj-register
3772 ,(+ (slot-offset 'movitz-funobj 'constant0)
3773 (* 4 (borrowed-binding-reference-slot borrowing-binding)))))))))
3774 (ensure-local-binding (binding)
3775 (if (eq funobj (binding-funobj binding))
3776 binding
3777 (or (find binding (borrowed-bindings funobj)
3778 :key #'borrowed-binding-target)
3779 (error "Can't install non-local binding ~W." binding)))))
3780 (labels ((fix-edi-offset (tree)
3781 (cond
3782 ((atom tree)
3783 tree)
3784 ((eq :edi-offset (car tree))
3785 (check-type (cadr tree) symbol "a Movitz run-time-context label")
3786 (+ (global-constant-offset (cadr tree))
3787 (reduce #'+ (cddr tree))))
3788 (t (cons (fix-edi-offset (car tree))
3789 (fix-edi-offset (cdr tree)))))))
3790 (loop for instruction in code
3791 appending
3792 (cond
3793 ((atom instruction)
3794 (list instruction))
3795 ((and (= 2 (length instruction))
3796 (let ((operand (second instruction)))
3797 (and (listp operand)
3798 (symbolp (first operand))
3799 (string= 'quote (first operand))
3800 (listp (second operand)))))
3801 ;;(break "op: ~S" (second (second instruction)))
3802 ;; recurse into program-to-append..
3803 (list (list (first instruction)
3804 (list 'quote (finalize-code (second (second instruction))
3805 funobj frame-map)))))
3807 (t ;; (warn "finalizing ~S" instruction)
3808 (case (first instruction)
3809 ((:locally :globally)
3810 (destructuring-bind (sub-instr)
3811 (cdr instruction)
3812 (let ((pf (ecase (first instruction)
3813 (:locally *compiler-local-segment-prefix*)
3814 (:globally *compiler-global-segment-prefix*))))
3815 (list (fix-edi-offset
3816 (cond
3817 ((atom sub-instr)
3818 sub-instr)
3819 ((consp (car sub-instr))
3820 (list* (append pf (car sub-instr))
3821 (cdr sub-instr)))
3822 (t (list* pf sub-instr))))))))
3823 ((:declare-label-set
3824 :declare-key-arg-set)
3825 nil)
3826 (:local-function-init
3827 (destructuring-bind (function-binding)
3828 (operands instruction)
3829 #+ignore
3830 (warn "local-function-init: init ~S at ~S"
3831 function-binding
3832 (new-binding-location function-binding frame-map))
3833 (finalize-code
3834 (let* ((sub-funobj (function-binding-funobj function-binding)))
3835 (cond
3836 ((eq (movitz-funobj-extent sub-funobj) :unused)
3837 (unless (or (movitz-env-get (binding-name function-binding)
3838 'ignore nil
3839 (binding-env function-binding) nil)
3840 (movitz-env-get (binding-name function-binding)
3841 'ignorable nil
3842 (binding-env function-binding) nil))
3843 (warn "Unused local function: ~S"
3844 (binding-name function-binding)))
3845 nil)
3846 ((typep function-binding 'funobj-binding)
3847 nil)
3848 #+ignore
3849 ((member (movitz-funobj-extent sub-funobj)
3850 '(:dynamic-extent :lexical-extent))
3851 (check-type function-binding closure-binding)
3852 (when (plusp (movitz-funobj-num-jumpers sub-funobj))
3853 (break "Don't know yet how to stack a funobj with jumpers."))
3854 (let ((words (+ (movitz-funobj-num-constants sub-funobj)
3855 (/ (sizeof 'movitz-funobj) 4))))
3856 (break "words for ~S: ~S" words sub-funobj)
3857 (append `((:movl :esp :eax)
3858 (:testl 4 :eax)
3859 (:jz 'no-alignment-needed)
3860 (:pushl :edi)
3861 no-alignment-needed)
3862 (make-load-constant sub-funobj :eax funobj frame-map)
3864 (t (assert (not (null (borrowed-bindings sub-funobj))))
3865 (append (make-load-constant sub-funobj :eax funobj frame-map)
3866 `((:movl (:edi ,(global-constant-offset 'copy-funobj)) :esi)
3867 (:call (:esi ,(bt:slot-offset 'movitz-funobj 'code-vector%1op)))
3868 (:movl :eax :edx))
3869 (make-store-lexical function-binding :eax nil funobj frame-map)
3870 (loop for bb in (borrowed-bindings sub-funobj)
3871 append (make-lend-lexical bb :edx nil))))))
3872 funobj frame-map)))
3873 (:load-lambda
3874 (destructuring-bind (function-binding register capture-env)
3875 (operands instruction)
3876 (declare (ignore capture-env))
3877 (finalize-code
3878 (let* ((sub-funobj (function-binding-funobj function-binding))
3879 (lend-code (loop for bb in (borrowed-bindings sub-funobj)
3880 appending
3881 (make-lend-lexical bb :edx nil))))
3882 (cond
3883 ((null lend-code)
3884 ;; (warn "null lambda lending")
3885 (append (make-load-constant sub-funobj register funobj frame-map)))
3886 ((typep (movitz-allocation sub-funobj)
3887 'with-dynamic-extent-scope-env)
3888 (setf (headers-on-stack-frame-p funobj) t)
3889 (let ((dynamic-scope (movitz-allocation sub-funobj)))
3890 (append (make-load-lexical (base-binding dynamic-scope) :edx
3891 funobj nil frame-map)
3892 `((:leal (:edx ,(tag :other)
3893 ,(dynamic-extent-object-offset dynamic-scope
3894 sub-funobj))
3895 :edx))
3896 lend-code
3897 `((:movl :edx ,register)))))
3898 (t (append (make-load-constant sub-funobj :eax funobj frame-map)
3899 `((:movl (:edi ,(global-constant-offset 'copy-funobj)) :esi)
3900 (:call (:esi ,(bt:slot-offset 'movitz-funobj 'code-vector%1op)))
3901 (:movl :eax :edx))
3902 lend-code
3903 `((:movl :edx ,register))))))
3904 funobj frame-map)))
3905 (:load-constant
3906 (destructuring-bind (object result-mode &key (op :movl))
3907 (cdr instruction)
3908 (make-load-constant object result-mode funobj frame-map :op op)))
3909 (:lexical-control-transfer
3910 (destructuring-bind (return-code return-mode from-env to-env &optional to-label)
3911 (cdr instruction)
3912 (declare (ignore return-code))
3913 (let ((x (apply #'make-compiled-lexical-control-transfer
3915 return-mode from-env to-env
3916 (when to-label (list to-label)))))
3917 (finalize-code x funobj frame-map))))
3918 (:call-lexical
3919 (destructuring-bind (binding num-args)
3920 (operands instruction)
3921 (append (etypecase binding
3922 (closure-binding
3923 (make-load-lexical (ensure-local-binding binding)
3924 :esi funobj nil frame-map
3925 :tmp-register :edx))
3926 (funobj-binding
3927 (make-load-constant (function-binding-funobj binding)
3928 :esi funobj frame-map)))
3929 (make-compiled-funcall-by-esi num-args))))
3930 (t (expand-extended-code instruction funobj frame-map)))))))))
3933 (defun image-t-symbol-p (x)
3934 (eq x (image-t-symbol *image*)))
3936 (deftype movitz-t ()
3937 `(satisfies image-t-symbol-p))
3939 (defun make-load-constant (object result-mode funobj frame-map &key (op :movl))
3940 (let ((movitz-obj (movitz-read object)))
3941 (case op
3942 (:movl
3943 (etypecase movitz-obj
3944 (movitz-null
3945 (ecase (result-mode-type result-mode)
3946 (:lexical-binding
3947 (make-store-lexical result-mode :edi nil funobj frame-map))
3948 (:push
3949 '((:pushl :edi)))
3950 ((:eax :ebx :ecx :edx)
3951 `((:movl :edi ,result-mode)))
3952 (:boolean-branch-on-true
3953 ;; (warn "branch-on-true for nil!")
3954 nil)
3955 (:boolean-branch-on-false
3956 ;; (warn "branch-on-false for nil!")
3957 `((:jmp ',(operands result-mode))))
3958 ((:multiple-values :function)
3959 '((:movl :edi :eax)
3960 (:clc)))
3961 #+ignore
3962 (t (when (eq :boolean result-mode)
3963 (warn "Compiling ~S for mode ~S." object result-mode))
3964 (make-result-and-returns-glue result-mode :edi nil)
3965 #+ignore '((:movl :edi :eax)))))
3966 (movitz-t
3967 (ecase (result-mode-type result-mode)
3968 (:push
3969 `((:pushl (:edi ,(global-constant-offset 't-symbol)))))
3970 ((:eax :ebx :ecx :edx)
3971 `((:movl (:edi ,(global-constant-offset 't-symbol)) ,result-mode)))
3972 (:boolean-branch-on-false
3973 ;; (warn "boolean-branch-on-false T")
3974 nil)
3975 (:boolean-branch-on-true
3976 ;; (warn "boolean-branch-on-true T")
3977 `((:jmp ',(operands result-mode))))
3978 ((:multiple-values :function)
3979 `((:movl (:edi ,(global-constant-offset 't-symbol))
3980 :eax)
3981 (:clc)))
3982 (:lexical-binding
3983 (append `((:movl (:edi ,(global-constant-offset 't-symbol))
3984 :eax))
3985 (make-store-lexical result-mode :eax nil funobj frame-map)))
3986 #+ignore
3987 (t (when (eq :boolean result-mode)
3988 (warn "Compiling ~S for mode ~S." object result-mode))
3989 (make-result-and-returns-glue result-mode :eax
3990 `((:movl (:edi ,(global-constant-offset 't-symbol))
3991 :eax))))))
3992 (movitz-immediate-object
3993 (let ((x (movitz-immediate-value movitz-obj)))
3994 (ecase (result-mode-type result-mode)
3995 (:lexical-binding
3996 (append (make-immediate-move x :eax)
3997 (make-store-lexical result-mode :eax nil funobj frame-map)))
3998 (:untagged-fixnum-ecx
3999 (let ((value (movitz-fixnum-value object)))
4000 (check-type value (unsigned-byte 32))
4001 (make-immediate-move value :ecx)))
4002 (:push
4003 `((:pushl ,x)))
4004 ((:eax :ebx :ecx :edx)
4005 (make-immediate-move x result-mode))
4006 ((:multiple-values :function)
4007 (append (make-immediate-move x :eax)
4008 '((:clc)))))))
4009 (movitz-heap-object
4010 (ecase (result-mode-type result-mode)
4011 (:untagged-fixnum-ecx
4012 (let ((value (movitz-bignum-value object)))
4013 (make-immediate-move (ldb (byte 32 0) value) :ecx)))
4014 (:lexical-binding
4015 (cond
4016 ((and (typep movitz-obj 'movitz-bignum)
4017 (eq :untagged-fixnum-ecx
4018 (new-binding-location result-mode frame-map :default nil)))
4019 (unless (typep (movitz-bignum-value movitz-obj) '(unsigned-byte 32))
4020 (warn "Loading non-u32 ~S into ~S."
4021 (movitz-bignum-value movitz-obj)
4022 result-mode))
4023 (make-immediate-move (ldb (byte 32 0) (movitz-bignum-value movitz-obj))
4024 :ecx))
4025 (t (when (member (new-binding-location result-mode frame-map :default nil)
4026 '(:ebx :ecx :edx :esi))
4027 (warn "load to ~S at ~S from ~S"
4028 result-mode (new-binding-location result-mode frame-map) movitz-obj))
4029 (append `((:movl ,(new-make-compiled-constant-reference movitz-obj funobj)
4030 :eax))
4031 (make-store-lexical result-mode :eax nil funobj frame-map)))))
4032 (:push
4033 `((:pushl ,(new-make-compiled-constant-reference movitz-obj funobj))))
4034 ((:eax :ebx :ecx :edx :esi)
4035 `((,op ,(new-make-compiled-constant-reference movitz-obj funobj)
4036 ,result-mode)))
4037 ((:edi)
4038 (assert (eq op :cmpl))
4039 `((,op ,(new-make-compiled-constant-reference movitz-obj funobj)
4040 ,result-mode)))
4041 ((:function :multiple-values)
4042 (assert (eq op :movl))
4043 `((,op ,(new-make-compiled-constant-reference movitz-obj funobj)
4044 :eax)
4045 (:clc)))))))
4046 (t (ecase result-mode
4047 ((:eax :ebx :ecx :edx :esi)
4048 `((,op ,(new-make-compiled-constant-reference movitz-obj funobj)
4049 ,result-mode)))
4050 ((:edi)
4051 (assert (eq op :cmpl))
4052 `((,op ,(new-make-compiled-constant-reference movitz-obj funobj)
4053 ,result-mode))))))))
4055 (defparameter +movitz-lambda-list-keywords+
4056 '(muerte.cl:&OPTIONAL
4057 muerte.cl:&REST
4058 muerte.cl:&KEY
4059 muerte.cl:&AUX
4060 muerte.cl:&BODY
4061 muerte.cl:&WHOLE
4062 muerte.cl:&ALLOW-OTHER-KEYS
4063 muerte.cl:&ENVIRONMENT))
4065 (defun add-bindings-from-lambda-list (lambda-list env)
4066 "From a (normal) <lambda-list>, add bindings to <env>."
4067 (let ((arg-pos 0))
4068 (multiple-value-bind (required-vars optional-vars rest-var key-vars auxes allow-p min-args max-args edx-var oddeven key-vars-p)
4069 (decode-normal-lambda-list lambda-list)
4070 (setf (min-args env) min-args
4071 (max-args env) max-args
4072 (oddeven-args env) oddeven
4073 (aux-vars env) auxes
4074 (allow-other-keys-p env) allow-p)
4075 (flet ((shadow-when-special (formal env)
4076 "Iff <formal> is special, return a fresh variable-name that takes <formal>'s place
4077 as the lexical variable-name, and add a new shadowing dynamic binding for <formal> in <env>."
4078 (if (not (movitz-env-get formal 'special nil env))
4079 formal
4080 (let* ((shadowed-formal (gensym (format nil "shady-~A-" formal)))
4081 (shadowing-binding (make-instance 'shadowing-dynamic-binding
4082 :name shadowed-formal
4083 :shadowing-variable formal
4084 :shadowed-variable shadowed-formal)))
4085 (movitz-env-add-binding env shadowing-binding formal)
4086 (push (list formal shadowed-formal)
4087 (special-variable-shadows env))
4088 shadowed-formal))))
4089 (when edx-var
4090 (movitz-env-add-binding env
4091 (setf (edx-var env)
4092 (make-instance 'edx-function-argument
4093 :name edx-var))))
4094 (setf (required-vars env)
4095 (loop for formal in required-vars
4096 do (check-type formal symbol)
4097 do (setf formal
4098 (shadow-when-special formal env))
4099 do (movitz-env-add-binding env (cond
4100 ((< arg-pos 2)
4101 (make-instance 'register-required-function-argument
4102 :name formal
4103 :argnum arg-pos))
4104 ((and max-args (= min-args max-args))
4105 (make-instance 'fixed-required-function-argument
4106 :name formal
4107 :argnum arg-pos
4108 :numargs min-args))
4109 (t (make-instance 'floating-required-function-argument
4110 :name formal
4111 :argnum arg-pos))))
4112 do (incf arg-pos)
4113 collect formal))
4114 (setf (optional-vars env)
4115 (loop for spec in optional-vars
4116 collect
4117 (multiple-value-bind (formal init-form supplied-p-parameter)
4118 (decode-optional-formal spec)
4119 (setf formal (shadow-when-special formal env))
4120 (movitz-env-add-binding env (make-instance 'optional-function-argument
4121 :name formal
4122 :argnum (post-incf arg-pos)
4123 'init-form init-form
4124 'supplied-p-var supplied-p-parameter))
4125 (when supplied-p-parameter
4126 (setf supplied-p-parameter
4127 (shadow-when-special supplied-p-parameter env))
4128 (movitz-env-add-binding env (make-instance 'supplied-p-function-argument
4129 :name supplied-p-parameter)))
4130 formal)))
4131 (when (or rest-var key-vars-p)
4132 (setf (rest-args-position env) arg-pos))
4133 (when rest-var
4134 (check-type rest-var symbol)
4135 (let ((formal (shadow-when-special rest-var env)))
4136 (setf (rest-var env) formal)
4137 (movitz-env-add-binding env (make-instance 'rest-function-argument
4138 :name formal
4139 :argnum (post-incf arg-pos)))))
4140 (when key-vars-p
4141 (setf (key-vars-p env) t)
4142 (when (>= 1 (rest-args-position env))
4143 (let ((name (gensym "save-ebx-for-keyscan")))
4144 (setf (required-vars env)
4145 (append (required-vars env)
4146 (list name)))
4147 (movitz-env-add-binding env (make-instance 'register-required-function-argument
4148 :name name
4149 :argnum 1
4150 :declarations '(muerte.cl:ignore)))
4151 (setf (movitz-env-get name 'ignore nil env) t)))
4152 (when (= 0 (rest-args-position env))
4153 (let ((name (gensym "save-eax-for-keyscan")))
4154 (push name (required-vars env))
4155 (movitz-env-add-binding env (make-instance 'register-required-function-argument
4156 :name name
4157 :argnum 0))
4158 (setf (movitz-env-get name 'ignore nil env) t))))
4159 (setf (key-vars env)
4160 (loop for spec in key-vars
4161 collect
4162 (multiple-value-bind (formal keyword-name init-form supplied-p)
4163 (decode-keyword-formal spec)
4164 (let ((formal (shadow-when-special formal env))
4165 (supplied-p-parameter supplied-p))
4166 (movitz-env-add-binding env (make-instance 'keyword-function-argument
4167 :name formal
4168 'init-form init-form
4169 'supplied-p-var supplied-p-parameter
4170 :keyword-name keyword-name))
4171 (when supplied-p-parameter
4172 (movitz-env-add-binding env (make-instance 'supplied-p-function-argument
4173 :name (shadow-when-special supplied-p-parameter env))))
4174 formal))))
4175 #+ignore
4176 (multiple-value-bind (key-decode-map key-decode-shift)
4177 (best-key-encode (key-vars env))
4178 (setf (key-decode-map env) key-decode-map
4179 (key-decode-shift env) key-decode-shift))
4180 #+ignore
4181 (when key-vars
4182 (warn "~D waste, keys: ~S, shift ~D, map: ~S"
4183 (- (length (key-decode-map env))
4184 (length key-vars))
4185 (key-vars env)
4186 (key-decode-shift env)
4187 (key-decode-map env))))))
4188 env)
4190 (defun make-compiled-function-prelude-numarg-check (min-args max-args)
4191 "The prelude is compiled after the function's body."
4192 (assert (or (not max-args) (<= 0 min-args max-args)))
4193 (assert (<= 0 min-args (or max-args min-args) #xffff) ()
4194 "Lambda lists longer than #xffff are not yet implemented.")
4195 (let ((wrong-numargs (make-symbol "wrong-numargs")))
4196 (cond
4197 ((and (zerop min-args) ; any number of arguments is
4198 (not max-args)) ; acceptable, no check necessary.
4199 nil)
4200 ((not max-args)
4201 ;; only minimum
4202 (if (< min-args #x80)
4203 `((:cmpb ,min-args :cl)
4204 (:jb '(:sub-program (,wrong-numargs) (:int 100))))
4205 `((:cmpl ,(dpb min-args (byte 24 8) #x80) :ecx)
4206 (:jb '(:sub-program (,wrong-numargs) (:int 100))))))
4207 ((and max-args (= 0 min-args max-args))
4208 ;; exactly zero
4209 `((:testb :cl :cl)
4210 (:jnz '(:sub-program (,wrong-numargs) (:int 100)))))
4211 ((and max-args (= min-args max-args))
4212 ;; exact number
4213 (cond
4214 ((= 1 min-args max-args)
4215 `((:call (:edi ,(global-constant-offset 'assert-1arg)))))
4216 ((= 2 min-args max-args)
4217 `((:call (:edi ,(global-constant-offset 'assert-2args)))))
4218 ((= 3 min-args max-args)
4219 `((:call (:edi ,(global-constant-offset 'assert-3args)))))
4220 ((< min-args #x80)
4221 `((:cmpb ,min-args :cl)
4222 (:jne '(:sub-program (,wrong-numargs) (:int 100)))))
4223 (t `((:cmpl ,(dpb min-args (byte 24 8) #x80) :ecx)
4224 (:jne '(:sub-program (,wrong-numargs) (:int 100)))))))
4225 ((and max-args (/= min-args max-args) (= 0 min-args))
4226 ;; only maximum
4227 (if (< max-args #x80)
4228 `((:cmpb ,max-args :cl)
4229 (:ja '(:sub-program (,wrong-numargs) (:int 100))))
4230 `((:cmpl ,(dpb max-args (byte 24 8) #x80) :ecx)
4231 (:ja '(:sub-program (,wrong-numargs) (:int 100))))))
4232 ((and max-args (/= min-args max-args))
4233 ;; both max and min
4234 (append (if (< min-args #x80)
4235 `((:cmpb ,min-args :cl)
4236 (:jb '(:sub-program (,wrong-numargs) (:int 100))))
4237 `((:cmpl ,(dpb min-args (byte 24 8) #x80) :ecx)
4238 (:jb '(:sub-program (,wrong-numargs) (:int 100)))))
4239 (if (< max-args #x80)
4240 `((:cmpb ,max-args :cl)
4241 (:ja '(:sub-program (,wrong-numargs) (:int 100))))
4242 `((:cmpl ,(dpb max-args (byte 24 8) #x80) :ecx)
4243 (:ja '(:sub-program (,wrong-numargs) (:int 100)))))))
4244 (t (error "Don't know how to compile checking for ~A to ~A arguments."
4245 min-args max-args)))))
4247 (defun make-stack-setup-code (stack-setup-size)
4248 (loop repeat stack-setup-size
4249 collect '(:pushl :edi))
4250 #+ignore
4251 (case stack-setup-size
4252 (0 nil)
4253 (1 '((:pushl :edi)))
4254 (2 '((:pushl :edi) (:pushl :edi)))
4255 (3 '((:pushl :edi) (:pushl :edi) (:pushl :edi)))
4256 (t `((:subl ,(* 4 stack-setup-size) :esp)))))
4258 (defun make-compiled-function-prelude (stack-frame-size env use-stack-frame-p
4259 need-normalized-ecx-p frame-map
4260 &key do-check-stack-p)
4261 "The prelude is compiled after the function's body is."
4262 (when (without-function-prelude-p env)
4263 (return-from make-compiled-function-prelude
4264 (when use-stack-frame-p
4265 `((:pushl :ebp)
4266 (:movl :esp :ebp)
4267 (:pushl :esi)))))
4268 (let ((required-vars (required-vars env))
4269 (min-args (min-args env))
4270 (max-args (max-args env)))
4271 (let ((stack-setup-size stack-frame-size)
4272 (edx-needs-saving-p (and (edx-var env)
4273 (new-binding-location (edx-var env) frame-map :default nil))))
4274 (multiple-value-bind (eax-ebx-code eax-ebx-code-post-stackframe)
4275 (let* ((map0 (find-if (lambda (bb)
4276 (and (typep (car bb) '(or required-function-argument
4277 optional-function-argument))
4278 (= 0 (function-argument-argnum (car bb)))))
4279 frame-map))
4280 (location-0 (cdr map0))
4281 (map1 (find-if (lambda (bb)
4282 (and (typep (car bb) '(or required-function-argument
4283 optional-function-argument))
4284 (= 1 (function-argument-argnum (car bb)))))
4285 frame-map))
4286 (location-1 (cdr map1))
4287 (edx-location
4288 (and (edx-var env)
4289 (new-binding-location (edx-var env) frame-map :default nil))))
4290 #+ignore (warn "l0: ~S, l1: ~S" location-0 location-1)
4291 (assert (not (and location-0
4292 (eql location-0 location-1))) ()
4293 "Compiler bug: two bindings in same location.")
4294 (cond
4295 ((and (eq :ebx location-0) (eq :eax location-1))
4296 `((:xchgl :eax :ebx)))
4297 ((and (eql 1 location-0) (eql 2 location-1))
4298 (decf stack-setup-size 2)
4299 (when (eql 3 edx-location)
4300 (decf stack-setup-size 1)
4301 (setf edx-needs-saving-p nil))
4302 (let (before-code after-code)
4303 (setf before-code
4304 (append
4305 `((:pushl :eax)
4306 (:pushl :ebx))
4307 (when (eql 3 edx-location)
4308 `((:pushl :edx)))
4309 ;; Keep pushing any sequentially following floating requireds.
4310 ;; NB: Fixed-floats are used in-place, e.g above the stack-frame,
4311 ;; so no need to worry about them.
4312 (loop with expected-location = 2
4313 for var in (cddr required-vars)
4314 as binding = (movitz-binding var env)
4315 if (and expected-location
4316 (typep binding 'floating-required-function-argument)
4317 (new-binding-located-p binding frame-map)
4318 (= expected-location
4319 (new-binding-location binding frame-map)))
4320 do (decf stack-setup-size)
4321 and do (incf expected-location)
4322 and do (setq need-normalized-ecx-p t)
4323 and collect
4324 `(:pushl (:ebp (:ecx 4)
4325 ,(* -4 (1- (function-argument-argnum binding)))))
4326 else do (setf expected-location nil)
4327 and do (when (and (typep binding 'floating-required-function-argument)
4328 (new-binding-located-p binding frame-map))
4329 (setq need-normalized-ecx-p t)
4330 (setf after-code
4331 (append
4332 after-code
4333 `((:movl (:ebp (:ecx 4)
4334 ,(* -4 (1- (function-argument-argnum binding))))
4335 :edx)
4336 (:movl :edx (:ebp ,(stack-frame-offset
4337 (new-binding-location binding frame-map)))))))))))
4338 (values before-code after-code)))
4339 (t (values (append
4340 (cond
4341 ((and (eq :ebx location-0)
4342 (eql 1 location-1))
4343 (decf stack-setup-size)
4344 `((:pushl :ebx)
4345 (:xchgl :eax :ebx)))
4346 ((and (eq :ebx location-0)
4347 (eq :edx location-1))
4348 `((:movl :ebx :edx)
4349 (:movl :eax :ebx)))
4350 (t (append
4351 (cond
4352 ((eql 1 location-0)
4353 (decf stack-setup-size)
4354 '((:pushl :eax)))
4355 (t (ecase location-0
4356 ((nil :eax) nil)
4357 (:ebx (assert (not location-1))
4358 '((:movl :eax :ebx)))
4359 (:edx (assert (not edx-location))
4360 '((:movl :eax :edx))))))
4361 (cond
4362 ((eql 1 location-1)
4363 (decf stack-setup-size)
4364 '((:pushl :ebx)))
4365 (t (ecase location-1
4366 ((nil :ebx) nil)
4367 (:edx '((:movl :ebx :edx)))
4368 (:eax `((:movl :ebx :eax)))))))))
4369 (cond
4370 ((or (and (or (eql 1 location-0)
4371 (eql 1 location-1))
4372 (eql 2 edx-location))
4373 (and (not (integerp location-0))
4374 (not (integerp location-1))
4375 (eql 1 edx-location)))
4376 (decf stack-setup-size)
4377 (setf edx-needs-saving-p nil)
4378 `((:pushl :edx)))))
4379 (loop for var in (cddr required-vars)
4380 as binding = (movitz-binding var env)
4381 when (and (typep binding 'floating-required-function-argument)
4382 (new-binding-located-p binding frame-map))
4383 append
4384 `((:movl (:ebp (:ecx 4)
4385 ,(* -4 (1- (function-argument-argnum binding))))
4386 :edx)
4387 (:movl :edx (:ebp ,(stack-frame-offset
4388 (new-binding-location binding frame-map)))))
4389 and do
4390 (setq need-normalized-ecx-p t))))))
4391 (assert (not (minusp stack-setup-size)))
4392 (let ((stack-frame-init-code
4393 (append (when (and do-check-stack-p use-stack-frame-p
4394 *compiler-auto-stack-checks-p*
4395 (not (without-check-stack-limit-p env)))
4396 `((,*compiler-local-segment-prefix*
4397 :bound (:edi ,(global-constant-offset 'stack-bottom)) :esp)))
4398 (when use-stack-frame-p
4399 `((:pushl :ebp)
4400 (:movl :esp :ebp)
4401 (:pushl :esi))))))
4402 (values
4403 (append
4404 (cond
4405 ((and (eql 1 min-args)
4406 (eql 1 max-args))
4407 (append (make-compiled-function-prelude-numarg-check min-args max-args)
4408 '(entry%1op)
4409 stack-frame-init-code))
4410 ((and (eql 2 min-args)
4411 (eql 2 max-args))
4412 (append (make-compiled-function-prelude-numarg-check min-args max-args)
4413 '(entry%2op)
4414 stack-frame-init-code))
4415 ((and (eql 3 min-args)
4416 (eql 3 max-args))
4417 (append (make-compiled-function-prelude-numarg-check min-args max-args)
4418 '(entry%3op)
4419 stack-frame-init-code))
4420 (t (append stack-frame-init-code
4421 (make-compiled-function-prelude-numarg-check min-args max-args))))
4422 '(start-stack-frame-setup)
4423 eax-ebx-code
4424 (make-stack-setup-code stack-setup-size)
4425 (when need-normalized-ecx-p
4426 (append (cond
4427 ;; normalize arg-count in ecx..
4428 ((and max-args (= min-args max-args))
4429 (error "huh?"))
4430 ((and max-args (<= 0 min-args max-args #x7f))
4431 `((:andl #x7f :ecx)))
4432 ((>= min-args #x80)
4433 `((:shrl 8 :ecx)))
4434 (t (let ((normalize (make-symbol "normalize-ecx"))
4435 (normalize-done (make-symbol "normalize-ecx-done")))
4436 `((:testb :cl :cl)
4437 (:js '(:sub-program (,normalize)
4438 (:shrl 8 :ecx)
4439 (:jmp ',normalize-done)))
4440 (:andl #x7f :ecx)
4441 ,normalize-done))))))
4442 (when edx-needs-saving-p
4443 `((:movl :edx (:ebp ,(stack-frame-offset (new-binding-location (edx-var env) frame-map))))))
4444 eax-ebx-code-post-stackframe
4445 (loop for binding in (potentially-lended-bindings env)
4446 as lended-cons-position = (getf (binding-lending binding) :stack-cons-location)
4447 as location = (new-binding-location binding frame-map :default nil)
4448 when (and (not (typep binding 'borrowed-binding))
4449 lended-cons-position
4450 location)
4451 append
4452 (typecase binding
4453 (required-function-argument
4454 ;; (warn "lend: ~W => ~W" binding lended-cons-position)
4455 (etypecase (operator location)
4456 ((eql :eax)
4457 (warn "lending EAX..")
4458 `((:movl :edi
4459 (:ebp ,(stack-frame-offset lended-cons-position))) ; cdr
4460 (:movl :eax
4461 (:ebp ,(stack-frame-offset (1+ lended-cons-position)))) ; car
4462 (:leal (:ebp 1 ,(stack-frame-offset (1+ lended-cons-position)))
4463 :eax)))
4464 ((eql :argument-stack)
4465 `((:movl (:ebp ,(argument-stack-offset binding)) :edx)
4466 (:movl :edi
4467 (:ebp ,(stack-frame-offset lended-cons-position))) ; cdr
4468 (:movl :edx
4469 (:ebp ,(stack-frame-offset (1+ lended-cons-position)))) ; car
4470 (:leal (:ebp 1 ,(stack-frame-offset (1+ lended-cons-position)))
4471 :edx)
4472 (:movl :edx
4473 (:ebp ,(argument-stack-offset binding)))))
4474 (integer
4475 `((:movl (:ebp ,(stack-frame-offset location))
4476 :edx)
4477 (:movl :edi
4478 (:ebp ,(stack-frame-offset lended-cons-position))) ; cdr
4479 (:movl :edx
4480 (:ebp ,(stack-frame-offset (1+ lended-cons-position)))) ; car
4481 (:leal (:ebp 1 ,(stack-frame-offset (1+ lended-cons-position)))
4482 :edx)
4483 (:movl :edx
4484 (:ebp ,(stack-frame-offset location)))))))
4485 (closure-binding
4486 ;; (warn "lend closure-binding: ~W => ~W" binding lended-cons-position)
4487 (etypecase (operator location)
4488 ((eql :argument-stack)
4489 `((:movl (:edi ,(global-constant-offset 'unbound-function)) :edx)
4490 (:movl :edi (:ebp ,(stack-frame-offset lended-cons-position))) ; cdr
4491 (:movl :edx (:ebp ,(stack-frame-offset (1+ lended-cons-position)))) ; car
4492 (:leal (:ebp 1 ,(stack-frame-offset (1+ lended-cons-position))) :edx)
4493 (:movl :edx (:ebp ,(argument-stack-offset binding)))))
4494 (integer
4495 `((:movl (:edi ,(global-constant-offset 'unbound-function)) :edx)
4496 (:movl :edi (:ebp ,(stack-frame-offset lended-cons-position))) ; cdr
4497 (:movl :edx (:ebp ,(stack-frame-offset (1+ lended-cons-position)))) ; car
4498 (:leal (:ebp 1 ,(stack-frame-offset (1+ lended-cons-position))) :edx)
4499 (:movl :edx (:ebp ,(stack-frame-offset location)))))))
4500 #+ignore
4501 (t (etypecase location
4502 ((eql :argument-stack)
4503 `((:movl :edi (:ebp ,(stack-frame-offset lended-cons-position))) ; cdr
4504 (:movl :edi (:ebp ,(stack-frame-offset (1+ lended-cons-position)))) ; car
4505 (:leal (:ebp 1 ,(stack-frame-offset (1+ lended-cons-position))) :edx)
4506 (:movl :edx (:ebp ,(argument-stack-offset binding)))))
4507 (integer
4508 `((:movl :edi (:ebp ,(stack-frame-offset lended-cons-position))) ; cdr
4509 (:movl :edi (:ebp ,(stack-frame-offset (1+ lended-cons-position)))) ; car
4510 (:leal (:ebp 1 ,(stack-frame-offset (1+ lended-cons-position))) :edx)
4511 (:movl :edx (:ebp ,(stack-frame-offset location))))))))))
4512 need-normalized-ecx-p))))))
4514 (defparameter *restify-stats* (make-hash-table :test #'eql))
4516 (defparameter *ll* (make-array 20 :initial-element 0))
4517 (defparameter *xx* (make-array 20))
4519 (defun install-arg-cmp (code have-normalized-ecx-p)
4520 (loop for i in code
4521 collecting
4522 (if (not (and (listp i) (eq :arg-cmp (car i))))
4524 (let ((arg-count (second i)))
4525 (cond
4526 (have-normalized-ecx-p
4527 `(:cmpl ,arg-count :ecx))
4528 ((< arg-count #x80)
4529 `(:cmpb ,arg-count :cl))
4530 (t `(:cmpl ,(dpb arg-count (byte 24 8) #x80) :ecx)))))))
4532 (defun make-function-arguments-init (funobj env)
4533 "The arugments-init is compiled before the function's body is.
4534 Return arg-init-code, need-normalized-ecx-p."
4535 (when (without-function-prelude-p env)
4536 (return-from make-function-arguments-init
4537 (values nil nil)))
4538 (let ((need-normalized-ecx-p nil)
4539 (required-vars (required-vars env))
4540 (optional-vars (optional-vars env))
4541 (rest-var (rest-var env))
4542 (key-vars (key-vars env)))
4543 (values
4544 (append
4545 (loop for optional in optional-vars
4546 as optional-var = (decode-optional-formal optional)
4547 as binding = (movitz-binding optional-var env)
4548 as last-optional-p = (and (null key-vars)
4549 (not rest-var)
4550 (= 1 (- (+ (length optional-vars) (length required-vars))
4551 (function-argument-argnum binding))))
4552 as supplied-p-var = (optional-function-argument-supplied-p-var binding)
4553 as supplied-p-binding = (movitz-binding supplied-p-var env)
4554 as not-present-label = (make-symbol (format nil "optional-~D-not-present"
4555 (function-argument-argnum binding)))
4556 and optional-ok-label = (make-symbol (format nil "optional-~D-ok"
4557 (function-argument-argnum binding)))
4558 unless (movitz-env-get optional-var 'ignore nil env nil) ; XXX
4559 append
4560 (cond
4561 ((= 0 (function-argument-argnum binding))
4562 `((:init-lexvar ,binding :init-with-register :eax :init-with-type t)))
4563 ((= 1 (function-argument-argnum binding))
4564 `((:init-lexvar ,binding :init-with-register :ebx :init-with-type t)))
4565 (t `((:init-lexvar ,binding))))
4566 when supplied-p-binding
4567 append `((:init-lexvar ,supplied-p-binding))
4568 append
4569 (compiler-values-bind (&code init-code-edx &producer producer)
4570 (compiler-call #'compile-form
4571 :form (optional-function-argument-init-form binding)
4572 :funobj funobj
4573 :env env
4574 :result-mode :edx)
4575 (cond
4576 ((and (eq 'compile-self-evaluating producer)
4577 (member (function-argument-argnum binding) '(0 1)))
4578 ;; The binding is already preset with EAX or EBX.
4579 (check-type binding lexical-binding)
4580 (append
4581 (when supplied-p-var
4582 `((:load-constant ,(movitz-read t) :edx)
4583 (:store-lexical ,supplied-p-binding :edx :type (member t))))
4584 `((:arg-cmp ,(function-argument-argnum binding))
4585 (:ja ',optional-ok-label))
4586 (compiler-call #'compile-form
4587 :form (optional-function-argument-init-form binding)
4588 :funobj funobj
4589 :env env
4590 :result-mode binding)
4591 (when supplied-p-var
4592 `((:store-lexical ,supplied-p-binding :edi :type null)))
4593 `(,optional-ok-label)))
4594 ((eq 'compile-self-evaluating producer)
4595 `(,@(when supplied-p-var
4596 `((:store-lexical ,supplied-p-binding :edi :type null)))
4597 ,@(if (optional-function-argument-init-form binding)
4598 (append init-code-edx `((:store-lexical ,binding :edx :type t)))
4599 `((:store-lexical ,binding :edi :type null)))
4600 (:arg-cmp ,(function-argument-argnum binding))
4601 (:jbe ',not-present-label)
4602 ,@(case (function-argument-argnum binding)
4603 (0 `((:store-lexical ,binding :eax :type t)))
4604 (1 `((:store-lexical ,binding :ebx :type t)))
4605 (t (cond
4606 (last-optional-p
4607 `((:movl (:ebp ,(* 4 (- (1+ (function-argument-argnum binding))
4608 -1 (function-argument-argnum binding))))
4609 :eax)
4610 (:store-lexical ,binding :eax :type t)))
4611 (t (setq need-normalized-ecx-p t)
4612 `((:movl (:ebp (:ecx 4)
4613 ,(* -4 (1- (function-argument-argnum binding))))
4614 :eax)
4615 (:store-lexical ,binding :eax :type t))))))
4616 ,@(when supplied-p-var
4617 `((:movl (:edi ,(global-constant-offset 't-symbol)) :eax)
4618 (:store-lexical ,supplied-p-binding :eax
4619 :type (eql ,(image-t-symbol *image*)))))
4620 ,not-present-label))
4621 (t `((:arg-cmp ,(function-argument-argnum binding))
4622 (:jbe ',not-present-label)
4623 ,@(when supplied-p-var
4624 `((:movl (:edi ,(global-constant-offset 't-symbol)) :eax)
4625 (:store-lexical ,supplied-p-binding :eax
4626 :type (eql ,(image-t-symbol *image*)))))
4627 ,@(case (function-argument-argnum binding)
4628 (0 `((:store-lexical ,binding :eax :type t)))
4629 (1 `((:store-lexical ,binding :ebx :type t)))
4630 (t (cond
4631 (last-optional-p
4632 `((:movl (:ebp ,(* 4 (- (1+ (function-argument-argnum binding))
4633 -1 (function-argument-argnum binding))))
4634 :eax)
4635 (:store-lexical ,binding :eax :type t)))
4636 (t (setq need-normalized-ecx-p t)
4637 `((:movl (:ebp (:ecx 4)
4638 ,(* -4 (1- (function-argument-argnum binding))))
4639 :eax)
4640 (:store-lexical ,binding :eax :type t))))))
4641 (:jmp ',optional-ok-label)
4642 ,not-present-label
4643 ,@(when supplied-p-var
4644 `((:store-lexical ,supplied-p-binding :edi :type null)))
4645 ,@(when (and (= 0 (function-argument-argnum binding))
4646 (not last-optional-p))
4647 `((:pushl :ebx))) ; protect ebx
4648 ,@(if (optional-function-argument-init-form binding)
4649 (append `((:shll ,+movitz-fixnum-shift+ :ecx)
4650 (:pushl :ecx))
4651 (when (= 0 (function-argument-argnum binding))
4652 `((:pushl :ebx)))
4653 init-code-edx
4654 `((:store-lexical ,binding :edx :type t))
4655 (when (= 0 (function-argument-argnum binding))
4656 `((:popl :ebx)))
4657 `((:popl :ecx)
4658 (:shrl ,+movitz-fixnum-shift+ :ecx)))
4659 (progn (error "Unsupported situation.")
4660 #+ignore `((:store-lexical ,binding :edi :type null))))
4661 ,@(when (and (= 0 (function-argument-argnum binding))
4662 (not last-optional-p))
4663 `((:popl :ebx))) ; protect ebx
4664 ,optional-ok-label)))))
4665 (when rest-var
4666 (let* ((rest-binding (movitz-binding rest-var env)))
4667 `((:init-lexvar ,rest-binding
4668 :init-with-register :edx
4669 :init-with-type list))))
4670 (when key-vars
4671 (play-with-keys key-vars))
4672 (when (key-vars-p env)
4673 ;; &key processing..
4674 (setq need-normalized-ecx-p t)
4675 (append
4676 `((:declare-key-arg-set ,@(mapcar (lambda (k)
4677 (movitz-read
4678 (keyword-function-argument-keyword-name
4679 (movitz-binding (decode-keyword-formal k) env))))
4680 key-vars)))
4681 (make-immediate-move (* +movitz-fixnum-factor+
4682 (rest-args-position env))
4683 :edx)
4684 `((:call (:edi ,(global-constant-offset 'decode-keyargs-default))))
4685 (unless (allow-other-keys-p env)
4686 `((:testl :eax :eax)
4687 (:jnz '(:sub-program (unknown-keyword)
4688 (:int 72)))))
4689 (loop for key-var in key-vars
4690 as key-location upfrom 3 by 2
4691 as key-var-name =
4692 (decode-keyword-formal key-var)
4693 as binding =
4694 (movitz-binding key-var-name env)
4695 as supplied-p-binding =
4696 (when (optional-function-argument-supplied-p-var binding)
4697 (movitz-binding (optional-function-argument-supplied-p-var binding)
4698 env))
4699 as keyword-ok-label = (make-symbol (format nil "keyword-~A-ok" key-var-name))
4700 do (assert binding)
4701 ;; (not (movitz-constantp (optional-function-argument-init-form binding)))
4702 append
4703 (append `((:init-lexvar ,binding
4704 :init-with-register ,binding
4705 :init-with-type t
4706 :shared-reference-p t))
4707 (when supplied-p-binding
4708 `((:init-lexvar ,supplied-p-binding
4709 :init-with-register ,supplied-p-binding
4710 :init-with-type t
4711 :shared-reference-p t)))
4712 (when (optional-function-argument-init-form binding)
4713 `((:cmpl :edi (:ebp ,(stack-frame-offset (1+ key-location))))
4714 (:jne ',keyword-ok-label)
4715 ,@(compiler-call #'compile-form
4716 :form (optional-function-argument-init-form binding)
4717 :env env
4718 :funobj funobj
4719 :result-mode binding)
4720 ,keyword-ok-label)))
4721 ;;; else append
4722 ;;; nil
4723 #+ignore
4724 (append (when supplied-p-var
4725 `((:init-lexvar ,supplied-p-binding
4726 :init-with-register :edi
4727 :init-with-type null)))
4728 (compiler-call #'compile-form
4729 :form (list 'muerte.cl:quote
4730 (eval-form (optional-function-argument-init-form binding)
4731 env))
4732 :env env
4733 :funobj funobj
4734 :result-mode :eax)
4735 `((:load-constant
4736 ,(movitz-read (keyword-function-argument-keyword-name binding)) :ecx)
4737 (:load-lexical ,rest-binding :ebx)
4738 (:call (:edi ,(global-constant-offset 'keyword-search))))
4739 (when supplied-p-var
4740 `((:jz ',keyword-not-supplied-label)
4741 (:movl (:edi ,(global-constant-offset 't-symbol)) :ebx)
4742 (:store-lexical ,supplied-p-binding :ebx
4743 :type (eql ,(image-t-symbol *image*)))
4744 ,keyword-not-supplied-label))
4745 `((:init-lexvar ,binding
4746 :init-with-register :eax
4747 :init-with-type t)))))))
4748 need-normalized-ecx-p)))
4750 (defun old-key-encode (vars &key (size (ash 1 (integer-length (1- (length vars)))))
4751 (byte (byte 16 0)))
4752 (assert (<= (length vars) size))
4753 (if (null vars)
4754 (values nil 0)
4755 (loop with h = (make-array size)
4756 with crash
4757 for var in (sort (copy-list vars) #'<
4758 :key (lambda (v)
4759 (mod (ldb byte (movitz-sxhash (movitz-read v)))
4760 (length h))))
4761 do (let ((pos (mod (ldb byte (movitz-sxhash (movitz-read var)))
4762 (length h))))
4763 (loop while (aref h pos)
4764 do (push var crash)
4765 (setf pos (mod (1+ pos) (length h))))
4766 (setf (aref h pos) var))
4767 finally (return (values (subseq h 0 (1+ (position-if-not #'null h :from-end t)))
4768 (length crash))))))
4770 (define-condition key-encoding-failed () ())
4772 (defun key-cuckoo (x shift table &optional path old-position)
4773 (if (member x path)
4774 (error 'key-encoding-failed)
4775 (let* ((pos1 (mod (ash (movitz-sxhash (movitz-read x)) (- shift))
4776 (length table)))
4777 (pos2 (mod (ash (movitz-sxhash (movitz-read x)) (- 0 shift 9))
4778 (length table)))
4779 (pos (if (eql pos1 old-position) pos2 pos1))
4780 (kickout (aref table pos)))
4781 (setf (aref table pos)
4783 (when kickout
4784 (key-cuckoo kickout shift table (cons x path) pos)))))
4786 (defun key-encode (vars &key (size (ash 1 (integer-length (1- (length vars)))))
4787 (shift 0))
4788 (declare (ignore byte))
4789 (assert (<= (length vars) size))
4790 (if (null vars)
4791 (values nil 0)
4792 (loop with table = (make-array size)
4793 for var in (sort (copy-list vars) #'<
4794 :key (lambda (v)
4795 (mod (movitz-sxhash (movitz-read v))
4796 (length table))))
4797 do (key-cuckoo var shift table)
4798 finally
4799 (return (values table
4800 (- (length vars)
4801 (count-if (lambda (v)
4802 (eq v (aref table (mod (ash (movitz-sxhash (movitz-read v))
4803 (- shift))
4804 (length table)))))
4805 vars)))))))
4807 (defun best-key-encode (vars)
4808 (when vars
4809 (loop with best-encoding = nil
4810 with best-shift
4811 with best-crashes
4812 for size = (ash 1 (integer-length (1- (length vars))))
4813 then (* size 2)
4814 ;; from (length vars) to (+ 8 (ash 1 (integer-length (1- (length vars)))))
4815 while (<= size (max 16 (ash 1 (integer-length (1- (length vars))))))
4816 do (loop for shift from 0 to 9 by 3
4817 do (handler-case
4818 (multiple-value-bind (encoding crashes)
4819 (key-encode vars :size size :shift shift)
4820 (when (or (not best-encoding)
4821 (< crashes best-crashes)
4822 (and (= crashes best-crashes)
4823 (or (< shift best-shift)
4824 (and (= shift best-shift)
4825 (< (length encoding)
4826 (length best-encoding))))))
4827 (setf best-encoding encoding
4828 best-shift shift
4829 best-crashes crashes)))
4830 (key-encoding-failed ())))
4831 finally
4832 (unless best-encoding
4833 (warn "Key-encoding failed for ~S: ~S."
4834 vars
4835 (mapcar (lambda (v)
4836 (list (movitz-sxhash (movitz-read v))
4837 (ldb (byte (+ 3 (integer-length (1- (length vars)))) 0)
4838 (movitz-sxhash (movitz-read v)))
4839 (ldb (byte (+ 3 (integer-length (1- (length vars)))) 9)
4840 (movitz-sxhash (movitz-read v)))))
4841 vars)))
4842 #+ignore
4843 (warn "~D waste for ~S"
4844 (- (length best-encoding)
4845 (length vars))
4846 vars)
4847 (return (values best-encoding best-shift best-crashes)))))
4851 (defun play-with-keys (key-vars)
4852 #+ignore
4853 (let* ((vars (mapcar #'decode-keyword-formal key-vars)))
4854 (multiple-value-bind (encoding shift crashes)
4855 (best-key-encode vars)
4856 (when (or (plusp crashes)
4857 #+ignore (>= shift 3)
4858 (>= (- (length encoding) (length vars))
4860 (warn "KEY vars: ~S, crash ~D, shift ~D, waste: ~D hash: ~S"
4861 vars crashes shift
4862 (- (length encoding) (length vars))
4863 (mapcar (lambda (s)
4864 (movitz-sxhash (movitz-read s)))
4865 vars))))))
4868 (defun make-special-funarg-shadowing (env function-body)
4869 "Wrap function-body in a let, if we need to.
4870 We need to when the function's lambda-list binds a special variable,
4871 or when there's a non-dynamic-extent &rest binding."
4872 (if (without-function-prelude-p env)
4873 function-body
4874 (let ((shadowing
4875 (append (special-variable-shadows env)
4876 (aux-vars env)
4877 (when (and (rest-var env)
4878 (not (movitz-env-get (rest-var env) 'dynamic-extent nil env nil))
4879 (not (movitz-env-get (rest-var env) 'ignore nil env nil)))
4880 (movitz-env-load-declarations `((muerte.cl:dynamic-extent ,(rest-var env)))
4881 env :funobj)
4882 `((,(rest-var env) (muerte.cl:copy-list ,(rest-var env))))))))
4883 (if (null shadowing)
4884 function-body
4885 `(muerte.cl::let ,shadowing ,function-body)))))
4887 (defun make-compiled-function-postlude (funobj env use-stack-frame-p)
4888 (declare (ignore funobj env))
4889 (let ((p '((:movl (:ebp -4) :esi)
4890 (:ret))))
4891 (if use-stack-frame-p
4892 (cons '(:leave) p)
4893 p)))
4895 (defun complement-boolean-result-mode (mode)
4896 (etypecase mode
4897 (keyword
4898 (ecase mode
4899 (:boolean-greater :boolean-less-equal)
4900 (:boolean-less :boolean-greater-equal)
4901 (:boolean-greater-equal :boolean-less)
4902 (:boolean-less-equal :boolean-greater)
4903 (:boolean-below :boolean-above-equal)
4904 (:boolean-above :boolean-below-equal)
4905 (:boolean-below-equal :boolean-above)
4906 (:boolean-above-equal :boolean-below)
4907 (:boolean-zf=1 :boolean-zf=0)
4908 (:boolean-zf=0 :boolean-zf=1)
4909 (:boolean-cf=1 :boolean-cf=0)
4910 (:boolean-cf=0 :boolean-cf=1)))
4911 (cons
4912 (let ((args (cdr mode)))
4913 (ecase (car mode)
4914 (:boolean-ecx
4915 (list :boolean-ecx (second args) (first args)))
4916 (:boolean-branch-on-true
4917 (cons :boolean-branch-on-false args))
4918 (:boolean-branch-on-false
4919 (cons :boolean-branch-on-true args)))))))
4921 (defun make-branch-on-boolean (mode label &key invert)
4922 (list (ecase (if invert (complement-boolean-result-mode mode) mode)
4923 (:boolean-greater :jg) ; ZF=0 and SF=OF
4924 (:boolean-greater-equal :jge) ; SF=OF
4925 (:boolean-less :jl) ; SF!=OF
4926 (:boolean-less-equal :jle) ; ZF=1 or SF!=OF
4927 (:boolean-below :jb)
4928 (:boolean-above :ja)
4929 (:boolean-below-equal :jbe)
4930 (:boolean-above-equal :jae)
4931 (:boolean-zf=1 :jz)
4932 (:boolean-zf=0 :jnz)
4933 (:boolean-cf=1 :jc)
4934 (:boolean-cf=0 :jnc)
4935 (:boolean-true :jmp))
4936 (list 'quote label)))
4939 (defun make-cmov-on-boolean (mode src dst &key invert)
4940 (list (ecase (if invert (complement-boolean-result-mode mode) mode)
4941 (:boolean-greater :cmovg) ; ZF=0 and SF=OF
4942 (:boolean-greater-equal :cmovge) ; SF=OF
4943 (:boolean-less :cmovl) ; SF!=OF
4944 (:boolean-less-equal :cmovle) ; ZF=1 or SF!=OF
4945 (:boolean-zf=1 :cmovz)
4946 (:boolean-zf=0 :cmovnz)
4947 (:boolean-cf=1 :cmovc)
4948 (:boolean-cf=0 :cmovnc))
4949 src dst))
4951 (defun return-satisfies-result-p (desired-result returns-provided)
4952 (or (eq desired-result returns-provided)
4953 (case desired-result
4954 (:ignore t)
4955 ((:eax :single-value)
4956 (member returns-provided '(:eax :multiple-values :single-value)))
4957 (:function
4958 (member returns-provided '(:multiple-values :function)))
4959 (:boolean
4960 (member returns-provided +boolean-modes+)))))
4962 (defun make-result-and-returns-glue (desired-result returns-provided
4963 &optional code
4964 &key (type t) provider really-desired)
4965 "Returns new-code and new-returns-provided, and glue-side-effects-p."
4966 (declare (optimize (debug 3)))
4967 (case returns-provided
4968 (:non-local-exit
4969 ;; when CODE does a non-local exit, we certainly don't need any glue.
4970 (return-from make-result-and-returns-glue
4971 (values code :non-local-exit))))
4972 (multiple-value-bind (new-code new-returns-provided glue-side-effects-p)
4973 (case (result-mode-type desired-result)
4974 ((:lexical-binding)
4975 (case (result-mode-type returns-provided)
4976 (:lexical-binding
4977 (if (eq desired-result returns-provided)
4978 (values code returns-provided)
4979 (values (append code `((:load-lexical ,returns-provided ,desired-result)))
4980 returns-provided)))
4981 ((:eax :multiple-values)
4982 (values (append code
4983 `((:store-lexical ,desired-result :eax
4984 :type ,(type-specifier-primary type))))
4985 desired-result
4987 ((:ebx :ecx)
4988 (values (append code
4989 `((:store-lexical ,desired-result
4990 ,(result-mode-type returns-provided)
4991 :type ,(type-specifier-primary type))))
4992 desired-result
4993 t))))
4994 (:ignore (values code :nothing))
4995 ((:boolean-ecx)
4996 (let ((true (first (operands desired-result)))
4997 (false (second (operands desired-result))))
4998 (etypecase (operator returns-provided)
4999 ((eql :boolean-ecx)
5000 (if (equal (operands desired-result)
5001 (operands returns-provided))
5002 (values code desired-result)
5004 ((eql :boolean-cf=1)
5005 (cond
5006 ((and (= -1 true) (= 0 false))
5007 (values (append code
5008 `((:sbbl :ecx :ecx)))
5009 '(:boolean-ecx -1 0)))
5010 ((and (= 0 true) (= -1 false))
5011 (values (append code
5012 `((:sbbl :ecx :ecx)
5013 (:notl :ecx)))
5014 '(:boolean-ecx 0 -1)))
5015 (t (error "Don't know modes ~S => ~S." returns-provided desired-result))))
5016 ((eql :eax)
5017 (make-result-and-returns-glue desired-result
5018 :boolean-cf=1
5019 (append code
5020 `((:leal (:eax ,(- (image-nil-word *image*)))
5021 :ecx)
5022 (:subl 1 :ecx)))
5023 :type type
5024 :provider provider
5025 :really-desired desired-result)))))
5026 (:boolean-branch-on-true
5027 ;; (warn "rm :b-true with ~S." returns-provided)
5028 (etypecase (operator returns-provided)
5029 ((member :boolean-branch-on-true)
5030 (assert (eq (operands desired-result) (operands returns-provided)))
5031 (values code returns-provided))
5032 ((member :eax :multiple-values)
5033 (values (append code
5034 `((:cmpl :edi :eax)
5035 (:jne ',(operands desired-result))))
5036 desired-result))
5037 ((member :ebx :ecx :edx)
5038 (values (append code
5039 `((:cmpl :edi ,returns-provided)
5040 (:jne ',(operands desired-result))))
5041 desired-result))
5042 ((member :nothing)
5043 ;; no branch, nothing is nil is false.
5044 (values code desired-result))
5045 ((member . #.+boolean-modes+)
5046 (values (append code
5047 (list (make-branch-on-boolean returns-provided (operands desired-result))))
5048 desired-result))
5049 (lexical-binding
5050 (values (append code
5051 `((:load-lexical ,returns-provided ,desired-result)))
5052 desired-result))
5053 (constant-object-binding
5054 (values (if (eq *movitz-nil* (constant-object returns-provided))
5056 `((:jmp ',(operands desired-result))))
5057 desired-result))))
5058 (:boolean-branch-on-false
5059 (etypecase (operator returns-provided)
5060 ((member :boolean-branch-on-false)
5061 (assert (eq (operands desired-result)
5062 (operands returns-provided)))
5063 (values code desired-result))
5064 ((member :nothing)
5065 (values (append code
5066 `((:jmp ',(operands desired-result))))
5067 desired-result))
5068 ((member . #.+boolean-modes+)
5069 (values (append code
5070 (list (make-branch-on-boolean returns-provided (operands desired-result)
5071 :invert t)))
5072 desired-result))
5073 ((member :ebx :ecx :edx)
5074 (values (append code
5075 `((:cmpl :edi ,returns-provided)
5076 (:je ',(operands desired-result))))
5077 desired-result))
5078 ((member :eax :multiple-values)
5079 (values (append code
5080 `((:cmpl :edi :eax)
5081 (:je ',(operands desired-result))))
5082 desired-result))
5083 (lexical-binding
5084 (values (append code
5085 `((:load-lexical ,returns-provided ,desired-result)))
5086 desired-result))
5087 (constant-object-binding
5088 (values (if (not (eq *movitz-nil* (constant-object returns-provided)))
5090 `((:jmp ',(operands desired-result))))
5091 desired-result))))
5092 (:untagged-fixnum-ecx
5093 (case (result-mode-type returns-provided)
5094 (:untagged-fixnum-ecx
5095 (values code :untagged-fixnum-ecx))
5096 ((:eax :single-value :multiple-values :function)
5097 (values (append code
5098 `((,*compiler-global-segment-prefix*
5099 :call (:edi ,(global-constant-offset 'unbox-u32)))))
5100 :untagged-fixnum-ecx))
5101 (:ecx
5102 ;; In theory (at least..) ECX can only hold non-pointers, so don't check.
5103 (values (append code
5104 `((:shrl ,+movitz-fixnum-shift+ :ecx)))
5105 :untagged-fixnum-ecx))
5106 ((:ebx :edx)
5107 (values (append code
5108 `((:movl ,returns-provided :eax)
5109 (,*compiler-global-segment-prefix*
5110 :call (:edi ,(global-constant-offset 'unbox-u32)))))
5111 :untagged-fixnum-ecx))
5112 (:lexical-binding
5113 (values (append code
5114 `((:load-lexical ,returns-provided :untagged-fixnum-ecx)))
5115 :untagged-fixnum-ecx))))
5116 ((:single-value :eax)
5117 (cond
5118 ((eq returns-provided :eax)
5119 (values code :eax))
5120 ((typep returns-provided 'lexical-binding)
5121 (values (append code `((:load-lexical ,returns-provided :eax)))
5122 :eax))
5123 (t (case (operator returns-provided)
5124 (:untagged-fixnum-eax
5125 (values (append code `((:shll ,+movitz-fixnum-shift+ :eax))) :eax))
5126 (:values
5127 (case (first (operands returns-provided))
5128 (0 (values (append code '((:movl :edi :eax)))
5129 :eax))
5130 (t (values code :eax))))
5131 ((:single-value :eax :function :multiple-values)
5132 (values code :eax))
5133 (:nothing
5134 (values (append code '((:movl :edi :eax)))
5135 :eax))
5136 ((:ebx :ecx :edx :edi)
5137 (values (append code `((:movl ,returns-provided :eax)))
5138 :eax))
5139 (:boolean-ecx
5140 (let ((true-false (operands returns-provided)))
5141 (cond
5142 ((equal '(0 1) true-false)
5143 (values (append code `((:movl (:edi (:ecx 4) ,(global-constant-offset 'boolean-zero))
5144 :eax)))
5145 :eax))
5146 ((equal '(1 0) true-false)
5147 (values (append code `((:movl (:edi (:ecx 4) ,(global-constant-offset 'boolean-one))
5148 :eax)))
5149 :eax))
5150 (t (error "Don't know ECX mode ~S." returns-provided)))))
5151 (:boolean-cf=1
5152 (values (append code
5153 `((:sbbl :ecx :ecx) ; T => -1, NIL => 0
5154 (:movl (:edi (:ecx 4) ,(global-constant-offset 'not-not-nil))
5155 :eax)))
5156 :eax))
5157 (#.+boolean-modes+
5158 ;; (warn "bool for ~S" returns-provided)
5159 (let ((boolean-false-label (make-symbol "boolean-false-label")))
5160 (values (append code
5161 '((:movl :edi :eax))
5162 (if *compiler-use-cmov-p*
5163 `(,(make-cmov-on-boolean returns-provided
5164 `(:edi ,(global-constant-offset 't-symbol))
5165 :eax
5166 :invert nil))
5167 `(,(make-branch-on-boolean returns-provided
5168 boolean-false-label
5169 :invert t)
5170 (:movl (:edi ,(global-constant-offset 't-symbol))
5171 :eax)
5172 ,boolean-false-label)))
5173 :eax)))))))
5174 ((:ebx :ecx :edx :esp :esi)
5175 (cond
5176 ((eq returns-provided desired-result)
5177 (values code returns-provided))
5178 ((typep returns-provided 'lexical-binding)
5179 (values (append code `((:load-lexical ,returns-provided ,desired-result)))
5180 desired-result))
5181 (t (case (operator returns-provided)
5182 (:nothing
5183 (values (append code
5184 `((:movl :edi ,desired-result)))
5185 desired-result))
5186 ((:ebx :ecx :edx :esp)
5187 (values (append code
5188 `((:movl ,returns-provided ,desired-result)))
5189 desired-result))
5190 ((:eax :single-value :multiple-values :function)
5191 (values (append code
5192 `((:movl :eax ,desired-result)))
5193 desired-result))
5194 (:boolean-ecx
5195 (let ((true-false (operands returns-provided)))
5196 (cond
5197 ((equal '(0 1) true-false)
5198 (values (append code `((:movl (:edi (:ecx 4) ,(global-constant-offset 'boolean-zero))
5199 ,desired-result)))
5200 desired-result))
5201 ((equal '(1 0) true-false)
5202 (values (append code `((:movl (:edi (:ecx 4) ,(global-constant-offset 'boolean-one))
5203 ,desired-result)))
5204 desired-result))
5205 (t (error "Don't know ECX mode ~S." returns-provided)))))
5206 ;;; (:boolean-ecx=0
5207 ;;; (values (append code `((:movl (:edi (:ecx 4) ,(global-constant-offset 'boolean-zero))
5208 ;;; ,desired-result)))
5209 ;;; desired-result))
5210 ;;; (:boolean-ecx=1
5211 ;;; (values (append code `((:movl (:edi (:ecx 4) ,(global-constant-offset 'boolean-one))
5212 ;;; ,desired-result)))
5213 ;;; desired-result))
5214 (:boolean-cf=1
5215 (values (append code
5216 `((:sbbl :ecx :ecx)
5217 (:movl (:edi (:ecx 4) ,(global-constant-offset 'not-not-nil))
5218 ,desired-result)))
5219 desired-result))
5220 (#.+boolean-modes+
5221 ;; (warn "bool to ~S for ~S" desired-result returns-provided)
5222 (values (append code
5223 (cond
5224 (*compiler-use-cmov-p*
5225 `((:movl :edi ,desired-result)
5226 ,(make-cmov-on-boolean returns-provided
5227 `(:edi ,(global-constant-offset 't-symbol))
5228 desired-result)))
5229 ((not *compiler-use-cmov-p*)
5230 (let ((boolean-false-label (make-symbol "boolean-false-label")))
5231 `((:movl :edi ,desired-result)
5232 ,(make-branch-on-boolean returns-provided
5233 boolean-false-label
5234 :invert t)
5235 (:movl (:edi ,(global-constant-offset 't-symbol))
5236 ,desired-result)
5237 ,boolean-false-label)))))
5238 desired-result))))))
5239 (:push
5240 (typecase returns-provided
5241 ((member :push) (values code :push))
5242 ((member :nothing)
5243 (values (append code '((:pushl :edi)))
5244 :push))
5245 ((member :single-value :eax :multiple-values :function)
5246 (values (append code `((:pushl :eax)))
5247 :push))
5248 ((member :ebx :ecx :edx)
5249 (values (append code `((:pushl ,returns-provided)))
5250 :push))
5251 (lexical-binding
5252 (values (append code `((:load-lexical ,returns-provided :push)))
5253 :push))))
5254 (:values
5255 (case (operator returns-provided)
5256 (:values
5257 (values code returns-provided))
5258 (:multiple-values
5259 (values code :values))
5260 (t (values (make-result-and-returns-glue :eax returns-provided code
5261 :type type)
5262 '(:values 1)))))
5263 ((:multiple-values :function)
5264 (case (operator returns-provided)
5265 ((:multiple-values :function)
5266 (values code :multiple-values))
5267 (:values
5268 (case (first (operands returns-provided))
5269 (0 (values (append code '((:movl :edi :eax) (:xorl :ecx :ecx) (:stc)))
5270 :multiple-values))
5271 (1 (values (append code '((:clc)))
5272 :multiple-values))
5273 ((nil) (values code :multiple-values))
5274 (t (values (append code
5275 (make-immediate-move (first (operands returns-provided)) :ecx)
5276 '((:stc)))
5277 :multiple-values))))
5278 (t (values (append (make-result-and-returns-glue :eax
5279 returns-provided
5280 code
5281 :type type
5282 :provider provider
5283 :really-desired desired-result)
5284 '((:clc)))
5285 :multiple-values)))))
5286 (unless new-returns-provided
5287 (multiple-value-setq (new-code new-returns-provided glue-side-effects-p)
5288 (ecase (result-mode-type returns-provided)
5289 (:constant-binding
5290 (case (result-mode-type desired-result)
5291 ((:eax :ebx :ecx :edx :push :lexical-binding)
5292 (values (append code
5293 `((:load-constant ,(constant-object returns-provided)
5294 ,desired-result)))
5295 desired-result))))
5296 (#.+boolean-modes+
5297 (make-result-and-returns-glue desired-result :eax
5298 (make-result-and-returns-glue :eax returns-provided code
5299 :type type
5300 :provider provider
5301 :really-desired desired-result)
5302 :type type
5303 :provider provider))
5304 (:untagged-fixnum-ecx
5305 (let ((fixnump (subtypep type `(integer 0 ,+movitz-most-positive-fixnum+))))
5306 (cond
5307 ((and fixnump
5308 (member (result-mode-type desired-result) '(:eax :ebx :ecx :edx)))
5309 (values (append code
5310 `((:leal ((:ecx ,+movitz-fixnum-factor+))
5311 ,(result-mode-type desired-result))))
5312 desired-result))
5313 ((and (not fixnump)
5314 (member (result-mode-type desired-result) '(:eax :single-value)))
5315 (values (append code
5316 `((:call (:edi ,(global-constant-offset 'box-u32-ecx)))))
5317 desired-result))
5318 (t (make-result-and-returns-glue
5319 desired-result :eax
5320 (make-result-and-returns-glue :eax :untagged-fixnum-ecx code
5321 :provider provider
5322 :really-desired desired-result
5323 :type type)
5324 :provider provider
5325 :type type)))))
5326 #+ignore
5327 (:untagged-fixnum-eax
5328 (make-result-and-returns-glue desired-result :eax
5329 (make-result-and-returns-glue :eax :untagged-fixnum-eax code
5330 :provider provider
5331 :really-desired desired-result)
5332 :provider provider)))))
5333 (assert new-returns-provided ()
5334 "Don't know how to match desired-result ~S with returns-provided ~S~@[ from ~S~]."
5335 (or really-desired desired-result) returns-provided provider)
5336 (values new-code new-returns-provided glue-side-effects-p)))
5338 (define-compiler compile-form (&all form-info &result-mode result-mode)
5339 "3.1.2.1 Form Evaluation. Guaranteed to honor RESULT-MODE."
5340 (compiler-values-bind (&all unprotected-values &code form-code &returns form-returns
5341 &producer producer &type form-type &functional-p functional-p)
5342 (compiler-call #'compile-form-unprotected :forward form-info)
5343 (multiple-value-bind (new-code new-returns-provided glue-side-effects-p)
5344 (make-result-and-returns-glue result-mode form-returns form-code
5345 :provider producer
5346 :type form-type)
5347 (compiler-values (unprotected-values)
5348 :type form-type
5349 :functional-p (and functional-p (not glue-side-effects-p))
5350 :producer producer
5351 :code new-code
5352 :returns new-returns-provided))))
5354 (define-compiler compile-form-selected (&all form-info &result-mode result-modes)
5355 "3.1.2.1 Form Evaluation. Guaranteed to honor one of RESULT-MODE, which
5356 for this call (exclusively!) is a list of the acceptable result-modes, where
5357 the first one takes preference. Note that :non-local-exit might also be returned."
5358 (check-type result-modes list "a list of result-modes")
5359 (compiler-values-bind (&all unprotected-values &code form-code &returns form-returns
5360 &producer producer &type form-type)
5361 (compiler-call #'compile-form-unprotected
5362 :result-mode (car result-modes)
5363 :forward form-info)
5364 (if (member form-returns result-modes)
5365 (compiler-values (unprotected-values))
5366 (compiler-call #'compile-form
5367 :result-mode (car result-modes)
5368 :forward form-info))))
5370 (define-compiler compile-form-to-register (&all form-info)
5371 (compiler-values-bind (&all unprotected-values &code form-code &returns form-returns
5372 &final-form final-form &producer producer &type form-type)
5373 (compiler-call #'compile-form-unprotected
5374 :result-mode :eax
5375 :forward form-info)
5376 (cond
5377 #+ignore
5378 ((and (typep final-form 'required-function-argument)
5379 (= 1 (function-argument-argnum final-form)))
5380 (compiler-call #'compile-form
5381 :result-mode :ebx
5382 :forward form-info))
5383 ((member form-returns '(:eax :ebx :ecx :edx :edi :untagged-fixnum-ecx))
5384 (compiler-values (unprotected-values)))
5385 (t (compiler-call #'compile-form
5386 :result-mode :eax
5387 :forward form-info)))))
5389 (define-compiler compile-form-unprotected (&all downstream &form form &result-mode result-mode
5390 &extent extent)
5391 "3.1.2.1 Form Evaluation. May not honor RESULT-MODE.
5392 That is, RESULT-MODE is taken to be a suggestion, not an imperative."
5393 (compiler-values-bind (&all upstream)
5394 (typecase form
5395 (symbol (compiler-call #'compile-symbol :forward downstream))
5396 (cons (compiler-call #'compile-cons :forward downstream))
5397 (t (compiler-call #'compile-self-evaluating :forward downstream)))
5398 (when (typep (upstream :final-form) 'lexical-binding)
5399 (labels ((fix-extent (binding)
5400 (cond
5401 ((sub-env-p extent (binding-extent-env binding))
5402 #+ignore (warn "Binding ~S OK in ~S wrt. ~S."
5403 binding
5404 (binding-extent-env binding)
5405 (downstream :env)))
5406 (t #+ignore (break "Binding ~S escapes from ~S to ~S"
5407 binding (binding-extent-env binding)
5408 extent)
5409 (setf (binding-extent-env binding) extent)))
5410 (when (typep binding 'forwarding-binding)
5411 (fix-extent (forwarding-binding-target binding)))))
5412 (when extent
5413 (fix-extent (upstream :final-form)))))
5414 (compiler-values (upstream))))
5416 (defun lambda-form-p (form)
5417 (and (listp form)
5418 (eq 'muerte.cl:lambda (first form))))
5420 (defun function-name-p (operator)
5421 (or (and (symbolp operator) operator)
5422 (setf-name operator)))
5424 (define-compiler compile-cons (&all all &form form &env env)
5425 "3.1.2.1.2 Conses as Forms"
5426 (let ((operator (car form)))
5427 (if (and (symbolp operator) (movitz-special-operator-p operator))
5428 (compiler-call (movitz-special-operator-compiler operator) :forward all)
5429 (let* ((compiler-macro-function (movitz-compiler-macro-function operator env))
5430 (compiler-macro-expansion (and compiler-macro-function
5431 (handler-case
5432 (funcall *movitz-macroexpand-hook*
5433 compiler-macro-function
5434 form env)
5435 (error (c)
5436 (warn "Compiler-macro for ~S failed: ~A" operator c)
5437 form)))))
5438 (cond
5439 ((and compiler-macro-function
5440 (not (movitz-env-get operator 'notinline nil env))
5441 (not (eq form compiler-macro-expansion)))
5442 (compiler-call #'compile-form-unprotected :forward all :form compiler-macro-expansion))
5443 ((movitz-constantp form env)
5444 (compiler-call #'compile-constant-compound :forward all))
5445 ((lambda-form-p operator) ; 3.1.2.1.2.4
5446 (compiler-call #'compile-lambda-form :forward all))
5447 ((symbolp operator)
5448 (cond
5449 ((movitz-special-operator-p operator)
5450 (compiler-call (movitz-special-operator-compiler operator) :forward all))
5451 ((movitz-macro-function operator env)
5452 (compiler-call #'compile-macro-form :forward all))
5453 ((movitz-operator-binding operator env)
5454 (compiler-call #'compile-apply-lexical-funobj :forward all))
5455 (t (compiler-call #'compile-apply-symbol :forward all))))
5456 (t (error "Don't know how to compile compound form ~A" form)))))))
5458 (define-compiler compile-compiler-macro-form (&all all &form form &env env)
5459 (compiler-call #'compile-form-unprotected
5460 :forward all
5461 :form (funcall *movitz-macroexpand-hook*
5462 (movitz-compiler-macro-function (car form) env)
5463 form env)))
5465 (define-compiler compile-macro-form (&all all &form form &env env)
5466 "3.1.2.1.2.2 Macro Forms"
5467 (let* ((operator (car form))
5468 (macro-function (movitz-macro-function operator env)))
5469 (compiler-call #'compile-form-unprotected
5470 :forward all
5471 :form (funcall *movitz-macroexpand-hook* macro-function form env))))
5473 (define-compiler compile-lexical-macro-form (&all all &form form &env env)
5474 "Compiles MACROLET and SYMBOL-MACROLET forms."
5475 (compiler-call #'compile-form-unprotected
5476 :forward all
5477 :form (funcall *movitz-macroexpand-hook*
5478 (macro-binding-expander (movitz-operator-binding form env))
5479 form env)))
5481 (defun like-compile-macroexpand-form (form env)
5482 (typecase form
5483 ;; (symbol (compile-macroexpand-symbol form funobj env top-level-p result-mode))
5484 (cons (like-compile-macroexpand-cons form env))
5485 (t (values form nil))))
5487 (defun like-compile-macroexpand-cons (form env)
5488 "3.1.2.1.2 Conses as Forms"
5489 (let* ((operator (car form))
5490 (notinline (movitz-env-get operator 'notinline nil env))
5491 (compiler-macro-function (movitz-compiler-macro-function operator env))
5492 (compiler-macro-expansion (and compiler-macro-function
5493 (funcall *movitz-macroexpand-hook*
5494 compiler-macro-function
5495 form env))))
5496 (cond
5497 ((and (not notinline)
5498 compiler-macro-function
5499 (not (eq form compiler-macro-expansion)))
5500 (values compiler-macro-expansion t))
5501 ((symbolp operator)
5502 (cond
5503 ((movitz-macro-function operator env)
5504 (values (funcall *movitz-macroexpand-hook*
5505 (movitz-macro-function operator env)
5506 form env)
5508 (t form)))
5509 (t form))))
5511 (defun make-compiled-stack-restore (stack-displacement result-mode returns)
5512 "Return the code required to reset the stack according to stack-displacement,
5513 result-mode, and returns (which specify the returns-mode of the immediately
5514 preceding code). As secondary value, returns the new :returns value."
5515 (flet ((restore-by-pop (scratch)
5516 (case stack-displacement
5517 (1 `((:popl ,scratch)))
5518 (2 `((:popl ,scratch) (:popl ,scratch))))))
5519 (if (zerop stack-displacement)
5520 (values nil returns)
5521 (ecase (result-mode-type result-mode)
5522 (:function
5523 (values nil returns))
5524 ((:multiple-values :values)
5525 (ecase returns
5526 (:multiple-values
5527 (values `((:leal (:esp ,(* 4 stack-displacement)) :esp))
5528 :multiple-values))
5529 ((:single-value :eax :ebx)
5530 (values `((:addl ,(* 4 stack-displacement) :esp))
5531 :multiple-values)))) ; assume this addl will set CF=0
5532 ((:single-value :eax :ebx :ecx :edx :push :lexical-binding :untagged-fixnum-ecx
5533 :boolean :boolean-branch-on-false :boolean-branch-on-true)
5534 (ecase returns
5535 (#.+boolean-modes+
5536 (values (or (restore-by-pop :eax)
5537 `((:leal (:esp ,(* 4 stack-displacement)) :esp))) ; preserve all flags
5538 returns))
5539 (:ebx
5540 (values (or (restore-by-pop :eax)
5541 `((:addl ,(* 4 stack-displacement) :esp)))
5542 :ebx))
5543 ((:multiple-values :single-value :eax)
5544 (values (or (restore-by-pop :ebx)
5545 `((:addl ,(* 4 stack-displacement) :esp)))
5546 :eax))))
5547 (:ignore
5548 (values (or (restore-by-pop :eax)
5549 `((:addl ,(* 4 stack-displacement) :esp)))
5550 :nothing))))))
5552 (define-compiler compile-apply-symbol (&form form &funobj funobj &env env
5553 &result-mode result-mode)
5554 "3.1.2.1.2.3 Function Forms"
5555 (destructuring-bind (operator &rest arg-forms)
5556 form
5557 #+ignore (when (and (eq result-mode :function)
5558 (eq operator (movitz-print (movitz-funobj-name funobj))))
5559 (warn "Tail-recursive call detected."))
5560 (when (eq operator 'muerte.cl::declare)
5561 (break "Compiling funcall to ~S" 'muerte.cl::declare))
5562 (pushnew (cons operator muerte.cl::*compile-file-pathname*)
5563 (image-called-functions *image*)
5564 :key #'first)
5565 (multiple-value-bind (arguments-code stack-displacement arguments-modifies)
5566 (make-compiled-argument-forms arg-forms funobj env)
5567 (multiple-value-bind (stack-restore-code new-returns)
5568 (make-compiled-stack-restore stack-displacement result-mode :multiple-values)
5569 (compiler-values ()
5570 :returns new-returns
5571 :functional-p nil
5572 :modifies arguments-modifies
5573 :code (append arguments-code
5574 (if (and (not *compiler-relink-recursive-funcall*)
5575 (eq (movitz-read operator)
5576 (movitz-read (movitz-funobj-name funobj)))) ; recursive?
5577 (make-compiled-funcall-by-esi (length arg-forms))
5578 (make-compiled-funcall-by-symbol operator (length arg-forms) funobj))
5579 stack-restore-code))))))
5581 (define-compiler compile-apply-lexical-funobj (&all all &form form &funobj funobj &env env
5582 &result-mode result-mode)
5583 "3.1.2.1.2.3 Function Forms"
5584 (destructuring-bind (operator &rest arg-forms)
5585 form
5586 (let ((binding (movitz-operator-binding operator env)))
5587 (multiple-value-bind (arguments-code stack-displacement)
5588 (make-compiled-argument-forms arg-forms funobj env)
5589 (multiple-value-bind (stack-restore-code new-returns)
5590 (make-compiled-stack-restore stack-displacement result-mode :multiple-values)
5591 (compiler-values ()
5592 :returns new-returns
5593 :functional-p nil
5594 :code (append arguments-code
5595 (if (eq funobj (function-binding-funobj binding))
5596 (make-compiled-funcall-by-esi (length arg-forms)) ; call ourselves
5597 `((:call-lexical ,binding ,(length arg-forms))))
5598 stack-restore-code)))))))
5600 (defun make-compiled-funcall-by-esi (num-args)
5601 (case num-args
5602 (1 `((:call (:esi ,(slot-offset 'movitz-funobj 'code-vector%1op)))))
5603 (2 `((:call (:esi ,(slot-offset 'movitz-funobj 'code-vector%2op)))))
5604 (3 `((:call (:esi ,(slot-offset 'movitz-funobj 'code-vector%3op)))))
5605 (t (append (if (< num-args #x80)
5606 `((:movb ,num-args :cl))
5607 (make-immediate-move (dpb num-args (byte 24 8) #x80) :ecx))
5608 ; call new ESI's code-vector
5609 `((:call (:esi ,(slot-offset 'movitz-funobj 'code-vector))))))))
5611 (defun make-compiled-funcall-by-symbol (apply-symbol num-args funobj)
5612 (declare (ignore funobj))
5613 (check-type apply-symbol symbol)
5614 `((:load-constant ,(movitz-read apply-symbol) :edx) ; put function symbol in EDX
5615 (:movl (:edx ,(slot-offset 'movitz-symbol 'function-value))
5616 :esi) ; load new funobj from symbol into ESI
5617 ,@(make-compiled-funcall-by-esi num-args)))
5619 (defun make-compiled-funcall-by-funobj (apply-funobj num-args funobj)
5620 (declare (ignore funobj))
5621 (check-type apply-funobj movitz-funobj)
5622 (compiler-values ()
5623 :returns :multiple-values
5624 :functional-p :nil
5625 :code `( ; put function funobj in ESI
5626 (:load-constant ,apply-funobj :esi)
5627 ,@(make-compiled-funcall-by-esi num-args))))
5629 (defun make-compiled-argument-forms (argument-forms funobj env)
5630 "Return code as primary value, and stack displacement as secondary value.
5631 Return the set of modified lexical bindings third. Fourth, a list of the individual
5632 compile-time types of each argument. Fifth: The combined functional-p."
5633 ;; (incf (aref *args* (min (length argument-forms) 9)))
5634 (case (length argument-forms) ;; "optimized" versions for 0, 1, 2, and 3 aruments.
5635 (0 (values nil 0 nil () t))
5636 (1 (compiler-values-bind (&code code &type type &functional-p functional-p)
5637 (compiler-call #'compile-form
5638 :form (first argument-forms)
5639 :funobj funobj
5640 :env env
5641 :result-mode :eax)
5642 (values code 0 t (list (type-specifier-primary type)) functional-p)))
5643 (2 (multiple-value-bind (code functional-p modified first-values second-values)
5644 (make-compiled-two-forms-into-registers (first argument-forms) :eax
5645 (second argument-forms) :ebx
5646 funobj env)
5647 (values code 0 modified
5648 (list (type-specifier-primary (compiler-values-getf first-values :type))
5649 (type-specifier-primary (compiler-values-getf second-values :type)))
5650 functional-p)))
5651 (t (let* ((arguments-self-evaluating-p t)
5652 (arguments-are-load-lexicals-p t)
5653 (arguments-lexical-variables ())
5654 (arguments-modifies nil)
5655 (arguments-functional-p t)
5656 (arguments-types nil)
5657 (producers nil)
5658 (stack-pos 0)
5659 (arguments-code
5660 (loop for form in (nthcdr 2 argument-forms)
5661 appending
5662 (compiler-values-bind (&code code &producer producer &modifies modifies &type type
5663 &functional-p functional-p)
5664 (compiler-call #'compile-form
5665 :form form
5666 :funobj funobj
5667 :env env
5668 :result-mode :push
5669 :with-stack-used (post-incf stack-pos))
5670 ;; (incf (stack-used arg-env))
5671 (unless functional-p
5672 (setf arguments-functional-p nil))
5673 (push producer producers)
5674 (push (type-specifier-primary type)
5675 arguments-types)
5676 (setf arguments-modifies
5677 (modifies-union arguments-modifies modifies))
5678 (case producer
5679 (compile-self-evaluating)
5680 (compile-lexical-variable
5681 (setf arguments-self-evaluating-p nil)
5682 (assert (eq :load-lexical (caar code)) ()
5683 "comp-lex-var produced for ~S~% ~S" form code)
5684 (pushnew (cadar code) arguments-lexical-variables))
5685 (t (setf arguments-self-evaluating-p nil
5686 arguments-are-load-lexicals-p nil)))
5687 code))))
5688 (multiple-value-bind (code01 functionalp01 modifies01 all0 all1)
5689 (make-compiled-two-forms-into-registers (first argument-forms) :eax
5690 (second argument-forms) :ebx
5691 funobj env)
5692 (unless functionalp01
5693 (setf arguments-functional-p nil))
5694 (let ((final0 (compiler-values-getf all0 :final-form))
5695 (final1 (compiler-values-getf all1 :final-form))
5696 (types (list* (type-specifier-primary (compiler-values-getf all0 :type))
5697 (type-specifier-primary (compiler-values-getf all1 :type))
5698 (nreverse arguments-types))))
5699 (cond
5700 ((or arguments-self-evaluating-p
5701 (and (typep final0 'lexical-binding)
5702 (typep final1 'lexical-binding)))
5703 (values (append arguments-code code01)
5704 ;; restore stack..
5705 (+ -2 (length argument-forms))
5707 types
5708 arguments-functional-p))
5709 ((and arguments-are-load-lexicals-p
5710 (typep final0 '(or lexical-binding movitz-object))
5711 (typep final1 '(or lexical-binding movitz-object)))
5712 (values (append arguments-code code01)
5713 (+ -2 (length argument-forms))
5715 types
5716 arguments-functional-p))
5717 ((and arguments-are-load-lexicals-p
5718 (not (some (lambda (arg-binding)
5719 (code-uses-binding-p code01 arg-binding :store t :load nil))
5720 arguments-lexical-variables)))
5721 (values (append arguments-code code01)
5722 (+ -2 (length argument-forms))
5724 types
5725 arguments-functional-p))
5726 (t ;; (warn "fail: ~S by ~S" argument-forms (nreverse producers))
5727 (let ((stack-pos 0))
5728 (values (append (compiler-call #'compile-form
5729 :form (first argument-forms)
5730 :funobj funobj
5731 :env env
5732 :top-level-p nil
5733 :result-mode :push
5734 :with-stack-used (post-incf stack-pos))
5735 ;; (prog1 nil (incf (stack-used arg-env)))
5736 (compiler-call #'compile-form
5737 :form (second argument-forms)
5738 :funobj funobj
5739 :env env
5740 :top-level-p nil
5741 :result-mode :push
5742 :with-stack-used (post-incf stack-pos))
5743 ;; (prog1 nil (incf (stack-used arg-env)))
5744 (loop for form in (nthcdr 2 argument-forms)
5745 appending
5746 (compiler-call #'compile-form
5747 :form form
5748 :funobj funobj
5749 :env env
5750 :result-mode :push
5751 :with-stack-used (post-incf stack-pos)))
5752 `((:movl (:esp ,(* 4 (- (length argument-forms) 1))) :eax)
5753 (:movl (:esp ,(* 4 (- (length argument-forms) 2))) :ebx)))
5754 ;; restore-stack.. don't mess up CF!
5755 (prog1 (length argument-forms)
5756 #+ignore (assert (= (length argument-forms) (stack-used arg-env))))
5757 (modifies-union modifies01 arguments-modifies)
5758 types
5759 arguments-functional-p))))))))))
5761 (defun program-is-load-lexical-of-binding (prg)
5762 (and (not (cdr prg))
5763 (instruction-is-load-lexical-of-binding (car prg))))
5765 (defun instruction-is-load-lexical-of-binding (instruction)
5766 (and (listp instruction)
5767 (eq :load-lexical (car instruction))
5768 (destructuring-bind (binding destination &key &allow-other-keys)
5769 (operands instruction)
5770 (values binding destination))))
5772 (defun make-compiled-two-forms-into-registers (form0 reg0 form1 reg1 funobj env)
5773 "Returns first: code that does form0 into reg0, form1 into reg1.
5774 second: whether code is functional-p,
5775 third: combined set of modified bindings
5776 fourth: all compiler-values for form0, as a list.
5777 fifth: all compiler-values for form1, as a list."
5778 (assert (not (eq reg0 reg1)))
5779 (compiler-values-bind (&all all0 &code code0 &functional-p functional0
5780 &final-form final0 &type type0)
5781 (compiler-call #'compile-form
5782 :form form0
5783 :funobj funobj
5784 :env env
5785 :result-mode reg0)
5786 (compiler-values-bind (&all all1 &code code1 &functional-p functional1
5787 &final-form final1 &type type1)
5788 (compiler-call #'compile-form
5789 :form form1
5790 :funobj funobj
5791 :env env
5792 :result-mode reg1)
5793 (values (cond
5794 ((and (typep final0 'binding)
5795 (not (code-uses-binding-p code1 final0 :load nil :store t)))
5796 (append (compiler-call #'compile-form-unprotected
5797 :form form0
5798 :result-mode :ignore
5799 :funobj funobj
5800 :env env)
5801 code1
5802 `((:load-lexical ,final0 ,reg0 :protect-registers (,reg1)))))
5803 ((program-is-load-lexical-of-binding code1)
5804 (destructuring-bind (src dst &key protect-registers shared-reference-p)
5805 (cdar code1)
5806 (assert (eq reg1 dst))
5807 (append code0
5808 `((:load-lexical ,src ,reg1
5809 :protect-registers ,(union protect-registers
5810 (list reg0))
5811 :shared-reference-p ,shared-reference-p)))))
5812 ;; XXX if we knew that code1 didn't mess up reg0, we could do more..
5813 (t #+ignore (when (and (not (tree-search code1 reg0))
5814 (not (tree-search code1 :call)))
5815 (warn "got b: ~S ~S for ~S: ~{~&~A~}" form0 form1 reg0 code1))
5816 (let ((binding (make-instance 'temporary-name :name (gensym "tmp-")))
5817 (xenv (make-local-movitz-environment env funobj)))
5818 (movitz-env-add-binding xenv binding)
5819 (append (compiler-call #'compile-form
5820 :form form0
5821 :funobj funobj
5822 :env env
5823 :result-mode reg0)
5824 `((:init-lexvar ,binding :init-with-register ,reg0
5825 :init-with-type ,(type-specifier-primary type0)))
5826 (compiler-call #'compile-form
5827 :form form1
5828 :funobj funobj
5829 :env xenv
5830 :result-mode reg1)
5831 `((:load-lexical ,binding ,reg0))))))
5832 (and functional0 functional1)
5834 (compiler-values-list (all0))
5835 (compiler-values-list (all1))))))
5837 (define-compiler compile-symbol (&all all &form form &env env &result-mode result-mode)
5838 "3.1.2.1.1 Symbols as Forms"
5839 (if (movitz-constantp form env)
5840 (compiler-call #'compile-self-evaluating
5841 :forward all
5842 :form (eval-form form env))
5843 (let ((binding (movitz-binding form env)))
5844 (cond
5845 ((typep binding 'lexical-binding)
5846 #+ignore (make-compiled-lexical-variable form binding result-mode env)
5847 (compiler-call #'compile-lexical-variable :forward all))
5848 ((typep binding 'symbol-macro-binding)
5849 (compiler-call #'compile-form-unprotected
5850 :forward all
5851 :form (funcall *movitz-macroexpand-hook*
5852 (macro-binding-expander (movitz-binding form env)) form env)))
5853 (t (compiler-call #'compile-dynamic-variable :forward all))))))
5855 (define-compiler compile-lexical-variable (&form variable &result-mode result-mode &env env)
5856 (let ((binding (movitz-binding variable env)))
5857 (check-type binding lexical-binding)
5858 (case (operator result-mode)
5859 (:ignore
5860 (compiler-values ()
5861 :final-form binding))
5862 (t (compiler-values ()
5863 :code nil
5864 :final-form binding
5865 :returns binding
5866 :functional-p t)))))
5868 (defun make-compiled-lexical-load (binding result-mode &rest key-args)
5869 "Do what is necessary to load lexical binding <binding>."
5870 `((:load-lexical ,binding ,result-mode ,@key-args)))
5872 (define-compiler compile-dynamic-variable (&form form &env env &result-mode result-mode)
5873 "3.1.2.1.1.2 Dynamic Variables"
5874 (if (eq :ignore result-mode)
5875 (compiler-values ())
5876 (let ((binding (movitz-binding form env)))
5877 (cond
5878 ((not binding)
5879 (unless (movitz-env-get form 'special nil env)
5880 (cerror "Compile like a special." "Undeclared variable: ~S." form))
5881 (compiler-values ()
5882 :returns :eax
5883 :functional-p t
5884 :modifies nil
5885 :final-form form
5886 :code (if *compiler-use-into-unbound-protocol*
5887 `((:load-constant ,form :ebx)
5888 (,*compiler-local-segment-prefix*
5889 :call (:edi ,(global-constant-offset 'dynamic-variable-lookup)))
5890 (:cmpl -1 :eax)
5891 (:into))
5892 (let ((not-unbound (gensym "not-unbound-")))
5893 `((:load-constant ,form :ebx)
5894 (,*compiler-local-segment-prefix*
5895 :call (:edi ,(global-constant-offset 'dynamic-variable-lookup)))
5896 (,*compiler-local-segment-prefix*
5897 :cmpl :eax (:edi ,(global-constant-offset 'unbound-value)))
5898 (:jne ',not-unbound)
5899 (:int 99)
5900 ,not-unbound)))))
5901 (t (check-type binding dynamic-binding)
5902 (compiler-values ()
5903 :returns :eax
5904 :functional-p t
5905 :modifies nil
5906 :final-form form
5907 :code (if *compiler-use-into-unbound-protocol*
5908 `((:load-constant ,form :ebx)
5909 (,*compiler-local-segment-prefix*
5910 :call (:edi ,(global-constant-offset 'dynamic-variable-lookup)))
5911 (:cmpl -1 :eax)
5912 (:into))
5913 (let ((not-unbound (gensym "not-unbound-")))
5914 `((:load-constant ,form :ebx)
5915 (,*compiler-local-segment-prefix*
5916 :call (:edi ,(global-constant-offset 'dynamic-variable-lookup)))
5917 (,*compiler-local-segment-prefix*
5918 :cmpl :eax (:edi ,(global-constant-offset 'unbound-value)))
5919 (:jne ',not-unbound)
5920 (:int 99)
5921 ,not-unbound)))))))))
5923 (define-compiler compile-lambda-form (&form form &all all)
5924 "3.1.2.2.4 Lambda Forms"
5925 (let ((lambda-expression (car form))
5926 (lambda-args (cdr form)))
5927 (compiler-call #'compile-form-unprotected
5928 :forward all
5929 :form `(muerte.cl:funcall ,lambda-expression ,@lambda-args))))
5931 (define-compiler compile-constant-compound (&all all &form form &env env &top-level-p top-level-p)
5932 (compiler-call #'compile-self-evaluating
5933 :forward all
5934 :form (eval-form form env top-level-p)))
5936 (defun register32-to-low8 (register)
5937 (ecase register
5938 (:eax :al)
5939 (:ebx :bl)
5940 (:ecx :cl)
5941 (:edx :dl)))
5943 (defun make-immediate-move (value destination-register)
5944 (cond
5945 ((zerop value)
5946 `((:xorl ,destination-register ,destination-register)))
5947 ((= value (image-nil-word *image*))
5948 `((:movl :edi ,destination-register)))
5949 ((<= #x-80 (- value (image-nil-word *image*)) #x7f)
5950 `((:leal (:edi ,(- value (image-nil-word *image*))) ,destination-register)))
5951 ((<= #x-80 (- value (* 2 (image-nil-word *image*))) #x7f)
5952 `((:leal (:edi (:edi 1) ,(- value (* 2 (image-nil-word *image*)))) ,destination-register)))
5953 ((<= #x-80 (- value (* 3 (image-nil-word *image*))) #x7f)
5954 `((:leal (:edi (:edi 2) ,(- value (* 3 (image-nil-word *image*)))) ,destination-register)))
5955 ((<= #x-80 (- value (* 5 (image-nil-word *image*))) #x7f)
5956 `((:leal (:edi (:edi 4) ,(- value (* 5 (image-nil-word *image*)))) ,destination-register)))
5957 ((<= #x-80 (- value (* 9 (image-nil-word *image*))) #x7f)
5958 `((:leal (:edi (:edi 8) ,(- value (* 9 (image-nil-word *image*)))) ,destination-register)))
5959 ((<= 0 value #xff)
5960 `((:xorl ,destination-register ,destination-register)
5961 (:movb ,value ,(register32-to-low8 destination-register))))
5962 (t `((:movl ,value ,destination-register)))))
5964 (defparameter *prev-self-eval* nil)
5966 (define-compiler compile-self-evaluating (&form form &result-mode result-mode &funobj funobj)
5967 "3.1.2.1.3 Self-Evaluating Objects"
5968 (let* ((object form)
5969 (movitz-obj (image-read-intern-constant *image* object))
5970 (funobj-env (funobj-env funobj))
5971 (binding (or (cdr (assoc movitz-obj (movitz-environment-bindings funobj-env)))
5972 (let ((binding (make-instance 'constant-object-binding
5973 :name (gensym "self-eval-")
5974 :object movitz-obj)))
5975 (setf (binding-env binding) funobj-env)
5976 (push (cons movitz-obj binding)
5977 (movitz-environment-bindings funobj-env))
5978 binding))))
5979 (compiler-values-bind (&all self-eval)
5980 (compiler-values (nil :abstract t)
5981 :producer (default-compiler-values-producer)
5982 :type `(eql ,movitz-obj)
5983 :final-form binding
5984 :functional-p t)
5985 (case (operator result-mode)
5986 (:ignore
5987 (compiler-values (self-eval)
5988 :returns :nothing
5989 :type nil))
5990 (t (compiler-values (self-eval)
5991 :returns binding))))))
5993 (define-compiler compile-implicit-progn (&all all &form forms &top-level-p top-level-p
5994 &result-mode result-mode)
5995 "Compile all the elements of the list <forms> as a progn."
5996 (check-type forms list)
5997 (case (length forms)
5998 (0 (compiler-values ()))
5999 (1 (compiler-call #'compile-form-unprotected
6000 :forward all
6001 :form (first forms)))
6002 (t (loop with no-side-effects-p = t
6003 with progn-codes = nil
6004 for (sub-form . more-forms-p) on forms
6005 as current-result-mode = (if more-forms-p :ignore result-mode)
6006 do (compiler-values-bind (&code code &returns sub-returns-mode
6007 &functional-p no-sub-side-effects-p
6008 &type type &final-form final-form &producer sub-producer)
6009 (compiler-call (if (not more-forms-p)
6010 #'compile-form-unprotected
6011 #'compile-form)
6012 :defaults all
6013 :form sub-form
6014 :top-level-p top-level-p
6015 :result-mode current-result-mode)
6016 (assert sub-returns-mode ()
6017 "~S produced no returns-mode for form ~S." sub-producer sub-form)
6018 (unless no-sub-side-effects-p
6019 (setf no-side-effects-p nil))
6020 (push (if (and no-sub-side-effects-p (eq current-result-mode :ignore))
6022 code)
6023 progn-codes)
6024 (when (not more-forms-p)
6025 (return (compiler-values ()
6026 :returns sub-returns-mode
6027 :functional-p no-side-effects-p
6028 :final-form final-form
6029 :type type
6030 :code (reduce #'append (nreverse progn-codes))))))))))
6033 (defun new-make-compiled-constant-reference (obj funobj)
6034 (let ((movitz-obj (movitz-read obj)))
6035 (if (eq movitz-obj (image-t-symbol *image*))
6036 (make-indirect-reference :edi (global-constant-offset 't-symbol))
6037 (etypecase movitz-obj
6038 (movitz-null :edi)
6039 (movitz-immediate-object (movitz-immediate-value movitz-obj))
6040 (movitz-heap-object
6041 (make-indirect-reference :esi (movitz-funobj-intern-constant funobj movitz-obj)))))))
6043 (defun make-compiled-lexical-control-transfer (return-code return-mode from-env to-env
6044 &optional (to-label (exit-label to-env)))
6045 "<return-code> running in <from-env> produces <return-mode>, and we need to
6046 generate code that transfers control (and unwinds dynamic bindings, runs unwind-protect
6047 cleanup-forms etc.) to <to-env> with <return-code>'s result intact."
6048 (check-type to-env lexical-exit-point-env)
6049 (multiple-value-bind (stack-distance num-dynamic-slots unwind-protects)
6050 (stack-delta from-env to-env)
6051 (assert stack-distance)
6052 (assert (null unwind-protects) ()
6053 "Lexical unwind-protect not implemented, to-env: ~S. (this is not supposed to happen)"
6054 to-env)
6055 ;; (warn "dist: ~S, slots: ~S" stack-distance num-dynamic-slots)
6056 (assert (not (eq t num-dynamic-slots)) ()
6057 "Don't know how to make lexical-control-transfer across unknown number of dynamic slots.")
6058 (cond
6059 ((and (eq t stack-distance)
6060 (eql 0 num-dynamic-slots))
6061 (compiler-values ()
6062 :returns :non-local-exit
6063 :code (append return-code
6064 (unless (eq :function (exit-result-mode to-env))
6065 `((:load-lexical ,(movitz-binding (save-esp-variable to-env) to-env nil) :esp)))
6066 `((:jmp ',to-label)))))
6067 ((eq t stack-distance)
6068 (compiler-values ()
6069 :returns :non-local-exit
6070 :code (append return-code
6071 (compiler-call #'special-operator-with-cloak
6072 :env to-env
6073 :result-mode (exit-result-mode to-env)
6074 :form `(muerte::with-cloak (,return-mode)
6075 (muerte::with-inline-assembly (:returns :nothing)
6076 ;; Compute target dynamic-env
6077 (:locally (:movl (:edi (:edi-offset dynamic-env)) :eax))
6078 ,@(loop repeat num-dynamic-slots
6079 collect `(:movl (:eax 12) :eax))
6080 (:locally (:call (:edi (:edi-offset dynamic-unwind-next))))
6081 (:locally (:movl :eax (:edi (:edi-offset dynamic-env))))
6082 (:jc '(:sub-program () (:int 63))))))
6083 `((:load-lexical ,(movitz-binding (save-esp-variable to-env) to-env nil) :esp)
6084 (:jmp ',to-label)))))
6085 ((zerop num-dynamic-slots)
6086 (compiler-values ()
6087 :returns :non-local-exit
6088 :code (append return-code
6089 (make-compiled-stack-restore stack-distance
6090 (exit-result-mode to-env)
6091 return-mode)
6092 `((:jmp ',to-label)))))
6093 ((plusp num-dynamic-slots)
6094 ;; (warn "num-dynamic-slots: ~S, distance: ~D" num-dynamic-slots stack-distance)
6095 (compiler-values ()
6096 :returns :non-local-exit
6097 :code (append return-code
6098 (compiler-call #'special-operator-with-cloak
6099 :env to-env
6100 :result-mode (exit-result-mode to-env)
6101 :form `(muerte::with-cloak (,return-mode)
6102 (muerte::with-inline-assembly (:returns :nothing)
6103 ;; Compute target dynamic-env
6104 (:locally (:movl (:edi (:edi-offset dynamic-env)) :eax))
6105 ,@(loop repeat num-dynamic-slots
6106 collect `(:movl (:eax 12) :eax))
6107 (:locally (:call (:edi (:edi-offset dynamic-unwind-next))))
6108 (:locally (:movl :eax (:edi (:edi-offset dynamic-env))))
6109 (:jc '(:sub-program () (:int 63))))))
6110 `((:leal (:esp ,(* 4 stack-distance)) :esp)
6111 (:jmp ',to-label)))))
6112 (t (error "unknown!")))))
6114 (defun make-compiled-push-current-values ()
6115 "Return code that pushes the current values onto the stack, and returns
6116 in ECX the number of values (as fixnum)."
6117 (let ((not-single-value (gensym "not-single-value-"))
6118 (push-values-done (gensym "push-values-done-"))
6119 (push-values-loop (gensym "push-values-loop-")))
6120 `((:jc ',not-single-value)
6121 (:movl 4 :ecx)
6122 (:pushl :eax)
6123 (:jmp ',push-values-done)
6124 ,not-single-value
6125 (:shll ,+movitz-fixnum-shift+ :ecx)
6126 (:jz ',push-values-done)
6127 (:xorl :edx :edx)
6128 (:pushl :eax)
6129 (:addl 4 :edx)
6130 (:cmpl :edx :ecx)
6131 (:je ',push-values-done)
6132 (:pushl :ebx)
6133 (:addl 4 :edx)
6134 (:cmpl :edx :ecx)
6135 (:je ',push-values-done)
6136 ,push-values-loop
6137 (:locally (:pushl (:edi (:edi-offset values) :edx -8)))
6138 (:addl 4 :edx)
6139 (:cmpl :edx :ecx)
6140 (:jne ',push-values-loop)
6141 ,push-values-done)))
6143 (defun stack-add (x y)
6144 (if (and (integerp x) (integerp y))
6145 (+ x y)
6148 (define-modify-macro stack-incf (&optional (delta 1)) stack-add)
6150 (defun stack-delta (inner-env outer-env)
6151 "Calculate the amount of stack-space used (in 32-bit stack slots) at the time
6152 of <inner-env> since <outer-env>,
6153 the number of intervening dynamic-slots (special bindings, unwind-protects, and catch-tags),
6154 and a list of any intervening unwind-protect environment-slots."
6155 (labels
6156 ((find-stack-delta (env stack-distance num-dynamic-slots unwind-protects)
6157 #+ignore (warn "find-stack-delta: ~S dist ~S, slots ~S" env
6158 (stack-used env) (num-dynamic-slots env))
6159 (cond
6160 ((eq outer-env env)
6161 ;; Each dynamic-slot is 4 stack-distances, so let's check that..
6162 (assert (or (eq t stack-distance)
6163 (>= stack-distance (* 4 num-dynamic-slots))) ()
6164 "The stack-distance ~D is smaller than number of dynamic-slots ~D, which is inconsistent."
6165 stack-distance num-dynamic-slots)
6166 (values stack-distance num-dynamic-slots unwind-protects))
6167 ((null env)
6168 (values nil 0 nil))
6169 (t (find-stack-delta (movitz-environment-uplink env)
6170 (stack-add stack-distance (stack-used env))
6171 (stack-add num-dynamic-slots (num-dynamic-slots env))
6172 (if (typep env 'unwind-protect-env)
6173 (cons env unwind-protects)
6174 unwind-protects))))))
6175 (find-stack-delta inner-env 0 0 nil)))
6177 (defun print-stack-delta (inner-env outer-env)
6178 (labels ((print-stack-delta (env)
6179 (cond
6180 ((or (eq outer-env env)
6181 (null env)))
6182 (t (format t "~&Env: ~S used: ~S, slots: ~S"
6183 env (stack-used env) (num-dynamic-slots env))
6184 (print-stack-delta (movitz-environment-uplink env))))))
6185 (print-stack-delta inner-env)))
6187 ;;;;;;;
6188 ;;;;;;; Extended-code declarations
6189 ;;;;;;;
6191 (defvar *extended-code-find-read-binding*
6192 (make-hash-table :test #'eq))
6194 (defvar *extended-code-find-used-bindings*
6195 (make-hash-table :test #'eq))
6197 (defmacro define-find-read-bindings (name lambda-list &body body)
6198 (let ((defun-name (intern
6199 (with-standard-io-syntax
6200 (format nil "~A-~A" 'find-read-bindings name)))))
6201 `(progn
6202 (setf (gethash ',name *extended-code-find-read-binding*) ',defun-name)
6203 (defun ,defun-name (instruction)
6204 (destructuring-bind ,lambda-list
6205 (cdr instruction)
6206 ,@body)))))
6208 (defmacro define-find-used-bindings (name lambda-list &body body)
6209 (let ((defun-name (intern
6210 (with-standard-io-syntax
6211 (format nil "~A-~A" 'find-used-bindings name)))))
6212 `(progn
6213 (setf (gethash ',name *extended-code-find-used-bindings*) ',defun-name)
6214 (defun ,defun-name (instruction)
6215 (destructuring-bind ,lambda-list
6216 (cdr instruction)
6217 ,@body)))))
6219 (defun find-used-bindings (extended-instruction)
6220 "Return zero, one or two bindings that this instruction reads."
6221 (when (listp extended-instruction)
6222 (let* ((operator (car extended-instruction))
6223 (finder (or (gethash operator *extended-code-find-used-bindings*)
6224 (gethash operator *extended-code-find-read-binding*))))
6225 (when finder
6226 (let ((result (funcall finder extended-instruction)))
6227 (check-type result list "a list of read bindings")
6228 result)))))
6230 (defun find-read-bindings (extended-instruction)
6231 "Return zero, one or two bindings that this instruction reads."
6232 (when (listp extended-instruction)
6233 (let* ((operator (car extended-instruction))
6234 (finder (gethash operator *extended-code-find-read-binding*)))
6235 (when finder
6236 (funcall finder extended-instruction)))))
6238 (defmacro define-find-write-binding-and-type (name lambda-list &body body)
6239 (let ((defun-name (intern
6240 (with-standard-io-syntax
6241 (format nil "~A-~A" 'find-write-binding-and-type name)))))
6242 `(progn
6243 (setf (gethash ',name *extended-code-find-write-binding-and-type*) ',defun-name)
6244 (defun ,defun-name ,lambda-list ,@body))))
6246 (defun find-written-binding-and-type (extended-instruction)
6247 (when (listp extended-instruction)
6248 (let* ((operator (car extended-instruction))
6249 (finder (gethash operator *extended-code-find-write-binding-and-type*)))
6250 (when finder
6251 (funcall finder extended-instruction)))))
6253 (defmacro define-extended-code-expander (name lambda-list &body body)
6254 (let ((defun-name (intern
6255 (with-standard-io-syntax
6256 (format nil "~A-~A" 'extended-code-expander- name)))))
6257 `(progn
6258 (setf (gethash ',name *extended-code-expanders*) ',defun-name)
6259 (defun ,defun-name ,lambda-list ,@body))))
6261 (defun can-expand-extended-p (extended-instruction frame-map)
6262 "Given frame-map, can we expand i at this point?"
6263 (and (every (lambda (b)
6264 (or (typep (binding-target b) 'constant-object-binding)
6265 (new-binding-located-p (binding-target b) frame-map)))
6266 (find-read-bindings extended-instruction))
6267 (let ((written-binding (find-written-binding-and-type extended-instruction)))
6268 (or (not written-binding)
6269 (new-binding-located-p (binding-target written-binding) frame-map)))))
6271 (defun expand-extended-code (extended-instruction funobj frame-map)
6272 (if (not (listp extended-instruction))
6273 (list extended-instruction)
6274 (let* ((operator (car extended-instruction))
6275 (expander (gethash operator *extended-code-expanders*)))
6276 (if (not expander)
6277 (list extended-instruction)
6278 (let ((expansion (funcall expander extended-instruction funobj frame-map)))
6279 (mapcan (lambda (e)
6280 (expand-extended-code e funobj frame-map))
6281 expansion))))))
6283 (defun ensure-local-binding (binding funobj)
6284 "When referencing binding in funobj, ensure we have the binding local to funobj."
6285 (if (typep binding '(or (not binding) constant-object-binding))
6286 binding ; Never mind if "binding" isn't a binding, or is a constant-binding.
6287 (let ((target-binding (binding-target binding)))
6288 (cond
6289 ((eq funobj (binding-funobj target-binding))
6290 binding)
6291 (t (or (find target-binding (borrowed-bindings funobj)
6292 :key (lambda (binding)
6293 (borrowed-binding-target binding)))
6294 (error "Can't install non-local binding ~W." binding)))))))
6296 (defun binding-store-subtypep (binding type-specifier)
6297 "Is type-specifier a supertype of all values ever stored to binding?
6298 (Assuming analyze-bindings has put this information into binding-store-type.)"
6299 (if (not (binding-store-type binding))
6301 (multiple-value-call #'encoded-subtypep
6302 (values-list (binding-store-type binding))
6303 (type-specifier-encode type-specifier))))
6305 (defun binding-singleton (binding)
6306 (let ((btype (binding-store-type binding)))
6307 (when btype
6308 (type-specifier-singleton (apply #'encoded-type-decode btype)))))
6310 ;;;;;;;
6311 ;;;;;;; Extended-code handlers
6312 ;;;;;;;
6315 ;;;;;;;;;;;;;;;;;; Load-lexical
6317 (define-find-write-binding-and-type :load-lexical (instruction)
6318 (destructuring-bind (source destination &key &allow-other-keys)
6319 (cdr instruction)
6320 (when (typep destination 'binding)
6321 (values destination t #+ignore (binding-type-specifier source)
6322 (lambda (source-type)
6323 source-type)
6324 (list source)))))
6326 (define-find-read-bindings :load-lexical (source destination &key &allow-other-keys)
6327 (check-type source binding)
6328 (values (list source)
6329 (list destination)))
6331 (define-extended-code-expander :load-lexical (instruction funobj frame-map)
6332 (destructuring-bind (source destination &key shared-reference-p tmp-register protect-registers)
6333 (cdr instruction)
6334 (make-load-lexical (ensure-local-binding source funobj)
6335 (ensure-local-binding destination funobj)
6336 funobj shared-reference-p frame-map
6337 :tmp-register tmp-register
6338 :protect-registers protect-registers)))
6341 ;;;;;;;;;;;;;;;;;; Lisp-move
6343 (define-find-write-binding-and-type :lmove (instruction)
6344 (destructuring-bind (source destination)
6345 (cdr instruction)
6346 (values destination source)))
6348 (define-find-read-bindings :lmove (source destination)
6349 (declare (ignore destination))
6350 (list source))
6352 ;;;;;;;;;;;;;;;;;; Store-lexical
6354 (define-find-write-binding-and-type :store-lexical (instruction)
6355 (destructuring-bind (destination source &key (type (error "No type")) &allow-other-keys)
6356 (cdr instruction)
6357 (declare (ignore source))
6358 (check-type destination binding)
6359 (values destination type)))
6361 (define-find-read-bindings :store-lexical (destination source &key &allow-other-keys)
6362 (declare (ignore destination))
6363 (when (typep source 'binding)
6364 (list source)))
6366 (define-extended-code-expander :store-lexical (instruction funobj frame-map)
6367 (destructuring-bind (destination source &key shared-reference-p type protect-registers)
6368 (cdr instruction)
6369 (declare (ignore type))
6370 (make-store-lexical (ensure-local-binding destination funobj)
6371 (ensure-local-binding source funobj)
6372 shared-reference-p funobj frame-map
6373 :protect-registers protect-registers)))
6375 ;;;;;;;;;;;;;;;;;; Init-lexvar
6377 (define-find-write-binding-and-type :init-lexvar (instruction)
6378 (destructuring-bind (binding &key init-with-register init-with-type
6379 protect-registers protect-carry
6380 shared-reference-p)
6381 (cdr instruction)
6382 (declare (ignore protect-registers protect-carry shared-reference-p))
6383 (cond
6384 (init-with-register
6385 (cond
6386 ((not (typep init-with-register 'binding))
6387 (assert init-with-type)
6388 (values binding init-with-type) )
6389 ((and init-with-type (not (bindingp init-with-type)))
6390 (values binding init-with-type))
6391 ((and init-with-type
6392 (bindingp init-with-type)
6393 (binding-store-type init-with-type))
6394 (apply #'encoded-type-decode (binding-store-type init-with-type)))
6395 (t (values binding t
6396 (lambda (x) x)
6397 (list init-with-register)))))
6398 ((not (typep binding 'temporary-name))
6399 (values binding t)))))
6401 (define-find-read-bindings :init-lexvar (binding &key init-with-register &allow-other-keys)
6402 (declare (ignore binding))
6403 (when (typep init-with-register 'binding)
6404 (list init-with-register)))
6406 (define-extended-code-expander :init-lexvar (instruction funobj frame-map)
6407 (destructuring-bind (binding &key protect-registers protect-carry
6408 init-with-register init-with-type
6409 shared-reference-p)
6410 (cdr instruction)
6411 (declare (ignore protect-carry)) ; nothing modifies carry anyway.
6412 ;; (assert (eq binding (ensure-local-binding binding funobj)))
6413 (assert (eq funobj (binding-funobj binding)))
6414 (cond
6415 ((not (new-binding-located-p binding frame-map))
6416 (unless (or (movitz-env-get (binding-name binding) 'ignore nil (binding-env binding))
6417 (movitz-env-get (binding-name binding) 'ignorable nil (binding-env binding)))))
6418 ((typep binding 'forwarding-binding)
6419 ;; No need to do any initialization because the target will be initialized.
6420 (assert (not (binding-lended-p binding)))
6421 nil)
6422 (t (when (movitz-env-get (binding-name binding) 'ignore nil (binding-env binding))
6423 (warn "Variable ~S used while declared ignored." (binding-name binding)))
6424 (append
6425 (cond
6426 ((typep binding 'rest-function-argument)
6427 (assert (eq :edx init-with-register))
6428 (assert (movitz-env-get (binding-name binding)
6429 'dynamic-extent nil (binding-env binding))
6431 "&REST variable ~S must be dynamic-extent." (binding-name binding))
6432 (setf (need-normalized-ecx-p (find-function-env (binding-env binding)
6433 funobj))
6435 (let ((restify-alloca-loop (gensym "alloca-loop-"))
6436 (restify-done (gensym "restify-done-"))
6437 (restify-at-one (gensym "restify-at-one-"))
6438 (restify-loop (gensym "restify-loop-"))
6439 (save-ecx-p (key-vars-p (find-function-env (binding-env binding)
6440 funobj))))
6441 (append
6442 ;; (make-immediate-move (function-argument-argnum binding) :edx)
6443 ;; `((:call (:edi ,(global-constant-offset 'restify-dynamic-extent))))
6444 ;; Make space for (1+ (* 2 (- ECX rest-pos))) words on the stack.
6445 ;; Factor two is for one cons-cell per word, 1 is for 8-byte alignment.
6446 (when save-ecx-p
6447 `((,*compiler-local-segment-prefix*
6448 :movl :ecx (:edi ,(global-constant-offset 'raw-scratch0)))))
6449 `((:movl :edi :edx)
6450 (:subl ,(function-argument-argnum binding) :ecx)
6451 (:jbe ',restify-done)
6452 (:leal ((:ecx 8) 4) :edx) ; EDX is fixnum counter
6453 ,restify-alloca-loop
6454 (:pushl :edi)
6455 (:subl 4 :edx)
6456 (:jnz ',restify-alloca-loop)
6457 ,@(when *compiler-auto-stack-checks-p*
6458 `((,*compiler-local-segment-prefix*
6459 :bound (:edi ,(global-constant-offset 'stack-bottom)) :esp)))
6460 (:leal (:esp 5) :edx)
6461 (:andl -7 :edx)) ; Make EDX a proper consp into the alloca area.
6462 (cond
6463 ((= 0 (function-argument-argnum binding))
6464 `((:movl :eax (:edx -1))
6465 (:movl :edx :eax)
6466 (:subl 1 :ecx)
6467 (:jz ',restify-done)
6468 (:addl 8 :eax)
6469 (:movl :eax (:eax -5))))
6470 (t `((:movl :edx :eax))))
6471 (when (>= 1 (function-argument-argnum binding))
6472 `((:jmp ',restify-at-one)))
6473 `(,restify-loop
6474 (:movl (:ebp (:ecx 4) 4) :ebx)
6475 ,restify-at-one
6476 (:movl :ebx (:eax -1))
6477 (:subl 1 :ecx)
6478 (:jz ',restify-done)
6479 (:addl 8 :eax)
6480 (:movl :eax (:eax -5))
6481 (:jmp ',restify-loop)
6482 ,restify-done)
6483 (when save-ecx-p
6484 `((,*compiler-local-segment-prefix*
6485 :movl (:edi ,(global-constant-offset 'raw-scratch0)) :ecx)))
6486 ))))
6487 (cond
6488 ((binding-lended-p binding)
6489 (let* ((cons-position (getf (binding-lending binding)
6490 :stack-cons-location))
6491 (init-register (etypecase init-with-register
6492 ((or lexical-binding constant-object-binding)
6493 (or (find-if (lambda (r)
6494 (not (member r protect-registers)))
6495 '(:edx :ebx :eax))
6496 (error "Unable to get a register.")))
6497 (keyword init-with-register)
6498 (null :edi)))
6499 (tmp-register (find-if (lambda (r)
6500 (and (not (member r protect-registers))
6501 (not (eq r init-register))))
6502 '(:edx :ebx :eax))))
6503 (when init-with-register
6504 (assert (not (null init-with-type))))
6505 (assert tmp-register () ; solve this with push eax .. pop eax if ever needed.
6506 "Unable to find a tmp-register for ~S." instruction)
6507 (append (when (typep init-with-register 'binding)
6508 (make-load-lexical init-with-register init-register funobj
6509 shared-reference-p frame-map
6510 :protect-registers protect-registers))
6511 `((:leal (:ebp ,(1+ (stack-frame-offset (1+ cons-position))))
6512 ,tmp-register)
6513 (:movl :edi (,tmp-register 3)) ; cdr
6514 (:movl ,init-register (,tmp-register -1)) ; car
6515 (:movl ,tmp-register
6516 (:ebp ,(stack-frame-offset
6517 (new-binding-location binding frame-map))))))))
6518 ((typep init-with-register 'lexical-binding)
6519 (make-load-lexical init-with-register binding funobj nil frame-map))
6520 (init-with-register
6521 (make-store-lexical binding init-with-register nil funobj frame-map))))))))
6523 ;;;;;;;;;;;;;;;;;; car
6525 (define-find-read-bindings :cons-get (op cell dst)
6526 (declare (ignore op dst protect-registers))
6527 (when (typep cell 'binding)
6528 (list cell)))
6530 (define-extended-code-expander :cons-get (instruction funobj frame-map)
6531 (destructuring-bind (op cell dst)
6532 (cdr instruction)
6533 (check-type dst (member :eax :ebx :ecx :edx))
6534 (multiple-value-bind (op-offset fast-op fast-op-ebx cl-op)
6535 (ecase op
6536 (:car (values (bt:slot-offset 'movitz-cons 'car)
6537 'fast-car
6538 'fast-car-ebx
6539 'movitz-car))
6540 (:cdr (values (bt:slot-offset 'movitz-cons 'cdr)
6541 'fast-cdr
6542 'fast-cdr-ebx
6543 'movitz-cdr)))
6544 (let ((binding (binding-target (ensure-local-binding (binding-target cell) funobj))))
6545 (etypecase binding
6546 (constant-object-binding
6547 (let ((x (constant-object binding)))
6548 (typecase x
6549 (movitz-null
6550 (make-load-constant *movitz-nil* dst funobj frame-map))
6551 (movitz-cons
6552 (append (make-load-constant x dst funobj frame-map)
6553 `((:movl (,dst ,op-offset) ,dst))))
6554 (t `(,@(make-load-lexical binding :eax funobj nil frame-map)
6555 (,*compiler-global-segment-prefix*
6556 :call (:edi ,(global-constant-offset fast-op)))
6557 ,@(when (not (eq dst :eax))
6558 `((:movl :eax ,dst))))))))
6559 (lexical-binding
6560 (let ((location (new-binding-location (binding-target binding) frame-map))
6561 (binding-is-list-p (binding-store-subtypep binding 'list)))
6562 #+ignore (warn "~A of loc ~A bind ~A" op location binding)
6563 (cond
6564 ((and binding-is-list-p
6565 (member location '(:eax :ebx :ecx :edx)))
6566 `((,*compiler-nonlocal-lispval-read-segment-prefix*
6567 :movl (,location ,op-offset) ,dst)))
6568 (binding-is-list-p
6569 `(,@(make-load-lexical binding dst funobj nil frame-map)
6570 (,*compiler-nonlocal-lispval-read-segment-prefix*
6571 :movl (,dst ,op-offset) ,dst)))
6572 ((not *compiler-use-cons-reader-segment-protocol-p*)
6573 (cond
6574 ((eq location :ebx)
6575 `((,*compiler-global-segment-prefix*
6576 :call (:edi ,(global-constant-offset fast-op-ebx)))
6577 ,@(when (not (eq dst :eax))
6578 `((:movl :eax ,dst)))))
6579 (t `(,@(make-load-lexical binding :eax funobj nil frame-map)
6580 (,*compiler-global-segment-prefix*
6581 :call (:edi ,(global-constant-offset fast-op)))
6582 ,@(when (not (eq dst :eax))
6583 `((:movl :eax ,dst)))))))
6584 (t (cond
6585 ((member location '(:ebx :ecx :edx))
6586 `((,(or *compiler-cons-read-segment-prefix*
6587 *compiler-nonlocal-lispval-read-segment-prefix*)
6588 :movl (:eax ,op-offset) ,dst)))
6589 (t (append (make-load-lexical binding :eax funobj nil frame-map)
6590 `((,(or *compiler-cons-read-segment-prefix*
6591 *compiler-nonlocal-lispval-read-segment-prefix*)
6592 :movl (:eax ,op-offset) ,dst))))))))))))))
6595 ;;;;;;;;;;;;;;;;;; endp
6597 (define-find-read-bindings :endp (cell result-mode)
6598 (declare (ignore result-mode))
6599 (when (typep cell 'binding)
6600 (list cell)))
6602 (define-extended-code-expander :endp (instruction funobj frame-map)
6603 (destructuring-bind (cell result-mode)
6604 (cdr instruction)
6605 (let ((binding (binding-target (ensure-local-binding (binding-target cell) funobj))))
6606 (etypecase binding
6607 (constant-object-binding
6608 (let ((x (constant-object binding)))
6609 (typecase x
6610 (movitz-cons
6611 (make-load-constant *movitz-nil* result-mode funobj frame-map))
6612 (movitz-null
6613 (make-load-constant (image-t-symbol *image*) result-mode funobj frame-map))
6614 (t '((:int 61))))))
6615 (lexical-binding
6616 (let* ((location (new-binding-location (binding-target binding) frame-map))
6617 (binding-is-list-p (binding-store-subtypep binding 'list))
6618 (tmp-register (case location
6619 ((:eax :ebx :ecx :edx)
6620 location))))
6621 ;; (warn "endp of loc ~A bind ~A" location binding)
6622 (cond
6623 ((and binding-is-list-p
6624 (member location '(:eax :ebx :ecx :edx)))
6625 (make-result-and-returns-glue result-mode :boolean-zf=1
6626 `((:cmpl :edi ,location))))
6627 ((eq :boolean-branch-on-true (result-mode-type result-mode))
6628 (let ((tmp-register (or tmp-register :ecx)))
6629 (append (make-load-lexical binding
6630 (cons :boolean-branch-on-false
6631 (cdr result-mode))
6632 funobj nil frame-map)
6633 (unless binding-is-list-p
6634 (append (make-load-lexical binding tmp-register funobj nil frame-map)
6635 `((:leal (,tmp-register -1) :ecx)
6636 (:testb 3 :cl)
6637 (:jnz '(:sub-program (,(gensym "endp-not-list-"))
6638 (:int 61)))))))))
6639 (t (let ((tmp-register (or tmp-register :eax)))
6640 (append (make-load-lexical binding tmp-register funobj nil frame-map)
6641 (unless binding-is-list-p
6642 `((:leal (,tmp-register -1) :ecx)
6643 (:testb 3 :cl)
6644 (:jnz '(:sub-program (,(gensym "endp-not-list-"))
6645 (:int 61)))))
6646 `((:cmpl :edi ,tmp-register))
6647 (make-result-and-returns-glue result-mode :boolean-zf=1)))))))))))
6650 ;;;;;;;;;;;;;;;;;; incf-lexvar
6652 (define-find-write-binding-and-type :incf-lexvar (instruction)
6653 (destructuring-bind (binding delta &key protect-registers)
6654 (cdr instruction)
6655 (declare (ignore delta protect-registers))
6656 (values binding 'integer)))
6658 (define-find-read-bindings :incf-lexvar (binding delta &key protect-registers)
6659 (declare (ignore delta protect-registers binding))
6660 nil)
6662 (define-extended-code-expander :incf-lexvar (instruction funobj frame-map)
6663 (break "incf-lexvar??")
6664 (destructuring-bind (binding delta &key protect-registers)
6665 (cdr instruction)
6666 (check-type binding binding)
6667 (check-type delta integer)
6668 (let* ((binding (binding-target binding))
6669 (location (new-binding-location binding frame-map :default nil))
6670 (binding-type (binding-store-type binding)))
6671 ;;; (warn "incf b ~A, loc: ~A, typ: ~A" binding location binding-type)
6672 (cond
6673 ((and binding-type
6674 location
6675 (not (binding-lended-p binding))
6676 (binding-store-subtypep binding 'integer))
6677 ;; This is an optimized incf that doesn't have to do type-checking.
6678 (check-type location (integer 1 *))
6679 `((:addl ,(* delta +movitz-fixnum-factor+)
6680 (:ebp ,(stack-frame-offset location)))
6681 (:into)))
6682 ((binding-store-subtypep binding 'integer)
6683 (let ((register (chose-free-register protect-registers)))
6684 `(,@(make-load-lexical (ensure-local-binding binding funobj)
6685 register funobj nil frame-map
6686 :protect-registers protect-registers)
6687 (:addl ,(* delta +movitz-fixnum-factor+) :eax)
6688 (:into)
6689 ,@(make-store-lexical (ensure-local-binding binding funobj)
6690 register nil funobj frame-map
6691 :protect-registers protect-registers))))
6692 (t (let ((register (chose-free-register protect-registers)))
6693 `(,@(make-load-lexical (ensure-local-binding binding funobj)
6694 register funobj nil frame-map
6695 :protect-registers protect-registers)
6696 (:testb ,+movitz-fixnum-zmask+ ,(register32-to-low8 register))
6697 (:jnz '(:sub-program (,(gensym "not-integer-"))
6698 (:int 107)
6699 (:jmp (:pc+ -4))))
6700 (:addl ,(* delta +movitz-fixnum-factor+) ,register)
6701 (:into)
6702 ,@(make-store-lexical (ensure-local-binding binding funobj)
6703 register nil funobj frame-map
6704 :protect-registers protect-registers))))))))
6706 ;;;;; Load-constant
6708 (define-find-write-binding-and-type :load-constant (instruction)
6709 (destructuring-bind (object result-mode &key (op :movl))
6710 (cdr instruction)
6711 (when (and (eq op :movl) (typep result-mode 'binding))
6712 (check-type result-mode lexical-binding)
6713 (values result-mode `(eql ,object)))))
6715 (define-extended-code-expander :load-constant (instruction funobj frame-map)
6716 (destructuring-bind (object result-mode &key (op :movl))
6717 (cdr instruction)
6718 (make-load-constant object result-mode funobj frame-map :op op)))
6720 ;;;;; Add
6722 (define-find-write-binding-and-type :add (instruction)
6723 (destructuring-bind (term0 term1 destination)
6724 (cdr instruction)
6725 (when (typep destination 'binding)
6726 (assert (and (bindingp term0) (bindingp term1)))
6727 (values destination
6729 (lambda (type0 type1)
6730 (let ((x (multiple-value-call #'encoded-integer-types-add
6731 (type-specifier-encode type0)
6732 (type-specifier-encode type1))))
6733 #+ignore (warn "thunked: ~S ~S -> ~S" term0 term1 x)
6735 (list term0 term1)
6736 ))))
6738 (define-find-used-bindings :add (term0 term1 destination)
6739 (if (bindingp destination)
6740 (list term0 term1 destination)
6741 (list term0 term1)))
6743 (define-find-read-bindings :add (term0 term1 destination)
6744 (declare (ignore destination))
6745 (let* ((type0 (and (binding-store-type term0)
6746 (apply #'encoded-type-decode (binding-store-type term0))))
6747 (type1 (and (binding-store-type term1)
6748 (apply #'encoded-type-decode (binding-store-type term1))))
6749 (singleton0 (and type0 (type-specifier-singleton type0)))
6750 (singleton1 (and type1 (type-specifier-singleton type1)))
6751 (singleton-sum (and singleton0 singleton1
6752 (type-specifier-singleton
6753 (apply #'encoded-integer-types-add
6754 (append (binding-store-type term0)
6755 (binding-store-type term1)))))))
6756 (cond
6757 (singleton-sum
6758 (let ((b (make-instance 'constant-object-binding
6759 :name (gensym "constant-sum")
6760 :object (car singleton-sum))))
6761 (movitz-env-add-binding (binding-env term0) b)
6762 (list b)))
6763 (t (append (unless (and singleton0 (typep (car singleton0) 'movitz-fixnum))
6764 (list term0))
6765 (unless (and singleton1 (typep (car singleton1) 'movitz-fixnum))
6766 (list term1)))))))
6768 (define-extended-code-expander :add (instruction funobj frame-map)
6769 (destructuring-bind (term0 term1 destination)
6770 (cdr instruction)
6771 (assert (and (bindingp term0)
6772 (bindingp term1)
6773 (member (result-mode-type destination)
6774 '(:lexical-binding :function :multple-values :eax :ebx :ecx :edx))))
6775 (let* ((destination (ensure-local-binding destination funobj))
6776 (term0 (ensure-local-binding term0 funobj))
6777 (term1 (ensure-local-binding term1 funobj))
6778 (destination-location (if (or (not (bindingp destination))
6779 (typep destination 'borrowed-binding))
6780 destination
6781 (new-binding-location (binding-target destination)
6782 frame-map
6783 :default nil)))
6784 (type0 (apply #'encoded-type-decode (binding-store-type term0)))
6785 (type1 (apply #'encoded-type-decode (binding-store-type term1)))
6786 (result-type (multiple-value-call #'encoded-integer-types-add
6787 (values-list (binding-store-type term0))
6788 (values-list (binding-store-type term1)))))
6789 ;; A null location means the binding is unused, in which
6790 ;; case there's no need to perform the addition.
6791 (when destination-location
6792 (let ((loc0 (new-binding-location (binding-target term0) frame-map :default nil))
6793 (loc1 (new-binding-location (binding-target term1) frame-map :default nil)))
6794 #+ignore
6795 (warn "add: ~A for ~A" instruction result-type)
6796 #+ignore
6797 (warn "add for: ~S is ~A, from ~A/~A and ~A/~A."
6798 destination result-type
6799 term0 loc0
6800 term1 loc1)
6801 #+ignore
6802 (when (eql destination-location 9)
6803 (warn "add for: ~S/~S~%= ~A/~A in ~S~&~A/~A in ~S."
6804 destination destination-location
6805 term0 loc0 (binding-extent-env (binding-target term0))
6806 term1 loc1 (binding-extent-env (binding-target term1)))
6807 (print-code 'load-term1 (make-load-lexical term1 :eax funobj nil frame-map))
6808 (print-code 'load-dest (make-load-lexical destination :eax funobj nil frame-map)))
6809 (flet ((make-store (source destination)
6810 (cond
6811 ((eq source destination)
6812 nil)
6813 ((member destination '(:eax :ebx :ecx :edx))
6814 `((:movl ,source ,destination)))
6815 (t (make-store-lexical destination source nil funobj frame-map))))
6816 (make-default-add ()
6817 (when (movitz-subtypep result-type '(unsigned-byte 32))
6818 (warn "Defaulting u32 ADD: ~A/~S = ~A/~S + ~A/~S"
6819 destination-location
6820 destination
6821 loc0 term0
6822 loc1 term1))
6823 (append (cond
6824 ((type-specifier-singleton type0)
6825 (append (make-load-lexical term1 :eax funobj nil frame-map)
6826 (make-load-constant (car (type-specifier-singleton type0))
6827 :ebx funobj frame-map)))
6828 ((type-specifier-singleton type1)
6829 (append (make-load-lexical term0 :eax funobj nil frame-map)
6830 (make-load-constant (car (type-specifier-singleton type1))
6831 :ebx funobj frame-map)))
6832 ((and (eq :eax loc0) (eq :ebx loc1))
6833 nil)
6834 ((and (eq :ebx loc0) (eq :eax loc1))
6835 nil) ; terms order isn't important
6836 ((eq :eax loc1)
6837 (append
6838 (make-load-lexical term0 :ebx funobj nil frame-map)))
6839 (t (append
6840 (make-load-lexical term0 :eax funobj nil frame-map)
6841 (make-load-lexical term1 :ebx funobj nil frame-map))))
6842 `((:movl (:edi ,(global-constant-offset '+)) :esi))
6843 (make-compiled-funcall-by-esi 2)
6844 (etypecase destination
6845 (symbol
6846 (unless (eq destination :eax)
6847 `((:movl :eax ,destination))))
6848 (binding
6849 (make-store-lexical destination :eax nil funobj frame-map))))))
6850 (let ((constant0 (let ((x (type-specifier-singleton type0)))
6851 (when (and x (typep (car x) 'movitz-fixnum))
6852 (movitz-immediate-value (car x)))))
6853 (constant1 (let ((x (type-specifier-singleton type1)))
6854 (when (and x (typep (car x) 'movitz-fixnum))
6855 (movitz-immediate-value (car x))))))
6856 (cond
6857 ((type-specifier-singleton result-type)
6858 ;; (break "constant add: ~S" instruction)
6859 (make-load-constant (car (type-specifier-singleton result-type))
6860 destination funobj frame-map))
6861 ((movitz-subtypep type0 '(integer 0 0))
6862 (cond
6863 ((eql destination loc1)
6864 #+ignore (break "NOP add: ~S" instruction)
6865 nil)
6866 ((and (member destination-location '(:eax :ebx :ecx :edx))
6867 (member loc1 '(:eax :ebx :ecx :edx)))
6868 `((:movl ,loc1 ,destination-location)))
6869 ((integerp loc1)
6870 (make-load-lexical term1 destination funobj nil frame-map))
6871 #+ignore
6872 ((integerp destination-location)
6873 (make-store-lexical destination-location loc1 nil funobj frame-map))
6874 (t (break "Unknown X zero-add: ~S" instruction))))
6875 ((movitz-subtypep type1 '(integer 0 0))
6876 ;; (warn "zero-add ~S => ~S [~S]" loc0 destination-location result-type)
6877 (cond
6878 ((eql destination-location loc0)
6879 #+ignore (break "NOP add: ~S" instruction)
6880 nil)
6881 ((and (member destination-location '(:eax :ebx :ecx :edx))
6882 (member loc0 '(:eax :ebx :ecx :edx)))
6883 `((:movl ,loc0 ,destination-location)))
6884 ((member loc0 '(:eax :ebx :ecx :edx))
6885 (make-store-lexical destination loc0 nil funobj frame-map))
6886 ((integerp loc0)
6887 (make-load-lexical term0 destination funobj nil frame-map))
6888 (t (break "Unknown Y zero-add: ~S" instruction))))
6889 ((and (movitz-subtypep type0 'fixnum)
6890 (movitz-subtypep type1 'fixnum)
6891 (movitz-subtypep result-type 'fixnum))
6892 (assert (not (and constant0 (zerop constant0))))
6893 (assert (not (and constant1 (zerop constant1))))
6894 (cond
6895 ((and (not (binding-lended-p (binding-target term0)))
6896 (not (binding-lended-p (binding-target term1)))
6897 (not (and (bindingp destination)
6898 (binding-lended-p (binding-target destination)))))
6899 (cond
6900 ((and constant0
6901 (equal loc1 destination-location))
6902 (cond
6903 ((member destination-location '(:eax :ebx :ecx :edx))
6904 `((:addl ,constant0 ,destination-location)))
6905 ((integerp loc1)
6906 `((:addl ,constant0 (:ebp ,(stack-frame-offset loc1)))))
6907 ((eq :argument-stack (operator loc1))
6908 `((:addl ,constant0
6909 (:ebp ,(argument-stack-offset (binding-target term1))))))
6910 ((eq :untagged-fixnum-ecx (operator loc1))
6911 `((:addl ,(truncate constant0 +movitz-fixnum-factor+) :ecx)))
6912 (t (error "Don't know how to add this for loc1 ~S" loc1))))
6913 ((and constant0
6914 (integerp destination-location)
6915 (eql term1 destination-location))
6916 (break "untested")
6917 `((:addl ,constant0 (:ebp ,(stack-frame-offset destination-location)))))
6918 ((and constant0
6919 (integerp destination-location)
6920 (member loc1 '(:eax :ebx :ecx :edx)))
6921 `((:addl ,constant0 ,loc1)
6922 (:movl ,loc1 (:ebp ,(stack-frame-offset destination-location)))))
6923 ((and (integerp loc0)
6924 (integerp loc1)
6925 (member destination-location '(:eax :ebx :ecx :edx)))
6926 (append `((:movl (:ebp ,(stack-frame-offset loc0)) ,destination-location)
6927 (:addl (:ebp ,(stack-frame-offset loc1)) ,destination-location))))
6928 ((and (integerp destination-location)
6929 (eql loc0 destination-location)
6930 constant1)
6931 `((:addl ,constant1 (:ebp ,(stack-frame-offset destination-location)))))
6932 ((and (integerp destination-location)
6933 (eql loc1 destination-location)
6934 constant0)
6935 `((:addl ,constant0 (:ebp ,(stack-frame-offset destination-location)))))
6936 ((and (member destination-location '(:eax :ebx :ecx :edx))
6937 (eq loc0 :untagged-fixnum-ecx)
6938 constant1)
6939 `((:leal ((:ecx ,+movitz-fixnum-factor+) ,constant1)
6940 ,destination-location)))
6941 ((and (member destination-location '(:eax :ebx :ecx :edx))
6942 (integerp loc1)
6943 constant0)
6944 `((:movl (:ebp ,(stack-frame-offset loc1)) ,destination-location)
6945 (:addl ,constant0 ,destination-location)))
6946 ((and (member destination-location '(:eax :ebx :ecx :edx))
6947 (integerp loc0)
6948 constant1)
6949 `((:movl (:ebp ,(stack-frame-offset loc0)) ,destination-location)
6950 (:addl ,constant1 ,destination-location)))
6951 ((and (member destination-location '(:eax :ebx :ecx :edx))
6952 (integerp loc0)
6953 (member loc1 '(:eax :ebx :ecx :edx))
6954 (not (eq destination-location loc1)))
6955 `((:movl (:ebp ,(stack-frame-offset loc0)) ,destination-location)
6956 (:addl ,loc1 ,destination-location)))
6957 ((and (member destination-location '(:eax :ebx :ecx :edx))
6958 constant0
6959 (member loc1 '(:eax :ebx :ecx :edx)))
6960 `((:leal (,loc1 ,constant0) ,destination-location)))
6961 ((and (member destination-location '(:eax :ebx :ecx :edx))
6962 constant1
6963 (member loc0 '(:eax :ebx :ecx :edx)))
6964 `((:leal (,loc0 ,constant1) ,destination-location)))
6965 ((and (member destination-location '(:eax :ebx :ecx :edx))
6966 constant0
6967 (eq :argument-stack (operator loc1)))
6968 `((:movl (:ebp ,(argument-stack-offset (binding-target term1)))
6969 ,destination-location)
6970 (:addl ,constant0 ,destination-location)))
6971 ((and (member destination-location '(:eax :ebx :ecx :edx))
6972 constant1
6973 (eq :argument-stack (operator loc0)))
6974 `((:movl (:ebp ,(argument-stack-offset (binding-target term0)))
6975 ,destination-location)
6976 (:addl ,constant1 ,destination-location)))
6977 (constant0
6978 (append (make-load-lexical term1 :eax funobj nil frame-map)
6979 `((:addl ,constant0 :eax))
6980 (make-store :eax destination)))
6981 (constant1
6982 (append (make-load-lexical term0 :eax funobj nil frame-map)
6983 `((:addl ,constant1 :eax))
6984 (make-store :eax destination)))
6985 ((eql loc0 loc1)
6986 (append (make-load-lexical term0 :eax funobj nil frame-map)
6987 `((:addl :eax :eax))
6988 (make-store :eax destination)))
6989 ((and (integerp loc0)
6990 (integerp loc1)
6991 (integerp destination-location)
6992 (/= loc0 loc1 destination-location))
6993 `((:movl (:ebp ,(stack-frame-offset loc0))
6994 :ecx)
6995 (:addl (:ebp ,(stack-frame-offset loc1))
6996 :ecx)
6997 (:movl :ecx (:ebp ,(stack-frame-offset destination-location)))))
6998 (t (warn "Unknown fixnum ADD: ~A/~S = ~A/~S + ~A/~S"
6999 destination-location
7000 destination
7001 loc0 term0
7002 loc1 term1)
7003 #+ignore (warn "map: ~A" frame-map)
7004 ;;; (warn "ADDI: ~S" instruction)
7005 (append (cond
7006 ((type-specifier-singleton type0)
7007 (append (make-load-lexical term1 :eax funobj nil frame-map)
7008 (make-load-constant (car (type-specifier-singleton type0))
7009 :ebx funobj frame-map)))
7010 ((type-specifier-singleton type1)
7011 (append (make-load-lexical term0 :eax funobj nil frame-map)
7012 (make-load-constant (car (type-specifier-singleton type1))
7013 :ebx funobj frame-map)))
7014 ((and (eq :eax loc0) (eq :ebx loc1))
7015 nil)
7016 ((and (eq :ebx loc0) (eq :eax loc1))
7017 nil) ; terms order isn't important
7018 ((eq :eax loc1)
7019 (append
7020 (make-load-lexical term0 :ebx funobj nil frame-map)))
7021 (t (append
7022 (make-load-lexical term0 :eax funobj nil frame-map)
7023 (make-load-lexical term1 :ebx funobj nil frame-map))))
7024 `((:movl (:edi ,(global-constant-offset '+)) :esi))
7025 (make-compiled-funcall-by-esi 2)
7026 (etypecase destination
7027 (symbol
7028 (unless (eq destination :eax)
7029 `((:movl :eax ,destination))))
7030 (binding
7031 (make-store-lexical destination :eax nil funobj frame-map)))))))
7032 ((and constant0
7033 (integerp destination-location)
7034 (eql loc1 destination-location)
7035 (binding-lended-p (binding-target destination)))
7036 (assert (binding-lended-p (binding-target term1)))
7037 (append (make-load-lexical destination :eax funobj t frame-map)
7038 `((:addl ,constant0 (-1 :eax)))))
7039 ((warn "~S" (list (and (bindingp destination)
7040 (binding-lended-p (binding-target destination)))
7041 (binding-lended-p (binding-target term0))
7042 (binding-lended-p (binding-target term1)))))
7043 (t (warn "Unknown fixnum add: ~S" instruction)
7044 (make-default-add))))
7045 ((and (movitz-subtypep type0 'fixnum)
7046 (movitz-subtypep type1 'fixnum))
7047 (flet ((mkadd-into (src destreg)
7048 (assert (eq destreg :eax) (destreg)
7049 "Movitz' INTO protocol says the overflowed value must be in EAX, ~
7050 but it's requested to be in ~S."
7051 destreg)
7052 (let ((srcloc (new-binding-location (binding-target src) frame-map)))
7053 (unless (eql srcloc loc1) (break))
7054 (if (integerp srcloc)
7055 `((:addl (:ebp ,(stack-frame-offset srcloc))
7056 ,destreg)
7057 (:into))
7058 (ecase (operator srcloc)
7059 ((:eax :ebx :ecx :edx)
7060 `((:addl ,srcloc ,destreg)
7061 (:into)))
7062 ((:argument-stack)
7063 `((:addl (:ebx ,(argument-stack-offset src))
7064 ,destreg)
7065 (:into)))
7066 )))))
7067 (cond
7068 ((and (not constant0)
7069 (not constant1)
7070 (not (binding-lended-p (binding-target term0)))
7071 (not (binding-lended-p (binding-target term1)))
7072 (not (and (bindingp destination)
7073 (binding-lended-p (binding-target destination)))))
7074 (cond
7075 ((and (not (eq loc0 :untagged-fixnum-ecx))
7076 (not (eq loc1 :untagged-fixnum-ecx))
7077 (not (eq destination-location :untagged-fixnum-ecx)))
7078 (append (cond
7079 ((and (eq loc0 :eax) (eq loc1 :eax))
7080 `((:addl :eax :eax)
7081 (:into)))
7082 ((eq loc0 :eax)
7083 (mkadd-into term1 :eax))
7084 ((eq loc1 :eax)
7085 (mkadd-into term0 :eax))
7086 (t (append (make-load-lexical term0 :eax funobj nil frame-map
7087 :protect-registers (list loc1))
7088 (mkadd-into term1 :eax))))
7089 (make-store :eax destination)))
7090 (t (make-default-add)
7091 #+ignore
7092 (append (make-load-lexical term0 :untagged-fixnum-ecx funobj nil frame-map)
7093 `((,*compiler-local-segment-prefix*
7094 :movl :ecx (:edi ,(global-constant-offset 'raw-scratch0))))
7095 (make-load-lexical term1 :untagged-fixnum-ecx funobj nil frame-map)
7096 `((,*compiler-local-segment-prefix*
7097 :addl (:edi ,(global-constant-offset 'raw-scratch0)) :ecx))
7098 (if (integerp destination-location)
7099 `((,*compiler-local-segment-prefix*
7100 :call (:edi ,(global-constant-offset 'box-u32-ecx)))
7101 (:movl :eax (:ebp ,(stack-frame-offset destination-location))))
7102 (ecase (operator destination-location)
7103 ((:untagged-fixnum-ecx)
7104 nil)
7105 ((:eax)
7106 `((,*compiler-local-segment-prefix*
7107 :call (:edi ,(global-constant-offset 'box-u32-ecx)))))
7108 ((:ebx :ecx :edx)
7109 `((,*compiler-local-segment-prefix*
7110 :call (:edi ,(global-constant-offset 'box-u32-ecx)))
7111 (:movl :eax ,destination-location)))
7112 ((:argument-stack)
7113 `((,*compiler-local-segment-prefix*
7114 :call (:edi ,(global-constant-offset 'box-u32-ecx)))
7115 (:movl :eax (:ebp ,(argument-stack-offset
7116 (binding-target destination))))))))))))
7117 (t (make-default-add)))))
7118 (t (make-default-add))))))))))
7120 ;;;;;;;
7122 (define-find-read-bindings :eql (x y mode)
7123 (declare (ignore mode))
7124 (list x y))
7126 (define-extended-code-expander :eql (instruction funobj frame-map)
7127 (destructuring-bind (x y return-mode)
7128 (cdr instruction)
7129 (let* ((x-type (apply #'encoded-type-decode (binding-store-type x)))
7130 (y-type (apply #'encoded-type-decode (binding-store-type y)))
7131 (x-singleton (type-specifier-singleton x-type))
7132 (y-singleton (type-specifier-singleton y-type)))
7133 (when (and y-singleton (not x-singleton))
7134 (rotatef x y)
7135 (rotatef x-type y-type)
7136 (rotatef x-singleton y-singleton))
7137 (let (#+ignore (x-loc (new-binding-location (binding-target x) frame-map :default nil))
7138 (y-loc (new-binding-location (binding-target y) frame-map :default nil)))
7139 #+ignore
7140 (warn "eql ~S/~S xx~Xxx ~S/~S: ~S"
7141 x x-loc (binding-target y)
7142 y y-loc
7143 instruction)
7144 (flet ((make-branch ()
7145 (ecase (operator return-mode)
7146 (:boolean-branch-on-false
7147 `((:jne ',(operands return-mode))))
7148 (:boolean-branch-on-true
7149 `((:je ',(operands return-mode))))
7150 (:boolean-zf=1)))
7151 (make-load-eax-ebx ()
7152 (if (eq :eax y-loc)
7153 (make-load-lexical x :ebx funobj nil frame-map)
7154 (append (make-load-lexical x :eax funobj nil frame-map)
7155 (make-load-lexical y :ebx funobj nil frame-map)))))
7156 (cond
7157 ((and x-singleton y-singleton)
7158 (let ((eql (etypecase (car x-singleton)
7159 (movitz-immediate-object
7160 (and (typep (car y-singleton) 'movitz-immediate-object)
7161 (eql (movitz-immediate-value (car x-singleton))
7162 (movitz-immediate-value (car y-singleton))))))))
7163 (case (operator return-mode)
7164 (:boolean-branch-on-false
7165 (when (not eql)
7166 `((:jmp ',(operands return-mode)))))
7167 (t (break "Constant EQL: ~S ~S" (car x-singleton) (car y-singleton))))))
7168 ((and x-singleton
7169 (eq :untagged-fixnum-ecx y-loc))
7170 (let ((value (etypecase (car x-singleton)
7171 (movitz-fixnum
7172 (movitz-fixnum-value (car x-singleton)))
7173 (movitz-bignum
7174 (movitz-bignum-value (car x-singleton))))))
7175 (check-type value (unsigned-byte 32))
7176 `((:cmpl ,value :ecx)
7177 ,@(make-branch))))
7178 ((and x-singleton
7179 (typep (car x-singleton) '(or movitz-immediate-object movitz-null)))
7180 (let ((value (if (typep (car x-singleton) 'movitz-null)
7181 :edi
7182 (movitz-immediate-value (car x-singleton)))))
7183 (append (cond
7184 ((and (eql value 0)
7185 (member y-loc '(:eax :ebx :ecx :edx)))
7186 `((:testl ,y-loc ,y-loc)))
7187 ((and (member y-loc '(:eax :ebx :ecx :edx))
7188 (not (binding-lended-p y)))
7189 `((:cmpl ,value ,y-loc)))
7190 ((and (integerp y-loc)
7191 (not (binding-lended-p y)))
7192 `((:cmpl ,value (:ebp ,(stack-frame-offset y-loc)))))
7193 ((and (eq :argument-stack (operator y-loc))
7194 (not (binding-lended-p y)))
7195 `((:cmpl ,value (:ebp ,(argument-stack-offset (binding-target y))))))
7196 (t (break "x-singleton: ~S with loc ~S"
7197 (movitz-immediate-value (car x-singleton))
7198 y-loc)))
7199 (make-branch))))
7200 ((and x-singleton
7201 (typep (car x-singleton) 'movitz-symbol)
7202 (member y-loc '(:eax :ebx :edx)))
7203 (append (make-load-constant (car x-singleton) y-loc funobj frame-map :op :cmpl)
7204 (make-branch)))
7205 (y-singleton
7206 (break "y-singleton"))
7207 ((and (not (eq t x-type)) ; this is for bootstrapping purposes.
7208 (not (eq t y-type)) ; ..
7209 (or (movitz-subtypep x-type '(or fixnum character symbol vector))
7210 (movitz-subtypep y-type '(or fixnum character symbol vector))))
7211 (append (make-load-eax-ebx)
7212 `((:cmpl :eax :ebx))
7213 (make-branch)))
7214 #+ignore
7215 ((warn "eql ~S/~S ~S/~S"
7216 x x-loc
7217 y y-loc))
7218 ((eq :boolean-branch-on-false (operator return-mode))
7219 (let ((eql-done (gensym "eql-done-"))
7220 (on-false-label (operands return-mode)))
7221 (append (make-load-eax-ebx)
7222 `((:cmpl :eax :ebx)
7223 (:je ',eql-done)
7224 (,*compiler-global-segment-prefix*
7225 :movl (:edi ,(global-constant-offset 'complicated-eql)) :esi)
7226 (:call (:esi ,(bt:slot-offset 'movitz-funobj 'code-vector%2op)))
7227 (:jne ',on-false-label)
7228 ,eql-done))))
7229 ((eq :boolean-branch-on-true (operator return-mode))
7230 (let ((on-true-label (operands return-mode)))
7231 (append (make-load-eax-ebx)
7232 `((:cmpl :eax :ebx)
7233 (:je ',on-true-label)
7234 (,*compiler-global-segment-prefix*
7235 :movl (:edi ,(global-constant-offset 'complicated-eql)) :esi)
7236 (:call (:esi ,(bt:slot-offset 'movitz-funobj 'code-vector%2op)))
7237 (:je ',on-true-label)))))
7238 ((eq return-mode :boolean-zf=1)
7239 (append (make-load-eax-ebx)
7240 (let ((eql-done (gensym "eql-done-")))
7241 `((:cmpl :eax :ebx)
7242 (:je ',eql-done)
7243 (,*compiler-global-segment-prefix*
7244 :movl (:edi ,(global-constant-offset 'complicated-eql)) :esi)
7245 (:call (:esi ,(bt:slot-offset 'movitz-funobj 'code-vector%2op)))
7246 ,eql-done))))
7247 (t (error "unknown eql: ~S" instruction))))))))
7249 (define-find-read-bindings :load-lambda (lambda-binding result-mode capture-env)
7250 (declare (ignore result-mode capture-env))
7251 (let ((allocation (movitz-allocation (function-binding-funobj lambda-binding))))
7252 (when (typep allocation 'with-dynamic-extent-scope-env)
7253 (values (list (base-binding allocation))
7254 (list :edx)))))
7256 (define-find-write-binding-and-type :enter-dynamic-scope (instruction)
7257 (destructuring-bind (scope-env)
7258 (cdr instruction)
7259 (if (null (dynamic-extent-scope-members scope-env))
7260 (values nil)
7261 (values (base-binding scope-env) 'fixnum))))
7263 (define-extended-code-expander :enter-dynamic-scope (instruction funobj frame-map)
7264 (declare (ignore funobj frame-map))
7265 (destructuring-bind (scope-env)
7266 (cdr instruction)
7267 (if (null (dynamic-extent-scope-members scope-env))
7269 (append `((:pushl :edi)
7270 (:movl :esp :eax)
7271 (:andl 4 :eax)
7272 (:addl :eax :esp))
7273 (loop for object in (reverse (dynamic-extent-scope-members scope-env))
7274 appending
7275 (etypecase object
7276 (movitz-cons
7277 `((:pushl :edi)
7278 (:pushl :edi)))
7279 (movitz-funobj
7280 (append (unless (zerop (mod (sizeof object) 8))
7281 `((:pushl :edi)))
7282 `((:load-constant ,object :eax))
7283 (loop for i from (1- (movitz-funobj-num-constants object))
7284 downto (movitz-funobj-num-jumpers object)
7285 collect `(:pushl (:eax ,(slot-offset 'movitz-funobj 'constant0)
7286 ,(* 4 i))))
7287 (loop repeat (movitz-funobj-num-jumpers object)
7288 collect `(:pushl 0))
7289 `((:pushl (:eax ,(slot-offset 'movitz-funobj 'num-jumpers)))
7290 (:pushl (:eax ,(slot-offset 'movitz-funobj 'name)))
7291 (:pushl (:eax ,(slot-offset 'movitz-funobj 'lambda-list)))
7293 (:pushl 0) ; %3op
7294 (:pushl 0) ; %2op
7295 (:pushl 0) ; %1op
7296 (:pushl 2) ; (default) 2 is recognized by map-header-vals as non-initialized funobj.
7298 (:pushl (:eax ,(slot-offset 'movitz-funobj 'type)))
7299 (:leal (:esp ,(tag :other)) :ebx)
7300 (,*compiler-local-segment-prefix*
7301 :call (:edi ,(global-constant-offset 'copy-funobj-code-vector-slots)))
7302 )))))))))
7304 ;;;(define-extended-code-expander :exit-dynamic-scope (instruction funobj frame-map)
7305 ;;; nil)
7307 (define-find-read-bindings :lexical-control-transfer (return-code return-mode from-env to-env
7308 &optional to-label)
7309 (declare (ignore return-code return-mode to-label))
7310 (let ((distance (stack-delta from-env to-env)))
7311 (when (eq t distance)
7312 (values (list (movitz-binding (save-esp-variable to-env) to-env nil))
7313 (list :esp)))))
7315 (define-find-read-bindings :stack-cons (proto-cons scope-env)
7316 (declare (ignore proto-cons))
7317 (values (list (base-binding scope-env))
7318 (list :edx)))
7320 (define-extended-code-expander :stack-cons (instruction funobj frame-map)
7321 (destructuring-bind (proto-cons dynamic-scope)
7322 (cdr instruction)
7323 (append (make-load-lexical (base-binding dynamic-scope) :edx
7324 funobj nil frame-map)
7325 `((:movl :eax (:edx ,(dynamic-extent-object-offset dynamic-scope proto-cons)))
7326 (:movl :ebx (:edx ,(+ 4 (dynamic-extent-object-offset dynamic-scope proto-cons))))
7327 (:leal (:edx ,(+ (tag :cons) (dynamic-extent-object-offset dynamic-scope proto-cons)))
7328 :eax)))))