~ chicken-core (chicken-5) bae7d92d9d1d17330b43fba4e278159c7d0d7fc1


commit bae7d92d9d1d17330b43fba4e278159c7d0d7fc1
Author:     felix <felix@call-with-current-continuation.org>
AuthorDate: Sat Oct 15 23:41:38 2011 +0200
Commit:     Christian Kellermann <ckeen@pestilenz.org>
CommitDate: Fri Nov 11 15:02:30 2011 +0100

    clustering optimization added
    
    Signed-off-by: Christian Kellermann <ckeen@pestilenz.org>

diff --git a/batch-driver.scm b/batch-driver.scm
index 1bd4f678..cd1cd609 100644
--- a/batch-driver.scm
+++ b/batch-driver.scm
@@ -85,6 +85,7 @@
 	 (and-let* ((pn (memq 'profile-name options))) (cadr pn)))
 	(hsize (memq 'heap-size options))
 	(kwstyle (memq 'keyword-style options))
+        (loop/dispatch (memq 'clustering options))
 	(uses-units '())
 	(uunit (memq 'unit options))
 	(a-only (memq 'analyze-only options))
@@ -573,8 +574,11 @@
 		 (print-node "cps" '|3| node1)
 
 		 ;; Optimization loop:
-		 (let loop ([i 1] [node2 node1] [progress #t])
-
+		 (let loop ((i 1)
+			    (node2 node1)
+			    (progress #t)
+			    (l/d #f)
+			    (l/d-done #f))
 		   (begin-time)
 		   (let ([db (analyze 'opt node2 i progress)])
 		     (when first-analysis
@@ -592,29 +596,42 @@
 		     (end-time "analysis")
 		     (print-db "analysis" '|4| db i)
 
-		     (when (memq 's debugging-chicken) (print-program-statistics db))
+		     (when (memq 's debugging-chicken) 
+		       (print-program-statistics db))
 
 		     (cond (progress
 			    (debugging 'p "optimization pass" i)
 			    (begin-time)
 			    (receive (node2 progress-flag)
-				(perform-high-level-optimizations node2 db)
+				(if l/d
+				    (determine-loop-and-dispatch node2 db)
+				    (perform-high-level-optimizations node2 db))
 			      (end-time "optimization")
 			      (print-node "optimized-iteration" '|5| node2)
-			      (cond [progress-flag (loop (add1 i) node2 #t)]
-				    [(not inline-substitutions-enabled)
-				     (debugging 'p "rewritings enabled...")
+			      (cond (progress-flag
+				     (loop (add1 i) node2 #t #f l/d))
+				    ((and (not l/d-done) loop/dispatch)
+				     (debugging 'p "clustering enabled")
+				     (loop (add1 i) node2 #t #t #t))
+				    ((not inline-substitutions-enabled)
+				     (debugging 'p "rewritings enabled")
 				     (set! inline-substitutions-enabled #t)
-				     (loop (add1 i) node2 #t) ]
-				    [optimize-leaf-routines
+				     (loop (add1 i) node2 #t #f l/d-done) )
+				    (optimize-leaf-routines
 				     (begin-time)
 				     (let ([db (analyze 'leaf node2)])
 				       (end-time "analysis")
 				       (begin-time)
-				       (let ([progress (transform-direct-lambdas! node2 db)])
+				       (let ((progress
+					      (transform-direct-lambdas! node2 db)))
 					 (end-time "leaf routine optimization")
-					 (loop (add1 i) node2 progress) ) ) ]
-				    [else (loop (add1 i) node2 #f)] ) ) )
+					 (loop (add1 i) 
+					       node2
+					       progress
+					       #f
+					       l/d-done) ) ) )
+				    (else
+				     (loop (add1 i) node2 #f #f l/d-done)) ) ) )
 			   
 			   (else
 			    (print-node "optimized" '|7| node2)
diff --git a/c-platform.scm b/c-platform.scm
index 5d69bcdf..fbe77ac1 100644
--- a/c-platform.scm
+++ b/c-platform.scm
@@ -91,7 +91,7 @@
     no-procedure-checks-for-toplevel-bindings module
     no-bound-checks no-procedure-checks-for-usual-bindings no-compiler-syntax
     no-parentheses-synonyms no-symbol-escape r5rs-syntax emit-all-import-libraries
-    strict-types
+    strict-types clustering
     lambda-lift				; OBSOLETE
     setup-mode unboxing no-module-registration) )
 
diff --git a/chicken.scm b/chicken.scm
index 4aa066bb..538fafac 100644
--- a/chicken.scm
+++ b/chicken.scm
@@ -88,6 +88,7 @@
 		      (cons* 'optimize-leaf-routines
 			     'inline
 			     'unboxing
+			     ;XXX 'clustering
 			     options)) ) 
 		   ((3)
 		    (set! options
@@ -95,6 +96,7 @@
 			     'inline
 			     'inline-global
 			     'unboxing 'local
+			     ;XXX 'clustering
 			     'specialize
 			     options) ) )
 		   ((4)
@@ -104,6 +106,7 @@
 			     'inline-global
 			     'unboxing 
 			     'specialize
+			     ;XXX 'clustering
 			     'local 'unsafe
 			     options) ) )
 		   (else
@@ -120,6 +123,7 @@
 			       'no-lambda-info
 			       'inline
 			       'inline-global
+			       ;XXX 'clustering
 			       'unboxing
 			       options) ) ) ) )
 		 (loop (cdr rest)) ) )
diff --git a/compiler-namespace.scm b/compiler-namespace.scm
index 819aae11..9cf865f8 100644
--- a/compiler-namespace.scm
+++ b/compiler-namespace.scm
@@ -78,6 +78,7 @@
  debugging
  debugging-chicken
  debugging-executable
+ determine-loop-and-dispatch
  decompose-lambda-list
  default-debugging-declarations
  default-declarations
diff --git a/csc.scm b/csc.scm
index 8afbdf63..b5da715a 100644
--- a/csc.scm
+++ b/csc.scm
@@ -139,7 +139,7 @@
     -no-argc-checks -no-bound-checks -no-procedure-checks -no-compiler-syntax
     -emit-all-import-libraries -setup-mode -unboxing -no-elevation -no-module-registration
     -no-procedure-checks-for-usual-bindings -module
-    -specialize -strict-types
+    -specialize -strict-types -clustering
     -lambda-lift			; OBSOLETE
     -no-procedure-checks-for-toplevel-bindings))
 
@@ -399,6 +399,8 @@ Usage: #{csc} FILENAME | OPTION ...
                                    disable procedure call checks for toplevel
                                     bindings
     -strict-types                  assume variable do not change their type
+    -clustering                    combine groups of local procedures into dispatch
+                                     loop
 
   Configuration options:
 
diff --git a/manual/Using the compiler b/manual/Using the compiler
index 9d9253e8..c99f9984 100644
--- a/manual/Using the compiler	
+++ b/manual/Using the compiler	
@@ -20,7 +20,7 @@ on the command line for a list of options.
 be compiled. A filename argument of {{-}} specifies that
 the source text should be read from standard input.
 
-==== Basic command-line options
+==== Command-line options
 
 ; -analyze-only : Stop compilation after first analysis pass.
 
@@ -30,6 +30,8 @@ the source text should be read from standard input.
 
 ; -check-syntax : Aborts compilation process after macro-expansion and syntax checks.
 
+; -clustering : Combine groups of local procedures into dispatch-loops, if possible.
+
 ; -consult-inline-file FILENAME : load file with definitions for cross-module inlining generated by a previous compiloer invocation via {{-emit-inline-file}}. Implies {{-inline}}.
 
 ; -debug MODES : Enables one or more compiler debugging modes. {{MODES}} is a string of characters that select debugging information about the compiler that will be printed to standard output. Use {{-debug h}} to see a list of available debugging options.
diff --git a/optimizer.scm b/optimizer.scm
index 40974dda..aa5f7fd8 100644
--- a/optimizer.scm
+++ b/optimizer.scm
@@ -1526,3 +1526,208 @@
     (debugging 'p "direct leaf routine optimization pass...")
     (walk #f node #f)
     dirty) )
+
+
+;;; turn groups of local procedures into dispatch loop ("clustering")
+;
+; This turns (in bodies)
+;
+;   :
+;   (define (a x) (b x))
+;   (define (b y) (a y))
+;   (a z)))
+;
+; into something similar to
+;
+;   (letrec ((<dispatch>
+;              (lambda (<a1> <i>)
+;                (case <i>
+;                  ((1) (let ((x <a1>)) (<dispatch> x 2)))
+;                  ((2) (let ((y <a1>)) (<dispatch> y 1)))
+;                  (else (<dispatch> z 1))))))
+;     (<dispatch> #f 0))
+
+(define (determine-loop-and-dispatch node db)
+  (let ((groups '())
+	(outer #f)
+	(group '()))
+
+    (define (close)			; "close" group of local definitions
+      (when (pair? group)
+	(when (> (length group) 1)
+	  (set! groups (alist-cons outer group groups)))
+	(set! group '())
+	(set! outer #f)))
+
+    (define (user-lambda? n)
+      (and (eq? '##core#lambda (node-class n))
+	   (list? (third (node-parameters n))))) ; no rest argument allowed
+
+    (define (walk n e)
+      (let ((subs (node-subexpressions n))
+	    (params (node-parameters n)) 
+	    (class (node-class n)) )
+	(case class
+	  ((let)
+	   (let ((var (first params))
+		 (val (first subs))
+		 (body (second subs)))
+	     (cond ((and (not outer) 
+			 (eq? '##core#undefined (node-class val)))
+		    ;; find outermost "(let ((VAR (##core#undefined))) ...)"
+		    (set! outer n)
+		    (walk body (cons var e)))
+		   ((and outer
+			 (eq? 'set! (node-class val))
+			 (let ((sval (first (node-subexpressions val)))
+			       (svar (first (node-parameters val))))
+			   ;;XXX should we also accept "##core#direct_lambda" ?
+			   (and (eq? '##core#lambda (node-class sval))
+				(= (length (or (get db svar 'references) '()))
+				   (length (or (get db svar 'call-sites) '())))
+				(memq svar e)
+				(user-lambda? sval))))
+		    ;; "(set! VAR (lambda ...))" - add to group
+		    (set! group (cons val group))
+		    (walk body (cons var e)))
+		   (else
+		    ;; other "let" binding, close group (if any)
+		    (close)
+		    (walk val e)
+		    (walk body (cons var e))))))
+	  ((##core#lambda ##core#direct_lambda)
+	   (decompose-lambda-list
+	    (third params)
+	    (lambda (vars argc rest)
+	      ;; walk recursively, with cleared cluster state
+	      (fluid-let ((group '())
+			  (outer #f))
+		(walk (first subs) vars)))))
+	  (else
+	   ;; other form, close group (if any)
+	   (close)
+	   (for-each (cut walk <> e) subs)))))
+
+    (debugging 'p "collecting clusters ...")
+
+    ;; walk once and gather groups
+    (walk node '())
+
+    ;; process found clusters
+    (for-each
+     (lambda (g)
+       (let* ((outer (car g))
+	      (group (cdr g))
+	      (dname (gensym 'dispatch))
+	      (i (gensym 'i))
+	      (n 1)
+	      (bodies
+	       (map (lambda (assign)
+		      ;; collect information and replace assignment
+		      ;; with "(##core#undefined)"
+		      (let* ((name (first (node-parameters assign)))
+			     (proc (first (node-subexpressions assign)))
+			     (pparams (node-parameters proc))
+			     (llist (third pparams))
+			     (aliases (map gensym llist)))
+			(decompose-lambda-list
+			 llist
+			 (lambda (vars argc rest)
+			   (let ((body (first (node-subexpressions proc)))
+				 (m n))
+			     (set! n (add1 n))
+			     (copy-node!
+			      (make-node '##core#undefined '() '())
+			      assign)
+			     (list name m llist body))))))
+		    group))
+	      (k (gensym 'k))
+	      (maxargs (apply max (map (o length third) bodies)))
+	      (dllist (append
+		       (list-tabulate maxargs (lambda _ (gensym 'a)))
+		       (list i))))
+
+	 (debugging 'x "clustering" (map first bodies)) ;XXX
+
+	 ;; first descend into "(let ((_ (##core#undefined))) ...)" forms
+	 ;; to make them visible everywhere
+
+	 (let descend ((outer outer))
+	   ;;(print "outer: " (node-parameters outer))
+	   (let ((body (second (node-subexpressions outer))))
+	     (if (and (eq? 'let (node-class body))
+		      (let ((val (first (node-subexpressions body))))
+			(eq? '##core#undefined (node-class val))))
+		 (descend body)
+		 ;; wrap cluster into dispatch procedure
+		 (copy-node!
+		  (make-node
+		   'let
+		   (list dname)
+		   (list
+		    (make-node '##core#undefined '() '())
+		    (make-node
+		     'let (list (gensym))
+		     (list
+		      (make-node 
+		       'set! (list dname)
+		       (list
+			(make-node
+			 '##core#lambda
+			 (list (gensym 'f_) #t dllist 0)
+			 (list
+			  ;; dispatch to cluster member or main body
+			  (make-node
+			   '##core#switch
+			   (list (sub1 n))
+			   (append
+			    (list (varnode i))
+			    (append-map
+			     (lambda (b)
+			       (list (qnode (second b))
+				     (let loop ((args dllist)
+						(vars (third b)))
+				       (if (null? vars)
+					   (fourth b)
+					   (make-node
+					    'let (list (car vars))
+					    (list (varnode (car args))
+						  (loop (cdr args) (cdr vars))))))))
+			     bodies)
+			    (cdr (node-subexpressions outer))))))))
+		      ;; call to enter dispatch loop - the current continuation is
+		      ;; not used, so the first parameter is passed as "#f" (it is
+		      ;; a tail call)
+		      (make-node
+		       '##core#call '(#t)
+		       (cons* (varnode dname)
+			      (append
+			       (list-tabulate maxargs (lambda _ (qnode #f)))
+			       (list (qnode 0)))))))))
+		  outer))))
+
+	 ;; modify call-sites to invoke dispatch loop instead
+	 (for-each
+	  (lambda (b)
+	    (let ((sites (get db (car b) 'call-sites)))
+	      (for-each
+	       (lambda (site)
+		 (let* ((callnode (cdr site))
+			(args (cdr (node-subexpressions callnode))))
+		   (copy-node!
+		    (make-node
+		     '##core#call (node-parameters callnode)
+		     (cons* (varnode dname)
+			    (append
+			     args
+			     (list-tabulate
+			      (- maxargs (length args))
+			      (lambda _ (qnode #f)))
+			     (list (qnode (second b))))))
+		    callnode)))
+	       sites)))
+	  bodies)))
+
+     groups)
+    (values node (pair? groups))))
+
diff --git a/support.scm b/support.scm
index 8bf2b6d0..97ba03ab 100644
--- a/support.scm
+++ b/support.scm
@@ -434,6 +434,7 @@
 		 (inline-target . ilt) (inline-transient . itr)
 		 (undefined . und) (replacing . rpg) (unused . uud) (extended-binding . xtb)
 		 (inline-export . ilx) (hidden-refs . hrf)
+		 (value-ref . vvf)
 		 (customizable . cst) (has-unused-parameters . hup) (boxed-rest . bxr) ) ) 
 	(omit #f))
     (lambda (db)
@@ -618,7 +619,11 @@
 	((##core#closure)
 	 `(##core#closure ,params ,@(map walk subs)) )
 	((##core#variable) (car params))
-	((quote) `(quote ,(car params)))
+	((quote)
+	 (let ((c (car params)))
+	   (if (or (boolean? c) (string? c) (number? c) (char? c))
+	       c
+	       `(quote ,(car params)))))
 	((let)
 	 `(let ,(map list params (map walk (butlast subs)))
 	    ,(walk (last subs)) ) )
@@ -1673,12 +1678,14 @@ Usage: chicken FILENAME OPTION ...
     -no-bound-checks             disable bound variable checks
     -no-procedure-checks         disable procedure call checks
     -no-procedure-checks-for-usual-bindings
-                                 disable procedure call checks only for usual
-                                  bindings
+                                   disable procedure call checks only for usual
+                                   bindings
     -no-procedure-checks-for-toplevel-bindings
                                    disable procedure call checks for toplevel
-                                    bindings
+                                   bindings
     -strict-types                assume variable do not change their type
+    -clustering                  combine groups of local procedures into dispatch
+                                   loop
 
   Configuration options:
 
diff --git a/tests/scrutiny.expected b/tests/scrutiny.expected
index c735c802..2a242925 100644
--- a/tests/scrutiny.expected
+++ b/tests/scrutiny.expected
@@ -8,12 +8,12 @@ Note: in local procedure `c',
   expected value of type boolean in conditional but were given a value of type
   `number' which is always true:
 
-(if x3 '1 '2)
+(if x3 1 2)
 
 Warning: in toplevel procedure `foo':
   branches in conditional expression differ in the number of results:
 
-(if x5 (values '1 '2) (values '1 '2 (+ (+ ...))))
+(if x5 (values 1 2) (values 1 2 (+ (+ ...))))
 
 Warning: at toplevel:
   scrutiny-tests.scm:18: in procedure call to `bar6', expected argument #2 of type `number', but was given an argument of type `symbol'
diff --git a/types.db b/types.db
index 1676962e..67013070 100644
--- a/types.db
+++ b/types.db
@@ -35,10 +35,10 @@
 ; - in templates "#(SYMBOL)" binds X to a temporary gensym'd variable, further references
 ;   to "#(SYMBOL)" allow backreferences to this generated identifier
 ; - "#(procedure PROPERTY ...)" may be used in place of "procedure", properties are:
-;     #:clean
-;     #:enforce
-;     #:predicate TYPE
-;     #:pure
+;     #:clean - procedure does not modify state that might be used locally
+;     #:enforce - when procedure returns, arguments are of correct type
+;     #:predicate TYPE - procedure is a predicate on TYPE
+;     #:pure - procedure has no side effects
 ; - "#:clean" means: will not invoke procedures that modify local variables and
 ;   will not modify list or vector data held locally (note that I/O may invoke
 ;   port handlers)
Trap