找回密码
 注册
关于网站域名变更的通知
查看: 681|回复: 2
打印 上一主题 下一主题

ARM的常数表达式

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2021-6-4 10:33 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x
如果说Intel指令中的立即数,相信大家都很熟悉。类似的,ARM指令中的“立即数”就是常数表达式。之所以称为常数表达式,而不称为立即数是有原因的。
4 j) F) K0 G4 b* ]5 lIntel指令属于CISC指令集,指令是不定长的,因此可以将任意32位立即数编码到指令内。
" J/ ^. x6 ?# p9 V9 E- Y
, _6 b$ x; [% I& R

, N1 H. N& u4 A, [% Q

# u$ I2 N4 f* g6 R0 ?5 uArm指令属于RISC指令集,指令是定长的32字节。众所周知,指令中操作码是必须的字段,如果把32位立即数直接编码到指令内部,操作码就无“容身之地”了……
  ~2 N" Q: m; G& @" |% ^. ~: ]" _' ]# B3 p, q

% a! V- Y3 ]/ T1 s0 Y7 h* ~7 B, P4 K
/ }/ z3 ~2 R) y' X1 [+ R2 O
因此,Arm指令中“立即数”的位数必小于32位。那么如何在Arm指令中正常表示立即数呢?我们看看Arm的通用指令格式。
+ |  G9 E9 [' r% o' y/ M/ a  o+ ]2 L, v
9 j1 `1 W6 O/ G! P0 ~2 Z1 W
4 ?1 Y) \2 j) q
Arm指令中,操作码(opcode)、目的操作数(Rd)、源操作数1(Rn)是必须的字段。条件码(cond)、符号位标记(s)源操作数2(oprand2)是可选的。其中Rd和Rn必须是寄存器,因此Arm的“立即数”只能存储在oprand2。0 M0 D2 r) d2 p) w

8 q% c3 A  E+ f5 X, R, G' r
# A7 j$ z0 u5 @- q6 ?在Arm的指令编码内,使用“立即数”的指令为“立即数”提供了12bit的存储空间,也就是说Arm的“立即数”只能表示212=4096个数字。这显然无法表示所有的32位立即数,如果使用这12bit表示0~4095的数字,那么从4096~(232-1)的数组都不能表示了。考虑到这种“后果”,Arm指令集的设计者们使用了一个技巧,即使用常数表达式代替立即数的概念。( E0 Q0 R: r6 s$ u* F
常数表达式表示一个常数,且该常数对应8位的位图,即可以由一个8位的常数通过循环移位偶数位得到。

( c6 l" n  P5 V: w! K, r
, u- b8 X, b2 ~0 r* i1 ^! }
; b$ C, V7 g$ M0 P4 N; z
比如0x3fc可以由8位常数0xff循环左移2位或循环右移30位得到,是常数表达式。再比如0x1fe,虽然可以有0xff循环左移1位或循环右移31位得到,但是移动的位数不是偶数,因此不是常数表达式。
2 a) B5 J$ F3 i1 A: m根据常数表达式的定义,常数表达式只需要12bit的存储空间。
2 M/ p2 w7 E: ^: L1 m  H: _. a+ `& J  O4 H

6 {; h, G; Q$ a* E- f12bit空间中,低8字节存储8位位图,高4字节存储循环右移的次数。4字节只能表示24=16种移动次数,但是由于常数表达式定义中,移位限定为偶数次,因此这4个字节刚好能表示0、2、4、6、8…32位16个数字!( _' v4 a- c0 t# _
比如0x3fc的二进制表示为0xfff,即使用8位数字0xff循环右移0xf*2=30次得到。
0 ^/ o& ~* g+ ~) w明确了常数表达式的含义后,可以通过简单的编程识别一个数字是否是常数表达式。
! g" J% n! L6 ]+ ?7 H% G/*
. i, d4 v: e5 x* l$ ]; j循环左移两位
. T& a4 k/ i7 g*/+ V2 B& E" Z7 d# ~9 r
void roundLeftShiftTwoBit(unsigned int& num)! m# ^' Y3 O# Q; I( G7 I3 w
{3 F' M8 T2 l$ L% \; C% ^
unsigned int oveRFlow=num & 0xc0;//取左移即将溢出的两位
! W) I3 P% E8 jnum=(num<<2) (overFlow>>30);//将溢出部分追加到尾部
6 Z5 Z, |4 C3 I4 p5 X( p  b) y}, _$ |4 N" y7 O# O9 p
/*
$ ]- d5 O- I! [. S/ ^判断num是否是常数表达式,8位数字循环右移偶数位得到
( v) M/ q2 Y& V*/$ T& z; [* [, d6 W' a5 T4 c& X
bool constExpr(unsigned int num)
6 n; g1 H' k: K5 A' ?7 ~{
# ^+ q- {4 d4 P+ Y$ v, X; lfor(int i=0;i<16;i++){
3 X- Z& [5 v  p" Aif(num<=0xff)4 s' s6 z# e7 w3 z2 n7 l
return true;//有效位图% J* ?$ X' Z7 @
else9 {+ H8 p- n8 h/ |7 K, `
roundLeftShiftTwoBit(num);//循环左移2位
2 D2 V$ {( u* J! p4 ]}/ d+ i+ z5 C/ G
}
3 J3 e1 A  C- @! ^, o
roundLeftShiftTwoBit函数将一个无符号32位整数循环左移2位,constExpr反复将一个无符号32位整数循环左移2位,直到发现该数字在0到0xff之间为止,否则表示该数字不是常数表达式。
: \. v! B( v& U% g1 I' s+ c' C* L通过常数表达式,Arm指令便避免了“立即数”仅仅局限于0~4095的数字范围的问题了。但是常数表达式仍不能表示全部的32位整数,比如0x1fe。对于非常数表达式的数字,只能通过拆分来解决。
: E# x, j$ {) T7 p9 c2 [  {- _9 a比如Arm指令mov r0,#0x1fe不是合法的指令,因为0x1fe不是常数表达式。那么只能将0x1fe拆分,表示为两条指令,比如:
) R+ e8 \) M5 v& a# X. q4 i9 c& ~' tmov r0,#0xfe' c& Q0 {1 _+ T+ l+ F6 n- H" m
add r0,r0,#0x100
) ~3 b) |! n/ H( |, m- e$ s. Y
除了拆分数字的方法,Arm的LDR宏使用临时变量的方式实现非常数表达式的数据传送,即:
8 g7 x3 R. F; ~# B) v& tLDR r0,=0x1fe
/ {" L  f3 }4 X9 Z9 b' O. ^ldr宏指令不是Arm的真正指令,它由如下两条指令实现:9 }) c6 a; m1 F- o5 v& g0 h( @5 |
.tmp .word 0x1fe
8 p' F5 z  f; y& b' f' ]/ a5 [ldr r0,.tmp- v8 B' {) n4 ]
ldr r0,[r0]
( S! t& `% }: \- d! n( C0 Q/ X: j
Arm的ldr指令用于将内存的数据加载到寄存器。这里数字0x1fe被存储在ldr指令附近的位置.tmp处,ldr r0,.tmp被编译为ldr r0,[pc+offset]的形式,其中offset为符号.tmp相对ldr指令的偏移。这样ldr r0,.tmp获取了符号.tmp的地址,然后再次使用ldr r0,[r0]将.tmp地址处的数据取出,即0x1fe。  s7 \% t& R# Y% l# j+ h
通过以上的描述,我们明确了Arm指令中常数表达式的真正含义,并基于此编写了一个验证常数表达式的函数。最后,我们解析了如何在Arm中实现非常数表达式数据的传送,并讨论了它的实现。希望对你理解Arm的常数表达式有所帮助。' S/ Q& X, @' z* d) D" }
  • TA的每日心情
    开心
    2023-1-3 15:10
  • 签到天数: 2 天

    [LV.1]初来乍到

    2#
    发表于 2021-6-4 13:20 | 只看该作者
    ARM指令中的“立即数”就是常数表达式
    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    关闭

    推荐内容上一条 /1 下一条

    EDA365公众号

    关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

    GMT+8, 2025-6-23 02:36 , Processed in 0.093750 second(s), 26 queries , Gzip On.

    深圳市墨知创新科技有限公司

    地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

    快速回复 返回顶部 返回列表