6

我正在尝试将uint32_t产生完整 64 位结果的uint64_t向量乘以 gcc 中的向量。我期望的结果是 gcc 发出一条VPMULUDQ指令。但是 gcc 作为代码输出的内容是对各个uint32_t源向量进行可怕的洗牌,然后是完整的 64*64=64 乘法。这是我尝试过的:

#include <stdint.h>

typedef uint32_t v8lu __attribute__ ((vector_size (32)));
typedef uint64_t v4llu __attribute__ ((vector_size (32)));

v4llu mul(v8lu x, v8lu y) {
    x[1] = 0; x[3] = 0; x[5] = 0; x[7] = 0;
    y[1] = 0; y[3] = 0; y[5] = 0; y[7] = 0;
    return (v4llu)x * (v4llu)y;
}

第一个屏蔽掉uint32_t向量中不需要的部分,希望 gcc 能优化掉 64*64=64 乘法中不需要的部分,然后看到屏蔽也毫无意义。没有这样的运气。

v4llu mul2(v8lu x, v8lu y) {
    v4llu tx = {x[0], x[2], x[4], x[6]};
    v4llu ty = {y[0], y[2], y[4], y[6]};
    return tx * ty;
}

在这里,我尝试uint64_t从头开始创建一个仅包含使用过的部件集的矢量。再次 gcc 应该看到每个的前 32 位uint64_t是 0,而不是做一个完整的 64*64=64 乘法。相反,会发生大量提取和放回值,并且 64*64=64 相乘。

v4llu mul3(v8lu x, v8lu y) {
    v4llu t = {x[0] * (uint64_t)y[0], x[2] * (uint64_t)y[2], x[4] * (uint64_t)y[4], x[6] * (uint64_t)y[6]};
    return t;
}

让我们通过将部分相乘来构建结果向量。也许 gcc 看到它可以VPMULUDQ用来实现这一点。不走运,它回落到 4 个IMUL操作码。

有没有办法告诉 gcc 我想要它做什么(32*32=64 乘法,一切都完美放置)?

注意:内联汇编或内在不是答案。手动编写操作码显然有效。但随后我将不得不为许多目标架构和功能集编写不同版本的代码。我希望 gcc 能够理解问题并从单个源代码中生成正确的解决方案。

4

1 回答 1

2

正如 chtz 在评论中指出的那样, mul1 和 mul2 都通过 clang 进行了优化。类似于 mul3 但使用 for 循环的代码也将被优化(但不是那么好)。

所以在我看来,表达代码应该做什么的语法是正确的,而 gcc 到目前为止只是缺乏正确优化它的聪明才智。

于 2019-11-14T14:12:57.100 回答