AMD has an ABI specification that describes the calling convention to use on x86-64. All OSes follow it, except for Windows which has it's own x86-64 calling convention. Why?
Does anyone know the technical, historical, or political reasons for this difference, or is it purely a matter of NIHsyndrome?
I understand that different OSes may have different needs for higher level things, but that doesn't explain why for example the register parameter passing order on Windows is rcx - rdx …
我让Google告诉我该gcc选项的含义-fomit-frame-pointer,它将我重定向到以下声明.
-fomit帧指针
不要将帧指针保存在寄存器中以查找不需要的函数.这避免了保存,设置和恢复帧指针的指令; 它还在许多功能中提供额外的寄存器.它还使某些机器无法进行调试.
根据我对每个函数的了解,将在进程内存的堆栈中创建激活记录,以保留所有局部变量和更多信息.我希望这个帧指针意味着一个函数的激活记录的地址.
在这种情况下,什么是函数类型,它不需要将帧指针保持在寄存器中?如果我得到这个信息,我会尝试设计基于它的新函数(如果可能),因为如果帧指针没有保存在寄存器中,一些指令将在二进制中省略.在具有许多功能的应用程序中,这将显着提高性能.
LOOP(英特尔参考手动输入)递减ecx/rcx,然后如果非零则跳转.这很慢,但是英特尔不能廉价地把它变得很快吗? dec/jnz已经将宏观融合成 Sandybridge家族的一个 uop; 唯一的区别是设置标志.
loop关于各种微体系结构,来自Agner Fog的说明表:
Bulldozer-family/Ryzen:1 m-op(与宏观融合测试和分支相同,或者jecxz)
P4:4次(相同jecxz)
loope/ loopne).吞吐量= 4c(loop)或7c(loope/ne).loope/ loopne). 吞吐量=每5个循环一个,这是将循环计数器保留在内存中的瓶颈!jecxz只有2 uops,吞吐量与普通吞吐量相同jcc难道解码器不能像lea rcx, [rcx-1]/ 那样解码jrcxz吗?这将是3 uops.至少那是没有地址大小前缀的情况,否则它必须使用ecx和截断RIP,EIP如果跳转; 也许奇怪的地址大小选择控制减量的宽度解释了许多uops?
或者更好,只需将其解码为不设置标志的融合分支和分支? dec ecx …
例如,累加器被命名EAX,并且在调用指令指针时IP.我也知道有些字节叫做CL和DH.我知道所有名字都必须有一个约定,但它是什么?
我刚刚查看了彼得·科德斯(Peter Cordes)的回答,他说,
如果读取标志,则部分标志停顿会发生,如果它们确实发生的话。P4永远不会有部分标志停顿,因为它们永远不需要合并。相反,它具有错误的依赖关系。几个答案/评论混淆了术语。它们描述了一个错误的依赖关系,但随后将其称为部分标志停顿。这是由于仅写入一些标志而导致的速度下降,但是术语“部分标志停顿”是指必须合并部分标志写入时在SnB之前的Intel硬件上发生的情况。英特尔SnB系列CPU插入一个额外的uop来合并标志而不会停顿。Nehalem和更早的失速约7个周期。我不确定AMD CPU会受到多大的损失。
我感觉我还不明白什么是“部分国旗摊位”。我怎么知道一个人发生了?除了读取标志的某些时间之外,什么触发事件?合并标志是什么意思?在什么情况下会“写一些标志”,但不会发生部分标志合并?我需要了解哪些有关旗位的知识才能理解它们?
如果您正在编写程序集,那么将值分配给哪个寄存器重要吗?假设您将累积/中间值存储在 %ebx 而不是 %eax 中,后者传统上用于此目的。这是不好的做法吗?会影响性能吗?
换句话说,您是否可以将它们等同于存储空间,还是应该坚持将它们用于特定目的?
我注意到很多调用约定都坚持为被调用者保留[e] bx.
现在,我可以理解为什么他们会保留像[e] sp或[e] bp这样的东西,因为这会弄乱被调用者的堆栈.我也可以理解为什么你可能想要保留[e] si或[e] di,因为如果他们不是特别小心的话可以打破被调用者的字符串指令.
但是[e] bx?究竟是什么对[e] bx如此重要?是什么让[e] bx如此特殊以至于多个调用约定都坚持在整个函数调用中保留它?
是否有某种微妙的错误/陷阱可能会因为[e] bx的混乱而产生?
修改[e] bx对被调用者的影响是否比修改[e] dx或[e] cx更大?
我只是不明白为什么这么多的召唤惯例单独列出[e] bx进行保存.
x64寄存器是否可以互换,因为任何与它们组合使用的指令都可以与其他指令一起使用?除了名称之外,是否有性能差异或任何其他因素使它们彼此不同?
我一直在学习汇编,并且我读到四个主要的 x86 通用寄存器(eax、ebx、ecx 和 edx)每个都有一个预期或建议的用途。例如,eax 是累加器寄存器,ecx 用作循环的计数器,等等。大多数编译器是否尝试将寄存器用于建议的目的,或者他们是否忽略了寄存器“应该”的用途而只是将值分配给下一个可用的寄存器?
此外,在查看 x64 寄存器时,我注意到添加了额外的 8 个通用寄存器,如果忽略 rbp、rsp、rsi 和 rdi(因为它们具有非通用用途),gp 寄存器的总数将达到 12 ),如果包括它们,则为十六个。在普通用户程序(即浏览器、文字处理器等,而不是需要大量寄存器的密码程序)中,在任何给定时间通常使用多少这些寄存器?像 Firefox 这样的程序一次使用所有 12/16 个普通寄存器是常见的,还是因为它们没有足够的变量来填充它们而只使用一个子集?我将通过反汇编二进制文件来自己研究这个问题,看看一般情况是什么,但我希望能得到比我更有见识的人的回答。
此外,如果当前未用于非通用应用程序,编译器是否通常将 semi-gp 寄存器(rsi、rdi、rsp 和 rbp)用于通用用途?我很好奇,因为我看到这些寄存器被列为“通用”,但即使我能想到这些寄存器不能用于通用存储的实例(例如,你不想存储rbp 和 rsp 变量,然后将值推入堆栈!)。那么编译器会尽量使用这些寄存器吗?x86 和 x64 编译之间是否有区别,因为 x64 处理器有更多可用寄存器,因此没有必要将变量填充到任何可用寄存器中?
读完这个问题后为什么 %cl 是 sal 操作接受作为参数的唯一寄存器?,我很好奇还有哪些指令需要计数寄存器?
\n循环控制
\nLOOP 指令假定 CX、ECX 或 RCX 寄存器包含循环计数。
\nloop\nloopnz\nloopne\nloopz\nloope\nRun Code Online (Sandbox Code Playgroud)\n跳跃
\n如果 CX、ECX 或 RCX 寄存器为零,则执行跳转的跳转指令。
\njcxz\njecxz\njrcxz\nRun Code Online (Sandbox Code Playgroud)\n重复前缀
\n这些重复前缀与字符串指令一起使用并使用计数寄存器。
\nrep\nrepe\nrepz\nrepne\nrepnz\nRun Code Online (Sandbox Code Playgroud)\n位移位
\n逻辑、移位、循环和位指令(省略了双字和四字变体,例如 shld、shlq...)
\nshl\nshr\nsal\nsar\nrol\nror\nrcl\nrcr\nRun Code Online (Sandbox Code Playgroud)\nFastcall 调用约定
\n参数列表中从左到右找到的前两个 DWORD 或更小的参数被传入ECXEDX 寄存器;所有其他参数都在堆栈上从右向左传递。
__attribute__((fastcall)) void printnums(int num1, int num2, int num3){\n printf("The numbers you sent are: %d %d %d", num1, num2, num3);\n}\n\nint main(){\n printnums(1, 2, 3);\n return 0;\n}\nRun Code Online (Sandbox Code Playgroud)\nMicrosoft x64 …
assembly ×8
x86 ×6
x86-64 ×4
performance ×2
accumulator ×1
c ×1
compilation ×1
gcc ×1
history ×1
intel ×1
masm ×1
naming ×1
nasm ×1
optimization ×1
windows ×1