;;; -*- 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. ;;; ;;; Checking for user errors and gathering information... ;;; Needs to deal with object nodes (defvar *definitions* '()) (defvar *uses* '()) ;;; Return lists of variable definitions and uses. ;;; *DEFINITIONS* is a list of the variables defined in the given node tree. ;;; *USES* is a list of uses in the following form: ;;; ( ) ;;; is either 'TOP or the variable whose value contains the use. ;;; is one of 'CALL-ARG 'OPERATION, '(CALL . ), ;;; or 'WEIRD. (defun def-and-use-analyze (node) (let ((*definitions* '()) (*uses* '())) (check-call-node (lambda-body node) 'top) (values *definitions* *uses*))) ;;; Check a value node - look at the body of lambdas and the support of ;;; variables. (defun check-value-node (node loc) (if (lambda-node? node) (check-call-node (lambda-body node) loc) (let ((support (and (reference-node? node) (variable-support (reference-variable node))))) (if support (if (eq *new-support* (support-table support)) (push `(,(support-variable support) ,loc ,(node-use node)) *uses*)))))) (defun check-value-list (list loc) (dolist (node list) (check-value-node node loc))) ;;; Check calls. Checks the procedure and then dispatches on the type of ;;; the call. (defun check-call-node (call loc) (check-call-using-proc call loc) (cond ((variable-definition? call) (check-value-node (call-arg-n 1 call) loc) (check-value-node (call-arg-n 3 call) (reference-variable (call-arg-n 2 call))) (add-definition-value call loc)) ((primop-ref? (call-proc call) primop/y) (check-y (call-arg-n 1 call) loc)) ((not (eq loc 'top)) (check-value-list (call-proc+args call) loc)) ((= 0 (call-exits call)) (check-value-node (call-proc call) 'top) (check-value-list (call-args call) nil)) ((= 1 (call-exits call)) (check-value-node (call-proc call) nil) (check-value-node (call-arg-n 1 call) 'top) (check-value-list (cdr (call-args call)) nil)) (t (check-value-list (call-proc+args call) nil))) (clear-check-flags call)) ;;; Does CALL define a variable. (defun variable-definition? (call) (let ((primop (known-primop (call-proc call)))) (and primop (primop.defines-support? primop) (= 3 (length (call-args call))) (reference-node? (call-arg-n 2 call)) (variable-support (reference-variable (call-arg-n 2 call)))))) ;;; Set the support value and type of a variable. (defun add-definition-value (call loc) (destructure (((proc nil ref val) (call-proc+args call))) (let* ((var (reference-variable ref)) (support (variable-support var)) (variant (primop.support-variant (known-primop proc)))) (pushnew var *definitions* :test #'eq) (if (eq variant (support-variant support)) (add-support-value var val loc)) (if (member variant '(define constant) :test #'eq) (setf (support-type support) (get-support-type val)))))) ;(call-arg-n 3 call))))))) (defun add-support-value (var val loc) (let* ((support (variable-support var)) (val (and (eq 'constant (support-variant support)) (not (primop? (support-value support))) (eq 'top loc) (node->vector val)))) (when val (setf (support-value support) val) t))) ;;; Type check of a call using the type of the procedure. (defun check-call-using-proc (node loc) (let ((proc (call-proc node))) (cond ((literal-node? proc) (fix-call-to-literal node (literal-value proc))) ((reference-node? proc) (check-call-to-var node (reference-variable proc) loc)) ((lambda-node? proc) (if (arg-check-of-lambda proc node) (set-check-flags (lambda-variables proc) (call-args node)) (fix-call-to-lambda node proc)))))) ;;; Special procedure for checking calls to Y. (defun check-y (l-node loc) (let ((vals (mapcar #'thunk-value (cdr (call-args (lambda-body l-node)))))) (set-check-flags (cdr (lambda-variables l-node)) vals) (cond ((eq loc 'top) (check-value-node (call-arg-n 1 (lambda-body l-node)) 'top) (check-value-list vals nil)) (t (check-value-node (call-arg-n 1 (lambda-body l-node)) loc) (check-value-list vals loc))) (reset-check-flags (lambda-variables l-node)))) ;;; Variables that have known values keep those values in the VARIABLE-FLAG ;;; field for the purposes of type checking. (defun clear-check-flags (node) (if (lambda-node? (call-proc node)) (reset-check-flags (lambda-variables (call-proc node))))) (defun set-check-flags (vars args) (mapc #'(lambda (var val) (if (and var val (lambda-node? val)) (setf (variable-flag var) val))) vars args)) (defun reset-check-flags (vars) (mapc #'(lambda (var) (if var (setf (variable-flag var) nil))) vars)) ;;; Checking a call to a known variables (defun check-call-to-var (call var loc) (if (variable-binder var) (check-call-to-lexical-var call var loc) (let ((support (get-variable-support var))) (if (and support (not (eq *new-support* (support-table support)))) (check-call-to-bound-var call var support loc))))) (defun check-call-to-lexical-var (call var loc) (let ((type (variable-flag var))) (cond ((not (node-p type)) nil) ((lambda-node? type) (if (not (arg-check-of-lambda type call)) (fix-call-to-bound-lambda call var type)))))) (defun check-call-to-bound-var (call var support loc) (let ((type (support-type support))) (cond ((eq type 'literal) (fix-call-to-support-literal (call-proc call)) (replace-with-free-variable (call-proc call))) ((and (consp type) (eq (car type) 'proc) (not (arg-check-of-type type call))) (fix-call-to-support-proc (call-proc call)))))) (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))))) (defun arg-check-of-type (type node) (let ((left-over (- (length (call-args node)) (caddr type)))) (or (zerop left-over) (and (> left-over 0) (cadr type))))) ;;; The way in which a node is used. (defun node-use (node) (let ((role (node-role node))) (cond ((eq role call-proc) `(call . ,(length (call-args (node-parent node))))) ; ((object-op? role) 'operation) ((call-arg? role) 'call-arg) ;node??? (t 'weird)))) (defun use-type (use) (support-type (variable-support (car use)))) (defun check-uses (vars new-uses old-uses) (let ((left (remove-if #'use-type new-uses))) (dolist (use old-uses) (let ((type (use-type use))) (if type (check-variable-use use type) (push use left)))) left)) (defun check-variable-use (use var-type) (destructure (((var loc use-type) use)) (cond ((or (not var-type) (eq use-type 'call-arg) (eq use-type 'weird)) t) ((eq use-type 'operation) t) ; Operations are not annotated yet ((or (not (consp use-type)) (not (eq 'call (car use-type)))) (bug "unknown use-type ~S in CHECK-VARIABLE-USE" use-type)) ((eq var-type 'literal) (user-message 'warning loc "call to ~S which is bound to a literal" nil (variable-name var))) ((and (consp var-type) (eq (car var-type) 'proc)) (if (not (arg-check-of-use var-type use-type)) (user-message 'warning loc "wrong number of arguments in a call to ~A" nil (variable-name var))))))) (defun arg-check-of-use (var-type use-type) (let ((left-over (- (cdr use-type) (caddr var-type)))) (or (zerop left-over) (and (> left-over 0) (cadr var-type))))) ;;; Quick version of the above. Just finds defs and uses. This is used on ;;; integrable definitions before they are simplified. (defun quick-def-and-use-analyze (node) (let ((uses '())) (labels ((tree-walk (node) (if (lambda-node? node) (mapc #'tree-walk (call-proc+args (lambda-body node))) (let ((support (and (reference-node? node) (variable-support (reference-variable node))))) (if (and support (eq *new-support* (support-table support)) (not (member (support-variable support) uses :test #'eq))) (push (support-variable support) uses)))))) (tree-walk (call-arg-n 3 (lambda-body node)))) (values (reference-variable (call-arg-n 2 (lambda-body node))) uses)))