<dynwind> no longer has "pre" or "post" fields
authorAndy Wingo <wingo@pobox.com>
Sun, 16 Jun 2013 13:06:59 +0000 (15:06 +0200)
committerAndy Wingo <wingo@pobox.com>
Sun, 16 Jun 2013 14:51:28 +0000 (16:51 +0200)
* module/language/tree-il.scm (<tree-il>): Remove pre and post fields
  from <dynwind>.  A dynwind now assumes that in normal entry and exit,
  that the code runs the winders and unwinders using <seq> and
  <let-values> and such things.
  (parse-tree-il, unparse-tree-il, make-tree-il-folder, pre-post-order):
  Adapt <dynwind> users.

* module/language/tree-il/analyze.scm (analyze-lexicals):
* module/language/tree-il/compile-glil.scm (flatten-lambda-case):
* module/language/tree-il/cse.scm (cse):
* module/language/tree-il/debug.scm (verify-tree-il):
* module/language/tree-il/effects.scm (make-effects-analyzer): Adapt.

* module/language/tree-il/peval.scm (peval):
* module/language/tree-il/primitives.scm (*primitive-expand-table*):
  Produce tree-il that calls the winder and unwinder.  Recognize
  singly-valued dynamic-wind expressions.

* test-suite/tests/peval.test ("partial evaluation"): Add tests.

module/language/tree-il.scm
module/language/tree-il/analyze.scm
module/language/tree-il/compile-glil.scm
module/language/tree-il/cse.scm
module/language/tree-il/debug.scm
module/language/tree-il/effects.scm
module/language/tree-il/peval.scm
module/language/tree-il/primitives.scm
test-suite/tests/peval.test

index 580bc6c..b800912 100644 (file)
@@ -46,7 +46,7 @@
             <letrec> letrec? make-letrec letrec-src letrec-in-order? letrec-names letrec-gensyms letrec-vals letrec-body
             <fix> fix? make-fix fix-src fix-names fix-gensyms fix-vals fix-body
             <let-values> let-values? make-let-values let-values-src let-values-exp let-values-body
-            <dynwind> dynwind? make-dynwind dynwind-src dynwind-winder dynwind-pre dynwind-body dynwind-post dynwind-unwinder
+            <dynwind> dynwind? make-dynwind dynwind-src dynwind-winder dynwind-body dynwind-unwinder
             <dynlet> dynlet? make-dynlet dynlet-src dynlet-fluids dynlet-vals dynlet-body
             <dynref> dynref? make-dynref dynref-src dynref-fluid
             <dynset> dynset? make-dynset dynset-src dynset-fluid dynset-exp
 (define-type (<tree-il> #:common-slots (src) #:printer print-tree-il)
   (<fix> names gensyms vals body)
   (<let-values> exp body)
-  (<dynwind> winder pre body post unwinder)
+  (<dynwind> winder body unwinder)
   (<dynref> fluid)
   (<dynset> fluid exp)
   (<prompt> tag body handler)
      (('let-values exp body)
       (make-let-values loc (retrans exp) (retrans body)))
 
-     (('dynwind winder pre body post unwinder)
-      (make-dynwind loc (retrans winder) (retrans pre)
-                    (retrans body)
-                    (retrans post) (retrans unwinder)))
+     (('dynwind winder body unwinder)
+      (make-dynwind loc (retrans winder) (retrans body) (retrans unwinder)))
 
      (('dynlet fluids vals body)
       (make-dynlet loc (map retrans fluids) (map retrans vals) (retrans body)))
     (($ <let-values> src exp body)
      `(let-values ,(unparse-tree-il exp) ,(unparse-tree-il body)))
 
-    (($ <dynwind> src winder pre body post unwinder)
-     `(dynwind ,(unparse-tree-il winder) ,(unparse-tree-il pre)
+    (($ <dynwind> src winder body unwinder)
+     `(dynwind ,(unparse-tree-il winder)
                ,(unparse-tree-il body)
-               ,(unparse-tree-il post) ,(unparse-tree-il unwinder)))
+               ,(unparse-tree-il unwinder)))
 
     (($ <dynlet> src fluids vals body)
      `(dynlet ,(map unparse-tree-il fluids) ,(map unparse-tree-il vals)
               (($ <let-values> src exp body)
                (let*-values (((seed ...) (foldts exp seed ...)))
                  (foldts body seed ...)))
-              (($ <dynwind> src winder pre body post unwinder)
+              (($ <dynwind> src winder body unwinder)
                (let*-values (((seed ...) (foldts winder seed ...))
-                             ((seed ...) (foldts pre seed ...))
-                             ((seed ...) (foldts body seed ...))
-                             ((seed ...) (foldts post seed ...)))
-                 (foldts unwinder seed ...)))
+                             ((seed ...) (foldts unwinder seed ...)))
+                 (foldts body seed ...)))
               (($ <dynlet> src fluids vals body)
                (let*-values (((seed ...) (fold-values foldts fluids seed ...))
                              ((seed ...) (fold-values foldts vals seed ...)))
@@ -531,9 +527,8 @@ This is an implementation of `foldts' as described by Andy Wingo in
        (($ <let-values> src exp body)
         (make-let-values src (lp exp) (lp body)))
 
-       (($ <dynwind> src winder pre body post unwinder)
-        (make-dynwind src
-                      (lp winder) (lp pre) (lp body) (lp post) (lp unwinder)))
+       (($ <dynwind> src winder body unwinder)
+        (make-dynwind src (lp winder) (lp body) (lp unwinder)))
 
        (($ <dynlet> src fluids vals body)
         (make-dynlet src (map lp fluids) (map lp vals) (lp body)))
index aff05d7..84a044c 100644 (file)
       ((<let-values> exp body)
        (lset-union eq? (step exp) (step body)))
       
-      ((<dynwind> winder pre body post unwinder)
-       (lset-union eq? (step winder) (step pre)
-                   (step body)
-                   (step post) (step unwinder)))
+      ((<dynwind> winder body unwinder)
+       (lset-union eq? (step winder) (step body) (step unwinder)))
       
       ((<dynlet> fluids vals body)
        (apply lset-union eq? (step body) (map step (append fluids vals))))
       ((<let-values> exp body)
        (max (recur exp) (recur body)))
       
-      ((<dynwind> winder pre body post unwinder)
-       (max (recur winder) (recur pre)
-            (recur body)
-            (recur post) (recur unwinder)))
+      ((<dynwind> winder body unwinder)
+       (max (recur winder) (recur body) (recur unwinder)))
       
       ((<dynlet> fluids vals body)
        (apply max (recur body) (map recur (append fluids vals))))
index 353bd03..c06a1f6 100644 (file)
             (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 winder pre body post unwinder)
+      ((<dynwind> src winder body unwinder)
        (define (thunk? x)
          (and (lambda? x)
               (null? (lambda-case-gensyms (lambda-body 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.
+       ;; The `winder' and `unwinder' of a dynwind 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 pre)
        (emit-code #f (make-glil-call 'wind 2))
 
        (case context
          ((tail)
           (let ((MV (make-label)))
             (comp-vals body MV)
-            ;; one value: unwind...
+            ;; One value.  Unwind and return the value.
             (emit-code #f (make-glil-call 'unwind 0))
-            (comp-drop post)
-            ;; ...and return the val
             (emit-code #f (make-glil-call 'return 1))
             
             (emit-label MV)
-            ;; multiple values: unwind...
+            ;; Multiple values.  Unwind and return the values.
             (emit-code #f (make-glil-call 'unwind 0))
-            (comp-drop post)
-            ;; and return the values.
             (emit-code #f (make-glil-call 'return/nvalues 1))))
          
          ((push)
-          ;; we only want one value. so ask for one value
+          ;; We only want one value, so ask for one value and then
+          ;; unwind, leaving the value on the stack.
           (comp-push body)
-          ;; and unwind, leaving the val on the stack
-          (emit-code #f (make-glil-call 'unwind 0))
-          (comp-drop post))
+          (emit-code #f (make-glil-call 'unwind 0)))
          
          ((vals)
           (let ((MV (make-label)))
             (comp-vals body MV)
-            ;; one value: push 1 and fall through to MV case
+            ;; Transform a singly-valued return to a multiple-value
+            ;; return and fall through to MV case.
             (emit-code #f (make-glil-const 1))
             
             (emit-label MV)
-            ;; multiple values: unwind...
+            ;; Multiple values: unwind and go to the MVRA.
             (emit-code #f (make-glil-call 'unwind 0))
-            (comp-drop post)
-            ;; and goto the MVRA.
             (emit-branch #f 'br MVRA)))
          
          ((drop)
-          ;; compile body, discarding values. then unwind...
+          ;; Compile body, discarding values.  Then unwind and fall
+          ;; through, or goto RA if there is one.
           (comp-drop body)
           (emit-code #f (make-glil-call 'unwind 0))
-          (comp-drop post)
-          ;; and fall through, or goto RA if there is one.
           (if RA
               (emit-branch #f 'br RA)))))
 
index 9531149..4c50114 100644 (file)
                      ((consumer db**) (visit consumer (concat db* db) env ctx)))
          (return (make-let-values src producer consumer)
                  (concat db** db*))))
-      (($ <dynwind> src winder pre body post unwinder)
+      (($ <dynwind> src winder body unwinder)
        (let*-values (((winder db*) (visit winder db env 'value))
                      ((db**) db*)
                      ((unwinder db*) (visit unwinder db env 'value))
                      ((db**) (concat db* db**))
-                     ((pre db*) (visit pre (concat db** db) env 'effect))
-                     ((db**) (concat db* db**))
                      ((body db*) (visit body (concat db** db) env ctx))
-                     ((db**) (concat db* db**))
-                     ((post db*) (visit post (concat db** db) env 'effect))
                      ((db**) (concat db* db**)))
-         (return (make-dynwind src winder pre body post unwinder)
+         (return (make-dynwind src winder body unwinder)
                  db**)))
       (($ <dynlet> src fluids vals body)
        (let*-values (((fluids db*) (parallel-visit fluids db env 'value))
index 65fd58e..6a3b3dc 100644 (file)
          (for-each (cut visit <> env) fluids)
          (for-each (cut visit <> env) vals)
          (visit body env))))
-      (($ <dynwind> src winder pre body post unwinder)
+      (($ <dynwind> src winder body unwinder)
        (visit winder env)
-       (visit pre env)
        (visit body env)
-       (visit post env)
        (visit unwinder env))
       (($ <dynref> src fluid)
        (visit fluid env))
index b5586e2..b9b34a1 100644 (file)
@@ -211,11 +211,9 @@ of an expression."
            (logior (compute-effects producer)
                    (compute-effects consumer)
                    (cause &type-check)))
-          (($ <dynwind> _ winder pre body post unwinder)
+          (($ <dynwind> _ winder body unwinder)
            (logior (compute-effects winder)
-                   (compute-effects pre)
                    (compute-effects body)
-                   (compute-effects post)
                    (compute-effects unwinder)))
           (($ <dynlet> _ fluids vals body)
            (logior (accumulate-effects fluids)
index 6271143..27da460 100644 (file)
     (($ <conditional> _ test consequent alternate)
      (and (singly-valued-expression? consequent)
           (singly-valued-expression? alternate)))
+    (($ <dynwind> _ winder body unwinder)
+     (singly-valued-expression? body))
     (else #f)))
 
 (define (truncate-values x)
@@ -1000,10 +1002,11 @@ top-level bindings from ENV and return the resulting expression."
                    (else #f))))
                (_ #f))
              (make-let-values lv-src producer (for-tail consumer)))))
-      (($ <dynwind> src winder pre body post unwinder)
-       (make-dynwind src (for-value winder) (for-effect pre)
+      (($ <dynwind> src winder body unwinder)
+       (make-dynwind src
+                     (for-value winder)
                      (for-tail body)
-                     (for-effect post) (for-value unwinder)))
+                     (for-value unwinder)))
       (($ <dynlet> src fluids vals body)
        (make-dynlet src (map for-value fluids) (map for-value vals)
                     (for-tail body)))
@@ -1122,47 +1125,57 @@ top-level bindings from ENV and return the resulting expression."
        (for-tail (make-let-values src (make-call src producer '())
                                   consumer)))
       (($ <primcall> src 'dynamic-wind (w thunk u))
+       (define (with-temporaries exps refcount k)
+         (let* ((pairs (map (match-lambda
+                             ((and exp (? constant-expression?))
+                              (cons #f exp))
+                             (exp
+                              (let ((sym (gensym "tmp ")))
+                                (record-new-temporary! 'tmp sym refcount)
+                                (cons sym exp))))
+                            exps))
+                (tmps (filter car pairs)))
+           (match tmps
+             (() (k exps))
+             (tmps
+              (make-let src
+                        (make-list (length tmps) 'tmp)
+                        (map car tmps)
+                        (map cdr tmps)
+                        (k (map (match-lambda
+                                 ((#f . val) val)
+                                 ((sym . _)
+                                  (make-lexical-ref #f 'tmp sym)))
+                                pairs)))))))
+       (define (make-begin0 src first second)
+         (make-let-values
+          src
+          first
+          (let ((vals (gensym "vals ")))
+            (record-new-temporary! 'vals vals 1)
+            (make-lambda-case
+             #f
+             '() #f 'vals #f '() (list vals)
+             (make-seq
+              src
+              second
+              (make-primcall #f 'apply
+                             (list
+                              (make-primitive-ref #f 'values)
+                              (make-lexical-ref #f 'vals vals))))
+             #f))))
        (for-tail
-        (cond
-         ((not (constant-expression? w))
-          (cond
-           ((not (constant-expression? u))
-            (let ((w-sym (gensym "w ")) (u-sym (gensym "u ")))
-              (record-new-temporary! 'w w-sym 2)
-              (record-new-temporary! 'u u-sym 2)
-              (make-let src '(w u) (list w-sym u-sym) (list w u)
-                        (make-dynwind
-                         src
-                         (make-lexical-ref #f 'w w-sym)
-                         (make-call #f (make-lexical-ref #f 'w w-sym) '())
-                         (make-call #f thunk '())
-                         (make-call #f (make-lexical-ref #f 'u u-sym) '())
-                         (make-lexical-ref #f 'u u-sym)))))
-           (else
-            (let ((w-sym (gensym "w ")))
-              (record-new-temporary! 'w w-sym 2)
-              (make-let src '(w) (list w-sym) (list w)
-                        (make-dynwind
-                         src
-                         (make-lexical-ref #f 'w w-sym)
-                         (make-call #f (make-lexical-ref #f 'w w-sym) '())
-                         (make-call #f thunk '())
-                         (make-call #f u '())
-                         u))))))
-         ((not (constant-expression? u))
-          (let ((u-sym (gensym "u ")))
-            (record-new-temporary! 'u u-sym 2)
-            (make-let src '(u) (list u-sym) (list u)
-                      (make-dynwind
-                       src
-                       w
-                       (make-call #f w '())
-                       (make-call #f thunk '())
-                       (make-call #f (make-lexical-ref #f 'u u-sym) '())
-                       (make-lexical-ref #f 'u u-sym)))))
-         (else
-          (make-dynwind src w (make-call #f w '()) (make-call #f thunk '())
-                        (make-call #f u '()) u)))))
+        (with-temporaries
+         (list w u) 2
+         (match-lambda
+          ((w u)
+           (make-seq src
+                     (make-call src w '())
+                     (make-begin0 src
+                                  (make-dynwind src w
+                                                (make-call src thunk '())
+                                                u)
+                                  (make-call src u '()))))))))
 
       (($ <primcall> src 'values exps)
        (cond
index cbda2db..4a1b98d 100644 (file)
             '@dynamic-wind
             (case-lambda
               ((src pre expr post)
-               (let ((PRE (gensym "pre-"))
-                     (POST (gensym "post-")))
-                 (make-let
-                  src
-                  '(pre post)
-                  (list PRE POST)
-                  (list pre post)
-                  (make-dynwind
-                   src
-                   (make-lexical-ref #f 'pre PRE)
-                   (make-call #f (make-lexical-ref #f 'pre PRE) '())
-                   expr
-                   (make-call #f (make-lexical-ref #f 'post POST) '())
-                   (make-lexical-ref #f 'post POST)))))))
+               (let* ((PRE (gensym "pre-"))
+                      (POST (gensym "post-"))
+                      (winder (make-lexical-ref #f 'winder PRE))
+                      (unwinder (make-lexical-ref #f 'unwinder POST)))
+                 (define (make-begin0 src first second)
+                   (make-let-values
+                    src
+                    first
+                    (let ((vals (gensym "vals ")))
+                      (make-lambda-case
+                       #f
+                       '() #f 'vals #f '() (list vals)
+                       (make-seq
+                        src
+                        second
+                        (make-primcall #f 'apply
+                                       (list
+                                        (make-primitive-ref #f 'values)
+                                        (make-lexical-ref #f 'vals vals))))
+                       #f))))
+                 (make-let src '(pre post) (list PRE POST) (list pre post)
+                           (make-seq src
+                                     (make-call src winder '())
+                                     (make-begin0
+                                      src
+                                      (make-dynwind src winder expr unwinder)
+                                      (make-call src unwinder '()))))))))
 
 (hashq-set! *primitive-expand-table*
             'fluid-ref
index 45e322a..7322d61 100644 (file)
    (seq (call (toplevel random)) (const #t)))
   
   (pass-if-peval
-   ;; Non-constant guards get lexical bindings.
+   ;; Non-constant guards get lexical bindings, invocation of winder and
+   ;; unwinder lifted out.  Unfortunately both have the generic variable
+   ;; name "tmp", so we can't distinguish them in this test, and they
+   ;; also collide in generic names with the single-value result from
+   ;; the dynwind; alack.
    (dynamic-wind foo (lambda () bar) baz)
-   (let (w u) (_ _) ((toplevel foo) (toplevel baz))
-        (dynwind (lexical w _)
-                 (call (lexical w _))
-                 (toplevel bar)
-                 (call (lexical u _))
-                 (lexical u _))))
+   (let (tmp tmp) (_ _) ((toplevel foo) (toplevel baz))
+        (seq (call (lexical tmp _))
+             (let (tmp) (_) ((dynwind (lexical tmp _)
+                                      (toplevel bar)
+                                      (lexical tmp _)))
+                  (seq (call (lexical tmp _))
+                       (lexical tmp _))))))
   
   (pass-if-peval
    ;; Constant guards don't need lexical bindings.
    (dynamic-wind (lambda () foo) (lambda () bar) (lambda () baz))
-   (dynwind
-    (lambda ()
-      (lambda-case
-       ((() #f #f #f () ()) (toplevel foo))))
-    (toplevel foo)
-    (toplevel bar)
-    (toplevel baz)
-    (lambda ()
-      (lambda-case
-       ((() #f #f #f () ()) (toplevel baz))))))
+   (seq (toplevel foo)
+        (let (tmp) (_) ((dynwind (lambda ()
+                                   (lambda-case
+                                    ((() #f #f #f () ()) (toplevel foo))))
+                                 (toplevel bar)
+                                 (lambda ()
+                                   (lambda-case
+                                    ((() #f #f #f () ()) (toplevel baz))))))
+             (seq (toplevel baz)
+                  (lexical tmp _)))))
+  
+  (pass-if-peval
+   ;; Dynwind bodies that return an unknown number of values need a
+   ;; let-values.
+   (dynamic-wind (lambda () foo) (lambda () (bar)) (lambda () baz))
+   (seq (toplevel foo)
+        (let-values (dynwind (lambda ()
+                               (lambda-case
+                                ((() #f #f #f () ()) (toplevel foo))))
+                             (call (toplevel bar))
+                             (lambda ()
+                               (lambda-case
+                                ((() #f #f #f () ()) (toplevel baz)))))
+          (lambda-case
+           ((() #f vals #f () (_))
+            (seq (toplevel baz)
+                 (primcall @apply (primitive values) (lexical vals _))))))))
   
   (pass-if-peval
    ;; Prompt is removed if tag is unreferenced