Update of /project/movitz/cvsroot/movitz
In directory common-lisp.net:/tmp/cvs-serv3296
Modified Files:
compiler.lisp
Log Message:
Some more work on register scheduling. I'm starting to see how this
should have been designed in the first place.
Date: Tue Feb 17 15:23:51 2004
Author: ffjeld
Index: movitz/compiler.lisp
diff -u movitz/compiler.lisp:1.29 movitz/compiler.lisp:1.30
--- movitz/compiler.lisp:1.29 Mon Feb 16 20:42:50 2004
+++ movitz/compiler.lisp Tue Feb 17 15:23:51 2004
@@ -8,7 +8,7 @@
;;;; Created at: Wed Oct 25 12:30:49 2000
;;;; Distribution: See the accompanying file COPYING.
;;;;
-;;;; $Id: compiler.lisp,v 1.29 2004/02/17 01:42:50 ffjeld Exp $
+;;;; $Id: compiler.lisp,v 1.30 2004/02/17 20:23:51 ffjeld Exp $
;;;;
;;;;------------------------------------------------------------------
@@ -432,7 +432,7 @@
(setf more-binding-references-p t))))))
binding-usage))
(when more-binding-references-p
- (warn "Unable to remove all binding-references duding lexical type analysis."))
+ (warn "Unable to remove all binding-references during lexical type analysis."))
;; 3.
(maphash (lambda (binding analysis)
(assert (null (type-analysis-binding-types analysis)) ()
@@ -442,6 +442,8 @@
(type-analysis-binding-types analysis))
(setf (binding-store-type binding)
(type-analysis-encoded-type analysis))
+ (when (apply #'encoded-type-singleton (type-analysis-encoded-type analysis))
+ (warn "Singleton: ~A" binding))
#+ignore
(when (or #+ignore (not (apply #'encoded-allp (type-analysis-encoded-type analysis)))
#+ignore (multiple-value-call #'encoded-subtypep
@@ -504,7 +506,7 @@
(when (listp instruction)
(let ((store-binding (find-written-binding-and-type instruction)))
(when store-binding
- (process-binding funobj store-binding '(:read))))
+ (process-binding funobj store-binding '(:write))))
(dolist (load-binding (find-read-bindings instruction))
(process-binding funobj load-binding '(:read)))
(case (car instruction)
@@ -1375,691 +1377,697 @@
(error "Peephole-optimizer recursive count reached ~D.
There is (propably) a bug in the peephole optimizer." recursive-count))
;; (warn "==================OPTIMIZE: ~{~&~A~}" unoptimized-code)
- (labels
- ((explain (always format &rest args)
- (when (or always *explain-peephole-optimizations*)
- (warn "Peephole: ~?~&----------------------------" format args)))
- (twop-p (c &optional op)
- (let ((c (ignore-instruction-prefixes c)))
- (and (listp c) (= 3 (length c))
- (or (not op) (eq op (first c)))
- (cdr c))))
- (twop-dst (c &optional op src)
- (let ((c (ignore-instruction-prefixes c)))
- (and (or (not src)
- (equal src (first (twop-p c op))))
- (second (twop-p c op)))))
- (twop-src (c &optional op dest)
- (let ((c (ignore-instruction-prefixes c)))
- (and (or (not dest)
- (equal dest (second (twop-p c op))))
- (first (twop-p c op)))))
- #+ignore
- (isrc (c)
- (let ((c (ignore-instruction-prefixes c)))
- (ecase (length (cdr c))
- (0 nil)
- (1 (cadr c))
- (2 (twop-src c)))))
- (idst (c)
- (let ((c (ignore-instruction-prefixes c)))
- (ecase (length (cdr c))
- (0 nil)
- (1 (cadr c))
- (2 (twop-dst c)))))
- (non-destructuve-p (c)
- (let ((c (ignore-instruction-prefixes c)))
- (and (consp c)
- (member (car c) '(:testl :testb :pushl :cmpl :cmpb :frame-map)))))
- (simple-instruction-p (c)
- (let ((c (ignore-instruction-prefixes c)))
- (and (listp c)
- (member (car c) '(:movl :xorl :popl :cmpl :leal :andl :addl :subl)))))
- (register-indirect-operand (op base)
- (multiple-value-bind (reg off)
- (when (listp op)
- (loop for x in op
- if (integerp x) sum x into off
- else collect x into reg
- finally (return (values reg off))))
- (and (eq base (car reg))
- (not (rest reg))
- off)))
- (stack-frame-operand (op)
- (register-indirect-operand op :ebp))
- (funobj-constant-operand (op)
- (register-indirect-operand op :esi))
- (global-constant-operand (op)
- (register-indirect-operand op :edi))
- (global-funcall-p (op &optional funs)
- (let ((op (ignore-instruction-prefixes op)))
- (when (instruction-is op :call)
- (let ((x (global-constant-operand (second op))))
- (flet ((try (name)
- (and (eql x (slot-offset 'movitz-constant-block name))
- name)))
- (cond
- ((not x) nil)
- ((null funs) t)
- ((atom funs) (try funs))
- (t (some #'try funs))))))))
- (preserves-stack-location-p (i stack-location)
- (let ((i (ignore-instruction-prefixes i)))
- (and (not (atom i))
- (or (global-funcall-p i)
- (instruction-is i :frame-map)
- (branch-instruction-label i)
- (non-destructuve-p i)
- (and (simple-instruction-p i)
- (not (eql stack-location (stack-frame-operand (idst i)))))))))
- (preserves-register-p (i register)
- (let ((i (ignore-instruction-prefixes i)))
- (and (not (atom i))
- (or (and (member register '(:edx))
- (member (global-funcall-p i)
- '(fast-car fast-cdr fast-car-ebx fast-cdr-ebx)))
- (instruction-is i :frame-map)
- (branch-instruction-label i)
- (non-destructuve-p i)
- (and (simple-instruction-p i)
- (not (eq register (idst i))))))))
- (register-operand (op)
- (and (member op '(:eax :ebx :ecx :edx :edi))
- op))
- (true-and-equal (x &rest more)
- (declare (dynamic-extent more))
- (and x (dolist (y more t)
- (unless (equal x y)
- (return nil)))))
- #+ignore
- (uses-stack-frame-p (c)
- (and (consp c)
- (some #'stack-frame-operand (cdr c))))
- (load-stack-frame-p (c &optional (op :movl))
- (stack-frame-operand (twop-src c op)))
- (store-stack-frame-p (c &optional (op :movl))
- (stack-frame-operand (twop-dst c op)))
- (read-stack-frame-p (c)
- (or (load-stack-frame-p c :movl)
- (load-stack-frame-p c :cmpl)
- (store-stack-frame-p c :cmpl)
+ (macrolet ((explain (always format &rest args)
+ `(when (or *explain-peephole-optimizations* ,always)
+ (warn "Peephole: ~@?~&----------------------------" ,format ,@args))))
+ (labels
+ (#+ignore
+ (explain (always format &rest args)
+ (when (or always *explain-peephole-optimizations*)
+ (warn "Peephole: ~?~&----------------------------" format args)))
+ (twop-p (c &optional op)
+ (let ((c (ignore-instruction-prefixes c)))
+ (and (listp c) (= 3 (length c))
+ (or (not op) (eq op (first c)))
+ (cdr c))))
+ (twop-dst (c &optional op src)
+ (let ((c (ignore-instruction-prefixes c)))
+ (and (or (not src)
+ (equal src (first (twop-p c op))))
+ (second (twop-p c op)))))
+ (twop-src (c &optional op dest)
+ (let ((c (ignore-instruction-prefixes c)))
+ (and (or (not dest)
+ (equal dest (second (twop-p c op))))
+ (first (twop-p c op)))))
+ #+ignore
+ (isrc (c)
+ (let ((c (ignore-instruction-prefixes c)))
+ (ecase (length (cdr c))
+ (0 nil)
+ (1 (cadr c))
+ (2 (twop-src c)))))
+ (idst (c)
+ (let ((c (ignore-instruction-prefixes c)))
+ (ecase (length (cdr c))
+ (0 nil)
+ (1 (cadr c))
+ (2 (twop-dst c)))))
+ (non-destructuve-p (c)
+ (let ((c (ignore-instruction-prefixes c)))
(and (consp c)
- (eq :pushl (car c))
- (stack-frame-operand (second c)))))
- (in-stack-frame-p (c reg)
- "Does c ensure that reg is in some particular stack-frame location?"
- (or (and (load-stack-frame-p c)
- (eq reg (twop-dst c))
- (stack-frame-operand (twop-src c)))
- (and (store-stack-frame-p c)
- (eq reg (twop-src c))
- (stack-frame-operand (twop-dst c)))))
- (load-funobj-constant-p (c)
- (funobj-constant-operand (twop-src c :movl)))
- #+ignore
- (sub-program-label-p (l)
- (and (consp l)
- (eq :sub-program (car l))))
- (local-load-p (c)
- (if (or (load-stack-frame-p c)
- (load-funobj-constant-p c))
- (twop-src c)
- nil))
- (label-here-p (label code)
- "Is <label> at this point in <code>?"
- (loop for i in code
- while (or (symbolp i)
- (instruction-is i :frame-map))
- thereis (eq label i)))
- (negate-branch (branch-type)
- (ecase branch-type
- (:jbe :ja) (:ja :jbe)
- (:jz :jnz) (:jnz :jz)
- (:je :jne) (:jne :je)
- (:jc :jnc) (:jnc :jc)
- (:jl :jge) (:jge :jl)
- (:jle :jg) (:jg :jle)))
- (branch-instruction-label (i &optional jmp (branch-types '(:je :jne :jb :jnb :jbe :jz :jl :jnz
- :jle :ja :jae :jg :jge :jnc :jc :js :jns)))
- "If i is a branch, return the label."
- (when jmp (push :jmp branch-types))
- (let ((i (ignore-instruction-prefixes i)))
- (or (and (listp i) (member (car i) branch-types)
- (listp (second i)) (member (car (second i)) '(quote muerte.cl::quote))
- (second (second i)))
- #+ignore
- (and (listp i)
- branch-types
- (symbolp (car i))
- (not (member (car i) '(:jmp :jecxz)))
- (char= #\J (char (symbol-name (car i)) 0))
- (warn "Not a branch: ~A / ~A [~A]" i (symbol-package (caadr i)) branch-types)))))
- (find-branches-to-label (start-pc label &optional (context-size 0))
- "Context-size is the number of instructions _before_ the branch you want returned."
- (dotimes (i context-size)
- (push nil start-pc))
- (loop for pc on start-pc
- as i = (nth context-size pc)
- as i-label = (branch-instruction-label i t)
- if (or (eq label i-label)
- (and (consp i-label)
- (eq :label-plus-one (car i-label))))
- nconc (list pc)
- else if (let ((sub-program i-label))
- (and (consp sub-program)
- (eq :sub-program (car sub-program))))
- nconc (find-branches-to-label (cddr (branch-instruction-label i t))
- label context-size)
- else if (and (not (atom i))
- (tree-search i label))
- nconc (list 'unknown-label-usage)))
- (optimize-trim-stack-frame (unoptimized-code)
- "Any unused local variables on the stack-frame?"
- unoptimized-code
- ;; BUILD A MAP OF USED STACK-VARS AND REMAP THEM!
- #+ignore (if (not (and stack-frame-size
- (find 'start-stack-frame-setup unoptimized-code)))
- unoptimized-code
- (let ((old-code unoptimized-code)
- (new-code ()))
- ;; copy everything upto start-stack-frame-setup
- (loop for i = (pop old-code)
- do (push i new-code)
- while old-code
- until (eq i 'start-stack-frame-setup))
- (assert (eq (car new-code) 'start-stack-frame-setup) ()
- "no start-stack-frame-setup label, but we already checked!")
- (loop for pos downfrom -8 by 4
- as i = (pop old-code)
- if (and (consp i) (eq :pushl (car i)) (symbolp (cadr i)))
- collect (cons pos (cadr i))
- and do (unless (find pos old-code :key #'read-stack-frame-p)
- (cond
- ((find pos old-code :key #'store-stack-frame-p)
- (warn "Unused local but stored var: ~S" pos))
- ((find pos old-code :key #'uses-stack-frame-p)
- (warn "Unused BUT USED local var: ~S" pos))
- (t (warn "Unused local var: ~S" pos))))
- else do
- (push i old-code)
- (loop-finish))))
- unoptimized-code)
- (frame-map-code (unoptimized-code)
- "After each label in unoptimized-code, insert a (:frame-map <full-map> <branch-map> <sticky>)
+ (member (car c) '(:testl :testb :pushl :cmpl :cmpb :frame-map)))))
+ (simple-instruction-p (c)
+ (let ((c (ignore-instruction-prefixes c)))
+ (and (listp c)
+ (member (car c) '(:movl :xorl :popl :cmpl :leal :andl :addl :subl)))))
+ (register-indirect-operand (op base)
+ (multiple-value-bind (reg off)
+ (when (listp op)
+ (loop for x in op
+ if (integerp x) sum x into off
+ else collect x into reg
+ finally (return (values reg off))))
+ (and (eq base (car reg))
+ (not (rest reg))
+ off)))
+ (stack-frame-operand (op)
+ (register-indirect-operand op :ebp))
+ (funobj-constant-operand (op)
+ (register-indirect-operand op :esi))
+ (global-constant-operand (op)
+ (register-indirect-operand op :edi))
+ (global-funcall-p (op &optional funs)
+ (let ((op (ignore-instruction-prefixes op)))
+ (when (instruction-is op :call)
+ (let ((x (global-constant-operand (second op))))
+ (flet ((try (name)
+ (and (eql x (slot-offset 'movitz-constant-block name))
+ name)))
+ (cond
+ ((not x) nil)
+ ((null funs) t)
+ ((atom funs) (try funs))
+ (t (some #'try funs))))))))
+ (preserves-stack-location-p (i stack-location)
+ (let ((i (ignore-instruction-prefixes i)))
+ (and (not (atom i))
+ (or (global-funcall-p i)
+ (instruction-is i :frame-map)
+ (branch-instruction-label i)
+ (non-destructuve-p i)
+ (and (simple-instruction-p i)
+ (not (eql stack-location (stack-frame-operand (idst i)))))))))
+ (preserves-register-p (i register)
+ (let ((i (ignore-instruction-prefixes i)))
+ (and (not (atom i))
+ (or (and (member register '(:edx))
+ (member (global-funcall-p i)
+ '(fast-car fast-cdr fast-car-ebx fast-cdr-ebx)))
+ (instruction-is i :frame-map)
+ (branch-instruction-label i)
+ (non-destructuve-p i)
+ (and (simple-instruction-p i)
+ (not (eq register (idst i))))))))
+ (register-operand (op)
+ (and (member op '(:eax :ebx :ecx :edx :edi))
+ op))
+ (true-and-equal (x &rest more)
+ (declare (dynamic-extent more))
+ (and x (dolist (y more t)
+ (unless (equal x y)
+ (return nil)))))
+ #+ignore
+ (uses-stack-frame-p (c)
+ (and (consp c)
+ (some #'stack-frame-operand (cdr c))))
+ (load-stack-frame-p (c &optional (op :movl))
+ (stack-frame-operand (twop-src c op)))
+ (store-stack-frame-p (c &optional (op :movl))
+ (stack-frame-operand (twop-dst c op)))
+ (read-stack-frame-p (c)
+ (or (load-stack-frame-p c :movl)
+ (load-stack-frame-p c :cmpl)
+ (store-stack-frame-p c :cmpl)
+ (and (consp c)
+ (eq :pushl (car c))
+ (stack-frame-operand (second c)))))
+ (in-stack-frame-p (c reg)
+ "Does c ensure that reg is in some particular stack-frame location?"
+ (or (and (load-stack-frame-p c)
+ (eq reg (twop-dst c))
+ (stack-frame-operand (twop-src c)))
+ (and (store-stack-frame-p c)
+ (eq reg (twop-src c))
+ (stack-frame-operand (twop-dst c)))))
+ (load-funobj-constant-p (c)
+ (funobj-constant-operand (twop-src c :movl)))
+ #+ignore
+ (sub-program-label-p (l)
+ (and (consp l)
+ (eq :sub-program (car l))))
+ (local-load-p (c)
+ (if (or (load-stack-frame-p c)
+ (load-funobj-constant-p c))
+ (twop-src c)
+ nil))
+ (label-here-p (label code)
+ "Is <label> at this point in <code>?"
+ (loop for i in code
+ while (or (symbolp i)
+ (instruction-is i :frame-map))
+ thereis (eq label i)))
+ (negate-branch (branch-type)
+ (ecase branch-type
+ (:jbe :ja) (:ja :jbe)
+ (:jz :jnz) (:jnz :jz)
+ (:je :jne) (:jne :je)
+ (:jc :jnc) (:jnc :jc)
+ (:jl :jge) (:jge :jl)
+ (:jle :jg) (:jg :jle)))
+ (branch-instruction-label (i &optional jmp (branch-types '(:je :jne :jb :jnb :jbe :jz :jl :jnz
+ :jle :ja :jae :jg :jge :jnc :jc :js :jns)))
+ "If i is a branch, return the label."
+ (when jmp (push :jmp branch-types))
+ (let ((i (ignore-instruction-prefixes i)))
+ (or (and (listp i) (member (car i) branch-types)
+ (listp (second i)) (member (car (second i)) '(quote muerte.cl::quote))
+ (second (second i)))
+ #+ignore
+ (and (listp i)
+ branch-types
+ (symbolp (car i))
+ (not (member (car i) '(:jmp :jecxz)))
+ (char= #\J (char (symbol-name (car i)) 0))
+ (warn "Not a branch: ~A / ~A [~A]" i (symbol-package (caadr i)) branch-types)))))
+ (find-branches-to-label (start-pc label &optional (context-size 0))
+ "Context-size is the number of instructions _before_ the branch you want returned."
+ (dotimes (i context-size)
+ (push nil start-pc))
+ (loop for pc on start-pc
+ as i = (nth context-size pc)
+ as i-label = (branch-instruction-label i t)
+ if (or (eq label i-label)
+ (and (consp i-label)
+ (eq :label-plus-one (car i-label))))
+ nconc (list pc)
+ else if (let ((sub-program i-label))
+ (and (consp sub-program)
+ (eq :sub-program (car sub-program))))
+ nconc (find-branches-to-label (cddr (branch-instruction-label i t))
+ label context-size)
+ else if (and (not (atom i))
+ (tree-search i label))
+ nconc (list 'unknown-label-usage)))
+ (optimize-trim-stack-frame (unoptimized-code)
+ "Any unused local variables on the stack-frame?"
+ unoptimized-code
+ ;; BUILD A MAP OF USED STACK-VARS AND REMAP THEM!
+ #+ignore (if (not (and stack-frame-size
+ (find 'start-stack-frame-setup unoptimized-code)))
+ unoptimized-code
+ (let ((old-code unoptimized-code)
+ (new-code ()))
+ ;; copy everything upto start-stack-frame-setup
+ (loop for i = (pop old-code)
+ do (push i new-code)
+ while old-code
+ until (eq i 'start-stack-frame-setup))
+ (assert (eq (car new-code) 'start-stack-frame-setup) ()
+ "no start-stack-frame-setup label, but we already checked!")
+ (loop for pos downfrom -8 by 4
+ as i = (pop old-code)
+ if (and (consp i) (eq :pushl (car i)) (symbolp (cadr i)))
+ collect (cons pos (cadr i))
+ and do (unless (find pos old-code :key #'read-stack-frame-p)
+ (cond
+ ((find pos old-code :key #'store-stack-frame-p)
+ (warn "Unused local but stored var: ~S" pos))
+ ((find pos old-code :key #'uses-stack-frame-p)
+ (warn "Unused BUT USED local var: ~S" pos))
+ (t (warn "Unused local var: ~S" pos))))
+ else do
+ (push i old-code)
+ (loop-finish))))
+ unoptimized-code)
+ (frame-map-code (unoptimized-code)
+ "After each label in unoptimized-code, insert a (:frame-map <full-map> <branch-map> <sticky>)
that says which registers are known to hold which stack-frame-locations.
A branch-map is the map that is guaranteed after every branch to the label, i.e. not including
falling below the label."
- #+ignore (warn "unmapped:~{~&~A~}" unoptimized-code)
- (flet ((rcode-map (code)
- #+ignore (when (instruction-is (car code) :testb)
- (warn "rcoding ~A" code))
- (loop with modifieds = nil
- with registers = (list :eax :ebx :ecx :edx)
- with local-map = nil
- for ii in code
- while registers
- do (flet ((add-map (stack reg)
- (when (and (not (member stack modifieds))
- (member reg registers))
- (push (cons stack reg)
- local-map))))
- (cond ((instruction-is ii :frame-map)
- (dolist (m (second ii))
- (add-map (car m) (cdr m))))
- ((load-stack-frame-p ii)
- (add-map (load-stack-frame-p ii)
- (twop-dst ii)))
- ((store-stack-frame-p ii)
- (add-map (store-stack-frame-p ii)
- (twop-src ii))
- (pushnew (store-stack-frame-p ii)
- modifieds))
- ((non-destructuve-p ii))
- ((branch-instruction-label ii))
- ((simple-instruction-p ii)
- (let ((op (idst ii)))
- (cond
- ((stack-frame-operand op)
- (pushnew (stack-frame-operand op) modifieds))
- ((symbolp op)
- (setf registers (delete op registers))))))
- (t #+ignore (when (instruction-is (car code) :testb)
- (warn "stopped at ~A" ii))
- (loop-finish))))
- (setf registers
- (delete-if (lambda (r)
- (not (preserves-register-p ii r)))
- registers))
- finally
- #+ignore (when (instruction-is (car code) :testb)
- (warn "..map ~A" local-map))
- (return local-map))))
- (loop with next-pc = 'auto-next
- ;; initially (warn "opt: ~{ ~A~%~}" unoptimized-code)
- for pc = unoptimized-code then (prog1 (if (eq 'auto-next next-pc) auto-next-pc next-pc)
- (setq next-pc 'auto-next))
- as auto-next-pc = (cdr unoptimized-code) then (cdr pc)
- as p = (list (car pc)) ; will be appended.
- as i1 = (first pc) ; current instruction, collected by default.
- and i2 = (second pc)
- while pc
- do (when (and (symbolp i1)
- (not (and (instruction-is i2 :frame-map)
- (fourth i2))))
- (let* ((label i1)
- (branch-map (reduce (lambda (&optional x y)
- (intersection x y :test #'equal))
- (mapcar (lambda (lpc)
- (if (eq 'unknown-label-usage lpc)
- nil
- (rcode-map (nreverse (subseq lpc 0 9)))))
- (find-branches-to-label unoptimized-code label 9))))
- (full-map (let ((rcode (nreverse (let* ((pos (loop for x on unoptimized-code
- as pos upfrom 0
- until (eq x pc)
- finally (return pos)))
- (back9 (max 0 (- pos 9))))
- (subseq unoptimized-code
- back9 pos)))))
- (if (instruction-uncontinues-p (car rcode))
- branch-map
- (intersection branch-map (rcode-map rcode) :test #'equal)))))
- (when (or full-map branch-map nil)
- #+ignore
- (explain nil "Inserting at ~A frame-map ~S branch-map ~S."
- label full-map branch-map))
- (setq p (list label `(:frame-map ,full-map ,branch-map))
- next-pc (if (instruction-is i2 :frame-map)
- (cddr pc)
- (cdr pc)))))
- nconc p)))
- (optimize-stack-frame-init (unoptimized-code)
- "Look at the function's stack-frame initialization code, and see
+ #+ignore (warn "unmapped:~{~&~A~}" unoptimized-code)
+ (flet ((rcode-map (code)
+ #+ignore (when (instruction-is (car code) :testb)
+ (warn "rcoding ~A" code))
+ (loop with modifieds = nil
+ with registers = (list :eax :ebx :ecx :edx)
+ with local-map = nil
+ for ii in code
+ while registers
+ do (flet ((add-map (stack reg)
+ (when (and (not (member stack modifieds))
+ (member reg registers))
+ (push (cons stack reg)
+ local-map))))
+ (cond ((instruction-is ii :frame-map)
+ (dolist (m (second ii))
+ (add-map (car m) (cdr m))))
+ ((load-stack-frame-p ii)
+ (add-map (load-stack-frame-p ii)
+ (twop-dst ii)))
+ ((store-stack-frame-p ii)
+ (add-map (store-stack-frame-p ii)
+ (twop-src ii))
+ (pushnew (store-stack-frame-p ii)
+ modifieds))
+ ((non-destructuve-p ii))
+ ((branch-instruction-label ii))
+ ((simple-instruction-p ii)
+ (let ((op (idst ii)))
+ (cond
+ ((stack-frame-operand op)
+ (pushnew (stack-frame-operand op) modifieds))
+ ((symbolp op)
+ (setf registers (delete op registers))))))
+ (t #+ignore (when (instruction-is (car code) :testb)
+ (warn "stopped at ~A" ii))
+ (loop-finish))))
+ (setf registers
+ (delete-if (lambda (r)
+ (not (preserves-register-p ii r)))
+ registers))
+ finally
+ #+ignore (when (instruction-is (car code) :testb)
+ (warn "..map ~A" local-map))
+ (return local-map))))
+ (loop with next-pc = 'auto-next
+ ;; initially (warn "opt: ~{ ~A~%~}" unoptimized-code)
+ for pc = unoptimized-code then (prog1 (if (eq 'auto-next next-pc) auto-next-pc next-pc)
+ (setq next-pc 'auto-next))
+ as auto-next-pc = (cdr unoptimized-code) then (cdr pc)
+ as p = (list (car pc)) ; will be appended.
+ as i1 = (first pc) ; current instruction, collected by default.
+ and i2 = (second pc)
+ while pc
+ do (when (and (symbolp i1)
+ (not (and (instruction-is i2 :frame-map)
+ (fourth i2))))
+ (let* ((label i1)
+ (branch-map (reduce (lambda (&optional x y)
+ (intersection x y :test #'equal))
+ (mapcar (lambda (lpc)
+ (if (eq 'unknown-label-usage lpc)
+ nil
+ (rcode-map (nreverse (subseq lpc 0 9)))))
+ (find-branches-to-label unoptimized-code label 9))))
+ (full-map (let ((rcode (nreverse (let* ((pos (loop for x on unoptimized-code
+ as pos upfrom 0
+ until (eq x pc)
+ finally (return pos)))
+ (back9 (max 0 (- pos 9))))
+ (subseq unoptimized-code
+ back9 pos)))))
+ (if (instruction-uncontinues-p (car rcode))
+ branch-map
+ (intersection branch-map (rcode-map rcode) :test #'equal)))))
+ (when (or full-map branch-map nil)
+ #+ignore
+ (explain nil "Inserting at ~A frame-map ~S branch-map ~S."
+ label full-map branch-map))
+ (setq p (list label `(:frame-map ,full-map ,branch-map))
+ next-pc (if (instruction-is i2 :frame-map)
+ (cddr pc)
+ (cdr pc)))))
+ nconc p)))
+ (optimize-stack-frame-init (unoptimized-code)
+ "Look at the function's stack-frame initialization code, and see
if we can optimize that, and/or immediately subsequent loads/stores."
- (if (not (find 'start-stack-frame-setup unoptimized-code))
- unoptimized-code
- (let ((old-code unoptimized-code)
- (new-code ()))
- ;; copy everything upto start-stack-frame-setup
- (loop for i = (pop old-code)
- do (push i new-code)
- while old-code
- until (eq i 'start-stack-frame-setup))
- (assert (eq (car new-code) 'start-stack-frame-setup) ()
- "no start-stack-frame-setup label, but we already checked!")
- (let* ((frame-map (loop for pos downfrom -8 by 4
- as i = (pop old-code)
- if (and (consp i) (eq :pushl (car i)) (symbolp (cadr i)))
- collect (cons pos (cadr i))
- and do (push i new-code)
- else do
- (push i old-code)
- (loop-finish)))
- (mod-p (loop with mod-p = nil
- for i = `(:frame-map ,(copy-list frame-map) nil t)
- then (pop old-code)
- while i
- do (let ((new-i (cond
- ((let ((store-pos (store-stack-frame-p i)))
- (and store-pos
- (eq (cdr (assoc store-pos frame-map))
- (twop-src i))))
- (explain nil "removed stack-init store: ~S" i)
- nil)
- ((let ((load-pos (load-stack-frame-p i)))
- (and load-pos
- (eq (cdr (assoc load-pos frame-map))
- (twop-dst i))))
- (explain nil "removed stack-init load: ~S" i)
- nil)
- ((and (load-stack-frame-p i)
- (assoc (load-stack-frame-p i) frame-map))
- (let ((old-reg (cdr (assoc (load-stack-frame-p i)
- frame-map))))
- (explain nil "load ~S already in ~S."
- i old-reg)
- `(:movl ,old-reg ,(twop-dst i))))
- (t i))))
- (unless (eq new-i i)
- (setf mod-p t))
- (when (branch-instruction-label new-i t)
- (setf mod-p t)
- (push `(:frame-map ,(copy-list frame-map) nil t)
- new-code))
- (when new-i
- (push new-i new-code)
- ;; (warn "new-i: ~S, fm: ~S" new-i frame-map)
- (setf frame-map
- (delete-if (lambda (map)
- ;; (warn "considering: ~S" map)
- (not (and (preserves-register-p new-i (cdr map))
- (preserves-stack-location-p new-i
- (car map)))))
- frame-map))
- ;; (warn "Frame-map now: ~S" frame-map)
- (when (store-stack-frame-p new-i)
- (loop for map in frame-map
- do (when (= (store-stack-frame-p new-i)
- (car map))
- (setf (cdr map) (twop-src new-i)))))))
- while frame-map
- finally (return mod-p))))
- (if (not mod-p)
- unoptimized-code
- (append (nreverse new-code)
- old-code)))))))
- (let* ((unoptimized-code (frame-map-code (optimize-stack-frame-init unoptimized-code)))
- (code-modified-p nil)
- (stack-frame-used-map (loop with map = nil
- for i in unoptimized-code
- do (let ((x (read-stack-frame-p i)))
- (when x (pushnew x map)))
- (when (and (instruction-is i :leal)
- (stack-frame-operand (twop-src i)))
- (let ((x (stack-frame-operand (twop-src i))))
- (when (= (tag :cons) (ldb (byte 2 0) x))
- (pushnew (+ x -1) map)
- (pushnew (+ x 3) map))))
- finally (return map)))
- (optimized-code
- ;; This loop applies a set of (hard-coded) heuristics on unoptimized-code.
- (loop with next-pc = 'auto-next
- ;; initially (warn "opt: ~{ ~A~%~}" unoptimized-code)
- for pc = unoptimized-code then (prog1 (if (eq 'auto-next next-pc) auto-next-pc next-pc)
- (setq next-pc 'auto-next))
- as auto-next-pc = (cdr unoptimized-code) then (cdr pc)
- as p = (list (car pc)) ; will be appended.
- as original-p = p
- as i = (first pc) ; current instruction, collected by default.
- and i2 = (second pc) and i3 = (third pc) and i4 = (fourth pc) and i5 = (fifth pc)
- while pc
- do (cond
- ((and (instruction-is i :frame-map)
- (instruction-is i2 :frame-map)
- (not (fourth i))
- (not (fourth i2)))
- (let ((map (union (second i) (second i2) :test #'equal)))
- (explain nil "Merged maps:~%~A + ~A~% => ~A"
- (second i) (second i2) map)
- (setq p `((:frame-map ,map))
- next-pc (cddr pc))))
- ((let ((x (store-stack-frame-p i)))
- (and x (not (member x stack-frame-used-map))))
- (setq p nil)
- (explain nil "Removed store of unused local var: ~S" i))
- ((and (global-funcall-p i2 '(fast-car))
- (global-funcall-p i5 '(fast-cdr))
- (true-and-equal (in-stack-frame-p i :eax)
- (in-stack-frame-p i4 :eax)))
- (let ((call-prefix (if (consp (car i2)) (car i2) nil)))
- (cond
- ((equal i3 '(:pushl :eax))
- (explain nil "merge car,push,cdr to cdr-car,push")
- (setf p (list i
- `(,call-prefix :call
- (:edi ,(global-constant-offset 'fast-cdr-car)))
- `(:pushl :ebx))
- next-pc (nthcdr 5 pc)))
- ((and (store-stack-frame-p i3)
- (eq :eax (twop-src i3)))
- (explain nil "merge car,store,cdr to cdr-car,store")
- (setf p (list i
- `(,call-prefix :call
- (:edi ,(global-constant-offset 'fast-cdr-car)))
- `(:movl :ebx ,(twop-dst i3)))
- next-pc (nthcdr 5 pc)))
- (t (error "can't deal with cdr-car here: ~{~&~A~}" (subseq pc 0 8))))))
- ((flet ((try (place register &optional map reason)
- "See if we can remove a stack-frame load below current pc,
+ (if (not (find 'start-stack-frame-setup unoptimized-code))
+ unoptimized-code
+ (let ((old-code unoptimized-code)
+ (new-code ()))
+ ;; copy everything upto start-stack-frame-setup
+ (loop for i = (pop old-code)
+ do (push i new-code)
+ while old-code
+ until (eq i 'start-stack-frame-setup))
+ (assert (eq (car new-code) 'start-stack-frame-setup) ()
+ "no start-stack-frame-setup label, but we already checked!")
+ (let* ((frame-map (loop for pos downfrom -8 by 4
+ as i = (pop old-code)
+ if (and (consp i) (eq :pushl (car i)) (symbolp (cadr i)))
+ collect (cons pos (cadr i))
+ and do (push i new-code)
+ else do
+ (push i old-code)
+ (loop-finish)))
+ (mod-p (loop with mod-p = nil
+ for i = `(:frame-map ,(copy-list frame-map) nil t)
+ then (pop old-code)
+ while i
+ do (let ((new-i (cond
+ ((let ((store-pos (store-stack-frame-p i)))
+ (and store-pos
+ (eq (cdr (assoc store-pos frame-map))
+ (twop-src i))))
+ (explain nil "removed stack-init store: ~S" i)
+ nil)
+ ((let ((load-pos (load-stack-frame-p i)))
+ (and load-pos
+ (eq (cdr (assoc load-pos frame-map))
+ (twop-dst i))))
+ (explain nil "removed stack-init load: ~S" i)
+ nil)
+ ((and (load-stack-frame-p i)
+ (assoc (load-stack-frame-p i) frame-map))
+ (let ((old-reg (cdr (assoc (load-stack-frame-p i)
+ frame-map))))
+ (explain nil "load ~S already in ~S."
+ i old-reg)
+ `(:movl ,old-reg ,(twop-dst i))))
+ (t i))))
+ (unless (eq new-i i)
+ (setf mod-p t))
+ (when (branch-instruction-label new-i t)
+ (setf mod-p t)
+ (push `(:frame-map ,(copy-list frame-map) nil t)
+ new-code))
+ (when new-i
+ (push new-i new-code)
+ ;; (warn "new-i: ~S, fm: ~S" new-i frame-map)
+ (setf frame-map
+ (delete-if (lambda (map)
+ ;; (warn "considering: ~S" map)
+ (not (and (preserves-register-p new-i (cdr map))
+ (preserves-stack-location-p new-i
+ (car map)))))
+ frame-map))
+ ;; (warn "Frame-map now: ~S" frame-map)
+ (when (store-stack-frame-p new-i)
+ (loop for map in frame-map
+ do (when (= (store-stack-frame-p new-i)
+ (car map))
+ (setf (cdr map) (twop-src new-i)))))))
+ while frame-map
+ finally (return mod-p))))
+ (if (not mod-p)
+ unoptimized-code
+ (append (nreverse new-code)
+ old-code)))))))
+ (let* ((unoptimized-code (frame-map-code (optimize-stack-frame-init unoptimized-code)))
+ (code-modified-p nil)
+ (stack-frame-used-map (loop with map = nil
+ for i in unoptimized-code
+ do (let ((x (read-stack-frame-p i)))
+ (when x (pushnew x map)))
+ (when (and (instruction-is i :leal)
+ (stack-frame-operand (twop-src i)))
+ (let ((x (stack-frame-operand (twop-src i))))
+ (when (= (tag :cons) (ldb (byte 2 0) x))
+ (pushnew (+ x -1) map)
+ (pushnew (+ x 3) map))))
+ finally (return map)))
+ (optimized-code
+ ;; This loop applies a set of (hard-coded) heuristics on unoptimized-code.
+ (loop with next-pc = 'auto-next
+ ;; initially (warn "opt: ~{ ~A~%~}" unoptimized-code)
+ for pc = unoptimized-code then (prog1 (if (eq 'auto-next next-pc) auto-next-pc next-pc)
+ (setq next-pc 'auto-next))
+ as auto-next-pc = (cdr unoptimized-code) then (cdr pc)
+ as p = (list (car pc)) ; will be appended.
+ as original-p = p
+ as i = (first pc) ; current instruction, collected by default.
+ and i2 = (second pc) and i3 = (third pc) and i4 = (fourth pc) and i5 = (fifth pc)
+ while pc
+ do (cond
+ ((and (instruction-is i :frame-map)
+ (instruction-is i2 :frame-map)
+ (not (fourth i))
+ (not (fourth i2)))
+ (let ((map (union (second i) (second i2) :test #'equal)))
+ (explain nil "Merged maps:~%~A + ~A~% => ~A"
+ (second i) (second i2) map)
+ (setq p `((:frame-map ,map))
+ next-pc (cddr pc))))
+ ((let ((x (store-stack-frame-p i)))
+ (and x (not (member x stack-frame-used-map))))
+ (setq p nil)
+ (explain nil "Removed store of unused local var: ~S" i))
+ ((and (global-funcall-p i2 '(fast-car))
+ (global-funcall-p i5 '(fast-cdr))
+ (true-and-equal (in-stack-frame-p i :eax)
+ (in-stack-frame-p i4 :eax)))
+ (let ((call-prefix (if (consp (car i2)) (car i2) nil)))
+ (cond
+ ((equal i3 '(:pushl :eax))
+ (explain nil "merge car,push,cdr to cdr-car,push")
+ (setf p (list i
+ `(,call-prefix :call
+ (:edi ,(global-constant-offset 'fast-cdr-car)))
+ `(:pushl :ebx))
+ next-pc (nthcdr 5 pc)))
+ ((and (store-stack-frame-p i3)
+ (eq :eax (twop-src i3)))
+ (explain nil "merge car,store,cdr to cdr-car,store")
+ (setf p (list i
+ `(,call-prefix :call
+ (:edi ,(global-constant-offset 'fast-cdr-car)))
+ `(:movl :ebx ,(twop-dst i3)))
+ next-pc (nthcdr 5 pc)))
+ (t (error "can't deal with cdr-car here: ~{~&~A~}" (subseq pc 0 8))))))
+ ((flet ((try (place register &optional map reason)
+ "See if we can remove a stack-frame load below current pc,
given the knowledge that <register> is equal to <place>."
- (let ((next-load (and place
- (dolist (si (cdr pc))
- (when (and (twop-p si :cmpl)
- (equal place (twop-src si)))
- (warn "Reverse cmp not yet dealed with.."))
- (cond
- ((and (twop-p si :cmpl)
- (equal place (twop-dst si)))
- (return si))
- ((equal place (local-load-p si))
- (return si))
- ((or (not (consp si))
- (not (preserves-register-p si register))
- (equal place (twop-dst si)))
- (return nil)))
- (setf map
- (remove-if (lambda (m)
- (not (preserves-register-p si (cdr m))))
- map))))))
- (case (instruction-is next-load)
- (:movl
- (let ((pos (position next-load pc)))
- (setq p (nconc (subseq pc 0 pos)
- (if (or (eq register (twop-dst next-load))
- (find-if (lambda (m)
- (and (eq (twop-dst next-load) (cdr m))
- (= (car m) (stack-frame-operand place))))
- map))
- nil
- (list `(:movl ,register ,(twop-dst next-load)))))
- next-pc (nthcdr (1+ pos) pc))
- (explain nil "preserved load/store .. load ~S of place ~S because ~S."
- next-load place reason)))
- (:cmpl
- (let ((pos (position next-load pc)))
- (setq p (nconc (subseq pc 0 pos)
- (list `(:cmpl ,(twop-src next-load) ,register)))
- next-pc (nthcdr (1+ pos) pc))
- (explain nil "preserved load/store..cmp: ~S" p next-load))))
- (if next-load t nil))))
- (or (when (instruction-is i :frame-map)
- (loop for (place . register) in (second i)
+ (let ((next-load (and place
+ (dolist (si (cdr pc))
+ (when (and (twop-p si :cmpl)
+ (equal place (twop-src si)))
+ (warn "Reverse cmp not yet dealed with.."))
+ (cond
+ ((and (twop-p si :cmpl)
+ (equal place (twop-dst si)))
+ (return si))
+ ((equal place (local-load-p si))
+ (return si))
+ ((or (not (consp si))
+ (not (preserves-register-p si register))
+ (equal place (twop-dst si)))
+ (return nil)))
+ (setf map
+ (remove-if (lambda (m)
+ (not (preserves-register-p si (cdr m))))
+ map))))))
+ (case (instruction-is next-load)
+ (:movl
+ (let ((pos (position next-load pc)))
+ (setq p (nconc (subseq pc 0 pos)
+ (if (or (eq register (twop-dst next-load))
+ (find-if (lambda (m)
+ (and (eq (twop-dst next-load) (cdr m))
+ (= (car m) (stack-frame-operand place))))
+ map))
+ nil
+ (list `(:movl ,register ,(twop-dst next-load)))))
+ next-pc (nthcdr (1+ pos) pc))
+ (explain nil "preserved load/store .. load ~S of place ~S because ~S."
+ next-load place reason)))
+ (:cmpl
+ (let ((pos (position next-load pc)))
+ (setq p (nconc (subseq pc 0 pos)
+ (list `(:cmpl ,(twop-src next-load) ,register)))
+ next-pc (nthcdr (1+ pos) pc))
+ (explain nil "preserved load/store..cmp: ~S" p next-load))))
+ (if next-load t nil))))
+ (or (when (instruction-is i :frame-map)
+ (loop for (place . register) in (second i)
;;; do (warn "map try ~S ~S: ~S" place register
;;; (try place register))
- thereis (try `(:ebp ,place) register (second i) :frame-map)))
- (try (or (local-load-p i)
- (and (store-stack-frame-p i)
- (twop-dst i)))
- (if (store-stack-frame-p i)
- (twop-src i)
- (twop-dst i))
- nil i))))
- ((and (symbolp i)
- (instruction-is i2 :frame-map)
- (load-stack-frame-p i3)
- (eq (twop-dst i3)
- (cdr (assoc (load-stack-frame-p i3) (third i2))))
- (not (assoc (load-stack-frame-p i3) (second i2))))
- (let ((reg (cdr (assoc (load-stack-frame-p i3) (third i2)))))
- (explain nil "factor out load from loop: ~S" i3)
- (assert (eq reg (twop-dst i3)))
- (setq p (if (eq reg (twop-dst i3))
- (list i3 i i2)
- (append (list i3 i i2)
- `((:movl ,reg ,(twop-dst i3)))))
- next-pc (cdddr pc))))
- ;; ((:jmp x) ...(no labels).... x ..)
- ;; => (x ...)
- ((let ((x (branch-instruction-label i t nil)))
- (and (position x (cdr pc))
- (not (find-if #'symbolp (cdr pc) :end (position x (cdr pc))))))
- (explain nil "jmp x .. x: ~W"
- (subseq pc 0 (1+ (position (branch-instruction-label i t nil)
- pc))))
- (setq p nil
- next-pc (member (branch-instruction-label i t nil) pc)))
- ;; (:jcc 'x) .... x (:jmp 'y) ..
- ;; => (:jcc 'y) .... x (:jmp 'y) ..
- ((let* ((from (branch-instruction-label i t))
- (dest (member (branch-instruction-label i t)
- unoptimized-code))
- (to (branch-instruction-label (if (instruction-is (second dest) :frame-map)
- (third dest)
- (second dest))
- t nil)))
- (when (and from to (not (eq from to)))
- (setq p (list `(,(car i) ',to)))
- (explain nil "branch redirect from ~S to ~S" from to)
- t)))
- ;; remove branch no-ops.
- ((and (branch-instruction-label i t)
- (label-here-p (branch-instruction-label i t)
- (cdr pc)))
- (explain nil "branch no-op: ~A" i)
- (setq p nil))
- ((and (symbolp i)
- (null (symbol-package i))
- (null (find-branches-to-label unoptimized-code i))
- (not (member i keep-labels)))
- (setq p nil
- next-pc (cdr pc))
- (explain nil "unused label: ~S" i))
- ;; ((:jcc 'label) (:jmp 'y) label) => ((:jncc 'y) label)
- ((and (branch-instruction-label i)
- (branch-instruction-label i2 t nil)
- (symbolp i3)
- (eq (branch-instruction-label i) i3))
- (setq p (list `(,(negate-branch (first i))
- ',(branch-instruction-label i2 t nil)))
- next-pc (nthcdr 2 pc))
- (explain nil "collapsed double negative branch to ~S: ~A." i3 p))
- ((and (branch-instruction-label i)
- (instruction-is i2 :frame-map)
- (branch-instruction-label i3 t nil)
- (symbolp i4)
- (eq (branch-instruction-label i) i4))
- (setq p (list `(,(negate-branch (first i))
- ',(branch-instruction-label i3 t nil)))
- next-pc (nthcdr 3 pc))
- (explain nil "collapsed double negative branch to ~S: ~A." i4 p))
- ((and (twop-p i :movl)
- (register-operand (twop-src i))
- (register-operand (twop-dst i))
- (twop-p i2 :movl)
- (eq (twop-dst i) (twop-dst i2))
- (register-indirect-operand (twop-src i2) (twop-dst i)))
- (setq p (list `(:movl (,(twop-src i)
- ,(register-indirect-operand (twop-src i2)
- (twop-dst i)))
- ,(twop-dst i2)))
- next-pc (nthcdr 2 pc))
- (explain nil "(movl edx eax) (movl (eax <z>) eax) => (movl (edx <z>) eax: ~S"
- p))
- ((and (twop-p i :movl)
- (instruction-is i2 :pushl)
- (eq (twop-dst i) (second i2))
- (twop-p i3 :movl)
- (eq (twop-dst i) (twop-dst i3)))
- (setq p (list `(:pushl ,(twop-src i)))
- next-pc (nthcdr 2 pc))
- (explain nil "(movl <z> :eax) (pushl :eax) => (pushl <z>): ~S" p))
- ((and (instruction-uncontinues-p i)
- (not (or (symbolp i2)
- #+ignore (member (instruction-is i2) '(:foobar)))))
- (do ((x (cdr pc) (cdr x)))
- (nil)
- (cond
- ((not (or (symbolp (car x))
- #+ignore (member (instruction-is (car x)) '(:foobar))))
- (explain nil "Removing unreachable code ~A after ~A." (car x) i))
- (t (setf p (list i)
- next-pc x)
- (return)))))
- ((and (store-stack-frame-p i)
- (load-stack-frame-p i2)
- (load-stack-frame-p i3)
- (= (store-stack-frame-p i)
- (load-stack-frame-p i3))
- (not (eq (twop-dst i2) (twop-dst i3))))
- (setq p (list i `(:movl ,(twop-src i) ,(twop-dst i3)) i2)
- next-pc (nthcdr 3 pc))
- (explain nil "store, z, load => store, move, z: ~A" p))
- ((and (instruction-is i :movl)
- (member (twop-dst i) '(:eax :ebx :ecx :edx))
- (instruction-is i2 :pushl)
- (not (member (second i2) '(:eax :ebx :ecx :edx)))
- (equal (twop-src i) (second i2)))
- (setq p (list i `(:pushl ,(twop-dst i)))
- next-pc (nthcdr 2 pc))
- (explain t "load, push => load, push reg."))
- ((and (instruction-is i :movl)
- (member (twop-src i) '(:eax :ebx :ecx :edx))
- (instruction-is i2 :pushl)
- (not (member (second i2) '(:eax :ebx :ecx :edx)))
- (equal (twop-dst i) (second i2)))
- (setq p (list i `(:pushl ,(twop-src i)))
- next-pc (nthcdr 2 pc))
- (explain nil "store, push => store, push reg: ~S ~S" i i2))
- ((and (instruction-is i :cmpl)
- (true-and-equal (stack-frame-operand (twop-dst i))
- (load-stack-frame-p i3))
- (branch-instruction-label i2))
- (setf p (list i3
- `(:cmpl ,(twop-src i) ,(twop-dst i3))
- i2)
- next-pc (nthcdr 3 pc))
- (explain nil "~S ~S ~S => ~S" i i2 i3 p))
- ((and (instruction-is i :pushl)
- (instruction-is i3 :popl)
- (store-stack-frame-p i2)
- (store-stack-frame-p i4)
- (eq (idst i3) (twop-src i4)))
- (setf p (list i2
- `(:movl ,(idst i) ,(twop-dst i4))
- `(:movl ,(idst i) ,(idst i3)))
- next-pc (nthcdr 4 pc))
- (explain nil "~S => ~S" (subseq pc 0 4) p))
- #+ignore
- ((let ((i6 (nth 6 pc)))
- (and (global-funcall-p i2 '(fast-car))
- (global-funcall-p i6 '(fast-cdr))
- (load-stack-frame-p i)
- (eq :eax (twop-dst i))
- (equal i i4))))
- ((and (equal i '(:movl :ebx :eax))
- (global-funcall-p i2 '(fast-car fast-cdr)))
- (let ((newf (ecase (global-funcall-p i2 '(fast-car fast-cdr))
- (fast-car 'fast-car-ebx)
- (fast-cdr 'fast-cdr-ebx))))
- (setq p `((:call (:edi ,(global-constant-offset newf))))
+ thereis (try `(:ebp ,place) register (second i) :frame-map)))
+ (try (or (local-load-p i)
+ (and (store-stack-frame-p i)
+ (twop-dst i)))
+ (if (store-stack-frame-p i)
+ (twop-src i)
+ (twop-dst i))
+ nil i))))
+ ((and (symbolp i)
+ (instruction-is i2 :frame-map)
+ (load-stack-frame-p i3)
+ (eq (twop-dst i3)
+ (cdr (assoc (load-stack-frame-p i3) (third i2))))
+ (not (assoc (load-stack-frame-p i3) (second i2))))
+ (let ((reg (cdr (assoc (load-stack-frame-p i3) (third i2)))))
+ (explain nil "factor out load from loop: ~S" i3)
+ (assert (eq reg (twop-dst i3)))
+ (setq p (if (eq reg (twop-dst i3))
+ (list i3 i i2)
+ (append (list i3 i i2)
+ `((:movl ,reg ,(twop-dst i3)))))
+ next-pc (cdddr pc))))
+ ;; ((:jmp x) ...(no labels).... x ..)
+ ;; => (x ...)
+ ((let ((x (branch-instruction-label i t nil)))
+ (and (position x (cdr pc))
+ (not (find-if #'symbolp (cdr pc) :end (position x (cdr pc))))))
+ (explain nil "jmp x .. x: ~W"
+ (subseq pc 0 (1+ (position (branch-instruction-label i t nil)
+ pc))))
+ (setq p nil
+ next-pc (member (branch-instruction-label i t nil) pc)))
+ ;; (:jcc 'x) .... x (:jmp 'y) ..
+ ;; => (:jcc 'y) .... x (:jmp 'y) ..
+ ((let* ((from (branch-instruction-label i t))
+ (dest (member (branch-instruction-label i t)
+ unoptimized-code))
+ (to (branch-instruction-label (if (instruction-is (second dest) :frame-map)
+ (third dest)
+ (second dest))
+ t nil)))
+ (when (and from to (not (eq from to)))
+ (setq p (list `(,(car i) ',to)))
+ (explain nil "branch redirect from ~S to ~S" from to)
+ t)))
+ ;; remove branch no-ops.
+ ((and (branch-instruction-label i t)
+ (label-here-p (branch-instruction-label i t)
+ (cdr pc)))
+ (explain nil "branch no-op: ~A" i)
+ (setq p nil))
+ ((and (symbolp i)
+ (null (symbol-package i))
+ (null (find-branches-to-label unoptimized-code i))
+ (not (member i keep-labels)))
+ (setq p nil
+ next-pc (cdr pc))
+ (explain nil "unused label: ~S" i))
+ ;; ((:jcc 'label) (:jmp 'y) label) => ((:jncc 'y) label)
+ ((and (branch-instruction-label i)
+ (branch-instruction-label i2 t nil)
+ (symbolp i3)
+ (eq (branch-instruction-label i) i3))
+ (setq p (list `(,(negate-branch (first i))
+ ',(branch-instruction-label i2 t nil)))
+ next-pc (nthcdr 2 pc))
+ (explain nil "collapsed double negative branch to ~S: ~A." i3 p))
+ ((and (branch-instruction-label i)
+ (instruction-is i2 :frame-map)
+ (branch-instruction-label i3 t nil)
+ (symbolp i4)
+ (eq (branch-instruction-label i) i4))
+ (setq p (list `(,(negate-branch (first i))
+ ',(branch-instruction-label i3 t nil)))
+ next-pc (nthcdr 3 pc))
+ (explain nil "collapsed double negative branch to ~S: ~A." i4 p))
+ ((and (twop-p i :movl)
+ (register-operand (twop-src i))
+ (register-operand (twop-dst i))
+ (twop-p i2 :movl)
+ (eq (twop-dst i) (twop-dst i2))
+ (register-indirect-operand (twop-src i2) (twop-dst i)))
+ (setq p (list `(:movl (,(twop-src i)
+ ,(register-indirect-operand (twop-src i2)
+ (twop-dst i)))
+ ,(twop-dst i2)))
+ next-pc (nthcdr 2 pc))
+ (explain nil "(movl edx eax) (movl (eax <z>) eax) => (movl (edx <z>) eax: ~S"
+ p))
+ ((and (twop-p i :movl)
+ (instruction-is i2 :pushl)
+ (eq (twop-dst i) (second i2))
+ (twop-p i3 :movl)
+ (eq (twop-dst i) (twop-dst i3)))
+ (setq p (list `(:pushl ,(twop-src i)))
+ next-pc (nthcdr 2 pc))
+ (explain nil "(movl <z> :eax) (pushl :eax) => (pushl <z>): ~S" p))
+ ((and (instruction-uncontinues-p i)
+ (not (or (symbolp i2)
+ #+ignore (member (instruction-is i2) '(:foobar)))))
+ (do ((x (cdr pc) (cdr x)))
+ (nil)
+ (cond
+ ((not (or (symbolp (car x))
+ #+ignore (member (instruction-is (car x)) '(:foobar))))
+ (explain nil "Removing unreachable code ~A after ~A." (car x) i))
+ (t (setf p (list i)
+ next-pc x)
+ (return)))))
+ ((and (store-stack-frame-p i)
+ (load-stack-frame-p i2)
+ (load-stack-frame-p i3)
+ (= (store-stack-frame-p i)
+ (load-stack-frame-p i3))
+ (not (eq (twop-dst i2) (twop-dst i3))))
+ (setq p (list i `(:movl ,(twop-src i) ,(twop-dst i3)) i2)
+ next-pc (nthcdr 3 pc))
+ (explain nil "store, z, load => store, move, z: ~A" p))
+ ((and (instruction-is i :movl)
+ (member (twop-dst i) '(:eax :ebx :ecx :edx))
+ (instruction-is i2 :pushl)
+ (not (member (second i2) '(:eax :ebx :ecx :edx)))
+ (equal (twop-src i) (second i2)))
+ (setq p (list i `(:pushl ,(twop-dst i)))
next-pc (nthcdr 2 pc))
- (explain nil "Changed [~S ~S] to ~S" i i2 newf)))
- ((and (equal i '(:movl :eax :ebx))
- (global-funcall-p i2 '(fast-car-ebx fast-cdr-ebx)))
- (let ((newf (ecase (global-funcall-p i2 '(fast-car-ebx fast-cdr-ebx))
- (fast-car-ebx 'fast-car)
- (fast-cdr-ebx 'fast-cdr))))
- (setq p `((:call (:edi ,(global-constant-offset newf))))
+ (explain t "load, push => load, push reg."))
+ ((and (instruction-is i :movl)
+ (member (twop-src i) '(:eax :ebx :ecx :edx))
+ (instruction-is i2 :pushl)
+ (not (member (second i2) '(:eax :ebx :ecx :edx)))
+ (equal (twop-dst i) (second i2)))
+ (setq p (list i `(:pushl ,(twop-src i)))
next-pc (nthcdr 2 pc))
- (explain nil "Changed [~S ~S] to ~S" i i2 newf)))
- ((and (load-stack-frame-p i) (eq :eax (twop-dst i))
- (global-funcall-p i2 '(fast-car fast-cdr))
- (preserves-stack-location-p i3 (load-stack-frame-p i))
- (eql (load-stack-frame-p i)
- (load-stack-frame-p i4)))
- (let ((newf (ecase (global-funcall-p i2 '(fast-car fast-cdr))
- (fast-car 'fast-car-ebx)
- (fast-cdr 'fast-cdr-ebx))))
- (setq p `((:movl ,(twop-src i) :ebx)
- (:call (:edi ,(global-constant-offset newf)))
- ,i3
- ,@(unless (eq :ebx (twop-dst i4))
- `((:movl :ebx ,(twop-dst i4)))))
+ (explain nil "store, push => store, push reg: ~S ~S" i i2))
+ ((and (instruction-is i :cmpl)
+ (true-and-equal (stack-frame-operand (twop-dst i))
+ (load-stack-frame-p i3))
+ (branch-instruction-label i2))
+ (setf p (list i3
+ `(:cmpl ,(twop-src i) ,(twop-dst i3))
+ i2)
+ next-pc (nthcdr 3 pc))
+ (explain nil "~S ~S ~S => ~S" i i2 i3 p))
+ ((and (instruction-is i :pushl)
+ (instruction-is i3 :popl)
+ (store-stack-frame-p i2)
+ (store-stack-frame-p i4)
+ (eq (idst i3) (twop-src i4)))
+ (setf p (list i2
+ `(:movl ,(idst i) ,(twop-dst i4))
+ `(:movl ,(idst i) ,(idst i3)))
next-pc (nthcdr 4 pc))
- (explain nil "load around ~A" newf))))
- do (unless (eq p original-p) ; auto-detect whether any heuristic fired..
- #+ignore (warn "at ~A, ~A inserted ~A" i i2 p)
- #+ignore (warn "modified at ~S ~S ~S" i i2 i3)
- (setf code-modified-p t))
- nconc p)))
- (if code-modified-p
- (apply #'optimize-code-internal optimized-code (1+ recursive-count) key-args)
- (optimize-trim-stack-frame
- (remove :frame-map (progn #+ignore (warn "maps:~{~&~A~}" unoptimized-code)
- unoptimized-code)
- :key (lambda (x)
- (when (consp x)
- (car x)))))))))
+ (explain nil "~S => ~S" (subseq pc 0 4) p))
+ #+ignore
+ ((let ((i6 (nth 6 pc)))
+ (and (global-funcall-p i2 '(fast-car))
+ (global-funcall-p i6 '(fast-cdr))
+ (load-stack-frame-p i)
+ (eq :eax (twop-dst i))
+ (equal i i4))))
+ ((and (equal i '(:movl :ebx :eax))
+ (global-funcall-p i2 '(fast-car fast-cdr)))
+ (let ((newf (ecase (global-funcall-p i2 '(fast-car fast-cdr))
+ (fast-car 'fast-car-ebx)
+ (fast-cdr 'fast-cdr-ebx))))
+ (setq p `((:call (:edi ,(global-constant-offset newf))))
+ next-pc (nthcdr 2 pc))
+ (explain nil "Changed [~S ~S] to ~S" i i2 newf)))
+ ((and (equal i '(:movl :eax :ebx))
+ (global-funcall-p i2 '(fast-car-ebx fast-cdr-ebx)))
+ (let ((newf (ecase (global-funcall-p i2 '(fast-car-ebx fast-cdr-ebx))
+ (fast-car-ebx 'fast-car)
+ (fast-cdr-ebx 'fast-cdr))))
+ (setq p `((:call (:edi ,(global-constant-offset newf))))
+ next-pc (nthcdr 2 pc))
+ (explain nil "Changed [~S ~S] to ~S" i i2 newf)))
+ ((and (load-stack-frame-p i) (eq :eax (twop-dst i))
+ (global-funcall-p i2 '(fast-car fast-cdr))
+ (preserves-stack-location-p i3 (load-stack-frame-p i))
+ (preserves-register-p i3 :ebx)
+ (eql (load-stack-frame-p i)
+ (load-stack-frame-p i4)))
+ (let ((newf (ecase (global-funcall-p i2 '(fast-car fast-cdr))
+ (fast-car 'fast-car-ebx)
+ (fast-cdr 'fast-cdr-ebx))))
+ (setq p `((:movl ,(twop-src i) :ebx)
+ (:call (:edi ,(global-constant-offset newf)))
+ ,i3
+ ,@(unless (eq :ebx (twop-dst i4))
+ `((:movl :ebx ,(twop-dst i4)))))
+ next-pc (nthcdr 4 pc))
+ (explain nil "load around ~A: ~{~&~A~}~%=>~% ~{~&~A~}"
+ newf (subseq pc 0 5) p))))
+ do (unless (eq p original-p) ; auto-detect whether any heuristic fired..
+ #+ignore (warn "at ~A, ~A inserted ~A" i i2 p)
+ #+ignore (warn "modified at ~S ~S ~S" i i2 i3)
+ (setf code-modified-p t))
+ nconc p)))
+ (if code-modified-p
+ (apply #'optimize-code-internal optimized-code (1+ recursive-count) key-args)
+ (optimize-trim-stack-frame
+ (remove :frame-map (progn #+ignore (warn "maps:~{~&~A~}" unoptimized-code)
+ unoptimized-code)
+ :key (lambda (x)
+ (when (consp x)
+ (car x))))))))))
;;;; Compiler internals
@@ -2076,11 +2084,12 @@
(defmethod print-object ((object binding) stream)
(print-unreadable-object (object stream :type t :identity t)
(when (slot-boundp object 'name)
- (format stream "name: ~S~@[->~S~]~@[ stype: ~A~]"
+ (format stream "name: ~S~@[->~S~]~@[ %~A~]"
(binding-name object)
(unless (eq object (binding-target object))
(binding-name (binding-target object)))
- (when (binding-store-type object)
+ (when (and (slot-boundp object 'store-type)
+ (binding-store-type object))
(apply #'encoded-type-decode
(binding-store-type object)))))))
@@ -2428,27 +2437,39 @@
(declare (ignore binding protect-registers protect-carry init-with-type))
(when init-with-register
(setf free-so-far (remove init-with-register free-so-far)))))
- ((member (instruction-is i)
- '(:movl :testl :andl :addl))
- (setf free-so-far
- (remove-if (lambda (r)
- (or (tree-search i r)
- (tree-search i (register32-to-low8 r))))
- free-so-far)))
- ((member (instruction-is i)
- '(:load-lexical :init-lexvar :car :incf-lexvar))
- (unless (can-expand-extended-p i frame-map)
- (return (values nil t)))
- (let ((exp (expand-extended-code i funobj frame-map)))
- (when (tree-search exp '(:call))
- (return nil))
- (setf free-so-far
- (remove-if (lambda (r)
- (tree-search exp r))
- free-so-far))))
- (t #+ignore (warn "Dist ~D stopped by ~A"
- distance i)
- (return nil)))
+ (t (case (instruction-is i)
+ ((nil :call)
+ (return nil))
+ ((:into))
+ ((:jnz :je :jne :jz))
+ ((:outb)
+ (setf free-so-far
+ (set-difference free-so-far '(:eax :edx))))
+ ((:movb :testb :andb :cmpb)
+ (setf free-so-far
+ (remove-if (lambda (r)
+ (or (tree-search i r)
+ (tree-search i (register32-to-low8 r))))
+ free-so-far)))
+ ((:shrl :cmpl :pushl :popl :leal :movl :testl :andl :addl :subl :imull)
+ (setf free-so-far
+ (remove-if (lambda (r)
+ (tree-search i r))
+ free-so-far)))
+ ((:load-constant :load-lexical :store-lexical :init-lexvar :car :incf-lexvar)
+ (unless (can-expand-extended-p i frame-map)
+ (return (values nil t)))
+ (let ((exp (expand-extended-code i funobj frame-map)))
+ (when (tree-search exp '(:call))
+ (return nil))
+ (setf free-so-far
+ (remove-if (lambda (r)
+ (or (tree-search exp r)
+ (tree-search exp (register32-to-low8 r))))
+ free-so-far))))
+ (t (warn "Dist ~D stopped by ~A"
+ distance i)
+ (return nil)))))
finally (return free-so-far)))
(defun try-locate-in-register (binding var-counts funobj frame-map)
@@ -2987,14 +3008,14 @@
(:eax
(assert (not indirect-p))
(ecase result-mode
- ((:ebx :ecx :edx) `((:movl :eax ,result-mode)))
+ ((:ebx :ecx :edx :esi) `((:movl :eax ,result-mode)))
((:eax :single-value) nil)))
((:ebx :ecx :edx)
(assert (not indirect-p))
(unless (eq result-mode lexb-location)
(ecase result-mode
((:eax :single-value) `((:movl ,lexb-location :eax)))
- ((:ebx :ecx :ecx :esi) `((:movl ,lexb-location ,result-mode))))))
+ ((:ebx :ecx :edx :esi) `((:movl ,lexb-location ,result-mode))))))
(:argument-stack
(assert (<= 2 (function-argument-argnum lexb)) ()
"lexical :argument-stack argnum can't be ~A." (function-argument-argnum lexb))
@@ -3047,7 +3068,7 @@
(cond
((and (binding-lended-p binding)
(not shared-reference-p))
- (case result-mode
+ (case (result-mode-type result-mode)
((:single-value :eax :ebx :ecx :edx :esi :esp)
(install-for-single-value binding binding-location
(single-value-register result-mode) t))
@@ -3066,7 +3087,7 @@
(t (make-result-and-returns-glue
result-mode :eax
(install-for-single-value binding binding-location :eax t)))))
- (t (case (operator result-mode)
+ (t (case (result-mode-type result-mode)
((:single-value :eax :ebx :ecx :edx :esi :esp)
(install-for-single-value binding binding-location
(single-value-register result-mode) nil))
@@ -3106,6 +3127,27 @@
(make-result-and-returns-glue
result-mode :ecx
(install-for-single-value binding binding-location :ecx nil)))
+ (:lexical-binding
+ (let* ((destination result-mode)
+ (dest-location (new-binding-location destination frame-map :default nil)))
+ (cond
+ ((not dest-location) ; unknown, e.g. a borrowed-binding.
+ (warn "unknown dest-loc for ~A" destination)
+ (append (install-for-single-value binding binding-location :ecx nil)
+ (make-store-lexical result-mode :ecx nil frame-map)))
+ ((eql binding-location dest-location)
+ nil)
+ ((member binding-location '(:eax :ebx :ecx :edx))
+ (make-store-lexical destination binding-location nil frame-map))
+ ((member dest-location '(:eax :ebx :ecx :edx))
+ (install-for-single-value binding binding-location dest-location nil))
+ (t #+ignore (warn "binding => binding: ~A => ~A~% => ~A ~A"
+ binding-location
+ dest-location
+ binding
+ destination)
+ (append (install-for-single-value binding binding-location :ecx nil)
+ (make-store-lexical result-mode :ecx nil frame-map))))))
(t (make-result-and-returns-glue
result-mode :eax
(install-for-single-value binding binding-location :eax nil)))
@@ -4266,7 +4308,7 @@
:type ,(type-specifier-primary type))))
desired-result
t))
- ((:ebx)
+ ((:ebx :ecx)
(values (append code
`((:store-lexical ,desired-result
,(result-mode-type returns-provided)
@@ -5108,7 +5150,13 @@
((:function :multiple-values :eax)
:eax)
(:lexical-binding
- :eax)
+ ;; We can use ECX as temporary storage,
+ ;; because this value will be reachable
+ ;; from at least one variable.
+ ;; XXXX But, probably we shouldn't decide
+ ;; on this here, rather use binding
+ ;; as result-mode in :load-lexical.
+ result-mode #+ignore :ecx)
((:ebx :ecx :edx :esi :push
:untagged-fixnum-eax
:untagged-fixnum-ecx
@@ -5441,15 +5489,16 @@
(defun ensure-local-binding (binding funobj)
"When referencing binding in funobj, ensure we have the binding local to funobj."
- (cond
- ((not (typep binding 'binding))
- binding)
- ((eq funobj (binding-funobj binding))
- binding)
- (t (or (find binding (borrowed-bindings funobj)
- :key (lambda (binding)
- (borrowed-binding-target binding)))
- (error "Can't install non-local binding ~W." binding)))))
+ (if (not (typep binding 'binding))
+ binding
+ (let ((binding (binding-target binding)))
+ (cond
+ ((eq funobj (binding-funobj binding))
+ binding)
+ (t (or (find binding (borrowed-bindings funobj)
+ :key (lambda (binding)
+ (borrowed-binding-target binding)))
+ (error "Can't install non-local binding ~W." binding)))))))
(defun binding-type-specifier (binding)
(etypecase binding
@@ -5636,6 +5685,7 @@
(let* ((binding (binding-target binding))
(location (new-binding-location binding frame-map :default nil))
(binding-type (binding-store-type binding)))
+;;; (warn "incf b ~A, loc: ~A, typ: ~A" binding location binding-type)
(cond
((and binding-type
location
@@ -5670,3 +5720,16 @@
register nil frame-map
:protect-registers protect-registers))))))))
+;;;;; Load-constant
+
+(define-find-write-binding-and-type :load-constant (instruction)
+ (destructuring-bind (object result-mode &key (op :movl))
+ (cdr instruction)
+ (when (and (eq op :movl) (typep result-mode 'binding))
+ (check-type result-mode 'lexical-binding)
+ (values result-mode `(eql ,object)))))
+
+(define-extended-code-expander :load-constant (instruction funobj frame-map)
+ (destructuring-bind (object result-mode &key (op :movl))
+ (cdr instruction)
+ (make-load-constant object result-mode funobj frame-map :op op)))