Is it possible to see the code generated by ARC at compile time?

前端 未结 3 1975
春和景丽
春和景丽 2020-12-25 15:16

I have read the Transitioning to ARC Release Notes in \"Summary\" Section. They told :

ARC works by adding code at compile time to ensure that object

3条回答
  •  遥遥无期
    2020-12-25 15:58

    ARC in clang doesn't work by rewriting code from ObjC to ObjC, but emitting the extra retain/release LLVM bitcode during code-gen. That means, it is not possible to know how the compiler "fix" it without going to LLVM IR / assembly level.


    If ARC emit the LLVM bitcode as you said. Is it made for the purpose that use less time in compile process? (less complex ObjC code, less header file?)

    It is always better if the compiler can reduce the number of passes through the code.


    Can you show me some example or utility that show the code in assembly level?

    To get the assembly code, you could either

    1. Generate assembly directly from the compiler. In the command line, add the -S flag when invoking the compiler. The result is a .S file containing the assembly code. In an Xcode project, open the source code file, then go to Product (on menu bar) → Generate OutputAssembly File.

    2. Generate the object file, and then disassemble it. The built-in command otool -tvV can perform disassembly, and there are advanced tools like otx (free) or IDA (free for evaluation).

    I prefer route 2 because it generates less garbage and the disassembly tool can be configured to produce more useful information. Anyway, with either method you need to be able to read assembly code.

    Take this code as example:

    - (BOOL)application:(UIApplication*)application 
            didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
    {
       self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
       self.window.backgroundColor = [UIColor whiteColor];
       [self.window makeKeyAndVisible];
       return YES;
    }
    

    After compiling the following assembly will be produced (analyzed using IDA):

    -[SomeAppDelegate application:didFinishLaunchingWithOptions:]:
        push       {r4-r7,lr}
        add        r7, sp, #0xC
        str.w      r8, [sp,-#0x4]!
        sub        sp, sp, #0x18
        movw       r1, #(0x343c - 0x2574)       ; @selector(alloc)
        mov        r8, r0
        movt.w     r1, #0
        mov        r0, (0x3464 - 0x2576)        ; _OBJC_CLASS_$_UIWindow
        add        r1, pc
        add        r0, pc
        ldr        r1, [r1]
        ldr        r0, [r0]
        blx        _objc_msgSend
        mov        r1, (0x3440 - 0x258e)        ; @selector(mainScreen)
        mov        r6, r0
        movw       r0, #(0x3468 - 0x2594)       ; _OBJC_CLASS_$_UIScreen
        add        r1, pc
        movt.w     r0, #0
        add        r0, pc
        ldr        r1, [r1]
        ldr        r0, [r0]
        blx        _objc_msgSend
        mov        r7, r7
        blx        _objc_retainAutoreleasedReturnValue
        mov        r5, r0
        cbz        r5, L25ba
        movw       r0, #(0x3444 - 0x25b2)       ; @selector(bounds)
        mov        r1, r5
        movt.w     r0, #0
        add        r0, pc
        ldr        r2, [r0]
        add        r0, sp, #0x8
        blx        _objc_msgSend_stret
        b          L25c4
    
    L25ba:
        add        r0, sp, #0x8
        vmov.i32   q8, #0x80
        vstmia     r0, {d16-d17}
    
    L25c4:
        mov        r1, (0x3448 - 0x25d2)        ; @selector(initWithFrame:)
        ldr        r0, [sp,#0x10]
        add        r1, pc
        ldr        r2, [sp,#0x8]
        ldr        r3, [sp,#0xc]
        ldr        r4, [sp,#0x14]
        stmea.w    sp, {r0,r4}
        mov        r0, r6
        ldr        r1, [r1]
        blx        _objc_msgSend
        mov        r4, r0
        mov        r0, (0x344c - 0x25F2)        ; @selector(setWindow:)
        mov        r2, r4
        add        r0, pc
        ldr        r1, [r0]
        mov        r0, r8
        blx        _objc_msgSend
        mov        r0, r4
        blx        _objc_release
        mov        r0, r5
        blx        _objc_release
        mov        r0, (0x3450 - 0x2610)        ; @selector(window)
        add        r0, pc
        ldr        r5, [r0]
        mov        r0, r8
        mov        r1, r5
        blx        _objc_msgSend
        mov        r7, r7
        blx        _objc_retainAutoreleasedReturnValue
        mov        r1, (0x3454 - 0x2630)        ; @selector(whiteColor)
        mov        r6, r0
        movw       r0, #(0x346C - 0x2636)       ; _OBJC_CLASS_$_UIColor
        add        r1, pc
        movt.w     r0, #0
        add        r0, pc
        ldr        r1, [r1]
        ldr        r0, [r0]
        blx        _objc_msgSend
        mov        r7, r7
        blx        _objc_retainAutoreleasedReturnValue
        mov        r4, r0
        mov        r0, (0x3458 - 0x2652)        ; @selector(setBackgroundColor:)
        mov        r2, r4
        add        r0, pc
        ldr        r1, [r0]
        mov        r0, r6
        blx        _objc_msgSend
        mov        r0, r4
        blx        _objc_release
        mov        r0, r6
        blx        _objc_release
        mov        r0, r8
        mov        r1, r5
        blx        _objc_msgSend
        mov        r7, r7
        blx        _objc_retainAutoreleasedReturnValue
        mov        r4, r0
        mov        r0, (0x345C - 0x2680)        ; @selector(makeKeyAndVisible)
        add        r0, pc
        ldr        r1, [r0]
        mov        r0, r4
        blx        _objc_msgSend
        mov        r0, r4
        blx        _objc_release
        movs       r0, #1
        add        sp, sp, #0x18
        ldr.w      r8, [sp], #4
        pop        {r4-r7,pc}
    

    Without going into detail, you can see there are many _objc_release and _objc_retainAutoreleasedReturnValue. These are what ARC inserts during code-gen. Decompiling it by hand, we'll get:

    UIScreen* r5 = objc_retainAutoreleasedReturnValue([UIScreen mainScreen]);
    CGRect sp8 = r5 != nil ? [r5 bounds] : CGRectZero;
    UIWindow* r4 = [[UIWindow alloc] initWithFrame:sp8];
    [self setWindow:r4];
    objc_release(r4);
    objc_release(r5);
    
    UIWindow* r6a = objc_retainAutoreleasedReturnValue([self window])
    UIColor* r4a = objc_retainAutoreleasedReturnValue([UIColor whiteColor])
    [r6a setBackgroundColor:r4a];
    objc_release(r4a);
    objc_release(r6a);
    
    UIWindow* r4b = objc_retainAutoreleasedReturnValue([self window])
    [r4b makeKeyAndVisible];
    objc_release(r4b);
    
    return 1;
    

    which is just the same as what @c roald's link describes.

提交回复
热议问题