2017-01-10 7 views
6

Я играл с разработчиком компилятора и обнаружил, что эти 2 функции генерируют разные сборки как в gcc, так и в clang. Я ожидал, что после инкрустации они будут производить одинаковые деревья выражений и, следовательно, идентичные и оптимальные сборки.Является ли это причудой оптимизаторов или результатом языковых правил, запрещающих оптимизацию?

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 результат

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 результат

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 

Итак, это причуда оптимизаторов или результат языковых правил, запрещающих оптимизацию?

+4

Предположительно, пропуск, который объединил сравнения OR'd в одно вычитание + сравнение, прогонял перед проходом вложения. –

+3

[Снимки gcc 7.0 оптимизируют их в один и тот же код] (https://godbolt.org/g/iPXtEi). Я думаю, что T.C. верно. – Cornstalks

ответ

3

Это причуда gcc < 7.0 и clang optimizers. Как отметили Cornstalks в комментариях, gcc 7.0 способен генерировать оптимальную сборку. Я также проверил VC++ 2015, который делает это тоже:

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 

As T.C. заостренная, вставка выполняется после прохождения некоторой оптимизации, которая в этом конкретном коде объединяет цепочку сравнений с более простой проверкой диапазона. Полезно сделать это до вставки, чтобы уменьшить функции листа, что, в свою очередь, увеличивает их шансы быть вложенными. В основном, функция v1 была преобразована в нечто вроде этого:

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

тогда v2 была преобразована в более простой форме:

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

Сформирован сборки для v3 похож на сгенерированный для 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 

Я предполагаю, что для преобразования v3 в v4 требуется некоторый нетривиальный анализ, который gcc 7.0 способен сделать. Эта версия создает абсолютно ту же сборку для всех четырех фрагментов:

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 не может преобразовать v3 в v4 и производит эту сборку:

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 

Если бы я был догадаться, я бы сказал, почему он генерирует оптимальный код для v1, но не для v3, потому что он делает вложение перед уменьшением сравнений с проверкой диапазона.

 Смежные вопросы

  • Нет связанных вопросов^_^