gcc 内联 asm 不编译
gcc inline asm not compiling
我目前正在尝试使用内置函数 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;
}
编译器一直在说
suffix or operands invalid for add
除了代码中的语法错误 (" "r"
-> : "r"
),您的问题是参数不匹配。
查看 -S 的输出:
add rax, r8b
因为 cnt
是 size_t 而 bytes[i]
是一个字节,这就是你所期望的。添加要求它们的大小相同。
我还可以建议使用内置函数而不是内联汇编吗?它避免了这样的问题(以及许多其他问题)。
Is there any way to add up the result from popcnt without storing it off in a register first?
嗯。这实际上是一个完全不同的问题。您询问的错误是由于在单个 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 的 'false dependency' 问题。
使用 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
你指的是哪个"storing it off in a register"?
我目前正在尝试使用内置函数 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;
}
编译器一直在说
suffix or operands invalid for add
除了代码中的语法错误 (" "r"
-> : "r"
),您的问题是参数不匹配。
查看 -S 的输出:
add rax, r8b
因为 cnt
是 size_t 而 bytes[i]
是一个字节,这就是你所期望的。添加要求它们的大小相同。
我还可以建议使用内置函数而不是内联汇编吗?它避免了这样的问题(以及许多其他问题)。
Is there any way to add up the result from popcnt without storing it off in a register first?
嗯。这实际上是一个完全不同的问题。您询问的错误是由于在单个 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 的 'false dependency' 问题。
使用 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
你指的是哪个"storing it off in a register"?