; -*- Mode: Scheme; Syntax: Scheme; Package: Scheme; -*- ; File strategy.scm / Copyright (c) 1991 Jonathan Rees / See file COPYING ;;;; Compute strategy for compiling a LETREC (define (get-letrec-strategy node) (or (letrec-strategy node) (let ((strategy (let ((vars (letrec-vars node)) (vals (letrec-vals node))) (cond ((or (null? vars) (not (function-bindable? vars vals))) 'general) ((or (some variable-value-refs? vars) (some n-ary? vals) (exists-losing-call? node)) 'labels) (else 'prog))))) (set-letrec-strategy! node strategy) strategy))) ; The following procedure does a tail recursion analysis to find calls ; to the labels functions that are in non-tail-recursive positions. (define (exists-losing-call? node) (let ((vars (letrec-vars node))) (or (contains-loser? (letrec-body node) vars 'win) (some (lambda (proc) (call-will-lose? proc vars 'win)) (letrec-vals node))))) (define (contains-loser? node vars k) (case (node-type node) ((local-variable program-variable constant) #f) ((letrec) (or (contains-loser? (letrec-body node) vars k) (if (eq? (get-letrec-strategy node) 'prog) (some (lambda (proc) (call-will-lose? proc vars k)) (letrec-vals node)) (list-contains-loser? (letrec-vals node) vars 'lose)))) ((if) (or (contains-loser? (if-test node) vars 'lose) (contains-loser? (if-con node) vars k) (contains-loser? (if-alt node) vars k))) ((begin) (or (contains-loser? (begin-first node) vars 'lose) (contains-loser? (begin-second node) vars k))) ((set!) (contains-loser? (set!-rhs node) vars 'lose)) ((lambda) (contains-loser? (lambda-body node) vars 'lose)) ((call) (let ((proc (call-proc node))) (cond ((lambda? proc) ;;+++ Could deal with (let ((p (lambda ...))) ... (p ...)) ;; here, but punt for now. (or (call-will-lose? proc vars k) (list-contains-loser? (call-args node) vars 'lose))) ((program-variable? proc) (let ((n (number-of-non-continuation-args proc))) (if n (let loop ((a (call-args node)) (i 0)) (if (= i n) (some (lambda (arg) (call-will-lose? arg vars k)) a) (or (contains-loser? (car a) vars 'lose) (loop (cdr a) (+ i 1))))) (list-contains-loser? (call-args node) vars 'lose)))) (else (or (if (memq proc vars) (eq? k 'lose) (contains-loser? proc vars 'lose)) (list-contains-loser? (call-args node) vars 'lose)))))) (else (error "unknown node type" node)))) (define (list-contains-loser? node-list vars k) (some (lambda (node) (contains-loser? node vars k)) node-list)) ; PROC-NODE will be evaluated and then immediately invoked. (define (call-will-lose? proc-node vars k) (if (lambda? proc-node) (contains-loser? (lambda-body proc-node) vars k) (contains-loser? proc-node vars 'lose))) (define (number-of-non-continuation-args var) ;; Kind of slow -- should speed this up somehow? This information ;; ought to be in the integrations-table, at least. (cond ((or (eq? var (built-in 'and-aux)) (eq? var (built-in 'or-aux))) 1) ((eq? var (built-in '=>-aux)) 2) ((eq? var (built-in 'case-aux)) 1) (else #f))) ; True if it will be possible to bind the variables using FLET or LABELS. (define (function-bindable? vars vals) (and (not (null? vars)) (every (lambda (var) ;; Maybe require that there be no non-function refs? (not (variable-assigned? var))) vars) (every lambda? vals)))