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.194 2008/03/06 21:14: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-x86:*cpu-mode
* :32-bit
)
147 (asm:*instruction-compute-extra-prefix-map
*
148 '((:call . compute-call-extra-prefix
))))
149 (asm:assemble-proglist
(translate-program resolved-code
:muerte.cl
:cl
)
150 :symtab
(list (cons :nil-value
(image-nil-word *image
*)))))
151 (values (make-movitz-vector (length code-vector
)
153 :initial-contents code-vector
)
156 (defun register-function-code-size (funobj)
157 (let* ((name (movitz-print (movitz-funobj-name funobj
)))
159 (new-size (length (movitz-vector-symbolic-data (movitz-funobj-code-vector funobj
)))))
161 (let ((old-size (gethash hash-name
(function-code-sizes *image
*))))
164 ((not *warn-function-change-p
*))
165 ((> new-size old-size
)
166 (warn "~S grew from ~D to ~D bytes." name old-size new-size
))
167 ((< new-size old-size
)
168 (warn "~S shrunk from ~D to ~D bytes" name old-size new-size
))))
169 (setf (gethash hash-name
(function-code-sizes *image
*)) new-size
))
172 (defclass movitz-funobj-pass1
()
175 :accessor movitz-funobj-name
)
177 :initarg
:lambda-list
178 :accessor movitz-funobj-lambda-list
)
180 :accessor function-envs
)
183 :accessor funobj-env
)
187 :accessor movitz-funobj-extent
)
190 :accessor movitz-allocation
)
193 :initarg
:entry-protocol
194 :reader funobj-entry-protocol
))
195 (:documentation
"This class is used for funobjs during the first compiler pass.
196 Before the second pass, such objects will be change-class-ed to proper movitz-funobjs.
197 This way, we ensure that no undue side-effects on the funobj occur during pass 1."))
199 (defmethod print-object ((object movitz-funobj-pass1
) stream
)
200 (print-unreadable-object (object stream
:type t
:identity t
)
201 (when (slot-boundp object
'name
)
202 (write (movitz-funobj-name object
) :stream stream
)))
205 (defun movitz-macro-expander-make-function (lambda-form &key name
(type :unknown
))
206 "Make a lambda-form that is a macro-expander into a proper function.
207 Gensym a name whose symbol-function is set to the macro-expander, and return that symbol."
208 (let ((function-name (gensym (format nil
"~A-expander-~@[~A-~]" type name
))))
209 (if *compiler-compile-macro-expanders
*
210 (with-host-environment ()
211 (compile function-name lambda-form
))
212 (setf (symbol-function function-name
)
213 (coerce lambda-form
'function
)))
216 (defun make-compiled-funobj (name lambda-list declarations form env top-level-p
&key funobj
)
217 "Compiler entry-point for making a (lexically) top-level function."
218 (handler-bind (((or warning error
)
221 (if (not (boundp 'muerte.cl
:*compile-file-pathname
*))
222 (format *error-output
*
223 "~&;; While Movitz compiling ~S:" name
)
224 (format *error-output
*
225 "~&;; While Movitz compiling ~S in ~A:"
226 name muerte.cl
:*compile-file-pathname
*)))))
227 (with-retries-until-true (retry-funobj "Retry compilation of ~S." name
)
228 (make-compiled-funobj-pass2
229 (make-compiled-funobj-pass1 name lambda-list declarations
230 form env top-level-p
:funobj funobj
)))))
232 (defun make-compiled-funobj-pass1 (name lambda-list declarations form env top-level-p
234 "Per funobj (i.e. not necessarily top-level) entry-point for first-pass compilation.
235 If funobj is provided, its identity will be kept, but its type (and values) might change."
236 ;; The ability to provide funobj's identity is important when a
237 ;; function must be referenced before it can be compiled, e.g. for
238 ;; mutually recursive (lexically bound) functions.
239 (with-retries-until-true (retry-pass1 "Retry first-pass compilation of ~S." name
)
240 ;; First-pass is mostly functional, so it can safely be restarted.
241 (multiple-value-bind (required-vars optional-vars rest-var key-vars aux-vars allow-p min max edx-var
)
242 (decode-normal-lambda-list lambda-list
)
243 (declare (ignore aux-vars allow-p min max
))
244 ;; There are several main branches through the function
245 ;; compiler, and this is where we decide which one to take.
247 ((let ((sub-form (cddr form
)))
248 (and (consp (car sub-form
))
249 (eq 'muerte
::numargs-case
(caar sub-form
))))
250 'make-compiled-function-pass1-numarg-case
)
251 ((and (= 1 (length required-vars
)) ; (x &optional y)
252 (= 1 (length optional-vars
))
253 (movitz-constantp (nth-value 1 (decode-optional-formal (first optional-vars
)))
258 'make-compiled-function-pass1-1req1opt
)
259 (t 'make-compiled-function-pass1
))
260 name lambda-list declarations form env top-level-p funobj
))))
262 (defun ensure-pass1-funobj (funobj class
&rest init-args
)
263 "If funobj is nil, return a fresh funobj of class.
264 Otherwise coerce funobj to class."
265 (apply #'reinitialize-instance
267 (change-class funobj class
)
268 (make-instance class
))
271 (defun make-compiled-function-pass1-numarg-case (name lambda-list declarations form env top-level-p funobj
)
272 (let* ((funobj (ensure-pass1-funobj funobj
'movitz-funobj-pass1
273 :entry-protocol
:numargs-case
275 :lambda-list
(movitz-read (lambda-list-simplify lambda-list
))))
276 (funobj-env (make-local-movitz-environment env funobj
:type
'funobj-env
)))
277 (setf (funobj-env funobj
) funobj-env
278 (function-envs funobj
) nil
)
279 (loop for
(numargs lambda-list . clause-body
) in
(cdr (caddr form
))
280 do
(when (duplicatesp lambda-list
)
281 (error "There are duplicates in lambda-list ~S." lambda-list
))
282 (multiple-value-bind (clause-body clause-declarations
)
283 (parse-declarations-and-body clause-body
)
285 (add-bindings-from-lambda-list lambda-list
286 (make-local-movitz-environment
289 :declaration-context
:funobj
291 (append clause-declarations
293 (function-form (list* 'muerte.cl
::block
294 (compute-function-block-name name
)
296 (multiple-value-bind (arg-init-code need-normalized-ecx-p
)
297 (make-function-arguments-init funobj function-env
)
298 (setf (extended-code function-env
)
299 (append arg-init-code
300 (compiler-call #'compile-form
301 :form
(make-special-funarg-shadowing function-env function-form
)
304 :top-level-p top-level-p
305 :result-mode
:function
)))
306 (setf (need-normalized-ecx-p function-env
) need-normalized-ecx-p
))
307 (push (cons numargs function-env
)
308 (function-envs funobj
)))))
311 (defun make-compiled-function-pass1-1req1opt (name lambda-list declarations form env top-level-p funobj
)
313 (when (duplicatesp lambda-list
)
314 (error "There are duplicates in lambda-list ~S." lambda-list
))
315 (let* ((funobj (ensure-pass1-funobj funobj
'movitz-funobj-pass1
316 :entry-protocol
:1req1opt
318 :lambda-list
(movitz-read (lambda-list-simplify lambda-list
))))
319 (funobj-env (make-local-movitz-environment env funobj
:type
'funobj-env
))
320 (function-env (add-bindings-from-lambda-list
322 (make-local-movitz-environment funobj-env funobj
324 :need-normalized-ecx-p nil
325 :declaration-context
:funobj
326 :declarations declarations
)))
327 (optional-env (make-local-movitz-environment function-env funobj
328 :type
'function-env
)))
329 (setf (funobj-env funobj
) funobj-env
)
330 ;; (print-code 'arg-init-code arg-init-code)
331 (setf (extended-code optional-env
)
332 (compiler-call #'compile-form
333 :form
(optional-function-argument-init-form
334 (movitz-binding (first (optional-vars function-env
)) function-env nil
))
338 (setf (extended-code function-env
)
339 (append #+ignore arg-init-code
340 (compiler-call #'compile-form
341 :form
(make-special-funarg-shadowing function-env form
)
344 :top-level-p top-level-p
345 :result-mode
:function
)))
346 (setf (function-envs funobj
)
347 (list (cons 'muerte.cl
::t function-env
)
348 (cons :optional optional-env
)))
351 (defun make-compiled-function-pass1 (name lambda-list declarations form env top-level-p funobj
)
353 (when (duplicatesp lambda-list
)
354 (error "There are duplicates in lambda-list ~S." lambda-list
))
355 (let* ((funobj (ensure-pass1-funobj funobj
'movitz-funobj-pass1
357 :lambda-list
(movitz-read (lambda-list-simplify lambda-list
))))
358 (funobj-env (make-local-movitz-environment env funobj
:type
'funobj-env
))
359 (function-env (add-bindings-from-lambda-list
361 (make-local-movitz-environment funobj-env funobj
363 :declaration-context
:funobj
364 :declarations declarations
))))
365 (setf (funobj-env funobj
) funobj-env
366 (function-envs funobj
) (list (cons 'muerte.cl
::t function-env
)))
367 (multiple-value-bind (arg-init-code need-normalized-ecx-p
)
368 (make-function-arguments-init funobj function-env
)
369 (setf (need-normalized-ecx-p function-env
) need-normalized-ecx-p
)
370 (setf (extended-code function-env
)
371 (append arg-init-code
372 (compiler-call #'compile-form
373 :form
(make-special-funarg-shadowing function-env form
)
376 :top-level-p top-level-p
377 :result-mode
:function
))))
381 (defun make-compiled-funobj-pass2 (toplevel-funobj-pass1)
382 "This is the entry-poing for second pass compilation for each top-level funobj."
383 (check-type toplevel-funobj-pass1 movitz-funobj-pass1
)
384 (let ((toplevel-funobj (change-class toplevel-funobj-pass1
'movitz-funobj
)))
385 (multiple-value-bind (toplevel-funobj function-binding-usage
)
386 (resolve-borrowed-bindings toplevel-funobj
)
390 (resolve-sub-functions toplevel-funobj function-binding-usage
)))))))
392 (defstruct (type-analysis (:type list
))
396 (multiple-value-list (type-specifier-encode nil
)))
397 (declared-encoded-type
398 (multiple-value-list (type-specifier-encode t
))))
400 (defun make-type-analysis-with-declaration (binding)
402 (if (not (and *compiler-trust-user-type-declarations-p
*
403 (movitz-env-get (binding-name binding
) :variable-type
404 nil
(binding-env binding
) nil
)))
405 (multiple-value-list (type-specifier-encode t
))
407 (type-specifier-encode (movitz-env-get (binding-name binding
) :variable-type
408 t
(binding-env binding
) nil
))))))
409 ;; (warn "~S decl: ~A" binding (apply #'encoded-type-decode declared-type))
410 (make-type-analysis :declared-encoded-type declared-type
)))
412 (defun analyze-bindings (toplevel-funobj)
413 "Figure out usage of bindings in a toplevel funobj.
414 Side-effects each binding's binding-store-type."
415 (if (not *compiler-do-type-inference
*)
417 ((analyze-code (code)
418 (dolist (instruction code
)
419 (when (listp instruction
)
421 (find-written-binding-and-type instruction
)))
423 (setf (binding-store-type binding
)
424 (multiple-value-list (type-specifier-encode t
)))))
425 (analyze-code (instruction-sub-program instruction
)))))
426 (analyze-funobj (funobj)
427 (loop for
(nil . function-env
) in
(function-envs funobj
)
428 do
(analyze-code (extended-code function-env
)))
429 (loop for function-binding in
(sub-function-binding-usage funobj
) by
#'cddr
430 do
(analyze-funobj (function-binding-funobj function-binding
)))
432 (analyze-funobj toplevel-funobj
))
433 (let ((binding-usage (make-hash-table :test
'eq
)))
434 (labels ((binding-resolved-p (binding)
435 (or (typep binding
'constant-object-binding
)
436 (typep binding
'function-argument
)
437 (let ((analysis (gethash binding binding-usage
)))
439 (null (type-analysis-thunks analysis
))))))
440 (binding-resolve (binding)
442 ((not (bindingp binding
))
444 ((typep binding
'constant-object-binding
)
445 (apply #'encoded-type-decode
446 (binding-store-type binding
)))
447 ((typep binding
'function-argument
)
449 ((let ((analysis (gethash binding binding-usage
)))
450 (assert (and (and analysis
451 (null (type-analysis-thunks analysis
))))
453 "Can't resolve unresolved binding ~S." binding
)))
454 (*compiler-trust-user-type-declarations-p
*
455 (let ((analysis (gethash binding binding-usage
)))
456 (multiple-value-call #'encoded-type-decode
457 (apply #'encoded-types-and
458 (append (type-analysis-declared-encoded-type analysis
)
459 (type-analysis-encoded-type analysis
))))))
460 (t (let ((analysis (gethash binding binding-usage
)))
461 (apply #'encoded-type-decode
462 (type-analysis-encoded-type analysis
))))))
463 (type-is-t (type-specifier)
464 (or (eq type-specifier t
)
465 (and (listp type-specifier
)
466 (eq 'or
(car type-specifier
))
467 (some #'type-is-t
(cdr type-specifier
)))))
468 (analyze-store (binding type thunk thunk-args
)
469 (assert (not (null type
)) ()
470 "store-lexical with empty type.")
471 (assert (or (typep type
'binding
)
472 (eql 1 (type-specifier-num-values type
))) ()
473 "store-lexical with multiple-valued type: ~S for ~S" type binding
)
474 #+ignore
(warn "store ~S type ~S, thunk ~S" binding type thunk
)
475 (let ((analysis (or (gethash binding binding-usage
)
476 (setf (gethash binding binding-usage
)
477 (make-type-analysis-with-declaration binding
)))))
480 (assert (some #'bindingp thunk-args
))
481 (push (cons thunk thunk-args
) (type-analysis-thunks analysis
)))
482 ((and (bindingp type
)
483 (binding-eql type binding
))
484 (break "got binding type")
486 (t (setf (type-analysis-encoded-type analysis
)
490 (values-list (type-analysis-encoded-type analysis
))
491 (type-specifier-encode type
))))))))
493 #+ignore
(print-code 'analyze code
)
494 (dolist (instruction code
)
495 (when (listp instruction
)
496 (multiple-value-bind (store-binding store-type thunk thunk-args
)
497 (find-written-binding-and-type instruction
)
499 #+ignore
(warn "store: ~S binding ~S type ~S thunk ~S"
500 instruction store-binding store-type thunk
)
501 (analyze-store store-binding store-type thunk thunk-args
)))
502 (analyze-code (instruction-sub-program instruction
)))))
503 (analyze-funobj (funobj)
504 (loop for
(nil . function-env
) in
(function-envs funobj
)
505 do
(analyze-code (extended-code function-env
)))
506 (loop for function-binding in
(sub-function-binding-usage funobj
) by
#'cddr
507 do
(analyze-funobj (function-binding-funobj function-binding
)))
509 ;; 1. Examine each store to lexical bindings.
510 (analyze-funobj toplevel-funobj
)
512 (flet ((resolve-thunks ()
513 (loop with more-thunks-p
= t
516 do
(setf more-thunks-p nil
)
517 (maphash (lambda (binding analysis
)
518 (declare (ignore binding
))
519 (setf (type-analysis-thunks analysis
)
520 (loop for
(thunk . thunk-args
) in
(type-analysis-thunks analysis
)
521 if
(not (every #'binding-resolved-p thunk-args
))
522 collect
(cons thunk thunk-args
)
525 (warn "because ~S=>~S->~S completing ~S: ~S and ~S"
527 (mapcar #'binding-resolve thunk-args
)
529 (type-analysis-declared-encoded-type analysis
)
534 (type-analysis-encoded-type analysis
))
535 (type-specifier-encode
536 (apply thunk
(mapcar #'binding-resolve
538 (setf (type-analysis-encoded-type analysis
)
543 (type-analysis-declared-encoded-type analysis
))
547 (type-analysis-encoded-type analysis
))
548 (type-specifier-encode
549 (apply thunk
(mapcar #'binding-resolve
551 (setf more-thunks-p t
))))
554 (when *compiler-trust-user-type-declarations-p
*
555 ;; For each unresolved binding, just use the declared type.
556 (maphash (lambda (binding analysis
)
557 (declare (ignore binding
))
558 (when (and (not (null (type-analysis-thunks analysis
)))
559 (not (apply #'encoded-allp
560 (type-analysis-declared-encoded-type analysis
))))
562 (warn "Trusting ~S, was ~S, because ~S [~S]"
564 (type-analysis-encoded-type analysis
)
565 (type-analysis-thunks analysis
)
566 (loop for
(thunk . thunk-args
) in
(type-analysis-thunks analysis
)
567 collect
(mapcar #'binding-resolved-p thunk-args
)))
568 (setf (type-analysis-encoded-type analysis
)
569 (type-analysis-declared-encoded-type analysis
))
570 (setf (type-analysis-thunks analysis
) nil
))) ; Ignore remaining thunks.
572 ;; Try one more time to resolve thunks.
575 (maphash (lambda (binding analysis
)
576 (when (type-analysis-thunks analysis
)
577 (warn "Unable to infer type for ~S: ~S" binding
578 (type-analysis-thunks analysis
))))
581 (maphash (lambda (binding analysis
)
582 (setf (binding-store-type binding
)
584 ((and (not (null (type-analysis-thunks analysis
)))
585 *compiler-trust-user-type-declarations-p
*
586 (movitz-env-get (binding-name binding
) :variable-type nil
587 (binding-env binding
) nil
))
589 (type-specifier-encode (movitz-env-get (binding-name binding
) :variable-type
590 t
(binding-env binding
) nil
))))
591 ((and *compiler-trust-user-type-declarations-p
*
592 (movitz-env-get (binding-name binding
) :variable-type nil
593 (binding-env binding
) nil
))
595 (multiple-value-call #'encoded-types-and
596 (type-specifier-encode (movitz-env-get (binding-name binding
) :variable-type
597 t
(binding-env binding
) nil
))
598 (values-list (type-analysis-encoded-type analysis
)))))
599 ((not (null (type-analysis-thunks analysis
)))
600 (multiple-value-list (type-specifier-encode t
)))
601 (t (type-analysis-encoded-type analysis
))))
602 #+ignore
(warn "Finally: ~S" binding
))
606 (defun resolve-borrowed-bindings (toplevel-funobj)
607 "For <funobj>'s code, for every non-local binding used we create
608 a borrowing-binding in the funobj-env. This process must be done
609 recursively, depth-first wrt. sub-functions. Also, return a plist
610 of all function-bindings seen."
611 (check-type toplevel-funobj movitz-funobj
)
612 (let ((function-binding-usage ()))
613 (labels ((process-binding (funobj binding usages
)
615 ((typep binding
'constant-object-binding
))
616 ((not (eq funobj
(binding-funobj binding
)))
617 (let ((borrowing-binding
618 (or (find binding
(borrowed-bindings funobj
)
619 :key
#'borrowed-binding-target
)
620 (car (push (movitz-env-add-binding (funobj-env funobj
)
621 (make-instance 'borrowed-binding
622 :name
(binding-name binding
)
623 :target-binding binding
))
624 (borrowed-bindings funobj
))))))
625 ;; We don't want to borrow a forwarding-binding..
626 (when (typep (borrowed-binding-target borrowing-binding
)
628 (change-class (borrowed-binding-target borrowing-binding
)
630 ;;; (warn "binding ~S of ~S is not local to ~S, replacing with ~S of ~S."
631 ;;; binding (binding-env binding) funobj
632 ;;; borrowing-binding (binding-env borrowing-binding))
633 ;;; (pushnew borrowing-binding
634 ;;; (getf (binding-lended-p binding) :lended-to))
635 (dolist (usage usages
)
636 (pushnew usage
(borrowed-binding-usage borrowing-binding
)))
638 (t ; Binding is local to this funobj
641 (process-binding funobj
(forwarding-binding-target binding
) usages
)
643 (setf (forwarding-binding-target binding
)
644 (process-binding funobj
(forwarding-binding-target binding
) usages
)))
646 (dolist (usage usages
)
648 (getf (sub-function-binding-usage (function-binding-parent binding
))
650 (pushnew usage
(getf function-binding-usage binding
)))
653 (resolve-sub-funobj (funobj sub-funobj
)
654 (dolist (binding-we-lend (borrowed-bindings (resolve-funobj-borrowing sub-funobj
)))
656 (warn "Lending from ~S to ~S: ~S <= ~S"
658 (borrowed-binding-target binding-we-lend
)
660 (process-binding funobj
661 (borrowed-binding-target binding-we-lend
)
662 (borrowed-binding-usage binding-we-lend
))))
663 (resolve-code (funobj code
)
664 (dolist (instruction code
)
665 (when (listp instruction
)
666 (let ((store-binding (find-written-binding-and-type instruction
)))
668 (process-binding funobj store-binding
'(:write
))))
669 (dolist (load-binding (find-read-bindings instruction
))
670 (process-binding funobj load-binding
'(:read
)))
671 (case (car instruction
)
673 (process-binding funobj
(second instruction
) '(:call
)))
675 (destructuring-bind (proto-cons dynamic-scope
)
677 (push proto-cons
(dynamic-extent-scope-members dynamic-scope
))))
679 (destructuring-bind (lambda-binding lambda-result-mode capture-env
)
681 (declare (ignore lambda-result-mode
))
682 (assert (eq funobj
(binding-funobj lambda-binding
)) ()
683 "A non-local lambda doesn't make sense. There must be a bug.")
684 (let ((lambda-funobj (function-binding-funobj lambda-binding
)))
685 (let ((dynamic-scope (find-dynamic-extent-scope capture-env
)))
687 ;; (warn "Adding ~S to ~S/~S" lambda-funobj dynamic-extent dynamic-scope)
688 (setf (movitz-funobj-extent lambda-funobj
) :dynamic-extent
689 (movitz-allocation lambda-funobj
) dynamic-scope
)
690 (push lambda-funobj
(dynamic-extent-scope-members dynamic-scope
))
691 (process-binding funobj
(base-binding dynamic-scope
) '(:read
))))
692 (resolve-sub-funobj funobj lambda-funobj
)
693 (process-binding funobj lambda-binding
'(:read
))
694 ;; This funobj is effectively using every binding that the lambda
696 (map nil
(lambda (borrowed-binding)
697 (process-binding funobj
698 (borrowed-binding-target borrowed-binding
)
700 (borrowed-bindings (function-binding-funobj lambda-binding
))))))
701 (:local-function-init
702 (let ((function-binding (second instruction
)))
703 (assert (eq funobj
(binding-funobj function-binding
)) ()
704 "Initialization of a non-local function doesn't make sense.")
705 (resolve-sub-funobj funobj
(function-binding-funobj (second instruction
)))
706 (map nil
(lambda (borrowed-binding)
707 (process-binding funobj
708 (borrowed-binding-target borrowed-binding
)
710 (borrowed-bindings (function-binding-funobj (second instruction
)))))))
711 (resolve-code funobj
(instruction-sub-program instruction
)))))
712 (resolve-funobj-borrowing (funobj)
713 (let ((funobj (change-class funobj
'movitz-funobj
:borrowed-bindings nil
)))
714 (loop for
(nil . function-env
) in
(function-envs funobj
)
715 do
(resolve-code funobj
(extended-code function-env
)))
716 ;; (warn "~S borrows ~S." funobj (borrowed-bindings funobj))
718 (values (resolve-funobj-borrowing toplevel-funobj
)
719 function-binding-usage
))))
721 (defun resolve-sub-functions (toplevel-funobj function-binding-usage
)
722 (assert (null (borrowed-bindings toplevel-funobj
)) ()
723 "Can't deal with toplevel closures yet. Borrowed: ~S"
724 (borrowed-bindings toplevel-funobj
))
725 (setf (movitz-funobj-extent toplevel-funobj
) :indefinite-extent
)
726 (let ((sub-funobj-index 0))
727 (loop for
(function-binding usage
) on function-binding-usage by
#'cddr
728 do
(let ((sub-funobj (function-binding-funobj function-binding
)))
729 ;; (warn "USage: ~S => ~S" sub-funobj usage)
730 (case (car (movitz-funobj-name sub-funobj
))
732 (setf (movitz-funobj-name sub-funobj
)
733 (list 'muerte.cl
:lambda
734 (movitz-funobj-name toplevel-funobj
)
735 (post-incf sub-funobj-index
)))))
736 (loop for borrowed-binding in
(borrowed-bindings sub-funobj
)
737 do
(pushnew borrowed-binding
738 (getf (binding-lending (borrowed-binding-target borrowed-binding
))
740 ;; (warn "old extent: ~S" (movitz-funobj-extent sub-funobj))
743 (null (borrowed-bindings sub-funobj
)))
745 (warn "null usage for ~S" sub-funobj
))
746 (change-class function-binding
'funobj-binding
)
747 (setf (movitz-funobj-extent sub-funobj
)
749 ((equal usage
'(:call
))
750 (change-class function-binding
'closure-binding
)
751 (setf (movitz-funobj-extent sub-funobj
)
753 ((eq :dynamic-extent
(movitz-funobj-extent sub-funobj
))
754 (change-class function-binding
'closure-binding
))
755 (t (change-class function-binding
'closure-binding
)
756 (setf (movitz-funobj-extent sub-funobj
)
757 :indefinite-extent
))))))
758 (loop for function-binding in function-binding-usage by
#'cddr
759 do
(finalize-funobj (function-binding-funobj function-binding
)))
760 (finalize-funobj toplevel-funobj
))
762 (defun finalize-funobj (funobj)
763 "Calculate funobj's constants, jumpers."
764 (loop with all-key-args-constants
= nil
765 with all-constants-plist
= () and all-jumper-sets
= ()
766 for
(nil . function-env
) in
(function-envs funobj
)
767 ;; (borrowed-bindings body-code) in code-specs
768 as body-code
= (extended-code function-env
)
769 as
(const-plist jumper-sets key-args-constants
) =
770 (multiple-value-list (find-code-constants-and-jumpers body-code
))
771 do
(when key-args-constants
772 (assert (not all-key-args-constants
) ()
773 "only one &key parsing allowed per funobj.")
774 (setf all-key-args-constants key-args-constants
))
775 (loop for
(constant usage
) on const-plist by
#'cddr
776 do
(incf (getf all-constants-plist constant
0) usage
))
777 (loop for
(name set
) on jumper-sets by
#'cddr
778 do
(assert (not (getf all-jumper-sets name
)) ()
779 "Jumper-set ~S multiply defined." name
)
780 (setf (getf all-jumper-sets name
) set
))
782 (multiple-value-bind (const-list num-jumpers jumpers-map borrower-map
)
783 (layout-funobj-vector all-constants-plist
784 all-key-args-constants
785 #+ignore
(mapcar (lambda (x)
786 (cons (movitz-read x
) 1))
789 (borrowed-bindings funobj
))
790 (setf (movitz-funobj-num-jumpers funobj
) num-jumpers
791 (movitz-funobj-const-list funobj
) const-list
792 (movitz-funobj-num-constants funobj
) (length const-list
)
793 (movitz-funobj-jumpers-map funobj
) jumpers-map
)
794 (loop for
(binding . pos
) in borrower-map
795 do
(setf (borrowed-binding-reference-slot binding
) pos
))
798 (defun layout-stack-frames (funobj)
799 "Lay out the stack-frame (i.e. create a frame-map) for funobj
800 and all its local functions. This must be done breadth-first, because
801 a (lexical-extent) sub-function might care about its parent frame-map."
802 (loop for
(nil . function-env
) in
(function-envs funobj
)
803 do
(assert (not (slot-boundp function-env
'frame-map
)))
804 (setf (frame-map function-env
)
805 (funobj-assign-bindings (extended-code function-env
)
807 (loop for
(sub-function-binding) on
(sub-function-binding-usage funobj
) by
#'cddr
808 do
(layout-stack-frames (function-binding-funobj sub-function-binding
)))
811 (defun complete-funobj (funobj)
812 (case (funobj-entry-protocol funobj
)
814 (complete-funobj-1req1opt funobj
))
815 (t (complete-funobj-default funobj
)))
816 (loop for
(sub-function-binding) on
(sub-function-binding-usage funobj
) by
#'cddr
817 do
(complete-funobj (function-binding-funobj sub-function-binding
)))
818 (register-function-code-size funobj
))
820 (defun complete-funobj-1req1opt (funobj)
821 (assert (= 2 (length (function-envs funobj
))))
822 (let* ((function-env (cdr (assoc 'muerte.cl
::t
(function-envs funobj
))))
823 (optional-env (cdr (assoc :optional
(function-envs funobj
))))
824 (frame-map (frame-map function-env
))
825 (resolved-code (finalize-code (extended-code function-env
) funobj frame-map
))
826 (resolved-optional-code (finalize-code (extended-code optional-env
) funobj frame-map
))
827 (stack-frame-size (frame-map-size (frame-map function-env
)))
828 (use-stack-frame-p (or (plusp stack-frame-size
)
829 (tree-search resolved-code
830 '(:pushl
:popl
:ebp
:esp
:call
:leave
))
832 (and (not (equal '(:movl
(:ebp -
4) :esi
) x
))
833 (tree-search x
':esi
)))
835 (let* ((function-code
836 (let* ((req-binding (movitz-binding (first (required-vars function-env
))
838 (req-location (cdr (assoc req-binding frame-map
)))
839 (opt-binding (movitz-binding (first (optional-vars function-env
))
841 (opt-location (cdr (assoc opt-binding frame-map
)))
842 (optp-binding (movitz-binding (optional-function-argument-supplied-p-var opt-binding
)
844 (optp-location (cdr (assoc optp-binding frame-map
)))
846 (append `((:jmp
(:edi
,(global-constant-offset 'trampoline-cl-dispatch-1or2
))))
848 (unless (eql nil opt-location
)
849 resolved-optional-code
)
852 (:jmp
'optp-into-edx-ok
)))
855 `((,*compiler-global-segment-prefix
*
856 :movl
(:edi
,(global-constant-offset 't-symbol
)) :edx
)
858 (when use-stack-frame-p
859 +enter-stack-frame-code
+)
860 '(start-stack-frame-setup)
862 ((and (eql 1 req-location
)
863 (eql 2 opt-location
))
864 (incf stack-setup-pre
2)
867 ((and (eql 1 req-location
)
868 (eql nil opt-location
))
869 (incf stack-setup-pre
1)
871 ((and (member req-location
'(nil :eax
))
872 (eql 1 opt-location
))
873 (incf stack-setup-pre
1)
875 ((and (member req-location
'(nil :eax
))
876 (member opt-location
'(nil :ebx
)))
878 (t (error "Can't deal with req ~S opt ~S."
879 req-location opt-location
)))
882 (make-stack-setup-code (- stack-frame-size stack-setup-pre
)))
883 ((and (integerp optp-location
)
884 (= optp-location
(1+ stack-setup-pre
)))
885 (append `((:pushl
:edx
))
886 (make-stack-setup-code (- stack-frame-size stack-setup-pre
1))))
887 ((integerp optp-location
)
888 (append (make-stack-setup-code (- stack-frame-size stack-setup-pre
))
889 `((:movl
:edx
(:ebp
,(stack-frame-offset optp-location
))))))
890 (t (error "Can't deal with optional-p at ~S, after (~S ~S)."
891 optp-location req-location opt-location
)))
892 (flet ((make-lending (location lended-cons-position
)
893 (etypecase req-location
895 `((:movl
(:ebp
,(stack-frame-offset location
)) :edx
)
896 (:movl
:edi
(:ebp
,(stack-frame-offset lended-cons-position
))) ; cdr
897 (:movl
:edx
(:ebp
,(stack-frame-offset (1+ lended-cons-position
)))) ; car
898 (:leal
(:ebp
1 ,(stack-frame-offset (1+ lended-cons-position
)))
900 (:movl
:edx
(:ebp
,(stack-frame-offset location
))))))))
902 (when (binding-lended-p req-binding
)
903 (make-lending req-location
(getf (binding-lending req-binding
)
904 :stack-cons-location
)))
905 (when (binding-lended-p opt-binding
)
906 (make-lending opt-location
(getf (binding-lending opt-binding
)
907 :stack-cons-location
)))
908 (when (and optp-binding
(binding-lended-p optp-binding
))
909 (make-lending optp-location
(getf (binding-lending optp-binding
)
910 :stack-cons-location
)))))
912 (make-compiled-function-postlude funobj function-env
913 use-stack-frame-p
)))))
914 (let ((optimized-function-code
915 (optimize-code function-code
916 :keep-labels
(append (subseq (movitz-funobj-const-list funobj
)
917 0 (movitz-funobj-num-jumpers funobj
))
918 '(entry%
1op entry%
2op
)))))
919 (assemble-funobj funobj optimized-function-code
)))))
921 (defun complete-funobj-default (funobj)
923 (loop for
(numargs . function-env
) in
(function-envs funobj
)
925 (let* ((frame-map (frame-map function-env
))
926 (resolved-code (finalize-code (extended-code function-env
) funobj frame-map
))
927 (stack-frame-size (frame-map-size (frame-map function-env
)))
928 (use-stack-frame-p (or (plusp stack-frame-size
)
929 (tree-search resolved-code
930 '(:push
:pop
:ebp
:esp
:call
:leave
))
932 (and (not (equal '(:movl
(:ebp -
4) :esi
) x
))
933 (tree-search x
':esi
)))
935 (multiple-value-bind (prelude-code have-normalized-ecx-p
)
936 (make-compiled-function-prelude stack-frame-size function-env use-stack-frame-p
937 (need-normalized-ecx-p function-env
) frame-map
938 :do-check-stack-p
(or (<= 32 stack-frame-size
)
939 (tree-search resolved-code
942 (install-arg-cmp (append prelude-code
944 (make-compiled-function-postlude funobj function-env
946 have-normalized-ecx-p
)))
947 (let ((optimized-function-code
948 (optimize-code function-code
950 (subseq (movitz-funobj-const-list funobj
)
951 0 (movitz-funobj-num-jumpers funobj
))
955 (cons numargs optimized-function-code
))))))))
956 (let ((code1 (cdr (assoc 1 code-specs
)))
957 (code2 (cdr (assoc 2 code-specs
)))
958 (code3 (cdr (assoc 3 code-specs
)))
959 (codet (cdr (assoc 'muerte.cl
::t code-specs
))))
960 (assert codet
() "A default numargs-case is required.")
961 ;; (format t "codet:~{~&~A~}" codet)
963 (delete 'start-stack-frame-setup
968 ,@(unless (find 'entry%
1op code1
)
969 '(entry%
1op
(:movb
1 :cl
)))
975 ,@(unless (find 'entry%
2op code2
)
976 '(entry%
2op
(:movb
2 :cl
)))
981 (:jne
'not-three-args
)
982 ,@(unless (find 'entry%
3op code3
)
983 '(entry%
3op
(:movb
3 :cl
)))
986 (delete-if (lambda (x)
987 (or (and code1
(eq x
'entry%
1op
))
988 (and code2
(eq x
'entry%
2op
))
989 (and code3
(eq x
'entry%
3op
))))
991 ;; (print-code funobj combined-code)
992 (assemble-funobj funobj combined-code
))))
995 (defun assemble-funobj (funobj combined-code
)
996 (multiple-value-bind (code-vector code-symtab
)
997 (let ((asm-x86:*cpu-mode
* :32-bit
)
998 (asm:*instruction-compute-extra-prefix-map
*
999 '((:call . compute-call-extra-prefix
))))
1000 (asm:assemble-proglist combined-code
1001 :symtab
(list* (cons :nil-value
(image-nil-word *image
*))
1002 (loop for
(label . set
) in
(movitz-funobj-jumpers-map funobj
)
1004 (* 4 (or (search set
(movitz-funobj-const-list funobj
)
1005 :end2
(movitz-funobj-num-jumpers funobj
))
1006 (error "Jumper for ~S missing." label
))))))))
1007 (setf (movitz-funobj-symtab funobj
) code-symtab
)
1008 (let* ((code-length (- (length code-vector
) 3 -
3))
1009 (code-vector (make-array code-length
1010 :initial-contents code-vector
1012 (setf (fill-pointer code-vector
) code-length
)
1014 (setf (ldb (byte 1 5) (slot-value funobj
'debug-info
))
1015 1 #+ignore
(if use-stack-frame-p
1 0))
1016 (let ((x (cdr (assoc 'start-stack-frame-setup code-symtab
))))
1019 #+ignore
(warn "No start-stack-frame-setup label for ~S." name
))
1021 (setf (ldb (byte 5 0) (slot-value funobj
'debug-info
)) x
))
1022 (t (warn "Can't encode start-stack-frame-setup label ~D into debug-info for ~S."
1023 x
(movitz-funobj-name funobj
)))))
1024 (let* ((a (or (cdr (assoc 'entry%
1op code-symtab
)) 0))
1025 (b (or (cdr (assoc 'entry%
2op code-symtab
)) a
))
1026 (c (or (cdr (assoc 'entry%
3op code-symtab
)) b
)))
1028 (warn "Weird code-entries: ~D, ~D, ~D." a b c
))
1029 (unless (<= 0 a
255)
1030 (break "entry%1: ~D" a
))
1031 (unless (<= 0 b
2047)
1032 (break "entry%2: ~D" b
))
1033 (unless (<= 0 c
4095)
1034 (break "entry%3: ~D" c
)))
1035 (loop for
(entry-label slot-name
) in
'((entry%
1op code-vector%
1op
)
1036 (entry%
2op code-vector%
2op
)
1037 (entry%
3op code-vector%
3op
))
1038 do
(when (assoc entry-label code-symtab
)
1039 (let ((offset (cdr (assoc entry-label code-symtab
))))
1040 (setf (slot-value funobj slot-name
)
1041 (cons offset funobj
)))))
1042 (check-locate-concistency code-vector
)
1043 (setf (movitz-funobj-code-vector funobj
)
1044 (make-movitz-vector (length code-vector
)
1045 :fill-pointer code-length
1047 :initial-contents code-vector
))))
1050 (defun check-locate-concistency (code-vector)
1051 (loop for x from
0 below
(length code-vector
) by
8
1052 do
(when (and (= (tag :basic-vector
) (aref code-vector x
))
1053 (= (enum-value 'movitz-vector-element-type
:code
) (aref code-vector
(1+ x
)))
1054 (or (<= #x4000
(length code-vector
))
1055 (and (= (ldb (byte 8 0) (length code-vector
))
1056 (aref code-vector
(+ x
2)))
1057 (= (ldb (byte 8 8) (length code-vector
))
1058 (aref code-vector
(+ x
3))))))
1059 (break "Code-vector (length #x~X) can break %find-code-vector at ~D: #x~2,'0X~2,'0X ~2,'0X~2,'0X."
1060 (length code-vector
) x
1061 (aref code-vector
(+ x
0))
1062 (aref code-vector
(+ x
1))
1063 (aref code-vector
(+ x
2))
1064 (aref code-vector
(+ x
3)))))
1068 (defun make-2req (binding0 binding1 frame-map
)
1069 (let ((location-0 (new-binding-location binding0 frame-map
))
1070 (location-1 (new-binding-location binding1 frame-map
)))
1072 ((and (eq :eax location-0
)
1073 (eq :ebx location-1
))
1075 ((and (eq :ebx location-0
)
1076 (eq :eax location-1
))
1077 (values '((:xchgl
:eax
:ebx
)) 0))
1078 ((and (eql 1 location-0
)
1080 (values '((:pushl
:eax
)
1083 ((and (eq :eax location-0
)
1085 (values '((:pushl
:ebx
))
1087 (t (error "make-2req confused by loc0: ~W, loc1: ~W" location-0 location-1
)))))
1090 (defun movitz-compile-file (path &key
((:image
*image
*) *image
*)
1092 (delete-file-p nil
))
1094 (#+sbcl
(sb-ext:defconstant-uneql
#'continue
))
1096 (let ((*movitz-host-features
* *features
*)
1097 (*features
* (image-movitz-features *image
*)))
1098 (multiple-value-prog1
1099 (movitz-compile-file-internal path load-priority
)
1100 (unless (equalp *features
* (image-movitz-features *image
*))
1101 (warn "*features* changed from ~S to ~S." (image-movitz-features *image
*) *features
*)
1102 (setf (image-movitz-features *image
*) *features
*))))
1104 (assert (equal (pathname-directory "/tmp/")
1105 (pathname-directory path
))
1107 "Refusing to delete file not in /tmp.")
1108 (delete-file path
)))))
1110 (defun movitz-compile-file-internal (path &optional
(*default-load-priority
*
1111 (and (boundp '*default-load-priority
*)
1112 (symbol-value '*default-load-priority
*)
1113 (1+ (symbol-value '*default-load-priority
*)))))
1114 (declare (special *default-load-priority
*))
1115 (with-simple-restart (continue "Skip Movitz compilation of ~S." path
)
1116 (with-retries-until-true (retry "Restart Movitz compilation of ~S." path
)
1117 (with-open-file (stream path
:direction
:input
)
1118 (let ((*package
* (find-package :muerte
)))
1119 (movitz-compile-stream-internal stream
:path path
))))))
1121 (defun movitz-compile-stream (stream &key
(path "unknown-toplevel.lisp") (package :muerte
))
1123 (#+sbcl
(sb-ext:defconstant-uneql
#'continue
))
1125 (let ((*package
* (find-package package
))
1126 (*movitz-host-features
* *features
*)
1127 (*features
* (image-movitz-features *image
*)))
1128 (multiple-value-prog1
1129 (movitz-compile-stream-internal stream
:path path
)
1130 (unless (equalp *features
* (image-movitz-features *image
*))
1131 (warn "*features* changed from ~S to ~S." (image-movitz-features *image
*) *features
*)
1132 (setf (image-movitz-features *image
*) *features
*)))))))
1134 (defun movitz-compile-stream-internal (stream &key
(path "unknown-toplevel.lisp"))
1135 (let* ((muerte.cl
::*compile-file-pathname
* path
)
1136 (funobj (make-instance 'movitz-funobj-pass1
1137 :name
(intern (format nil
"~A" path
) :muerte
)
1138 :lambda-list
(movitz-read nil
)))
1139 (funobj-env (make-local-movitz-environment nil funobj
1141 :declaration-context
:funobj
))
1142 (function-env (make-local-movitz-environment funobj-env funobj
1144 :declaration-context
:funobj
))
1146 (with-compilation-unit ()
1147 (add-bindings-from-lambda-list () function-env
)
1148 (setf (funobj-env funobj
) funobj-env
)
1149 (loop for form
= (with-movitz-syntax ()
1150 (read stream nil
'#0=#:eof
))
1151 until
(eq form
'#0#)
1153 (with-simple-restart (skip-toplevel-form
1154 "Skip the compilation of top-level form~{ ~A~}."
1158 ((symbolp (car form
))
1161 (when *compiler-verbose-p
*
1162 (format *query-io
* "~&Movitz Compiling ~S..~%"
1164 ((symbolp form
) form
)
1165 ((symbolp (car form
))
1166 (xsubseq form
0 2)))))
1167 (compiler-call #'compile-form
1172 :result-mode
:ignore
))))))
1175 (setf (image-load-time-funobjs *image
*)
1176 (delete funobj
(image-load-time-funobjs *image
*) :key
#'first
))
1177 'muerte
::constantly-true
)
1178 (t (setf (extended-code function-env
) file-code
1179 (need-normalized-ecx-p function-env
) nil
1180 (function-envs funobj
) (list (cons 'muerte.cl
::t function-env
))
1181 (funobj-env funobj
) funobj-env
)
1182 (make-compiled-funobj-pass2 funobj
)
1183 (let ((name (funobj-name funobj
)))
1184 (setf (movitz-env-named-function name
) funobj
)
1189 (defun print-code (x code
)
1190 (let ((*print-level
* 4))
1191 (format t
"~&~A code:~{~& ~A~}" x code
))
1194 (defun layout-program (pc)
1195 "For the program in pc, layout sub-programs at the top-level program."
1196 (do ((previous-subs nil
)
1200 (assert (not pending-subs
) ()
1201 "pending sub-programs: ~S" pending-subs
)
1202 (nreverse new-program
))
1204 (multiple-value-bind (sub-prg sub-opts
)
1205 (instruction-sub-program i
)
1207 (push i new-program
)
1208 (destructuring-bind (&optional
(label (gensym "sub-prg-label-")))
1210 (let ((x (cons label sub-prg
)))
1211 (unless (find x previous-subs
:test
#'equal
)
1212 (push x pending-subs
)
1213 (push x previous-subs
)))
1214 (unless (instruction-is i
:jnever
)
1215 (push `(,(car i
) ',label
)
1217 (when (or (instruction-uncontinues-p i
)
1219 (let* ((match-label (and (eq (car i
) :jmp
)
1221 (eq (car (second i
)) 'quote
)
1222 (symbolp (second (second i
)))
1223 (second (second i
))))
1224 (matching-sub (assoc match-label pending-subs
)))
1225 (unless (and match-label
1226 (or (eq match-label
(first pc
))
1227 (and (symbolp (first pc
))
1228 (eq match-label
(second pc
)))))
1230 (setf pc
(append (cdr matching-sub
) pc
)
1231 pending-subs
(delete matching-sub pending-subs
))
1232 (setf pc
(append (reduce #'append
(nreverse pending-subs
)) pc
)
1233 pending-subs nil
)))))))))
1236 (defun optimize-code (unoptimized-code &rest args
)
1237 #+ignore
(print-code 'to-optimize unoptimized-code
)
1238 (if (not *compiler-do-optimize
*)
1239 (layout-program (optimize-code-unfold-branches unoptimized-code
))
1240 (apply #'optimize-code-internal
1241 (optimize-code-dirties
1242 (layout-program (optimize-code-unfold-branches unoptimized-code
)))
1245 (defun optimize-code-unfold-branches (unoptimized-code)
1246 "This particular optimization should be done before code layout:
1247 (:jcc 'label) (:jmp 'foo) label => (:jncc 'foo) label"
1248 (flet ((explain (always format
&rest args
)
1249 (when (or always
*explain-peephole-optimizations
*)
1250 (warn "Peephole: ~?~&----------------------------" format args
)))
1251 (branch-instruction-label (i &optional jmp
(branch-types '(:je
:jne
:jb
:jnb
:jbe
:jz
1252 :jl
:jnz
:jle
:ja
:jae
:jg
1253 :jge
:jnc
:jc
:js
:jns
)))
1254 "If i is a branch, return the label."
1255 (when jmp
(push :jmp branch-types
))
1256 (let ((i (ignore-instruction-prefixes i
)))
1257 (or (and (listp i
) (member (car i
) branch-types
)
1258 (listp (second i
)) (member (car (second i
)) '(quote muerte.cl
::quote
))
1259 (second (second i
))))))
1260 (negate-branch (branch-type)
1262 (:jb
:jnb
) (:jnb
:jb
)
1263 (:jbe
:ja
) (:ja
:jbe
)
1264 (:jz
:jnz
) (:jnz
:jz
)
1265 (:je
:jne
) (:jne
:je
)
1266 (:jc
:jnc
) (:jnc
:jc
)
1267 (:jl
:jge
) (:jge
:jl
)
1268 (:jle
:jg
) (:jg
:jle
))))
1269 (loop with next-pc
= 'auto-next
1270 ;; initially (warn "opt: ~{ ~A~%~}" unoptimized-code)
1271 for pc
= unoptimized-code then
(prog1 (if (eq 'auto-next next-pc
) auto-next-pc next-pc
)
1272 (setq next-pc
'auto-next
))
1273 as auto-next-pc
= (cdr unoptimized-code
) then
(cdr pc
)
1274 as p
= (list (car pc
)) ; will be appended.
1275 as i1
= (first pc
) ; current instruction, collected by default.
1276 and i2
= (second pc
) and i3
= (third pc
)
1278 do
(when (and (branch-instruction-label i1
)
1279 (branch-instruction-label i2 t nil
)
1281 (eq i3
(branch-instruction-label i1
)))
1282 (setf p
(list `(,(negate-branch (car i1
)) ',(branch-instruction-label i2 t nil
))
1284 next-pc
(nthcdr 3 pc
))
1285 (explain nil
"Got a sit: ~{~&~A~} => ~{~&~A~}" (subseq pc
0 3) p
))
1288 (defun optimize-code-dirties (unoptimized-code)
1289 "These optimizations may rearrange register usage in a way that is incompatible
1290 with other optimizations that track register usage. So this is performed just once,
1294 (labels ; This stuff doesn't work..
1295 ((explain (always format
&rest args
)
1296 (when (or always
*explain-peephole-optimizations
*)
1297 (warn "Peephole: ~?~&----------------------------" format args
)))
1298 (twop-p (c &optional op
)
1299 (let ((c (ignore-instruction-prefixes c
)))
1300 (and (listp c
) (= 3 (length c
))
1301 (or (not op
) (eq op
(first c
)))
1303 (twop-dst (c &optional op src
)
1304 (let ((c (ignore-instruction-prefixes c
)))
1306 (equal src
(first (twop-p c op
))))
1307 (second (twop-p c op
)))))
1308 (twop-src (c &optional op dest
)
1309 (let ((c (ignore-instruction-prefixes c
)))
1311 (equal dest
(second (twop-p c op
))))
1312 (first (twop-p c op
)))))
1313 (register-operand (op)
1314 (and (member op
'(:eax
:ebx
:ecx
:edx
:edi
))
1316 (loop with next-pc
= 'auto-next
1317 ;; initially (warn "opt: ~{ ~A~%~}" unoptimized-code)
1318 for pc
= unoptimized-code then
(prog1 (if (eq 'auto-next next-pc
) auto-next-pc next-pc
)
1319 (setq next-pc
'auto-next
))
1320 as auto-next-pc
= (cdr unoptimized-code
) then
(cdr pc
)
1321 as p
= (list (car pc
)) ; will be appended.
1322 as i1
= (first pc
) ; current instruction, collected by default.
1323 and i2
= (second pc
) and i3
= (third pc
)
1325 do
(let ((regx (register-operand (twop-src i1
:movl
)))
1326 (regy (register-operand (twop-dst i1
:movl
))))
1327 (when (and regx regy
1328 (eq regx
(twop-dst i2
:movl
))
1329 (eq regx
(twop-src i3
:cmpl
))
1330 (eq regy
(twop-dst i3
:cmpl
)))
1331 (setq p
(list `(:cmpl
,(twop-src i2
) ,regx
) i1
)
1332 next-pc
(nthcdr 3 pc
))
1333 (explain t
"4: ~S for ~S [regx ~S, regy ~S]" p
(subseq pc
0 5) regx regy
)))
1336 (defun xsubseq (sequence start end
)
1337 (subseq sequence start
(min (length sequence
) end
)))
1339 (defun optimize-code-internal (unoptimized-code recursive-count
&rest key-args
1340 &key keep-labels stack-frame-size
)
1341 "Peephole optimizer. Based on a lot of rather random heuristics."
1342 (declare (ignore stack-frame-size
))
1343 (when (<= 20 recursive-count
)
1344 (error "Peephole-optimizer recursive count reached ~D.
1345 There is (propably) a bug in the peephole optimizer." recursive-count
))
1346 ;; (warn "==================OPTIMIZE: ~{~&~A~}" unoptimized-code)
1347 (macrolet ((explain (always format
&rest args
)
1348 `(when (or *explain-peephole-optimizations
* ,always
)
1349 (warn "Peephole: ~@?~&----------------------------" ,format
,@args
))))
1352 (explain (always format
&rest args
)
1353 (when (or always
*explain-peephole-optimizations
*)
1354 (warn "Peephole: ~?~&----------------------------" format args
)))
1355 (twop-p (c &optional op
)
1356 (let ((c (ignore-instruction-prefixes c
)))
1357 (and (listp c
) (= 3 (length c
))
1358 (or (not op
) (eq op
(first c
)))
1360 (twop-dst (c &optional op src
)
1361 (let ((c (ignore-instruction-prefixes c
)))
1363 (equal src
(first (twop-p c op
))))
1364 (second (twop-p c op
)))))
1365 (twop-src (c &optional op dest
)
1366 (let ((c (ignore-instruction-prefixes c
)))
1368 (equal dest
(second (twop-p c op
))))
1369 (first (twop-p c op
)))))
1371 (let ((c (ignore-instruction-prefixes c
)))
1372 (ecase (length (cdr c
))
1377 (let ((c (ignore-instruction-prefixes c
)))
1378 (ecase (length (cdr c
))
1382 (non-destructive-p (c)
1383 (let ((c (ignore-instruction-prefixes c
)))
1385 (member (car c
) '(:testl
:testb
:cmpl
:cmpb
:frame-map
:std
)))))
1386 (simple-instruction-p (c)
1387 (let ((c (ignore-instruction-prefixes c
)))
1390 '(:movl
:xorl
:popl
:pushl
:cmpl
:leal
:andl
:addl
:subl
)))))
1391 (register-indirect-operand (op base
)
1392 (multiple-value-bind (reg off
)
1395 if
(integerp x
) sum x into off
1396 else collect x into reg
1397 finally
(return (values reg off
))))
1398 (and (eq base
(car reg
))
1401 (stack-frame-operand (op)
1402 (register-indirect-operand op
:ebp
))
1403 (funobj-constant-operand (op)
1404 (register-indirect-operand op
:esi
))
1405 (global-constant-operand (op)
1406 (register-indirect-operand op
:edi
))
1407 (global-funcall-p (op &optional funs
)
1408 (let ((op (ignore-instruction-prefixes op
)))
1409 (when (instruction-is op
:call
)
1410 (let ((x (global-constant-operand (second op
))))
1412 (and (eql x
(slot-offset 'movitz-run-time-context name
))
1417 ((atom funs
) (try funs
))
1418 (t (some #'try funs
))))))))
1419 (preserves-stack-location-p (i stack-location
)
1420 (let ((i (ignore-instruction-prefixes i
)))
1422 (or (global-funcall-p i
)
1423 (instruction-is i
:frame-map
)
1424 (branch-instruction-label i
)
1425 (non-destructive-p i
)
1426 (and (simple-instruction-p i
)
1427 (not (eql stack-location
(stack-frame-operand (idst i
)))))))))
1428 (preserves-register-p (i register
)
1429 (let ((i (ignore-instruction-prefixes i
)))
1431 (not (and (eq register
:esp
)
1432 (member (instruction-is i
)
1434 (or (and (simple-instruction-p i
)
1435 (not (eq register
(idst i
))))
1436 (instruction-is i
:frame-map
)
1437 (branch-instruction-label i
)
1438 (non-destructive-p i
)
1439 (and (member register
'(:edx
))
1440 (member (global-funcall-p i
)
1441 '(fast-car fast-cdr fast-car-ebx fast-cdr-ebx
)))
1442 (and (not (eq register
:esp
))
1443 (instruction-is i
:pushl
))))))
1444 (operand-register-indirect-p (operand register
)
1445 (and (consp operand
)
1446 (tree-search operand register
)))
1447 (doesnt-read-register-p (i register
)
1448 (let ((i (ignore-instruction-prefixes i
)))
1450 (and (simple-instruction-p i
)
1451 (if (member (instruction-is i
) '(:movl
))
1452 (and (not (eq register
(twop-src i
)))
1453 (not (operand-register-indirect-p (twop-src i
) register
))
1454 (not (operand-register-indirect-p (twop-dst i
) register
)))
1455 (not (or (eq register
(isrc i
))
1456 (operand-register-indirect-p (isrc i
) register
)
1457 (eq register
(idst i
))
1458 (operand-register-indirect-p (idst i
) register
)))))
1459 (instruction-is i
:frame-map
)
1460 (and (member register
'(:edx
))
1461 (member (global-funcall-p i
)
1462 '(fast-car fast-cdr fast-car-ebx fast-cdr-ebx
))))))
1463 (register-operand (op)
1464 (and (member op
'(:eax
:ebx
:ecx
:edx
:edi
))
1466 (true-and-equal (x &rest more
)
1467 (declare (dynamic-extent more
))
1468 (and x
(dolist (y more t
)
1471 (uses-stack-frame-p (c)
1473 (some #'stack-frame-operand
(cdr (ignore-instruction-prefixes c
)))))
1474 (load-stack-frame-p (c &optional
(op :movl
))
1475 (stack-frame-operand (twop-src c op
)))
1476 (store-stack-frame-p (c &optional
(op :movl
))
1477 (stack-frame-operand (twop-dst c op
)))
1478 (read-stack-frame-p (c)
1479 (or (load-stack-frame-p c
:movl
)
1480 (load-stack-frame-p c
:addl
)
1481 (load-stack-frame-p c
:subl
)
1482 (load-stack-frame-p c
:cmpl
)
1483 (store-stack-frame-p c
:cmpl
)
1486 (stack-frame-operand (second c
)))))
1487 (in-stack-frame-p (c reg
)
1488 "Does c ensure that reg is in some particular stack-frame location?"
1489 (or (and (load-stack-frame-p c
)
1490 (eq reg
(twop-dst c
))
1491 (stack-frame-operand (twop-src c
)))
1492 (and (store-stack-frame-p c
)
1493 (eq reg
(twop-src c
))
1494 (stack-frame-operand (twop-dst c
)))))
1495 (load-funobj-constant-p (c)
1496 (funobj-constant-operand (twop-src c
:movl
)))
1498 (sub-program-label-p (l)
1500 (eq :sub-program
(car l
))))
1502 (if (or (load-stack-frame-p c
)
1503 (load-funobj-constant-p c
))
1506 (label-here-p (label code
)
1507 "Is <label> at this point in <code>?"
1509 while
(or (symbolp i
)
1510 (instruction-is i
:frame-map
))
1511 thereis
(eq label i
)))
1512 (negate-branch (branch-type)
1514 (:jbe
:ja
) (:ja
:jbe
)
1515 (:jz
:jnz
) (:jnz
:jz
)
1516 (:je
:jne
) (:jne
:je
)
1517 (:jc
:jnc
) (:jnc
:jc
)
1518 (:jl
:jge
) (:jge
:jl
)
1519 (:jle
:jg
) (:jg
:jle
)))
1520 (branch-instruction-label (i &optional jmp
(branch-types '(:je
:jne
:jb
:jnb
:jbe
:jz
:jl
:jnz
1521 :jle
:ja
:jae
:jg
:jge
:jnc
:jc
:js
:jns
)))
1522 "If i is a branch, return the label."
1523 (when jmp
(push :jmp branch-types
))
1524 (let ((i (ignore-instruction-prefixes i
)))
1527 (member (car (second i
)) '(quote muerte.cl
::quote
))
1528 (member (car i
) branch-types
)
1529 (second (second i
)))
1534 (not (member (car i
) '(:jmp
:jecxz
)))
1535 (char= #\J
(char (symbol-name (car i
)) 0))
1536 (warn "Not a branch: ~A / ~A [~A]" i
(symbol-package (caadr i
)) branch-types
)))))
1537 (find-branches-to-label (start-pc label
&optional
(context-size 0))
1538 "Context-size is the number of instructions _before_ the branch you want returned."
1539 (dotimes (i context-size
)
1540 (push nil start-pc
))
1541 (loop for pc on start-pc
1542 as i
= (nth context-size pc
)
1543 as i-label
= (branch-instruction-label i t
)
1544 if
(or (eq label i-label
)
1545 (and (consp i-label
)
1546 (eq :label-plus-one
(car i-label
))))
1548 else if
(let ((sub-program i-label
))
1549 (and (consp sub-program
)
1550 (eq :sub-program
(car sub-program
))))
1551 nconc
(find-branches-to-label (cddr (branch-instruction-label i t
))
1553 else if
(and (not (atom i
))
1554 (tree-search i label
))
1555 nconc
(list 'unknown-label-usage
)))
1556 (optimize-trim-stack-frame (unoptimized-code)
1557 "Any unused local variables on the stack-frame?"
1559 ;; BUILD A MAP OF USED STACK-VARS AND REMAP THEM!
1560 #+ignore
(if (not (and stack-frame-size
1561 (find 'start-stack-frame-setup unoptimized-code
)))
1563 (let ((old-code unoptimized-code
)
1565 ;; copy everything upto start-stack-frame-setup
1566 (loop for i
= (pop old-code
)
1567 do
(push i new-code
)
1569 until
(eq i
'start-stack-frame-setup
))
1570 (assert (eq (car new-code
) 'start-stack-frame-setup
) ()
1571 "no start-stack-frame-setup label, but we already checked!")
1572 (loop for pos downfrom -
8 by
4
1573 as i
= (pop old-code
)
1574 if
(and (consp i
) (eq :pushl
(car i
)) (symbolp (cadr i
)))
1575 collect
(cons pos
(cadr i
))
1576 and do
(unless (find pos old-code
:key
#'read-stack-frame-p
)
1578 ((find pos old-code
:key
#'store-stack-frame-p
)
1579 (warn "Unused local but stored var: ~S" pos
))
1580 ((find pos old-code
:key
#'uses-stack-frame-p
)
1581 (warn "Unused BUT USED local var: ~S" pos
))
1582 (t (warn "Unused local var: ~S" pos
))))
1587 (frame-map-code (unoptimized-code)
1588 "After each label in unoptimized-code, insert a (:frame-map <full-map> <branch-map> <sticky>)
1589 that says which registers are known to hold which stack-frame-locations.
1590 A branch-map is the map that is guaranteed after every branch to the label, i.e. not including
1591 falling below the label."
1592 #+ignore
(warn "unmapped:~{~&~A~}" unoptimized-code
)
1593 (flet ((rcode-map (code)
1594 #+ignore
(when (instruction-is (car code
) :testb
)
1595 (warn "rcoding ~A" code
))
1596 (loop with modifieds
= nil
1597 with registers
= (list :eax
:ebx
:ecx
:edx
)
1598 with local-map
= nil
1601 do
(flet ((add-map (stack reg
)
1602 (when (and (not (member stack modifieds
))
1603 (member reg registers
))
1604 (push (cons stack reg
)
1606 (cond ((instruction-is ii
:frame-map
)
1607 (dolist (m (second ii
))
1608 (add-map (car m
) (cdr m
))))
1609 ((load-stack-frame-p ii
)
1610 (add-map (load-stack-frame-p ii
)
1612 ((store-stack-frame-p ii
)
1613 (add-map (store-stack-frame-p ii
)
1615 (pushnew (store-stack-frame-p ii
)
1617 ((non-destructive-p ii
))
1618 ((branch-instruction-label ii
))
1619 ((simple-instruction-p ii
)
1620 (let ((op (idst ii
)))
1622 ((stack-frame-operand op
)
1623 (pushnew (stack-frame-operand op
) modifieds
))
1625 (setf registers
(delete op registers
))))))
1626 (t #+ignore
(when (instruction-is (car code
) :testb
)
1627 (warn "stopped at ~A" ii
))
1630 (delete-if (lambda (r)
1631 (not (preserves-register-p ii r
)))
1634 #+ignore
(when (instruction-is (car code
) :testb
)
1635 (warn "..map ~A" local-map
))
1636 (return local-map
))))
1637 (loop with next-pc
= 'auto-next
1638 ;; initially (warn "opt: ~{ ~A~%~}" unoptimized-code)
1639 for pc
= unoptimized-code then
(prog1 (if (eq 'auto-next next-pc
) auto-next-pc next-pc
)
1640 (setq next-pc
'auto-next
))
1641 as auto-next-pc
= (cdr unoptimized-code
) then
(cdr pc
)
1642 as p
= (list (car pc
)) ; will be appended.
1643 as i1
= (first pc
) ; current instruction, collected by default.
1644 and i2
= (second pc
)
1646 do
(when (and (symbolp i1
)
1647 (not (and (instruction-is i2
:frame-map
)
1650 (branch-map (reduce (lambda (&optional x y
)
1651 (intersection x y
:test
#'equal
))
1652 (mapcar (lambda (lpc)
1653 (if (eq 'unknown-label-usage lpc
)
1655 (rcode-map (nreverse (xsubseq lpc
0 9)))))
1656 (find-branches-to-label unoptimized-code label
9))))
1657 (full-map (let ((rcode (nreverse (let* ((pos (loop for x on unoptimized-code
1660 finally
(return pos
)))
1661 (back9 (max 0 (- pos
9))))
1662 (subseq unoptimized-code
1664 (if (instruction-uncontinues-p (car rcode
))
1666 (intersection branch-map
(rcode-map rcode
) :test
#'equal
)))))
1667 (when (or full-map branch-map nil
)
1669 (explain nil
"Inserting at ~A frame-map ~S branch-map ~S."
1670 label full-map branch-map
))
1671 (setq p
(list label
`(:frame-map
,full-map
,branch-map
))
1672 next-pc
(if (instruction-is i2
:frame-map
)
1676 (optimize-stack-frame-init (unoptimized-code)
1677 "Look at the function's stack-frame initialization code, and see
1678 if we can optimize that, and/or immediately subsequent loads/stores."
1679 (if (not (find 'start-stack-frame-setup unoptimized-code
))
1681 (let ((old-code unoptimized-code
)
1683 ;; copy everything upto start-stack-frame-setup
1684 (loop for i
= (pop old-code
)
1685 do
(push i new-code
)
1687 until
(eq i
'start-stack-frame-setup
))
1688 (assert (eq (car new-code
) 'start-stack-frame-setup
) ()
1689 "no start-stack-frame-setup label, but we already checked!")
1690 (let* ((frame-map (loop with pos
= -
8
1691 as i
= (pop old-code
)
1692 if
(instruction-is i
:frame-map
)
1695 (and (consp i
) (eq :pushl
(car i
)) (symbolp (cadr i
)))
1704 (mod-p (loop with mod-p
= nil
1705 for i
= `(:frame-map
,(copy-list frame-map
) nil t
)
1708 do
(let ((new-i (cond
1709 ((let ((store-pos (store-stack-frame-p i
)))
1711 (eq (cdr (assoc store-pos frame-map
))
1713 (explain nil
"removed stack-init store: ~S" i
)
1715 ((let ((load-pos (load-stack-frame-p i
)))
1717 (eq (cdr (assoc load-pos frame-map
))
1719 (explain nil
"removed stack-init load: ~S" i
)
1721 ((and (load-stack-frame-p i
)
1722 (assoc (load-stack-frame-p i
) frame-map
))
1723 (let ((old-reg (cdr (assoc (load-stack-frame-p i
)
1725 (explain nil
"load ~S already in ~S."
1727 `(:movl
,old-reg
,(twop-dst i
))))
1728 ((and (instruction-is i
:pushl
)
1729 (stack-frame-operand (idst i
))
1730 (assoc (stack-frame-operand (idst i
))
1733 (cdr (assoc (stack-frame-operand (idst i
))
1735 (explain nil
"push ~S already in ~S."
1737 `(:pushl
,old-reg
)))
1739 (unless (eq new-i i
)
1741 (when (branch-instruction-label new-i t
)
1743 (push `(:frame-map
,(copy-list frame-map
) nil t
)
1746 (push new-i new-code
)
1747 ;; (warn "new-i: ~S, fm: ~S" new-i frame-map)
1749 (delete-if (lambda (map)
1750 ;; (warn "considering: ~S" map)
1751 (not (and (preserves-register-p new-i
(cdr map
))
1752 (preserves-stack-location-p new-i
1755 ;; (warn "Frame-map now: ~S" frame-map)
1756 (when (store-stack-frame-p new-i
)
1757 (loop for map in frame-map
1758 do
(when (= (store-stack-frame-p new-i
)
1760 (setf (cdr map
) (twop-src new-i
)))))))
1762 finally
(return mod-p
))))
1765 (append (nreverse new-code
)
1767 (remove-frame-maps (code)
1768 (remove-if (lambda (x)
1769 (typep x
'(cons (eql :frame-map
) *)))
1771 (let* ((unoptimized-code (frame-map-code (optimize-stack-frame-init unoptimized-code
)))
1772 (code-modified-p nil
)
1773 (stack-frame-used-map (loop with map
= nil
1774 for i in unoptimized-code
1775 do
(let ((x (read-stack-frame-p i
)))
1776 (when x
(pushnew x map
)))
1777 (when (and (instruction-is i
:leal
)
1778 (stack-frame-operand (twop-src i
)))
1779 (let ((x (stack-frame-operand (twop-src i
))))
1780 (when (= (tag :cons
) (ldb (byte 2 0) x
))
1781 (pushnew (+ x -
1) map
)
1782 (pushnew (+ x
3) map
))))
1783 finally
(return map
)))
1785 ;; This loop applies a set of (hard-coded) heuristics on unoptimized-code.
1786 (loop with next-pc
= 'auto-next
1787 ;; initially (warn "opt: ~{ ~A~%~}" unoptimized-code)
1788 for pc
= unoptimized-code then
(prog1 (if (eq 'auto-next next-pc
) auto-next-pc next-pc
)
1789 (setq next-pc
'auto-next
))
1790 as auto-next-pc
= (cdr unoptimized-code
) then
(cdr pc
)
1791 as p
= (list (car pc
)) ; will be appended.
1793 as i
= (first pc
) ; current instruction, collected by default.
1794 and i2
= (second pc
) and i3
= (third pc
) and i4
= (fourth pc
) and i5
= (fifth pc
)
1797 ((and (instruction-is i
:frame-map
)
1798 (instruction-is i2
:frame-map
)
1801 (let ((map (union (second i
) (second i2
) :test
#'equal
)))
1802 (explain nil
"Merged maps:~%~A + ~A~% => ~A"
1803 (second i
) (second i2
) map
)
1804 (setq p
`((:frame-map
,map
))
1805 next-pc
(cddr pc
))))
1806 ((let ((x (store-stack-frame-p i
)))
1807 (and x
(not (member x stack-frame-used-map
))))
1809 (explain nil
"Removed store of unused local var: ~S" i
))
1810 ((and (global-funcall-p i2
'(fast-car))
1811 (global-funcall-p i5
'(fast-cdr))
1812 (true-and-equal (in-stack-frame-p i
:eax
)
1813 (in-stack-frame-p i4
:eax
)))
1814 (let ((call-prefix (if (consp (car i2
)) (car i2
) nil
)))
1816 ((equal i3
'(:pushl
:eax
))
1817 (explain nil
"merge car,push,cdr to cdr-car,push")
1819 `(,call-prefix
:call
1820 (:edi
,(global-constant-offset 'fast-cdr-car
)))
1822 next-pc
(nthcdr 5 pc
)))
1823 ((and (store-stack-frame-p i3
)
1824 (eq :eax
(twop-src i3
)))
1825 (explain nil
"merge car,store,cdr to cdr-car,store")
1827 `(,call-prefix
:call
1828 (:edi
,(global-constant-offset 'fast-cdr-car
)))
1829 `(:movl
:ebx
,(twop-dst i3
)))
1830 next-pc
(nthcdr 5 pc
)))
1831 (t (error "can't deal with cdr-car here: ~{~&~A~}" (subseq pc
0 8))))))
1832 ((flet ((try (place register
&optional map reason
)
1833 "See if we can remove a stack-frame load below current pc,
1834 given the knowledge that <register> is equal to <place>."
1837 (dolist (si (cdr pc
))
1838 (when (and (twop-p si
:cmpl
)
1839 (equal place
(twop-src si
)))
1840 (warn "Reverse cmp not yet dealed with.."))
1842 ((and (twop-p si
:cmpl
)
1843 (equal place
(twop-dst si
)))
1845 ((equal place
(local-load-p si
))
1847 ((or (not (consp si
))
1848 (not (preserves-register-p si register
))
1849 (equal place
(twop-dst si
)))
1852 (remove-if (lambda (m)
1853 (not (preserves-register-p si
(cdr m
))))
1855 (case (instruction-is next-load
)
1857 (let ((pos (position next-load pc
)))
1858 (setq p
(nconc (subseq pc
0 pos
)
1859 (if (or (eq register
(twop-dst next-load
))
1860 (find-if (lambda (m)
1861 (and (eq (twop-dst next-load
) (cdr m
))
1862 (= (car m
) (stack-frame-operand place
))))
1865 (list `(:movl
,register
,(twop-dst next-load
)))))
1866 next-pc
(nthcdr (1+ pos
) pc
))
1867 (explain nil
"preserved load/store .. load ~S of place ~S because ~S."
1868 next-load place reason
)))
1870 (let ((pos (position next-load pc
)))
1871 (setq p
(nconc (subseq pc
0 pos
)
1872 (list `(:cmpl
,(twop-src next-load
) ,register
)))
1873 next-pc
(nthcdr (1+ pos
) pc
))
1874 (explain nil
"preserved load/store..cmp: ~S" p next-load
))))
1875 (if next-load t nil
))))
1876 (or (when (instruction-is i
:frame-map
)
1877 (loop for
(place . register
) in
(second i
)
1878 ;;; do (warn "map try ~S ~S: ~S" place register
1879 ;;; (try place register))
1880 thereis
(try `(:ebp
,place
) register
(second i
) :frame-map
)))
1881 (try (or (local-load-p i
)
1882 (and (store-stack-frame-p i
)
1884 (if (store-stack-frame-p i
)
1889 (instruction-is i2
:frame-map
)
1890 (load-stack-frame-p i3
)
1892 (cdr (assoc (load-stack-frame-p i3
) (third i2
))))
1893 (not (assoc (load-stack-frame-p i3
) (second i2
))))
1894 (let ((reg (cdr (assoc (load-stack-frame-p i3
) (third i2
)))))
1895 (explain nil
"factor out load from loop: ~S" i3
)
1896 (assert (eq reg
(twop-dst i3
)))
1897 (setq p
(if (eq reg
(twop-dst i3
))
1899 (append (list i3 i i2
)
1900 `((:movl
,reg
,(twop-dst i3
)))))
1901 next-pc
(cdddr pc
))))
1902 ;; ((:movl <foo> <bar>) label (:movl <zot> <bar>))
1903 ;; => (label (:movl <zot> <bar>))
1904 ((and (instruction-is i
:movl
)
1906 (and (not (branch-instruction-label i2
))
1907 (symbolp (twop-dst i
))
1908 (doesnt-read-register-p i2
(twop-dst i
))))
1909 (instruction-is i3
:frame-map
)
1910 (instruction-is i4
:movl
)
1911 (equal (twop-dst i
) (twop-dst i4
))
1912 (not (and (symbolp (twop-dst i
))
1913 (operand-register-indirect-p (twop-src i4
)
1915 (setq p
(list i2 i3 i4
)
1916 next-pc
(nthcdr 4 pc
))
1917 (explain nil
"Removed redundant store before ~A: ~A"
1918 i2
(subseq pc
0 4)))
1919 ((and (instruction-is i
:movl
)
1920 (not (branch-instruction-label i2
))
1921 (symbolp (twop-dst i
))
1922 (doesnt-read-register-p i2
(twop-dst i
))
1923 (instruction-is i3
:movl
)
1924 (equal (twop-dst i
) (twop-dst i3
))
1925 (not (and (symbolp (twop-dst i
))
1926 (operand-register-indirect-p (twop-src i3
)
1928 (setq p
(list i2 i3
)
1929 next-pc
(nthcdr 3 pc
))
1930 (explain nil
"Removed redundant store before ~A: ~A"
1931 i2
(subseq pc
0 3)))
1933 ((let ((stack-pos (store-stack-frame-p i
)))
1935 (loop with search-pc
= (cdr pc
)
1938 for ii
= (pop search-pc
)
1939 thereis
(eql stack-pos
1940 (store-stack-frame-p ii
))
1941 while
(or (global-funcall-p ii
)
1942 (and (simple-instruction-p ii
)
1944 (uses-stack-frame-p ii
))))))
1947 (store-stack-frame-p i4
))
1950 (or (global-funcall-p ii
)
1951 (and (simple-instruction-p ii
)
1953 (uses-stack-frame-p ii
))))))
1957 (explain t
"removing redundant store at ~A"
1958 (subseq pc
0 (min 10 (length pc
)))))
1959 ((and (member (instruction-is i
)
1960 '(:cmpl
:cmpb
:cmpw
:testl
:testb
:testw
))
1961 (member (instruction-is i2
)
1962 '(:cmpl
:cmpb
:cmpw
:testl
:testb
:testw
)))
1964 next-pc
(nthcdr 2 pc
))
1965 (explain nil
"Trimmed double test: ~A" (subseq pc
0 4)))
1966 ;; ((:jmp x) ...(no labels).... x ..)
1968 ((let ((x (branch-instruction-label i t nil
)))
1969 (and (position x
(cdr pc
))
1970 (not (find-if #'symbolp
(cdr pc
) :end
(position x
(cdr pc
))))))
1971 (explain nil
"jmp x .. x: ~W"
1972 (subseq pc
0 (1+ (position (branch-instruction-label i t nil
)
1975 next-pc
(member (branch-instruction-label i t nil
) pc
)))
1976 ;; (:jcc 'x) .... x (:jmp 'y) ..
1977 ;; => (:jcc 'y) .... x (:jmp 'y) ..
1978 ((let* ((from (branch-instruction-label i t
))
1979 (dest (member (branch-instruction-label i t
)
1981 (to (branch-instruction-label (if (instruction-is (second dest
) :frame-map
)
1985 (when (and from to
(not (eq from to
)))
1986 (setq p
(list `(,(car i
) ',to
)))
1987 (explain nil
"branch redirect from ~S to ~S" from to
)
1989 ;; remove back-to-back std/cld
1990 ((and (instruction-is i
:cld
)
1991 (instruction-is i2
:std
))
1992 (explain nil
"removing back-to-back cld, std.")
1993 (setq p nil next-pc
(cddr pc
)))
1994 ;; remove branch no-ops.
1995 ((and (branch-instruction-label i t
)
1996 (label-here-p (branch-instruction-label i t
)
1998 (explain nil
"branch no-op: ~A" i
)
2001 (null (symbol-package i
))
2002 (null (find-branches-to-label unoptimized-code i
))
2003 (not (member i keep-labels
)))
2005 next-pc
(if (instruction-is i2
:frame-map
)
2008 (explain nil
"unused label: ~S" i
))
2009 ;; ((:jcc 'label) (:jmp 'y) label) => ((:jncc 'y) label)
2010 ((and (branch-instruction-label i
)
2011 (branch-instruction-label i2 t nil
)
2013 (eq (branch-instruction-label i
) i3
))
2014 (setq p
(list `(,(negate-branch (first i
))
2015 ',(branch-instruction-label i2 t nil
)))
2016 next-pc
(nthcdr 2 pc
))
2017 (explain nil
"collapsed double negative branch to ~S: ~A." i3 p
))
2018 ((and (branch-instruction-label i
)
2019 (instruction-is i2
:frame-map
)
2020 (branch-instruction-label i3 t nil
)
2022 (eq (branch-instruction-label i
) i4
))
2023 (setq p
(list `(,(negate-branch (first i
))
2024 ',(branch-instruction-label i3 t nil
)))
2025 next-pc
(nthcdr 3 pc
))
2026 (explain nil
"collapsed double negative branch to ~S: ~A." i4 p
))
2027 ((and (twop-p i
:movl
)
2028 (register-operand (twop-src i
))
2029 (register-operand (twop-dst i
))
2031 (eq (twop-dst i
) (twop-dst i2
))
2032 (register-indirect-operand (twop-src i2
) (twop-dst i
)))
2033 (setq p
(list `(:movl
(,(twop-src i
)
2034 ,(register-indirect-operand (twop-src i2
)
2037 next-pc
(nthcdr 2 pc
))
2038 (explain nil
"(movl edx eax) (movl (eax <z>) eax) => (movl (edx <z>) eax: ~S"
2040 ((and (twop-p i
:movl
)
2041 (instruction-is i2
:pushl
)
2042 (eq (twop-dst i
) (second i2
))
2044 (eq (twop-dst i
) (twop-dst i3
)))
2045 (setq p
(list `(:pushl
,(twop-src i
)))
2046 next-pc
(nthcdr 2 pc
))
2047 (explain nil
"(movl <z> :eax) (pushl :eax) => (pushl <z>): ~S" p
))
2048 ((and (instruction-uncontinues-p i
)
2049 (not (or (symbolp i2
)
2050 #+ignore
(member (instruction-is i2
) '(:foobar
)))))
2051 (do ((x (cdr pc
) (cdr x
)))
2054 ((not (or (symbolp (car x
))
2055 #+ignore
(member (instruction-is (car x
)) '(:foobar
))))
2056 (explain nil
"Removing unreachable code ~A after ~A." (car x
) i
))
2060 ((and (store-stack-frame-p i
)
2061 (load-stack-frame-p i2
)
2062 (load-stack-frame-p i3
)
2063 (= (store-stack-frame-p i
)
2064 (load-stack-frame-p i3
))
2065 (not (eq (twop-dst i2
) (twop-dst i3
))))
2066 (setq p
(list i
`(:movl
,(twop-src i
) ,(twop-dst i3
)) i2
)
2067 next-pc
(nthcdr 3 pc
))
2068 (explain nil
"store, z, load => store, move, z: ~A" p
))
2069 ((and (instruction-is i
:movl
)
2070 (member (twop-dst i
) '(:eax
:ebx
:ecx
:edx
))
2071 (instruction-is i2
:pushl
)
2072 (not (member (second i2
) '(:eax
:ebx
:ecx
:edx
)))
2073 (equal (twop-src i
) (second i2
)))
2074 (setq p
(list i
`(:pushl
,(twop-dst i
)))
2075 next-pc
(nthcdr 2 pc
))
2076 (explain t
"load, push => load, push reg."))
2077 ((and (instruction-is i
:movl
)
2078 (member (twop-src i
) '(:eax
:ebx
:ecx
:edx
))
2079 (instruction-is i2
:pushl
)
2080 (not (member (second i2
) '(:eax
:ebx
:ecx
:edx
)))
2081 (equal (twop-dst i
) (second i2
)))
2082 (setq p
(list i
`(:pushl
,(twop-src i
)))
2083 next-pc
(nthcdr 2 pc
))
2084 (explain nil
"store, push => store, push reg: ~S ~S" i i2
))
2085 ;;; ((and (instruction-is i :cmpl)
2086 ;;; (true-and-equal (stack-frame-operand (twop-dst i))
2087 ;;; (load-stack-frame-p i3))
2088 ;;; (branch-instruction-label i2))
2089 ;;; (setf p (list i3
2090 ;;; `(:cmpl ,(twop-src i) ,(twop-dst i3))
2092 ;;; next-pc (nthcdr 3 pc))
2093 ;;; (explain t "~S ~S ~S => ~S" i i2 i3 p))
2094 ((and (instruction-is i
:pushl
)
2095 (instruction-is i3
:popl
)
2096 (store-stack-frame-p i2
)
2097 (store-stack-frame-p i4
)
2098 (eq (idst i3
) (twop-src i4
)))
2100 `(:movl
,(idst i
) ,(twop-dst i4
))
2101 `(:movl
,(idst i
) ,(idst i3
)))
2102 next-pc
(nthcdr 4 pc
))
2103 (explain nil
"~S => ~S" (subseq pc
0 4) p
))
2105 ((let ((i6 (nth 6 pc
)))
2106 (and (global-funcall-p i2
'(fast-car))
2107 (global-funcall-p i6
'(fast-cdr))
2108 (load-stack-frame-p i
)
2109 (eq :eax
(twop-dst i
))
2111 ((and (equal i
'(:movl
:ebx
:eax
))
2112 (global-funcall-p i2
'(fast-car fast-cdr
)))
2113 (let ((newf (ecase (global-funcall-p i2
'(fast-car fast-cdr
))
2114 (fast-car 'fast-car-ebx
)
2115 (fast-cdr 'fast-cdr-ebx
))))
2116 (setq p
`((:call
(:edi
,(global-constant-offset newf
))))
2117 next-pc
(nthcdr 2 pc
))
2118 (explain nil
"Changed [~S ~S] to ~S" i i2 newf
)))
2119 ((and (equal i
'(:movl
:eax
:ebx
))
2120 (global-funcall-p i2
'(fast-car-ebx fast-cdr-ebx
)))
2121 (let ((newf (ecase (global-funcall-p i2
'(fast-car-ebx fast-cdr-ebx
))
2122 (fast-car-ebx 'fast-car
)
2123 (fast-cdr-ebx 'fast-cdr
))))
2124 (setq p
`((:call
(:edi
,(global-constant-offset newf
))))
2125 next-pc
(nthcdr 2 pc
))
2126 (explain nil
"Changed [~S ~S] to ~S" i i2 newf
)))
2128 ((and (global-funcall-p i
'(fast-cdr))
2129 (global-funcall-p i2
'(fast-cdr))
2130 (global-funcall-p i3
'(fast-cdr)))
2131 (setq p
`((:call
(:edi
,(global-constant-offset 'fast-cdddr
))))
2132 next-pc
(nthcdr 3 pc
))
2133 (explain nil
"Changed (cdr (cdr (cdr :eax))) to (cdddr :eax)."))
2134 ((and (global-funcall-p i
'(fast-cdr))
2135 (global-funcall-p i2
'(fast-cdr)))
2136 (setq p
`((:call
(:edi
,(global-constant-offset 'fast-cddr
))))
2137 next-pc
(nthcdr 2 pc
))
2138 (explain nil
"Changed (cdr (cdr :eax)) to (cddr :eax)."))
2139 ((and (load-stack-frame-p i
) (eq :eax
(twop-dst i
))
2140 (global-funcall-p i2
'(fast-car fast-cdr
))
2141 (preserves-stack-location-p i3
(load-stack-frame-p i
))
2142 (preserves-register-p i3
:ebx
)
2143 (eql (load-stack-frame-p i
)
2144 (load-stack-frame-p i4
)))
2145 (let ((newf (ecase (global-funcall-p i2
'(fast-car fast-cdr
))
2146 (fast-car 'fast-car-ebx
)
2147 (fast-cdr 'fast-cdr-ebx
))))
2148 (setq p
`((:movl
,(twop-src i
) :ebx
)
2149 (:call
(:edi
,(global-constant-offset newf
)))
2151 ,@(unless (eq :ebx
(twop-dst i4
))
2152 `((:movl
:ebx
,(twop-dst i4
)))))
2153 next-pc
(nthcdr 4 pc
))
2154 (explain nil
"load around ~A: ~{~&~A~}~%=>~% ~{~&~A~}"
2155 newf
(subseq pc
0 5) p
))))
2156 do
(unless (eq p original-p
) ; auto-detect whether any heuristic fired..
2157 #+ignore
(warn "at ~A, ~A inserted ~A" i i2 p
)
2158 #+ignore
(warn "modified at ~S ~S ~S" i i2 i3
)
2159 (setf code-modified-p t
))
2162 (apply #'optimize-code-internal optimized-code
(1+ recursive-count
) key-args
)
2163 (optimize-trim-stack-frame (remove-frame-maps unoptimized-code
)))))))
2164 ;;;; Compiler internals
2166 (defclass binding
()
2169 :accessor binding-name
)
2171 :accessor binding-env
)
2173 :initarg
:declarations
2174 :accessor binding-declarations
)
2176 :accessor binding-extent-env
2179 (defmethod (setf binding-env
) :after
(env (binding binding
))
2180 (unless (binding-extent-env binding
)
2181 (setf (binding-extent-env binding
) env
)))
2183 (defmethod print-object ((object binding
) stream
)
2184 (print-unreadable-object (object stream
:type t
:identity t
)
2185 (when (slot-boundp object
'name
)
2186 (format stream
"name: ~S~@[->~S~]~@[ %~A~]"
2187 (and (slot-boundp object
'name
)
2188 (binding-name object
))
2189 (when (and (binding-target object
)
2190 (not (eq object
(binding-target object
))))
2191 (binding-name (forwarding-binding-target object
)))
2192 (when (and (slot-exists-p object
'store-type
)
2193 (slot-boundp object
'store-type
)
2194 (binding-store-type object
))
2195 (or (apply #'encoded-type-decode
2196 (binding-store-type object
))
2199 (defclass constant-object-binding
(binding)
2202 :reader constant-object
)))
2204 (defmethod binding-lended-p ((binding constant-object-binding
)) nil
)
2205 (defmethod binding-store-type ((binding constant-object-binding
))
2206 (multiple-value-list (type-specifier-encode `(eql ,(constant-object binding
)))))
2209 (defclass operator-binding
(binding) ())
2211 (defclass macro-binding
(operator-binding)
2214 :accessor macro-binding-expander
)))
2216 (defclass symbol-macro-binding
(binding)
2219 :accessor macro-binding-expander
)))
2221 (defclass variable-binding
(binding)
2222 ((lending ; a property-list
2224 :accessor binding-lending
)
2225 (store-type ; union of all types ever stored here
2227 ;; :initarg :store-type
2228 :accessor binding-store-type
)))
2230 (defmethod binding-lended-p ((binding variable-binding
))
2231 (and (getf (binding-lending binding
) :lended-to
)
2232 (not (eq :unused
(getf (binding-lending binding
) :lended-to
)))))
2234 (defclass lexical-binding
(variable-binding) ())
2235 (defclass located-binding
(lexical-binding) ())
2237 (defclass function-binding
(operator-binding located-binding
)
2240 :accessor function-binding-funobj
)
2242 :initarg
:parent-funobj
2243 :reader function-binding-parent
)))
2245 (defclass funobj-binding
(function-binding) ())
2246 (defclass closure-binding
(function-binding located-binding
) ())
2247 (defclass lambda-binding
(function-binding) ())
2249 (defclass temporary-name
(located-binding)
2252 (defclass borrowed-binding
(located-binding)
2254 :initarg
:reference-slot
2255 :accessor borrowed-binding-reference-slot
)
2257 :initarg
:target-binding
2258 :reader borrowed-binding-target
)
2262 :accessor borrowed-binding-usage
)))
2264 (defclass lexical-borrowed-binding
(borrowed-binding)
2265 ((stack-frame-distance
2266 :initarg
:stack-frame-distance
2267 :reader stack-frame-distance
))
2268 (:documentation
"A closure with lexical extent borrows bindings using this class."))
2270 (defclass indefinite-borrowed-binding
(borrowed-binding)
2272 :initarg
:reference-slot
2273 :reader borrowed-binding-reference-slot
)))
2276 (defclass constant-reference-binding
(lexical-binding)
2279 :reader constant-reference-object
)))
2282 (defmethod print-object ((object constant-reference-binding
) stream
)
2283 (print-unreadable-object (object stream
:type t
:identity t
)
2284 (format stream
"object: ~S" (constant-reference-object object
)))
2287 (defclass forwarding-binding
(lexical-binding)
2289 :initarg
:target-binding
2290 :accessor forwarding-binding-target
)))
2292 (defmethod binding-funobj ((binding binding
))
2293 (movitz-environment-funobj (binding-env binding
)))
2295 (defmethod binding-funobj ((binding forwarding-binding
))
2296 (movitz-environment-funobj (binding-env (forwarding-binding-target binding
))))
2298 (defclass function-argument
(located-binding) ())
2299 (defclass edx-function-argument
(function-argument) ())
2301 (defclass positional-function-argument
(function-argument)
2304 :reader function-argument-argnum
)))
2306 (defclass required-function-argument
(positional-function-argument) ())
2308 (defclass register-required-function-argument
(required-function-argument) ())
2309 (defclass fixed-required-function-argument
(required-function-argument)
2312 :reader binding-numargs
)))
2313 (defclass floating-required-function-argument
(required-function-argument) ())
2315 (defclass non-required-function-argument
(function-argument)
2318 :reader optional-function-argument-init-form
)
2320 :initarg supplied-p-var
2321 :reader optional-function-argument-supplied-p-var
)))
2323 (defclass optional-function-argument
(non-required-function-argument positional-function-argument
) ())
2325 (defclass supplied-p-function-argument
(function-argument) ())
2327 (defclass rest-function-argument
(positional-function-argument) ())
2329 (defclass keyword-function-argument
(non-required-function-argument)
2331 :initarg
:keyword-name
2332 :reader keyword-function-argument-keyword-name
)))
2334 (defclass dynamic-binding
(variable-binding) ())
2336 (defclass shadowing-binding
(binding) ())
2338 (defclass shadowing-dynamic-binding
(dynamic-binding shadowing-binding
)
2340 :initarg
:shadowed-variable
2341 :reader shadowed-variable
)
2343 :initarg
:shadowing-variable
2344 :reader shadowing-variable
)))
2346 (defmethod binding-store-type ((binding dynamic-binding
))
2347 (multiple-value-list (type-specifier-encode t
)))
2349 (defun stack-frame-offset (stack-frame-position)
2350 (* -
4 (1+ stack-frame-position
)))
2352 (defun argument-stack-offset (binding)
2353 (check-type binding fixed-required-function-argument
)
2354 (argument-stack-offset-shortcut (binding-numargs binding
)
2355 (function-argument-argnum binding
)))
2357 (defun argument-stack-offset-shortcut (numargs argnum
)
2358 "For a function of <numargs> arguments, locate the ebp-relative position
2359 of argument <argnum>."
2360 (* 4 (- numargs -
1 argnum
)))
2364 ;;; New style of locating bindings. The point is to not side-effect the binding objects.
2366 (defun new-binding-location (binding map
&key
(default nil default-p
))
2367 (check-type binding
(or binding
(cons keyword binding
)))
2368 (let ((x (assoc binding map
)))
2372 (t (error "No location for ~S." binding
)))))
2374 (defun make-binding-map () nil
)
2376 (defun new-binding-located-p (binding map
)
2377 (check-type binding
(or null binding
(cons keyword binding
)))
2378 (and (assoc binding map
) t
))
2380 (defun frame-map-size (map)
2384 (if (integerp (cdr x
))
2388 (defun frame-map-next-free-location (frame-map env
&optional
(size 1))
2389 (labels ((stack-location (binding)
2390 (if (typep binding
'forwarding-binding
)
2391 (stack-location (forwarding-binding-target binding
))
2392 (new-binding-location binding frame-map
:default nil
)))
2393 (env-extant (env1 env2
)
2394 "Is env1 active whenever env2 is active?"
2399 ;; (warn "~S shadowed by ~S" env env2)
2401 (t (env-extant env1
(movitz-environment-extent-uplink env2
))))))
2402 (let ((frame-size (frame-map-size frame-map
)))
2403 (or (loop for location from
1 to frame-size
2405 (loop for sub-location from location below
(+ location size
)
2407 (find-if (lambda (b-loc)
2408 (destructuring-bind (binding . binding-location
)
2410 (or (and (eq binding nil
) ; nil means "back off!"
2411 (eql sub-location binding-location
))
2412 (and (not (bindingp binding
))
2413 (eql sub-location binding-location
))
2414 (and (bindingp binding
)
2415 (eql sub-location
(stack-location binding
))
2419 (or (env-extant (binding-env b
) env
)
2420 (env-extant env
(binding-env b
))
2421 (when (typep b
'forwarding-binding
)
2422 (z (forwarding-binding-target b
)))))))
2426 (1+ frame-size
))))) ; no free location found, so grow frame-size.
2428 (define-setf-expander new-binding-location
(binding map-place
&environment env
)
2429 (multiple-value-bind (temps values stores setter getter
)
2430 (get-setf-expansion map-place env
)
2431 (let ((new-value (gensym))
2432 (binding-var (gensym)))
2433 (values (append temps
(list binding-var
))
2434 (append values
(list binding
))
2436 `(let ((,(car stores
) (progn
2437 (assert (or (null binding
)
2438 (not (new-binding-located-p ,binding-var
,getter
))))
2439 (check-type ,new-value
(or keyword
2442 (cons (eql :argument-stack
) *)))
2443 (acons ,binding-var
,new-value
,getter
))))
2446 `(new-binding-location ,binding-var
,getter
)))))
2448 ;;; Objects with dynamic extent may be located on the stack-frame, which at
2449 ;;; compile-time is represented with this structure.
2451 ;;;(defclass stack-allocated-object ()
2453 ;;; ;; Size in words (4 octets) this object occupies in the stack-frame.
2457 ;;; ;; Stack-frame offset (in words) this object is allocated to.
2458 ;;; :accessor location)))
2464 (defun ignore-instruction-prefixes (instruction)
2465 (if (and (consp instruction
)
2466 (listp (car instruction
)))
2470 (defun instruction-sub-program (instruction)
2471 "When an instruction contains a sub-program, return that program, and
2472 the sub-program options (&optional label) as secondary value."
2473 (let ((instruction (ignore-instruction-prefixes instruction
)))
2474 (and (consp instruction
)
2475 (consp (second instruction
))
2476 (symbolp (car (second instruction
)))
2477 (string= 'quote
(car (second instruction
)))
2478 (let ((x (second (second instruction
))))
2480 (eq :sub-program
(car x
))
2484 (defun instruction-is (instruction &optional operator
)
2485 (and (listp instruction
)
2486 (if (member (car instruction
) '(:globally
:locally
))
2487 (instruction-is (second instruction
) operator
)
2488 (let ((instruction (ignore-instruction-prefixes instruction
)))
2490 (eq operator
(car instruction
))
2491 (car instruction
))))))
2493 (defun instruction-uncontinues-p (instruction)
2494 "Is it impossible for control to return after instruction?"
2495 (or (member (instruction-is instruction
)
2501 #+ignore
(defun sub-environment-p (env1 env2
)
2505 (t (sub-environment-p (movitz-environment-uplink env1
) env2
))))
2507 (defun find-code-constants-and-jumpers (code &key include-programs
)
2508 "Return code's constants (a plist of constants and their usage-counts) and jumper-sets."
2509 (let (jumper-sets constants key-args-set
)
2510 (labels ((process-binding (binding)
2511 "Some bindings are really references to constants."
2513 (constant-object-binding
2514 (let ((object (movitz-read (constant-object binding
))))
2515 (when (typep object
'movitz-heap-object
)
2516 (incf (getf constants object
0)))))
2518 (process-binding (forwarding-binding-target binding
)))
2520 (let ((funobj (function-binding-funobj binding
)))
2521 (incf (getf constants funobj
0))))
2524 (error "No function-binding now..: ~S" binding
))))
2526 "This local function side-effects the variables jumper-sets and constants."
2527 (loop for instruction in sub-code
2528 do
(case (instruction-is instruction
)
2529 ((:local-function-init
:load-lambda
)
2530 (let* ((binding (second instruction
))
2531 (funobj (function-binding-funobj binding
)))
2532 (unless (eq :unused
(movitz-funobj-extent funobj
))
2533 (incf (getf constants funobj
0))
2534 (dolist (binding (borrowed-bindings funobj
))
2535 (process-binding binding
)))))
2536 ((:load-lexical
:lend-lexical
:call-lexical
)
2537 (process-binding (second instruction
)))
2539 (let ((object (movitz-read (second instruction
))))
2540 (when (typep object
'movitz-heap-object
)
2541 (incf (getf constants object
0)))))
2543 (destructuring-bind (name set
)
2545 (assert (not (getf jumper-sets name
)) ()
2546 "Duplicate jumper declaration for ~S." name
)
2547 (setf (getf jumper-sets name
) set
)))
2548 (:declare-key-arg-set
2549 (setf key-args-set
(cdr instruction
)))
2550 (t (when (listp instruction
)
2551 (dolist (binding (find-read-bindings instruction
))
2552 (process-binding binding
)))))
2553 do
(let ((sub (instruction-sub-program instruction
)))
2554 (when sub
(process sub
))))))
2556 (map nil
#'process include-programs
))
2557 (loop for key-arg in key-args-set
2558 do
(remf constants key-arg
))
2559 (values constants jumper-sets key-args-set
)))
2561 (defun layout-funobj-vector (constants key-args-constants jumper-sets borrowing-bindings
)
2562 (let* ((jumpers (loop with x
2563 for set in
(cdr jumper-sets
) by
#'cddr
2564 unless
(search set x
)
2565 do
(setf x
(nconc x
(copy-list set
)))
2566 finally
(return x
)))
2567 (num-jumpers (length jumpers
))
2568 (stuff (append (mapcar (lambda (c)
2571 (when key-args-constants
2572 (list (cons (movitz-read 0)
2574 (sort (loop for
(constant count
) on constants by
#'cddr
2575 unless
(or (eq constant
*movitz-nil
*)
2576 (eq constant
(image-t-symbol *image
*)))
2577 collect
(cons constant count
))
2579 (values (append jumpers
2581 (movitz-read (car x
)))
2583 (make-list (length borrowing-bindings
)
2584 :initial-element
*movitz-nil
*))
2586 (loop for
(name set
) on jumper-sets by
#'cddr
2587 collect
(cons name set
))
2588 (loop for borrowing-binding in borrowing-bindings
2589 as pos upfrom
(+ num-jumpers
(length stuff
))
2590 collect
(cons borrowing-binding pos
)))))
2592 (defun movitz-funobj-intern-constant (funobj obj
)
2594 (let ((cobj (movitz-read obj
)))
2595 (+ (slot-offset 'movitz-funobj
'constant0
)
2597 (let* ((pos (position cobj
(movitz-funobj-const-list funobj
)
2598 :start
(movitz-funobj-num-jumpers funobj
))))
2600 "Couldn't find constant ~S in ~S's set of constants ~S."
2601 obj funobj
(movitz-funobj-const-list funobj
))
2604 (defun compute-free-registers (pc distance funobj frame-map
2605 &key
(free-registers '(:ecx
:eax
:ebx
:edx
)))
2606 "Return set of free register, and whether there may be more registers
2607 free later, with a more specified frame-map."
2608 (loop with free-so-far
= free-registers
2609 repeat distance for i in pc
2610 while
(not (null free-so-far
))
2613 ((and (instruction-is i
:init-lexvar
)
2614 (typep (second i
) 'required-function-argument
)) ; XXX
2615 (destructuring-bind (binding &key init-with-register init-with-type
2616 protect-registers protect-carry
)
2618 (declare (ignore protect-carry init-with-type
))
2619 (when init-with-register
2620 (setf free-so-far
(remove-if (lambda (x)
2621 (if (new-binding-located-p binding frame-map
)
2622 (eq x
(new-binding-location binding frame-map
))
2623 (or (eq x init-with-register
)
2624 (member x protect-registers
))))
2626 (t (case (instruction-is i
)
2628 (return nil
)) ; a label, most likely
2629 ((:declare-key-arg-set
:declare-label-set
)
2631 ((:lexical-control-transfer
:load-lambda
)
2632 (return nil
)) ; not sure about these.
2635 (remove-if (lambda (r)
2640 (remove :ecx free-so-far
)))
2643 (set-difference free-so-far
'(:eax
:edx
))))
2644 ((:into
:clc
:stc
:int
))
2645 ((:jmp
:jnz
:je
:jne
:jz
:jge
:jae
:jnc
:jbe
)
2647 (remove :push free-so-far
)))
2650 (remove-if (lambda (r)
2656 (set-difference free-so-far
'(:eax
:edx
))))
2657 ((:movb
:testb
:andb
:cmpb
)
2659 (remove-if (lambda (r)
2660 (and (not (eq r
:push
))
2661 (or (tree-search i r
)
2662 (tree-search i
(register32-to-low8 r
)))))
2664 ((:sarl
:shrl
:shll
:xorl
:cmpl
:leal
:btl
:sbbl
:cdq
2665 :movl
:movzxw
:movzxb
:testl
:andl
:addl
:subl
:imull
:idivl
)
2667 (remove-if (lambda (r)
2670 ((:load-constant
:load-lexical
:store-lexical
:cons-get
:endp
:incf-lexvar
:init-lexvar
)
2671 (assert (gethash (instruction-is i
) *extended-code-expanders
*))
2673 ((and (instruction-is i
:init-lexvar
) ; special case..
2674 (typep (second i
) 'forwarding-binding
)))
2675 (t (unless (can-expand-extended-p i frame-map
)
2676 ;; (warn "can't expand ~A from ~A" i frame-map)
2677 (return (values nil t
)))
2678 (let ((exp (expand-extended-code i funobj frame-map
)))
2679 (when (tree-search exp
'(:call
:local-function-init
))
2681 (remove-if (lambda (r)
2685 (remove-if (lambda (r)
2686 (and (not (eq r
:push
))
2687 (or (tree-search exp r
)
2688 (tree-search exp
(register32-to-low8 r
)))))
2690 ((:local-function-init
)
2691 (destructuring-bind (binding)
2693 (unless (typep binding
'funobj-binding
)
2695 (t #+ignore
(warn "Dist ~D stopped by ~A"
2698 ;; do (warn "after ~A: ~A" i free-so-far)
2699 finally
(return free-so-far
)))
2701 (defun try-locate-in-register (binding var-counts funobj frame-map
)
2702 "Try to locate binding in a register. Return a register, or
2703 nil and :not-now, or :never.
2704 This function is factored out from assign-bindings."
2705 (assert (not (typep binding
'forwarding-binding
)))
2706 (let* ((count-init-pc (gethash binding var-counts
))
2707 (count (car count-init-pc
))
2708 (init-pc (second count-init-pc
)))
2709 #+ignore
(warn "b ~S: count: ~D, init-pc: ~{~&~A~}" binding count init-pc
)
2711 ((and (not *compiler-allow-transients
*)
2712 (typep binding
'function-argument
))
2713 (values nil
:never
))
2714 ((binding-lended-p binding
)
2715 ;; We can't lend a register.
2716 (values nil
:never
))
2719 (assert (instruction-is (first init-pc
) :init-lexvar
))
2720 (destructuring-bind (init-binding &key init-with-register init-with-type
2721 protect-registers protect-carry
)
2722 (cdr (first init-pc
))
2723 (declare (ignore protect-registers protect-carry init-with-type
))
2724 (assert (eq binding init-binding
))
2725 (multiple-value-bind (load-instruction binding-destination distance
)
2726 (loop for i in
(cdr init-pc
) as distance upfrom
0
2727 do
(when (not (instruction-is i
:init-lexvar
))
2728 (multiple-value-bind (read-bindings read-destinations
)
2729 (find-read-bindings i
)
2730 (let ((pos (position binding read-bindings
:test
#'binding-eql
)))
2732 (return (values i
(nth pos read-destinations
) distance
)))))))
2733 (declare (ignore load-instruction
))
2734 (multiple-value-bind (free-registers more-later-p
)
2735 (and distance
(compute-free-registers (cdr init-pc
) distance funobj frame-map
))
2737 (when (string= 'num-jumpers
(binding-name binding
))
2738 (warn "load: ~S, dist: ~S, dest: ~S" load-instruction distance binding-destination
)
2739 (warn "free: ~S, more: ~S" free-registers more-later-p
))
2740 (let ((free-registers-no-ecx (remove :ecx free-registers
)))
2742 ((member binding-destination free-registers-no-ecx
)
2743 binding-destination
)
2744 ((and (not (typep binding
'(or fixed-required-function-argument
2745 register-required-function-argument
)))
2746 (member binding-destination free-registers
))
2747 binding-destination
)
2748 ((member init-with-register free-registers
)
2750 ((and (member :ecx free-registers
)
2751 (not (typep binding
'function-argument
))
2752 (or (eq :untagged-fixnum-ecx binding-destination
)
2753 (eq :untagged-fixnum-ecx init-with-register
)))
2754 :untagged-fixnum-ecx
)
2755 ((and (binding-store-type binding
)
2756 (member :ecx free-registers
)
2757 (not (typep binding
'(or fixed-required-function-argument
2758 register-required-function-argument
)))
2759 (multiple-value-call #'encoded-subtypep
2760 (values-list (binding-store-type binding
))
2761 (type-specifier-encode '(or integer character
))))
2763 ((not (null free-registers-no-ecx
))
2764 (first free-registers-no-ecx
))
2766 (values nil
:not-now
))
2767 ((and distance
(typep binding
'temporary-name
))
2768 ;; We might push/pop this variable
2769 (multiple-value-bind (push-available-p maybe-later
)
2770 (compute-free-registers (cdr init-pc
) distance funobj frame-map
2771 :free-registers
'(:push
))
2772 ;; (warn "pushing.. ~S ~A ~A" binding push-available-p maybe-later)
2777 (values nil
:not-now
))
2778 (t (values nil
:never
)))))
2779 (t (values nil
:never
))))))))
2780 (t (values nil
:never
)))))
2782 (defun discover-variables (code function-env
)
2783 "Iterate over CODE, and take note in the hash-table VAR-COUNTER which ~
2784 variables CODE references that are lexically bound in ENV."
2785 (check-type function-env function-env
)
2786 ;; (print-code 'discover code)
2787 (let ((var-counter (make-hash-table :test
#'eq
:size
40)))
2788 (labels ((record-binding-used (binding)
2789 (let ((count-init-pc (or (gethash binding var-counter
)
2790 (setf (gethash binding var-counter
)
2792 (setf (third count-init-pc
) t
)
2793 (when (typep binding
'forwarding-binding
)
2794 (record-binding-used (forwarding-binding-target binding
)))))
2795 (take-note-of-binding (binding &optional storep init-pc
)
2796 (let ((count-init-pc (or (gethash binding var-counter
)
2797 (setf (gethash binding var-counter
)
2798 (list 0 nil
(not storep
))))))
2800 (assert (not (second count-init-pc
)))
2801 (setf (second count-init-pc
) init-pc
))
2803 (unless (eq binding
(binding-target binding
))
2804 ;; (break "ewfew: ~S" (gethash (binding-target binding) var-counter))
2805 (take-note-of-binding (binding-target binding
)))
2806 (setf (third count-init-pc
) t
)
2807 (incf (car count-init-pc
))))
2809 (when (typep binding
'forwarding-binding
)
2810 (take-note-of-binding (forwarding-binding-target binding
) storep
)))
2811 (take-note-of-init (binding init-pc
)
2812 (let ((count-init-pc (or (gethash binding var-counter
)
2813 (setf (gethash binding var-counter
)
2814 (list 0 nil nil
)))))
2815 (assert (not (second count-init-pc
)))
2816 (setf (second count-init-pc
) init-pc
)))
2817 (do-discover-variables (code env
)
2818 (loop for pc on code as instruction in code
2819 when
(listp instruction
)
2820 do
(flet ((lend-lexical (borrowing-binding dynamic-extent-p
)
2821 (let ((lended-binding
2822 (borrowed-binding-target borrowing-binding
)))
2823 (assert (not (typep lended-binding
'forwarding-binding
)) ()
2824 "Can't lend a forwarding-binding.")
2825 (pushnew lended-binding
2826 (potentially-lended-bindings function-env
))
2827 (take-note-of-binding lended-binding
)
2828 (symbol-macrolet ((p (binding-lending lended-binding
)))
2829 (incf (getf p
:lended-count
0))
2830 (setf (getf p
:dynamic-extent-p
) (and (getf p
:dynamic-extent-p t
)
2831 dynamic-extent-p
))))))
2832 (case (instruction-is instruction
)
2833 ((:local-function-init
:load-lambda
)
2834 (let ((function-binding (second instruction
)))
2835 (take-note-of-binding function-binding
)
2836 (let ((sub-funobj (function-binding-funobj function-binding
)))
2838 (warn "fun-ext: ~S ~S ~S"
2840 (movitz-funobj-extent sub-funobj
)
2841 (movitz-allocation sub-funobj
))
2842 (when (typep (movitz-allocation sub-funobj
)
2843 'with-dynamic-extent-scope-env
)
2844 (take-note-of-binding (base-binding (movitz-allocation sub-funobj
)))))
2845 (let ((closure-funobj (function-binding-funobj function-binding
)))
2846 (dolist (borrowing-binding (borrowed-bindings closure-funobj
))
2847 (lend-lexical borrowing-binding nil
)))))
2849 (destructuring-bind (binding num-args
)
2851 (declare (ignore num-args
))
2854 (take-note-of-binding binding
))
2857 (destructuring-bind (binding &key init-with-register init-with-type
2858 protect-registers protect-carry
2861 (declare (ignore protect-registers protect-carry init-with-type
2862 shared-reference-p
))
2864 ((not init-with-register
)
2865 (take-note-of-init binding pc
))
2867 (take-note-of-binding binding t pc
)
2868 (when (and (typep init-with-register
'binding
)
2869 (not (typep binding
'forwarding-binding
))
2870 (not (typep binding
'keyword-function-argument
))) ; XXX
2871 (take-note-of-binding init-with-register
))))))
2872 (t (mapcar #'take-note-of-binding
2873 (find-read-bindings instruction
))
2874 (mapcar #'record-binding-used
; This is just concerning "unused variable"
2875 (find-used-bindings instruction
)) ; warnings!
2876 (let ((store-binding (find-written-binding-and-type instruction
)))
2878 (take-note-of-binding store-binding t
)))
2879 (do-discover-variables (instruction-sub-program instruction
) env
)))))))
2880 (do-discover-variables code function-env
))
2881 (values var-counter
)))
2883 (defun assign-bindings (code function-env
&optional
(initial-stack-frame-position 1)
2884 (frame-map (make-binding-map)))
2885 "Assign locations to all lexical variables in CODE. Recurses into any
2886 sub-environments found in CODE. A frame-map which is an assoc from
2887 bindings to stack-frame locations."
2888 ;; Then assign them to locations in the stack-frame.
2889 #+ignore
(warn "assigning code:~%~{~& ~A~}" code
)
2890 (check-type function-env function-env
)
2891 (assert (= initial-stack-frame-position
2892 (1+ (frame-map-size frame-map
))))
2893 (let* ((env-assigned-p nil
) ; memoize result of assign-env-bindings
2895 (var-counts (discover-variables flat-program function-env
)))
2897 ((assign-env-bindings (env)
2898 (unless (member env env-assigned-p
)
2899 (unless (eq env function-env
)
2900 (assign-env-bindings (movitz-environment-extent-uplink env
)))
2901 (let* ((bindings-to-locate
2902 (loop for binding being the hash-keys of var-counts
2904 (and (eq env
(binding-extent-env binding
))
2905 (not (let ((variable (binding-name binding
)))
2907 ((not (typep binding
'lexical-binding
)))
2908 ((typep binding
'lambda-binding
))
2909 ((typep binding
'constant-object-binding
))
2910 ((typep binding
'forwarding-binding
)
2911 (when (plusp (or (car (gethash binding var-counts
)) 0))
2912 (assert (new-binding-located-p binding frame-map
)))
2914 ((typep binding
'borrowed-binding
))
2915 ((typep binding
'funobj-binding
))
2916 ((and (typep binding
'fixed-required-function-argument
)
2917 (plusp (or (car (gethash binding var-counts
)) 0)))
2918 (prog1 nil
; may need lending-cons
2919 (setf (new-binding-location binding frame-map
)
2920 `(:argument-stack
,(function-argument-argnum binding
)))))
2921 ((unless (or (movitz-env-get variable
'ignore nil
2922 (binding-env binding
) nil
)
2923 (movitz-env-get variable
'ignorable nil
2924 (binding-env binding
) nil
)
2925 (third (gethash binding var-counts
)))
2926 (warn "Unused variable: ~S"
2927 (binding-name binding
))))
2928 ((not (plusp (or (car (gethash binding var-counts
)) 0))))))))
2930 (bindings-fun-arg-sorted
2931 (when (eq env function-env
)
2932 (sort (copy-list bindings-to-locate
) #'<
2933 :key
(lambda (binding)
2935 (edx-function-argument 3)
2936 (positional-function-argument
2937 (* 2 (function-argument-argnum binding
)))
2938 (binding 100000))))))
2939 (bindings-register-goodness-sort
2940 (sort (copy-list bindings-to-locate
) #'<
2941 ;; Sort so as to make the most likely
2942 ;; candidates for locating to registers
2943 ;; be assigned first (i.e. maps to
2944 ;; a smaller value).
2947 ((or constant-object-binding
2951 (fixed-required-function-argument
2952 (+ 100 (function-argument-argnum b
)))
2954 (let* ((count-init (gethash b var-counts
))
2955 (count (car count-init
))
2956 (init-pc (second count-init
)))
2957 (if (not (and count init-pc
))
2960 (or (position-if (lambda (i)
2961 (member b
(find-read-bindings i
)))
2965 ;; First, make several passes while trying to locate bindings
2967 (loop repeat
100 with try-again
= t and did-assign
= t
2968 do
(unless (and try-again did-assign
)
2970 do
(setf try-again nil did-assign nil
)
2971 (loop for binding in bindings-fun-arg-sorted
2972 while
(or (typep binding
'register-required-function-argument
)
2973 (typep binding
'floating-required-function-argument
)
2974 (and (typep binding
'positional-function-argument
)
2975 (< (function-argument-argnum binding
)
2977 do
(unless (new-binding-located-p binding frame-map
)
2978 (multiple-value-bind (register status
)
2979 (try-locate-in-register binding var-counts
2980 (movitz-environment-funobj function-env
)
2984 (setf (new-binding-location binding frame-map
)
2986 (setf did-assign t
))
2987 ((eq status
:not-now
)
2988 ;; (warn "Wait for ~S map ~A" binding frame-map)
2990 (t (assert (eq status
:never
)))))))
2991 (dolist (binding bindings-register-goodness-sort
)
2992 (unless (and (binding-lended-p binding
)
2993 (not (typep binding
'borrowed-binding
))
2994 (not (getf (binding-lending binding
) :stack-cons-location
)))
2995 (unless (new-binding-located-p binding frame-map
)
2996 (check-type binding located-binding
)
2997 (multiple-value-bind (register status
)
2998 (try-locate-in-register binding var-counts
2999 (movitz-environment-funobj function-env
)
3003 (setf (new-binding-location binding frame-map
)
3005 (setf did-assign t
))
3006 ((eq status
:not-now
)
3008 (t (assert (eq status
:never
))))))))
3009 do
(when (and try-again
(not did-assign
))
3010 (let ((binding (or (find-if (lambda (b)
3011 (and (typep b
'positional-function-argument
)
3012 (= 0 (function-argument-argnum b
))
3013 (not (new-binding-located-p b frame-map
))))
3014 bindings-fun-arg-sorted
)
3015 (find-if (lambda (b)
3016 (and (typep b
'positional-function-argument
)
3017 (= 1 (function-argument-argnum b
))
3018 (not (new-binding-located-p b frame-map
))))
3019 bindings-fun-arg-sorted
)
3020 (find-if (lambda (b)
3021 (and (not (new-binding-located-p b frame-map
))
3022 (not (typep b
'function-argument
))))
3023 bindings-register-goodness-sort
3026 (setf (new-binding-location binding frame-map
)
3027 (frame-map-next-free-location frame-map
(binding-env binding
)))
3028 (setf did-assign t
))))
3029 finally
(break "100 iterations didn't work"))
3030 ;; Then, make one pass assigning bindings to stack-frame.
3031 (loop for binding in bindings-fun-arg-sorted
3032 while
(or (typep binding
'register-required-function-argument
)
3033 (typep binding
'floating-required-function-argument
)
3034 (and (typep binding
'positional-function-argument
)
3035 (< (function-argument-argnum binding
)
3037 do
(unless (new-binding-located-p binding frame-map
)
3038 (setf (new-binding-location binding frame-map
)
3039 (frame-map-next-free-location frame-map
(binding-env binding
)))))
3040 (dolist (binding bindings-register-goodness-sort
)
3041 (when (and (binding-lended-p binding
)
3042 (not (typep binding
'borrowed-binding
))
3043 (not (getf (binding-lending binding
) :stack-cons-location
)))
3045 (assert (not (typep binding
'keyword-function-argument
)) ()
3046 "Can't lend keyword binding ~S." binding
)
3047 ;; (warn "assigning lending-cons for ~W at ~D" binding stack-frame-position)
3048 (let ((cons-pos (frame-map-next-free-location frame-map function-env
2)))
3049 (setf (new-binding-location (cons :lended-cons binding
) frame-map
)
3051 (setf (new-binding-location (cons :lended-cons binding
) frame-map
)
3053 (setf (getf (binding-lending binding
) :stack-cons-location
)
3055 (unless (new-binding-located-p binding frame-map
)
3057 (constant-object-binding) ; no location needed.
3058 (forwarding-binding) ; will use the location of target binding.
3059 (borrowed-binding) ; location is predetermined
3060 (fixed-required-function-argument
3061 (setf (new-binding-location binding frame-map
)
3062 `(:argument-stack
,(function-argument-argnum binding
))))
3064 (setf (new-binding-location binding frame-map
)
3065 (frame-map-next-free-location frame-map
(binding-env binding
)))))))
3066 (push env env-assigned-p
)))))
3067 ;; First, "assign" each forwarding binding to their target.
3068 (loop for binding being the hash-keys of var-counts
3069 do
(when (and (typep binding
'forwarding-binding
)
3070 (plusp (car (gethash binding var-counts
'(0)))))
3071 (setf (new-binding-location binding frame-map
)
3072 (forwarding-binding-target binding
))))
3074 (flet ((set-exclusive-location (binding location
)
3075 (assert (not (rassoc location frame-map
))
3076 () "Fixed location ~S for ~S is taken by ~S."
3077 location binding
(rassoc location frame-map
))
3078 (setf (new-binding-location binding frame-map
) location
)))
3079 (when (key-vars-p function-env
)
3080 (when (= 0 (rest-args-position function-env
))
3081 (set-exclusive-location (loop for var in
(required-vars function-env
)
3082 as binding
= (movitz-binding var function-env nil
)
3083 thereis
(when (= 0 (function-argument-argnum binding
))
3086 (when (>= 1 (rest-args-position function-env
))
3087 (set-exclusive-location (loop for var in
(required-vars function-env
)
3088 as binding
= (movitz-binding var function-env nil
)
3089 thereis
(when (= 1 (function-argument-argnum binding
))
3092 (loop for key-var in
(key-vars function-env
)
3093 as key-binding
= (or (movitz-binding key-var function-env nil
)
3094 (error "No binding for key-var ~S." key-var
))
3095 as used-key-binding
=
3096 (when (plusp (car (gethash key-binding var-counts
'(0))))
3098 as used-supplied-p-binding
=
3099 (when (optional-function-argument-supplied-p-var key-binding
)
3100 (let ((b (or (movitz-binding (optional-function-argument-supplied-p-var key-binding
)
3102 (error "No binding for supplied-p-var ~S."
3103 (optional-function-argument-supplied-p-var key-binding
)))))
3104 (when (plusp (car (gethash key-binding var-counts
'(0))))
3106 as location upfrom
3 by
2
3107 do
(set-exclusive-location used-key-binding location
)
3108 (set-exclusive-location used-supplied-p-binding
(1+ location
))))
3109 ;; Now, use assing-env-bindings on the remaining bindings.
3112 for b being the hash-keys of var-counts using
(hash-value c
)
3113 as env
= (binding-env b
)
3114 when
(sub-env-p env function-env
)
3115 do
(incf (getf z env
0) (car c
))
3117 (return (sort (loop for x in z by
#'cddr
3122 do
(assign-env-bindings env
))
3123 #+ignore
(warn "Frame-map ~D:~{~&~A~}"
3124 (frame-map-size frame-map
)
3125 (stable-sort (sort (loop for
(b . l
) in frame-map
3126 collect
(list b l
(car (gethash b var-counts nil
))))
3129 (and (bindingp (car x
))
3130 (binding-name (car x
)))))
3133 (if (integerp (cadr x
))
3139 (defun operators-present-in-code-p (code operators operands
&key
(operand-test #'eql
)
3141 "A simple tree search for `(<one of operators> ,operand) in CODE."
3142 ;; (break "Deprecated operators-present-in-code-p")
3146 ((and (member (first code
) operators
)
3149 (funcall operand-test
(second code
) operands
)
3150 (member (second code
) operands
:test operand-test
)))
3153 (t (or (operators-present-in-code-p (car code
) operators operands
3154 :operand-test operand-test
3156 (operators-present-in-code-p (cdr code
) operators operands
3157 :operand-test operand-test
3161 (defun code-uses-binding-p (code binding
&key
(load t
) store call
)
3162 "Does extended <code> potentially read/write/call <binding>?"
3163 (labels ((search-funobj (funobj binding load store call
)
3164 ;; If this is a recursive lexical call (i.e. labels),
3165 ;; the function-envs might not be bound, but then this
3166 ;; code is searched already.
3167 (when (slot-boundp funobj
'function-envs
)
3168 (some (lambda (function-env-spec)
3169 (code-search (extended-code (cdr function-env-spec
)) binding
3171 (function-envs funobj
))))
3172 (code-search (code binding load store call
)
3173 (dolist (instruction code
)
3174 (when (consp instruction
)
3175 (let ((x (or (when load
3176 (some (lambda (read-binding)
3177 (binding-eql read-binding binding
))
3178 (find-read-bindings instruction
)))
3180 (let ((store-binding (find-written-binding-and-type instruction
)))
3182 (binding-eql binding store-binding
))))
3183 (case (car instruction
)
3184 (:local-function-init
3185 (search-funobj (function-binding-funobj (second instruction
))
3186 binding load store call
))
3189 (binding-eql binding
(second instruction
)))
3190 (let ((allocation (movitz-allocation
3191 (function-binding-funobj (second instruction
)))))
3193 (typep allocation
'with-dynamic-extent-scope-env
))
3194 (binding-eql binding
(base-binding allocation
))))
3195 (search-funobj (function-binding-funobj (second instruction
))
3196 binding load store call
)))
3199 (binding-eql binding
(second instruction
)))
3200 (search-funobj (function-binding-funobj (second instruction
))
3201 binding load store call
))))
3202 (code-search (instruction-sub-program instruction
)
3203 binding load store call
))))
3204 (when x
(return t
)))))))
3205 (code-search code binding load store call
)))
3210 (defun binding-target (binding)
3211 "Resolve a binding in terms of forwarding."
3214 (binding-target (forwarding-binding-target binding
)))
3218 (defun binding-eql (x y
)
3219 (check-type x binding
)
3220 (check-type y binding
)
3222 (and (typep x
'forwarding-binding
)
3223 (binding-eql (forwarding-binding-target x
) y
))
3224 (and (typep y
'forwarding-binding
)
3225 (binding-eql x
(forwarding-binding-target y
)))))
3227 (defun tree-search (tree items
)
3228 (if (and (atom items
) ; make common case fast(er), hopefully.
3229 (not (numberp items
)))
3230 (labels ((tree-search* (tree item
)
3234 (or (tree-search* (car tree
) item
)
3235 (tree-search* (cdr tree
) item
)))
3236 (t (eq tree item
)))))
3237 (tree-search* tree items
))
3242 (member tree items
)))
3244 (or (tree-search (car tree
) items
)
3245 (tree-search (cdr tree
) items
))))))
3248 (if (atom x
) x
(car x
)))
3250 (defun result-mode-type (x)
3254 (constant-object-binding :constant-binding
)
3255 (lexical-binding :lexical-binding
)
3256 (dynamic-binding :dynamic-binding
)))
3259 (if (symbolp x
) nil
(cdr x
)))
3261 (defun funobj-assign-bindings (code env
&optional
(stack-frame-position 1)
3262 (frame-map (make-binding-map)))
3263 "This wrapper around assign-bindings checks if the first instructions of CODE
3264 are load-lexicals of the first two function arguments, and if possible these
3265 bindings are located in the appropriate register, so no stack location is needed."
3266 (check-type env function-env
)
3267 (assign-bindings (append (when (first (required-vars env
))
3268 (let ((binding (movitz-binding (first (required-vars env
))
3270 (check-type binding required-function-argument
)
3271 `((:init-lexvar
,binding
:init-with-register
:eax
:init-with-type t
))))
3272 (when (second (required-vars env
))
3273 (let ((binding (movitz-binding (second (required-vars env
))
3275 (check-type binding required-function-argument
)
3276 `((:init-lexvar
,binding
:init-with-register
:ebx
:init-with-type t
))))
3278 env stack-frame-position frame-map
))
3280 (defun single-value-register (mode)
3282 ((:eax
:single-value
:multiple-values
:function
) :eax
)
3283 ((:ebx
:ecx
:edx
:esi
:esp
:ebp
) mode
)))
3285 (defun result-mode-register (mode)
3287 ((:eax
:single-value
) :eax
)
3288 ((:ebx
:ecx
:edx
:esi
:esp
) mode
)
3291 (defun accept-register-mode (mode &optional
(default-mode :eax
))
3293 ((:eax
:ebx
:ecx
:edx
)
3297 (defun chose-free-register (unfree-registers &optional
(preferred-register :eax
))
3299 ((not (member preferred-register unfree-registers
))
3301 ((find-if (lambda (r) (not (member r unfree-registers
)))
3302 '(:eax
:ebx
:ecx
:edx
)))
3303 (t (error "Unable to find a free register."))))
3305 (defun make-indirect-reference (base-register offset
)
3306 "Make the shortest possible assembly indirect reference, explointing the constant edi register."
3307 (if (<= #x-80 offset
#x7f
)
3308 (list base-register offset
)
3309 (let ((edi (image-nil-word *image
*)))
3311 ((<= #x-80
(- offset edi
) #x7f
)
3312 `(,base-register
:edi
,(- offset edi
)))
3313 ((<= #x-80
(- offset
(* 2 edi
)) #x7f
)
3314 `(,base-register
(:edi
2) ,(- offset
(* 2 edi
))))
3315 ((<= #x-80
(- offset
(* 4 edi
)) #x7f
)
3316 `(,base-register
(:edi
4) ,(- offset
(* 4 edi
))))
3317 ((<= #x-80
(- offset
(* 8 edi
)) #x7f
)
3318 `(,base-register
(:edi
8) ,(- offset
(* 8 edi
))))
3319 (t (list base-register offset
))))))
3321 (defun make-load-lexical (binding result-mode funobj shared-reference-p frame-map
3322 &key tmp-register protect-registers override-binding-type
)
3323 "When tmp-register is provided, use that for intermediate storage required when
3324 loading borrowed bindings."
3326 (when (eq :ecx result-mode
)
3327 ;; (warn "loading to ecx: ~S" binding)
3328 (unless (or (null (binding-store-type binding
))
3329 (movitz-subtypep (apply #'encoded-type-decode
3330 (binding-store-type binding
))
3332 (warn "ecx from ~S" binding
)))
3333 (when (movitz-env-get (binding-name binding
) 'ignore nil
(binding-env binding
))
3334 (break "The variable ~S is used even if it was declared ignored."
3335 (binding-name binding
)))
3336 (let ((binding (ensure-local-binding binding funobj
))
3337 (protect-registers (cons :edx protect-registers
)))
3338 (labels ((chose-tmp-register (&optional preferred
)
3340 (unless (member preferred protect-registers
)
3342 (first (set-difference '(:eax
:ebx
:edx
)
3344 (error "Unable to chose a temporary register.")))
3345 (install-for-single-value (lexb lexb-location result-mode indirect-p
3346 &optional binding-type
)
3347 (let ((decoded-type (when binding-type
3348 (apply #'encoded-type-decode binding-type
))))
3350 ((and (eq result-mode
:untagged-fixnum-ecx
)
3351 (integerp lexb-location
))
3354 (type-specifier-singleton decoded-type
))
3355 #+ignore
(warn "Immloadlex: ~S"
3356 (type-specifier-singleton decoded-type
))
3357 (make-immediate-move (movitz-fixnum-value
3358 (car (type-specifier-singleton decoded-type
)))
3361 (movitz-subtypep decoded-type
'(and fixnum
(unsigned-byte 32))))
3362 (assert (not indirect-p
))
3363 (append (install-for-single-value lexb lexb-location
:ecx nil
)
3364 `((:shrl
,+movitz-fixnum-shift
+ :ecx
))))
3365 #+ignore
((warn "utecx ~S bt: ~S" lexb decoded-type
))
3367 (assert (not indirect-p
))
3368 (assert (not (member :eax protect-registers
)))
3369 (append (install-for-single-value lexb lexb-location
:eax nil
)
3370 `((,*compiler-global-segment-prefix
*
3371 :call
(:edi
,(global-constant-offset 'unbox-u32
))))))))
3372 ((integerp lexb-location
)
3373 (append `((:movl
,(make-indirect-reference :ebp
(stack-frame-offset lexb-location
))
3374 ,(single-value-register result-mode
)))
3376 `((:movl
(-1 ,(single-value-register result-mode
))
3377 ,(single-value-register result-mode
))))))
3378 ((eq lexb-location result-mode
)
3380 (t (when (and (eq result-mode
:untagged-fixnum-ecx
)
3382 (type-specifier-singleton decoded-type
))
3383 (break "xxx Immloadlex: ~S ~S"
3384 (operator lexb-location
)
3385 (type-specifier-singleton decoded-type
)))
3386 (ecase (operator lexb-location
)
3388 (assert (member result-mode
'(:eax
:ebx
:ecx
:edx
)))
3389 (assert (not indirect-p
))
3390 `((:popl
,result-mode
)))
3392 (assert (not indirect-p
))
3394 ((:ebx
:ecx
:edx
:esi
) `((:movl
:eax
,result-mode
)))
3395 ((:eax
:single-value
) nil
)
3396 (:untagged-fixnum-ecx
3397 `((,*compiler-global-segment-prefix
*
3398 :call
(:edi
,(global-constant-offset 'unbox-u32
)))))))
3400 (assert (not indirect-p
))
3401 (unless (eq result-mode lexb-location
)
3403 ((:eax
:single-value
) `((:movl
,lexb-location
:eax
)))
3404 ((:ebx
:ecx
:edx
:esi
) `((:movl
,lexb-location
,result-mode
)))
3405 (:untagged-fixnum-ecx
3406 `((:movl
,lexb-location
:ecx
)
3407 (:sarl
,movitz
:+movitz-fixnum-shift
+ :ecx
))))))
3409 (assert (<= 2 (function-argument-argnum lexb
)) ()
3410 "lexical :argument-stack argnum can't be ~A." (function-argument-argnum lexb
))
3412 ((eq result-mode
:untagged-fixnum-ecx
)
3413 (assert (not indirect-p
))
3414 `((:movl
(:ebp
,(argument-stack-offset lexb
)) :ecx
)
3415 (:sarl
,+movitz-fixnum-shift
+ :ecx
)))
3416 (t (append `((:movl
(:ebp
,(argument-stack-offset lexb
))
3417 ,(single-value-register result-mode
)))
3419 `((:movl
(-1 ,(single-value-register result-mode
))
3420 ,(single-value-register result-mode
))))))))
3421 (:untagged-fixnum-ecx
3423 ((:eax
:ebx
:ecx
:edx
)
3424 `((:leal
((:ecx
,+movitz-fixnum-factor
+)) ,result-mode
)))
3425 (:untagged-fixnum-ecx
3429 (assert (not (binding-lended-p binding
)) (binding)
3430 "Can't lend a forwarding-binding ~S." binding
)
3431 (make-load-lexical (forwarding-binding-target binding
)
3432 result-mode funobj shared-reference-p frame-map
3433 :override-binding-type
(binding-store-type binding
)))
3434 (constant-object-binding
3435 (assert (not (binding-lended-p binding
)) (binding)
3436 "Can't lend a constant-reference-binding ~S." binding
)
3437 (make-load-constant (constant-object binding
)
3441 (make-load-constant (function-binding-funobj binding
)
3442 result-mode funobj frame-map
))
3444 (let ((slot (borrowed-binding-reference-slot binding
)))
3447 (ecase (result-mode-type result-mode
)
3448 ((:eax
:ebx
:ecx
:edx
)
3449 `((:movl
(:esi
,(+ (slot-offset 'movitz-funobj
'constant0
) (* 4 slot
)))
3450 ,(result-mode-type result-mode
))))))
3451 ((not shared-reference-p
)
3453 ((:single-value
:eax
:ebx
:ecx
:edx
:esi
)
3454 (let ((tmp-register (chose-tmp-register (single-value-register result-mode
))))
3455 `((:movl
(:esi
,(+ (slot-offset 'movitz-funobj
'constant0
) (* 4 slot
)))
3457 (:movl
(,tmp-register -
1)
3458 ,(single-value-register result-mode
)))))
3460 (let ((tmp-register (chose-tmp-register :eax
)))
3461 `((:movl
(:esi
,(+ (slot-offset 'movitz-funobj
'constant0
) (* 4 slot
)))
3463 (:pushl
(,tmp-register -
1)))))
3464 (t (let ((tmp-register (chose-tmp-register :eax
)))
3465 (make-result-and-returns-glue
3466 result-mode tmp-register
3467 `((:movl
(:esi
,(+ (slot-offset 'movitz-funobj
'constant0
) (* 4 slot
)))
3469 (:movl
(,tmp-register -
1) ,tmp-register
))))))))))
3471 (let ((binding-type (or override-binding-type
3472 (binding-store-type binding
)))
3473 (binding-location (new-binding-location binding frame-map
)))
3474 #+ignore
(warn "~S type: ~S ~:[~;lended~]"
3477 (binding-lended-p binding
))
3479 ((and (binding-lended-p binding
)
3480 (not shared-reference-p
))
3481 (case (result-mode-type result-mode
)
3482 ((:single-value
:eax
:ebx
:ecx
:edx
:esi
:esp
)
3483 (install-for-single-value binding binding-location
3484 (single-value-register result-mode
) t
))
3486 (if (integerp binding-location
)
3487 `((:movl
(:ebp
,(stack-frame-offset binding-location
)) :eax
)
3489 (ecase (operator binding-location
)
3491 (assert (<= 2 (function-argument-argnum binding
)) ()
3492 ":load-lexical argnum can't be ~A." (function-argument-argnum binding
))
3493 `((:movl
(:ebp
,(argument-stack-offset binding
)) :eax
)
3494 (:pushl
(:eax -
1)))))))
3495 (t (make-result-and-returns-glue
3497 (install-for-single-value binding binding-location
:eax t
)))))
3498 (t (when (integerp result-mode
)
3499 (break "result-mode: ~S" result-mode
))
3500 (case (result-mode-type result-mode
)
3501 ((:single-value
:eax
:ebx
:ecx
:edx
:esi
:esp
:ebp
)
3502 (install-for-single-value binding binding-location
3503 (single-value-register result-mode
) nil
))
3505 (if (integerp binding-location
)
3506 `((:pushl
(:ebp
,(stack-frame-offset binding-location
))))
3507 (ecase (operator binding-location
)
3508 ((:eax
:ebx
:ecx
:edx
)
3509 `((:pushl
,binding-location
)))
3510 (:untagged-fixnum-ecx
3511 `((,*compiler-local-segment-prefix
*
3512 :call
(:edi
,(global-constant-offset 'box-u32-ecx
)))
3515 (assert (<= 2 (function-argument-argnum binding
)) ()
3516 ":load-lexical argnum can't be ~A." (function-argument-argnum binding
))
3517 `((:pushl
(:ebp
,(argument-stack-offset binding
))))))))
3518 (:boolean-branch-on-true
3519 (if (integerp binding-location
)
3520 `((:cmpl
:edi
(:ebp
,(stack-frame-offset binding-location
)))
3521 (:jne
',(operands result-mode
)))
3522 (ecase (operator binding-location
)
3524 `((:cmpl
:edi
,binding-location
)
3525 (:jne
',(operands result-mode
))))
3527 `((:cmpl
:edi
(:ebp
,(argument-stack-offset binding
)))
3528 (:jne
',(operands result-mode
)))))))
3529 (:boolean-branch-on-false
3530 (if (integerp binding-location
)
3531 `((:cmpl
:edi
(:ebp
,(stack-frame-offset binding-location
)))
3532 (:je
',(operands result-mode
)))
3533 (ecase (operator binding-location
)
3535 `((:cmpl
:edi
,binding-location
)
3536 (:je
',(operands result-mode
))))
3538 `((:cmpl
:edi
(:ebp
,(argument-stack-offset binding
)))
3539 (:je
',(operands result-mode
)))))))
3540 (:untagged-fixnum-ecx
3541 (install-for-single-value binding binding-location
:untagged-fixnum-ecx nil
3544 (let* ((destination result-mode
)
3545 (dest-location (new-binding-location destination frame-map
:default nil
)))
3547 ((not dest-location
) ; unknown, e.g. a borrowed-binding.
3548 (append (install-for-single-value binding binding-location
:edx nil
)
3549 (make-store-lexical result-mode
:edx nil funobj frame-map
)))
3550 ((equal binding-location dest-location
)
3552 ((member binding-location
'(:eax
:ebx
:ecx
:edx
))
3553 (make-store-lexical destination binding-location nil funobj frame-map
))
3554 ((member dest-location
'(:eax
:ebx
:ecx
:edx
))
3555 (install-for-single-value binding binding-location dest-location nil
))
3556 (t #+ignore
(warn "binding => binding: ~A => ~A~% => ~A ~A"
3561 (append (install-for-single-value binding binding-location
:eax nil
)
3562 (make-store-lexical result-mode
:eax nil funobj frame-map
))))))
3563 (t (make-result-and-returns-glue
3565 (install-for-single-value binding binding-location
:eax nil
)))
3568 (defun make-store-lexical (binding source shared-reference-p funobj frame-map
3569 &key protect-registers
)
3570 (let ((binding (ensure-local-binding binding funobj
)))
3571 (assert (not (and shared-reference-p
3572 (not (binding-lended-p binding
))))
3574 "funny binding: ~W" binding
)
3575 (if (and nil
(typep source
'constant-object-binding
))
3576 (make-load-constant (constant-object source
) binding funobj frame-map
)
3577 (let ((protect-registers (cons source protect-registers
)))
3579 ((eq :untagged-fixnum-ecx source
)
3580 (if (eq :untagged-fixnum-ecx
3581 (new-binding-location binding frame-map
))
3583 (append (make-result-and-returns-glue :ecx
:untagged-fixnum-ecx
)
3584 (make-store-lexical binding
:ecx shared-reference-p funobj frame-map
3585 :protect-registers protect-registers
))))
3586 ((typep binding
'borrowed-binding
)
3587 (let ((slot (borrowed-binding-reference-slot binding
)))
3588 (if (not shared-reference-p
)
3589 (let ((tmp-reg (chose-free-register protect-registers
)
3590 #+ignore
(if (eq source
:eax
) :ebx
:eax
)))
3591 (when (eq :ecx source
)
3592 (break "loading a word from ECX?"))
3593 `((:movl
(:esi
,(+ (slot-offset 'movitz-funobj
'constant0
) (* 4 slot
)))
3595 (:movl
,source
(-1 ,tmp-reg
))))
3596 `((:movl
,source
(:esi
,(+ (slot-offset 'movitz-funobj
'constant0
) (* 4 slot
))))))))
3597 ((typep binding
'forwarding-binding
)
3598 (assert (not (binding-lended-p binding
)) (binding))
3599 (make-store-lexical (forwarding-binding-target binding
)
3600 source shared-reference-p funobj frame-map
))
3601 ((not (new-binding-located-p binding frame-map
))
3602 ;; (warn "Can't store to unlocated binding ~S." binding)
3604 ((and (binding-lended-p binding
)
3605 (not shared-reference-p
))
3606 (let ((tmp-reg (chose-free-register protect-registers
)
3607 #+ignore
(if (eq source
:eax
) :ebx
:eax
))
3608 (location (new-binding-location binding frame-map
)))
3609 (if (integerp location
)
3610 `((:movl
(:ebp
,(stack-frame-offset location
)) ,tmp-reg
)
3611 (:movl
,source
(,tmp-reg -
1)))
3612 (ecase (operator location
)
3614 (assert (<= 2 (function-argument-argnum binding
)) ()
3615 "store-lexical argnum can't be ~A." (function-argument-argnum binding
))
3616 `((:movl
(:ebp
,(argument-stack-offset binding
)) ,tmp-reg
)
3617 (:movl
,source
(,tmp-reg -
1))))))))
3618 (t (let ((location (new-binding-location binding frame-map
)))
3620 ((member source
'(:eax
:ebx
:ecx
:edx
:edi
:esp
))
3621 (if (integerp location
)
3622 `((:movl
,source
(:ebp
,(stack-frame-offset location
))))
3623 (ecase (operator location
)
3625 `((:pushl
,source
)))
3626 ((:eax
:ebx
:ecx
:edx
)
3627 (unless (eq source location
)
3628 `((:movl
,source
,location
))))
3630 (assert (<= 2 (function-argument-argnum binding
)) ()
3631 "store-lexical argnum can't be ~A." (function-argument-argnum binding
))
3632 `((:movl
,source
(:ebp
,(argument-stack-offset binding
)))))
3633 (:untagged-fixnum-ecx
3634 (assert (not (eq source
:edi
)))
3636 ((eq source
:untagged-fixnum-ecx
)
3639 `((,*compiler-global-segment-prefix
*
3640 :call
(:edi
,(global-constant-offset 'unbox-u32
)))))
3641 (t `((:movl
,source
:eax
)
3642 (,*compiler-global-segment-prefix
*
3643 :call
(:edi
,(global-constant-offset 'unbox-u32
))))))))))
3644 ((eq source
:boolean-cf
=1)
3645 (let ((tmp (chose-free-register protect-registers
)))
3647 (,*compiler-local-segment-prefix
*
3648 :movl
(:edi
(:ecx
4) ,(global-constant-offset 'not-not-nil
)) ,tmp
)
3649 ,@(make-store-lexical binding tmp shared-reference-p funobj frame-map
3650 :protect-registers protect-registers
))))
3651 ((eq source
:boolean-cf
=0)
3652 (let ((tmp (chose-free-register protect-registers
)))
3654 (,*compiler-local-segment-prefix
*
3655 :movl
(:edi
(:ecx
4) ,(global-constant-offset 'boolean-zero
)) ,tmp
)
3656 ,@(make-store-lexical binding tmp shared-reference-p funobj frame-map
3657 :protect-registers protect-registers
))))
3658 ((and *compiler-use-cmov-p
*
3659 (member source
+boolean-modes
+))
3660 (let ((tmp (chose-free-register protect-registers
)))
3661 (append `((:movl
:edi
,tmp
))
3662 (list (cons *compiler-local-segment-prefix
*
3663 (make-cmov-on-boolean source
3664 `(:edi
,(global-constant-offset 't-symbol
))
3666 (make-store-lexical binding tmp shared-reference-p funobj frame-map
3667 :protect-registers protect-registers
))))
3668 ((member source
+boolean-modes
+)
3669 (let ((tmp (chose-free-register protect-registers
))
3670 (label (gensym "store-lexical-bool-")))
3671 (append `((:movl
:edi
,tmp
))
3672 (list (make-branch-on-boolean source label
:invert t
))
3673 `((,*compiler-local-segment-prefix
*
3674 :movl
(:edi
,(global-constant-offset 't-symbol
)) ,tmp
))
3676 (make-store-lexical binding tmp shared-reference-p funobj frame-map
3677 :protect-registers protect-registers
))))
3678 ((not (bindingp source
))
3679 (error "Unknown source for store-lexical: ~S" source
))
3680 ((binding-singleton source
)
3681 (assert (not shared-reference-p
))
3682 (let ((value (car (binding-singleton source
))))
3685 (let ((immediate (movitz-immediate-value value
)))
3686 (if (integerp location
)
3687 (let ((tmp (chose-free-register protect-registers
)))
3688 (append (make-immediate-move immediate tmp
)
3689 `((:movl
,tmp
(:ebp
,(stack-frame-offset location
))))))
3690 #+ignore
(if (= 0 immediate
)
3691 (let ((tmp (chose-free-register protect-registers
)))
3693 (:movl
,tmp
(:ebp
,(stack-frame-offset location
)))))
3694 `((:movl
,immediate
(:ebp
,(stack-frame-offset location
)))))
3695 (ecase (operator location
)
3697 `((:movl
,immediate
(:ebp
,(argument-stack-offset binding
)))))
3698 ((:eax
:ebx
:ecx
:edx
)
3699 (make-immediate-move immediate location
))
3700 ((:untagged-fixnum-ecx
)
3701 (make-immediate-move (movitz-fixnum-value value
) :ecx
))))))
3703 (let ((immediate (movitz-immediate-value value
)))
3704 (if (integerp location
)
3705 (let ((tmp (chose-free-register protect-registers
)))
3706 (append (make-immediate-move immediate tmp
)
3707 `((:movl
,tmp
(:ebp
,(stack-frame-offset location
))))))
3708 (ecase (operator location
)
3710 `((:movl
,immediate
(:ebp
,(argument-stack-offset binding
)))))
3711 ((:eax
:ebx
:ecx
:edx
)
3712 (make-immediate-move immediate location
))))))
3715 ((member :eax
:ebx
:edx
)
3716 (make-load-constant value location funobj frame-map
))
3718 (let ((tmp (chose-free-register protect-registers
)))
3719 (append (make-load-constant value tmp funobj frame-map
)
3720 (make-store-lexical binding tmp shared-reference-p
3722 :protect-registers protect-registers
))))
3723 ((eql :untagged-fixnum-ecx
)
3724 (check-type value movitz-bignum
)
3725 (let ((immediate (movitz-bignum-value value
)))
3726 (check-type immediate
(unsigned-byte 32))
3727 (make-immediate-move immediate
:ecx
)))
3729 (t (error "Generalized lexb source for store-lexical not implemented: ~S" source
))))))))))
3731 (defun finalize-code (code funobj frame-map
)
3732 ;; (print-code 'to-be-finalized code)
3733 ;; (warn "frame-map: ~A" frame-map)
3734 (labels ((actual-binding (b)
3735 (if (typep b
'borrowed-binding
)
3736 (borrowed-binding-target b
)
3738 (make-lend-lexical (borrowing-binding funobj-register dynamic-extent-p
)
3739 (let ((lended-binding (ensure-local-binding
3740 (borrowed-binding-target borrowing-binding
))))
3741 #+ignore
(warn "LB: in ~S ~S from ~S"
3743 lended-binding borrowing-binding
)
3744 (assert (eq funobj
(binding-funobj lended-binding
)))
3745 (assert (plusp (getf (binding-lending (actual-binding lended-binding
))
3746 :lended-count
0)) ()
3747 "Asked to lend ~S of ~S to ~S of ~S with no lended-count."
3748 lended-binding
(binding-env lended-binding
)
3749 borrowing-binding
(binding-env borrowing-binding
))
3750 (assert (eq funobj-register
:edx
))
3751 (when (getf (binding-lending lended-binding
) :dynamic-extent-p
)
3752 (assert dynamic-extent-p
))
3754 (warn "lending: ~W: ~S"
3756 (mapcar #'movitz-funobj-extent
3757 (mapcar #'binding-funobj
3758 (getf (binding-lending lended-binding
) :lended-to
))))
3759 (append (make-load-lexical lended-binding
:eax funobj t frame-map
)
3760 (unless (or (typep lended-binding
'borrowed-binding
)
3761 (getf (binding-lending lended-binding
) :dynamic-extent-p
)
3762 (every (lambda (borrower)
3763 (member (movitz-funobj-extent (binding-funobj borrower
))
3764 '(:lexical-extent
:dynamic-extent
)))
3765 (getf (binding-lending lended-binding
) :lended-to
)))
3766 (append `((:pushl
:edx
)
3767 (:globally
(:call
(:edi
(:edi-offset ensure-heap-cons-variable
))))
3769 (make-store-lexical lended-binding
:eax t funobj frame-map
)))
3772 ,(+ (slot-offset 'movitz-funobj
'constant0
)
3773 (* 4 (borrowed-binding-reference-slot borrowing-binding
)))))))))
3774 (ensure-local-binding (binding)
3775 (if (eq funobj
(binding-funobj binding
))
3777 (or (find binding
(borrowed-bindings funobj
)
3778 :key
#'borrowed-binding-target
)
3779 (error "Can't install non-local binding ~W." binding
)))))
3780 (labels ((fix-edi-offset (tree)
3784 ((eq :edi-offset
(car tree
))
3785 (check-type (cadr tree
) symbol
"a Movitz run-time-context label")
3786 (+ (global-constant-offset (cadr tree
))
3787 (reduce #'+ (cddr tree
))))
3788 (t (cons (fix-edi-offset (car tree
))
3789 (fix-edi-offset (cdr tree
)))))))
3790 (loop for instruction in code
3795 ((and (= 2 (length instruction
))
3796 (let ((operand (second instruction
)))
3797 (and (listp operand
)
3798 (symbolp (first operand
))
3799 (string= 'quote
(first operand
))
3800 (listp (second operand
)))))
3801 ;;(break "op: ~S" (second (second instruction)))
3802 ;; recurse into program-to-append..
3803 (list (list (first instruction
)
3804 (list 'quote
(finalize-code (second (second instruction
))
3805 funobj frame-map
)))))
3807 (t ;; (warn "finalizing ~S" instruction)
3808 (case (first instruction
)
3809 ((:locally
:globally
)
3810 (destructuring-bind (sub-instr)
3812 (let ((pf (ecase (first instruction
)
3813 (:locally
*compiler-local-segment-prefix
*)
3814 (:globally
*compiler-global-segment-prefix
*))))
3815 (list (fix-edi-offset
3819 ((consp (car sub-instr
))
3820 (list* (append pf
(car sub-instr
))
3822 (t (list* pf sub-instr
))))))))
3823 ((:declare-label-set
3824 :declare-key-arg-set
)
3826 (:local-function-init
3827 (destructuring-bind (function-binding)
3828 (operands instruction
)
3830 (warn "local-function-init: init ~S at ~S"
3832 (new-binding-location function-binding frame-map
))
3834 (let* ((sub-funobj (function-binding-funobj function-binding
)))
3836 ((eq (movitz-funobj-extent sub-funobj
) :unused
)
3837 (unless (or (movitz-env-get (binding-name function-binding
)
3839 (binding-env function-binding
) nil
)
3840 (movitz-env-get (binding-name function-binding
)
3842 (binding-env function-binding
) nil
))
3843 (warn "Unused local function: ~S"
3844 (binding-name function-binding
)))
3846 ((typep function-binding
'funobj-binding
)
3849 ((member (movitz-funobj-extent sub-funobj
)
3850 '(:dynamic-extent
:lexical-extent
))
3851 (check-type function-binding closure-binding
)
3852 (when (plusp (movitz-funobj-num-jumpers sub-funobj
))
3853 (break "Don't know yet how to stack a funobj with jumpers."))
3854 (let ((words (+ (movitz-funobj-num-constants sub-funobj
)
3855 (/ (sizeof 'movitz-funobj
) 4))))
3856 (break "words for ~S: ~S" words sub-funobj
)
3857 (append `((:movl
:esp
:eax
)
3859 (:jz
'no-alignment-needed
)
3861 no-alignment-needed
)
3862 (make-load-constant sub-funobj
:eax funobj frame-map
)
3864 (t (assert (not (null (borrowed-bindings sub-funobj
))))
3865 (append (make-load-constant sub-funobj
:eax funobj frame-map
)
3866 `((:movl
(:edi
,(global-constant-offset 'copy-funobj
)) :esi
)
3867 (:call
(:esi
,(bt:slot-offset
'movitz-funobj
'code-vector%
1op
)))
3869 (make-store-lexical function-binding
:eax nil funobj frame-map
)
3870 (loop for bb in
(borrowed-bindings sub-funobj
)
3871 append
(make-lend-lexical bb
:edx nil
))))))
3874 (destructuring-bind (function-binding register capture-env
)
3875 (operands instruction
)
3876 (declare (ignore capture-env
))
3878 (let* ((sub-funobj (function-binding-funobj function-binding
))
3879 (lend-code (loop for bb in
(borrowed-bindings sub-funobj
)
3881 (make-lend-lexical bb
:edx nil
))))
3884 ;; (warn "null lambda lending")
3885 (append (make-load-constant sub-funobj register funobj frame-map
)))
3886 ((typep (movitz-allocation sub-funobj
)
3887 'with-dynamic-extent-scope-env
)
3888 (setf (headers-on-stack-frame-p funobj
) t
)
3889 (let ((dynamic-scope (movitz-allocation sub-funobj
)))
3890 (append (make-load-lexical (base-binding dynamic-scope
) :edx
3891 funobj nil frame-map
)
3892 `((:leal
(:edx
,(tag :other
)
3893 ,(dynamic-extent-object-offset dynamic-scope
3897 `((:movl
:edx
,register
)))))
3898 (t (append (make-load-constant sub-funobj
:eax funobj frame-map
)
3899 `((:movl
(:edi
,(global-constant-offset 'copy-funobj
)) :esi
)
3900 (:call
(:esi
,(bt:slot-offset
'movitz-funobj
'code-vector%
1op
)))
3903 `((:movl
:edx
,register
))))))
3906 (destructuring-bind (object result-mode
&key
(op :movl
))
3908 (make-load-constant object result-mode funobj frame-map
:op op
)))
3909 (:lexical-control-transfer
3910 (destructuring-bind (return-code return-mode from-env to-env
&optional to-label
)
3912 (declare (ignore return-code
))
3913 (let ((x (apply #'make-compiled-lexical-control-transfer
3915 return-mode from-env to-env
3916 (when to-label
(list to-label
)))))
3917 (finalize-code x funobj frame-map
))))
3919 (destructuring-bind (binding num-args
)
3920 (operands instruction
)
3921 (append (etypecase binding
3923 (make-load-lexical (ensure-local-binding binding
)
3924 :esi funobj nil frame-map
3925 :tmp-register
:edx
))
3927 (make-load-constant (function-binding-funobj binding
)
3928 :esi funobj frame-map
)))
3929 (make-compiled-funcall-by-esi num-args
))))
3930 (t (expand-extended-code instruction funobj frame-map
)))))))))
3933 (defun image-t-symbol-p (x)
3934 (eq x
(image-t-symbol *image
*)))
3936 (deftype movitz-t
()
3937 `(satisfies image-t-symbol-p
))
3939 (defun make-load-constant (object result-mode funobj frame-map
&key
(op :movl
))
3940 (let ((movitz-obj (movitz-read object
)))
3943 (etypecase movitz-obj
3945 (ecase (result-mode-type result-mode
)
3947 (make-store-lexical result-mode
:edi nil funobj frame-map
))
3950 ((:eax
:ebx
:ecx
:edx
)
3951 `((:movl
:edi
,result-mode
)))
3952 (:boolean-branch-on-true
3953 ;; (warn "branch-on-true for nil!")
3955 (:boolean-branch-on-false
3956 ;; (warn "branch-on-false for nil!")
3957 `((:jmp
',(operands result-mode
))))
3958 ((:multiple-values
:function
)
3962 (t (when (eq :boolean result-mode
)
3963 (warn "Compiling ~S for mode ~S." object result-mode
))
3964 (make-result-and-returns-glue result-mode
:edi nil
)
3965 #+ignore
'((:movl
:edi
:eax
)))))
3967 (ecase (result-mode-type result-mode
)
3969 `((:pushl
(:edi
,(global-constant-offset 't-symbol
)))))
3970 ((:eax
:ebx
:ecx
:edx
)
3971 `((:movl
(:edi
,(global-constant-offset 't-symbol
)) ,result-mode
)))
3972 (:boolean-branch-on-false
3973 ;; (warn "boolean-branch-on-false T")
3975 (:boolean-branch-on-true
3976 ;; (warn "boolean-branch-on-true T")
3977 `((:jmp
',(operands result-mode
))))
3978 ((:multiple-values
:function
)
3979 `((:movl
(:edi
,(global-constant-offset 't-symbol
))
3983 (append `((:movl
(:edi
,(global-constant-offset 't-symbol
))
3985 (make-store-lexical result-mode
:eax nil funobj frame-map
)))
3987 (t (when (eq :boolean result-mode
)
3988 (warn "Compiling ~S for mode ~S." object result-mode
))
3989 (make-result-and-returns-glue result-mode
:eax
3990 `((:movl
(:edi
,(global-constant-offset 't-symbol
))
3992 (movitz-immediate-object
3993 (let ((x (movitz-immediate-value movitz-obj
)))
3994 (ecase (result-mode-type result-mode
)
3996 (append (make-immediate-move x
:eax
)
3997 (make-store-lexical result-mode
:eax nil funobj frame-map
)))
3998 (:untagged-fixnum-ecx
3999 (let ((value (movitz-fixnum-value object
)))
4000 (check-type value
(unsigned-byte 32))
4001 (make-immediate-move value
:ecx
)))
4004 ((:eax
:ebx
:ecx
:edx
)
4005 (make-immediate-move x result-mode
))
4006 ((:multiple-values
:function
)
4007 (append (make-immediate-move x
:eax
)
4010 (ecase (result-mode-type result-mode
)
4011 (:untagged-fixnum-ecx
4012 (let ((value (movitz-bignum-value object
)))
4013 (make-immediate-move (ldb (byte 32 0) value
) :ecx
)))
4016 ((and (typep movitz-obj
'movitz-bignum
)
4017 (eq :untagged-fixnum-ecx
4018 (new-binding-location result-mode frame-map
:default nil
)))
4019 (unless (typep (movitz-bignum-value movitz-obj
) '(unsigned-byte 32))
4020 (warn "Loading non-u32 ~S into ~S."
4021 (movitz-bignum-value movitz-obj
)
4023 (make-immediate-move (ldb (byte 32 0) (movitz-bignum-value movitz-obj
))
4025 (t (when (member (new-binding-location result-mode frame-map
:default nil
)
4026 '(:ebx
:ecx
:edx
:esi
))
4027 (warn "load to ~S at ~S from ~S"
4028 result-mode
(new-binding-location result-mode frame-map
) movitz-obj
))
4029 (append `((:movl
,(new-make-compiled-constant-reference movitz-obj funobj
)
4031 (make-store-lexical result-mode
:eax nil funobj frame-map
)))))
4033 `((:pushl
,(new-make-compiled-constant-reference movitz-obj funobj
))))
4034 ((:eax
:ebx
:ecx
:edx
:esi
)
4035 `((,op
,(new-make-compiled-constant-reference movitz-obj funobj
)
4038 (assert (eq op
:cmpl
))
4039 `((,op
,(new-make-compiled-constant-reference movitz-obj funobj
)
4041 ((:function
:multiple-values
)
4042 (assert (eq op
:movl
))
4043 `((,op
,(new-make-compiled-constant-reference movitz-obj funobj
)
4046 (t (ecase result-mode
4047 ((:eax
:ebx
:ecx
:edx
:esi
)
4048 `((,op
,(new-make-compiled-constant-reference movitz-obj funobj
)
4051 (assert (eq op
:cmpl
))
4052 `((,op
,(new-make-compiled-constant-reference movitz-obj funobj
)
4053 ,result-mode
))))))))
4055 (defparameter +movitz-lambda-list-keywords
+
4056 '(muerte.cl
:&OPTIONAL
4062 muerte.cl
:&ALLOW-OTHER-KEYS
4063 muerte.cl
:&ENVIRONMENT
))
4065 (defun add-bindings-from-lambda-list (lambda-list env
)
4066 "From a (normal) <lambda-list>, add bindings to <env>."
4068 (multiple-value-bind (required-vars optional-vars rest-var key-vars auxes allow-p min-args max-args edx-var oddeven key-vars-p
)
4069 (decode-normal-lambda-list lambda-list
)
4070 (setf (min-args env
) min-args
4071 (max-args env
) max-args
4072 (oddeven-args env
) oddeven
4073 (aux-vars env
) auxes
4074 (allow-other-keys-p env
) allow-p
)
4075 (flet ((shadow-when-special (formal env
)
4076 "Iff <formal> is special, return a fresh variable-name that takes <formal>'s place
4077 as the lexical variable-name, and add a new shadowing dynamic binding for <formal> in <env>."
4078 (if (not (movitz-env-get formal
'special nil env
))
4080 (let* ((shadowed-formal (gensym (format nil
"shady-~A-" formal
)))
4081 (shadowing-binding (make-instance 'shadowing-dynamic-binding
4082 :name shadowed-formal
4083 :shadowing-variable formal
4084 :shadowed-variable shadowed-formal
)))
4085 (movitz-env-add-binding env shadowing-binding formal
)
4086 (push (list formal shadowed-formal
)
4087 (special-variable-shadows env
))
4090 (movitz-env-add-binding env
4092 (make-instance 'edx-function-argument
4094 (setf (required-vars env
)
4095 (loop for formal in required-vars
4096 do
(check-type formal symbol
)
4098 (shadow-when-special formal env
))
4099 do
(movitz-env-add-binding env
(cond
4101 (make-instance 'register-required-function-argument
4104 ((and max-args
(= min-args max-args
))
4105 (make-instance 'fixed-required-function-argument
4109 (t (make-instance 'floating-required-function-argument
4114 (setf (optional-vars env
)
4115 (loop for spec in optional-vars
4117 (multiple-value-bind (formal init-form supplied-p-parameter
)
4118 (decode-optional-formal spec
)
4119 (setf formal
(shadow-when-special formal env
))
4120 (movitz-env-add-binding env
(make-instance 'optional-function-argument
4122 :argnum
(post-incf arg-pos
)
4123 'init-form init-form
4124 'supplied-p-var supplied-p-parameter
))
4125 (when supplied-p-parameter
4126 (setf supplied-p-parameter
4127 (shadow-when-special supplied-p-parameter env
))
4128 (movitz-env-add-binding env
(make-instance 'supplied-p-function-argument
4129 :name supplied-p-parameter
)))
4131 (when (or rest-var key-vars-p
)
4132 (setf (rest-args-position env
) arg-pos
))
4134 (check-type rest-var symbol
)
4135 (let ((formal (shadow-when-special rest-var env
)))
4136 (setf (rest-var env
) formal
)
4137 (movitz-env-add-binding env
(make-instance 'rest-function-argument
4139 :argnum
(post-incf arg-pos
)))))
4141 (setf (key-vars-p env
) t
)
4142 (when (>= 1 (rest-args-position env
))
4143 (let ((name (gensym "save-ebx-for-keyscan")))
4144 (setf (required-vars env
)
4145 (append (required-vars env
)
4147 (movitz-env-add-binding env
(make-instance 'register-required-function-argument
4150 :declarations
'(muerte.cl
:ignore
)))
4151 (setf (movitz-env-get name
'ignore nil env
) t
)))
4152 (when (= 0 (rest-args-position env
))
4153 (let ((name (gensym "save-eax-for-keyscan")))
4154 (push name
(required-vars env
))
4155 (movitz-env-add-binding env
(make-instance 'register-required-function-argument
4158 (setf (movitz-env-get name
'ignore nil env
) t
))))
4159 (setf (key-vars env
)
4160 (loop for spec in key-vars
4162 (multiple-value-bind (formal keyword-name init-form supplied-p
)
4163 (decode-keyword-formal spec
)
4164 (let ((formal (shadow-when-special formal env
))
4165 (supplied-p-parameter supplied-p
))
4166 (movitz-env-add-binding env
(make-instance 'keyword-function-argument
4168 'init-form init-form
4169 'supplied-p-var supplied-p-parameter
4170 :keyword-name keyword-name
))
4171 (when supplied-p-parameter
4172 (movitz-env-add-binding env
(make-instance 'supplied-p-function-argument
4173 :name
(shadow-when-special supplied-p-parameter env
))))
4176 (multiple-value-bind (key-decode-map key-decode-shift
)
4177 (best-key-encode (key-vars env
))
4178 (setf (key-decode-map env
) key-decode-map
4179 (key-decode-shift env
) key-decode-shift
))
4182 (warn "~D waste, keys: ~S, shift ~D, map: ~S"
4183 (- (length (key-decode-map env
))
4186 (key-decode-shift env
)
4187 (key-decode-map env
))))))
4190 (defun make-compiled-function-prelude-numarg-check (min-args max-args
)
4191 "The prelude is compiled after the function's body."
4192 (assert (or (not max-args
) (<= 0 min-args max-args
)))
4193 (assert (<= 0 min-args
(or max-args min-args
) #xffff
) ()
4194 "Lambda lists longer than #xffff are not yet implemented.")
4195 (let ((wrong-numargs (make-symbol "wrong-numargs")))
4197 ((and (zerop min-args
) ; any number of arguments is
4198 (not max-args
)) ; acceptable, no check necessary.
4202 (if (< min-args
#x80
)
4203 `((:cmpb
,min-args
:cl
)
4204 (:jb
'(:sub-program
(,wrong-numargs
) (:int
100))))
4205 `((:cmpl
,(dpb min-args
(byte 24 8) #x80
) :ecx
)
4206 (:jb
'(:sub-program
(,wrong-numargs
) (:int
100))))))
4207 ((and max-args
(= 0 min-args max-args
))
4210 (:jnz
'(:sub-program
(,wrong-numargs
) (:int
100)))))
4211 ((and max-args
(= min-args max-args
))
4214 ((= 1 min-args max-args
)
4215 `((:call
(:edi
,(global-constant-offset 'assert-1arg
)))))
4216 ((= 2 min-args max-args
)
4217 `((:call
(:edi
,(global-constant-offset 'assert-2args
)))))
4218 ((= 3 min-args max-args
)
4219 `((:call
(:edi
,(global-constant-offset 'assert-3args
)))))
4221 `((:cmpb
,min-args
:cl
)
4222 (:jne
'(:sub-program
(,wrong-numargs
) (:int
100)))))
4223 (t `((:cmpl
,(dpb min-args
(byte 24 8) #x80
) :ecx
)
4224 (:jne
'(:sub-program
(,wrong-numargs
) (:int
100)))))))
4225 ((and max-args
(/= min-args max-args
) (= 0 min-args
))
4227 (if (< max-args
#x80
)
4228 `((:cmpb
,max-args
:cl
)
4229 (:ja
'(:sub-program
(,wrong-numargs
) (:int
100))))
4230 `((:cmpl
,(dpb max-args
(byte 24 8) #x80
) :ecx
)
4231 (:ja
'(:sub-program
(,wrong-numargs
) (:int
100))))))
4232 ((and max-args
(/= min-args max-args
))
4234 (append (if (< min-args
#x80
)
4235 `((:cmpb
,min-args
:cl
)
4236 (:jb
'(:sub-program
(,wrong-numargs
) (:int
100))))
4237 `((:cmpl
,(dpb min-args
(byte 24 8) #x80
) :ecx
)
4238 (:jb
'(:sub-program
(,wrong-numargs
) (:int
100)))))
4239 (if (< max-args
#x80
)
4240 `((:cmpb
,max-args
:cl
)
4241 (:ja
'(:sub-program
(,wrong-numargs
) (:int
100))))
4242 `((:cmpl
,(dpb max-args
(byte 24 8) #x80
) :ecx
)
4243 (:ja
'(:sub-program
(,wrong-numargs
) (:int
100)))))))
4244 (t (error "Don't know how to compile checking for ~A to ~A arguments."
4245 min-args max-args
)))))
4247 (defun make-stack-setup-code (stack-setup-size)
4248 (loop repeat stack-setup-size
4249 collect
'(:pushl
:edi
))
4251 (case stack-setup-size
4253 (1 '((:pushl
:edi
)))
4254 (2 '((:pushl
:edi
) (:pushl
:edi
)))
4255 (3 '((:pushl
:edi
) (:pushl
:edi
) (:pushl
:edi
)))
4256 (t `((:subl
,(* 4 stack-setup-size
) :esp
)))))
4258 (defun make-compiled-function-prelude (stack-frame-size env use-stack-frame-p
4259 need-normalized-ecx-p frame-map
4260 &key do-check-stack-p
)
4261 "The prelude is compiled after the function's body is."
4262 (when (without-function-prelude-p env
)
4263 (return-from make-compiled-function-prelude
4264 (when use-stack-frame-p
4268 (let ((required-vars (required-vars env
))
4269 (min-args (min-args env
))
4270 (max-args (max-args env
)))
4271 (let ((stack-setup-size stack-frame-size
)
4272 (edx-needs-saving-p (and (edx-var env
)
4273 (new-binding-location (edx-var env
) frame-map
:default nil
))))
4274 (multiple-value-bind (eax-ebx-code eax-ebx-code-post-stackframe
)
4275 (let* ((map0 (find-if (lambda (bb)
4276 (and (typep (car bb
) '(or required-function-argument
4277 optional-function-argument
))
4278 (= 0 (function-argument-argnum (car bb
)))))
4280 (location-0 (cdr map0
))
4281 (map1 (find-if (lambda (bb)
4282 (and (typep (car bb
) '(or required-function-argument
4283 optional-function-argument
))
4284 (= 1 (function-argument-argnum (car bb
)))))
4286 (location-1 (cdr map1
))
4289 (new-binding-location (edx-var env
) frame-map
:default nil
))))
4290 #+ignore
(warn "l0: ~S, l1: ~S" location-0 location-1
)
4291 (assert (not (and location-0
4292 (eql location-0 location-1
))) ()
4293 "Compiler bug: two bindings in same location.")
4295 ((and (eq :ebx location-0
) (eq :eax location-1
))
4296 `((:xchgl
:eax
:ebx
)))
4297 ((and (eql 1 location-0
) (eql 2 location-1
))
4298 (decf stack-setup-size
2)
4299 (when (eql 3 edx-location
)
4300 (decf stack-setup-size
1)
4301 (setf edx-needs-saving-p nil
))
4302 (let (before-code after-code
)
4307 (when (eql 3 edx-location
)
4309 ;; Keep pushing any sequentially following floating requireds.
4310 ;; NB: Fixed-floats are used in-place, e.g above the stack-frame,
4311 ;; so no need to worry about them.
4312 (loop with expected-location
= 2
4313 for var in
(cddr required-vars
)
4314 as binding
= (movitz-binding var env
)
4315 if
(and expected-location
4316 (typep binding
'floating-required-function-argument
)
4317 (new-binding-located-p binding frame-map
)
4318 (= expected-location
4319 (new-binding-location binding frame-map
)))
4320 do
(decf stack-setup-size
)
4321 and do
(incf expected-location
)
4322 and do
(setq need-normalized-ecx-p t
)
4324 `(:pushl
(:ebp
(:ecx
4)
4325 ,(* -
4 (1- (function-argument-argnum binding
)))))
4326 else do
(setf expected-location nil
)
4327 and do
(when (and (typep binding
'floating-required-function-argument
)
4328 (new-binding-located-p binding frame-map
))
4329 (setq need-normalized-ecx-p t
)
4333 `((:movl
(:ebp
(:ecx
4)
4334 ,(* -
4 (1- (function-argument-argnum binding
))))
4336 (:movl
:edx
(:ebp
,(stack-frame-offset
4337 (new-binding-location binding frame-map
)))))))))))
4338 (values before-code after-code
)))
4341 ((and (eq :ebx location-0
)
4343 (decf stack-setup-size
)
4345 (:xchgl
:eax
:ebx
)))
4346 ((and (eq :ebx location-0
)
4347 (eq :edx location-1
))
4353 (decf stack-setup-size
)
4355 (t (ecase location-0
4357 (:ebx
(assert (not location-1
))
4358 '((:movl
:eax
:ebx
)))
4359 (:edx
(assert (not edx-location
))
4360 '((:movl
:eax
:edx
))))))
4363 (decf stack-setup-size
)
4365 (t (ecase location-1
4367 (:edx
'((:movl
:ebx
:edx
)))
4368 (:eax
`((:movl
:ebx
:eax
)))))))))
4370 ((or (and (or (eql 1 location-0
)
4372 (eql 2 edx-location
))
4373 (and (not (integerp location-0
))
4374 (not (integerp location-1
))
4375 (eql 1 edx-location
)))
4376 (decf stack-setup-size
)
4377 (setf edx-needs-saving-p nil
)
4379 (loop for var in
(cddr required-vars
)
4380 as binding
= (movitz-binding var env
)
4381 when
(and (typep binding
'floating-required-function-argument
)
4382 (new-binding-located-p binding frame-map
))
4384 `((:movl
(:ebp
(:ecx
4)
4385 ,(* -
4 (1- (function-argument-argnum binding
))))
4387 (:movl
:edx
(:ebp
,(stack-frame-offset
4388 (new-binding-location binding frame-map
)))))
4390 (setq need-normalized-ecx-p t
))))))
4391 (assert (not (minusp stack-setup-size
)))
4392 (let ((stack-frame-init-code
4393 (append (when (and do-check-stack-p use-stack-frame-p
4394 *compiler-auto-stack-checks-p
*
4395 (not (without-check-stack-limit-p env
)))
4396 `((,*compiler-local-segment-prefix
*
4397 :bound
(:edi
,(global-constant-offset 'stack-bottom
)) :esp
)))
4398 (when use-stack-frame-p
4405 ((and (eql 1 min-args
)
4407 (append (make-compiled-function-prelude-numarg-check min-args max-args
)
4409 stack-frame-init-code
))
4410 ((and (eql 2 min-args
)
4412 (append (make-compiled-function-prelude-numarg-check min-args max-args
)
4414 stack-frame-init-code
))
4415 ((and (eql 3 min-args
)
4417 (append (make-compiled-function-prelude-numarg-check min-args max-args
)
4419 stack-frame-init-code
))
4420 (t (append stack-frame-init-code
4421 (make-compiled-function-prelude-numarg-check min-args max-args
))))
4422 '(start-stack-frame-setup)
4424 (make-stack-setup-code stack-setup-size
)
4425 (when need-normalized-ecx-p
4427 ;; normalize arg-count in ecx..
4428 ((and max-args
(= min-args max-args
))
4430 ((and max-args
(<= 0 min-args max-args
#x7f
))
4431 `((:andl
#x7f
:ecx
)))
4434 (t (let ((normalize (make-symbol "normalize-ecx"))
4435 (normalize-done (make-symbol "normalize-ecx-done")))
4437 (:js
'(:sub-program
(,normalize
)
4439 (:jmp
',normalize-done
)))
4441 ,normalize-done
))))))
4442 (when edx-needs-saving-p
4443 `((:movl
:edx
(:ebp
,(stack-frame-offset (new-binding-location (edx-var env
) frame-map
))))))
4444 eax-ebx-code-post-stackframe
4445 (loop for binding in
(potentially-lended-bindings env
)
4446 as lended-cons-position
= (getf (binding-lending binding
) :stack-cons-location
)
4447 as location
= (new-binding-location binding frame-map
:default nil
)
4448 when
(and (not (typep binding
'borrowed-binding
))
4449 lended-cons-position
4453 (required-function-argument
4454 ;; (warn "lend: ~W => ~W" binding lended-cons-position)
4455 (etypecase (operator location
)
4457 (warn "lending EAX..")
4459 (:ebp
,(stack-frame-offset lended-cons-position
))) ; cdr
4461 (:ebp
,(stack-frame-offset (1+ lended-cons-position
)))) ; car
4462 (:leal
(:ebp
1 ,(stack-frame-offset (1+ lended-cons-position
)))
4464 ((eql :argument-stack
)
4465 `((:movl
(:ebp
,(argument-stack-offset binding
)) :edx
)
4467 (:ebp
,(stack-frame-offset lended-cons-position
))) ; cdr
4469 (:ebp
,(stack-frame-offset (1+ lended-cons-position
)))) ; car
4470 (:leal
(:ebp
1 ,(stack-frame-offset (1+ lended-cons-position
)))
4473 (:ebp
,(argument-stack-offset binding
)))))
4475 `((:movl
(:ebp
,(stack-frame-offset location
))
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
)))
4484 (:ebp
,(stack-frame-offset location
)))))))
4486 ;; (warn "lend closure-binding: ~W => ~W" binding lended-cons-position)
4487 (etypecase (operator location
)
4488 ((eql :argument-stack
)
4489 `((:movl
(:edi
,(global-constant-offset 'unbound-function
)) :edx
)
4490 (:movl
:edi
(:ebp
,(stack-frame-offset lended-cons-position
))) ; cdr
4491 (:movl
:edx
(:ebp
,(stack-frame-offset (1+ lended-cons-position
)))) ; car
4492 (:leal
(:ebp
1 ,(stack-frame-offset (1+ lended-cons-position
))) :edx
)
4493 (:movl
:edx
(:ebp
,(argument-stack-offset binding
)))))
4495 `((:movl
(:edi
,(global-constant-offset 'unbound-function
)) :edx
)
4496 (:movl
:edi
(:ebp
,(stack-frame-offset lended-cons-position
))) ; cdr
4497 (:movl
:edx
(:ebp
,(stack-frame-offset (1+ lended-cons-position
)))) ; car
4498 (:leal
(:ebp
1 ,(stack-frame-offset (1+ lended-cons-position
))) :edx
)
4499 (:movl
:edx
(:ebp
,(stack-frame-offset location
)))))))
4501 (t (etypecase location
4502 ((eql :argument-stack
)
4503 `((:movl
:edi
(:ebp
,(stack-frame-offset lended-cons-position
))) ; cdr
4504 (:movl
:edi
(:ebp
,(stack-frame-offset (1+ lended-cons-position
)))) ; car
4505 (:leal
(:ebp
1 ,(stack-frame-offset (1+ lended-cons-position
))) :edx
)
4506 (:movl
:edx
(:ebp
,(argument-stack-offset binding
)))))
4508 `((:movl
:edi
(:ebp
,(stack-frame-offset lended-cons-position
))) ; cdr
4509 (:movl
:edi
(:ebp
,(stack-frame-offset (1+ lended-cons-position
)))) ; car
4510 (:leal
(:ebp
1 ,(stack-frame-offset (1+ lended-cons-position
))) :edx
)
4511 (:movl
:edx
(:ebp
,(stack-frame-offset location
))))))))))
4512 need-normalized-ecx-p
))))))
4514 (defparameter *restify-stats
* (make-hash-table :test
#'eql
))
4516 (defparameter *ll
* (make-array 20 :initial-element
0))
4517 (defparameter *xx
* (make-array 20))
4519 (defun install-arg-cmp (code have-normalized-ecx-p
)
4522 (if (not (and (listp i
) (eq :arg-cmp
(car i
))))
4524 (let ((arg-count (second i
)))
4526 (have-normalized-ecx-p
4527 `(:cmpl
,arg-count
:ecx
))
4529 `(:cmpb
,arg-count
:cl
))
4530 (t `(:cmpl
,(dpb arg-count
(byte 24 8) #x80
) :ecx
)))))))
4532 (defun make-function-arguments-init (funobj env
)
4533 "The arugments-init is compiled before the function's body is.
4534 Return arg-init-code, need-normalized-ecx-p."
4535 (when (without-function-prelude-p env
)
4536 (return-from make-function-arguments-init
4538 (let ((need-normalized-ecx-p nil
)
4539 (required-vars (required-vars env
))
4540 (optional-vars (optional-vars env
))
4541 (rest-var (rest-var env
))
4542 (key-vars (key-vars env
)))
4545 (loop for optional in optional-vars
4546 as optional-var
= (decode-optional-formal optional
)
4547 as binding
= (movitz-binding optional-var env
)
4548 as last-optional-p
= (and (null key-vars
)
4550 (= 1 (- (+ (length optional-vars
) (length required-vars
))
4551 (function-argument-argnum binding
))))
4552 as supplied-p-var
= (optional-function-argument-supplied-p-var binding
)
4553 as supplied-p-binding
= (movitz-binding supplied-p-var env
)
4554 as not-present-label
= (make-symbol (format nil
"optional-~D-not-present"
4555 (function-argument-argnum binding
)))
4556 and optional-ok-label
= (make-symbol (format nil
"optional-~D-ok"
4557 (function-argument-argnum binding
)))
4558 unless
(movitz-env-get optional-var
'ignore nil env nil
) ; XXX
4561 ((= 0 (function-argument-argnum binding
))
4562 `((:init-lexvar
,binding
:init-with-register
:eax
:init-with-type t
)))
4563 ((= 1 (function-argument-argnum binding
))
4564 `((:init-lexvar
,binding
:init-with-register
:ebx
:init-with-type t
)))
4565 (t `((:init-lexvar
,binding
))))
4566 when supplied-p-binding
4567 append
`((:init-lexvar
,supplied-p-binding
))
4569 (compiler-values-bind (&code init-code-edx
&producer producer
)
4570 (compiler-call #'compile-form
4571 :form
(optional-function-argument-init-form binding
)
4576 ((and (eq 'compile-self-evaluating producer
)
4577 (member (function-argument-argnum binding
) '(0 1)))
4578 ;; The binding is already preset with EAX or EBX.
4579 (check-type binding lexical-binding
)
4581 (when supplied-p-var
4582 `((:load-constant
,(movitz-read t
) :edx
)
4583 (:store-lexical
,supplied-p-binding
:edx
:type
(member t
))))
4584 `((:arg-cmp
,(function-argument-argnum binding
))
4585 (:ja
',optional-ok-label
))
4586 (compiler-call #'compile-form
4587 :form
(optional-function-argument-init-form binding
)
4590 :result-mode binding
)
4591 (when supplied-p-var
4592 `((:store-lexical
,supplied-p-binding
:edi
:type null
)))
4593 `(,optional-ok-label
)))
4594 ((eq 'compile-self-evaluating producer
)
4595 `(,@(when supplied-p-var
4596 `((:store-lexical
,supplied-p-binding
:edi
:type null
)))
4597 ,@(if (optional-function-argument-init-form binding
)
4598 (append init-code-edx
`((:store-lexical
,binding
:edx
:type t
)))
4599 `((:store-lexical
,binding
:edi
:type null
)))
4600 (:arg-cmp
,(function-argument-argnum binding
))
4601 (:jbe
',not-present-label
)
4602 ,@(case (function-argument-argnum binding
)
4603 (0 `((:store-lexical
,binding
:eax
:type t
)))
4604 (1 `((:store-lexical
,binding
:ebx
:type t
)))
4607 `((:movl
(:ebp
,(* 4 (- (1+ (function-argument-argnum binding
))
4608 -
1 (function-argument-argnum binding
))))
4610 (:store-lexical
,binding
:eax
:type t
)))
4611 (t (setq need-normalized-ecx-p t
)
4612 `((:movl
(:ebp
(:ecx
4)
4613 ,(* -
4 (1- (function-argument-argnum binding
))))
4615 (:store-lexical
,binding
:eax
:type t
))))))
4616 ,@(when supplied-p-var
4617 `((:movl
(:edi
,(global-constant-offset 't-symbol
)) :eax
)
4618 (:store-lexical
,supplied-p-binding
:eax
4619 :type
(eql ,(image-t-symbol *image
*)))))
4620 ,not-present-label
))
4621 (t `((:arg-cmp
,(function-argument-argnum binding
))
4622 (:jbe
',not-present-label
)
4623 ,@(when supplied-p-var
4624 `((:movl
(:edi
,(global-constant-offset 't-symbol
)) :eax
)
4625 (:store-lexical
,supplied-p-binding
:eax
4626 :type
(eql ,(image-t-symbol *image
*)))))
4627 ,@(case (function-argument-argnum binding
)
4628 (0 `((:store-lexical
,binding
:eax
:type t
)))
4629 (1 `((:store-lexical
,binding
:ebx
:type t
)))
4632 `((:movl
(:ebp
,(* 4 (- (1+ (function-argument-argnum binding
))
4633 -
1 (function-argument-argnum binding
))))
4635 (:store-lexical
,binding
:eax
:type t
)))
4636 (t (setq need-normalized-ecx-p t
)
4637 `((:movl
(:ebp
(:ecx
4)
4638 ,(* -
4 (1- (function-argument-argnum binding
))))
4640 (:store-lexical
,binding
:eax
:type t
))))))
4641 (:jmp
',optional-ok-label
)
4643 ,@(when supplied-p-var
4644 `((:store-lexical
,supplied-p-binding
:edi
:type null
)))
4645 ,@(when (and (= 0 (function-argument-argnum binding
))
4646 (not last-optional-p
))
4647 `((:pushl
:ebx
))) ; protect ebx
4648 ,@(if (optional-function-argument-init-form binding
)
4649 (append `((:shll
,+movitz-fixnum-shift
+ :ecx
)
4651 (when (= 0 (function-argument-argnum binding
))
4654 `((:store-lexical
,binding
:edx
:type t
))
4655 (when (= 0 (function-argument-argnum binding
))
4658 (:shrl
,+movitz-fixnum-shift
+ :ecx
)))
4659 (progn (error "Unsupported situation.")
4660 #+ignore
`((:store-lexical
,binding
:edi
:type null
))))
4661 ,@(when (and (= 0 (function-argument-argnum binding
))
4662 (not last-optional-p
))
4663 `((:popl
:ebx
))) ; protect ebx
4664 ,optional-ok-label
)))))
4666 (let* ((rest-binding (movitz-binding rest-var env
)))
4667 `((:init-lexvar
,rest-binding
4668 :init-with-register
:edx
4669 :init-with-type list
))))
4671 (play-with-keys key-vars
))
4672 (when (key-vars-p env
)
4673 ;; &key processing..
4674 (setq need-normalized-ecx-p t
)
4676 `((:declare-key-arg-set
,@(mapcar (lambda (k)
4678 (keyword-function-argument-keyword-name
4679 (movitz-binding (decode-keyword-formal k
) env
))))
4681 (make-immediate-move (* +movitz-fixnum-factor
+
4682 (rest-args-position env
))
4684 `((:call
(:edi
,(global-constant-offset 'decode-keyargs-default
))))
4685 (unless (allow-other-keys-p env
)
4686 `((:testl
:eax
:eax
)
4687 (:jnz
'(:sub-program
(unknown-keyword)
4689 (loop for key-var in key-vars
4690 as key-location upfrom
3 by
2
4692 (decode-keyword-formal key-var
)
4694 (movitz-binding key-var-name env
)
4695 as supplied-p-binding
=
4696 (when (optional-function-argument-supplied-p-var binding
)
4697 (movitz-binding (optional-function-argument-supplied-p-var binding
)
4699 as keyword-ok-label
= (make-symbol (format nil
"keyword-~A-ok" key-var-name
))
4701 ;; (not (movitz-constantp (optional-function-argument-init-form binding)))
4703 (append `((:init-lexvar
,binding
4704 :init-with-register
,binding
4706 :shared-reference-p t
))
4707 (when supplied-p-binding
4708 `((:init-lexvar
,supplied-p-binding
4709 :init-with-register
,supplied-p-binding
4711 :shared-reference-p t
)))
4712 (when (optional-function-argument-init-form binding
)
4713 `((:cmpl
:edi
(:ebp
,(stack-frame-offset (1+ key-location
))))
4714 (:jne
',keyword-ok-label
)
4715 ,@(compiler-call #'compile-form
4716 :form
(optional-function-argument-init-form binding
)
4719 :result-mode binding
)
4720 ,keyword-ok-label
)))
4724 (append (when supplied-p-var
4725 `((:init-lexvar
,supplied-p-binding
4726 :init-with-register
:edi
4727 :init-with-type null
)))
4728 (compiler-call #'compile-form
4729 :form
(list 'muerte.cl
:quote
4730 (eval-form (optional-function-argument-init-form binding
)
4736 ,(movitz-read (keyword-function-argument-keyword-name binding
)) :ecx
)
4737 (:load-lexical
,rest-binding
:ebx
)
4738 (:call
(:edi
,(global-constant-offset 'keyword-search
))))
4739 (when supplied-p-var
4740 `((:jz
',keyword-not-supplied-label
)
4741 (:movl
(:edi
,(global-constant-offset 't-symbol
)) :ebx
)
4742 (:store-lexical
,supplied-p-binding
:ebx
4743 :type
(eql ,(image-t-symbol *image
*)))
4744 ,keyword-not-supplied-label
))
4745 `((:init-lexvar
,binding
4746 :init-with-register
:eax
4747 :init-with-type t
)))))))
4748 need-normalized-ecx-p
)))
4750 (defun old-key-encode (vars &key
(size (ash 1 (integer-length (1- (length vars
)))))
4752 (assert (<= (length vars
) size
))
4755 (loop with h
= (make-array size
)
4757 for var in
(sort (copy-list vars
) #'<
4759 (mod (ldb byte
(movitz-sxhash (movitz-read v
)))
4761 do
(let ((pos (mod (ldb byte
(movitz-sxhash (movitz-read var
)))
4763 (loop while
(aref h pos
)
4765 (setf pos
(mod (1+ pos
) (length h
))))
4766 (setf (aref h pos
) var
))
4767 finally
(return (values (subseq h
0 (1+ (position-if-not #'null h
:from-end t
)))
4770 (define-condition key-encoding-failed
() ())
4772 (defun key-cuckoo (x shift table
&optional path old-position
)
4774 (error 'key-encoding-failed
)
4775 (let* ((pos1 (mod (ash (movitz-sxhash (movitz-read x
)) (- shift
))
4777 (pos2 (mod (ash (movitz-sxhash (movitz-read x
)) (- 0 shift
9))
4779 (pos (if (eql pos1 old-position
) pos2 pos1
))
4780 (kickout (aref table pos
)))
4781 (setf (aref table pos
)
4784 (key-cuckoo kickout shift table
(cons x path
) pos
)))))
4786 (defun key-encode (vars &key
(size (ash 1 (integer-length (1- (length vars
)))))
4788 (declare (ignore byte
))
4789 (assert (<= (length vars
) size
))
4792 (loop with table
= (make-array size
)
4793 for var in
(sort (copy-list vars
) #'<
4795 (mod (movitz-sxhash (movitz-read v
))
4797 do
(key-cuckoo var shift table
)
4799 (return (values table
4801 (count-if (lambda (v)
4802 (eq v
(aref table
(mod (ash (movitz-sxhash (movitz-read v
))
4807 (defun best-key-encode (vars)
4809 (loop with best-encoding
= nil
4812 for size
= (ash 1 (integer-length (1- (length vars
))))
4814 ;; from (length vars) to (+ 8 (ash 1 (integer-length (1- (length vars)))))
4815 while
(<= size
(max 16 (ash 1 (integer-length (1- (length vars
))))))
4816 do
(loop for shift from
0 to
9 by
3
4818 (multiple-value-bind (encoding crashes
)
4819 (key-encode vars
:size size
:shift shift
)
4820 (when (or (not best-encoding
)
4821 (< crashes best-crashes
)
4822 (and (= crashes best-crashes
)
4823 (or (< shift best-shift
)
4824 (and (= shift best-shift
)
4825 (< (length encoding
)
4826 (length best-encoding
))))))
4827 (setf best-encoding encoding
4829 best-crashes crashes
)))
4830 (key-encoding-failed ())))
4832 (unless best-encoding
4833 (warn "Key-encoding failed for ~S: ~S."
4836 (list (movitz-sxhash (movitz-read v
))
4837 (ldb (byte (+ 3 (integer-length (1- (length vars
)))) 0)
4838 (movitz-sxhash (movitz-read v
)))
4839 (ldb (byte (+ 3 (integer-length (1- (length vars
)))) 9)
4840 (movitz-sxhash (movitz-read v
)))))
4843 (warn "~D waste for ~S"
4844 (- (length best-encoding
)
4847 (return (values best-encoding best-shift best-crashes
)))))
4851 (defun play-with-keys (key-vars)
4853 (let* ((vars (mapcar #'decode-keyword-formal key-vars
)))
4854 (multiple-value-bind (encoding shift crashes
)
4855 (best-key-encode vars
)
4856 (when (or (plusp crashes
)
4857 #+ignore
(>= shift
3)
4858 (>= (- (length encoding
) (length vars
))
4860 (warn "KEY vars: ~S, crash ~D, shift ~D, waste: ~D hash: ~S"
4862 (- (length encoding
) (length vars
))
4864 (movitz-sxhash (movitz-read s
)))
4868 (defun make-special-funarg-shadowing (env function-body
)
4869 "Wrap function-body in a let, if we need to.
4870 We need to when the function's lambda-list binds a special variable,
4871 or when there's a non-dynamic-extent &rest binding."
4872 (if (without-function-prelude-p env
)
4875 (append (special-variable-shadows env
)
4877 (when (and (rest-var env
)
4878 (not (movitz-env-get (rest-var env
) 'dynamic-extent nil env nil
))
4879 (not (movitz-env-get (rest-var env
) 'ignore nil env nil
)))
4880 (movitz-env-load-declarations `((muerte.cl
:dynamic-extent
,(rest-var env
)))
4882 `((,(rest-var env
) (muerte.cl
:copy-list
,(rest-var env
))))))))
4883 (if (null shadowing
)
4885 `(muerte.cl
::let
,shadowing
,function-body
)))))
4887 (defun make-compiled-function-postlude (funobj env use-stack-frame-p
)
4888 (declare (ignore funobj env
))
4889 (let ((p '((:movl
(:ebp -
4) :esi
)
4891 (if use-stack-frame-p
4895 (defun complement-boolean-result-mode (mode)
4899 (:boolean-greater
:boolean-less-equal
)
4900 (:boolean-less
:boolean-greater-equal
)
4901 (:boolean-greater-equal
:boolean-less
)
4902 (:boolean-less-equal
:boolean-greater
)
4903 (:boolean-below
:boolean-above-equal
)
4904 (:boolean-above
:boolean-below-equal
)
4905 (:boolean-below-equal
:boolean-above
)
4906 (:boolean-above-equal
:boolean-below
)
4907 (:boolean-zf
=1 :boolean-zf
=0)
4908 (:boolean-zf
=0 :boolean-zf
=1)
4909 (:boolean-cf
=1 :boolean-cf
=0)
4910 (:boolean-cf
=0 :boolean-cf
=1)))
4912 (let ((args (cdr mode
)))
4915 (list :boolean-ecx
(second args
) (first args
)))
4916 (:boolean-branch-on-true
4917 (cons :boolean-branch-on-false args
))
4918 (:boolean-branch-on-false
4919 (cons :boolean-branch-on-true args
)))))))
4921 (defun make-branch-on-boolean (mode label
&key invert
)
4922 (list (ecase (if invert
(complement-boolean-result-mode mode
) mode
)
4923 (:boolean-greater
:jg
) ; ZF=0 and SF=OF
4924 (:boolean-greater-equal
:jge
) ; SF=OF
4925 (:boolean-less
:jl
) ; SF!=OF
4926 (:boolean-less-equal
:jle
) ; ZF=1 or SF!=OF
4927 (:boolean-below
:jb
)
4928 (:boolean-above
:ja
)
4929 (:boolean-below-equal
:jbe
)
4930 (:boolean-above-equal
:jae
)
4932 (:boolean-zf
=0 :jnz
)
4934 (:boolean-cf
=0 :jnc
)
4935 (:boolean-true
:jmp
))
4936 (list 'quote label
)))
4939 (defun make-cmov-on-boolean (mode src dst
&key invert
)
4940 (list (ecase (if invert
(complement-boolean-result-mode mode
) mode
)
4941 (:boolean-greater
:cmovg
) ; ZF=0 and SF=OF
4942 (:boolean-greater-equal
:cmovge
) ; SF=OF
4943 (:boolean-less
:cmovl
) ; SF!=OF
4944 (:boolean-less-equal
:cmovle
) ; ZF=1 or SF!=OF
4945 (:boolean-zf
=1 :cmovz
)
4946 (:boolean-zf
=0 :cmovnz
)
4947 (:boolean-cf
=1 :cmovc
)
4948 (:boolean-cf
=0 :cmovnc
))
4951 (defun return-satisfies-result-p (desired-result returns-provided
)
4952 (or (eq desired-result returns-provided
)
4953 (case desired-result
4955 ((:eax
:single-value
)
4956 (member returns-provided
'(:eax
:multiple-values
:single-value
)))
4958 (member returns-provided
'(:multiple-values
:function
)))
4960 (member returns-provided
+boolean-modes
+)))))
4962 (defun make-result-and-returns-glue (desired-result returns-provided
4964 &key
(type t
) provider really-desired
)
4965 "Returns new-code and new-returns-provided, and glue-side-effects-p."
4966 (declare (optimize (debug 3)))
4967 (case returns-provided
4969 ;; when CODE does a non-local exit, we certainly don't need any glue.
4970 (return-from make-result-and-returns-glue
4971 (values code
:non-local-exit
))))
4972 (multiple-value-bind (new-code new-returns-provided glue-side-effects-p
)
4973 (case (result-mode-type desired-result
)
4975 (case (result-mode-type returns-provided
)
4977 (if (eq desired-result returns-provided
)
4978 (values code returns-provided
)
4979 (values (append code
`((:load-lexical
,returns-provided
,desired-result
)))
4981 ((:eax
:multiple-values
)
4982 (values (append code
4983 `((:store-lexical
,desired-result
:eax
4984 :type
,(type-specifier-primary type
))))
4988 (values (append code
4989 `((:store-lexical
,desired-result
4990 ,(result-mode-type returns-provided
)
4991 :type
,(type-specifier-primary type
))))
4994 (:ignore
(values code
:nothing
))
4996 (let ((true (first (operands desired-result
)))
4997 (false (second (operands desired-result
))))
4998 (etypecase (operator returns-provided
)
5000 (if (equal (operands desired-result
)
5001 (operands returns-provided
))
5002 (values code desired-result
)
5004 ((eql :boolean-cf
=1)
5006 ((and (= -
1 true
) (= 0 false
))
5007 (values (append code
5008 `((:sbbl
:ecx
:ecx
)))
5009 '(:boolean-ecx -
1 0)))
5010 ((and (= 0 true
) (= -
1 false
))
5011 (values (append code
5014 '(:boolean-ecx
0 -
1)))
5015 (t (error "Don't know modes ~S => ~S." returns-provided desired-result
))))
5017 (make-result-and-returns-glue desired-result
5020 `((:leal
(:eax
,(- (image-nil-word *image
*)))
5025 :really-desired desired-result
)))))
5026 (:boolean-branch-on-true
5027 ;; (warn "rm :b-true with ~S." returns-provided)
5028 (etypecase (operator returns-provided
)
5029 ((member :boolean-branch-on-true
)
5030 (assert (eq (operands desired-result
) (operands returns-provided
)))
5031 (values code returns-provided
))
5032 ((member :eax
:multiple-values
)
5033 (values (append code
5035 (:jne
',(operands desired-result
))))
5037 ((member :ebx
:ecx
:edx
)
5038 (values (append code
5039 `((:cmpl
:edi
,returns-provided
)
5040 (:jne
',(operands desired-result
))))
5043 ;; no branch, nothing is nil is false.
5044 (values code desired-result
))
5045 ((member .
#.
+boolean-modes
+)
5046 (values (append code
5047 (list (make-branch-on-boolean returns-provided
(operands desired-result
))))
5050 (values (append code
5051 `((:load-lexical
,returns-provided
,desired-result
)))
5053 (constant-object-binding
5054 (values (if (eq *movitz-nil
* (constant-object returns-provided
))
5056 `((:jmp
',(operands desired-result
))))
5058 (:boolean-branch-on-false
5059 (etypecase (operator returns-provided
)
5060 ((member :boolean-branch-on-false
)
5061 (assert (eq (operands desired-result
)
5062 (operands returns-provided
)))
5063 (values code desired-result
))
5065 (values (append code
5066 `((:jmp
',(operands desired-result
))))
5068 ((member .
#.
+boolean-modes
+)
5069 (values (append code
5070 (list (make-branch-on-boolean returns-provided
(operands desired-result
)
5073 ((member :ebx
:ecx
:edx
)
5074 (values (append code
5075 `((:cmpl
:edi
,returns-provided
)
5076 (:je
',(operands desired-result
))))
5078 ((member :eax
:multiple-values
)
5079 (values (append code
5081 (:je
',(operands desired-result
))))
5084 (values (append code
5085 `((:load-lexical
,returns-provided
,desired-result
)))
5087 (constant-object-binding
5088 (values (if (not (eq *movitz-nil
* (constant-object returns-provided
)))
5090 `((:jmp
',(operands desired-result
))))
5092 (:untagged-fixnum-ecx
5093 (case (result-mode-type returns-provided
)
5094 (:untagged-fixnum-ecx
5095 (values code
:untagged-fixnum-ecx
))
5096 ((:eax
:single-value
:multiple-values
:function
)
5097 (values (append code
5098 `((,*compiler-global-segment-prefix
*
5099 :call
(:edi
,(global-constant-offset 'unbox-u32
)))))
5100 :untagged-fixnum-ecx
))
5102 ;; In theory (at least..) ECX can only hold non-pointers, so don't check.
5103 (values (append code
5104 `((:shrl
,+movitz-fixnum-shift
+ :ecx
)))
5105 :untagged-fixnum-ecx
))
5107 (values (append code
5108 `((:movl
,returns-provided
:eax
)
5109 (,*compiler-global-segment-prefix
*
5110 :call
(:edi
,(global-constant-offset 'unbox-u32
)))))
5111 :untagged-fixnum-ecx
))
5113 (values (append code
5114 `((:load-lexical
,returns-provided
:untagged-fixnum-ecx
)))
5115 :untagged-fixnum-ecx
))))
5116 ((:single-value
:eax
)
5118 ((eq returns-provided
:eax
)
5120 ((typep returns-provided
'lexical-binding
)
5121 (values (append code
`((:load-lexical
,returns-provided
:eax
)))
5123 (t (case (operator returns-provided
)
5124 (:untagged-fixnum-eax
5125 (values (append code
`((:shll
,+movitz-fixnum-shift
+ :eax
))) :eax
))
5127 (case (first (operands returns-provided
))
5128 (0 (values (append code
'((:movl
:edi
:eax
)))
5130 (t (values code
:eax
))))
5131 ((:single-value
:eax
:function
:multiple-values
)
5134 (values (append code
'((:movl
:edi
:eax
)))
5136 ((:ebx
:ecx
:edx
:edi
)
5137 (values (append code
`((:movl
,returns-provided
:eax
)))
5140 (let ((true-false (operands returns-provided
)))
5142 ((equal '(0 1) true-false
)
5143 (values (append code
`((:movl
(:edi
(:ecx
4) ,(global-constant-offset 'boolean-zero
))
5146 ((equal '(1 0) true-false
)
5147 (values (append code
`((:movl
(:edi
(:ecx
4) ,(global-constant-offset 'boolean-one
))
5150 (t (error "Don't know ECX mode ~S." returns-provided
)))))
5152 (values (append code
5153 `((:sbbl
:ecx
:ecx
) ; T => -1, NIL => 0
5154 (:movl
(:edi
(:ecx
4) ,(global-constant-offset 'not-not-nil
))
5158 ;; (warn "bool for ~S" returns-provided)
5159 (let ((boolean-false-label (make-symbol "boolean-false-label")))
5160 (values (append code
5161 '((:movl
:edi
:eax
))
5162 (if *compiler-use-cmov-p
*
5163 `(,(make-cmov-on-boolean returns-provided
5164 `(:edi
,(global-constant-offset 't-symbol
))
5167 `(,(make-branch-on-boolean returns-provided
5170 (:movl
(:edi
,(global-constant-offset 't-symbol
))
5172 ,boolean-false-label
)))
5174 ((:ebx
:ecx
:edx
:esp
:esi
)
5176 ((eq returns-provided desired-result
)
5177 (values code returns-provided
))
5178 ((typep returns-provided
'lexical-binding
)
5179 (values (append code
`((:load-lexical
,returns-provided
,desired-result
)))
5181 (t (case (operator returns-provided
)
5183 (values (append code
5184 `((:movl
:edi
,desired-result
)))
5186 ((:ebx
:ecx
:edx
:esp
)
5187 (values (append code
5188 `((:movl
,returns-provided
,desired-result
)))
5190 ((:eax
:single-value
:multiple-values
:function
)
5191 (values (append code
5192 `((:movl
:eax
,desired-result
)))
5195 (let ((true-false (operands returns-provided
)))
5197 ((equal '(0 1) true-false
)
5198 (values (append code
`((:movl
(:edi
(:ecx
4) ,(global-constant-offset 'boolean-zero
))
5201 ((equal '(1 0) true-false
)
5202 (values (append code
`((:movl
(:edi
(:ecx
4) ,(global-constant-offset 'boolean-one
))
5205 (t (error "Don't know ECX mode ~S." returns-provided
)))))
5207 ;;; (values (append code `((:movl (:edi (:ecx 4) ,(global-constant-offset 'boolean-zero))
5208 ;;; ,desired-result)))
5209 ;;; desired-result))
5211 ;;; (values (append code `((:movl (:edi (:ecx 4) ,(global-constant-offset 'boolean-one))
5212 ;;; ,desired-result)))
5213 ;;; desired-result))
5215 (values (append code
5217 (:movl
(:edi
(:ecx
4) ,(global-constant-offset 'not-not-nil
))
5221 ;; (warn "bool to ~S for ~S" desired-result returns-provided)
5222 (values (append code
5224 (*compiler-use-cmov-p
*
5225 `((:movl
:edi
,desired-result
)
5226 ,(make-cmov-on-boolean returns-provided
5227 `(:edi
,(global-constant-offset 't-symbol
))
5229 ((not *compiler-use-cmov-p
*)
5230 (let ((boolean-false-label (make-symbol "boolean-false-label")))
5231 `((:movl
:edi
,desired-result
)
5232 ,(make-branch-on-boolean returns-provided
5235 (:movl
(:edi
,(global-constant-offset 't-symbol
))
5237 ,boolean-false-label
)))))
5238 desired-result
))))))
5240 (typecase returns-provided
5241 ((member :push
) (values code
:push
))
5243 (values (append code
'((:pushl
:edi
)))
5245 ((member :single-value
:eax
:multiple-values
:function
)
5246 (values (append code
`((:pushl
:eax
)))
5248 ((member :ebx
:ecx
:edx
)
5249 (values (append code
`((:pushl
,returns-provided
)))
5252 (values (append code
`((:load-lexical
,returns-provided
:push
)))
5255 (case (operator returns-provided
)
5257 (values code returns-provided
))
5259 (values code
:values
))
5260 (t (values (make-result-and-returns-glue :eax returns-provided code
5263 ((:multiple-values
:function
)
5264 (case (operator returns-provided
)
5265 ((:multiple-values
:function
)
5266 (values code
:multiple-values
))
5268 (case (first (operands returns-provided
))
5269 (0 (values (append code
'((:movl
:edi
:eax
) (:xorl
:ecx
:ecx
) (:stc
)))
5271 (1 (values (append code
'((:clc
)))
5273 ((nil) (values code
:multiple-values
))
5274 (t (values (append code
5275 (make-immediate-move (first (operands returns-provided
)) :ecx
)
5277 :multiple-values
))))
5278 (t (values (append (make-result-and-returns-glue :eax
5283 :really-desired desired-result
)
5285 :multiple-values
)))))
5286 (unless new-returns-provided
5287 (multiple-value-setq (new-code new-returns-provided glue-side-effects-p
)
5288 (ecase (result-mode-type returns-provided
)
5290 (case (result-mode-type desired-result
)
5291 ((:eax
:ebx
:ecx
:edx
:push
:lexical-binding
)
5292 (values (append code
5293 `((:load-constant
,(constant-object returns-provided
)
5297 (make-result-and-returns-glue desired-result
:eax
5298 (make-result-and-returns-glue :eax returns-provided code
5301 :really-desired desired-result
)
5303 :provider provider
))
5304 (:untagged-fixnum-ecx
5305 (let ((fixnump (subtypep type
`(integer 0 ,+movitz-most-positive-fixnum
+))))
5308 (member (result-mode-type desired-result
) '(:eax
:ebx
:ecx
:edx
)))
5309 (values (append code
5310 `((:leal
((:ecx
,+movitz-fixnum-factor
+))
5311 ,(result-mode-type desired-result
))))
5314 (member (result-mode-type desired-result
) '(:eax
:single-value
)))
5315 (values (append code
5316 `((:call
(:edi
,(global-constant-offset 'box-u32-ecx
)))))
5318 (t (make-result-and-returns-glue
5320 (make-result-and-returns-glue :eax
:untagged-fixnum-ecx code
5322 :really-desired desired-result
5327 (:untagged-fixnum-eax
5328 (make-result-and-returns-glue desired-result
:eax
5329 (make-result-and-returns-glue :eax
:untagged-fixnum-eax code
5331 :really-desired desired-result
)
5332 :provider provider
)))))
5333 (assert new-returns-provided
()
5334 "Don't know how to match desired-result ~S with returns-provided ~S~@[ from ~S~]."
5335 (or really-desired desired-result
) returns-provided provider
)
5336 (values new-code new-returns-provided glue-side-effects-p
)))
5338 (define-compiler compile-form
(&all form-info
&result-mode result-mode
)
5339 "3.1.2.1 Form Evaluation. Guaranteed to honor RESULT-MODE."
5340 (compiler-values-bind (&all unprotected-values
&code form-code
&returns form-returns
5341 &producer producer
&type form-type
&functional-p functional-p
)
5342 (compiler-call #'compile-form-unprotected
:forward form-info
)
5343 (multiple-value-bind (new-code new-returns-provided glue-side-effects-p
)
5344 (make-result-and-returns-glue result-mode form-returns form-code
5347 (compiler-values (unprotected-values)
5349 :functional-p
(and functional-p
(not glue-side-effects-p
))
5352 :returns new-returns-provided
))))
5354 (define-compiler compile-form-selected
(&all form-info
&result-mode result-modes
)
5355 "3.1.2.1 Form Evaluation. Guaranteed to honor one of RESULT-MODE, which
5356 for this call (exclusively!) is a list of the acceptable result-modes, where
5357 the first one takes preference. Note that :non-local-exit might also be returned."
5358 (check-type result-modes list
"a list of result-modes")
5359 (compiler-values-bind (&all unprotected-values
&code form-code
&returns form-returns
5360 &producer producer
&type form-type
)
5361 (compiler-call #'compile-form-unprotected
5362 :result-mode
(car result-modes
)
5364 (if (member form-returns result-modes
)
5365 (compiler-values (unprotected-values))
5366 (compiler-call #'compile-form
5367 :result-mode
(car result-modes
)
5368 :forward form-info
))))
5370 (define-compiler compile-form-to-register
(&all form-info
)
5371 (compiler-values-bind (&all unprotected-values
&code form-code
&returns form-returns
5372 &final-form final-form
&producer producer
&type form-type
)
5373 (compiler-call #'compile-form-unprotected
5378 ((and (typep final-form
'required-function-argument
)
5379 (= 1 (function-argument-argnum final-form
)))
5380 (compiler-call #'compile-form
5382 :forward form-info
))
5383 ((member form-returns
'(:eax
:ebx
:ecx
:edx
:edi
:untagged-fixnum-ecx
))
5384 (compiler-values (unprotected-values)))
5385 (t (compiler-call #'compile-form
5387 :forward form-info
)))))
5389 (define-compiler compile-form-unprotected
(&all downstream
&form form
&result-mode result-mode
5391 "3.1.2.1 Form Evaluation. May not honor RESULT-MODE.
5392 That is, RESULT-MODE is taken to be a suggestion, not an imperative."
5393 (compiler-values-bind (&all upstream
)
5395 (symbol (compiler-call #'compile-symbol
:forward downstream
))
5396 (cons (compiler-call #'compile-cons
:forward downstream
))
5397 (t (compiler-call #'compile-self-evaluating
:forward downstream
)))
5398 (when (typep (upstream :final-form
) 'lexical-binding
)
5399 (labels ((fix-extent (binding)
5401 ((sub-env-p extent
(binding-extent-env binding
))
5402 #+ignore
(warn "Binding ~S OK in ~S wrt. ~S."
5404 (binding-extent-env binding
)
5406 (t #+ignore
(break "Binding ~S escapes from ~S to ~S"
5407 binding
(binding-extent-env binding
)
5409 (setf (binding-extent-env binding
) extent
)))
5410 (when (typep binding
'forwarding-binding
)
5411 (fix-extent (forwarding-binding-target binding
)))))
5413 (fix-extent (upstream :final-form
)))))
5414 (compiler-values (upstream))))
5416 (defun lambda-form-p (form)
5418 (eq 'muerte.cl
:lambda
(first form
))))
5420 (defun function-name-p (operator)
5421 (or (and (symbolp operator
) operator
)
5422 (setf-name operator
)))
5424 (define-compiler compile-cons
(&all all
&form form
&env env
)
5425 "3.1.2.1.2 Conses as Forms"
5426 (let ((operator (car form
)))
5427 (if (and (symbolp operator
) (movitz-special-operator-p operator
))
5428 (compiler-call (movitz-special-operator-compiler operator
) :forward all
)
5429 (let* ((compiler-macro-function (movitz-compiler-macro-function operator env
))
5430 (compiler-macro-expansion (and compiler-macro-function
5432 (funcall *movitz-macroexpand-hook
*
5433 compiler-macro-function
5436 (warn "Compiler-macro for ~S failed: ~A" operator c
)
5439 ((and compiler-macro-function
5440 (not (movitz-env-get operator
'notinline nil env
))
5441 (not (eq form compiler-macro-expansion
)))
5442 (compiler-call #'compile-form-unprotected
:forward all
:form compiler-macro-expansion
))
5443 ((movitz-constantp form env
)
5444 (compiler-call #'compile-constant-compound
:forward all
))
5445 ((lambda-form-p operator
) ; 3.1.2.1.2.4
5446 (compiler-call #'compile-lambda-form
:forward all
))
5449 ((movitz-special-operator-p operator
)
5450 (compiler-call (movitz-special-operator-compiler operator
) :forward all
))
5451 ((movitz-macro-function operator env
)
5452 (compiler-call #'compile-macro-form
:forward all
))
5453 ((movitz-operator-binding operator env
)
5454 (compiler-call #'compile-apply-lexical-funobj
:forward all
))
5455 (t (compiler-call #'compile-apply-symbol
:forward all
))))
5456 (t (error "Don't know how to compile compound form ~A" form
)))))))
5458 (define-compiler compile-compiler-macro-form
(&all all
&form form
&env env
)
5459 (compiler-call #'compile-form-unprotected
5461 :form
(funcall *movitz-macroexpand-hook
*
5462 (movitz-compiler-macro-function (car form
) env
)
5465 (define-compiler compile-macro-form
(&all all
&form form
&env env
)
5466 "3.1.2.1.2.2 Macro Forms"
5467 (let* ((operator (car form
))
5468 (macro-function (movitz-macro-function operator env
)))
5469 (compiler-call #'compile-form-unprotected
5471 :form
(funcall *movitz-macroexpand-hook
* macro-function form env
))))
5473 (define-compiler compile-lexical-macro-form
(&all all
&form form
&env env
)
5474 "Compiles MACROLET and SYMBOL-MACROLET forms."
5475 (compiler-call #'compile-form-unprotected
5477 :form
(funcall *movitz-macroexpand-hook
*
5478 (macro-binding-expander (movitz-operator-binding form env
))
5481 (defun like-compile-macroexpand-form (form env
)
5483 ;; (symbol (compile-macroexpand-symbol form funobj env top-level-p result-mode))
5484 (cons (like-compile-macroexpand-cons form env
))
5485 (t (values form nil
))))
5487 (defun like-compile-macroexpand-cons (form env
)
5488 "3.1.2.1.2 Conses as Forms"
5489 (let* ((operator (car form
))
5490 (notinline (movitz-env-get operator
'notinline nil env
))
5491 (compiler-macro-function (movitz-compiler-macro-function operator env
))
5492 (compiler-macro-expansion (and compiler-macro-function
5493 (funcall *movitz-macroexpand-hook
*
5494 compiler-macro-function
5497 ((and (not notinline
)
5498 compiler-macro-function
5499 (not (eq form compiler-macro-expansion
)))
5500 (values compiler-macro-expansion t
))
5503 ((movitz-macro-function operator env
)
5504 (values (funcall *movitz-macroexpand-hook
*
5505 (movitz-macro-function operator env
)
5511 (defun make-compiled-stack-restore (stack-displacement result-mode returns
)
5512 "Return the code required to reset the stack according to stack-displacement,
5513 result-mode, and returns (which specify the returns-mode of the immediately
5514 preceding code). As secondary value, returns the new :returns value."
5515 (flet ((restore-by-pop (scratch)
5516 (case stack-displacement
5517 (1 `((:popl
,scratch
)))
5518 (2 `((:popl
,scratch
) (:popl
,scratch
))))))
5519 (if (zerop stack-displacement
)
5520 (values nil returns
)
5521 (ecase (result-mode-type result-mode
)
5523 (values nil returns
))
5524 ((:multiple-values
:values
)
5527 (values `((:leal
(:esp
,(* 4 stack-displacement
)) :esp
))
5529 ((:single-value
:eax
:ebx
)
5530 (values `((:addl
,(* 4 stack-displacement
) :esp
))
5531 :multiple-values
)))) ; assume this addl will set CF=0
5532 ((:single-value
:eax
:ebx
:ecx
:edx
:push
:lexical-binding
:untagged-fixnum-ecx
5533 :boolean
:boolean-branch-on-false
:boolean-branch-on-true
)
5536 (values (or (restore-by-pop :eax
)
5537 `((:leal
(:esp
,(* 4 stack-displacement
)) :esp
))) ; preserve all flags
5540 (values (or (restore-by-pop :eax
)
5541 `((:addl
,(* 4 stack-displacement
) :esp
)))
5543 ((:multiple-values
:single-value
:eax
)
5544 (values (or (restore-by-pop :ebx
)
5545 `((:addl
,(* 4 stack-displacement
) :esp
)))
5548 (values (or (restore-by-pop :eax
)
5549 `((:addl
,(* 4 stack-displacement
) :esp
)))
5552 (define-compiler compile-apply-symbol
(&form form
&funobj funobj
&env env
5553 &result-mode result-mode
)
5554 "3.1.2.1.2.3 Function Forms"
5555 (destructuring-bind (operator &rest arg-forms
)
5557 #+ignore
(when (and (eq result-mode
:function
)
5558 (eq operator
(movitz-print (movitz-funobj-name funobj
))))
5559 (warn "Tail-recursive call detected."))
5560 (when (eq operator
'muerte.cl
::declare
)
5561 (break "Compiling funcall to ~S" 'muerte.cl
::declare
))
5562 (pushnew (cons operator muerte.cl
::*compile-file-pathname
*)
5563 (image-called-functions *image
*)
5565 (multiple-value-bind (arguments-code stack-displacement arguments-modifies
)
5566 (make-compiled-argument-forms arg-forms funobj env
)
5567 (multiple-value-bind (stack-restore-code new-returns
)
5568 (make-compiled-stack-restore stack-displacement result-mode
:multiple-values
)
5570 :returns new-returns
5572 :modifies arguments-modifies
5573 :code
(append arguments-code
5574 (if (and (not *compiler-relink-recursive-funcall
*)
5575 (eq (movitz-read operator
)
5576 (movitz-read (movitz-funobj-name funobj
)))) ; recursive?
5577 (make-compiled-funcall-by-esi (length arg-forms
))
5578 (make-compiled-funcall-by-symbol operator
(length arg-forms
) funobj
))
5579 stack-restore-code
))))))
5581 (define-compiler compile-apply-lexical-funobj
(&all all
&form form
&funobj funobj
&env env
5582 &result-mode result-mode
)
5583 "3.1.2.1.2.3 Function Forms"
5584 (destructuring-bind (operator &rest arg-forms
)
5586 (let ((binding (movitz-operator-binding operator env
)))
5587 (multiple-value-bind (arguments-code stack-displacement
)
5588 (make-compiled-argument-forms arg-forms funobj env
)
5589 (multiple-value-bind (stack-restore-code new-returns
)
5590 (make-compiled-stack-restore stack-displacement result-mode
:multiple-values
)
5592 :returns new-returns
5594 :code
(append arguments-code
5595 (if (eq funobj
(function-binding-funobj binding
))
5596 (make-compiled-funcall-by-esi (length arg-forms
)) ; call ourselves
5597 `((:call-lexical
,binding
,(length arg-forms
))))
5598 stack-restore-code
)))))))
5600 (defun make-compiled-funcall-by-esi (num-args)
5602 (1 `((:call
(:esi
,(slot-offset 'movitz-funobj
'code-vector%
1op
)))))
5603 (2 `((:call
(:esi
,(slot-offset 'movitz-funobj
'code-vector%
2op
)))))
5604 (3 `((:call
(:esi
,(slot-offset 'movitz-funobj
'code-vector%
3op
)))))
5605 (t (append (if (< num-args
#x80
)
5606 `((:movb
,num-args
:cl
))
5607 (make-immediate-move (dpb num-args
(byte 24 8) #x80
) :ecx
))
5608 ; call new ESI's code-vector
5609 `((:call
(:esi
,(slot-offset 'movitz-funobj
'code-vector
))))))))
5611 (defun make-compiled-funcall-by-symbol (apply-symbol num-args funobj
)
5612 (declare (ignore funobj
))
5613 (check-type apply-symbol symbol
)
5614 `((:load-constant
,(movitz-read apply-symbol
) :edx
) ; put function symbol in EDX
5615 (:movl
(:edx
,(slot-offset 'movitz-symbol
'function-value
))
5616 :esi
) ; load new funobj from symbol into ESI
5617 ,@(make-compiled-funcall-by-esi num-args
)))
5619 (defun make-compiled-funcall-by-funobj (apply-funobj num-args funobj
)
5620 (declare (ignore funobj
))
5621 (check-type apply-funobj movitz-funobj
)
5623 :returns
:multiple-values
5625 :code
`( ; put function funobj in ESI
5626 (:load-constant
,apply-funobj
:esi
)
5627 ,@(make-compiled-funcall-by-esi num-args
))))
5629 (defun make-compiled-argument-forms (argument-forms funobj env
)
5630 "Return code as primary value, and stack displacement as secondary value.
5631 Return the set of modified lexical bindings third. Fourth, a list of the individual
5632 compile-time types of each argument. Fifth: The combined functional-p."
5633 ;; (incf (aref *args* (min (length argument-forms) 9)))
5634 (case (length argument-forms
) ;; "optimized" versions for 0, 1, 2, and 3 aruments.
5635 (0 (values nil
0 nil
() t
))
5636 (1 (compiler-values-bind (&code code
&type type
&functional-p functional-p
)
5637 (compiler-call #'compile-form
5638 :form
(first argument-forms
)
5642 (values code
0 t
(list (type-specifier-primary type
)) functional-p
)))
5643 (2 (multiple-value-bind (code functional-p modified first-values second-values
)
5644 (make-compiled-two-forms-into-registers (first argument-forms
) :eax
5645 (second argument-forms
) :ebx
5647 (values code
0 modified
5648 (list (type-specifier-primary (compiler-values-getf first-values
:type
))
5649 (type-specifier-primary (compiler-values-getf second-values
:type
)))
5651 (t (let* ((arguments-self-evaluating-p t
)
5652 (arguments-are-load-lexicals-p t
)
5653 (arguments-lexical-variables ())
5654 (arguments-modifies nil
)
5655 (arguments-functional-p t
)
5656 (arguments-types nil
)
5660 (loop for form in
(nthcdr 2 argument-forms
)
5662 (compiler-values-bind (&code code
&producer producer
&modifies modifies
&type type
5663 &functional-p functional-p
)
5664 (compiler-call #'compile-form
5669 :with-stack-used
(post-incf stack-pos
))
5670 ;; (incf (stack-used arg-env))
5671 (unless functional-p
5672 (setf arguments-functional-p nil
))
5673 (push producer producers
)
5674 (push (type-specifier-primary type
)
5676 (setf arguments-modifies
5677 (modifies-union arguments-modifies modifies
))
5679 (compile-self-evaluating)
5680 (compile-lexical-variable
5681 (setf arguments-self-evaluating-p nil
)
5682 (assert (eq :load-lexical
(caar code
)) ()
5683 "comp-lex-var produced for ~S~% ~S" form code
)
5684 (pushnew (cadar code
) arguments-lexical-variables
))
5685 (t (setf arguments-self-evaluating-p nil
5686 arguments-are-load-lexicals-p nil
)))
5688 (multiple-value-bind (code01 functionalp01 modifies01 all0 all1
)
5689 (make-compiled-two-forms-into-registers (first argument-forms
) :eax
5690 (second argument-forms
) :ebx
5692 (unless functionalp01
5693 (setf arguments-functional-p nil
))
5694 (let ((final0 (compiler-values-getf all0
:final-form
))
5695 (final1 (compiler-values-getf all1
:final-form
))
5696 (types (list* (type-specifier-primary (compiler-values-getf all0
:type
))
5697 (type-specifier-primary (compiler-values-getf all1
:type
))
5698 (nreverse arguments-types
))))
5700 ((or arguments-self-evaluating-p
5701 (and (typep final0
'lexical-binding
)
5702 (typep final1
'lexical-binding
)))
5703 (values (append arguments-code code01
)
5705 (+ -
2 (length argument-forms
))
5708 arguments-functional-p
))
5709 ((and arguments-are-load-lexicals-p
5710 (typep final0
'(or lexical-binding movitz-object
))
5711 (typep final1
'(or lexical-binding movitz-object
)))
5712 (values (append arguments-code code01
)
5713 (+ -
2 (length argument-forms
))
5716 arguments-functional-p
))
5717 ((and arguments-are-load-lexicals-p
5718 (not (some (lambda (arg-binding)
5719 (code-uses-binding-p code01 arg-binding
:store t
:load nil
))
5720 arguments-lexical-variables
)))
5721 (values (append arguments-code code01
)
5722 (+ -
2 (length argument-forms
))
5725 arguments-functional-p
))
5726 (t ;; (warn "fail: ~S by ~S" argument-forms (nreverse producers))
5727 (let ((stack-pos 0))
5728 (values (append (compiler-call #'compile-form
5729 :form
(first argument-forms
)
5734 :with-stack-used
(post-incf stack-pos
))
5735 ;; (prog1 nil (incf (stack-used arg-env)))
5736 (compiler-call #'compile-form
5737 :form
(second argument-forms
)
5742 :with-stack-used
(post-incf stack-pos
))
5743 ;; (prog1 nil (incf (stack-used arg-env)))
5744 (loop for form in
(nthcdr 2 argument-forms
)
5746 (compiler-call #'compile-form
5751 :with-stack-used
(post-incf stack-pos
)))
5752 `((:movl
(:esp
,(* 4 (- (length argument-forms
) 1))) :eax
)
5753 (:movl
(:esp
,(* 4 (- (length argument-forms
) 2))) :ebx
)))
5754 ;; restore-stack.. don't mess up CF!
5755 (prog1 (length argument-forms
)
5756 #+ignore
(assert (= (length argument-forms
) (stack-used arg-env
))))
5757 (modifies-union modifies01 arguments-modifies
)
5759 arguments-functional-p
))))))))))
5761 (defun program-is-load-lexical-of-binding (prg)
5762 (and (not (cdr prg
))
5763 (instruction-is-load-lexical-of-binding (car prg
))))
5765 (defun instruction-is-load-lexical-of-binding (instruction)
5766 (and (listp instruction
)
5767 (eq :load-lexical
(car instruction
))
5768 (destructuring-bind (binding destination
&key
&allow-other-keys
)
5769 (operands instruction
)
5770 (values binding destination
))))
5772 (defun make-compiled-two-forms-into-registers (form0 reg0 form1 reg1 funobj env
)
5773 "Returns first: code that does form0 into reg0, form1 into reg1.
5774 second: whether code is functional-p,
5775 third: combined set of modified bindings
5776 fourth: all compiler-values for form0, as a list.
5777 fifth: all compiler-values for form1, as a list."
5778 (assert (not (eq reg0 reg1
)))
5779 (compiler-values-bind (&all all0
&code code0
&functional-p functional0
5780 &final-form final0
&type type0
)
5781 (compiler-call #'compile-form
5786 (compiler-values-bind (&all all1
&code code1
&functional-p functional1
5787 &final-form final1
&type type1
)
5788 (compiler-call #'compile-form
5794 ((and (typep final0
'binding
)
5795 (not (code-uses-binding-p code1 final0
:load nil
:store t
)))
5796 (append (compiler-call #'compile-form-unprotected
5798 :result-mode
:ignore
5802 `((:load-lexical
,final0
,reg0
:protect-registers
(,reg1
)))))
5803 ((program-is-load-lexical-of-binding code1
)
5804 (destructuring-bind (src dst
&key protect-registers shared-reference-p
)
5806 (assert (eq reg1 dst
))
5808 `((:load-lexical
,src
,reg1
5809 :protect-registers
,(union protect-registers
5811 :shared-reference-p
,shared-reference-p
)))))
5812 ;; XXX if we knew that code1 didn't mess up reg0, we could do more..
5813 (t #+ignore
(when (and (not (tree-search code1 reg0
))
5814 (not (tree-search code1
:call
)))
5815 (warn "got b: ~S ~S for ~S: ~{~&~A~}" form0 form1 reg0 code1
))
5816 (let ((binding (make-instance 'temporary-name
:name
(gensym "tmp-")))
5817 (xenv (make-local-movitz-environment env funobj
)))
5818 (movitz-env-add-binding xenv binding
)
5819 (append (compiler-call #'compile-form
5824 `((:init-lexvar
,binding
:init-with-register
,reg0
5825 :init-with-type
,(type-specifier-primary type0
)))
5826 (compiler-call #'compile-form
5831 `((:load-lexical
,binding
,reg0
))))))
5832 (and functional0 functional1
)
5834 (compiler-values-list (all0))
5835 (compiler-values-list (all1))))))
5837 (define-compiler compile-symbol
(&all all
&form form
&env env
&result-mode result-mode
)
5838 "3.1.2.1.1 Symbols as Forms"
5839 (if (movitz-constantp form env
)
5840 (compiler-call #'compile-self-evaluating
5842 :form
(eval-form form env
))
5843 (let ((binding (movitz-binding form env
)))
5845 ((typep binding
'lexical-binding
)
5846 #+ignore
(make-compiled-lexical-variable form binding result-mode env
)
5847 (compiler-call #'compile-lexical-variable
:forward all
))
5848 ((typep binding
'symbol-macro-binding
)
5849 (compiler-call #'compile-form-unprotected
5851 :form
(funcall *movitz-macroexpand-hook
*
5852 (macro-binding-expander (movitz-binding form env
)) form env
)))
5853 (t (compiler-call #'compile-dynamic-variable
:forward all
))))))
5855 (define-compiler compile-lexical-variable
(&form variable
&result-mode result-mode
&env env
)
5856 (let ((binding (movitz-binding variable env
)))
5857 (check-type binding lexical-binding
)
5858 (case (operator result-mode
)
5861 :final-form binding
))
5862 (t (compiler-values ()
5866 :functional-p t
)))))
5868 (defun make-compiled-lexical-load (binding result-mode
&rest key-args
)
5869 "Do what is necessary to load lexical binding <binding>."
5870 `((:load-lexical
,binding
,result-mode
,@key-args
)))
5872 (define-compiler compile-dynamic-variable
(&form form
&env env
&result-mode result-mode
)
5873 "3.1.2.1.1.2 Dynamic Variables"
5874 (if (eq :ignore result-mode
)
5875 (compiler-values ())
5876 (let ((binding (movitz-binding form env
)))
5879 (unless (movitz-env-get form
'special nil env
)
5880 (cerror "Compile like a special." "Undeclared variable: ~S." form
))
5886 :code
(if *compiler-use-into-unbound-protocol
*
5887 `((:load-constant
,form
:ebx
)
5888 (,*compiler-local-segment-prefix
*
5889 :call
(:edi
,(global-constant-offset 'dynamic-variable-lookup
)))
5892 (let ((not-unbound (gensym "not-unbound-")))
5893 `((:load-constant
,form
:ebx
)
5894 (,*compiler-local-segment-prefix
*
5895 :call
(:edi
,(global-constant-offset 'dynamic-variable-lookup
)))
5896 (,*compiler-local-segment-prefix
*
5897 :cmpl
:eax
(:edi
,(global-constant-offset 'unbound-value
)))
5898 (:jne
',not-unbound
)
5901 (t (check-type binding dynamic-binding
)
5907 :code
(if *compiler-use-into-unbound-protocol
*
5908 `((:load-constant
,form
:ebx
)
5909 (,*compiler-local-segment-prefix
*
5910 :call
(:edi
,(global-constant-offset 'dynamic-variable-lookup
)))
5913 (let ((not-unbound (gensym "not-unbound-")))
5914 `((:load-constant
,form
:ebx
)
5915 (,*compiler-local-segment-prefix
*
5916 :call
(:edi
,(global-constant-offset 'dynamic-variable-lookup
)))
5917 (,*compiler-local-segment-prefix
*
5918 :cmpl
:eax
(:edi
,(global-constant-offset 'unbound-value
)))
5919 (:jne
',not-unbound
)
5921 ,not-unbound
)))))))))
5923 (define-compiler compile-lambda-form
(&form form
&all all
)
5924 "3.1.2.2.4 Lambda Forms"
5925 (let ((lambda-expression (car form
))
5926 (lambda-args (cdr form
)))
5927 (compiler-call #'compile-form-unprotected
5929 :form
`(muerte.cl
:funcall
,lambda-expression
,@lambda-args
))))
5931 (define-compiler compile-constant-compound
(&all all
&form form
&env env
&top-level-p top-level-p
)
5932 (compiler-call #'compile-self-evaluating
5934 :form
(eval-form form env top-level-p
)))
5936 (defun register32-to-low8 (register)
5943 (defun make-immediate-move (value destination-register
)
5946 `((:xorl
,destination-register
,destination-register
)))
5947 ((= value
(image-nil-word *image
*))
5948 `((:movl
:edi
,destination-register
)))
5949 ((<= #x-80
(- value
(image-nil-word *image
*)) #x7f
)
5950 `((:leal
(:edi
,(- value
(image-nil-word *image
*))) ,destination-register
)))
5951 ((<= #x-80
(- value
(* 2 (image-nil-word *image
*))) #x7f
)
5952 `((:leal
(:edi
(:edi
1) ,(- value
(* 2 (image-nil-word *image
*)))) ,destination-register
)))
5953 ((<= #x-80
(- value
(* 3 (image-nil-word *image
*))) #x7f
)
5954 `((:leal
(:edi
(:edi
2) ,(- value
(* 3 (image-nil-word *image
*)))) ,destination-register
)))
5955 ((<= #x-80
(- value
(* 5 (image-nil-word *image
*))) #x7f
)
5956 `((:leal
(:edi
(:edi
4) ,(- value
(* 5 (image-nil-word *image
*)))) ,destination-register
)))
5957 ((<= #x-80
(- value
(* 9 (image-nil-word *image
*))) #x7f
)
5958 `((:leal
(:edi
(:edi
8) ,(- value
(* 9 (image-nil-word *image
*)))) ,destination-register
)))
5960 `((:xorl
,destination-register
,destination-register
)
5961 (:movb
,value
,(register32-to-low8 destination-register
))))
5962 (t `((:movl
,value
,destination-register
)))))
5964 (defparameter *prev-self-eval
* nil
)
5966 (define-compiler compile-self-evaluating
(&form form
&result-mode result-mode
&funobj funobj
)
5967 "3.1.2.1.3 Self-Evaluating Objects"
5968 (let* ((object form
)
5969 (movitz-obj (image-read-intern-constant *image
* object
))
5970 (funobj-env (funobj-env funobj
))
5971 (binding (or (cdr (assoc movitz-obj
(movitz-environment-bindings funobj-env
)))
5972 (let ((binding (make-instance 'constant-object-binding
5973 :name
(gensym "self-eval-")
5974 :object movitz-obj
)))
5975 (setf (binding-env binding
) funobj-env
)
5976 (push (cons movitz-obj binding
)
5977 (movitz-environment-bindings funobj-env
))
5979 (compiler-values-bind (&all self-eval
)
5980 (compiler-values (nil :abstract t
)
5981 :producer
(default-compiler-values-producer)
5982 :type
`(eql ,movitz-obj
)
5985 (case (operator result-mode
)
5987 (compiler-values (self-eval)
5990 (t (compiler-values (self-eval)
5991 :returns binding
))))))
5993 (define-compiler compile-implicit-progn
(&all all
&form forms
&top-level-p top-level-p
5994 &result-mode result-mode
)
5995 "Compile all the elements of the list <forms> as a progn."
5996 (check-type forms list
)
5997 (case (length forms
)
5998 (0 (compiler-values ()))
5999 (1 (compiler-call #'compile-form-unprotected
6001 :form
(first forms
)))
6002 (t (loop with no-side-effects-p
= t
6003 with progn-codes
= nil
6004 for
(sub-form . more-forms-p
) on forms
6005 as current-result-mode
= (if more-forms-p
:ignore result-mode
)
6006 do
(compiler-values-bind (&code code
&returns sub-returns-mode
6007 &functional-p no-sub-side-effects-p
6008 &type type
&final-form final-form
&producer sub-producer
)
6009 (compiler-call (if (not more-forms-p
)
6010 #'compile-form-unprotected
6014 :top-level-p top-level-p
6015 :result-mode current-result-mode
)
6016 (assert sub-returns-mode
()
6017 "~S produced no returns-mode for form ~S." sub-producer sub-form
)
6018 (unless no-sub-side-effects-p
6019 (setf no-side-effects-p nil
))
6020 (push (if (and no-sub-side-effects-p
(eq current-result-mode
:ignore
))
6024 (when (not more-forms-p
)
6025 (return (compiler-values ()
6026 :returns sub-returns-mode
6027 :functional-p no-side-effects-p
6028 :final-form final-form
6030 :code
(reduce #'append
(nreverse progn-codes
))))))))))
6033 (defun new-make-compiled-constant-reference (obj funobj
)
6034 (let ((movitz-obj (movitz-read obj
)))
6035 (if (eq movitz-obj
(image-t-symbol *image
*))
6036 (make-indirect-reference :edi
(global-constant-offset 't-symbol
))
6037 (etypecase movitz-obj
6039 (movitz-immediate-object (movitz-immediate-value movitz-obj
))
6041 (make-indirect-reference :esi
(movitz-funobj-intern-constant funobj movitz-obj
)))))))
6043 (defun make-compiled-lexical-control-transfer (return-code return-mode from-env to-env
6044 &optional
(to-label (exit-label to-env
)))
6045 "<return-code> running in <from-env> produces <return-mode>, and we need to
6046 generate code that transfers control (and unwinds dynamic bindings, runs unwind-protect
6047 cleanup-forms etc.) to <to-env> with <return-code>'s result intact."
6048 (check-type to-env lexical-exit-point-env
)
6049 (multiple-value-bind (stack-distance num-dynamic-slots unwind-protects
)
6050 (stack-delta from-env to-env
)
6051 (assert stack-distance
)
6052 (assert (null unwind-protects
) ()
6053 "Lexical unwind-protect not implemented, to-env: ~S. (this is not supposed to happen)"
6055 ;; (warn "dist: ~S, slots: ~S" stack-distance num-dynamic-slots)
6056 (assert (not (eq t num-dynamic-slots
)) ()
6057 "Don't know how to make lexical-control-transfer across unknown number of dynamic slots.")
6059 ((and (eq t stack-distance
)
6060 (eql 0 num-dynamic-slots
))
6062 :returns
:non-local-exit
6063 :code
(append return-code
6064 (unless (eq :function
(exit-result-mode to-env
))
6065 `((:load-lexical
,(movitz-binding (save-esp-variable to-env
) to-env nil
) :esp
)))
6066 `((:jmp
',to-label
)))))
6067 ((eq t stack-distance
)
6069 :returns
:non-local-exit
6070 :code
(append return-code
6071 (compiler-call #'special-operator-with-cloak
6073 :result-mode
(exit-result-mode to-env
)
6074 :form
`(muerte::with-cloak
(,return-mode
)
6075 (muerte::with-inline-assembly
(:returns
:nothing
)
6076 ;; Compute target dynamic-env
6077 (:locally
(:movl
(:edi
(:edi-offset dynamic-env
)) :eax
))
6078 ,@(loop repeat num-dynamic-slots
6079 collect
`(:movl
(:eax
12) :eax
))
6080 (:locally
(:call
(:edi
(:edi-offset dynamic-unwind-next
))))
6081 (:locally
(:movl
:eax
(:edi
(:edi-offset dynamic-env
))))
6082 (:jc
'(:sub-program
() (:int
63))))))
6083 `((:load-lexical
,(movitz-binding (save-esp-variable to-env
) to-env nil
) :esp
)
6084 (:jmp
',to-label
)))))
6085 ((zerop num-dynamic-slots
)
6087 :returns
:non-local-exit
6088 :code
(append return-code
6089 (make-compiled-stack-restore stack-distance
6090 (exit-result-mode to-env
)
6092 `((:jmp
',to-label
)))))
6093 ((plusp num-dynamic-slots
)
6094 ;; (warn "num-dynamic-slots: ~S, distance: ~D" num-dynamic-slots stack-distance)
6096 :returns
:non-local-exit
6097 :code
(append return-code
6098 (compiler-call #'special-operator-with-cloak
6100 :result-mode
(exit-result-mode to-env
)
6101 :form
`(muerte::with-cloak
(,return-mode
)
6102 (muerte::with-inline-assembly
(:returns
:nothing
)
6103 ;; Compute target dynamic-env
6104 (:locally
(:movl
(:edi
(:edi-offset dynamic-env
)) :eax
))
6105 ,@(loop repeat num-dynamic-slots
6106 collect
`(:movl
(:eax
12) :eax
))
6107 (:locally
(:call
(:edi
(:edi-offset dynamic-unwind-next
))))
6108 (:locally
(:movl
:eax
(:edi
(:edi-offset dynamic-env
))))
6109 (:jc
'(:sub-program
() (:int
63))))))
6110 `((:leal
(:esp
,(* 4 stack-distance
)) :esp
)
6111 (:jmp
',to-label
)))))
6112 (t (error "unknown!")))))
6114 (defun make-compiled-push-current-values ()
6115 "Return code that pushes the current values onto the stack, and returns
6116 in ECX the number of values (as fixnum)."
6117 (let ((not-single-value (gensym "not-single-value-"))
6118 (push-values-done (gensym "push-values-done-"))
6119 (push-values-loop (gensym "push-values-loop-")))
6120 `((:jc
',not-single-value
)
6123 (:jmp
',push-values-done
)
6125 (:shll
,+movitz-fixnum-shift
+ :ecx
)
6126 (:jz
',push-values-done
)
6131 (:je
',push-values-done
)
6135 (:je
',push-values-done
)
6137 (:locally
(:pushl
(:edi
(:edi-offset values
) :edx -
8)))
6140 (:jne
',push-values-loop
)
6141 ,push-values-done
)))
6143 (defun stack-add (x y
)
6144 (if (and (integerp x
) (integerp y
))
6148 (define-modify-macro stack-incf
(&optional
(delta 1)) stack-add
)
6150 (defun stack-delta (inner-env outer-env
)
6151 "Calculate the amount of stack-space used (in 32-bit stack slots) at the time
6152 of <inner-env> since <outer-env>,
6153 the number of intervening dynamic-slots (special bindings, unwind-protects, and catch-tags),
6154 and a list of any intervening unwind-protect environment-slots."
6156 ((find-stack-delta (env stack-distance num-dynamic-slots unwind-protects
)
6157 #+ignore
(warn "find-stack-delta: ~S dist ~S, slots ~S" env
6158 (stack-used env
) (num-dynamic-slots env
))
6161 ;; Each dynamic-slot is 4 stack-distances, so let's check that..
6162 (assert (or (eq t stack-distance
)
6163 (>= stack-distance
(* 4 num-dynamic-slots
))) ()
6164 "The stack-distance ~D is smaller than number of dynamic-slots ~D, which is inconsistent."
6165 stack-distance num-dynamic-slots
)
6166 (values stack-distance num-dynamic-slots unwind-protects
))
6169 (t (find-stack-delta (movitz-environment-uplink env
)
6170 (stack-add stack-distance
(stack-used env
))
6171 (stack-add num-dynamic-slots
(num-dynamic-slots env
))
6172 (if (typep env
'unwind-protect-env
)
6173 (cons env unwind-protects
)
6174 unwind-protects
))))))
6175 (find-stack-delta inner-env
0 0 nil
)))
6177 (defun print-stack-delta (inner-env outer-env
)
6178 (labels ((print-stack-delta (env)
6180 ((or (eq outer-env env
)
6182 (t (format t
"~&Env: ~S used: ~S, slots: ~S"
6183 env
(stack-used env
) (num-dynamic-slots env
))
6184 (print-stack-delta (movitz-environment-uplink env
))))))
6185 (print-stack-delta inner-env
)))
6188 ;;;;;;; Extended-code declarations
6191 (defvar *extended-code-find-read-binding
*
6192 (make-hash-table :test
#'eq
))
6194 (defvar *extended-code-find-used-bindings
*
6195 (make-hash-table :test
#'eq
))
6197 (defmacro define-find-read-bindings
(name lambda-list
&body body
)
6198 (let ((defun-name (intern
6199 (with-standard-io-syntax
6200 (format nil
"~A-~A" 'find-read-bindings name
)))))
6202 (setf (gethash ',name
*extended-code-find-read-binding
*) ',defun-name
)
6203 (defun ,defun-name
(instruction)
6204 (destructuring-bind ,lambda-list
6208 (defmacro define-find-used-bindings
(name lambda-list
&body body
)
6209 (let ((defun-name (intern
6210 (with-standard-io-syntax
6211 (format nil
"~A-~A" 'find-used-bindings name
)))))
6213 (setf (gethash ',name
*extended-code-find-used-bindings
*) ',defun-name
)
6214 (defun ,defun-name
(instruction)
6215 (destructuring-bind ,lambda-list
6219 (defun find-used-bindings (extended-instruction)
6220 "Return zero, one or two bindings that this instruction reads."
6221 (when (listp extended-instruction
)
6222 (let* ((operator (car extended-instruction
))
6223 (finder (or (gethash operator
*extended-code-find-used-bindings
*)
6224 (gethash operator
*extended-code-find-read-binding
*))))
6226 (let ((result (funcall finder extended-instruction
)))
6227 (check-type result list
"a list of read bindings")
6230 (defun find-read-bindings (extended-instruction)
6231 "Return zero, one or two bindings that this instruction reads."
6232 (when (listp extended-instruction
)
6233 (let* ((operator (car extended-instruction
))
6234 (finder (gethash operator
*extended-code-find-read-binding
*)))
6236 (funcall finder extended-instruction
)))))
6238 (defmacro define-find-write-binding-and-type
(name lambda-list
&body body
)
6239 (let ((defun-name (intern
6240 (with-standard-io-syntax
6241 (format nil
"~A-~A" 'find-write-binding-and-type name
)))))
6243 (setf (gethash ',name
*extended-code-find-write-binding-and-type
*) ',defun-name
)
6244 (defun ,defun-name
,lambda-list
,@body
))))
6246 (defun find-written-binding-and-type (extended-instruction)
6247 (when (listp extended-instruction
)
6248 (let* ((operator (car extended-instruction
))
6249 (finder (gethash operator
*extended-code-find-write-binding-and-type
*)))
6251 (funcall finder extended-instruction
)))))
6253 (defmacro define-extended-code-expander
(name lambda-list
&body body
)
6254 (let ((defun-name (intern
6255 (with-standard-io-syntax
6256 (format nil
"~A-~A" 'extended-code-expander- name
)))))
6258 (setf (gethash ',name
*extended-code-expanders
*) ',defun-name
)
6259 (defun ,defun-name
,lambda-list
,@body
))))
6261 (defun can-expand-extended-p (extended-instruction frame-map
)
6262 "Given frame-map, can we expand i at this point?"
6263 (and (every (lambda (b)
6264 (or (typep (binding-target b
) 'constant-object-binding
)
6265 (new-binding-located-p (binding-target b
) frame-map
)))
6266 (find-read-bindings extended-instruction
))
6267 (let ((written-binding (find-written-binding-and-type extended-instruction
)))
6268 (or (not written-binding
)
6269 (new-binding-located-p (binding-target written-binding
) frame-map
)))))
6271 (defun expand-extended-code (extended-instruction funobj frame-map
)
6272 (if (not (listp extended-instruction
))
6273 (list extended-instruction
)
6274 (let* ((operator (car extended-instruction
))
6275 (expander (gethash operator
*extended-code-expanders
*)))
6277 (list extended-instruction
)
6278 (let ((expansion (funcall expander extended-instruction funobj frame-map
)))
6280 (expand-extended-code e funobj frame-map
))
6283 (defun ensure-local-binding (binding funobj
)
6284 "When referencing binding in funobj, ensure we have the binding local to funobj."
6285 (if (typep binding
'(or (not binding
) constant-object-binding
))
6286 binding
; Never mind if "binding" isn't a binding, or is a constant-binding.
6287 (let ((target-binding (binding-target binding
)))
6289 ((eq funobj
(binding-funobj target-binding
))
6291 (t (or (find target-binding
(borrowed-bindings funobj
)
6292 :key
(lambda (binding)
6293 (borrowed-binding-target binding
)))
6294 (error "Can't install non-local binding ~W." binding
)))))))
6296 (defun binding-store-subtypep (binding type-specifier
)
6297 "Is type-specifier a supertype of all values ever stored to binding?
6298 (Assuming analyze-bindings has put this information into binding-store-type.)"
6299 (if (not (binding-store-type binding
))
6301 (multiple-value-call #'encoded-subtypep
6302 (values-list (binding-store-type binding
))
6303 (type-specifier-encode type-specifier
))))
6305 (defun binding-singleton (binding)
6306 (let ((btype (binding-store-type binding
)))
6308 (type-specifier-singleton (apply #'encoded-type-decode btype
)))))
6311 ;;;;;;; Extended-code handlers
6315 ;;;;;;;;;;;;;;;;;; Load-lexical
6317 (define-find-write-binding-and-type :load-lexical
(instruction)
6318 (destructuring-bind (source destination
&key
&allow-other-keys
)
6320 (when (typep destination
'binding
)
6321 (values destination t
#+ignore
(binding-type-specifier source
)
6322 (lambda (source-type)
6326 (define-find-read-bindings :load-lexical
(source destination
&key
&allow-other-keys
)
6327 (check-type source binding
)
6328 (values (list source
)
6329 (list destination
)))
6331 (define-extended-code-expander :load-lexical
(instruction funobj frame-map
)
6332 (destructuring-bind (source destination
&key shared-reference-p tmp-register protect-registers
)
6334 (make-load-lexical (ensure-local-binding source funobj
)
6335 (ensure-local-binding destination funobj
)
6336 funobj shared-reference-p frame-map
6337 :tmp-register tmp-register
6338 :protect-registers protect-registers
)))
6341 ;;;;;;;;;;;;;;;;;; Lisp-move
6343 (define-find-write-binding-and-type :lmove
(instruction)
6344 (destructuring-bind (source destination
)
6346 (values destination source
)))
6348 (define-find-read-bindings :lmove
(source destination
)
6349 (declare (ignore destination
))
6352 ;;;;;;;;;;;;;;;;;; Store-lexical
6354 (define-find-write-binding-and-type :store-lexical
(instruction)
6355 (destructuring-bind (destination source
&key
(type (error "No type")) &allow-other-keys
)
6357 (declare (ignore source
))
6358 (check-type destination binding
)
6359 (values destination type
)))
6361 (define-find-read-bindings :store-lexical
(destination source
&key
&allow-other-keys
)
6362 (declare (ignore destination
))
6363 (when (typep source
'binding
)
6366 (define-extended-code-expander :store-lexical
(instruction funobj frame-map
)
6367 (destructuring-bind (destination source
&key shared-reference-p type protect-registers
)
6369 (declare (ignore type
))
6370 (make-store-lexical (ensure-local-binding destination funobj
)
6371 (ensure-local-binding source funobj
)
6372 shared-reference-p funobj frame-map
6373 :protect-registers protect-registers
)))
6375 ;;;;;;;;;;;;;;;;;; Init-lexvar
6377 (define-find-write-binding-and-type :init-lexvar
(instruction)
6378 (destructuring-bind (binding &key init-with-register init-with-type
6379 protect-registers protect-carry
6382 (declare (ignore protect-registers protect-carry shared-reference-p
))
6386 ((not (typep init-with-register
'binding
))
6387 (assert init-with-type
)
6388 (values binding init-with-type
) )
6389 ((and init-with-type
(not (bindingp init-with-type
)))
6390 (values binding init-with-type
))
6391 ((and init-with-type
6392 (bindingp init-with-type
)
6393 (binding-store-type init-with-type
))
6394 (apply #'encoded-type-decode
(binding-store-type init-with-type
)))
6395 (t (values binding t
6397 (list init-with-register
)))))
6398 ((not (typep binding
'temporary-name
))
6399 (values binding t
)))))
6401 (define-find-read-bindings :init-lexvar
(binding &key init-with-register
&allow-other-keys
)
6402 (declare (ignore binding
))
6403 (when (typep init-with-register
'binding
)
6404 (list init-with-register
)))
6406 (define-extended-code-expander :init-lexvar
(instruction funobj frame-map
)
6407 (destructuring-bind (binding &key protect-registers protect-carry
6408 init-with-register init-with-type
6411 (declare (ignore protect-carry
)) ; nothing modifies carry anyway.
6412 ;; (assert (eq binding (ensure-local-binding binding funobj)))
6413 (assert (eq funobj
(binding-funobj binding
)))
6415 ((not (new-binding-located-p binding frame-map
))
6416 (unless (or (movitz-env-get (binding-name binding
) 'ignore nil
(binding-env binding
))
6417 (movitz-env-get (binding-name binding
) 'ignorable nil
(binding-env binding
)))))
6418 ((typep binding
'forwarding-binding
)
6419 ;; No need to do any initialization because the target will be initialized.
6420 (assert (not (binding-lended-p binding
)))
6422 (t (when (movitz-env-get (binding-name binding
) 'ignore nil
(binding-env binding
))
6423 (warn "Variable ~S used while declared ignored." (binding-name binding
)))
6426 ((typep binding
'rest-function-argument
)
6427 (assert (eq :edx init-with-register
))
6428 (assert (movitz-env-get (binding-name binding
)
6429 'dynamic-extent nil
(binding-env binding
))
6431 "&REST variable ~S must be dynamic-extent." (binding-name binding
))
6432 (setf (need-normalized-ecx-p (find-function-env (binding-env binding
)
6435 (let ((restify-alloca-loop (gensym "alloca-loop-"))
6436 (restify-done (gensym "restify-done-"))
6437 (restify-at-one (gensym "restify-at-one-"))
6438 (restify-loop (gensym "restify-loop-"))
6439 (save-ecx-p (key-vars-p (find-function-env (binding-env binding
)
6442 ;; (make-immediate-move (function-argument-argnum binding) :edx)
6443 ;; `((:call (:edi ,(global-constant-offset 'restify-dynamic-extent))))
6444 ;; Make space for (1+ (* 2 (- ECX rest-pos))) words on the stack.
6445 ;; Factor two is for one cons-cell per word, 1 is for 8-byte alignment.
6447 `((,*compiler-local-segment-prefix
*
6448 :movl
:ecx
(:edi
,(global-constant-offset 'raw-scratch0
)))))
6450 (:subl
,(function-argument-argnum binding
) :ecx
)
6451 (:jbe
',restify-done
)
6452 (:leal
((:ecx
8) 4) :edx
) ; EDX is fixnum counter
6453 ,restify-alloca-loop
6456 (:jnz
',restify-alloca-loop
)
6457 ,@(when *compiler-auto-stack-checks-p
*
6458 `((,*compiler-local-segment-prefix
*
6459 :bound
(:edi
,(global-constant-offset 'stack-bottom
)) :esp
)))
6460 (:leal
(:esp
5) :edx
)
6461 (:andl -
7 :edx
)) ; Make EDX a proper consp into the alloca area.
6463 ((= 0 (function-argument-argnum binding
))
6464 `((:movl
:eax
(:edx -
1))
6467 (:jz
',restify-done
)
6469 (:movl
:eax
(:eax -
5))))
6470 (t `((:movl
:edx
:eax
))))
6471 (when (>= 1 (function-argument-argnum binding
))
6472 `((:jmp
',restify-at-one
)))
6474 (:movl
(:ebp
(:ecx
4) 4) :ebx
)
6476 (:movl
:ebx
(:eax -
1))
6478 (:jz
',restify-done
)
6480 (:movl
:eax
(:eax -
5))
6481 (:jmp
',restify-loop
)
6484 `((,*compiler-local-segment-prefix
*
6485 :movl
(:edi
,(global-constant-offset 'raw-scratch0
)) :ecx
)))
6488 ((binding-lended-p binding
)
6489 (let* ((cons-position (getf (binding-lending binding
)
6490 :stack-cons-location
))
6491 (init-register (etypecase init-with-register
6492 ((or lexical-binding constant-object-binding
)
6493 (or (find-if (lambda (r)
6494 (not (member r protect-registers
)))
6496 (error "Unable to get a register.")))
6497 (keyword init-with-register
)
6499 (tmp-register (find-if (lambda (r)
6500 (and (not (member r protect-registers
))
6501 (not (eq r init-register
))))
6502 '(:edx
:ebx
:eax
))))
6503 (when init-with-register
6504 (assert (not (null init-with-type
))))
6505 (assert tmp-register
() ; solve this with push eax .. pop eax if ever needed.
6506 "Unable to find a tmp-register for ~S." instruction
)
6507 (append (when (typep init-with-register
'binding
)
6508 (make-load-lexical init-with-register init-register funobj
6509 shared-reference-p frame-map
6510 :protect-registers protect-registers
))
6511 `((:leal
(:ebp
,(1+ (stack-frame-offset (1+ cons-position
))))
6513 (:movl
:edi
(,tmp-register
3)) ; cdr
6514 (:movl
,init-register
(,tmp-register -
1)) ; car
6515 (:movl
,tmp-register
6516 (:ebp
,(stack-frame-offset
6517 (new-binding-location binding frame-map
))))))))
6518 ((typep init-with-register
'lexical-binding
)
6519 (make-load-lexical init-with-register binding funobj nil frame-map
))
6521 (make-store-lexical binding init-with-register nil funobj frame-map
))))))))
6523 ;;;;;;;;;;;;;;;;;; car
6525 (define-find-read-bindings :cons-get
(op cell dst
)
6526 (declare (ignore op dst protect-registers
))
6527 (when (typep cell
'binding
)
6530 (define-extended-code-expander :cons-get
(instruction funobj frame-map
)
6531 (destructuring-bind (op cell dst
)
6533 (check-type dst
(member :eax
:ebx
:ecx
:edx
))
6534 (multiple-value-bind (op-offset fast-op fast-op-ebx cl-op
)
6536 (:car
(values (bt:slot-offset
'movitz-cons
'car
)
6540 (:cdr
(values (bt:slot-offset
'movitz-cons
'cdr
)
6544 (let ((binding (binding-target (ensure-local-binding (binding-target cell
) funobj
))))
6546 (constant-object-binding
6547 (let ((x (constant-object binding
)))
6550 (make-load-constant *movitz-nil
* dst funobj frame-map
))
6552 (append (make-load-constant x dst funobj frame-map
)
6553 `((:movl
(,dst
,op-offset
) ,dst
))))
6554 (t `(,@(make-load-lexical binding
:eax funobj nil frame-map
)
6555 (,*compiler-global-segment-prefix
*
6556 :call
(:edi
,(global-constant-offset fast-op
)))
6557 ,@(when (not (eq dst
:eax
))
6558 `((:movl
:eax
,dst
))))))))
6560 (let ((location (new-binding-location (binding-target binding
) frame-map
))
6561 (binding-is-list-p (binding-store-subtypep binding
'list
)))
6562 #+ignore
(warn "~A of loc ~A bind ~A" op location binding
)
6564 ((and binding-is-list-p
6565 (member location
'(:eax
:ebx
:ecx
:edx
)))
6566 `((,*compiler-nonlocal-lispval-read-segment-prefix
*
6567 :movl
(,location
,op-offset
) ,dst
)))
6569 `(,@(make-load-lexical binding dst funobj nil frame-map
)
6570 (,*compiler-nonlocal-lispval-read-segment-prefix
*
6571 :movl
(,dst
,op-offset
) ,dst
)))
6572 ((not *compiler-use-cons-reader-segment-protocol-p
*)
6575 `((,*compiler-global-segment-prefix
*
6576 :call
(:edi
,(global-constant-offset fast-op-ebx
)))
6577 ,@(when (not (eq dst
:eax
))
6578 `((:movl
:eax
,dst
)))))
6579 (t `(,@(make-load-lexical binding
:eax funobj nil frame-map
)
6580 (,*compiler-global-segment-prefix
*
6581 :call
(:edi
,(global-constant-offset fast-op
)))
6582 ,@(when (not (eq dst
:eax
))
6583 `((:movl
:eax
,dst
)))))))
6585 ((member location
'(:ebx
:ecx
:edx
))
6586 `((,(or *compiler-cons-read-segment-prefix
*
6587 *compiler-nonlocal-lispval-read-segment-prefix
*)
6588 :movl
(:eax
,op-offset
) ,dst
)))
6589 (t (append (make-load-lexical binding
:eax funobj nil frame-map
)
6590 `((,(or *compiler-cons-read-segment-prefix
*
6591 *compiler-nonlocal-lispval-read-segment-prefix
*)
6592 :movl
(:eax
,op-offset
) ,dst
))))))))))))))
6595 ;;;;;;;;;;;;;;;;;; endp
6597 (define-find-read-bindings :endp
(cell result-mode
)
6598 (declare (ignore result-mode
))
6599 (when (typep cell
'binding
)
6602 (define-extended-code-expander :endp
(instruction funobj frame-map
)
6603 (destructuring-bind (cell result-mode
)
6605 (let ((binding (binding-target (ensure-local-binding (binding-target cell
) funobj
))))
6607 (constant-object-binding
6608 (let ((x (constant-object binding
)))
6611 (make-load-constant *movitz-nil
* result-mode funobj frame-map
))
6613 (make-load-constant (image-t-symbol *image
*) result-mode funobj frame-map
))
6616 (let* ((location (new-binding-location (binding-target binding
) frame-map
))
6617 (binding-is-list-p (binding-store-subtypep binding
'list
))
6618 (tmp-register (case location
6619 ((:eax
:ebx
:ecx
:edx
)
6621 ;; (warn "endp of loc ~A bind ~A" location binding)
6623 ((and binding-is-list-p
6624 (member location
'(:eax
:ebx
:ecx
:edx
)))
6625 (make-result-and-returns-glue result-mode
:boolean-zf
=1
6626 `((:cmpl
:edi
,location
))))
6627 ((eq :boolean-branch-on-true
(result-mode-type result-mode
))
6628 (let ((tmp-register (or tmp-register
:ecx
)))
6629 (append (make-load-lexical binding
6630 (cons :boolean-branch-on-false
6632 funobj nil frame-map
)
6633 (unless binding-is-list-p
6634 (append (make-load-lexical binding tmp-register funobj nil frame-map
)
6635 `((:leal
(,tmp-register -
1) :ecx
)
6637 (:jnz
'(:sub-program
(,(gensym "endp-not-list-"))
6639 (t (let ((tmp-register (or tmp-register
:eax
)))
6640 (append (make-load-lexical binding tmp-register funobj nil frame-map
)
6641 (unless binding-is-list-p
6642 `((:leal
(,tmp-register -
1) :ecx
)
6644 (:jnz
'(:sub-program
(,(gensym "endp-not-list-"))
6646 `((:cmpl
:edi
,tmp-register
))
6647 (make-result-and-returns-glue result-mode
:boolean-zf
=1)))))))))))
6650 ;;;;;;;;;;;;;;;;;; incf-lexvar
6652 (define-find-write-binding-and-type :incf-lexvar
(instruction)
6653 (destructuring-bind (binding delta
&key protect-registers
)
6655 (declare (ignore delta protect-registers
))
6656 (values binding
'integer
)))
6658 (define-find-read-bindings :incf-lexvar
(binding delta
&key protect-registers
)
6659 (declare (ignore delta protect-registers binding
))
6662 (define-extended-code-expander :incf-lexvar
(instruction funobj frame-map
)
6663 (break "incf-lexvar??")
6664 (destructuring-bind (binding delta
&key protect-registers
)
6666 (check-type binding binding
)
6667 (check-type delta integer
)
6668 (let* ((binding (binding-target binding
))
6669 (location (new-binding-location binding frame-map
:default nil
))
6670 (binding-type (binding-store-type binding
)))
6671 ;;; (warn "incf b ~A, loc: ~A, typ: ~A" binding location binding-type)
6675 (not (binding-lended-p binding
))
6676 (binding-store-subtypep binding
'integer
))
6677 ;; This is an optimized incf that doesn't have to do type-checking.
6678 (check-type location
(integer 1 *))
6679 `((:addl
,(* delta
+movitz-fixnum-factor
+)
6680 (:ebp
,(stack-frame-offset location
)))
6682 ((binding-store-subtypep binding
'integer
)
6683 (let ((register (chose-free-register protect-registers
)))
6684 `(,@(make-load-lexical (ensure-local-binding binding funobj
)
6685 register funobj nil frame-map
6686 :protect-registers protect-registers
)
6687 (:addl
,(* delta
+movitz-fixnum-factor
+) :eax
)
6689 ,@(make-store-lexical (ensure-local-binding binding funobj
)
6690 register nil funobj frame-map
6691 :protect-registers protect-registers
))))
6692 (t (let ((register (chose-free-register protect-registers
)))
6693 `(,@(make-load-lexical (ensure-local-binding binding funobj
)
6694 register funobj nil frame-map
6695 :protect-registers protect-registers
)
6696 (:testb
,+movitz-fixnum-zmask
+ ,(register32-to-low8 register
))
6697 (:jnz
'(:sub-program
(,(gensym "not-integer-"))
6700 (:addl
,(* delta
+movitz-fixnum-factor
+) ,register
)
6702 ,@(make-store-lexical (ensure-local-binding binding funobj
)
6703 register nil funobj frame-map
6704 :protect-registers protect-registers
))))))))
6708 (define-find-write-binding-and-type :load-constant
(instruction)
6709 (destructuring-bind (object result-mode
&key
(op :movl
))
6711 (when (and (eq op
:movl
) (typep result-mode
'binding
))
6712 (check-type result-mode lexical-binding
)
6713 (values result-mode
`(eql ,object
)))))
6715 (define-extended-code-expander :load-constant
(instruction funobj frame-map
)
6716 (destructuring-bind (object result-mode
&key
(op :movl
))
6718 (make-load-constant object result-mode funobj frame-map
:op op
)))
6722 (define-find-write-binding-and-type :add
(instruction)
6723 (destructuring-bind (term0 term1 destination
)
6725 (when (typep destination
'binding
)
6726 (assert (and (bindingp term0
) (bindingp term1
)))
6729 (lambda (type0 type1
)
6730 (let ((x (multiple-value-call #'encoded-integer-types-add
6731 (type-specifier-encode type0
)
6732 (type-specifier-encode type1
))))
6733 #+ignore
(warn "thunked: ~S ~S -> ~S" term0 term1 x
)
6738 (define-find-used-bindings :add
(term0 term1 destination
)
6739 (if (bindingp destination
)
6740 (list term0 term1 destination
)
6741 (list term0 term1
)))
6743 (define-find-read-bindings :add
(term0 term1 destination
)
6744 (declare (ignore destination
))
6745 (let* ((type0 (and (binding-store-type term0
)
6746 (apply #'encoded-type-decode
(binding-store-type term0
))))
6747 (type1 (and (binding-store-type term1
)
6748 (apply #'encoded-type-decode
(binding-store-type term1
))))
6749 (singleton0 (and type0
(type-specifier-singleton type0
)))
6750 (singleton1 (and type1
(type-specifier-singleton type1
)))
6751 (singleton-sum (and singleton0 singleton1
6752 (type-specifier-singleton
6753 (apply #'encoded-integer-types-add
6754 (append (binding-store-type term0
)
6755 (binding-store-type term1
)))))))
6758 (let ((b (make-instance 'constant-object-binding
6759 :name
(gensym "constant-sum")
6760 :object
(car singleton-sum
))))
6761 (movitz-env-add-binding (binding-env term0
) b
)
6763 (t (append (unless (and singleton0
(typep (car singleton0
) 'movitz-fixnum
))
6765 (unless (and singleton1
(typep (car singleton1
) 'movitz-fixnum
))
6768 (define-extended-code-expander :add
(instruction funobj frame-map
)
6769 (destructuring-bind (term0 term1 destination
)
6771 (assert (and (bindingp term0
)
6773 (member (result-mode-type destination
)
6774 '(:lexical-binding
:function
:multple-values
:eax
:ebx
:ecx
:edx
))))
6775 (let* ((destination (ensure-local-binding destination funobj
))
6776 (term0 (ensure-local-binding term0 funobj
))
6777 (term1 (ensure-local-binding term1 funobj
))
6778 (destination-location (if (or (not (bindingp destination
))
6779 (typep destination
'borrowed-binding
))
6781 (new-binding-location (binding-target destination
)
6784 (type0 (apply #'encoded-type-decode
(binding-store-type term0
)))
6785 (type1 (apply #'encoded-type-decode
(binding-store-type term1
)))
6786 (result-type (multiple-value-call #'encoded-integer-types-add
6787 (values-list (binding-store-type term0
))
6788 (values-list (binding-store-type term1
)))))
6789 ;; A null location means the binding is unused, in which
6790 ;; case there's no need to perform the addition.
6791 (when destination-location
6792 (let ((loc0 (new-binding-location (binding-target term0
) frame-map
:default nil
))
6793 (loc1 (new-binding-location (binding-target term1
) frame-map
:default nil
)))
6795 (warn "add: ~A for ~A" instruction result-type
)
6797 (warn "add for: ~S is ~A, from ~A/~A and ~A/~A."
6798 destination result-type
6802 (when (eql destination-location
9)
6803 (warn "add for: ~S/~S~%= ~A/~A in ~S~&~A/~A in ~S."
6804 destination destination-location
6805 term0 loc0
(binding-extent-env (binding-target term0
))
6806 term1 loc1
(binding-extent-env (binding-target term1
)))
6807 (print-code 'load-term1
(make-load-lexical term1
:eax funobj nil frame-map
))
6808 (print-code 'load-dest
(make-load-lexical destination
:eax funobj nil frame-map
)))
6809 (flet ((make-store (source destination
)
6811 ((eq source destination
)
6813 ((member destination
'(:eax
:ebx
:ecx
:edx
))
6814 `((:movl
,source
,destination
)))
6815 (t (make-store-lexical destination source nil funobj frame-map
))))
6816 (make-default-add ()
6817 (when (movitz-subtypep result-type
'(unsigned-byte 32))
6818 (warn "Defaulting u32 ADD: ~A/~S = ~A/~S + ~A/~S"
6819 destination-location
6824 ((type-specifier-singleton type0
)
6825 (append (make-load-lexical term1
:eax funobj nil frame-map
)
6826 (make-load-constant (car (type-specifier-singleton type0
))
6827 :ebx funobj frame-map
)))
6828 ((type-specifier-singleton type1
)
6829 (append (make-load-lexical term0
:eax funobj nil frame-map
)
6830 (make-load-constant (car (type-specifier-singleton type1
))
6831 :ebx funobj frame-map
)))
6832 ((and (eq :eax loc0
) (eq :ebx loc1
))
6834 ((and (eq :ebx loc0
) (eq :eax loc1
))
6835 nil
) ; terms order isn't important
6838 (make-load-lexical term0
:ebx funobj nil frame-map
)))
6840 (make-load-lexical term0
:eax funobj nil frame-map
)
6841 (make-load-lexical term1
:ebx funobj nil frame-map
))))
6842 `((:movl
(:edi
,(global-constant-offset '+)) :esi
))
6843 (make-compiled-funcall-by-esi 2)
6844 (etypecase destination
6846 (unless (eq destination
:eax
)
6847 `((:movl
:eax
,destination
))))
6849 (make-store-lexical destination
:eax nil funobj frame-map
))))))
6850 (let ((constant0 (let ((x (type-specifier-singleton type0
)))
6851 (when (and x
(typep (car x
) 'movitz-fixnum
))
6852 (movitz-immediate-value (car x
)))))
6853 (constant1 (let ((x (type-specifier-singleton type1
)))
6854 (when (and x
(typep (car x
) 'movitz-fixnum
))
6855 (movitz-immediate-value (car x
))))))
6857 ((type-specifier-singleton result-type
)
6858 ;; (break "constant add: ~S" instruction)
6859 (make-load-constant (car (type-specifier-singleton result-type
))
6860 destination funobj frame-map
))
6861 ((movitz-subtypep type0
'(integer 0 0))
6863 ((eql destination loc1
)
6864 #+ignore
(break "NOP add: ~S" instruction
)
6866 ((and (member destination-location
'(:eax
:ebx
:ecx
:edx
))
6867 (member loc1
'(:eax
:ebx
:ecx
:edx
)))
6868 `((:movl
,loc1
,destination-location
)))
6870 (make-load-lexical term1 destination funobj nil frame-map
))
6872 ((integerp destination-location
)
6873 (make-store-lexical destination-location loc1 nil funobj frame-map
))
6874 (t (break "Unknown X zero-add: ~S" instruction
))))
6875 ((movitz-subtypep type1
'(integer 0 0))
6876 ;; (warn "zero-add ~S => ~S [~S]" loc0 destination-location result-type)
6878 ((eql destination-location loc0
)
6879 #+ignore
(break "NOP add: ~S" instruction
)
6881 ((and (member destination-location
'(:eax
:ebx
:ecx
:edx
))
6882 (member loc0
'(:eax
:ebx
:ecx
:edx
)))
6883 `((:movl
,loc0
,destination-location
)))
6884 ((member loc0
'(:eax
:ebx
:ecx
:edx
))
6885 (make-store-lexical destination loc0 nil funobj frame-map
))
6887 (make-load-lexical term0 destination funobj nil frame-map
))
6888 (t (break "Unknown Y zero-add: ~S" instruction
))))
6889 ((and (movitz-subtypep type0
'fixnum
)
6890 (movitz-subtypep type1
'fixnum
)
6891 (movitz-subtypep result-type
'fixnum
))
6892 (assert (not (and constant0
(zerop constant0
))))
6893 (assert (not (and constant1
(zerop constant1
))))
6895 ((and (not (binding-lended-p (binding-target term0
)))
6896 (not (binding-lended-p (binding-target term1
)))
6897 (not (and (bindingp destination
)
6898 (binding-lended-p (binding-target destination
)))))
6901 (equal loc1 destination-location
))
6903 ((member destination-location
'(:eax
:ebx
:ecx
:edx
))
6904 `((:addl
,constant0
,destination-location
)))
6906 `((:addl
,constant0
(:ebp
,(stack-frame-offset loc1
)))))
6907 ((eq :argument-stack
(operator loc1
))
6909 (:ebp
,(argument-stack-offset (binding-target term1
))))))
6910 ((eq :untagged-fixnum-ecx
(operator loc1
))
6911 `((:addl
,(truncate constant0
+movitz-fixnum-factor
+) :ecx
)))
6912 (t (error "Don't know how to add this for loc1 ~S" loc1
))))
6914 (integerp destination-location
)
6915 (eql term1 destination-location
))
6917 `((:addl
,constant0
(:ebp
,(stack-frame-offset destination-location
)))))
6919 (integerp destination-location
)
6920 (member loc1
'(:eax
:ebx
:ecx
:edx
)))
6921 `((:addl
,constant0
,loc1
)
6922 (:movl
,loc1
(:ebp
,(stack-frame-offset destination-location
)))))
6923 ((and (integerp loc0
)
6925 (member destination-location
'(:eax
:ebx
:ecx
:edx
)))
6926 (append `((:movl
(:ebp
,(stack-frame-offset loc0
)) ,destination-location
)
6927 (:addl
(:ebp
,(stack-frame-offset loc1
)) ,destination-location
))))
6928 ((and (integerp destination-location
)
6929 (eql loc0 destination-location
)
6931 `((:addl
,constant1
(:ebp
,(stack-frame-offset destination-location
)))))
6932 ((and (integerp destination-location
)
6933 (eql loc1 destination-location
)
6935 `((:addl
,constant0
(:ebp
,(stack-frame-offset destination-location
)))))
6936 ((and (member destination-location
'(:eax
:ebx
:ecx
:edx
))
6937 (eq loc0
:untagged-fixnum-ecx
)
6939 `((:leal
((:ecx
,+movitz-fixnum-factor
+) ,constant1
)
6940 ,destination-location
)))
6941 ((and (member destination-location
'(:eax
:ebx
:ecx
:edx
))
6944 `((:movl
(:ebp
,(stack-frame-offset loc1
)) ,destination-location
)
6945 (:addl
,constant0
,destination-location
)))
6946 ((and (member destination-location
'(:eax
:ebx
:ecx
:edx
))
6949 `((:movl
(:ebp
,(stack-frame-offset loc0
)) ,destination-location
)
6950 (:addl
,constant1
,destination-location
)))
6951 ((and (member destination-location
'(:eax
:ebx
:ecx
:edx
))
6953 (member loc1
'(:eax
:ebx
:ecx
:edx
))
6954 (not (eq destination-location loc1
)))
6955 `((:movl
(:ebp
,(stack-frame-offset loc0
)) ,destination-location
)
6956 (:addl
,loc1
,destination-location
)))
6957 ((and (member destination-location
'(:eax
:ebx
:ecx
:edx
))
6959 (member loc1
'(:eax
:ebx
:ecx
:edx
)))
6960 `((:leal
(,loc1
,constant0
) ,destination-location
)))
6961 ((and (member destination-location
'(:eax
:ebx
:ecx
:edx
))
6963 (member loc0
'(:eax
:ebx
:ecx
:edx
)))
6964 `((:leal
(,loc0
,constant1
) ,destination-location
)))
6965 ((and (member destination-location
'(:eax
:ebx
:ecx
:edx
))
6967 (eq :argument-stack
(operator loc1
)))
6968 `((:movl
(:ebp
,(argument-stack-offset (binding-target term1
)))
6969 ,destination-location
)
6970 (:addl
,constant0
,destination-location
)))
6971 ((and (member destination-location
'(:eax
:ebx
:ecx
:edx
))
6973 (eq :argument-stack
(operator loc0
)))
6974 `((:movl
(:ebp
,(argument-stack-offset (binding-target term0
)))
6975 ,destination-location
)
6976 (:addl
,constant1
,destination-location
)))
6978 (append (make-load-lexical term1
:eax funobj nil frame-map
)
6979 `((:addl
,constant0
:eax
))
6980 (make-store :eax destination
)))
6982 (append (make-load-lexical term0
:eax funobj nil frame-map
)
6983 `((:addl
,constant1
:eax
))
6984 (make-store :eax destination
)))
6986 (append (make-load-lexical term0
:eax funobj nil frame-map
)
6987 `((:addl
:eax
:eax
))
6988 (make-store :eax destination
)))
6989 ((and (integerp loc0
)
6991 (integerp destination-location
)
6992 (/= loc0 loc1 destination-location
))
6993 `((:movl
(:ebp
,(stack-frame-offset loc0
))
6995 (:addl
(:ebp
,(stack-frame-offset loc1
))
6997 (:movl
:ecx
(:ebp
,(stack-frame-offset destination-location
)))))
6998 (t (warn "Unknown fixnum ADD: ~A/~S = ~A/~S + ~A/~S"
6999 destination-location
7003 #+ignore
(warn "map: ~A" frame-map
)
7004 ;;; (warn "ADDI: ~S" instruction)
7006 ((type-specifier-singleton type0
)
7007 (append (make-load-lexical term1
:eax funobj nil frame-map
)
7008 (make-load-constant (car (type-specifier-singleton type0
))
7009 :ebx funobj frame-map
)))
7010 ((type-specifier-singleton type1
)
7011 (append (make-load-lexical term0
:eax funobj nil frame-map
)
7012 (make-load-constant (car (type-specifier-singleton type1
))
7013 :ebx funobj frame-map
)))
7014 ((and (eq :eax loc0
) (eq :ebx loc1
))
7016 ((and (eq :ebx loc0
) (eq :eax loc1
))
7017 nil
) ; terms order isn't important
7020 (make-load-lexical term0
:ebx funobj nil frame-map
)))
7022 (make-load-lexical term0
:eax funobj nil frame-map
)
7023 (make-load-lexical term1
:ebx funobj nil frame-map
))))
7024 `((:movl
(:edi
,(global-constant-offset '+)) :esi
))
7025 (make-compiled-funcall-by-esi 2)
7026 (etypecase destination
7028 (unless (eq destination
:eax
)
7029 `((:movl
:eax
,destination
))))
7031 (make-store-lexical destination
:eax nil funobj frame-map
)))))))
7033 (integerp destination-location
)
7034 (eql loc1 destination-location
)
7035 (binding-lended-p (binding-target destination
)))
7036 (assert (binding-lended-p (binding-target term1
)))
7037 (append (make-load-lexical destination
:eax funobj t frame-map
)
7038 `((:addl
,constant0
(-1 :eax
)))))
7039 ((warn "~S" (list (and (bindingp destination
)
7040 (binding-lended-p (binding-target destination
)))
7041 (binding-lended-p (binding-target term0
))
7042 (binding-lended-p (binding-target term1
)))))
7043 (t (warn "Unknown fixnum add: ~S" instruction
)
7044 (make-default-add))))
7045 ((and (movitz-subtypep type0
'fixnum
)
7046 (movitz-subtypep type1
'fixnum
))
7047 (flet ((mkadd-into (src destreg
)
7048 (assert (eq destreg
:eax
) (destreg)
7049 "Movitz' INTO protocol says the overflowed value must be in EAX, ~
7050 but it's requested to be in ~S."
7052 (let ((srcloc (new-binding-location (binding-target src
) frame-map
)))
7053 (unless (eql srcloc loc1
) (break))
7054 (if (integerp srcloc
)
7055 `((:addl
(:ebp
,(stack-frame-offset srcloc
))
7058 (ecase (operator srcloc
)
7059 ((:eax
:ebx
:ecx
:edx
)
7060 `((:addl
,srcloc
,destreg
)
7063 `((:addl
(:ebx
,(argument-stack-offset src
))
7068 ((and (not constant0
)
7070 (not (binding-lended-p (binding-target term0
)))
7071 (not (binding-lended-p (binding-target term1
)))
7072 (not (and (bindingp destination
)
7073 (binding-lended-p (binding-target destination
)))))
7075 ((and (not (eq loc0
:untagged-fixnum-ecx
))
7076 (not (eq loc1
:untagged-fixnum-ecx
))
7077 (not (eq destination-location
:untagged-fixnum-ecx
)))
7079 ((and (eq loc0
:eax
) (eq loc1
:eax
))
7083 (mkadd-into term1
:eax
))
7085 (mkadd-into term0
:eax
))
7086 (t (append (make-load-lexical term0
:eax funobj nil frame-map
7087 :protect-registers
(list loc1
))
7088 (mkadd-into term1
:eax
))))
7089 (make-store :eax destination
)))
7090 (t (make-default-add)
7092 (append (make-load-lexical term0
:untagged-fixnum-ecx funobj nil frame-map
)
7093 `((,*compiler-local-segment-prefix
*
7094 :movl
:ecx
(:edi
,(global-constant-offset 'raw-scratch0
))))
7095 (make-load-lexical term1
:untagged-fixnum-ecx funobj nil frame-map
)
7096 `((,*compiler-local-segment-prefix
*
7097 :addl
(:edi
,(global-constant-offset 'raw-scratch0
)) :ecx
))
7098 (if (integerp destination-location
)
7099 `((,*compiler-local-segment-prefix
*
7100 :call
(:edi
,(global-constant-offset 'box-u32-ecx
)))
7101 (:movl
:eax
(:ebp
,(stack-frame-offset destination-location
))))
7102 (ecase (operator destination-location
)
7103 ((:untagged-fixnum-ecx
)
7106 `((,*compiler-local-segment-prefix
*
7107 :call
(:edi
,(global-constant-offset 'box-u32-ecx
)))))
7109 `((,*compiler-local-segment-prefix
*
7110 :call
(:edi
,(global-constant-offset 'box-u32-ecx
)))
7111 (:movl
:eax
,destination-location
)))
7113 `((,*compiler-local-segment-prefix
*
7114 :call
(:edi
,(global-constant-offset 'box-u32-ecx
)))
7115 (:movl
:eax
(:ebp
,(argument-stack-offset
7116 (binding-target destination
))))))))))))
7117 (t (make-default-add)))))
7118 (t (make-default-add))))))))))
7122 (define-find-read-bindings :eql
(x y mode
)
7123 (declare (ignore mode
))
7126 (define-extended-code-expander :eql
(instruction funobj frame-map
)
7127 (destructuring-bind (x y return-mode
)
7129 (let* ((x-type (apply #'encoded-type-decode
(binding-store-type x
)))
7130 (y-type (apply #'encoded-type-decode
(binding-store-type y
)))
7131 (x-singleton (type-specifier-singleton x-type
))
7132 (y-singleton (type-specifier-singleton y-type
)))
7133 (when (and y-singleton
(not x-singleton
))
7135 (rotatef x-type y-type
)
7136 (rotatef x-singleton y-singleton
))
7137 (let (#+ignore
(x-loc (new-binding-location (binding-target x
) frame-map
:default nil
))
7138 (y-loc (new-binding-location (binding-target y
) frame-map
:default nil
)))
7140 (warn "eql ~S/~S xx~Xxx ~S/~S: ~S"
7141 x x-loc
(binding-target y
)
7144 (flet ((make-branch ()
7145 (ecase (operator return-mode
)
7146 (:boolean-branch-on-false
7147 `((:jne
',(operands return-mode
))))
7148 (:boolean-branch-on-true
7149 `((:je
',(operands return-mode
))))
7151 (make-load-eax-ebx ()
7153 (make-load-lexical x
:ebx funobj nil frame-map
)
7154 (append (make-load-lexical x
:eax funobj nil frame-map
)
7155 (make-load-lexical y
:ebx funobj nil frame-map
)))))
7157 ((and x-singleton y-singleton
)
7158 (let ((eql (etypecase (car x-singleton
)
7159 (movitz-immediate-object
7160 (and (typep (car y-singleton
) 'movitz-immediate-object
)
7161 (eql (movitz-immediate-value (car x-singleton
))
7162 (movitz-immediate-value (car y-singleton
))))))))
7163 (case (operator return-mode
)
7164 (:boolean-branch-on-false
7166 `((:jmp
',(operands return-mode
)))))
7167 (t (break "Constant EQL: ~S ~S" (car x-singleton
) (car y-singleton
))))))
7169 (eq :untagged-fixnum-ecx y-loc
))
7170 (let ((value (etypecase (car x-singleton
)
7172 (movitz-fixnum-value (car x-singleton
)))
7174 (movitz-bignum-value (car x-singleton
))))))
7175 (check-type value
(unsigned-byte 32))
7176 `((:cmpl
,value
:ecx
)
7179 (typep (car x-singleton
) '(or movitz-immediate-object movitz-null
)))
7180 (let ((value (if (typep (car x-singleton
) 'movitz-null
)
7182 (movitz-immediate-value (car x-singleton
)))))
7185 (member y-loc
'(:eax
:ebx
:ecx
:edx
)))
7186 `((:testl
,y-loc
,y-loc
)))
7187 ((and (member y-loc
'(:eax
:ebx
:ecx
:edx
))
7188 (not (binding-lended-p y
)))
7189 `((:cmpl
,value
,y-loc
)))
7190 ((and (integerp y-loc
)
7191 (not (binding-lended-p y
)))
7192 `((:cmpl
,value
(:ebp
,(stack-frame-offset y-loc
)))))
7193 ((and (eq :argument-stack
(operator y-loc
))
7194 (not (binding-lended-p y
)))
7195 `((:cmpl
,value
(:ebp
,(argument-stack-offset (binding-target y
))))))
7196 (t (break "x-singleton: ~S with loc ~S"
7197 (movitz-immediate-value (car x-singleton
))
7201 (typep (car x-singleton
) 'movitz-symbol
)
7202 (member y-loc
'(:eax
:ebx
:edx
)))
7203 (append (make-load-constant (car x-singleton
) y-loc funobj frame-map
:op
:cmpl
)
7206 (break "y-singleton"))
7207 ((and (not (eq t x-type
)) ; this is for bootstrapping purposes.
7208 (not (eq t y-type
)) ; ..
7209 (or (movitz-subtypep x-type
'(or fixnum character symbol vector
))
7210 (movitz-subtypep y-type
'(or fixnum character symbol vector
))))
7211 (append (make-load-eax-ebx)
7212 `((:cmpl
:eax
:ebx
))
7215 ((warn "eql ~S/~S ~S/~S"
7218 ((eq :boolean-branch-on-false
(operator return-mode
))
7219 (let ((eql-done (gensym "eql-done-"))
7220 (on-false-label (operands return-mode
)))
7221 (append (make-load-eax-ebx)
7224 (,*compiler-global-segment-prefix
*
7225 :movl
(:edi
,(global-constant-offset 'complicated-eql
)) :esi
)
7226 (:call
(:esi
,(bt:slot-offset
'movitz-funobj
'code-vector%
2op
)))
7227 (:jne
',on-false-label
)
7229 ((eq :boolean-branch-on-true
(operator return-mode
))
7230 (let ((on-true-label (operands return-mode
)))
7231 (append (make-load-eax-ebx)
7233 (:je
',on-true-label
)
7234 (,*compiler-global-segment-prefix
*
7235 :movl
(:edi
,(global-constant-offset 'complicated-eql
)) :esi
)
7236 (:call
(:esi
,(bt:slot-offset
'movitz-funobj
'code-vector%
2op
)))
7237 (:je
',on-true-label
)))))
7238 ((eq return-mode
:boolean-zf
=1)
7239 (append (make-load-eax-ebx)
7240 (let ((eql-done (gensym "eql-done-")))
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
)))
7247 (t (error "unknown eql: ~S" instruction
))))))))
7249 (define-find-read-bindings :load-lambda
(lambda-binding result-mode capture-env
)
7250 (declare (ignore result-mode capture-env
))
7251 (let ((allocation (movitz-allocation (function-binding-funobj lambda-binding
))))
7252 (when (typep allocation
'with-dynamic-extent-scope-env
)
7253 (values (list (base-binding allocation
))
7256 (define-find-write-binding-and-type :enter-dynamic-scope
(instruction)
7257 (destructuring-bind (scope-env)
7259 (if (null (dynamic-extent-scope-members scope-env
))
7261 (values (base-binding scope-env
) 'fixnum
))))
7263 (define-extended-code-expander :enter-dynamic-scope
(instruction funobj frame-map
)
7264 (declare (ignore funobj frame-map
))
7265 (destructuring-bind (scope-env)
7267 (if (null (dynamic-extent-scope-members scope-env
))
7269 (append `((:pushl
:edi
)
7273 (loop for object in
(reverse (dynamic-extent-scope-members scope-env
))
7280 (append (unless (zerop (mod (sizeof object
) 8))
7282 `((:load-constant
,object
:eax
))
7283 (loop for i from
(1- (movitz-funobj-num-constants object
))
7284 downto
(movitz-funobj-num-jumpers object
)
7285 collect
`(:pushl
(:eax
,(slot-offset 'movitz-funobj
'constant0
)
7287 (loop repeat
(movitz-funobj-num-jumpers object
)
7288 collect
`(:pushl
0))
7289 `((:pushl
(:eax
,(slot-offset 'movitz-funobj
'num-jumpers
)))
7290 (:pushl
(:eax
,(slot-offset 'movitz-funobj
'name
)))
7291 (:pushl
(:eax
,(slot-offset 'movitz-funobj
'lambda-list
)))
7296 (:pushl
2) ; (default) 2 is recognized by map-header-vals as non-initialized funobj.
7298 (:pushl
(:eax
,(slot-offset 'movitz-funobj
'type
)))
7299 (:leal
(:esp
,(tag :other
)) :ebx
)
7300 (,*compiler-local-segment-prefix
*
7301 :call
(:edi
,(global-constant-offset 'copy-funobj-code-vector-slots
)))
7304 ;;;(define-extended-code-expander :exit-dynamic-scope (instruction funobj frame-map)
7307 (define-find-read-bindings :lexical-control-transfer
(return-code return-mode from-env to-env
7309 (declare (ignore return-code return-mode to-label
))
7310 (let ((distance (stack-delta from-env to-env
)))
7311 (when (eq t distance
)
7312 (values (list (movitz-binding (save-esp-variable to-env
) to-env nil
))
7315 (define-find-read-bindings :stack-cons
(proto-cons scope-env
)
7316 (declare (ignore proto-cons
))
7317 (values (list (base-binding scope-env
))
7320 (define-extended-code-expander :stack-cons
(instruction funobj frame-map
)
7321 (destructuring-bind (proto-cons dynamic-scope
)
7323 (append (make-load-lexical (base-binding dynamic-scope
) :edx
7324 funobj nil frame-map
)
7325 `((:movl
:eax
(:edx
,(dynamic-extent-object-offset dynamic-scope proto-cons
)))
7326 (:movl
:ebx
(:edx
,(+ 4 (dynamic-extent-object-offset dynamic-scope proto-cons
))))
7327 (:leal
(:edx
,(+ (tag :cons
) (dynamic-extent-object-offset dynamic-scope proto-cons
)))