【发布时间】:2015-10-09 14:41:43
【问题描述】:
我有一个正确运行的 SSE 代码,我通常为 Win64 编译(我使用 Intel C++ Compiler 14)。此代码(由 SSE 内部函数组成)在完成后还会执行性能计数操作。当我为 Win32 编译相同的代码时,我遇到了这个操作的问题。
操作简单:
LARGE_INTEGER Count;
QueryPerformanceCounter( &Count );
uint64_t v = Count.QuadPart;
printf( "%llu\n", v );
printf( "%f\n", (double) v );
第一个 printf 打印一个正确的 64 位值。第二个 printf 产生 -1.#IND00.
如果我手动分配 v,错误就会消失。
针对可能的缓冲区不足/溢出和未初始化的访问检查了代码。不知道出了什么问题。 Win64上没有这样的错误。
编译器生成以下代码:在该块上:
;;; LARGE_INTEGER Count;
;;; QueryPerformanceCounter( &Count );
lea eax, DWORD PTR [1408+esp] ;152.1
push eax ;152.1
call DWORD PTR [__imp__QueryPerformanceCounter@4] ;152.1
; LOE ebx esi
.B1.94: ; Preds .B1.93
;;; uint64_t v = Count.QuadPart;
mov eax, DWORD PTR [1408+esp] ;153.14
mov edi, DWORD PTR [1412+esp] ;153.14
mov DWORD PTR [24+esp], eax ;153.14
;;; printf( "%llu\n", v );
push edi ;154.1
push eax ;154.1
push OFFSET FLAT: ??_C@_05A@?$CFllu?6?$AA@ ;154.1
call _printf ;154.1
; LOE ebx esi edi
.B1.344: ; Preds .B1.94
add esp, 12 ;154.1
; LOE ebx esi edi
.B1.95: ; Preds .B1.344
;;; printf( "%f\n", (double) v );
mov DWORD PTR [esp], OFFSET FLAT: ??_C@_03A@?$CFf?6?$AA@ ;155.1
mov eax, DWORD PTR [24+esp] ;155.1
mov DWORD PTR [32+esp], eax ;155.1
mov DWORD PTR [36+esp], edi ;155.1
fild QWORD PTR [32+esp] ;155.1
shr edi, 31 ;155.1
fadd QWORD PTR [_2il0floatpacket.1575+edi*8] ;155.1
fstp QWORD PTR [4+esp] ;155.1
call _printf ;155.1
但是,如果我在第二次 printf 之后复制这部分:
QueryPerformanceCounter( &Count );
v = Count.QuadPart;
printf( "%f\n", (double) v );
printf 打印一个正确的值。 不过汇编代码有点不同:
;;; QueryPerformanceCounter( &Count );
lea eax, DWORD PTR [1408+esp] ;156.1
push eax ;156.1
call DWORD PTR [__imp__QueryPerformanceCounter@4] ;156.1
; LOE ebx esi
.B1.97: ; Preds .B1.96
;;; v = Count.QuadPart;
;;; printf( "%f\n", (double) v );
fild QWORD PTR [1408+esp] ;158.1
mov eax, DWORD PTR [1412+esp] ;158.1
shr eax, 31 ;158.1
mov DWORD PTR [esp], OFFSET FLAT: ??_C@_03A@?$CFf?6?$AA@ ;158.1
fadd QWORD PTR [_2il0floatpacket.1575+eax*8] ;158.1
fstp QWORD PTR [4+esp] ;158.1
call _printf ;158.1
【问题讨论】:
-
对我来说很合适。
printf的%f转换确实需要双精度,而不是浮点数(与 scanf 不同)。也许检查装配?也许您的 32 位代码将双精度放在 xmm 寄存器中,而不是在堆栈上传递? 64 位 ABI 在 xmm 寄存器中传递双参数,但 32 位 Windows 和 linux ABI 都在堆栈上传递双精度参数(并在 x87 FP 堆栈上返回它们)。也许这是一个编译器错误。 -
David Heffernan,如果我能做这样的例子,这将很容易,不需要任何咨询。谢谢阅读。我会试着看看组装——坦率地说,没有太多有趣的事情要做。也许是时候升级编译器了,或者忘记 Win32 架构。
-
奇数。
static_cast<double>(v)有效吗?出于某种原因,它可能会给你reinterpret_cast语义。 -
我无法发现生成的代码有任何明显的问题。不过,直接演员“消失”的问题是一个有趣的线索。在这种情况下,您正在转换有符号的 int64_t 而不是无符号的 uint64_t,后者不受 FPU/SSE 的支持,并在此处使用 bit-hack 和表格进行伪造。您能否尝试将
v的类型切换为int64_t,并逐步执行原始程序集并转储_2il0floatpacket.1575 表周围的16 个字节?它应该包含 0 和 2^64 的双精度浮点数,但可能在此过程中搞砸了。