;;; -*- Mode:LISP; Package:(NC LISP); Base:10; Readtable:CL -*- ;;; Copyright (c) 1985 Yale University ;;; Authors: N Adams, R Kelsey, D Kranz, J Philbin, J Rees. ;;; This material was developed by the T Project at the Yale University Computer ;;; Science Department. Permission to copy this software, to redistribute it, ;;; and to use it for any purpose is granted, subject to the following restric- ;;; tions and understandings. ;;; 1. Any copy made of this software must include this copyright notice in full. ;;; 2. Users of this software agree to make their best efforts (a) to return ;;; to the T Project at Yale any improvements or extensions that they make, ;;; so that these may be included in future releases; and (b) to inform ;;; the T Project of noteworthy uses of this software. ;;; 3. All materials developed as a consequence of the use of this software ;;; shall duly acknowledge such use, in accordance with the usual standards ;;; of acknowledging credit in academic research. ;;; 4. Yale has made no warrantee or representation that the operation of ;;; this software will be error-free, and Yale is under no obligation to ;;; provide any services, by way of maintenance, update, or otherwise. ;;; 5. In conjunction with products arising from the use of this material, ;;; there shall be no use of the name of the Yale University nor of any ;;; adaptation thereof in any advertising, promotional, or sales literature ;;; without prior written consent from Yale in each case. ;;; ;;; Simplifying LET nodes, i.e. any call with a lambda node as the procedure. ;;; Only a small percentage of these are actually created by LET. ;;; First pass at simplifying a LET node. This does as many substitutions as ;;; possible without simplifying the body of PROC. If any arguments survive ;;; REALLY-SIMPLIFY-LET is called. (defun simplify-let (proc call) (cond ((not (arg-check-of-lambda proc call)) (mapl #'simplify (call-args call)) ; Just looking for more errors (simplify-call proc) (warn "Wrong number of args to a lambda function") nil) ((and (null (lambda-variables proc)) (null (lambda-rest-var proc))) ;; *** should this be here, or should ;; *** the open not be generated?? (bash-open call) (replace-node call (detach (lambda-body proc))) t) (t (setf (node-simplified? call) t) (remove-unused-arguments proc call) (mapl #'simplify (call-args call)) (quick-substitute-arguments proc call) (if (remove-unused-let call proc) t (really-simplify-let proc call))))) ;;; Simplify the body of PROC and then try substituting the arguments again. ;;; If all the arguments can be substituted the call node is removed. ;;; CHANGE? indicates that the let node has been moved in the tree and thus ;;; its parent must resimplify. (defun really-simplify-let (proc call) (do ((change? nil)) (()) (setf (node-simplified? proc) t) (simplify-call proc) (substitute-arguments proc call) (cond ((substitute-join-arguments proc call) (setq change? t)) ((not (node-simplified? proc))) (t (remove-unused-arguments proc call) (return (not (setf (node-simplified? call) (not (or (remove-unused-let call proc) change?))))))))) (defun remove-unused-let (call proc) (cond ((null (call-args call)) (if (lambda-rest-var proc) (walk-refs-safely (lambda (ref) (replace-node ref (create-literal-node '()))) (lambda-rest-var proc))) ;; *** should this be here, or should ;; *** the open not be generated?? (bash-open call) (replace-node call (detach (lambda-body proc))) t))) ;;; Removed unused arguments to a lambda node in call position. Destructively ;;; changes the lists of arguments and variables. (defun remove-unused-arguments (node call) (if (and (lambda-rest-var node) (not (used? (lambda-rest-var node)))) (setf (cadr (lambda-all-variables node)) nil)) (do ((vars (lambda-rest+variables node)) (args (call-proc+args call)) (exits (call-exits call) (1- exits)) (n 2)) ((null (cdr vars)) (cond ((not (used? (lambda-rest-var node))) (setf (cadr (lambda-all-variables node)) nil) (mapc #'erase-all (cdr args)) (setf (cdr args) '())))) ; Extremely Evil (cond ((used? (cadr vars)) (setf (variable-number (cadr vars)) n) (setf (node-role (cadr args)) (call-arg (1- n))) (pop vars) (pop args) (incf n)) (t (debug :sub (format t "~%Removing: ~s" (cadr vars))) (setf (cdr vars) (cddr vars)) ; Evil (erase-all (cadr args)) (setf (cdr args) (cddr args)) ; Extremely Evil (if (< 0 exits) (decf (call-exits call))))))) (defun quick-substitute-arguments (node call) (do ((vars (lambda-rest+variables node)) (args (call-proc+args call)) (exits (call-exits call) (1- exits)) (n 2)) ((null (cdr vars)) nil) (cond ((quick-substitute (cadr vars) (cadr args) n) (setf (cdr vars) (cddr vars)) ; Evil (setf (cdr args) (cddr args)) ; Extremely Evil (if (< 0 exits) (decf (call-exits call)))) (t (pop vars) (pop args) (incf n))))) ;;; Set the index (defun quick-substitute (var val n) (setf (variable-number var) n) (setf (node-role val) (call-arg (1- n))) (cond ((substitute? var val) (substitute-value var val t) t) (t nil))) ;;; Is it okay to substitute VAL for variable VAR. (defun substitute? (var val) (and ;; can't substitute for setqed vars (null (variable-setqs var)) ;; can't substitute for special vars (null (variable-special-p var)) (or (and (reference-node? val) (let ((value-var (reference-variable val))) (not (or ;; can't substitute in a value that can change (variable-setqs value-var) ;(var-setqed-in-scope-of-var value-var var) (variable-special-p value-var) ;; Prohibit substitution of global registers for now. ;; It tends to break such code as ;; '(prog1 gr::foo (incf gr::foo)) ;; - JRM 15-Mar-87 20:56:42 ;; This may not be the best way to do it, but it works. (variable-global-p value-var))))) (literal-node? val) (primop-node? val)))) ;;; This is hard to do here, because it is not the scope of SCOPE-VAR ;;; we are worried about, but the extent. If var is setqed within ;;; the extent, then we can't substitute. This is fairly easy to ;;; determine if live variable analysis has been done, but not before. ;;; Return T if VAR is setqed anywhere within the scope of SCOPE-VAR ;(defun var-setqed-in-scope-of-var (var scope-var) ; (and (variable-setqs var) ; (let ((var-binder (variable-binder var)) ; (scope-binder (variable-binder scope-var))) ; (dolist (setq (variable-setqs var)) ; (do ((parent (node-parent setq) ; (node-parent (node-parent parent)))) ; ((eq parent var-binder) nil) ; ;; if the setq is inside a subroutine, ; ;; wimp out and return T ; (let ((proc (call-proc (node-parent parent)))) ; (when (or (lambda-node? proc) ; (primop-ref? proc primop/Y)) ; (return-from var-setqed-in-scope-of-var t))) ; (when (eq parent scope-binder) ; (return-from var-setqed-in-scope-of-var t))))))) ;;; Try to substitute any arguments to LAMBDA-PROC that are lambda nodes. ;;; Three different methods are tried. SUBSTITUTE-LAMBDA? checks that there ;;; is only one reference to the variable and the reference is in call or ;;; exit position or only one call down in the tree. PARTIAL-SUBSTITUTE? ;;; determines whether it is worth duplicating the argument to do the ;;; substitution. (defun substitute-arguments (lambda-proc call-node) (mapc #'(lambda (var val) (cond ((not (used? var)) nil) ; VAL may be *EMPTY* if VAR is unused ((reference-node? val) (partial-substitute-variable var (reference-variable val))) ((not (lambda-node? val)) ; (or (object-node? val) ; (lambda-node? val))) nil) ((substitute-lambda? var) (substitute-value var val t)) ((and (lambda-node? val) (partial-substitute-lambda? val)) (partial-substitute var val)))) (lambda-variables lambda-proc) (call-args call-node))) ;;; Return T if there are any special bindings in effect at ;;; references to VAR that were not in effect when VAR was bound. (defun special-bindings-in-effect? (var) (let ((refs (variable-refs var)) (binder (variable-binder var))) (some #'(lambda (ref) (do ((lambda (node-parent (node-parent ref)) (node-parent (node-parent lambda)))) (()) (when (some #'(lambda (var) (and var (variable-special-p var))) (lambda-rest+variables lambda)) (return t)) (when (eq lambda binder) (return nil)))) refs))) (defun strong-substitute-lambda? (var) (and (null (variable-setqs var)) (null (cdr (variable-refs var))) (not (special-bindings-in-effect? var)))) (defun weak-substitute-lambda? (var) (and (null (cdr (variable-refs var))) (let ((ref (car (variable-refs var)))) (or (eq (node-role ref) call-proc) (call-exit? ref) (eq (variable-binder var) (node-parent (node-parent ref))))))) (zl:deff substitute-lambda? #'strong-substitute-lambda?) (defun partial-substitute-variable (var val) (let ((call (lambda-body (variable-binder var)))) (walk-refs-safely #'(lambda (ref) (if (eq call (node-parent ref)) (replace-node ref (create-reference-node val)))) var))) ;;; Is VAL a simple enough lambda-node to be integrated. (note "PARTIAL-SUBSTITUTE-LAMBDA? nullified because it is causing conts of GO's to be different causing STRATEGY/PROC") (defun partial-substitute-lambda? (val) nil) ; (and (or (not (lambda-rest-var val)) ; (null (variable-refs (lambda-rest-var val)))) ; (every #'(lambda (n) ; (not (lambda-node? n))) ; (call-proc+args (lambda-body val))) ;; (let ((proc (call-proc (lambda-body val)))) ;; (not (and (reference-node? proc) ;; (let ((var (reference-variable proc))) ;; (and (variable-support var) ;; (eq (support-variant (variable-support var)) ;; 'constant)))))) ; )) ;;; (eq? (variable-name var '*proclaim)) ;;; => ;;; (variable-binder (reference-variable (call-exit call))) ;;; is CALL-PROC or argument to PRIMOP/Y ;;; i.e. don't duplicate calls to unknown procedures whose continuations are ;;; calls to known procedures ;;; Substitute VAL (a lambda-node) for VAR everywhere that it can be ;;; integrated. References in non-call position need to be consed anyway ;;; so there is no reason not to substitute them as well. (defun partial-substitute (var val) (cond ((some #'(lambda (ref) (eq (node-role ref) call-proc)) (variable-refs var)) (substitute-value var val t)))) ;;; Simplifying joins... ;;; This code is attempting, in the much more intractable CPS domain, ;;; to perform the optimization: ;;; (IF (IF a b c) d e) => (IF a (IF b d e) (IF c d e)) (defun substitute-join-arguments (lambda-proc call) (do ((vars (lambda-variables lambda-proc) (cdr vars)) (vals (call-args call) (cdr vals)) (change? nil)) ((null vars) change?) (cond ((and (used? (car vars)) ; (CAR VALS) may be *EMPTY* if VAR is unused (lambda-node? (car vals)) (join-substitute (car vars) (car vals))) (setq change? t))))) (defun join-substitute (var val) (let ((calls (mapcar #'get-simple-cond-call (lambda-variables val)))) (dolist (ref (variable-refs var) nil) (let ((call (and (eq call-proc (node-role ref)) (call-and-literal-match calls (call-args (node-parent ref)))))) (when (and call (parameterize val call)) (debug :simp (format *debug-stream* "Substituting Join: ~a := ~a" (variable-unique-name var) (pp-cps-2 val))) (walk-refs-safely #'(lambda (ref) (if (eq call-proc (node-role ref)) (replace-node ref (copy-node-tree val)))) var) (debug :simp-tree (pp-cps (node-base val))) (return t)))))) (defun call-and-literal-match (calls args) (do ((calls calls (cdr calls)) (args args (cdr args))) ((or (null args) (and (car calls) (literal-node? (car args)))) (if (null calls) nil (car calls))))) (defun get-simple-cond-call (var) (and (variable-p var) (some #'simple-cond-ref (variable-refs var)))) (defun simple-cond-ref (ref) (let ((call (node-parent ref))) (and (= 2 (call-exits call)) (destructure (((cond? nil nil true? t-ref) (call-proc+args call))) (and (primop-ref? cond? primop/conditional) (primop-ref? true? primop/true?) (eq ref t-ref) call))))) ;;; ******************* ;;; this was in analyze (defun arg-check-of-lambda (proc node) (let ((left-over (- (length (call-args node)) (length (lambda-variables proc))))) (or (zerop left-over) (and (> left-over 0) (lambda-rest-var proc)))))