vmovdqu 指令运行触发指令异常问题分析

问题描述

某设备使用 C3758 cpu,在运行 vpp 时触发了指令异常,gdb 中看到的关键信息如下:

Thread 1 "vpp" received signal SIGILL, Illegal instruction.
0x00007fff8c22ceff in mm_loadu_si128 (__P=0x7fff8c52b3a8) at /usr/lib/gcc/x86_64-buildroot-linux-gnu/5.4.0/include/emmintrin.h: 698

环境信息

cpuflags

查看 /proc/cpuinfo 确认 C3758 cpu 支持 SSE、SSE2、SSE4 向量指令,不支持 AVX 指令。

问题分析

使用 gdb 反汇编确认非法指令的位置如下:

→ 0x00007fff8c22ceff <+2472>: vmovdqu (%rax),%xmm0

初步结论是该款 cpu 不支持 vmovdqu 指令。

对比测试

不指定额外编译参数

使用如下 demo 调用 mm_loadu_si128 函数测试:

#include <stdio.h>
#include <emmintrin.h>

int main() {
    int data[4] = {1, 2, 3, 4};

    __m128i xmm_data = _mm_loadu_si128((__m128i*)data);

    int loaded_data[4];
    _mm_storeu_si128((__m128i*)loaded_data, xmm_data);
    for (int i = 0; i < 4; ++i) {
        printf("%d ", loaded_data[i]);
    }
    printf("\n");

    return 0;
}

仅指定 -g 编译参数,gcc 编译,在相同的环境中程序能够正常运行。反汇编查看相关函数的指令,有如下内容:

    11c8:       48 8b 45 a8             mov    -0x58(%rbp),%rax
    11cc:       f3 0f 6f 00             movdqu (%rax),%xmm0

    __m128i xmm_data = _mm_loadu_si128((__m128i*)data);
    11d0:       0f 29 45 b0             movaps %xmm0,-0x50(%rbp)
    11d4:       48 8d 45 e0             lea    -0x20(%rbp),%rax
    11d8:       48 89 45 a0             mov    %rax,-0x60(%rbp)
    11dc:       66 0f 6f 45 b0          movdqa -0x50(%rbp),%xmm0
    11e1:       0f 29 45 c0             movaps %xmm0,-0x40(%rbp)

可以看到并未生成使用 vmovdqu 指令的机器码,程序能够正常运行。

指定 vpp 编译的部分参数

使用如下编译参数:

-g  -mno-avx512f  -gdwarf-4  -march=native 

编译出来的程序在目标环境中运行也会触发指令异常,反汇编查看指令,得到如下信息:

    11c8:       48 8b 45 a8             mov    -0x58(%rbp),%rax
    11cc:       c5 fa 6f 00             vmovdqu (%rax),%xmm0

    __m128i xmm_data = _mm_loadu_si128((__m128i*)data);
    11d0:       c5 f8 29 45 b0          vmovaps %xmm0,-0x50(%rbp)
    11d5:       48 8d 45 e0             lea    -0x20(%rbp),%rax
    11d9:       48 89 45 a0             mov    %rax,-0x60(%rbp)
    11dd:       c5 f9 6f 45 b0          vmovdqa -0x50(%rbp),%xmm0
    11e2:       c5 f8 29 45 c0          vmovaps %xmm0,-0x40(%rbp)

可以看到这次生成的机器码中使用了 vmovdqu 指令,确定与编译参数强相关。

问题原因分析

为什么会触发指令异常?

在 https://www.intel.com/content/dam/develop/external/us/en/documents/319433-024-697869.pdf 这篇 pdf 文档中,找到如下内容:
在这里插入图片描述使用 vmovdqu cpu 需要支持 avx 指令集,C3758 不支持 avx 指令集,进而不支持 vmovdqu 指令,执行该指令时就会触发指令异常。

为什么会生成使用 avx 指令集的机器码?

gcc 编译 vpp 时指定了 -march 为 native ,这时 gcc 会根据当前主机的处理器类型和特性来优化生成的机器码,以提高代码的执行效率。编译环境支持 avx 指令集,gcc 使用 avx 指令优化代码执行效率。

解决方法

修改 -march 参数指定的 cpu 类型,例如 corei7,生成未使用 avx 指令集的机器码。

联想

dpdk 中有许多向量指令的使用场景,收发包函数优化就是一个很典型的示例,一般来说一个网卡驱动可能有几套收发包函数,根据向量指令划分,有支持 sse、avx、avx512 这几个版本,实际应用中编译环境一般只有一套,运行环境却可能存在多套,这样就存在不兼容的问题。

如何解决这种问题呢?

dpdk 会在启动的时候获取 cpu 支持的 CPIID feature flag,根据 flag 去判断,动态选择最适合目标机器的收发包函数向量实现,很好地解决了目标环境与编译环境不兼容的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值