~ chicken-core (chicken-5) da39e738821488c545d17ca2d89db0ad6f65769f


commit da39e738821488c545d17ca2d89db0ad6f65769f
Author:     Peter Bex <peter@more-magic.net>
AuthorDate: Wed Sep 16 13:02:46 2020 +0200
Commit:     felix <felix@call-with-current-continuation.org>
CommitDate: Wed Sep 16 13:26:43 2020 +0200

    Don't re-use argvector when the CPS call's arguments use rest-ops (#1703)
    
    Code using restops can be compiled to C code essentially like this:
    
    av2 = av;
    av2[0]=*((C_word*)lf[0]+1);
    av2[1]=t1;
    av2[2]=t2;
    av2[3]=C_get_rest_arg(c,2,av,2,t0);
    tp(4,av2);
    
    But this means the get_rest_arg is taking the newly-written value from
    av2 instead of from the original av.
    
    It would be better if we could assign the argument to a temporary
    instead so that it precedes the call, but that would require a
    restructure of the CPS call itself, which is quite complicated.  For
    now this workaround should be fine, as the situation is relatively
    rare anyway.
    
    Signed-off-by: felix <felix@call-with-current-continuation.org>

diff --git a/NEWS b/NEWS
index 7164df42..87587eeb 100644
--- a/NEWS
+++ b/NEWS
@@ -34,6 +34,10 @@
   - At program cleanup, finalizers are only forced when the live
     finalizer count is non-zero
 
+- Compiler
+  - Avoid re-using argvector when inline rest operations are being
+    used in CPS calls (#1703, thanks to Jakob L. Keuze).
+
 - Build system
   - Auto-configure at build time on most platforms. Cross-compilation
     still requires PLATFORM to be set, and it can still be provided
diff --git a/c-backend.scm b/c-backend.scm
index 1753fea5..67f83691 100644
--- a/c-backend.scm
+++ b/c-backend.scm
@@ -553,6 +553,16 @@
 	    (expr (car xs) i)
 	    (loop (cdr xs)))))
 
+      (define (contains-restop? args)
+	(let loop ((args args))
+	  (if (null? args)
+	      #f
+	      (let ((node (car args)))
+		;; Only rest-car accesses av
+		(or (eq? (node-class node) '##core#rest-car)
+		    (contains-restop? (node-subexpressions node))
+		    (loop (cdr args)))))))
+
       (define (push-args args i selfarg)
 	(let* ((n (length args))
 	       (avl (+ n (if selfarg 1 0)))
@@ -567,7 +577,8 @@
 	  (cond
 	   ((or (not caller-has-av?)	     ; Argvec missing or
 		(and (< caller-argcount avl) ; known to be too small?
-		     (eq? caller-rest-mode 'none)))
+		     (eq? caller-rest-mode 'none))
+		(contains-restop? args))     ; Restops work on original av
 	    (gen #t "C_word av2[" avl "];"))
 	   ((>= caller-argcount avl)   ; Argvec known to be re-usable?
 	    (gen #t "C_word *av2=av;")) ; Re-use our own argvector
diff --git a/tests/compiler-tests.scm b/tests/compiler-tests.scm
index 53275a62..9cda75ef 100644
--- a/tests/compiler-tests.scm
+++ b/tests/compiler-tests.scm
@@ -461,4 +461,11 @@
   (append-map (lambda (a b) (assert (and (= a 3) (= b 4))))
     x y))
 (outer 3 4)
-  
+
+; #1703: argvector re-use interfered with rest-arg optimization
+(define reduce (lambda (_l ini) (+ ini 1)))
+
+(print ((lambda xs (reduce xs (car xs))) 1 2 3)) ;; prints 2
+
+(define fold- (lambda xs (reduce xs (car xs))))
+(print (fold- 1 2 3))
Trap