Использование операнда m64 для инструкции AND (asmjit)
я играю с
asmjit
и генерирующая сборка. Тем самым я заметил, что нельзя использовать 64-битные константы для инструкций (исключая
mov
что имеет смысл).
Из-за этого я помещаю 64-битные константы в стек и использую их, обращаясь к стеку, вместо того, чтобы использовать константу в качестве операнда. Различные ресурсы говорят, что можно использовать память в качестве операнда для инструкции (например, [1] , [2]).
Однако я заметил, что инструкция работает не так, как ожидалось. Я приведу вам пример из моего кода:
mov r14, qword ptr [r15+32] ; r14 holds a masked pointer now
mov qword ptr [rsp], 281474976710655 ; 0xFFFFFFFFFFFF is the mask for the pointer
and r14, [rsp] ; Using pointer&mask I want to unmask the pointer
После этой инструкции значение in остается прежним:
- до :
- после :
421609184805440
При использовании вместо этого регистра все работает так, как ожидалось:
mov r14, qword ptr [r15+32] ; r14 holds a masked pointer now
mov r13, 281474976710655 ; 0xFFFFFFFFFFFF is the mask for the pointer
and r14, r13 ; Using pointer&mask I want to unmask the pointer
- до :
421723605418560
-
r14
послеand
:140248628707904
Конечно, я мог бы использовать регистр вместо доступа к стеку, но мне было бы интересно, почему это ведет себя по-другому.
1 ответ
Похоже, вы не проверили ошибки asmjit. Документы говорят , что есть
kErrorInvalidImmediate
- Недопустимое немедленное (за пределами X86 и неверный шаблон на ARM).
Единственная инструкция x86-64, которая может использовать 64-битную непосредственную команду, это
mov
-немедленно зарегистрировать, специальный код операции no-modrm, который дает нам 5-байтовый
mov eax, 12345
, или 10-байтовый
mov rax, 0x0123456789abcdef
, где префикс REX.W изменяет этот код операции для поиска 64-битного непосредственного. См. https://www.felixcloutier.com/x86/mov/ , почему мы не можем переместить 64-битное непосредственное значение в память?
Ваш титул — отвлекающий маневр. Это не имеет ничего общего с наличием
m64
операнд для , это константа, в которой проблема. Вы можете убедиться в этом, выполнив пошаговую сборку ассемблера с помощью отладчика и проверив оба операнда перед , включая тот, который находится в памяти. (Это, вероятно
-1
из
0xFFFFFFFF
в качестве немедленного для
mov m64, sign_extended_imm32
, что объяснило бы И не изменение значения в R14).
Также дизассемблирование машинного кода JIT должно показать вам, что непосредственно закодировано на самом деле; опять же, отладчик может предоставить это, когда вы выполняете его пошагово.
Используйте свой временный регистр для константы (например,
mov r14, 0xFFFFFFFFFFFF
), тогда
and reg,mem
загружать и маскировать.
Или лучше, если целевая машина, для которой вы выполняете JITint, имеет BMI1.
andn
, построить инвертированную константу один раз вне цикла с
mov r13, ~0xFFFFFFFFFFFF
затем внутри цикла используйте
andn r14, r13, [r15+32]
который выполняет загрузку + и без разрушения маски, все с одной инструкцией, которая может декодировать в один uop на процессорах Intel/AMD.
Если вы не можете повторно использовать постоянный регистр в цикле, возможно,
mov reg,imm64
, тогда
push reg
или же
mov mem,reg
и используйте это в будущих инструкциях AND. Или выдавать какие-то постоянные данные где-то достаточно близко, чтобы ссылаться на режим адресации, относящийся к RIP, хотя это требует немного больше размера кода на каждом этапе.
and
инструкция. (ModRM + 4 байта rel32, по сравнению с ModRM + SIB + 0 или 1 байт для данных в стеке, близком к RSP).
Кстати, если вы просто усекаете вместо расширения знака, вы также предполагаете, что этот адрес находится в нижней половине виртуального адресного пространства (т.е. пользовательского пространства). Но это нормально. Забавный факт: будущие процессоры x86 (первые Sapphire Rapids) будут иметь дополнительную функцию, позволяющую операционным системам прозрачно игнорировать старшие биты, за исключением MSB: LAM = Linear Address Masking. См . руководство по будущим расширениям Intel.
Поэтому, если эта функция включена с 48-битной маскировкой для пользовательского пространства, вы можете полностью пропустить маскирование И. (Если ваш код гарантирует, что бит 47 соответствует биту 63; вы можете оставить верхний бит без изменений или 0, чтобы ваш код мог использовать преимущества LAM, когда он доступен для сохранения инструкций).
Если бы вы маскировались, чтобы сохранить низкие 32, вы могли бы просто
mov r14d, [r15+32]
для расширения нулями младшего двойного слова значения до 64-битного R14. Но для сохранения старших 48 или 57 бит вам нужна маска или BMI2.
bzhi
с
48
в реестре.