1 ;;;;------------------------------------------------------------------
3 ;;;; Copyright (C) 2001,2000, 2002-2005,
4 ;;;; Department of Computer Science, University of Tromso, Norway
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.
11 ;;;; $Id: compiler.lisp,v 1.191 2008/02/16 23:35:22 ffjeld Exp $
13 ;;;;------------------------------------------------------------------
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
45 (defvar *compiler-global-segment-prefix
* nil
46 "Use these assembly-instruction prefixes when accessing the global
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
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
+
108 (defun duplicatesp (list)
109 "Returns TRUE iff at least one object occurs more than once in 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)
119 ((or (= (tag :even-fixnum
) return-pointer-tag
)
120 (= (tag :odd-fixnum
) return-pointer-tag
))
123 ;;; ((= 3 return-pointer-tag)
124 ;;; ;; Insert two NOPs, 3 -> 5
126 ((= (tag :character
) return-pointer-tag
)
127 ;; Insert three NOPs, 2 -> 5
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
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:*instruction-compute-extra-prefix-map
*
147 '((:call . compute-call-extra-prefix
))))
148 (asm:assemble-proglist
(translate-program resolved-code
:muerte.cl
:cl
)
149 :symtab
(list (cons :nil-value
(image-nil-word *image
*)))))
150 (values (make-movitz-vector (length code-vector
)
152 :initial-contents code-vector
)
155 (defun register-function-code-size (funobj)
156 (let* ((name (movitz-print (movitz-funobj-name funobj
)))
158 (new-size (length (movitz-vector-symbolic-data (movitz-funobj-code-vector funobj
)))))
160 (let ((old-size (gethash hash-name
(function-code-sizes *image
*))))
163 ((not *warn-function-change-p
*))
164 ((> new-size old-size
)
165 (warn "~S grew from ~D to ~D bytes." name old-size new-size
))
166 ((< new-size old-size
)
167 (warn "~S shrunk from ~D to ~D bytes" name old-size new-size
))))
168 (setf (gethash hash-name
(function-code-sizes *image
*)) new-size
))
171 (defclass movitz-funobj-pass1
()
174 :accessor movitz-funobj-name
)
176 :initarg
:lambda-list
177 :accessor movitz-funobj-lambda-list
)
179 :accessor function-envs
)
182 :accessor funobj-env
)
186 :accessor movitz-funobj-extent
)
189 :accessor movitz-allocation
)
192 :initarg
:entry-protocol
193 :reader funobj-entry-protocol
))
194 (:documentation
"This class is used for funobjs during the first compiler pass.
195 Before the second pass, such objects will be change-class-ed to proper movitz-funobjs.
196 This way, we ensure that no undue side-effects on the funobj occur during pass 1."))
198 (defmethod print-object ((object movitz-funobj-pass1
) stream
)
199 (print-unreadable-object (object stream
:type t
:identity t
)
200 (when (slot-boundp object
'name
)
201 (write (movitz-funobj-name object
) :stream stream
)))
204 (defun movitz-macro-expander-make-function (lambda-form &key name
(type :unknown
))
205 "Make a lambda-form that is a macro-expander into a proper function.
206 Gensym a name whose symbol-function is set to the macro-expander, and return that symbol."
207 (let ((function-name (gensym (format nil
"~A-expander-~@[~A-~]" type name
))))
208 (if *compiler-compile-macro-expanders
*
209 (with-host-environment ()
210 (compile function-name lambda-form
))
211 (setf (symbol-function function-name
)
212 (coerce lambda-form
'function
)))
215 (defun make-compiled-funobj (name lambda-list declarations form env top-level-p
&key funobj
)
216 "Compiler entry-point for making a (lexically) top-level function."
217 (handler-bind (((or warning error
)
220 (if (not (boundp 'muerte.cl
:*compile-file-pathname
*))
221 (format *error-output
*
222 "~&;; While Movitz compiling ~S:" name
)
223 (format *error-output
*
224 "~&;; While Movitz compiling ~S in ~A:"
225 name muerte.cl
:*compile-file-pathname
*)))))
226 (with-retries-until-true (retry-funobj "Retry compilation of ~S." name
)
227 (make-compiled-funobj-pass2
228 (make-compiled-funobj-pass1 name lambda-list declarations
229 form env top-level-p
:funobj funobj
)))))
231 (defun make-compiled-funobj-pass1 (name lambda-list declarations form env top-level-p
233 "Per funobj (i.e. not necessarily top-level) entry-point for first-pass compilation.
234 If funobj is provided, its identity will be kept, but its type (and values) might change."
235 ;; The ability to provide funobj's identity is important when a
236 ;; function must be referenced before it can be compiled, e.g. for
237 ;; mutually recursive (lexically bound) functions.
238 (with-retries-until-true (retry-pass1 "Retry first-pass compilation of ~S." name
)
239 ;; First-pass is mostly functional, so it can safely be restarted.
240 (multiple-value-bind (required-vars optional-vars rest-var key-vars aux-vars allow-p min max edx-var
)
241 (decode-normal-lambda-list lambda-list
)
242 (declare (ignore aux-vars allow-p min max
))
243 ;; There are several main branches through the function
244 ;; compiler, and this is where we decide which one to take.
246 ((let ((sub-form (cddr form
)))
247 (and (consp (car sub-form
))
248 (eq 'muerte
::numargs-case
(caar sub-form
))))
249 'make-compiled-function-pass1-numarg-case
)
250 ((and (= 1 (length required-vars
)) ; (x &optional y)
251 (= 1 (length optional-vars
))
252 (movitz-constantp (nth-value 1 (decode-optional-formal (first optional-vars
)))
257 'make-compiled-function-pass1-1req1opt
)
258 (t 'make-compiled-function-pass1
))
259 name lambda-list declarations form env top-level-p funobj
))))
261 (defun ensure-pass1-funobj (funobj class
&rest init-args
)
262 "If funobj is nil, return a fresh funobj of class.
263 Otherwise coerce funobj to class."
264 (apply #'reinitialize-instance
266 (change-class funobj class
)
267 (make-instance class
))
270 (defun make-compiled-function-pass1-numarg-case (name lambda-list declarations form env top-level-p funobj
)
271 (let* ((funobj (ensure-pass1-funobj funobj
'movitz-funobj-pass1
272 :entry-protocol
:numargs-case
274 :lambda-list
(movitz-read (lambda-list-simplify lambda-list
))))
275 (funobj-env (make-local-movitz-environment env funobj
:type
'funobj-env
)))
276 (setf (funobj-env funobj
) funobj-env
277 (function-envs funobj
) nil
)
278 (loop for
(numargs lambda-list . clause-body
) in
(cdr (caddr form
))
279 do
(when (duplicatesp lambda-list
)
280 (error "There are duplicates in lambda-list ~S." lambda-list
))
281 (multiple-value-bind (clause-body clause-declarations
)
282 (parse-declarations-and-body clause-body
)
284 (add-bindings-from-lambda-list lambda-list
285 (make-local-movitz-environment
288 :declaration-context
:funobj
290 (append clause-declarations
292 (function-form (list* 'muerte.cl
::block
293 (compute-function-block-name name
)
295 (multiple-value-bind (arg-init-code need-normalized-ecx-p
)
296 (make-function-arguments-init funobj function-env
)
297 (setf (extended-code function-env
)
298 (append arg-init-code
299 (compiler-call #'compile-form
300 :form
(make-special-funarg-shadowing function-env function-form
)
303 :top-level-p top-level-p
304 :result-mode
:function
)))
305 (setf (need-normalized-ecx-p function-env
) need-normalized-ecx-p
))
306 (push (cons numargs function-env
)
307 (function-envs funobj
)))))
310 (defun make-compiled-function-pass1-1req1opt (name lambda-list declarations form env top-level-p funobj
)
312 (when (duplicatesp lambda-list
)
313 (error "There are duplicates in lambda-list ~S." lambda-list
))
314 (let* ((funobj (ensure-pass1-funobj funobj
'movitz-funobj-pass1
315 :entry-protocol
:1req1opt
317 :lambda-list
(movitz-read (lambda-list-simplify lambda-list
))))
318 (funobj-env (make-local-movitz-environment env funobj
:type
'funobj-env
))
319 (function-env (add-bindings-from-lambda-list
321 (make-local-movitz-environment funobj-env funobj
323 :need-normalized-ecx-p nil
324 :declaration-context
:funobj
325 :declarations declarations
)))
326 (optional-env (make-local-movitz-environment function-env funobj
327 :type
'function-env
)))
328 (setf (funobj-env funobj
) funobj-env
)
329 ;; (print-code 'arg-init-code arg-init-code)
330 (setf (extended-code optional-env
)
331 (compiler-call #'compile-form
332 :form
(optional-function-argument-init-form
333 (movitz-binding (first (optional-vars function-env
)) function-env nil
))
337 (setf (extended-code function-env
)
338 (append #+ignore arg-init-code
339 (compiler-call #'compile-form
340 :form
(make-special-funarg-shadowing function-env form
)
343 :top-level-p top-level-p
344 :result-mode
:function
)))
345 (setf (function-envs funobj
)
346 (list (cons 'muerte.cl
::t function-env
)
347 (cons :optional optional-env
)))
350 (defun make-compiled-function-pass1 (name lambda-list declarations form env top-level-p funobj
)
352 (when (duplicatesp lambda-list
)
353 (error "There are duplicates in lambda-list ~S." lambda-list
))
354 (let* ((funobj (ensure-pass1-funobj funobj
'movitz-funobj-pass1
356 :lambda-list
(movitz-read (lambda-list-simplify lambda-list
))))
357 (funobj-env (make-local-movitz-environment env funobj
:type
'funobj-env
))
358 (function-env (add-bindings-from-lambda-list
360 (make-local-movitz-environment funobj-env funobj
362 :declaration-context
:funobj
363 :declarations declarations
))))
364 (setf (funobj-env funobj
) funobj-env
365 (function-envs funobj
) (list (cons 'muerte.cl
::t function-env
)))
366 (multiple-value-bind (arg-init-code need-normalized-ecx-p
)
367 (make-function-arguments-init funobj function-env
)
368 (setf (need-normalized-ecx-p function-env
) need-normalized-ecx-p
)
369 (setf (extended-code function-env
)
370 (append arg-init-code
371 (compiler-call #'compile-form
372 :form
(make-special-funarg-shadowing function-env form
)
375 :top-level-p top-level-p
376 :result-mode
:function
))))
380 (defun make-compiled-funobj-pass2 (toplevel-funobj-pass1)
381 "This is the entry-poing for second pass compilation for each top-level funobj."
382 (check-type toplevel-funobj-pass1 movitz-funobj-pass1
)
383 (let ((toplevel-funobj (change-class toplevel-funobj-pass1
'movitz-funobj
)))
384 (multiple-value-bind (toplevel-funobj function-binding-usage
)
385 (resolve-borrowed-bindings toplevel-funobj
)
389 (resolve-sub-functions toplevel-funobj function-binding-usage
)))))))
391 (defstruct (type-analysis (:type list
))
395 (multiple-value-list (type-specifier-encode nil
)))
396 (declared-encoded-type
397 (multiple-value-list (type-specifier-encode t
))))
399 (defun make-type-analysis-with-declaration (binding)
401 (if (not (and *compiler-trust-user-type-declarations-p
*
402 (movitz-env-get (binding-name binding
) :variable-type
403 nil
(binding-env binding
) nil
)))
404 (multiple-value-list (type-specifier-encode t
))
406 (type-specifier-encode (movitz-env-get (binding-name binding
) :variable-type
407 t
(binding-env binding
) nil
))))))
408 ;; (warn "~S decl: ~A" binding (apply #'encoded-type-decode declared-type))
409 (make-type-analysis :declared-encoded-type declared-type
)))
411 (defun analyze-bindings (toplevel-funobj)
412 "Figure out usage of bindings in a toplevel funobj.
413 Side-effects each binding's binding-store-type."
414 (if (not *compiler-do-type-inference
*)
416 ((analyze-code (code)
417 (dolist (instruction code
)
418 (when (listp instruction
)
420 (find-written-binding-and-type instruction
)))
422 (setf (binding-store-type binding
)
423 (multiple-value-list (type-specifier-encode t
)))))
424 (analyze-code (instruction-sub-program instruction
)))))
425 (analyze-funobj (funobj)
426 (loop for
(nil . function-env
) in
(function-envs funobj
)
427 do
(analyze-code (extended-code function-env
)))
428 (loop for function-binding in
(sub-function-binding-usage funobj
) by
#'cddr
429 do
(analyze-funobj (function-binding-funobj function-binding
)))
431 (analyze-funobj toplevel-funobj
))
432 (let ((binding-usage (make-hash-table :test
'eq
)))
433 (labels ((binding-resolved-p (binding)
434 (or (typep binding
'constant-object-binding
)
435 (typep binding
'function-argument
)
436 (let ((analysis (gethash binding binding-usage
)))
438 (null (type-analysis-thunks analysis
))))))
439 (binding-resolve (binding)
441 ((not (bindingp binding
))
443 ((typep binding
'constant-object-binding
)
444 (apply #'encoded-type-decode
445 (binding-store-type binding
)))
446 ((typep binding
'function-argument
)
448 ((let ((analysis (gethash binding binding-usage
)))
449 (assert (and (and analysis
450 (null (type-analysis-thunks analysis
))))
452 "Can't resolve unresolved binding ~S." binding
)))
453 (*compiler-trust-user-type-declarations-p
*
454 (let ((analysis (gethash binding binding-usage
)))
455 (multiple-value-call #'encoded-type-decode
456 (apply #'encoded-types-and
457 (append (type-analysis-declared-encoded-type analysis
)
458 (type-analysis-encoded-type analysis
))))))
459 (t (let ((analysis (gethash binding binding-usage
)))
460 (apply #'encoded-type-decode
461 (type-analysis-encoded-type analysis
))))))
462 (type-is-t (type-specifier)
463 (or (eq type-specifier t
)
464 (and (listp type-specifier
)
465 (eq 'or
(car type-specifier
))
466 (some #'type-is-t
(cdr type-specifier
)))))
467 (analyze-store (binding type thunk thunk-args
)
468 (assert (not (null type
)) ()
469 "store-lexical with empty type.")
470 (assert (or (typep type
'binding
)
471 (eql 1 (type-specifier-num-values type
))) ()
472 "store-lexical with multiple-valued type: ~S for ~S" type binding
)
473 #+ignore
(warn "store ~S type ~S, thunk ~S" binding type thunk
)
474 (let ((analysis (or (gethash binding binding-usage
)
475 (setf (gethash binding binding-usage
)
476 (make-type-analysis-with-declaration binding
)))))
479 (assert (some #'bindingp thunk-args
))
480 (push (cons thunk thunk-args
) (type-analysis-thunks analysis
)))
481 ((and (bindingp type
)
482 (binding-eql type binding
))
483 (break "got binding type")
485 (t (setf (type-analysis-encoded-type analysis
)
489 (values-list (type-analysis-encoded-type analysis
))
490 (type-specifier-encode type
))))))))
492 #+ignore
(print-code 'analyze code
)
493 (dolist (instruction code
)
494 (when (listp instruction
)
495 (multiple-value-bind (store-binding store-type thunk thunk-args
)
496 (find-written-binding-and-type instruction
)
498 #+ignore
(warn "store: ~S binding ~S type ~S thunk ~S"
499 instruction store-binding store-type thunk
)
500 (analyze-store store-binding store-type thunk thunk-args
)))
501 (analyze-code (instruction-sub-program instruction
)))))
502 (analyze-funobj (funobj)
503 (loop for
(nil . function-env
) in
(function-envs funobj
)
504 do
(analyze-code (extended-code function-env
)))
505 (loop for function-binding in
(sub-function-binding-usage funobj
) by
#'cddr
506 do
(analyze-funobj (function-binding-funobj function-binding
)))
508 ;; 1. Examine each store to lexical bindings.
509 (analyze-funobj toplevel-funobj
)
511 (flet ((resolve-thunks ()
512 (loop with more-thunks-p
= t
515 do
(setf more-thunks-p nil
)
516 (maphash (lambda (binding analysis
)
517 (declare (ignore binding
))
518 (setf (type-analysis-thunks analysis
)
519 (loop for
(thunk . thunk-args
) in
(type-analysis-thunks analysis
)
520 if
(not (every #'binding-resolved-p thunk-args
))
521 collect
(cons thunk thunk-args
)
524 (warn "because ~S=>~S->~S completing ~S: ~S and ~S"
526 (mapcar #'binding-resolve thunk-args
)
528 (type-analysis-declared-encoded-type analysis
)
533 (type-analysis-encoded-type analysis
))
534 (type-specifier-encode
535 (apply thunk
(mapcar #'binding-resolve
537 (setf (type-analysis-encoded-type analysis
)
542 (type-analysis-declared-encoded-type analysis
))
546 (type-analysis-encoded-type analysis
))
547 (type-specifier-encode
548 (apply thunk
(mapcar #'binding-resolve
550 (setf more-thunks-p t
))))
553 (when *compiler-trust-user-type-declarations-p
*
554 ;; For each unresolved binding, just use the declared type.
555 (maphash (lambda (binding analysis
)
556 (declare (ignore binding
))
557 (when (and (not (null (type-analysis-thunks analysis
)))
558 (not (apply #'encoded-allp
559 (type-analysis-declared-encoded-type analysis
))))
561 (warn "Trusting ~S, was ~S, because ~S [~S]"
563 (type-analysis-encoded-type analysis
)
564 (type-analysis-thunks analysis
)
565 (loop for
(thunk . thunk-args
) in
(type-analysis-thunks analysis
)
566 collect
(mapcar #'binding-resolved-p thunk-args
)))
567 (setf (type-analysis-encoded-type analysis
)
568 (type-analysis-declared-encoded-type analysis
))
569 (setf (type-analysis-thunks analysis
) nil
))) ; Ignore remaining thunks.
571 ;; Try one more time to resolve thunks.
574 (maphash (lambda (binding analysis
)
575 (when (type-analysis-thunks analysis
)
576 (warn "Unable to infer type for ~S: ~S" binding
577 (type-analysis-thunks analysis
))))
580 (maphash (lambda (binding analysis
)
581 (setf (binding-store-type binding
)
583 ((and (not (null (type-analysis-thunks analysis
)))
584 *compiler-trust-user-type-declarations-p
*
585 (movitz-env-get (binding-name binding
) :variable-type nil
586 (binding-env binding
) nil
))
588 (type-specifier-encode (movitz-env-get (binding-name binding
) :variable-type
589 t
(binding-env binding
) nil
))))
590 ((and *compiler-trust-user-type-declarations-p
*
591 (movitz-env-get (binding-name binding
) :variable-type nil
592 (binding-env binding
) nil
))
594 (multiple-value-call #'encoded-types-and
595 (type-specifier-encode (movitz-env-get (binding-name binding
) :variable-type
596 t
(binding-env binding
) nil
))
597 (values-list (type-analysis-encoded-type analysis
)))))
598 ((not (null (type-analysis-thunks analysis
)))
599 (multiple-value-list (type-specifier-encode t
)))
600 (t (type-analysis-encoded-type analysis
))))
601 #+ignore
(warn "Finally: ~S" binding
))
605 (defun resolve-borrowed-bindings (toplevel-funobj)
606 "For <funobj>'s code, for every non-local binding used we create
607 a borrowing-binding in the funobj-env. This process must be done
608 recursively, depth-first wrt. sub-functions. Also, return a plist
609 of all function-bindings seen."
610 (check-type toplevel-funobj movitz-funobj
)
611 (let ((function-binding-usage ()))
612 (labels ((process-binding (funobj binding usages
)
614 ((typep binding
'constant-object-binding
))
615 ((not (eq funobj
(binding-funobj binding
)))
616 (let ((borrowing-binding
617 (or (find binding
(borrowed-bindings funobj
)
618 :key
#'borrowed-binding-target
)
619 (car (push (movitz-env-add-binding (funobj-env funobj
)
620 (make-instance 'borrowed-binding
621 :name
(binding-name binding
)
622 :target-binding binding
))
623 (borrowed-bindings funobj
))))))
624 ;; We don't want to borrow a forwarding-binding..
625 (when (typep (borrowed-binding-target borrowing-binding
)
627 (change-class (borrowed-binding-target borrowing-binding
)
629 ;;; (warn "binding ~S of ~S is not local to ~S, replacing with ~S of ~S."
630 ;;; binding (binding-env binding) funobj
631 ;;; borrowing-binding (binding-env borrowing-binding))
632 ;;; (pushnew borrowing-binding
633 ;;; (getf (binding-lended-p binding) :lended-to))
634 (dolist (usage usages
)
635 (pushnew usage
(borrowed-binding-usage borrowing-binding
)))
637 (t ; Binding is local to this funobj
640 (process-binding funobj
(forwarding-binding-target binding
) usages
)
642 (setf (forwarding-binding-target binding
)
643 (process-binding funobj
(forwarding-binding-target binding
) usages
)))
645 (dolist (usage usages
)
647 (getf (sub-function-binding-usage (function-binding-parent binding
))
649 (pushnew usage
(getf function-binding-usage binding
)))
652 (resolve-sub-funobj (funobj sub-funobj
)
653 (dolist (binding-we-lend (borrowed-bindings (resolve-funobj-borrowing sub-funobj
)))
655 (warn "Lending from ~S to ~S: ~S <= ~S"
657 (borrowed-binding-target binding-we-lend
)
659 (process-binding funobj
660 (borrowed-binding-target binding-we-lend
)
661 (borrowed-binding-usage binding-we-lend
))))
662 (resolve-code (funobj code
)
663 (dolist (instruction code
)
664 (when (listp instruction
)
665 (let ((store-binding (find-written-binding-and-type instruction
)))
667 (process-binding funobj store-binding
'(:write
))))
668 (dolist (load-binding (find-read-bindings instruction
))
669 (process-binding funobj load-binding
'(:read
)))
670 (case (car instruction
)
672 (process-binding funobj
(second instruction
) '(:call
)))
674 (destructuring-bind (proto-cons dynamic-scope
)
676 (push proto-cons
(dynamic-extent-scope-members dynamic-scope
))))
678 (destructuring-bind (lambda-binding lambda-result-mode capture-env
)
680 (declare (ignore lambda-result-mode
))
681 (assert (eq funobj
(binding-funobj lambda-binding
)) ()
682 "A non-local lambda doesn't make sense. There must be a bug.")
683 (let ((lambda-funobj (function-binding-funobj lambda-binding
)))
684 (let ((dynamic-scope (find-dynamic-extent-scope capture-env
)))
686 ;; (warn "Adding ~S to ~S/~S" lambda-funobj dynamic-extent dynamic-scope)
687 (setf (movitz-funobj-extent lambda-funobj
) :dynamic-extent
688 (movitz-allocation lambda-funobj
) dynamic-scope
)
689 (push lambda-funobj
(dynamic-extent-scope-members dynamic-scope
))
690 (process-binding funobj
(base-binding dynamic-scope
) '(:read
))))
691 (resolve-sub-funobj funobj lambda-funobj
)
692 (process-binding funobj lambda-binding
'(:read
))
693 ;; This funobj is effectively using every binding that the lambda
695 (map nil
(lambda (borrowed-binding)
696 (process-binding funobj
697 (borrowed-binding-target borrowed-binding
)
699 (borrowed-bindings (function-binding-funobj lambda-binding
))))))
700 (:local-function-init
701 (let ((function-binding (second instruction
)))
702 (assert (eq funobj
(binding-funobj function-binding
)) ()
703 "Initialization of a non-local function doesn't make sense.")
704 (resolve-sub-funobj funobj
(function-binding-funobj (second instruction
)))
705 (map nil
(lambda (borrowed-binding)
706 (process-binding funobj
707 (borrowed-binding-target borrowed-binding
)
709 (borrowed-bindings (function-binding-funobj (second instruction
)))))))
710 (resolve-code funobj
(instruction-sub-program instruction
)))))
711 (resolve-funobj-borrowing (funobj)
712 (let ((funobj (change-class funobj
'movitz-funobj
:borrowed-bindings nil
)))
713 (loop for
(nil . function-env
) in
(function-envs funobj
)
714 do
(resolve-code funobj
(extended-code function-env
)))
715 ;; (warn "~S borrows ~S." funobj (borrowed-bindings funobj))
717 (values (resolve-funobj-borrowing toplevel-funobj
)
718 function-binding-usage
))))
720 (defun resolve-sub-functions (toplevel-funobj function-binding-usage
)
721 (assert (null (borrowed-bindings toplevel-funobj
)) ()
722 "Can't deal with toplevel closures yet. Borrowed: ~S"
723 (borrowed-bindings toplevel-funobj
))
724 (setf (movitz-funobj-extent toplevel-funobj
) :indefinite-extent
)
725 (let ((sub-funobj-index 0))
726 (loop for
(function-binding usage
) on function-binding-usage by
#'cddr
727 do
(let ((sub-funobj (function-binding-funobj function-binding
)))
728 ;; (warn "USage: ~S => ~S" sub-funobj usage)
729 (case (car (movitz-funobj-name sub-funobj
))
731 (setf (movitz-funobj-name sub-funobj
)
732 (list 'muerte.cl
:lambda
733 (movitz-funobj-name toplevel-funobj
)
734 (post-incf sub-funobj-index
)))))
735 (loop for borrowed-binding in
(borrowed-bindings sub-funobj
)
736 do
(pushnew borrowed-binding
737 (getf (binding-lending (borrowed-binding-target borrowed-binding
))
739 ;; (warn "old extent: ~S" (movitz-funobj-extent sub-funobj))
742 (null (borrowed-bindings sub-funobj
)))
744 (warn "null usage for ~S" sub-funobj
))
745 (change-class function-binding
'funobj-binding
)
746 (setf (movitz-funobj-extent sub-funobj
)
748 ((equal usage
'(:call
))
749 (change-class function-binding
'closure-binding
)
750 (setf (movitz-funobj-extent sub-funobj
)
752 ((eq :dynamic-extent
(movitz-funobj-extent sub-funobj
))
753 (change-class function-binding
'closure-binding
))
754 (t (change-class function-binding
'closure-binding
)
755 (setf (movitz-funobj-extent sub-funobj
)
756 :indefinite-extent
))))))
757 (loop for function-binding in function-binding-usage by
#'cddr
758 do
(finalize-funobj (function-binding-funobj function-binding
)))
759 (finalize-funobj toplevel-funobj
))
761 (defun finalize-funobj (funobj)
762 "Calculate funobj's constants, jumpers."
763 (loop with all-key-args-constants
= nil
764 with all-constants-plist
= () and all-jumper-sets
= ()
765 for
(nil . function-env
) in
(function-envs funobj
)
766 ;; (borrowed-bindings body-code) in code-specs
767 as body-code
= (extended-code function-env
)
768 as
(const-plist jumper-sets key-args-constants
) =
769 (multiple-value-list (find-code-constants-and-jumpers body-code
))
770 do
(when key-args-constants
771 (assert (not all-key-args-constants
) ()
772 "only one &key parsing allowed per funobj.")
773 (setf all-key-args-constants key-args-constants
))
774 (loop for
(constant usage
) on const-plist by
#'cddr
775 do
(incf (getf all-constants-plist constant
0) usage
))
776 (loop for
(name set
) on jumper-sets by
#'cddr
777 do
(assert (not (getf all-jumper-sets name
)) ()
778 "Jumper-set ~S multiply defined." name
)
779 (setf (getf all-jumper-sets name
) set
))
781 (multiple-value-bind (const-list num-jumpers jumpers-map borrower-map
)
782 (layout-funobj-vector all-constants-plist
783 all-key-args-constants
784 #+ignore
(mapcar (lambda (x)
785 (cons (movitz-read x
) 1))
788 (borrowed-bindings funobj
))
789 (setf (movitz-funobj-num-jumpers funobj
) num-jumpers
790 (movitz-funobj-const-list funobj
) const-list
791 (movitz-funobj-num-constants funobj
) (length const-list
)
792 (movitz-funobj-jumpers-map funobj
) jumpers-map
)
793 (loop for
(binding . pos
) in borrower-map
794 do
(setf (borrowed-binding-reference-slot binding
) pos
))
797 (defun layout-stack-frames (funobj)
798 "Lay out the stack-frame (i.e. create a frame-map) for funobj
799 and all its local functions. This must be done breadth-first, because
800 a (lexical-extent) sub-function might care about its parent frame-map."
801 (loop for
(nil . function-env
) in
(function-envs funobj
)
802 do
(assert (not (slot-boundp function-env
'frame-map
)))
803 (setf (frame-map function-env
)
804 (funobj-assign-bindings (extended-code function-env
)
806 (loop for
(sub-function-binding) on
(sub-function-binding-usage funobj
) by
#'cddr
807 do
(layout-stack-frames (function-binding-funobj sub-function-binding
)))
810 (defun complete-funobj (funobj)
811 (case (funobj-entry-protocol funobj
)
813 (complete-funobj-1req1opt funobj
))
814 (t (complete-funobj-default funobj
)))
815 (loop for
(sub-function-binding) on
(sub-function-binding-usage funobj
) by
#'cddr
816 do
(complete-funobj (function-binding-funobj sub-function-binding
)))
817 (register-function-code-size funobj
))
819 (defun complete-funobj-1req1opt (funobj)
820 (assert (= 2 (length (function-envs funobj
))))
821 (let* ((function-env (cdr (assoc 'muerte.cl
::t
(function-envs funobj
))))
822 (optional-env (cdr (assoc :optional
(function-envs funobj
))))
823 (frame-map (frame-map function-env
))
824 (resolved-code (finalize-code (extended-code function-env
) funobj frame-map
))
825 (resolved-optional-code (finalize-code (extended-code optional-env
) funobj frame-map
))
826 (stack-frame-size (frame-map-size (frame-map function-env
)))
827 (use-stack-frame-p (or (plusp stack-frame-size
)
828 (tree-search resolved-code
829 '(:pushl
:popl
:ebp
:esp
:call
:leave
))
831 (and (not (equal '(:movl
(:ebp -
4) :esi
) x
))
832 (tree-search x
':esi
)))
834 (let* ((function-code
835 (let* ((req-binding (movitz-binding (first (required-vars function-env
))
837 (req-location (cdr (assoc req-binding frame-map
)))
838 (opt-binding (movitz-binding (first (optional-vars function-env
))
840 (opt-location (cdr (assoc opt-binding frame-map
)))
841 (optp-binding (movitz-binding (optional-function-argument-supplied-p-var opt-binding
)
843 (optp-location (cdr (assoc optp-binding frame-map
)))
845 (append `((:jmp
(:edi
,(global-constant-offset 'trampoline-cl-dispatch-1or2
))))
847 (unless (eql nil opt-location
)
848 resolved-optional-code
)
851 (:jmp
'optp-into-edx-ok
)))
854 `((,*compiler-global-segment-prefix
*
855 :movl
(:edi
,(global-constant-offset 't-symbol
)) :edx
)
857 (when use-stack-frame-p
858 +enter-stack-frame-code
+)
859 '(start-stack-frame-setup)
861 ((and (eql 1 req-location
)
862 (eql 2 opt-location
))
863 (incf stack-setup-pre
2)
866 ((and (eql 1 req-location
)
867 (eql nil opt-location
))
868 (incf stack-setup-pre
1)
870 ((and (member req-location
'(nil :eax
))
871 (eql 1 opt-location
))
872 (incf stack-setup-pre
1)
874 ((and (member req-location
'(nil :eax
))
875 (member opt-location
'(nil :ebx
)))
877 (t (error "Can't deal with req ~S opt ~S."
878 req-location opt-location
)))
881 (make-stack-setup-code (- stack-frame-size stack-setup-pre
)))
882 ((and (integerp optp-location
)
883 (= optp-location
(1+ stack-setup-pre
)))
884 (append `((:pushl
:edx
))
885 (make-stack-setup-code (- stack-frame-size stack-setup-pre
1))))
886 ((integerp optp-location
)
887 (append (make-stack-setup-code (- stack-frame-size stack-setup-pre
))
888 `((:movl
:edx
(:ebp
,(stack-frame-offset optp-location
))))))
889 (t (error "Can't deal with optional-p at ~S, after (~S ~S)."
890 optp-location req-location opt-location
)))
891 (flet ((make-lending (location lended-cons-position
)
892 (etypecase req-location
894 `((:movl
(:ebp
,(stack-frame-offset location
)) :edx
)
895 (:movl
:edi
(:ebp
,(stack-frame-offset lended-cons-position
))) ; cdr
896 (:movl
:edx
(:ebp
,(stack-frame-offset (1+ lended-cons-position
)))) ; car
897 (:leal
(:ebp
1 ,(stack-frame-offset (1+ lended-cons-position
)))
899 (:movl
:edx
(:ebp
,(stack-frame-offset location
))))))))
901 (when (binding-lended-p req-binding
)
902 (make-lending req-location
(getf (binding-lending req-binding
)
903 :stack-cons-location
)))
904 (when (binding-lended-p opt-binding
)
905 (make-lending opt-location
(getf (binding-lending opt-binding
)
906 :stack-cons-location
)))
907 (when (and optp-binding
(binding-lended-p optp-binding
))
908 (make-lending optp-location
(getf (binding-lending optp-binding
)
909 :stack-cons-location
)))))
911 (make-compiled-function-postlude funobj function-env
912 use-stack-frame-p
)))))
913 (let ((optimized-function-code
914 (optimize-code function-code
915 :keep-labels
(append (subseq (movitz-funobj-const-list funobj
)
916 0 (movitz-funobj-num-jumpers funobj
))
917 '(entry%
1op entry%
2op
)))))
918 (assemble-funobj funobj optimized-function-code
)))))
920 (defun complete-funobj-default (funobj)
922 (loop for
(numargs . function-env
) in
(function-envs funobj
)
924 (let* ((frame-map (frame-map function-env
))
925 (resolved-code (finalize-code (extended-code function-env
) funobj frame-map
))
926 (stack-frame-size (frame-map-size (frame-map function-env
)))
927 (use-stack-frame-p (or (plusp stack-frame-size
)
928 (tree-search resolved-code
929 '(:push
:pop
:ebp
:esp
:call
:leave
))
931 (and (not (equal '(:movl
(:ebp -
4) :esi
) x
))
932 (tree-search x
':esi
)))
934 (multiple-value-bind (prelude-code have-normalized-ecx-p
)
935 (make-compiled-function-prelude stack-frame-size function-env use-stack-frame-p
936 (need-normalized-ecx-p function-env
) frame-map
937 :do-check-stack-p
(or (<= 32 stack-frame-size
)
938 (tree-search resolved-code
941 (install-arg-cmp (append prelude-code
943 (make-compiled-function-postlude funobj function-env
945 have-normalized-ecx-p
)))
946 (let ((optimized-function-code
947 (optimize-code function-code
949 (subseq (movitz-funobj-const-list funobj
)
950 0 (movitz-funobj-num-jumpers funobj
))
954 (cons numargs optimized-function-code
))))))))
955 (let ((code1 (cdr (assoc 1 code-specs
)))
956 (code2 (cdr (assoc 2 code-specs
)))
957 (code3 (cdr (assoc 3 code-specs
)))
958 (codet (cdr (assoc 'muerte.cl
::t code-specs
))))
959 (assert codet
() "A default numargs-case is required.")
960 ;; (format t "codet:~{~&~A~}" codet)
962 (delete 'start-stack-frame-setup
967 ,@(unless (find 'entry%
1op code1
)
968 '(entry%
1op
(:movb
1 :cl
)))
974 ,@(unless (find 'entry%
2op code2
)
975 '(entry%
2op
(:movb
2 :cl
)))
980 (:jne
'not-three-args
)
981 ,@(unless (find 'entry%
3op code3
)
982 '(entry%
3op
(:movb
3 :cl
)))
985 (delete-if (lambda (x)
986 (or (and code1
(eq x
'entry%
1op
))
987 (and code2
(eq x
'entry%
2op
))
988 (and code3
(eq x
'entry%
3op
))))
990 ;; (print-code funobj combined-code)
991 (assemble-funobj funobj combined-code
))))
996 (format nil
"~&;; Diss:
997 ~:{~4D: ~16<~{ ~2,'0X~}~;~> ~A~@[ ;~{ ~A~}~]~%~}"
998 (loop with code-position
= 0 and instruction-octets
= nil
999 for pc
= 0 then code-position
1000 for instruction
= (progn
1001 (setf instruction-octets nil
)
1002 (ia-x86:decode-read-octet
(lambda ()
1003 (incf code-position
)
1004 (loop while
(and code
(not (typep (car code
) '(unsigned-byte 8))))
1005 do
(warn "diss bad byte at ~D: ~S" code-position
(pop code
))
1006 (incf code-position
))
1007 (let ((x (pop code
)))
1008 (when x
(push x instruction-octets
))
1010 collect
(if (not instruction
)
1011 (list pc
(nreverse instruction-octets
) nil
'("???"))
1013 (nreverse instruction-octets
)
1014 ;;(ia-x86::cbyte-to-octet-list (ia-x86::instruction-original-datum instruction))
1016 (comment-instruction instruction nil pc
)))
1020 (defun assemble-funobj (funobj combined-code
)
1021 (multiple-value-bind (code-vector code-symtab
)
1022 (let ((asm:*instruction-compute-extra-prefix-map
*
1023 '((:call . compute-call-extra-prefix
))))
1024 (asm:assemble-proglist combined-code
1025 :symtab
(list* (cons :nil-value
(image-nil-word *image
*))
1026 (loop for
(label . set
) in
(movitz-funobj-jumpers-map funobj
)
1028 (* 4 (or (search set
(movitz-funobj-const-list funobj
)
1029 :end2
(movitz-funobj-num-jumpers funobj
))
1030 (error "Jumper for ~S missing." label
))))))))
1031 (setf (movitz-funobj-symtab funobj
) code-symtab
)
1032 (let* ((code-length (- (length code-vector
) 3 -
3))
1033 (code-vector (make-array code-length
1034 :initial-contents code-vector
1036 (setf (fill-pointer code-vector
) code-length
)
1038 (setf (ldb (byte 1 5) (slot-value funobj
'debug-info
))
1039 1 #+ignore
(if use-stack-frame-p
1 0))
1040 (let ((x (cdr (assoc 'start-stack-frame-setup code-symtab
))))
1043 #+ignore
(warn "No start-stack-frame-setup label for ~S." name
))
1045 (setf (ldb (byte 5 0) (slot-value funobj
'debug-info
)) x
))
1046 (t (warn "Can't encode start-stack-frame-setup label ~D into debug-info for ~S."
1047 x
(movitz-funobj-name funobj
)))))
1048 (let* ((a (or (cdr (assoc 'entry%
1op code-symtab
)) 0))
1049 (b (or (cdr (assoc 'entry%
2op code-symtab
)) a
))
1050 (c (or (cdr (assoc 'entry%
3op code-symtab
)) b
)))
1052 (warn "Weird code-entries: ~D, ~D, ~D." a b c
))
1053 (unless (<= 0 a
255)
1054 (break "entry%1: ~D" a
))
1055 (unless (<= 0 b
2047)
1056 (break "entry%2: ~D" b
))
1057 (unless (<= 0 c
4095)
1058 (break "entry%3: ~D" c
)))
1059 (loop for
((entry-label slot-name
)) on
'((entry%
1op code-vector%
1op
)
1060 (entry%
2op code-vector%
2op
)
1061 (entry%
3op code-vector%
3op
))
1063 ((assoc entry-label code-symtab
)
1064 (let ((offset (cdr (assoc entry-label code-symtab
))))
1065 (setf (slot-value funobj slot-name
)
1066 (cons offset funobj
))
1067 #+ignore
(when (< offset
#x100
)
1068 (vector-push offset code-vector
))))
1070 ((some (lambda (label) (assoc label code-symtab
))
1071 (mapcar #'car rest
))
1072 (vector-push 0 code-vector
))))
1073 (check-locate-concistency code-vector
)
1074 (setf (movitz-funobj-code-vector funobj
)
1075 (make-movitz-vector (length code-vector
)
1076 :fill-pointer code-length
1078 :initial-contents code-vector
))))
1081 (defun check-locate-concistency (code-vector)
1082 (loop for x from
0 below
(length code-vector
) by
8
1083 do
(when (and (= (tag :basic-vector
) (aref code-vector x
))
1084 (= (enum-value 'movitz-vector-element-type
:code
) (aref code-vector
(1+ x
)))
1085 (or (<= #x4000
(length code-vector
))
1086 (and (= (ldb (byte 8 0) (length code-vector
))
1087 (aref code-vector
(+ x
2)))
1088 (= (ldb (byte 8 8) (length code-vector
))
1089 (aref code-vector
(+ x
3))))))
1090 (break "Code-vector (length #x~X) can break %find-code-vector at ~D: #x~2,'0X~2,'0X ~2,'0X~2,'0X."
1091 (length code-vector
) x
1092 (aref code-vector
(+ x
0))
1093 (aref code-vector
(+ x
1))
1094 (aref code-vector
(+ x
2))
1095 (aref code-vector
(+ x
3)))))
1099 (defun make-2req (binding0 binding1 frame-map
)
1100 (let ((location-0 (new-binding-location binding0 frame-map
))
1101 (location-1 (new-binding-location binding1 frame-map
)))
1103 ((and (eq :eax location-0
)
1104 (eq :ebx location-1
))
1106 ((and (eq :ebx location-0
)
1107 (eq :eax location-1
))
1108 (values '((:xchgl
:eax
:ebx
)) 0))
1109 ((and (eql 1 location-0
)
1111 (values '((:pushl
:eax
)
1114 ((and (eq :eax location-0
)
1116 (values '((:pushl
:ebx
))
1118 (t (error "make-2req confused by loc0: ~W, loc1: ~W" location-0 location-1
)))))
1121 (defun movitz-compile-file (path &key
((:image
*image
*) *image
*)
1123 (delete-file-p nil
))
1125 (#+sbcl
(sb-ext:defconstant-uneql
#'continue
))
1127 (let ((*movitz-host-features
* *features
*)
1128 (*features
* (image-movitz-features *image
*)))
1129 (multiple-value-prog1
1130 (movitz-compile-file-internal path load-priority
)
1131 (unless (equalp *features
* (image-movitz-features *image
*))
1132 (warn "*features* changed from ~S to ~S." (image-movitz-features *image
*) *features
*)
1133 (setf (image-movitz-features *image
*) *features
*))))
1135 (assert (equal (pathname-directory "/tmp/")
1136 (pathname-directory path
))
1138 "Refusing to delete file not in /tmp.")
1139 (delete-file path
)))))
1141 (defun movitz-compile-file-internal (path &optional
(*default-load-priority
*
1142 (and (boundp '*default-load-priority
*)
1143 (symbol-value '*default-load-priority
*)
1144 (1+ (symbol-value '*default-load-priority
*)))))
1145 (declare (special *default-load-priority
*))
1146 (with-simple-restart (continue "Skip Movitz compilation of ~S." path
)
1147 (with-retries-until-true (retry "Restart Movitz compilation of ~S." path
)
1148 (with-open-file (stream path
:direction
:input
)
1149 (let ((*package
* (find-package :muerte
)))
1150 (movitz-compile-stream-internal stream
:path path
))))))
1152 (defun movitz-compile-stream (stream &key
(path "unknown-toplevel.lisp") (package :muerte
))
1154 (#+sbcl
(sb-ext:defconstant-uneql
#'continue
))
1156 (let ((*package
* (find-package package
))
1157 (*movitz-host-features
* *features
*)
1158 (*features
* (image-movitz-features *image
*)))
1159 (multiple-value-prog1
1160 (movitz-compile-stream-internal stream
:path path
)
1161 (unless (equalp *features
* (image-movitz-features *image
*))
1162 (warn "*features* changed from ~S to ~S." (image-movitz-features *image
*) *features
*)
1163 (setf (image-movitz-features *image
*) *features
*)))))))
1165 (defun movitz-compile-stream-internal (stream &key
(path "unknown-toplevel.lisp"))
1166 (let* ((muerte.cl
::*compile-file-pathname
* path
)
1167 (funobj (make-instance 'movitz-funobj-pass1
1168 :name
(intern (format nil
"~A" path
) :muerte
)
1169 :lambda-list
(movitz-read nil
)))
1170 (funobj-env (make-local-movitz-environment nil funobj
1172 :declaration-context
:funobj
))
1173 (function-env (make-local-movitz-environment funobj-env funobj
1175 :declaration-context
:funobj
))
1177 (with-compilation-unit ()
1178 (add-bindings-from-lambda-list () function-env
)
1179 (setf (funobj-env funobj
) funobj-env
)
1180 (loop for form
= (with-movitz-syntax ()
1181 (read stream nil
'#0=#:eof
))
1182 until
(eq form
'#0#)
1184 (with-simple-restart (skip-toplevel-form
1185 "Skip the compilation of top-level form~{ ~A~}."
1189 ((symbolp (car form
))
1192 (when *compiler-verbose-p
*
1193 (format *query-io
* "~&Movitz Compiling ~S..~%"
1195 ((symbolp form
) form
)
1196 ((symbolp (car form
))
1197 (xsubseq form
0 2)))))
1198 (compiler-call #'compile-form
1203 :result-mode
:ignore
))))))
1206 (setf (image-load-time-funobjs *image
*)
1207 (delete funobj
(image-load-time-funobjs *image
*) :key
#'first
))
1208 'muerte
::constantly-true
)
1209 (t (setf (extended-code function-env
) file-code
1210 (need-normalized-ecx-p function-env
) nil
1211 (function-envs funobj
) (list (cons 'muerte.cl
::t function-env
))
1212 (funobj-env funobj
) funobj-env
)
1213 (make-compiled-funobj-pass2 funobj
)
1214 (let ((name (funobj-name funobj
)))
1215 (setf (movitz-env-named-function name
) funobj
)
1220 (defun print-code (x code
)
1221 (let ((*print-level
* 4))
1222 (format t
"~&~A code:~{~& ~A~}" x code
))
1225 (defun layout-program (pc)
1226 "For the program in pc, layout sub-programs at the top-level program."
1227 (do ((previous-subs nil
)
1231 (assert (not pending-subs
) ()
1232 "pending sub-programs: ~S" pending-subs
)
1233 (nreverse new-program
))
1235 (multiple-value-bind (sub-prg sub-opts
)
1236 (instruction-sub-program i
)
1238 (push i new-program
)
1239 (destructuring-bind (&optional
(label (gensym "sub-prg-label-")))
1241 (let ((x (cons label sub-prg
)))
1242 (unless (find x previous-subs
:test
#'equal
)
1243 (push x pending-subs
)
1244 (push x previous-subs
)))
1245 (unless (instruction-is i
:jnever
)
1246 (push `(,(car i
) ',label
)
1248 (when (or (instruction-uncontinues-p i
)
1250 (let* ((match-label (and (eq (car i
) :jmp
)
1252 (eq (car (second i
)) 'quote
)
1253 (symbolp (second (second i
)))
1254 (second (second i
))))
1255 (matching-sub (assoc match-label pending-subs
)))
1256 (unless (and match-label
1257 (or (eq match-label
(first pc
))
1258 (and (symbolp (first pc
))
1259 (eq match-label
(second pc
)))))
1261 (setf pc
(append (cdr matching-sub
) pc
)
1262 pending-subs
(delete matching-sub pending-subs
))
1263 (setf pc
(append (reduce #'append
(nreverse pending-subs
)) pc
)
1264 pending-subs nil
)))))))))
1267 (defun optimize-code (unoptimized-code &rest args
)
1268 #+ignore
(print-code 'to-optimize unoptimized-code
)
1269 (if (not *compiler-do-optimize
*)
1270 (layout-program (optimize-code-unfold-branches unoptimized-code
))
1271 (apply #'optimize-code-internal
1272 (optimize-code-dirties
1273 (layout-program (optimize-code-unfold-branches unoptimized-code
)))
1276 (defun optimize-code-unfold-branches (unoptimized-code)
1277 "This particular optimization should be done before code layout:
1278 (:jcc 'label) (:jmp 'foo) label => (:jncc 'foo) label"
1279 (flet ((explain (always format
&rest args
)
1280 (when (or always
*explain-peephole-optimizations
*)
1281 (warn "Peephole: ~?~&----------------------------" format args
)))
1282 (branch-instruction-label (i &optional jmp
(branch-types '(:je
:jne
:jb
:jnb
:jbe
:jz
1283 :jl
:jnz
:jle
:ja
:jae
:jg
1284 :jge
:jnc
:jc
:js
:jns
)))
1285 "If i is a branch, return the label."
1286 (when jmp
(push :jmp branch-types
))
1287 (let ((i (ignore-instruction-prefixes i
)))
1288 (or (and (listp i
) (member (car i
) branch-types
)
1289 (listp (second i
)) (member (car (second i
)) '(quote muerte.cl
::quote
))
1290 (second (second i
))))))
1291 (negate-branch (branch-type)
1293 (:jb
:jnb
) (:jnb
:jb
)
1294 (:jbe
:ja
) (:ja
:jbe
)
1295 (:jz
:jnz
) (:jnz
:jz
)
1296 (:je
:jne
) (:jne
:je
)
1297 (:jc
:jnc
) (:jnc
:jc
)
1298 (:jl
:jge
) (:jge
:jl
)
1299 (:jle
:jg
) (:jg
:jle
))))
1300 (loop with next-pc
= 'auto-next
1301 ;; initially (warn "opt: ~{ ~A~%~}" unoptimized-code)
1302 for pc
= unoptimized-code then
(prog1 (if (eq 'auto-next next-pc
) auto-next-pc next-pc
)
1303 (setq next-pc
'auto-next
))
1304 as auto-next-pc
= (cdr unoptimized-code
) then
(cdr pc
)
1305 as p
= (list (car pc
)) ; will be appended.
1306 as i1
= (first pc
) ; current instruction, collected by default.
1307 and i2
= (second pc
) and i3
= (third pc
)
1309 do
(when (and (branch-instruction-label i1
)
1310 (branch-instruction-label i2 t nil
)
1312 (eq i3
(branch-instruction-label i1
)))
1313 (setf p
(list `(,(negate-branch (car i1
)) ',(branch-instruction-label i2 t nil
))
1315 next-pc
(nthcdr 3 pc
))
1316 (explain nil
"Got a sit: ~{~&~A~} => ~{~&~A~}" (subseq pc
0 3) p
))
1319 (defun optimize-code-dirties (unoptimized-code)
1320 "These optimizations may rearrange register usage in a way that is incompatible
1321 with other optimizations that track register usage. So this is performed just once,
1325 (labels ; This stuff doesn't work..
1326 ((explain (always format
&rest args
)
1327 (when (or always
*explain-peephole-optimizations
*)
1328 (warn "Peephole: ~?~&----------------------------" format args
)))
1329 (twop-p (c &optional op
)
1330 (let ((c (ignore-instruction-prefixes c
)))
1331 (and (listp c
) (= 3 (length c
))
1332 (or (not op
) (eq op
(first c
)))
1334 (twop-dst (c &optional op src
)
1335 (let ((c (ignore-instruction-prefixes c
)))
1337 (equal src
(first (twop-p c op
))))
1338 (second (twop-p c op
)))))
1339 (twop-src (c &optional op dest
)
1340 (let ((c (ignore-instruction-prefixes c
)))
1342 (equal dest
(second (twop-p c op
))))
1343 (first (twop-p c op
)))))
1344 (register-operand (op)
1345 (and (member op
'(:eax
:ebx
:ecx
:edx
:edi
))
1347 (loop with next-pc
= 'auto-next
1348 ;; initially (warn "opt: ~{ ~A~%~}" unoptimized-code)
1349 for pc
= unoptimized-code then
(prog1 (if (eq 'auto-next next-pc
) auto-next-pc next-pc
)
1350 (setq next-pc
'auto-next
))
1351 as auto-next-pc
= (cdr unoptimized-code
) then
(cdr pc
)
1352 as p
= (list (car pc
)) ; will be appended.
1353 as i1
= (first pc
) ; current instruction, collected by default.
1354 and i2
= (second pc
) and i3
= (third pc
)
1356 do
(let ((regx (register-operand (twop-src i1
:movl
)))
1357 (regy (register-operand (twop-dst i1
:movl
))))
1358 (when (and regx regy
1359 (eq regx
(twop-dst i2
:movl
))
1360 (eq regx
(twop-src i3
:cmpl
))
1361 (eq regy
(twop-dst i3
:cmpl
)))
1362 (setq p
(list `(:cmpl
,(twop-src i2
) ,regx
) i1
)
1363 next-pc
(nthcdr 3 pc
))
1364 (explain t
"4: ~S for ~S [regx ~S, regy ~S]" p
(subseq pc
0 5) regx regy
)))
1367 (defun xsubseq (sequence start end
)
1368 (subseq sequence start
(min (length sequence
) end
)))
1370 (defun optimize-code-internal (unoptimized-code recursive-count
&rest key-args
1371 &key keep-labels stack-frame-size
)
1372 "Peephole optimizer. Based on a lot of rather random heuristics."
1373 (declare (ignore stack-frame-size
))
1374 (when (<= 20 recursive-count
)
1375 (error "Peephole-optimizer recursive count reached ~D.
1376 There is (propably) a bug in the peephole optimizer." recursive-count
))
1377 ;; (warn "==================OPTIMIZE: ~{~&~A~}" unoptimized-code)
1378 (macrolet ((explain (always format
&rest args
)
1379 `(when (or *explain-peephole-optimizations
* ,always
)
1380 (warn "Peephole: ~@?~&----------------------------" ,format
,@args
))))
1383 (explain (always format
&rest args
)
1384 (when (or always
*explain-peephole-optimizations
*)
1385 (warn "Peephole: ~?~&----------------------------" format args
)))
1386 (twop-p (c &optional op
)
1387 (let ((c (ignore-instruction-prefixes c
)))
1388 (and (listp c
) (= 3 (length c
))
1389 (or (not op
) (eq op
(first c
)))
1391 (twop-dst (c &optional op src
)
1392 (let ((c (ignore-instruction-prefixes c
)))
1394 (equal src
(first (twop-p c op
))))
1395 (second (twop-p c op
)))))
1396 (twop-src (c &optional op dest
)
1397 (let ((c (ignore-instruction-prefixes c
)))
1399 (equal dest
(second (twop-p c op
))))
1400 (first (twop-p c op
)))))
1402 (let ((c (ignore-instruction-prefixes c
)))
1403 (ecase (length (cdr c
))
1408 (let ((c (ignore-instruction-prefixes c
)))
1409 (ecase (length (cdr c
))
1413 (non-destructive-p (c)
1414 (let ((c (ignore-instruction-prefixes c
)))
1416 (member (car c
) '(:testl
:testb
:cmpl
:cmpb
:frame-map
:std
)))))
1417 (simple-instruction-p (c)
1418 (let ((c (ignore-instruction-prefixes c
)))
1421 '(:movl
:xorl
:popl
:pushl
:cmpl
:leal
:andl
:addl
:subl
)))))
1422 (register-indirect-operand (op base
)
1423 (multiple-value-bind (reg off
)
1426 if
(integerp x
) sum x into off
1427 else collect x into reg
1428 finally
(return (values reg off
))))
1429 (and (eq base
(car reg
))
1432 (stack-frame-operand (op)
1433 (register-indirect-operand op
:ebp
))
1434 (funobj-constant-operand (op)
1435 (register-indirect-operand op
:esi
))
1436 (global-constant-operand (op)
1437 (register-indirect-operand op
:edi
))
1438 (global-funcall-p (op &optional funs
)
1439 (let ((op (ignore-instruction-prefixes op
)))
1440 (when (instruction-is op
:call
)
1441 (let ((x (global-constant-operand (second op
))))
1443 (and (eql x
(slot-offset 'movitz-run-time-context name
))
1448 ((atom funs
) (try funs
))
1449 (t (some #'try funs
))))))))
1450 (preserves-stack-location-p (i stack-location
)
1451 (let ((i (ignore-instruction-prefixes i
)))
1453 (or (global-funcall-p i
)
1454 (instruction-is i
:frame-map
)
1455 (branch-instruction-label i
)
1456 (non-destructive-p i
)
1457 (and (simple-instruction-p i
)
1458 (not (eql stack-location
(stack-frame-operand (idst i
)))))))))
1459 (preserves-register-p (i register
)
1460 (let ((i (ignore-instruction-prefixes i
)))
1462 (not (and (eq register
:esp
)
1463 (member (instruction-is i
)
1465 (or (and (simple-instruction-p i
)
1466 (not (eq register
(idst i
))))
1467 (instruction-is i
:frame-map
)
1468 (branch-instruction-label i
)
1469 (non-destructive-p i
)
1470 (and (member register
'(:edx
))
1471 (member (global-funcall-p i
)
1472 '(fast-car fast-cdr fast-car-ebx fast-cdr-ebx
)))
1473 (and (not (eq register
:esp
))
1474 (instruction-is i
:pushl
))))))
1475 (operand-register-indirect-p (operand register
)
1476 (and (consp operand
)
1477 (tree-search operand register
)))
1478 (doesnt-read-register-p (i register
)
1479 (let ((i (ignore-instruction-prefixes i
)))
1481 (and (simple-instruction-p i
)
1482 (if (member (instruction-is i
) '(:movl
))
1483 (and (not (eq register
(twop-src i
)))
1484 (not (operand-register-indirect-p (twop-src i
) register
))
1485 (not (operand-register-indirect-p (twop-dst i
) register
)))
1486 (not (or (eq register
(isrc i
))
1487 (operand-register-indirect-p (isrc i
) register
)
1488 (eq register
(idst i
))
1489 (operand-register-indirect-p (idst i
) register
)))))
1490 (instruction-is i
:frame-map
)
1491 (and (member register
'(:edx
))
1492 (member (global-funcall-p i
)
1493 '(fast-car fast-cdr fast-car-ebx fast-cdr-ebx
))))))
1494 (register-operand (op)
1495 (and (member op
'(:eax
:ebx
:ecx
:edx
:edi
))
1497 (true-and-equal (x &rest more
)
1498 (declare (dynamic-extent more
))
1499 (and x
(dolist (y more t
)
1502 (uses-stack-frame-p (c)
1504 (some #'stack-frame-operand
(cdr (ignore-instruction-prefixes c
)))))
1505 (load-stack-frame-p (c &optional
(op :movl
))
1506 (stack-frame-operand (twop-src c op
)))
1507 (store-stack-frame-p (c &optional
(op :movl
))
1508 (stack-frame-operand (twop-dst c op
)))
1509 (read-stack-frame-p (c)
1510 (or (load-stack-frame-p c
:movl
)
1511 (load-stack-frame-p c
:addl
)
1512 (load-stack-frame-p c
:subl
)
1513 (load-stack-frame-p c
:cmpl
)
1514 (store-stack-frame-p c
:cmpl
)
1517 (stack-frame-operand (second c
)))))
1518 (in-stack-frame-p (c reg
)
1519 "Does c ensure that reg is in some particular stack-frame location?"
1520 (or (and (load-stack-frame-p c
)
1521 (eq reg
(twop-dst c
))
1522 (stack-frame-operand (twop-src c
)))
1523 (and (store-stack-frame-p c
)
1524 (eq reg
(twop-src c
))
1525 (stack-frame-operand (twop-dst c
)))))
1526 (load-funobj-constant-p (c)
1527 (funobj-constant-operand (twop-src c
:movl
)))
1529 (sub-program-label-p (l)
1531 (eq :sub-program
(car l
))))
1533 (if (or (load-stack-frame-p c
)
1534 (load-funobj-constant-p c
))
1537 (label-here-p (label code
)
1538 "Is <label> at this point in <code>?"
1540 while
(or (symbolp i
)
1541 (instruction-is i
:frame-map
))
1542 thereis
(eq label i
)))
1543 (negate-branch (branch-type)
1545 (:jbe
:ja
) (:ja
:jbe
)
1546 (:jz
:jnz
) (:jnz
:jz
)
1547 (:je
:jne
) (:jne
:je
)
1548 (:jc
:jnc
) (:jnc
:jc
)
1549 (:jl
:jge
) (:jge
:jl
)
1550 (:jle
:jg
) (:jg
:jle
)))
1551 (branch-instruction-label (i &optional jmp
(branch-types '(:je
:jne
:jb
:jnb
:jbe
:jz
:jl
:jnz
1552 :jle
:ja
:jae
:jg
:jge
:jnc
:jc
:js
:jns
)))
1553 "If i is a branch, return the label."
1554 (when jmp
(push :jmp branch-types
))
1555 (let ((i (ignore-instruction-prefixes i
)))
1558 (member (car (second i
)) '(quote muerte.cl
::quote
))
1559 (member (car i
) branch-types
)
1560 (second (second i
)))
1565 (not (member (car i
) '(:jmp
:jecxz
)))
1566 (char= #\J
(char (symbol-name (car i
)) 0))
1567 (warn "Not a branch: ~A / ~A [~A]" i
(symbol-package (caadr i
)) branch-types
)))))
1568 (find-branches-to-label (start-pc label
&optional
(context-size 0))
1569 "Context-size is the number of instructions _before_ the branch you want returned."
1570 (dotimes (i context-size
)
1571 (push nil start-pc
))
1572 (loop for pc on start-pc
1573 as i
= (nth context-size pc
)
1574 as i-label
= (branch-instruction-label i t
)
1575 if
(or (eq label i-label
)
1576 (and (consp i-label
)
1577 (eq :label-plus-one
(car i-label
))))
1579 else if
(let ((sub-program i-label
))
1580 (and (consp sub-program
)
1581 (eq :sub-program
(car sub-program
))))
1582 nconc
(find-branches-to-label (cddr (branch-instruction-label i t
))
1584 else if
(and (not (atom i
))
1585 (tree-search i label
))
1586 nconc
(list 'unknown-label-usage
)))
1587 (optimize-trim-stack-frame (unoptimized-code)
1588 "Any unused local variables on the stack-frame?"
1590 ;; BUILD A MAP OF USED STACK-VARS AND REMAP THEM!
1591 #+ignore
(if (not (and stack-frame-size
1592 (find 'start-stack-frame-setup unoptimized-code
)))
1594 (let ((old-code unoptimized-code
)
1596 ;; copy everything upto start-stack-frame-setup
1597 (loop for i
= (pop old-code
)
1598 do
(push i new-code
)
1600 until
(eq i
'start-stack-frame-setup
))
1601 (assert (eq (car new-code
) 'start-stack-frame-setup
) ()
1602 "no start-stack-frame-setup label, but we already checked!")
1603 (loop for pos downfrom -
8 by
4
1604 as i
= (pop old-code
)
1605 if
(and (consp i
) (eq :pushl
(car i
)) (symbolp (cadr i
)))
1606 collect
(cons pos
(cadr i
))
1607 and do
(unless (find pos old-code
:key
#'read-stack-frame-p
)
1609 ((find pos old-code
:key
#'store-stack-frame-p
)
1610 (warn "Unused local but stored var: ~S" pos
))
1611 ((find pos old-code
:key
#'uses-stack-frame-p
)
1612 (warn "Unused BUT USED local var: ~S" pos
))
1613 (t (warn "Unused local var: ~S" pos
))))
1618 (frame-map-code (unoptimized-code)
1619 "After each label in unoptimized-code, insert a (:frame-map <full-map> <branch-map> <sticky>)
1620 that says which registers are known to hold which stack-frame-locations.
1621 A branch-map is the map that is guaranteed after every branch to the label, i.e. not including
1622 falling below the label."
1623 #+ignore
(warn "unmapped:~{~&~A~}" unoptimized-code
)
1624 (flet ((rcode-map (code)
1625 #+ignore
(when (instruction-is (car code
) :testb
)
1626 (warn "rcoding ~A" code
))
1627 (loop with modifieds
= nil
1628 with registers
= (list :eax
:ebx
:ecx
:edx
)
1629 with local-map
= nil
1632 do
(flet ((add-map (stack reg
)
1633 (when (and (not (member stack modifieds
))
1634 (member reg registers
))
1635 (push (cons stack reg
)
1637 (cond ((instruction-is ii
:frame-map
)
1638 (dolist (m (second ii
))
1639 (add-map (car m
) (cdr m
))))
1640 ((load-stack-frame-p ii
)
1641 (add-map (load-stack-frame-p ii
)
1643 ((store-stack-frame-p ii
)
1644 (add-map (store-stack-frame-p ii
)
1646 (pushnew (store-stack-frame-p ii
)
1648 ((non-destructive-p ii
))
1649 ((branch-instruction-label ii
))
1650 ((simple-instruction-p ii
)
1651 (let ((op (idst ii
)))
1653 ((stack-frame-operand op
)
1654 (pushnew (stack-frame-operand op
) modifieds
))
1656 (setf registers
(delete op registers
))))))
1657 (t #+ignore
(when (instruction-is (car code
) :testb
)
1658 (warn "stopped at ~A" ii
))
1661 (delete-if (lambda (r)
1662 (not (preserves-register-p ii r
)))
1665 #+ignore
(when (instruction-is (car code
) :testb
)
1666 (warn "..map ~A" local-map
))
1667 (return local-map
))))
1668 (loop with next-pc
= 'auto-next
1669 ;; initially (warn "opt: ~{ ~A~%~}" unoptimized-code)
1670 for pc
= unoptimized-code then
(prog1 (if (eq 'auto-next next-pc
) auto-next-pc next-pc
)
1671 (setq next-pc
'auto-next
))
1672 as auto-next-pc
= (cdr unoptimized-code
) then
(cdr pc
)
1673 as p
= (list (car pc
)) ; will be appended.
1674 as i1
= (first pc
) ; current instruction, collected by default.
1675 and i2
= (second pc
)
1677 do
(when (and (symbolp i1
)
1678 (not (and (instruction-is i2
:frame-map
)
1681 (branch-map (reduce (lambda (&optional x y
)
1682 (intersection x y
:test
#'equal
))
1683 (mapcar (lambda (lpc)
1684 (if (eq 'unknown-label-usage lpc
)
1686 (rcode-map (nreverse (xsubseq lpc
0 9)))))
1687 (find-branches-to-label unoptimized-code label
9))))
1688 (full-map (let ((rcode (nreverse (let* ((pos (loop for x on unoptimized-code
1691 finally
(return pos
)))
1692 (back9 (max 0 (- pos
9))))
1693 (subseq unoptimized-code
1695 (if (instruction-uncontinues-p (car rcode
))
1697 (intersection branch-map
(rcode-map rcode
) :test
#'equal
)))))
1698 (when (or full-map branch-map nil
)
1700 (explain nil
"Inserting at ~A frame-map ~S branch-map ~S."
1701 label full-map branch-map
))
1702 (setq p
(list label
`(:frame-map
,full-map
,branch-map
))
1703 next-pc
(if (instruction-is i2
:frame-map
)
1707 (optimize-stack-frame-init (unoptimized-code)
1708 "Look at the function's stack-frame initialization code, and see
1709 if we can optimize that, and/or immediately subsequent loads/stores."
1710 (if (not (find 'start-stack-frame-setup unoptimized-code
))
1712 (let ((old-code unoptimized-code
)
1714 ;; copy everything upto start-stack-frame-setup
1715 (loop for i
= (pop old-code
)
1716 do
(push i new-code
)
1718 until
(eq i
'start-stack-frame-setup
))
1719 (assert (eq (car new-code
) 'start-stack-frame-setup
) ()
1720 "no start-stack-frame-setup label, but we already checked!")
1721 (let* ((frame-map (loop with pos
= -
8
1722 as i
= (pop old-code
)
1723 if
(instruction-is i
:frame-map
)
1726 (and (consp i
) (eq :pushl
(car i
)) (symbolp (cadr i
)))
1735 (mod-p (loop with mod-p
= nil
1736 for i
= `(:frame-map
,(copy-list frame-map
) nil t
)
1739 do
(let ((new-i (cond
1740 ((let ((store-pos (store-stack-frame-p i
)))
1742 (eq (cdr (assoc store-pos frame-map
))
1744 (explain nil
"removed stack-init store: ~S" i
)
1746 ((let ((load-pos (load-stack-frame-p i
)))
1748 (eq (cdr (assoc load-pos frame-map
))
1750 (explain nil
"removed stack-init load: ~S" i
)
1752 ((and (load-stack-frame-p i
)
1753 (assoc (load-stack-frame-p i
) frame-map
))
1754 (let ((old-reg (cdr (assoc (load-stack-frame-p i
)
1756 (explain nil
"load ~S already in ~S."
1758 `(:movl
,old-reg
,(twop-dst i
))))
1759 ((and (instruction-is i
:pushl
)
1760 (stack-frame-operand (idst i
))
1761 (assoc (stack-frame-operand (idst i
))
1764 (cdr (assoc (stack-frame-operand (idst i
))
1766 (explain nil
"push ~S already in ~S."
1768 `(:pushl
,old-reg
)))
1770 (unless (eq new-i i
)
1772 (when (branch-instruction-label new-i t
)
1774 (push `(:frame-map
,(copy-list frame-map
) nil t
)
1777 (push new-i new-code
)
1778 ;; (warn "new-i: ~S, fm: ~S" new-i frame-map)
1780 (delete-if (lambda (map)
1781 ;; (warn "considering: ~S" map)
1782 (not (and (preserves-register-p new-i
(cdr map
))
1783 (preserves-stack-location-p new-i
1786 ;; (warn "Frame-map now: ~S" frame-map)
1787 (when (store-stack-frame-p new-i
)
1788 (loop for map in frame-map
1789 do
(when (= (store-stack-frame-p new-i
)
1791 (setf (cdr map
) (twop-src new-i
)))))))
1793 finally
(return mod-p
))))
1796 (append (nreverse new-code
)
1798 (remove-frame-maps (code)
1799 (remove-if (lambda (x)
1800 (typep x
'(cons (eql :frame-map
) *)))
1802 (let* ((unoptimized-code (frame-map-code (optimize-stack-frame-init unoptimized-code
)))
1803 (code-modified-p nil
)
1804 (stack-frame-used-map (loop with map
= nil
1805 for i in unoptimized-code
1806 do
(let ((x (read-stack-frame-p i
)))
1807 (when x
(pushnew x map
)))
1808 (when (and (instruction-is i
:leal
)
1809 (stack-frame-operand (twop-src i
)))
1810 (let ((x (stack-frame-operand (twop-src i
))))
1811 (when (= (tag :cons
) (ldb (byte 2 0) x
))
1812 (pushnew (+ x -
1) map
)
1813 (pushnew (+ x
3) map
))))
1814 finally
(return map
)))
1816 ;; This loop applies a set of (hard-coded) heuristics on unoptimized-code.
1817 (loop with next-pc
= 'auto-next
1818 ;; initially (warn "opt: ~{ ~A~%~}" unoptimized-code)
1819 for pc
= unoptimized-code then
(prog1 (if (eq 'auto-next next-pc
) auto-next-pc next-pc
)
1820 (setq next-pc
'auto-next
))
1821 as auto-next-pc
= (cdr unoptimized-code
) then
(cdr pc
)
1822 as p
= (list (car pc
)) ; will be appended.
1824 as i
= (first pc
) ; current instruction, collected by default.
1825 and i2
= (second pc
) and i3
= (third pc
) and i4
= (fourth pc
) and i5
= (fifth pc
)
1828 ((and (instruction-is i
:frame-map
)
1829 (instruction-is i2
:frame-map
)
1832 (let ((map (union (second i
) (second i2
) :test
#'equal
)))
1833 (explain nil
"Merged maps:~%~A + ~A~% => ~A"
1834 (second i
) (second i2
) map
)
1835 (setq p
`((:frame-map
,map
))
1836 next-pc
(cddr pc
))))
1837 ((let ((x (store-stack-frame-p i
)))
1838 (and x
(not (member x stack-frame-used-map
))))
1840 (explain nil
"Removed store of unused local var: ~S" i
))
1841 ((and (global-funcall-p i2
'(fast-car))
1842 (global-funcall-p i5
'(fast-cdr))
1843 (true-and-equal (in-stack-frame-p i
:eax
)
1844 (in-stack-frame-p i4
:eax
)))
1845 (let ((call-prefix (if (consp (car i2
)) (car i2
) nil
)))
1847 ((equal i3
'(:pushl
:eax
))
1848 (explain nil
"merge car,push,cdr to cdr-car,push")
1850 `(,call-prefix
:call
1851 (:edi
,(global-constant-offset 'fast-cdr-car
)))
1853 next-pc
(nthcdr 5 pc
)))
1854 ((and (store-stack-frame-p i3
)
1855 (eq :eax
(twop-src i3
)))
1856 (explain nil
"merge car,store,cdr to cdr-car,store")
1858 `(,call-prefix
:call
1859 (:edi
,(global-constant-offset 'fast-cdr-car
)))
1860 `(:movl
:ebx
,(twop-dst i3
)))
1861 next-pc
(nthcdr 5 pc
)))
1862 (t (error "can't deal with cdr-car here: ~{~&~A~}" (subseq pc
0 8))))))
1863 ((flet ((try (place register
&optional map reason
)
1864 "See if we can remove a stack-frame load below current pc,
1865 given the knowledge that <register> is equal to <place>."
1868 (dolist (si (cdr pc
))
1869 (when (and (twop-p si
:cmpl
)
1870 (equal place
(twop-src si
)))
1871 (warn "Reverse cmp not yet dealed with.."))
1873 ((and (twop-p si
:cmpl
)
1874 (equal place
(twop-dst si
)))
1876 ((equal place
(local-load-p si
))
1878 ((or (not (consp si
))
1879 (not (preserves-register-p si register
))
1880 (equal place
(twop-dst si
)))
1883 (remove-if (lambda (m)
1884 (not (preserves-register-p si
(cdr m
))))
1886 (case (instruction-is next-load
)
1888 (let ((pos (position next-load pc
)))
1889 (setq p
(nconc (subseq pc
0 pos
)
1890 (if (or (eq register
(twop-dst next-load
))
1891 (find-if (lambda (m)
1892 (and (eq (twop-dst next-load
) (cdr m
))
1893 (= (car m
) (stack-frame-operand place
))))
1896 (list `(:movl
,register
,(twop-dst next-load
)))))
1897 next-pc
(nthcdr (1+ pos
) pc
))
1898 (explain nil
"preserved load/store .. load ~S of place ~S because ~S."
1899 next-load place reason
)))
1901 (let ((pos (position next-load pc
)))
1902 (setq p
(nconc (subseq pc
0 pos
)
1903 (list `(:cmpl
,(twop-src next-load
) ,register
)))
1904 next-pc
(nthcdr (1+ pos
) pc
))
1905 (explain nil
"preserved load/store..cmp: ~S" p next-load
))))
1906 (if next-load t nil
))))
1907 (or (when (instruction-is i
:frame-map
)
1908 (loop for
(place . register
) in
(second i
)
1909 ;;; do (warn "map try ~S ~S: ~S" place register
1910 ;;; (try place register))
1911 thereis
(try `(:ebp
,place
) register
(second i
) :frame-map
)))
1912 (try (or (local-load-p i
)
1913 (and (store-stack-frame-p i
)
1915 (if (store-stack-frame-p i
)
1920 (instruction-is i2
:frame-map
)
1921 (load-stack-frame-p i3
)
1923 (cdr (assoc (load-stack-frame-p i3
) (third i2
))))
1924 (not (assoc (load-stack-frame-p i3
) (second i2
))))
1925 (let ((reg (cdr (assoc (load-stack-frame-p i3
) (third i2
)))))
1926 (explain nil
"factor out load from loop: ~S" i3
)
1927 (assert (eq reg
(twop-dst i3
)))
1928 (setq p
(if (eq reg
(twop-dst i3
))
1930 (append (list i3 i i2
)
1931 `((:movl
,reg
,(twop-dst i3
)))))
1932 next-pc
(cdddr pc
))))
1933 ;; ((:movl <foo> <bar>) label (:movl <zot> <bar>))
1934 ;; => (label (:movl <zot> <bar>))
1935 ((and (instruction-is i
:movl
)
1937 (and (not (branch-instruction-label i2
))
1938 (symbolp (twop-dst i
))
1939 (doesnt-read-register-p i2
(twop-dst i
))))
1940 (instruction-is i3
:frame-map
)
1941 (instruction-is i4
:movl
)
1942 (equal (twop-dst i
) (twop-dst i4
))
1943 (not (and (symbolp (twop-dst i
))
1944 (operand-register-indirect-p (twop-src i4
)
1946 (setq p
(list i2 i3 i4
)
1947 next-pc
(nthcdr 4 pc
))
1948 (explain nil
"Removed redundant store before ~A: ~A"
1949 i2
(subseq pc
0 4)))
1950 ((and (instruction-is i
:movl
)
1951 (not (branch-instruction-label i2
))
1952 (symbolp (twop-dst i
))
1953 (doesnt-read-register-p i2
(twop-dst i
))
1954 (instruction-is i3
:movl
)
1955 (equal (twop-dst i
) (twop-dst i3
))
1956 (not (and (symbolp (twop-dst i
))
1957 (operand-register-indirect-p (twop-src i3
)
1959 (setq p
(list i2 i3
)
1960 next-pc
(nthcdr 3 pc
))
1961 (explain nil
"Removed redundant store before ~A: ~A"
1962 i2
(subseq pc
0 3)))
1964 ((let ((stack-pos (store-stack-frame-p i
)))
1966 (loop with search-pc
= (cdr pc
)
1969 for ii
= (pop search-pc
)
1970 thereis
(eql stack-pos
1971 (store-stack-frame-p ii
))
1972 while
(or (global-funcall-p ii
)
1973 (and (simple-instruction-p ii
)
1975 (uses-stack-frame-p ii
))))))
1978 (store-stack-frame-p i4
))
1981 (or (global-funcall-p ii
)
1982 (and (simple-instruction-p ii
)
1984 (uses-stack-frame-p ii
))))))
1988 (explain t
"removing redundant store at ~A"
1989 (subseq pc
0 (min 10 (length pc
)))))
1990 ((and (member (instruction-is i
)
1991 '(:cmpl
:cmpb
:cmpw
:testl
:testb
:testw
))
1992 (member (instruction-is i2
)
1993 '(:cmpl
:cmpb
:cmpw
:testl
:testb
:testw
)))
1995 next-pc
(nthcdr 2 pc
))
1996 (explain nil
"Trimmed double test: ~A" (subseq pc
0 4)))
1997 ;; ((:jmp x) ...(no labels).... x ..)
1999 ((let ((x (branch-instruction-label i t nil
)))
2000 (and (position x
(cdr pc
))
2001 (not (find-if #'symbolp
(cdr pc
) :end
(position x
(cdr pc
))))))
2002 (explain nil
"jmp x .. x: ~W"
2003 (subseq pc
0 (1+ (position (branch-instruction-label i t nil
)
2006 next-pc
(member (branch-instruction-label i t nil
) pc
)))
2007 ;; (:jcc 'x) .... x (:jmp 'y) ..
2008 ;; => (:jcc 'y) .... x (:jmp 'y) ..
2009 ((let* ((from (branch-instruction-label i t
))
2010 (dest (member (branch-instruction-label i t
)
2012 (to (branch-instruction-label (if (instruction-is (second dest
) :frame-map
)
2016 (when (and from to
(not (eq from to
)))
2017 (setq p
(list `(,(car i
) ',to
)))
2018 (explain nil
"branch redirect from ~S to ~S" from to
)
2020 ;; remove back-to-back std/cld
2021 ((and (instruction-is i
:cld
)
2022 (instruction-is i2
:std
))
2023 (explain nil
"removing back-to-back cld, std.")
2024 (setq p nil next-pc
(cddr pc
)))
2025 ;; remove branch no-ops.
2026 ((and (branch-instruction-label i t
)
2027 (label-here-p (branch-instruction-label i t
)
2029 (explain nil
"branch no-op: ~A" i
)
2032 (null (symbol-package i
))
2033 (null (find-branches-to-label unoptimized-code i
))
2034 (not (member i keep-labels
)))
2036 next-pc
(if (instruction-is i2
:frame-map
)
2039 (explain nil
"unused label: ~S" i
))
2040 ;; ((:jcc 'label) (:jmp 'y) label) => ((:jncc 'y) label)
2041 ((and (branch-instruction-label i
)
2042 (branch-instruction-label i2 t nil
)
2044 (eq (branch-instruction-label i
) i3
))
2045 (setq p
(list `(,(negate-branch (first i
))
2046 ',(branch-instruction-label i2 t nil
)))
2047 next-pc
(nthcdr 2 pc
))
2048 (explain nil
"collapsed double negative branch to ~S: ~A." i3 p
))
2049 ((and (branch-instruction-label i
)
2050 (instruction-is i2
:frame-map
)
2051 (branch-instruction-label i3 t nil
)
2053 (eq (branch-instruction-label i
) i4
))
2054 (setq p
(list `(,(negate-branch (first i
))
2055 ',(branch-instruction-label i3 t nil
)))
2056 next-pc
(nthcdr 3 pc
))
2057 (explain nil
"collapsed double negative branch to ~S: ~A." i4 p
))
2058 ((and (twop-p i
:movl
)
2059 (register-operand (twop-src i
))
2060 (register-operand (twop-dst i
))
2062 (eq (twop-dst i
) (twop-dst i2
))
2063 (register-indirect-operand (twop-src i2
) (twop-dst i
)))
2064 (setq p
(list `(:movl
(,(twop-src i
)
2065 ,(register-indirect-operand (twop-src i2
)
2068 next-pc
(nthcdr 2 pc
))
2069 (explain nil
"(movl edx eax) (movl (eax <z>) eax) => (movl (edx <z>) eax: ~S"
2071 ((and (twop-p i
:movl
)
2072 (instruction-is i2
:pushl
)
2073 (eq (twop-dst i
) (second i2
))
2075 (eq (twop-dst i
) (twop-dst i3
)))
2076 (setq p
(list `(:pushl
,(twop-src i
)))
2077 next-pc
(nthcdr 2 pc
))
2078 (explain nil
"(movl <z> :eax) (pushl :eax) => (pushl <z>): ~S" p
))
2079 ((and (instruction-uncontinues-p i
)
2080 (not (or (symbolp i2
)
2081 #+ignore
(member (instruction-is i2
) '(:foobar
)))))
2082 (do ((x (cdr pc
) (cdr x
)))
2085 ((not (or (symbolp (car x
))
2086 #+ignore
(member (instruction-is (car x
)) '(:foobar
))))
2087 (explain nil
"Removing unreachable code ~A after ~A." (car x
) i
))
2091 ((and (store-stack-frame-p i
)
2092 (load-stack-frame-p i2
)
2093 (load-stack-frame-p i3
)
2094 (= (store-stack-frame-p i
)
2095 (load-stack-frame-p i3
))
2096 (not (eq (twop-dst i2
) (twop-dst i3
))))
2097 (setq p
(list i
`(:movl
,(twop-src i
) ,(twop-dst i3
)) i2
)
2098 next-pc
(nthcdr 3 pc
))
2099 (explain nil
"store, z, load => store, move, z: ~A" p
))
2100 ((and (instruction-is i
:movl
)
2101 (member (twop-dst i
) '(:eax
:ebx
:ecx
:edx
))
2102 (instruction-is i2
:pushl
)
2103 (not (member (second i2
) '(:eax
:ebx
:ecx
:edx
)))
2104 (equal (twop-src i
) (second i2
)))
2105 (setq p
(list i
`(:pushl
,(twop-dst i
)))
2106 next-pc
(nthcdr 2 pc
))
2107 (explain t
"load, push => load, push reg."))
2108 ((and (instruction-is i
:movl
)
2109 (member (twop-src i
) '(:eax
:ebx
:ecx
:edx
))
2110 (instruction-is i2
:pushl
)
2111 (not (member (second i2
) '(:eax
:ebx
:ecx
:edx
)))
2112 (equal (twop-dst i
) (second i2
)))
2113 (setq p
(list i
`(:pushl
,(twop-src i
)))
2114 next-pc
(nthcdr 2 pc
))
2115 (explain nil
"store, push => store, push reg: ~S ~S" i i2
))
2116 ;;; ((and (instruction-is i :cmpl)
2117 ;;; (true-and-equal (stack-frame-operand (twop-dst i))
2118 ;;; (load-stack-frame-p i3))
2119 ;;; (branch-instruction-label i2))
2120 ;;; (setf p (list i3
2121 ;;; `(:cmpl ,(twop-src i) ,(twop-dst i3))
2123 ;;; next-pc (nthcdr 3 pc))
2124 ;;; (explain t "~S ~S ~S => ~S" i i2 i3 p))
2125 ((and (instruction-is i
:pushl
)
2126 (instruction-is i3
:popl
)
2127 (store-stack-frame-p i2
)
2128 (store-stack-frame-p i4
)
2129 (eq (idst i3
) (twop-src i4
)))
2131 `(:movl
,(idst i
) ,(twop-dst i4
))
2132 `(:movl
,(idst i
) ,(idst i3
)))
2133 next-pc
(nthcdr 4 pc
))
2134 (explain nil
"~S => ~S" (subseq pc
0 4) p
))
2136 ((let ((i6 (nth 6 pc
)))
2137 (and (global-funcall-p i2
'(fast-car))
2138 (global-funcall-p i6
'(fast-cdr))
2139 (load-stack-frame-p i
)
2140 (eq :eax
(twop-dst i
))
2142 ((and (equal i
'(:movl
:ebx
:eax
))
2143 (global-funcall-p i2
'(fast-car fast-cdr
)))
2144 (let ((newf (ecase (global-funcall-p i2
'(fast-car fast-cdr
))
2145 (fast-car 'fast-car-ebx
)
2146 (fast-cdr 'fast-cdr-ebx
))))
2147 (setq p
`((:call
(:edi
,(global-constant-offset newf
))))
2148 next-pc
(nthcdr 2 pc
))
2149 (explain nil
"Changed [~S ~S] to ~S" i i2 newf
)))
2150 ((and (equal i
'(:movl
:eax
:ebx
))
2151 (global-funcall-p i2
'(fast-car-ebx fast-cdr-ebx
)))
2152 (let ((newf (ecase (global-funcall-p i2
'(fast-car-ebx fast-cdr-ebx
))
2153 (fast-car-ebx 'fast-car
)
2154 (fast-cdr-ebx 'fast-cdr
))))
2155 (setq p
`((:call
(:edi
,(global-constant-offset newf
))))
2156 next-pc
(nthcdr 2 pc
))
2157 (explain nil
"Changed [~S ~S] to ~S" i i2 newf
)))
2159 ((and (global-funcall-p i
'(fast-cdr))
2160 (global-funcall-p i2
'(fast-cdr))
2161 (global-funcall-p i3
'(fast-cdr)))
2162 (setq p
`((:call
(:edi
,(global-constant-offset 'fast-cdddr
))))
2163 next-pc
(nthcdr 3 pc
))
2164 (explain nil
"Changed (cdr (cdr (cdr :eax))) to (cdddr :eax)."))
2165 ((and (global-funcall-p i
'(fast-cdr))
2166 (global-funcall-p i2
'(fast-cdr)))
2167 (setq p
`((:call
(:edi
,(global-constant-offset 'fast-cddr
))))
2168 next-pc
(nthcdr 2 pc
))
2169 (explain nil
"Changed (cdr (cdr :eax)) to (cddr :eax)."))
2170 ((and (load-stack-frame-p i
) (eq :eax
(twop-dst i
))
2171 (global-funcall-p i2
'(fast-car fast-cdr
))
2172 (preserves-stack-location-p i3
(load-stack-frame-p i
))
2173 (preserves-register-p i3
:ebx
)
2174 (eql (load-stack-frame-p i
)
2175 (load-stack-frame-p i4
)))
2176 (let ((newf (ecase (global-funcall-p i2
'(fast-car fast-cdr
))
2177 (fast-car 'fast-car-ebx
)
2178 (fast-cdr 'fast-cdr-ebx
))))
2179 (setq p
`((:movl
,(twop-src i
) :ebx
)
2180 (:call
(:edi
,(global-constant-offset newf
)))
2182 ,@(unless (eq :ebx
(twop-dst i4
))
2183 `((:movl
:ebx
,(twop-dst i4
)))))
2184 next-pc
(nthcdr 4 pc
))
2185 (explain nil
"load around ~A: ~{~&~A~}~%=>~% ~{~&~A~}"
2186 newf
(subseq pc
0 5) p
))))
2187 do
(unless (eq p original-p
) ; auto-detect whether any heuristic fired..
2188 #+ignore
(warn "at ~A, ~A inserted ~A" i i2 p
)
2189 #+ignore
(warn "modified at ~S ~S ~S" i i2 i3
)
2190 (setf code-modified-p t
))
2193 (apply #'optimize-code-internal optimized-code
(1+ recursive-count
) key-args
)
2194 (optimize-trim-stack-frame (remove-frame-maps unoptimized-code
)))))))
2195 ;;;; Compiler internals
2197 (defclass binding
()
2200 :accessor binding-name
)
2202 :accessor binding-env
)
2204 :initarg
:declarations
2205 :accessor binding-declarations
)
2207 :accessor binding-extent-env
2210 (defmethod (setf binding-env
) :after
(env (binding binding
))
2211 (unless (binding-extent-env binding
)
2212 (setf (binding-extent-env binding
) env
)))
2214 (defmethod print-object ((object binding
) stream
)
2215 (print-unreadable-object (object stream
:type t
:identity t
)
2216 (when (slot-boundp object
'name
)
2217 (format stream
"name: ~S~@[->~S~]~@[ %~A~]"
2218 (and (slot-boundp object
'name
)
2219 (binding-name object
))
2220 (when (and (binding-target object
)
2221 (not (eq object
(binding-target object
))))
2222 (binding-name (forwarding-binding-target object
)))
2223 (when (and (slot-exists-p object
'store-type
)
2224 (slot-boundp object
'store-type
)
2225 (binding-store-type object
))
2226 (or (apply #'encoded-type-decode
2227 (binding-store-type object
))
2230 (defclass constant-object-binding
(binding)
2233 :reader constant-object
)))
2235 (defmethod binding-lended-p ((binding constant-object-binding
)) nil
)
2236 (defmethod binding-store-type ((binding constant-object-binding
))
2237 (multiple-value-list (type-specifier-encode `(eql ,(constant-object binding
)))))
2240 (defclass operator-binding
(binding) ())
2242 (defclass macro-binding
(operator-binding)
2245 :accessor macro-binding-expander
)))
2247 (defclass symbol-macro-binding
(binding)
2250 :accessor macro-binding-expander
)))
2252 (defclass variable-binding
(binding)
2253 ((lending ; a property-list
2255 :accessor binding-lending
)
2256 (store-type ; union of all types ever stored here
2258 ;; :initarg :store-type
2259 :accessor binding-store-type
)))
2261 (defmethod binding-lended-p ((binding variable-binding
))
2262 (and (getf (binding-lending binding
) :lended-to
)
2263 (not (eq :unused
(getf (binding-lending binding
) :lended-to
)))))
2265 (defclass lexical-binding
(variable-binding) ())
2266 (defclass located-binding
(lexical-binding) ())
2268 (defclass function-binding
(operator-binding located-binding
)
2271 :accessor function-binding-funobj
)
2273 :initarg
:parent-funobj
2274 :reader function-binding-parent
)))
2276 (defclass funobj-binding
(function-binding) ())
2277 (defclass closure-binding
(function-binding located-binding
) ())
2278 (defclass lambda-binding
(function-binding) ())
2280 (defclass temporary-name
(located-binding)
2283 (defclass borrowed-binding
(located-binding)
2285 :initarg
:reference-slot
2286 :accessor borrowed-binding-reference-slot
)
2288 :initarg
:target-binding
2289 :reader borrowed-binding-target
)
2293 :accessor borrowed-binding-usage
)))
2295 (defclass lexical-borrowed-binding
(borrowed-binding)
2296 ((stack-frame-distance
2297 :initarg
:stack-frame-distance
2298 :reader stack-frame-distance
))
2299 (:documentation
"A closure with lexical extent borrows bindings using this class."))
2301 (defclass indefinite-borrowed-binding
(borrowed-binding)
2303 :initarg
:reference-slot
2304 :reader borrowed-binding-reference-slot
)))
2307 (defclass constant-reference-binding
(lexical-binding)
2310 :reader constant-reference-object
)))
2313 (defmethod print-object ((object constant-reference-binding
) stream
)
2314 (print-unreadable-object (object stream
:type t
:identity t
)
2315 (format stream
"object: ~S" (constant-reference-object object
)))
2318 (defclass forwarding-binding
(lexical-binding)
2320 :initarg
:target-binding
2321 :accessor forwarding-binding-target
)))
2323 (defmethod binding-funobj ((binding binding
))
2324 (movitz-environment-funobj (binding-env binding
)))
2326 (defmethod binding-funobj ((binding forwarding-binding
))
2327 (movitz-environment-funobj (binding-env (forwarding-binding-target binding
))))
2329 (defclass function-argument
(located-binding) ())
2330 (defclass edx-function-argument
(function-argument) ())
2332 (defclass positional-function-argument
(function-argument)
2335 :reader function-argument-argnum
)))
2337 (defclass required-function-argument
(positional-function-argument) ())
2339 (defclass register-required-function-argument
(required-function-argument) ())
2340 (defclass fixed-required-function-argument
(required-function-argument)
2343 :reader binding-numargs
)))
2344 (defclass floating-required-function-argument
(required-function-argument) ())
2346 (defclass non-required-function-argument
(function-argument)
2349 :reader optional-function-argument-init-form
)
2351 :initarg supplied-p-var
2352 :reader optional-function-argument-supplied-p-var
)))
2354 (defclass optional-function-argument
(non-required-function-argument positional-function-argument
) ())
2356 (defclass supplied-p-function-argument
(function-argument) ())
2358 (defclass rest-function-argument
(positional-function-argument) ())
2360 (defclass keyword-function-argument
(non-required-function-argument)
2362 :initarg
:keyword-name
2363 :reader keyword-function-argument-keyword-name
)))
2365 (defclass dynamic-binding
(variable-binding) ())
2367 (defclass shadowing-binding
(binding) ())
2369 (defclass shadowing-dynamic-binding
(dynamic-binding shadowing-binding
)
2371 :initarg
:shadowed-variable
2372 :reader shadowed-variable
)
2374 :initarg
:shadowing-variable
2375 :reader shadowing-variable
)))
2377 (defmethod binding-store-type ((binding dynamic-binding
))
2378 (multiple-value-list (type-specifier-encode t
)))
2380 (defun stack-frame-offset (stack-frame-position)
2381 (* -
4 (1+ stack-frame-position
)))
2383 (defun argument-stack-offset (binding)
2384 (check-type binding fixed-required-function-argument
)
2385 (argument-stack-offset-shortcut (binding-numargs binding
)
2386 (function-argument-argnum binding
)))
2388 (defun argument-stack-offset-shortcut (numargs argnum
)
2389 "For a function of <numargs> arguments, locate the ebp-relative position
2390 of argument <argnum>."
2391 (* 4 (- numargs -
1 argnum
)))
2395 ;;; New style of locating bindings. The point is to not side-effect the binding objects.
2397 (defun new-binding-location (binding map
&key
(default nil default-p
))
2398 (check-type binding
(or binding
(cons keyword binding
)))
2399 (let ((x (assoc binding map
)))
2403 (t (error "No location for ~S." binding
)))))
2405 (defun make-binding-map () nil
)
2407 (defun new-binding-located-p (binding map
)
2408 (check-type binding
(or null binding
(cons keyword binding
)))
2409 (and (assoc binding map
) t
))
2411 (defun frame-map-size (map)
2415 (if (integerp (cdr x
))
2419 (defun frame-map-next-free-location (frame-map env
&optional
(size 1))
2420 (labels ((stack-location (binding)
2421 (if (typep binding
'forwarding-binding
)
2422 (stack-location (forwarding-binding-target binding
))
2423 (new-binding-location binding frame-map
:default nil
)))
2424 (env-extant (env1 env2
)
2425 "Is env1 active whenever env2 is active?"
2430 ;; (warn "~S shadowed by ~S" env env2)
2432 (t (env-extant env1
(movitz-environment-extent-uplink env2
))))))
2433 (let ((frame-size (frame-map-size frame-map
)))
2434 (or (loop for location from
1 to frame-size
2436 (loop for sub-location from location below
(+ location size
)
2438 (find-if (lambda (b-loc)
2439 (destructuring-bind (binding . binding-location
)
2441 (or (and (eq binding nil
) ; nil means "back off!"
2442 (eql sub-location binding-location
))
2443 (and (not (bindingp binding
))
2444 (eql sub-location binding-location
))
2445 (and (bindingp binding
)
2446 (eql sub-location
(stack-location binding
))
2450 (or (env-extant (binding-env b
) env
)
2451 (env-extant env
(binding-env b
))
2452 (when (typep b
'forwarding-binding
)
2453 (z (forwarding-binding-target b
)))))))
2457 (1+ frame-size
))))) ; no free location found, so grow frame-size.
2459 (define-setf-expander new-binding-location
(binding map-place
&environment env
)
2460 (multiple-value-bind (temps values stores setter getter
)
2461 (get-setf-expansion map-place env
)
2462 (let ((new-value (gensym))
2463 (binding-var (gensym)))
2464 (values (append temps
(list binding-var
))
2465 (append values
(list binding
))
2467 `(let ((,(car stores
) (progn
2468 (assert (or (null binding
)
2469 (not (new-binding-located-p ,binding-var
,getter
))))
2470 (check-type ,new-value
(or keyword
2473 (cons (eql :argument-stack
) *)))
2474 (acons ,binding-var
,new-value
,getter
))))
2477 `(new-binding-location ,binding-var
,getter
)))))
2479 ;;; Objects with dynamic extent may be located on the stack-frame, which at
2480 ;;; compile-time is represented with this structure.
2482 ;;;(defclass stack-allocated-object ()
2484 ;;; ;; Size in words (4 octets) this object occupies in the stack-frame.
2488 ;;; ;; Stack-frame offset (in words) this object is allocated to.
2489 ;;; :accessor location)))
2495 (defun ignore-instruction-prefixes (instruction)
2496 (if (and (consp instruction
)
2497 (listp (car instruction
)))
2501 (defun instruction-sub-program (instruction)
2502 "When an instruction contains a sub-program, return that program, and
2503 the sub-program options (&optional label) as secondary value."
2504 (let ((instruction (ignore-instruction-prefixes instruction
)))
2505 (and (consp instruction
)
2506 (consp (second instruction
))
2507 (symbolp (car (second instruction
)))
2508 (string= 'quote
(car (second instruction
)))
2509 (let ((x (second (second instruction
))))
2511 (eq :sub-program
(car x
))
2515 (defun instruction-is (instruction &optional operator
)
2516 (and (listp instruction
)
2517 (if (member (car instruction
) '(:globally
:locally
))
2518 (instruction-is (second instruction
) operator
)
2519 (let ((instruction (ignore-instruction-prefixes instruction
)))
2521 (eq operator
(car instruction
))
2522 (car instruction
))))))
2524 (defun instruction-uncontinues-p (instruction)
2525 "Is it impossible for control to return after instruction?"
2526 (or (member (instruction-is instruction
)
2532 #+ignore
(defun sub-environment-p (env1 env2
)
2536 (t (sub-environment-p (movitz-environment-uplink env1
) env2
))))
2538 (defun find-code-constants-and-jumpers (code &key include-programs
)
2539 "Return code's constants (a plist of constants and their usage-counts) and jumper-sets."
2540 (let (jumper-sets constants key-args-set
)
2541 (labels ((process-binding (binding)
2542 "Some bindings are really references to constants."
2544 (constant-object-binding
2545 (let ((object (movitz-read (constant-object binding
))))
2546 (when (typep object
'movitz-heap-object
)
2547 (incf (getf constants object
0)))))
2549 (process-binding (forwarding-binding-target binding
)))
2551 (let ((funobj (function-binding-funobj binding
)))
2552 (incf (getf constants funobj
0))))
2555 (error "No function-binding now..: ~S" binding
))))
2557 "This local function side-effects the variables jumper-sets and constants."
2558 (loop for instruction in sub-code
2559 do
(case (instruction-is instruction
)
2560 ((:local-function-init
:load-lambda
)
2561 (let* ((binding (second instruction
))
2562 (funobj (function-binding-funobj binding
)))
2563 (unless (eq :unused
(movitz-funobj-extent funobj
))
2564 (incf (getf constants funobj
0))
2565 (dolist (binding (borrowed-bindings funobj
))
2566 (process-binding binding
)))))
2567 ((:load-lexical
:lend-lexical
:call-lexical
)
2568 (process-binding (second instruction
)))
2570 (let ((object (movitz-read (second instruction
))))
2571 (when (typep object
'movitz-heap-object
)
2572 (incf (getf constants object
0)))))
2574 (destructuring-bind (name set
)
2576 (assert (not (getf jumper-sets name
)) ()
2577 "Duplicate jumper declaration for ~S." name
)
2578 (setf (getf jumper-sets name
) set
)))
2579 (:declare-key-arg-set
2580 (setf key-args-set
(cdr instruction
)))
2581 (t (when (listp instruction
)
2582 (dolist (binding (find-read-bindings instruction
))
2583 (process-binding binding
)))))
2584 do
(let ((sub (instruction-sub-program instruction
)))
2585 (when sub
(process sub
))))))
2587 (map nil
#'process include-programs
))
2588 (loop for key-arg in key-args-set
2589 do
(remf constants key-arg
))
2590 (values constants jumper-sets key-args-set
)))
2592 (defun layout-funobj-vector (constants key-args-constants jumper-sets borrowing-bindings
)
2593 (let* ((jumpers (loop with x
2594 for set in
(cdr jumper-sets
) by
#'cddr
2595 unless
(search set x
)
2596 do
(setf x
(nconc x
(copy-list set
)))
2597 finally
(return x
)))
2598 (num-jumpers (length jumpers
))
2599 (stuff (append (mapcar (lambda (c)
2602 (when key-args-constants
2603 (list (cons (movitz-read 0)
2605 (sort (loop for
(constant count
) on constants by
#'cddr
2606 unless
(or (eq constant
*movitz-nil
*)
2607 (eq constant
(image-t-symbol *image
*)))
2608 collect
(cons constant count
))
2610 (values (append jumpers
2612 (movitz-read (car x
)))
2614 (make-list (length borrowing-bindings
)
2615 :initial-element
*movitz-nil
*))
2617 (loop for
(name set
) on jumper-sets by
#'cddr
2618 collect
(cons name set
))
2619 (loop for borrowing-binding in borrowing-bindings
2620 as pos upfrom
(+ num-jumpers
(length stuff
))
2621 collect
(cons borrowing-binding pos
)))))
2623 (defun movitz-funobj-intern-constant (funobj obj
)
2625 (let ((cobj (movitz-read obj
)))
2626 (+ (slot-offset 'movitz-funobj
'constant0
)
2628 (let* ((pos (position cobj
(movitz-funobj-const-list funobj
)
2629 :start
(movitz-funobj-num-jumpers funobj
))))
2631 "Couldn't find constant ~S in ~S's set of constants ~S."
2632 obj funobj
(movitz-funobj-const-list funobj
))
2635 (defun compute-free-registers (pc distance funobj frame-map
2636 &key
(free-registers '(:ecx
:eax
:ebx
:edx
)))
2637 "Return set of free register, and whether there may be more registers
2638 free later, with a more specified frame-map."
2639 (loop with free-so-far
= free-registers
2640 repeat distance for i in pc
2641 while
(not (null free-so-far
))
2644 ((and (instruction-is i
:init-lexvar
)
2645 (typep (second i
) 'required-function-argument
)) ; XXX
2646 (destructuring-bind (binding &key init-with-register init-with-type
2647 protect-registers protect-carry
)
2649 (declare (ignore protect-carry init-with-type
))
2650 (when init-with-register
2651 (setf free-so-far
(remove-if (lambda (x)
2652 (if (new-binding-located-p binding frame-map
)
2653 (eq x
(new-binding-location binding frame-map
))
2654 (or (eq x init-with-register
)
2655 (member x protect-registers
))))
2657 (t (case (instruction-is i
)
2659 (return nil
)) ; a label, most likely
2660 ((:declare-key-arg-set
:declare-label-set
)
2662 ((:lexical-control-transfer
:load-lambda
)
2663 (return nil
)) ; not sure about these.
2666 (remove-if (lambda (r)
2671 (remove :ecx free-so-far
)))
2674 (set-difference free-so-far
'(:eax
:edx
))))
2675 ((:into
:clc
:stc
:int
))
2676 ((:jmp
:jnz
:je
:jne
:jz
:jge
:jae
:jnc
:jbe
)
2678 (remove :push free-so-far
)))
2681 (remove-if (lambda (r)
2687 (set-difference free-so-far
'(:eax
:edx
))))
2688 ((:movb
:testb
:andb
:cmpb
)
2690 (remove-if (lambda (r)
2691 (and (not (eq r
:push
))
2692 (or (tree-search i r
)
2693 (tree-search i
(register32-to-low8 r
)))))
2695 ((:sarl
:shrl
:shll
:xorl
:cmpl
:leal
:btl
:sbbl
:cdq
2696 :movl
:movzxw
:movzxb
:testl
:andl
:addl
:subl
:imull
:idivl
)
2698 (remove-if (lambda (r)
2701 ((:load-constant
:load-lexical
:store-lexical
:cons-get
:endp
:incf-lexvar
:init-lexvar
)
2702 (assert (gethash (instruction-is i
) *extended-code-expanders
*))
2704 ((and (instruction-is i
:init-lexvar
) ; special case..
2705 (typep (second i
) 'forwarding-binding
)))
2706 (t (unless (can-expand-extended-p i frame-map
)
2707 ;; (warn "can't expand ~A from ~A" i frame-map)
2708 (return (values nil t
)))
2709 (let ((exp (expand-extended-code i funobj frame-map
)))
2710 (when (tree-search exp
'(:call
:local-function-init
))
2712 (remove-if (lambda (r)
2716 (remove-if (lambda (r)
2717 (and (not (eq r
:push
))
2718 (or (tree-search exp r
)
2719 (tree-search exp
(register32-to-low8 r
)))))
2721 ((:local-function-init
)
2722 (destructuring-bind (binding)
2724 (unless (typep binding
'funobj-binding
)
2726 (t #+ignore
(warn "Dist ~D stopped by ~A"
2729 ;; do (warn "after ~A: ~A" i free-so-far)
2730 finally
(return free-so-far
)))
2732 (defun try-locate-in-register (binding var-counts funobj frame-map
)
2733 "Try to locate binding in a register. Return a register, or
2734 nil and :not-now, or :never.
2735 This function is factored out from assign-bindings."
2736 (assert (not (typep binding
'forwarding-binding
)))
2737 (let* ((count-init-pc (gethash binding var-counts
))
2738 (count (car count-init-pc
))
2739 (init-pc (second count-init-pc
)))
2740 #+ignore
(warn "b ~S: count: ~D, init-pc: ~{~&~A~}" binding count init-pc
)
2742 ((and (not *compiler-allow-transients
*)
2743 (typep binding
'function-argument
))
2744 (values nil
:never
))
2745 ((binding-lended-p binding
)
2746 ;; We can't lend a register.
2747 (values nil
:never
))
2750 (assert (instruction-is (first init-pc
) :init-lexvar
))
2751 (destructuring-bind (init-binding &key init-with-register init-with-type
2752 protect-registers protect-carry
)
2753 (cdr (first init-pc
))
2754 (declare (ignore protect-registers protect-carry init-with-type
))
2755 (assert (eq binding init-binding
))
2756 (multiple-value-bind (load-instruction binding-destination distance
)
2757 (loop for i in
(cdr init-pc
) as distance upfrom
0
2758 do
(when (not (instruction-is i
:init-lexvar
))
2759 (multiple-value-bind (read-bindings read-destinations
)
2760 (find-read-bindings i
)
2761 (let ((pos (position binding read-bindings
:test
#'binding-eql
)))
2763 (return (values i
(nth pos read-destinations
) distance
)))))))
2764 (declare (ignore load-instruction
))
2765 (multiple-value-bind (free-registers more-later-p
)
2766 (and distance
(compute-free-registers (cdr init-pc
) distance funobj frame-map
))
2768 (when (string= 'num-jumpers
(binding-name binding
))
2769 (warn "load: ~S, dist: ~S, dest: ~S" load-instruction distance binding-destination
)
2770 (warn "free: ~S, more: ~S" free-registers more-later-p
))
2771 (let ((free-registers-no-ecx (remove :ecx free-registers
)))
2773 ((member binding-destination free-registers-no-ecx
)
2774 binding-destination
)
2775 ((and (not (typep binding
'(or fixed-required-function-argument
2776 register-required-function-argument
)))
2777 (member binding-destination free-registers
))
2778 binding-destination
)
2779 ((member init-with-register free-registers
)
2781 ((and (member :ecx free-registers
)
2782 (not (typep binding
'function-argument
))
2783 (or (eq :untagged-fixnum-ecx binding-destination
)
2784 (eq :untagged-fixnum-ecx init-with-register
)))
2785 :untagged-fixnum-ecx
)
2786 ((and (binding-store-type binding
)
2787 (member :ecx free-registers
)
2788 (not (typep binding
'(or fixed-required-function-argument
2789 register-required-function-argument
)))
2790 (multiple-value-call #'encoded-subtypep
2791 (values-list (binding-store-type binding
))
2792 (type-specifier-encode '(or integer character
))))
2794 ((not (null free-registers-no-ecx
))
2795 (first free-registers-no-ecx
))
2797 (values nil
:not-now
))
2798 ((and distance
(typep binding
'temporary-name
))
2799 ;; We might push/pop this variable
2800 (multiple-value-bind (push-available-p maybe-later
)
2801 (compute-free-registers (cdr init-pc
) distance funobj frame-map
2802 :free-registers
'(:push
))
2803 ;; (warn "pushing.. ~S ~A ~A" binding push-available-p maybe-later)
2808 (values nil
:not-now
))
2809 (t (values nil
:never
)))))
2810 (t (values nil
:never
))))))))
2811 (t (values nil
:never
)))))
2813 (defun discover-variables (code function-env
)
2814 "Iterate over CODE, and take note in the hash-table VAR-COUNTER which ~
2815 variables CODE references that are lexically bound in ENV."
2816 (check-type function-env function-env
)
2817 ;; (print-code 'discover code)
2818 (let ((var-counter (make-hash-table :test
#'eq
:size
40)))
2819 (labels ((record-binding-used (binding)
2820 (let ((count-init-pc (or (gethash binding var-counter
)
2821 (setf (gethash binding var-counter
)
2823 (setf (third count-init-pc
) t
)
2824 (when (typep binding
'forwarding-binding
)
2825 (record-binding-used (forwarding-binding-target binding
)))))
2826 (take-note-of-binding (binding &optional storep init-pc
)
2827 (let ((count-init-pc (or (gethash binding var-counter
)
2828 (setf (gethash binding var-counter
)
2829 (list 0 nil
(not storep
))))))
2831 (assert (not (second count-init-pc
)))
2832 (setf (second count-init-pc
) init-pc
))
2834 (unless (eq binding
(binding-target binding
))
2835 ;; (break "ewfew: ~S" (gethash (binding-target binding) var-counter))
2836 (take-note-of-binding (binding-target binding
)))
2837 (setf (third count-init-pc
) t
)
2838 (incf (car count-init-pc
))))
2840 (when (typep binding
'forwarding-binding
)
2841 (take-note-of-binding (forwarding-binding-target binding
) storep
)))
2842 (take-note-of-init (binding init-pc
)
2843 (let ((count-init-pc (or (gethash binding var-counter
)
2844 (setf (gethash binding var-counter
)
2845 (list 0 nil nil
)))))
2846 (assert (not (second count-init-pc
)))
2847 (setf (second count-init-pc
) init-pc
)))
2848 (do-discover-variables (code env
)
2849 (loop for pc on code as instruction in code
2850 when
(listp instruction
)
2851 do
(flet ((lend-lexical (borrowing-binding dynamic-extent-p
)
2852 (let ((lended-binding
2853 (borrowed-binding-target borrowing-binding
)))
2854 (assert (not (typep lended-binding
'forwarding-binding
)) ()
2855 "Can't lend a forwarding-binding.")
2856 (pushnew lended-binding
2857 (potentially-lended-bindings function-env
))
2858 (take-note-of-binding lended-binding
)
2859 (symbol-macrolet ((p (binding-lending lended-binding
)))
2860 (incf (getf p
:lended-count
0))
2861 (setf (getf p
:dynamic-extent-p
) (and (getf p
:dynamic-extent-p t
)
2862 dynamic-extent-p
))))))
2863 (case (instruction-is instruction
)
2864 ((:local-function-init
:load-lambda
)
2865 (let ((function-binding (second instruction
)))
2866 (take-note-of-binding function-binding
)
2867 (let ((sub-funobj (function-binding-funobj function-binding
)))
2869 (warn "fun-ext: ~S ~S ~S"
2871 (movitz-funobj-extent sub-funobj
)
2872 (movitz-allocation sub-funobj
))
2873 (when (typep (movitz-allocation sub-funobj
)
2874 'with-dynamic-extent-scope-env
)
2875 (take-note-of-binding (base-binding (movitz-allocation sub-funobj
)))))
2876 (let ((closure-funobj (function-binding-funobj function-binding
)))
2877 (dolist (borrowing-binding (borrowed-bindings closure-funobj
))
2878 (lend-lexical borrowing-binding nil
)))))
2880 (destructuring-bind (binding num-args
)
2882 (declare (ignore num-args
))
2885 (take-note-of-binding binding
))
2888 (destructuring-bind (binding &key init-with-register init-with-type
2889 protect-registers protect-carry
2892 (declare (ignore protect-registers protect-carry init-with-type
2893 shared-reference-p
))
2895 ((not init-with-register
)
2896 (take-note-of-init binding pc
))
2898 (take-note-of-binding binding t pc
)
2899 (when (and (typep init-with-register
'binding
)
2900 (not (typep binding
'forwarding-binding
))
2901 (not (typep binding
'keyword-function-argument
))) ; XXX
2902 (take-note-of-binding init-with-register
))))))
2903 (t (mapcar #'take-note-of-binding
2904 (find-read-bindings instruction
))
2905 (mapcar #'record-binding-used
; This is just concerning "unused variable"
2906 (find-used-bindings instruction
)) ; warnings!
2907 (let ((store-binding (find-written-binding-and-type instruction
)))
2909 (take-note-of-binding store-binding t
)))
2910 (do-discover-variables (instruction-sub-program instruction
) env
)))))))
2911 (do-discover-variables code function-env
))
2912 (values var-counter
)))
2914 (defun assign-bindings (code function-env
&optional
(initial-stack-frame-position 1)
2915 (frame-map (make-binding-map)))
2916 "Assign locations to all lexical variables in CODE. Recurses into any
2917 sub-environments found in CODE. A frame-map which is an assoc from
2918 bindings to stack-frame locations."
2919 ;; Then assign them to locations in the stack-frame.
2920 #+ignore
(warn "assigning code:~%~{~& ~A~}" code
)
2921 (check-type function-env function-env
)
2922 (assert (= initial-stack-frame-position
2923 (1+ (frame-map-size frame-map
))))
2924 (let* ((env-assigned-p nil
) ; memoize result of assign-env-bindings
2926 (var-counts (discover-variables flat-program function-env
)))
2928 ((assign-env-bindings (env)
2929 (unless (member env env-assigned-p
)
2930 (unless (eq env function-env
)
2931 (assign-env-bindings (movitz-environment-extent-uplink env
)))
2932 (let* ((bindings-to-locate
2933 (loop for binding being the hash-keys of var-counts
2935 (and (eq env
(binding-extent-env binding
))
2936 (not (let ((variable (binding-name binding
)))
2938 ((not (typep binding
'lexical-binding
)))
2939 ((typep binding
'lambda-binding
))
2940 ((typep binding
'constant-object-binding
))
2941 ((typep binding
'forwarding-binding
)
2942 (when (plusp (or (car (gethash binding var-counts
)) 0))
2943 (assert (new-binding-located-p binding frame-map
)))
2945 ((typep binding
'borrowed-binding
))
2946 ((typep binding
'funobj-binding
))
2947 ((and (typep binding
'fixed-required-function-argument
)
2948 (plusp (or (car (gethash binding var-counts
)) 0)))
2949 (prog1 nil
; may need lending-cons
2950 (setf (new-binding-location binding frame-map
)
2951 `(:argument-stack
,(function-argument-argnum binding
)))))
2952 ((unless (or (movitz-env-get variable
'ignore nil
2953 (binding-env binding
) nil
)
2954 (movitz-env-get variable
'ignorable nil
2955 (binding-env binding
) nil
)
2956 (third (gethash binding var-counts
)))
2957 (warn "Unused variable: ~S"
2958 (binding-name binding
))))
2959 ((not (plusp (or (car (gethash binding var-counts
)) 0))))))))
2961 (bindings-fun-arg-sorted
2962 (when (eq env function-env
)
2963 (sort (copy-list bindings-to-locate
) #'<
2964 :key
(lambda (binding)
2966 (edx-function-argument 3)
2967 (positional-function-argument
2968 (* 2 (function-argument-argnum binding
)))
2969 (binding 100000))))))
2970 (bindings-register-goodness-sort
2971 (sort (copy-list bindings-to-locate
) #'<
2972 ;; Sort so as to make the most likely
2973 ;; candidates for locating to registers
2974 ;; be assigned first (i.e. maps to
2975 ;; a smaller value).
2978 ((or constant-object-binding
2982 (fixed-required-function-argument
2983 (+ 100 (function-argument-argnum b
)))
2985 (let* ((count-init (gethash b var-counts
))
2986 (count (car count-init
))
2987 (init-pc (second count-init
)))
2988 (if (not (and count init-pc
))
2991 (or (position-if (lambda (i)
2992 (member b
(find-read-bindings i
)))
2996 ;; First, make several passes while trying to locate bindings
2998 (loop repeat
100 with try-again
= t and did-assign
= t
2999 do
(unless (and try-again did-assign
)
3001 do
(setf try-again nil did-assign nil
)
3002 (loop for binding in bindings-fun-arg-sorted
3003 while
(or (typep binding
'register-required-function-argument
)
3004 (typep binding
'floating-required-function-argument
)
3005 (and (typep binding
'positional-function-argument
)
3006 (< (function-argument-argnum binding
)
3008 do
(unless (new-binding-located-p binding frame-map
)
3009 (multiple-value-bind (register status
)
3010 (try-locate-in-register binding var-counts
3011 (movitz-environment-funobj function-env
)
3015 (setf (new-binding-location binding frame-map
)
3017 (setf did-assign t
))
3018 ((eq status
:not-now
)
3019 ;; (warn "Wait for ~S map ~A" binding frame-map)
3021 (t (assert (eq status
:never
)))))))
3022 (dolist (binding bindings-register-goodness-sort
)
3023 (unless (and (binding-lended-p binding
)
3024 (not (typep binding
'borrowed-binding
))
3025 (not (getf (binding-lending binding
) :stack-cons-location
)))
3026 (unless (new-binding-located-p binding frame-map
)
3027 (check-type binding located-binding
)
3028 (multiple-value-bind (register status
)
3029 (try-locate-in-register binding var-counts
3030 (movitz-environment-funobj function-env
)
3034 (setf (new-binding-location binding frame-map
)
3036 (setf did-assign t
))
3037 ((eq status
:not-now
)
3039 (t (assert (eq status
:never
))))))))
3040 do
(when (and try-again
(not did-assign
))
3041 (let ((binding (or (find-if (lambda (b)
3042 (and (typep b
'positional-function-argument
)
3043 (= 0 (function-argument-argnum b
))
3044 (not (new-binding-located-p b frame-map
))))
3045 bindings-fun-arg-sorted
)
3046 (find-if (lambda (b)
3047 (and (typep b
'positional-function-argument
)
3048 (= 1 (function-argument-argnum b
))
3049 (not (new-binding-located-p b frame-map
))))
3050 bindings-fun-arg-sorted
)
3051 (find-if (lambda (b)
3052 (and (not (new-binding-located-p b frame-map
))
3053 (not (typep b
'function-argument
))))
3054 bindings-register-goodness-sort
3057 (setf (new-binding-location binding frame-map
)
3058 (frame-map-next-free-location frame-map
(binding-env binding
)))
3059 (setf did-assign t
))))
3060 finally
(break "100 iterations didn't work"))
3061 ;; Then, make one pass assigning bindings to stack-frame.
3062 (loop for binding in bindings-fun-arg-sorted
3063 while
(or (typep binding
'register-required-function-argument
)
3064 (typep binding
'floating-required-function-argument
)
3065 (and (typep binding
'positional-function-argument
)
3066 (< (function-argument-argnum binding
)
3068 do
(unless (new-binding-located-p binding frame-map
)
3069 (setf (new-binding-location binding frame-map
)
3070 (frame-map-next-free-location frame-map
(binding-env binding
)))))
3071 (dolist (binding bindings-register-goodness-sort
)
3072 (when (and (binding-lended-p binding
)
3073 (not (typep binding
'borrowed-binding
))
3074 (not (getf (binding-lending binding
) :stack-cons-location
)))
3076 (assert (not (typep binding
'keyword-function-argument
)) ()
3077 "Can't lend keyword binding ~S." binding
)
3078 ;; (warn "assigning lending-cons for ~W at ~D" binding stack-frame-position)
3079 (let ((cons-pos (frame-map-next-free-location frame-map function-env
2)))
3080 (setf (new-binding-location (cons :lended-cons binding
) frame-map
)
3082 (setf (new-binding-location (cons :lended-cons binding
) frame-map
)
3084 (setf (getf (binding-lending binding
) :stack-cons-location
)
3086 (unless (new-binding-located-p binding frame-map
)
3088 (constant-object-binding) ; no location needed.
3089 (forwarding-binding) ; will use the location of target binding.
3090 (borrowed-binding) ; location is predetermined
3091 (fixed-required-function-argument
3092 (setf (new-binding-location binding frame-map
)
3093 `(:argument-stack
,(function-argument-argnum binding
))))
3095 (setf (new-binding-location binding frame-map
)
3096 (frame-map-next-free-location frame-map
(binding-env binding
)))))))
3097 (push env env-assigned-p
)))))
3098 ;; First, "assign" each forwarding binding to their target.
3099 (loop for binding being the hash-keys of var-counts
3100 do
(when (and (typep binding
'forwarding-binding
)
3101 (plusp (car (gethash binding var-counts
'(0)))))
3102 (setf (new-binding-location binding frame-map
)
3103 (forwarding-binding-target binding
))))
3105 (flet ((set-exclusive-location (binding location
)
3106 (assert (not (rassoc location frame-map
))
3107 () "Fixed location ~S for ~S is taken by ~S."
3108 location binding
(rassoc location frame-map
))
3109 (setf (new-binding-location binding frame-map
) location
)))
3110 (when (key-vars-p function-env
)
3111 (when (= 0 (rest-args-position function-env
))
3112 (set-exclusive-location (loop for var in
(required-vars function-env
)
3113 as binding
= (movitz-binding var function-env nil
)
3114 thereis
(when (= 0 (function-argument-argnum binding
))
3117 (when (>= 1 (rest-args-position function-env
))
3118 (set-exclusive-location (loop for var in
(required-vars function-env
)
3119 as binding
= (movitz-binding var function-env nil
)
3120 thereis
(when (= 1 (function-argument-argnum binding
))
3123 (loop for key-var in
(key-vars function-env
)
3124 as key-binding
= (or (movitz-binding key-var function-env nil
)
3125 (error "No binding for key-var ~S." key-var
))
3126 as used-key-binding
=
3127 (when (plusp (car (gethash key-binding var-counts
'(0))))
3129 as used-supplied-p-binding
=
3130 (when (optional-function-argument-supplied-p-var key-binding
)
3131 (let ((b (or (movitz-binding (optional-function-argument-supplied-p-var key-binding
)
3133 (error "No binding for supplied-p-var ~S."
3134 (optional-function-argument-supplied-p-var key-binding
)))))
3135 (when (plusp (car (gethash key-binding var-counts
'(0))))
3137 as location upfrom
3 by
2
3138 do
(set-exclusive-location used-key-binding location
)
3139 (set-exclusive-location used-supplied-p-binding
(1+ location
))))
3140 ;; Now, use assing-env-bindings on the remaining bindings.
3143 for b being the hash-keys of var-counts using
(hash-value c
)
3144 as env
= (binding-env b
)
3145 when
(sub-env-p env function-env
)
3146 do
(incf (getf z env
0) (car c
))
3148 (return (sort (loop for x in z by
#'cddr
3153 do
(assign-env-bindings env
))
3154 #+ignore
(warn "Frame-map ~D:~{~&~A~}"
3155 (frame-map-size frame-map
)
3156 (stable-sort (sort (loop for
(b . l
) in frame-map
3157 collect
(list b l
(car (gethash b var-counts nil
))))
3160 (and (bindingp (car x
))
3161 (binding-name (car x
)))))
3164 (if (integerp (cadr x
))
3170 (defun operators-present-in-code-p (code operators operands
&key
(operand-test #'eql
)
3172 "A simple tree search for `(<one of operators> ,operand) in CODE."
3173 ;; (break "Deprecated operators-present-in-code-p")
3177 ((and (member (first code
) operators
)
3180 (funcall operand-test
(second code
) operands
)
3181 (member (second code
) operands
:test operand-test
)))
3184 (t (or (operators-present-in-code-p (car code
) operators operands
3185 :operand-test operand-test
3187 (operators-present-in-code-p (cdr code
) operators operands
3188 :operand-test operand-test
3192 (defun code-uses-binding-p (code binding
&key
(load t
) store call
)
3193 "Does extended <code> potentially read/write/call <binding>?"
3194 (labels ((search-funobj (funobj binding load store call
)
3195 ;; If this is a recursive lexical call (i.e. labels),
3196 ;; the function-envs might not be bound, but then this
3197 ;; code is searched already.
3198 (when (slot-boundp funobj
'function-envs
)
3199 (some (lambda (function-env-spec)
3200 (code-search (extended-code (cdr function-env-spec
)) binding
3202 (function-envs funobj
))))
3203 (code-search (code binding load store call
)
3204 (dolist (instruction code
)
3205 (when (consp instruction
)
3206 (let ((x (or (when load
3207 (some (lambda (read-binding)
3208 (binding-eql read-binding binding
))
3209 (find-read-bindings instruction
)))
3211 (let ((store-binding (find-written-binding-and-type instruction
)))
3213 (binding-eql binding store-binding
))))
3214 (case (car instruction
)
3215 (:local-function-init
3216 (search-funobj (function-binding-funobj (second instruction
))
3217 binding load store call
))
3220 (binding-eql binding
(second instruction
)))
3221 (let ((allocation (movitz-allocation
3222 (function-binding-funobj (second instruction
)))))
3224 (typep allocation
'with-dynamic-extent-scope-env
))
3225 (binding-eql binding
(base-binding allocation
))))
3226 (search-funobj (function-binding-funobj (second instruction
))
3227 binding load store call
)))
3230 (binding-eql binding
(second instruction
)))
3231 (search-funobj (function-binding-funobj (second instruction
))
3232 binding load store call
))))
3233 (code-search (instruction-sub-program instruction
)
3234 binding load store call
))))
3235 (when x
(return t
)))))))
3236 (code-search code binding load store call
)))
3241 (defun binding-target (binding)
3242 "Resolve a binding in terms of forwarding."
3245 (binding-target (forwarding-binding-target binding
)))
3249 (defun binding-eql (x y
)
3250 (check-type x binding
)
3251 (check-type y binding
)
3253 (and (typep x
'forwarding-binding
)
3254 (binding-eql (forwarding-binding-target x
) y
))
3255 (and (typep y
'forwarding-binding
)
3256 (binding-eql x
(forwarding-binding-target y
)))))
3258 (defun tree-search (tree items
)
3260 (atom (if (atom items
)
3262 (member tree items
)))
3263 (cons (or (tree-search (car tree
) items
)
3264 (tree-search (cdr tree
) items
)))))
3267 (if (atom x
) x
(car x
)))
3269 (defun result-mode-type (x)
3273 (constant-object-binding :constant-binding
)
3274 (lexical-binding :lexical-binding
)
3275 (dynamic-binding :dynamic-binding
)))
3278 (if (symbolp x
) nil
(cdr x
)))
3280 (defun funobj-assign-bindings (code env
&optional
(stack-frame-position 1)
3281 (frame-map (make-binding-map)))
3282 "This wrapper around assign-bindings checks if the first instructions of CODE
3283 are load-lexicals of the first two function arguments, and if possible these
3284 bindings are located in the appropriate register, so no stack location is needed."
3285 (check-type env function-env
)
3286 (assign-bindings (append (when (first (required-vars env
))
3287 (let ((binding (movitz-binding (first (required-vars env
))
3289 (check-type binding required-function-argument
)
3290 `((:init-lexvar
,binding
:init-with-register
:eax
:init-with-type t
))))
3291 (when (second (required-vars env
))
3292 (let ((binding (movitz-binding (second (required-vars env
))
3294 (check-type binding required-function-argument
)
3295 `((:init-lexvar
,binding
:init-with-register
:ebx
:init-with-type t
))))
3297 env stack-frame-position frame-map
))
3299 (defun single-value-register (mode)
3301 ((:eax
:single-value
:multiple-values
:function
) :eax
)
3302 ((:ebx
:ecx
:edx
:esi
:esp
:ebp
) mode
)))
3304 (defun result-mode-register (mode)
3306 ((:eax
:single-value
) :eax
)
3307 ((:ebx
:ecx
:edx
:esi
:esp
) mode
)
3310 (defun accept-register-mode (mode &optional
(default-mode :eax
))
3312 ((:eax
:ebx
:ecx
:edx
)
3316 (defun chose-free-register (unfree-registers &optional
(preferred-register :eax
))
3318 ((not (member preferred-register unfree-registers
))
3320 ((find-if (lambda (r) (not (member r unfree-registers
)))
3321 '(:eax
:ebx
:ecx
:edx
)))
3322 (t (error "Unable to find a free register."))))
3324 (defun make-indirect-reference (base-register offset
)
3325 "Make the shortest possible assembly indirect reference, explointing the constant edi register."
3326 (if (<= #x-80 offset
#x7f
)
3327 (list base-register offset
)
3328 (let ((edi (image-nil-word *image
*)))
3330 ((<= #x-80
(- offset edi
) #x7f
)
3331 `(,base-register
:edi
,(- offset edi
)))
3332 ((<= #x-80
(- offset
(* 2 edi
)) #x7f
)
3333 `(,base-register
(:edi
2) ,(- offset
(* 2 edi
))))
3334 ((<= #x-80
(- offset
(* 4 edi
)) #x7f
)
3335 `(,base-register
(:edi
4) ,(- offset
(* 4 edi
))))
3336 ((<= #x-80
(- offset
(* 8 edi
)) #x7f
)
3337 `(,base-register
(:edi
8) ,(- offset
(* 8 edi
))))
3338 (t (list base-register offset
))))))
3340 (defun make-load-lexical (binding result-mode funobj shared-reference-p frame-map
3341 &key tmp-register protect-registers override-binding-type
)
3342 "When tmp-register is provided, use that for intermediate storage required when
3343 loading borrowed bindings."
3345 (when (eq :ecx result-mode
)
3346 ;; (warn "loading to ecx: ~S" binding)
3347 (unless (or (null (binding-store-type binding
))
3348 (movitz-subtypep (apply #'encoded-type-decode
3349 (binding-store-type binding
))
3351 (warn "ecx from ~S" binding
)))
3352 (when (movitz-env-get (binding-name binding
) 'ignore nil
(binding-env binding
))
3353 (break "The variable ~S is used even if it was declared ignored."
3354 (binding-name binding
)))
3355 (let ((binding (ensure-local-binding binding funobj
))
3356 (protect-registers (cons :edx protect-registers
)))
3357 (labels ((chose-tmp-register (&optional preferred
)
3359 (unless (member preferred protect-registers
)
3361 (first (set-difference '(:eax
:ebx
:edx
)
3363 (error "Unable to chose a temporary register.")))
3364 (install-for-single-value (lexb lexb-location result-mode indirect-p
3365 &optional binding-type
)
3366 (let ((decoded-type (when binding-type
3367 (apply #'encoded-type-decode binding-type
))))
3369 ((and (eq result-mode
:untagged-fixnum-ecx
)
3370 (integerp lexb-location
))
3373 (type-specifier-singleton decoded-type
))
3374 #+ignore
(warn "Immloadlex: ~S"
3375 (type-specifier-singleton decoded-type
))
3376 (make-immediate-move (movitz-fixnum-value
3377 (car (type-specifier-singleton decoded-type
)))
3380 (movitz-subtypep decoded-type
'(and fixnum
(unsigned-byte 32))))
3381 (assert (not indirect-p
))
3382 (append (install-for-single-value lexb lexb-location
:ecx nil
)
3383 `((:shrl
,+movitz-fixnum-shift
+ :ecx
))))
3384 #+ignore
((warn "utecx ~S bt: ~S" lexb decoded-type
))
3386 (assert (not indirect-p
))
3387 (assert (not (member :eax protect-registers
)))
3388 (append (install-for-single-value lexb lexb-location
:eax nil
)
3389 `((,*compiler-global-segment-prefix
*
3390 :call
(:edi
,(global-constant-offset 'unbox-u32
))))))))
3391 ((integerp lexb-location
)
3392 (append `((:movl
,(make-indirect-reference :ebp
(stack-frame-offset lexb-location
))
3393 ,(single-value-register result-mode
)))
3395 `((:movl
(-1 ,(single-value-register result-mode
))
3396 ,(single-value-register result-mode
))))))
3397 ((eq lexb-location result-mode
)
3399 (t (when (and (eq result-mode
:untagged-fixnum-ecx
)
3401 (type-specifier-singleton decoded-type
))
3402 (break "xxx Immloadlex: ~S ~S"
3403 (operator lexb-location
)
3404 (type-specifier-singleton decoded-type
)))
3405 (ecase (operator lexb-location
)
3407 (assert (member result-mode
'(:eax
:ebx
:ecx
:edx
)))
3408 (assert (not indirect-p
))
3409 `((:popl
,result-mode
)))
3411 (assert (not indirect-p
))
3413 ((:ebx
:ecx
:edx
:esi
) `((:movl
:eax
,result-mode
)))
3414 ((:eax
:single-value
) nil
)
3415 (:untagged-fixnum-ecx
3416 `((,*compiler-global-segment-prefix
*
3417 :call
(:edi
,(global-constant-offset 'unbox-u32
)))))))
3419 (assert (not indirect-p
))
3420 (unless (eq result-mode lexb-location
)
3422 ((:eax
:single-value
) `((:movl
,lexb-location
:eax
)))
3423 ((:ebx
:ecx
:edx
:esi
) `((:movl
,lexb-location
,result-mode
)))
3424 (:untagged-fixnum-ecx
3425 `((:movl
,lexb-location
:ecx
)
3426 (:sarl
,movitz
:+movitz-fixnum-shift
+ :ecx
))))))
3428 (assert (<= 2 (function-argument-argnum lexb
)) ()
3429 "lexical :argument-stack argnum can't be ~A." (function-argument-argnum lexb
))
3431 ((eq result-mode
:untagged-fixnum-ecx
)
3432 (assert (not indirect-p
))
3433 `((:movl
(:ebp
,(argument-stack-offset lexb
)) :ecx
)
3434 (:sarl
,+movitz-fixnum-shift
+ :ecx
)))
3435 (t (append `((:movl
(:ebp
,(argument-stack-offset lexb
))
3436 ,(single-value-register result-mode
)))
3438 `((:movl
(-1 ,(single-value-register result-mode
))
3439 ,(single-value-register result-mode
))))))))
3440 (:untagged-fixnum-ecx
3442 ((:eax
:ebx
:ecx
:edx
)
3443 `((:leal
((:ecx
,+movitz-fixnum-factor
+)) ,result-mode
)))
3444 (:untagged-fixnum-ecx
3448 (assert (not (binding-lended-p binding
)) (binding)
3449 "Can't lend a forwarding-binding ~S." binding
)
3450 (make-load-lexical (forwarding-binding-target binding
)
3451 result-mode funobj shared-reference-p frame-map
3452 :override-binding-type
(binding-store-type binding
)))
3453 (constant-object-binding
3454 (assert (not (binding-lended-p binding
)) (binding)
3455 "Can't lend a constant-reference-binding ~S." binding
)
3456 (make-load-constant (constant-object binding
)
3460 (make-load-constant (function-binding-funobj binding
)
3461 result-mode funobj frame-map
))
3463 (let ((slot (borrowed-binding-reference-slot binding
)))
3466 (ecase (result-mode-type result-mode
)
3467 ((:eax
:ebx
:ecx
:edx
)
3468 `((:movl
(:esi
,(+ (slot-offset 'movitz-funobj
'constant0
) (* 4 slot
)))
3469 ,(result-mode-type result-mode
))))))
3470 ((not shared-reference-p
)
3472 ((:single-value
:eax
:ebx
:ecx
:edx
:esi
)
3473 (let ((tmp-register (chose-tmp-register (single-value-register result-mode
))))
3474 `((:movl
(:esi
,(+ (slot-offset 'movitz-funobj
'constant0
) (* 4 slot
)))
3476 (:movl
(,tmp-register -
1)
3477 ,(single-value-register result-mode
)))))
3479 (let ((tmp-register (chose-tmp-register :eax
)))
3480 `((:movl
(:esi
,(+ (slot-offset 'movitz-funobj
'constant0
) (* 4 slot
)))
3482 (:pushl
(,tmp-register -
1)))))
3483 (t (let ((tmp-register (chose-tmp-register :eax
)))
3484 (make-result-and-returns-glue
3485 result-mode tmp-register
3486 `((:movl
(:esi
,(+ (slot-offset 'movitz-funobj
'constant0
) (* 4 slot
)))
3488 (:movl
(,tmp-register -
1) ,tmp-register
))))))))))
3490 (let ((binding-type (or override-binding-type
3491 (binding-store-type binding
)))
3492 (binding-location (new-binding-location binding frame-map
)))
3493 #+ignore
(warn "~S type: ~S ~:[~;lended~]"
3496 (binding-lended-p binding
))
3498 ((and (binding-lended-p binding
)
3499 (not shared-reference-p
))
3500 (case (result-mode-type result-mode
)
3501 ((:single-value
:eax
:ebx
:ecx
:edx
:esi
:esp
)
3502 (install-for-single-value binding binding-location
3503 (single-value-register result-mode
) t
))
3505 (if (integerp binding-location
)
3506 `((:movl
(:ebp
,(stack-frame-offset binding-location
)) :eax
)
3508 (ecase (operator binding-location
)
3510 (assert (<= 2 (function-argument-argnum binding
)) ()
3511 ":load-lexical argnum can't be ~A." (function-argument-argnum binding
))
3512 `((:movl
(:ebp
,(argument-stack-offset binding
)) :eax
)
3513 (:pushl
(:eax -
1)))))))
3514 (t (make-result-and-returns-glue
3516 (install-for-single-value binding binding-location
:eax t
)))))
3517 (t (when (integerp result-mode
)
3518 (break "result-mode: ~S" result-mode
))
3519 (case (result-mode-type result-mode
)
3520 ((:single-value
:eax
:ebx
:ecx
:edx
:esi
:esp
:ebp
)
3521 (install-for-single-value binding binding-location
3522 (single-value-register result-mode
) nil
))
3524 (if (integerp binding-location
)
3525 `((:pushl
(:ebp
,(stack-frame-offset binding-location
))))
3526 (ecase (operator binding-location
)
3527 ((:eax
:ebx
:ecx
:edx
)
3528 `((:pushl
,binding-location
)))
3529 (:untagged-fixnum-ecx
3530 `((,*compiler-local-segment-prefix
*
3531 :call
(:edi
,(global-constant-offset 'box-u32-ecx
)))
3534 (assert (<= 2 (function-argument-argnum binding
)) ()
3535 ":load-lexical argnum can't be ~A." (function-argument-argnum binding
))
3536 `((:pushl
(:ebp
,(argument-stack-offset binding
))))))))
3537 (:boolean-branch-on-true
3538 (if (integerp binding-location
)
3539 `((:cmpl
:edi
(:ebp
,(stack-frame-offset binding-location
)))
3540 (:jne
',(operands result-mode
)))
3541 (ecase (operator binding-location
)
3543 `((:cmpl
:edi
,binding-location
)
3544 (:jne
',(operands result-mode
))))
3546 `((:cmpl
:edi
(:ebp
,(argument-stack-offset binding
)))
3547 (:jne
',(operands result-mode
)))))))
3548 (:boolean-branch-on-false
3549 (if (integerp binding-location
)
3550 `((:cmpl
:edi
(:ebp
,(stack-frame-offset binding-location
)))
3551 (:je
',(operands result-mode
)))
3552 (ecase (operator binding-location
)
3554 `((:cmpl
:edi
,binding-location
)
3555 (:je
',(operands result-mode
))))
3557 `((:cmpl
:edi
(:ebp
,(argument-stack-offset binding
)))
3558 (:je
',(operands result-mode
)))))))
3559 (:untagged-fixnum-ecx
3560 (install-for-single-value binding binding-location
:untagged-fixnum-ecx nil
3563 (let* ((destination result-mode
)
3564 (dest-location (new-binding-location destination frame-map
:default nil
)))
3566 ((not dest-location
) ; unknown, e.g. a borrowed-binding.
3567 (append (install-for-single-value binding binding-location
:edx nil
)
3568 (make-store-lexical result-mode
:edx nil funobj frame-map
)))
3569 ((equal binding-location dest-location
)
3571 ((member binding-location
'(:eax
:ebx
:ecx
:edx
))
3572 (make-store-lexical destination binding-location nil funobj frame-map
))
3573 ((member dest-location
'(:eax
:ebx
:ecx
:edx
))
3574 (install-for-single-value binding binding-location dest-location nil
))
3575 (t #+ignore
(warn "binding => binding: ~A => ~A~% => ~A ~A"
3580 (append (install-for-single-value binding binding-location
:eax nil
)
3581 (make-store-lexical result-mode
:eax nil funobj frame-map
))))))
3582 (t (make-result-and-returns-glue
3584 (install-for-single-value binding binding-location
:eax nil
)))
3587 (defun make-store-lexical (binding source shared-reference-p funobj frame-map
3588 &key protect-registers
)
3589 (let ((binding (ensure-local-binding binding funobj
)))
3590 (assert (not (and shared-reference-p
3591 (not (binding-lended-p binding
))))
3593 "funny binding: ~W" binding
)
3594 (if (and nil
(typep source
'constant-object-binding
))
3595 (make-load-constant (constant-object source
) binding funobj frame-map
)
3596 (let ((protect-registers (cons source protect-registers
)))
3598 ((eq :untagged-fixnum-ecx source
)
3599 (if (eq :untagged-fixnum-ecx
3600 (new-binding-location binding frame-map
))
3602 (append (make-result-and-returns-glue :ecx
:untagged-fixnum-ecx
)
3603 (make-store-lexical binding
:ecx shared-reference-p funobj frame-map
3604 :protect-registers protect-registers
))))
3605 ((typep binding
'borrowed-binding
)
3606 (let ((slot (borrowed-binding-reference-slot binding
)))
3607 (if (not shared-reference-p
)
3608 (let ((tmp-reg (chose-free-register protect-registers
)
3609 #+ignore
(if (eq source
:eax
) :ebx
:eax
)))
3610 (when (eq :ecx source
)
3611 (break "loading a word from ECX?"))
3612 `((:movl
(:esi
,(+ (slot-offset 'movitz-funobj
'constant0
) (* 4 slot
)))
3614 (:movl
,source
(-1 ,tmp-reg
))))
3615 `((:movl
,source
(:esi
,(+ (slot-offset 'movitz-funobj
'constant0
) (* 4 slot
))))))))
3616 ((typep binding
'forwarding-binding
)
3617 (assert (not (binding-lended-p binding
)) (binding))
3618 (make-store-lexical (forwarding-binding-target binding
)
3619 source shared-reference-p funobj frame-map
))
3620 ((not (new-binding-located-p binding frame-map
))
3621 ;; (warn "Can't store to unlocated binding ~S." binding)
3623 ((and (binding-lended-p binding
)
3624 (not shared-reference-p
))
3625 (let ((tmp-reg (chose-free-register protect-registers
)
3626 #+ignore
(if (eq source
:eax
) :ebx
:eax
))
3627 (location (new-binding-location binding frame-map
)))
3628 (if (integerp location
)
3629 `((:movl
(:ebp
,(stack-frame-offset location
)) ,tmp-reg
)
3630 (:movl
,source
(,tmp-reg -
1)))
3631 (ecase (operator location
)
3633 (assert (<= 2 (function-argument-argnum binding
)) ()
3634 "store-lexical argnum can't be ~A." (function-argument-argnum binding
))
3635 `((:movl
(:ebp
,(argument-stack-offset binding
)) ,tmp-reg
)
3636 (:movl
,source
(,tmp-reg -
1))))))))
3637 (t (let ((location (new-binding-location binding frame-map
)))
3639 ((member source
'(:eax
:ebx
:ecx
:edx
:edi
:esp
))
3640 (if (integerp location
)
3641 `((:movl
,source
(:ebp
,(stack-frame-offset location
))))
3642 (ecase (operator location
)
3644 `((:pushl
,source
)))
3645 ((:eax
:ebx
:ecx
:edx
)
3646 (unless (eq source location
)
3647 `((:movl
,source
,location
))))
3649 (assert (<= 2 (function-argument-argnum binding
)) ()
3650 "store-lexical argnum can't be ~A." (function-argument-argnum binding
))
3651 `((:movl
,source
(:ebp
,(argument-stack-offset binding
)))))
3652 (:untagged-fixnum-ecx
3653 (assert (not (eq source
:edi
)))
3655 ((eq source
:untagged-fixnum-ecx
)
3658 `((,*compiler-global-segment-prefix
*
3659 :call
(:edi
,(global-constant-offset 'unbox-u32
)))))
3660 (t `((:movl
,source
:eax
)
3661 (,*compiler-global-segment-prefix
*
3662 :call
(:edi
,(global-constant-offset 'unbox-u32
))))))))))
3663 ((eq source
:boolean-cf
=1)
3664 (let ((tmp (chose-free-register protect-registers
)))
3666 (,*compiler-local-segment-prefix
*
3667 :movl
(:edi
(:ecx
4) ,(global-constant-offset 'not-not-nil
)) ,tmp
)
3668 ,@(make-store-lexical binding tmp shared-reference-p funobj frame-map
3669 :protect-registers protect-registers
))))
3670 ((eq source
:boolean-cf
=0)
3671 (let ((tmp (chose-free-register protect-registers
)))
3673 (,*compiler-local-segment-prefix
*
3674 :movl
(:edi
(:ecx
4) ,(global-constant-offset 'boolean-zero
)) ,tmp
)
3675 ,@(make-store-lexical binding tmp shared-reference-p funobj frame-map
3676 :protect-registers protect-registers
))))
3677 ((and *compiler-use-cmov-p
*
3678 (member source
+boolean-modes
+))
3679 (let ((tmp (chose-free-register protect-registers
)))
3680 (append `((:movl
:edi
,tmp
))
3681 (list (cons *compiler-local-segment-prefix
*
3682 (make-cmov-on-boolean source
3683 `(:edi
,(global-constant-offset 't-symbol
))
3685 (make-store-lexical binding tmp shared-reference-p funobj frame-map
3686 :protect-registers protect-registers
))))
3687 ((member source
+boolean-modes
+)
3688 (let ((tmp (chose-free-register protect-registers
))
3689 (label (gensym "store-lexical-bool-")))
3690 (append `((:movl
:edi
,tmp
))
3691 (list (make-branch-on-boolean source label
:invert t
))
3692 `((,*compiler-local-segment-prefix
*
3693 :movl
(:edi
,(global-constant-offset 't-symbol
)) ,tmp
))
3695 (make-store-lexical binding tmp shared-reference-p funobj frame-map
3696 :protect-registers protect-registers
))))
3697 ((not (bindingp source
))
3698 (error "Unknown source for store-lexical: ~S" source
))
3699 ((binding-singleton source
)
3700 (assert (not shared-reference-p
))
3701 (let ((value (car (binding-singleton source
))))
3704 (let ((immediate (movitz-immediate-value value
)))
3705 (if (integerp location
)
3706 (let ((tmp (chose-free-register protect-registers
)))
3707 (append (make-immediate-move immediate tmp
)
3708 `((:movl
,tmp
(:ebp
,(stack-frame-offset location
))))))
3709 #+ignore
(if (= 0 immediate
)
3710 (let ((tmp (chose-free-register protect-registers
)))
3712 (:movl
,tmp
(:ebp
,(stack-frame-offset location
)))))
3713 `((:movl
,immediate
(:ebp
,(stack-frame-offset location
)))))
3714 (ecase (operator location
)
3716 `((:movl
,immediate
(:ebp
,(argument-stack-offset binding
)))))
3717 ((:eax
:ebx
:ecx
:edx
)
3718 (make-immediate-move immediate location
))
3719 ((:untagged-fixnum-ecx
)
3720 (make-immediate-move (movitz-fixnum-value value
) :ecx
))))))
3722 (let ((immediate (movitz-immediate-value value
)))
3723 (if (integerp location
)
3724 (let ((tmp (chose-free-register protect-registers
)))
3725 (append (make-immediate-move immediate tmp
)
3726 `((:movl
,tmp
(:ebp
,(stack-frame-offset location
))))))
3727 (ecase (operator location
)
3729 `((:movl
,immediate
(:ebp
,(argument-stack-offset binding
)))))
3730 ((:eax
:ebx
:ecx
:edx
)
3731 (make-immediate-move immediate location
))))))
3734 ((member :eax
:ebx
:edx
)
3735 (make-load-constant value location funobj frame-map
))
3737 (let ((tmp (chose-free-register protect-registers
)))
3738 (append (make-load-constant value tmp funobj frame-map
)
3739 (make-store-lexical binding tmp shared-reference-p
3741 :protect-registers protect-registers
))))
3742 ((eql :untagged-fixnum-ecx
)
3743 (check-type value movitz-bignum
)
3744 (let ((immediate (movitz-bignum-value value
)))
3745 (check-type immediate
(unsigned-byte 32))
3746 (make-immediate-move immediate
:ecx
)))
3748 (t (error "Generalized lexb source for store-lexical not implemented: ~S" source
))))))))))
3750 (defun finalize-code (code funobj frame-map
)
3751 ;; (print-code 'to-be-finalized code)
3752 ;; (warn "frame-map: ~A" frame-map)
3753 (labels ((actual-binding (b)
3754 (if (typep b
'borrowed-binding
)
3755 (borrowed-binding-target b
)
3757 (make-lend-lexical (borrowing-binding funobj-register dynamic-extent-p
)
3758 (let ((lended-binding (ensure-local-binding
3759 (borrowed-binding-target borrowing-binding
))))
3760 #+ignore
(warn "LB: in ~S ~S from ~S"
3762 lended-binding borrowing-binding
)
3763 (assert (eq funobj
(binding-funobj lended-binding
)))
3764 (assert (plusp (getf (binding-lending (actual-binding lended-binding
))
3765 :lended-count
0)) ()
3766 "Asked to lend ~S of ~S to ~S of ~S with no lended-count."
3767 lended-binding
(binding-env lended-binding
)
3768 borrowing-binding
(binding-env borrowing-binding
))
3769 (assert (eq funobj-register
:edx
))
3770 (when (getf (binding-lending lended-binding
) :dynamic-extent-p
)
3771 (assert dynamic-extent-p
))
3773 (warn "lending: ~W: ~S"
3775 (mapcar #'movitz-funobj-extent
3776 (mapcar #'binding-funobj
3777 (getf (binding-lending lended-binding
) :lended-to
))))
3778 (append (make-load-lexical lended-binding
:eax funobj t frame-map
)
3779 (unless (or (typep lended-binding
'borrowed-binding
)
3780 (getf (binding-lending lended-binding
) :dynamic-extent-p
)
3781 (every (lambda (borrower)
3782 (member (movitz-funobj-extent (binding-funobj borrower
))
3783 '(:lexical-extent
:dynamic-extent
)))
3784 (getf (binding-lending lended-binding
) :lended-to
)))
3785 (append `((:pushl
:edx
)
3786 (:globally
(:call
(:edi
(:edi-offset ensure-heap-cons-variable
))))
3788 (make-store-lexical lended-binding
:eax t funobj frame-map
)))
3791 ,(+ (slot-offset 'movitz-funobj
'constant0
)
3792 (* 4 (borrowed-binding-reference-slot borrowing-binding
)))))))))
3793 (ensure-local-binding (binding)
3794 (if (eq funobj
(binding-funobj binding
))
3796 (or (find binding
(borrowed-bindings funobj
)
3797 :key
#'borrowed-binding-target
)
3798 (error "Can't install non-local binding ~W." binding
)))))
3799 (labels ((fix-edi-offset (tree)
3803 ((eq :edi-offset
(car tree
))
3804 (check-type (cadr tree
) symbol
"a Movitz run-time-context label")
3805 (+ (global-constant-offset (cadr tree
))
3806 (reduce #'+ (cddr tree
))))
3807 (t (cons (fix-edi-offset (car tree
))
3808 (fix-edi-offset (cdr tree
)))))))
3809 (loop for instruction in code
3814 ((and (= 2 (length instruction
))
3815 (let ((operand (second instruction
)))
3816 (and (listp operand
)
3817 (symbolp (first operand
))
3818 (string= 'quote
(first operand
))
3819 (listp (second operand
)))))
3820 ;;(break "op: ~S" (second (second instruction)))
3821 ;; recurse into program-to-append..
3822 (list (list (first instruction
)
3823 (list 'quote
(finalize-code (second (second instruction
))
3824 funobj frame-map
)))))
3826 (t ;; (warn "finalizing ~S" instruction)
3827 (case (first instruction
)
3828 ((:locally
:globally
)
3829 (destructuring-bind (sub-instr)
3831 (let ((pf (ecase (first instruction
)
3832 (:locally
*compiler-local-segment-prefix
*)
3833 (:globally
*compiler-global-segment-prefix
*))))
3834 (list (fix-edi-offset
3838 ((consp (car sub-instr
))
3839 (list* (append pf
(car sub-instr
))
3841 (t (list* pf sub-instr
))))))))
3842 ((:declare-label-set
3843 :declare-key-arg-set
)
3845 (:local-function-init
3846 (destructuring-bind (function-binding)
3847 (operands instruction
)
3849 (warn "local-function-init: init ~S at ~S"
3851 (new-binding-location function-binding frame-map
))
3853 (let* ((sub-funobj (function-binding-funobj function-binding
)))
3855 ((eq (movitz-funobj-extent sub-funobj
) :unused
)
3856 (unless (or (movitz-env-get (binding-name function-binding
)
3858 (binding-env function-binding
) nil
)
3859 (movitz-env-get (binding-name function-binding
)
3861 (binding-env function-binding
) nil
))
3862 (warn "Unused local function: ~S"
3863 (binding-name function-binding
)))
3865 ((typep function-binding
'funobj-binding
)
3868 ((member (movitz-funobj-extent sub-funobj
)
3869 '(:dynamic-extent
:lexical-extent
))
3870 (check-type function-binding closure-binding
)
3871 (when (plusp (movitz-funobj-num-jumpers sub-funobj
))
3872 (break "Don't know yet how to stack a funobj with jumpers."))
3873 (let ((words (+ (movitz-funobj-num-constants sub-funobj
)
3874 (/ (sizeof 'movitz-funobj
) 4))))
3875 (break "words for ~S: ~S" words sub-funobj
)
3876 (append `((:movl
:esp
:eax
)
3878 (:jz
'no-alignment-needed
)
3880 no-alignment-needed
)
3881 (make-load-constant sub-funobj
:eax funobj frame-map
)
3883 (t (assert (not (null (borrowed-bindings sub-funobj
))))
3884 (append (make-load-constant sub-funobj
:eax funobj frame-map
)
3885 `((:movl
(:edi
,(global-constant-offset 'copy-funobj
)) :esi
)
3886 (:call
(:esi
,(bt:slot-offset
'movitz-funobj
'code-vector%
1op
)))
3888 (make-store-lexical function-binding
:eax nil funobj frame-map
)
3889 (loop for bb in
(borrowed-bindings sub-funobj
)
3890 append
(make-lend-lexical bb
:edx nil
))))))
3893 (destructuring-bind (function-binding register capture-env
)
3894 (operands instruction
)
3895 (declare (ignore capture-env
))
3897 (let* ((sub-funobj (function-binding-funobj function-binding
))
3898 (lend-code (loop for bb in
(borrowed-bindings sub-funobj
)
3900 (make-lend-lexical bb
:edx nil
))))
3903 ;; (warn "null lambda lending")
3904 (append (make-load-constant sub-funobj register funobj frame-map
)))
3905 ((typep (movitz-allocation sub-funobj
)
3906 'with-dynamic-extent-scope-env
)
3907 (setf (headers-on-stack-frame-p funobj
) t
)
3908 (let ((dynamic-scope (movitz-allocation sub-funobj
)))
3909 (append (make-load-lexical (base-binding dynamic-scope
) :edx
3910 funobj nil frame-map
)
3911 `((:leal
(:edx
,(tag :other
)
3912 ,(dynamic-extent-object-offset dynamic-scope
3916 `((:movl
:edx
,register
)))))
3917 (t (append (make-load-constant sub-funobj
:eax funobj frame-map
)
3918 `((:movl
(:edi
,(global-constant-offset 'copy-funobj
)) :esi
)
3919 (:call
(:esi
,(bt:slot-offset
'movitz-funobj
'code-vector%
1op
)))
3922 `((:movl
:edx
,register
))))))
3925 (destructuring-bind (object result-mode
&key
(op :movl
))
3927 (make-load-constant object result-mode funobj frame-map
:op op
)))
3928 (:lexical-control-transfer
3929 (destructuring-bind (return-code return-mode from-env to-env
&optional to-label
)
3931 (declare (ignore return-code
))
3932 (let ((x (apply #'make-compiled-lexical-control-transfer
3934 return-mode from-env to-env
3935 (when to-label
(list to-label
)))))
3936 (finalize-code x funobj frame-map
))))
3938 (destructuring-bind (binding num-args
)
3939 (operands instruction
)
3940 (append (etypecase binding
3942 (make-load-lexical (ensure-local-binding binding
)
3943 :esi funobj nil frame-map
3944 :tmp-register
:edx
))
3946 (make-load-constant (function-binding-funobj binding
)
3947 :esi funobj frame-map
)))
3948 (make-compiled-funcall-by-esi num-args
))))
3949 (t (expand-extended-code instruction funobj frame-map
)))))))))
3952 (defun image-t-symbol-p (x)
3953 (eq x
(image-t-symbol *image
*)))
3955 (deftype movitz-t
()
3956 `(satisfies image-t-symbol-p
))
3958 (defun make-load-constant (object result-mode funobj frame-map
&key
(op :movl
))
3959 (let ((movitz-obj (movitz-read object
)))
3962 (etypecase movitz-obj
3964 (ecase (result-mode-type result-mode
)
3966 (make-store-lexical result-mode
:edi nil funobj frame-map
))
3969 ((:eax
:ebx
:ecx
:edx
)
3970 `((:movl
:edi
,result-mode
)))
3971 (:boolean-branch-on-true
3972 ;; (warn "branch-on-true for nil!")
3974 (:boolean-branch-on-false
3975 ;; (warn "branch-on-false for nil!")
3976 `((:jmp
',(operands result-mode
))))
3977 ((:multiple-values
:function
)
3981 (t (when (eq :boolean result-mode
)
3982 (warn "Compiling ~S for mode ~S." object result-mode
))
3983 (make-result-and-returns-glue result-mode
:edi nil
)
3984 #+ignore
'((:movl
:edi
:eax
)))))
3986 (ecase (result-mode-type result-mode
)
3988 `((:pushl
(:edi
,(global-constant-offset 't-symbol
)))))
3989 ((:eax
:ebx
:ecx
:edx
)
3990 `((:movl
(:edi
,(global-constant-offset 't-symbol
)) ,result-mode
)))
3991 (:boolean-branch-on-false
3992 ;; (warn "boolean-branch-on-false T")
3994 (:boolean-branch-on-true
3995 ;; (warn "boolean-branch-on-true T")
3996 `((:jmp
',(operands result-mode
))))
3997 ((:multiple-values
:function
)
3998 `((:movl
(:edi
,(global-constant-offset 't-symbol
))
4002 (append `((:movl
(:edi
,(global-constant-offset 't-symbol
))
4004 (make-store-lexical result-mode
:eax nil funobj frame-map
)))
4006 (t (when (eq :boolean result-mode
)
4007 (warn "Compiling ~S for mode ~S." object result-mode
))
4008 (make-result-and-returns-glue result-mode
:eax
4009 `((:movl
(:edi
,(global-constant-offset 't-symbol
))
4011 (movitz-immediate-object
4012 (let ((x (movitz-immediate-value movitz-obj
)))
4013 (ecase (result-mode-type result-mode
)
4015 (append (make-immediate-move x
:eax
)
4016 (make-store-lexical result-mode
:eax nil funobj frame-map
)))
4017 (:untagged-fixnum-ecx
4018 (let ((value (movitz-fixnum-value object
)))
4019 (check-type value
(unsigned-byte 32))
4020 (make-immediate-move value
:ecx
)))
4023 ((:eax
:ebx
:ecx
:edx
)
4024 (make-immediate-move x result-mode
))
4025 ((:multiple-values
:function
)
4026 (append (make-immediate-move x
:eax
)
4029 (ecase (result-mode-type result-mode
)
4030 (:untagged-fixnum-ecx
4031 (let ((value (movitz-bignum-value object
)))
4032 (make-immediate-move (ldb (byte 32 0) value
) :ecx
)))
4035 ((and (typep movitz-obj
'movitz-bignum
)
4036 (eq :untagged-fixnum-ecx
4037 (new-binding-location result-mode frame-map
:default nil
)))
4038 (unless (typep (movitz-bignum-value movitz-obj
) '(unsigned-byte 32))
4039 (warn "Loading non-u32 ~S into ~S."
4040 (movitz-bignum-value movitz-obj
)
4042 (make-immediate-move (ldb (byte 32 0) (movitz-bignum-value movitz-obj
))
4044 (t (when (member (new-binding-location result-mode frame-map
:default nil
)
4045 '(:ebx
:ecx
:edx
:esi
))
4046 (warn "load to ~S at ~S from ~S"
4047 result-mode
(new-binding-location result-mode frame-map
) movitz-obj
))
4048 (append `((:movl
,(new-make-compiled-constant-reference movitz-obj funobj
)
4050 (make-store-lexical result-mode
:eax nil funobj frame-map
)))))
4052 `((:pushl
,(new-make-compiled-constant-reference movitz-obj funobj
))))
4053 ((:eax
:ebx
:ecx
:edx
:esi
)
4054 `((,op
,(new-make-compiled-constant-reference movitz-obj funobj
)
4057 (assert (eq op
:cmpl
))
4058 `((,op
,(new-make-compiled-constant-reference movitz-obj funobj
)
4060 ((:function
:multiple-values
)
4061 (assert (eq op
:movl
))
4062 `((,op
,(new-make-compiled-constant-reference movitz-obj funobj
)
4065 (t (ecase result-mode
4066 ((:eax
:ebx
:ecx
:edx
:esi
)
4067 `((,op
,(new-make-compiled-constant-reference movitz-obj funobj
)
4070 (assert (eq op
:cmpl
))
4071 `((,op
,(new-make-compiled-constant-reference movitz-obj funobj
)
4072 ,result-mode
))))))))
4074 (defparameter +movitz-lambda-list-keywords
+
4075 '(muerte.cl
:&OPTIONAL
4081 muerte.cl
:&ALLOW-OTHER-KEYS
4082 muerte.cl
:&ENVIRONMENT
))
4084 (defun add-bindings-from-lambda-list (lambda-list env
)
4085 "From a (normal) <lambda-list>, add bindings to <env>."
4087 (multiple-value-bind (required-vars optional-vars rest-var key-vars auxes allow-p min-args max-args edx-var oddeven key-vars-p
)
4088 (decode-normal-lambda-list lambda-list
)
4089 (setf (min-args env
) min-args
4090 (max-args env
) max-args
4091 (oddeven-args env
) oddeven
4092 (aux-vars env
) auxes
4093 (allow-other-keys-p env
) allow-p
)
4094 (flet ((shadow-when-special (formal env
)
4095 "Iff <formal> is special, return a fresh variable-name that takes <formal>'s place
4096 as the lexical variable-name, and add a new shadowing dynamic binding for <formal> in <env>."
4097 (if (not (movitz-env-get formal
'special nil env
))
4099 (let* ((shadowed-formal (gensym (format nil
"shady-~A-" formal
)))
4100 (shadowing-binding (make-instance 'shadowing-dynamic-binding
4101 :name shadowed-formal
4102 :shadowing-variable formal
4103 :shadowed-variable shadowed-formal
)))
4104 (movitz-env-add-binding env shadowing-binding formal
)
4105 (push (list formal shadowed-formal
)
4106 (special-variable-shadows env
))
4109 (movitz-env-add-binding env
4111 (make-instance 'edx-function-argument
4113 (setf (required-vars env
)
4114 (loop for formal in required-vars
4115 do
(check-type formal symbol
)
4117 (shadow-when-special formal env
))
4118 do
(movitz-env-add-binding env
(cond
4120 (make-instance 'register-required-function-argument
4123 ((and max-args
(= min-args max-args
))
4124 (make-instance 'fixed-required-function-argument
4128 (t (make-instance 'floating-required-function-argument
4133 (setf (optional-vars env
)
4134 (loop for spec in optional-vars
4136 (multiple-value-bind (formal init-form supplied-p-parameter
)
4137 (decode-optional-formal spec
)
4138 (setf formal
(shadow-when-special formal env
))
4139 (movitz-env-add-binding env
(make-instance 'optional-function-argument
4141 :argnum
(post-incf arg-pos
)
4142 'init-form init-form
4143 'supplied-p-var supplied-p-parameter
))
4144 (when supplied-p-parameter
4145 (setf supplied-p-parameter
4146 (shadow-when-special supplied-p-parameter env
))
4147 (movitz-env-add-binding env
(make-instance 'supplied-p-function-argument
4148 :name supplied-p-parameter
)))
4150 (when (or rest-var key-vars-p
)
4151 (setf (rest-args-position env
) arg-pos
))
4153 (check-type rest-var symbol
)
4154 (let ((formal (shadow-when-special rest-var env
)))
4155 (setf (rest-var env
) formal
)
4156 (movitz-env-add-binding env
(make-instance 'rest-function-argument
4158 :argnum
(post-incf arg-pos
)))))
4160 (setf (key-vars-p env
) t
)
4161 (when (>= 1 (rest-args-position env
))
4162 (let ((name (gensym "save-ebx-for-keyscan")))
4163 (setf (required-vars env
)
4164 (append (required-vars env
)
4166 (movitz-env-add-binding env
(make-instance 'register-required-function-argument
4169 :declarations
'(muerte.cl
:ignore
)))
4170 (setf (movitz-env-get name
'ignore nil env
) t
)))
4171 (when (= 0 (rest-args-position env
))
4172 (let ((name (gensym "save-eax-for-keyscan")))
4173 (push name
(required-vars env
))
4174 (movitz-env-add-binding env
(make-instance 'register-required-function-argument
4177 (setf (movitz-env-get name
'ignore nil env
) t
))))
4178 (setf (key-vars env
)
4179 (loop for spec in key-vars
4181 (multiple-value-bind (formal keyword-name init-form supplied-p
)
4182 (decode-keyword-formal spec
)
4183 (let ((formal (shadow-when-special formal env
))
4184 (supplied-p-parameter supplied-p
))
4185 (movitz-env-add-binding env
(make-instance 'keyword-function-argument
4187 'init-form init-form
4188 'supplied-p-var supplied-p-parameter
4189 :keyword-name keyword-name
))
4190 (when supplied-p-parameter
4191 (movitz-env-add-binding env
(make-instance 'supplied-p-function-argument
4192 :name
(shadow-when-special supplied-p-parameter env
))))
4195 (multiple-value-bind (key-decode-map key-decode-shift
)
4196 (best-key-encode (key-vars env
))
4197 (setf (key-decode-map env
) key-decode-map
4198 (key-decode-shift env
) key-decode-shift
))
4201 (warn "~D waste, keys: ~S, shift ~D, map: ~S"
4202 (- (length (key-decode-map env
))
4205 (key-decode-shift env
)
4206 (key-decode-map env
))))))
4209 (defun make-compiled-function-prelude-numarg-check (min-args max-args
)
4210 "The prelude is compiled after the function's body."
4211 (assert (or (not max-args
) (<= 0 min-args max-args
)))
4212 (assert (<= 0 min-args
(or max-args min-args
) #xffff
) ()
4213 "Lambda lists longer than #xffff are not yet implemented.")
4214 (let ((wrong-numargs (make-symbol "wrong-numargs")))
4216 ((and (zerop min-args
) ; any number of arguments is
4217 (not max-args
)) ; acceptable, no check necessary.
4221 (if (< min-args
#x80
)
4222 `((:cmpb
,min-args
:cl
)
4223 (:jb
'(:sub-program
(,wrong-numargs
) (:int
100))))
4224 `((:cmpl
,(dpb min-args
(byte 24 8) #x80
) :ecx
)
4225 (:jb
'(:sub-program
(,wrong-numargs
) (:int
100))))))
4226 ((and max-args
(= 0 min-args max-args
))
4229 (:jnz
'(:sub-program
(,wrong-numargs
) (:int
100)))))
4230 ((and max-args
(= min-args max-args
))
4233 ((= 1 min-args max-args
)
4234 `((:call
(:edi
,(global-constant-offset 'assert-1arg
)))))
4235 ((= 2 min-args max-args
)
4236 `((:call
(:edi
,(global-constant-offset 'assert-2args
)))))
4237 ((= 3 min-args max-args
)
4238 `((:call
(:edi
,(global-constant-offset 'assert-3args
)))))
4240 `((:cmpb
,min-args
:cl
)
4241 (:jne
'(:sub-program
(,wrong-numargs
) (:int
100)))))
4242 (t `((:cmpl
,(dpb min-args
(byte 24 8) #x80
) :ecx
)
4243 (:jne
'(:sub-program
(,wrong-numargs
) (:int
100)))))))
4244 ((and max-args
(/= min-args max-args
) (= 0 min-args
))
4246 (if (< max-args
#x80
)
4247 `((:cmpb
,max-args
:cl
)
4248 (:ja
'(:sub-program
(,wrong-numargs
) (:int
100))))
4249 `((:cmpl
,(dpb max-args
(byte 24 8) #x80
) :ecx
)
4250 (:ja
'(:sub-program
(,wrong-numargs
) (:int
100))))))
4251 ((and max-args
(/= min-args max-args
))
4253 (append (if (< min-args
#x80
)
4254 `((:cmpb
,min-args
:cl
)
4255 (:jb
'(:sub-program
(,wrong-numargs
) (:int
100))))
4256 `((:cmpl
,(dpb min-args
(byte 24 8) #x80
) :ecx
)
4257 (:jb
'(:sub-program
(,wrong-numargs
) (:int
100)))))
4258 (if (< max-args
#x80
)
4259 `((:cmpb
,max-args
:cl
)
4260 (:ja
'(:sub-program
(,wrong-numargs
) (:int
100))))
4261 `((:cmpl
,(dpb max-args
(byte 24 8) #x80
) :ecx
)
4262 (:ja
'(:sub-program
(,wrong-numargs
) (:int
100)))))))
4263 (t (error "Don't know how to compile checking for ~A to ~A arguments."
4264 min-args max-args
)))))
4266 (defun make-stack-setup-code (stack-setup-size)
4267 (loop repeat stack-setup-size
4268 collect
'(:pushl
:edi
))
4270 (case stack-setup-size
4272 (1 '((:pushl
:edi
)))
4273 (2 '((:pushl
:edi
) (:pushl
:edi
)))
4274 (3 '((:pushl
:edi
) (:pushl
:edi
) (:pushl
:edi
)))
4275 (t `((:subl
,(* 4 stack-setup-size
) :esp
)))))
4277 (defun make-compiled-function-prelude (stack-frame-size env use-stack-frame-p
4278 need-normalized-ecx-p frame-map
4279 &key do-check-stack-p
)
4280 "The prelude is compiled after the function's body is."
4281 (when (without-function-prelude-p env
)
4282 (return-from make-compiled-function-prelude
4283 (when use-stack-frame-p
4287 (let ((required-vars (required-vars env
))
4288 (min-args (min-args env
))
4289 (max-args (max-args env
)))
4290 (let ((stack-setup-size stack-frame-size
)
4291 (edx-needs-saving-p (and (edx-var env
)
4292 (new-binding-location (edx-var env
) frame-map
:default nil
))))
4293 (multiple-value-bind (eax-ebx-code eax-ebx-code-post-stackframe
)
4294 (let* ((map0 (find-if (lambda (bb)
4295 (and (typep (car bb
) '(or required-function-argument
4296 optional-function-argument
))
4297 (= 0 (function-argument-argnum (car bb
)))))
4299 (location-0 (cdr map0
))
4300 (map1 (find-if (lambda (bb)
4301 (and (typep (car bb
) '(or required-function-argument
4302 optional-function-argument
))
4303 (= 1 (function-argument-argnum (car bb
)))))
4305 (location-1 (cdr map1
))
4308 (new-binding-location (edx-var env
) frame-map
:default nil
))))
4309 #+ignore
(warn "l0: ~S, l1: ~S" location-0 location-1
)
4310 (assert (not (and location-0
4311 (eql location-0 location-1
))) ()
4312 "Compiler bug: two bindings in same location.")
4314 ((and (eq :ebx location-0
) (eq :eax location-1
))
4315 `((:xchgl
:eax
:ebx
)))
4316 ((and (eql 1 location-0
) (eql 2 location-1
))
4317 (decf stack-setup-size
2)
4318 (when (eql 3 edx-location
)
4319 (decf stack-setup-size
1)
4320 (setf edx-needs-saving-p nil
))
4321 (let (before-code after-code
)
4326 (when (eql 3 edx-location
)
4328 ;; Keep pushing any sequentially following floating requireds.
4329 ;; NB: Fixed-floats are used in-place, e.g above the stack-frame,
4330 ;; so no need to worry about them.
4331 (loop with expected-location
= 2
4332 for var in
(cddr required-vars
)
4333 as binding
= (movitz-binding var env
)
4334 if
(and expected-location
4335 (typep binding
'floating-required-function-argument
)
4336 (new-binding-located-p binding frame-map
)
4337 (= expected-location
4338 (new-binding-location binding frame-map
)))
4339 do
(decf stack-setup-size
)
4340 and do
(incf expected-location
)
4341 and do
(setq need-normalized-ecx-p t
)
4343 `(:pushl
(:ebp
(:ecx
4)
4344 ,(* -
4 (1- (function-argument-argnum binding
)))))
4345 else do
(setf expected-location nil
)
4346 and do
(when (and (typep binding
'floating-required-function-argument
)
4347 (new-binding-located-p binding frame-map
))
4348 (setq need-normalized-ecx-p t
)
4352 `((:movl
(:ebp
(:ecx
4)
4353 ,(* -
4 (1- (function-argument-argnum binding
))))
4355 (:movl
:edx
(:ebp
,(stack-frame-offset
4356 (new-binding-location binding frame-map
)))))))))))
4357 (values before-code after-code
)))
4360 ((and (eq :ebx location-0
)
4362 (decf stack-setup-size
)
4364 (:xchgl
:eax
:ebx
)))
4365 ((and (eq :ebx location-0
)
4366 (eq :edx location-1
))
4372 (decf stack-setup-size
)
4374 (t (ecase location-0
4376 (:ebx
(assert (not location-1
))
4377 '((:movl
:eax
:ebx
)))
4378 (:edx
(assert (not edx-location
))
4379 '((:movl
:eax
:edx
))))))
4382 (decf stack-setup-size
)
4384 (t (ecase location-1
4386 (:edx
'((:movl
:ebx
:edx
)))
4387 (:eax
`((:movl
:ebx
:eax
)))))))))
4389 ((or (and (or (eql 1 location-0
)
4391 (eql 2 edx-location
))
4392 (and (not (integerp location-0
))
4393 (not (integerp location-1
))
4394 (eql 1 edx-location
)))
4395 (decf stack-setup-size
)
4396 (setf edx-needs-saving-p nil
)
4398 (loop for var in
(cddr required-vars
)
4399 as binding
= (movitz-binding var env
)
4400 when
(and (typep binding
'floating-required-function-argument
)
4401 (new-binding-located-p binding frame-map
))
4403 `((:movl
(:ebp
(:ecx
4)
4404 ,(* -
4 (1- (function-argument-argnum binding
))))
4406 (:movl
:edx
(:ebp
,(stack-frame-offset
4407 (new-binding-location binding frame-map
)))))
4409 (setq need-normalized-ecx-p t
))))))
4410 (assert (not (minusp stack-setup-size
)))
4411 (let ((stack-frame-init-code
4412 (append (when (and do-check-stack-p use-stack-frame-p
4413 *compiler-auto-stack-checks-p
*
4414 (not (without-check-stack-limit-p env
)))
4415 `((,*compiler-local-segment-prefix
*
4416 :bound
(:edi
,(global-constant-offset 'stack-bottom
)) :esp
)))
4417 (when use-stack-frame-p
4424 ((and (eql 1 min-args
)
4426 (append (make-compiled-function-prelude-numarg-check min-args max-args
)
4428 stack-frame-init-code
))
4429 ((and (eql 2 min-args
)
4431 (append (make-compiled-function-prelude-numarg-check min-args max-args
)
4433 stack-frame-init-code
))
4434 ((and (eql 3 min-args
)
4436 (append (make-compiled-function-prelude-numarg-check min-args max-args
)
4438 stack-frame-init-code
))
4439 (t (append stack-frame-init-code
4440 (make-compiled-function-prelude-numarg-check min-args max-args
))))
4441 '(start-stack-frame-setup)
4443 (make-stack-setup-code stack-setup-size
)
4444 (when need-normalized-ecx-p
4446 ;; normalize arg-count in ecx..
4447 ((and max-args
(= min-args max-args
))
4449 ((and max-args
(<= 0 min-args max-args
#x7f
))
4450 `((:andl
#x7f
:ecx
)))
4453 (t (let ((normalize (make-symbol "normalize-ecx"))
4454 (normalize-done (make-symbol "normalize-ecx-done")))
4456 (:js
'(:sub-program
(,normalize
)
4458 (:jmp
',normalize-done
)))
4460 ,normalize-done
))))))
4461 (when edx-needs-saving-p
4462 `((:movl
:edx
(:ebp
,(stack-frame-offset (new-binding-location (edx-var env
) frame-map
))))))
4463 eax-ebx-code-post-stackframe
4464 (loop for binding in
(potentially-lended-bindings env
)
4465 as lended-cons-position
= (getf (binding-lending binding
) :stack-cons-location
)
4466 as location
= (new-binding-location binding frame-map
:default nil
)
4467 when
(and (not (typep binding
'borrowed-binding
))
4468 lended-cons-position
4472 (required-function-argument
4473 ;; (warn "lend: ~W => ~W" binding lended-cons-position)
4474 (etypecase (operator location
)
4476 (warn "lending EAX..")
4478 (:ebp
,(stack-frame-offset lended-cons-position
))) ; cdr
4480 (:ebp
,(stack-frame-offset (1+ lended-cons-position
)))) ; car
4481 (:leal
(:ebp
1 ,(stack-frame-offset (1+ lended-cons-position
)))
4483 ((eql :argument-stack
)
4484 `((:movl
(:ebp
,(argument-stack-offset binding
)) :edx
)
4486 (:ebp
,(stack-frame-offset lended-cons-position
))) ; cdr
4488 (:ebp
,(stack-frame-offset (1+ lended-cons-position
)))) ; car
4489 (:leal
(:ebp
1 ,(stack-frame-offset (1+ lended-cons-position
)))
4492 (:ebp
,(argument-stack-offset binding
)))))
4494 `((:movl
(:ebp
,(stack-frame-offset location
))
4497 (:ebp
,(stack-frame-offset lended-cons-position
))) ; cdr
4499 (:ebp
,(stack-frame-offset (1+ lended-cons-position
)))) ; car
4500 (:leal
(:ebp
1 ,(stack-frame-offset (1+ lended-cons-position
)))
4503 (:ebp
,(stack-frame-offset location
)))))))
4505 ;; (warn "lend closure-binding: ~W => ~W" binding lended-cons-position)
4506 (etypecase (operator location
)
4507 ((eql :argument-stack
)
4508 `((:movl
(:edi
,(global-constant-offset 'unbound-function
)) :edx
)
4509 (:movl
:edi
(:ebp
,(stack-frame-offset lended-cons-position
))) ; cdr
4510 (:movl
:edx
(:ebp
,(stack-frame-offset (1+ lended-cons-position
)))) ; car
4511 (:leal
(:ebp
1 ,(stack-frame-offset (1+ lended-cons-position
))) :edx
)
4512 (:movl
:edx
(:ebp
,(argument-stack-offset binding
)))))
4514 `((:movl
(:edi
,(global-constant-offset 'unbound-function
)) :edx
)
4515 (:movl
:edi
(:ebp
,(stack-frame-offset lended-cons-position
))) ; cdr
4516 (:movl
:edx
(:ebp
,(stack-frame-offset (1+ lended-cons-position
)))) ; car
4517 (:leal
(:ebp
1 ,(stack-frame-offset (1+ lended-cons-position
))) :edx
)
4518 (:movl
:edx
(:ebp
,(stack-frame-offset location
)))))))
4520 (t (etypecase location
4521 ((eql :argument-stack
)
4522 `((:movl
:edi
(:ebp
,(stack-frame-offset lended-cons-position
))) ; cdr
4523 (:movl
:edi
(:ebp
,(stack-frame-offset (1+ lended-cons-position
)))) ; car
4524 (:leal
(:ebp
1 ,(stack-frame-offset (1+ lended-cons-position
))) :edx
)
4525 (:movl
:edx
(:ebp
,(argument-stack-offset binding
)))))
4527 `((:movl
:edi
(:ebp
,(stack-frame-offset lended-cons-position
))) ; cdr
4528 (:movl
:edi
(:ebp
,(stack-frame-offset (1+ lended-cons-position
)))) ; car
4529 (:leal
(:ebp
1 ,(stack-frame-offset (1+ lended-cons-position
))) :edx
)
4530 (:movl
:edx
(:ebp
,(stack-frame-offset location
))))))))))
4531 need-normalized-ecx-p
))))))
4533 (defparameter *restify-stats
* (make-hash-table :test
#'eql
))
4535 (defparameter *ll
* (make-array 20 :initial-element
0))
4536 (defparameter *xx
* (make-array 20))
4538 (defun install-arg-cmp (code have-normalized-ecx-p
)
4541 (if (not (and (listp i
) (eq :arg-cmp
(car i
))))
4543 (let ((arg-count (second i
)))
4545 (have-normalized-ecx-p
4546 `(:cmpl
,arg-count
:ecx
))
4548 `(:cmpb
,arg-count
:cl
))
4549 (t `(:cmpl
,(dpb arg-count
(byte 24 8) #x80
) :ecx
)))))))
4551 (defun make-function-arguments-init (funobj env
)
4552 "The arugments-init is compiled before the function's body is.
4553 Return arg-init-code, need-normalized-ecx-p."
4554 (when (without-function-prelude-p env
)
4555 (return-from make-function-arguments-init
4557 (let ((need-normalized-ecx-p nil
)
4558 (required-vars (required-vars env
))
4559 (optional-vars (optional-vars env
))
4560 (rest-var (rest-var env
))
4561 (key-vars (key-vars env
)))
4564 (loop for optional in optional-vars
4565 as optional-var
= (decode-optional-formal optional
)
4566 as binding
= (movitz-binding optional-var env
)
4567 as last-optional-p
= (and (null key-vars
)
4569 (= 1 (- (+ (length optional-vars
) (length required-vars
))
4570 (function-argument-argnum binding
))))
4571 as supplied-p-var
= (optional-function-argument-supplied-p-var binding
)
4572 as supplied-p-binding
= (movitz-binding supplied-p-var env
)
4573 as not-present-label
= (make-symbol (format nil
"optional-~D-not-present"
4574 (function-argument-argnum binding
)))
4575 and optional-ok-label
= (make-symbol (format nil
"optional-~D-ok"
4576 (function-argument-argnum binding
)))
4577 unless
(movitz-env-get optional-var
'ignore nil env nil
) ; XXX
4580 ((= 0 (function-argument-argnum binding
))
4581 `((:init-lexvar
,binding
:init-with-register
:eax
:init-with-type t
)))
4582 ((= 1 (function-argument-argnum binding
))
4583 `((:init-lexvar
,binding
:init-with-register
:ebx
:init-with-type t
)))
4584 (t `((:init-lexvar
,binding
))))
4585 when supplied-p-binding
4586 append
`((:init-lexvar
,supplied-p-binding
))
4588 (compiler-values-bind (&code init-code-edx
&producer producer
)
4589 (compiler-call #'compile-form
4590 :form
(optional-function-argument-init-form binding
)
4595 ((and (eq 'compile-self-evaluating producer
)
4596 (member (function-argument-argnum binding
) '(0 1)))
4597 ;; The binding is already preset with EAX or EBX.
4598 (check-type binding lexical-binding
)
4600 (when supplied-p-var
4601 `((:load-constant
,(movitz-read t
) :edx
)
4602 (:store-lexical
,supplied-p-binding
:edx
:type
(member t
))))
4603 `((:arg-cmp
,(function-argument-argnum binding
))
4604 (:ja
',optional-ok-label
))
4605 (compiler-call #'compile-form
4606 :form
(optional-function-argument-init-form binding
)
4609 :result-mode binding
)
4610 (when supplied-p-var
4611 `((:store-lexical
,supplied-p-binding
:edi
:type null
)))
4612 `(,optional-ok-label
)))
4613 ((eq 'compile-self-evaluating producer
)
4614 `(,@(when supplied-p-var
4615 `((:store-lexical
,supplied-p-binding
:edi
:type null
)))
4616 ,@(if (optional-function-argument-init-form binding
)
4617 (append init-code-edx
`((:store-lexical
,binding
:edx
:type t
)))
4618 `((:store-lexical
,binding
:edi
:type null
)))
4619 (:arg-cmp
,(function-argument-argnum binding
))
4620 (:jbe
',not-present-label
)
4621 ,@(case (function-argument-argnum binding
)
4622 (0 `((:store-lexical
,binding
:eax
:type t
)))
4623 (1 `((:store-lexical
,binding
:ebx
:type t
)))
4626 `((:movl
(:ebp
,(* 4 (- (1+ (function-argument-argnum binding
))
4627 -
1 (function-argument-argnum binding
))))
4629 (:store-lexical
,binding
:eax
:type t
)))
4630 (t (setq need-normalized-ecx-p t
)
4631 `((:movl
(:ebp
(:ecx
4)
4632 ,(* -
4 (1- (function-argument-argnum binding
))))
4634 (:store-lexical
,binding
:eax
:type t
))))))
4635 ,@(when supplied-p-var
4636 `((:movl
(:edi
,(global-constant-offset 't-symbol
)) :eax
)
4637 (:store-lexical
,supplied-p-binding
:eax
4638 :type
(eql ,(image-t-symbol *image
*)))))
4639 ,not-present-label
))
4640 (t `((:arg-cmp
,(function-argument-argnum binding
))
4641 (:jbe
',not-present-label
)
4642 ,@(when supplied-p-var
4643 `((:movl
(:edi
,(global-constant-offset 't-symbol
)) :eax
)
4644 (:store-lexical
,supplied-p-binding
:eax
4645 :type
(eql ,(image-t-symbol *image
*)))))
4646 ,@(case (function-argument-argnum binding
)
4647 (0 `((:store-lexical
,binding
:eax
:type t
)))
4648 (1 `((:store-lexical
,binding
:ebx
:type t
)))
4651 `((:movl
(:ebp
,(* 4 (- (1+ (function-argument-argnum binding
))
4652 -
1 (function-argument-argnum binding
))))
4654 (:store-lexical
,binding
:eax
:type t
)))
4655 (t (setq need-normalized-ecx-p t
)
4656 `((:movl
(:ebp
(:ecx
4)
4657 ,(* -
4 (1- (function-argument-argnum binding
))))
4659 (:store-lexical
,binding
:eax
:type t
))))))
4660 (:jmp
',optional-ok-label
)
4662 ,@(when supplied-p-var
4663 `((:store-lexical
,supplied-p-binding
:edi
:type null
)))
4664 ,@(when (and (= 0 (function-argument-argnum binding
))
4665 (not last-optional-p
))
4666 `((:pushl
:ebx
))) ; protect ebx
4667 ,@(if (optional-function-argument-init-form binding
)
4668 (append `((:shll
,+movitz-fixnum-shift
+ :ecx
)
4670 (when (= 0 (function-argument-argnum binding
))
4673 `((:store-lexical
,binding
:edx
:type t
))
4674 (when (= 0 (function-argument-argnum binding
))
4677 (:shrl
,+movitz-fixnum-shift
+ :ecx
)))
4678 (progn (error "Unsupported situation.")
4679 #+ignore
`((:store-lexical
,binding
:edi
:type null
))))
4680 ,@(when (and (= 0 (function-argument-argnum binding
))
4681 (not last-optional-p
))
4682 `((:popl
:ebx
))) ; protect ebx
4683 ,optional-ok-label
)))))
4685 (let* ((rest-binding (movitz-binding rest-var env
)))
4686 `((:init-lexvar
,rest-binding
4687 :init-with-register
:edx
4688 :init-with-type list
))))
4690 (play-with-keys key-vars
))
4691 (when (key-vars-p env
)
4692 ;; &key processing..
4693 (setq need-normalized-ecx-p t
)
4695 `((:declare-key-arg-set
,@(mapcar (lambda (k)
4697 (keyword-function-argument-keyword-name
4698 (movitz-binding (decode-keyword-formal k
) env
))))
4700 (make-immediate-move (* +movitz-fixnum-factor
+
4701 (rest-args-position env
))
4703 `((:call
(:edi
,(global-constant-offset 'decode-keyargs-default
))))
4704 (unless (allow-other-keys-p env
)
4705 `((:testl
:eax
:eax
)
4706 (:jnz
'(:sub-program
(unknown-keyword)
4708 (loop for key-var in key-vars
4709 as key-location upfrom
3 by
2
4711 (decode-keyword-formal key-var
)
4713 (movitz-binding key-var-name env
)
4714 as supplied-p-binding
=
4715 (when (optional-function-argument-supplied-p-var binding
)
4716 (movitz-binding (optional-function-argument-supplied-p-var binding
)
4718 as keyword-ok-label
= (make-symbol (format nil
"keyword-~A-ok" key-var-name
))
4720 ;; (not (movitz-constantp (optional-function-argument-init-form binding)))
4722 (append `((:init-lexvar
,binding
4723 :init-with-register
,binding
4725 :shared-reference-p t
))
4726 (when supplied-p-binding
4727 `((:init-lexvar
,supplied-p-binding
4728 :init-with-register
,supplied-p-binding
4730 :shared-reference-p t
)))
4731 (when (optional-function-argument-init-form binding
)
4732 `((:cmpl
:edi
(:ebp
,(stack-frame-offset (1+ key-location
))))
4733 (:jne
',keyword-ok-label
)
4734 ,@(compiler-call #'compile-form
4735 :form
(optional-function-argument-init-form binding
)
4738 :result-mode binding
)
4739 ,keyword-ok-label
)))
4743 (append (when supplied-p-var
4744 `((:init-lexvar
,supplied-p-binding
4745 :init-with-register
:edi
4746 :init-with-type null
)))
4747 (compiler-call #'compile-form
4748 :form
(list 'muerte.cl
:quote
4749 (eval-form (optional-function-argument-init-form binding
)
4755 ,(movitz-read (keyword-function-argument-keyword-name binding
)) :ecx
)
4756 (:load-lexical
,rest-binding
:ebx
)
4757 (:call
(:edi
,(global-constant-offset 'keyword-search
))))
4758 (when supplied-p-var
4759 `((:jz
',keyword-not-supplied-label
)
4760 (:movl
(:edi
,(global-constant-offset 't-symbol
)) :ebx
)
4761 (:store-lexical
,supplied-p-binding
:ebx
4762 :type
(eql ,(image-t-symbol *image
*)))
4763 ,keyword-not-supplied-label
))
4764 `((:init-lexvar
,binding
4765 :init-with-register
:eax
4766 :init-with-type t
)))))))
4767 need-normalized-ecx-p
)))
4769 (defun old-key-encode (vars &key
(size (ash 1 (integer-length (1- (length vars
)))))
4771 (assert (<= (length vars
) size
))
4774 (loop with h
= (make-array size
)
4776 for var in
(sort (copy-list vars
) #'<
4778 (mod (ldb byte
(movitz-sxhash (movitz-read v
)))
4780 do
(let ((pos (mod (ldb byte
(movitz-sxhash (movitz-read var
)))
4782 (loop while
(aref h pos
)
4784 (setf pos
(mod (1+ pos
) (length h
))))
4785 (setf (aref h pos
) var
))
4786 finally
(return (values (subseq h
0 (1+ (position-if-not #'null h
:from-end t
)))
4789 (define-condition key-encoding-failed
() ())
4791 (defun key-cuckoo (x shift table
&optional path old-position
)
4793 (error 'key-encoding-failed
)
4794 (let* ((pos1 (mod (ash (movitz-sxhash (movitz-read x
)) (- shift
))
4796 (pos2 (mod (ash (movitz-sxhash (movitz-read x
)) (- 0 shift
9))
4798 (pos (if (eql pos1 old-position
) pos2 pos1
))
4799 (kickout (aref table pos
)))
4800 (setf (aref table pos
)
4803 (key-cuckoo kickout shift table
(cons x path
) pos
)))))
4805 (defun key-encode (vars &key
(size (ash 1 (integer-length (1- (length vars
)))))
4807 (declare (ignore byte
))
4808 (assert (<= (length vars
) size
))
4811 (loop with table
= (make-array size
)
4812 for var in
(sort (copy-list vars
) #'<
4814 (mod (movitz-sxhash (movitz-read v
))
4816 do
(key-cuckoo var shift table
)
4818 (return (values table
4820 (count-if (lambda (v)
4821 (eq v
(aref table
(mod (ash (movitz-sxhash (movitz-read v
))
4826 (defun best-key-encode (vars)
4828 (loop with best-encoding
= nil
4831 for size
= (ash 1 (integer-length (1- (length vars
))))
4833 ;; from (length vars) to (+ 8 (ash 1 (integer-length (1- (length vars)))))
4834 while
(<= size
(max 16 (ash 1 (integer-length (1- (length vars
))))))
4835 do
(loop for shift from
0 to
9 by
3
4837 (multiple-value-bind (encoding crashes
)
4838 (key-encode vars
:size size
:shift shift
)
4839 (when (or (not best-encoding
)
4840 (< crashes best-crashes
)
4841 (and (= crashes best-crashes
)
4842 (or (< shift best-shift
)
4843 (and (= shift best-shift
)
4844 (< (length encoding
)
4845 (length best-encoding
))))))
4846 (setf best-encoding encoding
4848 best-crashes crashes
)))
4849 (key-encoding-failed ())))
4851 (unless best-encoding
4852 (warn "Key-encoding failed for ~S: ~S."
4855 (list (movitz-sxhash (movitz-read v
))
4856 (ldb (byte (+ 3 (integer-length (1- (length vars
)))) 0)
4857 (movitz-sxhash (movitz-read v
)))
4858 (ldb (byte (+ 3 (integer-length (1- (length vars
)))) 9)
4859 (movitz-sxhash (movitz-read v
)))))
4862 (warn "~D waste for ~S"
4863 (- (length best-encoding
)
4866 (return (values best-encoding best-shift best-crashes
)))))
4870 (defun play-with-keys (key-vars)
4872 (let* ((vars (mapcar #'decode-keyword-formal key-vars
)))
4873 (multiple-value-bind (encoding shift crashes
)
4874 (best-key-encode vars
)
4875 (when (or (plusp crashes
)
4876 #+ignore
(>= shift
3)
4877 (>= (- (length encoding
) (length vars
))
4879 (warn "KEY vars: ~S, crash ~D, shift ~D, waste: ~D hash: ~S"
4881 (- (length encoding
) (length vars
))
4883 (movitz-sxhash (movitz-read s
)))
4887 (defun make-special-funarg-shadowing (env function-body
)
4888 "Wrap function-body in a let, if we need to.
4889 We need to when the function's lambda-list binds a special variable,
4890 or when there's a non-dynamic-extent &rest binding."
4891 (if (without-function-prelude-p env
)
4894 (append (special-variable-shadows env
)
4896 (when (and (rest-var env
)
4897 (not (movitz-env-get (rest-var env
) 'dynamic-extent nil env nil
))
4898 (not (movitz-env-get (rest-var env
) 'ignore nil env nil
)))
4899 (movitz-env-load-declarations `((muerte.cl
:dynamic-extent
,(rest-var env
)))
4901 `((,(rest-var env
) (muerte.cl
:copy-list
,(rest-var env
))))))))
4902 (if (null shadowing
)
4904 `(muerte.cl
::let
,shadowing
,function-body
)))))
4906 (defun make-compiled-function-postlude (funobj env use-stack-frame-p
)
4907 (declare (ignore funobj env
))
4908 (let ((p '((:movl
(:ebp -
4) :esi
)
4910 (if use-stack-frame-p
4914 (defun complement-boolean-result-mode (mode)
4918 (:boolean-greater
:boolean-less-equal
)
4919 (:boolean-less
:boolean-greater-equal
)
4920 (:boolean-greater-equal
:boolean-less
)
4921 (:boolean-less-equal
:boolean-greater
)
4922 (:boolean-below
:boolean-above-equal
)
4923 (:boolean-above
:boolean-below-equal
)
4924 (:boolean-below-equal
:boolean-above
)
4925 (:boolean-above-equal
:boolean-below
)
4926 (:boolean-zf
=1 :boolean-zf
=0)
4927 (:boolean-zf
=0 :boolean-zf
=1)
4928 (:boolean-cf
=1 :boolean-cf
=0)
4929 (:boolean-cf
=0 :boolean-cf
=1)))
4931 (let ((args (cdr mode
)))
4934 (list :boolean-ecx
(second args
) (first args
)))
4935 (:boolean-branch-on-true
4936 (cons :boolean-branch-on-false args
))
4937 (:boolean-branch-on-false
4938 (cons :boolean-branch-on-true args
)))))))
4940 (defun make-branch-on-boolean (mode label
&key invert
)
4941 (list (ecase (if invert
(complement-boolean-result-mode mode
) mode
)
4942 (:boolean-greater
:jg
) ; ZF=0 and SF=OF
4943 (:boolean-greater-equal
:jge
) ; SF=OF
4944 (:boolean-less
:jl
) ; SF!=OF
4945 (:boolean-less-equal
:jle
) ; ZF=1 or SF!=OF
4946 (:boolean-below
:jb
)
4947 (:boolean-above
:ja
)
4948 (:boolean-below-equal
:jbe
)
4949 (:boolean-above-equal
:jae
)
4951 (:boolean-zf
=0 :jnz
)
4953 (:boolean-cf
=0 :jnc
)
4954 (:boolean-true
:jmp
))
4955 (list 'quote label
)))
4958 (defun make-cmov-on-boolean (mode src dst
&key invert
)
4959 (list (ecase (if invert
(complement-boolean-result-mode mode
) mode
)
4960 (:boolean-greater
:cmovg
) ; ZF=0 and SF=OF
4961 (:boolean-greater-equal
:cmovge
) ; SF=OF
4962 (:boolean-less
:cmovl
) ; SF!=OF
4963 (:boolean-less-equal
:cmovle
) ; ZF=1 or SF!=OF
4964 (:boolean-zf
=1 :cmovz
)
4965 (:boolean-zf
=0 :cmovnz
)
4966 (:boolean-cf
=1 :cmovc
)
4967 (:boolean-cf
=0 :cmovnc
))
4970 (defun return-satisfies-result-p (desired-result returns-provided
)
4971 (or (eq desired-result returns-provided
)
4972 (case desired-result
4974 ((:eax
:single-value
)
4975 (member returns-provided
'(:eax
:multiple-values
:single-value
)))
4977 (member returns-provided
'(:multiple-values
:function
)))
4979 (member returns-provided
+boolean-modes
+)))))
4981 (defun make-result-and-returns-glue (desired-result returns-provided
4983 &key
(type t
) provider really-desired
)
4984 "Returns new-code and new-returns-provided, and glue-side-effects-p."
4985 (declare (optimize (debug 3)))
4986 (case returns-provided
4988 ;; when CODE does a non-local exit, we certainly don't need any glue.
4989 (return-from make-result-and-returns-glue
4990 (values code
:non-local-exit
))))
4991 (multiple-value-bind (new-code new-returns-provided glue-side-effects-p
)
4992 (case (result-mode-type desired-result
)
4994 (case (result-mode-type returns-provided
)
4996 (if (eq desired-result returns-provided
)
4997 (values code returns-provided
)
4998 (values (append code
`((:load-lexical
,returns-provided
,desired-result
)))
5000 ((:eax
:multiple-values
)
5001 (values (append code
5002 `((:store-lexical
,desired-result
:eax
5003 :type
,(type-specifier-primary type
))))
5007 (values (append code
5008 `((:store-lexical
,desired-result
5009 ,(result-mode-type returns-provided
)
5010 :type
,(type-specifier-primary type
))))
5013 (:ignore
(values code
:nothing
))
5015 (let ((true (first (operands desired-result
)))
5016 (false (second (operands desired-result
))))
5017 (etypecase (operator returns-provided
)
5019 (if (equal (operands desired-result
)
5020 (operands returns-provided
))
5021 (values code desired-result
)
5023 ((eql :boolean-cf
=1)
5025 ((and (= -
1 true
) (= 0 false
))
5026 (values (append code
5027 `((:sbbl
:ecx
:ecx
)))
5028 '(:boolean-ecx -
1 0)))
5029 ((and (= 0 true
) (= -
1 false
))
5030 (values (append code
5033 '(:boolean-ecx
0 -
1)))
5034 (t (error "Don't know modes ~S => ~S." returns-provided desired-result
))))
5036 (make-result-and-returns-glue desired-result
5039 `((:leal
(:eax
,(- (image-nil-word *image
*)))
5044 :really-desired desired-result
)))))
5045 (:boolean-branch-on-true
5046 ;; (warn "rm :b-true with ~S." returns-provided)
5047 (etypecase (operator returns-provided
)
5048 ((member :boolean-branch-on-true
)
5049 (assert (eq (operands desired-result
) (operands returns-provided
)))
5050 (values code returns-provided
))
5051 ((member :eax
:multiple-values
)
5052 (values (append code
5054 (:jne
',(operands desired-result
))))
5056 ((member :ebx
:ecx
:edx
)
5057 (values (append code
5058 `((:cmpl
:edi
,returns-provided
)
5059 (:jne
',(operands desired-result
))))
5062 ;; no branch, nothing is nil is false.
5063 (values code desired-result
))
5064 ((member .
#.
+boolean-modes
+)
5065 (values (append code
5066 (list (make-branch-on-boolean returns-provided
(operands desired-result
))))
5069 (values (append code
5070 `((:load-lexical
,returns-provided
,desired-result
)))
5072 (constant-object-binding
5073 (values (if (eq *movitz-nil
* (constant-object returns-provided
))
5075 `((:jmp
',(operands desired-result
))))
5077 (:boolean-branch-on-false
5078 (etypecase (operator returns-provided
)
5079 ((member :boolean-branch-on-false
)
5080 (assert (eq (operands desired-result
)
5081 (operands returns-provided
)))
5082 (values code desired-result
))
5084 (values (append code
5085 `((:jmp
',(operands desired-result
))))
5087 ((member .
#.
+boolean-modes
+)
5088 (values (append code
5089 (list (make-branch-on-boolean returns-provided
(operands desired-result
)
5092 ((member :ebx
:ecx
:edx
)
5093 (values (append code
5094 `((:cmpl
:edi
,returns-provided
)
5095 (:je
',(operands desired-result
))))
5097 ((member :eax
:multiple-values
)
5098 (values (append code
5100 (:je
',(operands desired-result
))))
5103 (values (append code
5104 `((:load-lexical
,returns-provided
,desired-result
)))
5106 (constant-object-binding
5107 (values (if (not (eq *movitz-nil
* (constant-object returns-provided
)))
5109 `((:jmp
',(operands desired-result
))))
5111 (:untagged-fixnum-ecx
5112 (case (result-mode-type returns-provided
)
5113 (:untagged-fixnum-ecx
5114 (values code
:untagged-fixnum-ecx
))
5115 ((:eax
:single-value
:multiple-values
:function
)
5116 (values (append code
5117 `((,*compiler-global-segment-prefix
*
5118 :call
(:edi
,(global-constant-offset 'unbox-u32
)))))
5119 :untagged-fixnum-ecx
))
5121 ;; In theory (at least..) ECX can only hold non-pointers, so don't check.
5122 (values (append code
5123 `((:shrl
,+movitz-fixnum-shift
+ :ecx
)))
5124 :untagged-fixnum-ecx
))
5126 (values (append code
5127 `((:movl
,returns-provided
:eax
)
5128 (,*compiler-global-segment-prefix
*
5129 :call
(:edi
,(global-constant-offset 'unbox-u32
)))))
5130 :untagged-fixnum-ecx
))
5132 (values (append code
5133 `((:load-lexical
,returns-provided
:untagged-fixnum-ecx
)))
5134 :untagged-fixnum-ecx
))))
5135 ((:single-value
:eax
)
5137 ((eq returns-provided
:eax
)
5139 ((typep returns-provided
'lexical-binding
)
5140 (values (append code
`((:load-lexical
,returns-provided
:eax
)))
5142 (t (case (operator returns-provided
)
5143 (:untagged-fixnum-eax
5144 (values (append code
`((:shll
,+movitz-fixnum-shift
+ :eax
))) :eax
))
5146 (case (first (operands returns-provided
))
5147 (0 (values (append code
'((:movl
:edi
:eax
)))
5149 (t (values code
:eax
))))
5150 ((:single-value
:eax
:function
:multiple-values
)
5153 (values (append code
'((:movl
:edi
:eax
)))
5155 ((:ebx
:ecx
:edx
:edi
)
5156 (values (append code
`((:movl
,returns-provided
:eax
)))
5159 (let ((true-false (operands returns-provided
)))
5161 ((equal '(0 1) true-false
)
5162 (values (append code
`((:movl
(:edi
(:ecx
4) ,(global-constant-offset 'boolean-zero
))
5165 ((equal '(1 0) true-false
)
5166 (values (append code
`((:movl
(:edi
(:ecx
4) ,(global-constant-offset 'boolean-one
))
5169 (t (error "Don't know ECX mode ~S." returns-provided
)))))
5171 (values (append code
5172 `((:sbbl
:ecx
:ecx
) ; T => -1, NIL => 0
5173 (:movl
(:edi
(:ecx
4) ,(global-constant-offset 'not-not-nil
))
5177 ;; (warn "bool for ~S" returns-provided)
5178 (let ((boolean-false-label (make-symbol "boolean-false-label")))
5179 (values (append code
5180 '((:movl
:edi
:eax
))
5181 (if *compiler-use-cmov-p
*
5182 `(,(make-cmov-on-boolean returns-provided
5183 `(:edi
,(global-constant-offset 't-symbol
))
5186 `(,(make-branch-on-boolean returns-provided
5189 (:movl
(:edi
,(global-constant-offset 't-symbol
))
5191 ,boolean-false-label
)))
5193 ((:ebx
:ecx
:edx
:esp
:esi
)
5195 ((eq returns-provided desired-result
)
5196 (values code returns-provided
))
5197 ((typep returns-provided
'lexical-binding
)
5198 (values (append code
`((:load-lexical
,returns-provided
,desired-result
)))
5200 (t (case (operator returns-provided
)
5202 (values (append code
5203 `((:movl
:edi
,desired-result
)))
5205 ((:ebx
:ecx
:edx
:esp
)
5206 (values (append code
5207 `((:movl
,returns-provided
,desired-result
)))
5209 ((:eax
:single-value
:multiple-values
:function
)
5210 (values (append code
5211 `((:movl
:eax
,desired-result
)))
5214 (let ((true-false (operands returns-provided
)))
5216 ((equal '(0 1) true-false
)
5217 (values (append code
`((:movl
(:edi
(:ecx
4) ,(global-constant-offset 'boolean-zero
))
5220 ((equal '(1 0) true-false
)
5221 (values (append code
`((:movl
(:edi
(:ecx
4) ,(global-constant-offset 'boolean-one
))
5224 (t (error "Don't know ECX mode ~S." returns-provided
)))))
5226 ;;; (values (append code `((:movl (:edi (:ecx 4) ,(global-constant-offset 'boolean-zero))
5227 ;;; ,desired-result)))
5228 ;;; desired-result))
5230 ;;; (values (append code `((:movl (:edi (:ecx 4) ,(global-constant-offset 'boolean-one))
5231 ;;; ,desired-result)))
5232 ;;; desired-result))
5234 (values (append code
5236 (:movl
(:edi
(:ecx
4) ,(global-constant-offset 'not-not-nil
))
5240 ;; (warn "bool to ~S for ~S" desired-result returns-provided)
5241 (values (append code
5243 (*compiler-use-cmov-p
*
5244 `((:movl
:edi
,desired-result
)
5245 ,(make-cmov-on-boolean returns-provided
5246 `(:edi
,(global-constant-offset 't-symbol
))
5248 ((not *compiler-use-cmov-p
*)
5249 (let ((boolean-false-label (make-symbol "boolean-false-label")))
5250 `((:movl
:edi
,desired-result
)
5251 ,(make-branch-on-boolean returns-provided
5254 (:movl
(:edi
,(global-constant-offset 't-symbol
))
5256 ,boolean-false-label
)))))
5257 desired-result
))))))
5259 (typecase returns-provided
5260 ((member :push
) (values code
:push
))
5262 (values (append code
'((:pushl
:edi
)))
5264 ((member :single-value
:eax
:multiple-values
:function
)
5265 (values (append code
`((:pushl
:eax
)))
5267 ((member :ebx
:ecx
:edx
)
5268 (values (append code
`((:pushl
,returns-provided
)))
5271 (values (append code
`((:load-lexical
,returns-provided
:push
)))
5274 (case (operator returns-provided
)
5276 (values code returns-provided
))
5278 (values code
:values
))
5279 (t (values (make-result-and-returns-glue :eax returns-provided code
5282 ((:multiple-values
:function
)
5283 (case (operator returns-provided
)
5284 ((:multiple-values
:function
)
5285 (values code
:multiple-values
))
5287 (case (first (operands returns-provided
))
5288 (0 (values (append code
'((:movl
:edi
:eax
) (:xorl
:ecx
:ecx
) (:stc
)))
5290 (1 (values (append code
'((:clc
)))
5292 ((nil) (values code
:multiple-values
))
5293 (t (values (append code
5294 (make-immediate-move (first (operands returns-provided
)) :ecx
)
5296 :multiple-values
))))
5297 (t (values (append (make-result-and-returns-glue :eax
5302 :really-desired desired-result
)
5304 :multiple-values
)))))
5305 (unless new-returns-provided
5306 (multiple-value-setq (new-code new-returns-provided glue-side-effects-p
)
5307 (ecase (result-mode-type returns-provided
)
5309 (case (result-mode-type desired-result
)
5310 ((:eax
:ebx
:ecx
:edx
:push
:lexical-binding
)
5311 (values (append code
5312 `((:load-constant
,(constant-object returns-provided
)
5316 (make-result-and-returns-glue desired-result
:eax
5317 (make-result-and-returns-glue :eax returns-provided code
5320 :really-desired desired-result
)
5322 :provider provider
))
5323 (:untagged-fixnum-ecx
5324 (let ((fixnump (subtypep type
`(integer 0 ,+movitz-most-positive-fixnum
+))))
5327 (member (result-mode-type desired-result
) '(:eax
:ebx
:ecx
:edx
)))
5328 (values (append code
5329 `((:leal
((:ecx
,+movitz-fixnum-factor
+))
5330 ,(result-mode-type desired-result
))))
5333 (member (result-mode-type desired-result
) '(:eax
:single-value
)))
5334 (values (append code
5335 `((:call
(:edi
,(global-constant-offset 'box-u32-ecx
)))))
5337 (t (make-result-and-returns-glue
5339 (make-result-and-returns-glue :eax
:untagged-fixnum-ecx code
5341 :really-desired desired-result
5346 (:untagged-fixnum-eax
5347 (make-result-and-returns-glue desired-result
:eax
5348 (make-result-and-returns-glue :eax
:untagged-fixnum-eax code
5350 :really-desired desired-result
)
5351 :provider provider
)))))
5352 (assert new-returns-provided
()
5353 "Don't know how to match desired-result ~S with returns-provided ~S~@[ from ~S~]."
5354 (or really-desired desired-result
) returns-provided provider
)
5355 (values new-code new-returns-provided glue-side-effects-p
)))
5357 (define-compiler compile-form
(&all form-info
&result-mode result-mode
)
5358 "3.1.2.1 Form Evaluation. Guaranteed to honor RESULT-MODE."
5359 (compiler-values-bind (&all unprotected-values
&code form-code
&returns form-returns
5360 &producer producer
&type form-type
&functional-p functional-p
)
5361 (compiler-call #'compile-form-unprotected
:forward form-info
)
5362 (multiple-value-bind (new-code new-returns-provided glue-side-effects-p
)
5363 (make-result-and-returns-glue result-mode form-returns form-code
5366 (compiler-values (unprotected-values)
5368 :functional-p
(and functional-p
(not glue-side-effects-p
))
5371 :returns new-returns-provided
))))
5373 (define-compiler compile-form-selected
(&all form-info
&result-mode result-modes
)
5374 "3.1.2.1 Form Evaluation. Guaranteed to honor one of RESULT-MODE, which
5375 for this call (exclusively!) is a list of the acceptable result-modes, where
5376 the first one takes preference. Note that :non-local-exit might also be returned."
5377 (check-type result-modes list
"a list of result-modes")
5378 (compiler-values-bind (&all unprotected-values
&code form-code
&returns form-returns
5379 &producer producer
&type form-type
)
5380 (compiler-call #'compile-form-unprotected
5381 :result-mode
(car result-modes
)
5383 (if (member form-returns result-modes
)
5384 (compiler-values (unprotected-values))
5385 (compiler-call #'compile-form
5386 :result-mode
(car result-modes
)
5387 :forward form-info
))))
5389 (define-compiler compile-form-to-register
(&all form-info
)
5390 (compiler-values-bind (&all unprotected-values
&code form-code
&returns form-returns
5391 &final-form final-form
&producer producer
&type form-type
)
5392 (compiler-call #'compile-form-unprotected
5397 ((and (typep final-form
'required-function-argument
)
5398 (= 1 (function-argument-argnum final-form
)))
5399 (compiler-call #'compile-form
5401 :forward form-info
))
5402 ((member form-returns
'(:eax
:ebx
:ecx
:edx
:edi
:untagged-fixnum-ecx
))
5403 (compiler-values (unprotected-values)))
5404 (t (compiler-call #'compile-form
5406 :forward form-info
)))))
5408 (define-compiler compile-form-unprotected
(&all downstream
&form form
&result-mode result-mode
5410 "3.1.2.1 Form Evaluation. May not honor RESULT-MODE.
5411 That is, RESULT-MODE is taken to be a suggestion, not an imperative."
5412 (compiler-values-bind (&all upstream
)
5414 (symbol (compiler-call #'compile-symbol
:forward downstream
))
5415 (cons (compiler-call #'compile-cons
:forward downstream
))
5416 (t (compiler-call #'compile-self-evaluating
:forward downstream
)))
5417 (when (typep (upstream :final-form
) 'lexical-binding
)
5418 (labels ((fix-extent (binding)
5420 ((sub-env-p extent
(binding-extent-env binding
))
5421 #+ignore
(warn "Binding ~S OK in ~S wrt. ~S."
5423 (binding-extent-env binding
)
5425 (t #+ignore
(break "Binding ~S escapes from ~S to ~S"
5426 binding
(binding-extent-env binding
)
5428 (setf (binding-extent-env binding
) extent
)))
5429 (when (typep binding
'forwarding-binding
)
5430 (fix-extent (forwarding-binding-target binding
)))))
5432 (fix-extent (upstream :final-form
)))))
5433 (compiler-values (upstream))))
5435 (defun lambda-form-p (form)
5437 (eq 'muerte.cl
:lambda
(first form
))))
5439 (defun function-name-p (operator)
5440 (or (and (symbolp operator
) operator
)
5441 (setf-name operator
)))
5443 (define-compiler compile-cons
(&all all
&form form
&env env
)
5444 "3.1.2.1.2 Conses as Forms"
5445 (let ((operator (car form
)))
5446 (if (and (symbolp operator
) (movitz-special-operator-p operator
))
5447 (compiler-call (movitz-special-operator-compiler operator
) :forward all
)
5448 (let* ((compiler-macro-function (movitz-compiler-macro-function operator env
))
5449 (compiler-macro-expansion (and compiler-macro-function
5451 (funcall *movitz-macroexpand-hook
*
5452 compiler-macro-function
5455 (warn "Compiler-macro for ~S failed: ~A" operator c
)
5458 ((and compiler-macro-function
5459 (not (movitz-env-get operator
'notinline nil env
))
5460 (not (eq form compiler-macro-expansion
)))
5461 (compiler-call #'compile-form-unprotected
:forward all
:form compiler-macro-expansion
))
5462 ((movitz-constantp form env
)
5463 (compiler-call #'compile-constant-compound
:forward all
))
5464 ((lambda-form-p operator
) ; 3.1.2.1.2.4
5465 (compiler-call #'compile-lambda-form
:forward all
))
5468 ((movitz-special-operator-p operator
)
5469 (compiler-call (movitz-special-operator-compiler operator
) :forward all
))
5470 ((movitz-macro-function operator env
)
5471 (compiler-call #'compile-macro-form
:forward all
))
5472 ((movitz-operator-binding operator env
)
5473 (compiler-call #'compile-apply-lexical-funobj
:forward all
))
5474 (t (compiler-call #'compile-apply-symbol
:forward all
))))
5475 (t (error "Don't know how to compile compound form ~A" form
)))))))
5477 (define-compiler compile-compiler-macro-form
(&all all
&form form
&env env
)
5478 (compiler-call #'compile-form-unprotected
5480 :form
(funcall *movitz-macroexpand-hook
*
5481 (movitz-compiler-macro-function (car form
) env
)
5484 (define-compiler compile-macro-form
(&all all
&form form
&env env
)
5485 "3.1.2.1.2.2 Macro Forms"
5486 (let* ((operator (car form
))
5487 (macro-function (movitz-macro-function operator env
)))
5488 (compiler-call #'compile-form-unprotected
5490 :form
(funcall *movitz-macroexpand-hook
* macro-function form env
))))
5492 (define-compiler compile-lexical-macro-form
(&all all
&form form
&env env
)
5493 "Compiles MACROLET and SYMBOL-MACROLET forms."
5494 (compiler-call #'compile-form-unprotected
5496 :form
(funcall *movitz-macroexpand-hook
*
5497 (macro-binding-expander (movitz-operator-binding form env
))
5500 (defun like-compile-macroexpand-form (form env
)
5502 ;; (symbol (compile-macroexpand-symbol form funobj env top-level-p result-mode))
5503 (cons (like-compile-macroexpand-cons form env
))
5504 (t (values form nil
))))
5506 (defun like-compile-macroexpand-cons (form env
)
5507 "3.1.2.1.2 Conses as Forms"
5508 (let* ((operator (car form
))
5509 (notinline (movitz-env-get operator
'notinline nil env
))
5510 (compiler-macro-function (movitz-compiler-macro-function operator env
))
5511 (compiler-macro-expansion (and compiler-macro-function
5512 (funcall *movitz-macroexpand-hook
*
5513 compiler-macro-function
5516 ((and (not notinline
)
5517 compiler-macro-function
5518 (not (eq form compiler-macro-expansion
)))
5519 (values compiler-macro-expansion t
))
5522 ((movitz-macro-function operator env
)
5523 (values (funcall *movitz-macroexpand-hook
*
5524 (movitz-macro-function operator env
)
5530 (defun make-compiled-stack-restore (stack-displacement result-mode returns
)
5531 "Return the code required to reset the stack according to stack-displacement,
5532 result-mode, and returns (which specify the returns-mode of the immediately
5533 preceding code). As secondary value, returns the new :returns value."
5534 (flet ((restore-by-pop (scratch)
5535 (case stack-displacement
5536 (1 `((:popl
,scratch
)))
5537 (2 `((:popl
,scratch
) (:popl
,scratch
))))))
5538 (if (zerop stack-displacement
)
5539 (values nil returns
)
5540 (ecase (result-mode-type result-mode
)
5542 (values nil returns
))
5543 ((:multiple-values
:values
)
5546 (values `((:leal
(:esp
,(* 4 stack-displacement
)) :esp
))
5548 ((:single-value
:eax
:ebx
)
5549 (values `((:addl
,(* 4 stack-displacement
) :esp
))
5550 :multiple-values
)))) ; assume this addl will set CF=0
5551 ((:single-value
:eax
:ebx
:ecx
:edx
:push
:lexical-binding
:untagged-fixnum-ecx
5552 :boolean
:boolean-branch-on-false
:boolean-branch-on-true
)
5555 (values (or (restore-by-pop :eax
)
5556 `((:leal
(:esp
,(* 4 stack-displacement
)) :esp
))) ; preserve all flags
5559 (values (or (restore-by-pop :eax
)
5560 `((:addl
,(* 4 stack-displacement
) :esp
)))
5562 ((:multiple-values
:single-value
:eax
)
5563 (values (or (restore-by-pop :ebx
)
5564 `((:addl
,(* 4 stack-displacement
) :esp
)))
5567 (values (or (restore-by-pop :eax
)
5568 `((:addl
,(* 4 stack-displacement
) :esp
)))
5571 (define-compiler compile-apply-symbol
(&form form
&funobj funobj
&env env
5572 &result-mode result-mode
)
5573 "3.1.2.1.2.3 Function Forms"
5574 (destructuring-bind (operator &rest arg-forms
)
5576 #+ignore
(when (and (eq result-mode
:function
)
5577 (eq operator
(movitz-print (movitz-funobj-name funobj
))))
5578 (warn "Tail-recursive call detected."))
5579 (when (eq operator
'muerte.cl
::declare
)
5580 (break "Compiling funcall to ~S" 'muerte.cl
::declare
))
5581 (pushnew (cons operator muerte.cl
::*compile-file-pathname
*)
5582 (image-called-functions *image
*)
5584 (multiple-value-bind (arguments-code stack-displacement arguments-modifies
)
5585 (make-compiled-argument-forms arg-forms funobj env
)
5586 (multiple-value-bind (stack-restore-code new-returns
)
5587 (make-compiled-stack-restore stack-displacement result-mode
:multiple-values
)
5589 :returns new-returns
5591 :modifies arguments-modifies
5592 :code
(append arguments-code
5593 (if (and (not *compiler-relink-recursive-funcall
*)
5594 (eq (movitz-read operator
)
5595 (movitz-read (movitz-funobj-name funobj
)))) ; recursive?
5596 (make-compiled-funcall-by-esi (length arg-forms
))
5597 (make-compiled-funcall-by-symbol operator
(length arg-forms
) funobj
))
5598 stack-restore-code
))))))
5600 (define-compiler compile-apply-lexical-funobj
(&all all
&form form
&funobj funobj
&env env
5601 &result-mode result-mode
)
5602 "3.1.2.1.2.3 Function Forms"
5603 (destructuring-bind (operator &rest arg-forms
)
5605 (let ((binding (movitz-operator-binding operator env
)))
5606 (multiple-value-bind (arguments-code stack-displacement
)
5607 (make-compiled-argument-forms arg-forms funobj env
)
5608 (multiple-value-bind (stack-restore-code new-returns
)
5609 (make-compiled-stack-restore stack-displacement result-mode
:multiple-values
)
5611 :returns new-returns
5613 :code
(append arguments-code
5614 (if (eq funobj
(function-binding-funobj binding
))
5615 (make-compiled-funcall-by-esi (length arg-forms
)) ; call ourselves
5616 `((:call-lexical
,binding
,(length arg-forms
))))
5617 stack-restore-code
)))))))
5619 (defun make-compiled-funcall-by-esi (num-args)
5621 (1 `((:call
(:esi
,(slot-offset 'movitz-funobj
'code-vector%
1op
)))))
5622 (2 `((:call
(:esi
,(slot-offset 'movitz-funobj
'code-vector%
2op
)))))
5623 (3 `((:call
(:esi
,(slot-offset 'movitz-funobj
'code-vector%
3op
)))))
5624 (t (append (if (< num-args
#x80
)
5625 `((:movb
,num-args
:cl
))
5626 (make-immediate-move (dpb num-args
(byte 24 8) #x80
) :ecx
))
5627 ; call new ESI's code-vector
5628 `((:call
(:esi
,(slot-offset 'movitz-funobj
'code-vector
))))))))
5630 (defun make-compiled-funcall-by-symbol (apply-symbol num-args funobj
)
5631 (declare (ignore funobj
))
5632 (check-type apply-symbol symbol
)
5633 `((:load-constant
,(movitz-read apply-symbol
) :edx
) ; put function symbol in EDX
5634 (:movl
(:edx
,(slot-offset 'movitz-symbol
'function-value
))
5635 :esi
) ; load new funobj from symbol into ESI
5636 ,@(make-compiled-funcall-by-esi num-args
)))
5638 (defun make-compiled-funcall-by-funobj (apply-funobj num-args funobj
)
5639 (declare (ignore funobj
))
5640 (check-type apply-funobj movitz-funobj
)
5642 :returns
:multiple-values
5644 :code
`( ; put function funobj in ESI
5645 (:load-constant
,apply-funobj
:esi
)
5646 ,@(make-compiled-funcall-by-esi num-args
))))
5648 (defun make-compiled-argument-forms (argument-forms funobj env
)
5649 "Return code as primary value, and stack displacement as secondary value.
5650 Return the set of modified lexical bindings third. Fourth, a list of the individual
5651 compile-time types of each argument. Fifth: The combined functional-p."
5652 ;; (incf (aref *args* (min (length argument-forms) 9)))
5653 (case (length argument-forms
) ;; "optimized" versions for 0, 1, 2, and 3 aruments.
5654 (0 (values nil
0 nil
() t
))
5655 (1 (compiler-values-bind (&code code
&type type
&functional-p functional-p
)
5656 (compiler-call #'compile-form
5657 :form
(first argument-forms
)
5661 (values code
0 t
(list (type-specifier-primary type
)) functional-p
)))
5662 (2 (multiple-value-bind (code functional-p modified first-values second-values
)
5663 (make-compiled-two-forms-into-registers (first argument-forms
) :eax
5664 (second argument-forms
) :ebx
5666 (values code
0 modified
5667 (list (type-specifier-primary (compiler-values-getf first-values
:type
))
5668 (type-specifier-primary (compiler-values-getf second-values
:type
)))
5670 (t (let* ((arguments-self-evaluating-p t
)
5671 (arguments-are-load-lexicals-p t
)
5672 (arguments-lexical-variables ())
5673 (arguments-modifies nil
)
5674 (arguments-functional-p t
)
5675 (arguments-types nil
)
5679 (loop for form in
(nthcdr 2 argument-forms
)
5681 (compiler-values-bind (&code code
&producer producer
&modifies modifies
&type type
5682 &functional-p functional-p
)
5683 (compiler-call #'compile-form
5688 :with-stack-used
(post-incf stack-pos
))
5689 ;; (incf (stack-used arg-env))
5690 (unless functional-p
5691 (setf arguments-functional-p nil
))
5692 (push producer producers
)
5693 (push (type-specifier-primary type
)
5695 (setf arguments-modifies
5696 (modifies-union arguments-modifies modifies
))
5698 (compile-self-evaluating)
5699 (compile-lexical-variable
5700 (setf arguments-self-evaluating-p nil
)
5701 (assert (eq :load-lexical
(caar code
)) ()
5702 "comp-lex-var produced for ~S~% ~S" form code
)
5703 (pushnew (cadar code
) arguments-lexical-variables
))
5704 (t (setf arguments-self-evaluating-p nil
5705 arguments-are-load-lexicals-p nil
)))
5707 (multiple-value-bind (code01 functionalp01 modifies01 all0 all1
)
5708 (make-compiled-two-forms-into-registers (first argument-forms
) :eax
5709 (second argument-forms
) :ebx
5711 (unless functionalp01
5712 (setf arguments-functional-p nil
))
5713 (let ((final0 (compiler-values-getf all0
:final-form
))
5714 (final1 (compiler-values-getf all1
:final-form
))
5715 (types (list* (type-specifier-primary (compiler-values-getf all0
:type
))
5716 (type-specifier-primary (compiler-values-getf all1
:type
))
5717 (nreverse arguments-types
))))
5719 ((or arguments-self-evaluating-p
5720 (and (typep final0
'lexical-binding
)
5721 (typep final1
'lexical-binding
)))
5722 (values (append arguments-code code01
)
5724 (+ -
2 (length argument-forms
))
5727 arguments-functional-p
))
5728 ((and arguments-are-load-lexicals-p
5729 (typep final0
'(or lexical-binding movitz-object
))
5730 (typep final1
'(or lexical-binding movitz-object
)))
5731 (values (append arguments-code code01
)
5732 (+ -
2 (length argument-forms
))
5735 arguments-functional-p
))
5736 ((and arguments-are-load-lexicals-p
5737 (not (some (lambda (arg-binding)
5738 (code-uses-binding-p code01 arg-binding
:store t
:load nil
))
5739 arguments-lexical-variables
)))
5740 (values (append arguments-code code01
)
5741 (+ -
2 (length argument-forms
))
5744 arguments-functional-p
))
5745 (t ;; (warn "fail: ~S by ~S" argument-forms (nreverse producers))
5746 (let ((stack-pos 0))
5747 (values (append (compiler-call #'compile-form
5748 :form
(first argument-forms
)
5753 :with-stack-used
(post-incf stack-pos
))
5754 ;; (prog1 nil (incf (stack-used arg-env)))
5755 (compiler-call #'compile-form
5756 :form
(second argument-forms
)
5761 :with-stack-used
(post-incf stack-pos
))
5762 ;; (prog1 nil (incf (stack-used arg-env)))
5763 (loop for form in
(nthcdr 2 argument-forms
)
5765 (compiler-call #'compile-form
5770 :with-stack-used
(post-incf stack-pos
)))
5771 `((:movl
(:esp
,(* 4 (- (length argument-forms
) 1))) :eax
)
5772 (:movl
(:esp
,(* 4 (- (length argument-forms
) 2))) :ebx
)))
5773 ;; restore-stack.. don't mess up CF!
5774 (prog1 (length argument-forms
)
5775 #+ignore
(assert (= (length argument-forms
) (stack-used arg-env
))))
5776 (modifies-union modifies01 arguments-modifies
)
5778 arguments-functional-p
))))))))))
5780 (defun program-is-load-lexical-of-binding (prg)
5781 (and (not (cdr prg
))
5782 (instruction-is-load-lexical-of-binding (car prg
))))
5784 (defun instruction-is-load-lexical-of-binding (instruction)
5785 (and (listp instruction
)
5786 (eq :load-lexical
(car instruction
))
5787 (destructuring-bind (binding destination
&key
&allow-other-keys
)
5788 (operands instruction
)
5789 (values binding destination
))))
5791 (defun make-compiled-two-forms-into-registers (form0 reg0 form1 reg1 funobj env
)
5792 "Returns first: code that does form0 into reg0, form1 into reg1.
5793 second: whether code is functional-p,
5794 third: combined set of modified bindings
5795 fourth: all compiler-values for form0, as a list.
5796 fifth: all compiler-values for form1, as a list."
5797 (assert (not (eq reg0 reg1
)))
5798 (compiler-values-bind (&all all0
&code code0
&functional-p functional0
5799 &final-form final0
&type type0
)
5800 (compiler-call #'compile-form
5805 (compiler-values-bind (&all all1
&code code1
&functional-p functional1
5806 &final-form final1
&type type1
)
5807 (compiler-call #'compile-form
5813 ((and (typep final0
'binding
)
5814 (not (code-uses-binding-p code1 final0
:load nil
:store t
)))
5815 (append (compiler-call #'compile-form-unprotected
5817 :result-mode
:ignore
5821 `((:load-lexical
,final0
,reg0
:protect-registers
(,reg1
)))))
5822 ((program-is-load-lexical-of-binding code1
)
5823 (destructuring-bind (src dst
&key protect-registers shared-reference-p
)
5825 (assert (eq reg1 dst
))
5827 `((:load-lexical
,src
,reg1
5828 :protect-registers
,(union protect-registers
5830 :shared-reference-p
,shared-reference-p
)))))
5831 ;; XXX if we knew that code1 didn't mess up reg0, we could do more..
5832 (t #+ignore
(when (and (not (tree-search code1 reg0
))
5833 (not (tree-search code1
:call
)))
5834 (warn "got b: ~S ~S for ~S: ~{~&~A~}" form0 form1 reg0 code1
))
5835 (let ((binding (make-instance 'temporary-name
:name
(gensym "tmp-")))
5836 (xenv (make-local-movitz-environment env funobj
)))
5837 (movitz-env-add-binding xenv binding
)
5838 (append (compiler-call #'compile-form
5843 `((:init-lexvar
,binding
:init-with-register
,reg0
5844 :init-with-type
,(type-specifier-primary type0
)))
5845 (compiler-call #'compile-form
5850 `((:load-lexical
,binding
,reg0
))))))
5851 (and functional0 functional1
)
5853 (compiler-values-list (all0))
5854 (compiler-values-list (all1))))))
5856 (define-compiler compile-symbol
(&all all
&form form
&env env
&result-mode result-mode
)
5857 "3.1.2.1.1 Symbols as Forms"
5858 (if (movitz-constantp form env
)
5859 (compiler-call #'compile-self-evaluating
5861 :form
(eval-form form env
))
5862 (let ((binding (movitz-binding form env
)))
5864 ((typep binding
'lexical-binding
)
5865 #+ignore
(make-compiled-lexical-variable form binding result-mode env
)
5866 (compiler-call #'compile-lexical-variable
:forward all
))
5867 ((typep binding
'symbol-macro-binding
)
5868 (compiler-call #'compile-form-unprotected
5870 :form
(funcall *movitz-macroexpand-hook
*
5871 (macro-binding-expander (movitz-binding form env
)) form env
)))
5872 (t (compiler-call #'compile-dynamic-variable
:forward all
))))))
5874 (define-compiler compile-lexical-variable
(&form variable
&result-mode result-mode
&env env
)
5875 (let ((binding (movitz-binding variable env
)))
5876 (check-type binding lexical-binding
)
5877 (case (operator result-mode
)
5880 :final-form binding
))
5881 (t (compiler-values ()
5885 :functional-p t
)))))
5887 (defun make-compiled-lexical-load (binding result-mode
&rest key-args
)
5888 "Do what is necessary to load lexical binding <binding>."
5889 `((:load-lexical
,binding
,result-mode
,@key-args
)))
5891 (define-compiler compile-dynamic-variable
(&form form
&env env
&result-mode result-mode
)
5892 "3.1.2.1.1.2 Dynamic Variables"
5893 (if (eq :ignore result-mode
)
5894 (compiler-values ())
5895 (let ((binding (movitz-binding form env
)))
5898 (unless (movitz-env-get form
'special nil env
)
5899 (cerror "Compile like a special." "Undeclared variable: ~S." form
))
5905 :code
(if *compiler-use-into-unbound-protocol
*
5906 `((:load-constant
,form
:ebx
)
5907 (,*compiler-local-segment-prefix
*
5908 :call
(:edi
,(global-constant-offset 'dynamic-variable-lookup
)))
5911 (let ((not-unbound (gensym "not-unbound-")))
5912 `((:load-constant
,form
:ebx
)
5913 (,*compiler-local-segment-prefix
*
5914 :call
(:edi
,(global-constant-offset 'dynamic-variable-lookup
)))
5915 (,*compiler-local-segment-prefix
*
5916 :cmpl
:eax
(:edi
,(global-constant-offset 'unbound-value
)))
5917 (:jne
',not-unbound
)
5920 (t (check-type binding dynamic-binding
)
5926 :code
(if *compiler-use-into-unbound-protocol
*
5927 `((:load-constant
,form
:ebx
)
5928 (,*compiler-local-segment-prefix
*
5929 :call
(:edi
,(global-constant-offset 'dynamic-variable-lookup
)))
5932 (let ((not-unbound (gensym "not-unbound-")))
5933 `((:load-constant
,form
:ebx
)
5934 (,*compiler-local-segment-prefix
*
5935 :call
(:edi
,(global-constant-offset 'dynamic-variable-lookup
)))
5936 (,*compiler-local-segment-prefix
*
5937 :cmpl
:eax
(:edi
,(global-constant-offset 'unbound-value
)))
5938 (:jne
',not-unbound
)
5940 ,not-unbound
)))))))))
5942 (define-compiler compile-lambda-form
(&form form
&all all
)
5943 "3.1.2.2.4 Lambda Forms"
5944 (let ((lambda-expression (car form
))
5945 (lambda-args (cdr form
)))
5946 (compiler-call #'compile-form-unprotected
5948 :form
`(muerte.cl
:funcall
,lambda-expression
,@lambda-args
))))
5950 (define-compiler compile-constant-compound
(&all all
&form form
&env env
&top-level-p top-level-p
)
5951 (compiler-call #'compile-self-evaluating
5953 :form
(eval-form form env top-level-p
)))
5955 (defun register32-to-low8 (register)
5962 (defun make-immediate-move (value destination-register
)
5965 `((:xorl
,destination-register
,destination-register
)))
5966 ((= value
(image-nil-word *image
*))
5967 `((:movl
:edi
,destination-register
)))
5968 ((<= #x-80
(- value
(image-nil-word *image
*)) #x7f
)
5969 `((:leal
(:edi
,(- value
(image-nil-word *image
*))) ,destination-register
)))
5970 ((<= #x-80
(- value
(* 2 (image-nil-word *image
*))) #x7f
)
5971 `((:leal
(:edi
(:edi
1) ,(- value
(* 2 (image-nil-word *image
*)))) ,destination-register
)))
5972 ((<= #x-80
(- value
(* 3 (image-nil-word *image
*))) #x7f
)
5973 `((:leal
(:edi
(:edi
2) ,(- value
(* 3 (image-nil-word *image
*)))) ,destination-register
)))
5974 ((<= #x-80
(- value
(* 5 (image-nil-word *image
*))) #x7f
)
5975 `((:leal
(:edi
(:edi
4) ,(- value
(* 5 (image-nil-word *image
*)))) ,destination-register
)))
5976 ((<= #x-80
(- value
(* 9 (image-nil-word *image
*))) #x7f
)
5977 `((:leal
(:edi
(:edi
8) ,(- value
(* 9 (image-nil-word *image
*)))) ,destination-register
)))
5979 `((:xorl
,destination-register
,destination-register
)
5980 (:movb
,value
,(register32-to-low8 destination-register
))))
5981 (t `((:movl
,value
,destination-register
)))))
5983 (defparameter *prev-self-eval
* nil
)
5985 (define-compiler compile-self-evaluating
(&form form
&result-mode result-mode
&funobj funobj
)
5986 "3.1.2.1.3 Self-Evaluating Objects"
5987 (let* ((object form
)
5988 (movitz-obj (image-read-intern-constant *image
* object
))
5989 (funobj-env (funobj-env funobj
))
5990 (binding (or (cdr (assoc movitz-obj
(movitz-environment-bindings funobj-env
)))
5991 (let ((binding (make-instance 'constant-object-binding
5992 :name
(gensym "self-eval-")
5993 :object movitz-obj
)))
5994 (setf (binding-env binding
) funobj-env
)
5995 (push (cons movitz-obj binding
)
5996 (movitz-environment-bindings funobj-env
))
5998 (compiler-values-bind (&all self-eval
)
5999 (compiler-values (nil :abstract t
)
6000 :producer
(default-compiler-values-producer)
6001 :type
`(eql ,movitz-obj
)
6004 (case (operator result-mode
)
6006 (compiler-values (self-eval)
6009 (t (compiler-values (self-eval)
6010 :returns binding
))))))
6012 (define-compiler compile-implicit-progn
(&all all
&form forms
&top-level-p top-level-p
6013 &result-mode result-mode
)
6014 "Compile all the elements of the list <forms> as a progn."
6015 (check-type forms list
)
6016 (case (length forms
)
6017 (0 (compiler-values ()))
6018 (1 (compiler-call #'compile-form-unprotected
6020 :form
(first forms
)))
6021 (t (loop with no-side-effects-p
= t
6022 with progn-codes
= nil
6023 for
(sub-form . more-forms-p
) on forms
6024 as current-result-mode
= (if more-forms-p
:ignore result-mode
)
6025 do
(compiler-values-bind (&code code
&returns sub-returns-mode
6026 &functional-p no-sub-side-effects-p
6027 &type type
&final-form final-form
&producer sub-producer
)
6028 (compiler-call (if (not more-forms-p
)
6029 #'compile-form-unprotected
6033 :top-level-p top-level-p
6034 :result-mode current-result-mode
)
6035 (assert sub-returns-mode
()
6036 "~S produced no returns-mode for form ~S." sub-producer sub-form
)
6037 (unless no-sub-side-effects-p
6038 (setf no-side-effects-p nil
))
6039 (push (if (and no-sub-side-effects-p
(eq current-result-mode
:ignore
))
6043 (when (not more-forms-p
)
6044 (return (compiler-values ()
6045 :returns sub-returns-mode
6046 :functional-p no-side-effects-p
6047 :final-form final-form
6049 :code
(reduce #'append
(nreverse progn-codes
))))))))))
6052 (defun new-make-compiled-constant-reference (obj funobj
)
6053 (let ((movitz-obj (movitz-read obj
)))
6054 (if (eq movitz-obj
(image-t-symbol *image
*))
6055 (make-indirect-reference :edi
(global-constant-offset 't-symbol
))
6056 (etypecase movitz-obj
6058 (movitz-immediate-object (movitz-immediate-value movitz-obj
))
6060 (make-indirect-reference :esi
(movitz-funobj-intern-constant funobj movitz-obj
)))))))
6062 (defun make-compiled-lexical-control-transfer (return-code return-mode from-env to-env
6063 &optional
(to-label (exit-label to-env
)))
6064 "<return-code> running in <from-env> produces <return-mode>, and we need to
6065 generate code that transfers control (and unwinds dynamic bindings, runs unwind-protect
6066 cleanup-forms etc.) to <to-env> with <return-code>'s result intact."
6067 (check-type to-env lexical-exit-point-env
)
6068 (multiple-value-bind (stack-distance num-dynamic-slots unwind-protects
)
6069 (stack-delta from-env to-env
)
6070 (assert stack-distance
)
6071 (assert (null unwind-protects
) ()
6072 "Lexical unwind-protect not implemented, to-env: ~S. (this is not supposed to happen)"
6074 ;; (warn "dist: ~S, slots: ~S" stack-distance num-dynamic-slots)
6075 (assert (not (eq t num-dynamic-slots
)) ()
6076 "Don't know how to make lexical-control-transfer across unknown number of dynamic slots.")
6078 ((and (eq t stack-distance
)
6079 (eql 0 num-dynamic-slots
))
6081 :returns
:non-local-exit
6082 :code
(append return-code
6083 (unless (eq :function
(exit-result-mode to-env
))
6084 `((:load-lexical
,(movitz-binding (save-esp-variable to-env
) to-env nil
) :esp
)))
6085 `((:jmp
',to-label
)))))
6086 ((eq t stack-distance
)
6088 :returns
:non-local-exit
6089 :code
(append return-code
6090 (compiler-call #'special-operator-with-cloak
6092 :result-mode
(exit-result-mode to-env
)
6093 :form
`(muerte::with-cloak
(,return-mode
)
6094 (muerte::with-inline-assembly
(:returns
:nothing
)
6095 ;; Compute target dynamic-env
6096 (:locally
(:movl
(:edi
(:edi-offset dynamic-env
)) :eax
))
6097 ,@(loop repeat num-dynamic-slots
6098 collect
`(:movl
(:eax
12) :eax
))
6099 (:locally
(:call
(:edi
(:edi-offset dynamic-unwind-next
))))
6100 (:locally
(:movl
:eax
(:edi
(:edi-offset dynamic-env
))))
6101 (:jc
'(:sub-program
() (:int
63))))))
6102 `((:load-lexical
,(movitz-binding (save-esp-variable to-env
) to-env nil
) :esp
)
6103 (:jmp
',to-label
)))))
6104 ((zerop num-dynamic-slots
)
6106 :returns
:non-local-exit
6107 :code
(append return-code
6108 (make-compiled-stack-restore stack-distance
6109 (exit-result-mode to-env
)
6111 `((:jmp
',to-label
)))))
6112 ((plusp num-dynamic-slots
)
6113 ;; (warn "num-dynamic-slots: ~S, distance: ~D" num-dynamic-slots stack-distance)
6115 :returns
:non-local-exit
6116 :code
(append return-code
6117 (compiler-call #'special-operator-with-cloak
6119 :result-mode
(exit-result-mode to-env
)
6120 :form
`(muerte::with-cloak
(,return-mode
)
6121 (muerte::with-inline-assembly
(:returns
:nothing
)
6122 ;; Compute target dynamic-env
6123 (:locally
(:movl
(:edi
(:edi-offset dynamic-env
)) :eax
))
6124 ,@(loop repeat num-dynamic-slots
6125 collect
`(:movl
(:eax
12) :eax
))
6126 (:locally
(:call
(:edi
(:edi-offset dynamic-unwind-next
))))
6127 (:locally
(:movl
:eax
(:edi
(:edi-offset dynamic-env
))))
6128 (:jc
'(:sub-program
() (:int
63))))))
6129 `((:leal
(:esp
,(* 4 stack-distance
)) :esp
)
6130 (:jmp
',to-label
)))))
6131 (t (error "unknown!")))))
6133 (defun make-compiled-push-current-values ()
6134 "Return code that pushes the current values onto the stack, and returns
6135 in ECX the number of values (as fixnum)."
6136 (let ((not-single-value (gensym "not-single-value-"))
6137 (push-values-done (gensym "push-values-done-"))
6138 (push-values-loop (gensym "push-values-loop-")))
6139 `((:jc
',not-single-value
)
6142 (:jmp
',push-values-done
)
6144 (:shll
,+movitz-fixnum-shift
+ :ecx
)
6145 (:jz
',push-values-done
)
6150 (:je
',push-values-done
)
6154 (:je
',push-values-done
)
6156 (:locally
(:pushl
(:edi
(:edi-offset values
) :edx -
8)))
6159 (:jne
',push-values-loop
)
6160 ,push-values-done
)))
6162 (defun stack-add (x y
)
6163 (if (and (integerp x
) (integerp y
))
6167 (define-modify-macro stack-incf
(&optional
(delta 1)) stack-add
)
6169 (defun stack-delta (inner-env outer-env
)
6170 "Calculate the amount of stack-space used (in 32-bit stack slots) at the time
6171 of <inner-env> since <outer-env>,
6172 the number of intervening dynamic-slots (special bindings, unwind-protects, and catch-tags),
6173 and a list of any intervening unwind-protect environment-slots."
6175 ((find-stack-delta (env stack-distance num-dynamic-slots unwind-protects
)
6176 #+ignore
(warn "find-stack-delta: ~S dist ~S, slots ~S" env
6177 (stack-used env
) (num-dynamic-slots env
))
6180 ;; Each dynamic-slot is 4 stack-distances, so let's check that..
6181 (assert (or (eq t stack-distance
)
6182 (>= stack-distance
(* 4 num-dynamic-slots
))) ()
6183 "The stack-distance ~D is smaller than number of dynamic-slots ~D, which is inconsistent."
6184 stack-distance num-dynamic-slots
)
6185 (values stack-distance num-dynamic-slots unwind-protects
))
6188 (t (find-stack-delta (movitz-environment-uplink env
)
6189 (stack-add stack-distance
(stack-used env
))
6190 (stack-add num-dynamic-slots
(num-dynamic-slots env
))
6191 (if (typep env
'unwind-protect-env
)
6192 (cons env unwind-protects
)
6193 unwind-protects
))))))
6194 (find-stack-delta inner-env
0 0 nil
)))
6196 (defun print-stack-delta (inner-env outer-env
)
6197 (labels ((print-stack-delta (env)
6199 ((or (eq outer-env env
)
6201 (t (format t
"~&Env: ~S used: ~S, slots: ~S"
6202 env
(stack-used env
) (num-dynamic-slots env
))
6203 (print-stack-delta (movitz-environment-uplink env
))))))
6204 (print-stack-delta inner-env
)))
6207 ;;;;;;; Extended-code declarations
6210 (defvar *extended-code-find-read-binding
*
6211 (make-hash-table :test
#'eq
))
6213 (defvar *extended-code-find-used-bindings
*
6214 (make-hash-table :test
#'eq
))
6216 (defmacro define-find-read-bindings
(name lambda-list
&body body
)
6217 (let ((defun-name (intern
6218 (with-standard-io-syntax
6219 (format nil
"~A-~A" 'find-read-bindings name
)))))
6221 (setf (gethash ',name
*extended-code-find-read-binding
*) ',defun-name
)
6222 (defun ,defun-name
(instruction)
6223 (destructuring-bind ,lambda-list
6227 (defmacro define-find-used-bindings
(name lambda-list
&body body
)
6228 (let ((defun-name (intern
6229 (with-standard-io-syntax
6230 (format nil
"~A-~A" 'find-used-bindings name
)))))
6232 (setf (gethash ',name
*extended-code-find-used-bindings
*) ',defun-name
)
6233 (defun ,defun-name
(instruction)
6234 (destructuring-bind ,lambda-list
6238 (defun find-used-bindings (extended-instruction)
6239 "Return zero, one or two bindings that this instruction reads."
6240 (when (listp extended-instruction
)
6241 (let* ((operator (car extended-instruction
))
6242 (finder (or (gethash operator
*extended-code-find-used-bindings
*)
6243 (gethash operator
*extended-code-find-read-binding
*))))
6245 (let ((result (funcall finder extended-instruction
)))
6246 (check-type result list
"a list of read bindings")
6249 (defun find-read-bindings (extended-instruction)
6250 "Return zero, one or two bindings that this instruction reads."
6251 (when (listp extended-instruction
)
6252 (let* ((operator (car extended-instruction
))
6253 (finder (gethash operator
*extended-code-find-read-binding
*)))
6255 (funcall finder extended-instruction
)))))
6257 (defmacro define-find-write-binding-and-type
(name lambda-list
&body body
)
6258 (let ((defun-name (intern
6259 (with-standard-io-syntax
6260 (format nil
"~A-~A" 'find-write-binding-and-type name
)))))
6262 (setf (gethash ',name
*extended-code-find-write-binding-and-type
*) ',defun-name
)
6263 (defun ,defun-name
,lambda-list
,@body
))))
6265 (defun find-written-binding-and-type (extended-instruction)
6266 (when (listp extended-instruction
)
6267 (let* ((operator (car extended-instruction
))
6268 (finder (gethash operator
*extended-code-find-write-binding-and-type
*)))
6270 (funcall finder extended-instruction
)))))
6272 (defmacro define-extended-code-expander
(name lambda-list
&body body
)
6273 (let ((defun-name (intern
6274 (with-standard-io-syntax
6275 (format nil
"~A-~A" 'extended-code-expander- name
)))))
6277 (setf (gethash ',name
*extended-code-expanders
*) ',defun-name
)
6278 (defun ,defun-name
,lambda-list
,@body
))))
6280 (defun can-expand-extended-p (extended-instruction frame-map
)
6281 "Given frame-map, can we expand i at this point?"
6282 (and (every (lambda (b)
6283 (or (typep (binding-target b
) 'constant-object-binding
)
6284 (new-binding-located-p (binding-target b
) frame-map
)))
6285 (find-read-bindings extended-instruction
))
6286 (let ((written-binding (find-written-binding-and-type extended-instruction
)))
6287 (or (not written-binding
)
6288 (new-binding-located-p (binding-target written-binding
) frame-map
)))))
6290 (defun expand-extended-code (extended-instruction funobj frame-map
)
6291 (if (not (listp extended-instruction
))
6292 (list extended-instruction
)
6293 (let* ((operator (car extended-instruction
))
6294 (expander (gethash operator
*extended-code-expanders
*)))
6296 (list extended-instruction
)
6297 (let ((expansion (funcall expander extended-instruction funobj frame-map
)))
6299 (expand-extended-code e funobj frame-map
))
6302 (defun ensure-local-binding (binding funobj
)
6303 "When referencing binding in funobj, ensure we have the binding local to funobj."
6304 (if (typep binding
'(or (not binding
) constant-object-binding
))
6305 binding
; Never mind if "binding" isn't a binding, or is a constant-binding.
6306 (let ((target-binding (binding-target binding
)))
6308 ((eq funobj
(binding-funobj target-binding
))
6310 (t (or (find target-binding
(borrowed-bindings funobj
)
6311 :key
(lambda (binding)
6312 (borrowed-binding-target binding
)))
6313 (error "Can't install non-local binding ~W." binding
)))))))
6315 (defun binding-store-subtypep (binding type-specifier
)
6316 "Is type-specifier a supertype of all values ever stored to binding?
6317 (Assuming analyze-bindings has put this information into binding-store-type.)"
6318 (if (not (binding-store-type binding
))
6320 (multiple-value-call #'encoded-subtypep
6321 (values-list (binding-store-type binding
))
6322 (type-specifier-encode type-specifier
))))
6324 (defun binding-singleton (binding)
6325 (let ((btype (binding-store-type binding
)))
6327 (type-specifier-singleton (apply #'encoded-type-decode btype
)))))
6330 ;;;;;;; Extended-code handlers
6334 ;;;;;;;;;;;;;;;;;; Load-lexical
6336 (define-find-write-binding-and-type :load-lexical
(instruction)
6337 (destructuring-bind (source destination
&key
&allow-other-keys
)
6339 (when (typep destination
'binding
)
6340 (values destination t
#+ignore
(binding-type-specifier source
)
6341 (lambda (source-type)
6345 (define-find-read-bindings :load-lexical
(source destination
&key
&allow-other-keys
)
6346 (check-type source binding
)
6347 (values (list source
)
6348 (list destination
)))
6350 (define-extended-code-expander :load-lexical
(instruction funobj frame-map
)
6351 (destructuring-bind (source destination
&key shared-reference-p tmp-register protect-registers
)
6353 (make-load-lexical (ensure-local-binding source funobj
)
6354 (ensure-local-binding destination funobj
)
6355 funobj shared-reference-p frame-map
6356 :tmp-register tmp-register
6357 :protect-registers protect-registers
)))
6360 ;;;;;;;;;;;;;;;;;; Lisp-move
6362 (define-find-write-binding-and-type :lmove
(instruction)
6363 (destructuring-bind (source destination
)
6365 (values destination source
)))
6367 (define-find-read-bindings :lmove
(source destination
)
6368 (declare (ignore destination
))
6371 ;;;;;;;;;;;;;;;;;; Store-lexical
6373 (define-find-write-binding-and-type :store-lexical
(instruction)
6374 (destructuring-bind (destination source
&key
(type (error "No type")) &allow-other-keys
)
6376 (declare (ignore source
))
6377 (check-type destination binding
)
6378 (values destination type
)))
6380 (define-find-read-bindings :store-lexical
(destination source
&key
&allow-other-keys
)
6381 (declare (ignore destination
))
6382 (when (typep source
'binding
)
6385 (define-extended-code-expander :store-lexical
(instruction funobj frame-map
)
6386 (destructuring-bind (destination source
&key shared-reference-p type protect-registers
)
6388 (declare (ignore type
))
6389 (make-store-lexical (ensure-local-binding destination funobj
)
6390 (ensure-local-binding source funobj
)
6391 shared-reference-p funobj frame-map
6392 :protect-registers protect-registers
)))
6394 ;;;;;;;;;;;;;;;;;; Init-lexvar
6396 (define-find-write-binding-and-type :init-lexvar
(instruction)
6397 (destructuring-bind (binding &key init-with-register init-with-type
6398 protect-registers protect-carry
6401 (declare (ignore protect-registers protect-carry shared-reference-p
))
6405 ((not (typep init-with-register
'binding
))
6406 (assert init-with-type
)
6407 (values binding init-with-type
) )
6408 ((and init-with-type
(not (bindingp init-with-type
)))
6409 (values binding init-with-type
))
6410 ((and init-with-type
6411 (bindingp init-with-type
)
6412 (binding-store-type init-with-type
))
6413 (apply #'encoded-type-decode
(binding-store-type init-with-type
)))
6414 (t (values binding t
6416 (list init-with-register
)))))
6417 ((not (typep binding
'temporary-name
))
6418 (values binding t
)))))
6420 (define-find-read-bindings :init-lexvar
(binding &key init-with-register
&allow-other-keys
)
6421 (declare (ignore binding
))
6422 (when (typep init-with-register
'binding
)
6423 (list init-with-register
)))
6425 (define-extended-code-expander :init-lexvar
(instruction funobj frame-map
)
6426 (destructuring-bind (binding &key protect-registers protect-carry
6427 init-with-register init-with-type
6430 (declare (ignore protect-carry
)) ; nothing modifies carry anyway.
6431 ;; (assert (eq binding (ensure-local-binding binding funobj)))
6432 (assert (eq funobj
(binding-funobj binding
)))
6434 ((not (new-binding-located-p binding frame-map
))
6435 (unless (or (movitz-env-get (binding-name binding
) 'ignore nil
(binding-env binding
))
6436 (movitz-env-get (binding-name binding
) 'ignorable nil
(binding-env binding
)))))
6437 ((typep binding
'forwarding-binding
)
6438 ;; No need to do any initialization because the target will be initialized.
6439 (assert (not (binding-lended-p binding
)))
6441 (t (when (movitz-env-get (binding-name binding
) 'ignore nil
(binding-env binding
))
6442 (warn "Variable ~S used while declared ignored." (binding-name binding
)))
6445 ((typep binding
'rest-function-argument
)
6446 (assert (eq :edx init-with-register
))
6447 (assert (movitz-env-get (binding-name binding
)
6448 'dynamic-extent nil
(binding-env binding
))
6450 "&REST variable ~S must be dynamic-extent." (binding-name binding
))
6451 (setf (need-normalized-ecx-p (find-function-env (binding-env binding
)
6454 (let ((restify-alloca-loop (gensym "alloca-loop-"))
6455 (restify-done (gensym "restify-done-"))
6456 (restify-at-one (gensym "restify-at-one-"))
6457 (restify-loop (gensym "restify-loop-"))
6458 (save-ecx-p (key-vars-p (find-function-env (binding-env binding
)
6461 ;; (make-immediate-move (function-argument-argnum binding) :edx)
6462 ;; `((:call (:edi ,(global-constant-offset 'restify-dynamic-extent))))
6463 ;; Make space for (1+ (* 2 (- ECX rest-pos))) words on the stack.
6464 ;; Factor two is for one cons-cell per word, 1 is for 8-byte alignment.
6466 `((,*compiler-local-segment-prefix
*
6467 :movl
:ecx
(:edi
,(global-constant-offset 'raw-scratch0
)))))
6469 (:subl
,(function-argument-argnum binding
) :ecx
)
6470 (:jbe
',restify-done
)
6471 (:leal
((:ecx
8) 4) :edx
) ; EDX is fixnum counter
6472 ,restify-alloca-loop
6475 (:jnz
',restify-alloca-loop
)
6476 ,@(when *compiler-auto-stack-checks-p
*
6477 `((,*compiler-local-segment-prefix
*
6478 :bound
(:edi
,(global-constant-offset 'stack-bottom
)) :esp
)))
6479 (:leal
(:esp
5) :edx
)
6480 (:andl -
7 :edx
)) ; Make EDX a proper consp into the alloca area.
6482 ((= 0 (function-argument-argnum binding
))
6483 `((:movl
:eax
(:edx -
1))
6486 (:jz
',restify-done
)
6488 (:movl
:eax
(:eax -
5))))
6489 (t `((:movl
:edx
:eax
))))
6490 (when (>= 1 (function-argument-argnum binding
))
6491 `((:jmp
',restify-at-one
)))
6493 (:movl
(:ebp
(:ecx
4) 4) :ebx
)
6495 (:movl
:ebx
(:eax -
1))
6497 (:jz
',restify-done
)
6499 (:movl
:eax
(:eax -
5))
6500 (:jmp
',restify-loop
)
6503 `((,*compiler-local-segment-prefix
*
6504 :movl
(:edi
,(global-constant-offset 'raw-scratch0
)) :ecx
)))
6507 ((binding-lended-p binding
)
6508 (let* ((cons-position (getf (binding-lending binding
)
6509 :stack-cons-location
))
6510 (init-register (etypecase init-with-register
6511 ((or lexical-binding constant-object-binding
)
6512 (or (find-if (lambda (r)
6513 (not (member r protect-registers
)))
6515 (error "Unable to get a register.")))
6516 (keyword init-with-register
)
6518 (tmp-register (find-if (lambda (r)
6519 (and (not (member r protect-registers
))
6520 (not (eq r init-register
))))
6521 '(:edx
:ebx
:eax
))))
6522 (when init-with-register
6523 (assert (not (null init-with-type
))))
6524 (assert tmp-register
() ; solve this with push eax .. pop eax if ever needed.
6525 "Unable to find a tmp-register for ~S." instruction
)
6526 (append (when (typep init-with-register
'binding
)
6527 (make-load-lexical init-with-register init-register funobj
6528 shared-reference-p frame-map
6529 :protect-registers protect-registers
))
6530 `((:leal
(:ebp
,(1+ (stack-frame-offset (1+ cons-position
))))
6532 (:movl
:edi
(,tmp-register
3)) ; cdr
6533 (:movl
,init-register
(,tmp-register -
1)) ; car
6534 (:movl
,tmp-register
6535 (:ebp
,(stack-frame-offset
6536 (new-binding-location binding frame-map
))))))))
6537 ((typep init-with-register
'lexical-binding
)
6538 (make-load-lexical init-with-register binding funobj nil frame-map
))
6540 (make-store-lexical binding init-with-register nil funobj frame-map
))))))))
6542 ;;;;;;;;;;;;;;;;;; car
6544 (define-find-read-bindings :cons-get
(op cell dst
)
6545 (declare (ignore op dst protect-registers
))
6546 (when (typep cell
'binding
)
6549 (define-extended-code-expander :cons-get
(instruction funobj frame-map
)
6550 (destructuring-bind (op cell dst
)
6552 (check-type dst
(member :eax
:ebx
:ecx
:edx
))
6553 (multiple-value-bind (op-offset fast-op fast-op-ebx cl-op
)
6555 (:car
(values (bt:slot-offset
'movitz-cons
'car
)
6559 (:cdr
(values (bt:slot-offset
'movitz-cons
'cdr
)
6563 (let ((binding (binding-target (ensure-local-binding (binding-target cell
) funobj
))))
6565 (constant-object-binding
6566 (let ((x (constant-object binding
)))
6569 (make-load-constant *movitz-nil
* dst funobj frame-map
))
6571 (append (make-load-constant x dst funobj frame-map
)
6572 `((:movl
(,dst
,op-offset
) ,dst
))))
6573 (t `(,@(make-load-lexical binding
:eax funobj nil frame-map
)
6574 (,*compiler-global-segment-prefix
*
6575 :call
(:edi
,(global-constant-offset fast-op
)))
6576 ,@(when (not (eq dst
:eax
))
6577 `((:movl
:eax
,dst
))))))))
6579 (let ((location (new-binding-location (binding-target binding
) frame-map
))
6580 (binding-is-list-p (binding-store-subtypep binding
'list
)))
6581 #+ignore
(warn "~A of loc ~A bind ~A" op location binding
)
6583 ((and binding-is-list-p
6584 (member location
'(:eax
:ebx
:ecx
:edx
)))
6585 `((,*compiler-nonlocal-lispval-read-segment-prefix
*
6586 :movl
(,location
,op-offset
) ,dst
)))
6588 `(,@(make-load-lexical binding dst funobj nil frame-map
)
6589 (,*compiler-nonlocal-lispval-read-segment-prefix
*
6590 :movl
(,dst
,op-offset
) ,dst
)))
6591 ((not *compiler-use-cons-reader-segment-protocol-p
*)
6594 `((,*compiler-global-segment-prefix
*
6595 :call
(:edi
,(global-constant-offset fast-op-ebx
)))
6596 ,@(when (not (eq dst
:eax
))
6597 `((:movl
:eax
,dst
)))))
6598 (t `(,@(make-load-lexical binding
:eax funobj nil frame-map
)
6599 (,*compiler-global-segment-prefix
*
6600 :call
(:edi
,(global-constant-offset fast-op
)))
6601 ,@(when (not (eq dst
:eax
))
6602 `((:movl
:eax
,dst
)))))))
6604 ((member location
'(:ebx
:ecx
:edx
))
6605 `((,(or *compiler-cons-read-segment-prefix
*
6606 *compiler-nonlocal-lispval-read-segment-prefix
*)
6607 :movl
(:eax
,op-offset
) ,dst
)))
6608 (t (append (make-load-lexical binding
:eax funobj nil frame-map
)
6609 `((,(or *compiler-cons-read-segment-prefix
*
6610 *compiler-nonlocal-lispval-read-segment-prefix
*)
6611 :movl
(:eax
,op-offset
) ,dst
))))))))))))))
6614 ;;;;;;;;;;;;;;;;;; endp
6616 (define-find-read-bindings :endp
(cell result-mode
)
6617 (declare (ignore result-mode
))
6618 (when (typep cell
'binding
)
6621 (define-extended-code-expander :endp
(instruction funobj frame-map
)
6622 (destructuring-bind (cell result-mode
)
6624 (let ((binding (binding-target (ensure-local-binding (binding-target cell
) funobj
))))
6626 (constant-object-binding
6627 (let ((x (constant-object binding
)))
6630 (make-load-constant *movitz-nil
* result-mode funobj frame-map
))
6632 (make-load-constant (image-t-symbol *image
*) result-mode funobj frame-map
))
6635 (let* ((location (new-binding-location (binding-target binding
) frame-map
))
6636 (binding-is-list-p (binding-store-subtypep binding
'list
))
6637 (tmp-register (case location
6638 ((:eax
:ebx
:ecx
:edx
)
6640 ;; (warn "endp of loc ~A bind ~A" location binding)
6642 ((and binding-is-list-p
6643 (member location
'(:eax
:ebx
:ecx
:edx
)))
6644 (make-result-and-returns-glue result-mode
:boolean-zf
=1
6645 `((:cmpl
:edi
,location
))))
6646 ((eq :boolean-branch-on-true
(result-mode-type result-mode
))
6647 (let ((tmp-register (or tmp-register
:ecx
)))
6648 (append (make-load-lexical binding
6649 (cons :boolean-branch-on-false
6651 funobj nil frame-map
)
6652 (unless binding-is-list-p
6653 (append (make-load-lexical binding tmp-register funobj nil frame-map
)
6654 `((:leal
(,tmp-register -
1) :ecx
)
6656 (:jnz
'(:sub-program
(,(gensym "endp-not-list-"))
6658 (t (let ((tmp-register (or tmp-register
:eax
)))
6659 (append (make-load-lexical binding tmp-register funobj nil frame-map
)
6660 (unless binding-is-list-p
6661 `((:leal
(,tmp-register -
1) :ecx
)
6663 (:jnz
'(:sub-program
(,(gensym "endp-not-list-"))
6665 `((:cmpl
:edi
,tmp-register
))
6666 (make-result-and-returns-glue result-mode
:boolean-zf
=1)))))))))))
6669 ;;;;;;;;;;;;;;;;;; incf-lexvar
6671 (define-find-write-binding-and-type :incf-lexvar
(instruction)
6672 (destructuring-bind (binding delta
&key protect-registers
)
6674 (declare (ignore delta protect-registers
))
6675 (values binding
'integer
)))
6677 (define-find-read-bindings :incf-lexvar
(binding delta
&key protect-registers
)
6678 (declare (ignore delta protect-registers binding
))
6681 (define-extended-code-expander :incf-lexvar
(instruction funobj frame-map
)
6682 (break "incf-lexvar??")
6683 (destructuring-bind (binding delta
&key protect-registers
)
6685 (check-type binding binding
)
6686 (check-type delta integer
)
6687 (let* ((binding (binding-target binding
))
6688 (location (new-binding-location binding frame-map
:default nil
))
6689 (binding-type (binding-store-type binding
)))
6690 ;;; (warn "incf b ~A, loc: ~A, typ: ~A" binding location binding-type)
6694 (not (binding-lended-p binding
))
6695 (binding-store-subtypep binding
'integer
))
6696 ;; This is an optimized incf that doesn't have to do type-checking.
6697 (check-type location
(integer 1 *))
6698 `((:addl
,(* delta
+movitz-fixnum-factor
+)
6699 (:ebp
,(stack-frame-offset location
)))
6701 ((binding-store-subtypep binding
'integer
)
6702 (let ((register (chose-free-register protect-registers
)))
6703 `(,@(make-load-lexical (ensure-local-binding binding funobj
)
6704 register funobj nil frame-map
6705 :protect-registers protect-registers
)
6706 (:addl
,(* delta
+movitz-fixnum-factor
+) :eax
)
6708 ,@(make-store-lexical (ensure-local-binding binding funobj
)
6709 register nil funobj frame-map
6710 :protect-registers protect-registers
))))
6711 (t (let ((register (chose-free-register protect-registers
)))
6712 `(,@(make-load-lexical (ensure-local-binding binding funobj
)
6713 register funobj nil frame-map
6714 :protect-registers protect-registers
)
6715 (:testb
,+movitz-fixnum-zmask
+ ,(register32-to-low8 register
))
6716 (:jnz
'(:sub-program
(,(gensym "not-integer-"))
6719 (:addl
,(* delta
+movitz-fixnum-factor
+) ,register
)
6721 ,@(make-store-lexical (ensure-local-binding binding funobj
)
6722 register nil funobj frame-map
6723 :protect-registers protect-registers
))))))))
6727 (define-find-write-binding-and-type :load-constant
(instruction)
6728 (destructuring-bind (object result-mode
&key
(op :movl
))
6730 (when (and (eq op
:movl
) (typep result-mode
'binding
))
6731 (check-type result-mode lexical-binding
)
6732 (values result-mode
`(eql ,object
)))))
6734 (define-extended-code-expander :load-constant
(instruction funobj frame-map
)
6735 (destructuring-bind (object result-mode
&key
(op :movl
))
6737 (make-load-constant object result-mode funobj frame-map
:op op
)))
6741 (define-find-write-binding-and-type :add
(instruction)
6742 (destructuring-bind (term0 term1 destination
)
6744 (when (typep destination
'binding
)
6745 (assert (and (bindingp term0
) (bindingp term1
)))
6748 (lambda (type0 type1
)
6749 (let ((x (multiple-value-call #'encoded-integer-types-add
6750 (type-specifier-encode type0
)
6751 (type-specifier-encode type1
))))
6752 #+ignore
(warn "thunked: ~S ~S -> ~S" term0 term1 x
)
6757 (define-find-used-bindings :add
(term0 term1 destination
)
6758 (if (bindingp destination
)
6759 (list term0 term1 destination
)
6760 (list term0 term1
)))
6762 (define-find-read-bindings :add
(term0 term1 destination
)
6763 (declare (ignore destination
))
6764 (let* ((type0 (and (binding-store-type term0
)
6765 (apply #'encoded-type-decode
(binding-store-type term0
))))
6766 (type1 (and (binding-store-type term1
)
6767 (apply #'encoded-type-decode
(binding-store-type term1
))))
6768 (singleton0 (and type0
(type-specifier-singleton type0
)))
6769 (singleton1 (and type1
(type-specifier-singleton type1
)))
6770 (singleton-sum (and singleton0 singleton1
6771 (type-specifier-singleton
6772 (apply #'encoded-integer-types-add
6773 (append (binding-store-type term0
)
6774 (binding-store-type term1
)))))))
6777 (let ((b (make-instance 'constant-object-binding
6778 :name
(gensym "constant-sum")
6779 :object
(car singleton-sum
))))
6780 (movitz-env-add-binding (binding-env term0
) b
)
6782 (t (append (unless (and singleton0
(typep (car singleton0
) 'movitz-fixnum
))
6784 (unless (and singleton1
(typep (car singleton1
) 'movitz-fixnum
))
6787 (define-extended-code-expander :add
(instruction funobj frame-map
)
6788 (destructuring-bind (term0 term1 destination
)
6790 (assert (and (bindingp term0
)
6792 (member (result-mode-type destination
)
6793 '(:lexical-binding
:function
:multple-values
:eax
:ebx
:ecx
:edx
))))
6794 (let* ((destination (ensure-local-binding destination funobj
))
6795 (term0 (ensure-local-binding term0 funobj
))
6796 (term1 (ensure-local-binding term1 funobj
))
6797 (destination-location (if (or (not (bindingp destination
))
6798 (typep destination
'borrowed-binding
))
6800 (new-binding-location (binding-target destination
)
6803 (type0 (apply #'encoded-type-decode
(binding-store-type term0
)))
6804 (type1 (apply #'encoded-type-decode
(binding-store-type term1
)))
6805 (result-type (multiple-value-call #'encoded-integer-types-add
6806 (values-list (binding-store-type term0
))
6807 (values-list (binding-store-type term1
)))))
6808 ;; A null location means the binding is unused, in which
6809 ;; case there's no need to perform the addition.
6810 (when destination-location
6811 (let ((loc0 (new-binding-location (binding-target term0
) frame-map
:default nil
))
6812 (loc1 (new-binding-location (binding-target term1
) frame-map
:default nil
)))
6814 (warn "add: ~A for ~A" instruction result-type
)
6816 (warn "add for: ~S is ~A, from ~A/~A and ~A/~A."
6817 destination result-type
6821 (when (eql destination-location
9)
6822 (warn "add for: ~S/~S~%= ~A/~A in ~S~&~A/~A in ~S."
6823 destination destination-location
6824 term0 loc0
(binding-extent-env (binding-target term0
))
6825 term1 loc1
(binding-extent-env (binding-target term1
)))
6826 (print-code 'load-term1
(make-load-lexical term1
:eax funobj nil frame-map
))
6827 (print-code 'load-dest
(make-load-lexical destination
:eax funobj nil frame-map
)))
6828 (flet ((make-store (source destination
)
6830 ((eq source destination
)
6832 ((member destination
'(:eax
:ebx
:ecx
:edx
))
6833 `((:movl
,source
,destination
)))
6834 (t (make-store-lexical destination source nil funobj frame-map
))))
6835 (make-default-add ()
6836 (when (movitz-subtypep result-type
'(unsigned-byte 32))
6837 (warn "Defaulting u32 ADD: ~A/~S = ~A/~S + ~A/~S"
6838 destination-location
6843 ((type-specifier-singleton type0
)
6844 (append (make-load-lexical term1
:eax funobj nil frame-map
)
6845 (make-load-constant (car (type-specifier-singleton type0
))
6846 :ebx funobj frame-map
)))
6847 ((type-specifier-singleton type1
)
6848 (append (make-load-lexical term0
:eax funobj nil frame-map
)
6849 (make-load-constant (car (type-specifier-singleton type1
))
6850 :ebx funobj frame-map
)))
6851 ((and (eq :eax loc0
) (eq :ebx loc1
))
6853 ((and (eq :ebx loc0
) (eq :eax loc1
))
6854 nil
) ; terms order isn't important
6857 (make-load-lexical term0
:ebx funobj nil frame-map
)))
6859 (make-load-lexical term0
:eax funobj nil frame-map
)
6860 (make-load-lexical term1
:ebx funobj nil frame-map
))))
6861 `((:movl
(:edi
,(global-constant-offset '+)) :esi
))
6862 (make-compiled-funcall-by-esi 2)
6863 (etypecase destination
6865 (unless (eq destination
:eax
)
6866 `((:movl
:eax
,destination
))))
6868 (make-store-lexical destination
:eax nil funobj frame-map
))))))
6869 (let ((constant0 (let ((x (type-specifier-singleton type0
)))
6870 (when (and x
(typep (car x
) 'movitz-fixnum
))
6871 (movitz-immediate-value (car x
)))))
6872 (constant1 (let ((x (type-specifier-singleton type1
)))
6873 (when (and x
(typep (car x
) 'movitz-fixnum
))
6874 (movitz-immediate-value (car x
))))))
6876 ((type-specifier-singleton result-type
)
6877 ;; (break "constant add: ~S" instruction)
6878 (make-load-constant (car (type-specifier-singleton result-type
))
6879 destination funobj frame-map
))
6880 ((movitz-subtypep type0
'(integer 0 0))
6882 ((eql destination loc1
)
6883 #+ignore
(break "NOP add: ~S" instruction
)
6885 ((and (member destination-location
'(:eax
:ebx
:ecx
:edx
))
6886 (member loc1
'(:eax
:ebx
:ecx
:edx
)))
6887 `((:movl
,loc1
,destination-location
)))
6889 (make-load-lexical term1 destination funobj nil frame-map
))
6891 ((integerp destination-location
)
6892 (make-store-lexical destination-location loc1 nil funobj frame-map
))
6893 (t (break "Unknown X zero-add: ~S" instruction
))))
6894 ((movitz-subtypep type1
'(integer 0 0))
6895 ;; (warn "zero-add ~S => ~S [~S]" loc0 destination-location result-type)
6897 ((eql destination-location loc0
)
6898 #+ignore
(break "NOP add: ~S" instruction
)
6900 ((and (member destination-location
'(:eax
:ebx
:ecx
:edx
))
6901 (member loc0
'(:eax
:ebx
:ecx
:edx
)))
6902 `((:movl
,loc0
,destination-location
)))
6903 ((member loc0
'(:eax
:ebx
:ecx
:edx
))
6904 (make-store-lexical destination loc0 nil funobj frame-map
))
6906 (make-load-lexical term0 destination funobj nil frame-map
))
6907 (t (break "Unknown Y zero-add: ~S" instruction
))))
6908 ((and (movitz-subtypep type0
'fixnum
)
6909 (movitz-subtypep type1
'fixnum
)
6910 (movitz-subtypep result-type
'fixnum
))
6911 (assert (not (and constant0
(zerop constant0
))))
6912 (assert (not (and constant1
(zerop constant1
))))
6914 ((and (not (binding-lended-p (binding-target term0
)))
6915 (not (binding-lended-p (binding-target term1
)))
6916 (not (and (bindingp destination
)
6917 (binding-lended-p (binding-target destination
)))))
6920 (equal loc1 destination-location
))
6922 ((member destination-location
'(:eax
:ebx
:ecx
:edx
))
6923 `((:addl
,constant0
,destination-location
)))
6925 `((:addl
,constant0
(:ebp
,(stack-frame-offset loc1
)))))
6926 ((eq :argument-stack
(operator loc1
))
6928 (:ebp
,(argument-stack-offset (binding-target term1
))))))
6929 ((eq :untagged-fixnum-ecx
(operator loc1
))
6930 `((:addl
,(truncate constant0
+movitz-fixnum-factor
+) :ecx
)))
6931 (t (error "Don't know how to add this for loc1 ~S" loc1
))))
6933 (integerp destination-location
)
6934 (eql term1 destination-location
))
6936 `((:addl
,constant0
(:ebp
,(stack-frame-offset destination-location
)))))
6938 (integerp destination-location
)
6939 (member loc1
'(:eax
:ebx
:ecx
:edx
)))
6940 `((:addl
,constant0
,loc1
)
6941 (:movl
,loc1
(:ebp
,(stack-frame-offset destination-location
)))))
6942 ((and (integerp loc0
)
6944 (member destination-location
'(:eax
:ebx
:ecx
:edx
)))
6945 (append `((:movl
(:ebp
,(stack-frame-offset loc0
)) ,destination-location
)
6946 (:addl
(:ebp
,(stack-frame-offset loc1
)) ,destination-location
))))
6947 ((and (integerp destination-location
)
6948 (eql loc0 destination-location
)
6950 `((:addl
,constant1
(:ebp
,(stack-frame-offset destination-location
)))))
6951 ((and (integerp destination-location
)
6952 (eql loc1 destination-location
)
6954 `((:addl
,constant0
(:ebp
,(stack-frame-offset destination-location
)))))
6955 ((and (member destination-location
'(:eax
:ebx
:ecx
:edx
))
6956 (eq loc0
:untagged-fixnum-ecx
)
6958 `((:leal
((:ecx
,+movitz-fixnum-factor
+) ,constant1
)
6959 ,destination-location
)))
6960 ((and (member destination-location
'(:eax
:ebx
:ecx
:edx
))
6963 `((:movl
(:ebp
,(stack-frame-offset loc1
)) ,destination-location
)
6964 (:addl
,constant0
,destination-location
)))
6965 ((and (member destination-location
'(:eax
:ebx
:ecx
:edx
))
6968 `((:movl
(:ebp
,(stack-frame-offset loc0
)) ,destination-location
)
6969 (:addl
,constant1
,destination-location
)))
6970 ((and (member destination-location
'(:eax
:ebx
:ecx
:edx
))
6972 (member loc1
'(:eax
:ebx
:ecx
:edx
))
6973 (not (eq destination-location loc1
)))
6974 `((:movl
(:ebp
,(stack-frame-offset loc0
)) ,destination-location
)
6975 (:addl
,loc1
,destination-location
)))
6976 ((and (member destination-location
'(:eax
:ebx
:ecx
:edx
))
6978 (member loc1
'(:eax
:ebx
:ecx
:edx
)))
6979 `((:leal
(,loc1
,constant0
) ,destination-location
)))
6980 ((and (member destination-location
'(:eax
:ebx
:ecx
:edx
))
6982 (member loc0
'(:eax
:ebx
:ecx
:edx
)))
6983 `((:leal
(,loc0
,constant1
) ,destination-location
)))
6984 ((and (member destination-location
'(:eax
:ebx
:ecx
:edx
))
6986 (eq :argument-stack
(operator loc1
)))
6987 `((:movl
(:ebp
,(argument-stack-offset (binding-target term1
)))
6988 ,destination-location
)
6989 (:addl
,constant0
,destination-location
)))
6990 ((and (member destination-location
'(:eax
:ebx
:ecx
:edx
))
6992 (eq :argument-stack
(operator loc0
)))
6993 `((:movl
(:ebp
,(argument-stack-offset (binding-target term0
)))
6994 ,destination-location
)
6995 (:addl
,constant1
,destination-location
)))
6997 (append (make-load-lexical term1
:eax funobj nil frame-map
)
6998 `((:addl
,constant0
:eax
))
6999 (make-store :eax destination
)))
7001 (append (make-load-lexical term0
:eax funobj nil frame-map
)
7002 `((:addl
,constant1
:eax
))
7003 (make-store :eax destination
)))
7005 (append (make-load-lexical term0
:eax funobj nil frame-map
)
7006 `((:addl
:eax
:eax
))
7007 (make-store :eax destination
)))
7008 ((and (integerp loc0
)
7010 (integerp destination-location
)
7011 (/= loc0 loc1 destination-location
))
7012 `((:movl
(:ebp
,(stack-frame-offset loc0
))
7014 (:addl
(:ebp
,(stack-frame-offset loc1
))
7016 (:movl
:ecx
(:ebp
,(stack-frame-offset destination-location
)))))
7017 (t (warn "Unknown fixnum ADD: ~A/~S = ~A/~S + ~A/~S"
7018 destination-location
7022 #+ignore
(warn "map: ~A" frame-map
)
7023 ;;; (warn "ADDI: ~S" instruction)
7025 ((type-specifier-singleton type0
)
7026 (append (make-load-lexical term1
:eax funobj nil frame-map
)
7027 (make-load-constant (car (type-specifier-singleton type0
))
7028 :ebx funobj frame-map
)))
7029 ((type-specifier-singleton type1
)
7030 (append (make-load-lexical term0
:eax funobj nil frame-map
)
7031 (make-load-constant (car (type-specifier-singleton type1
))
7032 :ebx funobj frame-map
)))
7033 ((and (eq :eax loc0
) (eq :ebx loc1
))
7035 ((and (eq :ebx loc0
) (eq :eax loc1
))
7036 nil
) ; terms order isn't important
7039 (make-load-lexical term0
:ebx funobj nil frame-map
)))
7041 (make-load-lexical term0
:eax funobj nil frame-map
)
7042 (make-load-lexical term1
:ebx funobj nil frame-map
))))
7043 `((:movl
(:edi
,(global-constant-offset '+)) :esi
))
7044 (make-compiled-funcall-by-esi 2)
7045 (etypecase destination
7047 (unless (eq destination
:eax
)
7048 `((:movl
:eax
,destination
))))
7050 (make-store-lexical destination
:eax nil funobj frame-map
)))))))
7052 (integerp destination-location
)
7053 (eql loc1 destination-location
)
7054 (binding-lended-p (binding-target destination
)))
7055 (assert (binding-lended-p (binding-target term1
)))
7056 (append (make-load-lexical destination
:eax funobj t frame-map
)
7057 `((:addl
,constant0
(-1 :eax
)))))
7058 ((warn "~S" (list (and (bindingp destination
)
7059 (binding-lended-p (binding-target destination
)))
7060 (binding-lended-p (binding-target term0
))
7061 (binding-lended-p (binding-target term1
)))))
7062 (t (warn "Unknown fixnum add: ~S" instruction
)
7063 (make-default-add))))
7064 ((and (movitz-subtypep type0
'fixnum
)
7065 (movitz-subtypep type1
'fixnum
))
7066 (flet ((mkadd-into (src destreg
)
7067 (assert (eq destreg
:eax
) (destreg)
7068 "Movitz' INTO protocol says the overflowed value must be in EAX, ~
7069 but it's requested to be in ~S."
7071 (let ((srcloc (new-binding-location (binding-target src
) frame-map
)))
7072 (unless (eql srcloc loc1
) (break))
7073 (if (integerp srcloc
)
7074 `((:addl
(:ebp
,(stack-frame-offset srcloc
))
7077 (ecase (operator srcloc
)
7078 ((:eax
:ebx
:ecx
:edx
)
7079 `((:addl
,srcloc
,destreg
)
7082 `((:addl
(:ebx
,(argument-stack-offset src
))
7087 ((and (not constant0
)
7089 (not (binding-lended-p (binding-target term0
)))
7090 (not (binding-lended-p (binding-target term1
)))
7091 (not (and (bindingp destination
)
7092 (binding-lended-p (binding-target destination
)))))
7094 ((and (not (eq loc0
:untagged-fixnum-ecx
))
7095 (not (eq loc1
:untagged-fixnum-ecx
))
7096 (not (eq destination-location
:untagged-fixnum-ecx
)))
7098 ((and (eq loc0
:eax
) (eq loc1
:eax
))
7102 (mkadd-into term1
:eax
))
7104 (mkadd-into term0
:eax
))
7105 (t (append (make-load-lexical term0
:eax funobj nil frame-map
7106 :protect-registers
(list loc1
))
7107 (mkadd-into term1
:eax
))))
7108 (make-store :eax destination
)))
7109 (t (make-default-add)
7111 (append (make-load-lexical term0
:untagged-fixnum-ecx funobj nil frame-map
)
7112 `((,*compiler-local-segment-prefix
*
7113 :movl
:ecx
(:edi
,(global-constant-offset 'raw-scratch0
))))
7114 (make-load-lexical term1
:untagged-fixnum-ecx funobj nil frame-map
)
7115 `((,*compiler-local-segment-prefix
*
7116 :addl
(:edi
,(global-constant-offset 'raw-scratch0
)) :ecx
))
7117 (if (integerp destination-location
)
7118 `((,*compiler-local-segment-prefix
*
7119 :call
(:edi
,(global-constant-offset 'box-u32-ecx
)))
7120 (:movl
:eax
(:ebp
,(stack-frame-offset destination-location
))))
7121 (ecase (operator destination-location
)
7122 ((:untagged-fixnum-ecx
)
7125 `((,*compiler-local-segment-prefix
*
7126 :call
(:edi
,(global-constant-offset 'box-u32-ecx
)))))
7128 `((,*compiler-local-segment-prefix
*
7129 :call
(:edi
,(global-constant-offset 'box-u32-ecx
)))
7130 (:movl
:eax
,destination-location
)))
7132 `((,*compiler-local-segment-prefix
*
7133 :call
(:edi
,(global-constant-offset 'box-u32-ecx
)))
7134 (:movl
:eax
(:ebp
,(argument-stack-offset
7135 (binding-target destination
))))))))))))
7136 (t (make-default-add)))))
7137 (t (make-default-add))))))))))
7141 (define-find-read-bindings :eql
(x y mode
)
7142 (declare (ignore mode
))
7145 (define-extended-code-expander :eql
(instruction funobj frame-map
)
7146 (destructuring-bind (x y return-mode
)
7148 (let* ((x-type (apply #'encoded-type-decode
(binding-store-type x
)))
7149 (y-type (apply #'encoded-type-decode
(binding-store-type y
)))
7150 (x-singleton (type-specifier-singleton x-type
))
7151 (y-singleton (type-specifier-singleton y-type
)))
7152 (when (and y-singleton
(not x-singleton
))
7154 (rotatef x-type y-type
)
7155 (rotatef x-singleton y-singleton
))
7156 (let (#+ignore
(x-loc (new-binding-location (binding-target x
) frame-map
:default nil
))
7157 (y-loc (new-binding-location (binding-target y
) frame-map
:default nil
)))
7159 (warn "eql ~S/~S xx~Xxx ~S/~S: ~S"
7160 x x-loc
(binding-target y
)
7163 (flet ((make-branch ()
7164 (ecase (operator return-mode
)
7165 (:boolean-branch-on-false
7166 `((:jne
',(operands return-mode
))))
7167 (:boolean-branch-on-true
7168 `((:je
',(operands return-mode
))))
7170 (make-load-eax-ebx ()
7172 (make-load-lexical x
:ebx funobj nil frame-map
)
7173 (append (make-load-lexical x
:eax funobj nil frame-map
)
7174 (make-load-lexical y
:ebx funobj nil frame-map
)))))
7176 ((and x-singleton y-singleton
)
7177 (let ((eql (etypecase (car x-singleton
)
7178 (movitz-immediate-object
7179 (and (typep (car y-singleton
) 'movitz-immediate-object
)
7180 (eql (movitz-immediate-value (car x-singleton
))
7181 (movitz-immediate-value (car y-singleton
))))))))
7182 (case (operator return-mode
)
7183 (:boolean-branch-on-false
7185 `((:jmp
',(operands return-mode
)))))
7186 (t (break "Constant EQL: ~S ~S" (car x-singleton
) (car y-singleton
))))))
7188 (eq :untagged-fixnum-ecx y-loc
))
7189 (let ((value (etypecase (car x-singleton
)
7191 (movitz-fixnum-value (car x-singleton
)))
7193 (movitz-bignum-value (car x-singleton
))))))
7194 (check-type value
(unsigned-byte 32))
7195 `((:cmpl
,value
:ecx
)
7198 (typep (car x-singleton
) '(or movitz-immediate-object movitz-null
)))
7199 (let ((value (if (typep (car x-singleton
) 'movitz-null
)
7201 (movitz-immediate-value (car x-singleton
)))))
7204 (member y-loc
'(:eax
:ebx
:ecx
:edx
)))
7205 `((:testl
,y-loc
,y-loc
)))
7206 ((and (member y-loc
'(:eax
:ebx
:ecx
:edx
))
7207 (not (binding-lended-p y
)))
7208 `((:cmpl
,value
,y-loc
)))
7209 ((and (integerp y-loc
)
7210 (not (binding-lended-p y
)))
7211 `((:cmpl
,value
(:ebp
,(stack-frame-offset y-loc
)))))
7212 ((and (eq :argument-stack
(operator y-loc
))
7213 (not (binding-lended-p y
)))
7214 `((:cmpl
,value
(:ebp
,(argument-stack-offset (binding-target y
))))))
7215 (t (break "x-singleton: ~S with loc ~S"
7216 (movitz-immediate-value (car x-singleton
))
7220 (typep (car x-singleton
) 'movitz-symbol
)
7221 (member y-loc
'(:eax
:ebx
:edx
)))
7222 (append (make-load-constant (car x-singleton
) y-loc funobj frame-map
:op
:cmpl
)
7225 (break "y-singleton"))
7226 ((and (not (eq t x-type
)) ; this is for bootstrapping purposes.
7227 (not (eq t y-type
)) ; ..
7228 (or (movitz-subtypep x-type
'(or fixnum character symbol vector
))
7229 (movitz-subtypep y-type
'(or fixnum character symbol vector
))))
7230 (append (make-load-eax-ebx)
7231 `((:cmpl
:eax
:ebx
))
7234 ((warn "eql ~S/~S ~S/~S"
7237 ((eq :boolean-branch-on-false
(operator return-mode
))
7238 (let ((eql-done (gensym "eql-done-"))
7239 (on-false-label (operands return-mode
)))
7240 (append (make-load-eax-ebx)
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 (:jne
',on-false-label
)
7248 ((eq :boolean-branch-on-true
(operator return-mode
))
7249 (let ((on-true-label (operands return-mode
)))
7250 (append (make-load-eax-ebx)
7252 (:je
',on-true-label
)
7253 (,*compiler-global-segment-prefix
*
7254 :movl
(:edi
,(global-constant-offset 'complicated-eql
)) :esi
)
7255 (:call
(:esi
,(bt:slot-offset
'movitz-funobj
'code-vector%
2op
)))
7256 (:je
',on-true-label
)))))
7257 ((eq return-mode
:boolean-zf
=1)
7258 (append (make-load-eax-ebx)
7259 (let ((eql-done (gensym "eql-done-")))
7262 (,*compiler-global-segment-prefix
*
7263 :movl
(:edi
,(global-constant-offset 'complicated-eql
)) :esi
)
7264 (:call
(:esi
,(bt:slot-offset
'movitz-funobj
'code-vector%
2op
)))
7266 (t (error "unknown eql: ~S" instruction
))))))))
7268 (define-find-read-bindings :load-lambda
(lambda-binding result-mode capture-env
)
7269 (declare (ignore result-mode capture-env
))
7270 (let ((allocation (movitz-allocation (function-binding-funobj lambda-binding
))))
7271 (when (typep allocation
'with-dynamic-extent-scope-env
)
7272 (values (list (base-binding allocation
))
7275 (define-find-write-binding-and-type :enter-dynamic-scope
(instruction)
7276 (destructuring-bind (scope-env)
7278 (if (null (dynamic-extent-scope-members scope-env
))
7280 (values (base-binding scope-env
) 'fixnum
))))
7282 (define-extended-code-expander :enter-dynamic-scope
(instruction funobj frame-map
)
7283 (declare (ignore funobj frame-map
))
7284 (destructuring-bind (scope-env)
7286 (if (null (dynamic-extent-scope-members scope-env
))
7288 (append `((:pushl
:edi
)
7292 (loop for object in
(reverse (dynamic-extent-scope-members scope-env
))
7299 (append (unless (zerop (mod (sizeof object
) 8))
7301 `((:load-constant
,object
:eax
))
7302 (loop for i from
(1- (movitz-funobj-num-constants object
))
7303 downto
(movitz-funobj-num-jumpers object
)
7304 collect
`(:pushl
(:eax
,(slot-offset 'movitz-funobj
'constant0
)
7306 (loop repeat
(movitz-funobj-num-jumpers object
)
7307 collect
`(:pushl
0))
7308 `((:pushl
(:eax
,(slot-offset 'movitz-funobj
'num-jumpers
)))
7309 (:pushl
(:eax
,(slot-offset 'movitz-funobj
'name
)))
7310 (:pushl
(:eax
,(slot-offset 'movitz-funobj
'lambda-list
)))
7315 (:pushl
2) ; (default) 2 is recognized by map-header-vals as non-initialized funobj.
7317 (:pushl
(:eax
,(slot-offset 'movitz-funobj
'type
)))
7318 (:leal
(:esp
,(tag :other
)) :ebx
)
7319 (,*compiler-local-segment-prefix
*
7320 :call
(:edi
,(global-constant-offset 'copy-funobj-code-vector-slots
)))
7323 ;;;(define-extended-code-expander :exit-dynamic-scope (instruction funobj frame-map)
7326 (define-find-read-bindings :lexical-control-transfer
(return-code return-mode from-env to-env
7328 (declare (ignore return-code return-mode to-label
))
7329 (let ((distance (stack-delta from-env to-env
)))
7330 (when (eq t distance
)
7331 (values (list (movitz-binding (save-esp-variable to-env
) to-env nil
))
7334 (define-find-read-bindings :stack-cons
(proto-cons scope-env
)
7335 (declare (ignore proto-cons
))
7336 (values (list (base-binding scope-env
))
7339 (define-extended-code-expander :stack-cons
(instruction funobj frame-map
)
7340 (destructuring-bind (proto-cons dynamic-scope
)
7342 (append (make-load-lexical (base-binding dynamic-scope
) :edx
7343 funobj nil frame-map
)
7344 `((:movl
:eax
(:edx
,(dynamic-extent-object-offset dynamic-scope proto-cons
)))
7345 (:movl
:ebx
(:edx
,(+ 4 (dynamic-extent-object-offset dynamic-scope proto-cons
))))
7346 (:leal
(:edx
,(+ (tag :cons
) (dynamic-extent-object-offset dynamic-scope proto-cons
)))