2017-01-10 13 views
6

Tôi đã chơi với trình thám hiểm trình biên dịch và thấy rằng 2 chức năng này tạo ra sự lắp ráp khác nhau trong cả gcc và clang. Tôi hy vọng rằng sau khi inlining họ sẽ sản xuất cây biểu hiện giống hệt nhau và do đó lắp ráp giống hệt nhau và tối ưu.Đây có phải là một sự lừa đảo của trình tối ưu hóa hoặc kết quả của các quy tắc ngôn ngữ cấm tối ưu hóa không?

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 kết quả

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 -std = C++ 1Z-O3 kết quả

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 

Vì vậy, là điều này là một sự lừa đảo của trình tối ưu hóa hoặc kết quả của các quy tắc ngôn ngữ cấm tối ưu hóa?

+4

Có lẽ, đường chuyền hợp nhất các phép so sánh HOẶC thành một phép trừ + so sánh chạy trước đường gạch chân nội tuyến. –

+3

[Ảnh chụp nhanh gcc 7.0 tối ưu hóa chúng thành cùng một mã] (https://godbolt.org/g/iPXtEi). Tôi nghĩ T.C. đúng. – Cornstalks

Trả lời

3

Đó là một điều không minh bạch của gcc < 7.0 và trình tối ưu hóa clang. Như Cornstalks chỉ trong các ý kiến, gcc 7.0 có thể tạo ra lắp ráp tối ưu. Tôi cũng đã kiểm tra VC++ 2015, cũng như vậy:

is_decimal_digit_v2: 
    sub cl, 48 
    cmp cl, 9 
    setbe al 
    ret 0 
is_decimal_digit_v1: 
    sub cl, 48 
    cmp cl, 9 
    setbe al 
    ret 0 

Như T.C. chỉ, nội tuyến được thực hiện sau khi một số tối ưu hóa vượt qua, mà trong mã cụ thể này sáp nhập một chuỗi các so sánh vào một kiểm tra phạm vi đơn giản. Nó rất hữu ích để làm như vậy trước khi nội tuyến để làm cho lá chức năng nhỏ hơn, do đó làm tăng cơ hội của họ để được inlined. Về cơ bản, chức năng v1 đã trở thành một cái gì đó như thế này:

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; 
} 

trong khi v2 đã trở thành hình thức đơn giản hơn nhiều:

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

tạo lắp ráp cho v3 là tương tự như tạo ra cho v1

#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 

Tôi đoán, để chuyển đổi v3 thành v4, nó đòi hỏi một số phân tích không tầm thường mà gcc 7.0 có thể thực hiện. Phiên bản này tạo ra hoàn toàn lắp ráp tương tự cho tất cả bốn đoạn:

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 

Điều thú vị là VC++ 2015 là không có khả năng chuyển đổi v3 thành v4 và sản xuất lắp ráp này:

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 

Nếu tôi có để đoán, tôi muốn nói lý do tại sao nó tạo ra mã tối ưu cho v1 nhưng không cho v3 là bởi vì nó không nội tuyến trước khi giảm so sánh để kiểm tra phạm vi.

Các vấn đề liên quan