gpt4 book ai didi

c++ - 为什么 MSVC 在执行此位测试之前会发出无用的 MOVSX?

转载 作者:IT老高 更新时间:2023-10-28 22:25:24 25 4
gpt4 key购买 nike

在 MSVC 2013 中编译以下代码,64 位发行版,/O2优化:

while (*s == ' ' || *s == ',' || *s == '\r' || *s == '\n') {
++s;
}

我得到了以下代码——它使用 64 位寄存器作为查找表进行了非常酷的优化,带有 bt (位测试)指令。

    mov     rcx, 17596481020928             ; 0000100100002400H
npad 5
$LL82@myFunc:
movzx eax, BYTE PTR [rsi]
cmp al, 44 ; 0000002cH
ja SHORT $LN81@myFunc
movsx rax, al
bt rcx, rax
jae SHORT $LN81@myFunc
inc rsi
jmp SHORT $LL82@myFunc
$LN81@myFunc:
; code after loop...

但我的问题是:movsx rax, al 的目的是什么?在第一个分支之后?

首先我们从字符串中加载一个字节到rax并对其进行零扩展:

movzx eax, BYTE PTR [rsi]

然后是 cmp/ja pair 在 al 之间执行 无符号 比较和 44 , 如果 al 则向前分支更大。

所以现在,我们知道 0 <= al <= 44在无符号数中。因此,al 的最高位无法设置!

不过,下一条指令是 movsx rax, al .这是一个符号扩展的举动。但是因为:

  • alrax 的最低字节
  • 我们已经知道 rax 的其他 7 个字节归零
  • 我们刚刚证明了 al的最高位不可能被设置

这个 movsx必须是空操作。

为什么 MSVC 会这样做?我假设它不是用于填充,因为在这种情况下另一个 npad会使意思更清楚。是刷新数据依赖还是什么?

(顺便说一句,这个 bt 优化真的让我很高兴。一些有趣的事实:它的运行时间是你可能期望的 4 个 cmp/je 对的 0.6 倍,这是 方式strspnstd::string::find_first_not_of 快,并且它只发生在 64 位构建中,即使感兴趣的字符的值低于 32。)

最佳答案

您肯定认识到,这种优化是由优化器中用于查找该模式的非常具体的代码产生的。只是位掩码的生成就泄露了它。是的,不错的技巧。

这里有两个基本的代码生成案例。第一个是更通用的,其中 (charmax - charmin <= 64) 但charmax >= 64。优化器需要生成与您看到的不同的代码,它需要减去charmin。该版本没有有 MOVSX 指令。您可以通过将 *s == ' ' 替换为 *s == 'A' 来查看它。

然后是您测试的特殊情况,所有要测试的字符代码恰好 < 64。Microsoft 程序员确实在他的代码中处理了这个问题,他确保不会生成愚蠢的 SUB EAX,0 指令。但忽略了生成 MOVSX 是不必要的。在一般情况下仅检查最佳代码肯定会错过。代码中还有一个通用的函数调用,很容易被忽略,注意当你用/J 编译时指令是如何变成 MOVZX 的。否则很容易被认为是必要的,没有 BT 指令将 8 位寄存器作为第二个操作数,因此 AL 寄存器加载本身是不够的。

可能存在一个假设的优化器后优化器,用于优化优化器生成的优化代码。并决定保留 MOVSX 以改进超标量执行。我严重怀疑它的存在。

关于c++ - 为什么 MSVC 在执行此位测试之前会发出无用的 MOVSX?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26124620/

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