(load "typecheck.scm")
(load "ast.scm")
(load "platform.scm")
+(load "stdlib.scm")
(define target host-os)
(define wordsize 8)
-(define (stack-type? data-layouts type)
- (if (assoc type data-layouts) #t #f))
+(define (stack-type? dls type)
+ (if (adt? dls type)
+ (> (type-size dls type) wordsize)
+ #f))
+(define (adt? data-layouts type) (assoc type data-layouts))
(define (type-size data-layouts type)
(case type
['Int wordsize]
- ['Bool wordsize]
[else
(let ([adt (assoc type data-layouts)])
(if adt
(adt-size adt)
(error #f "unknown size" type)))]))
+(define (singletons dls)
+ (flat-map
+ (lambda (sums)
+ (fold-left append '()
+ (filter (lambda (x) (= 1 (length x))) sums)))
+ (map cdr dls)))
+
+(define (singleton? dls x)
+ (memv x (singletons dls)))
+
; returns the size of an expression's result in bytes
(define (expr-size dls e)
(if (eqv? (ast-type e) 'stack)
(define make-env list)
(define env-data-layouts car)
(define env-bindings cadr)
+(define (env-append-bindings env bindings)
+ (make-env (env-data-layouts env)
+ (append bindings (env-bindings env))))
(define (codegen-add xs si env)
(define (go ys)
[dls (env-data-layouts env)]
- [singleton? (and stack-type
- (assoc name
- (cdr (assoc stack-type dls))))]
[stack-offset (assoc name (env-bindings env))])
+
(when (and (not stack-offset) (not singleton?))
(error #f (format "Variable ~a is not bound" name)))
- (if (on-stack? e)
- (if singleton?
+ (cond
+ [(singleton? dls name)
; singletons don't need to be in the environment
- ; just copy over the tag
- (emit "movq $~a, ~a(%rbp)"
- (data-sum-tag (env-data-layouts env)
- stack-type
- name)
- si)
- (emit-stack-copy (cdr stack-offset) si (type-size dls stack-type)))
- (emit "movq ~a(%rbp), %rax" (cdr stack-offset)))))
+ ; just store the tag
+ (let ([tag (data-sum-tag (env-data-layouts env) name)])
+ (if (on-stack? e)
+ (emit "movq $~a, ~a(%rbp)" tag si)
+ (emit "movq $~a, %rax" tag)))]
+ [(on-stack? e) (emit-stack-copy (cdr stack-offset) si (type-size dls stack-type))]
+ [else (emit "movq ~a(%rbp), %rax" (cdr stack-offset))])))
(define cur-lambda 0)
(define (fresh-lambda)
(define (codegen-lambda l)
(let* ((label (car l))
(stuff (cdr l))
- (captives (car stuff))
- (args (cadr stuff))
- (body (caddr stuff))
+ (dls (car stuff))
+ (captives (cadr stuff))
+ (args (caddr stuff))
+ (body (cadddr stuff))
; params = what actually gets passed
(params (append captives args))
(range 0 (length params))))
[bindings (map cons params stack-offsets)]
- [env (make-env '() bindings)])
+ [env (make-env dls bindings)])
(emit "~a:" label)
(display "## lambda captives: ")
; checks if equal and returns assoc list of bindings
(define (check-equal jne-label type inner-offset x)
+ ; TODO: tidy this up! comparibles and binds could be merged
; (foo a 2 (bar x)) -> ((2 Int 1) ((bar x) A 2))
; sum: foo
(define (comparibles sum)
- (if (not (list? sum))
- '()
(let ([product-types (cdr (assoc sum (cdr (assoc type dls))))])
+ (if (null? product-types)
+ '()
(filter (lambda (x) (not (eqv? 'var (ast-type (car x)))))
(map (lambda (x t i) (list x t i))
(cdr x)
product-types
(range 0 (length product-types)))))))
- (let ([sums (assoc type dls)])
- (if sums
+ (define (binds sum)
+ (let ([product-types (cdr (assoc sum (cdr (assoc type dls))))])
+ (if (null? product-types)
+ '()
+ (filter (lambda (x) (eqv? 'var (ast-type (car x))))
+ (map (lambda (x i)
+ (cons x
+ (- inner-offset
+ (data-product-offset dls type sum i))))
+ (cdr x)
+ (range 0 (length (cdr x))))))))
+
+
+ (if (adt? dls type)
(let* ([sum (if (list? x) (car x) x)] ; can sometimes be a singleton
- [tag (data-sum-tag dls type sum)])
+ [tag (data-sum-tag dls sum)])
; the tag is at the top (beginning) of the adt on the stack
(emit "cmpq $~a, ~a(%rbp)" tag inner-offset)
(emit "jne ~a" jne-label)
+
+ (append (binds sum)
(flat-map
- (lambda (cmpx cmpt cmpi)
+ (lambda (cmp) ; cmp = (x type index)
(check-equal jne-label
- cmpt
- (- inner-offset (data-product-offset dls type sum i))
- cmpx))
- (comparibles sum)))
+ (cadr cmp)
+ (- inner-offset (data-product-offset dls type sum (caddr cmp)))
+ (car cmp)))
+ (comparibles sum))))
(if (eqv? 'var (ast-type x))
(list (cons x inner-offset))
(begin
- (emit "cmp $~a, ~a(%rbp)" x inner-offset)
+ (emit "cmpq $~a, ~a(%rbp)" x inner-offset)
(emit "jne ~a" jne-label)
- '() )))))
+ '() ))))
(define (codegen-adt-match type case)
(let* ([match (car case)]
[expr (cadr case)]
[next-section-label (fresh-label)]
[inner-si (- si (type-size dls type))]
- [new-env (append (check-equal next-section-label type si match)
- env)])
+ [new-env (env-append-bindings env
+ (check-equal next-section-label type si match))])
+
(codegen-expr expr inner-si new-env)
(emit "jmp ~a" exit-label)
(emit "~a:" next-section-label)))
(define (codegen-literal-match case)
- (let ([next-section-label (fresh-label)])
- (emit "cmpq $~a, %rax" (car case))
+ (let* ([next-section-label (fresh-label)]
+ [match (car case)]
+ [val (if (singleton? dls match)
+ (data-sum-tag dls match)
+ match)])
+ (emit "cmpq $~a, %rax" val)
(emit "jne ~a" next-section-label)
(codegen-expr (cadr case) si env)
(emit "jmp ~a" exit-label)
; (and store it on the stack if not a stack value)
(codegen-expr switch si env)
- (if (eqv? 'stack (ast-type switch))
+ (if (on-stack? switch)
; adt pattern match
(for-each (lambda (x) (codegen-adt-match (cadr switch) x)) cases)
(for-each codegen-literal-match cases))
wordsize ; skip the tag in the first word
to-traverse)))
-(define (data-sum-tag data-layouts type sum)
+(define (data-sum-tag data-layouts sum)
(define (go acc sums)
(when (null? sums) (error #f "data-sum-tag no sum for type" sum type))
(if (eqv? sum (car sums))
acc
(go (+ 1 acc) (cdr sums))))
- (let* ([type-sums (cdr (assoc type data-layouts))])
+ (let* ([all-sum-names (flat-map (lambda (x) (map (lambda (y) (cons (car y) (car x))) (cdr x))) data-layouts)]
+ [type (cdr (assoc sum all-sum-names))]
+ [type-sums (cdr (assoc type data-layouts))])
(go 0 (map car type-sums))))
(define (codegen-data-tor e si env)
[args (cdr e)]
[tag (data-sum-tag (env-data-layouts env)
- type
sum)]
[inner-si (- si (type-size dls type))]
; takes in a expr annotated with types and returns a type-less AST
; with stack values wrapped
(define (annotate-stack-values data-layouts ann-e)
- (define (stack-type? type)
- (assoc type data-layouts))
(define (strip e)
(ast-traverse strip (ann-expr e)))
(let* ([e (ann-expr ann-e)]
[type (ann-type ann-e)])
- (if (stack-type? type)
+ (if (stack-type? data-layouts type)
`(stack ,type
,(ast-traverse (lambda (x) (annotate-stack-values data-layouts x)) e))
(ast-traverse (lambda (x)
(annotate-stack-values data-layouts x))
e))))
-(define (free-vars prog)
- (define bound '())
+(define (free-vars dls prog)
+ (define bound (singletons dls))
(define (collect e)
(case (ast-type e)
('builtin '()) ; do nothing
; outside of an immediate app
; but only one closure for each builtin
-(define (extract-lambdas program)
+(define (extract-lambdas dls program)
(define lambdas '())
(define (add-lambda e)
(let* ((label (fresh-lambda))
(args (lambda-args e))
- (captured (free-vars e))
+ (captured (free-vars dls e))
(body (extract (lambda-body e)))
- (new-lambda (cons label (list captured args body))))
+ (new-lambda (cons label (list dls captured args body))))
(set! lambdas (cons new-lambda lambdas))
`(closure ,label ,captured))) ; todo: should we string->symbol?
(captured '())
(args (builtin-args e))
(body `(,e ,@args))
- (new-lambda (cons label (list captured args body)))]
+ (new-lambda (cons label (list dls captured args body)))]
(set! lambdas (cons new-lambda lambdas))
`(closure ,label ,captured)))
(emit "movq heap_start@GOTPCREL(%rip), %rsi")
(emit "movq %rax, (%rsi)")))
-(define (codegen program)
+(define (codegen program-without-stdlib)
(set! cur-label 0)
(set! cur-lambda 0)
- (let* ([data-layouts (program-data-layouts program)]
+ (let* ([program (append stdlib program-without-stdlib)]
+ [data-layouts (program-data-layouts program)]
[pattern-matched (expand-pattern-matches program)]
[type-annotated (annotate-types pattern-matched)]
(strings-res (extract-strings stack-annotated))
(strings (car strings-res))
- (lambdas-res (extract-lambdas (cdr strings-res)))
+ (lambdas-res (extract-lambdas data-layouts (cdr strings-res)))
(lambdas (car lambdas-res))
(xform-prog (cdr lambdas-res)))
+ ; verify pattern matches are total
+ (verify-cases data-layouts type-annotated)
+
(emit "\t.global _start")
(emit "\t.text")
- ; (emit ".p2align 4,,15") is this needed?
(for-each codegen-lambda lambdas)
(initialize-heap)
(emit "movq %rsp, %rbp") ; set up the base pointer
-
(codegen-expr xform-prog (- wordsize) (make-env data-layouts '()))
; exit syscall