根据GCC版本的不同内在行为

时间:2019-06-10 08:47:48

标签: c++ gcc undefined-behavior intrinsics

我对内在函数还很陌生,我面对的代码使用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中,我们基本上只是从float变量构造两个__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时会收到不同的结果?它是未定义的行为吗?

1 个答案:

答案 0 :(得分:4)

您发现gcc8及更高版本中存在一个错误,该错误在启用/未启用优化的情况下发生。感谢reporting it

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

gcc7正确地将其优化为divss xmm0, xmm1以执行num / denom。然后它将转换为double,因为输出函数仅使用double,而不是float,将其传递给iostream函数。 (GCC7使用doubler14位模式保存在整数寄存器-mtune=skylake中而不是内存中,而将divss xmm0, .LC0[rip]保存在整数寄存器中。GCC8及更高版本仅使用可能更有意义的内存。)

gcc8和更高版本执行0,其中来自内存的常量为+0.0 num的位模式)。因此,它将denom除以零,而忽略了alignas(16) float res_arr[4];

the Godbolt compiler explorer上签出。

使用__v4sf *res来消除__attribute__((aligned(16)))可能存在的未对准问题没有帮助。 (您通常不再需要c.print_a; C ++ 11引入了用于对齐的标准语法。)