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
)))
57 ;; (scompile '(progn "foo" "bar" :true))
59 #+nil
(define-special return
(value)
63 ;;; fixme: this adds a :pop after :return-value, is that correct?
64 ;; (scompile '(progn "foo" (return :false) :true))
69 (define-special let
(bindings &rest body
)
70 (let ((bindings-indices
71 (loop for binding in bindings
72 for j from
(last-local-index)
73 for init
= (if (listp binding
) (second binding
) nil
)
74 for name
= (if (listp binding
) (first binding
) binding
)
75 collect
`(,init
,name .
,j
))))
76 (with-cleanup ((gensym "LET-CLEANUP")
77 `(%asm
(:comment
"let-kill")
78 ,@(loop for
(nil nil . index
) in bindings-indices
79 collect
`(:kill
,index
))
83 (loop for
(init nil . index
) in bindings-indices
84 append
(scompile init
)
85 collect
`(:set-local
,index
))
86 (with-local-vars ((mapcar 'cdr bindings-indices
))
87 ;; compile the body as a progn, and kill the locals on exit
90 ,@(loop for
(nil nil . index
) in bindings-indices
91 collect
`(:kill
,index
))))))))
92 ;; (with-lambda-context (:args '(foo)) (scompile '(let ((foo 1.23) (bar foo)) foo)))
94 ;;; let* is uglier to implement without modifying lambda context stuff
95 ;;; directly, so implementing in terms of let with a macro in cl lib
97 ;;(define-special let* (bindings &rest body)
98 ;; (with-nested-lambda-context
100 ;; ;; set up bindings
101 ;; (loop for binding in bindings
102 ;; for j from (length (locals *current-lambda*))
103 ;; if (consp binding)
104 ;; append (scompile (second binding))
105 ;; and collect `(:set-local ,j )
106 ;; and do (push (cons (car binding) j) (locals *current-lambda*))
108 ;; do (push (cons binding j) (locals *current-lambda*)))
109 ;; ;; compile the body as a progn, and kill the locals on exit
110 ;; `(,@(scompile `(progn ,@body))
111 ;; ,@(loop for binding in bindings
112 ;; for name = (if (consp binding) (car binding) binding)
113 ;; collect `(:kill ,(get-lambda-local-index name)))))))
114 ;; (with-simple-lambda-context (foo) (scompile '(let* ((foo 1.23) (bar foo)) foo)))
117 (define-special %set-local
(local value
)
118 ;; (%set-local var value) -> value
119 `(,@(scompile value
) ;; calculate value
120 (:dup
) ;; copy value so we can reurn it
121 (:set-local
,(get-lambda-local-index local
))))
122 ;; (with-lambda-context (foo) (scompile '(%set-local foo 2.3)))
124 (define-special %asm
(&rest cdr
)
125 ;; todo: add a version (or option?) to compile args, so we don't need to guess local indices, or compile them by hand
126 ;; (%asm (op1 args) (op2 ...) ... )
130 (define-special %label
(target)
131 ;; (%label name) ;; for reverse jumps only
133 ;; hack since we always pop after each statement in a progn, gets
134 ;; removed later by peephole pass
137 (define-special %dlabel
(target)
138 ;; (%dlabel name) ;; for forward jumps only
142 (define-special %go
(target)
147 (define-special* tagbody
(body)
148 (let ((tags (loop for tag-or-form in body
149 when
(atom tag-or-form
)
150 collect
(cons tag-or-form
151 (gensym (format nil
"TAGBODY-~a-" tag-or-form
))))))
152 (with-nested-lambda-tags (tags)
153 ;; fixme: use dlabel for forward jumps
154 `(,@(loop for tag-or-form in body
155 if
(atom tag-or-form
)
156 collect
`(:%label
,(get-lambda-tag tag-or-form
))
158 append
(scompile tag-or-form
)
162 (define-special go
(tag)
163 (scompile-cons '%go
(list (get-lambda-tag tag
))))
165 ;; (with-lambda-context () (scompile '(tagbody foo (go baz) bar 1 baz 2)))
167 (define-special %when
(cond label
)
168 ;; (%when cond label)
173 #+nil
(define-special when
(cond &rest body
)
175 (let ((label (gensym "WHEN1-"))
176 (label2 (gensym "WHEN2-")))
179 ,@(scompile `(progn ,@body
))
184 (:%dlabel
,label2
))))
186 (define-special %if
(cond false-test true-branch false-branch
)
187 (let (#+nil
(true-label (gensym "%IF-TRUE-"))
188 (false-label (gensym "%IF-FALSE-"))
189 (end-label (gensym "%IF-END-")))
191 (,false-test
,false-label
)
192 ,@(scompile true-branch
)
194 (:%dlabel
,false-label
)
195 ,@(scompile false-branch
)
196 (:%dlabel
,end-label
))))
198 (define-special if
(cond true-branch false-branch
)
199 `(,@(scompile `(%if
,cond
:if-false
,true-branch
,false-branch
))))
201 ;; (avm2-asm::with-assembler-context (avm2-asm::code (avm2-asm:assemble-method-body (scompile '(when :true 1)) )))
204 (define-special %inc-local-i
(var)
205 ;; (%inc-local-i var)
206 `((:inc-local-i
,(get-lambda-local-index var
))
207 ;; hack since we always pop after each statement in a progn :/
208 (:get-local
,(get-lambda-local-index var
))))
210 #+nil
(define-special dotimes
((var count
&optional result
) &rest body
)
211 ;; (dotimes (var count &optional result) body)
213 ;; set local for counter
214 ;; set local for limit
215 ;;(format t "dotimes : var=~s count=~s result=~s~%body=~s~%" var count result body)
216 (let ((label (gensym "LABEL-"))
217 (label2 (gensym "LABEL2-"))
218 (max (gensym "MAX-")))
219 (scompile ; format t "~s"
221 ;; var should not be valid while evaluating max
226 ;(%set-local ,var (+ ,var 1))
229 (%when
(%
2< ,var
,max
) ,label
)
230 ;; fixme: make sure var is still valid, and = max while evaluating result
239 #+nil
(defmethod scompile-cons ((car (eql 'and
)) cdr
)
242 (1 (scompile (first cdr
)))
244 (let ((true-label (gensym "true-"))
245 (false-label (gensym "false-")))
247 (loop for first
= t then nil
249 unless first collect
`(:pop
)
252 collect
`(:if-false
,false-label
))
253 `((:jump
,true-label
)
254 (:%dlabel
,false-label
)
257 (:%dlabel
,true-label
)))))))
260 ;;(scompile '(and 1))
261 ;;(scompile '(and 1 2))
264 (define-special* %array
(args)
265 ;; (%array ... ) -> array
266 `(,@(loop for i in args
267 append
(scompile i
)) ;; calculate args
268 (:new-array
,(length args
))))
271 (define-special %error
(value)
275 #+nil
(define-special %typep
(object type
)
276 `(,@(scompile object
)
279 (define-special %typep
(object type
)
280 `(,@(scompile object
)
284 (define-special %type-of
(object)
285 `(,@(scompile object
)
289 ;;; block/return-from
291 ;;; store list of blocks in context, each block has cleanup code and a jump target?
293 ;; return-from needs to be careful with stack, if it isn't just
294 ;; calling :Return-foo
297 (block foo
(return-from foo
1))
298 push block foo
, label
= (gensym block-foo
)
300 ,@compile return-value
306 (block bleh
(unwind-protect (return-from bleh
1) 2))
307 push block bleh
, label1
= gensym
308 push block uwp
, label2
= gensym
, cleanup
= gensym
309 ,@compile return-value
= 1
310 set-local foo
<index of goto to come back here
>
315 ,@compile cleanup
= 2
317 computed-goto back to %foo
324 (block bleh
(unwind-protect (unwind-protect 1 2) 3))
328 (define-special block
(name &body body
)
329 (let ((end (gensym "BLOCK-END-")))
330 (with-nested-lambda-block ((cons name
(make-lambda-block name end nil end
))
332 `(,@(scompile `(progn ,@body
))
333 (:set-local
,(get-lambda-local-index end
))
335 (:get-local
,(get-lambda-local-index end
))))))
337 (define-special %flet
((fn-name (&rest fn-args
) &body fn-body
) &body body
)
338 "limited version of flet, only handles 1 function, can't manipulate
339 the function directly, can only call it within the current function,
340 only normal args (no &rest,&key,&optional,etc)
341 call with %flet-call, which sets up hidden return label arg
343 ;; todo: handle multiple functions?
344 ;; fixme:would be nicer to put these at the end with the continuation table,
345 ;; but just compiling inline with a jump over it for now...
346 (let* ((end-label (gensym "%FLET-END-"))
347 (return-arg (gensym "%FLET-CONTINUATION-"))
348 (locals (loop for arg in
(cons return-arg fn-args
)
349 for j from
(last-local-index)
350 collect
(cons arg j
))))
351 ;; locals for a flet are ugly, since they need to keep their
352 ;; indices allocated during body, but names are only valid during
353 ;; fn-body, so we wrap both in with-local-vars, but kill the names
355 ;; we also add an implicit 'return' param to specify the continuation
356 (with-local-vars (locals)
357 ;;fixme: hack- write real code for this
358 (push (cons fn-name locals
) (%flets
*current-lambda
*))
361 ;; load parameters into regs
362 #+nil
,@(loop for
(nil . i
) in locals
363 collect
`(:set-local
,i
) into temp
364 finally
(return (nreverse temp
)))
365 ;; compile %flet body
366 ,@(scompile `(progn ,@fn-body
))
367 ;; store return value
368 (:set-local
,(get-lambda-local-index (local-return-var *current-lambda
*)))
369 ;; push return address index
370 (:get-local
,(get-lambda-local-index return-arg
))
371 (:set-local
,(get-lambda-local-index (continuation-var *current-lambda
*)))
373 ,@(loop for
(nil . i
) in locals
375 ;; return through continuation table
376 (:jump
,(continuation-var *current-lambda
*))
377 ;; remove local variable names from current scope (keeping indices used)
378 ,@(progn (kill-lambda-local-names fn-args
)
380 (:%dlabel
,end-label
)
382 ,@(scompile `(progn ,@body
))))))
384 (define-special call-%flet
(name &rest args
)
385 (let* ((continuation-label (gensym "CALL-%FLET-CONTINUATION-"))
386 (continuation-index (add-lambda-local-continuation continuation-label
))
387 (arg-indices (cdr (assoc name
(%flets
*current-lambda
*)))))
388 `((:push-int
,continuation-index
)
390 (:set-local
,(cdr (car arg-indices
)))
391 ,@(loop for arg in args
392 for
(nil . i
) in
(cdr arg-indices
)
393 append
(scompile arg
)
394 collect
`(:set-local
,i
))
395 (:comment
"call-%flet" ,name
,(%flets
*current-lambda
*) ,(unless name
(break)))
397 ;; need real label instead of dlabel, since we jump backwards
398 ;; from lookupswitch at end
399 (:%label
,continuation-label
)
401 (:get-local
,(get-lambda-local-index (local-return-var *current-lambda
*))))))
403 (define-special return-from
(name &optional value
)
404 (let ((block (get-lambda-block name
))
405 (cleanups (get-lambda-cleanups name
)))
407 (:set-local
,(get-lambda-local-index (return-var block
)))
408 ,@(loop for i in cleanups
409 collect
`(:comment
"return-from cleanup" ,i
,cleanups
,(blocks *current-lambda
*))
411 collect
`(:comment
"return-from cleanup done")
413 (:jump
,(end-label block
)))))
415 (define-special prog1
(value-form &body body
)
416 (let ((temp (gensym "PROG1-VALUE-")))
418 `(let ((,temp
,value-form
))
423 (define-special %with-cleanup
((name code
) form
)
424 (with-cleanup (name code
)
427 (define-special unwind-protect
(protected &body cleanup
)
428 (let ((cleanup-name (gensym "UWP-CLEANUP-")))
430 `(%flet
(,cleanup-name
() ,@cleanup
)
431 (%with-cleanup
(,cleanup-name
(call-%flet
,cleanup-name
))
434 (call-%flet
,cleanup-name
)))))))
436 (define-special* list
(rest)
437 (labels ((expand-rest (rest)
439 (list 'cons
(car rest
) (expand-rest (cdr rest
)))
441 (scompile (expand-rest rest
))))
443 (define-special* list
* (rest)
444 (labels ((expand-rest (rest)
445 (if (consp (cdr rest
))
446 (list 'cons
(car rest
) (expand-rest (cdr rest
)))
449 (error "not enough arguments to LIST*"))
450 (scompile (expand-rest rest
))))
452 ;;; partial implementation of aref, handles single dimensional flash::Array
453 (define-special aref
(array index
)
456 (:get-property
(:multiname-l
"" ""))))
459 ;;(scompile '(list* 1 2 3 4 5))
460 ;;(scompile '(list* 1))
462 #+nil
(with-lambda-context ()
463 (scompile '(block foo
2 (if nil
(return-from foo
4) 5) 3)))
467 (avm2-asm::avm2-disassemble
469 (avm2-asm::with-assembler-context
470 (avm2-asm::assemble-method-body
471 (with-simple-lambda-context ()
474 (scompile '(%flet
(bleh (a b c
) (+ a b c
))
475 (+ (call-%flet bleh
1 2 3)
476 (call-%flet bleh
5 6 7))))
477 (compile-lambda-context-cleanup 'foo
)))))))
481 (avm2-asm::avm2-disassemble
483 (avm2-asm::with-assembler-context
484 (avm2-asm::assemble-method-body
485 (dump-defun-asm () (let ((s2 "<"))
489 (return-from foo
"-ret-")