3

长期 MSVC 用户,gcc 新手(请耐心等待)。

我在 Windows 7 上使用 rubenvb 版本的 c++(请参阅主题中的版本,是的,我正在为 64 位构建)并且我在使用 _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

当我使用参数 8 运行 iTot.exe 时,我预计 _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]

怎么回事?为什么 -S 对我撒谎?

就像我说的,我是 gcc 的新手,所以如果我只是在做一些愚蠢的事情,请对我温柔一点。谢谢。

4

1 回答 1

1

好的,我想我已经回答了我自己的问题。感谢 Joachim PileBorg 让我看看定义在哪里,以及 Alexey Frunze 指出参数不能倒退。

虽然我对 gcc 太陌生,无法权威地说出这一点,但我认为 winnt.h 中 _BitScanForward64 的定义是非常错误的。

当前定义:

__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),我可以报告这两个问题,以便(希望)为每个人解决这个问题。

于 2013-03-20T19:31:53.693 回答