Skip to content

Instantly share code, notes, and snippets.

@Adlai-Holler
Last active April 19, 2017 21:24
Show Gist options
  • Save Adlai-Holler/9e18b5a0f3f30e549b2a2faa54fdaf4f to your computer and use it in GitHub Desktop.
Save Adlai-Holler/9e18b5a0f3f30e549b2a2faa54fdaf4f to your computer and use it in GitHub Desktop.
An analysis of the implementations of Objective-C atomic properties.

Objective-C Synthesized Atomic Properties

Summary at bottom.

// iOS, Xcode 8.3.2 stock, ARM, release build

// BOOL

char -[MYObject boolProp](void * self, void * _cmd) {
    r0 = sign_extend_32(self->_boolProp);
    return r0;
}

void -[MYObject setBoolProp:](void * self, void * _cmd, char arg2) {
    self->_boolProp = arg2;
    return;
}

// DOUBLE - 32bit

double -[MYObject doubleProp](void * self, void * _cmd) {
    r0 = self;
    r4 = sp - 0x20;
    asm { bfc        r4, #0x0, #0x3 };
    r0 = objc_copyStruct(r4 + 0x8, *0xc834 + r0, 0x8, 0x1, 0x0);
    asm { ldrd       r0, r1, [sp, #0x18 + var_10] };
    return r0;
}

void -[MYObject setDoubleProp:](void * self, void * _cmd, double arg2) {
    r0 = self;
    r4 = sp - 0x20;
    asm { bfc        r4, #0x0, #0x3 };
    sp = r4;
    r1 = 0x2240;
    asm { strd       r2, r3, [sp, #0x18 + var_10] };
    objc_copyStruct(r0 + *(r1 + 0xa5f4), sp + 0x8, 0x8, 0x1, 0x0);
    return;
}

// DOUBLE - 64bit

double -[MYObject doubleProp](void * self, void * _cmd) {
    r0 = self;
    return r0;
}

void -[MYObject setDoubleProp:](void * self, void * _cmd, double arg2) {
    *(self + sign_extend_64(0x100009004)) = d0;
    return;
}

// See note at the bottom about C11 atomics!
// DOUBLE - 32bit, C11 Atomics

double -[MYObject c11AtomicDoubleProp](void * self, void * _cmd) {
    r0 = self + *0xc89c;
    asm { ldrexd     r0, r1, [r0] };
    asm { clrex };
    asm { dmb };
    return r0;
}

void -[MYObject setC11AtomicDoubleProp:](void * self, void * _cmd, double arg2) {
    r0 = self;
    asm { dmb };
    do {
            asm { ldrexd     r1, r0, [sb] };
            asm { strexd     r0, r2, r3, [sb] };
    } while (r0 != 0x0);
    asm { dmb };
    return;
}

// DOUBLE - 64bit, C11 atomics

double -[MYObject c11AtomicDoubleProp](void * self, void * _cmd) {
    r0 = self;
    asm { ldar       x8, [x8] };
    return r0;
}

void -[MYObject setC11AtomicDoubleProp:](void * self, void * _cmd, double arg2) {
    asm { stlr       x9, [x8] };
    return;
}

// FLOAT

float -[MYObject floatProp](void * self, void * _cmd) {
    r0 = self->_floatProp;
    return r0;
}

void -[MYObject setFloatProp:](void * self, void * _cmd, float arg2) {
    self->_floatProp = arg2;
    return;
}

// STRONG OBJECT

void * -[MYObject strongObjProp](void * self, void * _cmd) {
    r1 = _cmd;
    r0 = self;
    r3 = 0x1;
    r2 = *0xc83c;
    r0 = objc_getProperty(r0, r1, r2, r3);
    return r0;
}

void -[MYObject setStrongObjProp:](void * self, void * _cmd, void * arg2) {
    objc_setProperty_atomic();
    return;
}

// WEAK OBJECT

void * -[MYObject weakObjProp](void * self, void * _cmd) {
    r0 = self + *0xc840;
    r0 = objc_loadWeakRetained(r0);
    r0 = [r0 autorelease];
    return r0;
}

void -[MYObject setWeakObjProp:](void * self, void * _cmd, void * arg2) {
    r0 = self + *0xc840;
    r1 = arg2;
    objc_storeWeak(r0, r1);
    return;
}

// UNSAFE OBJECT

void * -[MYObject unsafeObjProp](void * self, void * _cmd) {
    r0 = self->_unsafeObjProp;
    return r0;
}

void -[MYObject setUnsafeObjProp:](void * self, void * _cmd, void * arg2) {
    self->_unsafeObjProp = arg2;
    return;
}

// COPIED OBJECT

void * -[MYObject copiedObjectProp](void * self, void * _cmd) {
    r0 = loc_a78c(self, _cmd); // NOTE: Jumps to the -strongObjectProp method impl. Clever!
    return r0;
}

void -[MYObject setCopiedObjectProp:](void * self, void * _cmd, void * arg2) {
    objc_setProperty_atomic_copy();
    return;
}

// CGRECT

struct CGRect -[MYObject cgRectProp](void * self, void * _cmd) {
    r0 = objc_copyStruct(self, _cmd + *0xc848, 0x10, 0x1, 0x0);
    return r0;
}

void -[MYObject setCgRectProp:](void * self, void * _cmd, struct CGRect arg2) {
    r0 = self;
    sp = sp - 0x1c;
    r9 = 0xc848;
    asm { strd       r2, r3, [sp, #0x14 + var_10] };
    asm { strd       r1, ip, [sp, #0x14 + var_8] };
    objc_copyStruct(r0 + *r9, sp + 0x4, 0x10, 0x1, 0x0);
    return;
}

Summary:

Note: Implementations of objc_copyStruct, objc_setProperty_atomic, and objc_getProperty are available at https://opensource.apple.com/source/objc4/objc4-709/runtime/objc-accessors.mm.auto.html

  • Types that fit in the arch size (BOOL, int, unsafe_unretained id, etc) are read/set directly with no synchronization.
  • Bigger types (e.g. double on 32bit) use objc_copyStruct which uses spinlocks.
  • Retain/weak/copy objects use objc_setPropertyAtomic and objc_getProperty which use spinlocks
  • C11 atomics replace objc_copyStruct with a while loop – similar to a spinlock

Compare the assembly for setting double on 64-bit using Obj-C-atomic and C11 atomic:

-[MYObject setC11AtomicDoubleProp:]: // C11 atomic
ldrsw      x8, #0x100009060
add        x8, x0, x8
fmov       x9, d0
stlr       x9, [x8]
ret
                        
-[MYObject setDoubleProp:]: // ObjC atomic
ldrsw      x8, #0x100009068
fmov       x9, d0
str        x9, [x0, x8]
ret

The obj-c atomic manages to combine the add and stlr instructions from C11 atomic into one str instruction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment