Hoist tests from scan_weak_pointers() into scav_weak_pointer()
[sbcl.git] / doc / internals / slot-value.texinfo
blob5ad0dd241c21e2e06d3bc0a5484ba03b572508b8
1 @node Slot-Value
2 @comment  node-name,  next,  previous,  up
3 @chapter Slot-Value
5 @findex slot-value
6 @findex (setf slot-value)
7 @findex slot-boundp
8 @findex slot-makunbound
9 @findex slot-value-using-class
10 @findex (setf slot-value-using-class)
11 @findex slot-boundp-using-class
12 @findex slot-makunbound-using-class
14 @menu
15 * Basic Implementation::
16 * Compiler Transformations::
17 * MOP Optimizations::
18 @end menu
20 The ANSI Common Lisp standard specifies @code{slot-value}, @code{(setf
21 slot-value)}, @code{slot-boundp} and @code{slot-makunbound} for
22 standard-objects, and furthermore suggests that these be implemented in
23 terms of Metaobject generic functions @code{slot-value-using-class},
24 @code{(setf slot-value-using-class)}, @code{slot-boundp-using-class} and
25 @code{slot-makunbound-using-class}.  To make performance of these
26 operators tolerable, a number of optimizations are performed, at both
27 compile-time and run-time@footnote{Note that ,at present,
28 @code{slot-makunbound} and @code{slot-makunbound-using-class} are not
29 optimized in any of the ways mentioned below.}.
31 @node Basic Implementation
32 @comment  node-name,  next,  previous,  up
33 @section Basic Implementation
35 All of the following, while described in terms of @code{slot-value},
36 also applies to @code{(setf slot-value)} and to @code{slot-boundp}, and
37 could in principle be extended to @code{slot-makunbound}.
39 The basic implementation of @code{slot-value}, following the suggestion
40 in the standards document, is shown in @ref{ex:slot-value}; the
41 implementation of the other slot operators is similar.  The work to be
42 done simply to arrive at the generic function call is already
43 substantial: we need to look up the object's class and iterate over the
44 class' slots to find a slot of the right name, only then are we in a
45 position to call the generic function which implements the slot access
46 directly.
48 @float Example,ex:slot-value
49 @example
50 (defun slot-value (object slot-name)
51   (let* ((class (class-of object))
52          (slot-definition (find-slot-definition class slot-name)))
53     (if (null slot-definition)
54         (values (slot-missing class object slot-name 'slot-value))
55         (slot-value-using-class class object slot-definition))))
56 @end example
57 @end float
59 The basic implementation of @code{slot-value-using-class} specialized on
60 the standard metaobject classes is shown in
61 @ref{ex:slot-value-using-class}.  First, we check for an obsolete
62 instance (that is, one whose class has been redefined since the object
63 was last accessed; if it has, the object must be updated by
64 @code{update-instance-for-redefined-class}); then, we acquire the slot's
65 storage location from the slot definition, the value from the instance's
66 slot vector, and then after checking the value against the internal unbound
67 marker, we return it.
69 @float Example,ex:slot-value-using-class
70 @example
71 (defmethod slot-value-using-class
72     ((class std-class)
73      (object standard-object)
74      (slotd standard-effective-slot-definition))
75   (check-obsolete-instance object)
76   (let* ((location (slot-definition-location slotd))
77          (value
78           (etypecase location
79             (fixnum (clos-slots-ref (instance-slots object) location))
80             (cons (cdr location)))))
81     (if (eq value +slot-unbound+)
82         (values (slot-unbound class object (slot-definition-name slotd)))
83         value)))
84 @end example
85 @end float
87 Clearly, all of this activity will cause the performance of clos slot
88 access to compare poorly with structure slot access; while there will be
89 of necessity a slowdown between the slot accesses because the structure
90 class need not be redefineable (while redefinition of standard-object
91 classes is extremely common), the overhead presented in the above
92 implementation is excessive.
94 @node Compiler Transformations
95 @comment  node-name,  next,  previous,  up
96 @section Compiler Transformations
98 The compiler can assist in optimizing calls to @code{slot-value}: in
99 particular, and despite the highly-dynamic nature of CLOS, compile-time
100 knowledge of the name of the slot being accessed permits precomputation
101 of much of the access (along with a branch to the slow path in case the
102 parameters of the access change between compile-time and run-time).
104 @subsection Within Methods
106 @cindex permutation vector
108 If the object being accessed is a required parameter to the method,
109 where the parameter variable is unmodified in the method body, and the
110 slot name is a compile-time constant, then fast slot access can be
111 supported through @dfn{permutation vectors}.
113 (FIXME: what about the metaclasses of the object?  Does it have to be
114 standard-class, or can it be funcallable-standard-class?  Surely
115 structure-class objects could be completely optimized if the class
116 definition and slot name are both known at compile-time.)
118 Permutation vectors are built up and maintained to associate a
119 compile-time index associated with a slot name with an index into the
120 slot vector for a class of objects.  The permutation vector applicable
121 to a given method call (FIXME: or effective method? set of classes?
122 something else?) is passed to the method body, and slots are accessed by
123 looking up the index to the slot vector in the permutation vector, then
124 looking up the value from the slot vector.  (FIXME: a diagram would
125 help, if I understood this bit well enough to draw a diagram).
127 Subsequent redefinitions of classes or of methods on
128 @code{slot-value-using-class} cause an invalid index to be written into
129 the permutation vector, and the call falls back to a full call to
130 @code{slot-value}.
132 If the conditions for (structure or) permutation vector slot access
133 optimization are not met, optimization of @code{slot-value} within
134 methods falls back to the same as for calls to @code{slot-value} outside
135 of methods, below.
137 @subsection Outside of Methods
139 @findex load-time-value
141 A call to @code{slot-value} with a compile-time constant slot
142 @var{name} argument is compiled into a call to a generic function
143 named @code{(sb-pcl::slot-accessor :global @var{name} sb-pcl::reader)},
144 together with code providing load-time assurance (via
145 @code{load-time-value}) that the generic function is bound and has a
146 suitable accessor method.  This generic function then benefits from the
147 same optimizations as ordinary accessors, described in
148 @ref{Accessor Discriminating Functions}.
150 (FIXME: how does this get invalidated if we later add methods on
151 @code{slot-value-using-class}?  Hm, maybe it isn't.  I think this is
152 probably a bug, and that adding methods to @code{slot-value-using-class}
153 needs to invalidate accessor caches.  Bah, humbug.  Test code in
154 @ref{ex:buggycache}, and note that I think that the analogous case
155 involving adding or removing methods from
156 @code{compute-applicable-methods} is handled correctly by
157 @code{update-all-c-a-m-gf-info}.)
159 @float Example,ex:buggycache
160 @example
161 (defclass foo () ((a :initform 0)))
162 (defun foo (x) (slot-value x 'a))
163 (foo (make-instance 'foo)) ; => 0
164 (defmethod slot-value-using-class :after
165   ((class std-class) (object foo)
166    (slotd standard-effective-slot-definition))
167   (print "hi"))
168 (foo (make-instance 'foo)) ; => 0, no print
169 (defclass bar (foo) ((a :initform 1)))
170 (foo (make-instance 'bar)) ; => 1  and prints "hi"
171 (foo (make-instance 'foo)) ; => 0, no print
172 @end example
173 @end float
175 @node MOP Optimizations
176 @comment  node-name,  next,  previous,  up
177 @section MOP Optimizations
179 Even when nothing is known at compile-time about the call to
180 @code{slot-value}, it is possible to do marginally better than in
181 @ref{ex:slot-value-using-class}.  Each effective slot definition
182 metaobject can cache its own effective method, and the discriminating
183 function for @code{slot-value-using-class} is set to simply call the
184 function in its slot definition argument.
186 (FIXME: I'm pretty sure this is a bad plan in general.  Or rather, it's
187 probably a good plan, but the effective methods should probably be
188 computed lazily rather than eagerly.  The default image has 8589
189 closures implementing this optimization: 3 (@code{slot-value},
190 @code{set-slot-value} and @code{slot-boundp}) for each of 2863 effective
191 slots.)
193 (Also note that this optimization depends on not being able to
194 specialize the @code{new-value} argument to @code{(setf
195 slot-value-using-class)}.)