I'm learning a bit of assembly for fun (currently using NASM on Windows), and I have a question regarding the stdcall
calling convention and functions with variable numbers of arguments. For example, a sum
function that takes X integers and adds them all together.
Since the callee needs to clean/reset the stack when using stdcall
, but you can only use constant values with ret
, I've been wondering if there's anything wrong with pop
ping the return address, moving esp
, and jumping back to the caller yourself, instead of using ret
. I assume this would be slower, since it requires more instructions, but would it be acceptable?
; int sum(count, ...)
sum:
mov ecx, [esp+4] ; count
; calc args size
mov eax, ecx ; vars count
inc eax ; + count
mov edx, 4 ; * 4 byte per var
mul edx
mov edx, eax
xor eax, eax ; result
cmp ecx, 0 ; if count == 0
je .done
inc ecx ; count++, to start with last arg
.add:
add eax, [esp+4*ecx]
dec ecx ; if --ecx != 1, 0 = return, 1 = count
cmp ecx, 1
jnz .add
.done:
pop ebx
add esp,edx
jmp ebx
I don't see why this wouldn't be okay, and it appears to work, but I've read articles that talked about how stdcall
can't handle variable arguments, because the function can't know what value to pass to ret
. Am I missing something?
stdcall
. – Allround(r/e)sp
, then jump to that register's content. Performance may suffer also because you may confuse the function call handling of a branch predictor. The register needs to be available obviously because you cannot restore it later. Note thatadd
orsub
to the stack pointer would modify the status flags; uselea
orpop
to avoid that. – Earthenwarepush reg
/ret
instead ofjmp ebx
(because you still return to the same place. Return-address prediction works by assuming call/ret nest correctly (an internal stack-like hardware data structure of return addresses), nothing to do with where ESP points). – Incubator