首页 > 解决方案 > 强制 clang 发出 pmull2/umull2 操作码变体

问题描述

我有这段(和类似的)代码,我希望在其中发出 umull2 或 pmull2 指令。编译器 clang-11.0.1,选项-O3 -std=c++11 -target aarch64

#include "stdint.h"
#include "arm_neon.h"
inline poly16x8_t vmull_low_p8(poly8x16_t a, poly8x16_t b) {
    return vmull_p8(vget_low_p8(a), vget_low_p8(b));
}
// evaluates polynomials of length `len > 0` using Horner's method
// for 16 points `x`, `X = x 2^m mod n`
poly16x8x2_t p(const uint8_t *input, int len, poly8x16_t x, poly8x16_t X) {
    auto ptr = input + len;
    auto L = vdupq_n_p16(*--ptr), H = L;
    while (ptr > input) {
        auto s = vuzpq_p8(vreinterpretq_p8_p16(L), vreinterpretq_p8_p16(H));
        auto a = vmull_low_p8(s.val[0], x);
        auto b = vmull_high_p8(s.val[0], x);
        auto A = vmull_low_p8(s.val[1], X);
        auto B = vmull_high_p8(s.val[1], X);
        auto C = vdupq_n_p16(*--ptr);
        L = C ^ a ^ A;
        H = C ^ b ^ B;
    }
    return {L,H};
}

而不是像这样的代码

    ldrb    w9, [x8, #-1]!
    uzp1    v6.16b, v2.16b, v3.16b
    uzp2    v2.16b, v2.16b, v3.16b
    pmull   v3.8h, v6.8b, v0.8b
    pmull2  v7.8h, v6.8b, v0.8b
    pmull   v6.8h, v6.8b, v4.8b
    pmull2  v2.8h, v2.8b, v4.8b

编译器选择

    ldrb    w9, [x8, #-1]!
    uzp1    v6.16b, v2.16b, v3.16b
    uzp2    v2.16b, v2.16b, v3.16b
    pmull   v3.8h, v6.8b, v0.8b
    ext     v6.16b, v6.16b, v6.16b, #8 // aligns top 8 bytes over bottom 8 bytes  
    pmull   v7.8h, v2.8b, v1.8b        // uses a copy of v0.8 also shifted by 8 bytes
    ext     v2.16b, v2.16b, v2.16b, #8
    pmull   v6.8h, v6.8b, v4.8b
    pmull   v2.8h, v2.8b, v5.8b

在 uint8x16_t 或 uint16x8_t 上工作并不重要——这似乎是 clang 避免访问寄存器的最高位的一个反复出现的主题。我还没有找到-mtune=cortex-73一种会强制生成不同代码的选项,所以我至少可以评估这是否通常性能较低(我希望它在小内核上)。ARM64 gcc 按预期工作。

ext如果已知指令与pmullwhere 两个pmulls 不会发生双重问题,这可能无关紧要(除了大小和分配的寄存器较少) 。

标签: assemblycompiler-optimizationarm64neon

解决方案


推荐阅读