compile-assembly: use file-level constants table
authorAndy Wingo <wingo@pobox.com>
Sun, 8 May 2011 14:37:47 +0000 (16:37 +0200)
committerAndy Wingo <wingo@pobox.com>
Sun, 8 May 2011 14:37:47 +0000 (16:37 +0200)
* module/language/glil/compile-assembly.scm (compile-assembly): Rework
  to handle toplevel-specific code generation here, instead of in
  glil->assembly.  Specifically, here we build a global constant table,
  and arrange for it to be the objtable of the toplevel thunk.

  (compile-program): New helper, compiles a <glil-program> and returns
  just the (load-program ...) form.

  (compile-objtable): New helper, generates assembly to build an object
  table, using some other constants table, and possibly recursing to
  `compile-program' for cached GLIL programs.

  (glil->assembly): Simplify, removing the toplevel? argument, and
  replacing the object alist with an objtable computed in a previous
  pass.  Adapt to the new form of the objtable, and to use
  compile-program and compile-objtable.

module/language/glil/compile-assembly.scm

index 1c352cb..3c30228 100644 (file)
   (open-arity start nreq nopt rest kw (close-arity end arities)))
 
 (define (compile-assembly glil)
-  (receive (code . _)
-      (glil->assembly glil #t '(()) '() '() #f '() -1)
-    (car code)))
-
-(define (glil->assembly glil toplevel? bindings
-                        source-alist label-alist object-alist arities addr)
+  (let* ((all-constants (build-constant-store glil))
+         (prog (compile-program glil all-constants))
+         (len (byte-length prog)))
+    ;; The top objcode thunk.  We're going to wrap this thunk in
+    ;; a thunk -- yo dawgs -- with the goal being to lift all
+    ;; constants up to the top level.  The store forms a DAG, so
+    ;; we can actually build up later elements in terms of
+    ;; earlier ones.
+    ;;
+    (cond
+     ((vlist-null? all-constants)
+      ;; No constants: just emit the inner thunk.
+      prog)
+     (else
+      ;; We have an object store, so write it out, attach it
+      ;; to the inner thunk, and tail call.
+      (receive (tablecode addr) (dump-constants all-constants)
+        (let ((prog (align-program prog addr)))
+          ;; Outer thunk.
+          `(load-program ()
+                         ,(+ (addr+ addr prog)
+                             2          ; for (tail-call 0)
+                             )
+                         #f
+                         ;; Load the table, build the inner
+                         ;; thunk, then tail call.
+                         ,@tablecode
+                         ,@prog
+                         (tail-call 0))))))))
+
+(define (compile-program glil constants)
+  (record-case glil
+    ((<glil-program> meta body)
+     (let lp ((body body) (code '()) (bindings '(())) (source-alist '())
+              (label-alist '()) (arities '()) (addr 0))
+       (cond
+        ((null? body)
+         (let ((code (fold append '() code))
+               (bindings (close-all-bindings bindings addr))
+               (sources (limn-sources (reverse! source-alist)))
+               (labels (reverse label-alist))
+               (arities (reverse (close-arity addr arities)))
+               (len addr))
+           (let* ((meta (make-meta bindings sources arities meta))
+                  (meta-pad (if meta (modulo (- 8 (modulo len 8)) 8) 0)))
+             `(load-program ,labels
+                            ,(+ len meta-pad)
+                            ,meta
+                            ,@code
+                            ,@(if meta
+                                  (make-list meta-pad '(nop))
+                                  '())))))
+        (else
+         (receive (subcode bindings source-alist label-alist arities)
+             (glil->assembly (car body) bindings
+                             source-alist label-alist
+                             constants arities addr)
+           (lp (cdr body) (cons subcode code)
+               bindings source-alist label-alist arities
+               (addr+ addr subcode)))))))))
+
+(define (compile-objtable constants table addr)
+  (define (load-constant idx)
+    (if (< idx 256)
+        (values `((object-ref ,idx))
+                2)
+        (values `((long-object-ref
+                   ,(quotient idx 256) ,(modulo idx 256)))
+                3)))
+  (cond
+   ((vlist-null? table)
+    ;; Empty table; just return #f.
+    (values '((make-false))
+            (1+ addr)))
+   (else
+    (call-with-values
+        (lambda ()
+          (vhash-fold-right2
+           (lambda (obj idx codes addr)
+             (cond
+              ((vhash-assoc obj constants)
+               => (lambda (pair)
+                    (receive (load len) (load-constant (cdr pair))
+                      (values (cons load codes)
+                              (+ addr len)))))
+              ((variable-cache-cell? obj)
+               (cond
+                ((vhash-assoc (variable-cache-cell-key obj) constants)
+                 => (lambda (pair)
+                      (receive (load len) (load-constant (cdr pair))
+                        (values (cons load codes)
+                                (+ addr len)))))
+                (else (error "vcache cell key not in table" obj))))
+              ((glil-program? obj)
+               ;; Programs are not cached in the global constants
+               ;; table because when a program is loaded, its module
+               ;; is bound, and we want to do that only after any
+               ;; preceding effectful statements.
+               (let* ((table (build-object-table obj))
+                      (prog (compile-program obj table)))
+                 (receive (tablecode addr)
+                     (compile-objtable constants table addr)
+                   (let ((prog (align-program prog addr)))
+                     (values (cons `(,@tablecode ,@prog)
+                                   codes)
+                             (addr+ addr prog))))))
+              (else
+               (error "unrecognized constant" obj))))
+           table
+           '(((make-false))) (1+ addr)))
+      (lambda (elts addr)
+        (let ((len (1+ (vlist-length table))))
+          (values
+           (fold append
+                 `((vector ,(quotient len 256) ,(modulo len 256)))
+                 elts)
+           (+ addr 3))))))))
+
+(define (glil->assembly glil bindings source-alist label-alist
+                        constants arities addr)
   (define (emit-code x)
-    (values x bindings source-alist label-alist object-alist arities))
-  (define (emit-code/object x object-alist)
-    (values x bindings source-alist label-alist object-alist arities))
+    (values x bindings source-alist label-alist arities))
+  (define (emit-object-ref i)
+    (values (if (< i 256)
+                `((object-ref ,i))
+                `((long-object-ref ,(quotient i 256) ,(modulo i 256))))
+            bindings source-alist label-alist arities))
   (define (emit-code/arity x nreq nopt rest kw)
-    (values x bindings source-alist label-alist object-alist
+    (values x bindings source-alist label-alist
             (begin-arity addr (addr+ addr x) nreq nopt rest kw arities)))
   
   (record-case glil
     ((<glil-program> meta body)
-     (define (process-body)
-       (let lp ((body body) (code '()) (bindings '(())) (source-alist '())
-                (label-alist '()) (object-alist (if toplevel? #f '()))
-                (arities '()) (addr 0))
-         (cond
-          ((null? body)
-           (values (reverse code)
-                   (close-all-bindings bindings addr)
-                   (limn-sources (reverse! source-alist))
-                   (reverse label-alist)
-                   (and object-alist (map car (reverse object-alist)))
-                   (reverse (close-arity addr arities))
-                   addr))
-          (else
-           (receive (subcode bindings source-alist label-alist object-alist
-                     arities)
-               (glil->assembly (car body) #f bindings
-                               source-alist label-alist object-alist
-                               arities addr)
-             (lp (cdr body) (append (reverse subcode) code)
-                 bindings source-alist label-alist object-alist arities
-                 (addr+ addr subcode)))))))
-
-     (receive (code bindings sources labels objects arities len)
-         (process-body)
-       (let* ((meta (make-meta bindings sources arities meta))
-              (meta-pad (if meta (modulo (- 8 (modulo len 8)) 8) 0))
-              (prog `(load-program ,labels
-                                  ,(+ len meta-pad)
-                                  ,meta
-                                  ,@code
-                                  ,@(if meta
-                                        (make-list meta-pad '(nop))
-                                        '()))))
-         (cond
-          (toplevel?
-           ;; toplevel bytecode isn't loaded by the vm, no way to do
-           ;; object table or closure capture (not in the bytecode,
-           ;; anyway)
-           (emit-code (align-program prog addr)))
-          (else
-           (let ((table (make-object-table objects)))
-             (cond
-              (object-alist
-               ;; if we are being compiled from something with an object
-               ;; table, cache the program there
-               (receive (i object-alist)
-                   (object-index-and-alist (make-subprogram table prog)
-                                           object-alist)
-                 (emit-code/object `(,(if (< i 256)
-                                          `(object-ref ,i)
-                                          `(long-object-ref ,(quotient i 256)
-                                                            ,(modulo i 256))))
-                                   object-alist)))
-              (else
-               ;; otherwise emit a load directly
-               (let ((table-code (dump-object table addr)))
-                 (emit-code
-                  `(,@table-code
-                    ,@(align-program prog (addr+ addr table-code)))))))))))))
+     (cond
+      ((vhash-assoc glil constants)
+       ;; We are cached in someone's objtable; just emit a load.
+       => (lambda (pair)
+            (emit-object-ref (cdr pair))))
+      (else
+       ;; Otherwise, build an objtable for the program, compile it, and
+       ;; emit a load-program.
+       (let* ((table (build-object-table glil))
+              (prog (compile-program glil table)))
+         (receive (tablecode addr) (compile-objtable constants table addr)
+           (emit-code `(,@tablecode ,@(align-program prog addr))))))))
     
     ((<glil-std-prelude> nreq nlocs else-label)
      (emit-code/arity
         nreq nopt rest #f)))
     
     ((<glil-kw-prelude> nreq nopt rest kw allow-other-keys? nlocs else-label)
-     (receive (kw-idx object-alist)
-         (object-index-and-alist kw object-alist)
-       (let* ((bind-required
-               (if else-label
-                   `((br-if-nargs-lt ,(quotient nreq 256)
-                                     ,(modulo nreq 256)
-                                     ,else-label))
-                   `((assert-nargs-ge ,(quotient nreq 256)
-                                      ,(modulo nreq 256)))))
-              (ntotal (apply max (+ nreq nopt) (map 1+ (map cdr kw))))
-              (bind-optionals-and-shuffle
-               `((bind-optionals/shuffle
-                  ,(quotient nreq 256)
-                  ,(modulo nreq 256)
-                  ,(quotient (+ nreq nopt) 256)
-                  ,(modulo (+ nreq nopt) 256)
-                  ,(quotient ntotal 256)
-                  ,(modulo ntotal 256))))
-              (bind-kw
-               ;; when this code gets called, all optionals are filled
-               ;; in, space has been made for kwargs, and the kwargs
-               ;; themselves have been shuffled above the slots for all
-               ;; req/opt/kwargs locals.
-               `((bind-kwargs
-                  ,(quotient kw-idx 256)
-                  ,(modulo kw-idx 256)
-                  ,(quotient ntotal 256)
-                  ,(modulo ntotal 256)
-                  ,(logior (if rest 2 0)
-                           (if allow-other-keys? 1 0)))))
-              (bind-rest
-               (if rest
-                   `((bind-rest ,(quotient ntotal 256)
-                                ,(modulo ntotal 256)
-                                ,(quotient rest 256)
-                                ,(modulo rest 256)))
-                   '())))
+     (let* ((kw-idx (or (and=> (vhash-assoc kw constants) cdr)
+                        (error "kw not in objtable")))
+            (bind-required
+             (if else-label
+                 `((br-if-nargs-lt ,(quotient nreq 256)
+                                   ,(modulo nreq 256)
+                                   ,else-label))
+                 `((assert-nargs-ge ,(quotient nreq 256)
+                                    ,(modulo nreq 256)))))
+            (ntotal (apply max (+ nreq nopt) (map 1+ (map cdr kw))))
+            (bind-optionals-and-shuffle
+             `((bind-optionals/shuffle
+                ,(quotient nreq 256)
+                ,(modulo nreq 256)
+                ,(quotient (+ nreq nopt) 256)
+                ,(modulo (+ nreq nopt) 256)
+                ,(quotient ntotal 256)
+                ,(modulo ntotal 256))))
+            (bind-kw
+             ;; when this code gets called, all optionals are filled
+             ;; in, space has been made for kwargs, and the kwargs
+             ;; themselves have been shuffled above the slots for all
+             ;; req/opt/kwargs locals.
+             `((bind-kwargs
+                ,(quotient kw-idx 256)
+                ,(modulo kw-idx 256)
+                ,(quotient ntotal 256)
+                ,(modulo ntotal 256)
+                ,(logior (if rest 2 0)
+                         (if allow-other-keys? 1 0)))))
+            (bind-rest
+             (if rest
+                 `((bind-rest ,(quotient ntotal 256)
+                              ,(modulo ntotal 256)
+                              ,(quotient rest 256)
+                              ,(modulo rest 256)))
+                 '())))
          
-         (let ((code `(,@bind-required
-                       ,@bind-optionals-and-shuffle
-                       ,@bind-kw
-                       ,@bind-rest
-                       (reserve-locals ,(quotient nlocs 256)
-                                       ,(modulo nlocs 256)))))
-           (values code bindings source-alist label-alist object-alist
-                   (begin-arity addr (addr+ addr code) nreq nopt rest
-                                (and kw (cons allow-other-keys? kw))
-                                arities))))))
+       (let ((code `(,@bind-required
+                     ,@bind-optionals-and-shuffle
+                     ,@bind-kw
+                     ,@bind-rest
+                     (reserve-locals ,(quotient nlocs 256)
+                                     ,(modulo nlocs 256)))))
+         (values code bindings source-alist label-alist
+                 (begin-arity addr (addr+ addr code) nreq nopt rest
+                              (and kw (cons allow-other-keys? kw))
+                              arities)))))
     
     ((<glil-bind> vars)
      (values '()
              (open-binding bindings vars addr)
              source-alist
              label-alist
-             object-alist
              arities))
 
     ((<glil-mv-bind> vars rest)
                  bindings
                  source-alist
                  label-alist
-                 object-alist
                  arities)
          (values `((truncate-values ,(length vars) ,(if rest 1 0)))
                  (open-binding bindings vars addr)
                  source-alist
                  label-alist
-                 object-alist
                  arities)))
     
     ((<glil-unbind>)
              (close-binding bindings addr)
              source-alist
              label-alist
-             object-alist
              arities))
              
     ((<glil-source> props)
              bindings
              (acons addr props source-alist)
              label-alist
-             object-alist
              arities))
 
     ((<glil-void>)
       ((object->assembly obj)
        => (lambda (code)
             (emit-code (list code))))
-      ((not object-alist)
-       (emit-code (dump-object obj addr)))
-      (else
-       (receive (i object-alist)
-           (object-index-and-alist obj object-alist)
-         (emit-code/object (if (< i 256)
-                               `((object-ref ,i))
-                               `((long-object-ref ,(quotient i 256)
-                                                  ,(modulo i 256))))
-                           object-alist)))))
+      ((vhash-assoc obj constants)
+       => (lambda (pair)
+            (emit-object-ref (cdr pair))))
+      (else (error "const not in table" obj))))
 
     ((<glil-lexical> local? boxed? op index)
      (emit-code
      (case op
        ((ref set)
         (cond
-         ((not object-alist)
-          (emit-code `(,@(dump-object name addr)
-                       (link-now)
-                       ,(case op 
-                          ((ref) '(variable-ref))
-                          ((set) '(variable-set))))))
+         ((and=> (vhash-assoc (make-variable-cache-cell name) constants)
+                 cdr)
+          => (lambda (i)
+               (emit-code (if (< i 256)
+                              `((,(case op
+                                    ((ref) 'toplevel-ref)
+                                    ((set) 'toplevel-set))
+                                 ,i))
+                              `((,(case op
+                                    ((ref) 'long-toplevel-ref)
+                                    ((set) 'long-toplevel-set))
+                                 ,(quotient i 256)
+                                 ,(modulo i 256)))))))
          (else
-          (receive (i object-alist)
-              (object-index-and-alist (make-variable-cache-cell name)
-                                      object-alist)
-            (emit-code/object (if (< i 256)
-                                  `((,(case op
-                                        ((ref) 'toplevel-ref)
-                                        ((set) 'toplevel-set))
-                                     ,i))
-                                  `((,(case op
-                                        ((ref) 'long-toplevel-ref)
-                                        ((set) 'long-toplevel-set))
-                                     ,(quotient i 256)
-                                     ,(modulo i 256))))
-                              object-alist)))))
+          (let ((i (or (and=> (vhash-assoc name constants) cdr)
+                       (error "toplevel name not in objtable" name))))
+            (emit-code `(,(if (< i 256)
+                              `(object-ref ,i)
+                              `(long-object-ref ,(quotient i 256)
+                                                ,(modulo i 256)))
+                         (link-now)
+                         ,(case op
+                            ((ref) '(variable-ref))
+                            ((set) '(variable-set)))))))))
        ((define)
-        (emit-code `(,@(dump-object name addr)
-                     (define))))
+        (let ((i (or (and=> (vhash-assoc name constants) cdr)
+                     (error "toplevel name not in objtable" name))))
+          (emit-code `(,(if (< i 256)
+                            `(object-ref ,i)
+                            `(long-object-ref ,(quotient i 256)
+                                              ,(modulo i 256)))
+                       (define)))))
        (else
         (error "unknown toplevel var kind" op name))))
 
      (let ((key (list mod name public?)))
        (case op
          ((ref set)
-          (cond
-           ((not object-alist)
-            (emit-code `(,@(dump-object key addr)
-                         (link-now)
-                         ,(case op 
-                            ((ref) '(variable-ref))
-                            ((set) '(variable-set))))))
-           (else
-            (receive (i object-alist)
-                (object-index-and-alist (make-variable-cache-cell key)
-                                        object-alist)
-              (emit-code/object (case op
-                                  ((ref) `((toplevel-ref ,i)))
-                                  ((set) `((toplevel-set ,i))))
-                                object-alist)))))
+          (let ((i (or (and=> (vhash-assoc (make-variable-cache-cell key)
+                                           constants) cdr)
+                       (error "module vcache not in objtable" key))))
+            (emit-code (if (< i 256)
+                           `((,(case op
+                                 ((ref) 'toplevel-ref)
+                                 ((set) 'toplevel-set))
+                              ,i))
+                           `((,(case op
+                                 ((ref) 'long-toplevel-ref)
+                                 ((set) 'long-toplevel-set))
+                              ,(quotient i 256)
+                              ,(modulo i 256)))))))
          (else
           (error "unknown module var kind" op key)))))
 
                bindings
                source-alist
                (acons label (addr+ addr code) label-alist)
-               object-alist
                arities)))
 
     ((<glil-branch> inst label)