Add case statement codegen and singleton ADTs
authorLuke Lau <luke_lau@icloud.com>
Thu, 15 Aug 2019 15:51:36 +0000 (16:51 +0100)
committerLuke Lau <luke_lau@icloud.com>
Thu, 15 Aug 2019 15:51:36 +0000 (16:51 +0100)
codegen.scm
tests.scm

index dd7950765c9ef375d443a110788a9146079fad20..a70ef6c899f2d4c3d4c656bbf9676ca91dab08c5 100644 (file)
@@ -36,9 +36,9 @@
           (error #f "unknown size" type)))]))
 
                                        ; returns the size of an expression's result in bytes
-(define (expr-size e)
+(define (expr-size dls e)
   (if (eqv? (ast-type e) 'stack)
-      (cadr e)
+      (type-size dls (cadr e))
       wordsize))
 
 (define (on-stack? expr)
     [else #f]))
 
                                        ; does a movsq for something on the stack
+                                       ; src points to the start stack index, but not the top of that index
+                                       ; likewise for dst
+                                       ; | ...  |
+                                       ; +------+ <-- to here
+                                       ; | tag0 |
+                                       ; +------+ <-- src (size = 16)
+                                       ; |  42  |
+                                       ; +------+ <-- start copying from here...
 (define (emit-stack-copy src dst size)
-  (emit "leaq ~a(%rbp), %rsi" (- src size))
-  (emit "leaq ~a(%rbp), %rdi" (- dst size))
+  (let ([size-to-copy (- size wordsize)])
+    (emit "leaq ~a(%rbp), %rsi" (- src size-to-copy))
+    (emit "leaq ~a(%rbp), %rdi" (- dst size-to-copy))
     (emit "movq $~a, %rcx" (/ size wordsize))
-  (emit "rep movsq"))    
+    (emit "rep movsq")))
   
 
                                        ; an environment consists of adt layouts in scope,
 
   ;;   (fold-left emit-binding (cons env '()) scc))))
                                        ; assoc map of binding name to size
+
+
+  (define dls (env-data-layouts env))
+  
   (define stack-sizes
-    (map (lambda (binding) (cons (car binding) (expr-size (cadr binding))))
+    (map (lambda (binding) (cons (car binding) (expr-size dls (cadr binding))))
         bindings))
 
                                        ; assoc map of binding name to offset
               (for-each 
                (lambda (name)
                  (let* ([expr (cadr (assoc name bindings))]
-                        [size (expr-size expr)])
+                        [size (expr-size dls expr)])
                    (emit "## generating ~a with scc-env ~a" name scc-env)
                    (if (self-captive-closure? name expr)
                                        ; if self-captive, insert a flag into the environment to let
              body)))
 
 (define (codegen-var e si env)
-  (let* ([stack-size (on-stack? e)]
-        [name (if (on-stack? e) (caddr e) e)]
-        [stack-offset (cdr (assoc name (env-bindings env)))])
-    (when (not stack-offset)
+  (let* ([stack-type (on-stack? e)]     
+        [name (if stack-type (caddr e) e)]
+
+        [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)
-       (emit-stack-copy stack-offset si stack-size)
-       (emit "movq ~a(%rbp), %rax" stack-offset))))
+       (if singleton?
+                                       ; 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)))))
 
 (define cur-lambda 0)
 (define (fresh-lambda)
     (codegen-expr else si env)
     (emit "~a:" exit-label)))
 
+(define (codegen-case switch cases si env)
+  (define dls (env-data-layouts env))
+  (define exit-label (fresh-label))
+
+
+                                       ; checks if equal and returns assoc list of bindings
+  (define (check-equal jne-label type inner-offset x)
+
+                                       ; (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))))])
+           (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
+         (let* ([sum (if (list? x) (car x) x)] ; can sometimes be a singleton
+                [tag (data-sum-tag dls type 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)
+           (flat-map
+            (lambda (cmpx cmpt cmpi)
+              (check-equal jne-label
+                           cmpt
+                           (- inner-offset (data-product-offset dls type sum i))
+                           cmpx))
+            (comparibles sum)))
+         (if (eqv? 'var (ast-type x))
+             (list (cons x inner-offset))
+             (begin
+               (emit "cmp $~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)])
+      
+      (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))
+      (emit "jne ~a" next-section-label)
+      (codegen-expr (cadr case) si env)
+      (emit "jmp ~a" exit-label)
+      (emit "~a:" next-section-label)))
+  
+                                       ; generate the switch
+                                       ; (and store it on the stack if not a stack value)
+  (codegen-expr switch si env)
+
+  (if (eqv? 'stack (ast-type switch))
+                                       ; adt pattern match
+      (for-each (lambda (x) (codegen-adt-match (cadr switch) x)) cases)
+      (for-each codegen-literal-match cases))
+  (emit "~a:" exit-label))
+      
+
 (define (data-tor env e)
   (if (not (list? e)) #f    
       (assoc (car e) (flat-map data-tors (env-data-layouts env)))))
     ('var (codegen-var e si env))
 
     ('if (codegen-if (cadr e) (caddr e) (cadddr e) si env))
+    ('case (codegen-case (case-switch e) (case-cases e) si env))
     
     ('bool-literal (emit "movq $~a, %rax" (if e 1 0)))
     ('int-literal (emit "movq $~a, %rax" e))
   (let* ([e (ann-expr ann-e)]
         [type (ann-type ann-e)])
     (if (stack-type? type)
-       `(stack ,(type-size 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))
index 7185032945aab633c43d3fcf0b0f1c24d55d0eb9..0c11e89714b89d309cae775cc7f47d3d800e752f 100644 (file)
--- a/tests.scm
+++ b/tests.scm
 (test-prog '((data Foo [a] [b] [c])
             (let ([x b])
               (case x
-                [a b]
-                [b a]
-                [c x])))
+                [a 3]
+                [b 2]
+                [c 1])))
           2)