1 (define bank-1-used? #f)
3 (define (linearize-and-cleanup cfg)
5 (define bbs-vector (cfg->vector cfg))
10 (set! todo (cons bb todo)))
15 (set! rev-code (cons instr rev-code)))
17 (define (outside-bank-0? adr)
18 (if (and (> adr #x5F) (< adr #xF60)) ; not a special register
19 (begin (set! bank-1-used? #t) #t)
21 (define (emit-byte-oriented op file #!optional (d? #t) (w? #f))
22 ;; we might have to access the second bank
23 (emit (if (outside-bank-0? file)
25 (list op (- file 96) (if w? 'w 'f) 'b)
26 (list op (- file 96) 'b))
28 (list op file (if w? 'w 'f) 'a)
30 (define (emit-bit-oriented op file bit)
31 (emit (if (outside-bank-0? file)
32 (list op (- file 96) bit 'b)
33 (list op file bit 'a))))
36 (emit (list 'movlw val)))
38 (emit-byte-oriented 'movwf adr #f))
40 (emit-byte-oriented 'movf adr #t #t))
41 (define (movff src dst)
42 ;; anything over #x5f is in the second bank (at #x100)
43 (let ((src (if (outside-bank-0? src)
46 (dst (if (outside-bank-0? dst)
49 (emit (list 'movff src dst))))
52 (emit-byte-oriented 'clrf adr #f))
54 (emit-byte-oriented 'setf adr #f))
57 (emit-byte-oriented 'incf adr))
59 (emit-byte-oriented 'decf adr))
62 (emit-byte-oriented 'addwf adr))
64 (emit-byte-oriented 'addwfc adr))
67 (emit-byte-oriented 'subwf adr))
69 (emit-byte-oriented 'subwfb adr))
72 (emit (list 'mullw adr)))
74 (emit-byte-oriented 'mulwf adr #f))
77 (emit-byte-oriented 'andwf adr))
79 (emit-byte-oriented 'iorwf adr))
81 (emit-byte-oriented 'xorwf adr))
84 (emit-byte-oriented 'rlcf adr))
86 (emit-byte-oriented 'rrcf adr))
89 (emit-bit-oriented 'bcf adr bit))
91 (emit-bit-oriented 'bsf adr bit))
93 (emit-bit-oriented 'btg adr bit))
96 (emit-byte-oriented 'comf adr))
98 (define (tblrd) ;; TODO support the different modes
102 (emit-byte-oriented 'cpfseq adr #f))
104 (emit-byte-oriented 'cpfslt adr #f))
106 (emit-byte-oriented 'cpfsgt adr #f))
109 (emit (list 'bc label)))
110 (define (bra-or-goto label)
111 (emit (list 'bra-or-goto label)))
113 (emit (list 'goto label)))
115 (define (rcall label)
116 (emit (list 'rcall label)))
119 (if (and #f (and (not (null? rev-code))
120 (eq? (caar rev-code) 'rcall)))
121 (let ((label (cadar rev-code)))
122 (set! rev-code (cdr rev-code))
124 (emit (list 'return))))
127 (if (and (and (not (null? rev-code)) ;; TODO have a flag to disable this optimization
128 (or (eq? (caar rev-code) 'bra-or-goto)
129 (eq? (caar rev-code) 'goto))
130 (eq? (cadar rev-code) lab)))
132 (set! rev-code (cdr rev-code))
134 (emit (list 'label lab))))
137 (emit (list 'sleep)))
139 (define (move-reg src dst)
148 ;; takes 2 cycles (as much as movfw src ; movwf dst), but takes
149 ;; only 1 instruction
152 (define (bb-linearize bb)
153 (let ((label-num (bb-label-num bb)))
154 (let ((bb (vector-ref bbs-vector label-num)))
156 (define (move-lit n adr)
165 ;; when eliminating additions/substractions, it is important that the
166 ;; next one ignores the carry/borrow, to avoid using a leftover carry
167 ;; from an earlier operation
168 (define ignore-carry-borrow? #f)
170 (define (dump-instr instr)
171 (cond ((call-instr? instr)
172 (let* ((def-proc (call-instr-def-proc instr))
173 (entry (def-procedure-entry def-proc)))
177 (let ((label (bb-label entry)))
180 ((return-instr? instr)
183 (let ((src1 (instr-src1 instr))
184 (src2 (instr-src2 instr))
185 (dst (instr-dst instr))
186 (id (instr-id instr)))
187 (if (and (or (not (byte-cell? dst))
188 (and (byte-cell-adr dst)
189 ;; must go in a special register, or in a
190 ;; live variable, or else don't generate
192 #;(or (assq (byte-cell-adr dst) ;; TODO eliminating instructions does not work, too many are eliminated, it seems
194 ;; if the instruction affects the
195 ;; carry, it must be generated
196 (memq id carry-affecting-instrs)
197 ;; destination is used
199 (instr-live-after instr)
200 (byte-cell-id dst)))))
201 (or (not (byte-cell? src1))
202 (byte-cell-adr src1))
203 (or (not (byte-cell? src2))
204 (byte-cell-adr src2)))
210 (let ((n (byte-lit-val src1))
211 (z (byte-cell-adr dst)))
213 (let ((x (byte-cell-adr src1))
214 (z (byte-cell-adr dst)))
219 (let ((n (byte-lit-val src2))
220 (z (byte-cell-adr dst)))
222 (move-lit (byte-lit-val src1) z)
223 (move-reg (byte-cell-adr src1) z))
224 (if (and (= n 0) ; nop
227 (set! ignore-carry-borrow? #t)
230 ((add) (cond ((= n 1) (incf z))
231 ;; ((= n #xff) (decf z)) ;; TODO set the carry
236 (if ignore-carry-borrow?
239 ((sub) (cond ((= n 1) (decf z))
240 ;; ((= n #xff) (incf z)) ;; TODO same
245 (if ignore-carry-borrow?
248 (set! ignore-carry-borrow? #f))))
249 (let ((x (or (and (byte-cell? src1) (byte-cell-adr src1)) 0)) ;; FOO this should not be needed (or correct), but without it, PICOBIT without bignums won't compile. it gives the right results for the vectors test, haven't checked the others.
250 (y (byte-cell-adr src2))
251 (z (byte-cell-adr dst)))
252 (cond ((and (not (= x y))
254 (memq id '(add addc)))
255 ;; since this basically swaps the
256 ;; arguments, it can't be used for
261 ;; for subtraction, preserves argument
264 ;; this NEEDS to be done with movff, or
265 ;; else wreg will get clobbered and this
268 (else ;; TODO check if it could be merged with the previous case
273 ((addc) (if ignore-carry-borrow?
277 ((subb) (if ignore-carry-borrow?
280 (else (error "...")))
281 (set! ignore-carry-borrow? #f))))
283 ((mul) ; 8 by 8 multiplication
285 ;; since multiplication is commutative, the
286 ;; arguments are set up so the second one will
287 ;; be a literal if the operator is applied on a
288 ;; literal and a variable
289 (let ((n (byte-lit-val src2)))
291 (movlw (byte-lit-val src1))
292 (move-reg (byte-cell-adr src1) WREG))
293 ;; literal multiplication
295 (let ((x (byte-cell-adr src1))
296 (y (byte-cell-adr src2)))
301 (let* ((x (if (byte-lit? src1)
303 (byte-cell-adr src1)))
304 (y (if (byte-lit? src2)
306 (byte-cell-adr src2)))
307 (z (byte-cell-adr dst))
312 (else (error "...")))))
314 (cond ((byte-lit? src1)
315 ;; low-level constant folding
322 ((or (and (eq? id 'and) (= y #xff))
323 (and (eq? id 'ior) (= y #x00)))
324 ;; nop, just move the value
329 ((and (eq? id 'ior) (= y #xff))
331 ;; use bit-set or bit-toggle
332 ((and (memq id '(ior xor))
333 ;; a single bit is set
334 (memq y '(#x01 #x02 #x04 #x08
335 #x10 #x20 #x40 #x80))
337 ((if (eq? id 'ior) bsf btg)
339 (/ (log y) (log 2)))))
342 ;; a single bit is unset
343 (memq y '(#x7f #xbf #xdf #xef
344 #xf7 #xfb #xfd #xfe))
346 (bcf z (inexact->exact
348 (log 2))))) ;; TODO since this requires x and z to be in the same place to be efficient, maybe coalesce theses cases in priority ? for now, this optimization does not save much
353 (begin (if (and (not (= x y)) (= y z))
361 (let ((x (if (byte-lit? src1)
363 (byte-cell-adr src1)))
364 (z (byte-cell-adr dst)))
365 (cond ((byte-lit? src1) (move-lit x z))
366 ((not (= x z)) (move-reg x z)))
373 (if (not (byte-lit? src2))
374 (error "bit offset must be a literal"))
375 (let ((x (byte-cell-adr src1))
376 (y (byte-lit-val src2)))
380 ((toggle) (btg x y)))))
383 (let ((z (byte-cell-adr dst)))
385 (move-lit (byte-lit-val src1) z)
386 (move-reg (byte-cell-adr src1) z))
391 (move-lit (byte-lit-val src1) TBLPTRL)
392 (move-reg (byte-cell-adr src1) TBLPTRL))
394 (move-lit (byte-lit-val src2) TBLPTRH)
395 (move-reg (byte-cell-adr src2) TBLPTRH))
396 ;; TODO the 5 high bits are not used for now
400 (if (null? (bb-succs bb))
401 (error "I think you might have given me an empty source file."))
402 (let* ((succs (bb-succs bb))
404 (bra-or-goto (bb-label dest))
407 (let* ((succs (bb-succs bb))
408 (dest-true (car succs))
409 (dest-false (cadr succs)))
411 (define (compare flip adr)
413 ((x<y) (if flip (cpfsgt adr) (cpfslt adr)))
414 ((x>y) (if flip (cpfslt adr) (cpfsgt adr)))
416 (bra-or-goto (bb-label dest-false))
417 (bra-or-goto (bb-label dest-true))
418 (add-todo dest-false)
419 (add-todo dest-true))
421 (cond ((byte-lit? src1)
422 (let ((n (byte-lit-val src1))
423 (y (byte-cell-adr src2)))
424 (if #f #;(and (or (= n 0) (= n 1) (= n #xff))
426 (special-compare-eq-lit n x)
431 (let ((x (byte-cell-adr src1))
432 (n (byte-lit-val src2)))
433 (if #f #;(and (or (= n 0) (= n 1) (= n #xff))
435 (special-compare-eq-lit n x)
440 (let ((x (byte-cell-adr src1))
441 (y (byte-cell-adr src2)))
446 (let* ((succs (bb-succs bb))
447 (dest-true (car succs))
448 (dest-false (cadr succs))
449 ;; scratch is always a byte cell
450 (scratch (byte-cell-adr src1)))
451 ;; note : bc is too short for some cases
452 ;; (bc (bb-label dest-true))
453 ;; (bra-or-goto (bb-label dest-false))
454 ;; instead, we use scratch to indirectly test the
455 ;; carry and use regular branches
460 (bra-or-goto (bb-label dest-false))
461 (bra-or-goto (bb-label dest-true))
462 (add-todo dest-false)
463 (add-todo dest-true)))
466 (let* ((off (if (byte-lit? src1) ; branch no
468 (byte-cell-adr src1)))
469 (scratch (byte-cell-adr src2)) ; working space
470 (succs (bb-succs bb))
471 (n-succs (length succs)))
474 ;; ;; size of the branch table (without the
475 ;; ;; offset-calculating code), if it uses short jumps
476 ;; ;; that take 2 bytes per instruction
477 ;; (let ((size-using-bra (* 2 n-succs))
478 ;; ;; size of the offset-calculating code, if we
479 ;; ;; use short jumps
480 ;; (bra-header-size ))
483 ;; ;; check if the targets are close enough to use
484 ;; ;; short jumps. all the targets must be close
485 ;; ;; enough, since all jumps must be of the same
491 ;; (let ((dist (- (label-pos (car new))
492 ;; (+ self (cdr new)))))
493 ;; ;; close enough for short jumps
494 ;; (if (and (>= dist -2048)
502 ;; (cons l (+ self n bra-header-size)))
503 ;; succs (iota n-succs)))))) ;; FOO no time for this for the moment
506 ;; precalculate the low byte of the PC
507 ;; note: both branches (off is a literal or a
508 ;; register) are of the same length in terms of
509 ;; code, which is important
513 ;; we add 4 times the offset, since gotos are 4
522 ;; to compensate for the PC advancing while we calculate
525 (movfw PCL) ;; TODO at assembly, this can all be known statically
531 ;; create the jump table
532 (for-each (lambda (bb)
539 (emit (list id)))))))))
543 (vector-set! bbs-vector label-num #f)
544 (label (bb-label bb))
545 (for-each dump-instr (reverse (bb-rev-instrs bb))))))))
547 (let ((prog-label (asm-make-label 'PROG)))
552 (add-todo (vector-ref bbs-vector 0))
557 (let ((bb (car todo)))
558 (set! todo (cdr todo))
563 (define (assembler-gen filename cfg)
567 ((eval (car instr)) (cadr instr)))
569 ((eval (car instr)) (cadr instr) (caddr instr)))
571 ((eval (car instr)) (cadr instr) (caddr instr) (cadddr instr)))
573 (let ((id (car instr)))
574 ;; count instructions by kind
575 (table-set! concrete-instructions-counts id
576 (+ (table-ref concrete-instructions-counts id 0) 1))
580 ((movff movwf clrf setf cpfseq cpfslt cpfsgt mulwf)
582 ((incf decf addwf addwfc subwf subwfb andwf iorwf xorwf rlcf rrcf comf
586 (tblrd*)) ;; TODO support the other modes
594 (bra-or-goto (cadr instr)))
596 (rcall-or-call (cadr instr)))
601 (string-append (symbol->string (asm-label-id (cadr instr))) ":"))
602 (asm-label (cadr instr)))
606 (error "unknown instruction" instr)))))
610 ;; (pretty-print cfg)
612 (let ((code (linearize-and-cleanup cfg)))
613 ;; (pretty-print code)
614 ;; if we would need a second bank, load the address for the second bank in BSR
616 (begin (gen (list 'movlw 1))
617 (gen (list 'movwf BSR 'a))))
618 (for-each gen code)))