(define number-tag #b000)
(define immed-tag #b001)
(define pair-tag #b010)
(define string-tag #b011)
(define symbol-tag #b100)
(define vector-tag #b101)
(define closure-tag #b110)
(define mask #b111)
(define tag-len 3)
(define bool-tag #b00000001)
(define null-tag #b00001001)
(define char-tag #b00010001)
(define imm-mask #b11111111)
(define imm-tag-len 8)
(define ws 4)
(define encode
(let ([numtop (expt 2 29)])
(lambda (obj)
(cond
[(number? obj)
(cond
[(and (<= 0 obj) (< obj numtop)) (* obj (+ mask 1))]
[(and (<= (- numtop) obj) (< obj 0)) (* (+ numtop obj) (+ mask 1))]
[else
(error 'encode "~s is out of range" obj)])]
[(boolean? obj)
(+ (* (if obj 1 0) (+ imm-mask 1)) bool-tag)]
[(null? obj) null-tag]
[(char? obj)
(let ([val (char->integer obj)])
(+ (* val (+ imm-mask 1)) char-tag))]
[else
(error 'encode "~s not encodable" obj)]))))
(define instructions
(lambda args
(cons 'instructions
(let loop ([ls args])
(if (null? ls)
'()
(if (eq? (caar ls) 'instructions)
(append (cdar ls)
(loop (cdr ls)))
(cons (car ls)
(loop (cdr ls)))))))))
(define todo '())
(define cg-top
(lambda (exp)
(set! todo
(cons (list '_scheme_entry `(lambda () ,exp)) todo))
(cg-code)))
(define cg-code
(lambda ()
(if (null? todo)
(instructions)
(let ([first (car todo)]
[rest (cdr todo)])
(set! todo rest)
(let ([label (car first)])
(record-case (cadr first)
[lambda (formals body)
(instructions
`(label ,label)
(cg body (* (+ (length formals) 1) ws)
'ac 'return 'ignored)
(cg-code))]))))))
(define varref->address
(lambda (exp)
(record-case exp
[bound (n name)
`(fp ,(* (+ n 1) ws))]
[free (n name)
`(cp ,(* (+ n 2) ws))])))
(define cg
(lambda (exp fs dd cd nextlab)
(record-case exp
[bound (n name)
(cg-load-branch `(fp ,(* (+ n 1) ws)) dd cd nextlab)]
[free (n name)
(cg-load-branch `(cp ,(* (+ n 2) ws)) dd cd nextlab)]
[quote (obj)
(cg-set-branch obj dd cd nextlab)]
[begin (a b)
(let ([midlab (gen-label "begin")])
(instructions
(cg a fs 'effect midlab midlab)
`(label ,midlab)
(cg b fs dd cd nextlab)))]
[if (t c a)
(let ([truelab (gen-label "iftrue")]
[falselab (gen-label "iffalse")])
(instructions
(cg t fs 'effect (join-labels truelab falselab) truelab)
`(label ,truelab)
(cg c fs dd cd falselab)
`(label ,falselab)
(cg a fs dd cd nextlab)))]
[build-closure (code . fvars)
(if (eq? dd 'effect)
(cg-jump (cd->true cd) nextlab)
(let ([codelab (gen-label "code")])
(set! todo (cons (list codelab code) todo))
(instructions
`(comment "build-closure")
(cg-allocate (+ (length fvars) 2) 'ac)
`(set ,(length fvars) t1)
`(st t1 (ac 0))
`(set ,codelab t1)
`(st t1 (ac ,(* 1 ws)))
(let f ([ls fvars] [pos 2])
(if (null? ls)
(instructions)
(instructions
`(ld ,(varref->address (car ls)) t3)
`(st t3 (ac ,(* pos ws)))
(f (cdr ls) (+ pos 1)))))
(cg-type-tag closure-tag 'ac)
(cg-store 'ac dd)
`(comment "end build-closure")
(cg-jump cd nextlab))))]
[else
(let ([rator (car exp)]
[rands (cdr exp)]
[ratorlab (gen-label "endrator")])
(cond
[(symbol? rator)
(cg-inline exp rator rands fs dd cd nextlab)]
[(eq? cd 'return)
(instructions
(cg-rands rands fs)
(cg rator (+ fs (* (length rands) ws)) 'ac ratorlab ratorlab)
`(label ,ratorlab)
(cg-shuffle fs (length rands))
`(andn ac ,mask cp)
`(ld (cp ,(* 1 ws)) ac)
`(jmp ac)
`(nop))]
[else
(let ([retlab (gen-label "return")])
(instructions
`(st cp (fp ,fs))
`(set ,retlab ac)
`(st ac (fp ,(+ fs (* 1 ws))))
(cg-rands rands (+ fs (* 2 ws)))
(cg rator (+ fs (* (+ (length rands) 2) ws))
'ac ratorlab ratorlab)
`(label ,ratorlab)
`(andn ac ,mask cp)
(cg-pushstack (+ fs (* 1 ws)))
`(ld (cp ,(* 1 ws)) ac)
`(jmp ac)
`(nop)
`(label ,retlab)
(cg-pushstack (- (+ fs (* 1 ws))))
`(ld (fp ,fs) cp)
(cg-store 'ac dd)
(cond
[(pair? cd)
(let ([truelab (car cd)]
[falselab (cadr cd)])
(instructions
`(cmp ac ,(encode #f))
(cg-branch truelab falselab nextlab 'bne 'be)))]
[else
(cg-jump cd nextlab)])))]))])))
(define cg-shuffle
(lambda (fs num)
(let loop ([top fs] [bot ws] [num num])
(if (zero? num)
(instructions)
(instructions
`(ld (fp ,top) t1)
`(st t1 (fp ,bot))
(loop (+ top ws) (+ bot ws) (sub1 num)))))))
(define cg-jump
(lambda (lab nextlab)
(if (eq? lab 'return)
(instructions
`(ld (fp 0) t1)
`(jmp t1)
`(nop))
(if (eq? lab nextlab)
(instructions)
(instructions
`(ba ,lab)
`(nop))))))
(define cg-branch
(lambda (truelab falselab nextlab jump-if-true jump-if-false)
(instructions
(cond
[(eq? truelab nextlab)
`(,jump-if-false ,falselab)]
[(eq? falselab nextlab)
`(,jump-if-true ,truelab)]
[else
(instructions
`(,jump-if-true ,truelab)
`(nop)
`(ba ,falselab))])
`(nop))))
(define cg-store
(lambda (src dest)
(cond
[(eq? dest 'effect)
(instructions)]
[(pair? dest)
`(st ,src ,dest)]
[else
(if (eq? src dest)
(instructions)
`(mov ,src ,dest))])))
(define cg-load-branch
(lambda (loc dd cd nextlab)
(cond
[(eq? dd 'effect)
(cond
[(pair? cd)
(let ([truelab (car cd)]
[falselab (cadr cd)])
(instructions
`(ld ,loc t1)
`(cmp t1 ,(encode #f))
(cg-branch truelab falselab nextlab 'bne 'be)))]
[else
(cg-jump cd nextlab)])]
[(pair? dd)
(let ([register (car dd)]
[offset (cadr dd)])
(instructions
`(ld ,loc t1)
`(st t1 (,register ,offset))
(cg-jump cd nextlab)))]
[else
(instructions
`(ld ,loc ,dd)
(cg-jump cd nextlab))])))
(define cg-set-branch
(lambda (obj dd cd nextlab)
(cond
[(eq? dd 'effect)
(if (pair? cd)
(let ([truelab (car cd)]
[falselab (cadr cd)])
(cg-jump (if obj truelab falselab) nextlab))
(cg-jump cd nextlab))]
[(pair? dd)
(instructions
`(set ,(encode obj) t1 ,(format "~s" obj))
`(st t1 ,dd)
(cg-jump cd nextlab))]
[else
(instructions
`(set ,(encode obj) ,dd ,(format "~s" obj))
(cg-jump cd nextlab))])))
(define cg-rands
(lambda (rands fs)
(if (null? rands)
(instructions)
(let ([randlab (gen-label "rand")])
(instructions
(cg (car rands) fs `(fp ,fs) randlab randlab)
`(label ,randlab)
(cg-rands (cdr rands) (+ fs ws)))))))
(define cg-effect-rands
(lambda (ls fs)
(if (null? ls)
(instructions)
(let ([randlab (gen-label "rand")])
(instructions
(cg (car ls) fs 'effect randlab randlab)
`(label ,randlab)
(cg-effect-rands (cdr ls) fs))))))
(define cg-unary-rand
(lambda (rands fs)
(let ([rand (car rands)])
(let ([endlab (gen-label "unaryrand")])
(instructions
(cg (car rands) fs 't1 endlab endlab)
`(label ,endlab))))))
(define cg-binary-rands
(lambda (rands fs)
(let ([r0 (car rands)]
[r1 (cadr rands)])
(let ([r0lab (gen-label "binary0")]
[r1lab (gen-label "binary1")])
(instructions
(cg r0 fs `(fp ,fs) r0lab r0lab)
`(label ,r0lab)
(cg r1 (+ fs (* 1 ws)) 'ac r1lab r1lab)
`(label ,r1lab)
`(mov ac t2)
`(ld (fp ,fs) t1))))))
(define cg-ternary-rands
(lambda (rands fs)
(let ([r0 (car rands)]
[r1 (cadr rands)]
[r2 (caddr rands)])
(let ([r0lab (gen-label "ternary0")]
[r1lab (gen-label "ternary1")]
[r2lab (gen-label "ternary2")])
(instructions
(cg r0 fs `(fp ,fs) r0lab r0lab)
`(label ,r0lab)
(cg r1 (+ fs (* 1 ws)) `(fp ,(+ fs (* 1 ws))) r1lab r1lab)
`(label ,r1lab)
(cg r2 (+ fs (* 2 ws)) 'ac r2lab r2lab)
`(label ,r2lab)
`(mov ac t3)
`(ld (fp ,(+ fs (* 1 ws))) t2)
`(ld (fp ,fs) t1))))))
(define cg-inline
(lambda (exp name rands fs dd cd nextlab)
(case name
[(+)
(cg-true-inline cg-binary-rands rands fs dd cd nextlab
`(add t1 t2 ac))]
[(-)
(cg-true-inline cg-binary-rands rands fs dd cd nextlab
`(sub t1 t2 ac))]
[(*)
(cg-true-inline cg-binary-rands rands fs dd cd nextlab
(instructions
`(sra t2 ,tag-len t2)
`(smul t1 t2 ac)))]
[(/)
(cg-true-inline cg-binary-rands rands fs dd cd nextlab
(instructions
`(sdiv t1 t2 ac)
`(sll ac ,tag-len ac)))]
[(= eq?)
(cg-binary-pred-inline exp rands fs dd cd nextlab 'be 'bne
`(cmp t1 t2))]
[(<)
(cg-binary-pred-inline exp rands fs dd cd nextlab 'bl 'bge
`(cmp t1 t2))]
[(boolean?)
(cg-type-test exp bool-tag imm-mask rands fs dd cd nextlab)]
[(car)
(cg-ref-inline cg-unary-rand rands fs dd cd nextlab
`(ld (t1 ,(- pair-tag)) ac))]
[(cdr)
(cg-ref-inline cg-unary-rand rands fs dd cd nextlab
`(ld (t1 ,(- ws pair-tag)) ac))]
[(char?)
(cg-type-test exp char-tag imm-mask rands fs dd cd nextlab)]
[(char->integer)
(cg-true-inline cg-unary-rand rands fs dd cd nextlab
(instructions
`(srl t1 8 ac)
`(sll ac ,tag-len ac)))]
[(cons)
(cg-true-inline cg-binary-rands rands fs dd cd nextlab
(instructions
(cg-allocate 2 'ac)
`(st t1 (ac 0))
`(st t2 (ac ,(* 1 ws)))
(cg-type-tag pair-tag 'ac)))]
[(integer?)
(cg-type-test exp number-tag mask rands fs dd cd nextlab)]
[(string->uninterned-symbol)
(cg-true-inline cg-unary-rand rands fs dd cd nextlab
(instructions
(cg-allocate 1 'ac)
`(st t1 (ac 0))
(cg-type-tag symbol-tag 'ac)))]
[(not)
(if (eq? dd 'effect)
(if (pair? cd)
(let ([truelab (car cd)]
[falselab (cadr cd)])
(cg (car rands) fs 'effect (join-labels falselab truelab)
nextlab))
(instructions
(cg-effect-rands rands fs)
(cg-jump cd nextlab)))
(cg `(if ,(car rands) '#f '#t) fs dd cd nextlab))]
[(null?)
(cg-type-test exp null-tag imm-mask rands fs dd cd nextlab)]
[(pair?)
(cg-type-test exp pair-tag mask rands fs dd cd nextlab)]
[(procedure?)
(cg-type-test exp closure-tag mask rands fs dd cd nextlab)]
[(string)
(cg-true-inline cg-rands rands fs dd cd nextlab
(instructions
`(comment "string")
(cg-allocate (+ (quotient (+ (length rands) (- ws 1)) ws) 1) 'ac)
`(set ,(length rands) t1)
`(st t1 (ac 0))
(let loop ([fpos fs] [spos ws] [num (length rands)])
(if (zero? num)
(instructions)
(instructions
`(ld (fp ,fpos) t1)
`(srl t1 8 t1) `(stb t1 (ac ,spos))
(loop (+ fpos ws) (+ spos 1) (- num 1)))))
(cg-type-tag string-tag 'ac)
`(comment "end string")))]
[(string?)
(cg-type-test exp string-tag mask rands fs dd cd nextlab)]
[(string-length)
(cg-true-inline cg-unary-rand rands fs dd cd nextlab
(instructions
`(ld (t1 ,(- string-tag)) ac)
`(sll ac ,tag-len ac)))]
[(string-ref)
(cg-true-inline cg-binary-rands rands fs dd cd nextlab
(instructions
`(sra t2 ,tag-len t2)
`(add t1 t2 t1)
`(ldb (t1 ,(- ws string-tag)) ac)
`(sll ac 8 ac)
(cg-type-tag char-tag 'ac)))]
[(vector)
(cg-true-inline cg-rands rands fs dd cd nextlab
(instructions
`(comment "vector")
(cg-allocate (+ (length rands) 1) 'ac)
`(set ,(length rands) t1)
`(st t1 (ac 0))
(let loop ([fpos fs] [vpos 1] [num (length rands)])
(if (zero? num)
(instructions)
(instructions
`(ld (fp ,fpos) t1)
`(st t1 (ac ,(* vpos ws)))
(loop (+ fpos ws) (+ vpos 1) (- num 1)))))
(cg-type-tag vector-tag 'ac)
`(comment "end vector")))]
[(vector?)
(cg-type-test exp vector-tag mask rands fs dd cd nextlab)]
[(vector-length)
(cg-true-inline cg-unary-rand rands fs dd cd nextlab
(instructions
`(ld (t1 ,(- vector-tag)) ac)
`(sll ac ,tag-len ac)))]
[(vector-ref)
(cg-ref-inline cg-binary-rands rands fs dd cd nextlab
(instructions
`(sra t2 1 t2)
`(add t1 t2 t1)
`(ld (t1 ,(- ws vector-tag)) ac)))]
[(vector-set!)
(instructions
(cg-ternary-rands rands fs)
`(comment "vector-set")
`(sra t2 1 t2)
`(add t1 t2 t1)
`(st t3 (t1 ,(- ws vector-tag)))
`(comment "end vector-set")
(if (eq? dd 'effect)
(cg-jump (cd->true cd) nextlab)
(instructions
(cg-store 't3 dd) (cg-jump cd nextlab))))]
[(symbol?)
(cg-type-test exp symbol-tag mask rands fs dd cd nextlab)]
[(symbol->string)
(cg-true-inline cg-unary-rand rands fs dd cd nextlab
`(ld (t1 ,(- symbol-tag)) ac))]
[else
(error 'cg-prim "sanity-check: bad primitive ~s" name)])))
(define cg-true-inline
(lambda (rander rands fs dd cd nextlab code)
(if (eq? dd 'effect)
(instructions
(cg-effect-rands rands fs)
(cg-jump (cd->true cd) nextlab))
(instructions
(rander rands fs)
code
(cg-store 'ac dd)
(cg-jump cd nextlab)))))
(define cg-ref-inline
(lambda (rander rands fs dd cd nextlab code)
(if (eq? dd 'effect)
(if (pair? cd)
(let ([truelab (car cd)]
[falselab (cadr cd)])
(instructions
(rander rands fs)
code
`(cmp ac ,(encode #f))
(cg-branch truelab falselab nextlab 'bne 'be)))
(instructions
(cg-effect-rands rands fs)
(cg-jump cd nextlab)))
(instructions
(rander rands fs)
code
(cg-store 'ac dd)
(cg-jump cd nextlab)))))
(define cg-binary-pred-inline
(lambda (exp rands fs dd cd nextlab trueinst falseinst code)
(if (eq? dd 'effect)
(if (pair? cd)
(let ([truelab (car cd)]
[falselab (cadr cd)])
(instructions
(cg-binary-rands rands fs)
code
(cg-branch truelab falselab nextlab trueinst falseinst)))
(instructions
(cg-effect-rands rands fs)
(cg-jump cd nextlab)))
(cg `(if ,exp '#t '#f) fs dd cd nextlab))))
(define cg-type-test
(lambda (exp tag mask rands fs dd cd nextlab)
(if (eq? dd 'effect)
(if (pair? cd)
(let ([truelab (car cd)]
[falselab (cadr cd)])
(instructions
(cg-unary-rand rands fs)
`(and t1 ,mask t1)
`(cmp t1 ,tag)
(cg-branch truelab falselab nextlab 'be 'bne)))
(instructions
(cg-effect-rands rands fs)
(cg-jump cd nextlab)))
(cg `(if ,exp '#t '#f) fs dd cd nextlab))))
(define cg-type-tag
(lambda (tag reg)
`(or ,reg ,tag ,reg)))
(define cg-pushstack
(lambda (n)
`(add fp ,n fp)))
(define cg-allocate
(lambda (n target)
(let ([n (if (even? n) n (+ n 1))])
(instructions
`(mov ap ,target)
`(add ap ,(* n ws) ap)))))
(define join-labels
(lambda (a b)
(cond
[(pair? a)
(join-labels (car a) b)]
[(pair? b)
(list a (cadr b))]
[else
(list a b)])))
(define cd->true
(lambda (cd)
(if (pair? cd)
(car cd)
cd)))
(define gen-label
(let ([n 0])
(lambda (str)
(set! n (add1 n))
(string->uninterned-symbol
(string-append str (number->string n))))))