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> #include <string.h>
@implementation Protocol @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 // FIXME: This needs removing, but it's included for now because GNUstep's
// implementation of +[NSObject conformsToProtocol:] calls it. // implementation of +[NSObject conformsToProtocol:] calls it.
- (BOOL)conformsTo: (Protocol*)p - (BOOL)conformsTo: (Protocol*)p
@ -23,12 +19,6 @@
+ (Class)class { return self; } + (Class)class { return self; }
- (id)self { return self; } - (id)self { return self; }
@end @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 * 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/runtime.h"
#import "objc/blocks_runtime.h" #import "objc/blocks_runtime.h"
#import "nsobject.h" #import "nsobject.h"
#import "class.h"
#import "selector.h" #import "selector.h"
#import "visibility.h" #import "visibility.h"
#import "objc/hooks.h" #import "objc/hooks.h"
@ -24,6 +25,46 @@ static IMP NewAutoreleasePool;
static IMP DeleteAutoreleasePool; static IMP DeleteAutoreleasePool;
static IMP AutoreleaseAdd; 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) void *objc_autoreleasePoolPush(void)
{ {
@ -54,10 +95,9 @@ void objc_autoreleasePoolPop(void *pool)
id objc_autorelease(id obj) id objc_autorelease(id obj)
{ {
return [obj autorelease];
if (nil != obj) if (nil != obj)
{ {
AutoreleaseAdd(AutoreleasePool, SELECTOR(addObject:), obj); obj = autorelease(obj);
} }
return obj; return obj;
} }
@ -100,7 +140,8 @@ id objc_retainAutoreleasedReturnValue(id obj)
id objc_retain(id obj) id objc_retain(id obj)
{ {
return [obj retain]; if (nil == obj) { return nil; }
return retain(obj);
} }
id objc_retainAutorelease(id obj) id objc_retainAutorelease(id obj)
@ -121,15 +162,16 @@ id objc_retainBlock(id b)
void objc_release(id obj) 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]; value = objc_retain(value);
id oldValue = *object; id oldValue = *addr;
*object = value; *addr = value;
[oldValue release]; objc_release(oldValue);
return value; return value;
} }

@ -168,12 +168,10 @@ enum objc_class_flags
*/ */
objc_class_flag_user_created = (1<<5), objc_class_flag_user_created = (1<<5),
/** /**
* Instances of this class have a reference count and plane ID prepended to * Instances of this class are provide ARC-safe retain / release /
* them. The default for this is set for classes, unset for metaclasses. * autorelease implementations.
* It should be cleared by protocols, constant strings, and objects not
* allocated by NSAllocateObject().
*/ */
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 * This class is a hidden class (should not be registered in the class
* table nor returned from object_getClass()). * table nor returned from object_getClass()).
@ -185,16 +183,25 @@ enum objc_class_flags
objc_class_flag_assoc_class = (1<<8) 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, static inline void objc_set_class_flag(struct objc_class *aClass,
enum objc_class_flags flag) enum objc_class_flags flag)
{ {
aClass->info |= (unsigned long)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, static inline void objc_clear_class_flag(struct objc_class *aClass,
enum objc_class_flags flag) enum objc_class_flags flag)
{ {
aClass->info &= ~(unsigned long)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, static inline BOOL objc_test_class_flag(struct objc_class *aClass,
enum objc_class_flags flag) 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. */ /** Head of the list of temporary dtables. Protected by initialize_lock. */
PRIVATE InitializingDtable *temporary_dtables; PRIVATE InitializingDtable *temporary_dtables;
/** Lock used to protect the temporary dtables list. */
PRIVATE mutex_t initialize_lock; 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; 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( static void collectMethodsForMethodListToSparseArray(
struct objc_method_list *list, 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. // Methods now contains only the new methods for this class.
mergeMethodsFromSuperclass(cls, cls, methods); mergeMethodsFromSuperclass(cls, cls, methods);
SparseArrayDestroy(methods); SparseArrayDestroy(methods);
checkARCAccessors(cls);
} }
PRIVATE void add_method_list_to_class(Class 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. // Methods now contains only the new methods for this class.
mergeMethodsFromSuperclass(cls, cls, methods); mergeMethodsFromSuperclass(cls, cls, methods);
SparseArrayDestroy(methods); SparseArrayDestroy(methods);
checkARCAccessors(cls);
} }
static dtable_t create_dtable_for_class(Class class, dtable_t root_dtable) 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; meta->dtable = dtable;
class->dtable = class_dtable; class->dtable = class_dtable;
checkARCAccessors(class);
UNLOCK(&initialize_lock); UNLOCK(&initialize_lock);
return; return;
} }
@ -662,6 +718,8 @@ PRIVATE void objc_send_initialize(id object)
temporary_dtables = &meta_buffer; temporary_dtables = &meta_buffer;
UNLOCK(&initialize_lock); UNLOCK(&initialize_lock);
checkARCAccessors(class);
// Store the buffer in the temporary dtables list. Note that it is safe to // 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, // insert it into a global list, even though it's a temporary variable,
// because we will clean it up after this function. // 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 #define __sync_swap __sync_lock_test_and_set
#endif #endif
void call_cxx_destruct(id obj);
#ifdef NO_EXECINFO #ifdef NO_EXECINFO
static inline void dump_stack(char *msg, void *addr) {} static inline void dump_stack(char *msg, void *addr) {}
#else #else
@ -306,8 +308,6 @@ id objc_assign_weak(id value, id *location)
} }
static SEL finalize; static SEL finalize;
static SEL cxx_destruct;
Class zombie_class; Class zombie_class;
@ -327,17 +327,13 @@ static void runFinalize(void *addr, void *context)
abort(); abort();
} }
//fprintf(stderr, "FINALIZING %p (%s)\n", addr, ((id)addr)->isa->name); //fprintf(stderr, "FINALIZING %p (%s)\n", addr, ((id)addr)->isa->name);
if (Nil == ((id)addr)->isa) { return; } if (Nil == obj->isa) { return; }
struct objc_slot *slot = objc_get_slot(obj->isa, cxx_destruct); struct objc_slot *slot = objc_get_slot(obj->isa, finalize);
if (NULL != slot)
{
slot->method(obj, cxx_destruct);
}
slot = objc_get_slot(obj->isa, finalize);
if (NULL != slot) if (NULL != slot)
{ {
slot->method(obj, finalize); slot->method(obj, finalize);
} }
call_cxx_destruct(obj);
*(void**)addr = zombie_class; *(void**)addr = zombie_class;
} }
@ -728,6 +724,5 @@ PRIVATE void enableGC(BOOL exclude)
gc = &gc_ops_boehm; gc = &gc_ops_boehm;
refcount_initialize(&refcounts, 4096); refcount_initialize(&refcounts, 4096);
finalize = sel_registerName("finalize"); finalize = sel_registerName("finalize");
cxx_destruct = sel_registerName(".cxx_destruct");
GC_finalizer_notifier = runFinalizers; GC_finalizer_notifier = runFinalizers;
} }

@ -7,7 +7,8 @@
static id allocate_class(Class cls, size_t extraBytes) 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) static void *alloc(size_t size)

@ -22,6 +22,52 @@
struct objc_slot *objc_get_slot(Class cls, SEL selector); struct objc_slot *objc_get_slot(Class cls, SEL selector);
#define CHECK_ARG(arg) if (0 == arg) { return 0; } #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 * Looks up the instance method in a specific class, without recursing into
* superclasses. * superclasses.
@ -277,6 +323,7 @@ id class_createInstance(Class cls, size_t extraBytes)
if (Nil == cls) { return nil; } if (Nil == cls) { return nil; }
id obj = gc->allocate_class(cls, extraBytes); id obj = gc->allocate_class(cls, extraBytes);
obj->isa = cls; obj->isa = cls;
call_cxx_construct(obj);
return obj; return obj;
} }
@ -290,7 +337,8 @@ id object_copy(id obj, size_t size)
id object_dispose(id obj) id object_dispose(id obj)
{ {
free(obj); call_cxx_destruct(obj);
gc->free(obj);
return nil; return nil;
} }
@ -673,3 +721,4 @@ void objc_registerClassPair(Class cls)
LOCK_RUNTIME_FOR_SCOPE(); LOCK_RUNTIME_FOR_SCOPE();
class_table_insert(cls); class_table_insert(cls);
} }

Loading…
Cancel
Save