objective c - What did the compiler and run-time system really do in my generated assembly? -


i understand how generated assembly , runtime work together, , came across question while stepping through generated assembly code.

source example

here 3 lines of objective-c, running in xcode 4.5:

// line 1: nsobject *obj1 = [[nsobject alloc] init];  // line 2: [obj1 release];  // line 3: nsobject *obj2; 

comparing generated assembly

stepping through generated assembly, made few observations.

before line 1, address of obj1 shown:

obj1    (nsobject*) 0x00003604 

after line 1, changes:

obj1    nsobject *  0x08122110 

observations

1) address of obj1 changed. when source code compiled, compiler allocates temporarily memory obj1. then, (after line 1) compiler apparently re-allocates, object's address changes.

2) after line 2, address of obj2 still same (0x08122110)! when call [obj1 release], telling compiler: "i don't need anymore. please take away." system doing release @ point in future , can not seem control directly.

3) debugger can't step on line 3. don't understand why won't!

question

in terms of creating , destroying objects, compiler doing these lines of code (specifically "alloc-init", release, , nsobject pointer declaration without assignment)? also, why won't debugger let me step on third line? can debugger not see it?

along answer, if can please recommend documents or book compiler , run-time system do, appreciate it. thank much!

marcus's answer quite good, here more details (i'd been meaning brush reading generated assembly; having try , explain best way).

nsobject *obj1 = [[nsobject alloc] init]; // line 1 

the compiler compiles 2 function calls objc_msgsend(). first calls +alloc method on nsobject class. result of function call becomes first argument -- target object -- of second function call calls method -init.

the result of calling init stored on stack in chunk of memory have declared being named obj1 has type of a pointer instance of nsobject.

you can step through line in debugger because there executed expression on line. if code written as:

nsobject *obj1; // declaration obj1 = [[nsobject alloc] init]; 

then find can't step through declaration.

before obj1 = [[nsobject alloc] init];, value ofobj1is *undefined* under manual retain release, **will automatically set tonil` (0) under arc** (thereby eliminating source of bugs marcus indicated).

[obj1 release]; // line 2 

this line invokes release method on instance of nsobject pointed to obj1.

nsobject *obj2; // line 3 

this line nothing. if compiler's optimizer turned on, there no code generated @ all. without optimizer, compiler may bump stack pointer sizeof(nsobject*) reserve space on stack name obj2.

and, again, can't step through in debugger because there no expression execute on line.


of note, rewrite code as:

[[[nsobject alloc] init] release]; 

that identical original code wrote far execution concerned. without optimizer, little bit different in won't store on stack. optimizer, generate identical code original code. optimizer quite @ eliminating local variables when aren't needed (which partially why debugging optimized code hard).


given this:

(11) void f() (12) { (13)    nsobject *obj1 = [[nsobject alloc] init]; // line 1 (14)     (15)    [obj1 release]; // line 2 (16)     (17)    nsobject *obj2; // line 3 (18)} 

this unoptimized x86_64 assembly. ignore "fixup" stuff. @ callq lines; actual calls objc_msgsend() described above. on x86_64, %rdi -- register -- argument 0 function calls. thus, %rdi target of method calls goes. %rax register used return values.

so, when see callq, followed movq %rax, %rdi, followed callq, says "take return value of first callq , pass first argument next callq.

as variables, you'll see things movq %rax, -8(%rbp) after callq. says "take whatever returned callq, write current spot on stack, move stack pointer down 8 locations (the stack grows down)". unfortunately, assembly doesn't show variable names.

_f:                                     ## @f     .cfi_startproc lfunc_begin0:     .loc    1 12 0                  ## /tmp/asdfafsd/asdfafsd/main.m:12:0 ## bb#0:     pushq   %rbp ltmp2:     .cfi_def_cfa_offset 16 ltmp3:     .cfi_offset %rbp, -16     movq    %rsp, %rbp ltmp4:     .cfi_def_cfa_register %rbp     subq    $32, %rsp     leaq    l_objc_msgsend_fixup_release(%rip), %rax     leaq    l_objc_msgsend_fixup_alloc(%rip), %rcx     .loc    1 13 0 prologue_end     ## /tmp/asdfafsd/asdfafsd/main.m:13:0 ltmp5:     movq    l_objc_classlist_references_$_(%rip), %rdx     movq    %rdx, %rdi     movq    %rcx, %rsi     movq    %rax, -24(%rbp)         ## 8-byte spill     callq   *l_objc_msgsend_fixup_alloc(%rip)     movq    l_objc_selector_references_(%rip), %rsi     movq    %rax, %rdi     callq   _objc_msgsend     movq    %rax, -8(%rbp)     .loc    1 15 0                  ## /tmp/asdfafsd/asdfafsd/main.m:15:0     movq    -8(%rbp), %rax     movq    %rax, %rdi     movq    -24(%rbp), %rsi         ## 8-byte reload     callq   *l_objc_msgsend_fixup_release(%rip)     .loc    1 18 0                  ## /tmp/asdfafsd/asdfafsd/main.m:18:0     addq    $32, %rsp     popq    %rbp     ret ltmp6: lfunc_end0: 

for giggles, have @ assembly generated optimizer turned on (-os -- fastest, smallest, default deployed code):

the first thing note -- , gets question (3) -- there no manipulation of %rbp outside of first , last instructions. is, nothing pushed onto or pulled off stack; quite literally, there no evidence obj1 , obj2 ever declared because compiler didn't need them generate equivalent code.

everything done via registers , you'll note there 2 move %rax, %rdi. first "take result of +alloc , use first argument call -init" , second "take result of -init , use argument -release.

aside; %rsi second argument function calls resides on x86_64. method calls -- calls objc_msgsend() function -- argument contain name of method (the selector) called.

lfunc_begin0:     .loc    1 12 0                  ## /tmp/asdfafsd/asdfafsd/main.m:12:0 ## bb#0:     pushq   %rbp ltmp2:     .cfi_def_cfa_offset 16 ltmp3:     .cfi_offset %rbp, -16     movq    %rsp, %rbp ltmp4:     .cfi_def_cfa_register %rbp     .loc    1 13 0 prologue_end     ## /tmp/asdfafsd/asdfafsd/main.m:13:0 ltmp5:     movq    l_objc_classlist_references_$_(%rip), %rdi     leaq    l_objc_msgsend_fixup_alloc(%rip), %rsi     callq   *l_objc_msgsend_fixup_alloc(%rip)     movq    l_objc_selector_references_(%rip), %rsi     movq    %rax, %rdi     callq   *_objc_msgsend@gotpcrel(%rip)     .loc    1 15 0                  ## /tmp/asdfafsd/asdfafsd/main.m:15:0     leaq    l_objc_msgsend_fixup_release(%rip), %rsi     movq    l_objc_msgsend_fixup_release(%rip), %rcx     movq    %rax, %rdi     popq    %rbp     jmpq    *%rcx  # tailcall ltmp6: lfunc_end0: 

if want learn more method dispatch, wrote bit of guide. couple of versions of objc_msgsend() out of date, still relevant.

note arm code works same way philosophically, generated assembly bit different , quite bit more of it.


i can't still understand why can't step on line 3 ^^

if @ generated assembly, there nothing generated variable declarations. @ least not directly. closest movq %rax, -8(%rbp) moves result of init , after 2 function calls.

for nsobject *obj2;, compiler doesn't generate code. not optimizer disabled.

that because variable declaration not expression; doesn't other provide label -- developer -- use hold values. when use variable there code generated.

thus, when stepping in debugger, skips line because there nothing do.


Comments

Popular posts from this blog

android - getbluetoothservice() called with no bluetoothmanagercallback -

sql - ASP.NET SqlDataSource, like on SelectCommand -

ios - Undefined symbols for architecture armv7: "_OBJC_CLASS_$_SSZipArchive" -