;;; -*- Mode:LISP; Package:SYSTEM-INTERNALS; Cold-Load:T; Readtable:CL; Base:8 -*- ;;; (c) Copyright 1985, Lisp Machine Incorporated (LMI). ;;; This is a cold-load file. See also STORAGE-DEFS. ; forwarded to a-mem (defvar-resettable %inhibit-read-only nil nil "Bind this to T to do nasty horrible things behind the virtual memory system's back.") (defvar *area-list* :unbound "Call (CURRENT-AREA-LIST) for list of active areas.") (defun %region-free-pointer (region) (without-interrupts (compiler::invalidate-cons-caches) ;(%p-contents-offset (%region-origin region-free-pointer) region) (aref #'region-free-pointer region) )) (defun set-%region-free-pointer (region value) (without-interrupts (compiler::invalidate-cons-caches) ;(%p-store-contents-offset value (%region-origin region-free-pointer) region) (setf (aref #'region-free-pointer region) value) )) ;(defsetf %region-free-pointer set-%region-free-pointer) -- in storage-defs ;(defun %reset-temporary-area (area &optional inhibit-error) ; "Reclaim all storage associated with AREA. There must not be any references to storage ;in the area. References from unused storage are not permitted. References from active ;stack frames are not permitted. References from internal processor registers are not ;permitted. References from other stack groups, including inactive ones, are not permitted. ;In short, you shouldn't be using this. Use the garbage collector." ; (unless (or inhibit-error (area-temporary? area)) ; (multiple-cerror () () ; ("The area ~S (~S) was not created as temporary." (area-name area) area) ; ("Don't reset this area" (return-from %reset-temporary-area nil)) ; ("Make area temporary, and the reset it" (make-area-temporary area)))) ; (without-interrupts ; ;; We can't just iterate over the region tables here (because %free-region modifies ; ;; them), so we build a list of the regions we want to free, then %free-region them. ; (mapc #'%free-region ; (loop for region = (%area-region-list area) then (%region-list-thread region) ; until (minusp region) ; collect region)))) (defun reset-temporary-area (area &optional inhibit) area inhibit ;; Let's put the fear of God into casual users of this thing. ; (multiple-cerror () () ; ("RESET-TEMPORARY-AREA is obsolete and dangerous.") ; ("Don't reset this area." ()) ; ("Reset this area using SI::%RESET-TEMPORARY-AREA." (%reset-temporary-area area inhibit))) (ferror nil "RESET-TEMPORARY-AREA is no longer supported.") ) (make-obsolete reset-temporary-area "is no longer supported.") ;(defun %reset-region-free-pointer (region new-fp) ; (unless inhibit-scheduling-flag ; (ferror "This function must be called with scheduling inhibited.")) ; (let ((old-fp (%region-free-pointer region))) ; (when (< new-fp old-fp) ; (setf (%region-free-pointer region) new-fp) ; ;; Reset the structure-handles in the affected area. On the first page ; ;; (page-number new-fp), reset the first-header iff it needs to be lower. ; ;; For the following pages, just indicate no header and no initial qs. ; ;; Careful about the very last page -- if it's in the next region don't ; ;; touch it. ; (when (= old-fp (%region-length region)) (decf old-fp)) ; (setq old-fp (%pointer-plus old-fp (%region-origin region))) ; (setq new-fp (%pointer-plus new-fp (%region-origin region))) ; ;; If this function ever needs to be fast, use %BLT here. ; (loop initially ; (when (> (page-first-header (page-number new-fp)) (page-index new-fp)) ; (setf (page-first-header (page-number new-fp)) (page-index new-fp))) ; for page from (1+ (page-number new-fp)) to (page-number old-fp) ; do (setf (page-first-header page) #o400) ; do (setf (page-initial-qs page) 0)) ; (%gc-scav-reset region)))) ;(make-obsolete %reset-region-free-pointer "use garbage collector") ;(defun %free-region (region) ; "Removes all trace of REGION from the area, virtual memory, and GC tables." ; (unless inhibit-scheduling-flag ; (ferror "This function must be called with scheduling inhibited.")) ; (let ((area (%region-area region)) ; (area-region-list-base (%region-origin sys:area-region-list)) ; (region-list-thread-base (%region-origin sys:region-list-thread))) ; ;; This function needs to be pretty fast, to keep GC:RECLAIM-OLDSPACE from ; ;; consuming too much time without interrupts. Define some magic accessors ; ;; for the relevant region tables. (Note the local variables above.) ; (macrolet ((%area-region-list (area) ; `(%p-pointer (+ area-region-list-base ,area))) ; (%region-list-thread (region) ; `(%p-pointer (+ region-list-thread-base ,region)))) ; ;; If it's the first region in the area, delete from the start of the thread. ; (if (eq region (%area-region-list area)) ; (setf (%area-region-list area) (%region-list-thread region)) ; ;; Otherwise search for the region and snap it out of the thread. ; (loop with this = (%area-region-list area) ; for next = (%region-list-thread this) ; until (eq next region) ; do (setq this next) ; finally (setf (%region-list-thread this) (%region-list-thread next))))) ; (%gc-free-region region))) (defun %deallocate-end-of-region (region) "Return unused quantums in region to free pool." (unless inhibit-scheduling-flag (ferror "This function must be called with scheduling disabled.")) (let ((quantum-size %address-space-quantum-size) (origin (%region-origin region)) (length (%region-length region)) (free-pointer (%region-free-pointer region))) ;; If less than one quantum long, or if there is less than one quantum of free space, ;; don't do anything. It is illegal to have regions with no quantums. (unless (or (<= length quantum-size) (<= (- length free-pointer) quantum-size)) (loop with first-free-quantum = (ceiling free-pointer quantum-size) with new-length = (* first-free-quantum quantum-size) with origin-quantum = (truncate (si::%pointer-unsigned origin) quantum-size) with array = #'address-space-map initially (setf (%region-length region) new-length) for quantum from first-free-quantum below (truncate length quantum-size) do (setf (aref array (+ origin-quantum quantum)) 0) finally (%deallocate-pages (%pointer-plus origin new-length) (%pointer-plus origin length)))))) (defun %deallocate-pages (vma-start vma-bound) "Remove the pages between START and BOUND from the maps and the page-hash-table." (unless inhibit-scheduling-flag (ferror "This function must be called with scheduling disabled.")) (loop with page = page-size ;Yucky special variable. ;; Map gets Map Status: read/write unmodified, Map Access: no access. with bits = #o300 ;; Bit 30 in swap-status argument means disconnect virtual page from page frame. ;; It also makes the page unmodified in the other way, the details of which ;; I almost understood at one point, but no longer. Yech-oh. with swap-status = (%logdpb 1 (byte 1 #o30) %pht-swap-status-flushable) for address = vma-start then (%pointer-plus address page) until (= address vma-bound) do (%change-page-status address swap-status bits))) (defun %invalidate-region-mapping (region) "Decache all information about REGION from the virtual memory maps." (loop with page = page-size with origin = (%region-origin region) with bound = (%pointer-plus origin (%region-length region)) for address = origin then (%pointer-plus address page) until (eq address bound) do (%change-page-status address nil nil))) (defun %invalidate-area-mapping (area) "Decache all information about AREA from the virtual memory maps." (for-every-region-in-area (region area) (%invalidate-region-mapping region))) ;;; The following functions don't work at all. (defun %make-region-not-read-only (region) (let* ((old-region-bits (%region-bits region)) (old-access-status-meta (ldb %%region-map-bits old-region-bits)) (new-access-status-meta (%logdpb %pht-map-status-read-write-first %%region-map-status-code (%logdpb 3 %%region-map-access-code old-region-bits))) (new-region-bits (%logdpb new-access-status-meta %%region-map-bits old-region-bits))) (declare (ignore old-access-status-meta)) (setf (%region-bits region) new-region-bits) (loop with page = page-size with origin = (%region-origin region) with bound = (%pointer-plus origin (%region-length region)) for address = origin then (%pointer-plus address page) until (eq address bound) do (%change-page-status address nil new-region-bits)))) (defun %make-region-read-only (region) (let* ((old-region-bits (%region-bits region)) (old-access-status-meta (ldb %%region-map-bits old-region-bits)) (new-access-status-meta (%logdpb %pht-map-status-read-only %%region-map-status-code (%logdpb 2 %%region-map-access-code old-region-bits))) (new-region-bits (%logdpb new-access-status-meta %%region-map-bits old-region-bits))) (declare (ignore old-access-status-meta)) (setf (%region-bits region) new-region-bits) (loop with page = page-size with origin = (%region-origin region) with bound = (%pointer-plus origin (%region-length region)) for address = origin then (%pointer-plus address page) until (eq address bound) do (%change-page-status address nil new-region-bits)))) ;;; (defun current-area-list () "Use this instead of the variable AREA-LIST. That can get clobbered if someone reads in QCOM or something." (if (not (boundp '*area-list*)) (setq *area-list* (g-l-p (symbol-function 'area-name)))) *area-list* ;(g-l-p (symbol-function 'area-name)) was the old thing. As areas get deallocated, ; can be completely wrong now. ) (defun allocated-regions () (loop for region from 0 below sys:number-of-regions counting ( (%region-type region) %region-space-free))) (defun allocated-areas () (length (current-area-list)) ;(fill-pointer (symbol-function 'area-name)) ) ;(defvar area-temporary-flag-array :unbound ; "Array index by area number containing 1 if area is temporary, else 0.") (defun area-temporary-p (area) area "Return T if the specified area is a temporary area." ; (not (zerop (aref area-temporary-flag-array area))) nil ) (defun area-temporary? (area) area "Return T if the specified area is a temporary area." ; (not (zerop (aref area-temporary-flag-array area))) nil ) ;(defun make-area-temporary (area) ; "Mark an area (specified by number) as temporary." ; (setf (aref area-temporary-flag-array area) 1)) (defvar *room* :unbound "Areas to mention when ROOM is called with no arguments.") (forward-value-cell '*room* 'room) (defun make-area (&key name (region-size #o40000) (gc :dynamic) (read-only ()) (volatility 3 volatilityp) (room ()) (swap-recommendations 0) &allow-other-keys) "Create a new area, or modify an existing one. Returns the area number. Takes keyword argument pairs as follows: :NAME - Symbol which provides name of area. This symbol, which must be supplied, is SET to the area number. :REGION-SIZE - size for regions, between #o40000 and #o4000000 words. :GC - :DYNAMIC - garbage-collector treats this area normally (the default); :STATIC - garbage-collector ignores this area; :FIXED - garbage-collector ignores this area, which may not be consed in. :MOBY-CONSABLE - can be consed in. Moby regions are effectively STATIC for gc purposes. :MOBY-FIXED - already allocated or consable on another machine. Not consable here. :VOLATILITY - The volatility (number between 0 and 3) of newspace regions in this area. :READ-ONLY - If T, the area may not be written or consed in. :ROOM - if specified, push this area onto ROOM, so that (ROOM) will list it. :SWAP-RECOMMENDATIONS - pages prefetched upon a page fault." (declare (unspecial room)) ; (unless (variable-boundp area-temporary-flag-array) ; (setq area-temporary-flag-array (make-array #o400 :element-type 'bit))) (check-type name (and symbol (not null))) (check-type region-size (integer #o40000 #o4000000)) (check-type gc (member :static :dynamic :fixed :moby-consable :moby-fixed)) (check-type volatility (integer 0 3)) (check-type read-only (member t nil)) (check-type swap-recommendations (integer 0 31.)) (and volatilityp (neq gc ':dynamic) (ferror "~S specified, but ~S is not ~S" :volatility :gc :dynamic)) (let ((bits (%logdpb (case gc (:static %region-space-static) ;(:temporary %region-space-static) (:dynamic %region-space-new) (:fixed %region-space-fixed) (:moby-consable %region-space-moby-new) (:moby-fixed %region-space-moby-fixed)) %%region-space-type ;; I think you have to say "no scavenge" for the area so ;; new space regions don't get scavenged. when the microcode ;; allocates a copy region, it automatically turns this on. ;; This is the same behavior as the cold load builder. - Pace 17-Feb-86 (%logdpb (ecase gc (:static 1) ;(:temporary 1) (:dynamic 0) (:fixed 1) (:moby-consable 1) (:moby-fixed 1)) %%region-scavenge-enable (%logdpb swap-recommendations %%region-swapin-quantum (%logdpb volatility %%region-volatility (%logdpb 1 %%region-oldspace-meta-bit (%logdpb 1 %%region-extra-pdl-meta-bit (%logdpb 2 %%region-representation-type (%logdpb (if read-only %pht-map-status-read-only %pht-map-status-read-write-first) %%region-map-status-code (%logdpb (if read-only 2 3) %%region-map-access-code 0)))))))))) (number)) (without-interrupts (gc:without-scavenging (gc:without-flipping (cond ((memq name (current-area-list)) (setq number (symbol-value name))) (t (setq number (aref #'system-communication-area %sys-com-free-area#-list)) (when (= number 0) (ferror "Out of area numbers, cannot create ~S" name)) (setf (aref #'system-communication-area %sys-com-free-area#-list) (%area-region-list number)) (setf (%area-region-list number) (%logdpb 1 %%q-boxed-sign-bit number)) ;(setf (array-leader #'area-name 0) number) ;(vector-push name #'area-name) (setf (aref #'area-name number) name) (setf (symbol-value name) number) (do ((p (current-area-list) (cdr p)) (last-p (value-cell-location '*area-list*) p)) ((null p) (rplacd last-p (list name))) (cond ((not (< (symeval (car p)) number)) (return (rplacd last-p (cons name (cdr last-p))))))))) (setf (%area-region-size number) region-size) (setf (%area-region-bits number) bits) (when (and room (not (memq name *room*))) (push name *room*)) ; (when (eq gc ':temporary) ; (make-area-temporary number)) number))))) (defun rename-area (old-area-name new-area-name &aux tem) "Change the name of an area. This should not be done casually." (check-type old-area-name (and symbol (not null))) (check-type new-area-name (and symbol (not null))) (let ((number (symbol-value old-area-name))) (without-interrupts (gc:without-scavenging (gc:without-flipping (cond ((null (setq tem (memq old-area-name (current-area-list)))) (ferror "~S is not an active area" old-area-name)) ((not (eq old-area-name (aref #'area-name number))) (ferror "Area structure for ~s inconsistant" old-area-name))) (setf (aref #'area-name number) new-area-name) (rplaca tem new-area-name) (makunbound old-area-name) (setf (symbol-value new-area-name) number) (cond ((setq tem (memq old-area-name *room*)) (rplaca tem new-area-name))) number))))) (defun delete-null-areas () (dolist (a-n (current-area-list)) (let ((area-number (symbol-value a-n)) (count 0)) (for-every-region-in-area (region area-number) (incf count)) (if (zerop count) (delete-area a-n))))) (defun delete-area (a-n &aux tem) "Delete an area name. Area must have 0 regions." (check-type a-n (and symbol (not null))) (let ((number (symbol-value a-n))) (cond ((not (eq (%area-region-list number) (%logdpb 1 %%q-boxed-sign-bit number))) (ferror "Area to be deleted has regions."))) (cond ((null (setq tem (memq a-n (current-area-list)))) (ferror "~S is not an active area" a-n)) ((not (eq a-n (aref #'area-name number))) (ferror "Area structure for ~s inconsistant" a-n))) (without-interrupts (gc:without-scavenging (gc:without-flipping (setq *area-list* (delq a-n *area-list*)) (setf (%area-region-list number) (aref #'system-communication-area %sys-com-free-area#-list)) (setf (aref #'system-communication-area %sys-com-free-area#-list) number) (setf (aref #'area-name number) nil) (makunbound a-n) (setq *room* (delq a-n *room*)) number))))) ;;; Structure-handles initialization. This is called right at the beginning ;;; of SI::QLD. (defun setup-structure-handles-for-region (region) (loop with origin = (%region-origin region) with object = origin with page = -1 until (= object (%pointer-plus origin (%region-free-pointer region))) for boxed = (%structure-boxed-size object) for total = (%structure-total-size object) when ( (page-number object) page) do (setf (page-first-header (page-number object)) (page-index object)) for boundary = (+ (page-index object) boxed) when (> boundary #o400) do (loop for p from (1+ (page-number object)) do (decf boundary #o400) until (< boundary #o400) do (setf (page-first-header p) #o400) do (setf (page-initial-qs p) #o400) finally (setf (page-initial-qs p) (page-index boundary))) do (setq page (page-number object)) do (setq object (%pointer-plus object total)) finally (when (= (page-first-header (page-number object)) #o400) (setf (page-first-header (page-number object)) (page-index object))))) (defun setup-structure-handles-for-area (area) (for-every-region-in-area (region area) (initialize-structure-handles-for-region region) (unless (= (%region-representation-type region) %region-representation-type-unstructured) (setup-structure-handles-for-region region)))) (defun initialize-structure-handles-for-region (region) "Define every page in the region to contain #o400 unboxed words." (loop with origin = (%region-origin region) with bound = (%pointer-plus origin (%region-length region)) for page from (page-number origin) below (page-number bound) do (setf (page-first-header page) #o400);No header on this page. do (setf (page-initial-qs page) 0))) ;No initial Qs on this page. (defvar *structure-handles-setup* nil) (defun setup-structure-handles () (loop for symbolic-area in (current-area-list) for area = (symbol-value symbolic-area) do (setup-structure-handles-for-area area) finally (setf (%region-free-pointer virtual-page-data) (%region-length virtual-page-data))) (setq *structure-handles-setup* t) (enable-structure-handles-error-checks)) (defun enable-structure-handles-error-checks () (if *structure-handles-setup* (%p-dpb 1 %%m-flags-check-structure-handles (locf si:%mode-flags)))) (add-initialization 'enable-structure-handles-error-checks '(enable-structure-handles-error-checks) :system) ;;; This is a moderately critical function for the gc process, and is currently crippled by ;;; the (aref #'address-space-map ...). This function could be made about 4 times faster by ;;; converting the address space map into a one-word-per-quantum table, which could be scanned ;;; quickly using the sort of address arithmetic used in gc::compute-storage-distribution. (defun unallocated-space () "The amount of space not allocated to any region." ;; LOOP can't (declare (unspecial base)) (let ((base (truncate (%pointer-plus (%region-origin init-list-area) (%region-length init-list-area)) %address-space-quantum-size)) (bound (truncate virtual-memory-size %address-space-quantum-size))) (declare (unspecial base)) (* (loop for i from base below bound count (zerop (aref #'address-space-map i))) %address-space-quantum-size))) (defun unused-space () "The amount of space allocated to regions but not yet used by them." (with-quick-region-area-accessors (loop with bound = sys:number-of-regions with %%type = sys:%%region-space-type for region from 0 below bound when (memq (%logldb %%type (%region-bits region)) '#.(list %region-space-new %region-space-copy %region-space-static)) sum (- (%region-length region) (%region-free-pointer region))))) ;;; To be conservative, "free space", as far as the human or the GC are concerned, ;;; is the amount of space in unallocated quantums. Unused space in regions may or ;;; may not be usable. (deff free-space 'unallocated-space) (deff get-free-space-size 'unallocated-space) ;Crufty name has proliferated.