Мне всегда нравилась идея тестирования диапазона, а не просто для равенства, в случае, если бит случайно или что-то случится. Имейте в виду, что cmp/jge
не может macro-fuse на Core2 (в 32-битном режиме), но cmp/je
может. Я думал, что это будет более актуальным до тех пор, пока я не проведу Agner Fog's microarch pdf и не обнаружу, что это был только Core2, а не Nehalem, который не мог свести его на нет, поскольку макро-fusion вообще не работает в 64-битном режиме на Core2. (Более поздние микроархитектуры не имеют такого ограничения и могут создавать макро-предохранители все больше и больше.)
В зависимости от счетчика, вы можете обычно считать без CMP вообще (dec/jnz). И часто вы знаете, что он не должен быть 64-битным, поэтому вы можете использовать dec esi/jnz
или что угодно. dec esi/jge
действительно работает для подписанных счетчиков, но dec
не устанавливает CF, поэтому вы не можете (полезно) использовать JA.
Ваша структура цикла, с if() break
в середине и jmp в конце, не является идиоматической для asm. Нормальный:
mov ecx, 100
.loop: ; do{
;; stuff
dec ecx
jge .loop ; }while(--ecx >= 0)
Вы можете использовать JG только перезапустить цикл с положительной ECX, то есть петли из 100..1 вместо 100..0.
Наличие незанятой условной ветви и Принимаемая безусловная ветвь в петле менее эффективна.
Расширение на обсуждение в вопросе комментарии о сохранении/восстановлении r12: Обычно вы могли бы сделать что-то вроде:
my_func:
; push rbp
; mov rbp, rsp ; optional: make a stack frame
push rbx ; save the caller's value so we can use it
sub rsp, 32 ; reserve some space
imul edi, esi, 11 ; calculate something that we want to pass as an arg to foo
mov ebx, edi ; and save it in ebx
call foo
add eax, ebx ; and use value. If we don't need the value in rbx anymore, we can use the register for something else later.
... ;; calculate an array size in ecx
test ecx, ecx ; test for the special case of zero iterations *outside* the loop, instead of adding stuff inside. We can skip some of the loop setup/cleanup as well.
jz .skip_the_loop
; now use rbx as a loop counter
mov ebx, ecx
.loop:
lea edi, [rbx + rbx*4 + 10]
call bar ; bar(5*ebx+10);
; do something with the return value? In real code, you would usually want at least one more call-preserved register, but let's keep the example simple
dec ebx
jnz .loop
.skip_the_loop:
add rsp, 32 ; epilogue
pop rbx
;pop rbp ; pointless to use LEAVE; rsp had to already be pointing to the right place for POP RBX
ret
Обратите внимание, как мы используем RBX на пару вещей внутри функции, но только сохранить/восстановить его один раз.
Мне всегда нравилась идея тестирования диапазона, а не просто для равенства, в случае, если бит случайно или случайно. Но в x86 asm имейте в виду, что 'cmp/jge' не может замаскироваться на Core2 (в 32-битном режиме), но' cmp/je' может. Я подумал, что это будет более актуально, пока я не проведу проверку и не обнаружу, что это был только Core2, а не Nehalem, который не мог свести это на нет, поскольку макро-fusion не работает вообще в 64-битном режиме на Core2. (В более поздних микроархитектурах этого ограничения нет и можно комбинировать все больше и больше комбинаций.) –
Почему вы показываете какой-то странный разлив/перезагрузку r12, чтобы освободить его для использования в качестве временного счетчика? Это абсолютно неуместно (и не похоже на эффективный код). Конечно, есть какой-то реестр, который уже мертв, который можно использовать без сохранения. –
@Peter, как его правильно записать? Я думаю, что r12 - хороший выбор, потому что вызовы функций внутри цикла, такие как printf, не будут изменять регистр r12, сохраненный во время вызова, и нам не нужно вручную сохранять и восстанавливать счетчик вокруг вызовов.Пожалуйста, исправьте, если это неправильно. –