2 * This software is part of the SBCL system. See the README file for
5 * This software is derived from the CMU CL system, which was
6 * written at Carnegie Mellon University and released into the
7 * public domain. The software is in the public domain and is
8 * provided with absolutely no warranty. See the COPYING and CREDITS
9 * files for more information.
16 #include "genesis/sbcl.h"
23 #include "genesis/primitive-objects.h"
24 #include "genesis/instance.h"
25 #include "genesis/hash-table.h"
26 #include "genesis/package.h"
27 #include "genesis/split-ordered-list.h"
28 #include "genesis/brothertree.h"
31 search_read_only_space(void *pointer
)
33 lispobj
*start
= (lispobj
*) READ_ONLY_SPACE_START
;
34 lispobj
*end
= read_only_space_free_pointer
;
35 if ((pointer
< (void *)start
) || (pointer
>= (void *)end
))
37 return gc_search_space(start
, pointer
);
41 search_static_space(void *pointer
)
43 #ifdef LISP_FEATURE_PERMGEN // consider it part of static space
46 if ((uword_t
)pointer
>= PERMGEN_SPACE_START
&&
47 (lispobj
*)pointer
< permgen_space_free_pointer
&&
48 ((found
= gc_search_space((lispobj
*)PERMGEN_SPACE_START
, pointer
)) != NULL
))
52 lispobj
*start
= (lispobj
*)STATIC_SPACE_OBJECTS_START
;
53 lispobj
*end
= static_space_free_pointer
;
54 if ((pointer
< (void *)start
) || (pointer
>= (void *)end
))
56 return gc_search_space(start
, pointer
);
59 #ifndef LISP_FEATURE_IMMOBILE_SPACE
60 lispobj
*search_codeblob_offsets(void* pointer
) {
61 if ((uword_t
)pointer
< TEXT_SPACE_START
||
62 (uword_t
)pointer
>= (uword_t
)text_space_highwatermark
) return 0;
63 struct vector
* v
= (void*)TEXT_SPACE_START
;
64 #ifdef LISP_FEATURE_64_BIT
65 uint32_t* data
= (void*)v
->data
;
66 int index
= bsearch_lesseql_uint32((char*)pointer
- (char*)TEXT_SPACE_START
,
69 uword_t
* data
= v
->data
;
70 int index
= bsearch_lesseql_uword((char*)pointer
- (char*)TEXT_SPACE_START
,
74 lispobj
* base
= (lispobj
*)(TEXT_SPACE_START
+ data
[index
]);
75 gc_assert(widetag_of(base
) == CODE_HEADER_WIDETAG
);
82 lispobj
*search_all_gc_spaces(void *pointer
)
85 if (((start
= search_dynamic_space(pointer
)) != NULL
) ||
86 #ifdef LISP_FEATURE_IMMOBILE_SPACE
87 ((start
= search_immobile_space(pointer
)) != NULL
) ||
89 ((start
= search_codeblob_offsets(pointer
)) != NULL
) ||
91 ((start
= search_static_space(pointer
)) != NULL
) ||
92 ((start
= search_read_only_space(pointer
)) != NULL
))
97 static int __attribute__((unused
)) strcmp_ucs4_ascii(uint32_t* a
, unsigned char* b
,
102 // Lisp terminates UCS4 strings with NULL bytes - probably to no avail
103 // since null-terminated UCS4 isn't a common convention for any foreign ABI -
104 // but length has been pre-checked, so hitting an ASCII null is a win.
106 while (toupper(a
[i
]) == toupper(b
[i
]))
118 return a
[i
] - b
[i
]; // same return convention as strcmp()
121 struct symbol_search
{
125 static uword_t
search_symbol_aux(lispobj
* start
, lispobj
* end
, uword_t arg
)
127 struct symbol_search
* ss
= (struct symbol_search
*)arg
;
128 return (uword_t
)search_for_symbol(ss
->name
, (lispobj
)start
, (lispobj
)end
, ss
->ignore_case
);
130 lispobj
* search_for_symbol(char *name
, lispobj start
, lispobj end
, bool ignore_case
)
132 lispobj
* where
= (lispobj
*)start
;
133 lispobj
* limit
= (lispobj
*)end
;
134 lispobj namelen
= make_fixnum(strlen(name
));
136 #ifdef LISP_FEATURE_GENERATIONAL
137 // This function was never safe to use on pages that were dirtied with unboxed words.
138 // It has become even less safe now that don't prezero most pages during GC,
139 // because we will certainly encounter remnants of forwarding pointers etc.
140 // So if the specified range is all of dynamic space, defer to the space walker.
141 if (start
== DYNAMIC_SPACE_START
&& end
== dynamic_space_highwatermark()) {
142 struct symbol_search ss
= {name
, ignore_case
};
143 return (lispobj
*)walk_generation(search_symbol_aux
, -1, (uword_t
)&ss
);
146 while (where
< limit
) {
147 struct vector
*string
;
148 if (widetag_of(where
) == SYMBOL_WIDETAG
&&
149 (string
= symbol_name((struct symbol
*)where
)) != 0 &&
150 string
->length_
== namelen
) {
151 if (gc_managed_addr_p((lispobj
)string
) &&
152 ((widetag_of(&string
->header
) == SIMPLE_BASE_STRING_WIDETAG
153 && !(ignore_case
? strcasecmp
: strcmp
)((char *)string
->data
, name
))
154 #ifdef LISP_FEATURE_SB_UNICODE
155 || (widetag_of(&string
->header
) == SIMPLE_CHARACTER_STRING_WIDETAG
156 && !strcmp_ucs4_ascii((uint32_t*)string
->data
,
157 (unsigned char*)name
, ignore_case
))
162 where
+= object_size(where
);
167 /// This unfortunately entails a heap scan,
168 /// but it's quite fast if the symbol is found in immobile space.
169 #ifdef LISP_FEATURE_SB_THREAD
170 struct symbol
* lisp_symbol_from_tls_index(lispobj tls_index
)
174 #ifdef LISP_FEATURE_IMMOBILE_SPACE
175 where
= (lispobj
*)FIXEDOBJ_SPACE_START
;
176 end
= fixedobj_free_pointer
;
179 while (where
< end
) {
180 lispobj header
= *where
;
181 int widetag
= header_widetag(header
);
182 if (widetag
== SYMBOL_WIDETAG
&&
183 tls_index_of(((struct symbol
*)where
)) == tls_index
)
184 return (struct symbol
*)where
;
185 where
+= object_size2(where
, header
);
187 if (where
>= (lispobj
*)DYNAMIC_SPACE_START
)
189 where
= (lispobj
*)DYNAMIC_SPACE_START
;
190 end
= (lispobj
*)dynamic_space_highwatermark();
196 static uword_t
bruteforce_findpkg_by_id(lispobj
* where
, lispobj
* limit
, uword_t id
)
199 for ( where
= next_object(where
, 0, limit
) ; where
;
200 where
= next_object(where
, object_size(where
), limit
) ) {
201 if (widetag_of(where
) == INSTANCE_WIDETAG
202 && (layout
= instance_layout(where
)) != 0
203 && layout_depth2_id(LAYOUT(layout
)) == PACKAGE_LAYOUT_ID
204 && ((struct package
*)where
)->id
== id
) {
205 return make_lispobj(where
, INSTANCE_POINTER_LOWTAG
);
211 lispobj
get_package_by_id(int id
) {
212 // lisp_package_vector is a tagged pointer to SIMPLE-VECTOR
213 lispobj vector
= barrier_load(&lisp_package_vector
);
215 // Perform a heap walk. This should never occur except in core loading/saving.
216 lispobj result
= walk_generation(bruteforce_findpkg_by_id
, -1, make_fixnum(id
));
217 if (is_lisp_pointer(result
)) return result
;
218 lose("get_package_by_id: no package vector");
220 if (id
>= vector_len(VECTOR(vector
))) lose("can't decode package ID %d", id
);
221 return barrier_load(&VECTOR(vector
)->data
[id
]);
224 static bool sym_stringeq(lispobj sym
, const char *string
, int len
)
226 struct vector
* name
= symbol_name(SYMBOL(sym
));
227 return widetag_of(&name
->header
) == SIMPLE_BASE_STRING_WIDETAG
228 && vector_len(name
) == len
229 && !memcmp(name
->data
, string
, len
);
232 static lispobj
* search_package_symbols(lispobj package
, char* symbol_name
)
234 // Since we don't have Lisp's string hash algorithm in C, we can only
236 struct package
* pkg
= (void*)INSTANCE(package
);
238 for (pass
= 0; pass
<= 1; ++pass
) {
239 struct symbol_table
* table
= (void*)
240 INSTANCE(barrier_load(pass
? &pkg
->external_symbols
: &pkg
->internal_symbols
));
241 gc_assert(widetag_of(&table
->header
) == INSTANCE_WIDETAG
);
242 lispobj cells
= barrier_load(&table
->_cells
);
243 gc_assert(listp(cells
));
244 lispobj symbols
= barrier_load(&CONS(cells
)->cdr
);
245 gc_assert(simple_vector_p(symbols
));
246 struct vector
* v
= VECTOR(symbols
);
247 lispobj namelen
= strlen(symbol_name
);
249 for (index
= vector_len(v
)-1; index
>= 0 ; --index
) {
250 lispobj thing
= v
->data
[index
];
251 if (lowtag_of(thing
) == OTHER_POINTER_LOWTAG
252 && widetag_of(&SYMBOL(thing
)->header
) == SYMBOL_WIDETAG
253 && sym_stringeq(thing
, symbol_name
, namelen
)) {
254 return (lispobj
*)SYMBOL(thing
);
261 lispobj
* find_symbol(char* symbol_name
, lispobj package
)
263 return package
? search_package_symbols(package
, symbol_name
) : 0;
266 static inline bool fringe_node_p(struct binary_node
* node
)
268 const int internal_node_payload_words
=
269 ((sizeof (struct binary_node
) / sizeof (lispobj
)) - 1);
271 ((unsigned int)node
->header
>> INSTANCE_LENGTH_SHIFT
) & INSTANCE_LENGTH_MASK
;
272 return payload_words
< internal_node_payload_words
;
275 /* I anticipate using the brothertree search algorithms to find code
276 * while GC has already potentially moved some of the tree nodes,
277 * thus the use of follow_fp() before dereferencing a node pointer */
278 uword_t
brothertree_find_eql(uword_t key
, lispobj tree
)
280 while (tree
!= NIL
) {
281 tree
= follow_fp(tree
);
282 lispobj layout
= follow_fp(instance_layout(INSTANCE(tree
)));
283 if (layout_depth2_id(LAYOUT(layout
)) == BROTHERTREE_UNARY_NODE_LAYOUT_ID
) {
284 tree
= ((struct unary_node
*)INSTANCE(tree
))->child
;
286 struct binary_node
* node
= (void*)INSTANCE(tree
);
287 if (node
->uw_key
== key
) return tree
;
288 lispobj l
= NIL
, r
= NIL
;
289 // unless a fringe node, read the left and right pointers
290 if (!fringe_node_p(node
)) l
= node
->_left
, r
= node
->_right
;
291 if (key
< node
->uw_key
) tree
= l
; else tree
= r
;
297 uword_t
brothertree_find_lesseql(uword_t key
, lispobj tree
)
300 while (tree
!= NIL
) {
301 tree
= follow_fp(tree
);
302 lispobj layout
= follow_fp(instance_layout(INSTANCE(tree
)));
303 if (layout_depth2_id(LAYOUT(layout
)) == BROTHERTREE_UNARY_NODE_LAYOUT_ID
) {
304 tree
= ((struct unary_node
*)INSTANCE(tree
))->child
;
306 struct binary_node
* node
= (void*)INSTANCE(tree
);
307 if (node
->uw_key
== key
) return tree
;
308 lispobj l
= NIL
, r
= NIL
;
309 // unless a fringe node, read the left and right pointers
310 if (!fringe_node_p(node
)) l
= node
->_left
, r
= node
->_right
;
311 if (key
< node
->uw_key
) tree
= l
; else { best
= tree
; tree
= r
; }
317 uword_t
brothertree_find_greatereql(uword_t key
, lispobj tree
)
320 while (tree
!= NIL
) {
321 tree
= follow_fp(tree
);
322 lispobj layout
= follow_fp(instance_layout(INSTANCE(tree
)));
323 if (layout_depth2_id(LAYOUT(layout
)) == BROTHERTREE_UNARY_NODE_LAYOUT_ID
) {
324 tree
= ((struct unary_node
*)INSTANCE(tree
))->child
;
326 struct binary_node
* node
= (void*)INSTANCE(tree
);
327 if (node
->uw_key
== key
) return tree
;
328 lispobj l
= NIL
, r
= NIL
;
329 // unless a fringe node, read the left and right pointers
330 if (!fringe_node_p(node
)) l
= node
->_left
, r
= node
->_right
;
331 if (key
> node
->uw_key
) tree
= r
; else { best
= tree
; tree
= l
; }
337 #define BSEARCH_ALGORITHM_IMPL \
339 int high = nelements - 1; \
340 while (low <= high) { \
341 /* Many authors point out that this is a bug if overflow occurs \
342 * and it can be avoided by using low+(high-low)/2 or similar. \
343 * But we will never have so many code blobs that overflow occurs. */ \
344 int mid = (low + high) / 2; \
345 uword_t probe = array[mid]; \
346 if (probe < item) low = mid + 1; \
347 else if (probe > item) high = mid - 1; \
351 /* Binary search a sorted vector (of code base addresses).
352 * This might be useful for generations other than 0,
353 * because we only need to rebuild the vector in GC, which is
354 * easily done; and it's much denser than a tree */
355 int bsearch_lesseql_uword(uword_t item
, uword_t
* array
, int nelements
)
357 BSEARCH_ALGORITHM_IMPL
358 if (high
>= 0) return high
;
362 int bsearch_greatereql_uword(uword_t item
, uword_t
* array
, int nelements
)
364 BSEARCH_ALGORITHM_IMPL
365 if (low
< nelements
) return low
;
369 #ifdef LISP_FEATURE_64_BIT
370 /// As above, but using space-relative pointers which halve the storage requirement
371 int bsearch_lesseql_uint32(uint32_t item
, uint32_t* array
, int nelements
)
373 BSEARCH_ALGORITHM_IMPL
374 if (high
>= 0) return high
;
378 int bsearch_greatereql_uint32(uint32_t item
, uint32_t* array
, int nelements
)
380 BSEARCH_ALGORITHM_IMPL
381 if (low
< nelements
) return low
;
386 /* Find in an address-based split-ordered list
387 * Unlike the lisp algorithm, this does not "assist" a pending deletion
388 * by completing it with compare-and-swap - this loop simply ignores
389 * any deleted nodes that haven't been snipped out yet. */
391 split_ordered_list_find(struct split_ordered_list
* solist
,
394 struct cons
* bins_and_shift
= CONS(solist
->bins
);
395 struct vector
* bins
= VECTOR(bins_and_shift
->car
);
396 int shift
= fixnum_value(bins_and_shift
->cdr
);
397 // see MULTIPLICATIVE-HASH in src/code/solist.lisp
398 #ifdef LISP_FEATURE_64_BIT
399 lispobj prod
= 11400714819323198485UL * key
;
401 lispobj prod
= 2654435769U * key
;
403 lispobj full_hash
= (prod
>> (1+N_FIXNUM_TAG_BITS
)) | 1;
404 int bin_index
= full_hash
>> shift
;
405 lispobj nodeptr
= bins
->data
[bin_index
];
406 while ((nodeptr
& WIDETAG_MASK
) == UNBOUND_MARKER_WIDETAG
) {
407 nodeptr
= bins
->data
[--bin_index
];
409 struct solist_node
* node
= (void*)native_pointer(nodeptr
);
410 lispobj hash_as_fixnum
= make_fixnum(full_hash
);
412 if (node
->node_hash
== hash_as_fixnum
) { // possible hit
413 if (node
->so_key
== key
&& // looking good
414 lowtag_of(node
->_node_next
) != 0) { // node is not deleted, great
418 if (node
->node_hash
> hash_as_fixnum
||
419 node
->_node_next
== LFLIST_TAIL_ATOM
) return NULL
;
420 node
= (void*)native_pointer(node
->_node_next
);