【问题标题】:Assembly code x86汇编代码 x86
【发布时间】:2015-09-12 04:01:45
【问题描述】:

所以我对汇编代码和阅读它们都是个菜鸟

所以我有一个简单的 c 代码

void saxpy()
{
  for(int i = 0; i < ARRAY_SIZE; i++) {
  float product = a*x[i];
  z[i] = product + y[i];
}
}

以及使用

编译时的等效汇编代码
gcc -std=c99 -O3 -fno-tree-vectorize -S code.c -o code-O3.s 

给我以下汇编代码

saxpy:
.LFB0:
.cfi_startproc
movss   a(%rip), %xmm1
xorl    %eax, %eax
.p2align 4,,10
.p2align 3
.L3:
movss   x(%rax), %xmm0
addq    $4, %rax
mulss   %xmm1, %xmm0
addss   y-4(%rax), %xmm0
movss   %xmm0, z-4(%rax)
cmpq    $262144, %rax
jne .L3
rep ret
.cfi_endproc

我明白循环展开已经发生 但我无法理解背后的意图和想法

addq    $4, %rax
mulss   %xmm1, %xmm0
addss   y-4(%rax), %xmm0
movss   %xmm0, z-4(%rax)

谁能解释一下 4 的用法和 这些陈述是什么意思 y-4(%rax)

【问题讨论】:

  • 4 是浮点数的大小
  • 你能告诉我 y-4(%rax) 做了什么吗,假设我有循环运行 20 次。优化后我的静态和动态insns计数是多少?
  • 另外,向 rax 寄存器添加立即数 4 有什么必要。这是根据语句“addq $4, %rax”

标签: gcc assembly x86 intel compiler-optimization


【解决方案1】:

xyz 是全局数组。您遗漏了声明符号的清单末尾。

put your code on godbolt for you,定义了必要的全局变量(并修复了缩进)。看看底部。

顺便说一句,这里没有展开。每个标量单精度 mul 和 add 在循环中都有一个。尝试使用-funroll-loops 来查看它的展开。

With -march=haswell, gcc will use an FMA instruction。如果您通过省略 -fno-tree-vectorize 来解除编译器的瘫痪,并且 #define ARRAY_SIZE 很小,例如 100,它将完全展开循环,其中大部分是 32 字节 FMA ymm 指令,以一些 16 字节 FMA xmm 结束。

另外,将立即数 4 添加到 rax 寄存器有什么需要。 这是按照语句“addq $4, %rax”

循环将指针增加 4 个字节,而不是使用缩放索引寻址模式。


查看https://*.com/questions/tagged/x86 上的链接。此外,使用调试器单步执行代码通常是确保您了解它在做什么的好方法。

【讨论】:

  • 嘿彼得,我意识到这里没有展开,比我发布这个要晚得多。感谢您对 4 个字节的事情进行澄清。我得读很多这些东西。同时,您能告诉我除 gdb 之外逐步调试汇编代码的最佳方法吗?我可以使用任何其他编译器或方法或工具吗?
  • @sarath:任何调试器都应该能够单步执行。当上一条指令更改的寄存器被突出显示时,这很好。对于性能调优,性能计数器 (Linux perf) 可以很好地查看您的代码是否因意外而受到瓶颈。这取决于您要做什么,但是查看优化编译器的 asm 输出并弄清楚它们为什么这样做通常很有用。特别是。当您遇到他们无法优化的情况时,您可能会意识到您需要为编译器提供更多信息(__restrict__,对齐,无符号)