;;; -*- Mode:LISP; Package:SYSTEM-INTERNALS; Readtable:T; Base:8 -*-

;(defsubst %region-origin (region)
;  (aref #'region-origin region))

(defmacro define-region-accessor ((name . args) &body body)
  (let ((set-name (intern (string-append "SET-" (symbol-name name))))
	(set-args (append args '(value))))
    (loop for form in body
	  when (eq (car form) :array)
	    collect `(defun ,name ,args (aref ,(cadr form) ,(car args))) into result
	    and collect `(defun ,name ,set-args (aset value ,(cadr form) ,(car args))) into result
	  else when (eq (car form) :access)
	    collect `(defun ,name ,args ,@(cdr form)) into result
	  else when (eq (car form) :modify)
	    collect `(defun ,set-name ,set-args ,@(cdr form)) into result
	  finally (return (append '(progn) result `((defsetf ',name ',set-name)))))))

(define-region-accessor (%region-type region)
 ; (:access (%p-ldb-offset %%region-space-type (%region-origin region-bits) region))
  (:modify (%p-dpb-offset value %%region-space-type (%region-origin region-bits) region)))

(define-region-accessor (%region-representation-type region)
 ; (:access (%p-ldb-offset %%region-representation-type (%region-origin region-bits) region))
  (:modify
    (%p-dpb-offset value %%region-representation-type (%region-origin region-bits) region)))

;(define-region-accessor (%region-area region)
;  (:array #'region-area-map))

;(define-region-accessor (%region-bits region)
;  (:array #'region-bits))

;(define-region-accessor (%region-size region)
;  (:array #'region-size))

(define-region-accessor (%region-free-pointer region)
;  (:access (%p-contents-offset (%region-origin region-free-pointer) region))
;  (:modify (%p-store-contents-offset value (%region-origin region-free-pointer) region))
  )

(define-region-accessor (%region-scavenge-pointer region)
  (:array #'region-scavenge-pointer))

(define-region-accessor (%region-flippable? region)
  (:access
    (not (zerop (%p-ldb-offset %%region-flip-enable (%region-origin region-bits) region))))
  (:modify
    (%p-dpb-offset (if value 1 0) %%region-flip-enable (%region-origin region-bits) region)))

(define-region-accessor (%region-scavengeable? region)
  (:access
    (not (zerop (%p-ldb-offset %%region-scavenge-enable (%region-origin region-bits) region))))
  (:modify
    (%p-dpb-offset (if value 1 0) %%region-scavenge-enable (%region-origin region-bits) region)))

(define-region-accessor (%region-volatility region)
;  (:access
;    (- 3 (%p-ldb-offset %%region-volatility (%region-origin region-bits) region)))
  (:modify
    (%p-dpb-offset (- 3 value) %%region-volatility (%region-origin region-bits) region)))

(define-region-accessor (%region-swap-quantum region)
  (:access
    (%p-ldb-offset %%region-swapin-quantum (%region-origin region-bits) region))
  (:modify
    (%p-dpb-offset value %%region-swapin-quantum (%region-origin region) region)))

;;; Lisp Machine storage examiner.  KHS@LMI, December 1984.

(defsubst area-space-type (area)
  (ldb #.%%region-space-type (aref #'area-region-bits area)))

(defsubst region-space-type (region)
  (ldb #.%%region-space-type (aref #'region-bits region)))

(defsubst region-representation-type (region)
  (ldb #.%%region-representation-type (aref #'region-bits region)))

;(defsubst page-number (address)
;  (ldb #o1021 address))

;(defsubst page-index (address)
;  (ldb #o0010 address))

(defsubst pages-in-region (region)
  (floor (%region-free-pointer region) #.page-size))

(defsubst virtual-address-resident? (address)
  (or (system:%page-status address)
      (= (region-space-type (%region-number address)) #.%region-space-fixed)))

(defsubst io-space-pointer? (pointer)
  ( (%pointer-unsigned pointer) (%pointer-unsigned io-space-virtual-address)))

;;;

(defsubst dynamic-space-region? (region)
  (eq (region-space-type region) #.%region-space-new))

(defsubst old-space-region? (region)
  (eq (region-space-type region) #.%region-space-old))

(defsubst copy-space-region? (region)
  (eq (region-space-type region) #.%region-space-copy))

(defsubst static-space-region? (region)
  (eq (region-space-type region) #.%region-space-static))

(defsubst list-representation-region? (region)
  (eq (region-representation-type region) #.%region-representation-type-list))

(defsubst structure-representation-region? (region)
  (eq (region-representation-type region) #.%region-representation-type-structure))

(defsubst region-scavenge-enabled? (region)
  (not (zerop (ldb #.%%region-scavenge-enable (aref #'region-bits region)))))

(defsubst region-area (region)
  (%area-number (aref #'region-origin region)))

(defsubst cdr-normal? (address) (= (%p-cdr-code address) cdr-normal))
(defsubst cdr-error? (address) (= (%p-cdr-code address) cdr-error))
(defsubst cdr-next? (address) (= (%p-cdr-code address) cdr-next))
(defsubst cdr-nil? (address) (= (%p-cdr-code address) cdr-nil))

(defun array-leader? (address)
  (and (= (%p-data-type address) dtp-header)
       (= (%p-ldb %%header-type-field address) %header-type-array-leader)))

;;;

(defvar *output-indentation* -2)
(defvar *object-descriptor-table* (make-array 32.))
(defvar *object-size-table* (make-array 32.))

(defun detail-region (region)
  (setq *output-indentation* -2)
  (terpri)
  (loop with address = (aref #'region-origin region)
	with bound = (%pointer-plus (aref #'region-origin region)
				    (%region-free-pointer region))
	until ( (%pointer-difference address bound) 1)
	for boxed = (%structure-boxed-size address)
	for total = (%structure-total-size address)
	do (describe-storage address)
	unless (and (= (%p-data-type address) dtp-array-header)
		    (memq (%p-ldb %%array-type-field address) '(13 15)))
	  do (let ((*output-indentation* 0))
	       (loop for i from 1 below boxed
		     do (describe-storage (%pointer-plus address i))))
	when ( boxed total)
	  do (format t "~&     ")
	when (and (= boxed total)
		  (= (%p-data-type (+ address total -1)) dtp-header-forward)
		  (= (%p-cdr-code (+ address total -1)) cdr-normal))
	  do (incf address (- total 1))
	else
	  do (incf address total)))

;	do (multiple-value-bind (boxed total)
;	       (compute-object-size address)
;	     (unless (and (= boxed (%structure-boxed-size address))
;			  (= total (%structure-total-size address)))
;	       (unless (array-leader? address)
;		 (ferror "Structure size inconsistent.")))
;	     (describe-storage address)
;	     (let ((*output-indentation* 0))
;	       (loop for i from 1 below boxed
;		     do (unless (= (%pointer address) (%pointer (%find-structure-header address)))
;			  (unless (array-leader? address)
;			    (ferror "Couldn't find header")))
;		     do (describe-storage (%pointer-plus address i))))
;	     (if ( boxed total) (format t "~&  ..."))
;	     (incf address total))))

(defun describe-storage (address)
  (if (%region-number address)
      (let ((*output-indentation* (+ *output-indentation* 2)))
	(funcall (aref *object-descriptor-table* (%p-data-type address)) address))
    (format t "~%Invalid pointer: ~O" address)))

(defun output (address format-control &rest format-args)
  (terpri)
  (dotimes (i *output-indentation*) (tyo #/space))
  (format t "~O ~A ~24T" address (nth (%p-cdr-code address) q-cdr-codes))
  (apply #'format t format-control format-args))

(defmacro describe-object (data-type &body body)
  `(setf (aref *object-descriptor-table* ,(symbol-value data-type))
	 (compile-lambda '(lambda (address) ,@body))))

;;;

(describe-object dtp-trap
  (output address "DTP-TRAP"))

(describe-object dtp-null
  (output address "DTP-NULL"))

(describe-object dtp-free
  (output address "DTP-FREE"))

(describe-object dtp-symbol
  (output address "Symbol: ~O ~A"
	  (%p-pointer address)
	  (%make-pointer dtp-symbol (%p-pointer address))))

(describe-object dtp-symbol-header
  (output address "Symbol-header: ~A" (symbol-name (%make-pointer dtp-symbol address))))

(describe-object dtp-fix
  (output address "Fixnum: ~D" (%p-pointer address)))

(describe-object dtp-extended-number
  (output address "Extended number pointer: ~A"
	  (%make-pointer dtp-extended-number (%p-pointer address))))

(describe-object dtp-header
  (select (%p-ldb %%header-type-field address)
    (%header-type-error                            (output address "HEADER-TYPE-ERROR"))
    (%header-type-fef                              (ddt-describe-fef address))
    (%header-type-array-leader                     (ddt-describe-array-leader address))
    (%header-type-list                           (output address "HEADER-TYPE-LIST"))
    (%header-type-flonum                           (ddt-describe-extended-number address))
    (%header-type-complex                          (ddt-describe-extended-number address))
    (%header-type-bignum                           (ddt-describe-extended-number address))
    (%header-type-rational                         (ddt-describe-extended-number address))
    (%header-type-fast-fef-fixed-args-no-locals    (ddt-describe-fef address))
    (%header-type-fast-fef-var-args-no-locals      (ddt-describe-fef address))
    (%header-type-fast-fef-fixed-args-with-locals  (ddt-describe-fef address))
    (%header-type-fast-fef-var-args-with-locals    (ddt-describe-fef address))
    (otherwise                                     (output address "Unknown header type."))))

(defun ddt-describe-fef (address)
  (output address "FEF Header: ~S" (function-name (%make-pointer dtp-fef-pointer address))))

(defun ddt-describe-extended-number (address)
  (output address "Extended number: ~A" (%make-pointer dtp-extended-number address)))

(defun ddt-describe-array-leader (address)
  (let ((length (%p-ldb %%array-leader-length address)))
    (output address "Array leader: ~D elements" (- length 2))))

(describe-object dtp-gc-forward
  (output address "GC-forward: ~O" (%p-pointer address)))

(describe-object dtp-external-value-cell-pointer
  (output address "EVCP: ~O" (%p-pointer address)))

(describe-object dtp-one-q-forward
  (output address "One-Q-forward: ~O" (%p-pointer address)))

(describe-object dtp-header-forward
  (output address "Header-forward: ~O" (%p-pointer address)))

(describe-object dtp-body-forward
  (output address "Body-forward: ~O" (%p-pointer address)))

(describe-object dtp-locative
  (let ((*print-level* 2) (*print-length* 2))
    (output address "Locative: ~O ~S"
	    (%p-pointer address)
	    (catch-error (car (%make-pointer dtp-locative (%p-pointer address))) nil))))

(describe-object dtp-list
  (let ((*print-level* 2) (*print-length* 2))
    (output address "List: ~O ~S"
	    (%p-pointer address)
	    (%make-pointer dtp-list (%p-pointer address)))))

(describe-object dtp-u-entry
  (output address "Microcode-entry: ~A"
	  (function-name (%make-pointer dtp-u-entry (%p-pointer address)))))

(describe-object dtp-fef-pointer
  (output address "~S" (%make-pointer dtp-fef-pointer (%p-pointer address))))

(describe-object dtp-array-pointer
  (output address "Array pointer: ~S"
	  (%make-pointer dtp-array-pointer (%p-pointer address))))

(describe-object dtp-array-header
  (let* ((array (%make-pointer dtp-array-pointer address))
	 (array-type (%p-ldb %%array-type-field address))
	 (displaced? (%p-ldb %%array-displaced-bit address))
	 (leader-length (if (zerop (%p-ldb %%array-leader-bit address))
			    0
			  (+ (%p-ldb %%array-leader-length (1- address)) 2))))
    (if (typep array 'string)
	(output address "String: ~S" array)
      (output address "Array header: ~A ~A leader ~A"
	      (nth array-type array-types)
	      (if (zerop displaced?) "" "(Displaced)")
	      leader-length))))

(describe-object dtp-stack-group
  (output address "Stack group: ~A"
	  (function-name (%make-pointer dtp-stack-group (%p-pointer address)))))

(describe-object dtp-closure
  (output address "Closure:"))

(describe-object dtp-small-flonum
  (output address "Small flonum: ~S"
	  (%make-pointer dtp-small-flonum (%p-pointer address))))

(describe-object dtp-select-method
  (output address "Select method: ~O" (%p-pointer address)))

(describe-object dtp-instance
  (output address "Instance: ~O ~S"
	  (%p-pointer address)
	  (%make-pointer dtp-instance (%p-pointer address))))

(describe-object dtp-instance-header
  (output address "Instance header: ~S" (%make-pointer dtp-instance address)))

(describe-object dtp-entity
  (output address "Entity: ~O" (%p-pointer address)))

(describe-object dtp-stack-closure
  (output address "Stack closure: ~O" (%p-pointer address)))

(describe-object dtp-self-ref-pointer
  (output address "Self-reference"))

(describe-object dtp-character
  (output address "Character: ~S"
	  (%make-pointer dtp-character (%p-pointer address))))

;;;

(defun compute-object-size (address)
  (let ((entry (aref *object-size-table* (%p-data-type address))))
    (if (integerp entry)
	(values entry entry)
      (funcall entry address))))

(defmacro define-object-size (data-type &body body)
  (if (integerp (car body))
      `(setf (aref *object-size-table* ,(symeval data-type)) ,@body)
    `(setf (aref *object-size-table* ,(symeval data-type))
	   (compile-lambda '(lambda (address) ,@body)))))

(defun list-object-size (address)
  (loop for i from 0 to 100000
	for a = (+ address i)
	for s = 1 then (1+ s)
	when (memq (%p-data-type a)
		   (list dtp-symbol-header
			 dtp-header
			 dtp-array-header
			 dtp-instance-header))
	  do (return (compute-object-size a))
	when (or (= (%p-cdr-code a) cdr-error)
		 (= (%p-cdr-code a) cdr-nil))
	  do (return s s)))

(define-object-size dtp-trap 1)
(define-object-size dtp-null (list-object-size address))
(define-object-size dtp-free (list-object-size address))
(define-object-size dtp-symbol (list-object-size address))
(define-object-size dtp-symbol-header 5)
(define-object-size dtp-fix (list-object-size address))
(define-object-size dtp-extended-number (list-object-size address))

(define-object-size dtp-header
  (select (%p-ldb %%header-type-field address)
    ((%header-type-fef
      %header-type-fast-fef-fixed-args-no-locals
      %header-type-fast-fef-var-args-no-locals
      %header-type-fast-fef-fixed-args-with-locals
      %header-type-fast-fef-var-args-with-locals)
     (let ((boxed (%p-ldb %%fefh-pc-in-words address)))
       (values boxed (%p-contents-offset address %fefhi-storage-length))))
    (%header-type-array-leader
      (let ((length (%p-ldb %%array-leader-length address)))
	(values length length)))
    (%header-type-flonum
      (values 1 3))
    (%header-type-complex
      (values 3 3))
    (%header-type-bignum
      (values 1 (1+ (%p-ldb #o0022 address))))
    (%header-type-rational
      (values 3 3))
    (%header-type-error
      (ferror nil "%HEADER-TYPE-ERROR at ~O" address))
    (%header-type-list
      (values 1 1))
    (otherwise
      (ferror nil "Unknown header type at ~O" address))))

(define-object-size dtp-gc-forward (list-object-size address))
(define-object-size dtp-external-value-cell-pointer (list-object-size address))
(define-object-size dtp-one-q-forward (list-object-size address))

(define-object-size dtp-header-forward
  (do ((scan (1+ address) (1+ scan)))
      ((neq (%p-data-type scan) dtp-body-forward)
       (values 1 (- scan address)))))

(define-object-size dtp-body-forward 1)
(define-object-size dtp-locative (list-object-size address))
(define-object-size dtp-list (list-object-size address))
(define-object-size dtp-u-entry (list-object-size address))
(define-object-size dtp-fef-pointer (list-object-size address))
(define-object-size dtp-array-pointer (list-object-size address))

(define-object-size dtp-array-header
  ;; This is very dependent on the current world values.  This will be fixed as
  ;; soon as ARRAY-BOXED-WORDS-PER-ELEMENT is defined for the cold load.
  (flet ((boxed-array-size (address words-per-element)
	   (let ((length) (offset))
	     (cond ((zerop (%p-ldb %%array-long-length-flag address))
		    (setq length (%p-ldb %%array-index-length-if-short address))
		    (setq offset (%p-ldb %%array-number-dimensions address)))
		   (t
		    (setq length (%p-contents-offset address 1))
		    (setq offset (1+ (%p-ldb %%array-number-dimensions address)))))
	     (setq length (+ offset (ceiling (* length words-per-element) 1)))
	     (values length length)))
	 (unboxed-array-size (address words-per-element)
	   (let ((length) (offset))
	     (cond ((zerop (%p-ldb %%array-long-length-flag address))
		    (setq length (%p-ldb %%array-index-length-if-short address))
		    (setq offset (%p-ldb %%array-number-dimensions address)))
		   (t
		    (setq length (%p-contents-offset address 1))
		    (setq offset (1+ (%p-ldb %%array-number-dimensions address)))))
	     (values offset (+ offset (ceiling (* length words-per-element) 1))))))
    (if (not (zerop (%p-ldb %%array-displaced-bit address)))
	(let ((boxed-size (+ (max 1 (%p-ldb %%array-number-dimensions address))
			     (%p-ldb %%array-index-length-if-short address))))
	  (values boxed-size boxed-size))
      (case (%p-ldb %%array-type-field address)
	(1  (unboxed-array-size address 1\40))	;art-1b
	(2  (unboxed-array-size address 1\20))	;art-2b
	(3  (unboxed-array-size address 1\8))	;art-4b
	(4  (unboxed-array-size address 1\4))	;art-8b
	(5  (unboxed-array-size address 1\2))	;art-16b
	(6  (unboxed-array-size address 1))	;art-32b
	(7  (boxed-array-size address 1))	;art-q
	(10 (boxed-array-size address 1))	;art-q-list
	(11 (unboxed-array-size address 1\4))	;art-string
	(12 (boxed-array-size address 1))	;art-stack-group-head
	(13 (unboxed-array-size address 1))	;art-special-pdl
	(14 (unboxed-array-size address 1\2))	;art-half-fix
	(15 (unboxed-array-size address 1))	;art-regular-pdl
	(16 (unboxed-array-size address 2))	;art-float
	(17 (unboxed-array-size address 1))	;art-fps-float
	(20 (unboxed-array-size address 1\2))	;art-fat-string
	(21 (unboxed-array-size address 4))	;art-complex-float
	(22 (boxed-array-size address 2))	;art-complex
	(23 (unboxed-array-size address 2))	;art-complex-fps-float
	))))

(define-object-size dtp-stack-group (list-object-size address))
(define-object-size dtp-closure (list-object-size address))
(define-object-size dtp-small-flonum (list-object-size address))
(define-object-size dtp-select-method (list-object-size address))
(define-object-size dtp-instance (list-object-size address))

(define-object-size dtp-instance-header
  (let ((boxed (%p-contents-offset (%p-pointer address) %instance-descriptor-size)))
    (values boxed boxed)))

(define-object-size dtp-entity (list-object-size address))
(define-object-size dtp-stack-closure (list-object-size address))
(define-object-size dtp-self-ref-pointer (list-object-size address))
(define-object-size dtp-character (list-object-size address))

;;;

(defmacro for-every-area-in-world ((area) &body body)
  `(dolist (name (current-area-list))
     (let ((,area (symbol-value name)))
       (unless (= (area-space-type ,area) %region-space-fixed)
	 ,@body))))

(defmacro for-every-region-in-world ((region) &body body)
  `(do ((,region size-of-area-arrays (1- ,region)))
       ((minusp ,region))
     ,@body))

(defmacro for-every-list-region-in-world ((region) &body body)
  `(for-every-area-in-world (area)
     (for-every-region-in-area (,region area)
       (when (list-representation-region? ,region)
	 (format t "~%Searching region ~D of area ~S ..."
		 ,region
		 (aref #'area-name (region-area ,region)))
	 ,@body))))

(defmacro for-every-structure-region-in-world ((region) &body body)
  `(for-every-area-in-world (area)
     (for-every-region-in-area (,region area)
       (when (structure-representation-region? ,region)
	 (format t "~%Searching region ~D of area ~S ..."
		 ,region
		 (aref #'area-name (region-area ,region)))
	 ,@body))))

(defmacro for-every-region-in-area ((region area) &body body)
  `(do ((,region (aref #'area-region-list ,area) (aref #'region-list-thread ,region)))
       ((minusp ,region))
     ,@body))

(defmacro for-every-page-in-region ((region) &body body)
  `(do* ((.base. (page-number (aref #'region-origin ,region)))
	 (.size. (floor (%region-free-pointer ,region) #.page-size))
	 (.page. 0 (1+ .page.)))
       ((> .page. .size.))
     (let ((page (+ .base. .page.)))
       ,@body)))

(defmacro for-every-object-in-region ((object region) &body body)
  `(let* ((start (aref #'region-origin ,region))
	  (stop (%pointer-plus start (%region-free-pointer ,region))))
     (do ((,object start))
	 (( (%pointer-difference ,object stop) 0))
       (multiple-value-bind (object-boxed-size object-total-size)
	   (compute-object-size ,object)
	 ,@body
	 (setq ,object (%pointer-plus ,object object-total-size))))))

(defmacro for-every-pointer-in-object ((object) &body body)
  `(do ((address ,object (%pointer-plus address 1)))
       (( (%pointer-difference address ,object) object-boxed-size))
     (when (%p-pointerp address)
       ,@body)))

(defun describe-object-once-only (address &special *last-object-address*)
  (unless (eq address *last-object-address*)
    (describe-storage address)
    (setq *last-object-address* address)))

;;;

(defun search-region-for-pointer (region pointer)
  (for-every-object-in-region (object region)
    (for-every-pointer-in-object (object)
      (when (= (%p-pointer address) (%pointer pointer))
	(describe-object-once-only object)))))
      
(defun search-region-for-zero-rank-arrays (region)
  (for-every-object-in-region (object region)
    (when (= (%p-data-type object) dtp-array-header)
      (if (= (%p-ldb %%array-number-dimensions object) 0)
	  (describe-storage object)))))

(defun search-region-for-art-32b-arrays (region)
  (for-every-object-in-region (object region)
    (when (= (%p-data-type object) dtp-array-header)
      (if (= (%p-ldb %%array-type-field object) 6)
	  (describe-storage object)))))
      
(defun search-region-for-pointers-to-region (search-region target-region)
  (unless (= search-region target-region)
    (for-every-object-in-region (object search-region)
      (for-every-pointer-in-object (object)
	(when (= (%region-number (%p-pointer address)) target-region)
	  (describe-object-once-only object))))))

(defun search-region-for-pointers-to-area (search-region target-area)
  (for-every-object-in-region (object search-region)
    (for-every-pointer-in-object (object)
      (when (= (%area-number (%p-pointer address)) target-area)
	(describe-object-once-only object)))))

(defun search-region-for-illegal-pointers (region)
  (for-every-object-in-region (object region)
    (for-every-pointer-in-object (object)
      (let ((target-region (%region-number (%p-pointer address))))
	(if (integerp target-region)
	    (when (> (%pointer-unsigned (%p-pointer address))
		     (+ (%pointer-unsigned (aref #'region-origin target-region))
			(%region-free-pointer target-region)))
	      (format t "~% ~O: ~A ~A ~O" address
		      (nth (%p-cdr-code address) q-cdr-codes)
		      (nth (%p-data-type address) q-data-types)
		      (%p-pointer address)))
	  (unless (io-space-pointer? (%p-pointer address))
	    (format t "~% ~O: ~A ~A ~O  Not in any region."
		    address
		    (nth (%p-cdr-code address) q-cdr-codes)
		    (nth (%p-data-type address) q-data-types)
		    (%p-pointer address))))))))

(defun search-region-for-pointers-to-old-space (region)
  (unless (old-space-region? region)
    (for-every-object-in-region (object region)
      (for-every-pointer-in-object (object)
	(let ((target-region (%region-number (%p-pointer address))))
	  (if (integerp target-region)
	      (when (old-space-region? target-region)
		(format t "~% ~O: ~A ~A ~O" address
			(nth (%p-cdr-code address) q-cdr-codes)
			(nth (%p-data-type address) q-data-types)
			(%p-pointer address)))
	    (unless (io-space-pointer? (%p-pointer address))
	      (format t "~% ~O: ~A ~A ~O  Not in any region."
		      address
		      (nth (%p-cdr-code address) q-cdr-codes)
		      (nth (%p-data-type address) q-data-types)
		      (%p-pointer address)))))))))

;;;

(defun search-area-for-pointer (area pointer)
  (for-every-region-in-area (region area)
    (search-region-for-pointer region pointer)))

(defun search-area-for-pointers-to-region (area target-region)
  (for-every-region-in-area (region area)
    (search-region-for-pointers-to-region region target-region)))

(defun search-area-for-zero-rank-arrays (area)
  (for-every-region-in-area (region area)
    (search-region-for-zero-rank-arrays region)))

(defun search-area-for-art-32b-arrays (area)
  (for-every-region-in-area (region area)
    (search-region-for-art-32b-arrays region)))

(defun search-area-for-pointers-to-area (area target-area)
  (unless (= area target-area)
    (for-every-region-in-area (region area)
      (search-region-for-pointers-to-area region target-area))))

(defun search-area-for-illegal-pointers (area)
  (for-every-region-in-area (region area)
    (search-region-for-illegal-pointers region)))

(defun search-area-for-pointers-to-old-space (area)
  (for-every-region-in-area (region area)
    (search-region-for-pointers-to-old-space region)))

;;;

(defun print-area-message (area)
  (format t "~%Searching area ~S ..." (aref #'area-name area)))

(defun search-everywhere-for-pointer (pointer)
  (for-every-area-in-world (area)
    (print-area-message area)
    (search-area-for-pointer area pointer)))

(defun search-everywhere-for-art-32b-arrays ()
  (for-every-area-in-world (area)
    (print-area-message area)
    (search-area-for-art-32b-arrays area)))

(defun search-everywhere-for-pointers-to-region (region)
  (for-every-area-in-world (area)
    (print-area-message area)
    (search-area-for-pointers-to-region area region)))

(defun search-everywhere-for-pointers-to-area (target-area)
  (for-every-area-in-world (area)
    (print-area-message area)
    (search-area-for-pointers-to-area area target-area)))

(defun search-everywhere-for-illegal-pointers ()
  (for-every-area-in-world (area)
    (print-area-message area)
    (search-area-for-illegal-pointers area)))

(defun search-everywhere-for-pointers-to-old-space ()
  (for-every-area-in-world (area)
    (print-area-message area)
    (search-area-for-pointers-to-old-space area)))

;;;

(defsubst instruction-opcode (instruction)
  (if (< (ldb #o1104 instruction) #o11)
      (ldb #o1105 instruction)
    (ldb #o1104 instruction)))

(defsubst instruction-misc-code (instruction)
  (ldb #o0011 instruction))

(defsubst instruction-destination (instruction)
  (ldb #o1602 instruction))

(defsubst instruction-register (instruction)
  (ldb #o0603 instruction))

(defsubst instruction-offset (instruction)
  (ldb #o0006 instruction))

(defmacro for-every-instruction-in-compiled-function ((instruction fef) &body body)
  `(do ((pc (fef-initial-pc ,fef) (+ pc (fef-instruction-length ,fef pc))))
       (( pc (fef-limit-pc ,fef)))
     (let ((,instruction (fef-instruction ,fef pc)))
       ,@body)))

(defmacro for-every-compiled-function-in-world ((fef) &body body)
  `(for-every-region-in-area (region macro-compiled-program)
     (when (structure-representation-region? region)
       (for-every-object-in-region (object region)
	 (when (and (= (%p-data-type object) dtp-header)
		    (= (%p-ldb %%header-type-field object) %header-type-fef))
	   (let ((,fef (%make-pointer dtp-fef-pointer object)))
	     ,@body))))))

(defun search-for-misc-instruction (misc)
  (if (= (%data-type misc) dtp-symbol) (setq misc (get misc 'compiler:qlval)))
  (check-type misc integer)
  (terpri)
  (for-every-compiled-function-in-world (function)
     (tyo #/.)
    (let ((found 0))
      (for-every-instruction-in-compiled-function (instruction function)
	(when (= (instruction-opcode instruction) 15)
	  (when (= (instruction-misc-code instruction) misc)
	    (incf found))))
      (unless (zerop found)
	(format t "~%~D found in ~S" found function)))))

(defun search-for-arefi-sub-opcode (subop)
  (terpri)
  (for-every-compiled-function-in-world (function)
    (let ((found 0))
      (for-every-instruction-in-compiled-function (instruction function)
	(when (= (instruction-opcode instruction) 20)
	  (when (= (instruction-register instruction) subop)
	    (incf found))))
      (unless (zerop found)
	(format t "~%~D found in ~S" found function)))))

(defun search-for-fef3-instructions ()
  (terpri)
  (for-every-compiled-function-in-world (function)
     (tyo #/.)
    (let ((found 0))
      (for-every-instruction-in-compiled-function (instruction function)
	(when ( (instruction-opcode instruction) 13)
	  (if (= (instruction-register instruction) 3)
	      (incf found))))
      (unless (zerop found)
	(format t "~%~D found in ~S" found function)))))

(defun search-for-fef2-instructions ()
  (terpri)
  (for-every-compiled-function-in-world (function)
     (tyo #/.)
    (let ((found 0))
      (for-every-instruction-in-compiled-function (instruction function)
	(when ( (instruction-opcode instruction) 13)
	  (if (= (instruction-register instruction) 2)
	      (incf found))))
      (unless (zerop found)
	(format t "~%~D found in ~S" found function)))))

(defun search-for-long-branches ()
  (terpri)
  (for-every-compiled-function-in-world (function)
    (tyo #/.)
    (let ((found 0))
      (for-every-instruction-in-compiled-function (instruction function)
	(when (> (fef-instruction-length function pc) 1)
	  (incf found)))
      (unless (zerop found)
	(format t "~%~D found in ~S" found function)))))
      

(defun record-fef-header-block-sizes ()
  (let ((fef-0 0) (fef-1 0) (fef-2 0) (fef-3 0) (fail 0))
    (terpri)
    (for-every-compiled-function-in-world (function)
      (tyo #/.)
      (let ((size (// (fef-initial-pc function) 2)))
	(cond ((< size 100) (incf fef-0))
	      ((< size 200) (incf fef-1))
	      ((< size 300) (incf fef-2))
	      ((< size 400) (incf fef-3))
	      (t (incf fail)))))
    (values fef-0 fef-1 fef-2 fef-3 fail)))

(defun search-for-functions-without-debugging-info ()
  (terpri)
  (for-every-compiled-function-in-world (function)
    (tyo #/.)
    (unless (ldb-test %%fefhi-ms-debug-info-present (%p-contents-offset function %fefhi-misc))
      (format t "~%~S does not contain debugging info" (function-name function)))))

(defun search-for-calls-to-functions-with-quoted-arguments (&aux result)
  (format t "~&")
  (for-every-compiled-function-in-world (function)
    object-boxed-size
    (dotimes (i (%structure-boxed-size function))
      (let* ((address (%make-pointer-offset dtp-locative function i))
	     (called-function
	       (do ((current-address address))
		    ((not (= (%p-data-type current-address) dtp-external-value-cell-pointer))
		     (if (and (= (%p-data-type current-address) dtp-header)
			      (= (%p-ldb %%header-type-field current-address) header-type-fef))
			 (%make-pointer dtp-fef-pointer current-address)
		       nil))
		 (setq current-address (%p-pointer current-address)))))
	(cond ((null called-function))
	      ((or (not (zerop (logand (%args-info called-function) %arg-desc-quoted-rest)))
		   (not (zerop (logand (%args-info called-function) %arg-desc-fef-quote-hair))))
	       (pushnew function result)
	       (print function)))))))

