首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用64位MASM优化C函数调用

使用64位MASM优化C函数调用
EN

Stack Overflow用户
提问于 2021-08-14 23:08:11
回答 1查看 168关注 0票数 0

目前,使用此64位MASM代码调用C运行时函数(如memcmp( )。我记得这个约定来自一个关于优化的GoAsm文章

代码语言:javascript
复制
              memcmp          PROTO;:QWORD,:QWORD,:QWORD
              PUSH            RSP
              PUSH            QWORD PTR [RSP]
              AND             SPL,0F0h
              MOV             R8,R11
              MOV             RDX,R10
              MOV             RCX,RAX
              SUB             RSP,32
              CALL            memcmp
              LEA             RSP,[RSP+40]
              POP             RSP

下面这是一个有效的优化版本吗?

代码语言:javascript
复制
              memcmp          PROTO;:QWORD,:QWORD,:QWORD
              PUSH            RSP
              PUSH            QWORD PTR [RSP]
              AND             RSP,-16        ; new
              MOV             R8,R11
              MOV             RDX,R10
              MOV             RCX,RAX
              LEA             RSP,[RSP-32]   ; new
              CALL            memcmp
              LEA             RSP,[RSP+40]
              POP             RSP                  

替换的理由

代码语言:javascript
复制
              AND             SPL,0F0h

使用

代码语言:javascript
复制
              AND             RSP,-16

它避免调用部分寄存器更新。理解fastcall堆栈框架

顶替

代码语言:javascript
复制
              SUB             RSP,32

使用

代码语言:javascript
复制
              LEA             RSP,[RSP-32]

后续指令不依赖于通过减法更新的标志。

那么,不更新标志也会更有效。

为什么GCC要用"lea“而不是"sub”来减法呢?

在这种情况下,还有其他优化技巧吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-08-14 23:59:30

AND是的,原始代码很傻,没有保存任何代码大小(SPL也使用REX前缀,比如64位操作数大小)。

LEA --没有意义,而且浪费了代码大小: x86 CPU已经是通过寄存器重命名避免对标志的错误依赖了;这对于有效地运行普通的x86代码是必要的,后者充满了addsuband等指令。如果不是这样的话,编译器将更多地使用lea。关于这个问题的答案是错误的,应该被否决/删除。唯一的危险是在一些不太常见的CPU上(奔腾4和Silvermont,因为不同的原因),从inc这样的指令中只能写一些标志。(INC指令与添加1:这有关系吗?)。即使是inc在Silvermont家庭的成本也很小,只是额外的uop,而不是在解码期间,所以它不会停止。

add 在任何CPU上都不会比 lea 慢,无论是在CPU本身上,还是在对以后指令的影响方面。(除了lea运行得比add (在实际的AGU上)更早的无序Atom预-Silvermont之外,它可能更好,也可能更糟,这取决于数据的来源/去处)。在某些情况下,您只会使用lea,比如adc循环,实际上需要保持CF不变,以便下一次迭代可以读取它。也就是说,不要搞乱真正的依赖项(RAW),这与避免WAW (WAW)输出依赖无关。(请参见某些CPU上紧环ADC/SBB和INC/DEC的问题 --请注意,adc / inc / adc创建部分旗杆失速的情况是add会导致正确性问题的情况,因此我不将其计算为add会使以后的指令速度更快的情况。)

您可能不需要保存旧的RSP;ABI在调用之前需要16字节的堆栈对齐,其中包括调用方(除非您从不遵循ABI的代码中调用,所以您不知道RSP相对于16字节边界的对齐情况)。

通常,您只需要像编译器那样执行sub rsp, 40,以便重新对齐RSP并为阴影空间预留空间。(您可以在函数的顶部/底部,而不是在每个调用周围,以及保存/恢复调用保存寄存器时执行此操作)。

(实际上,memcmp不太可能关心堆栈对齐,除非它需要保存/恢复更多的XMM。Windows x64调用约定不明智地使用只有6个调用失败的x/ymm寄存器。,这可能有点紧,这取决于它们在手写(?)memcmp中展开循环的次数。

而且,即使您确实需要处理未知的传入RSP对齐,将RSP保存到两个不同的位置对于pop rsp来说仍然不是一种非常有效的方法。通常,您只需要使用RBP来创建一个传统的框架指针来清除mov rsp, rbp / pop rbp,而不管对RSP的未知调整如何。例如,即使在使用alloca的函数中(或者在asm中,执行未知数量的推送或可变大小的sub rsp,这与and rsp, -16实际上是一样的)。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68787630

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档