Извлечение ZF в GCC встроенной сборке - PullRequest
3 голосов
/ 01 августа 2011

Мне нужно использовать некоторые инструкции x86, которые не имеют встроенных функций GCC, такие как BSF и BSR. Со встроенной сборкой GCC я могу написать что-то вроде следующего

__INTRIN_INLINE unsigned char bsf64(unsigned long* const index, const uint64_t mask)
{
__asm__("bsf %[mask], %[index]" : [index] "=r" (*index) : [mask] "mr" (mask));
return mask ? 1 : 0;
}

Код типа if (bsf64(x, y)) { /* use x */ } переводится GCC на что-то вроде

0x000000010001bf04 <bsf64+0>:   bsf    %rax,%rdx
0x000000010001bf08 <bsf64+4>:   test   %rax,%rax
0x000000010001bf0b <bsf64+7>:   jne    0x10001bf44 <...>

Однако, если mask равно нулю, BSF уже устанавливает флаг ZF, поэтому test после bsf является избыточным.

Вместо возврата mask ? 1 : 0 возможно ли извлечь флаг ZF и вернуть его, чтобы GCC не генерировал test?

РЕДАКТИРОВАТЬ: сделал пример if более понятным

EDIT: В ответ на Damon, __builtin_ffsl генерирует еще менее оптимальный код. Если я использую следующий код

    int b = __builtin_ffsl(mask);
    if (b) {
        *index = b - 1;
        return true;
    } else {
        return false;
    }

GCC генерирует эту сборку

   0x000000000044736d <+1101>:  bsf    %r14,%r14
   0x0000000000447371 <+1105>:  cmove  %r12,%r14
   0x0000000000447375 <+1109>:  add    $0x1,%r14d
   0x0000000000447379 <+1113>:  je     0x4471c0 <...>
   0x000000000044737f <+1119>:  lea    -0x1(%r14),%ecx

Таким образом, test пропал, но генерируются избыточные условные перемещения, приращения и убывания.

1 Ответ

4 голосов
/ 01 августа 2011

Пара замечаний:

  • Это «антиоптимизация».Вы пытаетесь выполнить микрооптимизацию для чего-то, что компилятор уже поддерживает.
  • Ваш код вообще не генерирует инструкцию bsf с моей версией gcc со всеми включенными ключами оптимизации.Глядя на код, это не удивительно, потому что вы возвращаете mask, который является операндом source , а не операндом назначения (gcc использует синтаксис AT & T!).Компилятор достаточно умен, чтобы понять это, и отбрасывает ассемблерный код (который ничего не делает).
  • Есть встроенная функция __builtin_ffsl, которая делает то же самое, что и встроенная сборка (хотя,правильно).Intrinsic не менее переносим, ​​чем встроенный ассемблер, но компилятору его проще оптимизировать.
  • Использование встроенной функции приводит к последовательности bsf cmov в моем компиляторе (при условии, что вызывающий код вынуждает его фактически выполнить инструкцию), который показывает, что компилятор просто использует нулевой флаг без дополнительных инструкций теста.
  • Возвращение char, когда вы хотите bool, не является наилучшей возможной подсказкой для компилятора, хотявероятно, поймет это в любом случае большую часть времени.Однако указание компилятору использовать команду битового сканирования, когда вас действительно интересует только «ноль или не ноль», безусловно, неоптимально.if(x) и if(!x) отлично работают в этом отношении.Было бы иначе, если бы вы вернули результат в качестве ссылки, так что вы можете использовать его в другом месте, но ваш код представляет собой очень сложный способ записи if(x).
...