Saving rcx
is usually not necessary, it is not callee-save in common calling conventions. On Linux (and similar) rdi
also does not need to be saved, I guess you're using that since the Win64 calling convention does not pass an argument in rdi
. You can save them anyway if you want, which can be useful if you're using custom calling conventions. Saving an even number of registers makes the stack not-16-aligned though, you will probably get away with that now, but for example if you call some function that uses XMM registers it may save them at locations that it assumes are aligned (and there are some other cases where it causes trouble).
xor rcx, rcx
mov rcx, -1
The xor
is not useful, rcx
does not need to be zeroed before overwriting it for correctness reasons, and simply mov
-ing into a 64 (or 32) bit register already has no dependency on the previous value. By the way, when you do want to zero a 64bit register, you can use a 32bit xor
since writing to the low 32 bits of a register zeroes out the top half of the 64 bit register. There is not really an immediate performance difference, but using the 32bit version often lets you save the REX prefix, unless of course one of the "numbered registers" is an operand.
Because -x - 1= ~x + 1 - 1 = ~x
(using the definition of two's complement, -x = ~x + 1
) and you don't use the flags set by the sub
,
neg rcx
sub rcx, 1
mov rax, rcx
is equivalent to:
not rcx
mov rax, rcx
So all combined, this function could be simplified slightly to (assuming saving rdi
and rcx
is useful):
strlen:
push rdi
push rcx
mov rcx, -1
xor eax, eax
repne scasb
not rcx
mov rax, rcx
pop rcx
pop rdi
ret
xor al, al
. In general avoid partial register update like that \$\endgroup\$