~ chicken-core (chicken-5) b8d143a8ae8a40158e584021c6aeba6a69a3b39d


commit b8d143a8ae8a40158e584021c6aeba6a69a3b39d
Author:     megane <meganeka@gmail.com>
AuthorDate: Thu Feb 28 09:52:31 2019 +0200
Commit:     Peter Bex <peter@more-magic.net>
CommitDate: Sun Mar 31 16:10:05 2019 +0200

    Try constant folding before installing specializations
    
    If specializations are enabled the compiler currently doesn't constant
    simple expressions like this:
    
    (+ 1 1)
    
    Instead, this example is specialized to this:
    
    (##core#inline_allocate ("C_a_i_fixnum_plus" 5) 1 1)
    
    The optimizer cannot fold this.
    
    This patch adds constant folding capability to the scrutinizer.
    
    * tests/specialization-test-1.scm: Here (+) would get constant folded,
      whereas (+ (foo)) does not.
    
      Currently there's no guarantee specializations are installed at all.
      So I think it's OK that folding may happen instead of
      specialization, too.
    
      User installed specializations still precede built-ins, which is
      what the test is for.
    
    * optimizer.scm: Moved the "is this node constant-foldable?"
      -detection to support.scm
    
    Signed-off-by: Peter Bex <peter@more-magic.net>

diff --git a/optimizer.scm b/optimizer.scm
index 6318fbf2..8ad32580 100644
--- a/optimizer.scm
+++ b/optimizer.scm
@@ -206,32 +206,24 @@
 		      (else n1) ) )
 
 	       ((##core#call)
-		(if (eq? '##core#variable (node-class (car subs)))
-		    (let ((var (first (node-parameters (car subs)))))
-		      (if (and (intrinsic? var)
-			       (or (foldable? var)
-				   (predicate? var))
-			       (every constant-node? (cddr subs)))
-			  (constant-form-eval
-			   var
-			   (cddr subs)
-			   (lambda (ok result)
-			     (cond ((not ok)
-				    (unless odirty (set! dirty #f))
-				    (set! broken-constant-nodes
-				      (lset-adjoin/eq? broken-constant-nodes n1))
-				    n1)
-				   (else
-				    (touch)
-				    ;; Build call to continuation with new result...
-				    (let ((n2 (qnode result)))
-				      (make-node
-				       '##core#call
-				       (list #t)
-				       (list (cadr subs) n2) ) ) ) )))
-			  n1) )
-		    n1) )
-
+		(maybe-constant-fold-call
+		 n1
+		 (cons (car subs) (cddr subs))
+		 (lambda (ok result constant?)
+		   (cond ((not ok)
+			  (when constant?
+			    (unless odirty (set! dirty #f))
+			    (set! broken-constant-nodes
+				(lset-adjoin/eq? broken-constant-nodes n1)))
+			  n1)
+			 (else
+			  (touch)
+			  ;; Build call to continuation with new result...
+			  (let ((n2 (qnode result)))
+			    (make-node
+			     '##core#call
+			     (list #t)
+			     (list (cadr subs) n2) ) ) ) ))) )
 	       (else n1) ) ) ) ) )
 
     (define (replace-var var)
diff --git a/scrutinizer.scm b/scrutinizer.scm
index 0816e02d..60f0f04c 100644
--- a/scrutinizer.scm
+++ b/scrutinizer.scm
@@ -361,6 +361,13 @@
 					      (set! r '(false))
 					      (set! op (list pt `(not ,pt)))))
 					   (else (trail-restore trail0 typeenv)))))
+			     ((maybe-constant-fold-call node (node-subexpressions node)
+			     				(lambda (ok res _constant?)
+			     				  (and ok (cons res ok))))
+			      => (lambda (res.ok)
+			     	   ;; Actual type doesn't matter; the node gets walked again
+			     	   (set! r '*)
+			     	   (mutate-node! node (list 'quote (car res.ok)))))
 			     ((and specialize (get-specializations pn)) =>
 			      (lambda (specs)
 				(let loop ((specs specs))
@@ -391,7 +398,8 @@
 				(set! specialization-statistics
 				  (cons (cons op 1) 
 					specialization-statistics))))))
-		     (when (and specialize (not op) (procedure-type? ptype))
+		     (when (and specialize (not op) (procedure-type? ptype)
+				(eq? '##core#call (node-class node)))
 		       (set-car! (node-parameters node) #t)
 		       (set! safe-calls (add1 safe-calls))))
 		   (let ((r (if (eq? '* r) r (map (cut resolve <> typeenv) r))))
@@ -673,6 +681,8 @@
 			     (if (eq? '* r)
 				 r
 				 (map (cut resolve <> typeenv) r)))
+			    ((eq? 'quote (node-class n)) ; Call got constant folded
+			     (walk n e loc dest tail flow ctags))
 			    (else
 			     (for-each
 			      (lambda (arg argr)
diff --git a/support.scm b/support.scm
index c802880e..44352e98 100644
--- a/support.scm
+++ b/support.scm
@@ -65,7 +65,7 @@
      clear-real-name-table! get-real-name set-real-name!
      real-name real-name2 display-real-name-table
      source-info->string source-info->line source-info->name
-     call-info constant-form-eval
+     call-info constant-form-eval maybe-constant-fold-call
      dump-nodes read-info-hook read/source-info big-fixnum? small-bignum?
      hide-variable export-variable variable-hidden? variable-visible?
      mark-variable variable-mark intrinsic? predicate? foldable?
@@ -1505,6 +1505,18 @@
 		   (bomb "attempt to constant-fold call to procedure that has multiple results" form))))
 	  (bomb "attempt to constant-fold call to non-procedure" form)))))
 
+(define (maybe-constant-fold-call n subs k)
+  (define (constant-node? n2) (eq? 'quote (node-class n2)))
+  (if (eq? '##core#variable (node-class (car subs)))
+      (let ((var (first (node-parameters (car subs)))))
+	(if (and (intrinsic? var)
+		 (or (foldable? var)
+		     (predicate? var))
+		 (every constant-node? (cdr subs)) )
+	    (constant-form-eval var (cdr subs) (lambda (ok res) (k ok res #t)))
+	    (k #f #f #f)))
+      (k #f #f #f)))
+
 ;; Is the literal small enough to be encoded?  Otherwise, it should
 ;; not be constant-folded.
 (define (encodeable-literal? lit)
diff --git a/tests/scrutiny.expected b/tests/scrutiny.expected
index a16541c0..2134026c 100644
--- a/tests/scrutiny.expected
+++ b/tests/scrutiny.expected
@@ -13,7 +13,7 @@ Note: Test is always true
 
   Test condition has always true value of type:
 
-    number
+    fixnum
 
 Note: Test is always true
   In procedure `b',
@@ -30,7 +30,7 @@ Warning: Branch values mismatch
   In procedure `foo',
   In conditional expression:
 
-    (if x (scheme#values 1 2) (scheme#values 1 2 (scheme#+ (scheme#+ ...))))
+    (if x (scheme#values 1 2) (scheme#values 1 2 3))
 
   The branches have different numbers of values.
 
@@ -40,7 +40,7 @@ Warning: Branch values mismatch
 
   The false branch returns 3 values:
 
-    (scheme#values 1 2 (scheme#+ (scheme#+ (scheme#+ ...))))
+    (scheme#values 1 2 (scheme#+ 3))
 
 Warning: Invalid argument
   In file `scrutiny-tests.scm:XXX',
diff --git a/tests/specialization-test-1.scm b/tests/specialization-test-1.scm
index 52f72c30..42f6646b 100644
--- a/tests/specialization-test-1.scm
+++ b/tests/specialization-test-1.scm
@@ -69,7 +69,9 @@ return n;}
 (assert (abc 1))
 
 ;; user-defined specializations take precedence over built-ins
-(define-specialization (+) 1)
-(assert (= (+) 1))
+(: foo (-> fixnum))
+(define (foo) (begin))
+(define-specialization (+ fixnum) fixnum 1)
+(assert (= (+ (foo)) 1))
 
 )
diff --git a/tests/typematch-tests.scm b/tests/typematch-tests.scm
index 231207f2..ac2d447c 100644
--- a/tests/typematch-tests.scm
+++ b/tests/typematch-tests.scm
@@ -425,4 +425,6 @@
    ((list 'a (forall (a) (list 'b a))) #f)
    ((list 'b (forall (b) (list b 'a))) #t)))
 
+(infer true (= 3 (+ 1 2))) ; Constant folding should happen before / during scrutiny
+
 (test-exit)
Trap