Единственный относительный к RIP режим адресации - RIP + rel32
. RIP + reg недоступен.
(В машинном коде 32-битный код имел 2 избыточных способа кодирования [disp32]
. X86-64 использует более короткую (без SIB) форму как относительную RIP, более длинную форму SIB как [sign_extended_disp32]
).
Если вы компилируете для Linux с -fno-pie -no-pie
, GCC сможет получить доступ к статическим данным с 32-битным абсолютным адресом, поэтому он может использовать режим, такой как __ZL10MASK_TABLE(,%rdi,2)
. Это невозможно для MacOS, где базовый адрес всегда выше 2 ^ 32; 32-разрядная абсолютная адресация полностью не поддерживается в x86-64 MacOS.
В исполняемом файле PIE (или PIC-коде в целом, например, в библиотеке) вам требуется REA-относительный LEA для настройки индексации статического массива. Или любой другой случай, когда статический адрес не умещается в 32 бита и / или не является постоянной времени соединения.
Intrinsics
Да, встроенные функции делают очень неудобным выражение нагрузки pmovzx/sx
из узкого источника, поскольку версии встроенных указателей-источников отсутствуют.
*((__m128i*) &MASK_TABLE[mask]
небезопасно: если вы отключите оптимизацию, вы вполне можете получить movdqa
16-байтовую загрузку, но адрес будет смещен. Это безопасно только тогда, когда компилятор складывает загрузку в операнд памяти для pmovzxbq
, который имеет 2-байтовый операнд памяти, поэтому не требует выравнивания.
На самом деле текущий GCC действительно компилирует ваш код с movdqa
16-байтовой загрузкой, как movdqa xmm0, XMMWORD PTR [rax+rdi*2]
перед reg-reg pmovzx
. Это явно пропущенная оптимизация. :( clang / LLVM (который MacOS устанавливает как gcc
) действительно складывает нагрузку в pmovzx
.
Безопасный способ - _mm_cvtepi8_epi64( _mm_cvtsi32_si128(MASK_TABLE[mask]) )
или что-то еще, и затем надеется, что компилятор оптимизирует нулевое расширение от 2 до 4 байтов и сгибает movd
в нагрузку, когда вы включаете оптимизацию. Или, может быть, попробуйте _mm_loadu_si32
для 32-битной загрузки, даже если вы действительно хотите 16. Но в прошлый раз, когда я попытался, компиляторы засосали сложение встроенной 64-битной нагрузки в операнд памяти, например, для pmovzxbw
. GCC и clang все еще терпят неудачу в этом, но ICC19 преуспевает. https://godbolt.org/z/IdgoKV
Я уже писал об этом раньше:
Ваше целое число -> векторная стратегия
Ваш выбор pmovsx
кажется странным. Вам не нужно расширение знака, поэтому я бы выбрал pmovzx
(_mm_cvt_epu8_epi64
). На самом деле это не более эффективно на любых процессорах.
Здесь справочная таблица работает только с небольшим количеством статических данных. Если бы ваш диапазон маски был больше, вы, возможно, захотите посмотреть на
есть ли обратная инструкция к инструкции movemask в intel avx2? для альтернативных стратегий, таких как широковещательная + AND + (сдвиг или сравнение).
Если вы делаете это часто, лучше всего использовать целую строку кэша из 4x 16-байтовых векторных констант, поэтому вам не нужна инструкция pmovzx
, просто индексируйте в выровненную таблицу xmm2
или __m128i
векторы, которые могут быть источником памяти для любой другой инструкции SSE. Используйте alignas(64)
, чтобы получить все константы в одной строке кэша.
Вы также можете рассмотреть (встроенные для) pdep
+ movd xmm0, eax
+ pmovzxbq
reg-reg, если вы ориентируетесь на процессоры Intel с BMI2. (pdep
медленно на AMD, однако).