1 ;;; generation of control flow graph
3 ;; special variables whose contents are located in the FSR registers
4 (define fsr-variables '(SIXPIC_FSR0 SIXPIC_FSR1 SIXPIC_FSR2))
15 label-name ; if the block had a label
21 live-before) ; stored as a set
24 extender: define-type-of-instr
25 (live-before unprintable:) ; these 2 are stored as sets
26 (live-after unprintable:)
33 (define-type-of-instr call-instr
37 (define-type-of-instr return-instr
41 (define (new-instr id src1 src2 dst)
42 (make-instr (new-empty-set) (new-empty-set) #f id src1 src2 dst))
44 ;; list of all conditional branching generic instructions
45 (define conditional-instrs ;; TODO add as we add specialized instructions
46 '(x==y x!=y x<y x>y x<=y x>=y))
48 (define (new-call-instr def-proc)
49 (make-call-instr '() '() #f 'call #f #f #f def-proc))
51 (define (new-return-instr def-proc)
52 (make-return-instr '() '() #f 'return #f #f #f def-proc))
55 (let* ((label-num (cfg-next-label-num cfg))
56 (bb (make-bb label-num #f #f '() '() '() (new-empty-set))))
62 (number->string label-num)))))
63 (cfg-bbs-set! cfg (cons bb (cfg-bbs cfg)))
64 (cfg-next-label-num-set! cfg (+ 1 (cfg-next-label-num cfg)))
67 (define (add-instr bb instr)
68 (let ((rev-instrs (bb-rev-instrs bb)))
69 (bb-rev-instrs-set! bb (cons instr rev-instrs))))
71 (define (add-succ bb succ)
72 (bb-succs-set! bb (cons succ (bb-succs bb)))
73 (bb-preds-set! succ (cons bb (bb-preds succ))))
75 (define (generate-cfg ast)
77 (define cfg (new-cfg))
79 (define bb #f) ; current bb
81 (define (in x) (set! bb x))
83 (define (new-bb) (add-bb cfg))
85 (define (emit instr) (add-instr bb instr))
87 (define current-def-proc #f)
88 (define break-stack '())
89 (define continue-stack '())
90 (define delayed-post-incdec '())
92 (define (push-break x) (set! break-stack (cons x break-stack)))
93 (define (pop-break) (set! break-stack (cdr break-stack)))
95 (define (push-continue x) (set! continue-stack (cons x continue-stack)))
96 (define (pop-continue) (set! continue-stack (cdr continue-stack)))
98 (define (push-delayed-post-incdec ast)
99 (set! delayed-post-incdec (cons ast delayed-post-incdec))
100 ;; moves the original value to a new location (so it won't be modified)
101 ;; and returns that location to the original expression
102 (let ((x (subast1 ast)))
104 (error "assignment target must be a variable")
105 (let* ((def-var (ref-def-var x))
106 (result (alloc-value (def-variable-type def-var))))
107 (move-value (def-variable-value def-var) result)
110 (define (program ast)
111 (let loop ((asts (ast-subasts ast)))
112 (if (not (null? asts))
113 (let ((ast (car asts)))
114 (if (null? (cdr asts))
115 (let ((value (expression ast)))
116 (return-with-no-new-bb value))
119 (loop (cdr asts))))))))
121 (define (toplevel ast)
122 (cond ((def-variable? ast)
124 ((def-procedure? ast)
129 (define (def-variable ast)
130 (let ((subasts (ast-subasts ast)))
131 (if (not (null? subasts)) ; if needed, set the variable
132 (let ((value (expression (subast1 ast))))
133 (let ((ext-value (extend value (def-variable-type ast))))
134 (move-value value (def-variable-value ast)))))))
136 ;; resolve the C gotos by setting the appropriate successor to their bb
137 (define (resolve-all-gotos start table)
138 (let loop ((start start)
139 (visited (new-empty-set)))
140 (if (not (set-member? visited start)) ; not visited
143 (if (and (eq? (instr-id x) 'goto)
144 (instr-dst x)) ; unresolved label
145 (let ((target (assoc (instr-dst x) table))) ;; TODO use a set, but not urgent, not a bottleneck
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 (set-add! visited start)
154 (bb-succs start))))))
156 (define (def-procedure ast)
159 (def-procedure-entry-set! ast entry)
160 (set! current-def-proc ast)
161 (pp (list cfg: (def-id ast)))
163 (for-each statement (ast-subasts ast))
164 (return-with-no-new-bb ast)
165 (set! current-def-proc #f)
166 (resolve-all-gotos entry (list-named-bbs entry))
169 ;; returns a list of all named bbs in the successor-tree of a given bb
170 (define (list-named-bbs start)
171 (let ((visited (new-empty-set)))
172 (let loop ((start start) ;; TODO not really a loop, it's tree recursion
174 (if (set-member? visited start)
179 (set-add! visited start)
182 (if (bb-label-name start)
183 (cons (cons (bb-label-name start) start) succs)
186 (define (statement ast)
187 (cond ((def-variable? ast) (def-variable ast))
188 ((block? ast) (block ast))
189 ((return? ast) (return ast))
190 ((if? ast) (if (null? (cddr (ast-subasts ast)))
193 ((while? ast) (while ast))
194 ((do-while? ast) (do-while ast))
195 ((for? ast) (for ast))
196 ((switch? ast) (switch ast))
197 ((break? ast) (break ast))
198 ((continue? ast) (continue ast))
199 ((goto? ast) (goto ast))
200 (else (expression ast))))
203 (if (block-name ast) ; named block ?
204 (begin (let ((new (new-bb)))
207 (bb-label-name-set! bb (block-name ast)) ))
208 (for-each statement (ast-subasts ast)))
210 (define (move from to)
211 (emit (new-instr 'move from #f to)))
213 (define (move-value from to)
214 (let loop ((from (value-bytes from))
215 (to (value-bytes to)))
216 (cond ((null? to)) ; done, we truncate the rest
217 ((null? from) ; promote the value by padding
218 (move (new-byte-lit 0) (car to))
219 (loop from (cdr to)))
221 (move (car from) (car to))
222 (loop (cdr from) (cdr to))))))
224 (define (return-with-no-new-bb def-proc)
225 (emit (new-return-instr def-proc)))
228 (if (null? (ast-subasts ast))
229 (return-with-no-new-bb current-def-proc)
230 (let ((value (expression (subast1 ast))))
231 (let ((ext-value (extend value (def-procedure-type current-def-proc))))
232 (move-value value (def-procedure-value current-def-proc))
233 (return-with-no-new-bb current-def-proc))))
237 (let* ((bb-join (new-bb))
239 (test-expression (subast1 ast) bb-then bb-join)
241 (statement (subast2 ast))
246 (let* ((bb-join (new-bb))
249 (test-expression (subast1 ast) bb-then bb-else)
251 (statement (subast2 ast))
254 (statement (subast3 ast))
259 (let* ((bb-cont (new-bb))
262 (push-continue bb-cont)
266 (test-expression (subast1 ast) bb-body bb-exit)
268 (statement (subast2 ast))
274 (define (do-while ast)
275 (let* ((bb-body (new-bb))
278 (push-continue bb-cont)
282 (statement (subast1 ast))
285 (test-expression (subast2 ast) bb-body bb-exit)
291 (let* ((bb-loop (new-bb))
295 (statement (subast1 ast))
297 (push-continue bb-cont)
300 (test-expression (subast2 ast) bb-body bb-exit)
302 (statement (subast4 ast))
305 (statement (subast3 ast))
312 (let* ((var (subast1 ast))
317 (prev-bb decision-bb))
319 (for-each (lambda (x) ; generate each case
320 (in (new-bb)) ; this bb will be given the name of the case
321 (add-succ decision-bb bb)
322 ;; if the previous case didn't end in a break, fall through
323 (if (null? (bb-succs prev-bb))
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
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))
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 ;; since we cons each new successor at the front, true has to be
396 (add-succ bb bb-false)
397 (add-succ bb bb-true)
398 (emit (new-instr id byte1 byte2 #f)))
405 (else (error "invalid test")))))
406 (add-succ bb bb-false)
407 (add-succ bb bb-true)
408 (emit (new-instr id byte2 byte1 #f))))
410 (add-succ bb bb-false)
411 (add-succ bb bb-true)
412 (emit (new-instr id byte1 byte2 #f)))))
414 (define (test-value id value1 value2 bb-true bb-false)
415 (let loop ((bytes1 (value-bytes value1)) ; lsb first
416 (bytes2 (value-bytes value2))
419 (if (not (and (null? bytes1) (null? bytes2)))
420 ;; note: won't work with signed types, as the padding is done
422 (loop (if (null? bytes1) bytes1 (cdr bytes1))
423 (if (null? bytes2) bytes2 (cdr bytes2))
424 (cons (if (null? bytes1) (new-byte-lit 0) (car bytes1)) ;; TODO use extend ?
426 (cons (if (null? bytes2) (new-byte-lit 0) (car bytes2))
428 ;; now so the test itself, using the padded values
429 ;; the comparisons are done msb-first, for < and >
431 ((x==y) ; unlike < and >, must check all bytes, so is simpler
432 (let loop2 ((bytes1 padded1)
434 (let ((byte1 (car bytes1))
435 (byte2 (car bytes2)))
436 (if (null? (cdr bytes1)) ;; TODO factor with code for < and > ?
437 (test-byte 'x==y byte1 byte2 bb-true bb-false)
438 (let ((bb-true2 (new-bb)))
439 (test-byte 'x==y byte1 byte2 bb-true2 bb-false)
441 (loop2 (cdr bytes1) (cdr bytes2)))))))
444 (let loop2 ((bytes1 padded1) ; msb first
446 (let ((byte1 (car bytes1))
447 (byte2 (car bytes2)))
448 (if (null? (cdr bytes1))
449 (test-byte id byte1 byte2 bb-true bb-false)
450 (let ((bb-test-equal (new-bb))
451 (bb-keep-going (new-bb)))
452 ;; if the test is true for the msb, the whole test
454 (test-byte id byte1 byte2 bb-true bb-test-equal)
455 ;; if not, check for equality, if both bytes are
458 (test-byte 'x==y byte1 byte2 bb-keep-going bb-false)
459 ;; TODO do some analysis to check the value already in w, in this case, it won't change between both tests, so no need to charge it back, as is done now
461 (loop2 (cdr bytes1) (cdr bytes2)))))))))))
463 (define (test-relation id x y bb-true bb-false)
464 (cond ((and (literal? x) (not (literal? y)))
465 ;; literals must be in the last argument for code generation
466 ;; flip the relation if needed
467 (test-relation (case id
468 ((x==y x!=y) id) ; commutative, no change
473 (else (error "relation error")))
478 ((assq id '((x!=y . x==y) (x<=y . x>y) (x>=y . x<y)))
479 ;; flip the destination blocks to have a simpler comparison
481 (lambda (z) (test-relation (cdr z) x y bb-false bb-true)))
484 ;; ' ;; TODO use these special cases, but fall back on the current implementation for default
487 ;; (cond ((and (literal? y) (= (literal-val y) 0))
488 ;; (test-zero x bb-true bb-false))
490 ;; (test-eq-lit x (literal-val y) bb-true bb-false))
492 ;; (error "unhandled case"))))
494 ;; (cond ((and (literal? y) (= (literal-val y) 0))
495 ;; (test-negative x bb-true bb-false))
497 ;; (error "unhandled case"))))
499 ;; (cond ((and (literal? y) (= (literal-val y) 0))
500 ;; (test-positive x bb-true bb-false))
502 ;; (error "unhandled case"))))
504 ;; (error "unexpected operator")))
506 (let* ((value1 (expression x))
507 (value2 (expression y)))
508 (test-value id value1 value2 bb-true bb-false))
511 (define (test-zero ast bb-true bb-false)
514 (let ((type (expr-type ast))
515 (value (expression ast)))
516 ;; since nonzero is true, we must swap the destinations to use ==
517 (test-value 'x==y value (int->value 0 type) bb-false bb-true)))
520 (let* ((op (oper-op ast))
524 (test-zero (subast1 ast) bb-false bb-true))
526 (let ((bb-true2 (new-bb)))
527 (test-zero (subast1 ast) bb-true2 bb-false)
529 (test-zero (subast2 ast) bb-true bb-false)))
531 (let ((bb-false2 (new-bb)))
532 (test-zero (subast1 ast) bb-true bb-false2)
534 (test-zero (subast2 ast) bb-true bb-false)))
535 ((x==y x!=y x<y x>y x<=y x>=y)
544 (test-zero ast bb-true bb-false))
546 (define (expression ast)
548 (cond ((literal? ast) (literal ast))
549 ((ref? ast) (ref ast))
550 ((oper? ast) (oper ast))
551 ((call? ast) (call ast))
552 (else (error "unexpected ast" ast)))))
553 (do-delayed-post-incdec)
556 (define (literal ast)
557 (let ((val (literal-val ast)))
558 (int->value val (expr-type ast))))
561 (let* ((def-var (ref-def-var ast))
562 (value (def-variable-value def-var)))
565 (define (add-sub id value1 value2 result)
566 (let loop ((bytes1 (value-bytes value1)) ; car is lsb
567 (bytes2 (value-bytes value2))
568 (bytes3 (value-bytes result))
569 (ignore-carry-borrow? #t))
570 (if (not (null? bytes3))
572 (new-instr (if ignore-carry-borrow?
573 (case id ((x+y) 'add) ((x-y) 'sub))
574 (case id ((x+y) 'addc) ((x-y) 'subb)))
575 (car bytes1) (car bytes2) (car bytes3)))
576 (loop (cdr bytes1) (cdr bytes2) (cdr bytes3) #f)))))
578 (define (mul x y type result)
579 (let* ((value-x (expression x))
580 (value-y (expression y))
581 (bytes-x (value-bytes value-x))
582 (bytes-y (value-bytes value-y))
583 (lx (length bytes-x))
584 (ly (length bytes-y)))
585 ;; if this a multiplication by 2 or 4, we use additions instead
586 ;; at this point, only y (or both x and y) can contain a literal
588 (byte-lit? (car bytes-y))
589 (let ((v (byte-lit-val (car bytes-y))))
590 (or (= v 2) (= v 4))))
591 (case (byte-lit-val (car bytes-y))
592 ((2) (add-sub 'x+y value-x value-x result)) ; simple addition
593 ((4) (let ((tmp (alloc-value (bytes->type
594 (length (value-bytes result))))))
595 (add-sub 'x+y value-x value-x tmp)
596 (add-sub 'x+y tmp tmp result))))
597 ;; if not, we have to do it the long way
599 ;; finds the appropriate multiplication routine (depending on the
600 ;; length of each argument) and turns the multiplication into a
601 ;; call to the routine
602 ;; the arguments must be the asts of the 2 arguments (x and y) and
603 ;; the type of the returned value, since these are what are
604 ;; expected by the call function
606 ;; to avoid code duplication (i.e. habing a routine for 8 by 16
607 ;; multplication and one for 16 by 8), the longest operand goes first
616 (string->symbol ; mul8_8, mul8_16, etc
617 ;; for now, only unsigned multiplications are supported
619 (number->string (* lx 8)) "_"
620 (number->string (* ly 8))))
625 (define (mod x y result)
626 (let ((bytes1 (value-bytes x)) ;; TODO common pattern, abstract
627 (bytes2 (value-bytes y))
628 (bytes3 (value-bytes result)))
629 ;; if y is a literal and a power of 2, we can do a bitwise and
630 (let ((y0 (car bytes2)))
631 (if (and (byte-lit? y0)
632 (let ((x (/ (log (value->int y)) (log 2))))
634 ;; bitwise and with y - 1
635 (begin (let* ((l (bytes->type (length bytes2)))
636 (tmp (alloc-value l)))
637 (move-value (int->value (- (value->int y) 1)
638 (bytes->type (length bytes2)))
640 (bitwise 'x&y x tmp result)))
641 ;; TODO for the general case, try to optimise the case where division and modulo are used together, since they are used together
642 (error "modulo is only supported for powers of 2")))))
644 (define (shift id x y type result)
645 (let ((bytes1 (value-bytes (extend (expression x) type)))
646 (bytes2 (value-bytes (extend (expression y) type)))
647 (bytes3 (value-bytes result)))
648 ;; if the second argument is a literal and a multiple of 8, we can simply
649 ;; move the bytes around
650 (let ((y0 (car bytes2)))
651 (if (and (byte-lit? y0) (= (modulo (byte-lit-val y0) 8) 0))
652 ;; uses only the first byte, but shifting by 255 should be enough
653 (let ((n (/ (byte-lit-val y0) 8))
654 (l (length bytes1))) ; same length for x and result
661 (new-byte-lit 0) ; padding
664 (loop (+ i 1) (if (< i n) x (cdr x))))
666 (move (if (<= l (+ i n))
668 (list-ref x (+ i n)))
670 (loop (+ i 1) x))))))
674 (case id ((x<<y) "l") ((x>>y) "r"))
675 (number->string (* 8 (length bytes1)))))
680 ;; bitwise and, or, xor
681 ;; TODO similar to add-sub and probably others, abstract multi-byte ops
682 ;; TODO use bit set, clear and toggle for some shortcuts
683 (define (bitwise id value1 value2 result)
684 (let loop ((bytes1 (value-bytes value1))
685 (bytes2 (value-bytes value2))
686 (bytes3 (value-bytes result)))
687 (if (not (null? bytes3))
689 (emit (new-instr (case id ((x&y) 'and) ((|x\|y|) 'ior) ((x^y) 'xor))
690 (car bytes1) (car bytes2) (car bytes3)))
691 (loop (cdr bytes1) (cdr bytes2) (cdr bytes3))))))
693 (define (bitwise-negation x result)
694 (let loop ((bytes1 (value-bytes x))
695 (bytes2 (value-bytes result)))
696 (if (not (null? bytes2))
697 (begin (emit (new-instr 'not (car bytes1) #f (car bytes2)))
698 (loop (cdr bytes1) (cdr bytes2))))))
700 (define (do-delayed-post-incdec)
701 (if (not (null? delayed-post-incdec))
702 (let* ((ast (car delayed-post-incdec))
703 (type (expr-type ast))
706 (set! delayed-post-incdec (cdr delayed-post-incdec))
707 (let ((x (subast1 ast)))
709 (error "assignment target must be a variable"))
710 (let ((result (def-variable-value (ref-def-var x))))
711 ;; clobbers the original value, which is fine, since it
712 ;; was moved somewhere else for the expression
713 (add-sub (if (eq? id 'x++) 'x+y 'x-y)
717 (do-delayed-post-incdec))))
719 ;; calculates an address in an array by adding the base pointer and the offset
720 ;; and puts the answer in FSR0 so that changes to INDF0 change the array
722 (define (calculate-address ast)
723 ;; if we have a special FSR variable, no need to calculate the address as
724 ;; it is already in the register
725 (let ((base-name (array-base-name ast))
726 (index? (eq? (op-id (oper-op ast)) 'index)))
727 (if (not (and base-name
728 (memq base-name fsr-variables)))
729 (let ((base (expression (subast1 ast)))
730 ;; NB: actual addresses are 12 bits, not 16
731 (address (new-value (list (get-register FSR0L)
732 (get-register FSR0H)))))
734 ;; we pad up to int16, since it is the size of the addresses
735 (let ((value1 (extend base 'int16))
736 (value2 (extend (expression (subast2 ast)) 'int16)))
737 (add-sub 'x+y value1 value2 address))
738 ;; no offset with simple dereference
739 (move-value base address)))
740 (error "You used the array index syntax with a FSR variable, didn't you? I told you not to."))))
742 (define (array-base-name ast)
743 ;; returns #f if the lhs is not a direct variable reference
744 ;; eg : *x++ ; (x+y)* ; ...
745 (let ((lhs (subast1 ast)))
747 (def-id (ref-def-var lhs)))))
749 (define (get-indf base-name)
750 ;; INDF0 is not here, since it's already used for regular array accesses
751 (if (eq? base-name 'SIXPIC_FSR1)
752 (new-value (list (get-register INDF1)))
753 (new-value (list (get-register INDF2)))))
756 (let* ((type (expr-type ast))
759 (let ((op (oper-op ast)))
761 (define (arith-op id x y value-x value-y) ;; TODO find a way not to pass x and y as well
762 ;; since code generation does not accept literals as first
763 ;; arguments unless both arguments are, if this is the
764 ;; case, we either have to swap the arguments (if
765 ;; possible) or allocate the argument somewhere
766 (if (and (literal? x) (not (literal? y)))
767 (if (memq id '(x+y x*y x&y |x\|y| x^y))
768 ;; the operator is commutative, we can swap the args
770 (set! value-x value-y)
772 ;; the operator is not commutative, we have to
773 ;; allocate the first argument somewhere
774 (let ((dest (alloc-value (expr-type x))))
775 (move-value value-x dest)
776 (set! value-x dest))))
777 (let ((result (alloc-value type)))
779 ((x+y x-y) (add-sub id value-x value-y result))
780 ((x*y) (mul x y type result))
781 ((x/y) (error "division not implemented yet")) ;; TODO optimize for powers of 2
782 ((x%y) (mod value-x value-y result))
783 ((x&y |x\|y| x^y) (bitwise id value-x value-y result))
784 ((x>>y x<<y) (shift id x y type result)))
791 (let ((x (extend (expression (subast1 ast))
793 (result (alloc-value type)))
799 ((~x) (bitwise-negation x result)))
802 (let ((x (subast1 ast)))
804 (error "assignment target must be a variable"))
805 (let ((result (def-variable-value (ref-def-var x))))
806 (add-sub (if (eq? id '++x) 'x+y 'x-y)
812 (let ((x (subast1 ast)))
814 (error "assignment target must be a variable"))
815 ;; push-delayed-post-incdec moves the original value
816 ;; somewhere else, and returns that location
817 (push-delayed-post-incdec ast)))
819 ;; if it's a FSR variable, no adress to set
820 (let ((base-name (array-base-name ast)))
821 (if (and (ref? (subast1 ast)) ; do we have a FSR variable ?
823 (memq base-name fsr-variables))
825 (begin (calculate-address ast)
826 (new-value (list (get-register INDF0)))))))
828 (error "unary operation error" id))))
832 ((x+y x-y x*y x/y x%y x&y |x\|y| x^y x>>y x<<y)
833 (let* ((x (subast1 ast))
835 (let* ((value-x (extend (expression x) type))
836 (value-y (extend (expression y) type)))
837 (arith-op id x y value-x value-y))))
839 (let* ((x (subast1 ast))
841 (value-y (expression y)))
845 (let ((ext-value-y (extend value-y type)))
846 (let ((result (def-variable-value (ref-def-var x))))
847 (move-value value-y result)
849 ;; lhs is a pointer dereference
850 ((and (oper? x) (eq? (op-id (oper-op x)) '*x))
851 (let ((base-name (array-base-name x))
852 (val (car (value-bytes value-y))))
853 (if (and (ref? (subast1 x))
855 (memq base-name fsr-variables))
856 (move val (car (value-bytes (get-indf base-name))))
857 (begin (calculate-address x)
858 (move val (get-register INDF0))))))
859 ;; lhs is an indexed array access
860 ((and (oper? x) (eq? (op-id (oper-op x)) 'index))
861 ;; note: this will throw an error if SIXPIC_FSR{1,2} is
862 ;; used. this is by design, as it would clobber the value
863 ;; in the FSR registers, which goes against their purpose
864 ;; of storing a user-chosen value
865 (calculate-address x)
866 ;; this section of memory is a byte array, only the lsb
868 (move (car (value-bytes value-y)) (get-register INDF0)))
869 (else (error "assignment target must be a variable or an array slot")))))
871 ;; note: throws an error if given SIXPIC_FSR{1,2}, see above
872 (calculate-address ast)
873 (new-value (list (get-register INDF0))))
874 ((x+=y x-=y x*=y x/=y x%=y x&=y |x\|=y| x^=y x>>=y x<<=y)
875 (let* ((x (subast1 ast))
877 (value-x (extend (expression x) type))
878 (value-y (extend (expression y) type)))
879 (move-value (arith-op (case id
893 ((x==y x!=y x>y x>=y x<y x<=y x&&y |x\|\|y|) ;; TODO !x, have it also, maybe do this check before the op1-2-3 test to catch them all ?
898 (result (alloc-value type)))
900 (move-value (int->value 1 type) result)
903 (move-value (int->value 0 type) result)
906 (test-expression ast bb-true bb-false)
910 (error "binary operation error" id))))
917 (result (alloc-value type)))
919 (move-value (expression (subast2 ast)) result)
922 (move-value (expression (subast3 ast)) result)
925 (test-expression (subast1 ast) bb-true bb-false)
929 ;; generates the cfg for a predefined routine and adds it to the current cfg
930 (define (include-predefined-routine proc)
931 (define (get-bytes var)
932 (value-bytes (def-variable-value var)))
933 (let ((old-proc current-def-proc) ; if we were already defining a procedure, save it
935 (params (def-procedure-params proc))
936 (value (def-procedure-value proc))
938 (entry (new-bb))) ;; TODO insipired from def-procedure, abstract
939 (def-procedure-entry-set! proc entry)
940 (set! current-def-proc proc)
945 (let ((x (car params))
947 (z (value-bytes value)))
948 ;; TODO implement literal multiplication in the simulator
949 (emit (new-instr 'mul (car (get-bytes x)) (car (get-bytes y)) #f))
950 (move (get-register PRODL) (car z)))) ; lsb
953 (let* ((x (get-bytes (car params)))
956 (y (get-bytes (cadr params)))
958 (z (value-bytes value))
961 (emit (new-instr 'mul y0 x1 #f))
962 (move (get-register PRODL) z1)
964 (emit (new-instr 'mul y0 x0 #f))
965 (move (get-register PRODL) z0)
966 (emit (new-instr 'add (get-register PRODH) z1 z1))))
969 (let* ((x (get-bytes (car params)))
972 (y (get-bytes (cadr params)))
975 (z (value-bytes value))
979 (emit (new-instr 'mul x0 y0 #f))
980 (move (get-register PRODH) z1)
981 (move (get-register PRODL) z0)
983 (emit (new-instr 'mul x0 y1 #f))
984 (emit (new-instr 'add (get-register PRODL) z1 z1))
986 (emit (new-instr 'mul x1 y0 #f))
987 (emit (new-instr 'add (get-register PRODL) z1 z1))))
990 (let* ((x (get-bytes (car params)))
995 (y (get-bytes (cadr params)))
998 (z (value-bytes value))
1004 (emit (new-instr 'mul x0 y0 #f))
1005 (move (get-register PRODH) z1)
1006 (move (get-register PRODL) z0)
1008 (emit (new-instr 'mul x1 y1 #f))
1009 (move (get-register PRODH) z3)
1010 (move (get-register PRODL) z2)
1012 (emit (new-instr 'mul x1 y0 #f))
1013 (emit (new-instr 'add (get-register PRODL) z1 z1))
1014 (emit (new-instr 'addc (get-register PRODH) z2 z2))
1015 (emit (new-instr 'addc z3 (new-byte-lit 0) z3))
1017 (emit (new-instr 'mul x0 y1 #f))
1018 (emit (new-instr 'add (get-register PRODL) z1 z1))
1019 (emit (new-instr 'addc (get-register PRODH) z2 z2))
1020 (emit (new-instr 'addc z3 (new-byte-lit 0) z3))
1022 (emit (new-instr 'mul x2 y0 #f))
1023 (emit (new-instr 'add (get-register PRODL) z2 z2))
1024 (emit (new-instr 'addc (get-register PRODH) z3 z3))
1026 (emit (new-instr 'mul x2 y1 #f))
1027 (emit (new-instr 'add (get-register PRODL) z3 z3))
1029 (emit (new-instr 'mul x3 y0 #f))
1030 (emit (new-instr 'add (get-register PRODL) z3 z3))))
1032 ((shl8 shr8 shl16 shr16 shl32 shr32)
1033 (let* ((id (symbol->string id))
1034 (left-shift? (eq? (string-ref id 2) #\l))
1035 (x (def-variable-value (car params)))
1036 (y (def-variable-value (cadr params)))
1037 (y0 (car (value-bytes y))) ; shift by 255 is enough
1038 (bytes-z (value-bytes value))
1041 (after-bb (new-bb)))
1042 (move-value x value)
1043 (gen-goto start-bb) ; fall through to the loop
1045 ;; if we'd shift of 0, we're done
1046 (add-succ bb loop-bb) ; false
1047 (add-succ bb after-bb) ; true
1048 (emit (new-instr 'x==y y0 (new-byte-lit 0) #f))
1050 ;; shift for each byte, since it's a rotation using the carry,
1051 ;; what goes out from the low bytes gets into the high bytes
1052 (for-each (lambda (b)
1053 (emit (new-instr (if left-shift? 'shl 'shr)
1055 (if left-shift? bytes-z (reverse bytes-z)))
1056 ;; clear the carry, to avoid reinserting it in the register
1057 (emit (new-instr 'set
1058 (get-register STATUS)
1061 (emit (new-instr 'sub y0 (new-byte-lit 1) y0))
1064 (return-with-no-new-bb proc)
1065 (set! current-def-proc old-proc)
1066 (resolve-all-gotos entry (list-named-bbs entry))
1070 (let* ((def-proc (call-def-proc ast))
1071 (arguments (ast-subasts ast))
1072 (parameters (def-procedure-params def-proc)))
1073 (if (and (memq (def-id def-proc) predefined-routines)
1074 (not (def-procedure-entry def-proc)))
1075 ;; it's the first time we encounter this predefined routine, generate
1076 ;; the corresponding cfg
1077 (include-predefined-routine def-proc))
1078 ;; argument number check
1079 (if (not (= (length arguments) (length parameters))) ;; TODO check at parse time ?
1080 (error (string-append "wrong number of arguments given to function "
1081 (symbol->string (def-id def-proc)) ": "
1082 (number->string (length arguments)) " given, "
1083 (number->string (length parameters))
1085 (for-each (lambda (ast def-var)
1086 (let ((value (expression ast)))
1087 (let ((ext-value (extend value (def-variable-type def-var))))
1088 (move-value value (def-variable-value def-var)))))
1091 (emit (new-call-instr def-proc))
1092 (let ((value (def-procedure-value def-proc)))
1093 (let ((result (alloc-value (def-procedure-type def-proc))))
1094 (move-value value result)
1097 ;; call to a predefined routine, a simple wrapper to an ordinary call
1098 ;; name is a symbol, args is a list of the arguments
1099 (define (routine-call name args type result)
1100 (cond ((memp (lambda (x) (eq? (def-id x) name))
1102 => (lambda (x) (move-value (call (new-call args type (car x)))
1104 (else (error "unknown routine: " name))))
1106 ;; remplaces empty bbs by bbs with a single goto, to have a valid CFG for
1108 (define (fill-empty-bbs) ;; TODO is this legitimate ? its not active for the moment, see if it ever is
1109 (for-each (lambda (x) (if (null? (bb-rev-instrs x))
1111 (emit (new-instr 'goto #f #f #f)))))
1119 (define (print-cfg-bbs cfg)
1120 (for-each (lambda (bb)
1121 (pp (list "BB:" (bb-label-num bb)
1122 "SUCCS" (map bb-label-num (bb-succs bb))
1123 "PREDS" (map bb-label-num (bb-preds bb))
1124 (cond ((null? (bb-rev-instrs bb)) "EMPTY")
1125 ((and (null? (cdr (bb-rev-instrs bb)))
1126 (eq? (instr-id (car (bb-rev-instrs bb))) 'goto)) "SINGLE GOTO")