8 位旋转的内联汇编大小不匹配

Posted

技术标签:

【中文标题】8 位旋转的内联汇编大小不匹配【英文标题】:Inline assembly size mismatch for 8-bit rotate 【发布时间】:2017-12-27 20:20:50 【问题描述】:

我正在尝试使用内联汇编在 C 中编写向左旋转操作,如下所示:

byte rotate_left(byte a) 
    __asm__("rol %0, $1": "=a" (a) : "a" (a));
    return a;

(其中字节被定义为无符号字符)。

这会引发错误

/tmp/ccKYcEHR.s:363:错误:“rol”的操作数大小不匹配。

这里有什么问题?

【问题讨论】:

你在哪里定义byte 使用 AT&T 的语法 src 和 destination 是相反的。也许你的意思是"rol $1, %0" godbolt.org/g/z6Qof7 不需要内联汇编(至少对于 gcc 和 clang) 【参考方案1】:

AT&T 语法使用与 Intel 语法相反的顺序。轮换计数必须是第一个,而不是最后一个:rol $1, %0


此外,您不需要也不应该为此使用内联 asm:https://gcc.gnu.org/wiki/DontUseInlineAsm

如Best practices for circular shift (rotate) operations in C++ 中所述,GNU C 具有窄旋转的内在函数,因为旋转习语识别代码未能优化掉旋转计数的and。 x86 移位/旋转使用count & 31 屏蔽计数,即使对于 8 位和 16 位,但旋转仍然环绕。不过这对轮班很重要。

无论如何,gcc 有一个用于窄旋转的内置函数以避免任何开销。在x86intrin.h 中有一个__rolb 包装器,但MSVC 使用它自己的__rotr8 等等来自它的intrin.h。无论如何,clang 不支持 __builtinx86intrin.h 包装器进行旋转,但 gcc 和 ICC 支持。

#include <stdint.h>
uint8_t rotate_left_byte_by1(uint8_t a) 
    return __builtin_ia32_rolqi(a, 1);  // qi = quarter-integer

我像普通人一样使用来自stdint.huint8_t,而不是定义byte 类型。

这根本不能用clang编译,而是it compiles as you'd hope with gcc7.2:

rotate_left_byte_by1:
    movl    %edi, %eax
    rolb    %al
    ret

这为您提供了一个函数,它可以像您的内联汇编一样高效地编译,但它可以完全针对编译时常量进行优化,并且编译器知道它是如何工作的/它做了什么,并且可以进行相应的优化。

【讨论】:

如上所述,gcc does seem to recognize the naive rotate by one,因此似乎没有必要使用任何内在函数;奇怪的是 VC++ 和 icc 都无法识别它。 @MatteoItalia:它可以识别它,但是很难让它只发出 rolb 而没有 and 来掩盖运行时变量计数。也许在成语识别器可以处理字节和 16 位旋转之前添加了内置函数;我没有检查旧的 gcc 版本。 更新:VC++ does recognize it 如果我向uint8_t 大量添加一些演员表(即使只有一个左移似乎也可以正常工作)。 嗯,是的,运行时已知的转变更加棘手;我只是在测试“固定旋转”,因为那是 OP 在他的问题中写的。 不过,7.2 处理得很好godbolt.org/g/9ZpN1d - 看不到and。从快速二分搜索来看,它似乎从 4.9.0 开始巧妙地处理它; 4.8.5 仍然为此生成了可怕的代码。

以上是关于8 位旋转的内联汇编大小不匹配的主要内容,如果未能解决你的问题,请参考以下文章

64 位应用程序和内联汇编

64 位应用程序和内联汇编

32位汇编第七讲,混合编程,内联汇编

x86 - 使用内联汇编设置位

内联汇编代码和存储 128 位结果

Armv8a NEON 内联汇编代码:如何将 16x8 位向量转换为四个 4x32 位(整数)向量?