11

使用以下代码,我遇到了一个有趣的情况:

    static void DivideByZero() {
      // volatile to prevent compiler optimizations.
      volatile float zero = 0.0f;
      volatile float result __attribute__((unused)) = 123.0f / zero;
    }
  DivideByZero();
  int raised = fetestexcept(FE_DIVBYZERO | FE_OVERFLOW);
  ASSERT_TRUE((raised & FE_DIVBYZERO) != 0);

当我在KVM支持下运行我的 qemu 设备时,我得到了以下结果:

 FE_DIVBYZERO !=0; //and it's ok

但是当我在没有KVM支持的情况下运行相同的源时:

 FE_DIVBYZERO ==0; //and it's not ok

据我了解这种情况,它发生了,因为在mxcsr寄存器位(由零除)未设置。但我不明白为什么没有设置这个位。有任何想法吗?

更新 :基于 qemu
的模拟器也发生了同样的情况。android

emulator -avd test -qemu  

返回:FE_DIVBYZERO !=0;

emulator -avd test -qemu -disable-kvm

返回:FE_DIVBYZERO ==0;

4

3 回答 3

3

MXCSR寄存器在 英特尔® 64 和 IA-32 架构软件开发人员手册中进行了描述

在现代 x86 处理器上,编译器将浮点运算映射为scalarSIMD,使用与vector (SSE)指令相同的资源。

MXCSR寄存器控制浮点指令scalarvector (SSE)浮点指令的操作。我在下面包含了描述 MXCSR 的相关部分。 MXCSR[9]是除零掩码,如果清零 (0),则 CPU 将在检测到除零时引发异常。当您在虚拟机中运行时,异常被“虚拟化”,它们由“管理程序”处理,在您的情况下为KVM。然后管理程序决定是否将异常反映回来宾虚拟机。我的理论是清除除零掩码,引发异常,KVM 和/或 QEMU 正在清除指示发生除零异常的标志MXCSR[2]并恢复您的虚拟机。因此,这很可能是 KVM/QEMU 中的一个错误。

您可以在 之前发出fegetexcept()DivideByZero()以查明除零异常是否被屏蔽 (1) 或不 (0)。如果它没有被屏蔽,那么你可以使用fedisableexcept()来屏蔽它。

MXCSR 控制/状态寄存器

于 2013-03-18T07:13:21.730 回答
1

当 QEMU 作为软件仿真工作时。

来自http://qemu.weilnetz.de/qemu-tech.html#intro_005fx86_005femulation

2.8 异常支持

当遇到除零等异常时使用 longjmp()。

主机 SIGSEGV 和 SIGBUS 信号处理程序用于获取无效的内存访问。通过重新翻译相应的基本块并查看主机程序计数器在异常点的位置来找到模拟程序计数器。

虚拟 CPU 无法检索确切的 EFLAGS 寄存器,因为在某些情况下,由于条件代码优化,它没有被计算。这不是一个大问题,因为模拟代码在任何情况下仍然可以重新启动。

看起来 FPU 状态也丢失了,指示什么是 FPU 异常的位 - 在您的情况下除以零/无效参数也丢失了。并且 QEMU 无法正确模拟 fetestexcept。因此结果。

当 QEMU 使用硬件虚拟化 - KVM 工作时,浮点异常得到正确处理 - 所以测试是正确的。

-- 编辑:其他可能性是异常模式的初始化方式不同:您可以尝试添加它以通过零异常启用 div。费用除外(FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW);

于 2013-03-21T16:44:34.683 回答
0

我不确定逻辑,但 QEMU 是软件实现,KVM 是硬件支持。因此,可能在软件实现中,由于浮点计算错误或存储浮点值而未设置该位。

于 2013-03-18T03:53:10.543 回答