Использование операнда 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в реестре.

Другие вопросы по тегам