Поддерживает ли x64 поддержку BMI1? - PullRequest
1 голос
/ 25 апреля 2020

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

1 Ответ

8 голосов
/ 25 апреля 2020

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

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

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


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

Но когда он равен нулю, tzcnt выдаст размер операнда (например, 64), но bsf оставляет регистр назначения неизменным. tzcnt устанавливает флаги на основе результата, bsf на основании ввода. AMD документирует dst-неизмененное поведение в своем справочном руководстве по ISA. Intel только документирует это как «неопределенное значение», но реализует то же поведение, что и AMD, по крайней мере в существующих процессорах.

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


tzcnt значительно быстрее на AMD, поэтому компиляторы настраиваются на Процессоры «generi c» или AMD часто будут использовать tzcnt вместо bsf без проверки возможностей процессора.

, например, для GNU C __builtin_ctz. Это intrinsi c имеет неопределенное поведение для input = 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 ), потому что OR дешево (по крайней мере для 32-битной константы) и гарантирует, что всегда есть ненулевой бит для bsf найти. Но не меняет результат для любого ненулевого x, потому что последний бит bsf будет смотреть. Это лучший трюк для __builtin_clz(x|1) / bsr.

...