不同的内在行为取决于 GCC 版本

Different intrinsics behaviour depending on GCC version

我对内在函数很陌生,我的代码在 GCC-7.4 和 GCC-8.3 中的行为不同

我的代码很简单

b.cpp:

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

void foo(const float num, const float denom)
{
    const __v4sf num4 = {
        num,
        num,
        num,
        num,
    };
    const __v4sf denom4 = {
        denom,
        denom,
        denom,
        denom,
    };
    float res_arr[] = {0, 0, 0, 0};

    __v4sf *res = (__v4sf*)res_arr;
    *res = num4 / denom4;
    std::cout << res_arr[0] << std::endl;
    std::cout << res_arr[1] << std::endl;
    std::cout << res_arr[2] << std::endl;
    std::cout << res_arr[3] << std::endl;
}

在b.cpp中,我们基本上只是从浮点变量构造两个__v4sf并执行除法

b.h:

#ifndef B_H
#define B_H

void foo(const float num, const float denom);

#endif

a.cpp:

#include "b.h"

int main (void)
{
    const float denominator = 1.0f;
    const float numerator = 12.0f;
    foo(numerator, denominator);
    return 0;
}

这里我们只是从b.cpp

调用我们的函数

GCC 7.4 工作正常:

g++-7 -c b.cpp -o b.o && g++-7 a.cpp b.o -o a.out && ./a.out
12
12
12
12

但是 GCC 8.3 有问题

g++-8 -c b.cpp -o b.o && g++-8 a.cpp b.o -o a.out && ./a.out
inf
inf
inf
inf

所以我的问题是 - 为什么我使用不同版本的 GCC 会收到不同的结果?它是未定义的行为吗?

您在 gcc8 及更高版本中发现了一个错误,with/without 优化已启用。感谢 reporting it

启用优化后,很容易看出 asm 在做什么,因为 __v4sf 东西被优化掉了:它只是标量除法并将结果打印 4 次。 (加上 4 次 flush cout 调用,因为您出于某种原因使用了 std::endl。)

gcc7 正确地将其优化为 divss xmm0, xmm1 以执行 num / denom。然后它转换为 double,因为输出函数只采用 double,而不是 float,将其传递给 iostream 函数。 (GCC7 使用 -mtune=skylakedouble bit-pattern 保存在整数寄存器 r14 而不是内存中。GCC8 和更高版本只使用内存,这可能更有意义。)

gcc8 和后来的 divss xmm0, .LC0[rip],其中内存中的常量是 0+0.0 的 bit-pattern)。所以它将 num 除以零,忽略 denom.

the Godbolt compiler explorer 上查看。

使用alignas(16) float res_arr[4]; 消除__v4sf *res 的潜力under-alignment 无济于事。 (您通常不再需要 __attribute__((aligned(16)));C++11 引入了用于对齐的标准语法。)