guile-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Guile-commits] 02/03: Beginnings of CPS2 language.


From: Andy Wingo
Subject: [Guile-commits] 02/03: Beginnings of CPS2 language.
Date: Fri, 08 May 2015 11:09:14 +0000

wingo pushed a commit to branch master
in repository guile.

commit 6485e89276b262245251415c26492e2ab677085b
Author: Andy Wingo <address@hidden>
Date:   Fri May 1 13:18:22 2015 +0200

    Beginnings of CPS2 language.
    
    The tentative plan is to replace CPS with CPS2, and to rename CPS2 to
    CPS.  We will add a pass to compile tree-il to CPS2, then work from
    the top down to replace the CPS compiler passes.
    
    * module/language/cps2.scm:
    * module/language/cps2/compile-cps.scm:
    * module/language/cps2/renumber.scm:
    * module/language/cps2/utils.scm: New files.
    
    * module/Makefile.am: Add new files to build.
---
 .dir-locals.el                       |    6 +
 module/Makefile.am                   |    7 +
 module/language/cps2.scm             |  362 ++++++++++++++++++++++++++++++++++
 module/language/cps2/compile-cps.scm |  102 ++++++++++
 module/language/cps2/renumber.scm    |  218 ++++++++++++++++++++
 module/language/cps2/utils.scm       |  228 +++++++++++++++++++++
 6 files changed, 923 insertions(+), 0 deletions(-)

diff --git a/.dir-locals.el b/.dir-locals.el
index 399b8d2..895c112 100644
--- a/.dir-locals.el
+++ b/.dir-locals.el
@@ -21,6 +21,12 @@
      (eval . (put 'rewrite-cps-term    'scheme-indent-function 1))
      (eval . (put 'rewrite-cps-cont    'scheme-indent-function 1))
      (eval . (put 'rewrite-cps-exp     'scheme-indent-function 1))
+     (eval . (put 'build-term          'scheme-indent-function 0))
+     (eval . (put 'build-exp           'scheme-indent-function 0))
+     (eval . (put 'build-cont          'scheme-indent-function 0))
+     (eval . (put 'rewrite-term        'scheme-indent-function 1))
+     (eval . (put 'rewrite-cont        'scheme-indent-function 1))
+     (eval . (put 'rewrite-exp         'scheme-indent-function 1))
      (eval . (put '$letk               'scheme-indent-function 1))
      (eval . (put '$letk*              'scheme-indent-function 1))
      (eval . (put '$letconst           'scheme-indent-function 1))
diff --git a/module/Makefile.am b/module/Makefile.am
index 5f4baae..2a7b9e8 100644
--- a/module/Makefile.am
+++ b/module/Makefile.am
@@ -59,6 +59,7 @@ SOURCES =                                     \
                                                \
   language/tree-il.scm                         \
   $(TREE_IL_LANG_SOURCES)                      \
+  $(CPS2_LANG_SOURCES)                         \
   $(CPS_LANG_SOURCES)                          \
   $(BYTECODE_LANG_SOURCES)                     \
   $(VALUE_LANG_SOURCES)                                \
@@ -147,6 +148,12 @@ CPS_LANG_SOURCES =                                         
\
   language/cps/type-fold.scm                                   \
   language/cps/verify.scm
 
+CPS2_LANG_SOURCES =                                            \
+  language/cps2.scm                                            \
+  language/cps2/compile-cps.scm                                        \
+  language/cps2/renumber.scm                                   \
+  language/cps2/utils.scm
+
 BYTECODE_LANG_SOURCES =                                                \
   language/bytecode.scm                                                \
   language/bytecode/spec.scm
diff --git a/module/language/cps2.scm b/module/language/cps2.scm
new file mode 100644
index 0000000..6476c2d
--- /dev/null
+++ b/module/language/cps2.scm
@@ -0,0 +1,362 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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
+;;;; License as published by the Free Software Foundation; either
+;;;; version 3 of the License, or (at your option) any later version.
+;;;;
+;;;; This library is distributed in the hope that it will be useful,
+;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;;;; Lesser General Public License for more details.
+;;;;
+;;;; You should have received a copy of the GNU Lesser General Public
+;;;; License along with this library; if not, write to the Free Software
+;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 
USA
+
+;;; Commentary:
+;;;
+;;; [Transitional note: CPS2 is a new version of CPS, and is a bit of an
+;;; experiment.  All of the comments in this file pretend that CPS2 will
+;;; replace CPS, and will be named CPS.]
+;;;
+;;; This is the continuation-passing style (CPS) intermediate language
+;;; (IL) for Guile.
+;;;
+;;; In CPS, a term is a labelled expression that calls a continuation.
+;;; A function is a collection of terms.  No term belongs to more than
+;;; one function.  The function is identified by the label of its entry
+;;; term, and its body is composed of those terms that are reachable
+;;; from the entry term.  A program is a collection of functions,
+;;; identified by the entry label of the entry function.
+;;;
+;;; Terms are themselves wrapped in continuations, which specify how
+;;; predecessors may continue to them.  For example, a $kargs
+;;; continuation specifies that the term may be called with a specific
+;;; number of values, and that those values will then be bound to
+;;; lexical variables.  $kreceive specifies that some number of values
+;;; will be passed on the stack, as from a multiple-value return.  Those
+;;; values will be passed to a $kargs, if the number of values is
+;;; compatible with the $kreceive's arity.  $kfun is an entry point to a
+;;; function, and receives arguments according to a well-known calling
+;;; convention (currently, on the stack) and the stack before
+;;; dispatching to a $kclause.  A $kclause is a case-lambda clause, and
+;;; only appears within a $kfun; it checks the incoming values for the
+;;; correct arity and dispatches to a $kargs, or to the next clause.
+;;; Finally, $ktail is the tail continuation for a function, and
+;;; contains no term.
+;;;
+;;; Each continuation has a label that is unique in the program.  As an
+;;; implementation detail, the labels are integers, which allows us to
+;;; easily sort them topologically.  A program is a map from integers to
+;;; continuations, where continuation 0 in the map is the entry point
+;;; for the program, and is a $kfun of no arguments.
+;;;
+;;; $continue nodes call continuations.  The expression contained in the
+;;; $continue node determines the value or values that are passed to the
+;;; target continuation: $const to pass a constant value, $values to
+;;; pass multiple named values, etc.  $continue nodes also record the
+;;; source location corresponding to the expression.
+;;;
+;;; As mentioned above, a $kargs continuation can bind variables, if it
+;;; receives incoming values.  $kfun also binds a value, corresponding
+;;; to the closure being called.  A traditional CPS implementation will
+;;; nest terms in each other, binding them in "let" forms, ensuring that
+;;; continuations are declared and bound within the scope of the values
+;;; that they may use.  In this way, the scope tree is a proof that
+;;; variables are defined before they are used.  However, this proof is
+;;; conservative; it is possible for a variable to always be defined
+;;; before it is used, but not to be in scope:
+;;;
+;;;   (letrec ((k1 (lambda (v1) (k2)))
+;;;            (k2 (lambda () v1)))
+;;;     (k1 0))
+;;;
+;;; This example is invalid, as v1 is used outside its scope.  However
+;;; it would be perfectly fine for k2 to use v1 if k2 were nested inside
+;;; k1:
+;;;
+;;;   (letrec ((k1 (lambda (v1)
+;;;                  (letrec ((k2 (lambda () v1)))
+;;;                    (k2))))
+;;;     (k1 0))
+;;;
+;;; Because program transformation usually uses flow-based analysis,
+;;; having to update the scope tree to manifestly prove a transformation
+;;; that has already proven correct is needless overhead, and in the
+;;; worst case can prevent optimizations from occuring.  For that
+;;; reason, Guile's CPS language does not nest terms.  Instead, we use
+;;; the invariant that definitions must dominate uses.  To check the
+;;; validity of a CPS program is thus more involved than checking for a
+;;; well-scoped tree; you have to do flow analysis to determine a
+;;; dominator tree.  However the flexibility that this grants us is
+;;; worth the cost of throwing away the embedded proof of the scope
+;;; tree.
+;;;
+;;; This particular formulation of CPS was inspired by Andrew Kennedy's
+;;; 2007 paper, "Compiling with Continuations, Continued".  All Guile
+;;; hackers should read that excellent paper!  As in Kennedy's paper,
+;;; continuations are second-class, and may be thought of as basic block
+;;; labels.  All values are bound to variables using continuation calls:
+;;; even constants!
+;;;
+;;; Finally, note that there are two flavors of CPS: higher-order and
+;;; first-order.  By "higher-order", we mean that variables may be free
+;;; across function boundaries.  Higher-order CPS contains $fun and $rec
+;;; expressions that declare functions in the scope of their term.
+;;; Closure conversion results in first-order CPS, where closure
+;;; representations have been explicitly chosen, and all variables used
+;;; in a function are bound.  Higher-order CPS is good for
+;;; interprocedural optimizations like contification and beta reduction,
+;;; while first-order CPS is better for instruction selection, register
+;;; allocation, and code generation.
+;;;
+;;; See (language tree-il compile-cps) for details on how Tree-IL
+;;; converts to CPS.
+;;;
+;;; Code:
+
+(define-module (language cps2)
+  #:use-module (ice-9 match)
+  #:use-module (srfi srfi-9)
+  #:use-module (srfi srfi-9 gnu)
+  #:use-module (srfi srfi-11)
+  #:export (;; Helper.
+            $arity
+            make-$arity
+
+            ;; Continuations.
+            $kreceive $kargs $kfun $ktail $kclause
+
+            ;; Terms.
+            $continue
+
+            ;; Expressions.
+            $const $prim $fun $rec $closure $branch
+            $call $callk $primcall $values $prompt
+
+            ;; Building macros.
+            build-cont build-term build-exp
+            rewrite-cont rewrite-term rewrite-exp
+
+            ;; External representation.
+            parse-cps unparse-cps))
+
+;; FIXME: Use SRFI-99, when Guile adds it.
+(define-syntax define-record-type*
+  (lambda (x)
+    (define (id-append ctx . syms)
+      (datum->syntax ctx (apply symbol-append (map syntax->datum syms))))
+    (syntax-case x ()
+      ((_ name field ...)
+       (and (identifier? #'name) (and-map identifier? #'(field ...)))
+       (with-syntax ((cons (id-append #'name #'make- #'name))
+                     (pred (id-append #'name #'name #'?))
+                     ((getter ...) (map (lambda (f)
+                                          (id-append f #'name #'- f))
+                                        #'(field ...))))
+         #'(define-record-type name
+             (cons field ...)
+             pred
+             (field getter)
+             ...))))))
+
+(define-syntax-rule (define-cps-type name field ...)
+  (begin
+    (define-record-type* name field ...)
+    (set-record-type-printer! name print-cps)))
+
+(define (print-cps exp port)
+  (format port "#<cps ~S>" (unparse-cps exp)))
+
+;; Helper.
+(define-record-type* $arity req opt rest kw allow-other-keys?)
+
+;; Continuations
+(define-cps-type $kreceive arity kbody)
+(define-cps-type $kargs names syms term)
+(define-cps-type $kfun src meta self ktail kclause)
+(define-cps-type $ktail)
+(define-cps-type $kclause arity kbody kalternate)
+
+;; Terms.
+(define-cps-type $continue k src exp)
+
+;; Expressions.
+(define-cps-type $const val)
+(define-cps-type $prim name)
+(define-cps-type $fun body) ; Higher-order.
+(define-cps-type $rec names syms funs) ; Higher-order.
+(define-cps-type $closure label nfree) ; First-order.
+(define-cps-type $branch kt exp)
+(define-cps-type $call proc args)
+(define-cps-type $callk k proc args) ; First-order.
+(define-cps-type $primcall name args)
+(define-cps-type $values args)
+(define-cps-type $prompt escape? tag handler)
+
+(define-syntax build-arity
+  (syntax-rules (unquote)
+    ((_ (unquote exp)) exp)
+    ((_ (req opt rest kw allow-other-keys?))
+     (make-$arity req opt rest kw allow-other-keys?))))
+
+(define-syntax build-cont
+  (syntax-rules (unquote $kreceive $kargs $kfun $ktail $kclause)
+    ((_ (unquote exp))
+     exp)
+    ((_ ($kreceive req rest kargs))
+     (make-$kreceive (make-$arity req '() rest '() #f) kargs))
+    ((_ ($kargs (name ...) (unquote syms) body))
+     (make-$kargs (list name ...) syms (build-term body)))
+    ((_ ($kargs (name ...) (sym ...) body))
+     (make-$kargs (list name ...) (list sym ...) (build-term body)))
+    ((_ ($kargs names syms body))
+     (make-$kargs names syms (build-term body)))
+    ((_ ($kfun src meta self ktail kclause))
+     (make-$kfun src meta self ktail kclause))
+    ((_ ($ktail))
+     (make-$ktail))
+    ((_ ($kclause arity kbody kalternate))
+     (make-$kclause (build-arity arity) kbody kalternate))))
+
+(define-syntax build-term
+  (syntax-rules (unquote $rec $continue)
+    ((_ (unquote exp))
+     exp)
+    ((_ ($continue k src exp))
+     (make-$continue k src (build-exp exp)))))
+
+(define-syntax build-exp
+  (syntax-rules (unquote
+                 $const $prim $fun $rec $closure $branch
+                 $call $callk $primcall $values $prompt)
+    ((_ (unquote exp)) exp)
+    ((_ ($const val)) (make-$const val))
+    ((_ ($prim name)) (make-$prim name))
+    ((_ ($fun kentry)) (make-$fun kentry))
+    ((_ ($rec names gensyms funs)) (make-$rec names gensyms funs))
+    ((_ ($closure k nfree)) (make-$closure k nfree))
+    ((_ ($call proc (unquote args))) (make-$call proc args))
+    ((_ ($call proc (arg ...))) (make-$call proc (list arg ...)))
+    ((_ ($call proc args)) (make-$call proc args))
+    ((_ ($callk k proc (unquote args))) (make-$callk k proc args))
+    ((_ ($callk k proc (arg ...))) (make-$callk k proc (list arg ...)))
+    ((_ ($callk k proc args)) (make-$callk k proc args))
+    ((_ ($primcall name (unquote args))) (make-$primcall name args))
+    ((_ ($primcall name (arg ...))) (make-$primcall name (list arg ...)))
+    ((_ ($primcall name args)) (make-$primcall name args))
+    ((_ ($values (unquote args))) (make-$values args))
+    ((_ ($values (arg ...))) (make-$values (list arg ...)))
+    ((_ ($values args)) (make-$values args))
+    ((_ ($branch kt exp)) (make-$branch kt (build-exp exp)))
+    ((_ ($prompt escape? tag handler))
+     (make-$prompt escape? tag handler))))
+
+(define-syntax-rule (rewrite-cont x (pat cont) ...)
+  (match x
+    (pat (build-cont cont)) ...))
+(define-syntax-rule (rewrite-term x (pat term) ...)
+  (match x
+    (pat (build-term term)) ...))
+(define-syntax-rule (rewrite-exp x (pat body) ...)
+  (match x
+    (pat (build-exp body)) ...))
+
+(define (parse-cps exp)
+  (define (src exp)
+    (let ((props (source-properties exp)))
+      (and (pair? props) props)))
+  (match exp
+    ;; Continuations.
+    (('kreceive req rest k)
+     (build-cont ($kreceive req rest k)))
+    (('kargs names syms body)
+     (build-cont ($kargs names syms ,(parse-cps body))))
+    (('kfun src meta self ktail kclause)
+     (build-cont ($kfun (src exp) meta self ktail kclause)))
+    (('ktail)
+     (build-cont ($ktail)))
+    (('kclause (req opt rest kw allow-other-keys?) kbody)
+     (build-cont ($kclause (req opt rest kw allow-other-keys?) kbody #f)))
+    (('kclause (req opt rest kw allow-other-keys?) kbody kalt)
+     (build-cont ($kclause (req opt rest kw allow-other-keys?) kbody kalt)))
+
+    ;; Calls.
+    (('continue k exp)
+     (build-term ($continue k (src exp) ,(parse-cps exp))))
+    (('unspecified)
+     (build-exp ($const *unspecified*)))
+    (('const exp)
+     (build-exp ($const exp)))
+    (('prim name)
+     (build-exp ($prim name)))
+    (('fun kbody)
+     (build-exp ($fun kbody)))
+    (('closure k nfree)
+     (build-exp ($closure k nfree)))
+    (('rec (name sym fun) ...)
+     (build-exp ($rec name sym (map parse-cps fun))))
+    (('call proc arg ...)
+     (build-exp ($call proc arg)))
+    (('callk k proc arg ...)
+     (build-exp ($callk k proc arg)))
+    (('primcall name arg ...)
+     (build-exp ($primcall name arg)))
+    (('branch k exp)
+     (build-exp ($branch k ,(parse-cps exp))))
+    (('values arg ...)
+     (build-exp ($values arg)))
+    (('prompt escape? tag handler)
+     (build-cps-exp ($prompt escape? tag handler)))
+    (_
+     (error "unexpected cps" exp))))
+
+(define (unparse-cps exp)
+  (match exp
+    ;; Continuations.
+    (($ $kreceive ($ $arity req () rest () #f) k)
+     `(kreceive ,req ,rest ,k))
+    (($ $kargs names syms body)
+     `(kargs ,names ,syms ,(unparse-cps body)))
+    (($ $kfun src meta self ktail kclause)
+     `(kfun ,meta ,self ,ktail ,kclause))
+    (($ $ktail)
+     `(ktail))
+    (($ $kclause ($ $arity req opt rest kw allow-other-keys?) kbody kalternate)
+     `(kclause (,req ,opt ,rest ,kw ,allow-other-keys?) ,kbody
+               . ,(if kalternate (list kalternate) '())))
+
+    ;; Calls.
+    (($ $continue k src exp)
+     `(continue ,k ,(unparse-cps exp)))
+    (($ $const val)
+     (if (unspecified? val)
+         '(unspecified)
+         `(const ,val)))
+    (($ $prim name)
+     `(prim ,name))
+    (($ $fun kbody)
+     `(fun ,kbody))
+    (($ $closure k nfree)
+     `(closure ,k ,nfree))
+    (($ $rec names syms funs)
+     `(rec ,@(map (lambda (name sym fun)
+                    (list name sym (unparse-cps fun)))
+                  names syms funs)))
+    (($ $call proc args)
+     `(call ,proc ,@args))
+    (($ $callk k proc args)
+     `(callk ,k ,proc ,@args))
+    (($ $primcall name args)
+     `(primcall ,name ,@args))
+    (($ $branch k exp)
+     `(branch ,k ,(unparse-cps exp)))
+    (($ $values args)
+     `(values ,@args))
+    (($ $prompt escape? tag handler)
+     `(prompt ,escape? ,tag ,handler))
+    (_
+     (error "unexpected cps" exp))))
diff --git a/module/language/cps2/compile-cps.scm 
b/module/language/cps2/compile-cps.scm
new file mode 100644
index 0000000..f02f760
--- /dev/null
+++ b/module/language/cps2/compile-cps.scm
@@ -0,0 +1,102 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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
+;;;; License as published by the Free Software Foundation; either
+;;;; version 3 of the License, or (at your option) any later version.
+;;;;
+;;;; This library is distributed in the hope that it will be useful,
+;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;;;; Lesser General Public License for more details.
+;;;;
+;;;; You should have received a copy of the GNU Lesser General Public
+;;;; License along with this library; if not, write to the Free Software
+;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 
USA
+
+;;; Commentary:
+;;;
+;;; Compiling CPS2 to CPS.  When/if CPS2 replaces CPS, this module will be 
removed.
+;;;
+;;; Code:
+
+(define-module (language cps2 compile-cps)
+  #:use-module (ice-9 match)
+  #:use-module (language cps2)
+  #:use-module ((language cps) #:prefix cps:)
+  #:use-module (language cps2 utils)
+  #:use-module (language cps2 renumber)
+  #:use-module (language cps intmap)
+  #:export (compile-cps))
+
+;; Precondition: For each function in CONTS, the continuation names are
+;; topologically sorted.
+(define (conts->fun conts)
+  (define (convert-fun kfun)
+    (let ((doms (compute-dom-edges (compute-idoms conts kfun))))
+      (define (visit-cont label)
+        (cps:rewrite-cps-cont (intmap-ref conts label)
+          (($ $kargs names syms body)
+           (label (cps:$kargs names syms ,(redominate label (visit-term 
body)))))
+          (($ $ktail)
+           (label (cps:$ktail)))
+          (($ $kreceive ($ $arity req () rest () #f) kargs)
+           (label (cps:$kreceive req rest kargs)))))
+      (define (visit-clause label)
+        (and label
+             (cps:rewrite-cps-cont (intmap-ref conts label)
+               (($ $kclause ($ $arity req opt rest kw aok?) kbody kalt)
+                (label (cps:$kclause (req opt rest kw aok?)
+                                     ,(visit-cont kbody)
+                                     ,(visit-clause kalt)))))))
+      (define (redominate label term)
+        (define (visit-dom-conts label)
+          (match (intmap-ref conts label)
+            (($ $ktail) '())
+            (($ $kargs) (list (visit-cont label)))
+            (else
+             (cons (visit-cont label)
+                   (visit-dom-conts* (intmap-ref doms label))))))
+        (define (visit-dom-conts* labels)
+          (match labels
+            (() '())
+            ((label . labels)
+             (append (visit-dom-conts label)
+                     (visit-dom-conts* labels)))))
+        (cps:rewrite-cps-term (visit-dom-conts* (intmap-ref doms label))
+          (() ,term)
+          (conts (cps:$letk ,conts ,term))))
+      (define (visit-term term)
+        (cps:rewrite-cps-term term
+          (($ $continue k src (and ($ $fun) fun))
+           (cps:$continue k src ,(visit-fun fun)))
+          (($ $continue k src ($ $rec names syms funs))
+           (cps:$continue k src (cps:$rec names syms (map visit-fun funs))))
+          (($ $continue k src exp)
+           (cps:$continue k src ,(visit-exp exp)))))
+      (define (visit-exp exp)
+        (cps:rewrite-cps-exp exp
+          (($ $const val) (cps:$const val))
+          (($ $prim name) (cps:$prim name))
+          (($ $closure k nfree) (cps:$closure k nfree))
+          (($ $call proc args) (cps:$call proc args))
+          (($ $callk k proc args) (cps:$callk k proc args))
+          (($ $primcall name args) (cps:$primcall name args))
+          (($ $branch k exp) (cps:$branch k ,(visit-exp exp)))
+          (($ $values args) (cps:$values args))
+          (($ $prompt escape? tag handler) (cps:$prompt escape? tag handler))))
+      (define (visit-fun fun)
+        (cps:rewrite-cps-exp fun
+          (($ $fun body)
+           (cps:$fun ,(convert-fun body)))))
+
+      (cps:rewrite-cps-cont (intmap-ref conts kfun)
+        (($ $kfun src meta self tail clause)
+         (kfun (cps:$kfun src meta self (tail (cps:$ktail))
+                 ,(visit-clause clause)))))))
+  (convert-fun 0))
+
+(define (compile-cps exp env opts)
+  (values (conts->fun (renumber exp)) env env))
diff --git a/module/language/cps2/renumber.scm 
b/module/language/cps2/renumber.scm
new file mode 100644
index 0000000..a44f404
--- /dev/null
+++ b/module/language/cps2/renumber.scm
@@ -0,0 +1,218 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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
+;;;; License as published by the Free Software Foundation; either
+;;;; version 3 of the License, or (at your option) any later version.
+;;;;
+;;;; This library is distributed in the hope that it will be useful,
+;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;;;; Lesser General Public License for more details.
+;;;;
+;;;; You should have received a copy of the GNU Lesser General Public
+;;;; License along with this library; if not, write to the Free Software
+;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 
USA
+
+;;; Commentary:
+;;;
+;;; A pass to renumber variables and continuation labels so that they
+;;; are contiguous within each function and, in the case of labels,
+;;; topologically sorted.
+;;;
+;;; Code:
+
+(define-module (language cps2 renumber)
+  #:use-module (ice-9 match)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-11)
+  #:use-module (language cps2)
+  #:use-module (language cps2 utils)
+  #:use-module (language cps intset)
+  #:use-module (language cps intmap)
+  #:export (renumber))
+
+(define* (compute-tail-path-lengths conts kfun preds)
+  (define (add-lengths labels lengths length)
+    (intset-fold (lambda (label lengths)
+                   (intmap-add! lengths label length))
+                 labels
+                 lengths))
+  (define (compute-next labels lengths)
+    (intset-fold (lambda (label labels)
+                   (fold1 (lambda (pred labels)
+                            (if (intmap-ref lengths pred)
+                                labels
+                                (intset-add! labels pred)))
+                          (intmap-ref preds label)
+                          labels))
+                 labels
+                 empty-intset))
+  (define (visit labels lengths length)
+    (let ((lengths (add-lengths labels lengths length)))
+      (values (compute-next labels lengths) lengths (1+ length))))
+  (match (intmap-ref conts kfun)
+    (($ $kfun src meta self tail clause)
+     (worklist-fold2 visit (intset-add empty-intset tail) empty-intmap 0))))
+
+;; Topologically sort the continuation tree starting at k0, using
+;; reverse post-order numbering.
+(define (sort-labels-locally conts k0 path-lengths)
+  (let ((order '())
+        (visited empty-intset))
+    (define (visit k)
+      (define (maybe-visit k)
+        (unless (intset-ref visited k)
+          (visit k)))
+      (define (visit-successors k)
+        (match (intmap-ref conts k)
+          (($ $kargs names syms ($ $continue k src exp))
+           (match exp
+             (($ $prompt escape? tag handler)
+              (maybe-visit handler)
+              (maybe-visit k))
+             (($ $branch kt)
+              ;; Visit the successor with the shortest path length
+              ;; to the tail first, so that if the branches are
+              ;; unsorted, the longer path length will appear
+              ;; first.  This will move a loop exit out of a loop.
+              (let ((k-len (intmap-ref path-lengths k))
+                    (kt-len (intmap-ref path-lengths kt)))
+                (cond
+                 ((if kt-len
+                      (or (not k-len)
+                          (< k-len kt-len)
+                          ;; If the path lengths are the
+                          ;; same, preserve original order
+                          ;; to avoid squirreliness.
+                          (and (= k-len kt-len) (< kt k)))
+                      (if k-len #f (< kt k)))
+                  (maybe-visit k)
+                  (maybe-visit kt))
+                 (else
+                  (maybe-visit kt)
+                  (maybe-visit k)))))
+             (_
+              (maybe-visit k))))
+          (($ $kreceive arity k) (maybe-visit k))
+          (($ $kclause arity kbody kalt)
+           (when kalt (visit kalt))
+           (maybe-visit kbody))
+          (($ $kfun src meta self tail clause)
+           (visit tail)
+           (when clause (visit clause)))
+          (_ #f)))
+
+      ;; Mark this continuation as visited.
+      (set! visited (intset-add! visited k))
+
+      ;; Visit unvisited successors.
+      (visit-successors k)
+
+      ;; Add k to the reverse post-order.
+      (set! order (cons k order)))
+
+    ;; Recursively visit all continuations reachable from k0.
+    (visit k0)
+
+    ;; Return the sorted order.
+    order))
+
+(define (compute-renaming conts kfun)
+  ;; labels := old -> new
+  ;; vars := old -> new
+  (define *next-label* -1)
+  (define *next-var* -1)
+  (define (rename-label label labels)
+    (set! *next-label* (1+ *next-label*))
+    (intmap-add! labels label *next-label*))
+  (define (rename-var sym vars)
+    (set! *next-var* (1+ *next-var*))
+    (intmap-add! vars sym *next-var*))
+  (define (rename label labels vars)
+    (values (rename-label label labels)
+            (match (intmap-ref conts label)
+              (($ $kargs names syms exp)
+               (fold1 rename-var syms vars))
+              (($ $kfun src meta self tail clause)
+               (rename-var self vars))
+              (_ vars))))
+  (define (visit-nested-funs k labels vars)
+    (match (intmap-ref conts k)
+      (($ $kargs names syms ($ $continue k src ($ $fun kfun)))
+       (visit-fun kfun labels vars))
+      (($ $kargs names syms ($ $continue k src ($ $rec names* syms*
+                                                  (($ $fun kfun) ...))))
+       (fold2 visit-fun kfun labels vars))
+      (_ (values labels vars))))
+  (define (visit-fun kfun labels vars)
+    (let* ((preds (compute-predecessors conts kfun))
+           (path-lengths (compute-tail-path-lengths conts kfun preds))
+           (order (sort-labels-locally conts kfun path-lengths)))
+      ;; First rename locally, then recurse on nested functions.
+      (let-values (((labels vars) (fold2 rename order labels vars)))
+        (fold2 visit-nested-funs order labels vars))))
+  (let-values (((labels vars) (visit-fun kfun empty-intmap empty-intmap)))
+    (values (persistent-intmap labels) (persistent-intmap vars))))
+
+(define* (renumber conts #:optional (kfun 0))
+  (let-values (((label-map var-map) (compute-renaming conts kfun)))
+    (define (rename-label label)
+      (or (intmap-ref label-map label) (error "what" label)))
+    (define (rename-var var)
+      (or (intmap-ref var-map var) (error "what2" var)))
+    (define (rename-exp exp)
+      (rewrite-exp exp
+        ((or ($ $const) ($ $prim)) ,exp)
+        (($ $closure k nfree)
+         ($closure (rename-label k) nfree))
+        (($ $fun body)
+         ($fun (rename-label body)))
+        (($ $rec names vars funs)
+         ($rec names (map rename-var vars) (map rename-exp funs)))
+        (($ $values args)
+         ($values ,(map rename-var args)))
+        (($ $call proc args)
+         ($call (rename-var proc) ,(map rename-var args)))
+        (($ $callk k proc args)
+         ($callk (rename-label k) (rename-var proc) ,(map rename-var args)))
+        (($ $branch kt exp)
+         ($branch (rename-label kt) ,(rename-exp exp)))
+        (($ $primcall name args)
+         ($primcall name ,(map rename-var args)))
+        (($ $prompt escape? tag handler)
+         ($prompt escape? (rename-var tag) (rename-label handler)))))
+    (define (rename-arity arity)
+      (match arity
+        (($ $arity req opt rest () aok?)
+         arity)
+        (($ $arity req opt rest kw aok?)
+         (match kw
+           (() arity)
+           (((kw kw-name kw-var) ...)
+            (let ((kw (map list kw kw-name (map rename-var kw-var))))
+              (make-$arity req opt rest kw aok?)))))))
+    (persistent-intmap
+     (intmap-fold
+      (lambda (old-k new-k out)
+        (intmap-add!
+         out
+         new-k
+         (rewrite-cont (intmap-ref conts old-k)
+                       (($ $kargs names syms ($ $continue k src exp))
+                        ($kargs names (map rename-var syms)
+                          ($continue (rename-label k) src ,(rename-exp exp))))
+                       (($ $kreceive ($ $arity req () rest () #f) k)
+                        ($kreceive req rest (rename-label k)))
+                       (($ $ktail)
+                        ($ktail))
+                       (($ $kfun src meta self tail clause)
+                        ($kfun src meta (rename-var self) (rename-label tail)
+                          (and clause (rename-label clause))))
+                       (($ $kclause arity body alternate)
+                        ($kclause ,(rename-arity arity) (rename-label body)
+                                  (and alternate (rename-label alternate)))))))
+      label-map
+      empty-intmap))))
diff --git a/module/language/cps2/utils.scm b/module/language/cps2/utils.scm
new file mode 100644
index 0000000..8ef5f20
--- /dev/null
+++ b/module/language/cps2/utils.scm
@@ -0,0 +1,228 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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
+;;;; License as published by the Free Software Foundation; either
+;;;; version 3 of the License, or (at your option) any later version.
+;;;;
+;;;; This library is distributed in the hope that it will be useful,
+;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;;;; Lesser General Public License for more details.
+;;;;
+;;;; You should have received a copy of the GNU Lesser General Public
+;;;; License along with this library; if not, write to the Free Software
+;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 
USA
+
+;;; Commentary:
+;;;
+;;; Helper facilities for working with CPS.
+;;;
+;;; Code:
+
+(define-module (language cps2 utils)
+  #:use-module (ice-9 match)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-11)
+  #:use-module (language cps2)
+  #:use-module (language cps intset)
+  #:use-module (language cps intmap)
+  #:export (;; Fresh names.
+            label-counter var-counter
+            fresh-label fresh-var
+            with-fresh-name-state compute-max-label-and-var
+            let-fresh
+
+            ;; Various utilities.
+            fold1 fold2
+            intset->intmap
+            worklist-fold worklist-fold2
+            fixpoint
+
+            ;; Flow analysis.
+            compute-predecessors
+            compute-function-body
+            compute-idoms
+            compute-dom-edges
+            ))
+
+(define label-counter (make-parameter #f))
+(define var-counter (make-parameter #f))
+
+(define (fresh-label)
+  (let ((count (or (label-counter)
+                   (error "fresh-label outside with-fresh-name-state"))))
+    (label-counter (1+ count))
+    count))
+
+(define (fresh-var)
+  (let ((count (or (var-counter)
+                   (error "fresh-var outside with-fresh-name-state"))))
+    (var-counter (1+ count))
+    count))
+
+(define-syntax-rule (let-fresh (label ...) (var ...) body ...)
+  (let* ((label (fresh-label)) ...
+         (var (fresh-var)) ...)
+    body ...))
+
+(define-syntax-rule (with-fresh-name-state fun body ...)
+  (call-with-values (lambda () (compute-max-label-and-var fun))
+    (lambda (max-label max-var)
+      (parameterize ((label-counter (1+ max-label))
+                     (var-counter (1+ max-var)))
+        body ...))))
+
+(define (compute-max-label-and-var conts)
+  (values (or (intmap-prev conts) -1)
+          (intmap-fold (lambda (k cont max-var)
+                         (match cont
+                           (($ $kargs names syms body)
+                            (apply max max-var syms))
+                           (($ $kfun src meta self)
+                            (max max-var self))
+                           (_ max-var)))
+                       conts
+                       -1)))
+
+(define-inlinable (fold1 f l s0)
+  (let lp ((l l) (s0 s0))
+    (match l
+      (() s0)
+      ((elt . l) (lp l (f elt s0))))))
+
+(define-inlinable (fold2 f l s0 s1)
+  (let lp ((l l) (s0 s0) (s1 s1))
+    (match l
+      (() (values s0 s1))
+      ((elt . l)
+       (call-with-values (lambda () (f elt s0 s1))
+         (lambda (s0 s1)
+           (lp l s0 s1)))))))
+
+(define (intset->intmap f set)
+  (persistent-intmap
+   (intset-fold (lambda (label preds)
+                  (intmap-add! preds label (f label)))
+                set empty-intmap)))
+
+(define (worklist-fold f in out)
+  (if (eq? in empty-intset)
+      out
+      (call-with-values (lambda () (f in out))
+        (lambda (in out)
+          (worklist-fold f in out)))))
+
+(define (worklist-fold2 f in out0 out1)
+  (if (eq? in empty-intset)
+      (values out0 out1)
+      (call-with-values (lambda () (f in out0 out1))
+        (lambda (in out0 out1)
+          (worklist-fold2 f in out0 out1)))))
+
+(define (fixpoint f x)
+  (let ((x* (f x)))
+    (if (eq? x x*) x* (f x*))))
+
+(define (compute-function-body conts kfun)
+  (persistent-intset
+   (let visit-cont ((label kfun) (labels empty-intset))
+     (cond
+      ((intset-ref labels label) labels)
+      (else
+       (let ((labels (intset-add! labels label)))
+         (match (intmap-ref conts label)
+           (($ $kreceive arity k) (visit-cont k labels))
+           (($ $kfun src meta self ktail kclause)
+            (let ((labels (visit-cont ktail labels)))
+              (if kclause
+                  (visit-cont kclause labels)
+                  labels)))
+           (($ $ktail) labels)
+           (($ $kclause arity kbody kalt)
+            (if kalt
+                (visit-cont kalt (visit-cont kbody labels))
+                (visit-cont kbody labels)))
+           (($ $kargs names syms ($ $continue k src exp))
+            (visit-cont k (match exp
+                            (($ $branch k)
+                             (visit-cont k labels))
+                            (($ $callk k)
+                             (visit-cont k labels))
+                            (($ $prompt escape? tag k)
+                             (visit-cont k labels))
+                            (_ labels)))))))))))
+
+(define* (compute-predecessors conts kfun #:key
+                               (labels (compute-function-body conts kfun)))
+  (define (meet cdr car)
+    (cons car cdr))
+  (define (add-preds label preds)
+    (define (add-pred k preds)
+      (intmap-add! preds k label meet))
+    (match (intmap-ref conts label)
+      (($ $kreceive arity k)
+       (add-pred k preds))
+      (($ $kfun src meta self ktail kclause)
+       (add-pred ktail (if kclause (add-pred kclause preds) preds)))
+      (($ $ktail)
+       preds)
+      (($ $kclause arity kbody kalt)
+       (add-pred kbody (if kalt (add-pred kalt preds) preds)))
+      (($ $kargs names syms ($ $continue k src exp))
+       (add-pred k
+                 (match exp
+                   (($ $branch k) (add-pred k preds))
+                   (($ $prompt _ _ k) (add-pred k preds))
+                   (_ preds))))))
+  (persistent-intmap
+   (intset-fold add-preds labels
+                (intset->intmap (lambda (label) '()) labels))))
+
+;; Precondition: For each function in CONTS, the continuation names are
+;; topologically sorted.
+(define (compute-idoms conts kfun)
+  ;; This is the iterative O(n^2) fixpoint algorithm, originally from
+  ;; Allen and Cocke ("Graph-theoretic constructs for program flow
+  ;; analysis", 1972).  See the discussion in Cooper, Harvey, and
+  ;; Kennedy's "A Simple, Fast Dominance Algorithm", 2001.
+  (let ((preds-map (compute-predecessors conts kfun)))
+    (define (compute-idom idoms preds)
+      (match preds
+        (() -1)
+        ((pred) pred)                   ; Shortcut.
+        ((pred . preds)
+         (define (common-idom d0 d1)
+           ;; We exploit the fact that a reverse post-order is a
+           ;; topological sort, and so the idom of a node is always
+           ;; numerically less than the node itself.
+           (let lp ((d0 d0) (d1 d1))
+             (cond
+              ;; d0 or d1 can be false on the first iteration.
+              ((not d0) d1)
+              ((not d1) d0)
+              ((= d0 d1) d0)
+              ((< d0 d1) (lp d0 (intmap-ref idoms d1)))
+              (else (lp (intmap-ref idoms d0) d1)))))
+         (fold1 common-idom preds pred))))
+    (define (adjoin-idom label preds idoms)
+      (let ((idom (compute-idom idoms preds)))
+        ;; Don't use intmap-add! here.
+        (intmap-add idoms label idom (lambda (old new) new))))
+    (fixpoint (lambda (idoms)
+                (intmap-fold adjoin-idom preds-map idoms))
+              empty-intmap)))
+
+;; Compute a vector containing, for each node, a list of the nodes that
+;; it immediately dominates.  These are the "D" edges in the DJ tree.
+(define (compute-dom-edges idoms)
+  (define (snoc cdr car) (cons car cdr))
+  (intmap-fold (lambda (label idom doms)
+                 (let ((doms (intmap-add! doms label '())))
+                   (cond
+                    ((< idom 0) doms) ;; No edge to entry.
+                    (else (intmap-add! doms idom label snoc)))))
+               idoms
+               empty-intmap))



reply via email to

[Prev in Thread] Current Thread [Next in Thread]