Added a fast path for ARC. Now, if a class implements ARC-compatible retain / release / autorelease methods, we don't call them at all. Instead, we inline them in the ARC accessors. This avoids all of the overhead of the message send (lookup and call) and should make ARC quite a bit faster than manual reference counting.

main
theraven 15 years ago
parent 6a37a8c3bc
commit 34f7baf8d7

@ -5,10 +5,6 @@
#include <string.h>
@implementation Protocol
+ (void)load
{
objc_clear_class_flag(self, objc_class_flag_plane_aware);
}
// FIXME: This needs removing, but it's included for now because GNUstep's
// implementation of +[NSObject conformsToProtocol:] calls it.
- (BOOL)conformsTo: (Protocol*)p
@ -23,12 +19,6 @@
+ (Class)class { return self; }
- (id)self { return self; }
@end
@implementation Protocol2
+ (void)load
{
objc_clear_class_flag(self, objc_class_flag_plane_aware);
}
@end
/**
* This class exists for the sole reason that the legacy GNU ABI did not

60
arc.m

@ -2,6 +2,7 @@
#import "objc/runtime.h"
#import "objc/blocks_runtime.h"
#import "nsobject.h"
#import "class.h"
#import "selector.h"
#import "visibility.h"
#import "objc/hooks.h"
@ -24,6 +25,46 @@ static IMP NewAutoreleasePool;
static IMP DeleteAutoreleasePool;
static IMP AutoreleaseAdd;
extern BOOL FastARCRetain;
extern BOOL FastARCRelease;
extern BOOL FastARCAutorelease;
static inline id retain(id obj)
{
if (objc_test_class_flag(obj->isa, objc_class_flag_fast_arc))
{
intptr_t *refCount = ((intptr_t*)obj) - 1;
__sync_fetch_and_add(refCount, 1);
return obj;
}
return [obj retain];
}
static inline void release(id obj)
{
if (objc_test_class_flag(obj->isa, objc_class_flag_fast_arc))
{
intptr_t *refCount = ((intptr_t*)obj) - 1;
if (__sync_fetch_and_sub(refCount, 1) < 0)
{
objc_delete_weak_refs(obj);
[obj dealloc];
}
return;
}
[obj release];
}
static inline id autorelease(id obj)
{
if (objc_test_class_flag(obj->isa, objc_class_flag_fast_arc))
{
AutoreleaseAdd(AutoreleasePool, SELECTOR(addObject:), obj);
return obj;
}
return [obj autorelease];
}
void *objc_autoreleasePoolPush(void)
{
@ -54,10 +95,9 @@ void objc_autoreleasePoolPop(void *pool)
id objc_autorelease(id obj)
{
return [obj autorelease];
if (nil != obj)
{
AutoreleaseAdd(AutoreleasePool, SELECTOR(addObject:), obj);
obj = autorelease(obj);
}
return obj;
}
@ -100,7 +140,8 @@ id objc_retainAutoreleasedReturnValue(id obj)
id objc_retain(id obj)
{
return [obj retain];
if (nil == obj) { return nil; }
return retain(obj);
}
id objc_retainAutorelease(id obj)
@ -121,15 +162,16 @@ id objc_retainBlock(id b)
void objc_release(id obj)
{
[obj release];
if (nil == obj) { return; }
release(obj);
}
id objc_storeStrong(id *object, id value)
id objc_storeStrong(id *addr, id value)
{
value = [value retain];
id oldValue = *object;
*object = value;
[oldValue release];
value = objc_retain(value);
id oldValue = *addr;
*addr = value;
objc_release(oldValue);
return value;
}

@ -168,12 +168,10 @@ enum objc_class_flags
*/
objc_class_flag_user_created = (1<<5),
/**
* Instances of this class have a reference count and plane ID prepended to
* them. The default for this is set for classes, unset for metaclasses.
* It should be cleared by protocols, constant strings, and objects not
* allocated by NSAllocateObject().
* Instances of this class are provide ARC-safe retain / release /
* autorelease implementations.
*/
objc_class_flag_plane_aware = (1<<6),
objc_class_flag_fast_arc = (1<<6),
/**
* This class is a hidden class (should not be registered in the class
* table nor returned from object_getClass()).
@ -185,16 +183,25 @@ enum objc_class_flags
objc_class_flag_assoc_class = (1<<8)
};
/**
* Sets the specific class flag. Note: This is not atomic.
*/
static inline void objc_set_class_flag(struct objc_class *aClass,
enum objc_class_flags flag)
{
aClass->info |= (unsigned long)flag;
}
/**
* Unsets the specific class flag. Note: This is not atomic.
*/
static inline void objc_clear_class_flag(struct objc_class *aClass,
enum objc_class_flags flag)
{
aClass->info &= ~(unsigned long)flag;
}
/**
* Checks whether a specific class flag is set.
*/
static inline BOOL objc_test_class_flag(struct objc_class *aClass,
enum objc_class_flags flag)
{

@ -16,9 +16,62 @@ PRIVATE dtable_t uninstalled_dtable;
/** Head of the list of temporary dtables. Protected by initialize_lock. */
PRIVATE InitializingDtable *temporary_dtables;
/** Lock used to protect the temporary dtables list. */
PRIVATE mutex_t initialize_lock;
/** The size of the largest dtable, rounded up to the nearest power of two. */
static uint32_t dtable_depth = 8;
struct objc_slot* objc_get_slot(Class cls, SEL selector);
/**
* Returns YES if the class implements a method for the specified selector, NO
* otherwise.
*/
static BOOL ownsMethod(Class cls, SEL sel)
{
struct objc_slot *slot = objc_get_slot(cls, sel);
if ((NULL != slot) && (slot->owner == cls))
{
return YES;
}
return NO;
}
/**
* Checks whether the class implements memory management methods, and whether
* they are safe to use with ARC.
*/
static void checkARCAccessors(Class cls)
{
static SEL retain, release, autorelease, isARC;
if (NULL == retain)
{
retain = sel_registerName("retain");
release = sel_registerName("release");
autorelease = sel_registerName("autorelease");
isARC = sel_registerName("_ARCCompliantRetainRelease");
}
if (ownsMethod(cls, isARC))
{
objc_set_class_flag(cls, objc_class_flag_fast_arc);
return;
}
struct objc_slot *slot = objc_get_slot(cls, retain);
if (!ownsMethod(slot->owner, isARC))
{
objc_clear_class_flag(cls, objc_class_flag_fast_arc);
}
slot = objc_get_slot(cls, release);
if (!ownsMethod(slot->owner, isARC))
{
objc_clear_class_flag(cls, objc_class_flag_fast_arc);
}
slot = objc_get_slot(cls, autorelease);
if (!ownsMethod(slot->owner, isARC))
{
objc_clear_class_flag(cls, objc_class_flag_fast_arc);
}
}
static void collectMethodsForMethodListToSparseArray(
struct objc_method_list *list,
@ -418,6 +471,7 @@ PRIVATE void objc_update_dtable_for_class(Class cls)
// Methods now contains only the new methods for this class.
mergeMethodsFromSuperclass(cls, cls, methods);
SparseArrayDestroy(methods);
checkARCAccessors(cls);
}
PRIVATE void add_method_list_to_class(Class cls,
@ -434,6 +488,7 @@ PRIVATE void add_method_list_to_class(Class cls,
// Methods now contains only the new methods for this class.
mergeMethodsFromSuperclass(cls, cls, methods);
SparseArrayDestroy(methods);
checkARCAccessors(cls);
}
static dtable_t create_dtable_for_class(Class class, dtable_t root_dtable)
@ -645,6 +700,7 @@ PRIVATE void objc_send_initialize(id object)
{
meta->dtable = dtable;
class->dtable = class_dtable;
checkARCAccessors(class);
UNLOCK(&initialize_lock);
return;
}
@ -662,6 +718,8 @@ PRIVATE void objc_send_initialize(id object)
temporary_dtables = &meta_buffer;
UNLOCK(&initialize_lock);
checkARCAccessors(class);
// Store the buffer in the temporary dtables list. Note that it is safe to
// insert it into a global list, even though it's a temporary variable,
// because we will clean it up after this function.

@ -62,6 +62,8 @@ struct objc_slot* objc_get_slot(Class cls, SEL selector);
#define __sync_swap __sync_lock_test_and_set
#endif
void call_cxx_destruct(id obj);
#ifdef NO_EXECINFO
static inline void dump_stack(char *msg, void *addr) {}
#else
@ -306,8 +308,6 @@ id objc_assign_weak(id value, id *location)
}
static SEL finalize;
static SEL cxx_destruct;
Class zombie_class;
@ -327,17 +327,13 @@ static void runFinalize(void *addr, void *context)
abort();
}
//fprintf(stderr, "FINALIZING %p (%s)\n", addr, ((id)addr)->isa->name);
if (Nil == ((id)addr)->isa) { return; }
struct objc_slot *slot = objc_get_slot(obj->isa, cxx_destruct);
if (NULL != slot)
{
slot->method(obj, cxx_destruct);
}
slot = objc_get_slot(obj->isa, finalize);
if (Nil == obj->isa) { return; }
struct objc_slot *slot = objc_get_slot(obj->isa, finalize);
if (NULL != slot)
{
slot->method(obj, finalize);
}
call_cxx_destruct(obj);
*(void**)addr = zombie_class;
}
@ -728,6 +724,5 @@ PRIVATE void enableGC(BOOL exclude)
gc = &gc_ops_boehm;
refcount_initialize(&refcounts, 4096);
finalize = sel_registerName("finalize");
cxx_destruct = sel_registerName(".cxx_destruct");
GC_finalizer_notifier = runFinalizers;
}

@ -7,7 +7,8 @@
static id allocate_class(Class cls, size_t extraBytes)
{
return calloc(cls->instance_size + extraBytes, 1);
intptr_t *addr = calloc(cls->instance_size + extraBytes + sizeof(intptr_t), 1);
return (id)(addr + 1);
}
static void *alloc(size_t size)

@ -22,6 +22,52 @@
struct objc_slot *objc_get_slot(Class cls, SEL selector);
#define CHECK_ARG(arg) if (0 == arg) { return 0; }
/**
* Calls C++ destructors in the correct order.
*/
PRIVATE void call_cxx_destruct(id obj)
{
static SEL cxx_destruct;
if (NULL == cxx_destruct)
{
cxx_destruct = sel_registerName(".cxx_destruct");
}
// Don't call object_getClass(), because we want to get hidden classes too
Class cls = obj->isa;
while (cls)
{
struct objc_slot *slot = objc_get_slot(cls, cxx_destruct);
cls = Nil;
if (NULL != slot)
{
slot->method(obj, cxx_destruct);
cls = slot->owner->super_class;
}
}
}
static void call_cxx_construct_for_class(Class cls, id obj)
{
static SEL cxx_construct;
if (NULL == cxx_construct)
{
cxx_construct = sel_registerName(".cxx_contruct");
}
struct objc_slot *slot = objc_get_slot(cls, cxx_construct);
cls = slot->owner->super_class;
if (Nil != cls)
{
call_cxx_construct_for_class(cls, obj);
}
slot->method(obj, cxx_construct);
}
PRIVATE void call_cxx_construct(id obj)
{
call_cxx_construct_for_class(obj->isa, obj);
}
/**
* Looks up the instance method in a specific class, without recursing into
* superclasses.
@ -277,6 +323,7 @@ id class_createInstance(Class cls, size_t extraBytes)
if (Nil == cls) { return nil; }
id obj = gc->allocate_class(cls, extraBytes);
obj->isa = cls;
call_cxx_construct(obj);
return obj;
}
@ -290,7 +337,8 @@ id object_copy(id obj, size_t size)
id object_dispose(id obj)
{
free(obj);
call_cxx_destruct(obj);
gc->free(obj);
return nil;
}
@ -673,3 +721,4 @@ void objc_registerClassPair(Class cls)
LOCK_RUNTIME_FOR_SCOPE();
class_table_insert(cls);
}

Loading…
Cancel
Save