c++ - 在 C++ 中获取 uint64_t 的上半部分的指令/内在函数?
问题描述
想象以下代码:
uint64_t x = 0x81C6E3292A71F955ULL;
uint32_t y = (uint32_t) (x >> 32);
y
接收 64 位整数的高 32 位部分。我的问题是是否存在任何内在函数或任何 CPU 指令在单个操作中执行此操作而不进行移动和移位?
至少 CLang(在上面的 Try-it-online 中链接)为此创建了两条指令mov rax, rdi
,shr rax, 32
因此 CLang 不进行此类优化,或者不存在此类特殊指令。
如果存在像movhi dst_reg, src_reg
.
解决方案
如果有更好的方法来为任意 uint64_t 执行此位域提取,编译器将已经使用它。(至少在理论上;编译器确实错过了优化,他们的选择有时有利于延迟,即使它花费更多的微指令。)
您只需要内在函数来处理无法在纯 C 中有效表达的内容,编译器已经可以轻松理解这些内容。 (或者如果你的编译器很笨,无法发现明显的东西。)
您可能会想象输入值来自两个 32 位值的乘积的情况,那么在某些 CPU 上,编译器可能值得使用扩展mul r32
来在两个单独的 32 位寄存器中生成结果,而不是imul r64, r64
+ shr reg,32
,如果它可以轻松使用 EAX/EDX。但是除了gcc -mtune=silvermont
其他调整选项之外,您不能让编译器那样做。
shr reg, 32
具有 1 个周期延迟,并且可以在大多数现代 x86 微架构 ( https://uops.info/ )上的多个执行端口上运行。唯一可能希望的是它可以将结果放在不同的寄存器中,而不会覆盖输入。
大多数现代非 x86 ISA 都与 RISC 类似,带有 3 操作数指令,因此移位指令可以复制和移位,这与 x86 移位不同,x86 移位中编译器需要 amov
以及shr
以后还需要原始 64 位值,或(在小函数的情况下)需要不同寄存器中的返回值。
并且一些 ISA 具有位域提取指令。PowerPC 甚至有一个有趣的旋转和掩码指令 ( rlwinm
)(掩码是立即数指定的位范围),它是与正常移位不同的指令。编译器将酌情使用它 - 不需要内在的。 https://devblogs.microsoft.com/oldnewthing/20180810-00/?p=99465
带有BMI2rorx rax, rdi, 32
的 x86 必须复制和旋转,而不是在同一个寄存器中卡住移位。uint32_t
在不内联的独立版本中,返回的函数可以/应该使用它而不是 mov+shr,因为调用者已经必须忽略 RAX 中的高垃圾。(x86-64 System V 和 Windows x64 都将返回值定义为仅与 arg 的 C 类型匹配的寄存器宽度;例如,返回uint32_t
意味着 RAX 的高 32 位不是返回值的一部分,可以保存任何内容。通常它们为零,因为写入 32 位寄存器隐式地零扩展为 64,但类似return bar()
where bar 返回 uint64_t 可以让 RAX 保持不变而不必截断它;事实上,优化的尾调用是可能的。)
没有内在的 for rorx
; 编译器应该知道何时使用它。(但 gcc/clang-O3 -march=haswell
错过了这个优化。) https://godbolt.org/z/ozjhcc8Te
如果编译器在循环中执行此操作,它可以32
在寄存器中shrx reg,reg,reg
作为复制和移位。或者更愚蠢的是,它可以用作pext
面具0xffffffffULL << 32
。shrx
但由于延迟更高,情况更糟。
AMD TBM(仅限 Bulldozer 系列,而非 Zen)具有bextr
(bitfield-extract) 的直接形式,它以 1 uop ( https://agner.org/optimize/ ) 高效运行。 https://godbolt.org/z/bn3rfxzch显示 gcc11 -O3 -march=bdver4
(Excavator) uses bextr rax, rdi, 0x2020
,而 clang 错过了优化。 gcc -march=znver1
使用 mov + shr 因为 Zen 删除了 Trailing Bit Manipulation 以及 XOP 扩展。
标准 BMI1bextr
需要寄存器中的位置/长度,而在英特尔 CPU 上是 2 微指令,所以它是垃圾。它确实有内在的,但我建议不要使用它。 mov
+shr
在 Intel CPU 上更快。
推荐阅读
- c# - DinkToPdf Net Core 无法加载 DLL 文件
- google-sheets - Summarizing array of existing items and amounts from a range
- python-3.x - While practicing a program in python that basically finds all the prime multiples of any number, I got stuck here in a while loop
- konvajs - 如何使用 Konva.Text 显示分数
- android - 是否有浮雕文本视图的技巧(在 api 25 之后)
- c++ - How can I understand these symbols in a C++ code?
- python - extracting name, email and number and save it into a variable
- flutter - CurrentIndex is not updated in the Dots_Indicator PageView in flutter
- reactjs - 如何使用 redux 状态变量进行身份验证
- dns - IPv6 的动态 DNS 导致找不到 IP 地址