2012-02-05 16 views
17

jestem nieco zdezorientowany, dlaczego dzyń emituje inny kod dla następujących dwóch metod:Dlaczego te proste metody kompilują się inaczej?

@interface ClassA : NSObject 
@end 

@implementation ClassA 
+ (ClassA*)giveMeAnObject1 { 
    return [[ClassA alloc] init]; 
} 
+ (id)giveMeAnObject2 { 
    return [[ClassA alloc] init]; 
} 
@end 

Jeśli spojrzymy na ARMv7 emitowanego następnie widzimy to, co O3, z włączonym łuku:

 .align 2 
     .code 16 
     .thumb_func  "+[ClassA giveMeAnObject1]" 
"+[ClassA giveMeAnObject1]": 
     push {r7, lr} 
     movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_-(LPC0_0+4)) 
     mov  r7, sp 
     movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_-(LPC0_0+4)) 
     movw r0, :lower16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC0_1+4)) 
     movt r0, :upper16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC0_1+4)) 
LPC0_0: 
     add  r1, pc 
LPC0_1: 
     add  r0, pc 
     ldr  r1, [r1] 
     ldr  r0, [r0] 
     blx  _objc_msgSend 
     movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC0_2+4)) 
     movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC0_2+4)) 
LPC0_2: 
     add  r1, pc 
     ldr  r1, [r1] 
     blx  _objc_msgSend 
     pop.w {r7, lr} 
     b.w  _objc_autorelease 

     .align 2 
     .code 16 
     .thumb_func  "+[ClassA giveMeAnObject2]" 
"+[ClassA giveMeAnObject2]": 
     push {r7, lr} 
     movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_-(LPC2_0+4)) 
     mov  r7, sp 
     movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_-(LPC2_0+4)) 
     movw r0, :lower16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC2_1+4)) 
     movt r0, :upper16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC2_1+4)) 
LPC2_0: 
     add  r1, pc 
LPC2_1: 
     add  r0, pc 
     ldr  r1, [r1] 
     ldr  r0, [r0] 
     blx  _objc_msgSend 
     movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC2_2+4)) 
     movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC2_2+4)) 
LPC2_2: 
     add  r1, pc 
     ldr  r1, [r1] 
     blx  _objc_msgSend 
     pop.w {r7, lr} 
     b.w  _objc_autoreleaseReturnValue 

Różnica w wysokości jest numerem końcowym do objc_autoreleaseReturnValue w stosunku do objc_autorelease. Oczekuję, że oba będą wywoływać objc_autoreleaseReturnValue szczerze mówiąc. W rzeczywistości pierwsza metoda nie używająca objc_autoreleaseReturnValue oznacza, że ​​będzie potencjalnie wolniejsza niż druga, ponieważ z pewnością będzie autoreasada, a następnie zatrzymanie przez dzwoniącego, zamiast szybszego ominięcia tego zbędnego połączenia, które ARC może zrobić, jeśli jest obsługiwane w środowisko wykonawcze.

LLVM, który jest emitowany daje jakiś powód dlaczego tak jest:

define internal %1* @"\01+[ClassA giveMeAnObject1]"(i8* nocapture %self, i8* nocapture %_cmd) { 
    %1 = load %struct._class_t** @"\01L_OBJC_CLASSLIST_REFERENCES_$_", align 4 
    %2 = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_", align 4 
    %3 = bitcast %struct._class_t* %1 to i8* 
    %4 = tail call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i8* (i8*, i8*)*)(i8* %3, i8* %2) 
    %5 = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_2", align 4 
    %6 = tail call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i8* (i8*, i8*)*)(i8* %4, i8* %5) 
    %7 = tail call i8* @objc_autorelease(i8* %6) nounwind 
    %8 = bitcast i8* %6 to %1* 
    ret %1* %8 
} 

define internal i8* @"\01+[ClassA giveMeAnObject2]"(i8* nocapture %self, i8* nocapture %_cmd) { 
    %1 = load %struct._class_t** @"\01L_OBJC_CLASSLIST_REFERENCES_$_", align 4 
    %2 = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_", align 4 
    %3 = bitcast %struct._class_t* %1 to i8* 
    %4 = tail call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i8* (i8*, i8*)*)(i8* %3, i8* %2) 
    %5 = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_2", align 4 
    %6 = tail call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i8* (i8*, i8*)*)(i8* %4, i8* %5) 
    %7 = tail call i8* @objc_autoreleaseReturnValue(i8* %6) nounwind 
    ret i8* %6 
} 

Ale jestem stara się zrozumieć, dlaczego to postanowił skompilować te dwie metody inaczej. Czy ktoś może rzucić na to trochę światła?

Aktualizacja:

Jeszcze dziwniejsze jest te inne metody:

+ (ClassA*)giveMeAnObject3 { 
    ClassA *a = [[ClassA alloc] init]; 
    return a; 
} 

+ (id)giveMeAnObject4 { 
    ClassA *a = [[ClassA alloc] init]; 
    return a; 
} 

Są skompilować do:

 .align 2 
     .code 16 
     .thumb_func  "+[ClassA giveMeAnObject3]" 
"+[ClassA giveMeAnObject3]": 
     push {r4, r7, lr} 
     movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_-(LPC2_0+4)) 
     add  r7, sp, #4 
     movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_-(LPC2_0+4)) 
     movw r0, :lower16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC2_1+4)) 
     movt r0, :upper16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC2_1+4)) 
LPC2_0: 
     add  r1, pc 
LPC2_1: 
     add  r0, pc 
     ldr  r1, [r1] 
     ldr  r0, [r0] 
     blx  _objc_msgSend 
     movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC2_2+4)) 
     movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC2_2+4)) 
LPC2_2: 
     add  r1, pc 
     ldr  r1, [r1] 
     blx  _objc_msgSend 
     blx  _objc_retainAutoreleasedReturnValue 
     mov  r4, r0 
     mov  r0, r4 
     blx  _objc_release 
     mov  r0, r4 
     pop.w {r4, r7, lr} 
     b.w  _objc_autoreleaseReturnValue 

     .align 2 
     .code 16 
     .thumb_func  "+[ClassA giveMeAnObject4]" 
"+[ClassA giveMeAnObject4]": 
     push {r4, r7, lr} 
     movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_-(LPC3_0+4)) 
     add  r7, sp, #4 
     movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_-(LPC3_0+4)) 
     movw r0, :lower16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC3_1+4)) 
     movt r0, :upper16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC3_1+4)) 
LPC3_0: 
     add  r1, pc 
LPC3_1: 
     add  r0, pc 
     ldr  r1, [r1] 
     ldr  r0, [r0] 
     blx  _objc_msgSend 
     movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC3_2+4)) 
     movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC3_2+4)) 
LPC3_2: 
     add  r1, pc 
     ldr  r1, [r1] 
     blx  _objc_msgSend 
     blx  _objc_retainAutoreleasedReturnValue 
     mov  r4, r0 
     mov  r0, r4 
     blx  _objc_release 
     mov  r0, r4 
     pop.w {r4, r7, lr} 
     b.w  _objc_autoreleaseReturnValue 

tym razem, są one identyczne, jednak istnieje kilka rzeczy, które mogłyby być zoptymalizowanym jeszcze bardziej tutaj:

  1. Istnieje nadmiarowy kod mov r4, r0, a następnie mov r0, r4.

  2. Po zatrzymaniu następuje zwolnienie.

pewnością, dno nieco obu tych metod może prowadzić do:

LPC3_2: 
     add  r1, pc 
     ldr  r1, [r1] 
     blx  _objc_msgSend 
     pop.w {r4, r7, lr} 
     b.w  _objc_autoreleaseReturnValue 

Oczywiście możemy również pominąć wtedy pojawiały r4 ponieważ w rzeczywistości nie sprać go więcej. Wtedy metoda zmieni się w dokładnie taką samą, jak giveMeAnObject2, czego dokładnie oczekujemy.

Dlaczego klang nie jest sprytny i robi to ?!

+0

Nieparzysta, tak. Czy próbowałeś różnych poziomów optymalizacji? -Os? W każdym razie zgłoś błąd (i opublikuj # tutaj). – bbum

+0

Próbowałem różnych poziomów optymalizacji, ale nic naprawdę ekstra do zgłoszenia na ten temat. Chociaż nadal muszę przez to jeszcze trochę popracować. Zrobiłbym błąd, ale chcę się upewnić, że to w rzeczywistości błąd. Być może brakuje mi czegoś prostego, chociaż jest to bardzo dziwne. – mattjgalloway

+0

@bbum - Właściwie to skłamałem. 'O0' jest nieco interesujące. Przypadek 1 i 2 stają się takie same. Chociaż oczywiście nie ma optymalizacji połączeń końcowych ani nic takiego.Więc, umm, myślę, że to ma coś wspólnego z optymalizacją ogona. – mattjgalloway

Odpowiedz

5

Wygląda na to, że jest to błąd w optymalizatorze i jest śledzony jako rdar: // problem/10813093.

+0

Awesome. Dzięki, że mi się przyglądasz! Z pewnością jest to dziwne! – mattjgalloway

Powiązane problemy