Почему эти простые методы компилируются по-разному?

Я немного не понимаю, почему clang генерирует другой код для следующих двух методов:

@interface ClassA : NSObject
@end

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

Если мы посмотрим на генерируемый ARMv7, мы увидим следующее: at O3 , с включенным ARC:

        .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

Единственная разница - это хвостовой вызов objc_autoreleaseReturnValue по сравнению с objc_autorelease . Честно говоря, я ожидал, что оба вызовут objc_autoreleaseReturnValue . Фактически, первый метод, не использующий objc_autoreleaseReturnValue , означает, что он потенциально будет медленнее, чем второй, потому что определенно будет автоматический выпуск, а затем сохранение вызывающим, а не более быстрый обход этого избыточного вызова, который ARC можно сделать, если он поддерживается во время выполнения.

Сгенерированный LLVM дает какую-то причину, почему это так:

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
}

Но я изо всех сил пытаюсь понять, почему было решено скомпилировать эти два метода по-разному. Может ли кто-нибудь пролить свет на это?

Обновление:

Еще более странными являются эти другие методы:

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

+ (id)giveMeAnObject4 {
    ClassA *a = [[ClassA alloc] init];
    return 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

На этот раз они идентичны, однако есть несколько вещей, которые можно оптимизировать еще больше здесь:

  1. Есть лишний mov r4, r0 , за которым следует mov r0, r4 .

  2. За удержанием следует выпуск.

Конечно, нижний бит обоих этих методов может превратиться в:

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

Очевидно, мы могли бы также опустить popping r4 , потому что мы фактически больше не используем его. Тогда метод превратится в тот же самый, что и giveMeAnObject2 , чего мы и ожидали.

Почему Кланг не умный и делает это ?!

17
задан mattjgalloway 5 February 2012 в 17:06
поделиться