仅在Objective-C中使用内联汇编的ROL / ROR变量

时间:2013-05-08 17:47:38

标签: objective-c xcode inline-assembly

几天前,我问下面的问题。因为我需要快速回答,所以我补充道:

代码不需要使用内联汇编。但是,我还没有找到使用Objective-C / C ++ / C指令的方法。

今天,我想学点东西。所以我再次提出问题,使用内联汇编寻找答案。


我想在Objective-C程序中对变量执行ROR和ROL操作。但是,我无法管理它 - 我不是装配专家。

这是我到目前为止所做的:

uint8_t v1 = ....;
uint8_t v2 = ....; // v2 is either 1, 2, 3, 4 or 5

asm("ROR v1, v2"); 

我得到的错误是:

  

未知使用未知大小后缀的指令助记符

我该如何解决这个问题?

2 个答案:

答案 0 :(得分:5)

旋转只是两个班次 - 一些位留下,其他位置正确 - 一旦你看到这个旋转很容易没有装配。某些编译器识别该模式,并使用旋转指令进行编译。有关代码,请参阅wikipedia

更新:x86-64上的Xcode 4.6.2(其他未测试)编译双移+或旋转32和& 64位操作数,8位和8位16位操作数双移+或保留。为什么?也许编译器理解这些指令的性能,也许只是没有优化 - 但一般来说,如果你可以避免汇编程序这样做,编译器总是最了解!在函数上使用static inline,或者使用以与标准宏MAX相同的方式定义的宏(宏具有适应其操作数类型的优点),可以用于内联操作

OP评论后的附录

以下是i86_64汇编程序的示例,有关如何使用asm构造启动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(Product&gt; Generate Output&gt; Assembly File,然后在弹出菜单中选择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 - 编译器将为任何架构做正确的事情,而单个{{1}的成本i64版本中的指令。

所以and就是你需要它,使用它可能会有所涉及,编译器本身也会做得好或者更好......

HTH

答案 1 :(得分:0)

ARM中的32位旋转将是:

__asm__("MOV %0, %1, ROR %2\n" : "=r"(out) : "r"(in), "M"(N));

其中N需要是编译时常量。

但是桶形移位器的输出,无论是在寄存器还是立即操作数上使用,总是一个完整的寄存器宽度;你可以将一个恒定的8位数量移位到32位字内的任何位置,或者 - 如此处 - 以32位寄存器的方式移动/旋转任何方向的值。
但是不能使用单个ARM指令在寄存器中旋转16位或8位值。没有这样的存在。

这就是为什么当你使用“普通”(可移植[Objective-] C / C ++)代码(in << xx) | (in >> (w - xx))时,ARM目标上的编译器会为你创建一个汇编程序指令32位旋转,但至少 2 (正常移位后移位或)为8/16位旋转。