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

ARM的常数表达式

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x
如果说Intel指令中的立即数,相信大家都很熟悉。类似的,ARM指令中的“立即数”就是常数表达式。之所以称为常数表达式,而不称为立即数是有原因的。
' ]- i: O: v/ d/ HIntel指令属于CISC指令集,指令是不定长的,因此可以将任意32位立即数编码到指令内。" t7 ?; M! Z3 O% w4 a: c
+ g# C, {7 d8 l3 g
+ x; \; `+ x$ X; t& k) c- L; Q! d3 C
% B  {# [$ r: J3 t# g" `6 ?
Arm指令属于RISC指令集,指令是定长的32字节。众所周知,指令中操作码是必须的字段,如果把32位立即数直接编码到指令内部,操作码就无“容身之地”了……( n+ y6 s! `7 P8 [/ U7 k

6 l4 x: l9 M, i- ?6 ~& j  ^8 n
% J( a& i3 ~, s$ \0 ^2 d

/ y, Z5 C0 p, w* T/ h0 R因此,Arm指令中“立即数”的位数必小于32位。那么如何在Arm指令中正常表示立即数呢?我们看看Arm的通用指令格式。
# X9 K& q7 f" l0 U
5 I5 E" p3 \! L6 W+ N2 o
6 j" ?) A3 a& ~7 D4 H
, ^; W/ x; S/ M  l" q0 m9 Z
Arm指令中,操作码(opcode)、目的操作数(Rd)、源操作数1(Rn)是必须的字段。条件码(cond)、符号位标记(s)源操作数2(oprand2)是可选的。其中Rd和Rn必须是寄存器,因此Arm的“立即数”只能存储在oprand2。1 p+ H5 y* N. b3 D5 x
- v6 D: }5 a+ i. U  f* z
3 j8 F6 r9 u1 M' A
在Arm的指令编码内,使用“立即数”的指令为“立即数”提供了12bit的存储空间,也就是说Arm的“立即数”只能表示212=4096个数字。这显然无法表示所有的32位立即数,如果使用这12bit表示0~4095的数字,那么从4096~(232-1)的数组都不能表示了。考虑到这种“后果”,Arm指令集的设计者们使用了一个技巧,即使用常数表达式代替立即数的概念。
0 ^1 ]4 i1 ^. ?常数表达式表示一个常数,且该常数对应8位的位图,即可以由一个8位的常数通过循环移位偶数位得到。

# M. U4 A! L/ I+ _5 V
$ N4 f. F: `, T* G

1 Y: i% r- y. O; ~( `9 w4 e( W
比如0x3fc可以由8位常数0xff循环左移2位或循环右移30位得到,是常数表达式。再比如0x1fe,虽然可以有0xff循环左移1位或循环右移31位得到,但是移动的位数不是偶数,因此不是常数表达式。# ~6 j' q7 R  _0 `  ?
根据常数表达式的定义,常数表达式只需要12bit的存储空间。
1 t: k  y% o% G5 E8 q% ~! }
% n( m6 X3 Q- ~
/ U6 U+ S& S* J! B2 H3 ^) j+ K. d12bit空间中,低8字节存储8位位图,高4字节存储循环右移的次数。4字节只能表示24=16种移动次数,但是由于常数表达式定义中,移位限定为偶数次,因此这4个字节刚好能表示0、2、4、6、8…32位16个数字!6 y3 G# t) `  i
比如0x3fc的二进制表示为0xfff,即使用8位数字0xff循环右移0xf*2=30次得到。
9 J6 m* i! F( H( D+ n明确了常数表达式的含义后,可以通过简单的编程识别一个数字是否是常数表达式。$ l0 O' M' b. R% S: e: G
/** u$ \/ a+ c. U, `" z; h6 P$ C- g
循环左移两位
- N, I) N, l' Q/ z*/
! Q! }+ c1 u  Q$ Z# w1 @void roundLeftShiftTwoBit(unsigned int& num)6 g2 U& _. O7 F5 ?1 p2 v/ q' i' O
{( I6 \4 l+ N" G, P3 u
unsigned int oveRFlow=num & 0xc0;//取左移即将溢出的两位
' ]& g" K" X4 b7 ?# _num=(num<<2) (overFlow>>30);//将溢出部分追加到尾部
; k; h3 L# K; `' j5 A/ x}: M) g. x' ~  Z8 Z4 i% i0 h# G
/*7 }" `% p; L5 R/ k4 z0 E
判断num是否是常数表达式,8位数字循环右移偶数位得到/ N/ [( J& J. J$ R6 }' k
*/
2 O3 y( j& m' t9 x( pbool constExpr(unsigned int num)/ |4 G6 H0 a7 a
{
2 u" ]( }0 ?% C1 ?for(int i=0;i<16;i++){
* J& a0 s! _, R9 J0 i, `* Y! Qif(num<=0xff)
0 A4 N/ F3 L6 a( x1 G" E6 y1 freturn true;//有效位图
: k+ h3 R6 S2 N1 M6 q9 i0 Lelse0 e" J9 I' l( |/ O# D1 N
roundLeftShiftTwoBit(num);//循环左移2位
* |# q- o: G) T5 k! i3 Z1 p}4 w/ @9 }. }' j4 @" `' F( p
}

( p: \+ d1 M; f& KroundLeftShiftTwoBit函数将一个无符号32位整数循环左移2位,constExpr反复将一个无符号32位整数循环左移2位,直到发现该数字在0到0xff之间为止,否则表示该数字不是常数表达式。
1 r# J. i4 u0 @6 X" O通过常数表达式,Arm指令便避免了“立即数”仅仅局限于0~4095的数字范围的问题了。但是常数表达式仍不能表示全部的32位整数,比如0x1fe。对于非常数表达式的数字,只能通过拆分来解决。
  ^7 l+ U8 E1 B& ?6 h" c( _, D比如Arm指令mov r0,#0x1fe不是合法的指令,因为0x1fe不是常数表达式。那么只能将0x1fe拆分,表示为两条指令,比如:  E: z/ `& \- M+ ]' Y5 I6 H
mov r0,#0xfe
% C# v$ U7 W; Q- E: U" iadd r0,r0,#0x100
2 n+ g% u7 B* J5 j5 h: r
除了拆分数字的方法,Arm的LDR宏使用临时变量的方式实现非常数表达式的数据传送,即:
* b! x  @. z6 `3 w6 E* fLDR r0,=0x1fe
! z! m7 i4 c' y5 v; p- aldr宏指令不是Arm的真正指令,它由如下两条指令实现:
: u0 R! w6 k# O$ a2 R& |.tmp .word 0x1fe
0 V( [: q9 v4 Z4 Q8 [% ^ldr r0,.tmp8 A# R$ M5 W( c) M
ldr r0,[r0]

& k8 q8 ~0 d  }3 y/ k1 h' SArm的ldr指令用于将内存的数据加载到寄存器。这里数字0x1fe被存储在ldr指令附近的位置.tmp处,ldr r0,.tmp被编译为ldr r0,[pc+offset]的形式,其中offset为符号.tmp相对ldr指令的偏移。这样ldr r0,.tmp获取了符号.tmp的地址,然后再次使用ldr r0,[r0]将.tmp地址处的数据取出,即0x1fe。
( R7 R7 ~' ^0 ~! ]: _2 U) J通过以上的描述,我们明确了Arm指令中常数表达式的真正含义,并基于此编写了一个验证常数表达式的函数。最后,我们解析了如何在Arm中实现非常数表达式数据的传送,并讨论了它的实现。希望对你理解Arm的常数表达式有所帮助。9 L: T/ Y, G1 l0 R% G
  • 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-11-24 07:14 , Processed in 0.156250 second(s), 26 queries , Gzip On.

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

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

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