ARM汇编之解惑条件标志,条件码,条件执行

什么是条件执行(Conditional execution),它的机理是,根据运算结果更新的条件标志(condition flags),来判断指令的条件码(Condition code)是否符合条件,符合条件就执行,不符合条件则不执行。

A32/T32指令可以根据之前汇编指令更新的条件标志,来带条件的执行当前的汇编指令。为了让汇编指令带条件的执行,需要为汇编指令增加条件码后缀,这样就可以让处理器基于条件标志来测试是否需要执行该指令,如果条件测试不符,指令不会被执行,也不会影响其他任何标志,更不会产生异常,但是由于仍然占用了一个流水线空间,会消耗一个指令周期,除此之外什么也不会发生。

  • 几乎所有A32的指令都可以基于APSR中的条件标志,进行带条件判断的执行。
  • 而T32的话只支持部分 指令带条件的执行,分支指令,CBZ(零条件分支)/CBNZ(非零条件分支)——根据寄存器结果是否为零在同一个执行状态下进行短距离(0~126字节)跳转,IT(If-Then,16位指令)。
  • 同样在A64里面也只有少数指令可以真正的有条件执行(也就是说,要是条件测试不符,PC值仍然是往前增加的,除此没有别的影响),这些指令包括 B.EQ,CBNZ,CBZ,TBNZ,TBZ。

条件标志(condition flags)

总共有四个条件标志 N, Z, C, V,A32/T32中可存储更新到APSR寄存器最高的4个位置。大致示意图如下:


image.png

大部分的A32汇编指令编码中也会包含这四个位,而16位的T32指令大部分没有这些,所以不能带条件的执行,16位的指令编码带有的信息有限通常不会包含这四个位。

下面我们来说说,什么情况下这四个位会被置位/清除

条件标志 置位/清除
N 当运算的结果为负数的话置位,其他情况清0
Z 当运算的结果为0的话置位,其他情况清0
C 当运算的结果产生进位或者减法运算没有借位的话置位,其他情况清0
V 当运算的结果产生溢出的话置位,其他情况清0
  • 可能看完上面的表还是有点不明白,下面继续进行补充说明。最复杂的是C位了(这也是今年大家都想要的一个位:)),那就先讲一下C位,C位会在下面四种情况下被置位:

    • 无符号的(unsigned)加法运算(包括比较指令CMN)如果产生进位,C置位。

    • 无符号的(unsigned)减法运算(包括比较指令CMP)如果没有借位(减完结果是正的),C置位。

    • 移位操作也会影响C位(可以参考我之前文章中对移位操作介绍,ARM汇编指令中灵活的第二操作数),C的值是最后一个被移位器移出的位值,当然这个被移出的位值如果为1那么C值就为1

    • 除了上面3种情况,C位通常是不会被改变的,但也有特殊指令会造成C位改变,这个如果自己在编程时会用到C位最好参考一下汇编指令手册,确认一下对应的指令是否对C位有特殊影响。

例子1

ldr r1,=0xffffffff
ldr r2,=0x2
adds r3,r1,r2

例子2

mov r2,#3
mov r1,#1
subs r3,r2,r1;r3 = r2 - r1
  • 在来说一下V位,这个位是针对有符号的加法/减法及比较的,溢出,何为溢出呢,如果运算的结果大于等于2的31次方,或者小于负的2的31次方则V置位

例子:

ldr r1,=0x7fffffff
ldr r2,=0x2
adds r3,r1,r2
  • N位是用来检测运算结果是否为负数时比较有用。什么是负数呢,在计算机系统,负数通常用二进制的补码来表示,如果最高有效位被置位,则该二进制补码为负数。也就是说运算结果最高位为1那么N位就置位。

例子:

mov r1,#-1
mov r2,#-2
adds r3,r1,r2
  • Z位是最好理解的,只有运算的结果所有的位都为0则,Z置位。

例子:

mov r1,#0
mov r2,#0
adds r3,r1,r2

更新条件标志

  • 在A32/T32中条件标志大部分情况下,不会自动更新到APSR中,只有你的指令明确的告诉它要更新它才会去更新,比如你在汇编指令中使用 S 后缀明确告诉它要去更新 APSR中的这些位。如下面的这条指令语法格式,你用ADDS就会去更新条件标志,用ADD就不会去更新。
    ADD{S}{cond} {Rd}, Rn, Operand2
  • 至于四个标志位被更新了几个,这个跟具体的指令有关,有的4位都更新了,有的只更新了1位或者都不更新。为什么会更新多位,因为计算机并不知道你操作的是有符号数还是无符号数,是正数还是负数,它只能傻傻的把所有可能全部给你列出来,所以你要对你写的程序负责,你要清楚你在做什么,你是做有符号的操作,还是无符号的操作,还是对正/负数操作,并以此选择你想要测试的条件标志位,来看操作结果是否符合预期,简单说就是你要什么,你就要去判断什么。

例子:

ldr r1,=0x7fffffff
ldr r2,=0x2
adds r3,r1,r2
bvc stop
nop
nop
nop

stop b stop

下图是上面程序运行之后,条件标志更新结果:

image.png
  • 因为计算机不知道你要的是什么,所有它把所有情况都考虑进去了
    • 如果r1,r2是无符号数,显然加法结果没有溢出因此C位为0
    • 如果r1,r2是有符号数,加法结果超过了有符号数的上限而溢出因此V位为1
    • 同时r3 = r1 + r3 = 0x80000001,最高位是1,因此N位也为1
    • 计算的结果显然并不为0,因此Z为为0。

因此接下来你要用合适的条件码来对操作结果进行判断,比如你明确r1,r2是有符号数,你要判断有符号的加法是否溢出,你就需要对V位进行判断,如例子中的vc条件码。

备注:CMP, CMN,TEQ, TST这几条指令总是会更新条件标志位。他们没有S后缀的格式。如果带条件执行的指令没有被执行,它不会影响条件标志。

  • A64中这四个条件标志是放在NZCV系统寄存器的,除了CMP,CMN, CCMP, CCMN, TST这几条总是会更新条件标志的,其他指令需要更新条件标志同样需要带S的后缀。

条件码(Condition code)

  • 条件码由两个字母组成,在A32/A64中支持条件码后缀的汇编指令中通常会有一个 {cond}的语法选项,比如下面ADD的语法格式
    ADD{S}{cond} {Rd}, Rn, Operand2
  • 对于T32指令,条件码在前面的IT指令中编码。

所有支持的条件码(下图第一列)及含义如下图所示

image.png

例子:

    mov r2,#6
    ldr r1,=0x7fffffff
    subs r3,r2,r1;r3 = r2 - r1
    bgt stop
    nop
    nop
    nop 
stop b stop

比较指令

  • 上面提到的CMP, CMN,TEQ, TST这几条指令,总是会更新条件标志位,但运算结果总是被扔掉,不会进行保存。

他们的语法格式如下

CMP{cond} Rn, Operand2;Rn - Operand2 操作类似SUBS(除了扔掉运算结果)
CMN{cond} Rn, Operand2;Rn + Operand2 操作类似ADDS(除了扔掉运算结果)
TEQ{cond} Rn, Operand2;Rn EOR Operand2  操作类似EORS(除了扔掉运算结果)
TST{cond} Rn, Operand2;Rn AND Operand2  操作类似ANDS(除了扔掉运算结果)

备注:对TEQ和TST来说不会影响到V标志,N位和Z位会根据操作结果会被更新,Operand2移位操作也可能会影响到C位。而CMP和CMN则根据运算结果都有可能会被影响到。

例子:

    mov r1,#-1
    mov r2,#0
    tst r1,r2
    beq stop
    nop
    nop
    nop

stop b stop

为什么需要条件执行

  • 因为条件执行比条件分支效率更高。在没有分支预测(至于什么是分支预测,有兴趣的同学可以参考计算机原理的相关书籍,有详细介绍)的ARM处理器上如果发生分支跳转,需要重填三个机器指令周期的流水线,当然如果有分支预测功能的ARM处理器在预测失败时,也是需要重填的。
    下面是从ARM手册摘出来的一个例子:

C语言描述的算法如下:

int gcd(int a, int b)
{
    while (a != b)
      {
        if (a > b)
            a = a - b;
        else
            b = b - a;
      }
    return a;
}

如果用条件分支实现,代码如下:

gcd     CMP      r0, r1
        BEQ      end
        BLT      less
        SUBS     r0, r0, r1  ; could be SUB r0, r0, r1 for A32
        B        gcd
less
        SUBS     r1, r1, r0  ; could be SUB r1, r1, r0 for A32
        B        gcd
end

如果用带条件指令的条件执行,代码如下:

gcd
        CMP      r0, r1
        SUBGT    r0, r0, r1
        SUBLE    r1, r1, r0
        BNE      gcd

跟用分支执行的比较,该代码长度更小,而且执行的也更快。


参考资料

【1】DUI0801I_armasm_user_guide
【2】ARM Development Tools
【3】ARM Assembly Language_ Fundamentals and Techniques (2nd ed.)
【4】DDI0487C_a_armv8_arm

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容