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 不支持 __builtin
或 x86intrin.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.h
的uint8_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 位旋转的内联汇编大小不匹配的主要内容,如果未能解决你的问题,请参考以下文章