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