3 ;;;; simple peephole optimizer for as3 asm, to handle some obvious cases
4 ;;;; of redundant code in the compiler
5 ;;;; ex. (peephole '((:pushnull) (:pop))) -> ()
8 ;;; hash of symbol -> list of lambdas to call to try to optimize forms
9 ;;; starting with that symbol
10 (defparameter *peephole-patterns
* (make-hash-table))
12 (defun %peephole
(forms)
13 (let* ((sym (caar forms
))
14 (funs (gethash sym
*peephole-patterns
*)))
16 for j
= (funcall i forms
)
18 do
(return-from %peephole j
))
22 (defun peephole (forms)
23 "as3 asm peephole optimizer, pass in list of asm forms, returns
25 ;; quick hack peephole optimizer, for simple stuff like pushnull+pop, etc
27 (loop for i
= forms then
(if (not (eql j
:keep
)) j
(cdr i
))
30 ;;do (format t "i=~s~% j=~s~%" i j)
36 (defmacro def-peephole
(name length args
&body body
)
37 ;; name is symbol or list of symbols to match against car of form
38 ;; length = minimum # of forms required for a given pattern
39 ;; fixme: detect pattern length automatically
40 ;; args = destructuring lambda list passed the current remaining
41 ;; forms to be assembled, with the addition of nil to mark
42 ;; entries to be ignored when destructuring
43 ;; ex: (nil &rest rest) would get the forms after the current one
44 ;; ((nil arg) &rest rest) would get the same, + arg to the current form
45 ;; body = code to run to test/perform peephole pass, returns either new
46 ;; list of forms, or :keep to keep current form
47 ;; (can't return nil, since then we wouldn't be able to get rid of
48 ;; the last form(s) in a set)
49 (unless (consp name
) (setf name
(list name
)))
50 (let ((forms (gensym "FORMS-"))
57 (car (push (gensym "IGNORE-") arg-syms
)))
58 ((consp (car list
)) (gensym-nils (car list
)))
60 (gensym-nils (cdr list
))))))
63 with iargs
= (gensym-nils args
)
64 with lambda
= `(lambda (,forms
)
65 (when (>= (length ,forms
) ,length
)
66 (destructuring-bind (,@iargs
) ,forms
68 `((declare (ignore ,@arg-syms
))))
71 collect
`(push ,lambda
(gethash ',i
*peephole-patterns
* nil
)))))))
74 ;; (kill x)+ (returnvalue) -> (returnvalue)
75 ;; -- (can't do directly, since then it would only get the last kill
76 ;; need to check rest for more kills and then a returnvalue)
80 ;;; push-*/get-*+ pop -> ()
81 (def-peephole (:push-null
:push-undefined
:push-byte
:push-short
:push-true
82 :push-false
:push-nan
:push-string
:push-int
83 :push-uint
:push-double
:push-namespace
84 :get-local-0
:get-local-1
:get-local-2
:get-local-3
85 :get-local
) 2 (nil next
&rest rest
)
86 (if (eql :pop
(car next
))
87 (progn #+(or)(format t
"drop ~s -> ~s~%" next rest
)
91 ;;; get-local N (N<4) -> get-local-n
92 (def-peephole :get-local
1 ((nil local
) &rest rest
)
94 (cons (elt '((:get-local-0
) (:get-local-1
)
95 (:get-local-2
) (:get-local-3
)) local
)
99 ;;; set-local N (N<4) -> set-local-n
100 (def-peephole :set-local
1 ((nil local
) &rest rest
)
102 (cons (elt '((:set-local-0
) (:set-local-1
)
103 (:set-local-2
) (:set-local-3
)) local
)
107 ;;; dup setlocal pop -> setlocal
108 (def-peephole :dup
3 (nil next pop
&rest rest
)
109 (if (and (eql (car pop
) :pop
)
110 (member (car next
) '(:set-local-0
:set-local-1
:set-local-2
111 :set-local-3
:set-local
)))
116 ;;; less-than/greater-than/equals + if-true/if-false -> if-lt/etc
117 ;;; fixme: generalize/combine these
118 (def-peephole :less-than
2 (nil next
&rest rest
)
120 ((eql :if-true
(car next
))
121 (cons `(:if-lt
,@(cdr next
)) rest
))
122 ((eql :if-false
(car next
))
123 (cons `(:if-nlt
,@(cdr next
)) rest
))
126 (def-peephole :greater-than
2 (nil next
&rest rest
)
128 ((eql :if-true
(car next
))
129 (cons `(:if-gt
,@(cdr next
)) rest
))
130 ((eql :if-false
(car next
))
131 (cons `(:if-ngt
,@(cdr next
)) rest
))
134 (def-peephole :equals
2 (nil next
&rest rest
)
136 ((eql :if-true
(car next
))
137 (cons `(:if-eq
,@(cdr next
)) rest
))
138 ((eql :if-false
(car next
))
139 (cons `(:if-ne
,@(cdr next
)) rest
))
142 ;; hack to allow comments in generated asm
143 (def-peephole :comment
1 (nil &rest rest
)
146 ;; fixme: move interning into the assembler so we can peephole push-int
147 ;; into push-byte or push-short if applicable
151 (peephole `((get-local 1) (pop) ;; -> nil
152 (set-local 1) ;; -> (set-local-1)
154 (equals) (if-false) ;; -> (if-ne)
155 (less-than) (if-true) ;; -> (if-lt)
156 (push-null) (pop)))) ;; -> nil