Нет, конечно нет! 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
.