assembly 当std::cout float32数据两次时出现意外输出,之前已被_mm_shuffle_pi16交换

jm2pwxwz  于 11个月前  发布在  其他
关注(0)|答案(1)|浏览(86)

英语不是我的母语,请原谅我的语法错误
我的机器环境是

AMD 5900x, win10 latest, VS2022 MSVC lateset

字符串
下面的代码已经在我的机器和我同事的机器(与我的机器非常相似)上通过了编译(debug-X86,release-x86 MSVC),但是输出不是预期的

#include <intrin.h>
#include <iostream>
#include <xmmintrin.h>

int main(int argc, char* argv[])
{
    union
    {
        float f[2];
        __m64 m;
    } a = {{10.f, 200.f}};

    a.m = _mm_shuffle_pi16(a.m, _MM_SHUFFLE(1, 0, 3, 2));
    std::cout << a.f[0] << " " << a.f[1] << std::endl;
    std::cout << a.f[0] << " " << a.f[1] << std::endl;
    return 0;
 }
expected output:
200 10
200 10

ACTUAL output:
-nan(ind) 10
200 10
// note: no math operation undertake between two std::cout

的数据
我已经检查了二进制数据,它们在IEEE 754标准中都是法律的:
x1c 0d1x的数据



拆卸是很正常的:



这真的让我觉得有任何上游BUG?或任何编译环境问题?任何类似的情况,你遇到过?任何建议或进一步的信息需要?提前感谢。
样本1:相同代码

Linux kali 6.3.0-kali1-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.3.7-1kali1 (2023-06-29) x86_64 GNU/Linux

gcc (Debian 13.1.0-6) 13.1.0
g++ (Debian 13.1.0-6) 13.1.0
Debian clang version 14.0.6
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
compile command:

g++ a.cpp

clang++ -o aaa a.cpp

的字符串
输出(对于两种编译方法):

200 10
200 10


balp4ylt

balp4ylt1#

虽然我不知道在赋值给a.m和打印内容之间到底发生了什么,但代码本身存在一个bug:_mm_shuffle_pi16是MMX内在的,您没有调用_mm_empty(或_m_empty),因此FPU状态仍处于MMX模式。这将在以后中断x87风格的FPU指令。(所以是32位),所以很可能在某个时候使用了x87指令。64位代码大多不使用x87指令,在这种情况下,它可能看起来没有什么不好的事情发生,这可以解释为什么代码似乎在64位kali Linux上工作。
MMX在这一点上是retrocomputing。你可以添加_mm_empty来修复这段代码,但你也可以使用SSE。放弃union(如果需要的话,你可以使用“cast”intrinsic家族来进行安全的重新解释,但你不需要它,因为SSE有一个浮点 Shuffle ),并做一些类似的事情:

__m128 a = _mm_setr_ps(10.0f, 200.0f, 0.0f, 0.0f);
a = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 2, 0, 1));
float test[4];
_mm_storeu_ps(test, a);
std::cout << test[0] << " " << test[1] << std::endl;

字符串
历史上有some compiler bugs with _mm_empty,但这不是你在程序中看到的,它开始就没有_mm_empty

相关问题