Вы и ответ @ Rafael чрезвычайно усложняете ваш код.
Обычно вы никогда не захотите использовать mov rdi, msg
с 64-битным непосредственным значением абсолютного адреса.(См. 64-разрядный формат Mach-O не поддерживает 32-разрядные абсолютные адреса. NASM Accessing Array )
Используйте default rel
и используйте cmp byte [msg], 'H'
.Или, если вам нужен указатель в RDI, чтобы вы могли увеличивать его в цикле, используйте lea rdi, [rel msg]
.
Единственное, что отличается между вашими ветвями, это значение RDI.Вам не нужно дублировать настройку RAX или syscall
, просто получите правильное значение в RDI и затем ветки соединятся друг с другом.(Или делайте это без ответвлений.)
@ Ответ Рафаэля по какой-то причине все еще загружает 8 байтов из строки, как и обе загрузки в вашем вопросе.Предположительно, это sys_exit
, и он игнорирует старшие байты, устанавливая только состояние выхода процесса из младшего байта, но просто для забавы, давайте представим, что мы действительно хотим, чтобы все 8 байтов были загружены для системного вызова, сравнивая только младший байт.
default rel ; use RIP-relative addressing modes by default for [label]
global start
section .rodata ;; read-only data usually belongs in .rodata
msg: db "Hello, World!", 10, 0
section .text
start:
mov rdi, [msg] ; 8 byte load from a RIP-relative address
mov ecx, 'H'
cmp dil, cl ; compare the low byte of RDI (dil) with the low byte of RCX (cl)
jne .notequal
;; fall through on equal
mov edi, 58
.notequal: ; .labels are local labels in NASM
; mov rdi, [rdx] ; still loaded from before; we didn't destroy it.
mov eax, 0x2000001
syscall
По возможности избегайте записи в AH / BH / CH / DH.Он либо имеет ложную зависимость от старого значения RAX / RBX / RCX / RDX, либо может вызвать срыв частичных регистров при последующем чтении полного регистра.Ответ @ Рафаэля не делает этого, но mov ah, 'H'
зависит от загрузки в AL на некоторых процессорах.См. Почему GCC не использует частичные регистры? и Как именно работают частичные регистры в Haswell / Skylake?Написание AL, похоже, ложно зависит от RAX, а AH несовместимо - mov ah, 'H'
имеет ложную зависимость от старого значения AH в Haswell / Skylake, хотя AH переименовывается отдельно от RAX.Но AL не, так что да, это вполне может иметь ложную зависимость от нагрузки, мешая ей работать параллельно и задерживая cmp
на цикл.
В любом случае, TL: DR здесьчто вы не должны возиться с написанием AH / BH / CH / DH, если вам не нужно.Чтение их часто хорошо, но может иметь худшую задержку.И обратите внимание, что cmp dil, ah
не кодируется, потому что DIL доступен только с префиксом REX, а AH доступен только без.
Я выбрал RCX вместо RSI, потому что CL не нужен префикс REX, нотак как нам нужно взглянуть на младший байт RDI (dil), нам все равно нужен префикс REX для cmp.Я мог бы использовать mov cl, 'H'
для сохранения размера кода, потому что, вероятно, нет проблемы с ложной зависимостью от старого значения RCX.
Кстати, cmp dil, 'H'
будет работать так же хорошо, как и cmp dil, cl
.
Или, если мы загружаем байт с нулевым расширением в полный RDI, мы можем использовать cmp edi, 'H'
вместо его младшей версии.( Расширяющиеся с нуля нагрузки являются нормальным / рекомендуемым способом работы с байтами и 16-разрядными целыми числами на современном x86-64. Слияние с младшим байтом старого значения регистра обычно хуже для производительности, котораяпричина Почему инструкции x86-64 для 32-битных регистров обнуляют верхнюю часть полного 64-битного регистра? .)
И вместо ветвления мы могли бы использовать CMOV.Это иногда лучше, иногда нет, для размера кода и производительности.
Версия 2, фактически загружает только 1 байт:
start:
movzx edi, byte [msg] ; 1 byte load, zero extended to 4 (and implicitly to 8)
mov eax, 58 ; ASCII ':'
cmp edi, 'H'
cmove edi, eax ; edi = (edi == 'H') ? 58 : edi
; rdi = 58 or the first byte,
; unlike in the other version where it had 8 bytes of string data here
mov eax, 0x2000001
syscall
(Эта версия выглядит намного короче, но большинство дополнительных строк были пробелами, комментариями и метками. Оптимизация до cmp
- сразу делает эти 4 инструкции вместо 5 перед mov eax
/ syscall
, но кромечто они равны.)