mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-04-30 03:40:34 +02:00
fix self tail recursion to different case-lambda clauses
http://savannah.gnu.org/bugs/?33362 * module/language/tree-il/compile-glil.scm (flatten-lambda-case): Rename from flatten, as it really just takes a particular case. Instead of iteratively compiling lambda cases through `comp', tail-call through flatten-lambda-case. This allows code to see which case it's being compiled in. Take advantage of that to limit the self-tail-call optimization to self-calls to the same case -- otherwise we might be jumping to a label without having reserved the right number of locals. (flatten-lambda): Adapt the caller. * test-suite/tests/compiler.test ("case-lambda"): Add a test.
This commit is contained in:
parent
ad0fedbf82
commit
0083cb5ec4
2 changed files with 42 additions and 39 deletions
|
@ -207,10 +207,12 @@
|
|||
;; write source info for proc
|
||||
(if src (emit-code #f (make-glil-source src)))
|
||||
;; compile the body, yo
|
||||
(flatten body allocation x self-label (car (hashq-ref allocation x))
|
||||
(flatten-lambda-case body allocation x self-label
|
||||
(car (hashq-ref allocation x))
|
||||
emit-code)))))))
|
||||
|
||||
(define (flatten x allocation self self-label fix-labels emit-code)
|
||||
(define (flatten-lambda-case lcase allocation self self-label fix-labels
|
||||
emit-code)
|
||||
(define (emit-label label)
|
||||
(emit-code #f (make-glil-label label)))
|
||||
(define (emit-branch src inst label)
|
||||
|
@ -218,7 +220,7 @@
|
|||
|
||||
;; RA: "return address"; #f unless we're in a non-tail fix with labels
|
||||
;; MVRA: "multiple-values return address"; #f unless we're in a let-values
|
||||
(let comp ((x x) (context 'tail) (RA #f) (MVRA #f))
|
||||
(let comp ((x lcase) (context 'tail) (RA #f) (MVRA #f))
|
||||
(define (comp-tail tree) (comp tree context RA MVRA))
|
||||
(define (comp-push tree) (comp tree 'push #f #f))
|
||||
(define (comp-drop tree) (comp tree 'drop #f #f))
|
||||
|
@ -422,20 +424,15 @@
|
|||
(error "bad primitive op: too many pushes"
|
||||
op (instruction-pushes op))))))
|
||||
|
||||
;; self-call in tail position
|
||||
;; call to the same lambda-case in tail position
|
||||
((and (lexical-ref? proc)
|
||||
self-label (eq? (lexical-ref-gensym proc) self-label)
|
||||
(eq? context 'tail))
|
||||
(let lp ((lcase (lambda-body self)))
|
||||
(cond
|
||||
((and (lambda-case? lcase)
|
||||
(eq? context 'tail)
|
||||
(not (lambda-case-kw lcase))
|
||||
(not (lambda-case-rest lcase))
|
||||
(= (length args)
|
||||
(+ (length (lambda-case-req lcase))
|
||||
(or (and=> (lambda-case-opt lcase) length) 0))))
|
||||
;; we have a case that matches the args; evaluate new
|
||||
;; values, rename variables and goto the case label
|
||||
(for-each comp-push args)
|
||||
(for-each (lambda (sym)
|
||||
(pmatch (hashq-ref (hashq-ref allocation sym) self)
|
||||
|
@ -447,16 +444,6 @@
|
|||
(,x (error "bad lambda-case arg allocation" x))))
|
||||
(reverse (lambda-case-gensyms lcase)))
|
||||
(emit-branch src 'br (car (hashq-ref allocation lcase))))
|
||||
((lambda-case? lcase)
|
||||
;; no match, try next case
|
||||
(lp (lambda-case-alternate lcase)))
|
||||
(else
|
||||
;; no cases left -- use the normal tail call mechanism. we
|
||||
;; can't just shuffle the args down and jump back to the
|
||||
;; self label, because we don't have space.
|
||||
(comp-push proc)
|
||||
(for-each comp-push args)
|
||||
(emit-code src (make-glil-call 'tail-call (length args)))))))
|
||||
|
||||
;; lambda, the ultimate goto
|
||||
((and (lexical-ref? proc)
|
||||
|
@ -780,7 +767,8 @@
|
|||
(if alternate-label
|
||||
(begin
|
||||
(emit-label alternate-label)
|
||||
(comp-tail alternate)))))
|
||||
(flatten-lambda-case alternate allocation self self-label
|
||||
fix-labels emit-code)))))
|
||||
|
||||
((<let> src names gensyms vals body)
|
||||
(for-each comp-push vals)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
;;;; compiler.test --- tests for the compiler -*- scheme -*-
|
||||
;;;; Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
|
||||
;;;; Copyright (C) 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
|
||||
;;;;
|
||||
;;;; This library is free software; you can redistribute it and/or
|
||||
;;;; modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -149,3 +149,18 @@
|
|||
((y) y)
|
||||
((y z) (list y z)))))))
|
||||
(not (not (memv 0 (map source:addr s))))))))
|
||||
|
||||
(with-test-prefix "case-lambda"
|
||||
(pass-if "self recursion to different clause"
|
||||
(equal? (with-output-to-string
|
||||
(lambda ()
|
||||
(let ()
|
||||
(define t
|
||||
(case-lambda
|
||||
((x)
|
||||
(t x 'y))
|
||||
((x y)
|
||||
(display (list x y))
|
||||
(list x y))))
|
||||
(display (t 'x)))))
|
||||
"(x y)(x y)")))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue