;;; -*- Mode:LISP; Package:LAMBDA; Readtable:CL; Base:8 -*- ;;;;;;;;;;;;;;;;;;;;; ;;; Does this look ugly? Use a landscape. ;;;;;;;;;;;;;;;;;;;;; (defconstant *dont-count-these-instructions-in-those-that-have-no-predecessors* '(HIGHEST-KERNAL-UCODE-LOCATION) "Anything on this list is not included in the *instructions-without-predecessors*.") (defparameter *default-scanning-distance* 10. "Declarations can be this far ahead of applicable instruction.") (defvar *i-mem-array*) (defvar *a-mem-array*) (defvar *main-mem-array*) (defvar *mid-array*) (defvar *lmc-pathname*) (defvar *trace-lmc-version-number*) (defvar *trap-upc*) (defvar *illop-upc*) (defvar *highest-kernal-ucode-location+1*) (defvar *i-mem-symbols-array* (make-sequence 'vector 50000.)) (defvar *a-mem-symbols-array* (make-sequence 'vector 1024.)) (defvar *register-names* (make-sequence 'vector *number-of-registers*)) (defvar *registers* (make-hash-table :size *number-of-registers*)) (defvar *register-set-universe* (make-universe *number-of-registers*)) (defvar *m-mem-registers-set*) (defvar *constant-registers-set*) (defvar *global-registers-set*) (defvar *ucode-declarations*) ; a list ( ..) ;this subroutine takes args in these regs. ; (values ..) ;this subroutine returns values here ; (clobbers ..);clobbers or may clobber these. the server a documentation ; ; purpose and speeds up the micro-tracer. However, it is ; ; capable of figuring this out for itself. ; (local ...) ;holds state in these vars, error if caller depends on them. ; ; a-fake-microsecond-clock, for example. ; (must-avoid ) ;this subroutine, ones it calls, etc must never get to . ; ; intended mainly for (must-avoid INTR). ; (saves ( ..) ;place may be register or NIL if too complicated ; ; error if store it twice in any path and place is NON-NIL. ; (restores ( ..) ;restores from last saved. ; (suspend-flow-tracing) ;dont look past here, leaving environment of interest never to return (defvar *subroutine-entries*) (defvar *instructions-without-predecessors*) (defvar *basic-block-info*) ;;; This is where everything kicks off. If you do this by hand, be sure to follow ;;; the order of things. Many things are not idempotent and most must be done in the ;;; order shown. ;;; Diagnostic: (defun i-want-to-lose (&key (how-much? :really-big)) "Run the microcode disassembler until you lose." how-much? (format t "~%Anything you say...") (do ((count 0 (1+ count))) ((logic-has-fallen?) "Logic has fallen") (format t "~%~%Attempting to lose on pass ~d." count) (disassemble-microcode))) (defun disassemble-microcode (&optional (filename t)) ;will load latest version if t. (readin-files filename) (initial-setup) (compute-instructions) (find-subroutine-entries) (find-instructions-without-predecessors) ) (defun readin-files (filename) (multiple-value-setq (*i-mem-array* *a-mem-array* *main-mem-array* *mid-array* *lmc-pathname* *trace-lmc-version-number*) (si:load-lmc-file-into-arrays filename)) ;defined in sys:io;disk (si:load-i-mem-symbols-if-necessary *trace-lmc-version-number* *lmc-pathname*) (readin-declarations *trace-lmc-version-number* *lmc-pathname*)) (defun readin-declarations (&optional (vn *trace-lmc-version-number*) (filename *lmc-pathname*)) (setf filename (funcall filename :new-type-and-version "lmc-dcl" vn)) (with-open-file (f filename) (let* ((ibase 8) (base 8) (s-exp (read f))) (setf *ucode-declarations* (second (third s-exp)))))) ;flush (SETQ MICRO-DECLARATION-TABLE (quote ..)) (defun initial-setup () (setup-i-mem-symbols-array) (setup-a-mem-symbols-array) (setq *illop-upc* (i-mem-symbol->address "ILLOP") *trap-upc* (i-mem-symbol->address "TRAP")) (setup-register-info) ) (defun compute-instructions () (digest-i-mem-array) (digest-declarations) (compute-successors) (compute-predecessors) ) (defun setup-i-mem-symbols-array () (fill *i-mem-symbols-array* '()) (insert-i-mem-labels) (insert-relative-i-mem-labels) ) (defun insert-i-mem-labels () (let ((hash-table (cadr (assoc *trace-lmc-version-number* si:*i-mem-symbol-tables*)))) (maphash #'(lambda (symbol symbol-value) (when (array-in-bounds? *i-mem-symbols-array* symbol-value) (setf (elt *i-mem-symbols-array* symbol-value) symbol))) hash-table))) (defun insert-relative-i-mem-labels () (let (last-symbol last-symbol-value) (for-elements-in-vector *i-mem-symbols-array* #'(lambda (address entry) (if entry (setq last-symbol entry last-symbol-value address) (setf (elt *i-mem-symbols-array* address) (if (and last-symbol ;When last symbol is close enough, (< (- address last-symbol-value) 100.)) ;number relative to it. (list last-symbol (- address last-symbol-value)) address))))))) ;Otherwise just use the number. (defun address->i-mem-symbol (address) (when address (elt *i-mem-symbols-array* address))) (defun i-mem-symbol->address (symbol) (si:i-mem-symeval symbol *trace-lmc-version-number*)) (defun setup-a-mem-symbols-array () (dolist (symbol-address-pair (third (assoc *trace-lmc-version-number* si:*i-mem-symbol-tables*))) (setf (elt *a-mem-symbols-array* (first symbol-address-pair)) (second symbol-address-pair)))) (defun address->a-mem-symbol (address) (when (array-in-bounds? *a-mem-symbols-array* address) (elt *a-mem-symbols-array* address))) ;;; Registers (defun setup-register-info () (fill *register-names* '()) (let* ((symbol-table-list (assoc *trace-lmc-version-number* si:*i-mem-symbol-tables*)) (register-alist (third symbol-table-list)) (info-list (fourth symbol-table-list))) (setup-register-names register-alist) (check-a-mem-consistancy) (setup-register-set-universe) (setup-constants-set (get-from-alternating-list info-list 'a-constant-list)) (setup-globals-set) (setup-m-mem-set))) (defun setup-register-names (register-alist) (dolist (name-register-pairs register-alist) (let ((register-number (first name-register-pairs)) (register-name (second name-register-pairs))) (when (and (number? register-number) (array-in-bounds? *register-names* register-number)) (setf (elt *register-names* register-number) register-name))))) (defun check-a-mem-consistancy () "This just makes a-mem-symbol-array and register-names consistant" (for-elements-in-vector *a-mem-symbols-array* #'(lambda (index a-mem-name) (let ((register-name (register-number->symbol index))) (if a-mem-name (if register-name (unless (eq? a-mem-name register-name) (ferror nil "Name mismatch between ~S in a-mem and ~S register at ~D" a-mem-name register-name index)) (setf (elt *register-names* index) a-mem-name)) (if register-name (setf (elt *a-mem-symbols-array* index) register-name) (let ((name (intern (format nil "REGISTER-~D" index)))) (setf (elt *register-names* index) name) (setf (elt *a-mem-symbols-array* index) name)))))))) (defun setup-register-set-universe () ;; Setup universe for source/clobbers sets. ;; We assume that lower number registers are used more often. ;; This assumption is implementation dependant, but will only cause speed lossage ;; if it is not true. (for-elements-in-vector *register-names* (lambda (ignore-index name) ignore-index (element->number-inserting name *register-set-universe*))) (setq *default-universe* *register-set-universe*)) (defun setup-constants-set (assembly-constant-list) (setq *constant-registers-set* (list->set *constant-registers*)) (dolist (constant-register-pair assembly-constant-list) (let ((name (register-number->symbol (second constant-register-pair)))) (unless (null? name) (set-push! name *constant-registers-set*)))) (dolist (register *registers-used-for-M-constants*) ;;See beginning of file. (set-push! (register-number->symbol register) *constant-registers-set*))) (defun setup-globals-set () (setq *global-registers-set* (list->set *global-registers*))) (defun setup-m-mem-set () (setq *m-mem-registers-set* (list->set (sequence->list (subseq *a-mem-symbols-array* *m-mem-low-bound* *m-mem-high-bound*))))) (defun symbol->register-number (symbol) (si:register-symeval symbol *trace-lmc-version-number*)) (defun register-number->symbol (number) (elt *register-names* number)) (defun register-exists? (register-name) (not (null? (symbol->register-number register-name)))) (defun constant-register? (register) (set-element? register *constant-registers-set*)) ;;; Instructions. (defconstant *instruction-mask* 77777777777777777777) ;;ldb lossage? (defconstant-now *dispatch-address-mask* 177777) (defun valid-instruction? (instruction) (not (null? instruction))) (defun no-op? (instruction) (zero? (logand instruction *instruction-mask*))) ;;; Slow extractors ;(defun extractor (field) ; #'(lambda (fixinst) ; (ldb field fixinst))) ;(defun extract-flag (field) ; #'(lambda (fixinst) ; (= 1 (funcall (extractor field) fixinst)))) ;(defun extract-symbolic (field symbol-map) ; (let ((lookup (appropriate-lookup symbol-map))) ; #'(lambda (fixinst) ; (funcall lookup (funcall (extractor field) fixinst))))) ;(defun appropriate-lookup (alist-or-vector) ; (if (list? alist-or-vector) ; #'(lambda (value) (second (assoc value alist-or-vector))) ; #'(lambda (value) (aref alist-or-vector value)))) ;;; Fast extractors (defmacro defextractor (name field) `(DEFMACRO ,name (FIXINST) `(LDB ,,field ,fixinst))) (defmacro defflag-extractor (name field) `(DEFMACRO ,name (FIXINST) `(= 1 (LDB ,,field ,fixinst)))) (defmacro defsymbolic-extractor (name field symbol-table) `(DEFMACRO ,name (FIXINST) (IF (LIST? ,symbol-table) `(SECOND (ASSOC (LDB ,,field ,fixinst) (QUOTE ,,symbol-table) :TEST #'=)) `(ELT ,,symbol-table (LDB ,,field ,fixinst))))) (defconstant-now opcode-map (vector 'alu-op 'byte-op 'jump-op 'dispatch-op)) (defeq-test alu-op? 'alu-op) (defeq-test byte-op? 'byte-op) (defeq-test jump-op? 'jump-op) (defeq-test dispatch-op? 'dispatch-op) (defsymbolic-extractor raw-opcode-extractor lam-ir-op opcode-map) (defun extract-opcode (instruction) (if (no-op? instruction) 'no-op (raw-opcode-extractor instruction))) (defflag-extractor extract-popj-after-next lam-ir-popj-after-next) (defconstant-now jump-condition-map `((,lam-jump-cond-bit-set BIT-SET) (,lam-jump-cond-bit-clear BIT-CLEAR) (,lam-jump-cond-m=a M>=A) (,lam-jump-cond-m>a M>A) (,lam-jump-cond-m-neq-a M-NEQ-A) (,lam-jump-cond-no-page-fault NO-PAGE-FAULT) (,lam-jump-cond-data-type-not-equal DATA-TYPE-NOT-EQUAL) (,lam-jump-cond-data-type-equal DATA-TYPE-EQUAL))) (defeq-test unconditional? 'unconditional) (defconstant-now jump-rpn-map (vector 'jump-xct-next 'jump 'call-xct-next 'call 'return-xct-next 'return 'illegal-rpn 'illegal-rpn)) (defsymbolic-extractor extract-jump-condition lam-ir-jump-cond jump-condition-map) (defsymbolic-extractor extract-jump-rpn-bits lam-ir-rpn jump-rpn-map) (defextractor extract-jump-address lam-ir-jump-addr) (defconstant-now dispatch-rpn-map (vector 'jump-xct-next 'jump 'call-xct-next 'call 'return-xct-next 'return 'fall-through 'skip)) (defflag-extractor extract-dispatch-push-own-address lam-ir-disp-lpc) (defextractor extract-dispatch-base-address lam-ir-disp-addr) (defextractor extract-dispatch-bits lam-ir-disp-bytl) (defsymbolic-extractor extract-dispatch-rpn-bits lam-disp-rpn-bits dispatch-rpn-map) (defmacro extract-dispatch-address-from-entry (entry) `(LOGAND ,entry ,*dispatch-address-mask*)) (defextractor extract-a-source lam-ir-a-src) (defextractor extract-m-source-address lam-ir-m-src-adr) (defflag-extractor extract-functional-source-flag lam-ir-func-src-flag) (defflag-extractor extract-a-memory-destination-flag lam-ir-a-mem-dest-flag) (defextractor extract-a-memory-destination lam-ir-a-mem-dest) (defextractor extract-m-memory-destination lam-ir-m-mem-dest) (defun extract-instruction-sources (fixinst) (let ((opcode (extract-opcode fixinst)) (a-source (extract-a-source fixinst)) (m-source-address (extract-m-source-address fixinst)) (functional-source-flag (extract-functional-source-flag fixinst)) (ans (make-empty-set))) (unless (dispatch-op? opcode) (set-push! (address->a-mem-symbol a-source) ans)) (when (null? functional-source-flag) (set-push! (address->a-mem-symbol m-source-address) ans)) ans)) (defun extract-instruction-destination (fixinst) (let ((opcode (extract-opcode fixinst))) (if (or (alu-op? opcode) (byte-op? opcode)) (let ((address (if (extract-a-memory-destination-flag fixinst) (extract-a-memory-destination fixinst) (extract-m-memory-destination fixinst)))) (make-set (address->a-mem-symbol address))) (make-empty-set)))) (defstruct (instruction (:constructor make-instruction-internal) (:callable-constructors nil) ;; This makes the constructor a macro. (:print-function print-instruction) ) numeric-form ;; Fresh from the binary file address (declarations '()) opcode-type ;; alu-op, byte-op, jump-op, dispatch-op popj-after-next sources destination successors predecessors calling-subroutine trace-info trace-warnings ) (defsynonym instruction? instruction-p) (defalike #'instruction? #'eq?) (defun print-instruction (instruction stream level) (if (and *print-level* (> level *print-level*)) (format stream " # ") (format stream "#" `(,(instruction-address instruction) ;punctuation! ; ,(instruction-numeric-form instruction) ,(instruction-opcode-type instruction) ,@(when (jump-op? (instruction-opcode-type instruction)) (list (extract-jump-condition (instruction-numeric-form instruction)))) ,@(when (instruction-popj-after-next instruction) '(POPJ-AFTER-NEXT)) (SUCCESSORS ,@(map 'list #'names-of-successors (instruction-successors instruction))) ,@(instruction-declarations instruction)) ))) (defun names-of-successors (successor) (labels ( (maybe-successor-name (instruction) (when (instruction? instruction) (instruction-address instruction))) ) (if (instruction? successor) (instruction-address successor) (cons (first successor) (map 'list #'maybe-successor-name (rest successor)))))) (defvar *instruction-array* (make-sequence 'vector 50000 :initial-element '())) (defun instruction-at (address) (elt *instruction-array* address)) (defsetf instruction-at (address) (new-value) `(SETF (ELT *INSTRUCTION-ARRAY* ,address) ,new-value)) (defun for-each-instruction (operation) (for-elements-in-vector *instruction-array* #'(lambda (address possible-instruction) (when possible-instruction (funcall operation possible-instruction address))))) (defun make-instruction (fixinst address) (make-instruction-internal :numeric-form fixinst :address address :opcode-type (extract-opcode fixinst) :popj-after-next (extract-popj-after-next fixinst) :sources (extract-instruction-sources fixinst) :destination (extract-instruction-destination fixinst) )) (defun flush-field (field-selector) "Takes a symbol specifying a field-selector and removes this field from all instructions. Must be passed a symbol because of setf lossage (see this code)." ;; *Sigh* You can't pass a selector to setf! (let ((flusher (compile-lambda `(LAMBDA (INSTRUCTION) (WHEN INSTRUCTION (SETF (,field-selector INSTRUCTION) '())))))) (map '() flusher *instruction-array*))) (defun digest-i-mem-array () (for-elements-in-vector *i-mem-array* #'(lambda (index fixinst) (when (null? fixinst) (return-from digest-i-mem-array nil)) (when (valid-instruction? fixinst) (setf (instruction-at index) (make-instruction fixinst (address->i-mem-symbol index))))))) ;;; Declarations ;;; ;;; We play a little fast and loose with abstraction here, so watch out! (defconstant *set-declarations* '(args values clobbers local)) (defconstant *saves-tag* 'saves) (defconstant *restores-tag* 'restores) (defsynonym declaration-type car) (defsynonym declaration-info cdr) (defun declaration-type? (type) #'(lambda (declaration) (eq? (declaration-type declaration) type))) (defun spread-declarations (instruction &rest decls-return-map) (let ((decls (instruction-declarations instruction))) (labels ( (find-decl (type) (or (cdr (assoc type decls)) (when (member type *set-declarations*) (make-empty-set)))) ) (values-list (map 'list #'find-decl decls-return-map))))) (deff saves-declaration? (declaration-type? *saves-tag*)) (deff restores-declaration? (declaration-type? *restores-tag*)) (defun suspend-flow-tracing? (instruction) (assoc 'suspend-flow-tracing (instruction-declarations instruction))) (defun instruction-saves? (instruction) (assoc *saves-tag* (instruction-declarations instruction))) (defun instruction-restores? (instruction) (assoc *restores-tag* (instruction-declarations instruction))) (defun digest-declarations () (dolist (declarations *ucode-declarations*) (let ((address (first declarations))) (if (> address (length *instruction-array*)) (format t "~&Declaration ~s ignored: address too high." declarations) (dolist (declaration (rest declarations)) (associate-declaration-with-instruction address declaration)))))) (defun associate-declaration-with-instruction (address declaration) (case (declaration-type declaration) ((must-avoid suspend-flow-tracing) (push declaration (instruction-declarations (instruction-at address)))) (saves (associate-saves-with-instruction address (declaration-info declaration))) (restores (associate-restores-with-instruction address (declaration-info declaration))) (otherwise (push (cons (first declaration) (list->set (rest declaration))) (instruction-declarations (instruction-at address)))))) (defsubst make-test-triplet (predicate win-action lose-action) (list predicate win-action lose-action)) (defsynonym test-triplet-predicate first) (defsynonym test-triplet-win-action second) (defsynonym test-triplet-lose-action third) (defun scan-forward-to-appropriate-instruction (start-address test-triplets &optional (how-far-to-scan *default-scanning-distance*)) (dotimes (scan how-far-to-scan (when test-triplets (dolist (test-t test-triplets) (funcall (test-triplet-lose-action test-t))))) (let ((instruction (instruction-at (+ start-address scan))) (triplets-left '())) (dolist (test-t test-triplets) (if (funcall (test-triplet-predicate test-t) instruction) (funcall (test-triplet-win-action test-t) instruction) (push test-t triplets-left))) (unless triplets-left (return-from scan-forward-to-appropriate-instruction nil)) (setq test-triplets triplets-left)))) (defun scanned-declaration (tester win-action lose-action) #'(lambda (start-address declaration-guts) (let (test-triplets) (dolist (decl declaration-guts) (let ((possible-predicate (apply tester decl))) (unless (null? possible-predicate) (push (make-test-triplet possible-predicate (apply win-action decl) (apply lose-action start-address decl)) test-triplets)))) (scan-forward-to-appropriate-instruction start-address test-triplets)))) (defun tester (source destination barf-string) (if (and (register-exists? source) (register-exists? destination)) #'(lambda (instruction) (and (set-element? source (instruction-sources instruction)) (set-element? destination (instruction-destination instruction)))) (format t barf-string source destination) nil)) (defun tagger (source destination illegality-test tag) #'(lambda (instruction) (funcall illegality-test instruction) (pushnew (list tag) (instruction-declarations instruction)) (setf (instruction-sources instruction) (make-set source)) (setf (instruction-destination instruction) (make-set destination)))) (defun loser (barf-string source destination location) #'(lambda () (format t barf-string source destination (instruction-address (instruction-at location))))) (defun might-instruction-save? (from-register into-place) (tester from-register into-place "~&Ignoring save ~S into ~S.")) (defun tag-as-save-instruction (from into) (tagger from into #'(lambda (instruction) (when (instruction-restores? instruction) (format t "Invalid declaration: save and restore in ~S"))) *saves-tag*)) (defun losing-save (where from into) (loser "~&Cannot match save ~S into ~S at ~S." from into where)) (deff associate-saves-with-instruction (scanned-declaration #'might-instruction-save? #'tag-as-save-instruction #'losing-save)) (defun might-instruction-restore? (register from-register) (tester from-register register "~&Ignoring restore from ~S into ~S.")) (defun tag-as-restore-instruction (into from) (tagger from into #'(lambda (instruction) (when (instruction-saves? instruction) (format t "Invalid declaration: save and restore in ~S"))) *restores-tag*)) (defun losing-restore (where into from) (loser "~&Cannot match restore from ~S into ~S at ~S." from into where)) (deff associate-restores-with-instruction (scanned-declaration #'might-instruction-restore? #'tag-as-restore-instruction #'losing-restore)) (defvar *losing-successors* '() "Holds list of instruction addresses. Instructions at these addresses have successors that do not exist.") (defun compute-successors () (for-each-instruction #'(lambda (instruction index) (setf (instruction-successors instruction) (compute-instruction-successors instruction index))))) ;;; Uncomment these (and comment the macros) in order to check for the ;;; validity of successors. ;(defun verified-instruction (address referenced-by) ; (let ((instruction (instruction-at address)) ; (reference-symbolic-address (instruction-address (instruction-at referenced-by)))) ; #'(lambda (message) ; (when (null? instruction) ; (pushnew reference-symbolic-address *losing-successors* :test #'equal?) ; (fresh-line) ; (format t message '()) ; (format t " at ~S " address) ; (format t " referenced by instruction at ~S " reference-symbolic-address)) ; instruction))) ;(defun check-verification (instruction-or-null message) ; (if (null? instruction-or-null) ; `() ; (funcall instruction-or-null message))) (defmacro verified-instruction (address ignore-referenced-by) ignore-referenced-by `(INSTRUCTION-AT ,address)) (defmacro check-verification (instruction ignore-message) ignore-message instruction) ;;; format: ;;; (CALLS ) ;;; (JUMP-XCT-NEXT ) ;;; (RETURNS () ) (defsynonym instruction-xfered-to second) (defsynonym instruction-xct-along-the-way third) (defsynonym instruction-returned-to fourth) (defun make-normal-successor (instruction) (check-verification instruction "Missing instruction in stream")) (defun make-jump-successor (instruction) (check-verification instruction "Jump to missing instruction")) (defun make-dispatch-jump-successor (instruction) (check-verification instruction "Dispatch jump to missing instruction")) (defun make-dispatch-fall-through-successor (instruction) (check-verification instruction "Dispatch fall through to missing instruction")) (defun make-dispatch-skip-successor (instruction) (check-verification instruction "Dispatch skip to missing instruction")) (defun make-call-successor (instruction-called instruction-xct-next instruction-returned-to) `(CALLS ,(check-verification instruction-called "Call to missing instruction") ,(check-verification instruction-xct-next "Missing instruction xct-next'd during call") ,(check-verification instruction-returned-to "Return to missing instruction"))) (defun make-return-successor (instruction-returned-to instruction-xct-next) `(RETURNS ,(check-verification instruction-returned-to "Return to missing instruction") ,(check-verification instruction-xct-next "Missing instruction xct-next'd during return"))) (defun make-jump-xct-next-successor (instruction-xfered-to instruction-xct-next) `(JUMP-XCT-NEXT ,(check-verification instruction-xfered-to "Jump to missing instruction") ,(check-verification instruction-xct-next "Missing instruction xct-next'd during jump"))) (defun list-successor-type? (type) #'(lambda (successor) (and (list? successor) (eq? (first successor) type)))) (deff call-successor? (list-successor-type? 'calls)) (deff returns-successor? (list-successor-type? 'returns)) (deff jump-xct-next-successor? (list-successor-type? 'jump-xct-next)) (defsynonym next-instruction-successor? instruction?) (defun compute-instruction-successors (instruction in-address) (let ((numeric-form (instruction-numeric-form instruction)) (popj-after-next (instruction-popj-after-next instruction)) (next-instruction (verified-instruction (1+ in-address) in-address)) (after-next-instruction (verified-instruction (+ 2 in-address) in-address))) (case (instruction-opcode-type instruction) ((no-op alu-op byte-op) (if popj-after-next (list (make-return-successor '() next-instruction)) (list (make-normal-successor next-instruction)))) (jump-op (compute-jump-successors popj-after-next next-instruction after-next-instruction (verified-instruction (extract-jump-address numeric-form) in-address) (extract-jump-condition numeric-form) (extract-jump-rpn-bits numeric-form))) (dispatch-op (compute-dispatch-successors in-address popj-after-next instruction next-instruction after-next-instruction (extract-dispatch-push-own-address numeric-form) (extract-dispatch-base-address numeric-form) (extract-dispatch-bits numeric-form))) ))) (defun compute-jump-successors (popj-after-next next-instruction after-next-instruction to-address condition rpn-bits) (let ((ans '())) (labels ( (cannot-popj-after-next () (when popj-after-next (ferror nil "Popj-after-next combined with ~S" rpn-bits))) (can-fall-through () (unless (unconditional? condition) (can-go-to (make-normal-successor next-instruction)))) (can-go-to (where) (push where ans))) (case rpn-bits (jump-xct-next (cannot-popj-after-next) (can-go-to (make-jump-xct-next-successor to-address next-instruction)) (can-fall-through)) (jump (cannot-popj-after-next) (can-go-to (make-jump-successor to-address)) (can-fall-through)) (call-xct-next (cannot-popj-after-next) (can-go-to (make-call-successor to-address next-instruction after-next-instruction)) (can-fall-through)) (call (cannot-popj-after-next) (can-go-to (make-call-successor to-address '() next-instruction)) (can-fall-through)) (return-xct-next (cannot-popj-after-next) (can-go-to (make-return-successor '() next-instruction)) (can-fall-through)) (return (if popj-after-next (can-go-to (make-return-successor '() next-instruction)) (can-go-to (make-return-successor '() '()))) (unless (unconditional? condition) (can-fall-through))) (illegal-rpn (ferror nil "Illegal rpn bits in jump")))) ans)) (defun compute-dispatch-successors (dispatched-from-location popj-after-next instruction next-instruction after-next-instruction push-own-address? base-address bits) dispatched-from-location ;;This is not used if verification is off. (let ((number-of-dispatch-options (expt 2 bits)) (return-address (if push-own-address? instruction next-instruction)) (return-address-if-xct-next (if push-own-address? next-instruction after-next-instruction)) (ans '())) (labels ( (can-go-to (where) (pushnew where ans :test #'equal?))) (dotimes (option number-of-dispatch-options) (let* ((dispatch-entry (elt *a-mem-array* (+ base-address option))) (rpn-bits (extract-dispatch-rpn-bits dispatch-entry)) (dispatch-address (verified-instruction (extract-dispatch-address-from-entry dispatch-entry) dispatched-from-location))) (can-go-to (case rpn-bits (jump-xct-next (make-jump-xct-next-successor dispatch-address next-instruction)) (jump (make-dispatch-jump-successor dispatch-address)) (call-xct-next (make-call-successor dispatch-address return-address return-address-if-xct-next)) (call (make-call-successor dispatch-address '() return-address)) (return-xct-next (make-return-successor '() next-instruction)) (return (make-return-successor '() '())) (fall-through (if popj-after-next (make-return-successor '() next-instruction) (make-dispatch-fall-through-successor next-instruction))) (skip (if popj-after-next (make-return-successor '() '()) ;; Is this right? (make-dispatch-skip-successor after-next-instruction))))) ))) ans)) (defun compute-predecessors () (for-each-instruction #'(lambda (instruction address) address ;; is not needed here. (dolist (successor (instruction-successors instruction)) (unless (null? successor) (labels ( (preceeds (predecessor successor) (unless (null? successor) (push predecessor (instruction-predecessors successor)))) ) (if (instruction? successor) (preceeds instruction successor) (case (first successor) (calls (preceeds `(CALLED ,instruction ,(instruction-xct-along-the-way successor) ,(instruction-returned-to successor)) (instruction-xfered-to successor)) (preceeds `(XCT-NEXTED ,instruction) (instruction-xct-along-the-way successor)) (preceeds `(RETURNED-TO ,instruction) (instruction-returned-to successor))) (jump-xct-next (preceeds `(JUMP-XCT-NEXTED ,instruction ,(instruction-xct-along-the-way successor)) (instruction-xfered-to successor)) (preceeds `(XCT-NEXTED ,instruction) (instruction-xct-along-the-way successor))) (returns (preceeds `(XCT-NEXTED ,instruction) (instruction-xct-along-the-way successor))))))))))) (defun for-instruction-successors (predicate operation) (for-each-instruction #'(lambda (instruction address) address;; is ignored (dolist (successor (instruction-successors instruction)) (when (funcall predicate successor) (funcall operation successor)))))) (defun find-subroutine-entries () (setq *subroutine-entries* '()) (for-instruction-successors #'call-successor? #'(lambda (call-successor) (pushnew (instruction-xfered-to call-successor) *subroutine-entries*)))) (defun find-instructions-without-predecessors () (setq *instructions-without-predecessors* '()) (for-each-instruction #'(lambda (instruction address) address ;; is ignored (when (null? (instruction-predecessors instruction)) (unless (member (instruction-address instruction) *dont-count-these-instructions-in-those-that-have-no-predecessors*) (push instruction *instructions-without-predecessors*)))))) (defun find-basic-blocks () (setq *basic-block-info* '()) (for-each-instruction #'(lambda (instruction address) (check-basic-blockness instruction address))) (setq *basic-block-info* (cons *trace-lmc-version-number* (list *basic-block-info*)))) ;;; Doesn't seem to be any reasonable way to do this. (defun check-basic-blockness (instruction address) (let ((predecessors (instruction-predecessors instruction))) (labels ( (is-block () (push address *basic-block-info*)) (isnt-block () (values))) (if (or (null? predecessors) (cdr predecessors)) (is-block) (let ((p (car predecessors))) (if (list? p) (ecase (car p) (called (is-block)) ;; this is wrong, but probably what is wanted. (returned-to (is-block)) ((jump-xct-nexted xct-nexted) (if (null? (cdr (instruction-successors (cadr p)))) (isnt-block) (is-block)))) (if (null? (cdr (instruction-successors p))) (isnt-block) (is-block))))))))