Fixed some issues in runtime.c when looking up methods. This fixes some issues with DO.

Removed GNU dtable and sparse array implementations, replaced entirely now with versions based on the Étoilé runtime.  Performance is roughly equivalent in microbenchmarks, memory usage is significantly lower (Gorm goes from 95MB to 50MB on my machine - this will be even more pronounced on 64-bit systems), which should improve cache usage considerably.  Still room for some performance tuning, however.
This commit is contained in:
theraven 2010-05-16 20:39:54 +00:00
parent 4be9799259
commit b04cccf46b
13 changed files with 97 additions and 1195 deletions

View File

@ -33,7 +33,6 @@ libobjc_C_FILES = \
objects.c\
protocol.c\
runtime.c\
sarray.c\
sarray2.c\
selector.c\
selector_table.c\
@ -42,7 +41,7 @@ libobjc_C_FILES = \
libobjc_HEADER_FILES_DIR = objc
libobjc_HEADER_FILES_INSTALL_DIR = objc
libobjc_HEADER_FILES = \
#libobjc_HEADER_FILES = \
Availability.h\
NXConstStr.h\
Object.h\

View File

@ -88,7 +88,6 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
structures is freeing data when is removed from the structures. */
#include "objc/runtime-legacy.h" /* the kitchen sink */
#include "objc/sarray.h"
#include "objc/objc.h"
#include "objc/objc-api.h"

View File

@ -1,351 +0,0 @@
/* GNU Objective C Runtime initialization
Copyright (C) 1993, 1995, 1996, 1997, 2002, 2009
Free Software Foundation, Inc.
Contributed by Kresten Krab Thorup
+load support contributed by Ovidiu Predescu <ovidiu@net-community.com>
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
Under Section 7 of GPL version 3, you are granted additional
permissions described in the GCC Runtime Library Exception, version
3.1, as published by the Free Software Foundation.
You should have received a copy of the GNU General Public License and
a copy of the GCC Runtime Library Exception along with this program;
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
<http://www.gnu.org/licenses/>. */
/* The uninstalled dispatch table */
struct sarray *__objc_uninstalled_dtable = 0; /* !T:MUTEX */
static struct sarray *create_dtable_for_class (Class class);
void
__objc_init_dispatch_tables ()
{
INIT_LOCK(initialize_lock);
__objc_uninstalled_dtable = sarray_new (200, 0);
}
/**
* Returns the dtable for a given class. If we are currently in an +initialize
* method then this will block if called from a thread other than the one
* running the +initialize method.
*/
static inline struct sarray *dtable_for_class(Class cls)
{
if (cls->dtable != __objc_uninstalled_dtable)
{
return cls->dtable;
}
LOCK(&initialize_lock);
if (cls->dtable != __objc_uninstalled_dtable)
{
UNLOCK(&initialize_lock);
return cls->dtable;
}
/* This is a linear search, and so, in theory, could be very slow. It is
* O(n) where n is the number of +initialize methods on the stack. In
* practice, this is a very small number. Profiling with GNUstep showed that
* this peaks at 8. */
struct sarray *dtable = __objc_uninstalled_dtable;
InitializingDtable *buffer = temporary_dtables;
while (NULL != buffer)
{
if (buffer->class == cls)
{
dtable = buffer->dtable;
break;
}
buffer = buffer->next;
}
UNLOCK(&initialize_lock);
if (dtable == 0)
dtable = __objc_uninstalled_dtable;
return dtable;
}
/* This function is called by objc_msg_lookup when the
dispatch table needs to be installed; thus it is called once
for each class, namely when the very first message is sent to it. */
static void
__objc_init_install_dtable (id receiver, SEL op __attribute__ ((__unused__)))
{
objc_mutex_lock (__objc_runtime_mutex);
/* This may happen, if the programmer has taken the address of a
method before the dtable was initialized... too bad for him! */
if (dtable_for_class(receiver->class_pointer) != __objc_uninstalled_dtable)
{
objc_mutex_unlock (__objc_runtime_mutex);
return;
}
if (CLS_ISCLASS (receiver->class_pointer))
{
/* receiver is an ordinary object */
assert (CLS_ISCLASS (receiver->class_pointer));
/* call +initialize -- this will in turn install the factory
dispatch table if not already done :-) */
__objc_send_initialize (receiver->class_pointer);
}
else
{
/* receiver is a class object */
assert (CLS_ISCLASS ((Class)receiver));
assert (CLS_ISMETA (receiver->class_pointer));
__objc_send_initialize ((Class)receiver);
}
objc_mutex_unlock (__objc_runtime_mutex);
}
/* Install dummy table for class which causes the first message to
that class (or instances hereof) to be initialized properly */
void
__objc_install_premature_dtable (Class class)
{
assert (__objc_uninstalled_dtable);
class->dtable = __objc_uninstalled_dtable;
}
/* Walk on the methods list of class and install the methods in the reverse
order of the lists. Since methods added by categories are before the methods
of class in the methods list, this allows categories to substitute methods
declared in class. However if more than one category replaces the same
method nothing is guaranteed about what method will be used.
Assumes that __objc_runtime_mutex is locked down. */
static void
__objc_install_methods_in_dtable (Class class, MethodList_t method_list,
struct sarray *dtable)
{
int i;
if (! method_list)
return;
if (method_list->method_next)
__objc_install_methods_in_dtable (class, method_list->method_next, dtable);
for (i = 0; i < method_list->method_count; i++)
{
Method_t method = &(method_list->method_list[i]);
size_t sel_id = (size_t)method->method_name->sel_id;
/* If there is an existing slot with this value, just update it. */
struct objc_slot *slot = sarray_get_safe(dtable, sel_id);
if (NULL != slot && slot->owner == class)
{
slot->method = method->method_imp;
slot->version++;
}
else
{
//NOTE: We can improve this by sharing slots between subclasses where
// the IMPs are the same.
slot = new_slot_for_method_in_class(method, class);
sarray_at_put_safe (dtable, sel_id, slot);
/* Invalidate the superclass's slot, if it has one. */
slot = (NULL != class->super_class) ?
sarray_get_safe(dtable_for_class(class->super_class), sel_id) : NULL;
if (NULL != slot)
{
slot->version++;
}
}
}
}
/** Returns the dispatch table for the given class, but does not install it.
*/
static struct sarray *create_dtable_for_class (Class class)
{
Class super;
struct sarray *dtable = dtable_for_class(class);
if (dtable != __objc_uninstalled_dtable) return dtable;
/* If the class has not yet had its class links resolved, we must
re-compute all class links */
if (! CLS_ISRESOLV (class))
objc_resolve_class(class);
super = class->super_class;
if (super != 0 && (dtable_for_class(super) == __objc_uninstalled_dtable))
__objc_install_dispatch_table_for_class (super);
/* Allocate dtable if necessary */
if (super == 0)
{
dtable = sarray_new (__objc_selector_max_index, 0);
}
else
{
dtable = sarray_lazy_copy (dtable_for_class(super));
}
__objc_install_methods_in_dtable (class, class->methods, dtable);
return dtable;
}
/* Assumes that __objc_runtime_mutex is locked down. */
static void
__objc_install_dispatch_table_for_class(Class class)
{
class->dtable = create_dtable_for_class(class);
}
static void merge_method_list_to_class (Class class,
MethodList_t method_list,
struct sarray *dtable,
struct sarray *super_dtable)
{
// Sometimes we get method lists with no methods in them. This is weird and
// probably caused by someone else writing stupid code, but just ignore it,
// nod, smile, and move on.
if (method_list->method_count == 0)
{
if (method_list->method_next)
{
merge_method_list_to_class(class, method_list->method_next, dtable, super_dtable);
}
return;
}
/*
struct objc_slot *firstslot =
// sarray_get_safe(dtable, (size_t)method_list->method_list[0].method_name->sel_id);
// If we've already got the methods from this method list, we also have all
// of the methods from all of the ones further along the chain, so don't
// bother adding them again.
* FIXME: This doesn't take into account the lazy copying stuff in the sarray.
if (NULL != firstslot &&
firstslot->owner == class &&
firstslot->method == method_list->method_list[0].method_imp)
{
return;
}
*/
// If we haven't already visited this method list, then we might not have
// already visited the one after it either...
if (method_list->method_next)
{
merge_method_list_to_class(class, method_list->method_next, dtable, super_dtable);
}
{
int i;
/* Search the method list. */
for (i = 0; i < method_list->method_count; ++i)
{
Method_t method = &method_list->method_list[i];
size_t sel_id = (size_t)method->method_name->sel_id;
struct objc_slot *slot = sarray_get_safe(dtable, sel_id);
struct objc_slot *superslot = sarray_get_safe(super_dtable, sel_id);
// If there is no existing slot, then just use the superclass's one.
if (NULL == slot)
{
sarray_at_put_safe (dtable, sel_id, superslot);
}
else
{
// If there is, we need to find whether it comes from a subclass of
// the modified class. If it does, then we don't want to override it.
Class owner = slot->owner;
do
{
if (owner == superslot->owner)
{
break;
}
owner = owner->super_class;
} while(NULL != owner);
// This is reached if class is currently inheriting a method from a
// class up the hierarchy and a new method is added to a class
// somewhere in the middle that overrides it.
if (owner != superslot->owner)
{
sarray_at_put_safe (dtable, sel_id, superslot);
slot->version++;
}
}
}
}
}
static void merge_methods_up_from_superclass (Class class, Class super, Class modifedClass,
struct sarray *dtable,
struct sarray *super_dtable)
{
if (super->super_class && super != modifedClass)
{
merge_methods_up_from_superclass(class, super->super_class, modifedClass,
dtable, super_dtable);
}
if (super->methods)
{
merge_method_list_to_class(class, super->methods, dtable, super_dtable);
}
}
static void merge_methods_to_subclasses (Class class, Class modifedClass)
{
struct sarray *super_dtable = dtable_for_class(class);
// Don't merge things into the uninitialised dtable. That would be very bad.
if (super_dtable == __objc_uninstalled_dtable) { return; }
if (class->subclass_list) /* Traverse subclasses */
{
for (Class next = class->subclass_list; next; next = next->sibling_class)
{
struct sarray *dtable = dtable_for_class(next);
// Don't merge things into the uninitialised dtable. That would be very bad.
if (dtable != __objc_uninstalled_dtable)
{
merge_methods_up_from_superclass(next, class, modifedClass, dtable,
super_dtable);
merge_methods_to_subclasses(next, modifedClass);
}
}
}
}
void
__objc_update_dispatch_table_for_class (Class class)
{
/* not yet installed -- skip it */
if (dtable_for_class(class) == __objc_uninstalled_dtable)
return;
objc_mutex_lock (__objc_runtime_mutex);
__objc_install_methods_in_dtable (class, class->methods,
dtable_for_class(class));
// Don't merge things into the uninitialised dtable. That would be very bad.
if (dtable_for_class(class) != __objc_uninstalled_dtable)
{
merge_methods_to_subclasses(class, class);
}
objc_mutex_unlock (__objc_runtime_mutex);
}
void objc_resize_uninstalled_dtable(void)
{
assert(__objc_uninstalled_dtable != NULL);
sarray_realloc (__objc_uninstalled_dtable, __objc_selector_max_index + 1);
}

6
init.c
View File

@ -92,6 +92,8 @@ static void __objc_call_callback (Module_t module);
installed in the runtime. */
static BOOL class_is_subclass_of_class (Class class, Class superclass);
extern void *__objc_uninstalled_dtable;
typedef struct objc_class_tree {
Class class;
struct objc_list *subclasses; /* `head' is pointer to an objc_class_tree */
@ -596,8 +598,8 @@ __objc_exec_class (Module_t module)
__objc_register_selectors_from_class ((Class) class->class_pointer);
/* Install the fake dispatch tables */
__objc_install_premature_dtable (class);
__objc_install_premature_dtable (class->class_pointer);
class->dtable = __objc_uninstalled_dtable;
class->class_pointer->dtable = __objc_uninstalled_dtable;
/* Register the instance methods as class methods, this is
only done for root classes. */

View File

@ -1,243 +0,0 @@
/* Sparse Arrays for Objective C dispatch tables
Copyright (C) 1993, 1995, 1996, 2004, 2009 Free Software Foundation, Inc.
Contributed by Kresten Krab Thorup.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Under Section 7 of GPL version 3, you are granted additional
permissions described in the GCC Runtime Library Exception, version
3.1, as published by the Free Software Foundation.
You should have received a copy of the GNU General Public License and
a copy of the GCC Runtime Library Exception along with this program;
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
<http://www.gnu.org/licenses/>. */
#ifndef __sarray_INCLUDE_GNU
#define __sarray_INCLUDE_GNU
#include "thr.h"
#define OBJC_SPARSE2 /* 2-level sparse array */
/* #define OBJC_SPARSE3 */ /* 3-level sparse array */
#ifdef OBJC_SPARSE2
extern const char* __objc_sparse2_id;
#endif
#ifdef OBJC_SPARSE3
extern const char* __objc_sparse3_id;
#endif
#include <stddef.h>
#include <assert.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
extern int nbuckets; /* for stats */
extern int nindices;
extern int narrays;
extern int idxsize;
/* An unsigned integer of same size as a pointer */
#define SIZET_BITS (sizeof(size_t)*8)
#if defined(__sparc__) || defined(OBJC_SPARSE2)
#define PRECOMPUTE_SELECTORS
#endif
#ifdef OBJC_SPARSE3
/* Buckets are 8 words each */
#define BUCKET_BITS 3
#define BUCKET_SIZE (1<<BUCKET_BITS)
#define BUCKET_MASK (BUCKET_SIZE-1)
/* Indices are 16 words each */
#define INDEX_BITS 4
#define INDEX_SIZE (1<<INDEX_BITS)
#define INDEX_MASK (INDEX_SIZE-1)
#define INDEX_CAPACITY (BUCKET_SIZE*INDEX_SIZE)
#else /* OBJC_SPARSE2 */
/* Buckets are 32 words each */
#define BUCKET_BITS 5
#define BUCKET_SIZE (1<<BUCKET_BITS)
#define BUCKET_MASK (BUCKET_SIZE-1)
#endif /* OBJC_SPARSE2 */
typedef size_t sidx;
#ifdef PRECOMPUTE_SELECTORS
struct soffset {
#ifdef OBJC_SPARSE3
unsigned int unused : SIZET_BITS/4;
unsigned int eoffset : SIZET_BITS/4;
unsigned int boffset : SIZET_BITS/4;
unsigned int ioffset : SIZET_BITS/4;
#else /* OBJC_SPARSE2 */
#ifdef __sparc__
unsigned long boffset : (SIZET_BITS - 2) - BUCKET_BITS;
unsigned int eoffset : BUCKET_BITS;
unsigned int unused : 2;
#else
unsigned int boffset : SIZET_BITS/2;
unsigned int eoffset : SIZET_BITS/2;
#endif
#endif /* OBJC_SPARSE2 */
};
union sofftype {
struct soffset off;
sidx idx;
};
#endif /* not PRECOMPUTE_SELECTORS */
union sversion {
int version;
void *next_free;
};
struct sbucket {
void* elems[BUCKET_SIZE]; /* elements stored in array */
union sversion version; /* used for copy-on-write */
};
#ifdef OBJC_SPARSE3
struct sindex {
struct sbucket* buckets[INDEX_SIZE];
union sversion version; /* used for copy-on-write */
};
#endif /* OBJC_SPARSE3 */
struct sarray {
#ifdef OBJC_SPARSE3
struct sindex** indices;
struct sindex* empty_index;
#else /* OBJC_SPARSE2 */
struct sbucket** buckets;
#endif /* OBJC_SPARSE2 */
struct sbucket* empty_bucket;
union sversion version; /* used for copy-on-write */
short ref_count;
struct sarray* is_copy_of;
size_t capacity;
};
struct sarray* sarray_new(int, void* default_element);
void sarray_free(struct sarray*);
struct sarray* sarray_lazy_copy(struct sarray*);
void sarray_realloc(struct sarray*, int new_size);
void sarray_at_put(struct sarray*, sidx indx, void* elem);
void sarray_at_put_safe(struct sarray*, sidx indx, void* elem);
struct sarray* sarray_hard_copy(struct sarray*); /* ... like the name? */
void sarray_remove_garbage(void);
#ifdef PRECOMPUTE_SELECTORS
/* Transform soffset values to ints and vica verca */
static inline unsigned int
soffset_decode(sidx indx)
{
union sofftype x;
x.idx = indx;
#ifdef OBJC_SPARSE3
return x.off.eoffset
+ (x.off.boffset*BUCKET_SIZE)
+ (x.off.ioffset*INDEX_CAPACITY);
#else /* OBJC_SPARSE2 */
return x.off.eoffset + (x.off.boffset*BUCKET_SIZE);
#endif /* OBJC_SPARSE2 */
}
static inline sidx
soffset_encode(size_t offset)
{
union sofftype x;
x.off.eoffset = offset%BUCKET_SIZE;
#ifdef OBJC_SPARSE3
x.off.boffset = (offset/BUCKET_SIZE)%INDEX_SIZE;
x.off.ioffset = offset/INDEX_CAPACITY;
#else /* OBJC_SPARSE2 */
x.off.boffset = offset/BUCKET_SIZE;
#endif
return (sidx)x.idx;
}
#else /* not PRECOMPUTE_SELECTORS */
static inline size_t
soffset_decode(sidx indx)
{
return indx;
}
static inline sidx
soffset_encode(size_t offset)
{
return offset;
}
#endif /* not PRECOMPUTE_SELECTORS */
/* Get element from the Sparse array `array' at offset `indx' */
static inline void* sarray_get(struct sarray* array, sidx indx)
{
#ifdef PRECOMPUTE_SELECTORS
union sofftype x;
x.idx = indx;
#ifdef OBJC_SPARSE3
return
array->
indices[x.off.ioffset]->
buckets[x.off.boffset]->
elems[x.off.eoffset];
#else /* OBJC_SPARSE2 */
return array->buckets[x.off.boffset]->elems[x.off.eoffset];
#endif /* OBJC_SPARSE2 */
#else /* not PRECOMPUTE_SELECTORS */
#ifdef OBJC_SPARSE3
return array->
indices[indx/INDEX_CAPACITY]->
buckets[(indx/BUCKET_SIZE)%INDEX_SIZE]->
elems[indx%BUCKET_SIZE];
#else /* OBJC_SPARSE2 */
return array->buckets[indx/BUCKET_SIZE]->elems[indx%BUCKET_SIZE];
#endif /* not OBJC_SPARSE3 */
#endif /* not PRECOMPUTE_SELECTORS */
}
static inline void* sarray_get_safe(struct sarray* array, sidx indx)
{
if(soffset_decode(indx) < array->capacity)
return sarray_get(array, indx);
else
return (array->empty_bucket->elems[0]);
}
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __sarray_INCLUDE_GNU */

View File

@ -84,25 +84,15 @@ extern objc_mutex_t __objc_runtime_mutex;
*/
static Method class_getInstanceMethodNonrecursive(Class aClass, SEL aSelector)
{
const char *name = sel_get_name(aSelector);
const char *types = sel_get_type(aSelector);
for (struct objc_method_list *methods = aClass->methods;
methods != NULL ; methods = methods->method_next)
{
for (int i=0 ; i<methods->method_count ; i++)
{
Method_t method = &methods->method_list[i];
if (strcmp(sel_get_name(method->method_name), name) == 0)
if (method->method_name->sel_id == aSelector->sel_id)
{
if (NULL == types ||
strcmp(types, method->method_types) == 0)
{
return method;
}
// Return NULL if the method exists with this name but has the
// wrong types
return NULL;
return method;
}
}
}

517
sarray.c
View File

@ -1,517 +0,0 @@
/* Sparse Arrays for Objective C dispatch tables
Copyright (C) 1993, 1995, 1996, 2002, 2004, 2009 Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Under Section 7 of GPL version 3, you are granted additional
permissions described in the GCC Runtime Library Exception, version
3.1, as published by the Free Software Foundation.
You should have received a copy of the GNU General Public License and
a copy of the GCC Runtime Library Exception along with this program;
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
<http://www.gnu.org/licenses/>. */
#include "objc/sarray.h"
#include "objc/runtime-legacy.h"
#include <stdio.h>
#include "assert.h"
int nbuckets = 0; /* !T:MUTEX */
int nindices = 0; /* !T:MUTEX */
int narrays = 0; /* !T:MUTEX */
int idxsize = 0; /* !T:MUTEX */
static void *first_free_data = NULL; /* !T:MUTEX */
#ifdef OBJC_SPARSE2
const char *__objc_sparse2_id = "2 level sparse indices";
#endif
#ifdef OBJC_SPARSE3
const char *__objc_sparse3_id = "3 level sparse indices";
#endif
/* This function removes any structures left over from free operations
that were not safe in a multi-threaded environment. */
void
sarray_remove_garbage (void)
{
void **vp;
void *np;
objc_mutex_lock (__objc_runtime_mutex);
vp = first_free_data;
first_free_data = NULL;
while (vp) {
np = *vp;
objc_free (vp);
vp = np;
}
objc_mutex_unlock (__objc_runtime_mutex);
}
/* Free a block of dynamically allocated memory. If we are in multi-threaded
mode, it is ok to free it. If not, we add it to the garbage heap to be
freed later. */
static void
sarray_free_garbage (void *vp)
{
objc_mutex_lock (__objc_runtime_mutex);
if (__objc_runtime_threads_alive == 1) {
objc_free (vp);
if (first_free_data)
sarray_remove_garbage ();
}
else {
*(void **)vp = first_free_data;
first_free_data = vp;
}
objc_mutex_unlock (__objc_runtime_mutex);
}
/* sarray_at_put : copies data in such a way as to be thread reader safe. */
void
sarray_at_put (struct sarray *array, sidx index, void *element)
{
#ifdef OBJC_SPARSE3
struct sindex **the_index;
struct sindex *new_index;
#endif
struct sbucket **the_bucket;
struct sbucket *new_bucket;
#ifdef OBJC_SPARSE3
size_t ioffset;
#endif
size_t boffset;
size_t eoffset;
#ifdef PRECOMPUTE_SELECTORS
union sofftype xx;
xx.idx = index;
#ifdef OBJC_SPARSE3
ioffset = xx.off.ioffset;
#endif
boffset = xx.off.boffset;
eoffset = xx.off.eoffset;
#else /* not PRECOMPUTE_SELECTORS */
#ifdef OBJC_SPARSE3
ioffset = index/INDEX_CAPACITY;
boffset = (index/BUCKET_SIZE)%INDEX_SIZE;
eoffset = index%BUCKET_SIZE;
#else
boffset = index/BUCKET_SIZE;
eoffset = index%BUCKET_SIZE;
#endif
#endif /* not PRECOMPUTE_SELECTORS */
assert (soffset_decode (index) < array->capacity); /* Range check */
#ifdef OBJC_SPARSE3
the_index = &(array->indices[ioffset]);
the_bucket = &((*the_index)->buckets[boffset]);
#else
the_bucket = &(array->buckets[boffset]);
#endif
if ((*the_bucket)->elems[eoffset] == element)
return; /* great! we just avoided a lazy copy */
#ifdef OBJC_SPARSE3
/* First, perform lazy copy/allocation of index if needed */
if ((*the_index) == array->empty_index) {
/* The index was previously empty, allocate a new */
new_index = (struct sindex *) objc_malloc (sizeof (struct sindex));
memcpy (new_index, array->empty_index, sizeof (struct sindex));
new_index->version.version = array->version.version;
*the_index = new_index; /* Prepared for install. */
the_bucket = &((*the_index)->buckets[boffset]);
nindices += 1;
} else if ((*the_index)->version.version != array->version.version) {
/* This index must be lazy copied */
struct sindex *old_index = *the_index;
new_index = (struct sindex *) objc_malloc (sizeof (struct sindex));
memcpy (new_index, old_index, sizeof (struct sindex));
new_index->version.version = array->version.version;
*the_index = new_index; /* Prepared for install. */
the_bucket = &((*the_index)->buckets[boffset]);
nindices += 1;
}
#endif /* OBJC_SPARSE3 */
/* next, perform lazy allocation/copy of the bucket if needed */
if ((*the_bucket) == array->empty_bucket) {
/* The bucket was previously empty (or something like that), */
/* allocate a new. This is the effect of `lazy' allocation */
new_bucket = (struct sbucket *) objc_malloc (sizeof (struct sbucket));
memcpy ((void *) new_bucket, (const void *) array->empty_bucket,
sizeof (struct sbucket));
new_bucket->version.version = array->version.version;
*the_bucket = new_bucket; /* Prepared for install. */
nbuckets += 1;
} else if ((*the_bucket)->version.version != array->version.version) {
/* Perform lazy copy. */
struct sbucket *old_bucket = *the_bucket;
new_bucket = (struct sbucket *) objc_malloc (sizeof (struct sbucket));
memcpy (new_bucket, old_bucket, sizeof (struct sbucket));
new_bucket->version.version = array->version.version;
*the_bucket = new_bucket; /* Prepared for install. */
nbuckets += 1;
}
(*the_bucket)->elems[eoffset] = element;
}
void
sarray_at_put_safe (struct sarray *array, sidx index, void *element)
{
if (soffset_decode (index) >= array->capacity)
sarray_realloc (array, soffset_decode (index) + 1);
sarray_at_put (array, index, element);
}
struct sarray *
sarray_new (int size, void *default_element)
{
struct sarray *arr;
#ifdef OBJC_SPARSE3
size_t num_indices = ((size - 1)/(INDEX_CAPACITY)) + 1;
struct sindex **new_indices;
#else /* OBJC_SPARSE2 */
size_t num_indices = ((size - 1)/BUCKET_SIZE) + 1;
struct sbucket **new_buckets;
#endif
size_t counter;
assert (size > 0);
/* Allocate core array */
arr = (struct sarray *) objc_malloc (sizeof (struct sarray));
arr->version.version = 0;
/* Initialize members */
#ifdef OBJC_SPARSE3
arr->capacity = num_indices*INDEX_CAPACITY;
new_indices = (struct sindex **)
objc_malloc (sizeof (struct sindex *) * num_indices);
arr->empty_index = (struct sindex *) objc_malloc (sizeof (struct sindex));
arr->empty_index->version.version = 0;
narrays += 1;
idxsize += num_indices;
nindices += 1;
#else /* OBJC_SPARSE2 */
arr->capacity = num_indices*BUCKET_SIZE;
new_buckets = (struct sbucket **)
objc_malloc (sizeof (struct sbucket *) * num_indices);
narrays += 1;
idxsize += num_indices;
#endif
arr->empty_bucket = (struct sbucket *) objc_malloc (sizeof (struct sbucket));
arr->empty_bucket->version.version = 0;
nbuckets += 1;
arr->ref_count = 1;
arr->is_copy_of = (struct sarray *) 0;
for (counter = 0; counter < BUCKET_SIZE; counter++)
arr->empty_bucket->elems[counter] = default_element;
#ifdef OBJC_SPARSE3
for (counter = 0; counter < INDEX_SIZE; counter++)
arr->empty_index->buckets[counter] = arr->empty_bucket;
for (counter = 0; counter < num_indices; counter++)
new_indices[counter] = arr->empty_index;
#else /* OBJC_SPARSE2 */
for (counter = 0; counter < num_indices; counter++)
new_buckets[counter] = arr->empty_bucket;
#endif
#ifdef OBJC_SPARSE3
arr->indices = new_indices;
#else /* OBJC_SPARSE2 */
arr->buckets = new_buckets;
#endif
return arr;
}
/* Reallocate the sparse array to hold `newsize' entries
Note: We really allocate and then free. We have to do this to ensure that
any concurrent readers notice the update. */
void
sarray_realloc (struct sarray *array, int newsize)
{
#ifdef OBJC_SPARSE3
size_t old_max_index = (array->capacity - 1)/INDEX_CAPACITY;
size_t new_max_index = ((newsize - 1)/INDEX_CAPACITY);
size_t rounded_size = (new_max_index + 1) * INDEX_CAPACITY;
struct sindex **new_indices;
struct sindex **old_indices;
#else /* OBJC_SPARSE2 */
size_t old_max_index = (array->capacity - 1)/BUCKET_SIZE;
size_t new_max_index = ((newsize - 1)/BUCKET_SIZE);
size_t rounded_size = (new_max_index + 1) * BUCKET_SIZE;
struct sbucket **new_buckets;
struct sbucket **old_buckets;
#endif
size_t counter;
assert (newsize > 0);
/* The size is the same, just ignore the request */
if (rounded_size <= array->capacity)
return;
// assert (array->ref_count == 1); /* stop if lazy copied... */
/* We are asked to extend the array -- allocate new bucket table, */
/* and insert empty_bucket in newly allocated places. */
if (rounded_size > array->capacity)
{
#ifdef OBJC_SPARSE3
new_max_index += 4;
rounded_size = (new_max_index + 1) * INDEX_CAPACITY;
#else /* OBJC_SPARSE2 */
new_max_index += 4;
rounded_size = (new_max_index + 1) * BUCKET_SIZE;
#endif
/* update capacity */
array->capacity = rounded_size;
#ifdef OBJC_SPARSE3
/* alloc to force re-read by any concurrent readers. */
old_indices = array->indices;
new_indices = (struct sindex **)
objc_malloc ((new_max_index + 1) * sizeof (struct sindex *));
#else /* OBJC_SPARSE2 */
old_buckets = array->buckets;
new_buckets = (struct sbucket **)
objc_malloc ((new_max_index + 1) * sizeof (struct sbucket *));
#endif
/* copy buckets below old_max_index (they are still valid) */
for (counter = 0; counter <= old_max_index; counter++ ) {
#ifdef OBJC_SPARSE3
new_indices[counter] = old_indices[counter];
#else /* OBJC_SPARSE2 */
new_buckets[counter] = old_buckets[counter];
#endif
}
#ifdef OBJC_SPARSE3
/* reset entries above old_max_index to empty_bucket */
for (counter = old_max_index + 1; counter <= new_max_index; counter++)
new_indices[counter] = array->empty_index;
#else /* OBJC_SPARSE2 */
/* reset entries above old_max_index to empty_bucket */
for (counter = old_max_index + 1; counter <= new_max_index; counter++)
new_buckets[counter] = array->empty_bucket;
#endif
#ifdef OBJC_SPARSE3
/* install the new indices */
array->indices = new_indices;
#else /* OBJC_SPARSE2 */
array->buckets = new_buckets;
#endif
#ifdef OBJC_SPARSE3
/* free the old indices */
sarray_free_garbage (old_indices);
#else /* OBJC_SPARSE2 */
sarray_free_garbage (old_buckets);
#endif
idxsize += (new_max_index-old_max_index);
return;
}
}
/* Free a sparse array allocated with sarray_new */
void
sarray_free (struct sarray *array) {
#ifdef OBJC_SPARSE3
size_t old_max_index = (array->capacity - 1)/INDEX_CAPACITY;
struct sindex **old_indices;
#else
size_t old_max_index = (array->capacity - 1)/BUCKET_SIZE;
struct sbucket **old_buckets;
#endif
size_t counter = 0;
assert (array->ref_count != 0); /* Freed multiple times!!! */
if (--(array->ref_count) != 0) /* There exists copies of me */
return;
#ifdef OBJC_SPARSE3
old_indices = array->indices;
#else
old_buckets = array->buckets;
#endif
/* Free all entries that do not point to empty_bucket */
for (counter = 0; counter <= old_max_index; counter++ ) {
#ifdef OBJC_SPARSE3
struct sindex *idx = old_indices[counter];
if ((idx != array->empty_index) &&
(idx->version.version == array->version.version)) {
int c2;
for (c2 = 0; c2 < INDEX_SIZE; c2++) {
struct sbucket *bkt = idx->buckets[c2];
if ((bkt != array->empty_bucket) &&
(bkt->version.version == array->version.version))
{
sarray_free_garbage (bkt);
nbuckets -= 1;
}
}
sarray_free_garbage (idx);
nindices -= 1;
}
#else /* OBJC_SPARSE2 */
struct sbucket *bkt = array->buckets[counter];
if ((bkt != array->empty_bucket) &&
(bkt->version.version == array->version.version))
{
sarray_free_garbage (bkt);
nbuckets -= 1;
}
#endif
}
#ifdef OBJC_SPARSE3
/* free empty_index */
if (array->empty_index->version.version == array->version.version) {
sarray_free_garbage (array->empty_index);
nindices -= 1;
}
#endif
/* free empty_bucket */
if (array->empty_bucket->version.version == array->version.version) {
sarray_free_garbage (array->empty_bucket);
nbuckets -= 1;
}
idxsize -= (old_max_index + 1);
narrays -= 1;
#ifdef OBJC_SPARSE3
/* free bucket table */
sarray_free_garbage (array->indices);
#else
/* free bucket table */
sarray_free_garbage (array->buckets);
#endif
/* If this is a copy of another array, we free it (which might just
* decrement its reference count so it will be freed when no longer in use).
*/
if (array->is_copy_of)
sarray_free (array->is_copy_of);
/* free array */
sarray_free_garbage (array);
}
/* This is a lazy copy. Only the core of the structure is actually */
/* copied. */
struct sarray *
sarray_lazy_copy (struct sarray *oarr)
{
struct sarray *arr;
#ifdef OBJC_SPARSE3
size_t num_indices = ((oarr->capacity - 1)/INDEX_CAPACITY) + 1;
struct sindex **new_indices;
#else /* OBJC_SPARSE2 */
size_t num_indices = ((oarr->capacity - 1)/BUCKET_SIZE) + 1;
struct sbucket **new_buckets;
#endif
/* Allocate core array */
arr = (struct sarray *) objc_malloc (sizeof (struct sarray)); /* !!! */
arr->version.version = oarr->version.version + 1;
#ifdef OBJC_SPARSE3
arr->empty_index = oarr->empty_index;
#endif
arr->empty_bucket = oarr->empty_bucket;
arr->ref_count = 1;
oarr->ref_count += 1;
arr->is_copy_of = oarr;
arr->capacity = oarr->capacity;
#ifdef OBJC_SPARSE3
/* Copy bucket table */
new_indices = (struct sindex **)
objc_malloc (sizeof (struct sindex *) * num_indices);
memcpy (new_indices, oarr->indices, sizeof (struct sindex *) * num_indices);
arr->indices = new_indices;
#else
/* Copy bucket table */
new_buckets = (struct sbucket **)
objc_malloc (sizeof (struct sbucket *) * num_indices);
memcpy (new_buckets, oarr->buckets, sizeof (struct sbucket *) * num_indices);
arr->buckets = new_buckets;
#endif
idxsize += num_indices;
narrays += 1;
return arr;
}

View File

@ -68,33 +68,32 @@ SparseArray *SparseArrayExpandingArray(SparseArray *sarray)
return new;
}
void * SparseArrayNext(SparseArray * sarray, uint32_t * index)
static void *SparseArrayFind(SparseArray * sarray, uint32_t * index)
{
uint32_t j = MASK_INDEX((*index));
uint32_t max = MAX_INDEX(sarray);
if(sarray->shift == 0)
if (sarray->shift == 0)
{
while(j<max)
while (j<max)
{
(*index)++;
if(sarray->data[j] != SARRAY_EMPTY)
if (sarray->data[j] != SARRAY_EMPTY)
{
return sarray->data[j];
}
(*index)++;
j++;
}
}
else while(j<max)
else while (j<max)
{
uint32_t zeromask = ~(sarray->mask >> base_shift);
while(j<max)
while (j<max)
{
//Look in child nodes
if(sarray->data[j] != SARRAY_EMPTY)
if (sarray->data[j] != SARRAY_EMPTY)
{
void * ret = SparseArrayNext(sarray->data[j], index);
if(ret != SARRAY_EMPTY)
void * ret = SparseArrayFind(sarray->data[j], index);
if (ret != SARRAY_EMPTY)
{
return ret;
}
@ -110,6 +109,12 @@ void * SparseArrayNext(SparseArray * sarray, uint32_t * index)
return SARRAY_EMPTY;
}
void *SparseArrayNext(SparseArray * sarray, uint32_t * idx)
{
(*idx)++;
return SparseArrayFind(sarray, idx);
}
void SparseArrayInsert(SparseArray * sarray, uint32_t index, void *value)
{
fprintf(stderr, "Inserting in : %p\n", sarray);

View File

@ -67,7 +67,7 @@ static inline void* SparseArrayLookup(SparseArray * sarray, uint32_t index)
((SparseArray*)((SparseArray*)((SparseArray*)
sarray->data[(i & 0xff000000)>>24])->
data[(i & 0xff0000)>>16])->
data[(i & 0xff00)>>8])->data[(i & 0xff)];
data[(i & 0xff00)>>8])->data[(i & 0xff)];
}
/*
while(sarray->shift > 0)

View File

@ -24,7 +24,6 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
#include "objc/runtime-legacy.h"
#include "objc/sarray.h"
#include "objc/encoding.h"
/* Register instance methods as class methods for root classes */

View File

@ -33,7 +33,7 @@
* registered, its name field is replaced with its index in the selector_list
* array.
*/
uint32_t __objc_selector_max_index;
static uint32_t selector_count = 1;
/**
* Mapping from selector numbers to selector names.
*/
@ -44,7 +44,7 @@ static SparseArray *selector_list = NULL;
inline static BOOL isSelRegistered(SEL sel)
{
if ((uintptr_t)sel->name < (uintptr_t)__objc_selector_max_index)
if ((uintptr_t)sel->name < (uintptr_t)selector_count)
{
return YES;
}
@ -112,9 +112,10 @@ mutex_t selector_table_lock;
/**
* Hack to make the uninstalled dtable the right size. Won't be needed with sarray2.
* Resizes the dtables to ensure that they can store as many selectors as
* exist.
*/
void objc_resize_uninstalled_dtable(void);
void objc_resize_dtables(uint32_t);
/**
* Create data structures to store selectors.
@ -149,11 +150,11 @@ static inline void add_selector_to_table(SEL aSel, int32_t uid, uint32_t idx)
*/
static inline void register_selector_locked(SEL aSel)
{
uintptr_t idx = __objc_selector_max_index++;
uintptr_t idx = selector_count++;
if (NULL == aSel->types)
{
add_selector_to_table(aSel, idx, idx);
objc_resize_uninstalled_dtable();
objc_resize_dtables(selector_count);
return;
}
SEL untyped = selector_lookup(aSel->name, 0);
@ -166,7 +167,7 @@ static inline void register_selector_locked(SEL aSel)
add_selector_to_table(untyped, idx, idx);
// If we are in type dependent dispatch mode, the uid for the typed
// and untyped versions will be different
idx++; __objc_selector_max_index++;
idx++; selector_count++;
}
uintptr_t uid = (uintptr_t)untyped->name;
TDD(uid = idx);
@ -182,7 +183,7 @@ static inline void register_selector_locked(SEL aSel)
typeList->value = aSel->types;
typeList->next = typeListHead->next;
typeListHead->next = typeList;
objc_resize_uninstalled_dtable();
objc_resize_dtables(selector_count);
}
/**
* Registers a selector. This assumes that the argument is never deallocated.
@ -326,7 +327,9 @@ void __objc_register_selectors_from_class(Class class)
}
void __objc_register_selector_array(SEL selectors, unsigned long count)
{
for (unsigned long i=0 ; (i<count) && (NULL != selectors[i].name) ; i++)
// GCC is broken and always sets the count to 0, so we ignore count until
// we can throw stupid and buggy compilers in the bin.
for (unsigned long i=0 ; (NULL != selectors[i].name) ; i++)
{
objc_register_selector(&selectors[i]);
}
@ -346,6 +349,8 @@ void __objc_register_instance_methods_to_class (Class class);
SEL sel_get_typed_uid (const char *name, const char *types)
{
SEL sel = selector_lookup(name, types);
if (NULL == sel) { return sel_registerTypedName_np(name, types); }
struct sel_type_list *l =
SparseArrayLookup(selector_list, (uint32_t)(uintptr_t)sel->name);
// Skip the head, which just contains the name, not the types.
@ -359,7 +364,18 @@ SEL sel_get_typed_uid (const char *name, const char *types)
SEL sel_get_any_typed_uid (const char *name)
{
return selector_lookup(name, 0);
SEL sel = selector_lookup(name, 0);
if (NULL == sel) { return sel_registerName(name); }
struct sel_type_list *l =
SparseArrayLookup(selector_list, (uint32_t)(uintptr_t)sel->name);
// Skip the head, which just contains the name, not the types.
l = l->next;
if (NULL != l)
{
sel = selector_lookup(name, l->value);
}
return sel;
}
SEL sel_get_any_uid (const char *name)
@ -405,13 +421,14 @@ static void logSelector(SEL sel)
{
fprintf(stderr, "%s = {%p, %s}\n", sel_getName(sel), sel->name, sel_getType_np(sel));
}
void objc_resize_uninstalled_dtable(void) {}
void objc_resize_dtables(uint32_t ignored) {}
int main(void)
{
__objc_init_selector_tables();
SEL a = sel_registerTypedName_np("foo:", "1234");
logSelector(a);
a = sel_registerName("bar:");
a = sel_registerName("foo:");
logSelector(a);
logSelector(sel_get_any_typed_uid("foo:"));
@ -427,6 +444,12 @@ int main(void)
{
fprintf(stderr, "Found type %s\n", types[i]);
}
uint32_t idx=0;
struct sel_type_list *type;
while ((type= SparseArrayNext(selector_list, &idx)))
{
fprintf(stderr, "Idx: %d, sel: %s (%s)\n", idx, type->value, ((struct sel_type_list *)SparseArrayLookup(selector_list, idx))->value);
}
fprintf(stderr, "Number of types: %d\n", count);
SEL sel;
}

View File

@ -27,7 +27,6 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
#include <stdlib.h>
#include "objc/runtime-legacy.h"
#include "objc/slot.h"
#include "objc/sarray.h"
#include "objc/encoding.h"
#include "lock.h"
#include "slot_pool.h"
@ -37,6 +36,8 @@ static mutex_t initialize_lock;
void objc_resolve_class(Class);
#define sidx uint32_t
/* Two hooks for method forwarding. If either is set, it is invoked
* to return a function that performs the real forwarding. If both
* are set, the result of __objc_msg_forward2 will be preferred over
@ -52,18 +53,8 @@ static void __objc_send_initialize (Class);
static void __objc_install_dispatch_table_for_class (Class);
typedef struct _InitializingDtable
{
Class class;
void *dtable;
struct _InitializingDtable *next;
} InitializingDtable;
/** Protected by initialize_lock */
InitializingDtable *temporary_dtables;
#include "dtable_legacy.c"
//#include "dtable.c"
#include "dtable.c"
/* Forward declare some functions */
static void __objc_init_install_dtable (id, SEL);
@ -365,6 +356,41 @@ __objc_send_initialize (Class class)
}
}
/* This function is called by objc_msg_lookup when the
dispatch table needs to be installed; thus it is called once
for each class, namely when the very first message is sent to it. */
static void
__objc_init_install_dtable (id receiver, SEL op __attribute__ ((__unused__)))
{
objc_mutex_lock (__objc_runtime_mutex);
/* This may happen, if the programmer has taken the address of a
method before the dtable was initialized... too bad for him! */
if (dtable_for_class(receiver->class_pointer) != __objc_uninstalled_dtable)
{
objc_mutex_unlock (__objc_runtime_mutex);
return;
}
if (CLS_ISCLASS (receiver->class_pointer))
{
/* receiver is an ordinary object */
assert (CLS_ISCLASS (receiver->class_pointer));
/* call +initialize -- this will in turn install the factory
dispatch table if not already done :-) */
__objc_send_initialize (receiver->class_pointer);
}
else
{
/* receiver is a class object */
assert (CLS_ISCLASS ((Class)receiver));
assert (CLS_ISMETA (receiver->class_pointer));
__objc_send_initialize ((Class)receiver);
}
objc_mutex_unlock (__objc_runtime_mutex);
}
/* This function adds a method list to a class. This function is
typically called by another function specific to the run-time. As
such this function does not worry about thread safe issues.
@ -458,36 +484,6 @@ search_for_method_in_list (MethodList_t list, SEL op)
return NULL;
}
void
__objc_print_dtable_stats ()
{
int total = 0;
objc_mutex_lock (__objc_runtime_mutex);
#ifdef OBJC_SPARSE2
printf ("memory usage: (%s)\n", "2-level sparse arrays");
#else
printf ("memory usage: (%s)\n", "3-level sparse arrays");
#endif
printf ("arrays: %d = %ld bytes\n", narrays,
(long) ((size_t) narrays * sizeof (struct sarray)));
total += narrays * sizeof (struct sarray);
printf ("buckets: %d = %ld bytes\n", nbuckets,
(long) ((size_t) nbuckets * sizeof (struct sbucket)));
total += nbuckets * sizeof (struct sbucket);
printf ("idxtables: %d = %ld bytes\n",
idxsize, (long) ((size_t) idxsize * sizeof (void *)));
total += idxsize * sizeof (void *);
printf ("-----------------------------------\n");
printf ("total: %d bytes\n", total);
printf ("===================================\n");
objc_mutex_unlock (__objc_runtime_mutex);
}
/* Returns the uninstalled dispatch table indicator.
If a class' dispatch table points to __objc_uninstalled_dtable
then that means it needs its dispatch table to be installed. */

View File

@ -117,7 +117,7 @@ Slot_t objc_slot_lookup_super(Super_t super, SEL selector)
if (receiver)
{
Class class = super->class;
Slot_t result = sarray_get_safe(class->dtable, (sidx)selector->sel_id);
Slot_t result = sarray_get_safe(dtable_for_class(class), (sidx)selector->sel_id);
if (0 == result)
{
// Dtable should always be installed in the superclass