_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 заставляет память писать + читать здесь?
Итак, несколько вопросов:
- _BitScanForward64 так или иначе должен вызываться по-другому в gcc? Если бы я просто назвал это неправильно, это было бы полезно знать (хотя несовместимость с MSVC была бы болью).
- Почему встроенная функция _BitScanForward64 вызывает запись в память?
Взглянув на вывод ассемблера из -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 и сканировать его на биты. Компилируется, но выдает неправильный ответ.
Итак, чтобы принять мои вопросы по порядку:
- Нет, я не назвал это неправильно. Определение в winnt.h просто неверно. На самом деле, в этом файле, вероятно, есть куча с похожей проблемой (_BitScanForward, _BitScanForward64, _BitScanReverse, _BitScanReverse64 и т. Д.).
- Это вызывает запись в память, потому что код в winnt.h был неправильным. Мое предлагаемое изменение не вызывает никаких обращений к памяти.
-S неправильно записывает выходной файл (у objdump все правильно). Использование моего определения выше дает:
call atoi lea rcx, .LC0[rip] /APP # 7 "m.cpp" 1 bsfq rax,rdx /NO_APP call printf
И это не то, что на самом деле в исполняемом файле. Фактический исполняемый файл содержит (правильное) определение:
bsfq rdx,rax
Хотя я не в восторге от изменения файлов заголовков системы, похоже, что это будет мой ответ здесь. Если кто-то знает, как и где сообщить об этой проблеме, чтобы она была исправлена (как я уже говорил, я использую reubenvb), я мог бы сообщить об этих двух проблемах, так что (надеюсь) это будет исправлено для всех.