Подразумевает ли поддержка x64 поддержку BMI1?

Можно с уверенностью предположить, что сборки x64 могут использовать TZCNT без проверки его поддержка через флаги процессора?


person Pavel P    schedule 25.04.2020    source источник
comment
Мне любопытно, почему ты вообще должен спрашивать. Наверняка достаточно просто погуглить BMI1 и увидеть, что он был новым в Haswell для Intel. Я предполагаю, что вы столкнулись с tzcnt без ошибок на предыдущих процессорах или видели компилятор, использующий его без -mbmi1 или -march=haswell, и сомневались в других источниках, поэтому я подробно рассказал об этом в своем ответе. Но на первый взгляд, этот вопрос об ИМТ1 в целом, безусловно, может быть отклонен из-за отсутствия исследований. (Я думал об этом, но не стал.)   -  person Peter Cordes    schedule 25.04.2020
comment
Я видел, что zlib-ng условно использует tzcount во время выполнения. Я хотел предложить им всегда использовать bsf для x86 (они все равно проверяют 0) и использовать tzcnt для сборок x64. Но не удалось найти четкую информацию, поддерживают ли его все процессоры x64 или нет.   -  person Pavel P    schedule 26.04.2020
comment
Если они уже исключают input=0, то они должны использовать rep bsf = tzcnt безоговорочно (включая 32-битный режим), так что это быстрее на AMD, то же самое на Intel, за счет 1 байта инструкции.   -  person Peter Cordes    schedule 26.04.2020
comment
О, ну, это C, а не asm, поэтому они не могут просто использовать _tzcnt_u32. Э-э, подождите, это в ifdef _MSC_VER, поэтому он никогда не используется с компиляторами, которые требуют включения -mbmi1 для _tzcnt_u32. Так что да, они должны использовать _tzcnt_u32. Мне любопытно, насколько это ужасно для MSVC, когда x86_cpu_has_tzcnt не является константой времени компиляции.   -  person Peter Cordes    schedule 26.04.2020
comment
Это плохо, потому что x86_cpu_has_tzcnt не является константой времени компиляции (если бы она была, компилятор, очевидно, удалил бы ее). Устанавливается во время выполнения, если процессор поддерживает BMI1.   -  person Pavel P    schedule 26.04.2020


Ответы (1)


Нет, конечно нет! x86-64 был новым в конце 2003 года (AMD K8) с только устаревшими инструкциями битового сканирования bsf и bsr и без остальных инструкций BMI1.

Первым процессором Intel с поддержкой BMI1 был Haswell в 2013 году. (Также представлен BMI2.)
Первым процессором AMD с поддержкой BMI1 был Piledriver в 2012 году.
AMD ABM (Advanced Bit Manipulation) в процессорах AMD K10 и более поздних версиях добавлены только popcnt и lzcnt, а не tzcnt.

Википедия Наборы инструкций по обработке битов: поддержка процессоров. Обратите внимание, что процессоры Celeron/Pentium не декодируют префиксы VEX, поэтому у них отключены AVX и BMI1/BMI2, поскольку каждый из BMI1 и 2 включает некоторые инструкции, закодированные VEX, такие как andn и blsr. Это отстой; BMI1/2 наиболее полезен, когда компиляторы могут использовать его везде в исполняемом файле для более эффективных сдвигов числа переменных и глазков, поэтому по-прежнему продажа новых ЦП без BMI1/2 не приближает нас к возможности рассматривать их как базовый уровень, как мы делаем для P6 cmov в 32-битном режиме.


Декодирование TZCNT на старых процессорах

Поскольку вы конкретно упомянули tzcnt, его кодировка машинного кода - rep bsf, поэтому старые процессоры будут выполнять его как BSF. Это дает тот же результат, что и tzcnt, если ввод не равен нулю. т. е. tzcnt работает на всех процессорах x86 (начиная с 386), когда ввод не равен нулю.

Но когда он равен нулю, tzcnt выдаст размер операнда (например, 64), но < a href="https://www.felixcloutier.com/x86/bsf" rel="nofollow noreferrer">bsf оставляет регистр назначения без изменений. tzcnt устанавливает ФЛАГИ на основе результата, bsf на основе ввода. AMD документирует поведение dst-unmodified в своем справочном руководстве по ISA. Intel только документирует это как неопределенное значение, но реализует то же поведение, что и AMD, по крайней мере, в существующих процессорах.

(Вот почему bsf/bsr имеют выходную зависимость от всех процессоров, например add. К сожалению, tzcnt/lzcnt также имеют ложную зависимость от семейства Intel Sandybridge до Skylake: Почему нарушение выходной зависимости LZCNT имеет значение?. И почему popcnt имеет значение в SnB-семействе before Cannon/Ice Lake, потому что он использует один и тот же исполнительный модуль.)


tzcnt значительно быстрее на AMD, поэтому компиляторы, настраивающие общие процессоры или процессоры AMD, часто используют tzcnt вместо bsf без проверки функций процессора.

например для GNU C __builtin_ctz. Эта встроенная функция имеет неопределенное поведение для ввода = 0, поэтому разрешено просто использовать bsf без проверки на 0. И, следовательно, также разрешено использовать tzcnt, потому что результат в этом случае ничем не гарантируется.

Почему TZCNT работает с моим процессором Sandy Bridge?

Для lzcnt такого прямого/обратного сравнения не существует. Если его декодировать как rep bsr с игнорированием бессмысленного префикса rep, вы получите 31 - lzcnt(x), битовый индекс. https://fgiesen.wordpress.com/2013/10/18/bit-scanning-equivalencies/

Один из удобных приемов — ctz( x | 0x80000000 ), потому что операция ИЛИ дешева1 и гарантирует, что bsf всегда найдет ненулевой бит. Это не меняет результат для любых ненулевых x, потому что это последний бит, на который будет смотреть bsf. Этот трюк также работает для __builtin_clz(x|1) / bsr, где это даже лучше, потому что or reg, imm8 еще короче, чем imm32.

Сноска 1: or reg, imm32 работает для 32-битной константы; bts reg,63 на некоторых процессорах дешевле реализовать x|(1ULL<<63) для 64-битного ввода.

person Peter Cordes    schedule 25.04.2020