1 (in-package :avm2-compiler
)
3 ;;;; special forms (and probably some things that are techically
4 ;;;; macros/functions according to CL, but implemented directly here
7 ;; official list of special operators:
8 ;; http://www.lispworks.com/documentation/HyperSpec/Body/03_ababa.htm#clspecialops
37 ;; multiple-value-call
38 ;; multiple-value-prog1
47 (define-special* progn
(cdr)
51 append
(scompile form
)
52 ;; ignore return values from intermediate steps
53 when
(or next
(and (consp form
) (eql (car form
) 'return
)))
56 ;; (scompile '(progn "foo" "bar" :true))
59 (define-special let
(bindings &rest body
)
60 (let ((bindings-indices
61 (loop for binding in bindings
62 for j from
(last-local-index)
63 for init
= (if (listp binding
) (second binding
) nil
)
64 for name
= (if (listp binding
) (first binding
) binding
)
65 collect
`(,init
,name .
,j
))))
66 (with-cleanup ((gensym "LET-CLEANUP")
67 `(%asm
(:comment
"let-kill")
68 ,@(loop for
(nil nil . index
) in bindings-indices
69 collect
`(:kill
,index
))
73 (loop for
(init nil . index
) in bindings-indices
74 append
(scompile init
)
75 collect
`(:set-local
,index
))
76 (with-local-vars ((mapcar 'cdr bindings-indices
))
77 ;; compile the body as a progn, and kill the locals on exit
80 ,@(loop for
(nil nil . index
) in bindings-indices
81 collect
`(:kill
,index
))))))))
82 ;; (with-lambda-context (:args '(foo)) (scompile '(let ((foo 1.23) (bar foo)) foo)))
84 (define-special %set-local
(local value
)
85 ;; (%set-local var value) -> value
86 `(,@(scompile value
) ;; calculate value
87 (:dup
) ;; copy value so we can reurn it
88 (:set-local
,(or (get-lambda-local-index local
) (break)))))
89 ;; (with-lambda-context (foo) (scompile '(%set-local foo 2.3)))
91 (define-special %asm
(&rest cdr
)
92 ;; (%asm (op1 args) (op2 ...) ... )
95 (:@ `(:get-local
,(get-lambda-local-index (second x
))))
96 (:@kill
`(:kill
,(get-lambda-local-index (second x
))))
99 (define-special %asm
* (args &rest cdr
)
100 ;; (%asm* (arg list) (op1 args) (op2 ...) ... )
102 (loop for arg in args
103 append
(scompile arg
))
107 (define-special %label
(target)
108 ;; (%label name) ;; for reverse jumps only
110 ;; hack since we always pop after each statement in a progn, gets
111 ;; removed later by peephole pass
114 (define-special %dlabel
(target)
115 ;; (%dlabel name) ;; for forward jumps only
119 (define-special %go
(target)
124 (define-special* tagbody
(body)
125 (let ((tags (loop for tag-or-form in body
126 when
(atom tag-or-form
)
127 collect
(cons tag-or-form
128 (gensym (format nil
"TAGBODY-~a-" tag-or-form
))))))
129 (with-nested-lambda-tags (tags)
130 ;; fixme: use dlabel for forward jumps
131 `(,@(loop for tag-or-form in body
132 if
(atom tag-or-form
)
133 collect
`(:%label
,(get-lambda-tag tag-or-form
))
135 append
(scompile tag-or-form
)
139 (define-special go
(tag)
140 (scompile-cons '%go
(list (get-lambda-tag tag
))))
142 (define-special %go-when
(cond tag
)
143 (scompile-cons '%when
(list cond
(get-lambda-tag tag
))))
145 ;; (with-lambda-context () (scompile '(tagbody foo (go baz) bar 1 baz 2)))
147 (define-special %when
(cond label
)
148 ;; (%when cond label)
153 (define-special %if
(cond false-test true-branch false-branch
)
154 (let ((false-label (gensym "%IF-FALSE-"))
155 (end-label (gensym "%IF-END-")))
157 (,false-test
,false-label
)
158 ,@(scompile true-branch
)
160 (:%dlabel
,false-label
)
161 ,@(scompile false-branch
)
162 (:%dlabel
,end-label
))))
164 (define-special if
(cond true-branch false-branch
)
165 `(,@(scompile `(%if
,cond
:if-false
,true-branch
,false-branch
))))
167 ;; (avm2-asm::with-assembler-context (avm2-asm::code (avm2-asm:assemble-method-body (scompile '(when :true 1)) )))
170 (define-special %inc-local-i
(var)
171 ;; (%inc-local-i var)
172 `((:inc-local-i
,(get-lambda-local-index var
))
173 ;; hack since we always pop after each statement in a progn :/
174 (:get-local
,(get-lambda-local-index var
))))
178 ;;(scompile '(and 1))
179 ;;(scompile '(and 1 2))
182 (define-special* %array
(args)
183 ;; (%array ... ) -> array
184 `(,@(loop for i in args
185 append
(scompile i
)) ;; calculate args
186 (:new-array
,(length args
))))
189 (define-special %error
(value)
193 (define-special %typep
(object type
)
194 `(,@(scompile object
)
195 (:get-lex
,(or (swf-name (find-swf-class type
)) type
))
199 (define-special %type-of
(object)
200 `(,@(scompile object
)
204 ;;; block/return-from
206 ;;; store list of blocks in context, each block has cleanup code and a jump target?
208 ;; return-from needs to be careful with stack, if it isn't just
209 ;; calling :Return-foo
212 (block foo
(return-from foo
1))
213 push block foo
, label
= (gensym block-foo
)
215 ,@compile return-value
221 (block bleh
(unwind-protect (return-from bleh
1) 2))
222 push block bleh
, label1
= gensym
223 push block uwp
, label2
= gensym
, cleanup
= gensym
224 ,@compile return-value
= 1
225 set-local foo
<index of goto to come back here
>
230 ,@compile cleanup
= 2
232 computed-goto back to %foo
239 (block bleh
(unwind-protect (unwind-protect 1 2) 3))
243 (define-special block
(name &body body
)
244 (let ((end (gensym "BLOCK-END-")))
245 (with-nested-lambda-block ((cons name
(make-lambda-block name end nil end
))
247 `(,@(scompile `(progn ,@body
))
248 (:set-local
,(get-lambda-local-index end
))
250 (:get-local
,(get-lambda-local-index end
))))))
252 (define-special %flet
((fn-name (&rest fn-args
) &body fn-body
) &body body
)
253 "limited version of flet, only handles 1 function, can't manipulate
254 the function directly, can only call it within the current function,
255 only normal args (no &rest,&key,&optional,etc)
256 call with %flet-call, which sets up hidden return label arg
258 ;; todo: handle multiple functions?
259 ;; fixme:would be nicer to put these at the end with the continuation table,
260 ;; but just compiling inline with a jump over it for now...
261 (let* ((end-label (gensym "%FLET-END-"))
262 (return-arg (gensym "%FLET-CONTINUATION-"))
263 (locals (loop for arg in
(cons return-arg fn-args
)
264 for j from
(last-local-index)
265 collect
(cons arg j
))))
266 ;; locals for a flet are ugly, since they need to keep their
267 ;; indices allocated during body, but names are only valid during
268 ;; fn-body, so we wrap both in with-local-vars, but kill the names
270 ;; we also add an implicit 'return' param to specify the continuation
271 (with-local-vars (locals)
272 ;;fixme: hack- write real code for this
273 (push (cons fn-name locals
) (%flets
*current-lambda
*))
276 ;; load parameters into regs
277 #+nil
,@(loop for
(nil . i
) in locals
278 collect
`(:set-local
,i
) into temp
279 finally
(return (nreverse temp
)))
280 ;; compile %flet body
281 ,@(scompile `(progn ,@fn-body
))
282 ;; store return value
283 (:set-local
,(get-lambda-local-index (local-return-var *current-lambda
*)))
284 ;; push return address index
285 (:get-local
,(get-lambda-local-index return-arg
))
286 (:set-local
,(get-lambda-local-index (continuation-var *current-lambda
*)))
288 ,@(loop for
(nil . i
) in locals
290 ;; return through continuation table
291 (:jump
,(continuation-var *current-lambda
*))
292 ;; remove local variable names from current scope (keeping indices used)
293 ,@(progn (kill-lambda-local-names fn-args
)
295 (:%dlabel
,end-label
)
297 ,@(scompile `(progn ,@body
))))))
299 (define-special call-%flet
(name &rest args
)
300 (let* ((continuation-label (gensym "CALL-%FLET-CONTINUATION-"))
301 (continuation-index (add-lambda-local-continuation continuation-label
))
302 (arg-indices (cdr (assoc name
(%flets
*current-lambda
*)))))
303 `((:push-int
,continuation-index
)
305 (:set-local
,(cdr (car arg-indices
)))
306 ,@(loop for arg in args
307 for
(nil . i
) in
(cdr arg-indices
)
308 append
(scompile arg
)
309 collect
`(:set-local
,i
))
310 (:comment
"call-%flet" ,name
,(%flets
*current-lambda
*) ,(unless name
(break)))
312 ;; need real label instead of dlabel, since we jump backwards
313 ;; from lookupswitch at end
314 (:%label
,continuation-label
)
316 (:get-local
,(get-lambda-local-index (local-return-var *current-lambda
*))))))
318 (define-special return-from
(name &optional value
)
319 (let ((block (get-lambda-block name
))
320 (cleanups (get-lambda-cleanups name
)))
322 (:set-local
,(get-lambda-local-index (return-var block
)))
323 ,@(loop for i in cleanups
324 collect
`(:comment
"return-from cleanup" ,i
,cleanups
,(blocks *current-lambda
*))
326 collect
`(:comment
"return-from cleanup done")
328 (:jump
,(end-label block
)))))
330 (define-special prog1
(value-form &body body
)
331 (let ((temp (gensym "PROG1-VALUE-")))
333 `(let ((,temp
,value-form
))
338 (define-special %with-cleanup
((name code
) form
)
339 (with-cleanup (name code
)
342 (define-special unwind-protect
(protected &body cleanup
)
343 (let ((cleanup-name (gensym "UWP-CLEANUP-")))
345 `(%flet
(,cleanup-name
() ,@cleanup
)
346 (%with-cleanup
(,cleanup-name
(call-%flet
,cleanup-name
))
349 (call-%flet
,cleanup-name
)))))))
351 (define-special* list
(rest)
352 (labels ((expand-rest (rest)
354 (list 'cons
(car rest
) (expand-rest (cdr rest
)))
356 (scompile (expand-rest rest
))))
357 ;;(scompile '(list (list 1) (list 2)))
358 ;;(scompile '(list 1))
359 ;;(scompile '(quote (1 2 3)))
360 ;;(scompile '(list '(list 1 2 3)))
362 (define-special* list
* (rest)
363 (labels ((expand-rest (rest)
364 (if (consp (cdr rest
))
365 (list 'cons
(car rest
) (expand-rest (cdr rest
)))
368 (error "not enough arguments to LIST*"))
369 (scompile (expand-rest rest
))))
371 ;;; internal aref, handles single dimensional flash::Array
372 (define-special %aref-1
(array index
)
375 (:get-property
(:multiname-l
"" ""))))
378 (define-special %set-aref-1
(array index value
)
382 (:set-property
(:multiname-l
"" ""))))
384 ;;; temporary hack to get inlined cons/car/cdr, speeds up tests noticeably
385 ;;; types and better compilation should give a few orders of magnitude though
386 (define-special cons
(a b
)
387 `((:find-property-strict cons-type
)
390 (:construct-prop cons-type
2)
393 ;;; coercing to cons-type before accessing slots is ~2x faster
394 ;;; using get-slot instead of get-property is maybe a few % faster
395 ;;; checking type explicitly is slow, so just using built-in check for now
396 ;;; (which works, but doesn't throw the CL specified error type)
397 ;;; :get-lex might be the slow part, so putting cons-type in a global
398 ;;; might help speed of proper type check
399 (define-special car
(a) ;;; FIXME: handle non-cons properly
400 (let ((temp (gensym "CAR-TEMP-")))
407 #+nil
(:get-property %car
)
410 (define-special cdr
(a) ;;; FIXME: handle non-cons properly
411 (let ((temp (gensym "CDR-TEMP-")))
418 #+nil
(:get-property %cdr
)
423 ;;(scompile '(list* 1 2 3 4 5))
424 ;;(scompile '(list* 1))
426 (define-special function
(arg &optional object
)
427 ;; fixme: not all branches tested yet...
430 ;; if OPERATOR is a known method, call with %call-property
431 ;; (prop obj args...) === obj.prop(args)
432 ((setf tmp
(find-swf-method arg
*symbol-table
*))
433 (break "f-s-m ~s" tmp
)
434 (scompile `(%get-property
,(swf-name tmp
) ,object
)))
436 ;; if OPERATOR is a known static method, call with %call-lex-prop
437 ;; (prop obj args...) === obj.prop(args)
438 ((setf tmp
(find-swf-static-method arg
*symbol-table
*))
439 (scompile `(%get-lex-prop
,(first tmp
) ,(second tmp
))))
441 ;; todo: decide if we should do something for the pretend accessors?
443 ;; normal function call, find-prop-strict + call-property
444 ((setf tmp
(find-swf-function arg
*symbol-table
*))
445 (break "f-s-f ~s" tmp
)
446 (scompile `(%get-property-without-object
,tmp
)))
448 ;; default = normal call?
449 ;; fixme: might be nicer if we could detect unknown functions
451 (scompile `(%get-property-without-object
,arg
))))))
453 (define-special quote
(object)
456 #+nil
(dump-defun-asm (&arest rest
) 'a
)
457 #+nil
(dump-defun-asm (&arest rest
) '1)
460 #+nil
(with-lambda-context ()
461 (scompile '(block foo
2 (if nil
(return-from foo
4) 5) 3)))
465 (avm2-asm::avm2-disassemble
467 (avm2-asm::with-assembler-context
468 (avm2-asm::assemble-method-body
469 (with-simple-lambda-context ()
472 (scompile '(%flet
(bleh (a b c
) (+ a b c
))
473 (+ (call-%flet bleh
1 2 3)
474 (call-%flet bleh
5 6 7))))
475 (compile-lambda-context-cleanup 'foo
)))))))
479 (avm2-asm::avm2-disassemble
481 (avm2-asm::with-assembler-context
482 (avm2-asm::assemble-method-body
483 (dump-defun-asm () (let ((s2 "<"))
487 (return-from foo
"-ret-")