在x64上,每个推送指令是否推送8个字节的倍数?如果没有,它会推动多少?
另外,每个函数参数占用多少堆栈空间?
发布于 2016-10-28 17:22:08
不是,但实际上,一个人总是把一个8字节的值推到堆栈上。
函数参数消耗了不同数量的堆栈空间,这取决于函数参数的大小以及它是在堆栈中、寄存器中传递,还是通过引用传递。
如果通过推送传递堆栈中的函数参数,那么有方便的推送指令推送8字节这一事实强烈地暗示您将该参数作为8字节值传递。对于指针、int64和普通双打,这显然很容易。对于char、bool和其他内存大小较小的类型,大多数编译器所做的就是将值推到一个8字节的块中。需要16或32字节的类型可能会被编译器按下几个推送指令。较大的值往往不会通过推送来传递;编译器通常试图传递指向更大值的指针,而不是传递值本身。{我已经构建了一个编译器,它可以传递任意大的值,但它这样做是通过在堆栈中留出空间,然后执行块移动指令]。详细信息因编译器而异,并根据正在编译的程序的语言语义而有所不同。
一个非常聪明的编译器可能会注意到,有几个参数很小,可以打包成一个8字节的数量,只需要一次推送。我还没有见过真正这样做的人,可能是因为将这些值打包到一个寄存器中需要工作,而且通过设计和缓存,推送指令已经相当快了。
可以将较小的值推到堆栈上。根据体系结构,这是合法的,但如果推送的小值集不是8个字节的倍数,则可能会导致错误对齐访问性能的影响。然后,必须小心正确地弹出非多次值以恢复堆栈对齐。在我的经验中没有用(参见Peter的代码高尔夫评论)。
如果在寄存器中传递值,则不会推送任何内容:-}
您可能会安排将参数值存储在堆栈中已知的位置。那就没有任何推动:-}
发布于 2016-10-28 19:57:59
按64位模式推送操作数大小
堆栈上推送的值的大小和堆栈指针被调整的数量取决于PUSH指令的操作数大小。在64位模式下,操作数只能为16位或64位.在64位模式下编码32位推送指令是不可能的,也不可能在任何模式下编码8位推送指令。
例如,这些都是64位推送指令:
push rax
push 1 ; 8-bit immediate sign-extended to 64 bits
push 65536 ; 32-bit immediate sign-extended to 64 bits
push QWORD PTR[0]
push fs ; 16-bit segment register zero-extended to 64 bits上述指令均从RSP中减去8,然后将64位值写入RSP所指向的位置。
这些都是16位推送指令:
push ax
push WORD PTR[0]这些指令从RSP中减去2,然后将16位值写入RSP所指向的位置。因为他们严重错对堆栈,在64位模式下使用16位推送几乎总是一个错误。相反,您应该将16位值加载到寄存器中(如果还没有),根据需要对其进行扩展,然后使用64位推送。
下列指令是非法的,不能以64位模式编码:
push al
push eax
push BYTE PTR[0]
push DWORD PTR[0]
push 0100000000h ; 64-bit immediate value isn't supported在堆栈上推送8位或32位值需要将值加载到寄存器中,扩展它,然后使用64位推送,就像处理16位值一样。
参数以64位模式传递。
一般来说,在64位模式下,函数参数不会在堆栈上传递.微软和Linux 64位x86调用约定都在寄存器中传递大部分参数。只有当寄存器中没有足够的空间将参数传递给函数时,才使用堆栈。在这种情况下,每个参数占用一个或多个字节堆栈槽。请注意,编译器不一定使用推送指令将这些参数放到堆栈上。一种常见的策略是在堆栈上为函数序言中所有函数的传出参数分配足够的空间,然后根据需要使用MOV指令将参数放在堆栈上。
https://stackoverflow.com/questions/40305965
复制相似问题