mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-05-20 11:40:18 +02:00
peval: Abort inlining when the residual code contains recursive calls.
* module/language/tree-il/optimize.scm (code-contains-calls?): New procedure. (peval): Use it and abort inlining if the residual code of a procedure application contains recursive calls. Suggested by Wingo, Waddell, and Dybvig. Fixes <http://debbugs.gnu.org/9542>. * test-suite/tests/tree-il.test ("partial evaluation"): Update 2 tests that relied on the previous behavior. Add 1 another test.
This commit is contained in:
parent
239b4b2ac6
commit
72b2ca55f6
2 changed files with 70 additions and 50 deletions
|
@ -105,6 +105,34 @@ references to the new symbols."
|
|||
(($ <sequence> src exps)
|
||||
(make-sequence src (map (cut loop <> mapping) exps))))))
|
||||
|
||||
(define (code-contains-calls? body proc lookup)
|
||||
"Return true if BODY contains calls to PROC. Use LOOKUP to look up
|
||||
lexical references."
|
||||
(define exit
|
||||
;; The exit label.
|
||||
(gensym))
|
||||
|
||||
(catch exit
|
||||
(lambda ()
|
||||
(tree-il-fold (lambda (exp result) result)
|
||||
(lambda (exp result)
|
||||
(match exp
|
||||
(($ <application> _
|
||||
(and ref ($ <lexical-ref> _ _ gensym)) _)
|
||||
(and (or (equal? ref proc)
|
||||
(equal? (lookup gensym) proc))
|
||||
(throw exit #t)))
|
||||
(($ <application>
|
||||
(and proc* ($ <lambda>)))
|
||||
(and (equal? proc* proc)
|
||||
(throw exit #t)))
|
||||
(_ #f)))
|
||||
(lambda (exp result) result)
|
||||
#f
|
||||
body))
|
||||
(lambda (_ result)
|
||||
result)))
|
||||
|
||||
(define* (peval exp #:optional (cenv (current-module)) (env vlist-null))
|
||||
"Partially evaluate EXP in compilation environment CENV, with
|
||||
top-level bindings from ENV and return the resulting expression. Since
|
||||
|
@ -369,14 +397,21 @@ it does not handle <fix> and <let-values>, it should be called before
|
|||
(nopt (if opt (length opt) 0)))
|
||||
(if (and (>= nargs nreq) (<= nargs (+ nreq nopt))
|
||||
(every pure-expression? args))
|
||||
(loop body
|
||||
(fold vhash-consq env gensyms
|
||||
(append args
|
||||
(drop inits
|
||||
(max 0
|
||||
(- nargs
|
||||
(+ nreq nopt))))))
|
||||
(cons (cons proc args) calls))
|
||||
(let* ((params
|
||||
(append args
|
||||
(drop inits
|
||||
(max 0
|
||||
(- nargs
|
||||
(+ nreq nopt))))))
|
||||
(body
|
||||
(loop body
|
||||
(fold vhash-consq env gensyms params)
|
||||
(cons (cons proc args) calls))))
|
||||
;; If the residual code contains recursive
|
||||
;; calls, give up inlining.
|
||||
(if (code-contains-calls? body proc lookup)
|
||||
app
|
||||
body))
|
||||
app)))
|
||||
(($ <lambda>)
|
||||
app)
|
||||
|
|
|
@ -829,48 +829,27 @@
|
|||
(toplevel top)))))
|
||||
|
||||
(pass-if-peval
|
||||
;; In this example, the two anonymous lambdas are inlined more than
|
||||
;; once; thus, they should use different gensyms for their
|
||||
;; arguments, because the variable allocation process assumes
|
||||
;; globally unique gensyms. This test in itself doesn't check that;
|
||||
;; we rely on the next one to catch any error.
|
||||
;;
|
||||
;; Bug reported at
|
||||
;; <https://lists.gnu.org/archive/html/bug-guile/2011-09/msg00019.html> and
|
||||
;; <https://lists.gnu.org/archive/html/bug-guile/2011-09/msg00029.html>.
|
||||
;; Procedure not inlined when residual code contains recursive calls.
|
||||
;; <http://debbugs.gnu.org/9542>
|
||||
(letrec ((fold (lambda (f x3 b null? car cdr)
|
||||
(if (null? x3)
|
||||
b
|
||||
(f (car x3) (fold f (cdr x3) b null? car cdr))))))
|
||||
(fold * x 1 zero? (lambda (x1) x1) (lambda (x2) (- x2 1))))
|
||||
(letrec (fold) (_) (_)
|
||||
(if (apply (primitive zero?) (toplevel x))
|
||||
(const 1)
|
||||
(apply (primitive *) ; f
|
||||
(apply (lambda () ; car
|
||||
(lambda-case
|
||||
(((x1) #f #f #f () (_))
|
||||
(lexical x1 _))))
|
||||
(toplevel x))
|
||||
(apply (lexical fold _) ; fold
|
||||
(primitive *)
|
||||
(apply (lambda () ; cdr
|
||||
(lambda-case
|
||||
(((x2) #f #f #f () (_))
|
||||
(apply (primitive -)
|
||||
(lexical x2 _) (const 1)))))
|
||||
(toplevel x))
|
||||
(const 1)
|
||||
(primitive zero?)
|
||||
(lambda () ; car
|
||||
(lambda-case
|
||||
(((x1) #f #f #f () (_))
|
||||
(lexical x1 _))))
|
||||
(lambda () ; cdr
|
||||
(lambda-case
|
||||
(((x2) #f #f #f () (_))
|
||||
(apply (primitive -)
|
||||
(lexical x2 _) (const 1))))))))))
|
||||
(apply (lexical fold _)
|
||||
(primitive *)
|
||||
(toplevel x)
|
||||
(const 1)
|
||||
(primitive zero?)
|
||||
(lambda ()
|
||||
(lambda-case
|
||||
(((x1) #f #f #f () (_))
|
||||
(lexical x1 _))))
|
||||
(lambda ()
|
||||
(lambda-case
|
||||
(((x2) #f #f #f () (_))
|
||||
(apply (primitive -) (lexical x2 _) (const 1))))))))
|
||||
|
||||
(pass-if "inlined lambdas are alpha-renamed"
|
||||
;; In this example, the two anonymous lambdas are inlined more than
|
||||
|
@ -1077,7 +1056,17 @@
|
|||
(apply (lexical loop _) (toplevel x))))
|
||||
|
||||
(pass-if-peval
|
||||
;; Inlining stops at recursive calls (mixed static/dynamic arguments).
|
||||
;; Recursion on the 2nd argument is fully evaluated.
|
||||
(let loop ((x x) (y 10))
|
||||
(if (> y 0)
|
||||
(loop x (1- y))
|
||||
(foo x y)))
|
||||
(letrec (loop) (_) (_)
|
||||
(apply (toplevel foo) (toplevel x) (const 0))))
|
||||
|
||||
(pass-if-peval
|
||||
;; Inlining aborted when residual code contains recursive calls.
|
||||
;; <http://debbugs.gnu.org/9542>
|
||||
(let loop ((x x) (y 0))
|
||||
(if (> y 0)
|
||||
(loop (1+ x) (1+ y))
|
||||
|
@ -1088,11 +1077,7 @@
|
|||
(if (apply (primitive >)
|
||||
(lexical y _) (const 0))
|
||||
_ _)))))
|
||||
;; call to (loop x 0) is inlined & specialized
|
||||
(if (apply (primitive <) (toplevel x) (const 0))
|
||||
(toplevel x)
|
||||
(apply (lexical loop _)
|
||||
(apply (primitive 1-) (toplevel x))))))
|
||||
(apply (lexical loop _) (toplevel x) (const 0))))
|
||||
|
||||
(pass-if-peval
|
||||
;; Infinite recursion: `peval' gives up and leaves it as is.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue