我一直在尝试将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
}
答案 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专家,我不知道汇编程序的行为。)