【问题标题】:ARM inline assembly for 32-bit word rotate用于 32 位字循环的 ARM 内联汇编
【发布时间】:2015-07-20 10:23:55
【问题描述】:

我正在尝试制作一些内联程序集来测试旋转在 ARM 上的性能。该代码是 C++ 代码库的一部分,因此旋转是模板特化。代码如下,但它产生的消息对我来说没有多大意义。

根据ARM Assembly Language,指令大致是:

# rotate - rotate instruction
# dst - output operand
# lhs - value to be rotated
# rhs - rotate amount (immediate or register)
<rotate> <dst>, <lhs>, <rhs>

它们没有多大意义,因为(对我而言)例如,我使用 g 来约束输出寄存器,而这只是一个每个Simple Contraints 的通用寄存器。 ARM 应该有很多,Machine Specific Constraints 似乎并没有改变约束的行为。

我不确定解决这个问题的最佳方法,所以我要问三个问题:

  1. 如何在使用常量或立即数时对旋转进行编码?
  2. 当使用通过寄存器传递的值时,如何对旋转进行编码?
  3. 拇指模式如何改变内联汇编

arm-linux-androideabi-g++ -DNDEBUG -g2 -Os -pipe -fPIC -mfloat-abi=softfp
-mfpu=vfpv3-d16 -mthumb --sysroot=/opt/android-ndk-r10e/platforms/android-21/arch-arm
-I/opt/android-ndk-r10e/sources/cxx-stl/stlport/stlport/ -c camellia.cpp
In file included from seckey.h:9:0,
             from camellia.h:9,
             from camellia.cpp:14:
misc.h: In function 'T CryptoPP::rotlFixed(T, unsigned int) [with T = unsigned int]':
misc.h:1121:71: error: matching constraint not valid in output operand
  __asm__ ("rol %2, %0, %1" : "=g2" (z) : "g0" (x), "M1" ((int)(y%32)));
                                                                       ^
misc.h:1121:71: error: matching constraint references invalid operand number
misc.h: In function 'T CryptoPP::rotrFixed(T, unsigned int) [with T = unsigned int]':
misc.h:1129:71: error: matching constraint not valid in output operand
  __asm__ ("ror %2, %0, %1" : "=g2" (z) : "g0" (x), "M1" ((int)(y%32)));
                                                                       ^
misc.h:1129:71: error: matching constraint references invalid operand number
misc.h: In function 'T CryptoPP::rotlVariable(T, unsigned int) [with T = unsigned int]':
misc.h:1137:72: error: matching constraint not valid in output operand
  __asm__ ("rol %2, %0, %1"  : "=g2" (z) : "g0" (x), "g1" ((int)(y%32)));
                                                                        ^
misc.h:1137:72: error: matching constraint references invalid operand number
misc.h: In function 'T CryptoPP::rotrVariable(T, unsigned int) [with T = unsigned int]':
misc.h:1145:72: error: matching constraint not valid in output operand
  __asm__ ("ror %2, %0, %1"  : "=g2" (z) : "g0" (x), "g1" ((int)(y%32)));
                                                                        ^
misc.h:1145:72: error: matching constraint references invalid operand number
misc.h: In function 'T CryptoPP::rotrFixed(T, unsigned int) [with T = unsigned int]':
misc.h:1129:71: error: matching constraint not valid in output operand
  __asm__ ("ror %2, %0, %1" : "=g2" (z) : "g0" (x), "M1" ((int)(y%32)));
                                                                       ^
misc.h:1129:71: error: invalid lvalue in asm output 0
misc.h:1129:71: error: matching constraint references invalid operand number
misc.h: In function 'T CryptoPP::rotlFixed(T, unsigned int) [with T = unsigned int]':
misc.h:1121:71: error: matching constraint not valid in output operand
  __asm__ ("rol %2, %0, %1" : "=g2" (z) : "g0" (x), "M1" ((int)(y%32)));
                                                                       ^
misc.h:1121:71: error: invalid lvalue in asm output 0
misc.h:1121:71: error: matching constraint references invalid operand number

// ROL #n Rotate left immediate
template<> inline word32 rotlFixed<word32>(word32 x, unsigned int y)
{
    int z;
    __asm__ ("rol %2, %0, %1" : "=g2" (z) : "g0" (x), "M1" ((int)(y%32)));
    return static_cast<word32>(z);
}

// ROR #n Rotate right immediate
template<> inline word32 rotrFixed<word32>(word32 x, unsigned int y)
{
    int z;
    __asm__ ("ror %2, %0, %1" : "=g2" (z) : "g0" (x), "M1" ((int)(y%32)));
    return static_cast<word32>(z);
}

// ROR rn Rotate left by a register
template<> inline word32 rotlVariable<word32>(word32 x, unsigned int y)
{
    int z;
    __asm__ ("rol %2, %0, %1"  : "=g2" (z) : "g0" (x), "g1" ((int)(y%32)));
    return static_cast<word32>(z);
}

// ROR rn Rotate right by a register
template<> inline word32 rotrVariable<word32>(word32 x, unsigned int y)
{
    int z;
    __asm__ ("ror %2, %0, %1"  : "=g2" (z) : "g0" (x), "g1" ((int)(y%32)));
    return static_cast<word32>(z);
}

template<> inline word32 rotlMod<word32>(word32 x, unsigned int y)
{
    return rotlVariable<word32>(x, y);
}

template<> inline word32 rotrMod<word32>(word32 x, unsigned int y)
{
    return rotrVariable<word32>(x, y);
}

【问题讨论】:

  • 您想用g2M1 实现什么目标? 21 是似乎没有意义的匹配约束,编译器也不喜欢它们。
  • @Jester - 2 是输出操作数编号。它需要在一个寄存器中,因此是g2。对于1,即rhsshift amount。对于立即数,它需要被限制为立即数,因此 M1.
  • 请注意,GCC 足够聪明,可以选择 x &lt;&lt; y | x &gt;&gt; (32 - y) 习语并发出一条 ror 指令,前提是参数是无符号的。
  • 是的,但是为什么要添加21?这意味着,将其放在与给定的其他操作数相同的位置,而您在这里不需要。
  • @Notlikethat - x &lt;&lt; y | x &gt;&gt; (32 - y) - 这是y=0 时未定义的行为。该代码应该出现在生产中的任何地方。而且 GCC 没有提供轮换内在函数,这会浪费我的这些问题。如果他们提供它,那么我早就完成了。相关:Near constant time rotate that does not violate the standards.

标签: c++ gcc assembly arm inline-assembly


【解决方案1】:

首先,ARM没有左旋转(ROL),你需要通过ROR来模拟。

其次,M 约束由于某种原因接受 0 到 32,但 ROL 在处理立即数时只接受 0 到 31。

第三,g 约束过于通用,因为它还允许ROR 不接受的内存操作数。最好改用r

这是我想出的:

// Rotate right
inline word32 rotr(word32 x, unsigned int y)
{
    int z;
    if (__builtin_constant_p(y))
    {
        y &= 31;
        if (y != 0) // this should be optimized away by the compiler
        {
            __asm__ ("ror %0, %1, %2" : "=r" (z) : "r" (x), "M" (y));
        }
    } else {
        __asm__ ("ror %0, %1, %2" : "=r" (z) : "r" (x), "r" (y));
    }
    return static_cast<word32>(z);
}

// Rotate left
inline word32 rotl(word32 x, unsigned int y)
{
    int z;
    if (__builtin_constant_p(y))
    {
        y &= 31;
        if (y != 0) // this should be optimized away by the compiler
        {
            __asm__ ("ror %0, %1, %2" : "=r" (z) : "r" (x), "M" (32 - y));
        }
    } else {
        __asm__ ("ror %0, %1, %2" : "=r" (z) : "r" (x), "r" (32 - y));
    }
    return static_cast<word32>(z);
}

【讨论】:

  • “其次,M 约束...” - 是的,我真的想要简单的 I约束,但我也无法让它发挥作用。
  • 您通常根本不需要 asm 来让编译器有效地发出旋转指令。尽管请注意Best practices for circular shift (rotate) operations in C++ 中建议的c&amp;31 可能不会在 ARM 上优化,因为与 x86 不同,它还没有隐式屏蔽移位计数。 (但轮换计数可能本质上是模块化的。)
【解决方案2】:

我可以告诉你,THUMB 模式处理位旋转的方式非常不同。 ARM 模式具有所谓的“桶形移位器”,您可以在其中对任何参数进行位移或位旋转,而无需实际更改它。所以让我们考虑以下几点:

添加 r0,r0,r1 ror #1

这大致翻译为“将 r1 向右旋转一次,将其添加到 r0,然后将结果存储在 r0 中。”您可以决定是否移动/旋转其中一个操作数以及移动多少。没有 ROL,但 ROR #31 等于 ROL #1 如果 ARM 拥有它会做的事情,所以利用它来发挥你的优势。

存储在 r1 中的实际值不会改变,移位/旋转仅在此指令期间适用。这仅适用于 ARM 模式,在 THUMB 模式下,您将不得不使用其他处理器(如 x86、68000 等)典型的更传统的移位/旋转命令。

【讨论】:

    猜你喜欢
    • 2012-08-12
    • 1970-01-01
    • 1970-01-01
    • 2011-01-31
    • 1970-01-01
    • 2020-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多