~ chicken-core (chicken-5) 050ca67f43ba3cd4e424f403564f096af3c33440


commit 050ca67f43ba3cd4e424f403564f096af3c33440
Author:     Felix Winkelmann <felix@call-with-current-continuation.org>
AuthorDate: Tue Oct 9 03:49:24 2012 -0400
Commit:     Peter Bex <peter.bex@xs4all.nl>
CommitDate: Mon Oct 15 20:18:02 2012 +0200

    Add lightweight flow-analysis pass to remove some unnecessary type checks after normal optimization has taken place.
    
    Even with specialization, inlining (in particular cross-module
    inlining) may result in type checks that are in many cases redundant.
    For example inlined record accessors compiled in safe mode, and
    inlining of code in general (which is done after the normal
    flow-analysis pass) will introduce type-checks that specialization
    would have removed.
    
    This patch adds a new compiler pass (called "lfa2"), that does a
    simplified flow-analysis to identify redundant forms of the form
    '(##core#inline "C_i_check_..." ...)'. Type-information is obtained
    from constant forms and predicates like '(##core#inline
    "C_i_structurep" ...)'. In unsafe mode checks will generally be
    removed.
    
    Signed-off-by: Peter Bex <peter.bex@xs4all.nl>

diff --git a/batch-driver.scm b/batch-driver.scm
index 062bb6b5..099548e2 100644
--- a/batch-driver.scm
+++ b/batch-driver.scm
@@ -91,6 +91,7 @@
 	(a-only (memq 'analyze-only options))
 	(dynamic (memq 'dynamic options))
 	(do-scrutinize (memq 'scrutinize options))
+	(do-lfa2 (memq 'lfa2 options))
 	(dumpnodes #f)
 	(start-time #f)
 	(upap #f)
@@ -437,6 +438,7 @@
 	     (set! ##sys#explicit-library-modules
 	       (append ##sys#explicit-library-modules uses-units))
 	     (set! forms (cons `(declare (uses ,@uses-units)) forms)) )
+	   ;; Canonicalize s-expressions
 	   (let* ((exps0 (map canonicalize-expression
 			      (let ((forms (append initforms forms)))
 				(if wrap-module
@@ -493,6 +495,7 @@
 
 	     (when (memq 'check-syntax options) (exit))
 
+	     ;; User-defined pass (s-expressions)
 	     (let ([proc (user-pass)])
 	       (when proc
 		 (dribble "User pass...")
@@ -500,6 +503,7 @@
 		 (set! exps (map proc exps))
 		 (end-time "user pass") ) )
 
+	     ;; Convert s-expressions to node tree
 	     (let ((node0 (make-node
 			   'lambda '(())
 			   (list (build-node-graph
@@ -531,6 +535,7 @@
 			(dribble "Loading inline file ~a ..." ilf)
 			(load-inline-file ilf) )
 		      ifs)))
+		 ;; Perform scrutiny and optionally specialization
 		 (when (or do-scrutinize enable-specialization)
 		   ;;XXX hardcoded database file name
 		   (unless (memq 'ignore-repository options)
@@ -561,10 +566,12 @@
 	       (set! ##sys#line-number-database #f)
 	       (set! constant-table #f)
 	       (set! inline-table #f)
+	       ;; Analyze toplevel assignments
 	       (unless unsafe
 		 (scan-toplevel-assignments (first (node-subexpressions node0))) )
 
 	       (begin-time)
+	       ;; Convert to CPS
 	       (let ([node1 (perform-cps-conversion node0)])
 		 (end-time "cps conversion")
 		 (print-node "cps" '|3| node1)
@@ -576,6 +583,7 @@
 			    (l/d #f)
 			    (l/d-done #f))
 		   (begin-time)
+		   ;; Analyze node tree for optimization
 		   (let ([db (analyze 'opt node2 i progress)])
 		     (when first-analysis
 		       (when (memq 'u debugging-chicken)
@@ -595,6 +603,7 @@
 		     (when (memq 's debugging-chicken) 
 		       (print-program-statistics db))
 
+		     ;; Optimize (once)
 		     (cond (progress
 			    (debugging 'p "optimization pass" i)
 			    (begin-time)
@@ -630,6 +639,12 @@
 				     (loop (add1 i) node2 #f #f l/d-done)) ) ) )
 			   
 			   (else
+			    ;; Secondary flow-analysis
+			    (when do-lfa2
+			      (begin-time)
+			      (debugging 'p "doing lfa2")
+			      (perform-secondary-flow-analysis node2 db)
+			      (end-time "secondary flow analysis"))
 			    (print-node "optimized" '|7| node2)
 			    ;; inlining into a file with interrupts enabled would
 			    ;; change semantics
@@ -638,6 +653,7 @@
 				(dribble "generating global inline file `~a' ..." f)
 				(emit-global-inline-file f db) ) )
 			    (begin-time)
+			    ;; Closure conversion
 			    (set! node2 (perform-closure-conversion node2 db))
 			    (end-time "closure conversion")
 			    (print-db "final-analysis" '|8| db i)
@@ -647,11 +663,13 @@
 			    (print-node "closure-converted" '|9| node2)
 			    (when a-only (exit 0))
 			    (begin-time)
+			    ;; Preparation
 			    (receive 
 			     (node literals lliterals lambda-table)
 			     (prepare-for-code-generation node2 db)
 			     (end-time "preparation")
 			     (begin-time)
+			     ;; Code generation
 			     (let ((out (if outfile (open-output-file outfile) (current-output-port))) )
 			       (dribble "generating `~A' ..." outfile)
 			       (generate-code literals lliterals lambda-table out filename dynamic db)
diff --git a/c-platform.scm b/c-platform.scm
index c64db6ce..04a48799 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 clustering
+    strict-types clustering lfa2
     setup-mode no-module-registration) )
 
 (define valid-compiler-options-with-argument
diff --git a/chicken.h b/chicken.h
index 4ee1e53f..a946d0c9 100644
--- a/chicken.h
+++ b/chicken.h
@@ -1504,6 +1504,13 @@ extern double trunc(double);
 
 #define C_a_i_current_milliseconds(ptr, c, dummy) C_flonum(ptr, C_milliseconds())
 
+#define C_i_noop1(dummy)               ((dummy), C_SCHEME_UNDEFINED)
+#define C_i_noop2(dummy1, dummy2)      ((dummy1), (dummy2), C_SCHEME_UNDEFINED)
+#define C_i_noop3(dummy1, dummy2, dummy3)  ((dummy1), (dummy2), (dummy3), C_SCHEME_UNDEFINED)
+#define C_i_true1(dummy)               ((dummy), C_SCHEME_TRUE)
+#define C_i_true2(dummy1, dummy2)      ((dummy1), (dummy2), C_SCHEME_TRUE)
+#define C_i_true3(dummy1, dummy2, dummy3)  ((dummy1), (dummy2), (dummy3), C_SCHEME_TRUE)
+
 
 /* Variables: */
 
diff --git a/chicken.scm b/chicken.scm
index cc5e83d1..c1582102 100644
--- a/chicken.scm
+++ b/chicken.scm
@@ -28,7 +28,7 @@
 (declare
   (uses chicken-syntax chicken-ffi-syntax 
 	srfi-1 srfi-4 utils files extras data-structures support
-	compiler optimizer compiler-syntax scrutinizer driver platform backend 
+	compiler optimizer lfa2 compiler-syntax scrutinizer driver platform backend 
 	srfi-69))
 
 
@@ -82,12 +82,16 @@
 		    (set! options
 		      (cons* 'no-compiler-syntax 'no-usual-integrations options)) )
 		   ((1)
-		    (set! options (cons 'optimize-leaf-routines options)) )
+		    (set! options
+		      (cons* 'optimize-leaf-routines
+			     ;XXX 'lfa2 
+			     options)) )
 		   ((2)
 		    (set! options 
 		      (cons* 'optimize-leaf-routines
 			     'inline
 			     ;XXX 'clustering
+			     ;XXX 'lfa2
 			     options)) ) 
 		   ((3)
 		    (set! options
@@ -96,6 +100,7 @@
 			     'inline-global
 			     'local
 			     ;XXX 'clustering
+			     ;XXX 'lfa2
 			     'specialize
 			     options) ) )
 		   ((4)
@@ -105,6 +110,7 @@
 			     'inline-global
 			     'specialize
 			     ;XXX 'clustering
+			     ;XXX 'lfa2
 			     'local 'unsafe
 			     options) ) )
 		   (else
@@ -122,6 +128,7 @@
 			       'inline
 			       'inline-global
 			       'clustering
+			       'lfa2
 			       options) ) ) ) )
 		 (loop (cdr rest)) ) )
 	      ((eq? 'debug-level o)
diff --git a/compiler-namespace.scm b/compiler-namespace.scm
index ca873c90..69302063 100644
--- a/compiler-namespace.scm
+++ b/compiler-namespace.scm
@@ -223,6 +223,7 @@
  perform-high-level-optimizations
  perform-inlining!
  perform-pre-optimization!
+ perform-secondary-flow-analysis
  posv
  posq
  postponed-initforms
diff --git a/compiler.scm b/compiler.scm
index 64624bd0..c99260e8 100644
--- a/compiler.scm
+++ b/compiler.scm
@@ -1910,7 +1910,7 @@
 		  ;; decorate ##core#call node with size
 		  (set-car! (cdddr (node-parameters n)) (- current-program-size size0)) ) ) ) ) )
 	  
-	  ((set! ##core#set!) 
+	  ((set! ##core#set!) 		;XXX ##core#set! still used?
 	   (let* ((var (first params))
 		  (val (car subs)) )
 	     (when (and first-analysis (not bootstrap-mode))
diff --git a/csc.scm b/csc.scm
index b067c4e1..a981233d 100644
--- a/csc.scm
+++ b/csc.scm
@@ -144,7 +144,7 @@
     -no-argc-checks -no-bound-checks -no-procedure-checks -no-compiler-syntax
     -emit-all-import-libraries -setup-mode -no-elevation -no-module-registration
     -no-procedure-checks-for-usual-bindings -module
-    -specialize -strict-types -clustering
+    -specialize -strict-types -clustering -lfa2
     -no-procedure-checks-for-toplevel-bindings))
 
 (define-constant complex-options
@@ -182,7 +182,6 @@
 ;;; Variables:
 
 (define scheme-files '())
-(define generated-scheme-files '())
 (define c-files '())
 (define rc-files '())
 (define generated-c-files '())
@@ -405,6 +404,7 @@ Usage: #{csc} FILENAME | OPTION ...
     -strict-types                  assume variable do not change their type
     -clustering                    combine groups of local procedures into dispatch
                                      loop
+    -lfa2                          perform additional lightweight flow-analysis pass
 
   Configuration options:
 
@@ -830,8 +830,7 @@ EOF
 	 " ") )
        (set! c-files (append (list fc) c-files))
        (set! generated-c-files (append (list fc) generated-c-files))))
-   scheme-files)
-  (unless keep-files (for-each $delete-file generated-scheme-files)) )
+   scheme-files))
 
 
 ;;; Compile all C/C++  and .rc files:
diff --git a/distribution/manifest b/distribution/manifest
index 9f63422f..0f31254c 100644
--- a/distribution/manifest
+++ b/distribution/manifest
@@ -78,6 +78,8 @@ scrutinizer.scm
 irregex.scm
 irregex-core.scm
 irregex-utils.scm
+lfa2.c
+lfa2.scm
 posixunix.scm
 posixwin.scm
 posix-common.scm
diff --git a/lfa2.scm b/lfa2.scm
new file mode 100644
index 00000000..97e4101d
--- /dev/null
+++ b/lfa2.scm
@@ -0,0 +1,360 @@
+;;;; lfa2.scm - a lightweight "secondary" flow analysis
+;
+; Copyright (c) 2012, The Chicken Team
+; All rights reserved.
+;
+; Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
+; conditions are met:
+;
+;   Redistributions of source code must retain the above copyright notice, this list of conditions and the following
+;     disclaimer. 
+;   Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
+;     disclaimer in the documentation and/or other materials provided with the distribution. 
+;   Neither the name of the author nor the names of its contributors may be used to endorse or promote
+;     products derived from this software without specific prior written permission. 
+;
+; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
+; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+; AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+; POSSIBILITY OF SUCH DAMAGE.
+
+
+;; This pass does a lightweight flow-analysis on value types, mostly
+;; to handle the case where user code performs a type-check followed
+;; by inlined accessors (for example when using record structures).
+;; Specialization takes place before inlining, so even though we have
+;; the type-information, later inlining will still keep the code for
+;; checking argument types.
+
+
+(declare
+  (unit lfa2)
+  (hide d-depth lfa2-debug d dd +type-check-map+ +predicate-map+))
+
+
+(include "compiler-namespace")
+(include "tweaks")
+
+
+(define d-depth 0)
+(define lfa2-debug #t)
+
+(define (d fstr . args)
+  (when (and scrutiny-debug (##sys#fudge 13))
+    (printf "[debug|~a] ~a~?~%" d-depth (make-string d-depth #\space) fstr args)) )
+
+(define dd d)
+
+(define-syntax d (syntax-rules () ((_ . _) (void))))
+(define-syntax dd (syntax-rules () ((_ . _) (void))))
+
+
+;;; Maps checks to types
+
+(define +type-check-map+
+  '(("C_i_check_closure" procedure)
+    ("C_i_check_exact" fixnum)
+    ("C_i_check_inexact" flonum)
+    ("C_i_check_number" fixnum flonum number)
+    ("C_i_check_string" string)
+    ("C_i_check_bytevector" blob)
+    ("C_i_check_symbol" symbol)
+    ("C_i_check_list" null pair list)
+    ("C_i_check_pair" pair)
+    ("C_i_check_locative" locative)
+    ("C_i_check_boolean" boolean)
+    ("C_i_check_vector" vector)
+    ("C_i_check_structure" *struct*)	; special case
+    ("C_i_check_char" char)
+    ("C_i_check_closure_2" procedure)
+    ("C_i_check_exact_2" fixnum)
+    ("C_i_check_inexact_2" flonum)
+    ("C_i_check_number_2" fixnum flonum number)
+    ("C_i_check_string_2" string)
+    ("C_i_check_bytevector_2" blob)
+    ("C_i_check_symbol_2" symbol)
+    ("C_i_check_list_2" null pair list)
+    ("C_i_check_pair_2" pair)
+    ("C_i_check_locative_2" locative)
+    ("C_i_check_boolean_2" boolean)
+    ("C_i_check_vector_2" vector)
+    ("C_i_check_structure_2" *struct*)	; special case
+    ("C_i_check_char_2" char)))
+
+
+;; Maps predicates to types
+ 
+(define +predicate-map+
+  '(("C_i_closurep" procedure)
+    ("C_fixnump" fixnum)
+    ("C_i_flonump" flonum)
+    ("C_i_numberp" number)
+    ("C_stringp" string)
+    ("C_bytevectorp" blob)
+    ("C_i_symbolp" symbol)
+    ("C_i_listp" list)
+    ("C_i_pairp" pair)
+    ("C_locativep" locative)
+    ("C_booleanp" boolean)
+    ("C_i_vectorp" vector)
+    ("C_structurep" struct)
+    ("C_i_structurep" *struct*)		; special case
+    ("C_charp" char)
+    ("C_i_portp" port)
+    ("C_i_nullp" null)))
+
+
+;; Maps constructors to types
+
+(define +constructor-map+
+  '(("C_a_i_record1" *struct*)		; special case
+    ("C_a_i_record2" *struct*)
+    ("C_a_i_record3" *struct*)
+    ("C_a_i_record4" *struct*)
+    ("C_a_i_record5" *struct*)
+    ("C_a_i_record6" *struct*)
+    ("C_a_i_record7" *struct*)
+    ("C_a_i_record8" *struct*)
+    ("C_a_i_record" *struct*)
+    ("C_a_i_string" string)
+    ("C_a_i_port" port)
+    ("C_a_i_vector1" vector)
+    ("C_a_i_vector2" vector)
+    ("C_a_i_vector3" vector)
+    ("C_a_i_vector4" vector)
+    ("C_a_i_vector5" vector)
+    ("C_a_i_vector6" vector)
+    ("C_a_i_vector7" vector)
+    ("C_a_i_vector8" vector)
+    ("C_a_pair" pair)
+    ("C_a_i_bytevector" blob)
+    ("C_a_i_make_locative" locative)
+    ("C_a_i_vector" vector)
+    ("C_a_i_list1" pair)
+    ("C_a_i_list2" pair)
+    ("C_a_i_list3" pair)
+    ("C_a_i_list4" pair)
+    ("C_a_i_list5" pair)
+    ("C_a_i_list6" pair)
+    ("C_a_i_list7" pair)
+    ("C_a_i_list8" pair)
+    ("C_a_i_cons" pair)
+    ("C_a_i_flonum" flonum)
+    ("C_a_i_fix_to_flo" flonum)
+    ;;XXX there are endless more - is it worth it?
+    ))
+
+
+;;; Walk nodes and perform simplified type-analysis
+
+(define (perform-secondary-flow-analysis node db)
+  (let ((stats '()))
+
+    (define (constant-result lit) 
+      ;; a simplified variant of the one in scrutinizer.scm
+      (cond ((string? lit) 'string)
+	    ((symbol? lit) 'symbol)
+	    ((fixnum? lit) 'fixnum)
+	    ((flonum? lit) 'float)
+	    ((number? lit) 
+	     (case number-type 
+	       ((fixnum) 'fixnum)
+	       ((flonum) 'flonum)
+	       (else 'number)))
+	    ((boolean? lit) 'boolean)
+	    ((null? lit) 'null)
+	    ((list? lit) 'list)
+	    ((pair? lit) 'pair)
+	    ((eof-object? lit) 'eof)
+	    ((vector? lit) 'vector)
+	    ((and (not (##sys#immediate? lit)) (##sys#generic-structure? lit))
+	     `(struct ,(##sys#slot lit 0)))
+	    ((char? lit) 'char)
+	    (else '*)))
+
+    (define (report elim)
+      (cond ((assoc elim stats) =>
+	     (lambda (a) (set-cdr! a (add1 (cdr a)))))
+	    (else (set! stats (alist-cons elim 1 stats)))))
+ 
+    (define (assigned? var)
+      (get db var 'assigned))
+
+    (define (droppable? n)
+      (or (memq (node-class n) 
+		'(quote ##core#undefined ##core#primitive ##core#lambda))
+	  (and (eq? '##core#variable (node-class n))
+	       (let ((var (first (node-parameters n))))
+		 (or (not (get db var 'global))
+		     (variable-mark var '##compiler#always-bound))))))
+
+    (define (drop! n)
+      (node-class-set! n '##core#undefined)
+      (node-parameters-set! n '())
+      (node-subexpressions-set! n '()))
+
+    (define (extinguish! node rpl)	; replace ##core#inline call
+      (report (first (node-parameters node)))
+      (let ((subs (node-subexpressions node))
+	    (alldropped #t))
+	(for-each
+	 (lambda (sn)
+	   (if (droppable? sn)
+	       (drop! sn)
+	       (set! alldropped #f)))
+	 subs)
+	(if alldropped
+	    (drop! node)
+	    (node-parameters-set!
+	     node
+	     (list
+	      (string-append
+	       rpl
+	       (case (length (node-subexpressions node))
+		 ((1) "1")
+		 ((2) "2")
+		 ((3) "3")
+		 (else (bomb "bad number of arguments to extinguished ##core#inline")))))))))
+
+    (define (vartype v te ae)
+      (cond ((assq v te) => cdr)
+	    (else
+	     (let loop ((ae ae))
+	       (cond ((null? ae) '*)
+		     ((and (eq? v (cdar ae)) 
+			   (assq (caar ae) te) )
+		      => cdr)
+		     (else (loop (cdr ae))))))))
+
+    (define (walk n te ae)
+      (let ((class (node-class n))
+	    (params (node-parameters n))
+	    (subs (node-subexpressions n)))
+	(case class
+	  ((##core#variable)
+	   (vartype (first params) te ae))
+	  ((if ##core#cond) 
+	   (let ((tr (walk (first subs) te ae)))
+	     (cond ((and (pair? tr) (eq? 'boolean (car tr)))
+		    (walk (second subs)
+			  (append (second tr) te)
+			  ae)
+		    (walk (third subs)
+			  (append (third tr) te)
+			  ae))
+		   (else
+		    (walk (second subs) te ae)
+		    (walk (third subs) te ae)))))
+	  ((quote) (constant-result (first params)))
+	  ((let)
+	   (let* ((val (first subs))
+		  (var (first params))
+		  (r (walk val te ae)))
+	     (walk (second subs)
+		   (if (assigned? var) 
+		       te
+		       (alist-cons var r te))
+		   (if (and (eq? '##core#variable (node-class val))
+			    (not (assigned? var))
+			    (not (assigned? (first (node-parameters val)))))
+		       (let ((var2 (first (node-parameters val))))
+			 (alist-cons var var2 (alist-cons var2 var ae)))
+		       ae))))
+	  ((##core#lambda ##core#direct_lambda)
+	   ;; fresh env and we don't bother to create entries in the environment
+	   ;; for the llist-bound variables (missing implies type '*)
+	   ;;XXX (but we could treat the first arg in non-CPS lambdas as procedure...)
+	   (walk (first subs) '() '())
+	   'procedure)
+	  ((set! ##core#set!)	       ;XXX is ##core#set! still used?
+	   (walk (first subs) te ae)
+	   'undefined)
+	  ((##core#undefined) 'undefined)
+	  ((##core#primitive) 'procedure)
+	  ((##core#inline ##core#inline_allocate)
+	   (for-each (cut walk <> te ae) subs)
+	   (cond ((assoc (first params) +type-check-map+) =>
+		  (lambda (a)
+		    (let ((r1 (walk (first subs) te ae)))
+		      (cond (unsafe
+			     (extinguish! n "C_i_noop"))
+			    ((eq? '*struct* (cadr a))
+			     ;; handle known structure type
+			     (when (and (pair? r1)
+					(eq? 'struct (first r1))
+					(eq? 'quote (node-class (second subs))))
+			       (let ((st (first (node-parameters (second subs)))))
+				 (when (and (symbol? st)
+					    (eq? st (second r1)))
+				   (extinguish! n "C_i_noop")))))
+			    ((and (pair? r1) (eq? 'boolean (car r1)))
+			     (when (memq 'boolean (cdr a))
+			       (extinguish! n "C_i_noop")))
+			    ;; handle other types
+			    ((member r1 (cdr a))
+			     (extinguish! n "C_i_noop")))
+		      '*)))
+		 ((assoc (first params) +predicate-map+) =>
+		  (lambda (a)
+		    (let ((arg (first subs)))
+		      (if (eq? '##core#variable (node-class arg))
+			  `(boolean
+			    ((,(first (node-parameters arg)) 
+			      .
+			      ,(if (eq? '*struct* (cadr a))
+				   (if (eq? 'quote (node-class (second subs)))
+				       (let ((st (first
+						  (node-parameters
+						   (second subs)))))
+					 (if (symbol? st)
+					     `(struct ,st)
+					     'struct))
+				       'struct)
+				   (cadr a))))
+			    ())
+			  (let ((r1 (walk (first subs) te ae)))
+			    (cond ((eq? '*struct* (cadr a))
+				   ;; known structure type
+				   (when (and (pair? r1)
+					      (eq? 'struct (first r1))
+					      (eq? 'quote (node-class (second subs))))
+				     (let ((st (first 
+						(node-parameters (second subs)))))
+				       (when (and (symbol? st)
+						  (eq? st (second r1)))
+					 (extinguish! n "C_i_true")))))
+				  ((and (pair? r1) (eq? 'boolean (car r1)))
+				   (when (memq 'boolean (cdr a))
+				     (extinguish! n "C_i_true")))
+				  ;; other types
+				  ((member r1 (cdr a))
+				   (extinguish! n "C_i_true")))
+			    'boolean)))))
+		 ((assoc (first params) +constructor-map+) =>
+		  (lambda (a)
+		    (let ((arg1 (first subs)))
+		      (if (and (eq? '*struct* (cadr a))
+			       (eq? 'quote (node-class arg1)))
+			  (let ((tag (first (node-parameters arg1))))
+			    (if (symbol? tag)
+				`(struct ,tag)
+				'struct))
+			  (cadr a)))))))
+	  (else 
+	   (for-each (cut walk <> te ae) subs)
+	   '*))))
+
+    (walk node '() '())
+    (when (pair? stats)
+      (with-debugging-output
+       '(x o)
+       (lambda ()
+	 (print "eliminated type checks:")
+	 (for-each 
+	  (lambda (ss) (printf "  ~a:\t~a~%" (car ss) (cdr ss)))
+	  stats))))))
diff --git a/manual/Using the compiler b/manual/Using the compiler
index 568a2623..a1a0ff7e 100644
--- a/manual/Using the compiler	
+++ b/manual/Using the compiler	
@@ -88,6 +88,8 @@ the source text should be read from standard input.
 
 ; -local : Assume toplevel variables defined in the current compilation unit are not externally modified. This gives the compiler more opportunities for inlining. Note that this may result in counter-intuitive and non-standard behaviour: an asssignment to an exported toplevel variable executed in a different compilation unit or in evaluated code will possibly not be seen by code executing in the current compilation unit.
 
+; -lfa2 : Does an additional lightweight flow-analysis pass on the fully optimized program to remove more type checks.
+
 ; -module : wraps the compiled code in an implicit module named {{main}}, importing the {{scheme}} and {{chicken}} modules.
 
 ; -no-argc-checks : disable argument count checks
@@ -126,7 +128,7 @@ the source text should be read from standard input.
      -optimize-level 2          is equivalent to -optimize-leaf-routines -inline
      -optimize-level 3          is equivalent to -optimize-leaf-routines -local -inline -inline-global -specialize
      -optimize-level 4          is equivalent to -optimize-leaf-routines -local -inline -inline-global -specialize -unsafe
-     -optimize-level 5          is equivalent to -optimize-leaf-routines -block -inline -inline-global -specialize -unsafe -disable-interrupts -no-trace -no-lambda-info
+     -optimize-level 5          is equivalent to -optimize-leaf-routines -block -inline -inline-global -specialize -unsafe -disable-interrupts -no-trace -no-lambda-info -clustering -lfa2
 
 ; -output-file FILENAME : Specifies the pathname of the generated C file. Default is {{FILENAME.c}}.
 
diff --git a/rules.make b/rules.make
index 29bc2bf5..c467b88f 100644
--- a/rules.make
+++ b/rules.make
@@ -44,7 +44,7 @@ LIBCHICKEN_SHARED_OBJECTS = $(LIBCHICKEN_OBJECTS_1:=$(O))
 LIBCHICKEN_STATIC_OBJECTS = $(LIBCHICKEN_OBJECTS_1:=-static$(O))
 
 COMPILER_OBJECTS_1 = \
-	chicken batch-driver compiler optimizer compiler-syntax scrutinizer support \
+	chicken batch-driver compiler optimizer lfa2 compiler-syntax scrutinizer support \
 	c-platform c-backend
 COMPILER_OBJECTS        = $(COMPILER_OBJECTS_1:=$(O))
 COMPILER_STATIC_OBJECTS = $(COMPILER_OBJECTS_1:=-static$(O))
diff --git a/support.scm b/support.scm
index 7fab02bd..9c4cedc1 100644
--- a/support.scm
+++ b/support.scm
@@ -1705,6 +1705,7 @@ Usage: chicken FILENAME OPTION ...
     -strict-types                assume variable do not change their type
     -clustering                  combine groups of local procedures into dispatch
                                    loop
+    -lfa2                        perform additional lightweight flow-analysis pass
 
   Configuration options:
 
Trap