如何正确使用 ADDIS 和 ADDI 将 32 位常量直接加到寄存器中?

How do I correctly use ADDIS and ADDI to sum a 32 bit constant directly to a register?

我一直在尝试将 ADDIS 和 ADDI 指令用于将常数加到数组的基本函数,我按照 https://cr.yp.to/2005-590/powerpc-cwg.pdf 第 48 和 49 页中的说明进行操作,但行为并非如此我预料到了。

我想做的是在汇编程序中创建这个函数,但对于某些常量,ADDIS 将它们减 1,并且行为与手册所说的不一致,即检查是否设置了第 16 位。 GCC 和 clang 正确生成指令,他们使用的实际规则是什么? 没有按照手册中的说明进行递增或实施规则只会给我错误的总和。

在 ppc970 机器上测试,运行 64 位 linux 内核和用户空间。

C 函数的编译器汇编程序输出示例:

void func(int* r){
r[0] += r[0] + 0x9f321062; //addis r3,r3,0x9f32 #has bit 16 set to 1 but is not incremented.
                           //addi  r3,r3,0x1062

r[1] += r[1] + 0x760ae53;  //addis r4,r4,0x761  #compiler correctly increments this from 0x0760 to 0x0761, but bit 16 of this number is 0!
                           //addi r4,r4,0xae53
}

如果立即数 sign 扩展到 32 位,这就有意义了。

注意 0xae53 设置了高位,因此它会符号扩展到 0xffffae53。也就是说,它会从高半部分减去 1,同时将 0xae53 添加到低半部分(当然有可能进位到高半部分)。

所以不可能写成 add r4, r4, 0x0000ae53,因为那需要一个 16 位 unsigned 立即数,或者一个比 16 位更宽的有符号立即数。

但编译器生成的 asm 可能使用 0xae53 表示法来向您显示立即数的位模式而没有隐含的高位?

(抱歉,我不是 PowerPC 专家,我不知道汇编器的行为。)

has bit 16 set to 1 but is not incremented.

PowerPC 的 addi 指令与 MIPS CPUs:

上的同名指令类似

您可以将 (-0x8000) 到 (+0x7FFF) 范围内的数字添加到寄存器。

addis 指令会将 0x10000 的倍数添加到寄存器。

因为 0x9f321062 = 0x9f320000 + 0x1062,您可以通过使用一条 addis 和一条 addi 指令将 0x9f320000 和 0x1062 添加到寄存器来将 0x9f321062 添加到寄存器。

这里重要的是16位数0x1062的最高位是明确的,也就是说0x1062不大于0x7FFF所以在addi范围内可以处理。

现在让我们看看另一种情况:

0x760ae53 = 0x7600000 + 0xae53,因此您可以通过将 0x7600000 和 0xae53 添加到寄存器来将 0x760ae53 添加到寄存器。不幸的是,addi 只能在 (-0x8000) 到 (+0x7FFF) 范围内操作,因此添加 0xae53 不起作用。

如果addi的操作数的最高位被置位,addi指令对该值进行符号扩展,其实就是该指令加上(负值)(N-0x10000 ) 而不是 (正值) N 到寄存器。

因此您必须按以下方式进行计算:

0x760ae53 = 0x7600000 + (0xae53 - 0x10000) + 0x10000 = 0x7610000 + (0xae53 - 0x10000)

换句话说:您必须将 addis (0x760) 的操作数递增 1,以补偿 addi 将减去 0x10000 的效果。

bit 16

请注意 PowerPC 文档使用了非常混乱的位编号,甚至似乎与 CPU 和 CPU 不同:

32 位 (!) 汽车 PowerPC 的一些文档使用了 "bit 32"(最高位)到 "bit 63"(最低位)的寄存器名称,因此位编号 0 到 31 甚至不存在...