【问题标题】:gcc inline asm not compilinggcc inline asm 不编译
【发布时间】:2017-01-15 23:52:15
【问题描述】:

我目前正在尝试使用内置的popcnt 来计算unsigned char 数组中1 的数量。

我的函数与常规 __builtin_popcount 一起使用,但对速度要求更高,我决定使用内联 asm 方法。

size_t popcnt_manual( unsigned char *bytes, size_t len ) {
    size_t i, cnt = 0;
    for( i = 0; i < len; i++ ) {
        __asm__(
                 "popcnt %0, %0    \n\t"
                 "add    %0, %1    \n\t"
                 : "+r" (cnt)
                 : "r"  (bytes[i]));
    }

    return cnt;
}

编译器一直在说

后缀或操作数对添加无效

【问题讨论】:

    标签: c gcc inline-assembly


    【解决方案1】:

    除了代码中的语法错误(" "r" -> : "r"),您的问题是参数不匹配。

    查看 -S 的输出:

     add    rax, r8b
    

    由于cnt 是size_t 而bytes[i] 是一个字节,这就是您所期望的。添加要求它们的大小相同。

    我还可以建议使用内置而不是内联 asm 吗?它避免了这样的问题(以及许多其他问题)。


    有什么方法可以将 popcnt 的结果相加而不先将其存储在寄存器中?

    嗯。这实际上是一个完全不同的问题。您询问的错误是由于在单个 add 指令中混合了一个字节和一个 size_t 。可以通过以下方式解决:

        __asm__(
                 "popcnt %0, %0    \n\t"
                 "add    %0, %1    \n\t"
                 : "+r" (cnt)
                 : "r"  ((size_t)bytes[i]));
    

    我不应该鼓励你继续添加新问题(我将如何获得我的业力点数?),但是看看那个网站,他似乎在搞乱的代码是这样的:

    uint32_t builtin_popcnt_unrolled_errata(const uint64_t* buf, int len) {
      assert(len % 4 == 0);
      int cnt[4];
      for (int i = 0; i < 4; ++i) {
        cnt[i] = 0;
      }
    
      for (int i = 0; i < len; i+=4) {
        cnt[0] += __builtin_popcountll(buf[i]);
        cnt[1] += __builtin_popcountll(buf[i+1]);
        cnt[2] += __builtin_popcountll(buf[i+2]);
        cnt[3] += __builtin_popcountll(buf[i+3]);
      }
      return cnt[0] + cnt[1] + cnt[2] + cnt[3];
    }
    

    他明确使用 cnt[x] 试图避免 popcnt 的“错误依赖”问题。

    使用 gcc 6.1 并使用 -m64 -O3 -march=native -mtune=native 编译,我将其视为输出:

    .L14:
            popcnt  r11, QWORD PTR [rcx]
            add     rcx, 32
            add     edx, r11d
            popcnt  r11, QWORD PTR -24[rcx]
            add     eax, r11d
            popcnt  r11, QWORD PTR -16[rcx]
            add     r10d, r11d
            popcnt  r11, QWORD PTR -8[rcx]
            add     r9d, r11d
            cmp     rcx, r8
            jne     .L14
    

    您指的是哪个“将其存储在寄存器中”?

    【讨论】:

    • 忘了提到我从内​​置函数开始但想要更快的东西。有什么方法可以将 popcnt 的结果相加而不先将其存储在寄存器中?从danluu.com/assembly-intrinsics得到这个想法
    • 感谢您的回答?我一定只是对博客文章想要表达的内容感到困惑。这是你的业力点:)
    • 博客上没有日期,但它是使用 4.8.2 编写的。那是很久以前的事了,所以事情可能已经改变了。这是我建议使用内置函数而不是内联函数的另一个原因。内置函数可以随着时间的推移而改进,但如果你内联编写,你只能得到你所写的内容。顺便说一句,如果性能很关键,使用 64 位 popcnt (可能)比重复的 8 位更快。您需要检查 len 并处理奇数尺寸,但我会尝试并计时。
    • 明白。感谢您的输入
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-03
    • 2021-02-21
    • 1970-01-01
    • 1970-01-01
    • 2019-09-07
    • 1970-01-01
    相关资源
    最近更新 更多