Подтвердить что ты не робот

Qemu, div на ноль, регистр mxcsr

Я получил интересную ситуацию со следующим кодом:

    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);

Когда я запускаю свое устройство qemu с поддержкой KVM, я получил следующие результаты:

 FE_DIVBYZERO !=0; //and it ok

Но когда я запускаю тот же источник без поддержки KVM:

 FE_DIVBYZERO ==0; //and it not ok

Как я понимаю эту ситуацию, это происходит, потому что в mxcsr бит регистра (div на ноль) не установлен. Но я не понимаю, почему этот бит не установлен. Есть идеи?

ОБНОВЛЕНИЕ:
Такая же ситуация для эмулятора android основана на qemu.

emulator -avd test -qemu  

return: FE_DIVBYZERO!= 0;

emulator -avd test -qemu -disable-kvm

return: FE_DIVBYZERO == 0;

4b9b3361

Ответ 1

Регистр MXCSR описан в Руководство разработчика программного обеспечения для разработчиков Intel® 64 и IA-32

В современных процессорах x86 компилятор отображает операции с плавающей запятой как scalar SIMD, используя те же ресурсы, что и команды vector (SSE).

Регистр MXCSR управляет работой команд scalar и vector (SSE) с плавающей запятой. Я включил соответствующий раздел, описывающий MXCSR ниже. MXCSR[9] - это Divide-by-Zero Mask, если она очищена (0), тогда CPU будет вызывать исключение, когда обнаружен Divide by Zero. Когда вы работаете на виртуальной машине, исключения "виртуализированы", они обрабатываются "гипервизором", KVM в вашем случае. Затем гипервизор решает, следует ли отражать исключение обратно в гостевую виртуальную машину. Моя теория заключается в том, что маска Div-by-Zero очищается, исключение возбуждается, KVM и/или QEMU очищает флаг, который указывает, что деление на нулевое исключение произошло MXCSR[2] и возобновляет вашу виртуальную машину. Вероятно, это ошибка в KVM/QEMU.

Вы можете выпустить fegetexcept() перед DivideByZero(), чтобы узнать, есть ли Divide-By- Исключение Zero замаскировано (1) или нет (0). Если он не замаскирован, вы можете использовать fedisableexcept(), чтобы замаскировать его.

MXCSR Control/Status Register

Ответ 2

Когда QEMU работает как эмуляция программного обеспечения.

Из http://qemu.weilnetz.de/qemu-tech.html#intro_005fx86_005femulation.

2.8 Поддержка исключений

longjmp() используется, когда встречается исключение, такое как деление на ноль.

Обработчики сигналов хоста SIGSEGV и SIGBUS используются для получения недопустимых обращений к памяти. Имитационный программный счетчик найден путем повторной передачи соответствующего базового блока и поиска, где счетчик хост-программы находился в точке исключения.

Виртуальный ЦП не может получить точный регистр EFLAGS, потому что в некоторых случаях он не вычисляется из-за оптимизации кода условия. Это не является большой проблемой, потому что эмулированный код все же можно перезапустить в любом случае.

Похоже, что состояние FPU также потеряно, а биты, указывающие, что такое исключение FPU - деление на ноль/недопустимые аргументы в вашем случае, теряются. И QEMU не смог правильно подражать fetestexcept. Следовательно, результат.

Когда QEMU работает с использованием аппаратной виртуализации - KVM, исключения с плавающей запятой обрабатываются должным образом, поэтому тест правильный.

- изменить: Другая возможность заключается в том, что режим исключений инициализируется по-разному: Вы можете попытаться добавить это, чтобы включить исключение div на ноль. feenableexcept (FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW);

Ответ 3

Я не уверен в логике, но QEMU - это программная реализация, а KVM - аппаратная поддержка. Так может быть и в программной реализации бит не установлен из-за неверного вычисления с плавающей запятой или сохранения значения с плавающей запятой.