ART世界探险(4) - 数据传送指令和桶型移位器

ART世界探险(4) - 数据传送指令和桶型移位器

数据传送指令

将数在寄存器之间传递,或者将立即数传给寄存器。所谓的立即数,就是直接写在指令里的数,比如MOV X0,100,这个100就是立即数。立即数会存在指令的参数中。

将立即数传给寄存器:MOV

命令格式:MOV 寄存器, 立即数

AArch64状态下例:

MOV X0,100
MOV W1,200

AArch32状态例:

MOV R2,50

立即数取非送到寄存器:MVN

格式:MVN 寄存器,立即数

MVN X0,1,相当于X0=~1

我们写一个函数取非,看看编译成汇编指令是什么样子的.源代码如下:

long mvn(long value){
    return ~value;
}

反编译之后的代码:

; __int64 __fastcall mvn(__int64)
EXPORT _Z3mvnl
_Z3mvnl
MVN             X0, X0
RET

编译得真棒,就两条指令!

在AArch32下是这样的:

; _DWORD __fastcall mvn(__int32)
EXPORT _Z3mvnl
_Z3mvnl
MVNS            R0, R0
BX              LR

因为long在64位和32位系统上定义不同,所以在AArch64下是64位的,而编成AArch32下变成32位了。

然后我们看看同样的功能用java写出来之后,再通过ART编译之后的结果:

源码:

    public static long mvn(long value){
        return ~value;
    }

java字节码:

  public static long mvn(long);
    Code:
       0: lload_0
       1: ldc2_w        #2                  // long -1l
       4: lxor
       5: lreturn
  1. lload_0,从栈里将函数的实参取出来。
  2. ldc2_w,从常量池中读出参数-1l.
  3. 做异或运算。
  4. 返回长整型。

Dalvik字节码:

    DEX CODE:
      0x0000: 1600 ffff                 | const-wide/16 v0, #-1
      0x0002: c220                      | xor-long/2addr v0, v2
      0x0003: 1000                      | return-wide v0

Dalvik指令显示优势了,不用去查常量池了,直接立即数放在const-wide指令中。

OAT代码:

    CODE: (code_offset=0x005027fc size_offset=0x005027f8 size=80)...
      0x005027fc: d1400bf0  sub x16, sp, #0x2000 (8192)
      0x00502800: b940021f  ldr wzr, [x16]
      suspend point dex PC: 0x0000

首先将传进来的参数保存一下,x1是传进来的参数那个value,我们把它暂时存在sp+40位置。因为x1还要计算用。
LR的值也先存一下,函数返回的时候还得用呢。存到sp+24中。

      0x00502804: f81e0fe0  str x0, [sp, #-32]!
      0x00502808: f9000ffe  str lr, [sp, #24]
      0x0050280c: f90017e1  str x1, [sp, #40]

下面这两句是判断当前状态。
cbnz指令是不为0则跳转,跳转到pTestSuspend过程中去。

      0x00502810: 79400250  ldrh w16, [tr] (state_and_flags)
      0x00502814: 35000170  cbnz w16, #+0x2c (addr 0x502840)

常量-1,传给x16.
stur,将这个常量-1存到sp+12的内存。
然后再从sp+12x内存把这个常量重新读出来到x0中。
sp+40这个值是我们刚开始进来时将第1个参数保存的地方,大家还记得吧?现在再把它重新装回x1里。

      0x00502818: 92800010  mov x16, #0xffffffffffffffff
      0x0050281c: f800c3f0  stur x16, [sp, #12]
      0x00502820: f840c3e0  ldur x0, [sp, #12]
      0x00502824: f94017e1  ldr x1, [sp, #40]

参数终于凑齐了,可以开始进行异或运算了。
ARM中的异或指令的助记符是EOR: eor x2, x0, x1

      0x00502828: ca010002  eor x2, x0, x1

异或的结果在x2里,把它暂时保存在sp+12中。
然后再从sp+12中把刚才x2那个计算结果放到x0中。因为返回参数要放在x0中。
从sp+24中再把LR的值读回来,恢复一下栈指针,然后就可以返回了。

      0x0050282c: f800c3e2  stur x2, [sp, #12]
      0x00502830: f840c3e0  ldur x0, [sp, #12]
      0x00502834: f9400ffe  ldr lr, [sp, #24]
      0x00502838: 910083ff  add sp, sp, #0x20 (32)
      0x0050283c: d65f03c0  ret

后面这段代码是给前面讲到的cbnz跳转用的。

      0x00502840: f9421e5e  ldr lr, [tr, #1080] (pTestSuspend)
      0x00502844: d63f03c0  blr lr
      suspend point dex PC: 0x0000
      0x00502848: 17fffff4  b #-0x30 (addr 0x502818)

庆祝一下,虽然简单,但是我们已经看懂了一段真正OAT编译生成的代码了!

MOV操作SP

MOV指令操作SP或WSP,其实是ADD X1/SP, X2/SP, #0命令的别名。

例如:

MOV X0,SP

实际上是在执行:

ADD X0,SP,#0

MOV操作通用寄存器

这种情况下的MOV操作,实际上是ORR操作的别名。

例如:

MOV X0,X1
MOV W2,W3

分别相当于:

ORR X0,XZR,X1
ORR W2,WZR,W3

桶形移位器

在讲其它计算相关的指类之前,我们先看一个ARM芯片中特有的有趣的东西,叫做桶形移位器。

一般的计算操作,都是在算术逻辑单元ALU中完成的。但是ARM芯片在ALU之外,还有一个桶形的移位器,可以对数据进行移位的预处理,再送入到ALU中进行运算。
请注意,这个额外的移位操作是与ALU运算在同一个指令周期中完成的,桶形移位器的加入,增加了数据处理指令的灵活性。

在AArch64状态下,桶形移位器支持4种操作:

  • LSL:逻辑左移
  • LSR:逻辑右移
  • ASR:算术右移
  • ROR:循环右移

在AArch32状态下,还支持第5种操作:

  • RRX:扩展的循环右移

LSL逻辑左移

左移最省事,不用管符号,就相当于C语言中的:

unsigned long l_shift(unsigned long x0, unsigned long x1){
    return x0 << x1;
}

我们看看AArch64下反汇编的结果:

; __int64 __fastcall l_shift(unsigned __int64, unsigned __int64)
EXPORT _Z7l_shiftmm
_Z7l_shiftmm
LSL             X0, X0, X1
RET

太完美了!除了RET,就LSL一句话。

再来看看AArch32模式下的:

; _DWORD __fastcall l_shift(unsigned __int32, unsigned __int32)
EXPORT _Z7l_shiftmm
_Z7l_shiftmm
LSLS            R0, R1
BX              LR

LSL后面的S意思是修改标志位的状态,也就是说,比如移出的位是1,将更新C进位标志。

Java中的左移

我们看看对应的java写法,在java字节码,Dalvik字节码和OAT生成的代码中是什么样子吧:
Java字节码:

    Code:
       0: lload_1
       1: lload_3
       2: l2i
       3: lshl
       4: lreturn

lshl左移指令第二个参数需要int型,所以要多做一步l2i的操作。

Dalvik字节码:

  6: long com.yunos.xulun.testcppjni2.TestART.l_shift(long, long) (dex_method_idx=16779)
    DEX CODE:
      0x0000: 8460                      | long-to-int v0, v6
      0x0001: a300 0400                 | shl-long v0, v4, v0
      0x0003: 1000                      | return-wide v0

OAT代码:

前面的压栈备份,和检测是不是suspend状态的cbnz,上例已经讲过了,这里就不再讲了。

    CODE: (code_offset=0x0050295c size_offset=0x00502958 size=92)...
      0x0050295c: d1400bf0  sub x16, sp, #0x2000 (8192)
      0x00502960: b940021f  ldr wzr, [x16]
      suspend point dex PC: 0x0000
      GC map objects:  v3 ([sp + #40])
      0x00502964: f81e0fe0  str x0, [sp, #-32]!
      0x00502968: f9000ffe  str lr, [sp, #24]
      0x0050296c: b9002be1  str w1, [sp, #40]
      0x00502970: f802c3e2  stur x2, [sp, #44]
      0x00502974: f80343e3  stur x3, [sp, #52]
      0x00502978: 79400250  ldrh w16, [tr] (state_and_flags)
      0x0050297c: 35000190  cbnz w16, #+0x30 (addr 0x5029ac)

sp+52是第二个长整数参数,读到x0中。
然后调用sbfx,带符号的扩展,取32位,结果放到w1中。这就完成了一次long-to-int的计算。
接着再把w1中的值存到栈里,sp+8中。
把第一个参数从sp+44中读出来,放到x0中。
再把刚存进sp+8中的long-to-int的值重新读回w1中,白折腾两趟,哈哈
终于可以运行lsl了,结果在x2中。
把x2存到sp+8中,再从sp+8折腾到x0,准备返回。

      0x00502980: f84343e0  ldur x0, [sp, #52]
      0x00502984: 13007c01  sbfx w1, w0, #0, #32
      0x00502988: b9000be1  str w1, [sp, #8]
      0x0050298c: f842c3e0  ldur x0, [sp, #44]
      0x00502990: b9400be1  ldr w1, [sp, #8]
      0x00502994: 9ac12002  lsl x2, x0, x1
      0x00502998: f90007e2  str x2, [sp, #8]
      0x0050299c: f94007e0  ldr x0, [sp, #8]
      0x005029a0: f9400ffe  ldr lr, [sp, #24]
      0x005029a4: 910083ff  add sp, sp, #0x20 (32)
      0x005029a8: d65f03c0  ret

后面还是pTestSuspend的调用。

      0x005029ac: f9421e5e  ldr lr, [tr, #1080] (pTestSuspend)
      0x005029b0: d63f03c0  blr lr
      suspend point dex PC: 0x0000
      GC map objects:  v3 ([sp + #40])
      0x005029b4: 17fffff3  b #-0x34 (addr 0x502980)

LSR逻辑右移

相当于C语言中的无符号右移,我们用C语言模拟一下:

unsigned long r_shift(unsigned long x0, unsigned long x1){
    return x0 >> x1;
}

我们看看翻译的结果:
AArch64的:

; __int64 __fastcall r_shift(unsigned __int64, unsigned __int64)
EXPORT _Z7r_shiftmm
_Z7r_shiftmm
LSR             X0, X0, X1
RET

果然就被处理成LSR了!

再看AArch32的:

; _DWORD __fastcall r_shift(unsigned __int32, unsigned __int32)
EXPORT _Z7r_shiftmm
_Z7r_shiftmm
LSRS            R0, R1
BX              LR

LSRS,带置位的LSR,非常棒。

ASR算术右移

就是带符号的右移,我们用C模拟一下:

    public static long r_shift(long x0, long x1){
        return x0 >> x1;
    }

不出我们所料,就直接是ASR啊。

; __int64 __fastcall r_shift2(__int64, __int64)
EXPORT _Z8r_shift2ll
_Z8r_shift2ll
ASR             X0, X0, X1
RET

AArch32下,也就是ASRS:

; _DWORD __fastcall r_shift2(__int32, __int32)
EXPORT _Z8r_shift2ll
_Z8r_shift2ll
ASRS            R0, R1
BX              LR

Java中对应的指令是lshr,Dalvik指令shr-long,其它跟左移都一样。

循环右移ROR

这个就不多讲了,将右移出去的值补到左边。

扩展循环右移

就是C作为符号位放到最左边。

传送数据与桶移位同时进行

ARM的魔法开始了,我们可以将一个寄存器中的值左移两位,再送到另一个寄存器中:

MVN X0,X1,LSL #2

只需要一个指令周期哟。

以后我们学习了计算指令如加减之类的,也照样可以使用这个桶形移位器,比如一个数加另一个数乘以2的倍数,只要一条加法就搞定了。

小结

这次探险先到这里,我们学习了桶形移位器,这是个可以在ALU运算前对第二个操作数进行操作的神奇器件。
同时也学习了MOV, MVN和几个可以单独使用的移位指令,以及他们对应的java指令。

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

推荐阅读更多精彩内容

  • 8086汇编 本笔记是笔者观看小甲鱼老师(鱼C论坛)《零基础入门学习汇编语言》系列视频的笔记,在此感谢他和像他一样...
    Gibbs基阅读 37,077评论 8 114
  • 计算机通过执行指令序列来使机器得以工作,所以对于每一系列的计算机都有指定的一组指令集供计算机使用,这组指令...
    未来科技工作室阅读 7,789评论 1 10
  • 女人为什么要常做乳腺保养? 世界卫生组织(WHO)全球女性乳腺疾病调查数据显示:90%以上的女性胸部都有问题! 1...
    慧美小桃阅读 570评论 0 0
  • 2016-08-08 13:419 花Ⅲ 2016715 在孤独里开出一朵花来,那是属于自己独一无二的声音。 已经...
    包小包ya阅读 164评论 0 0
  • 文/古尔浪洼 1 春节前,儿子就开始念念不忘他的生日,每天都在计算还有多少天他就要过5岁生日了。今天晚上,睡觉前,...
    古尔浪洼阅读 676评论 5 6