mirror of
git://git.code.sf.net/p/sbcl/sbcl
synced 2026-09-10 07:26:40 -04:00
Shrink globldb packed infos if #+compact-instance-header
Don't use a simple-vector for storage, as it essentially wasted 12 bytea per vector (counting all the 0s in the header and length words). Rather than invent a new primitive object, this change uses a variable-length INSTANCE, similarly to how CONDITION primitive objects are represented, yielding somewhere between 5% to 10% space reduction for those data, which is significant if have >16MiB of 'em eating up heap space. There is no savings for #-compact-instance-header, but there are easy ways to remedy that: (1) invent a new headered primitive object that is vector-like but with the length in the header, (2) put a bit in an instance header saying that next word which would ordinarily be a LAYOUT is not, (3) just use the LAYOUT slot for whatever we like, and make GC robust against failure. Option (3) isn't actually too unsafe - the 0th info element is a fixnum. Of course there's always option (4) - implement #+compact-instance-header. And there's no real benefit for 32-bit, since there wasn't as much waste. A few more points to note: - SYMBOL-INFO-VECTOR got renamed to SYMBOL-DBINFO, and SYMBOL-INFO to SYMBOL-%INFO for the primitive slot to try to keep things clearer as to which accesses the globaldb. - It wasn't worth redoing all the specialized vops for SYMBOL-PLIST that would have been needed, so they're all gone. - these days we don't need so much of the "!" convention for removing unnecessary symbols rom the core, since the tree-shaker does that.
This commit is contained in:
parent
c8e8dead0d
commit
a2d379665d
|
|
@ -327,10 +327,11 @@ Please check that all strings which were not recognizable to the compiler
|
|||
;; such that it now has neither properties nor globaldb info,
|
||||
;; can have the slot set back to NIL if it wasn't already.
|
||||
(do-all-symbols (symbol)
|
||||
(when (and (sb-kernel:symbol-info symbol)
|
||||
(null (sb-kernel:symbol-info-vector symbol))
|
||||
(when (and (sb-kernel:symbol-%info symbol) ; "raw" value is something
|
||||
;; but both "cooked" values are empty
|
||||
(null (sb-kernel:symbol-dbinfo symbol))
|
||||
(null (symbol-plist symbol)))
|
||||
(setf (sb-kernel:symbol-info symbol) nil)))
|
||||
(setf (sb-kernel:symbol-%info symbol) nil)))
|
||||
)
|
||||
|
||||
(sb-ext:gc :full t)
|
||||
|
|
@ -395,7 +396,7 @@ Please check that all strings which were not recognizable to the compiler
|
|||
#+sb-devel
|
||||
(lambda (symbol accessibility)
|
||||
(declare (ignore accessibility))
|
||||
(or (sb-kernel:symbol-info symbol)
|
||||
(or (sb-kernel:symbol-%info symbol)
|
||||
(and (boundp symbol) (not (keywordp symbol)))))
|
||||
;; Release mode: retain all symbols satisfying this intricate test
|
||||
#-sb-devel
|
||||
|
|
@ -448,7 +449,7 @@ Please check that all strings which were not recognizable to the compiler
|
|||
sb-assem::*backend-instruction-set-package*)
|
||||
(or (eq accessibility :external) (asm-inst-p symbol))
|
||||
;; By default, retain any symbol with any attachments
|
||||
(or (sb-kernel:symbol-info symbol)
|
||||
(or (sb-kernel:symbol-%info symbol)
|
||||
(and (boundp symbol) (not (keywordp symbol))))))))
|
||||
:verbose nil :print nil)
|
||||
(unintern 'sb-impl::shake-packages 'sb-impl)
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@
|
|||
(:export tail-call-symbol))
|
||||
((:temp fun (any-reg descriptor-reg) lexenv-offset)
|
||||
(:temp length (any-reg descriptor-reg) nl0-offset)
|
||||
(:temp vector (any-reg descriptor-reg) r7-offset)
|
||||
(:temp packed-info (any-reg descriptor-reg) r7-offset)
|
||||
(:temp temp (any-reg descriptor-reg) nl1-offset)
|
||||
(:temp temp2 (any-reg descriptor-reg) nl2-offset))
|
||||
(inst str lr-tn (@ cfp-tn 8))
|
||||
|
|
@ -191,24 +191,30 @@
|
|||
(inst cmp temp symbol-widetag)
|
||||
(inst b :ne not-callable)
|
||||
|
||||
(load-symbol-info-vector vector fun temp)
|
||||
(load-symbol-dbinfo packed-info fun temp)
|
||||
|
||||
;; info-vector-fdefn
|
||||
(inst cmp vector null-tn)
|
||||
;; packed-info-fdefn
|
||||
(inst cmp packed-info null-tn)
|
||||
(inst b :eq undefined)
|
||||
|
||||
(inst ldr temp (@ vector (- (* 2 n-word-bytes) other-pointer-lowtag)))
|
||||
;; read the 0th info descriptor
|
||||
(inst ldr temp (@ packed-info
|
||||
(- (ash (+ instance-slots-offset instance-data-start) word-shift)
|
||||
instance-pointer-lowtag)))
|
||||
(inst and temp temp (fixnumize (1- (ash 1 (* info-number-bits 2)))))
|
||||
(inst movz temp2 (fixnumize (1+ (ash +fdefn-info-num+ info-number-bits))))
|
||||
(inst cmp temp temp2)
|
||||
(inst b :lt undefined)
|
||||
|
||||
(inst ldr length (@ vector
|
||||
(- (ash vector-length-slot word-shift) other-pointer-lowtag)))
|
||||
;; read the instance-length and mask out the extra bits
|
||||
(inst ldr length (@ packed-info (- instance-pointer-lowtag)))
|
||||
;; these next two instructions should be one 'ubfx' but I'm not smart enough
|
||||
(inst lsr length length instance-length-shift)
|
||||
(inst and length length instance-length-mask)
|
||||
(inst lsl length length word-shift)
|
||||
|
||||
(inst lsl length length (- word-shift n-fixnum-tag-bits))
|
||||
(inst sub length length (- other-pointer-lowtag 8))
|
||||
(inst ldr fun (@ vector length))
|
||||
(inst sub length length instance-pointer-lowtag)
|
||||
(inst ldr fun (@ packed-info length))
|
||||
(loadw lr-tn fun fdefn-raw-addr-slot other-pointer-lowtag)
|
||||
(inst add lr-tn lr-tn 4)
|
||||
(inst br lr-tn)
|
||||
|
|
|
|||
|
|
@ -229,27 +229,38 @@
|
|||
#+sb-assembling
|
||||
(define-assembly-routine (call-symbol
|
||||
(:return-style :none))
|
||||
((:temp fun (any-reg descriptor-reg) rax-offset)
|
||||
((:temp fun (any-reg descriptor-reg) rax-offset) ; FUN = the symbol
|
||||
(:temp length (any-reg descriptor-reg) rax-offset)
|
||||
(:temp vector (any-reg descriptor-reg) rbx-offset))
|
||||
(%lea-for-lowtag-test vector fun other-pointer-lowtag)
|
||||
(inst test :byte vector lowtag-mask)
|
||||
(:temp info (any-reg descriptor-reg) rbx-offset)) ; for the packed symbol-info
|
||||
(%lea-for-lowtag-test info fun other-pointer-lowtag)
|
||||
(inst test :byte info lowtag-mask)
|
||||
(inst jmp :nz not-callable)
|
||||
(inst cmp :byte (ea (- other-pointer-lowtag) fun) symbol-widetag)
|
||||
(inst jmp :ne not-callable)
|
||||
(load-symbol-info-vector vector fun)
|
||||
;; info-vector-fdefn
|
||||
(inst cmp vector nil-value)
|
||||
(load-symbol-dbinfo info fun)
|
||||
;; reimplement by hand PACKED-INFO-FDEFN, q.v.
|
||||
|
||||
;; This only has to compare the low byte of INFO,
|
||||
;; because INSTANCE-POINTER-LOWTAG won't match NIL in the low 8 bits.
|
||||
(inst cmp :byte info (logand nil-value #xff))
|
||||
(inst jmp :e undefined)
|
||||
|
||||
(inst mov :dword r10-tn (ea (- (* 2 n-word-bytes) other-pointer-lowtag) vector))
|
||||
;; XXX - WHAT IS R10? ARBITRARY? COMMENT NEEDED
|
||||
(inst mov :dword r10-tn (ea (- (ash (+ instance-slots-offset instance-data-start) word-shift)
|
||||
instance-pointer-lowtag) info))
|
||||
(inst and :dword r10-tn (fixnumize (1- (ash 1 (* info-number-bits 2)))))
|
||||
(inst cmp :dword r10-tn (fixnumize (1+ (ash +fdefn-info-num+ info-number-bits))))
|
||||
(inst jmp :b undefined)
|
||||
|
||||
(inst mov vector-len-op-size length (vector-len-ea vector))
|
||||
(inst mov fun (ea (- 8 other-pointer-lowtag) vector length
|
||||
(ash 1 (- word-shift n-fixnum-tag-bits))))
|
||||
;; Read the logical instance length, i.e. excluding a stable hash slot if present,
|
||||
;; but including a LAYOUT slot if #-compact-instance-header.
|
||||
;; There's a optimization possibility here to eliminate one SHR, which would use
|
||||
;; a 4-byte unaligned load at byte index 1 of the header, which would put the length
|
||||
;; in byte index 0 of the register, but over by 2 bits; mask that, adjust the EA SCALE
|
||||
;; to get the indexing right. That's too abstraction-violating for my taste.
|
||||
(load-instance-length length info nil)
|
||||
;; After this MOV, the FUN register will hold the FDEFN.
|
||||
(inst mov fun (ea (- instance-pointer-lowtag) info length n-word-bytes))
|
||||
|
||||
(inst jmp (ea (- (* fdefn-raw-addr-slot n-word-bytes) other-pointer-lowtag) fun))
|
||||
UNDEFINED
|
||||
|
|
|
|||
|
|
@ -432,7 +432,7 @@
|
|||
(logior (ash (1- symbol-size) n-widetag-bits) symbol-widetag)))))
|
||||
;; symbol-hash was initialized to 0
|
||||
(%set-symbol-global-value symbol (make-unbound-marker))
|
||||
(setf (symbol-info symbol) nil)
|
||||
(setf (symbol-%info symbol) nil)
|
||||
(%primitive sb-vm::set-slot symbol name
|
||||
'make-symbol sb-vm:symbol-name-slot sb-vm:other-pointer-lowtag)
|
||||
(%set-symbol-package symbol nil)
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@
|
|||
(def %raw-instance-cas/signed-word (instance index)
|
||||
%raw-instance-ref/signed-word
|
||||
%raw-instance-set/signed-word)
|
||||
(def %compare-and-swap-symbol-info (symbol) symbol-info)
|
||||
(def %compare-and-swap-symbol-value (symbol) symbol-value)
|
||||
(def %compare-and-swap-svref (vector index) svref))
|
||||
|
||||
|
|
|
|||
|
|
@ -167,7 +167,6 @@
|
|||
bits)
|
||||
(defsetf symbol-value set)
|
||||
(defsetf symbol-global-value set-symbol-global-value)
|
||||
(defsetf symbol-plist %set-symbol-plist)
|
||||
(defsetf fill-pointer %set-fill-pointer)
|
||||
(defsetf subseq (sequence start &optional end) (v)
|
||||
`(progn (replace ,sequence ,v :start1 ,start :end1 ,end) ,v))
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@
|
|||
;; the mask word indefinitely. Most bitmaps will have only 1 word,
|
||||
;; so this is almost always MOST-POSITIVE-FIXNUM.
|
||||
(,nbits (if (= ,bitmap-index ,bitmap-limit)
|
||||
sb-vm::instance-length-mask
|
||||
sb-vm:instance-length-mask
|
||||
(- sb-vm:n-word-bits sb-vm:instance-data-start))))
|
||||
(declare (type sb-vm:signed-word ,mask)
|
||||
(type fixnum ,nbits))
|
||||
|
|
@ -227,7 +227,7 @@
|
|||
(when (zerop ,nbits)
|
||||
(setq ,mask (%raw-instance-ref/signed-word ,bitmap ,bitmap-index)
|
||||
,nbits (if (= (incf ,bitmap-index) ,bitmap-limit)
|
||||
sb-vm::instance-length-mask
|
||||
sb-vm:instance-length-mask
|
||||
sb-vm:n-word-bits)))
|
||||
(when (logbitp 0 ,mask) ,@body)
|
||||
(setq ,mask (ash ,mask -1)
|
||||
|
|
|
|||
|
|
@ -37,14 +37,14 @@
|
|||
#+immobile-code (sb-vm::%set-fdefn-fun fdefn fun)
|
||||
#-immobile-code (setf (fdefn-fun fdefn) fun))
|
||||
|
||||
;; Given Info-Vector VECT, return the fdefn that it contains for its root name,
|
||||
;; Given PACKED-INFO, return the fdefn that it contains for its root name,
|
||||
;; or nil if there is no value. NIL input is acceptable and will return NIL.
|
||||
;; (see src/compiler/info-vector for more details)
|
||||
(declaim (inline info-vector-fdefn))
|
||||
(defun info-vector-fdefn (vect)
|
||||
(when vect
|
||||
;; This is safe: Info-Vector invariant requires that it have length >= 1.
|
||||
(let ((word (the fixnum (svref vect 0))))
|
||||
(declaim (inline packed-info-fdefn))
|
||||
(defun packed-info-fdefn (packed-info)
|
||||
(when packed-info
|
||||
;; This is safe: PACKED-INFO invariant requires that it have length >= 1.
|
||||
(let ((word (the fixnum (%info-ref packed-info 0))))
|
||||
;; Test that the first info-number is +fdefn-info-num+ and its n-infos
|
||||
;; field is nonzero. These conditions can be tested simultaneously
|
||||
;; using a SIMD-in-a-register idea. The low 6 bits must be nonzero
|
||||
|
|
@ -52,16 +52,15 @@
|
|||
;; as a 12-bit unsigned integer it must be >= #b111111000001
|
||||
(when (>= (ldb (byte (* info-number-bits 2) 0) word)
|
||||
(1+ (ash +fdefn-info-num+ info-number-bits)))
|
||||
;; DATA-REF-WITH-OFFSET doesn't know the info-vector length invariant,
|
||||
;; so depite (safety 0) eliding bounds check, FOLD-INDEX-ADDRESSING
|
||||
;; wasn't kicking in without (TRULY-THE (INTEGER 1 *)).
|
||||
(aref vect (1- (truly-the (integer 1 *) (length vect))))))))
|
||||
(%info-ref packed-info
|
||||
(1- (truly-the (integer 1 *)
|
||||
(packed-info-len packed-info))))))))
|
||||
|
||||
;; Return SYMBOL's fdefinition, if any, or NIL. SYMBOL must already
|
||||
;; have been verified to be a symbol by the caller.
|
||||
(defun symbol-fdefn (symbol)
|
||||
(declare (optimize (safety 0)))
|
||||
(info-vector-fdefn (symbol-info-vector symbol)))
|
||||
(packed-info-fdefn (symbol-dbinfo symbol)))
|
||||
|
||||
;; Return the fdefn object for NAME, or NIL if there is no fdefn.
|
||||
;; Signal an error if name isn't valid.
|
||||
|
|
@ -80,7 +79,7 @@
|
|||
(return-from find-fdefn it))
|
||||
:simple
|
||||
(progn
|
||||
(awhen (symbol-info-vector key1)
|
||||
(awhen (symbol-dbinfo key1)
|
||||
(multiple-value-bind (data-idx descriptor-idx field-idx)
|
||||
(info-find-aux-key/packed it key2)
|
||||
(declare (type index descriptor-idx)
|
||||
|
|
@ -93,7 +92,7 @@
|
|||
(when (eql (packed-info-field it descriptor-idx field-idx)
|
||||
+fdefn-info-num+)
|
||||
(return-from find-fdefn
|
||||
(aref it (1- (the index data-idx))))))))
|
||||
(%info-ref it (1- (the index data-idx))))))))
|
||||
(when (eq key1 'setf) ; bypass the legality test
|
||||
(return-from find-fdefn nil))))
|
||||
(legal-fun-name-or-type-error name))
|
||||
|
|
|
|||
|
|
@ -9,14 +9,27 @@
|
|||
|
||||
(in-package "SB-IMPL")
|
||||
|
||||
;; Call FUNCTION once for each Name in globaldb that has information associated
|
||||
;; with it, passing the function the Name as its only argument.
|
||||
;;
|
||||
;; This is in its own file to avoid creating an early dependency on
|
||||
;; target-package iterators.
|
||||
(macrolet
|
||||
((def (&rest situations)
|
||||
`(eval-when ,situations
|
||||
;; Return all function names that are stored in SYMBOL's packe-info.
|
||||
;; As an example, (INFO-NAME-LIST 'SB-PCL::DIRECT-SUPERCLASSES) =>
|
||||
;; ((SB-PCL::SLOT-ACCESSOR :GLOBAL SB-PCL::DIRECT-SUPERCLASSES SB-PCL::READER)
|
||||
;; (SB-PCL::SLOT-ACCESSOR :GLOBAL SB-PCL::DIRECT-SUPERCLASSES BOUNDP)
|
||||
;; (SB-PCL::SLOT-ACCESSOR :GLOBAL SB-PCL::DIRECT-SUPERCLASSES SB-PCL::WRITER))
|
||||
(defun info-name-list (symbol)
|
||||
(let ((packed-info (symbol-dbinfo symbol))
|
||||
(list))
|
||||
(when packed-info
|
||||
(do-packed-info-aux-key (packed-info key-index)
|
||||
(push (construct-globaldb-name (%info-ref packed-info key-index) symbol)
|
||||
list))
|
||||
(nconc (and (plusp (packed-info-field packed-info 0 0)) (list symbol))
|
||||
(nreverse list)))))
|
||||
;; Call FUNCTION once for each Name in globaldb that has information associated
|
||||
;; with it, passing the function the Name as its only argument.
|
||||
(defun call-with-each-globaldb-name (fun-designator)
|
||||
(let ((function (cl:coerce fun-designator 'function)))
|
||||
(with-package-iterator (iter (list-all-packages) :internal :external)
|
||||
|
|
@ -28,7 +41,7 @@
|
|||
;; always keep it since we can't know if it has been seen once.
|
||||
(when (or (not (sb-xc:symbol-package symbol))
|
||||
(eq package (sb-xc:symbol-package symbol)))
|
||||
(dolist (name (info-vector-name-list symbol))
|
||||
(dolist (name (info-name-list symbol))
|
||||
(funcall function name))))))
|
||||
,@(unless (equal situations '(:compile-toplevel))
|
||||
`((dovector (obj (car *fdefns*))
|
||||
|
|
|
|||
|
|
@ -1039,7 +1039,7 @@ We could try a few things to mitigate this:
|
|||
,.(make-case 'array)
|
||||
,.(make-case* 'symbol
|
||||
`(,functoid (%primitive sb-c:fast-symbol-global-value ,obj) ,@more)
|
||||
`(,functoid (symbol-info ,obj) ,@more)
|
||||
`(,functoid (symbol-%info ,obj) ,@more)
|
||||
`(,functoid (symbol-name ,obj) ,@more)
|
||||
`(,functoid (symbol-package ,obj) ,@more)
|
||||
`(when (symbol-extra-slot-p ,obj)
|
||||
|
|
|
|||
|
|
@ -162,7 +162,11 @@
|
|||
(def symbol-hash)
|
||||
(def sb-vm::symbol-extra)
|
||||
#+sb-thread (def symbol-tls-index)
|
||||
#.(if (fboundp 'symbol-info-vector) (values) '(def symbol-info-vector))
|
||||
(def symbol-%info) ; primitive reader always needs a stub
|
||||
(def (setf symbol-%info) (info symbol)) ; as does primitive writer
|
||||
;; but the "wrapped" reader might not need a stub.
|
||||
;; If it's already a proper function, then it doesn't.
|
||||
#.(if (fboundp 'symbol-dbinfo) (values) '(def symbol-dbinfo))
|
||||
#-(or x86 x86-64) (def lra-code-header)
|
||||
(def %make-lisp-obj)
|
||||
(def get-lisp-obj-address)
|
||||
|
|
|
|||
|
|
@ -149,14 +149,6 @@ distinct from the global value. Can also be SETF."
|
|||
;; State 2 transitions to state 3 via ({SETF|CAS} SYMBOL-PLIST).
|
||||
;; There are *no* other permissible state transitions.
|
||||
|
||||
(defun symbol-info (symbol)
|
||||
(symbol-info symbol))
|
||||
|
||||
;; An "interpreter stub" for an operation that is only implemented for
|
||||
;; the benefit of platforms without compare-and-swap-vops.
|
||||
(defun (setf symbol-info) (new-info symbol)
|
||||
(setf (symbol-info symbol) new-info))
|
||||
|
||||
;; Atomically update SYMBOL's info/plist slot to contain a new info vector.
|
||||
;; The vector is computed by calling UPDATE-FN on the old vector,
|
||||
;; repeatedly as necessary, until no conflict happens with other updaters.
|
||||
|
|
@ -164,25 +156,25 @@ distinct from the global value. Can also be SETF."
|
|||
(defun update-symbol-info (symbol update-fn)
|
||||
(declare (symbol symbol)
|
||||
(type (function (t) t) update-fn))
|
||||
(prog ((info-holder (symbol-info symbol))
|
||||
(current-vect))
|
||||
(prog ((info-holder (symbol-%info symbol))
|
||||
(current-info)) ; a PACKED-INFO or NIL
|
||||
outer-restart
|
||||
;; Do not use SYMBOL-INFO-VECTOR - this must not perform a slot read again.
|
||||
(setq current-vect (if (listp info-holder) (cdr info-holder) info-holder))
|
||||
;; This _must_ _not_ perform another read of the INFO slot here.
|
||||
(setq current-info (if (listp info-holder) (cdr info-holder) info-holder))
|
||||
inner-restart
|
||||
(let ((new-vect (funcall update-fn (or current-vect +nil-packed-infos+))))
|
||||
(unless (simple-vector-p new-vect)
|
||||
(aver (null new-vect))
|
||||
(let ((new-info (funcall update-fn (or current-info +nil-packed-infos+))))
|
||||
(unless (%instancep new-info)
|
||||
(aver (null new-info))
|
||||
(return)) ; nothing to do
|
||||
(if (consp info-holder) ; State 3: exchange the CDR
|
||||
(let ((old (%compare-and-swap-cdr info-holder current-vect new-vect)))
|
||||
(when (eq old current-vect) (return t)) ; win
|
||||
(setq current-vect old) ; Don't touch holder- it's still a cons
|
||||
(let ((old (cas (cdr info-holder) current-info new-info)))
|
||||
(when (eq old current-info) (return t)) ; win
|
||||
(setq current-info old) ; Don't touch holder- it's still a cons
|
||||
(go inner-restart)))
|
||||
;; State 1 or 2: info-holder is NIL or a vector.
|
||||
;; Exchange the contents of the info slot. Type-inference derives
|
||||
;; SIMPLE-VECTOR-P on the args to CAS, so no extra checking.
|
||||
(let ((old (%compare-and-swap-symbol-info symbol info-holder new-vect)))
|
||||
;; State 1 or 2: info-holder is NIL or a PACKED-INFO.
|
||||
;; Exchange the contents of the info slot. Type-inference should have
|
||||
;; derived that NEW-INFO satisfies the slot type restriction (I hope).
|
||||
(let ((old (cas-symbol-%info symbol info-holder new-info)))
|
||||
(when (eq old info-holder) (return t)) ; win
|
||||
;; Check whether we're in state 2 or 3 now.
|
||||
;; Impossible to be in state 1: nobody ever puts NIL in the slot.
|
||||
|
|
@ -190,38 +182,25 @@ distinct from the global value. Can also be SETF."
|
|||
(setq info-holder old)
|
||||
(go outer-restart)))))
|
||||
|
||||
(eval-when (:compile-toplevel)
|
||||
;; If we're in state 1 or state 3, we can take (CAR (SYMBOL-INFO S))
|
||||
;; to get the property list. If we're in state 2, this same access
|
||||
;; gets the fixnum which is the VECTOR-LENGTH of the info vector.
|
||||
;; So all we have to do is turn any fixnum to NIL, and we have a plist.
|
||||
;; Ensure that this pun stays working.
|
||||
#-ppc64 ; ppc64 has unevenly spaced lowtags
|
||||
(assert (= (- (* sb-vm:n-word-bytes sb-vm:cons-car-slot)
|
||||
sb-vm:list-pointer-lowtag)
|
||||
(- (* sb-vm:n-word-bytes sb-vm:vector-length-slot)
|
||||
sb-vm:other-pointer-lowtag))))
|
||||
|
||||
(defun symbol-plist (symbol)
|
||||
"Return SYMBOL's property list."
|
||||
(if (sb-c::vop-existsp :translate cl:symbol-plist)
|
||||
(symbol-plist symbol)
|
||||
(let ((list (car (truly-the list (symbol-info symbol))))) ; a harmless lie
|
||||
;; Just ensure the result is not a fixnum, and we're done.
|
||||
(if (fixnump list) nil list))))
|
||||
(let ((list (symbol-%info symbol)))
|
||||
;; See the comments above UPDATE-SYMBOL-INFO for a
|
||||
;; reminder as to why this logic is right.
|
||||
(if (%instancep list) nil (car list))))
|
||||
|
||||
(declaim (ftype (sfunction (symbol t) cons) %ensure-plist-holder)
|
||||
(inline %ensure-plist-holder))
|
||||
|
||||
;; When a plist update (setf or cas) is first performed on a symbol,
|
||||
;; a one-time allocation of an extra cons is done which creates two
|
||||
;; "slots" from one: a slot for the info-vector and a slot for the plist.
|
||||
;; "slots" from one: a slot for the PACKED-INFO and a slot for the plist.
|
||||
;; This avoids complications in the implementation of the user-facing
|
||||
;; (CAS SYMBOL-PLIST) function, which should not have to be aware of
|
||||
;; competition from globaldb mutators even if no other threads attempt
|
||||
;; to manipulate the plist per se.
|
||||
|
||||
;; Given a SYMBOL and its current INFO of type (OR LIST SIMPLE-VECTOR)
|
||||
;; Given a SYMBOL and its current INFO of type (OR LIST INSTANCE)
|
||||
;; ensure that SYMBOL's current info is a cons, and return that.
|
||||
;; If racing with multiple threads, at most one thread will install the cons.
|
||||
(defun %ensure-plist-holder (symbol info)
|
||||
|
|
@ -233,54 +212,55 @@ distinct from the global value. Can also be SETF."
|
|||
;; The pointer from the new cons to the old info must be persisted
|
||||
;; to memory before the symbol's info slot points to the cons.
|
||||
;; [x86oid doesn't need the barrier, others might]
|
||||
(sb-thread:barrier (:write)
|
||||
(setq newcell (cons nil info)))
|
||||
(loop (let ((old (%compare-and-swap-symbol-info symbol info newcell)))
|
||||
(setq newcell (cons nil info))
|
||||
(sb-thread:barrier (:write)) ; oh such ghastly syntax
|
||||
(loop (let ((old (cas-symbol-%info symbol info newcell)))
|
||||
(cond ((eq old info) (return newcell)) ; win
|
||||
((consp old) (return old))) ; somebody else made a cons!
|
||||
(setq info old)
|
||||
(sb-thread:barrier (:write) ; Retry using same newcell
|
||||
(rplacd newcell info)))))))
|
||||
(rplacd newcell info)
|
||||
(sb-thread:barrier (:write))))))) ; Retry using same newcell
|
||||
|
||||
(declaim (inline (cas symbol-plist) %set-symbol-plist))
|
||||
(declaim (inline (cas symbol-plist) (setf symbol-plist)))
|
||||
|
||||
(defun (cas symbol-plist) (old new symbol)
|
||||
;; If SYMBOL's info cell is a cons, we can do (CAS CAR). Otherwise punt.
|
||||
(declare (symbol symbol) (list old new))
|
||||
(let ((cell (symbol-info symbol)))
|
||||
(let ((cell (symbol-%info symbol)))
|
||||
(if (consp cell)
|
||||
(%compare-and-swap-car cell old new)
|
||||
(%compare-and-swap-symbol-plist old new symbol))))
|
||||
(%cas-symbol-plist old new symbol))))
|
||||
|
||||
(defun %compare-and-swap-symbol-plist (old new symbol)
|
||||
(defun %cas-symbol-plist (old new symbol)
|
||||
;; This is just the second half of a partially-inline function, to avoid
|
||||
;; code bloat in the exceptional case. Type assertions should have been
|
||||
;; done - or not, per policy - by the caller of %COMPARE-AND-SWAP-SYMBOL-PLIST
|
||||
;; so now use TRULY-THE to avoid further type checking.
|
||||
(%compare-and-swap-car (%ensure-plist-holder (truly-the symbol symbol)
|
||||
(symbol-info symbol))
|
||||
(symbol-%info symbol))
|
||||
old new))
|
||||
|
||||
(defun %set-symbol-plist (symbol new-value)
|
||||
;; This is the entry point into which (SETF SYMBOL-PLIST) is transformed.
|
||||
(defun (setf symbol-plist) (new-value symbol)
|
||||
;; If SYMBOL's info cell is a cons, we can do (SETF CAR). Otherwise punt.
|
||||
(declare (symbol symbol) (list new-value))
|
||||
(let ((cell (symbol-info symbol)))
|
||||
(let ((cell (symbol-%info symbol)))
|
||||
(if (consp cell)
|
||||
(setf (car cell) new-value)
|
||||
(%%set-symbol-plist symbol new-value))))
|
||||
(%set-symbol-plist symbol new-value))))
|
||||
|
||||
(defun %%set-symbol-plist (symbol new-value)
|
||||
;; Same considerations as for %%COMPARE-AND-SWAP-SYMBOL-PLIST,
|
||||
(defun %set-symbol-plist (symbol new-value)
|
||||
;; Same considerations as for %CAS-SYMBOL-PLIST,
|
||||
;; with a slight efficiency hack: if the symbol has no plist holder cell
|
||||
;; and the NEW-VALUE is NIL, try to avoid creating a holder cell.
|
||||
;; Yet we must write something, because omitting a memory operation
|
||||
;; could have a subtle effect in the presence of multi-threading.
|
||||
(let ((info (symbol-info (truly-the symbol symbol))))
|
||||
(let ((info (symbol-%info (truly-the symbol symbol))))
|
||||
(when (and (not new-value) (atom info)) ; try to treat this as a no-op
|
||||
(let ((old (%compare-and-swap-symbol-info symbol info info)))
|
||||
;; INFO is either an INSTANCE (a PACKED-INFO) or NIL.
|
||||
;; Write the same thing back, to say we set the plist to NIL.
|
||||
(let ((old (cas-symbol-%info symbol info info)))
|
||||
(if (eq old info) ; good enough
|
||||
(return-from %%set-symbol-plist new-value) ; = nil
|
||||
(return-from %set-symbol-plist new-value) ; = nil
|
||||
(setq info old))))
|
||||
(setf (car (%ensure-plist-holder symbol info)) new-value)))
|
||||
|
||||
|
|
|
|||
|
|
@ -1093,6 +1093,7 @@ possibly temporarily, because it might be used internally."
|
|||
"*RECOGNIZED-DECLARATIONS*"
|
||||
"+INFOS-PER-WORD+"
|
||||
"+FDEFN-INFO-NUM+"
|
||||
"PACKED-INFO"
|
||||
"+NIL-PACKED-INFOS+"
|
||||
"ATOMIC-SET-INFO-VALUE"
|
||||
"CALL-WITH-EACH-GLOBALDB-NAME"
|
||||
|
|
@ -1110,7 +1111,7 @@ possibly temporarily, because it might be used internally."
|
|||
"INFO-MAPHASH"
|
||||
"INFO-NUMBER"
|
||||
"INFO-NUMBER-BITS"
|
||||
"INFO-VECTOR-FDEFN"
|
||||
"PACKED-INFO-FDEFN"
|
||||
"MAKE-INFO-HASHTABLE"
|
||||
"META-INFO"
|
||||
"META-INFO-NUMBER"
|
||||
|
|
@ -1589,7 +1590,6 @@ is a good idea, but see SB-SYS re. blurring of boundaries."
|
|||
"%COMPARE-AND-SWAP-CAR"
|
||||
"%COMPARE-AND-SWAP-CDR"
|
||||
"%COMPARE-AND-SWAP-SVREF"
|
||||
"%COMPARE-AND-SWAP-SYMBOL-INFO"
|
||||
"%COMPARE-AND-SWAP-SYMBOL-VALUE"
|
||||
"%CONCATENATE-TO-BASE-STRING"
|
||||
"%CONCATENATE-TO-STRING"
|
||||
|
|
@ -2281,7 +2281,7 @@ is a good idea, but see SB-SYS re. blurring of boundaries."
|
|||
"MAKE-STATIC-CLASSOID"
|
||||
"%MAKE-SYMBOL"
|
||||
"%FUNCALLABLE-INSTANCE-FUN" "SYMBOL-HASH" "SYMBOL-HASH*"
|
||||
"SYMBOL-INFO" "SYMBOL-INFO-VECTOR"
|
||||
"SYMBOL-%INFO" "SYMBOL-DBINFO" "%INFO-REF"
|
||||
|
||||
"EXTENDED-SEQUENCE" "*EXTENDED-SEQUENCE-TYPE*"
|
||||
"EXTENDED-SEQUENCE-P"
|
||||
|
|
@ -3259,6 +3259,7 @@ structure representations"
|
|||
"THREAD-SPROF-DATA-SLOT"
|
||||
"TLS-SIZE" "N-WIDETAG-BITS" "WIDETAG-MASK"
|
||||
"INSTANCE-LENGTH-SHIFT"
|
||||
"INSTANCE-LENGTH-MASK"
|
||||
"UNBOUND-MARKER-WIDETAG"
|
||||
"UNDEFINED-FUNCTION-TRAP"
|
||||
"NO-TLS-VALUE-MARKER-WIDETAG"
|
||||
|
|
|
|||
|
|
@ -535,7 +535,7 @@
|
|||
(cond ((not s)
|
||||
(push (cons pkg string) nonexistent))
|
||||
((and (not (boundp s))
|
||||
(not (sb-kernel:symbol-info s))
|
||||
(not (sb-kernel:symbol-%info s))
|
||||
(not (gethash s sb-c::*backend-parsed-vops*)))
|
||||
(push s uninteresting))))))))
|
||||
(format t "~&Nonexistent:~%")
|
||||
|
|
|
|||
|
|
@ -216,9 +216,9 @@
|
|||
(inst sub ndescr ndescr (- other-pointer-lowtag fun-pointer-lowtag))
|
||||
(inst add func code ndescr)))
|
||||
;;;
|
||||
(define-vop (symbol-info-vector)
|
||||
(define-vop (symbol-dbinfo)
|
||||
(:policy :fast-safe)
|
||||
(:translate symbol-info-vector)
|
||||
(:translate symbol-dbinfo)
|
||||
(:args (x :scs (descriptor-reg)))
|
||||
(:results (res :scs (descriptor-reg)))
|
||||
(:temporary (:sc unsigned-reg) temp)
|
||||
|
|
@ -228,19 +228,6 @@
|
|||
(inst and temp res lowtag-mask)
|
||||
(inst cmp temp list-pointer-lowtag)
|
||||
(loadw res res cons-cdr-slot list-pointer-lowtag :eq)))
|
||||
|
||||
(define-vop (symbol-plist)
|
||||
(:policy :fast-safe)
|
||||
(:translate symbol-plist)
|
||||
(:args (x :scs (descriptor-reg)))
|
||||
(:results (res :scs (descriptor-reg)))
|
||||
(:generator 1
|
||||
(loadw res x symbol-info-slot other-pointer-lowtag)
|
||||
;; Instruction pun: (CAR x) is the same as (VECTOR-LENGTH x)
|
||||
;; so if the info slot holds a vector, this gets a fixnum- it's not a plist.
|
||||
(loadw res res cons-car-slot list-pointer-lowtag)
|
||||
(inst tst res fixnum-tag-mask)
|
||||
(inst mov :eq res null-tn)))
|
||||
|
||||
;;;; other miscellaneous VOPs
|
||||
|
||||
|
|
|
|||
|
|
@ -225,7 +225,7 @@
|
|||
(inst add func code ndescr)))
|
||||
;;;
|
||||
|
||||
(defun load-symbol-info-vector (result symbol temp)
|
||||
(defun load-symbol-dbinfo (result symbol temp)
|
||||
(assemble ()
|
||||
(loadw result symbol symbol-info-slot other-pointer-lowtag)
|
||||
;; If RESULT has list-pointer-lowtag, take its CDR. If not, use it as-is.
|
||||
|
|
@ -235,27 +235,14 @@
|
|||
(loadw result result cons-cdr-slot list-pointer-lowtag)
|
||||
NE))
|
||||
|
||||
(define-vop (symbol-info-vector)
|
||||
(define-vop (symbol-dbinfo)
|
||||
(:policy :fast-safe)
|
||||
(:translate symbol-info-vector)
|
||||
(:translate symbol-dbinfo)
|
||||
(:args (x :scs (descriptor-reg)))
|
||||
(:results (res :scs (descriptor-reg)))
|
||||
(:temporary (:sc unsigned-reg) temp)
|
||||
(:generator 1
|
||||
(load-symbol-info-vector res x temp)))
|
||||
|
||||
(define-vop (symbol-plist)
|
||||
(:policy :fast-safe)
|
||||
(:translate symbol-plist)
|
||||
(:args (x :scs (descriptor-reg)))
|
||||
(:results (res :scs (descriptor-reg)))
|
||||
(:generator 1
|
||||
(loadw res x symbol-info-slot other-pointer-lowtag)
|
||||
;; Instruction pun: (CAR x) is the same as (VECTOR-LENGTH x)
|
||||
;; so if the info slot holds a vector, this gets a fixnum- it's not a plist.
|
||||
(loadw res res cons-car-slot list-pointer-lowtag)
|
||||
(inst tst res fixnum-tag-mask)
|
||||
(inst csel res null-tn res :eq)))
|
||||
(load-symbol-dbinfo res x temp)))
|
||||
|
||||
;;;; other miscellaneous VOPs
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,66 @@
|
|||
#-sb-xc-host
|
||||
(declaim (ftype (sfunction (t) ctype) global-ftype))
|
||||
|
||||
;;; A bit about the physical representation of the packed info format:
|
||||
;;; With #+compact-instance-header it is possible to represent a vector of N things
|
||||
;;; in a structure using (ALIGN-UP (1+ N) 2) words of memory. This is a saving
|
||||
;;; of 1 word on average when compared to SIMPLE-VECTOR which needs
|
||||
;;; (ALIGN-UP (+ N 2) 2) words. Granted that either might have a padding word,
|
||||
;;; but I've observed 5% to 10% space reduction by eliminating one slot.
|
||||
;;; Without compact-instance-header, we'e indifferent, in terms of space,
|
||||
;;; as to whether this is an INSTANCE or a SIMPLE-VECTOR. For consistency,
|
||||
;;; we use an INSTANCE regardless of presence of the compact-header feature.
|
||||
;;; This makes assembly routines (e.g. CALL-SYMBOL) slightly less sensitive
|
||||
;;; to the feature's absence.
|
||||
|
||||
;;; Since variable-length instances aren't portably a thing,
|
||||
;;; we use a structure of one slot holding a vector.
|
||||
#+sb-xc-host (defstruct (packed-info
|
||||
(:constructor %make-packed-info (cells))
|
||||
(:copier nil))
|
||||
;; These objects are immutable.
|
||||
(cells #() :type simple-vector :read-only t))
|
||||
;;; Some abstractions for host/target compatibility of all the defuns.
|
||||
#+sb-xc-host
|
||||
(progn
|
||||
(defmacro make-packed-info (n) `(%make-packed-info (make-array ,n)))
|
||||
(defmacro copy-packed-info (info)
|
||||
`(%make-packed-info (copy-seq (packed-info-cells ,info))))
|
||||
(defmacro packed-info-len (info)
|
||||
`(length (packed-info-cells ,info)))
|
||||
(defmacro %info-ref (info index) `(svref (packed-info-cells ,info) ,index)))
|
||||
#-sb-xc-host
|
||||
(progn
|
||||
#+nil
|
||||
(defmethod print-object ((obj packed-info) stream)
|
||||
(format stream "[~{~W~^ ~}]"
|
||||
(loop for i below (%instance-length obj)
|
||||
collect (%instance-ref obj i))))
|
||||
(defmethod print-object ((self sb-int:packed-info) stream)
|
||||
(print-unreadable-object (self stream :type t :identity t)
|
||||
(format stream "len=~d" (sb-kernel:%instance-length self))))
|
||||
(eval-when (:compile-toplevel)
|
||||
(sb-xc:defmacro make-packed-info (n)
|
||||
;; this file is earlier than early-vm, so we have to take
|
||||
;; INSTANCE-DATA-START from the value set in make-host-1.
|
||||
`(let ((new (%make-instance (+ ,n #.sb-vm:instance-data-start))))
|
||||
(setf (%instance-wrapper new) #.(find-layout 'packed-info))
|
||||
new))
|
||||
;; We can't merely call COPY-STRUCTURE due to two issues:
|
||||
;; 1. bootstrapping- the DEFSTRUCT-DESCRIPTION is not available
|
||||
;; in cold-init by the first call to COPY-PACKED-INFO,
|
||||
;; but COPY-STRUCTURE needs it, because of raw slots.
|
||||
;; 2. the transform for COPY-STRUCTURE would think that
|
||||
;; it needs to copy exactly 1 slot. Well, it would think that,
|
||||
;; if the FREEZE-TYPE wasn't commented out.
|
||||
(sb-xc:defmacro copy-packed-info (info)
|
||||
;; not bothering with ONCE-ONLY here (doesn't matter)
|
||||
`(%copy-instance (%make-instance (%instance-length ,info)) ,info)))
|
||||
(defmacro packed-info-len (info)
|
||||
`(- (%instance-length ,info) sb-vm:instance-data-start))
|
||||
(defmacro %info-ref (v i)
|
||||
`(%instance-ref ,v (+ ,i #.sb-vm:instance-data-start))))
|
||||
|
||||
;;; At run time, we represent the type of a piece of INFO in the globaldb
|
||||
;;; by a small integer between 1 and 63. [0 is reserved for internal use.]
|
||||
;;; CLISP, and maybe others, need EVAL-WHEN because without it, the constant
|
||||
|
|
@ -85,25 +145,26 @@
|
|||
;;; presented at the definition of SYMBOL-PLIST, if the object in SYMBOL's
|
||||
;;; info slot is LISTP, it is in state 1 or 3. Either way, take the CDR.
|
||||
;;; Otherwise, it is in state 2 so return the value as-is.
|
||||
;;; In terms of this function being named "-vector", implying always a vector,
|
||||
;;; it is understood that NIL is a proxy for +NIL-PACKED-INFOS+, a vector.
|
||||
;;; NIL is an acceptable substitute for +NIL-PACKED-INFOS+,
|
||||
;;; but I might change that.
|
||||
;;;
|
||||
;;; Define SYMBOL-INFO-VECTOR as an inline function unless a vop translates it.
|
||||
;;; Define SYMBOL-INFO as an inline function unless a vop translates it.
|
||||
;;; (Inlining occurs first, which would cause the vop not to be used.)
|
||||
#-sb-xc-host
|
||||
(sb-c::unless-vop-existsp (:translate sb-kernel:symbol-info-vector)
|
||||
(declaim (inline symbol-info-vector))
|
||||
(defun symbol-info-vector (symbol)
|
||||
(let ((info-holder (symbol-info symbol)))
|
||||
(truly-the (or null simple-vector)
|
||||
(sb-c::unless-vop-existsp (:translate sb-kernel:symbol-dbinfo)
|
||||
(declaim (inline symbol-dbinfo))
|
||||
(defun symbol-dbinfo (symbol)
|
||||
(let ((info-holder (symbol-%info symbol)))
|
||||
(truly-the (or null instance)
|
||||
(if (listp info-holder) (cdr info-holder) info-holder)))))
|
||||
|
||||
;;; SYMBOL-INFO is a primitive object accessor defined in 'objdef.lisp'
|
||||
;;; But in the host Lisp, there is no such thing as a symbol-info slot.
|
||||
;;; Instead, symbol-info is kept in the host symbol's plist.
|
||||
;;; This must be a SETFable place.
|
||||
;;; %SYMBOL-INFO is a primitive object accessor defined in 'objdef.lisp'
|
||||
;;; But in the host Lisp, there is no such thing. Instead, SYMBOL-%INFO
|
||||
;;; is kept as a property on the host symbol.
|
||||
;;; The compatible "primitive" accessor must be a SETFable place.
|
||||
#+sb-xc-host
|
||||
(defmacro symbol-info-vector (symbol) `(get ,symbol :sb-xc-globaldb-info))
|
||||
(progn (defmacro symbol-%info (symbol) `(get ,symbol :sb-xc-globaldb-info))
|
||||
(defun symbol-dbinfo (symbol) (symbol-%info symbol)))
|
||||
|
||||
;; Perform the equivalent of (GET-INFO-VALUE KIND +INFO-METAINFO-TYPE-NUM+)
|
||||
;; but skipping the defaulting logic.
|
||||
|
|
@ -111,19 +172,21 @@
|
|||
;; - though not always - a unique identifier for the (:TYPE :KIND) pair.
|
||||
;; Note that bypassing of defaults is critical for bootstrapping,
|
||||
;; since INFO is used to retrieve its own META-INFO at system-build time.
|
||||
(defmacro !get-meta-infos (kind)
|
||||
`(let* ((info-vector (symbol-info-vector ,kind))
|
||||
(index (if info-vector
|
||||
(packed-info-value-index info-vector +no-auxiliary-key+
|
||||
(defmacro get-meta-infos (kind)
|
||||
`(let* ((packed-info (symbol-dbinfo ,kind))
|
||||
(index (if packed-info
|
||||
(packed-info-value-index packed-info +no-auxiliary-key+
|
||||
+info-metainfo-type-num+))))
|
||||
(if index (svref info-vector index))))
|
||||
(if index (%info-ref packed-info index))))
|
||||
|
||||
;; (UNSIGNED-BYTE 16) is an arbitrarily generous limit on the number of
|
||||
;; cells in an info-vector. Most vectors have a fewer than a handful of things,
|
||||
;; (UNSIGNED-BYTE 11) is an arbitrarily generous limit on the number of
|
||||
;; cells in a packed-info. Most packed-infos have fewer than a handful of things,
|
||||
;; and performance would need to be re-thought if more than about a dozen
|
||||
;; cells were in use. (It would want to become hash-based probably)
|
||||
(declaim (ftype (function (simple-vector (or (eql 0) symbol) info-number)
|
||||
(or null (unsigned-byte 16)))
|
||||
;; It has to be smaller than INSTANCE_LENGTH_MASK certainly,
|
||||
;; plus leaving room for a layout slot if #-compact-instance-header.
|
||||
(declaim (ftype (function (packed-info (or (eql 0) symbol) info-number)
|
||||
(or null (unsigned-byte 11)))
|
||||
packed-info-value-index))
|
||||
|
||||
;; Return the META-INFO object for CATEGORY and KIND, signaling an error
|
||||
|
|
@ -136,7 +199,7 @@
|
|||
;; through, whereas typically no more than 3 or 4 items have the same KIND.
|
||||
;;
|
||||
(defun meta-info (category kind &optional (errorp t))
|
||||
(or (let ((metadata (!get-meta-infos kind)))
|
||||
(or (let ((metadata (get-meta-infos kind)))
|
||||
(cond ((listp metadata) ; conveniently handles NIL
|
||||
(dolist (info metadata nil) ; FIND is slower :-(
|
||||
(when (eq (meta-info-category (truly-the meta-info info))
|
||||
|
|
@ -144,7 +207,7 @@
|
|||
(return info))))
|
||||
((eq (meta-info-category (truly-the meta-info metadata)) category)
|
||||
metadata)))
|
||||
;; !GET-META-INFOS enforces that KIND is a symbol, therefore
|
||||
;; GET-META-INFOS enforces that KIND is a symbol, therefore
|
||||
;; if a metaobject was found, CATEGORY was necessarily a symbol too.
|
||||
;; Otherwise, if the caller wants no error to be signaled on missing info,
|
||||
;; we must nevertheless enforce that CATEGORY was actually a symbol.
|
||||
|
|
|
|||
|
|
@ -1990,8 +1990,11 @@
|
|||
(defknown %scharset ((modifying simple-string) index character) character ())
|
||||
(defknown %set-symbol-value (symbol t) t ())
|
||||
(defknown (setf symbol-function) (function symbol) function ())
|
||||
(defknown %set-symbol-plist (symbol list) list ()
|
||||
:derive-type #'result-type-last-arg)
|
||||
;; Does this really need a type deriver? It's inline, and returns its 1st arg,
|
||||
;; i.e. we know exactly what object it returns, which is more precise than
|
||||
;; just knowing the type.
|
||||
(defknown (setf symbol-plist) (list symbol) list ()
|
||||
:derive-type #'result-type-first-arg)
|
||||
(defknown %setnth (unsigned-byte (modifying list) t) t ()
|
||||
:derive-type #'result-type-last-arg)
|
||||
(defknown %set-fill-pointer ((modifying complex-vector) index) index ()
|
||||
|
|
|
|||
|
|
@ -1929,7 +1929,7 @@ core and return a descriptor to it."
|
|||
(sort (%hash-table-alist *cold-package-symbols*)
|
||||
#'string< :key #'car)))) ; Sort by package-name
|
||||
|
||||
(dump-symbol-info-vectors
|
||||
(dump-symbol-infos
|
||||
(attach-fdefinitions-to-symbols
|
||||
(attach-classoid-cells-to-symbols (make-hash-table :test #'eq))))
|
||||
|
||||
|
|
@ -2101,7 +2101,7 @@ core and return a descriptor to it."
|
|||
;;
|
||||
(defun attach-fdefinitions-to-symbols (hashtable)
|
||||
;; Collect fdefinitions that go with one symbol, e.g. CAR and (SETF CAR),
|
||||
;; using the host's code for manipulating a packed info-vector.
|
||||
;; using the host's code for manipulating a packed-info.
|
||||
(maphash (lambda (warm-name cold-fdefn)
|
||||
(with-globaldb-name (key1 key2) warm-name
|
||||
:hairy (error "Hairy fdefn name in genesis: ~S" warm-name)
|
||||
|
|
@ -2113,24 +2113,33 @@ core and return a descriptor to it."
|
|||
*cold-fdefn-objects*)
|
||||
hashtable)
|
||||
|
||||
(defun dump-symbol-info-vectors (hashtable)
|
||||
;; Emit in the same order symbols reside in core to avoid
|
||||
;; sensitivity to the iteration order of host's maphash.
|
||||
(loop for (warm-sym . info)
|
||||
(defun dump-packed-info (list)
|
||||
;; Payload length is the element count + LAYOUT slot if necessary.
|
||||
;; Header word is added automatically by ALLOCATE-STRUCT
|
||||
(let ((s (allocate-struct (+ sb-vm:instance-data-start (length list))
|
||||
(cold-layout-descriptor (gethash 'packed-info *cold-layouts*)))))
|
||||
(loop for i from (+ sb-vm:instance-slots-offset sb-vm:instance-data-start)
|
||||
for elt in list do (write-wordindexed s i elt))
|
||||
s))
|
||||
(defun dump-symbol-infos (hashtable)
|
||||
(cold-set 'sb-impl::+nil-packed-infos+
|
||||
(dump-packed-info (list (make-fixnum-descriptor 0))))
|
||||
;; Emit in the same order symbols reside in core to avoid
|
||||
;; sensitivity to the iteration order of host's maphash.
|
||||
(loop for (warm-sym . info)
|
||||
in (sort (%hash-table-alist hashtable) #'<
|
||||
:key (lambda (x) (descriptor-bits (cold-intern (car x)))))
|
||||
do (write-wordindexed
|
||||
(cold-intern warm-sym) sb-vm:symbol-info-slot
|
||||
;; Each vector will have one fixnum, possibly the symbol SETF,
|
||||
(dump-packed-info
|
||||
;; Each packed-info will have one fixnum, possibly the symbol SETF,
|
||||
;; and one or two #<fdefn> objects in it, and/or a classoid-cell.
|
||||
(vector-in-core
|
||||
(map 'list (lambda (elt)
|
||||
(etypecase elt
|
||||
(symbol (cold-intern elt))
|
||||
(fixnum (make-fixnum-descriptor elt))
|
||||
(descriptor elt)))
|
||||
info)))))
|
||||
|
||||
(sb-impl::packed-info-cells info))))))
|
||||
|
||||
;;;; fixups and related stuff
|
||||
|
||||
|
|
@ -3410,7 +3419,7 @@ III. initially undefined function references (alphabetically):
|
|||
(dolist (classoid dumped-classoids)
|
||||
(let ((nwords (logand (ash (read-bits-wordindexed classoid 0)
|
||||
(- sb-vm:instance-length-shift))
|
||||
sb-vm::instance-length-mask)))
|
||||
sb-vm:instance-length-mask)))
|
||||
(format t "Classoid @ ~x, ~d words:~%" (descriptor-bits classoid) (1+ nwords))
|
||||
(dotimes (i (1+ nwords)) ; include the header word in output
|
||||
(format t "~2d: ~10x~%" i (read-bits-wordindexed classoid i)))
|
||||
|
|
|
|||
|
|
@ -374,11 +374,20 @@ during backtrace.
|
|||
:set-trans %set-symbol-global-value
|
||||
:set-known ())
|
||||
|
||||
(info :ref-trans symbol-info :ref-known (flushable)
|
||||
:set-trans (setf symbol-info)
|
||||
;; The private accessor for INFO reads the slot verbatim.
|
||||
;; In contrast, the SYMBOL-INFO function always returns a PACKED-INFO
|
||||
;; instance (see info-vector.lisp) or NIL. The slot itself may hold a cons
|
||||
;; of the user's PLIST and a PACKED-INFO or just a PACKED-INFO.
|
||||
;; It can't hold a PLIST alone without wrapping in an extra cons cell.
|
||||
(info :ref-trans symbol-%info :ref-known (flushable)
|
||||
:set-trans (setf symbol-%info)
|
||||
:set-known ()
|
||||
:cas-trans %compare-and-swap-symbol-info
|
||||
:type (or simple-vector list)
|
||||
;; IR2-CONVERT-CASSER only knows the arg order as (OBJECT OLD NEW),
|
||||
;; so as much as I'd like to name this (CAS SYMBOL-%INFO),
|
||||
;; it can't be that, because it'd need args of (OLD NEW OBJECT).
|
||||
;; This is a pretty close approximation of the desired name.
|
||||
:cas-trans sb-impl::cas-symbol-%info
|
||||
:type (or instance list)
|
||||
:init :null)
|
||||
(name :ref-trans symbol-name :init :arg)
|
||||
(package :ref-trans sb-xc:symbol-package
|
||||
|
|
|
|||
|
|
@ -123,7 +123,12 @@
|
|||
(defknown %set-symbol-hash (symbol hash-code)
|
||||
t ())
|
||||
|
||||
(defknown symbol-info-vector (symbol) (or null simple-vector))
|
||||
;;; TODOD: I'd like to eliminate the (OR NULL) from this return type.
|
||||
;;; For that to happen, I probably need +nil-packed-infos+ to become
|
||||
;;; placed in static space because assembly routines may need it.
|
||||
;;; On the other hand, they may not, because there is no special case
|
||||
;;; code needed when reading from it, which is entire point.
|
||||
(defknown symbol-dbinfo (symbol) (or null packed-info))
|
||||
|
||||
(defknown initialize-vector ((simple-array * (*)) &rest t)
|
||||
(simple-array * (*))
|
||||
|
|
|
|||
|
|
@ -51,9 +51,9 @@
|
|||
;;; sources partway through bootstrapping, tch tch, overwriting its
|
||||
;;; version with our version would be unlikely to help, because that
|
||||
;;; would make the cross-compiler very confused.)
|
||||
(defun !register-meta-info (metainfo)
|
||||
(defun register-meta-info (metainfo)
|
||||
(let* ((name (meta-info-kind metainfo))
|
||||
(list (!get-meta-infos name)))
|
||||
(list (get-meta-infos name)))
|
||||
(set-info-value name +info-metainfo-type-num+
|
||||
(cond ((not list) metainfo) ; unique, just store it
|
||||
((listp list) (cons metainfo list)) ; prepend to the list
|
||||
|
|
@ -67,7 +67,7 @@
|
|||
(return-from !%define-info-type it)) ; do nothing
|
||||
(let ((id (or id (position nil *info-types* :start 1)
|
||||
(error "no more INFO type numbers available"))))
|
||||
(!register-meta-info
|
||||
(register-meta-info
|
||||
(setf (aref *info-types* id)
|
||||
(!make-meta-info id category kind type-spec type-checker
|
||||
validate-function default)))))
|
||||
|
|
@ -107,7 +107,7 @@
|
|||
'#'identity
|
||||
`(named-lambda "check-type" (x) (the ,type-spec x)))
|
||||
,validate-function ,default
|
||||
;; Rationale for hardcoding here is explained at INFO-VECTOR-FDEFN.
|
||||
;; Rationale for hardcoding here is explained at PACKED-INFO-FDEFN.
|
||||
,(or (and (eq category :function) (eq kind :definition)
|
||||
+fdefn-info-num+)
|
||||
#+sb-xc (meta-info-number (meta-info category kind))))))
|
||||
|
|
@ -204,18 +204,18 @@
|
|||
(hookp (and (and hook
|
||||
(not (eql 0 (car hook)))
|
||||
(logbitp info-number (car hook))))))
|
||||
(multiple-value-bind (vector aux-key)
|
||||
(multiple-value-bind (packed-info aux-key)
|
||||
(let ((name (uncross name)))
|
||||
(with-globaldb-name (key1 key2) name
|
||||
;; In the :simple branch, KEY1 is no doubt a symbol,
|
||||
;; but constraint propagation isn't informing the compiler here.
|
||||
:simple (values (symbol-info-vector (truly-the symbol key1)) key2)
|
||||
:simple (values (symbol-dbinfo (truly-the symbol key1)) key2)
|
||||
:hairy (values (info-gethash name *info-environment*)
|
||||
+no-auxiliary-key+)))
|
||||
(when vector
|
||||
(let ((index (packed-info-value-index vector aux-key info-number)))
|
||||
(when packed-info
|
||||
(let ((index (packed-info-value-index packed-info aux-key info-number)))
|
||||
(when index
|
||||
(let ((answer (svref vector index)))
|
||||
(let ((answer (%info-ref packed-info index)))
|
||||
(when hookp
|
||||
(funcall (truly-the function (cdr hook))
|
||||
name info-number answer t))
|
||||
|
|
@ -583,7 +583,7 @@
|
|||
|
||||
;; This is for the SB-INTROSPECT contrib module, and debugging.
|
||||
(defun call-with-each-info (function symbol)
|
||||
(awhen (symbol-info-vector symbol)
|
||||
(awhen (symbol-dbinfo symbol)
|
||||
(%call-with-each-info function it symbol)))
|
||||
|
||||
;; This is for debugging at the REPL.
|
||||
|
|
|
|||
|
|
@ -44,31 +44,31 @@
|
|||
;;; * Names which are lists of length other than 2, or improper lists,
|
||||
;;; or whose elements are not both symbols, are disqualified.
|
||||
|
||||
;;; Packed vector layout
|
||||
;;; --------------------
|
||||
;;; Packed format
|
||||
;;; -------------
|
||||
;;; Because the keys to the inner lists are integers in the range 0 to 63,
|
||||
;;; either 5 or 10 keys will fit into a fixnum depending on word size.
|
||||
;;; This permits one memory read to retrieve a collection of keys. In packed
|
||||
;;; format, an ordered set of keys ("fields") is called a "descriptor".
|
||||
;;;
|
||||
;;; Descriptors are stored from element 0 upward in the packed vector,
|
||||
;;; and data are indexed downward from the last element of the vector.
|
||||
;;; Descriptors are stored from element 0 upward in the packed-info,
|
||||
;;; and data are indexed downward from the last element.
|
||||
;;;
|
||||
;;; #(descriptor0 descriptor1 ... descriptorN valueN ... value1 value0)
|
||||
;;; [descriptor0 descriptor1 ... descriptorN valueN ... value1 value0]
|
||||
;;;
|
||||
;;; e.g. The field at absolute index 3 - vector element 0, bit position 18 -
|
||||
;;; e.g. The field at absolute index 3 (physical element 0, bit position 18)
|
||||
;;; will find its data at index (- END 3). In this manner, it doesn't matter
|
||||
;;; how many more descriptors exist.
|
||||
|
||||
;;; A "group" comprises all the info for a particular Name, and its list
|
||||
;;; of types may may span descriptors, though rarely.
|
||||
;;; An "auxiliary key" is the first element of a 2-list Name. It is interposed
|
||||
;;; within the data portion of the vector after the preceding info group.
|
||||
;;; within the data portion of the packed-info after the preceding info group.
|
||||
;;; Descriptors are self-delimiting in that the first field in a group
|
||||
;;; indicates the number of additional fields in the group.
|
||||
|
||||
;;; Unpacked vector layout
|
||||
;;; ----------------------
|
||||
;;; Unpacked format
|
||||
;;; ---------------
|
||||
;;; This representation is used transiently during insertion/deletion.
|
||||
;;; It is a concatenation of plists as a vector, interposing at the splice
|
||||
;;; points the auxiliary key for the group, except for the root name which
|
||||
|
|
@ -85,10 +85,14 @@
|
|||
;;; One can envision that the first info group stores its auxiliary key
|
||||
;;; at vector index -1 when thinking about the correctness of algorithms
|
||||
;;; that process unpacked info-vectors.
|
||||
;;; See !TEST-PACKIFY-INFOS for examples of each format.
|
||||
;;; See TEST-PACKIFY-INFOS for examples of each format.
|
||||
|
||||
;;;;; Some stuff moved from 'globaldb.lisp':
|
||||
|
||||
;;; The structure constructor is never called
|
||||
(sb-xc:defstruct (packed-info (:predicate nil) (:constructor nil) (:copier nil))
|
||||
cells)
|
||||
;;(declaim (freeze-type packed-info)) ; crashes?
|
||||
(defconstant info-num-mask (ldb (byte info-number-bits 0) -1)) ; #b111111
|
||||
|
||||
;; Using 6 bits per packed field, 5 infos can be described in a 30-bit fixnum,
|
||||
|
|
@ -101,20 +105,25 @@
|
|||
(deftype info-descriptor () `(signed-byte ,sb-vm:n-fixnum-bits))
|
||||
|
||||
;; An empty info-vector. Its 0th field describes that there are no more fields.
|
||||
(defconstant-eqx +nil-packed-infos+ #(0) #'equalp)
|
||||
(defglobal +nil-packed-infos+
|
||||
(let ((v (make-packed-info 1)))
|
||||
(setf (%info-ref v 0) 0)
|
||||
v))
|
||||
|
||||
;; FDEFINITIONs have an info-number that admits slightly clever logic
|
||||
;; for INFO-VECTOR-FDEFN. Do not change this constant without
|
||||
;; for PACKED-INFO-FDEFN. Do not change this constant without
|
||||
;; careful examination of that function.
|
||||
(defconstant +fdefn-info-num+ info-num-mask)
|
||||
|
||||
;; Extract a field from a packed info descriptor.
|
||||
;; A field is either a count of info-numbers, or an info-number.
|
||||
(declaim (inline packed-info-field))
|
||||
(defun packed-info-field (vector desc-index field-index)
|
||||
(defun packed-info-field (packed-info desc-index field-index)
|
||||
;; (declare (optimize (safety 0))) ; comment out when debugging
|
||||
(ldb (byte info-number-bits
|
||||
(* (the (mod #.+infos-per-word+) field-index) info-number-bits))
|
||||
(the info-descriptor (svref vector desc-index))))
|
||||
(the info-descriptor
|
||||
(%info-ref (the packed-info packed-info) desc-index))))
|
||||
|
||||
;; Compute the number of elements needed to hold unpacked VECTOR after packing.
|
||||
;; This is not "compute-packed-info-size" since that could be misconstrued
|
||||
|
|
@ -159,10 +168,10 @@
|
|||
;;
|
||||
(defun packify-infos (input &optional (end (length input)))
|
||||
(declare (simple-vector input))
|
||||
(let* ((output (make-array (compute-packified-info-size input end)))
|
||||
(let* ((output (make-packed-info (compute-packified-info-size input end)))
|
||||
(i -1) ; input index: pre-increment to read the next datum
|
||||
(j -1) ; descriptor index: pre-increment to write
|
||||
(k (length output)) ; data index: pre-decrement to write
|
||||
(k (packed-info-len output)) ; data index: pre-decrement to write
|
||||
(field-shift 0)
|
||||
(word 0))
|
||||
(declare (type index-or-minus-1 i j k end)
|
||||
|
|
@ -174,19 +183,19 @@
|
|||
(setq word (logior (make-info-descriptor val field-shift) word))
|
||||
(if (< field-shift (* (1- +infos-per-word+) info-number-bits))
|
||||
(incf field-shift info-number-bits)
|
||||
(setf (svref output (incf j)) word field-shift 0 word 0))))
|
||||
(setf (%info-ref output (incf j)) word field-shift 0 word 0))))
|
||||
;; Truncating divide by 2: count = n-elements in the group @ 2 per entry,
|
||||
;; +1 for count itself but not including its aux-key.
|
||||
(loop (let ((count (ash (the index (svref input (incf i))) -1)))
|
||||
(put-field count) ; how many infos to follow
|
||||
(dotimes (iter count)
|
||||
(put-field (svref input (incf i))) ; an info-number
|
||||
(setf (svref output (decf k)) (svref input (incf i)))) ; value
|
||||
(setf (%info-ref output (decf k)) (svref input (incf i)))) ; value
|
||||
(when (>= (incf i) end)
|
||||
(return))
|
||||
(setf (svref output (decf k)) (svref input i))))) ; an aux-key
|
||||
(setf (%info-ref output (decf k)) (svref input i))))) ; an aux-key
|
||||
(unless (zerop field-shift) ; store the final descriptor word
|
||||
(setf (svref output (incf j)) word))
|
||||
(setf (%info-ref output (incf j)) word))
|
||||
(aver (eql (1+ j) k)) ; last descriptor must be adjacent final data cell
|
||||
output))
|
||||
|
||||
|
|
@ -194,11 +203,11 @@
|
|||
;; returns the next field from a descriptor in INPUT-VAR, a packed vector.
|
||||
;; The generator uses DESCRIPTOR-INDEX and updates it as a side-effect.
|
||||
;;
|
||||
(defmacro !with-packed-info-iterator ((generator input-var
|
||||
(defmacro with-packed-info-iterator ((generator input-var
|
||||
&key descriptor-index)
|
||||
&body body)
|
||||
(with-unique-names (input word count)
|
||||
`(let* ((,input (the simple-vector ,input-var))
|
||||
`(let* ((,input (the packed-info ,input-var))
|
||||
(,descriptor-index -1)
|
||||
(,count 0)
|
||||
(,word 0))
|
||||
|
|
@ -208,21 +217,21 @@
|
|||
(flet ((,generator ()
|
||||
(when (zerop ,count)
|
||||
(incf ,descriptor-index)
|
||||
(setq ,word (svref ,input ,descriptor-index)
|
||||
(setq ,word (%info-ref ,input ,descriptor-index)
|
||||
,count +infos-per-word+))
|
||||
(prog1 (logand ,word info-num-mask)
|
||||
(setq ,word (ash ,word (- info-number-bits)))
|
||||
(decf ,count))))
|
||||
,@body))))
|
||||
|
||||
;; Iterate over VECTOR, binding DATA-INDEX to the index of each aux-key in turn.
|
||||
;; Iterate over PACKED-INFO, binding DATA-INDEX to the index of each aux-key in turn.
|
||||
;; TOTAL-N-FIELDS is deliberately exposed to invoking code.
|
||||
;;
|
||||
(defmacro !do-packed-info-vector-aux-key ((vector &optional (data-index (gensym)))
|
||||
(defmacro do-packed-info-aux-key ((packed-info &optional (data-index (gensym)))
|
||||
step-form &optional result-form)
|
||||
(with-unique-names (descriptor-idx field-idx)
|
||||
(once-only ((vector vector))
|
||||
`(let ((,data-index (length ,vector))
|
||||
(with-unique-names (descriptor-idx field-idx info)
|
||||
`(let* ((,info ,packed-info)
|
||||
(,data-index (packed-info-len ,info))
|
||||
(,descriptor-idx 0)
|
||||
(,field-idx 0)
|
||||
(total-n-fields 0))
|
||||
|
|
@ -231,8 +240,7 @@
|
|||
;; Loop through the descriptors in random-access fashion.
|
||||
;; Skip 1+ n-infos each time, because the 'n-infos' is itself a field
|
||||
;; that is not accounted for in its own value.
|
||||
(loop (let ((n (1+ (packed-info-field ,vector
|
||||
,descriptor-idx ,field-idx))))
|
||||
(loop (let ((n (1+ (packed-info-field ,info ,descriptor-idx ,field-idx))))
|
||||
(incf total-n-fields n)
|
||||
(multiple-value-setq (,descriptor-idx ,field-idx)
|
||||
(floor total-n-fields +infos-per-word+))
|
||||
|
|
@ -240,36 +248,21 @@
|
|||
;; Done when the ascending index and descending index meet
|
||||
(unless (< ,descriptor-idx ,data-index)
|
||||
(return ,result-form))
|
||||
,@(if step-form (list step-form)))))))
|
||||
,@(if step-form (list step-form))))))
|
||||
|
||||
;; Return all function names that are stored in SYMBOL's info-vector.
|
||||
;; As an example, (INFO-VECTOR-NAME-LIST 'SB-PCL::DIRECT-SUPERCLASSES) =>
|
||||
;; ((SB-PCL::SLOT-ACCESSOR :GLOBAL SB-PCL::DIRECT-SUPERCLASSES SB-PCL::READER)
|
||||
;; (SB-PCL::SLOT-ACCESSOR :GLOBAL SB-PCL::DIRECT-SUPERCLASSES BOUNDP)
|
||||
;; (SB-PCL::SLOT-ACCESSOR :GLOBAL SB-PCL::DIRECT-SUPERCLASSES SB-PCL::WRITER))
|
||||
(defun info-vector-name-list (symbol)
|
||||
(let ((vector (symbol-info-vector symbol))
|
||||
(list))
|
||||
(when vector
|
||||
(!do-packed-info-vector-aux-key (vector key-index)
|
||||
(push (construct-globaldb-name (svref vector key-index) symbol)
|
||||
list))
|
||||
(nconc (and (plusp (packed-info-field vector 0 0)) (list symbol))
|
||||
(nreverse list)))))
|
||||
|
||||
;; Compute the number of elements needed to hold packed VECTOR after unpacking.
|
||||
;; Compute the number of elements needed to hold PACKED-INFO after unpacking.
|
||||
;; The unpacked size is the number of auxiliary keys plus the number of entries
|
||||
;; @ 2 cells per entry, plus the number of length cells which indicate the
|
||||
;; number of data cells used (including length cells but not aux key cells).
|
||||
;; Equivalently, it's the number of packed fields times 2 minus 1.
|
||||
;;
|
||||
(defun compute-unpackified-info-size (vector)
|
||||
(declare (simple-vector vector))
|
||||
(!do-packed-info-vector-aux-key (vector) ()
|
||||
(defun compute-unpackified-info-size (packed-info)
|
||||
(declare (packed-info packed-info))
|
||||
(do-packed-info-aux-key (packed-info) ()
|
||||
;; off-by-one: the first info group's auxiliary key is imaginary
|
||||
(1- (truly-the fixnum (ash total-n-fields 1)))))
|
||||
|
||||
;; Convert packed INPUT vector to unpacked.
|
||||
;; Convert packed INPUT to unpacked.
|
||||
;; If optional OUTPUT is supplied, it is used, otherwise output is allocated.
|
||||
;; For efficiency the OUTPUT should be provided as a dynamic-extent array.
|
||||
;;
|
||||
|
|
@ -277,19 +270,19 @@
|
|||
(make-array
|
||||
(compute-unpackified-info-size input))
|
||||
output-supplied-p))
|
||||
(declare (simple-vector input output))
|
||||
(let ((i (length input)) (j -1)) ; input index and output index respectively
|
||||
(declare (type packed-info input) (simple-vector output))
|
||||
(let ((i (packed-info-len input)) (j -1)) ; input index and output index respectively
|
||||
(declare (type index-or-minus-1 i j))
|
||||
(!with-packed-info-iterator (next-field input :descriptor-index desc-idx)
|
||||
(with-packed-info-iterator (next-field input :descriptor-index desc-idx)
|
||||
(loop ; over name
|
||||
(let ((n-infos (next-field)))
|
||||
;; store the info group length, including the length cell in the length
|
||||
(setf (svref output (incf j)) (1+ (ash n-infos 1)))
|
||||
(dotimes (iter n-infos) ; over info-types
|
||||
(setf (svref output (incf j)) (next-field) ; type-num
|
||||
(svref output (incf j)) (svref input (decf i))))) ; value
|
||||
(svref output (incf j)) (%info-ref input (decf i))))) ; value
|
||||
(if (< desc-idx (decf i)) ; as long as the indices haven't met
|
||||
(setf (svref output (incf j)) (svref input i)) ; copy next aux-key
|
||||
(setf (svref output (incf j)) (%info-ref input i)) ; copy next aux-key
|
||||
(return (if output-supplied-p nil output))))))) ; else done
|
||||
|
||||
;; Return the index of the 'length' item for an info group having
|
||||
|
|
@ -311,20 +304,20 @@
|
|||
((eq (svref vector (1- index)) key)
|
||||
(return index)))))))
|
||||
|
||||
;; In packed info VECTOR try to find the auxiliary key SYMBOL.
|
||||
;; Try to find the auxiliary key SYMBOL in PACKED-INFO.
|
||||
;; If found, return indices of its data, info descriptor word, and field.
|
||||
;; If not found, the first value is NIL and the descriptor indices
|
||||
;; arbitrarily point to the next available descriptor field.
|
||||
;;
|
||||
(defun info-find-aux-key/packed (vector symbol)
|
||||
(defun info-find-aux-key/packed (packed-info symbol)
|
||||
;; explicit bounds checking is done by the code below
|
||||
(declare (optimize (safety 0)))
|
||||
(aver (simple-vector-p vector))
|
||||
(let ((descriptor-idx 0) ; physical index to vector
|
||||
; (declare (optimize (safety 0)))
|
||||
(aver (typep packed-info 'packed-info))
|
||||
(let ((descriptor-idx 0) ; physical index to packed-info
|
||||
(field-idx 0) ; relative index within current descriptor
|
||||
;; On each iteration DATA-IDX points to an aux-key cell
|
||||
;; The first group's imaginary aux-key cell is past the end.
|
||||
(data-idx (length (the simple-vector vector))))
|
||||
(data-idx (packed-info-len (the packed-info packed-info))))
|
||||
(declare (type index descriptor-idx data-idx)
|
||||
(fixnum field-idx)) ; can briefly exceed +infos-per-word+
|
||||
;; Efficiently skip past N-INFOS infos. If decrementing the data index
|
||||
|
|
@ -343,28 +336,28 @@
|
|||
(declare (inline skip))
|
||||
;; While this could compare aux-keys with #'EQUAL, it is not obvious how
|
||||
;; in general one would pick a symbol from the name as that which
|
||||
;; is delegated as the one to hold the info-vector.
|
||||
(values (cond ((not (skip (packed-info-field vector 0 0))) nil)
|
||||
;; is delegated as the one to hold the packed-info
|
||||
(values (cond ((not (skip (packed-info-field packed-info 0 0))) nil)
|
||||
;; At least one aux key is present.
|
||||
((eq (aref vector data-idx) symbol) data-idx) ; yay
|
||||
((eq (%info-ref packed-info data-idx) symbol) data-idx) ; yay
|
||||
;; aux-key order invariant allows early fail on SETF
|
||||
((eq symbol 'setf) nil)
|
||||
(t
|
||||
(loop
|
||||
(cond ((not (skip (packed-info-field vector descriptor-idx
|
||||
(cond ((not (skip (packed-info-field packed-info descriptor-idx
|
||||
field-idx)))
|
||||
(return nil))
|
||||
((eq (aref vector data-idx) symbol)
|
||||
((eq (%info-ref packed-info data-idx) symbol)
|
||||
(return data-idx))))))
|
||||
descriptor-idx field-idx)))) ; can be ignored if 1st val is nil
|
||||
|
||||
;; Take a packed info-vector INPUT and insert (AUX-KEY,INFO-NUMBER,VALUE).
|
||||
;; Take a packed-info INPUT and insert (AUX-KEY,INFO-NUMBER,VALUE).
|
||||
;; Packed info-vectors are immutable. Any alteration must create a copy.
|
||||
;; This is done by unpacking/repacking - it's easy enough and fairly
|
||||
;; efficient since the temporary vector is stack-allocated.
|
||||
;;
|
||||
(defun %packed-info-insert (input aux-key info-number value)
|
||||
(declare (simple-vector input) (type info-number info-number))
|
||||
(declare (type packed-info input) (type info-number info-number))
|
||||
(let* ((n-extra-elts
|
||||
;; Test if the aux-key has been seen before or needs to be added.
|
||||
(if (and (not (eql aux-key +no-auxiliary-key+))
|
||||
|
|
@ -410,15 +403,15 @@
|
|||
;; exactly one descriptor for the root name, space for >= 1 more field,
|
||||
;; and no aux-keys.
|
||||
(declaim (inline info-quickly-insertable-p))
|
||||
(defun info-quickly-insertable-p (input)
|
||||
(let ((n-infos (packed-info-field input 0 0)))
|
||||
(defun info-quickly-insertable-p (packed-info)
|
||||
(let ((n-infos (packed-info-field packed-info 0 0)))
|
||||
;; We can easily determine if the no-aux-keys constraint is satisfied,
|
||||
;; because a secondary name's info occupies at least two cells,
|
||||
;; one for its aux-key and >= 1 for info values.
|
||||
(and (< n-infos (1- +infos-per-word+))
|
||||
(eql n-infos (1- (length input))))))
|
||||
(eql n-infos (1- (packed-info-len packed-info))))))
|
||||
|
||||
;; Take a packed info-vector INPUT and return a new one with INFO-NUMBER/VALUE
|
||||
;; Take a packed-info INPUT and return a new one with INFO-NUMBER/VALUE
|
||||
;; added for the root name. The vector must satisfy INFO-QUICKLY-INSERTABLE-P.
|
||||
;; This code is separate from PACKED-INFO-INSERT to facilitate writing
|
||||
;; a unit test of this logic against the complete logic.
|
||||
|
|
@ -426,57 +419,57 @@
|
|||
(defun quick-packed-info-insert (input info-number value)
|
||||
;; Because INPUT contains 1 descriptor and its corresponding values,
|
||||
;; the current length is exactly NEW-N, the new number of fields.
|
||||
(let* ((descriptor (svref input 0))
|
||||
(new-n (truly-the info-number (length input)))
|
||||
(new-vect (make-array (1+ new-n))))
|
||||
(let* ((descriptor (%info-ref input 0))
|
||||
(new-n (truly-the info-number (packed-info-len input)))
|
||||
(output (make-packed-info (1+ new-n))))
|
||||
;; Two cases: we're either inserting info for the fdefn, or not.
|
||||
(cond ((eq info-number +fdefn-info-num+)
|
||||
;; fdefn, if present, must remain the first packed field.
|
||||
;; Replace the lowest field (the count) with +fdefn-info-num+,
|
||||
;; shift everything left 6 bits, then OR in the new count.
|
||||
(setf (svref new-vect 0)
|
||||
(setf (%info-ref output 0)
|
||||
(logior (make-info-descriptor
|
||||
(dpb +fdefn-info-num+ (byte info-number-bits 0)
|
||||
descriptor) info-number-bits) new-n)
|
||||
;; Packed vectors are indexed "backwards". The first
|
||||
;; field's info is in the highest numbered cell.
|
||||
(svref new-vect new-n) value)
|
||||
(%info-ref output new-n) value)
|
||||
(loop for i from 1 below new-n
|
||||
do (setf (svref new-vect i) (svref input i))))
|
||||
do (setf (%info-ref output i) (%info-ref input i))))
|
||||
(t
|
||||
;; Add a field on the high end and increment the count.
|
||||
(setf (svref new-vect 0)
|
||||
(setf (%info-ref output 0)
|
||||
(logior (make-info-descriptor
|
||||
info-number (* info-number-bits new-n))
|
||||
(1+ descriptor))
|
||||
(svref new-vect 1) value)
|
||||
(%info-ref output 1) value)
|
||||
;; Slide the old data up 1 cell.
|
||||
(loop for i from 2 to new-n
|
||||
do (setf (svref new-vect i) (svref input (1- i))))))
|
||||
new-vect))
|
||||
do (setf (%info-ref output i) (%info-ref input (1- i))))))
|
||||
output))
|
||||
|
||||
(declaim (maybe-inline packed-info-insert))
|
||||
(defun packed-info-insert (vector aux-key info-number newval)
|
||||
(defun packed-info-insert (packed-info aux-key info-number newval)
|
||||
(if (and (eql aux-key +no-auxiliary-key+)
|
||||
(info-quickly-insertable-p vector))
|
||||
(quick-packed-info-insert vector info-number newval)
|
||||
(%packed-info-insert vector aux-key info-number newval)))
|
||||
(info-quickly-insertable-p packed-info))
|
||||
(quick-packed-info-insert packed-info info-number newval)
|
||||
(%packed-info-insert packed-info aux-key info-number newval)))
|
||||
|
||||
;; Search packed VECTOR for AUX-KEY and INFO-NUMBER, returning
|
||||
;; the index of the data if found, or NIL if not found.
|
||||
;;
|
||||
(defun packed-info-value-index (vector aux-key type-num)
|
||||
(declare (optimize (safety 0))) ; vector bounds are AVERed
|
||||
(let ((data-idx (length vector)) (descriptor-idx 0) (field-idx 0))
|
||||
(defun packed-info-value-index (packed-info aux-key type-num)
|
||||
;;(declare (optimize (safetya 0))) ; bounds are AVERed
|
||||
(let ((data-idx (packed-info-len packed-info)) (descriptor-idx 0) (field-idx 0))
|
||||
(declare (type index descriptor-idx)
|
||||
(type (mod #.+infos-per-word+) field-idx))
|
||||
(unless (eql aux-key +no-auxiliary-key+)
|
||||
(multiple-value-setq (data-idx descriptor-idx field-idx)
|
||||
(info-find-aux-key/packed vector aux-key))
|
||||
(info-find-aux-key/packed packed-info aux-key))
|
||||
(unless data-idx
|
||||
(return-from packed-info-value-index nil)))
|
||||
;; Fetch a descriptor and shift out trailing bits that won't be scanned.
|
||||
(let* ((descriptor (ash (the info-descriptor (aref vector descriptor-idx))
|
||||
(let* ((descriptor (ash (the info-descriptor (%info-ref packed-info descriptor-idx))
|
||||
(* (- info-number-bits) field-idx)))
|
||||
(n-infos (logand descriptor info-num-mask))
|
||||
;; Compute n things in this descriptor after extracting one field. e.g.
|
||||
|
|
@ -500,11 +493,11 @@
|
|||
(incf descriptor-idx)
|
||||
(decf data-idx swath)
|
||||
(aver (< descriptor-idx data-idx))
|
||||
(setq descriptor (svref vector descriptor-idx)
|
||||
(setq descriptor (%info-ref packed-info descriptor-idx)
|
||||
swath (min n-infos +infos-per-word+))))))
|
||||
|
||||
;; Helper for CLEAR-INFO-VALUES when Name has the efficient form.
|
||||
;; Given packed info-vector INPUT and auxiliary key KEY2
|
||||
;; Given packed-info INPUT and auxiliary key KEY2
|
||||
;; return a new vector in which TYPE-NUMS are absent.
|
||||
;; When none of TYPE-NUMs were present to begin with, return NIL.
|
||||
;;
|
||||
|
|
@ -512,8 +505,8 @@
|
|||
;; clearing does not happen often enough to warrant the pre-check.
|
||||
;;
|
||||
(defun packed-info-remove (input key2 type-nums)
|
||||
(declare (simple-vector input))
|
||||
(when (or (eql (length input) (length +nil-packed-infos+))
|
||||
(declare (type packed-info input))
|
||||
(when (or (eql (packed-info-len input) #.(packed-info-len +nil-packed-infos+))
|
||||
(and (not (eql key2 +no-auxiliary-key+))
|
||||
(not (info-find-aux-key/packed input key2))))
|
||||
(return-from packed-info-remove nil)) ; do nothing
|
||||
|
|
@ -602,27 +595,27 @@
|
|||
|
||||
;; Call FUNCTION with each piece of info in packed VECT using ROOT-SYMBOL
|
||||
;; as the primary name. FUNCTION must accept 3 values (NAME INFO-NUMBER VALUE).
|
||||
(defun %call-with-each-info (function vect root-symbol)
|
||||
(defun %call-with-each-info (function packed-info root-symbol)
|
||||
(let ((name root-symbol)
|
||||
(data-idx (length vect)))
|
||||
(data-idx (packed-info-len packed-info)))
|
||||
(declare (type index data-idx))
|
||||
(!with-packed-info-iterator (next-field vect :descriptor-index desc-idx)
|
||||
(with-packed-info-iterator (next-field packed-info :descriptor-index desc-idx)
|
||||
(loop ; over name
|
||||
(dotimes (i (next-field)) ; number of infos for this name
|
||||
(funcall function name (next-field) (svref vect (decf data-idx))))
|
||||
(funcall function name (next-field) (%info-ref packed-info (decf data-idx))))
|
||||
(if (< desc-idx (decf data-idx))
|
||||
(setq name
|
||||
(construct-globaldb-name (svref vect data-idx) root-symbol))
|
||||
(construct-globaldb-name (%info-ref packed-info data-idx) root-symbol))
|
||||
(return))))))
|
||||
|
||||
#|
|
||||
Info packing example. This example has 2 auxiliary-keys: SETF and CAS.
|
||||
|
||||
(!test-packify-infos '(13 :XYZ 18 "nine" 28 :BAR 7 T)
|
||||
'(SETF 8 NIL 17 :FGX)
|
||||
'(CAS 6 :MUMBLE 2 :BAZ 47 :FOO))
|
||||
(test-packify-infos '(13 :XYZ 18 "nine" 28 :BAR 7 T)
|
||||
'(SETF 8 NIL 17 :FGX)
|
||||
'(CAS 6 :MUMBLE 2 :BAZ 47 :FOO))
|
||||
=>
|
||||
#(109006134805865284 3010 :FOO :BAZ :MUMBLE CAS :FGX NIL SETF T :BAR "nine" :XYZ)
|
||||
[109006134805865284 3010 :FOO :BAZ :MUMBLE CAS :FGX NIL SETF T :BAR "nine" :XYZ]
|
||||
|
||||
(format nil "~4,'0o ~20,'0o" 3010 109006134805865284)
|
||||
=> "5702 06032110020734221504"
|
||||
|
|
@ -633,7 +626,7 @@ Which is interpreted as:
|
|||
2 infos for SETF auxiliary-key. type numbers: 8, 17
|
||||
3 infos for CAS auxiliary-key. type numbers: 6, 2, 47
|
||||
|
||||
(unpackify-infos (!test-packify-infos ...)) ; same input
|
||||
(unpackify-infos (test-packify-infos ...)) ; same input
|
||||
=> #(9 13 :XYZ 18 "nine" 28 :BAR 7 T
|
||||
SETF 5 8 NIL 17 :FGX
|
||||
CAS 7 6 :MUMBLE 2 :BAZ 47 :FOO)
|
||||
|
|
@ -647,8 +640,7 @@ This is interpreted as
|
|||
;; The info for a symbol's fdefn must precede other info-numbers.
|
||||
;; and SETF must be the first aux key if other aux keys are present.
|
||||
;; The test function does not enforce these invariants.
|
||||
#+nil ; for debugging only
|
||||
(defun !test-packify-infos (&rest lists)
|
||||
(defun test-packify-infos (&rest lists)
|
||||
(flet ((check (plist)
|
||||
(and (evenp (length plist))
|
||||
(loop for (indicator value) on plist by #'cddr
|
||||
|
|
@ -705,10 +697,9 @@ This is interpreted as
|
|||
(defun update-symbol-info (symbol update-fn)
|
||||
;; Never pass NIL to an update-fn. Pass the minimal info-vector instead,
|
||||
;; a vector describing 0 infos and 0 auxiliary keys.
|
||||
(let ((newval (funcall update-fn (or (symbol-info-vector symbol)
|
||||
+nil-packed-infos+))))
|
||||
(let ((newval (funcall update-fn (or (symbol-%info symbol) +nil-packed-infos+))))
|
||||
(when newval
|
||||
(setf (symbol-info-vector symbol) newval))
|
||||
(setf (symbol-%info symbol) newval))
|
||||
(values)))
|
||||
|
||||
;;; The current *INFO-ENVIRONMENT*, a structure of type INFO-HASHTABLE.
|
||||
|
|
@ -779,14 +770,14 @@ This is interpreted as
|
|||
(let ((name (uncross name)))
|
||||
;; If the INFO-NUMBER already exists in VECT, then copy it and
|
||||
;; alter one cell; otherwise unpack it, grow the vector, and repack.
|
||||
(dx-flet ((augment (vect aux-key) ; VECT is a packed vector, never NIL
|
||||
(declare (simple-vector vect))
|
||||
(dx-flet ((augment (packed-info aux-key) ; PACKED-INFO must not be NIL
|
||||
(declare (type packed-info packed-info))
|
||||
(let ((index
|
||||
(packed-info-value-index vect aux-key info-number)))
|
||||
(packed-info-value-index packed-info aux-key info-number)))
|
||||
(if (not index)
|
||||
(packed-info-insert vect aux-key info-number new-value)
|
||||
(let ((copy (copy-seq vect)))
|
||||
(setf (svref copy index) new-value)
|
||||
(packed-info-insert packed-info aux-key info-number new-value)
|
||||
(let ((copy (copy-packed-info packed-info)))
|
||||
(setf (%info-ref copy index) new-value)
|
||||
copy)))))
|
||||
(with-globaldb-name (key1 key2) name
|
||||
:simple
|
||||
|
|
@ -796,8 +787,7 @@ This is interpreted as
|
|||
:hairy
|
||||
;; INFO-PUTHASH supplies NIL for OLD-INFO if NAME was absent.
|
||||
(dx-flet ((hairy-name (old-info)
|
||||
(augment (or old-info +nil-packed-infos+)
|
||||
+no-auxiliary-key+)))
|
||||
(augment (or old-info +nil-packed-infos+) +no-auxiliary-key+)))
|
||||
(info-puthash *info-environment* name #'hairy-name)))))
|
||||
new-value)
|
||||
|
||||
|
|
@ -813,20 +803,20 @@ This is interpreted as
|
|||
(when (pcl-methodfn-name-p name)
|
||||
(error "Can't SET-INFO-VALUE on PCL-internal function"))
|
||||
(let ((name (uncross name)) new-value)
|
||||
(dx-flet ((augment (vect aux-key) ; VECT is a packed vector, never NIL
|
||||
(declare (simple-vector vect))
|
||||
(dx-flet ((augment (packed-info aux-key) ; PACKED-INFO must not be NIL
|
||||
(declare (type packed-info packed-info))
|
||||
(let ((index
|
||||
(packed-info-value-index vect aux-key info-number)))
|
||||
(packed-info-value-index packed-info aux-key info-number)))
|
||||
(if (not index)
|
||||
(packed-info-insert
|
||||
vect aux-key info-number
|
||||
packed-info aux-key info-number
|
||||
(setq new-value (funcall new-value-fun nil nil)))
|
||||
(let ((oldval (svref vect index)))
|
||||
(let ((oldval (%info-ref packed-info index)))
|
||||
(setq new-value (funcall new-value-fun oldval t))
|
||||
(if (eq new-value oldval)
|
||||
vect ; return the old vector
|
||||
(let ((copy (copy-seq vect)))
|
||||
(setf (svref copy index) new-value)
|
||||
packed-info ; return the old packed-info
|
||||
(let ((copy (copy-packed-info packed-info)))
|
||||
(setf (%info-ref copy index) new-value)
|
||||
copy)))))))
|
||||
(with-globaldb-name (key1 key2) name
|
||||
:simple
|
||||
|
|
@ -855,19 +845,19 @@ This is interpreted as
|
|||
(error "~D is not a legal INFO name." name))
|
||||
(let ((name (uncross name))
|
||||
result)
|
||||
(dx-flet ((get-or-set (info-vect aux-key)
|
||||
(dx-flet ((get-or-set (packed-info aux-key)
|
||||
(let ((index
|
||||
(packed-info-value-index info-vect aux-key info-number)))
|
||||
(packed-info-value-index packed-info aux-key info-number)))
|
||||
(cond (index
|
||||
(setq result (svref info-vect index))
|
||||
nil) ; no update to info-vector
|
||||
(setq result (%info-ref packed-info index))
|
||||
nil) ; no update
|
||||
(t
|
||||
;; Update conflicts possibly for unrelated info-number
|
||||
;; can force re-execution. (UNLESS result ...) tries
|
||||
;; to avoid calling the thunk more than once.
|
||||
(unless result
|
||||
(setq result (funcall creation-thunk)))
|
||||
(packed-info-insert info-vect aux-key info-number
|
||||
(packed-info-insert packed-info aux-key info-number
|
||||
result))))))
|
||||
(with-globaldb-name (key1 key2) name
|
||||
:simple
|
||||
|
|
@ -877,8 +867,7 @@ This is interpreted as
|
|||
:hairy
|
||||
;; INFO-PUTHASH supplies NIL for OLD-INFO if NAME was absent.
|
||||
(dx-flet ((hairy-name (old-info)
|
||||
(or (get-or-set (or old-info +nil-packed-infos+)
|
||||
+no-auxiliary-key+)
|
||||
(or (get-or-set (or old-info +nil-packed-infos+) +no-auxiliary-key+)
|
||||
;; Return OLD-INFO to elide writeback. Unlike for
|
||||
;; UPDATE-SYMBOL-INFO, NIL is not a no-op marker.
|
||||
old-info)))
|
||||
|
|
|
|||
|
|
@ -18,14 +18,13 @@
|
|||
;;; temp-reg-tn to access symbol slots.
|
||||
;;; Since the NIL-as-CONS is necessary, and efficient accessor to lists and
|
||||
;;; instances is desirable, we lose a little on symbol access by being forced
|
||||
;;; to pre-check for NIL. There is trick that can get back some performance
|
||||
;;; on SYMBOL-VALUE which I plan to implement after this much works right.
|
||||
;;; to pre-check for NIL.
|
||||
(define-vop (slot)
|
||||
(:args (object :scs (descriptor-reg)))
|
||||
(:info name offset lowtag)
|
||||
(:results (result :scs (descriptor-reg any-reg)))
|
||||
(:generator 1
|
||||
(cond ((member name '(symbol-name symbol-info sb-xc:symbol-package))
|
||||
(cond ((member name '(symbol-name symbol-%info sb-xc:symbol-package))
|
||||
(let ((null-label (gen-label))
|
||||
(done-label (gen-label)))
|
||||
(inst cmpld object null-tn)
|
||||
|
|
@ -261,27 +260,6 @@
|
|||
NULL
|
||||
(inst addi res null-tn (- (logand sb-vm:nil-value sb-vm:fixnum-tag-mask)))
|
||||
DONE))
|
||||
(define-vop (symbol-plist)
|
||||
(:policy :fast-safe)
|
||||
(:translate symbol-plist)
|
||||
(:args (symbol :scs (descriptor-reg)))
|
||||
(:results (res :scs (descriptor-reg)))
|
||||
(:temporary (:scs (unsigned-reg)) temp)
|
||||
(:generator 6
|
||||
(inst cmpld symbol null-tn)
|
||||
(inst beq NULL)
|
||||
(loadw res symbol symbol-info-slot other-pointer-lowtag)
|
||||
(inst andi. temp res lowtag-mask)
|
||||
(inst cmpwi temp list-pointer-lowtag)
|
||||
(inst beq take-car)
|
||||
(move res null-tn) ; if INFO is a non-list, then the PLIST is NIL
|
||||
(inst b DONE)
|
||||
NULL
|
||||
(loadw res symbol (1- symbol-info-slot) list-pointer-lowtag)
|
||||
;; fallthru. NULL's info slot always holds a cons
|
||||
TAKE-CAR
|
||||
(loadw res res cons-car-slot list-pointer-lowtag)
|
||||
DONE))
|
||||
|
||||
;;;; Fdefinition (fdefn) objects.
|
||||
|
||||
|
|
|
|||
|
|
@ -232,38 +232,6 @@
|
|||
(inst add ndescr ndescr offset)
|
||||
(inst subi ndescr ndescr (- other-pointer-lowtag fun-pointer-lowtag))
|
||||
(inst add func code ndescr)))
|
||||
;;;
|
||||
(define-vop (symbol-info-vector)
|
||||
(:policy :fast-safe)
|
||||
(:translate symbol-info-vector)
|
||||
(:args (x :scs (descriptor-reg)))
|
||||
(:results (res :scs (descriptor-reg)))
|
||||
(:temporary (:sc unsigned-reg) temp)
|
||||
(:generator 1
|
||||
(loadw res x symbol-info-slot other-pointer-lowtag)
|
||||
;; If RES has list-pointer-lowtag, take its CDR. If not, use it as-is.
|
||||
(inst andi temp res lowtag-mask)
|
||||
(inst xori temp temp list-pointer-lowtag)
|
||||
(inst bne temp zero-tn not-equal)
|
||||
(loadw res res cons-cdr-slot list-pointer-lowtag)
|
||||
NOT-EQUAL))
|
||||
|
||||
(define-vop (symbol-plist)
|
||||
(:policy :fast-safe)
|
||||
(:translate symbol-plist)
|
||||
(:args (x :scs (descriptor-reg)))
|
||||
(:results (res :scs (descriptor-reg)))
|
||||
(:temporary (:sc non-descriptor-reg) temp)
|
||||
(:generator 1
|
||||
(loadw res x symbol-info-slot other-pointer-lowtag)
|
||||
;; Instruction pun: (CAR x) is the same as (VECTOR-LENGTH x)
|
||||
;; so if the info slot holds a vector, this gets a fixnum- it's not a plist.
|
||||
(loadw res res cons-car-slot list-pointer-lowtag)
|
||||
(inst andi temp res fixnum-tag-mask)
|
||||
(inst bne temp zero-tn not-equal)
|
||||
(move res null-tn)
|
||||
NOT-EQUAL))
|
||||
|
||||
|
||||
;;;; Other random VOPs.
|
||||
|
||||
|
|
|
|||
|
|
@ -718,18 +718,24 @@
|
|||
|
||||
;;;; structure hackery
|
||||
|
||||
(defun load-instance-length (result instance taggedp)
|
||||
(inst mov :dword result (ea (- instance-pointer-lowtag) instance))
|
||||
;; Returning fixnum/any-reg elides some REX prefixes due to the shifts
|
||||
;; being small. Maybe the asm optimizer could figure it out now?
|
||||
(cond (taggedp
|
||||
(inst shr :dword result (- instance-length-shift n-fixnum-tag-bits))
|
||||
(inst and :dword result (fixnumize instance-length-mask)))
|
||||
(t
|
||||
(inst shr :dword result instance-length-shift)
|
||||
(inst and :dword result instance-length-mask))))
|
||||
|
||||
(define-vop ()
|
||||
(:policy :fast-safe)
|
||||
(:translate %instance-length)
|
||||
(:args (struct :scs (descriptor-reg)))
|
||||
(:results (res :scs (any-reg)))
|
||||
(:result-types positive-fixnum)
|
||||
(:generator 4
|
||||
(inst mov :dword res (ea (- instance-pointer-lowtag) struct))
|
||||
;; Returning fixnum/any-reg elides some REX prefixes due to the shifts
|
||||
;; being small. Maybe the asm optimizer could figure it out now?
|
||||
(inst shr :dword res (- instance-length-shift n-fixnum-tag-bits))
|
||||
(inst and :dword res (fixnumize instance-length-mask))))
|
||||
(:generator 4 (load-instance-length res struct t)))
|
||||
|
||||
(define-full-reffer instance-index-ref * instance-slots-offset
|
||||
instance-pointer-lowtag (any-reg descriptor-reg) * %instance-ref)
|
||||
|
|
|
|||
|
|
@ -335,53 +335,27 @@
|
|||
result))))
|
||||
|
||||
;;;; symbol frobbing
|
||||
(defun load-symbol-info-vector (result symbol)
|
||||
(defun load-symbol-dbinfo (result symbol)
|
||||
(loadw result symbol symbol-info-slot other-pointer-lowtag)
|
||||
;; If RES has list-pointer-lowtag, take its CDR. If not, use it as-is.
|
||||
;; This CMOV safely reads from memory when it does not move, because if
|
||||
;; there is an info-vector in the slot, it has at least one element.
|
||||
;; Use bit index 3 of the lowtag to distinguish list from vector.
|
||||
;; A vector will have a 1 in that bit.
|
||||
;; there is a PACKED-INFO in the slot, it has at least 4 data words in total
|
||||
;; - the header, at least one info descriptor, and at least one datum.
|
||||
;; And since 3 is odd, that would be aligned up to 4.
|
||||
;; Use bit index 2 of the lowtag to distinguish list from instance.
|
||||
;; An instance will have a 0 in that bit.
|
||||
;; This would compile to almost the same code without a VOP,
|
||||
;; but using a jmp around a mov instead.
|
||||
(aver (= (logior list-pointer-lowtag #b1000) other-pointer-lowtag))
|
||||
(inst test :byte result #b1000)
|
||||
(inst cmov :e result
|
||||
(object-slot-ea result cons-cdr-slot list-pointer-lowtag)))
|
||||
(aver (= (logior instance-pointer-lowtag #b0100) list-pointer-lowtag))
|
||||
(inst test :byte result #b0100)
|
||||
(inst cmov :nz result (object-slot-ea result cons-cdr-slot list-pointer-lowtag)))
|
||||
|
||||
(define-vop (symbol-info-vector)
|
||||
(define-vop (symbol-dbinfo)
|
||||
(:policy :fast-safe)
|
||||
(:translate symbol-info-vector)
|
||||
(:translate symbol-dbinfo)
|
||||
(:args (x :scs (descriptor-reg)))
|
||||
(:results (res :scs (descriptor-reg)))
|
||||
(:generator 1 (load-symbol-info-vector res x)))
|
||||
|
||||
(define-vop (symbol-plist)
|
||||
(:policy :fast-safe)
|
||||
(:translate symbol-plist)
|
||||
(:args (x :scs (descriptor-reg)))
|
||||
(:results (res :scs (descriptor-reg)))
|
||||
(:temporary (:sc unsigned-reg) temp)
|
||||
(:generator 1
|
||||
#-ubsan
|
||||
(progn
|
||||
(loadw res x symbol-info-slot other-pointer-lowtag)
|
||||
;; Instruction pun: (CAR x) is the same as (VECTOR-LENGTH x)
|
||||
;; so if the info slot holds a vector, this gets a fixnum- it's not a plist.
|
||||
(loadw res res cons-car-slot list-pointer-lowtag)
|
||||
(inst mov temp nil-value)
|
||||
(inst test :byte res fixnum-tag-mask)
|
||||
(inst cmov :e res temp))
|
||||
;; This way doesn't assume that CAR and VECTOR-LENGTH are the same memory access.
|
||||
;; (And it's not even clear that using CMOV is preferable)
|
||||
#+ubsan
|
||||
(let ((out (gen-label)))
|
||||
(loadw temp x symbol-info-slot other-pointer-lowtag)
|
||||
(inst mov res nil-value)
|
||||
(inst test :byte temp #b1000) ; if temp is a vector, return NIL
|
||||
(inst jmp :ne out)
|
||||
(loadw res temp cons-car-slot list-pointer-lowtag)
|
||||
(emit-label out))))
|
||||
(:generator 1 (load-symbol-dbinfo res x)))
|
||||
|
||||
;;;; other miscellaneous VOPs
|
||||
|
||||
|
|
|
|||
|
|
@ -233,39 +233,6 @@
|
|||
:disp (- fun-pointer-lowtag
|
||||
(* simple-fun-insts-offset n-word-bytes))))))
|
||||
|
||||
;;;; symbol frobbing
|
||||
|
||||
(define-vop (symbol-info-vector)
|
||||
(:policy :fast-safe)
|
||||
(:translate symbol-info-vector)
|
||||
(:args (x :scs (descriptor-reg)))
|
||||
(:results (res :scs (descriptor-reg)))
|
||||
(:temporary (:sc unsigned-reg :offset eax-offset) eax)
|
||||
(:generator 1
|
||||
(loadw res x symbol-info-slot other-pointer-lowtag)
|
||||
;; If RES has list-pointer-lowtag, take its CDR. If not, use it as-is.
|
||||
;; This CMOV safely reads from memory when it does not move, because if
|
||||
;; there is an info-vector in the slot, it has at least one element.
|
||||
;; This would compile to almost the same code without a VOP,
|
||||
;; but using a jmp around a mov instead.
|
||||
(inst lea eax (make-ea :dword :base res :disp (- list-pointer-lowtag)))
|
||||
(emit-optimized-test-inst eax lowtag-mask)
|
||||
(inst cmov :e res
|
||||
(object-slot-ea res cons-cdr-slot list-pointer-lowtag))))
|
||||
(define-vop (symbol-plist)
|
||||
(:policy :fast-safe)
|
||||
(:translate symbol-plist)
|
||||
(:args (x :scs (descriptor-reg)))
|
||||
(:results (res :scs (descriptor-reg)))
|
||||
(:temporary (:sc unsigned-reg) temp)
|
||||
(:generator 1
|
||||
(loadw res x symbol-info-slot other-pointer-lowtag)
|
||||
;; Instruction pun: (CAR x) is the same as (VECTOR-LENGTH x)
|
||||
;; so if the info slot holds a vector, this gets a fixnum- it's not a plist.
|
||||
(loadw res res cons-car-slot list-pointer-lowtag)
|
||||
(inst mov temp nil-value)
|
||||
(emit-optimized-test-inst res fixnum-tag-mask)
|
||||
(inst cmov :e res temp)))
|
||||
|
||||
;;;; other miscellaneous VOPs
|
||||
|
||||
|
|
|
|||
|
|
@ -573,19 +573,19 @@ static void print_slots(char **slots, int count, lispobj *ptr)
|
|||
|
||||
lispobj symbol_function(lispobj* symbol)
|
||||
{
|
||||
lispobj info = ((struct symbol*)symbol)->info;
|
||||
if (listp(info))
|
||||
info = CONS(info)->cdr;
|
||||
if (lowtag_of(info) == OTHER_POINTER_LOWTAG) {
|
||||
struct vector* v = VECTOR(info);
|
||||
int len = vector_len(v);
|
||||
if (len != 0) {
|
||||
lispobj elt = v->data[0]; // Just like INFO-VECTOR-FDEFN
|
||||
if (fixnump(elt) && (fixnum_value(elt) & 07777) >= 07701) {
|
||||
lispobj fdefn = v->data[len-1];
|
||||
if (lowtag_of(fdefn) == OTHER_POINTER_LOWTAG)
|
||||
return FDEFN(fdefn)->fun;
|
||||
}
|
||||
lispobj info_holder = ((struct symbol*)symbol)->info;
|
||||
if (listp(info_holder))
|
||||
info_holder = CONS(info_holder)->cdr;
|
||||
if (lowtag_of(info_holder) == INSTANCE_POINTER_LOWTAG) {
|
||||
struct instance* info = INSTANCE(info_holder);
|
||||
// Do the same thing as PACKED-INFO-FDEFN
|
||||
lispobj elt = info->slots[INSTANCE_DATA_START];
|
||||
if (fixnump(elt) && (fixnum_value(elt) & 07777) >= 07701) {
|
||||
// FIXME: genesis should be writing a #define for INSTANCE_LENGTH_MASK
|
||||
int len = (info->header >> INSTANCE_LENGTH_SHIFT) 0x3FF;
|
||||
lispobj fdefn = info->slots[len-1];
|
||||
if (lowtag_of(fdefn) == OTHER_POINTER_LOWTAG)
|
||||
return FDEFN(fdefn)->fun;
|
||||
}
|
||||
}
|
||||
return NIL;
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@
|
|||
(info '((bork 42))))
|
||||
(import s "CL-USER")
|
||||
(set s 'hi)
|
||||
(setf (symbol-info s) info)
|
||||
(setf (symbol-%info s) info)
|
||||
(walk-slots-test s `(hi ,info ,name ,(find-package "CL-USER")))))
|
||||
|
||||
(test-util:with-test (:name :walk-slots-closure)
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
(with-test (:name :map-referencing-objs)
|
||||
(sb-vm::map-referencing-objects (lambda (x) (assert (not (typep x 'afoo))))
|
||||
:dynamic '*posix-argv*)
|
||||
(let ((v (sb-kernel:symbol-info 'satisfies)) referers)
|
||||
(let ((v (sb-kernel:symbol-%info 'satisfies)) referers)
|
||||
(sb-vm::map-referencing-objects (lambda (referer) (push referer referers))
|
||||
#+gencgc :dynamic #-gencgc :static v)
|
||||
#+immobile-space
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@
|
|||
|
||||
;; removing the one info shrinks the vector to nothing
|
||||
;; and all values of nothing are EQ
|
||||
(assert (equalp #(0) (packed-info-remove foo-iv +no-auxiliary-key+ '(5))))
|
||||
(assert (equalp (packed-info-remove foo-iv +no-auxiliary-key+ '(5))
|
||||
+nil-packed-infos+))
|
||||
(assert (eq (packed-info-remove foo-iv +no-auxiliary-key+ '(5))
|
||||
(packed-info-remove bar-iv +no-auxiliary-key+ '(6))))
|
||||
(assert (eq (packed-info-remove foo-iv +no-auxiliary-key+ '(5))
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@
|
|||
(info :variable :macro-expansion 'fruitbaskets)
|
||||
(assert (and (not foundp) (not data)))))
|
||||
|
||||
;; packed info vector tests
|
||||
;; packed-info tests
|
||||
|
||||
(test-util:with-test (:name :globaldb-info-iterate)
|
||||
(let ((s (with-output-to-string (*standard-output*) (show-info '*))))
|
||||
|
|
@ -189,10 +189,10 @@
|
|||
|
||||
;; The real GET-INFO-VALUE AVERs that INFO-NUMBER is legal. This one doesn't.
|
||||
(defun cheating-get-info-value (sym aux-key info-number)
|
||||
(let* ((vector (symbol-info-vector sym))
|
||||
(let* ((vector (symbol-dbinfo sym))
|
||||
(index (packed-info-value-index vector aux-key info-number)))
|
||||
(if index
|
||||
(values (svref vector index) t)
|
||||
(values (sb-kernel:%info-ref vector index) t)
|
||||
(values nil nil))))
|
||||
|
||||
;; Info vectors may be concurrently updated. If more than one thread writes
|
||||
|
|
|
|||
Loading…
Reference in a new issue