假如说Intel指令中的当即数,信任咱们都很了解。相似的,Arm指令中的“当即数”便是常数表达式。之所以称为常数表达式,而不称为当即数是有原因的。
Intel指令归于CISC指令集,指令是不定长的,因而能够将恣意32位当即数编码到指令内。
Arm指令归于RISC指令集,指令是定长的32字节。众所周知,指令中操作码是有必要的字段,假如把32位当即数直接编码到指令内部,操作码就无“容身之地”了……
因而,Arm指令中“当即数”的位数必小于32位。那么如安在Arm指令中正常一共当即数呢?咱们看看Arm的通用指令格局。
Arm指令中,操作码(opcode)、意图操作数(Rd)、源操作数1(Rn)是有必要的字段。条件码(cond)、符号位符号(s)源操作数2(oprand2)是可选的。其间Rd和Rn有必要是寄存器,因而Arm的“当即数”只能存储在oprand2。
在Arm的指令编码内,运用“当即数”的指令为“当即数”供给了12bit的存储空间,也便是说Arm的“当即数”只能一共212=4096个数字。这明显无法一共一切的32位当即数,假如运用这12bit一共0~4095的数字,那么从4096~(232-1)的数组都不能一共了。考虑到这种“结果”,Arm指令集的设计者们运用了一个技巧,即运用常数表达式替代当即数的概念。
常数表达式一共一个常数,且该常数对应8位的位图,即能够由一个8位的常数经过循环移位偶数位得到。
比方0x3fc能够由8位常数0xff循环左移2位或循环右移30位得到,是常数表达式。再比方0x1fe,尽管能够有0xff循环左移1位或循环右移31位得到,可是移动的位数不是偶数,因而不是常数表达式。
依据常数表达式的界说,常数表达式只需要12bit的存储空间。
12bit空间中,低8字节存储8位位图,高4字节存储循环右移的次数。4字节只能一共24=16种移动次数,可是咱们常数表达式界说中,移位限定为偶数次,因而这4个字节刚好能一共0、2、4、6、8…32位16个数字!
比方0x3fc的二进制一共为0xfff,即运用8位数字0xff循环右移0xf*2=30次得到。
清晰了常数表达式的意义后,能够经过简略的编程辨认一个数字是否是常数表达式。
循环左移两位
*/
void roundLeftShiftTwoBit(unsigned int& num)
{
unsigned int overFlow=num & 0xc0;//取左移行将溢出的两位
num=(num<<2) (overFlow>>30);//将溢出部分追加到尾部
}
/*
判别num是否是常数表达式,8位数字循环右移偶数位得到
*/
bool constExpr(unsigned int num)
{
for(int i=0;i<16;i++){
if(num<=0xff)
return true;//有用位图
else
roundLeftShiftTwoBit(num);//循环左移2位
}
}
roundLeftShiftTwoBit函数将一个无符号32位整数循环左移2位,constExpr重复将一个无符号32位整数循环左移2位,直到发现该数字在0到0xff之间学校,不然一共该数字不是常数表达式。
经过常数表达式,Arm指令便避免了“当即数”只是局限于0~4095的数字规模的问题了。可是常数表达式仍不能一共悉数的32位整数,比方0x1fe。关于十分数表达式的数字,只能经过拆分来处理。
比方Arm指令mov r0,#0x1fe不是合法的指令,由于0x1fe不是常数表达式。那么只能将0x1fe拆分,一共为两条指令,比方:
add r0,r0,#0x100
除了拆分数字的办法,Arm的LDR宏运用暂时变量的方法完成十分数表达式的数据传送,即:
ldr宏指令不是Arm的真实指令,它由如下两条指令完成:
ldr r0,.tmp
ldr r0,[r0]
Arm的ldr指令用于将内存的数据加载到寄存器。这儿数字0x1fe被存储在ldr指令邻近的方位.tmp处,ldr r0,.tmp被编译为ldr r0,[pc+offset]的方式,其间offset为符号.tmp相对ldr指令的偏移。这样ldr r0,.tmp获取了符号.tmp的地址,然后再次运用ldr r0,[r0]将.tmp地址处的数据取出,即0x1fe。
经过以上的描绘,咱们清晰了Arm指令中常数表达式的真实意义,并基于此编写了一个验证常数表达式的函数。最终,咱们解析了如安在Arm中完成十分数表达式数据的传送,并评论了它的完成。期望对你了解Arm的常数表达式有所协助。