2012-02-05 15 views
17

estoy un poco confundido en cuanto a por qué sonido metálico es emisor de código diferente para los dos siguiente método:¿Por qué estos métodos simples compilan de manera diferente?

@interface ClassA : NSObject 
@end 

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

Si nos fijamos en el ARMv7 entonces emitida vemos esto, al O3, con ARC habilitado:

 .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 única diferencia es la última llamada a objc_autoreleaseReturnValue frente a objc_autorelease. Esperaría que ambos llamaran al objc_autoreleaseReturnValue para ser honestos. De hecho, el primer método que no usa objc_autoreleaseReturnValue significa que potencialmente será más lento que el segundo porque definitivamente habrá una liberación automática y luego un retener por la persona que llama, en lugar de la derivación más rápida de esta llamada redundante que ARC puede hacer si es compatible con el tiempo de ejecución.

El LLVM que se emite da algún tipo de razón por la que es así:

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 
} 

Pero estoy luchando para ver por qué se decidió recopilar estos dos método diferente. ¿Alguien puede arrojar algo de luz sobre él?

Actualizar:

Más extraño aún es estos otros métodos:

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

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

Estos compilan 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 

Esta vez, son idénticos sin embargo hay algunas cosas que podría optimícese aún más aquí:

  1. Hay un mov r4, r0 redundante seguido de mov r0, r4.

  2. Hay un retener seguido de un lanzamiento.

Sin duda, el bit inferior de estos dos métodos se puede convertir en:

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

Obviamente, podrían entonces omitir apareciendo r4 debido a que en realidad no se le daban un golpe más. Entonces el método se convertiría en el mismo que giveMeAnObject2 que es exactamente lo que esperaríamos.

¿Por qué clang no es inteligente y hace esto?

+0

Impar, sí. ¿Intentó diferentes niveles de optimización? -Os? En cualquier caso, presente un error (y publique el # aquí). – bbum

+0

He intentado diferentes niveles de optimización pero nada realmente extra para informar sobre ello. Aunque todavía tengo que trabajar un poco más. Bueno, archivaría un error, pero quiero asegurarme de que realmente sea un error primero. Me podría estar perdiendo algo simple, aunque es muy extraño. – mattjgalloway

+0

@bbum - En realidad, mentí. 'O0' es ligeramente interesante. Los casos 1 y 2 se vuelven lo mismo. Aunque obviamente no hay optimización de llamadas de cola ni nada de eso.Entonces, umm, creo que tiene que ver con la optimización de la llamada final. – mattjgalloway

Respuesta

5

Esto parece ser un error en el optimizador y se rastrea como rdar: // problem/10813093.

+0

Impresionante. ¡Gracias por mirar eso por mí! ¡Ciertamente es uno impar! – mattjgalloway

Cuestiones relacionadas