我正在为了娱乐学习一些汇编知识(目前在Windows下使用NASM),关于
我不认为这会有问题,而且它似乎可以工作,但我读过一些文章,讨论了
stdcall
calling convention和具有可变参数数量的函数,我有一个问题。例如,一个sum
函数,它接受X个整数并将它们全部加在一起。
由于调用方需要在使用< code> stdcall 时清除/重置堆栈,但您只能使用常量值< code> ret ,因此我一直在思考是否使用< code> pop 弹出返回地址,移动< code> esp 并跳回到调用者本身,而不是使用< code> ret 会出现任何问题。我认为这会更慢,因为它需要更多的指令,但它是否可行?
; 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
我不认为这会有问题,而且它似乎可以工作,但我读过一些文章,讨论了
stdcall
无法处理可变参数的问题,因为函数不知道要传递给 ret
的值。我有什么遗漏的吗?
stdcall
下切换到调用者清理。 - Jester(r/e)sp
,然后跳转到该寄存器的内容。性能可能会受到影响,因为你可能会混淆分支预测器的函数调用处理。显然需要可用的寄存器,因为你无法在以后恢复它。请注意,对堆栈指针进行add
或sub
操作会修改状态标志;使用lea
或pop
来避免这种情况。 - ecmpush reg
/ret
而不是jmp ebx
返回,分支预测就会很好(因为你仍然返回到相同的位置。返回地址预测通过假定调用/返回嵌套正确(一个类似于内部堆栈的硬件数据结构的返回地址),与ESP指向的位置无关)。 - Peter Cordes