2017-01-10 22 views
6

Derleyici gezgini ile oynuyordum ve bu 2 fonksiyonun hem gcc hem de clang'da farklı montajlar oluşturduğunu keşfettim. Çizgiyi çizdikten sonra aynı ifade ağaçlarını ve dolayısıyla aynı ve en uygun montajı üretmelerini bekledim.Bu, optimizasyonların bir tuhaflığı mıdır, yoksa optimizasyonları yasaklayan dil kuralları mıdır?

constexpr bool is_nonzero_decimal_digit(char const c) noexcept 
{ 
    return c == '1' || c == '2' || c == '3' || c == '4' || c == '5' 
     || c == '6' || c == '7' || c == '8' || c == '9'; 
} 

bool is_decimal_digit_v1(char const c) noexcept 
{ 
    return c == '0' || is_nonzero_decimal_digit(c); 
} 

bool is_decimal_digit_v2(char const c) noexcept 
{ 
    return c == '0' || c == '1' || c == '2' || c == '3' || c == '4' 
     || c == '5' || c == '6' || c == '7' || c == '8' || c == '9'; 
} 

Clang 3.9.1 -std = C++ 1z -O3 sonuç

is_decimal_digit_v1(char): 
    cmp  dil, 48 
    sete cl 
    add  dil, -49 
    cmp  dil, 9 
    setb al 
    or  al, cl 
    ret 

is_decimal_digit_v2(char): 
    add  dil, -48 
    cmp  dil, 10 
    setb al 
    ret 

gcc 6.3 C++ 1z -O3 sonuç

is_decimal_digit_v1(char): 
    cmp  dil, 48 
    je  .L3 
    sub  edi, 49 
    cmp  dil, 8 
    setbe al 
    ret 
.L3: 
    mov  eax, 1 
    ret 

is_decimal_digit_v2(char): 
    sub  edi, 48 
    cmp  dil, 9 
    setbe al 
    ret 

Yani = olup -std Bu optimizasyonların bir tuhaflığı mı yoksa optimizasyonları yasaklayan dil kuralları mı?

+4

Muhtemelen, OR'd karşılaştırmalarını bir çıkarma + toplama ile birleştiren geçiş, geçiş geçişinden önce gerçekleşti. –

+3

[The gcc 7.0 anlık görüntüsü bunları aynı koda göre optimize eder] (https://godbolt.org/g/iPXtEi). Bence T.C. doğru. – Cornstalks

cevap

3

Bu, gcc < 7.0 ve takoz optimize edici bir tuhaftır. Cornstalks yorumlarda işaret ettiği gibi, gcc 7.0 en uygun montajı üretebiliyor. Ayrıca, VC++ 2015'i de kontrol ettim:

T.C. sivri, bazı optimizasyonlar geçtikten sonra satır içi yapılır, ki bu kodda, bir karşılaştırma zincirini daha basit bir aralık kontrolüne birleştirmişlerdir. Yaprak fonksiyonlarını daha küçük yapmak için inline etmeden önce bunu yapmak yararlıdır, bu da sıralı olma şansını artırır. Temel olarak, v1 fonksiyonu böyle bir şey dönüştü:

bool is_decimal_digit_v3(char const c) noexcept 
{ 
    if (c == 48) return true; 
    // this is what was inlined 
    char tmp = c - 49; 
    return tmp >= 0 && tmp < 9; 
} 

oysa çok daha basit forma dönüştü v2:

bool is_decimal_digit_v4(char const c) noexcept 
{ 
    char tmp = c - 48; 
    return tmp >= 0 && tmp < 10; 
} 

v3 için montaj v1 için oluşturulan birine benzer Oluşturulan

#clang 3.9.1 
is_decimal_digit_v3(char):    # @is_decimal_digit_v3(char) 
    cmp  dil, 48 
    sete cl 
    add  dil, -49 
    cmp  dil, 9 
    setb al 
    or  al, cl 
    ret 
# gcc 6.3 
is_decimal_digit_v3(char): 
    cmp  dil, 48 
    je  .L8 
    sub  edi, 49 
    cmp  dil, 8 
    setbe al 
    ret 
.L8: 
    mov  eax, 1 
    ret 

Sanırım, v3'ü v4'e dönüştürmek için, gcc 7.0'ın yapabileceği bazı önemsiz olmayan analizler gerektirir. Bu sürüm kesinlikle dört snippet'lerde aynı derlemesi oluşturur:

is_decimal_digit_v1(char): 
    sub  edi, 48 
    cmp  dil, 9 
    setbe al 
    ret 
is_decimal_digit_v2(char): 
    sub  edi, 48 
    cmp  dil, 9 
    setbe al 
    ret 
is_decimal_digit_v3(char): 
    sub  edi, 48 
    cmp  dil, 9 
    setbe al 
    ret 
is_decimal_digit_v4(char): 
    sub  edi, 48 
    cmp  dil, 9 
    setbe al 
    ret 

VC++ 2015 v4 içine v3 dönüşümü mümkün olmadığını ilginçtir ve bu montaj üretir:

is_decimal_digit_v3: 
    cmp cl, 48 
    jne SHORT [email protected]_decimal 
    mov al, 1 
    ret 0 
[email protected]_decimal: 
    xor eax, eax 
    sub cl, 49 
    cmp cl, 8 
    setbe al 
    ret 0 

Verseydim tahmin etmek gerekirse, v1 için optimal kod oluşturmasının sebebi v3 için değil, aralık kontrolü ile karşılaştırmaları azaltmadan önce inlin yapmasıdır.

İlgili konular