Fix a rounding error in TAN type derivation.
[sbcl.git] / src / runtime / search.c
blob14b61b1691f1801d38b2507daa264fa7cc919014
1 /*
2 * This software is part of the SBCL system. See the README file for
3 * more information.
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.
12 #include <string.h>
13 #include <ctype.h>
14 #include <stdint.h>
16 #include "genesis/sbcl.h"
17 #include "interr.h"
18 #include "lispobj.h"
19 #include "os.h"
20 #include "search.h"
21 #include "thread.h"
22 #include "gc.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"
30 lispobj *
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))
36 return NULL;
37 return gc_search_space(start, pointer);
40 lispobj *
41 search_static_space(void *pointer)
43 #ifdef LISP_FEATURE_PERMGEN // consider it part of static space
45 lispobj* found = 0;
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))
49 return found;
51 #endif
52 lispobj *start = (lispobj *)STATIC_SPACE_OBJECTS_START;
53 lispobj *end = static_space_free_pointer;
54 if ((pointer < (void *)start) || (pointer >= (void *)end))
55 return NULL;
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,
67 data, vector_len(v));
68 #else
69 uword_t* data = v->data;
70 int index = bsearch_lesseql_uword((char*)pointer - (char*)TEXT_SPACE_START,
71 data, vector_len(v));
72 #endif
73 if (index >= 0) {
74 lispobj* base = (lispobj*)(TEXT_SPACE_START + data[index]);
75 gc_assert(widetag_of(base) == CODE_HEADER_WIDETAG);
76 return base;
78 return 0;
80 #endif
82 lispobj *search_all_gc_spaces(void *pointer)
84 lispobj *start;
85 if (((start = search_dynamic_space(pointer)) != NULL) ||
86 #ifdef LISP_FEATURE_IMMOBILE_SPACE
87 ((start = search_immobile_space(pointer)) != NULL) ||
88 #else
89 ((start = search_codeblob_offsets(pointer)) != NULL) ||
90 #endif
91 ((start = search_static_space(pointer)) != NULL) ||
92 ((start = search_read_only_space(pointer)) != NULL))
93 return start;
94 return NULL;
97 static int __attribute__((unused)) strcmp_ucs4_ascii(uint32_t* a, unsigned char* b,
98 bool ignore_case)
100 int i = 0;
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.
105 if (ignore_case) {
106 while (toupper(a[i]) == toupper(b[i]))
107 if (b[i] == 0)
108 return 0;
109 else
110 ++i;
111 } else {
112 while (a[i] == b[i])
113 if (b[i] == 0)
114 return 0;
115 else
116 ++i;
118 return a[i] - b[i]; // same return convention as strcmp()
121 struct symbol_search {
122 char *name;
123 bool ignore_case;
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);
145 #endif
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))
158 #endif
160 return where;
162 where += object_size(where);
164 return 0;
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)
172 lispobj* where = 0;
173 lispobj* end = 0;
174 #ifdef LISP_FEATURE_IMMOBILE_SPACE
175 where = (lispobj*)FIXEDOBJ_SPACE_START;
176 end = fixedobj_free_pointer;
177 #endif
178 while (1) {
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)
188 break;
189 where = (lispobj*)DYNAMIC_SPACE_START;
190 end = (lispobj*)dynamic_space_highwatermark();
192 return 0;
194 #endif
196 static uword_t bruteforce_findpkg_by_id(lispobj* where, lispobj* limit, uword_t id)
198 lispobj layout;
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);
208 return 0;
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);
214 if (!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
235 // scan linearly.
236 struct package* pkg = (void*)INSTANCE(package);
237 int pass;
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);
248 int index;
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);
258 return 0;
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);
270 int payload_words =
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;
285 } else {
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;
294 return 0;
297 uword_t brothertree_find_lesseql(uword_t key, lispobj tree)
299 lispobj best = NIL;
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;
305 } else {
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; }
314 return best;
317 uword_t brothertree_find_greatereql(uword_t key, lispobj tree)
319 lispobj best = NIL;
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;
325 } else {
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; }
334 return best;
337 #define BSEARCH_ALGORITHM_IMPL \
338 int low = 0; \
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; \
348 else return mid; \
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;
359 return -1;
362 int bsearch_greatereql_uword(uword_t item, uword_t* array, int nelements)
364 BSEARCH_ALGORITHM_IMPL
365 if (low < nelements) return low;
366 return -1;
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;
375 return -1;
378 int bsearch_greatereql_uint32(uint32_t item, uint32_t* array, int nelements)
380 BSEARCH_ALGORITHM_IMPL
381 if (low < nelements) return low;
382 return -1;
384 #endif
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. */
390 struct solist_node*
391 split_ordered_list_find(struct split_ordered_list* solist,
392 lispobj key)
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;
400 #else
401 lispobj prod = 2654435769U * key;
402 #endif
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);
411 while (1) {
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
415 return node;
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);