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

时间:2019-05-02 05:20:47

标签: assembly powerpc

我一直在尝试将ADDIS和ADDI指令用于将常量加到数组的基本函数,我遵循了https://cr.yp.to/2005-590/powerpc-cwg.pdf第48和49页中的指令,但是行为不是我所期望的

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

在运行64位linux内核和用户空间的ppc970计算机上进行了测试。

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
}

2 个答案:

答案 0 :(得分:2)

  

将第16位设置为1,但不递增。

PowerPC的addi指令与在MIPS CPU上具有相同名称的指令相似:

您可以在寄存器中添加(-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的影响。

  

第16位

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

一些32位(!)汽车PowerPC的文档使用的寄存器名称为“ bit 32”(最高位)至“ bit 63”(最低位),因此甚至不存在位号0至31 ... / p>

答案 1 :(得分:0)

如果立即数是 sign 扩展为32位,则这很有意义。

请注意,0xae53的高位已设置,因此它将符号扩展到0xffffae53。也就是说,它会从高半部分中减去1,并在低半部分中加上0xae53(当然,有可能进位到高半部分)。

因此不可能写add r4, r4, 0x0000ae53,因为那将需要16位的 unsigned 立即数,或者需要比16位宽的有符号立即数。

但是也许编译器生成的asm使用0xae53表示法只是向您显示立即数的位模式而没有隐含的更高位?

(对不起,我不是PowerPC专家,我不知道汇编程序的行为。)