;;; FEF Disassembler -*- Mode:LISP; Package:COMPILER; Readtable:CL; Base:8 -*- ; ** (c) Copyright 1980 Massachusetts Institute of Technology ** ;;; This stuff is used by SYS: DEBUGGER; EH >. If you change things around, ;;; make sure not to break that. ;;; "We could always add another MISC instruction (another misconstruction)." ;;; -- Heard outside RG's area ;; Aesthetic note: ;; do not change (princ 'foo) or (format t "~A" 'foo) into (format t "FOO") ;; as that loses when *print-case* neq :upcase (DEFVAR DISASSEMBLE-OBJECT-OUTPUT-FUN NIL) (DEFUN DISASSEMBLE (FUNCTION &AUX FEF LIM-PC ILEN (DISASSEMBLE-OBJECT-OUTPUT-FUN NIL)) "Print a disassembly of FUNCTION on *STANDARD-OUTPUT*. FUNCTION can be a compiled function, an LAMBDA-expression (which will be compiled), or a function spec (whose definition will be used)." (DO ((FUNCTION FUNCTION)) (()) (COND ((TYPEP FUNCTION 'COMPILED-FUNCTION) (SETQ FEF FUNCTION) (RETURN)) ((MEMQ (CAR-SAFE FUNCTION) '(LAMBDA NAMED-LAMBDA ZL:SUBST CL:SUBST NAMED-SUBST)) (SETQ FEF (COMPILE-LAMBDA FUNCTION (GENSYM))) (FORMAT T "~%Definition of compilation of function") (RETURN)) ((EQ (CAR-SAFE FUNCTION) 'MACRO) (FORMAT T "~%Definition as macro") (SETQ FUNCTION (CDR FUNCTION))) ((TYPEP FUNCTION 'CLOSURE) (FORMAT T "~%Definition of closed-over function") (SETQ FUNCTION (CLOSURE-FUNCTION FUNCTION))) (T ;>> Should mention if function is encapsulated. (SETQ FUNCTION (SI:DWIMIFY-PACKAGE FUNCTION)) (SETQ FUNCTION (FDEFINITION (SI:UNENCAPSULATE-FUNCTION-SPEC FUNCTION)))))) ;>> Print arglist? (WHEN (GET-MACRO-ARG-DESC-POINTER FEF) (SI::DESCRIBE-FEF-ADL FEF) (TERPRI)) (SETQ LIM-PC (DISASSEMBLE-LIM-PC FEF)) (DO ((PC (FEF-INITIAL-PC FEF) (+ PC ILEN))) (( PC LIM-PC)) (TERPRI) (SETQ ILEN (DISASSEMBLE-INSTRUCTION FEF PC))) (TERPRI) FUNCTION) (DEFF DISASSEMBLE-INSTRUCTION-LENGTH 'FEF-INSTRUCTION-LENGTH) (DEFF DISASSEMBLE-FETCH 'FEF-INSTRUCTION) (DEFF DISASSEMBLE-LIM-PC 'FEF-LIMIT-PC) (DEFUN DISASSEMBLE-LEXICAL-REFERENCE-INSTRUCTION-P (FEF PC) (IF ( PC (DISASSEMBLE-LIM-PC FEF)) NIL (LET* ((WD (DISASSEMBLE-FETCH FEF PC)) (OP (LDB (BYTE 4 #o11) WD)) (SUBOP (LDB (BYTE 3 #o15) WD)) (DISP (LDB (BYTE #o11 0) WD))) (cond (( OP #o15) NIL) ((BIT-TEST 1 SUBOP) (SETQ DISP (+ DISP #o1000))) ((< DISP #o200) NIL) (t (MEMQ (AREF (SYMBOL-FUNCTION 'MICRO-CODE-SYMBOL-NAME-AREA) (- DISP #o200)) '(%LOAD-FROM-HIGHER-CONTEXT %STORE-IN-HIGHER-CONTEXT %LOCATE-IN-HIGHER-CONTEXT))))))) (DEFUN DISASSEMBLE-INSTRUCTION (FEF PC &AUX ILEN) "Print on *STANDARD-OUTPUT* the disassembly of the instruction at PC in FEF. Returns the length of that instruction." (SETQ ILEN (DISASSEMBLE-INSTRUCTION-LENGTH FEF PC)) (BLOCK NIL (FORMAT T "~3D " PC) (LET* ((WD (DISASSEMBLE-FETCH FEF PC)) (OP (LDB (BYTE 4 #o11) WD)) (SUBOP (LDB (BYTE 3 #o15) WD)) (DEST (LDB (BYTE 2 #o16) WD)) (DISP (LDB (BYTE #o11 0) WD)) (REG (LDB (BYTE 3 6) WD)) SECOND-WORD) (WHEN (= ILEN 2) (INCF PC) (SETQ SECOND-WORD (DISASSEMBLE-FETCH FEF PC)) ;; If a two-word insn has a source address, it must be an extended address, ;; so set up REG and DISP to be right for that. (UNLESS (= OP #o14) (SETQ REG (LDB (BYTE 3 6) SECOND-WORD) DISP (DPB (LDB (BYTE 4 #o11) SECOND-WORD) (BYTE 4 6) (LDB (BYTE 6 0) SECOND-WORD))))) (IF (< OP #o11) (SETQ OP (LDB (BYTE 5 #o11) WD))) (COND ((ZEROP WD) (PRINC 0)) ((< OP #o11) ;DEST/ADDR (cond ((and (eq op 2) (eq dest 1)) ;move d-pdl (let (n name) (princ 'push) (cond ((not (and (disassemble-lexical-reference-instruction-p fef (+ pc ilen)) (fixnump (setq n (disassemble-address-contents fef reg disp second-word))) (setq name (disassemble-lexical-ref-name fef n)))) (disassemble-address fef reg disp second-word)) (t (format t " (lexical ref ~D,~D)~30,4T;" (ldb (byte 12. 12.) n) (ldb (byte 12. 0) n)) (if (eq (car-safe name) 'function) (format t "#'~S" (cadr name)) (prin1 name)))))) (t (FORMAT T "~A ~A" (NTH OP '(CALL CALL0 MOVE CAR CDR CADR CDDR CDAR CAAR)) (NTH DEST '(D-IGNORE D-PDL D-RETURN D-LAST))) (DISASSEMBLE-ADDRESS FEF REG DISP SECOND-WORD)))) ((= OP #o11) ;ND1 (cond ((zerop subop) (FORMAT T "~A local slot ~D" 'MAKE-LEXICAL-CLOSURE-TOP-LEVEL DISP)) (t (PRINC (NTH SUBOP '( + - * ZL-/ LOGAND LOGXOR LOGIOR))) (DISASSEMBLE-ADDRESS FEF REG DISP SECOND-WORD)))) ((= OP #o12) ;ND2 (PRINC (NTH SUBOP '(= > < EQ SETE-CDR SETE-CDDR INCF DECF #|SETE-CDR SETE-CDDR SETE-1+ SETE-1-|#))) (DISASSEMBLE-ADDRESS FEF REG DISP SECOND-WORD)) ((= OP #o13) ;ND3 (PRINC (NTH SUBOP '( BIND-NIL BIND-POP SET-NIL SET-ZERO PUSH-ADDRESS MOVEM POP))) (DISASSEMBLE-ADDRESS FEF REG DISP SECOND-WORD)) ((= OP #o14) ;BRANCH (PRINC (NTH SUBOP '(BR BR-NIL BR-NOT-NIL BR-NIL-POP BR-NOT-NIL-POP BR-ATOM BR-NOT-ATOM ))) (IF (> DISP #o400) (SETQ DISP (LOGIOR #o-400 DISP))) ;Sign-extend (cond ((NEQ DISP -1) ;; One word (FORMAT T " ~D" (+ PC DISP 1))) (t ;; Long branch (SETQ DISP SECOND-WORD) (IF ( DISP #o100000) (SETQ DISP (LOGIOR #o-100000 DISP))) (FORMAT T " ~A ~D" 'LONG (+ PC DISP 1)) (RETURN nil)))) ((= OP #o15) ;MISC (FORMAT T "(~A) " 'MISC) ;Moon likes to see this (IF (BIT-TEST 1 SUBOP) (SETQ DISP (+ DISP #o1000))) (COND ((< DISP #o200) (FORMAT T "~A (~D) " (NTH (LDB (BYTE 4 4) DISP) '(ZL:AR-1 ARRAY-LEADER %INSTANCE-REF UNUSED-AREFI-3 AS-1 STORE-ARRAY-LEADER %INSTANCE-SET )) (+ (LDB (BYTE 4 0) DISP) ;; bagbiting %instance-ref is 1-based. Struth. (IF (MEMQ (LDB (BYTE 2 4) DISP) '(2 6)) 1 0)))) ((< DISP #o220) (FORMAT T "~A ~D binding~:P " 'UNBIND (- DISP #o177));200 does 1 unbind. (AND (ZEROP DEST) (RETURN))) ((< DISP #o240) (FORMAT T "~A ~D time~:P " 'PDL-POP (- DISP #o220)) ;220 does 0 pops. (AND (ZEROP DEST) (RETURN))) ((= DISP #o460) ;(GET 'INTERNAL-FLOOR-1 'QLVAL) (PRINC (NTH DEST '(FLOOR CEILING TRUNCATE ROUND))) (PRINC " one value to stack") (SETQ DEST NIL)) ((= DISP #o510) ;(GET 'INTERNAL-FLOOR-2 'QLVAL) (PRINC (NTH DEST '(FLOOR CEILING TRUNCATE ROUND))) (PRINC " two values to stack") (SETQ DEST NIL)) (T (LET ((OP (AREF (SYMBOL-FUNCTION 'MICRO-CODE-SYMBOL-NAME-AREA) (- DISP #o200)))) (IF (NULL OP) (FORMAT T "#~O " DISP) (FORMAT T "~A " OP))))) (WHEN DEST (PRINC (NTH DEST '(D-IGNORE D-PDL D-RETURN D-LAST))))) ((= OP #o16) ;ND4 (CASE SUBOP (0 (FORMAT T "~A local slot ~D" 'LEXICAL-CLOSURE-DISCONNECT DISP)) (1 (LET ((LOCALNUM (LDB (BYTE #o12 0) (NTH DISP ;; This is in reverse order in the FEF (reverse (%P-CONTENTS-OFFSET FEF (- (%P-LDB %%FEFH-PC-IN-WORDS FEF) 2))))))) (FORMAT T "~A ~A|~D" 'LEXICAL-CLOSURE-UNSHARE 'LOCAL LOCALNUM) (LET ((TEM (DISASSEMBLE-LOCAL-NAME FEF LOCALNUM))) (AND TEM (FORMAT T "~30,4T;~A" TEM))))) (2 (FORMAT T "~A local slot ~D" 'MAKE-LEXICAL-CLOSURE DISP)) (3 (let (name) (cond ((not (and (disassemble-lexical-reference-instruction-p fef (+ pc ilen)) (setq name (disassemble-lexical-ref-name fef disp)))) (FORMAT T "~A ~S" 'PUSH-NUMBER DISP)) (t (format t "~A (lexical ref ~D,~D)~30,4T;" 'push (ldb (byte 12. 12.) disp) (ldb (byte 12. 0) disp) name) (if (eq (car-safe name) 'function) (format t "#'~S" (cadr name)) (prin1 name)))))) (4 (FORMAT T "~A local slot ~D" 'LEXICAL-CLOSURE-DISCONNECT-FIRST DISP)) (5 (PRINC 'PUSH-CDR-IF-CAR-EQUAL) (WRITE-CHAR #\SPACE) (DISASSEMBLE-ADDRESS FEF REG DISP SECOND-WORD PC)) (6 (PRINC 'PUSH-CDR-STORE-CAR-IF-CONS) (WRITE-CHAR #\SPACE) (DISASSEMBLE-ADDRESS FEF REG DISP SECOND-WORD PC)) (T (FORMAT T "<~A-~O> ~D" 'UNDEF-ND4 SUBOP DISP)))) ((= OP #o20) (FORMAT T "~A (~D) " (NTH REG '(ZL:AR-1 ARRAY-LEADER %INSTANCE-REF COMMON-LISP-AR-1 SET-AR-1 SET-ARRAY-LEADER SET-%INSTANCE-REF )) (+ (LDB (BYTE 6 0) DISP) ;; bagbiting %instance-ref is 1-based. Struth. (IF (MEMQ REG '(2 6)) 1 0))) (PRINC (NTH DEST '(D-IGNORE D-PDL D-RETURN D-LAST)))) ((= op #o21) (IF (zerop (ldb (byte 1 #o15) wd)) (FORMAT T "<~A-21-~O>" 'UNDEF SUBOP) (CASE (ldb (byte 2 #o16) wd) (0 (format t "~A 1+" 'push)) (1 (format t "~A 1-" 'push)) (2 (princ 'ZEROP)) (t (princ ')))) (DISASSEMBLE-ADDRESS FEF REG DISP SECOND-WORD)) (T ;UNDEF (FORMAT T "<~A-~O>" 'UNDEF OP))))) ILEN) ;;; This ought to figure out which flavor's mapping table is going to be current ;;; at a certain PC, assuming that the compiled code explicitly sets it up. (DEFUN DISASSEMBLE-CURRENT-FLAVOR (FEF PC) (DECLARE (IGNORE FEF PC)) NIL) (DEFUN DISASSEMBLE-ADDRESS (FEF REG DISP &OPTIONAL SECOND-WORD PC &AUX TEM) "Print out the disassembly of an instruction source address. REG is the register number of the address, and DISP is the displacement. SECOND-WORD should be the instruction's second word if it has two. PC should be where the instruction was found in the FEF." ;; In a one-word instruction, the displacement for types 4 through 7 is only 6 bits, ;; so ignore the rest. In a two word insn, we have been fed the full disp from word 2. (IF (AND ( REG 4) (NOT SECOND-WORD)) (SETQ DISP (LOGAND #o77 DISP))) (COND ((< REG 4) (FORMAT T " ~A|~D~30,4T;" 'FEF DISP) (DISASSEMBLE-POINTER FEF DISP PC)) ((= REG 4) (FORMAT T " '~S" (AREF (SYMBOL-FUNCTION 'CONSTANTS-AREA) DISP))) ((= REG 5) (FORMAT T " ~A|~D" 'LOCAL DISP) (SETQ TEM (DISASSEMBLE-LOCAL-NAME FEF DISP)) (AND TEM (FORMAT T "~30,4T;~A" TEM))) ((= REG 6) (FORMAT T " ~A|~D" 'ARG DISP) (SETQ TEM (DISASSEMBLE-ARG-NAME FEF DISP)) (AND TEM (FORMAT T "~30,4T;~A" TEM))) ((AND (NOT SECOND-WORD) (= DISP #o77)) (FORMAT T " ~A" 'PDL-POP)) ((< DISP #o40) (FORMAT T " ~A|~D" 'SELF DISP) (SETQ TEM (DISASSEMBLE-INSTANCE-VAR-NAME FEF DISP)) (AND TEM (FORMAT T "~30,4T;~A" TEM))) ((< DISP #o70) (FORMAT T " ~A|~D" 'SELF-MAP (- DISP #o40)) (SETQ TEM (DISASSEMBLE-MAPPED-INSTANCE-VAR-NAME FEF (- DISP #o40))) (AND TEM (FORMAT T "~30,4T;~A" TEM))) (T (FORMAT T " ~A|~D (undefined)" 'PDL DISP)))) (defun disassemble-address-contents (fef reg disp &optional second-word) (if (and ( reg 4) (not second-word)) (setq disp (logand #o77 disp))) (cond ((< reg 4) (cond ((= (%p-ldb-offset %%q-data-type fef disp) dtp-self-ref-pointer) (values nil nil)) ((= (%p-ldb-offset %%q-data-type fef disp) dtp-one-q-forward) (values (%p-contents-as-locative-offset fef disp) nil)) (t (values (%p-contents-offset fef disp) t)))) ((= reg 4) (values (aref (symbol-function 'constants-area) disp) t)) (t (values nil nil)))) (DEFUN DISASSEMBLE-POINTER (FEF DISP PC &AUX CELL LOC PTR OFFSET TEM) (SETQ LOC (%MAKE-POINTER-OFFSET DTP-LOCATIVE FEF DISP)) (COND ((= (%P-LDB-OFFSET %%Q-DATA-TYPE FEF DISP) DTP-SELF-REF-POINTER) (MULTIPLE-VALUE-BIND (PTR COMPONENT-FLAVOR-FLAG) (SI::FLAVOR-DECODE-SELF-REF-POINTER (OR (DISASSEMBLE-CURRENT-FLAVOR FEF PC) (SI::FEF-FLAVOR-NAME FEF)) (%P-LDB-OFFSET %%Q-POINTER FEF DISP)) (cond ((NULL PTR) (SETQ CELL "self-ref-pointer " PTR (%P-LDB-OFFSET %%Q-POINTER FEF DISP))) (t (SETQ CELL (IF COMPONENT-FLAVOR-FLAG "mapping table for " "")) (cond (DISASSEMBLE-OBJECT-OUTPUT-FUN (FUNCALL DISASSEMBLE-OBJECT-OUTPUT-FUN PTR CELL LOC T)) (t (FORMAT T "~A~S" CELL PTR) (IF (EQUAL CELL "") (PRINC " in SELF")))))))) ((= (%P-LDB-OFFSET %%Q-DATA-TYPE FEF DISP) DTP-ONE-Q-FORWARD) (SETQ PTR (%FIND-STRUCTURE-HEADER (SETQ TEM (%P-CONTENTS-AS-LOCATIVE-OFFSET FEF DISP))) OFFSET (%POINTER-DIFFERENCE TEM PTR)) (COND ((SYMBOLP PTR) (SETQ CELL (NTH OFFSET '("@+0?? " "" "#'" "@PLIST-HEAD-CELL " "@PACKAGE-CELL ")))) ((CONSP PTR) (SETQ PTR (CAR PTR) CELL "#'")) (T (SETQ CELL ""))) (IF DISASSEMBLE-OBJECT-OUTPUT-FUN (FUNCALL DISASSEMBLE-OBJECT-OUTPUT-FUN PTR CELL LOC T) (FORMAT T "~A~S" CELL PTR))) ((= (%p-ldb-offset %%q-data-type fef disp) dtp-indexed-forward) (let ((cell-name (aref si:*index-name-table* (%p-ldb-offset %%q-pointer fef disp)))) (setq ptr (cadr cell-name)) (setq cell (cdr (assq (car cell-name) '((:function . "#'") (:value . ""))))) (IF DISASSEMBLE-OBJECT-OUTPUT-FUN (FUNCALL DISASSEMBLE-OBJECT-OUTPUT-FUN PTR CELL LOC T) (FORMAT T "~A~S" CELL PTR)))) (T (IF DISASSEMBLE-OBJECT-OUTPUT-FUN (FUNCALL DISASSEMBLE-OBJECT-OUTPUT-FUN (CAR LOC) "'" LOC NIL) (FORMAT T "'~S" (%P-CONTENTS-OFFSET FEF DISP)))))) ;;; Given a fef and an instance variable slot number, ;;; find the name of the instance variable, ;;; if the fef knows which flavor is involved. (DEFUN DISASSEMBLE-INSTANCE-VAR-NAME (FEF SLOTNUM) (LET ((FLAVOR (GET (CADR (ASSQ ':FLAVOR (FEF-DEBUGGING-INFO FEF))) 'SI:FLAVOR))) (AND FLAVOR (NTH SLOTNUM (SI:FLAVOR-ALL-INSTANCE-VARIABLES FLAVOR))))) (DEFUN DISASSEMBLE-MAPPED-INSTANCE-VAR-NAME (FEF MAPSLOTNUM) (LET ((FLAVOR (GET (CADR (ASSQ ':FLAVOR (FEF-DEBUGGING-INFO FEF))) 'SI:FLAVOR))) (AND FLAVOR (NTH MAPSLOTNUM (SI:FLAVOR-MAPPED-INSTANCE-VARIABLES FLAVOR))))) ;;; Given a fef and the number of a slot in the local block, ;;; return the name of that local (or NIL if unknown). ;;; If it has more than one name due to slot-sharing, we return a list of ;;; the names, but if there is only one name we return it. (DEFUN DISASSEMBLE-LOCAL-NAME (FEF LOCALNUM) (LET ((NAMES (NTH LOCALNUM (CADR (ASSQ 'COMPILER::LOCAL-MAP (FEF-DEBUGGING-INFO FEF)))))) (COND ((NULL NAMES) NIL) ((NULL (CDR NAMES)) (CAR NAMES)) (T NAMES)))) ;; Given a fef and the number of a slot in the argument block, ;; return the name of that argument (or NIL if unknown). ;; First we look for an arg map, then we look for a name in the ADL. (DEFUN DISASSEMBLE-ARG-NAME (FEF ARGNUM) (LET* ((FDI (FEF-DEBUGGING-INFO FEF)) (ARGMAP (CADR (ASSQ 'ARG-MAP FDI)))) (IF ARGMAP (CAR (NTH ARGNUM ARGMAP)) (DO ((ADL (GET-MACRO-ARG-DESC-POINTER FEF) (CDR ADL)) (IDX 0 (1+ IDX)) (ADLWORD)) ((NULL ADL)) (SETQ ADLWORD (CAR ADL)) (SELECT (MASK-FIELD %%FEF-ARG-SYNTAX ADLWORD) ((FEF-ARG-REQ FEF-ARG-OPT)) (OTHERWISE (RETURN NIL))) (IF (= 1 (LDB %%FEF-NAME-PRESENT ADLWORD)) (SETQ ADL (CDR ADL))) (WHEN (= IDX ARGNUM) (RETURN (AND (= 1 (LDB %%FEF-NAME-PRESENT ADLWORD)) (CAR ADL)))) (SELECT (MASK-FIELD %%FEF-INIT-OPTION ADLWORD) ((FEF-INI-PNTR FEF-INI-C-PNTR FEF-INI-OPT-SA FEF-INI-EFF-ADR) (SETQ ADL (CDR ADL)))))))) (DEFUN DISASSEMBLE-LEXICAL-REF-NAME (FEF REF) (LET ((NAMES (ASSQ REF (CDR (ASSQ 'LEXICAL-REF-MAP (FEF-DEBUGGING-INFO FEF)))))) (COND (NAMES (CADR NAMES)) (T NIL))))