nifty generic compiler infrastructure -- no more hardcoded passes
[bpt/guile.git] / module / system / il / compile.scm
index 341b906..e5c1b48 100644 (file)
 ;;; Code:
 
 (define-module (system il compile)
-  :use-module (oop goops)
-  :use-syntax (system base syntax)
-  :use-module (system il glil)
-  :use-module (system il ghil)
-  :use-module (ice-9 common-list)
-  :export (compile))
-
-(define (compile x e . opts)
-  (set! x (parse-ghil x e))
-  (if (memq :O opts) (set! x (optimize x)))
-  (codegen x))
+  #:use-syntax (system base syntax)
+  #:use-module (system il glil)
+  #:use-module (system il ghil)
+  #:use-module (ice-9 common-list)
+  #:export (compile))
+
+(define (compile x e opts)
+  (if (memq #:O opts) (set! x (optimize x)))
+  (values (codegen x)
+          (and e (cons (car e) (cddr e)))))
 
 \f
 ;;;
 ;;; Stage 2: Optimization
 ;;;
 
+(define (lift-variables! env)
+  (let ((parent-env (ghil-env-parent env)))
+    (for-each (lambda (v)
+                (case (ghil-var-kind v)
+                  ((argument) (set! (ghil-var-kind v) 'local)))
+                (set! (ghil-var-env v) parent-env)
+                (ghil-env-add! parent-env v))
+              (ghil-env-variables env))))
+
 (define (optimize x)
-  (match x
-    (($ <ghil-set> env var val)
-     (make-<ghil-set> env var (optimize val)))
-
-    (($ <ghil-if> test then else)
-     (make-<ghil-if> (optimize test) (optimize then) (optimize else)))
-
-    (($ <ghil-begin> exps)
-     (make-<ghil-begin> (map optimize exps)))
-
-    (($ <ghil-bind> env vars vals body)
-     (make-<ghil-bind> env vars (map optimize vals) (optimize body)))
-
-    (($ <ghil-lambda> env vars rest body)
-     (make-<ghil-lambda> env vars rest (optimize body)))
-
-    (($ <ghil-inst> inst args)
-     (make-<ghil-inst> inst (map optimize args)))
-
-    (($ <ghil-call> env proc args)
-     (match proc
-       ;; ((@lambda (VAR...) BODY...) ARG...) =>
-       ;;   (@let ((VAR ARG) ...) BODY...)
-       (($ <ghil-lambda> lambda-env vars #f body)
-       (for-each (lambda (v)
-                   (if (eq? v.kind 'argument) (set! v.kind 'local))
-                   (set! v.env env)
-                   (ghil-env-add! env v))
-                 lambda-env.variables)
-       (optimize (make-<ghil-bind> env vars args body)))
-       (else
-       (make-<ghil-call> env (optimize proc) (map optimize args)))))
+  (record-case x
+    ((<ghil-set> env loc var val)
+     (make-ghil-set env var (optimize val)))
+
+    ((<ghil-define> env loc var val)
+     (make-ghil-define env var (optimize val)))
+
+    ((<ghil-if> env loc test then else)
+     (make-ghil-if env loc (optimize test) (optimize then) (optimize else)))
+
+    ((<ghil-and> env loc exps)
+     (make-ghil-and env loc (map optimize exps)))
+
+    ((<ghil-or> env loc exps)
+     (make-ghil-or env loc (map optimize exps)))
+
+    ((<ghil-begin> env loc exps)
+     (make-ghil-begin env loc (map optimize exps)))
+
+    ((<ghil-bind> env loc vars vals body)
+     (make-ghil-bind env loc vars (map optimize vals) (optimize body)))
+
+    ((<ghil-lambda> env loc vars rest meta body)
+     (make-ghil-lambda env loc vars rest meta (optimize body)))
+
+    ((<ghil-inline> env loc instruction args)
+     (make-ghil-inline env loc instruction (map optimize args)))
+
+    ((<ghil-call> env loc proc args)
+     (let ((parent-env env))
+       (record-case proc
+         ;; ((@lambda (VAR...) BODY...) ARG...) =>
+         ;;   (@let ((VAR ARG) ...) BODY...)
+         ((<ghil-lambda> env loc vars rest meta body)
+          (cond
+           ((not rest)
+            (lift-variables! env)
+            (make-ghil-bind parent-env loc (map optimize args)))
+           (else
+            (make-ghil-call parent-env loc (optimize proc) (map optimize args)))))
+         (else
+          (make-ghil-call parent-env loc (optimize proc) (map optimize args))))))
+
+    ((<ghil-mv-call> env loc producer consumer)
+     (record-case consumer
+      ;; (mv-call PRODUCER (lambda ARGS BODY...)) =>
+      ;;   (mv-let PRODUCER ARGS BODY...)
+      ((<ghil-lambda> env loc vars rest meta body)
+       (lift-variables! env)
+       (make-ghil-mv-bind producer vars rest body))
+      (else
+       (make-ghil-mv-call env loc (optimize producer) (optimize consumer)))))
+
     (else x)))
 
 \f
 ;;; Stage 3: Code generation
 ;;;
 
-(define *ia-void* (make-<glil-void>))
-(define *ia-drop* (make-<glil-call> 'drop 0))
-(define *ia-return* (make-<glil-call> 'return 0))
+(define *ia-void* (make-glil-void))
+(define *ia-drop* (make-glil-call 'drop 0))
+(define *ia-return* (make-glil-call 'return 0))
 
 (define (make-label) (gensym ":L"))
 
 (define (make-glil-var op env var)
-  (case var.kind
+  (case (ghil-var-kind var)
     ((argument)
-     (make-<glil-argument> op var.index))
+     (make-glil-argument op (ghil-var-index var)))
     ((local)
-     (make-<glil-local> op var.index))
+     (make-glil-local op (ghil-var-index var)))
     ((external)
      (do ((depth 0 (1+ depth))
-         (e env e.parent))
-        ((eq? e var.env)
-         (make-<glil-external> op depth var.index))))
-    ((module)
-     (make-<glil-module> op var.env var.name))
+         (e env (ghil-env-parent e)))
+        ((eq? e (ghil-var-env var))
+         (make-glil-external op depth (ghil-var-index var)))))
+    ((toplevel)
+     (make-glil-toplevel op (ghil-var-name var)))
+    ((public private)
+     (make-glil-module op (ghil-var-env var) (ghil-var-name var)
+                       (eq? (ghil-var-kind var) 'public)))
     (else (error "Unknown kind of variable:" var))))
 
+(define (constant? x)
+  (cond ((or (number? x) (string? x) (symbol? x) (keyword? x) (boolean? x)) #t)
+        ((pair? x) (and (constant? (car x))
+                        (constant? (cdr x))))
+        ((vector? x) (let lp ((i (vector-length x)))
+                       (or (zero? i)
+                           (and (constant? (vector-ref x (1- i)))
+                                (lp (1- i))))))))
+
 (define (codegen ghil)
   (let ((stack '()))
-    (define (push-code! code)
-      (set! stack (cons code stack)))
+    (define (push-code! loc code)
+      (set! stack (cons code stack))
+      (if loc (set! stack (cons (make-glil-source loc) stack))))
+    (define (var->binding var)
+      (list (ghil-var-name var) (ghil-var-kind var) (ghil-var-index var)))
+    (define (push-bindings! loc vars)
+      (if (not (null? vars))
+          (push-code! loc (make-glil-bind (map var->binding vars)))))
     (define (comp tree tail drop)
+      (define (push-label! label)
+       (push-code! #f (make-glil-label label)))
+      (define (push-branch! loc inst label)
+       (push-code! loc (make-glil-branch inst label)))
+      (define (push-call! loc inst args)
+       (for-each comp-push args)
+       (push-code! loc (make-glil-call inst (length args))))
       ;; possible tail position
       (define (comp-tail tree) (comp tree tail drop))
       ;; push the result
       (define (comp-push tree) (comp tree #f #f))
       ;; drop the result
       (define (comp-drop tree) (comp tree #f #t))
+      ;; drop the result if unnecessary
+      (define (maybe-drop)
+       (if drop (push-code! #f *ia-drop*)))
+      ;; return here if necessary
+      (define (maybe-return)
+       (if tail (push-code! #f *ia-return*)))
       ;; return this code if necessary
-      (define (return-code! code)
-       (if (not drop) (push-code! code))
-       (if tail (push-code! *ia-return*)))
+      (define (return-code! loc code)
+       (if (not drop) (push-code! loc code))
+       (maybe-return))
       ;; return void if necessary
-      (define (return-void!) (return-code! *ia-void*))
+      (define (return-void!)
+       (return-code! #f *ia-void*))
+      ;; return object if necessary
+      (define (return-object! loc obj)
+       (return-code! loc (make-glil-const #:obj obj)))
       ;;
       ;; dispatch
-      (match tree
-       ((<ghil-void>)
+      (record-case tree
+       ((<ghil-void>)
         (return-void!))
 
-       (($ <ghil-quote> obj)
-        (return-code! (make-<glil-const> obj)))
+       ((<ghil-quote> env loc obj)
+        (return-object! loc obj))
+
+       ((<ghil-quasiquote> env loc exp)
+        (let loop ((x exp) (in-car? #f))
+           (cond
+            ((list? x)
+             (push-call! #f 'mark '())
+             (for-each (lambda (x) (loop x #t)) x)
+             (push-call! #f 'list-mark '()))
+            ((pair? x)
+             (push-call! #f 'mark '())
+             (loop (car x) #t)
+             (loop (cdr x) #f)
+             (push-call! #f 'cons-mark '()))
+            ((record? x)
+             (record-case x
+              ((<ghil-unquote> env loc exp)
+               (comp-push exp))
+              ((<ghil-unquote-splicing> env loc exp)
+               (if (not in-car?)
+                   (error "unquote-splicing in the cdr of a pair" exp))
+               (comp-push exp)
+               (push-call! #f 'list-break '()))))
+            ((constant? x)
+             (push-code! #f (make-glil-const #:obj x)))
+            (else
+             (error "element of quasiquote can't be compiled" x))))
+        (maybe-drop)
+        (maybe-return))
 
-       (($ <ghil-ref> env var)
-        (return-code! (make-glil-var 'ref env var)))
+       ((<ghil-ref> env loc var)
+        (return-code! loc (make-glil-var 'ref env var)))
 
-       (($ <ghil-set> env var val)
+       ((<ghil-set> env loc var val)
         (comp-push val)
-        (push-code! (make-glil-var 'set env var))
+        (push-code! loc (make-glil-var 'set env var))
         (return-void!))
 
-       (($ <ghil-if> test then else)
+       ((<ghil-define> env loc var val)
+        (comp-push val)
+        (push-code! loc (make-glil-var 'define env var))
+        (return-void!))
+
+       ((<ghil-if> env loc test then else)
         ;;     TEST
         ;;     (br-if-not L1)
         ;;     THEN
-        ;;     (jump L2)
+        ;;     (br L2)
         ;; L1: ELSE
         ;; L2:
         (let ((L1 (make-label)) (L2 (make-label)))
           (comp-push test)
-          (push-code! (make-<glil-branch> 'br-if-not L1))
+          (push-branch! loc 'br-if-not L1)
           (comp-tail then)
-          (if (not tail) (push-code! (make-<glil-branch> 'jump L2)))
-          (push-code! (make-<glil-label> L1))
+          (if (not tail) (push-branch! #f 'br L2))
+          (push-label! L1)
           (comp-tail else)
-          (if (not tail) (push-code! (make-<glil-label> L2)))))
+          (if (not tail) (push-label! L2))))
+
+       ((<ghil-and> env loc exps)
+        ;;     EXP
+        ;;     (br-if-not L1)
+        ;;     ...
+        ;;     TAIL
+        ;;     (br L2)
+        ;; L1: (const #f)
+        ;; L2:
+         (cond ((null? exps) (return-object! loc #t))
+               ((null? (cdr exps)) (comp-tail (car exps)))
+               (else
+                (let ((L1 (make-label)) (L2 (make-label)))
+                  (let lp ((exps exps))
+                    (cond ((null? (cdr exps))
+                           (comp-tail (car exps))
+                           (push-branch! #f 'br L2)
+                           (push-label! L1)
+                           (return-object! #f #f)
+                           (push-label! L2)
+                           (maybe-return))
+                          (else
+                           (comp-push (car exps))
+                           (push-branch! #f 'br-if-not L1)
+                           (lp (cdr exps)))))))))
+
+       ((<ghil-or> env loc exps)
+        ;;     EXP
+        ;;     (dup)
+        ;;     (br-if L1)
+        ;;     (drop)
+        ;;     ...
+        ;;     TAIL
+        ;; L1:
+         (cond ((null? exps) (return-object! loc #f))
+               ((null? (cdr exps)) (comp-tail (car exps)))
+               (else
+                (let ((L1 (make-label)))
+                  (let lp ((exps exps))
+                    (cond ((null? (cdr exps))
+                           (comp-tail (car exps))
+                           (push-label! L1)
+                           (maybe-return))
+                          (else
+                           (comp-push (car exps))
+                           (if (not drop)
+                               (push-call! #f 'dup '()))
+                           (push-branch! #f 'br-if L1)
+                           (if (not drop)
+                               (push-call! #f 'drop '()))
+                           (lp (cdr exps)))))))))
 
-       (($ <ghil-begin> exps)
+       ((<ghil-begin> env loc exps)
         ;; EXPS...
         ;; TAIL
         (if (null? exps)
                  (comp-tail (car exps)))
               (comp-drop (car exps)))))
 
-       (($ <ghil-bind> env vars vals body)
+       ((<ghil-bind> env loc vars vals body)
         ;; VALS...
         ;; (set VARS)...
         ;; BODY
         (for-each comp-push vals)
-        (for-each (lambda (var) (push-code! (make-glil-var 'set env var)))
+         (push-bindings! loc vars)
+        (for-each (lambda (var) (push-code! #f (make-glil-var 'set env var)))
                   (reverse vars))
-        (comp-tail body))
+        (comp-tail body)
+        (push-code! #f (make-glil-unbind)))
 
-       (($ <ghil-lambda> env vars rest body)
-        (return-code! (codegen tree)))
+       ((<ghil-mv-bind> env loc producer vars rest body)
+        ;; VALS...
+        ;; (set VARS)...
+        ;; BODY
+         (let ((MV (make-label)))
+           (comp-push producer)
+           (push-code! loc (make-glil-mv-call 0 MV))
+           (push-code! #f (make-glil-const #:obj 1))
+           (push-label! MV)
+           (push-code! #f (make-glil-mv-bind (map var->binding vars) rest))
+           (for-each (lambda (var) (push-code! #f (make-glil-var 'set env var)))
+                     (reverse vars)))
+         (comp-tail body)
+         (push-code! #f (make-glil-unbind)))
+
+       ((<ghil-lambda> env loc vars rest meta body)
+        (return-code! loc (codegen tree)))
 
-       (($ <ghil-inst> inst args)
+       ((<ghil-inline> env loc inline args)
         ;; ARGS...
         ;; (INST NARGS)
-        (for-each comp-push args)
-        (push-code! (make-<glil-call> inst (length args)))
-        (if drop (push-code! *ia-drop*))
-        (if tail (push-code! *ia-return*)))
+         (let ((tail-table '((call . goto/args)
+                             (apply . goto/apply)
+                             (call/cc . goto/cc))))
+           (cond ((and tail (assq-ref tail-table inline))
+                  => (lambda (tail-inst)
+                       (push-call! loc tail-inst args)))
+                 (else
+                  (push-call! loc inline args)
+                  (maybe-drop)
+                  (maybe-return)))))
 
-       (($ <ghil-call> env proc args)
+        ((<ghil-values> env loc values)
+         (cond (tail ;; (lambda () (values 1 2))
+                (push-call! loc 'return/values values))
+               (drop ;; (lambda () (values 1 2) 3)
+                (for-each comp-drop values))
+               (else ;; (lambda () (list (values 10 12) 1))
+                (push-code! #f (make-glil-const #:obj 'values))
+                (push-code! #f (make-glil-call #:inst 'link-now #:nargs 1))
+                (push-code! #f (make-glil-call #:inst 'variable-ref #:nargs 0))
+                (push-call! loc 'call values))))
+                
+        ((<ghil-values*> env loc values)
+         (cond (tail ;; (lambda () (apply values '(1 2)))
+                (push-call! loc 'return/values* values))
+               (drop ;; (lambda () (apply values '(1 2)) 3)
+                (for-each comp-drop values))
+               (else ;; (lambda () (list (apply values '(10 12)) 1))
+                (push-code! #f (make-glil-const #:obj 'values))
+                (push-code! #f (make-glil-call #:inst 'link-now #:nargs 1))
+                (push-code! #f (make-glil-call #:inst 'variable-ref #:nargs 0))
+                (push-call! loc 'apply values))))
+                
+       ((<ghil-call> env loc proc args)
         ;; PROC
         ;; ARGS...
         ;; ([tail-]call NARGS)
         (comp-push proc)
-        (for-each comp-push args)
-        (let ((inst (if tail 'tail-call 'call)))
-          (push-code! (make-<glil-call> inst (length args))))
-        (if drop (push-code! *ia-drop*)))))
+        (push-call! loc (if tail 'goto/args 'call) args)
+        (maybe-drop))
+
+       ((<ghil-mv-call> env loc producer consumer)
+        ;; CONSUMER
+         ;; PRODUCER
+         ;; (mv-call MV)
+         ;; ([tail]-call 1)
+         ;; goto POST
+         ;; MV: [tail-]call/nargs
+         ;; POST: (maybe-drop)
+         (let ((MV (make-label)) (POST (make-label)))
+           (comp-push consumer)
+           (comp-push producer)
+           (push-code! loc (make-glil-mv-call 0 MV))
+           (push-code! loc (make-glil-call (if tail 'goto/args 'call) 1))
+           (cond ((not tail)
+                  (push-branch! #f 'br POST)))
+           (push-label! MV)
+           (push-code! loc (make-glil-call (if tail 'goto/nargs 'call/nargs) 0))
+           (cond ((not tail)
+                  (push-label! POST)
+                  (maybe-drop)))))
+
+        ((<ghil-reified-env> env loc)
+         (return-object! loc (ghil-env-reify env)))))
+
     ;;
     ;; main
-    (match ghil
-      (($ <ghil-lambda> env args rest body)
-       (let* ((vars env.variables)
-             (locs (pick (lambda (v) (eq? v.kind 'local)) vars))
-             (exts (pick (lambda (v) (eq? v.kind 'external)) vars)))
+    (record-case ghil
+      ((<ghil-lambda> env loc vars rest meta body)
+       (let* ((evars (ghil-env-variables env))
+             (locs (pick (lambda (v) (eq? (ghil-var-kind v) 'local)) evars))
+             (exts (pick (lambda (v) (eq? (ghil-var-kind v) 'external)) evars)))
         ;; initialize variable indexes
-        (finalize-index! args)
+        (finalize-index! vars)
         (finalize-index! locs)
         (finalize-index! exts)
+        ;; meta bindings
+         (push-bindings! #f vars)
         ;; export arguments
-        (do ((n 0 (1+ n)) (l args (cdr l)))
+        (do ((n 0 (1+ n))
+             (l vars (cdr l)))
             ((null? l))
           (let ((v (car l)))
-            (if (eq? v.kind 'external)
-                (begin (push-code! (make-<glil-argument> 'ref n))
-                       (push-code! (make-<glil-external> 'set 0 v.index))))))
+            (case (ghil-var-kind v)
+               ((external)
+                (push-code! #f (make-glil-argument 'ref n))
+                (push-code! #f (make-glil-external 'set 0 (ghil-var-index v)))))))
         ;; compile body
         (comp body #t #f)
         ;; create GLIL
-        (make-<glil-asm> (length args) (if rest 1 0) (length locs)
-                         (length exts) (reverse! stack)))))))
+        (let ((vars (make-glil-vars #:nargs (length vars)
+                                     #:nrest (if rest 1 0)
+                                     #:nlocs (length locs)
+                                     #:nexts (length exts))))
+          (make-glil-asm vars meta (reverse! stack))))))))
 
 (define (finalize-index! list)
   (do ((n 0 (1+ n))
        (l list (cdr l)))
       ((null? l))
-    (let ((v (car l))) (set! v.index n))))
+    (let ((v (car l))) (set! (ghil-var-index v) n))))