我想知道在x86 IA32 Intel CPU上以用户模式设置/清除EFLAGS.TF的步骤
下面尝试清除TF标志,但遇到错误***** Unhandled interrupt vector *****
__asm__ volatile("pushl %eax\n\t"
"pushfl\n\t"
"popl %eax\n\t"
"xorl $0x0100, %eax\n\t"
"pushl %eax\n\t"
"popfl\n\t"
"popl %eax\n\t");
答案 0 :(得分:3)
XOR会翻转而不是始终清除它。 AND是一个选项,BTR (bit-test-reset)是另一个选项。具有寄存器目标的BTR在寄存器源的情况下确实很慢,但是立即使用(在Haswell上只有2 uops,在Skylake上只有3 uo。在AMD上最多4,但在{{{ 1}}。)
btr $9, %eax
相当慢(9微秒,在Skylake上每20个周期1个)。或在Ryzen上,每35个微循环35次。 (http://agner.org/optimize)。因此,优化周围的代码不会有太大的不同,但是找到一种使代码大小保持紧凑的方法很有趣。
您不需要自己保存/恢复EAX,只需告诉编译器您要使用popf
作为EAB列表来对其进行破坏,也可以使用虚拟输出操作数(请注意,我使用的是GNU C扩展的asm,不是基本的)。
: "eax"
或者根本不去碰任何寄存器:这也是非常有效的,尤其是在AMD Ryzen上,那里没有堆栈同步的uops和内存目标,并且是单寄存器的。
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.
);
}
对于较小的代码大小,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.
}
可能很好。在Haswell上仍然只有2微码(在Skylake上只有3微码),但是比btrl $9, (%esp)
小2个字节。 andl
的大小也相同,但是在andb $0xfe, 1(%esp)
之后使用时,在Intel上会导致存储转发停顿,并且是2 oups +堆栈同步uop。 push
的大小也相同,也为3微码(加上部分寄存器合并的uop,它本身在一个周期内在Haswell / SKL上发出)。但这在AMD上很好。
可移植性
顺便说一句,在x86-64 System V用户空间代码中,如果不破坏编译器的红色区域,就不能安全地进行推送/弹出操作,因此您可能希望在pop %%eax; and $0xfe, %ah; push %eax
之前add $-128, %rsp
,并且之后将其恢复。
在内核代码中没有红色区域,因此内联asm中的push / pop很好。
Windows使用不同的ABI,没有红色区域。
答案 1 :(得分:1)
使用以下代码可以正常工作。谢谢
__asm__ volatile("pushl %eax;\
pushfl;\
popl %eax;\
andl $0xFFFFFEFF, %eax;\
pushl %eax;\
popfl;\
popl %eax;"
);