XOR 翻转一点而不是总是清除它。AND 是一种选择,BTR(位测试重置)是另一种选择。带有内存目标的 BTR 使用寄存器源确实很慢,但立即数一点也不差(Haswell 上只有 2 微指令,Skylake 上只有 3 微指令。不过,AMD 上最多 4 微指令,即使是 .)也需要 2 微指令btr $9, %eax
。)
popf
非常慢(9 uops,在 Skylake 上每 20 个周期 1 个)。或者在 Ryzen 上,35 微指令,每 13 个周期一个。(http://agner.org/optimize)。所以优化周围的代码不会有很大的不同,但是找到一种方法来保持代码大小的紧凑是很有趣的。
您不需要自己保存/恢复 EAX,只需告诉编译器您想将其: "eax"
作为 clobber 列表进行破坏,或使用虚拟输出操作数(请注意,我使用的是 GNU C 扩展 asm,而不是基本的)。
static inline
void clear_tf(void) {
long dummy; // there's no type that's always 32-bit on 32-bit, and always 64 on 64-bit. x32 uses 32-bit pointers in long mode so uintptr_t or size_t doesn't work.
// if porting to x86-64 System V user-space: beware that push clobbers the red-zone
__asm__ volatile("pushf \n\t"
"pop %[tmp] \n\t"
"btr $9, %[tmp]\n\t" // reset bit 9
"push %[tmp] \n\t"
"popf"
: [tmp] "=r"(dummy)
: // no inputs
: // no clobbers. // "memory" // would block reordering with loads/stores.
);
}
或者干脆不碰任何寄存器:这也非常有效,尤其是在没有堆栈同步微指令和内存目标并且是单微指令的 AMD Ryzen 上。
static inline
void clear_tf(void) {
// if porting to x86-64 System V user-space: beware that push clobbers the red-zone
__asm__ volatile("pushf \n\t"
"andl $0xFFFFFEFF, (%esp) \n\t" // 1 byte larger than the pop/btr/push version
"popf"
);
// Basic asm syntax: no clobbers.
}
对于较小的代码大小,btrl $9, (%esp)
可能是好的。Haswell 上仍然只有 2 个微指令(Skylake 上 3 个),但比andl
. andb $0xfe, 1(%esp)
大小也相同,但会导致存储转发停止,并且在push
. pop %%eax; and $0xfe, %ah; push %eax
也是相同的大小,还有 3 个微指令(加上一个在 Haswell / SKL 上循环发出的部分寄存器合并微指令)。但是在AMD上很好。
可移植性
顺便说一句,在 x86-64 System V 用户空间代码中,如果不破坏编译器的红色区域,您将无法安全地推送/弹出,因此您可能想要add $-128, %rsp
beforepush
并在之后恢复它。
在内核代码中没有红色区域,因此内联 asm 中的 push/pop 很好。
Windows 使用不同的 ABI,没有红色区域。