@@ -184,6 +184,13 @@ pagetable_t memory_map;
184
184
// List of marked big objects. Not per-thread. Accessed only by master thread.
185
185
bigval_t * big_objects_marked = NULL ;
186
186
187
+ // Eytzinger tree of images. Used for very fast jl_object_in_image queries during gc
188
+ // See https://algorithmica.org/en/eytzinger
189
+ static arraylist_t eytzinger_image_tree ;
190
+ static arraylist_t eytzinger_idxs ;
191
+ static uintptr_t gc_img_min ;
192
+ static uintptr_t gc_img_max ;
193
+
187
194
// -- Finalization --
188
195
// `ptls->finalizers` and `finalizer_list_marked` might have tagged pointers.
189
196
// If an object pointer has the lowest bit set, the next pointer is an unboxed c function pointer.
@@ -194,6 +201,118 @@ arraylist_t finalizer_list_marked;
194
201
arraylist_t to_finalize ;
195
202
JL_DLLEXPORT _Atomic(int ) jl_gc_have_pending_finalizers = 0 ;
196
203
204
+ static int ptr_cmp (const void * l , const void * r )
205
+ {
206
+ uintptr_t left = * (const uintptr_t * )l ;
207
+ uintptr_t right = * (const uintptr_t * )r ;
208
+ // jl_safe_printf("cmp %p %p\n", (void*)left, (void*)right);
209
+ return (left > right ) - (left < right );
210
+ }
211
+
212
+ // Build an eytzinger tree from a sorted array
213
+ static int eytzinger (uintptr_t * src , uintptr_t * dest , size_t i , size_t k , size_t n )
214
+ {
215
+ if (k <= n ) {
216
+ i = eytzinger (src , dest , i , 2 * k , n );
217
+ dest [k - 1 ] = src [i ];
218
+ i ++ ;
219
+ i = eytzinger (src , dest , i , 2 * k + 1 , n );
220
+ }
221
+ return i ;
222
+ }
223
+
224
+ static size_t eyt_obj_idx (jl_value_t * obj ) JL_NOTSAFEPOINT
225
+ {
226
+ size_t n = eytzinger_image_tree .len - 1 ;
227
+ if (n == 0 )
228
+ return n ;
229
+ assert (n % 2 == 0 && "Eytzinger tree not even length!" );
230
+ uintptr_t cmp = (uintptr_t ) obj ;
231
+ if (cmp <= gc_img_min || cmp > gc_img_max )
232
+ return n ;
233
+ uintptr_t * tree = (uintptr_t * )eytzinger_image_tree .items ;
234
+ size_t k = 1 ;
235
+ // note that k preserves the history of how we got to the current node
236
+ while (k <= n ) {
237
+ int greater = (cmp > tree [k - 1 ]);
238
+ k <<= 1 ;
239
+ k |= greater ;
240
+ }
241
+ // Free to assume k is nonzero, since we start with k = 1
242
+ // and cmp > gc_img_min
243
+ // This shift does a fast revert of the path until we get
244
+ // to a node that evaluated less than cmp.
245
+ k >>= (__builtin_ctzll (k ) + 1 );
246
+ assert (k != 0 );
247
+ assert (k <= n && "Eytzinger tree index out of bounds!" );
248
+ assert (tree [k - 1 ] < cmp && "Failed to find lower bound for object!" );
249
+ return k - 1 ;
250
+ }
251
+
252
+ //used in staticdata.c after we add an image
253
+ void rebuild_image_blob_tree (void )
254
+ {
255
+ size_t inc = 1 + jl_linkage_blobs .len - eytzinger_image_tree .len ;
256
+ assert (eytzinger_idxs .len == eytzinger_image_tree .len );
257
+ assert (eytzinger_idxs .max == eytzinger_image_tree .max );
258
+ arraylist_grow (& eytzinger_idxs , inc );
259
+ arraylist_grow (& eytzinger_image_tree , inc );
260
+ eytzinger_idxs .items [eytzinger_idxs .len - 1 ] = (void * )jl_linkage_blobs .len ;
261
+ eytzinger_image_tree .items [eytzinger_image_tree .len - 1 ] = (void * )1 ; // outside image
262
+ for (size_t i = 0 ; i < jl_linkage_blobs .len ; i ++ ) {
263
+ assert ((uintptr_t ) jl_linkage_blobs .items [i ] % 4 == 0 && "Linkage blob not 4-byte aligned!" );
264
+ // We abuse the pointer here a little so that a couple of properties are true:
265
+ // 1. a start and an end are never the same value. This simplifies the binary search.
266
+ // 2. ends are always after starts. This also simplifies the binary search.
267
+ // We assume that there exist no 0-size blobs, but that's a safe assumption
268
+ // since it means nothing could be there anyways
269
+ uintptr_t val = (uintptr_t ) jl_linkage_blobs .items [i ];
270
+ eytzinger_idxs .items [i ] = (void * )(val + (i & 1 ));
271
+ }
272
+ qsort (eytzinger_idxs .items , eytzinger_idxs .len - 1 , sizeof (void * ), ptr_cmp );
273
+ gc_img_min = (uintptr_t ) eytzinger_idxs .items [0 ];
274
+ gc_img_max = (uintptr_t ) eytzinger_idxs .items [eytzinger_idxs .len - 2 ] + 1 ;
275
+ eytzinger ((uintptr_t * )eytzinger_idxs .items , (uintptr_t * )eytzinger_image_tree .items , 0 , 1 , eytzinger_idxs .len - 1 );
276
+ // Reuse the scratch memory to store the indices
277
+ // Still O(nlogn) because binary search
278
+ for (size_t i = 0 ; i < jl_linkage_blobs .len ; i ++ ) {
279
+ uintptr_t val = (uintptr_t ) jl_linkage_blobs .items [i ];
280
+ // This is the same computation as in the prior for loop
281
+ uintptr_t eyt_val = val + (i & 1 );
282
+ size_t eyt_idx = eyt_obj_idx ((jl_value_t * )(eyt_val + 1 )); assert (eyt_idx < eytzinger_idxs .len - 1 );
283
+ assert (eytzinger_image_tree .items [eyt_idx ] == (void * )eyt_val && "Eytzinger tree failed to find object!" );
284
+ if (i & 1 )
285
+ eytzinger_idxs .items [eyt_idx ] = (void * )n_linkage_blobs ();
286
+ else
287
+ eytzinger_idxs .items [eyt_idx ] = (void * )(i / 2 );
288
+ }
289
+ }
290
+
291
+ static int eyt_obj_in_img (jl_value_t * obj ) JL_NOTSAFEPOINT
292
+ {
293
+ assert ((uintptr_t ) obj % 4 == 0 && "Object not 4-byte aligned!" );
294
+ int idx = eyt_obj_idx (obj );
295
+ // Now we use a tiny trick: tree[idx] & 1 is whether or not tree[idx] is a
296
+ // start (0) or an end (1) of a blob. If it's a start, then the object is
297
+ // in the image, otherwise it is not.
298
+ int in_image = ((uintptr_t )eytzinger_image_tree .items [idx ] & 1 ) == 0 ;
299
+ return in_image ;
300
+ }
301
+
302
+ size_t external_blob_index (jl_value_t * v ) JL_NOTSAFEPOINT
303
+ {
304
+ assert ((uintptr_t ) v % 4 == 0 && "Object not 4-byte aligned!" );
305
+ int eyt_idx = eyt_obj_idx (v );
306
+ // We fill the invalid slots with the length, so we can just return that
307
+ size_t idx = (size_t ) eytzinger_idxs .items [eyt_idx ];
308
+ return idx ;
309
+ }
310
+
311
+ uint8_t jl_object_in_image (jl_value_t * obj ) JL_NOTSAFEPOINT
312
+ {
313
+ return eyt_obj_in_img (obj );
314
+ }
315
+
197
316
NOINLINE uintptr_t gc_get_stack_ptr (void )
198
317
{
199
318
return (uintptr_t )jl_get_frame_addr ();
@@ -2673,7 +2792,7 @@ mark: {
2673
2792
jl_datatype_t * vt = (jl_datatype_t * )tag ;
2674
2793
int foreign_alloc = 0 ;
2675
2794
int update_meta = __likely (!meta_updated && !gc_verifying );
2676
- if (update_meta && jl_object_in_image (new_obj )) {
2795
+ if (update_meta && eyt_obj_in_img (new_obj )) {
2677
2796
foreign_alloc = 1 ;
2678
2797
update_meta = 0 ;
2679
2798
}
@@ -3668,6 +3787,10 @@ void jl_gc_init(void)
3668
3787
3669
3788
arraylist_new (& finalizer_list_marked , 0 );
3670
3789
arraylist_new (& to_finalize , 0 );
3790
+ arraylist_new (& eytzinger_image_tree , 0 );
3791
+ arraylist_new (& eytzinger_idxs , 0 );
3792
+ arraylist_push (& eytzinger_idxs , (void * )0 );
3793
+ arraylist_push (& eytzinger_image_tree , (void * )1 ); // outside image
3671
3794
3672
3795
gc_num .interval = default_collect_interval ;
3673
3796
last_long_collect_interval = default_collect_interval ;
0 commit comments