_BitScanForward64 возвращает неверный ответ в C++. Exe (rubenvb-4.7.2-release)

Долгое время пользователь MSVC, новичок в gcc (так что терпите меня).

Я использую версию C++ rubenvb (см. Версию в теме, да, я строю для 64-битной) на Windows 7, и у меня возникла проблема с использованием _BitScanForward64. Пример кода выглядит так:

int __cdecl main(int argc, char* argv[])
{
    DWORD d = (DWORD)atoi(argv[1]);

    DWORD ix, ix2;
    ix2 = _BitScanForward64(&ix, d);
    printf("bsf %u %u\n", ix, ix2);
}

Я собираю с:

"C: \ Program Files \ gcc2 \ mingw64 \ bin \ C++. Exe" -o iTot.exe -mno-ms-bitfields -march=native -momit-leaf-frame-pointer -mwin32 -Os -fomit-frame-pointer -m64 -msse4 -mpopcnt -D WINDOWS main.cpp

Когда я запустил iTot.exe, используя параметр 8, я ожидал, что _BitScanForward64 установит для ix значение 3. Это то, что делает MSVC. Однако ix равен 0, а ix2 равен 1.

Также, глядя на ассемблера, я вижу:

bsfq QWORD PTR 44[rsp],rax   # MEM[(volatile LONG64 *)&ix], Mask

При таких обстоятельствах, почему gcc заставляет память писать + читать здесь?

Итак, несколько вопросов:

  1. _BitScanForward64 так или иначе должен вызываться по-другому в gcc? Если бы я просто назвал это неправильно, это было бы полезно знать (хотя несовместимость с MSVC была бы болью).
  2. Почему встроенная функция _BitScanForward64 вызывает запись в память?
  3. Взглянув на вывод ассемблера из -S, я не увидел ничего плохого в генерируемом коде. Однако, используя objdump.exe -d -Mintel, я вижу, что вместо того, чтобы использовать приведенный выше код asm (который, похоже, будет работать), на самом деле получилось обратное:

    bsf rax, QWORD PTR [rsp + 0x2c]

WTF? Почему-лжет мне?

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

1 ответ

Решение

Хорошо, я думаю, что ответил на свои вопросы. Спасибо Joachim PileBorg, который заставил меня посмотреть, где было определение, и Алексею Фрунзе, который указал, что параметры не могут быть обратными.

Хотя я слишком новичок в gcc, чтобы говорить это авторитетно, я считаю, что определение _BitScanForward64 в winnt.h очень неверно.

Текущее определение:

__CRT_INLINE BOOLEAN _BitScanForward64(DWORD *Index,DWORD64 Mask) {
  __asm__ __volatile__("bsfq %1,%0" : "=r" (Mask),"=m" ((*(volatile LONG64 *)Index)));
  return Mask!=0;
}

Мое определение:

__CRT_INLINE BOOLEAN BSF(DWORD *Index,DWORD64 Mask) {
  LONG64 t;
  __asm__ ("bsfq %0,%1" : "=r" (Mask),"=r" (t));
  *Index = t;
  return Mask!=0;
}

Обратите внимание на удаление (ненужной) volatile, обращение параметров к bsfq, изменение с =m на =r и т. Д. По сути, кажется, что это определение настолько неверно, насколько это возможно, и все еще компилируется.

Я предполагаю, что человек, который написал это, посмотрел на прототип для BitScanForward64 и "знал", что один из параметров должен быть памятью, и, поскольку единственным, который может быть памятью для BSF, является p2, именно это он и сделал. Как написано, код будет читать неписанное содержимое p2 и сканировать его на биты. Компилируется, но выдает неправильный ответ.

Итак, чтобы принять мои вопросы по порядку:

  1. Нет, я не назвал это неправильно. Определение в winnt.h просто неверно. На самом деле, в этом файле, вероятно, есть куча с похожей проблемой (_BitScanForward, _BitScanForward64, _BitScanReverse, _BitScanReverse64 и т. Д.).
  2. Это вызывает запись в память, потому что код в winnt.h был неправильным. Мое предлагаемое изменение не вызывает никаких обращений к памяти.
  3. -S неправильно записывает выходной файл (у objdump все правильно). Использование моего определения выше дает:

    call    atoi
    lea rcx, .LC0[rip]
    /APP
    # 7 "m.cpp" 1
    bsfq rax,rdx
    /NO_APP
    call    printf
    

И это не то, что на самом деле в исполняемом файле. Фактический исполняемый файл содержит (правильное) определение:

bsfq rdx,rax

Хотя я не в восторге от изменения файлов заголовков системы, похоже, что это будет мой ответ здесь. Если кто-то знает, как и где сообщить об этой проблеме, чтобы она была исправлена ​​(как я уже говорил, я использую reubenvb), я мог бы сообщить об этих двух проблемах, так что (надеюсь) это будет исправлено для всех.

Другие вопросы по тегам