如何对 ARM 中存储在两个 32 位寄存器中的 64 位数字执行算术右移?

问题描述 投票:0回答:1

我正在尝试用ARM汇编语言实现展位的乘法算法。

Algorithm 2: Booth’s Algorithm to multiply two 32-bit numbers to produce a 64-bit result
Data: Multiplier in V , U = 0, Multiplicand in N
Result: The lower 64 bits of UV contain the result
1 i←0
2 prevBit ← 0
3 fori<32do
4 i←i+1
5 currBit ← LSB of V
6 if (currBit,prevBit) = (1,0) then
7 U←U−N
8 end
9 else if (currBit,prevBit) = (0,1) then
10 U←U+N
11 end
12 prevBit ← currBit
13 UV ← UV ≫ 1 (arithmetic right shift)
14 end

如何执行算法的第 13 步?如何对作为 32 位部分存储在两个寄存器上的 64 位数字执行 asr?

我尝试在两个寄存器上执行 asr,然后对于低 32 位,我将 MSB 替换为高 32 位的 LSB(在执行 asr 之前存储)。

assembly arm bit-manipulation bit-shift
1个回答
0
投票

询问编译器:

int64_t asr_1(int64_t a){ return a>>1; }

Godbolt 编译器浏览器 带有 GCC 和 clang for ARM

在标准调用约定中,第一个参数和返回值都在 R1:R0 中,因此编译器将使 asm 就地运行。 对于移位计数为 1 的情况,Clang 使用进位标志来获取从高半部分底部到低半部分顶部的位。

// clang -O2 -Wall -mcpu=cortex-a77
        asrs    r1, r1, #1         @ set flags, including C from the bit shifted out
        rrx     r0, r0             @ rotate-through-carry, shifting C into the top

GCC 不使用进位标志,使用与它和 clang 用于 2 到 31 之间的移位计数相同的策略。

int64_t
的低半部分是无符号的; 逻辑移位在某些地方可以进行 OR 运算时留下零。来自高半部分的位。对于
rrx
计数,此策略的效率低于
1
技巧,除非
rrx
在某些 CPU 上速度很慢。

// return a>>5   with clang; GCC is similar but uses a MOV to copy R1 and OR last
        lsr     r0, r0, #5                  @ lo >>= 5
        orr     r0, r0, r1, lsl #27         @ lo|=high<<(32-5)  to shift bits between them
        asr     r1, r1, #5                  @ hi >>= 5

Clang 与

-mthumb
奇怪地在
asrs.w
之前使用
rrx
,但对于
asrs
仍然是简单的
return a>>(32+5);
(16 位),这很简单:
asrs  r0, r1, #5
asrs   r1, r1, #31
(根据符号位,返回值的高半部分要么全0,要么全1。)

AFAICT 没有使用

asrs.w
的正确性理由。
asrs
可编码为 16 位 Thumb 指令,尽管
rrx
仅适用于 Thumb 2。这不需要 ARMv8 或任何东西,事实上
clang -march=armv4t
仍然使用它;我只是倾向于使用
-mcpu=cortex-a77
a53
因为它是最近出现的并且很容易想到,我想知道是否有任何新的技巧,以及适合现代 ARM 内核的调整选择。
cortex-m3
m0
也与某些项目相关。 M0 特别缺乏大多数 Thumb 2 编码:

// GCC or Clang  -mcpu=cortex-m0
        lsls    r2, r1, #31        // extract the low bit of the high half
        lsrs    r0, r0, #1
        adds    r0, r0, r2         // and OR it in to  lo >> 1
        asrs    r1, r1, #1         // hi >>= 1

变量计数移位更困难,但编译器仍然可以使用谓词执行向您展示如何操作。 GCC

-marm
是其中或 clang 的
int64_t asr(int64_t a, int c){ return a>>c; }

版本中最短的
// GCC -O3 -mcpu=cortex-a77   for a variable-count shift.  Count in R2
        lsr     r0, r0, r2
        rsb     r3, r2, #32           // 32-count
        subs    ip, r2, #32           // count-32 and set flags
        orr     r0, r0, r1, lsl r3
        orrpl   r0, r0, r1, asr ip    // ORR if PLus (if Negative flag == 0)
        asr     r1, r1, r2
        bx      lr

ARM 移位计数 >= 32 移出所有位,为逻辑移位产生零。因此,我认为,如果

r1, lsl r3
源操作数实际上是我们需要的其他位,则它为零。
asr r1, r1, r2
设置高半部分的效果类似于 r2==32 或更高的
asr r1, #31
;它仍然是原始输入移位计数。

© www.soinside.com 2019 - 2024. All rights reserved.