1 ;;; generation of control flow graph
3 ;; special variables whose contents are located in the FSR registers
5 (define fsr-variables '(SIXPIC_FSR0 SIXPIC_FSR1 SIXPIC_FSR2))
16 label-name ; if the block had a label
25 extender: define-type-of-instr
26 (live-before unprintable:)
27 (live-after unprintable:)
34 (define-type-of-instr call-instr
38 (define-type-of-instr return-instr
42 (define (new-instr id src1 src2 dst)
43 (make-instr '() '() #f id src1 src2 dst))
45 ;; list of all conditional branching generic instructions
46 (define conditional-instrs ;; TODO add as we add specialized instructions
47 '(x==y x!=y x<y x>y x<=y x>=y))
49 (define (new-call-instr def-proc)
50 (make-call-instr '() '() #f 'call #f #f #f def-proc))
52 (define (new-return-instr def-proc)
53 (make-return-instr '() '() #f 'return #f #f #f def-proc))
56 (let* ((label-num (cfg-next-label-num cfg))
57 (bb (make-bb label-num #f #f '() '() '() '())))
63 (number->string label-num)))))
64 (cfg-bbs-set! cfg (cons bb (cfg-bbs cfg)))
65 (cfg-next-label-num-set! cfg (+ 1 (cfg-next-label-num cfg)))
68 (define (add-instr bb instr)
69 (let ((rev-instrs (bb-rev-instrs bb)))
70 (bb-rev-instrs-set! bb (cons instr rev-instrs))))
72 (define (add-succ bb succ)
73 (bb-succs-set! bb (cons succ (bb-succs bb)))
74 (bb-preds-set! succ (cons bb (bb-preds succ))))
76 (define (generate-cfg ast)
78 (define cfg (new-cfg))
80 (define bb #f) ; current bb
91 (define current-def-proc #f)
92 (define break-stack '())
93 (define continue-stack '())
94 (define delayed-post-incdec '())
96 (define (push-break x)
97 (set! break-stack (cons x break-stack)))
100 (set! break-stack (cdr break-stack)))
102 (define (push-continue x)
103 (set! continue-stack (cons x continue-stack)))
105 (define (pop-continue)
106 (set! continue-stack (cdr continue-stack)))
108 (define (push-delayed-post-incdec x)
109 (set! delayed-post-incdec (cons x delayed-post-incdec)))
111 (define (program ast)
112 (let loop ((asts (ast-subasts ast)))
113 (if (not (null? asts))
114 (let ((ast (car asts)))
115 (if (null? (cdr asts))
116 (let ((value (expression ast)))
117 (return-with-no-new-bb value))
120 (loop (cdr asts))))))))
122 (define (toplevel ast)
123 (cond ((def-variable? ast)
125 ((def-procedure? ast)
130 (define (def-variable ast)
131 (let ((subasts (ast-subasts ast)))
132 (if (not (null? subasts)) ; if needed, set the variable
133 (let ((value (expression (subast1 ast))))
134 (let ((ext-value (extend value (def-variable-type ast))))
135 (move-value value (def-variable-value ast)))))))
137 (define (def-procedure ast)
139 ;; resolve the C gotos by setting the appropriate successor to their bb
140 (define (resolve-all-gotos start table visited)
141 (if (not (memq start visited))
142 (begin (for-each (lambda (x)
143 (if (and (eq? (instr-id x) 'goto)
144 (instr-dst x)) ; unresolved label
145 (let ((target (assoc (instr-dst x) table)))
147 (begin (add-succ start (cdr target))
148 (instr-dst-set! x #f))
149 (error "invalid goto target" (instr-dst x))))))
150 (bb-rev-instrs start))
151 (for-each (lambda (x)
152 (resolve-all-gotos x table (cons start visited)))
157 (def-procedure-entry-set! ast entry)
158 (set! current-def-proc ast)
160 (for-each statement (ast-subasts ast))
161 (return-with-no-new-bb ast)
162 (set! current-def-proc #f)
163 (resolve-all-gotos entry (list-named-bbs entry '()) '())
166 ;; returns a list of all named bbs in the successor-tree of a given bb
167 (define (list-named-bbs start visited)
168 (if (not (memq start visited))
171 (map (lambda (bb) (list-named-bbs bb (cons start visited)))
173 (if (bb-label-name start)
174 (cons (cons (bb-label-name start) start) succs)
178 (define (statement ast)
179 (cond ((def-variable? ast)
186 (if (null? (cddr (ast-subasts ast)))
207 (if (block-name ast) ; named block ?
208 (begin (let ((old-bb bb))
210 (add-succ old-bb bb))
211 (bb-label-name-set! bb (block-name ast)) ))
212 (for-each statement (ast-subasts ast)))
214 (define (move from to)
215 (emit (new-instr 'move from #f to)))
217 (define (move-value from to)
218 (let loop ((from (value-bytes from))
219 (to (value-bytes to)))
220 (cond ((null? to)) ; done
221 ((null? from) ; promote the value by padding
222 (move (new-byte-lit 0) (car to))
223 (loop from (cdr to)))
225 (move (car from) (car to))
226 (loop (cdr from) (cdr to))))))
228 (define (return-with-no-new-bb def-proc)
229 (emit (new-return-instr def-proc)))
232 (if (null? (ast-subasts ast))
233 (return-with-no-new-bb current-def-proc)
234 (let ((value (expression (subast1 ast))))
235 (let ((ext-value (extend value (def-procedure-type current-def-proc))))
236 (move-value value (def-procedure-value current-def-proc))
237 (return-with-no-new-bb current-def-proc))))
241 (let* ((bb-join (new-bb))
243 (test-expression (subast1 ast) bb-then bb-join)
245 (statement (subast2 ast))
249 (let* ((bb-join (new-bb))
252 (test-expression (subast1 ast) bb-then bb-else) ;; TODO invert ?
254 (statement (subast2 ast))
257 (statement (subast3 ast))
262 (let* ((bb-cont (new-bb))
265 (push-continue bb-cont)
269 (test-expression (subast1 ast) bb-body bb-exit)
271 (statement (subast2 ast))
277 (define (do-while ast)
278 (let* ((bb-body (new-bb))
281 (push-continue bb-cont)
284 (statement (subast1 ast))
286 (test-expression (subast2 ast) bb-body bb-exit)
292 (let* ((bb-loop (new-bb))
296 (statement (subast1 ast))
298 (push-continue bb-cont)
301 (test-expression (subast2 ast) bb-body bb-exit)
303 (statement (subast4 ast))
306 (expression (subast3 ast))
313 (let* ((var (subast1 ast))
318 (prev-bb decision-bb))
320 (for-each (lambda (x) ; generate each case
321 (in (new-bb)) ; this bb will be given the name of the case
322 (add-succ decision-bb bb)
323 (if (null? (bb-succs prev-bb)) ; if the previous case didn't end in a break, fall through
330 (cdr (ast-subasts ast)))
331 (if (null? (bb-succs prev-bb)) ; if the last case didn't end in a break, fall through to the exit
332 (add-succ prev-bb exit-bb))
333 (bb-succs-set! decision-bb (reverse (bb-succs decision-bb))) ; preserving the order is important in the absence of break
334 (set! case-list (list-named-bbs decision-bb '()))
335 (set! default (keep (lambda (x) (eq? (car x) 'default))
336 (list-named-bbs decision-bb '())))
337 (set! case-list (keep (lambda (x) (and (list? (car x))
338 (eq? (caar x) 'case)))
340 (bb-succs-set! decision-bb '()) ; now that we have the list of cases we don't need the successors anymore
341 (let loop ((case-list case-list)
342 (decision-bb decision-bb))
344 (if (not (null? case-list))
345 (let* ((next-bb (new-bb))
346 (curr-case (car case-list))
347 (curr-case-id (cadar curr-case))
348 (curr-case-bb (cdr curr-case)))
349 (emit (new-instr 'x==y
350 (car (value-bytes (expression var)))
351 (new-byte-lit curr-case-id) #f)) ;; TODO what about work duplication ?
352 (add-succ bb next-bb) ; if false, keep looking
353 (add-succ bb curr-case-bb) ; if true, go to the case
354 (loop (cdr case-list)
356 (gen-goto (if (not (null? default))
363 (gen-goto (car break-stack)))
365 (define (continue ast)
366 (gen-goto (car continue-stack)))
368 ;; generates a goto with a target label. once the current function definition
369 ;; is over, all these labels are resolved. therefore, we don't have any gotos
370 ;; that jump from a function to another
372 (emit (new-instr 'goto #f #f (subast1 ast))))
374 (define (gen-goto dest)
376 (emit (new-instr 'goto #f #f #f)))
378 (define (test-expression ast bb-true bb-false)
380 (define (test-byte id byte1 byte2 bb-true bb-false)
381 (define (test-lit id x y)
386 (else (error "invalid test")))
389 (cond ((and (byte-lit? byte1) (byte-lit? byte2))
390 (if (test-lit id (byte-lit-val byte1) (byte-lit-val byte2))
392 (gen-goto bb-false)))
394 (add-succ bb bb-false) ; since we cons each new successor at the front, true has to be added last
395 (add-succ bb bb-true)
396 (emit (new-instr id byte1 byte2 #f)))
403 (else (error "invalid test")))))
404 (add-succ bb bb-false)
405 (add-succ bb bb-true)
406 (emit (new-instr id byte2 byte1 #f))))
408 (add-succ bb bb-false)
409 (add-succ bb bb-true)
410 (emit (new-instr id byte1 byte2 #f))))) ;; TODO doesn't change from if we had literals, at least not now
412 (define (test-value id value1 value2 bb-true bb-false)
413 ;; note: for multi-byte values, only x==y works properly TODO fix it, will depend on byte order, is car the lsb or msb ?
414 (let loop ((bytes1 (value-bytes value1))
415 (bytes2 (value-bytes value2)))
416 ;; TODO won't work with values of different widths
417 (let ((byte1 (car bytes1))
418 (byte2 (car bytes2)))
419 (if (null? (cdr bytes1))
420 (test-byte id byte1 byte2 bb-true bb-false)
421 (let ((bb-true2 (new-bb)))
422 (test-byte id byte1 byte2 bb-true2 bb-false)
424 (loop (cdr bytes1) (cdr bytes2)))))))
426 (define (test-relation id x y bb-true bb-false)
427 (cond ((and (literal? x) (not (literal? y))) ; literals must be in the last argument for code generation
428 (test-relation (case id
434 (else (error "relation error")))
439 ((assq id '((x!=y . x==y) (x<=y . x>y) (x>=y . x<y))) ; flip the destination blocks to have a simpler comparison
441 (lambda (z) (test-relation (cdr z) x y bb-false bb-true)))
443 ;; ' ;; TODO use these special cases, but fall back on the current implementation for default
446 ;; (cond ((and (literal? y) (= (literal-val y) 0))
447 ;; (test-zero x bb-true bb-false))
449 ;; (test-eq-lit x (literal-val y) bb-true bb-false))
451 ;; (error "unhandled case"))))
453 ;; (cond ((and (literal? y) (= (literal-val y) 0))
454 ;; (test-negative x bb-true bb-false)) ;; TODO does this exist ?
456 ;; (error "unhandled case"))))
458 ;; (cond ((and (literal? y) (= (literal-val y) 0))
459 ;; (test-positive x bb-true bb-false))
461 ;; (error "unhandled case"))))
463 ;; (error "unexpected operator")))
465 (let* ((value1 (expression x))
466 (value2 (expression y)))
467 (test-value id value1 value2 bb-true bb-false))
470 (define (test-zero ast bb-true bb-false)
473 (let ((type (expr-type ast))
474 (value (expression ast)))
475 (test-value 'x==y value (int->value 0 type) bb-false bb-true)))
478 (let* ((op (oper-op ast))
482 (test-zero (subast1 ast) bb-false bb-true))
484 (let ((bb-true2 (new-bb)))
485 (test-zero (subast1 ast) bb-true2 bb-false)
487 (test-zero (subast2 ast) bb-true bb-false)))
489 (let ((bb-false2 (new-bb)))
490 (test-zero (subast1 ast) bb-true bb-false2)
492 (test-zero (subast2 ast) bb-true bb-false)))
493 ((x==y x!=y x<y x>y x<=y x>=y)
502 (test-zero ast bb-true bb-false))
504 (define (expression ast)
506 (cond ((literal? ast)
517 (error "unexpected ast" ast)))))
518 (do-delayed-post-incdec)
521 (define (literal ast)
522 (let ((val (literal-val ast)))
523 (int->value val (expr-type ast))))
526 (let* ((def-var (ref-def-var ast))
527 (value (def-variable-value def-var)))
530 ;; calculates an address in an array by adding the base pointer and the offset
531 ;; and puts the answer in FSR0 so that changes to INDF0 change the array
533 (define (calculate-address ast)
534 ;; if we have a special FSR variable, no need to calculate the address as
535 ;; it is already in the register
536 (if (not (memq (array-base-name ast) fsr-variables))
537 (let ((base (ref (array-ref-id ast)))
538 (offset (expression (array-ref-index ast)))
539 (address (new-value (list (get-register FSR0L)
540 (get-register FSR0H))))) ;; TODO actual addresses are 12 bits, not 16
541 (add-sub 'x+y base offset address))))
543 (define (array-base-name ast)
544 (def-id (ref-def-var (array-ref-id ast)))) ;; TODO if array wasn't a special case, would cover also dereference
546 (define (array-ref ast)
547 (calculate-address ast)
548 (new-value (list (get-register INDF0))))
550 (define (add-sub id value1 value2 result)
551 (let loop ((bytes1 (value-bytes value1))
552 (bytes2 (value-bytes value2))
553 (bytes3 (value-bytes result))
554 (ignore-carry-borrow? #t))
555 (if (not (null? bytes3))
557 (new-instr (if ignore-carry-borrow?
558 (case id ((x+y) 'add) ((x-y) 'sub))
559 (case id ((x+y) 'addc) ((x-y) 'subb)))
560 (if (null? bytes1) (new-byte-lit 0) (car bytes1))
561 (if (null? bytes2) (new-byte-lit 0) (car bytes2))
563 (loop (if (null? bytes1) bytes1 (cdr bytes1))
564 (if (null? bytes2) bytes2 (cdr bytes2))
568 (define (do-delayed-post-incdec)
569 (if (not (null? delayed-post-incdec))
570 (let* ((ast (car delayed-post-incdec))
571 (type (expr-type ast))
574 (set! delayed-post-incdec (cdr delayed-post-incdec))
575 (let ((x (subast1 ast)))
577 (error "assignment target must be a variable"))
578 (let ((result (def-variable-value (ref-def-var x))))
579 (add-sub (if (eq? id 'x++) 'x+y 'x-y)
583 (do-delayed-post-incdec))))
586 (let* ((type (expr-type ast))
589 (let ((op (oper-op ast)))
594 (let ((x (subast1 ast)))
595 (let ((value-x (expression x)))
596 (let ((ext-value-x (extend value-x type)))
597 (let ((result (alloc-value type)))
604 (let ((x (subast1 ast)))
606 (error "assignment target must be a variable"))
607 (let ((result (def-variable-value (ref-def-var x))))
608 (add-sub (if (eq? id '++x) 'x+y 'x-y)
613 ((x++ x--) ;; TODO not sure this works properly
614 (let ((x (subast1 ast)))
616 (error "assignment target must be a variable"))
617 (let ((result (def-variable-value (ref-def-var x))))
618 (push-delayed-post-incdec ast)
621 (let ((x (subast1 ast)))
622 ;; TODO merge (calculate-address x)
623 ;; TODO even if we do not merge with the other array syntax, at least merge with the set for this syntax
624 ;; if it's a FSR variable, no adress to set
625 (if (not (and (ref? x)
626 (memq (def-id (ref-def-var x)) ;; TODO use array-base-name once array-refs are not special cases anymore, only diff would be to use subast1 instead of array-ref-id
628 (begin (move-value (expression x)
629 (new-value (list (get-register FSR0L)
630 (get-register FSR0H))))
631 (new-value (list (get-register INDF0))))
632 (if (eq? (def-id (ref-def-var x)) 'SIXPIC_FSR1) ;; TODO ugly, fix this
633 (new-value (list (get-register INDF1)))
634 (new-value (list (get-register INDF2)))))))
636 (error "unary operation error" ast))))
639 ((x+y x-y x*y x/y x%y)
640 (let* ((x (subast1 ast))
642 (let* ((value-x (expression x))
643 (value-y (expression y)))
644 (let* ((ext-value-x (extend value-x type))
645 (ext-value-y (extend value-y type)))
646 (let ((result (alloc-value type)))
647 (cond ((or (eq? id 'x+y)
649 (add-sub id ext-value-x ext-value-y result))
651 (error "multiplication not implemented yet")) ;; TODO maybe just implement multiplication by powers of 2
653 (error "division not implemented yet")) ;; TODO implement these
655 (error "modulo not implemented yet")))
658 (let* ((x (subast1 ast))
660 (value-y (expression y)))
663 (let ((ext-value-y (extend value-y type))) ;; TODO useless for now, what could it have been for ?
664 (let ((result (def-variable-value (ref-def-var x))))
665 (move-value value-y result)
668 (calculate-address x)
669 ;; this section of memory is a byte array, only the lsb
671 (move (car (value-bytes value-y)) (get-register INDF0)))
672 ((and (oper? x) (eq? (op-id (oper-op x)) '*x))
673 ;; TODO not always a ref
674 (let ((address (subast1 x)))
675 (if (not (and (ref? address)
676 (memq (def-id (ref-def-var address)) ;; TODO use array-base-name once array-refs are not special cases anymore, only diff would be to use subast1 instead of array-ref-id
678 (begin (move-value (expression address)
679 (new-value (list (get-register FSR0L)
680 (get-register FSR0H)))) ;; TODO merge with calculate-address ?
681 (move (car (value-bytes value-y)) (get-register INDF0))) ;; TODO this pattern happens at lots of places, will the merging solve this ?
682 (if (eq? (def-id (ref-def-var address)) 'SIXPIC_FSR1) ;; TODO ugly, fix this
683 (move (car (value-bytes value-y)) (get-register INDF1))
684 (move (car (value-bytes value-y)) (get-register INDF2))))))
685 (else (error "assignment target must be a variable or an array slot")))))
687 (error "binary operation error" ast))))))))
690 (let ((def-proc (call-def-proc ast)))
691 (for-each (lambda (ast def-var)
692 (let ((value (expression ast)))
693 (let ((ext-value (extend value (def-variable-type def-var))))
694 (move-value value (def-variable-value def-var)))))
696 (def-procedure-params def-proc))
697 (emit (new-call-instr def-proc))
698 (let ((value (def-procedure-value def-proc)))
699 (let ((result (alloc-value (def-procedure-type def-proc))))
700 (move-value value result)
703 ;; remplaces empty bbs by bbs with a single goto, to have a valid CFG for optimizations
704 (define (fill-empty-bbs)
705 (for-each (lambda (x) (if (null? (bb-rev-instrs x))
707 (emit (new-instr 'goto #f #f #f)))))
715 (define (print-cfg-bbs cfg)
716 (for-each (lambda (bb)
717 (pp (list "BB:" (bb-label-num bb)
718 "SUCCS" (map bb-label-num (bb-succs bb))
719 "PREDS" (map bb-label-num (bb-preds bb))
720 (cond ((null? (bb-rev-instrs bb)) "EMPTY")
721 ((and (null? (cdr (bb-rev-instrs bb)))
722 (eq? (instr-id (car (bb-rev-instrs bb))) 'goto)) "SINGLE GOTO")