旋转只是两个班次 - 一些位向左,其他位向右 - 一旦你看到这种旋转很容易,无需组装。该模式被一些编译器识别并使用旋转指令进行编译。有关代码,请参见维基百科。
更新:x86-64 上的 Xcode 4.6.2(其他未测试)编译双移位 + 或对 32 位和 64 位操作数进行旋转,对于 8 位和 16 位操作数,双移位 + 或被保留。为什么?也许编译器了解这些指令的性能,也许只是没有优化 - 但一般来说,如果你可以避免汇编器这样做,编译器总是最清楚!也可以使用static inline
函数,或使用与标准宏相同的方式定义的宏MAX
(宏具有适应其操作数类型的优点),可用于内联操作。
OP评论后的附录
这里以 i86_64 汇编器为例,详细了解如何使用asm
构造 start here。
首先是非汇编版本:
static inline uint32 rotl32_i64(uint32 value, unsigned shift)
{
// assume shift is in range 0..31 or subtraction would be wrong
// however we know the compiler will spot the pattern and replace
// the expression with a single roll and there will be no subtraction
// so if the compiler changes this may break without:
// shift &= 0x1f;
return (value << shift) | (value >> (32 - shift));
}
void test_rotl32(uint32 value, unsigned shift)
{
uint32 shifted = rotl32_i64(value, shift);
NSLog(@"%8x <<< %u -> %8x", value & 0xFFFFFFFF, shift, shifted & 0xFFFFFFFF);
}
如果您在 Xcode 中查看汇编器输出进行分析(因此优化器启动)(产品 > 生成输出 > 汇编文件,然后在窗口底部的弹出菜单中选择 Profiling),您将看到它rotl32_i64
被内联到test_rotl32
并编译成一个旋转(roll
)指令。
现在,自己直接生成汇编程序比 FrankH 展示的 ARM 代码要复杂一些。cl
这是因为要获取变量移位值,必须使用特定的寄存器 , ,因此我们需要为编译器提供足够的信息来执行此操作。开始:
static inline uint32 rotl32_i64_asm(uint32 value, unsigned shift)
{
// i64 - shift must be in register cl so create a register local assigned to cl
// no need to mask as i64 will do that
register uint8 cl asm ( "cl" ) = shift;
uint32 shifted;
// emit the rotate left long
// %n values are replaced by args:
// 0: "=r" (shifted) - any register (r), result(=), store in var (shifted)
// 1: "0" (value) - *same* register as %0 (0), load from var (value)
// 2: "r" (cl) - any register (r), load from var (cl - which is the cl register so this one is used)
__asm__ ("roll %2,%0" : "=r" (shifted) : "0" (value), "r" (cl));
return shifted;
}
更改test_rotl32
为再次调用rotl32_i64_asm
并检查程序集输出 - 它应该是相同的,即编译器和我们一样。
进一步注意,如果包含注释掉的掩码行,rotl32_i64
它本质上会变成rotl32
- 编译器将为任何架构做正确的事情,所有这些都是and
i64 版本中单个指令的成本。
那么asm
您是否需要它,使用它可能会有些麻烦,并且编译器总是会自己做得更好或更好......
高温高压