;;; -*- Mode:LISP; Package:(NC LISP); Readtable:CL; Base:10 -*- ;;; this is crufty, see nnreg-alloc (defun reg-alloc-top (node) (hack-open node nil nil) (let ((*regs-used* '())) (reg-alloc node))) (zl:defsubst lambda-open (node) (lambda-env node)) (defun hack-open (lambda-node open-call nested-opens) (format t "~%~a: ~a" lambda-node open-call) (setf (lambda-open lambda-node) open-call) (let ((call (lambda-body lambda-node))) (cond ((primop-node? (call-proc call)) (let ((proc (leaf-value (call-proc call)))) (cond ((eq proc primop/open) ;; push open (push open-call nested-opens) (setq open-call (leaf-value (call-arg-n 2 call)))) ((eq proc primop/y) (setq call (lambda-body (call-arg-n 1 call))))))) (;; primops are not opened (eq call open-call) ;; pop open (setq open-call (pop nested-opens)))) (dolist (arg (call-proc+args call)) (if (lambda-node? arg) (case (lambda-strategy arg) ((STRATEGY/HEAP STRATEGY/PROC) (hack-open arg nil nil)) (t (hack-open arg open-call nested-opens))))))) (defvar *regs-used* '()) (defun note-used (loc) (if (integerp loc) (push loc *regs-used*)) loc) (defun find-a-reg () (do ((reg A0 (1+ reg))) ((> reg AN) (cerror "foo" "need a local slot, alloc to stack")) (unless (member reg *regs-used*) (return reg)))) (defun mark-var-in-reg (var reg) (debug :regs (format t "~&~a: ~a" (variable-unique-name var) (if (variable-p reg) (variable-unique-name reg) reg))) (note-used reg) (setf (variable-loc var) reg)) (defun mark-vars-in-arg-regs (vars) (do ((reg A0 (1+ reg)) (vars vars (cdr vars))) ((or (null vars) (and (>= reg AN) (cerror "foo" "need an arg slot, alloc to stack")))) (mark-var-in-reg (car vars) reg))) (defun reg-alloc (node) (let ((*regs-used* *regs-used*)) (ecase (lambda-strategy node) ((STRATEGY/HEAP STRATEGY/PROC) ;; Heaped lambdas take their args in ;; the standard arg regs ;; Procs don't really have to ;; buts its probably more efficient in ;; K machine (mark-vars-in-arg-regs (cdr (lambda-variables node)))) (STRATEGY/OPEN ;; this is a continuation for a call ;; if it takes 1 arg as usual (call returns 1 value) ;; it will be in wherever it wants it because ;; the destination will be the cont var loc ;; (is this always a cont to an opened call? ;; most primops can also return in wherever we want ;; but some might not?) ;; (can also be con't to label call but that can ;; also go where it wants) (dolist (var (lambda-rest+variables node)) (if var (find-var-location var () node)))) (STRATEGY/LABEL ;; Labels take their args wherever they like (let ((rest (lambda-rest-var node))) (if rest (find-var-location rest () node))) (dolist (var (cdr (lambda-variables node))) (find-var-location var () node)))) (let ((call (lambda-body node))) (if (eq (call-proc call) primop/y) (setq call (lambda-body (call-arg-n 1 call)))) (dolist (arg (call-proc+args call)) (when (lambda-node? arg) (reg-alloc arg)))))) ;;; return the place where a variable will be located ;;; either a register or IGNORE meaning the value is not used ;;; (or stack slot or ???) (defun find-var-location (var finding &optional (lambda-node (variable-binder var))) (cond ((member var finding) '*) ((variable-loc var) ;; already found (note-used (variable-loc var))) (t (let* ((refs (variable-refs var)) ; (x (cerror "foo" "var-target")) (reg (cond ((null refs) ;no refs 'IGNORE) ((null (cdr refs)) ;one ref (ref-target (car refs) var finding lambda-node)) (t (let ((targets '())) (dolist (ref refs) (pushnew (ref-target ref var finding lambda-node) targets)) (if (null (cdr targets)) (car targets) (setq targets (delete-ignored targets)) (if (null (cdr targets)) (car targets) (setq targets (delete '* targets)) (if (and targets (null (cdr targets))) (car targets) (first-A-reg targets)) ;??? ))))))) (mark-var-in-reg var (if (or (eq reg 'A*) (eq reg '*) ;; when a var is targeted to another var which ;; is allocated, then var wants to be in the same ;; reg, unless it is already taken ;; This is grossly non-optimal because ;; some other var in the same binding list ;; might have been just alloced and not really targeted ;; to the same place (dolist (other-var (lambda-rest+variables lambda-node)) (and other-var (eql reg (variable-loc other-var)) (return t)))) (find-a-reg) reg)))))) (defun delete-ignored (targets) (delete-if #'(lambda (elt) (or (eq elt 'IGNORE) (and (variable-p elt) (or (eq (variable-loc elt) 'IGNORE) (null (variable-refs elt)))))) targets)) (defun first-A-reg (targets) (dolist (target targets 'A*) (when (and (integerp target) (>= target A0) (<= target AN)) (return target)))) ;;; Return the place where the given reference ;;; would like to find its value ;;; returns a register, ;;; * for anywhere (we really don't care) ;;; A* to allocate an A reg ;;; IGNORE for nowhere (defun ref-target (ref var finding lambda-node) (let* ((parent (node-parent ref)) (proc (call-proc parent)) (number (call-arg-number (node-role ref)))) (cond ((<= number (call-exits parent)) 'IGNORE) ((primop-node? proc) (let ((primop (primop-value proc))) (cond (;; if the reference is the value of a setq ;; which is the first call (and (eq parent (lambda-body lambda-node)) (eq primop primop/setq-lexical) (= number 3)) ;; target the variable to the setq's var (find-var-location (reference-variable (call-arg-n 2 parent)) (cons var finding))) (t ;; try to put a primops arg where it's result goes?? (if ;; unless result is also an arg ;; check if all other args are literal nodes ;; (could be cleverer) (every #'(lambda (arg) (or (literal-node? arg) (and (reference-node? arg) (eq (reference-variable arg) (reference-variable ref))))) (cdr (call-args parent))) (let ((cont (call-arg-n 1 parent))) (if (lambda-node? cont) (let ((cvar (car (lambda-variables cont)))) (if cvar (find-var-location cvar (cons var finding)) '*)) '*)) '*))))) ((lambda-node? proc) (find-var-location (nth (1- number) (lambda-variables proc)) (cons var finding))) (t (let ((label (variable-known (leaf-value proc)))) (cond (label (cond ((eq ref proc) ;; this var is bound to a proc (case (lambda-strategy label) (STRATEGY/LABEL ;;This will be a jump, no var needed 'IGNORE) (t (cerror "foo" "reference is a call-proc non LABEL")))) (t (ecase (lambda-strategy label) ;; calling known procedure, ;; put var where proc wants it (STRATEGY/LABEL (let ((label-var (nth (1- number) (lambda-variables label)))) (cond ((null label-var) (setq label-var (lambda-rest-var label)) (if (and label-var ;; ignored rest (null (variable-refs label-var))) 'IGNORE (cerror "foo" "non ignored rest or bad # args in reg-alloc"))) (;; still problems... ;; (do ((a 0 b) (b 1 a)) (())) (or ;; this reference could want to be targeted ;; to a variable which is already targeted to it ;; (do ((a 0 (1+ a)) ... (eq (variable-loc label-var) (reference-variable ref)) ;; this reference might want to be targeted ;; to its own variable (do ((a 0 a)) ... (eq label-var (reference-variable ref)) ;; var will be bound to a var, but that var still needed ;; ??? (member label-var (lambda-live lambda-node))) '*) (t (find-var-location label-var (cons var finding) label))))) (STRATEGY/OPEN ;; This happens when a continuation to a label call ;; is set as the known value of the labels continuation ;; (foo (do (... ;; it might be more tasteful to change the open to a label ;; but then allocation screws up because there is no ;; continuation arg... (find-var-location (nth (1- number) (lambda-variables label)) (cons var finding))) (STRATEGY/PROC (cerror "Foo" "allocating arg to STRATEGY/PROC call")))))) ;; unknown proc, return open reg corresponding ;; to arg position of ref ((eq parent (lambda-open lambda-node)) (- (+ (1- number) O0) (call-exits parent))) (t 'A*)))))))