Pet*_*des 10
向编译器询问想法:int64_t neg(int64_t a) { return -a; }以32位模式编译.当然,询问编译器的不同方式将在内存中,编译器选择寄存器或已在EDX:EAX中具有起始值.在Godbolt编译器资源管理器中查看所有三种方式,使用gcc,clang和MSVC(又名CL)的asm输出.
当然有很多方法可以实现这一点,但任何可能的序列在某些时候都需要某种从低到高的进位,因此没有有效的方法来避免SBB或ADC.
如果值在内存中开始,或者您希望将原始值保留在寄存器中,则将目标xor-zero并使用SUB/SBB.SysV x86-32 ABI在堆栈上传递args并在EDX:EAX中返回64位整数.这就是clang3.9.1的-m32 -O3作用,用于neg_value_from_mem:
; optimal for data coming from memory: just subtract from zero
xor eax, eax
xor edx, edx
sub eax, dword ptr [esp + 4]
sbb edx, dword ptr [esp + 8]
Run Code Online (Sandbox Code Playgroud)
如果在寄存器中有值并且不需要就地结果,则可以使用NEG将寄存器设置为0 - 本身,如果输入为非零则设置CF. 即SUB会采用相同的方式.请注意,xor-zeroing是便宜的,而不是延迟关键路径的一部分,所以这肯定比gcc的3指令序列(下面)更好.
;; partially in-place: input in ecx:eax
xor edx, edx
neg eax ; eax = 0-eax, setting flags appropriately
sbb edx, ecx ;; result in edx:eax
Run Code Online (Sandbox Code Playgroud)
Clang甚至为就地案件做到这一点,即使这需要额外费用mov ecx,edx.这对于具有零延迟mov reg,reg(Intel IvB +和AMD Zen)的现代CPU的延迟是最佳的,但不适用于融合域uops(前端吞吐量)或代码大小的数量.
gcc的序列很有趣,并不是很明显.它为就地案件保存了一个指令与铿锵声,但情况更糟.
; gcc's in-place sequence, only good for in-place use
neg eax
adc edx, 0
neg edx
; disadvantage: higher latency for the upper half than subtract-from-zero
; advantage: result in edx:eax with no extra registers used
Run Code Online (Sandbox Code Playgroud)
不幸的是,gcc和MSVC都使用它,即使xor-zero + sub/sbb会更好.
有关编译器的更完整图片,请查看这些函数的输出(在godbolt上)
#include <stdint.h>
int64_t neg_value_from_mem(int64_t a) {
return -a;
}
int64_t neg_value_in_regs(int64_t a) {
// The OR makes the compiler load+OR first
// but it can choose regs to set up for the negate
int64_t reg = a | 0x1111111111LL;
// clang chooses mov reg,mem / or reg,imm8 when possible,
// otherwise mov reg,imm32 / or reg,mem. Nice :)
return -reg;
}
int64_t foo();
int64_t neg_value_in_place(int64_t a) {
// foo's return value will be in edx:eax
return -foo();
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
396 次 |
| 最近记录: |