1

我想知道在 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");
4

2 回答 2

3

XOR 翻转一点而不是总是清除它。AND 是一种选择,BTR(位测试重置)是另一种选择。带有内存目标的 BTR 使用寄存器源确实很慢,但立即数一点也不差(Has​​well 上只有 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, %rspbeforepush并在之后恢复它。

在内核代码中没有红色区域,因此内联 asm 中的 push/pop 很好。

Windows 使用不同的 ABI,没有红色区域。

于 2018-07-20T09:30:09.617 回答
1

使用下面的代码,它工作正常。谢谢

  __asm__ volatile("pushl %eax;\
                    pushfl;\
                    popl %eax;\
                    andl $0xFFFFFEFF, %eax;\
                    pushl %eax;\
                    popfl;\
                    popl %eax;"
                    );
于 2018-07-20T06:29:39.723 回答