Merge remote-tracking branch 'origin/stable-2.0'
[bpt/guile.git] / module / language / tree-il / compile-glil.scm
index b588802..1b6fea6 100644 (file)
@@ -1,6 +1,6 @@
 ;;; TREE-IL -> GLIL compiler
 
-;; Copyright (C) 2001,2008,2009,2010 Free Software Foundation, Inc.
+;; Copyright (C) 2001,2008,2009,2010,2011,2012,2013 Free Software Foundation, Inc.
 
 ;;;; This library is free software; you can redistribute it and/or
 ;;;; modify it under the terms of the GNU Lesser General Public
@@ -27,6 +27,7 @@
   #:use-module (system vm instruction)
   #:use-module (language tree-il)
   #:use-module (language tree-il optimize)
+  #:use-module (language tree-il canonicalize)
   #:use-module (language tree-il analyze)
   #:use-module ((srfi srfi-1) #:select (filter-map))
   #:export (compile-glil))
@@ -64,6 +65,7 @@
   (let* ((x (make-lambda (tree-il-src x) '()
                          (make-lambda-case #f '() #f #f #f '() '() x #f)))
          (x (optimize! x e opts))
+         (x (canonicalize! x))
          (allocation (analyze-lexicals x)))
 
     (with-fluids ((*comp-module* e))
    ((list? . 1) . list?)
    ((symbol? . 1) . symbol?)
    ((vector? . 1) . vector?)
+   ((nil? . 1) . nil?)
    (list . list)
    (vector . vector)
    ((class-of . 1) . class-of)
    ((@slot-ref . 2) . slot-ref)
    ((@slot-set! . 3) . slot-set)
+   ((string-length . 1) . string-length)
+   ((string-ref . 2) . string-ref)
+   ((vector-length . 1) . vector-length)
    ((vector-ref . 2) . vector-ref)
    ((vector-set! . 3) . vector-set)
    ((variable-ref . 1) . variable-ref)
 
    ;; hack for javascript
    ((return . 1) . return)
+   ;; hack for lua
+   (return/values . return/values)
 
    ((bytevector-u8-ref . 2) . bv-u8-ref)
    ((bytevector-u8-set! . 3) . bv-u8-set)
          (pmatch (hashq-ref (hashq-ref allocation v) proc)
            ((#t ,boxed? . ,n)
             (list id boxed? n))
-           (,x (error "badness" id v x))))
+           (,x (error "bad var list element" id v x))))
        ids
        vars))
 
        (lambda (emit-code)
          ;; write source info for proc
          (if src (emit-code #f (make-glil-source src)))
-         ;; emit pre-prelude label for self tail calls in which the
-         ;; number of arguments doesn't check out at compile time
-         (if self-label
-             (emit-code #f (make-glil-label self-label)))
          ;; compile the body, yo
-         (flatten body allocation x self-label (car (hashq-ref allocation x))
-                  emit-code)))))))
+         (flatten-lambda-case body allocation x self-label
+                              (car (hashq-ref allocation x))
+                              emit-code)))))))
 
-(define (flatten x allocation self self-label fix-labels emit-code)
+(define (flatten-lambda-case lcase allocation self self-label fix-labels
+                             emit-code)
   (define (emit-label label)
     (emit-code #f (make-glil-label label)))
   (define (emit-branch src inst label)
 
   ;; RA: "return address"; #f unless we're in a non-tail fix with labels
   ;; MVRA: "multiple-values return address"; #f unless we're in a let-values
-  (let comp ((x x) (context 'tail) (RA #f) (MVRA #f))
+  (let comp ((x lcase) (context 'tail) (RA #f) (MVRA #f))
     (define (comp-tail tree) (comp tree context RA MVRA))
     (define (comp-push tree) (comp tree 'push #f #f))
     (define (comp-drop tree) (comp tree 'drop #f #f))
           (if (eq? context 'tail)
               (emit-code #f (make-glil-call 'return 1)))))
     
+    ;; After lexical binding forms in non-tail context, call this
+    ;; function to clear stack slots, allowing their previous values to
+    ;; be collected.
+    (define (clear-stack-slots context syms)
+      (case context
+        ((push drop)
+         (for-each (lambda (v)
+                     (and=>
+                      ;; Can be #f if the var is labels-allocated.
+                      (hashq-ref allocation v)
+                      (lambda (h)
+                        (pmatch (hashq-ref h self)
+                          ((#t _ . ,n)
+                           (emit-code #f (make-glil-void))
+                           (emit-code #f (make-glil-lexical #t #f 'set n)))
+                          (,loc (error "bad let var allocation" x loc))))))
+                   syms))))
+
     (record-case x
       ((<void>)
        (case context
           (emit-code src (make-glil-const exp))))
        (maybe-emit-return))
 
-      ;; FIXME: should represent sequence as exps tail
-      ((<sequence> exps)
-       (let lp ((exps exps))
-         (if (null? (cdr exps))
-             (comp-tail (car exps))
-             (begin
-               (comp-drop (car exps))
-               (lp (cdr exps))))))
-
-      ((<application> src proc args)
-       ;; FIXME: need a better pattern-matcher here
+      ((<seq> head tail)
+       (comp-drop head)
+       (comp-tail tail))
+      
+      ((<call> src proc args)
        (cond
-        ((and (primitive-ref? proc)
-              (eq? (primitive-ref-name proc) '@apply)
-              (>= (length args) 1))
-         (let ((proc (car args))
-               (args (cdr args)))
-           (cond
-            ((and (primitive-ref? proc) (eq? (primitive-ref-name proc) 'values)
-                  (not (eq? context 'push)) (not (eq? context 'vals)))
-             ;; tail: (lambda () (apply values '(1 2)))
-             ;; drop: (lambda () (apply values '(1 2)) 3)
-             ;; push: (lambda () (list (apply values '(10 12)) 1))
-             (case context
-               ((drop) (for-each comp-drop args) (maybe-emit-return))
-               ((tail)
-                (for-each comp-push args)
-                (emit-code src (make-glil-call 'return/values* (length args))))))
-
-            (else
-             (case context
-               ((tail)
-                (comp-push proc)
-                (for-each comp-push args)
-                (emit-code src (make-glil-call 'tail-apply (1+ (length args)))))
-               ((push)
-                (emit-code src (make-glil-call 'new-frame 0))
-                (comp-push proc)
-                (for-each comp-push args)
-                (emit-code src (make-glil-call 'apply (1+ (length args))))
-                (maybe-emit-return))
-               ((vals)
-                (comp-vals
-                 (make-application src (make-primitive-ref #f 'apply)
-                                   (cons proc args))
-                 MVRA)
-                (maybe-emit-return))
-               ((drop)
-                ;; Well, shit. The proc might return any number of
-                ;; values (including 0), since it's in a drop context,
-                ;; yet apply does not create a MV continuation. So we
-                ;; mv-call out to our trampoline instead.
-                (comp-drop
-                 (make-application src (make-primitive-ref #f 'apply)
-                                   (cons proc args)))
-                (maybe-emit-return)))))))
-        
-        ((and (primitive-ref? proc) (eq? (primitive-ref-name proc) 'values)
-              (not (eq? context 'push)))
-         ;; tail: (lambda () (values '(1 2)))
-         ;; drop: (lambda () (values '(1 2)) 3)
-         ;; push: (lambda () (list (values '(10 12)) 1))
-         ;; vals: (let-values (((a b ...) (values 1 2 ...))) ...)
-         (case context
-           ((drop) (for-each comp-drop args) (maybe-emit-return))
-           ((vals)
-            (for-each comp-push args)
-            (emit-code #f (make-glil-const (length args)))
-            (emit-branch src 'br MVRA))
-           ((tail)
-            (for-each comp-push args)
-            (emit-code src (make-glil-call 'return/values (length args))))))
-        
-        ((and (primitive-ref? proc)
-              (eq? (primitive-ref-name proc) '@call-with-values)
-              (= (length args) 2))
-        ;; CONSUMER
-         ;; PRODUCER
-         ;; (mv-call MV)
-         ;; ([tail]-call 1)
-         ;; goto POST
-         ;; MV: [tail-]call/nargs
-         ;; POST: (maybe-drop)
-         (case context
-           ((vals)
-            ;; Fall back.
-            (comp-vals
-             (make-application src (make-primitive-ref #f 'call-with-values)
-                               args)
-             MVRA)
-            (maybe-emit-return))
-           (else
-            (let ((MV (make-label)) (POST (make-label))
-                  (producer (car args)) (consumer (cadr args)))
-              (if (not (eq? context 'tail))
-                  (emit-code src (make-glil-call 'new-frame 0)))
-              (comp-push consumer)
-              (emit-code src (make-glil-call 'new-frame 0))
-              (comp-push producer)
-              (emit-code src (make-glil-mv-call 0 MV))
-              (case context
-                ((tail) (emit-code src (make-glil-call 'tail-call 1)))
-                (else   (emit-code src (make-glil-call 'call 1))
-                        (emit-branch #f 'br POST)))
-              (emit-label MV)
-              (case context
-                ((tail) (emit-code src (make-glil-call 'tail-call/nargs 0)))
-                (else   (emit-code src (make-glil-call 'call/nargs 0))
-                        (emit-label POST)
-                        (if (eq? context 'drop)
-                            (emit-code #f (make-glil-call 'drop 1)))
-                        (maybe-emit-return)))))))
-
-        ((and (primitive-ref? proc)
-              (eq? (primitive-ref-name proc) '@call-with-current-continuation)
-              (= (length args) 1))
-         (case context
-           ((tail)
-            (comp-push (car args))
-            (emit-code src (make-glil-call 'tail-call/cc 1)))
-           ((vals)
-            (comp-vals
-             (make-application
-              src (make-primitive-ref #f 'call-with-current-continuation)
-              args)
-             MVRA)
-            (maybe-emit-return))
-           ((push)
-            (comp-push (car args))
-            (emit-code src (make-glil-call 'call/cc 1))
-            (maybe-emit-return))
-           ((drop)
-            ;; Crap. Just like `apply' in drop context.
-            (comp-drop
-             (make-application
-              src (make-primitive-ref #f 'call-with-current-continuation)
-              args))
-            (maybe-emit-return))))
-
-        ;; A hack for variable-set, the opcode for which takes its args
-        ;; reversed, relative to the variable-set! function
-        ((and (primitive-ref? proc)
-              (eq? (primitive-ref-name proc) 'variable-set!)
-              (= (length args) 2))
-         (comp-push (cadr args))
-         (comp-push (car args))
-         (emit-code src (make-glil-call 'variable-set 2))
-         (case context
-           ((tail push vals) (emit-code #f (make-glil-void))))
-         (maybe-emit-return))
-        
-        ((and (primitive-ref? proc)
-              (or (hash-ref *primcall-ops*
-                            (cons (primitive-ref-name proc) (length args)))
-                  (hash-ref *primcall-ops* (primitive-ref-name proc))))
-         => (lambda (op)
-              (for-each comp-push args)
-              (emit-code src (make-glil-call op (length args)))
-              (case (instruction-pushes op)
-                ((0)
-                 (case context
-                   ((tail push vals) (emit-code #f (make-glil-void))))
-                 (maybe-emit-return))
-                ((1)
-                 (case context
-                   ((drop) (emit-code #f (make-glil-call 'drop 1))))
-                 (maybe-emit-return))
-                (else
-                 (error "bad primitive op: too many pushes"
-                        op (instruction-pushes op))))))
-        
-        ;; self-call in tail position
+        ;; call to the same lambda-case in tail position
         ((and (lexical-ref? proc)
               self-label (eq? (lexical-ref-gensym proc) self-label)
-              (eq? context 'tail))
-         ;; first, evaluate new values, pushing them on the stack
+              (eq? context 'tail)
+              (not (lambda-case-kw lcase))
+              (not (lambda-case-rest lcase))
+              (= (length args)
+                 (+ (length (lambda-case-req lcase))
+                    (or (and=> (lambda-case-opt lcase) length) 0))))
          (for-each comp-push args)
-         (let lp ((lcase (lambda-body self)))
-           (cond
-            ((and (lambda-case? lcase)
-                  (not (lambda-case-kw lcase))
-                  (not (lambda-case-opt lcase))
-                  (not (lambda-case-rest lcase))
-                  (= (length args) (length (lambda-case-req lcase))))
-             ;; we have a case that matches the args; rename variables
-             ;; and goto the case label
-             (for-each (lambda (sym)
-                         (pmatch (hashq-ref (hashq-ref allocation sym) self)
-                           ((#t #f . ,index) ; unboxed
-                            (emit-code #f (make-glil-lexical #t #f 'set index)))
-                           ((#t #t . ,index) ; boxed
-                            ;; new box
-                            (emit-code #f (make-glil-lexical #t #t 'box index)))
-                           (,x (error "what" x))))
-                       (reverse (lambda-case-gensyms lcase)))
-             (emit-branch src 'br (car (hashq-ref allocation lcase))))
-            ((lambda-case? lcase)
-             ;; no match, try next case
-             (lp (lambda-case-alternate lcase)))
-            (else
-             ;; no cases left; shuffle args down and jump before the prelude.
-             (for-each (lambda (i)
-                         (emit-code #f (make-glil-lexical #t #f 'set i)))
-                       (reverse (iota (length args))))
-             (emit-branch src 'br self-label)))))
+         (for-each (lambda (sym)
+                     (pmatch (hashq-ref (hashq-ref allocation sym) self)
+                       ((#t #f . ,index) ; unboxed
+                        (emit-code #f (make-glil-lexical #t #f 'set index)))
+                       ((#t #t . ,index) ; boxed
+                        ;; new box
+                        (emit-code #f (make-glil-lexical #t #t 'box index)))
+                       (,x (error "bad lambda-case arg allocation" x))))
+                   (reverse (lambda-case-gensyms lcase)))
+         (emit-branch src 'br (car (hashq-ref allocation lcase))))
         
         ;; lambda, the ultimate goto
         ((and (lexical-ref? proc)
                             (emit-code #f (make-glil-lexical #t #f 'set index)))
                            ((#t #t . ,index) ; boxed
                             (emit-code #f (make-glil-lexical #t #t 'box index)))
-                           (,x (error "what" x))))
+                           (,x (error "bad lambda-case arg allocation" x))))
                        (reverse (lambda-case-gensyms lcase)))
              (emit-branch src 'br (car (hashq-ref allocation lcase))))
             ((lambda-case? lcase)
          (for-each comp-push args)
          (let ((len (length args)))
            (case context
-             ((tail) (emit-code src (make-glil-call 'tail-call len)))
-             ((push) (emit-code src (make-glil-call 'call len))
+             ((tail) (if (<= len #xff)
+                         (emit-code src (make-glil-call 'tail-call len))
+                         (begin
+                           (comp-push (make-const #f len))
+                           (emit-code src (make-glil-call 'tail-call/nargs 0)))))
+             ((push) (if (<= len #xff)
+                         (emit-code src (make-glil-call 'call len))
+                         (begin
+                           (comp-push (make-const #f len))
+                           (emit-code src (make-glil-call 'call/nargs 0))))
                      (maybe-emit-return))
+             ;; FIXME: mv-call doesn't have a /nargs variant, so it is
+             ;; limited to 255 args.  Can work around it with a
+             ;; trampoline and tail-call/nargs, but it's not so nice.
              ((vals) (emit-code src (make-glil-mv-call len MVRA))
                      (maybe-emit-return))
              ((drop) (let ((MV (make-label)) (POST (make-label)))
                            (emit-branch #f 'br RA)
                            (emit-label POST)))))))))
 
+      ((<primcall> src name args)
+       (pmatch (cons name args)
+         ((@apply ,proc . ,args)
+          (cond
+           ((and (primitive-ref? proc) (eq? (primitive-ref-name proc) 'values)
+                 (not (eq? context 'push)) (not (eq? context 'vals)))
+            ;; tail: (lambda () (apply values '(1 2)))
+            ;; drop: (lambda () (apply values '(1 2)) 3)
+            ;; push: (lambda () (list (apply values '(10 12)) 1))
+            (case context
+              ((drop) (for-each comp-drop args) (maybe-emit-return))
+              ((tail)
+               (for-each comp-push args)
+               (emit-code src (make-glil-call 'return/values* (length args))))))
+
+           (else
+            (case context
+              ((tail)
+               (comp-push proc)
+               (for-each comp-push args)
+               (emit-code src (make-glil-call 'tail-apply (1+ (length args)))))
+              ((push)
+               (emit-code src (make-glil-call 'new-frame 0))
+               (comp-push proc)
+               (for-each comp-push args)
+               (emit-code src (make-glil-call 'apply (1+ (length args))))
+               (maybe-emit-return))
+              (else
+               (comp-tail (make-primcall src 'apply (cons proc args))))))))
+
+         ((values . _)
+          ;; tail: (lambda () (values '(1 2)))
+          ;; drop: (lambda () (values '(1 2)) 3)
+          ;; push: (lambda () (list (values '(10 12)) 1))
+          ;; vals: (let-values (((a b ...) (values 1 2 ...))) ...)
+          (case context
+            ((drop) (for-each comp-drop args) (maybe-emit-return))
+            ((push)
+             (case (length args)
+               ((0)
+                ;; FIXME: This is surely an error.  We need to add a
+                ;; values-mismatch warning pass.
+                (comp-push (make-call src (make-primitive-ref #f 'values)
+                                      '())))
+               (else
+                ;; Taking advantage of unspecified order of evaluation of
+                ;; arguments.
+                (for-each comp-drop (cdr args))
+                (comp-push (car args))
+                (maybe-emit-return))))
+            ((vals)
+             (for-each comp-push args)
+             (emit-code #f (make-glil-const (length args)))
+             (emit-branch src 'br MVRA))
+            ((tail)
+             (for-each comp-push args)
+             (emit-code src (let ((len (length args)))
+                              (if (= len 1)
+                                  (make-glil-call 'return 1)
+                                  (make-glil-call 'return/values len)))))))
+        
+         ((@call-with-values ,producer ,consumer)
+          ;; CONSUMER
+          ;; PRODUCER
+          ;; (mv-call MV)
+          ;; ([tail]-call 1)
+          ;; goto POST
+          ;; MV: [tail-]call/nargs
+          ;; POST: (maybe-drop)
+          (case context
+            ((vals)
+             ;; Fall back.
+             (comp-tail (make-primcall src 'call-with-values args)))
+            (else
+             (let ((MV (make-label)) (POST (make-label)))
+               (if (not (eq? context 'tail))
+                   (emit-code src (make-glil-call 'new-frame 0)))
+               (comp-push consumer)
+               (emit-code src (make-glil-call 'new-frame 0))
+               (comp-push producer)
+               (emit-code src (make-glil-mv-call 0 MV))
+               (case context
+                 ((tail) (emit-code src (make-glil-call 'tail-call 1)))
+                 (else   (emit-code src (make-glil-call 'call 1))
+                         (emit-branch #f 'br POST)))
+               (emit-label MV)
+               (case context
+                 ((tail) (emit-code src (make-glil-call 'tail-call/nargs 0)))
+                 (else   (emit-code src (make-glil-call 'call/nargs 0))
+                         (emit-label POST)
+                         (if (eq? context 'drop)
+                             (emit-code #f (make-glil-call 'drop 1)))
+                         (maybe-emit-return)))))))
+
+         ((@call-with-current-continuation ,proc)
+          (case context
+            ((tail)
+             (comp-push proc)
+             (emit-code src (make-glil-call 'tail-call/cc 1)))
+            ((vals)
+             (comp-vals
+              (make-primcall src 'call-with-current-continuation args)
+              MVRA)
+             (maybe-emit-return))
+            ((push)
+             (comp-push proc)
+             (emit-code src (make-glil-call 'call/cc 1))
+             (maybe-emit-return))
+            ((drop)
+             ;; Fall back.
+             (comp-tail
+              (make-primcall src 'call-with-current-continuation args)))))
+         
+        ;; A hack for variable-set, the opcode for which takes its args
+        ;; reversed, relative to the variable-set! function
+        ((variable-set! ,var ,val)
+         (comp-push val)
+         (comp-push var)
+         (emit-code src (make-glil-call 'variable-set 2))
+         (case context
+           ((tail push vals) (emit-code #f (make-glil-void))))
+         (maybe-emit-return))
+        
+        (else
+         (cond
+          ((or (hash-ref *primcall-ops* (cons name (length args)))
+               (hash-ref *primcall-ops* name))
+           => (lambda (op)
+                (for-each comp-push args)
+                (emit-code src (make-glil-call op (length args)))
+                (case (instruction-pushes op)
+                  ((0)
+                   (case context
+                     ((tail push vals) (emit-code #f (make-glil-void))))
+                   (maybe-emit-return))
+                  ((1)
+                   (case context
+                     ((drop) (emit-code #f (make-glil-call 'drop 1))))
+                   (maybe-emit-return))
+                  ((-1)
+                   ;; A control instruction, like return/values.  Here we
+                   ;; just have to hope that the author of the tree-il
+                   ;; knew what they were doing.
+                   *unspecified*)
+                  (else
+                   (error "bad primitive op: too many pushes"
+                          op (instruction-pushes op))))))
+          (else
+           ;; Fall back to the normal compilation strategy.
+           (comp-tail (make-call src (make-primitive-ref #f name) args)))))))
+
       ((<conditional> src test consequent alternate)
        ;;     TEST
        ;;     (br-if-not L1)
        ;; L1: alternate
        ;; L2:
        (let ((L1 (make-label)) (L2 (make-label)))
-         ;; need a pattern matcher
          (record-case test
-           ((<application> proc args)
-            (record-case proc
-              ((<primitive-ref> name)
-               (let ((len (length args)))
-                 (cond
-
-                  ((and (eq? name 'eq?) (= len 2))
-                   (comp-push (car args))
-                   (comp-push (cadr args))
-                   (emit-branch src 'br-if-not-eq L1))
-
-                  ((and (eq? name 'null?) (= len 1))
-                   (comp-push (car args))
-                   (emit-branch src 'br-if-not-null L1))
-
-                  ((and (eq? name 'not) (= len 1))
-                   (let ((app (car args)))
-                     (record-case app
-                       ((<application> proc args)
-                        (let ((len (length args)))
-                          (record-case proc
-                            ((<primitive-ref> name)
-                             (cond
-
-                              ((and (eq? name 'eq?) (= len 2))
-                               (comp-push (car args))
-                               (comp-push (cadr args))
-                               (emit-branch src 'br-if-eq L1))
-                            
-                              ((and (eq? name 'null?) (= len 1))
-                               (comp-push (car args))
-                               (emit-branch src 'br-if-null L1))
-
-                              (else
-                               (comp-push app)
-                               (emit-branch src 'br-if L1))))
-                            (else
-                             (comp-push app)
-                             (emit-branch src 'br-if L1)))))
-                       (else
-                        (comp-push app)
-                        (emit-branch src 'br-if L1)))))
-                  
-                  (else
-                   (comp-push test)
-                   (emit-branch src 'br-if-not L1)))))
+           ((<primcall> name args)
+            (pmatch (cons name args)
+              ((eq? ,a ,b)
+               (comp-push a)
+               (comp-push b)
+               (emit-branch src 'br-if-not-eq L1))
+              ((null? ,x)
+               (comp-push x)
+               (emit-branch src 'br-if-not-null L1))
+              ((nil? ,x)
+               (comp-push x)
+               (emit-branch src 'br-if-not-nil L1))
+              ((not ,x)
+               (record-case x
+                 ((<primcall> name args)
+                  (pmatch (cons name args)
+                    ((eq? ,a ,b)
+                     (comp-push a)
+                     (comp-push b)
+                     (emit-branch src 'br-if-eq L1))
+                    ((null? ,x)
+                     (comp-push x)
+                     (emit-branch src 'br-if-null L1))
+                    ((nil? ,x)
+                     (comp-push x)
+                     (emit-branch src 'br-if-nil L1))
+                    (else
+                     (comp-push x)
+                     (emit-branch src 'br-if L1))))
+                 (else
+                  (comp-push x)
+                  (emit-branch src 'br-if L1))))
               (else
                (comp-push test)
                (emit-branch src 'br-if-not L1))))
             ((,local? ,boxed? . ,index)
              (emit-code src (make-glil-lexical local? boxed? 'ref index)))
             (,loc
-             (error "badness" x loc)))))
+             (error "bad lexical allocation" x loc)))))
        (maybe-emit-return))
       
       ((<lexical-set> src gensym exp)
          ((,local? ,boxed? . ,index)
           (emit-code src (make-glil-lexical local? boxed? 'set index)))
          (,loc
-          (error "badness" x loc)))
+          (error "bad lexical allocation" x loc)))
        (case context
          ((tail push vals)
           (emit-code #f (make-glil-void))))
                      (pmatch loc
                        ((,local? ,boxed? . ,n)
                         (emit-code #f (make-glil-lexical local? #f 'ref n)))
-                       (else (error "what" x loc))))
+                       (else (error "bad lambda free var allocation" x loc))))
                    free-locs)
                   (emit-code #f (make-glil-call 'make-closure
                                                 (length free-locs))))))))
          (or (= nargs
                 (length gensyms)
                 (+ nreq (length inits) (if rest 1 0)))
-             (error "something went wrong"
+             (error "lambda-case gensyms don't correspond to args"
                     req opt rest kw inits gensyms nreq nopt kw-indices nargs))
          ;; the prelude, to check args & reset the stack pointer,
          ;; allowing room for locals
                   (emit-code #f (make-glil-lexical #t boxed? 'set n))
                   (emit-label L)
                   (lp (cdr inits) (1+ n) (cdr gensyms))))
-               (#t (error "what" inits))))))
+               (#t (error "bad arg allocation" (car gensyms) inits))))))
          ;; post-prelude case label for label calls
          (emit-label (car (hashq-ref allocation x)))
          (comp-tail body)
          (if alternate-label
              (begin
                (emit-label alternate-label)
-               (comp-tail alternate)))))
+               (flatten-lambda-case alternate allocation self self-label
+                                    fix-labels emit-code)))))
       
       ((<let> src names gensyms vals body)
        (for-each comp-push vals)
                       (emit-code src (make-glil-lexical #t #f 'set n)))
                      ((#t #t . ,n)
                       (emit-code src (make-glil-lexical #t #t 'box n)))
-                     (,loc (error "badness" x loc))))
+                     (,loc (error "bad let var allocation" x loc))))
                  (reverse gensyms))
        (comp-tail body)
+       (clear-stack-slots context gensyms)
        (emit-code #f (make-glil-unbind)))
 
       ((<letrec> src in-order? names gensyms vals body)
                    (pmatch (hashq-ref (hashq-ref allocation v) self)
                      ((#t #t . ,n)
                       (emit-code src (make-glil-lexical #t #t 'empty-box n)))
-                     (,loc (error "badness" x loc))))
+                     (,loc (error "bad letrec var allocation" x loc))))
                  gensyms)
        ;; Even though the slots are empty, the bindings are valid.
        (emit-bindings src names gensyms allocation self emit-code)
                        ((#t #t . ,n)
                         (comp-push val)
                         (emit-code src (make-glil-lexical #t #t 'set n)))
-                       (,loc (error "badness" x loc))))
+                       (,loc (error "bad letrec var allocation" x loc))))
                    names gensyms vals))
         (else
          ;; But for letrec, eval all values, then bind.
                      (pmatch (hashq-ref (hashq-ref allocation v) self)
                        ((#t #t . ,n)
                         (emit-code src (make-glil-lexical #t #t 'set n)))
-                       (,loc (error "badness" x loc))))
+                       (,loc (error "bad letrec var allocation" x loc))))
                    (reverse gensyms))))
        (comp-tail body)
+       (clear-stack-slots context gensyms)
        (emit-code #f (make-glil-unbind)))
 
       ((<fix> src names gensyms vals body)
               (pmatch (hashq-ref (hashq-ref allocation v) self)
                 ((#t #f . ,n)
                  (emit-code src (make-glil-lexical #t #f 'set n)))
-                (,loc (error "badness" x loc))))
+                (,loc (error "bad fix var allocation" x loc))))
              (else
               ;; labels allocation: emit label & body, but jump over it
               (let ((POST (make-label)))
                        (pmatch loc
                          ((,local? ,boxed? . ,n)
                           (emit-code #f (make-glil-lexical local? #f 'ref n)))
-                         (else (error "what" x loc))))
+                         (else (error "bad free var allocation" x loc))))
                      free-locs)
                     (pmatch (hashq-ref (hashq-ref allocation v) self)
                       ((#t #f . ,n)
                        (emit-code #f (make-glil-lexical #t #f 'fix n)))
-                      (,loc (error "badness" x loc)))))))
+                      (,loc (error "bad fix var allocation" x loc)))))))
           vals
           gensyms)
          (comp-tail body)
          (if new-RA
              (emit-label new-RA))
+         (clear-stack-slots context gensyms)
          (emit-code #f (make-glil-unbind))))
 
       ((<let-values> src exp body)
                            (emit-code src (make-glil-lexical #t #f 'set n)))
                           ((#t #t . ,n)
                            (emit-code src (make-glil-lexical #t #t 'box n)))
-                          (,loc (error "badness" x loc))))
+                          (,loc (error "bad let-values var allocation" x loc))))
                       (reverse gensyms))
             (comp-tail body)
+            (clear-stack-slots context gensyms)
             (emit-code #f (make-glil-unbind))))))
 
       ;; much trickier than i thought this would be, at first, due to the need
       ;; to have body's return value(s) on the stack while the unwinder runs,
       ;; then proceed with returning or dropping or what-have-you, interacting
       ;; with RA and MVRA. What have you, I say.
-      ((<dynwind> src body winder unwinder)
+      ((<dynwind> src winder pre body post unwinder)
+       (define (thunk? x)
+         (and (lambda? x)
+              (null? (lambda-case-gensyms (lambda-body x)))))
+       (define (make-wrong-type-arg x)
+         (make-primcall src 'scm-error
+                        (list
+                         (make-const #f 'wrong-type-arg)
+                         (make-const #f "dynamic-wind")
+                         (make-const #f "Wrong type (expecting thunk): ~S")
+                         (make-primcall #f 'list (list x))
+                         (make-primcall #f 'list (list x)))))
+       (define (emit-thunk-check x)
+         (comp-drop (make-conditional
+                     src
+                     (make-primcall src 'thunk? (list x))
+                     (make-void #f)
+                     (make-wrong-type-arg x))))
+
+       ;; We know at this point that `winder' and `unwinder' are
+       ;; constant expressions and can be duplicated.
+       (if (not (thunk? winder))
+           (emit-thunk-check winder))
        (comp-push winder)
+       (if (not (thunk? unwinder))
+           (emit-thunk-check unwinder))
        (comp-push unwinder)
-       (comp-drop (make-application src winder '()))
+       (comp-drop pre)
        (emit-code #f (make-glil-call 'wind 2))
 
        (case context
             (comp-vals body MV)
             ;; one value: unwind...
             (emit-code #f (make-glil-call 'unwind 0))
-            (comp-drop (make-application src unwinder '()))
+            (comp-drop post)
             ;; ...and return the val
             (emit-code #f (make-glil-call 'return 1))
             
             (emit-label MV)
             ;; multiple values: unwind...
             (emit-code #f (make-glil-call 'unwind 0))
-            (comp-drop (make-application src unwinder '()))
+            (comp-drop post)
             ;; and return the values.
             (emit-code #f (make-glil-call 'return/nvalues 1))))
          
           (comp-push body)
           ;; and unwind, leaving the val on the stack
           (emit-code #f (make-glil-call 'unwind 0))
-          (comp-drop (make-application src unwinder '())))
+          (comp-drop post))
          
          ((vals)
           (let ((MV (make-label)))
             (emit-label MV)
             ;; multiple values: unwind...
             (emit-code #f (make-glil-call 'unwind 0))
-            (comp-drop (make-application src unwinder '()))
+            (comp-drop post)
             ;; and goto the MVRA.
             (emit-branch #f 'br MVRA)))
          
           ;; compile body, discarding values. then unwind...
           (comp-drop body)
           (emit-code #f (make-glil-call 'unwind 0))
-          (comp-drop (make-application src unwinder '()))
+          (comp-drop post)
           ;; and fall through, or goto RA if there is one.
           (if RA
               (emit-branch #f 'br RA)))))
             ;; post
             (comp-push body)
             (emit-code #f (make-glil-call 'unwind 0))
-            (emit-branch #f 'br POST))
+            (emit-branch #f 'br (or RA POST)))
            
            ((vals)
             (let ((MV (make-label)))
                            (emit-code src (make-glil-lexical #t #f 'set n)))
                           ((#t #t . ,n)
                            (emit-code src (make-glil-lexical #t #t 'box n)))
-                          (,loc (error "badness" x loc))))
+                          (,loc
+                           (error "bad prompt handler arg allocation" x loc))))
                       (reverse gensyms))
             (comp-tail body)
             (emit-code #f (make-glil-unbind))))
 
-         (if (or (eq? context 'push)
-                 (and (eq? context 'drop) (not RA)))
+         (if (and (not RA)
+                  (or (eq? context 'push) (eq? context 'drop)))
              (emit-label POST))))
 
       ((<abort> src tag args tail)