【发布时间】:2020-03-19 17:46:09
【问题描述】:
只有一种情况__builtin_clz 给出了错误的答案。我很好奇是什么导致了这种行为。
当我使用文字值 0 时,我总是按预期得到 32。但是 0 作为变量会产生 31。为什么存储值 0 的方法很重要?
我参加了建筑课程,但不了解差异程序集。看起来当给定文字值 0 时,即使没有优化,程序集也总是有 32 个硬编码的正确答案。使用 -march=native 时计算前导零的方法不同。
This post 关于用_BitScanReverse 模拟__builtin_clz 和bsrl %eax, %eax 行似乎暗示位扫描反向不适用于0。
+-------------------+-------------+--------------+
| Compile | literal.cpp | variable.cpp |
+-------------------+-------------+--------------+
| g++ | 32 | 31 |
| g++ -O | 32 | 32 |
| g++ -march=native | 32 | 32 |
+-------------------+-------------+--------------+
literal.cpp
#include <iostream>
int main(){
int i = 0;
std::cout << __builtin_clz(0) << std::endl;
}
变量.cpp
#include <iostream>
int main(){
int i = 0;
std::cout << __builtin_clz(i) << std::endl;
}
g++ -S [in name] -o [out name] 的差异
1c1
< .file "literal.cpp"
---
> .file "variable.cpp"
23c23,26
< movl $32, %esi
---
> movl -4(%rbp), %eax
> bsrl %eax, %eax
> xorl $31, %eax
> movl %eax, %esi
g++ 的差异 -march=native -S [in name] -o [out name]
1c1
< .file "literal.cpp"
---
> .file "variable.cpp"
23c23,25
< movl $32, %esi
---
> movl -4(%rbp), %eax
> lzcntl %eax, %eax
> movl %eax, %esi
g++ 的差异 -O -S [in name] -o [out name]
1c1
< .file "literal.cpp"
---
> .file "variable.cpp"
【问题讨论】:
-
@Matt 未定义行为意味着所有赌注都已取消。任何事情都有可能发生。出人意料的结果毫无意义。
-
请注意
bsrl %eax,%eax确实适用于eax=0,但结果是目标寄存器保持不变并根据输入设置ZFi> 为零。这是whybsrhas a false dependency。 AMD 记录了 Intel 和 AMD 实现的 dst-unmodified 行为,但 Intel 没有;他们说输出寄存器的值是任意的。 felixcloutier.com/x86/bsr。 (但不像 C++ UB;它不会导致周围指令出现不可预知的行为。)
标签: c++ gcc assembly undefined-behavior intrinsics