gpt4 book ai didi

c - 整数加法中多余的中间存储指令

转载 作者:太空宇宙 更新时间:2023-11-04 02:36:00 26 4
gpt4 key购买 nike

背景:-O3 优化级别的 GCC 6.1 生成此程序集:

    test    ecx, ecx
je .L8
xor r8d, r8d
xor eax, eax

.L7:
xor r9d, r9d
add eax, DWORD PTR [rdi+r8*4] ; adding a[i]
setc r9b
add eax, DWORD PTR [rsi+r8*4] ; adding b[i]
mov r11d, eax ; excessive mov (#1)
setc r10b
mov DWORD PTR [rdx+r8*4], r11d ; storing at s[i]
add r8, 1
movzx r10d, r10b
cmp ecx, r8d
lea eax, [r10+r9] ; sorcery (#2)
ja .L7
rep ret

.L8:
xor eax, eax
ret

对于这个函数:

limb_t add(
const limb_t *a,
const limb_t *b,
limb_t *s,
int n
) {
limb_t c = 0, t = 0;

for (int i = 0; i < n; ++i) {
t = a[i] + c;
c = t < a[i];

t += b[i];
c += t < b[i];

s[i] = t;
}

return c;
}

其中abs是等长n肢体的整数,在内存中存储为a Little Endian 中 32 位单元(肢、数字)的连续序列(即,第一个是最低有效肢)。

此函数将两个非负加数ab相加,将和存储在s中,并返回进位c 。临时变量 t 保存当前总和,并启用 a == sb == s 场景。

正如我从汇编中推断的那样,

  • rdi 寄存器保存a 加数的基址,
  • rsi寄存器保存b加数的基址,
  • rdx 寄存器保存s的基址,求和,
  • eaxc carry 和 t 临时注册帐户
  • r8 寄存器保存i,循环计数器,
  • ecx 寄存器保存 n,加数和总和的四肢长度。

我的第一个问题是:

<强>1。为什么 eax 寄存器值在 r11d 寄存器中的中间存储在将其移动到内存 [rdx + r8*4] 之前发生(当前分支和)?

我没有看到 r11 寄存器的任何其他用法,但用于这种过多的存储操作;而 mov 指令实际上允许从 eax 寄存器中移动,为什么不从那里移动值呢?


我的第二个问题是:

<强>2。这个带有 lea 指令和进位值的魔法是什么?

    lea     eax, [r10+r9]                 ; sorcery (#2)

它实际计算的是什么? lea = r10 + r9?而且,在这种情况下,为什么我们必须使用此 movzx 指令在每次循环迭代时清除 r10 的高位?

    movzx   r10d, r10b

最佳答案

第一个似乎是“错过优化”编译器错误。

第二个需要产生c t = a[i] + c; 的双字大小它通过使用不同的方法扩展两个逻辑值来做到这一点,这无疑有点奇怪:

t < a[i];xor r9d, r9d 执行和 setc r9b但是t < b[i]setc r10b 执行和 movzx r10d, r10b一对。这不是很明显,但这可能有合理的指令调度原因。

将两个逻辑值相加是由 lea eax, [r10+r9] 完成的用来代替 add有两个原因。首先,它不影响标志,所以它可以插入 cmp 之间和 ja .其次,它可以在第三个寄存器中产生输出。

另一种可能性是先将两个逻辑值相加,然后只扩展结果。不确定这是否是更好的方法。另外,一个临时寄存器就足够了。

关于c - 整数加法中多余的中间存储指令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37368777/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com