2012-02-05 13 views
17

Sono un po 'confuso sul motivo per cui è clang che emettono codice diverso per i due seguente metodo:Perché questi semplici metodi vengono compilati in modo diverso?

@interface ClassA : NSObject 
@end 

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

Se guardiamo al ARMv7 emessa poi vediamo questo, a O3, con ARC abilitato:

 .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 

la differenza solo è chiamata coda di objc_autoreleaseReturnValue vs objc_autorelease. Mi aspetto che entrambi chiamino lo objc_autoreleaseReturnValue a dire il vero. In effetti, il primo metodo che non utilizza objc_autoreleaseReturnValue significa che sarà potenzialmente più lento del secondo perché ci sarà sicuramente un autorelease quindi un retain dal chiamante, piuttosto che il bypass più veloce di questa chiamata ridondante che ARC può fare se è supportato in il runtime.

Il LLVM che viene emessa dà una sorta di motivo per cui è così:

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 
} 

ma sto lottando per capire perché si è deciso di compilare questi due metodo diverso. Qualcuno può far luce su di esso?

Aggiornamento:

ancora più strano è che questi altri metodi:

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

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

Questi compilare a:

 .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 

Questa volta, sono identici ma ci sono alcune cose che potrebbero essere ottimizzato ancora di più qui:

  1. C'è un ridondante mov r4, r0 seguito da mov r0, r4.

  2. C'è un fermo seguito da un rilascio.

Sicuramente, il bit di fondo di entrambi di questi metodi può trasformarsi in:

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

Ovviamente potremmo poi anche omettere popping r4, perché in realtà non Clobber più. Quindi il metodo si trasformerebbe nello stesso identico a giveMeAnObject2 che è esattamente quello che ci aspetteremmo.

Perché il clang non è intelligente e sta facendo questo ?!

+0

Dispari, sì. Hai provato diversi livelli di ottimizzazione? -Os? In ogni caso, invia un bug (e pubblica qui #). – bbum

+0

Ho provato diversi livelli di ottimizzazione ma nulla di veramente extra da riportare. Anche se devo ancora lavorarci un po 'di più. Beh, vorrei presentare un bug, ma voglio assicurarmi che sia in realtà un bug prima. Potrei semplicemente mancare qualcosa di semplice, anche se è molto strano. – mattjgalloway

+0

@bbum - In realtà, ho mentito. 'O0' è leggermente interessante. Le cause 1 e 2 diventano le stesse. Sebbene ovviamente nessuna ottimizzazione di chiamata di coda o qualcosa del genere.Quindi, umm, immagino che sia qualcosa a che fare con l'ottimizzazione della chiamata di coda. – mattjgalloway

risposta

5

Questo sembra essere un bug nell'ottimizzatore ed è stato rintracciato come rdar: // problema/10813093.

+0

Fantastico. Grazie per aver guardato quello per me! Certamente è uno strano! – mattjgalloway

Problemi correlati