ART世界探险(5) - 计算指令

来源:互联网 发布:住户部门贷款数据 编辑:程序博客网 时间:2024/04/16 12:03

ART世界探险(5) - 计算指令

整数运算

Java的整型运算

我们先看看JVM是如何处理这些基本整数运算的吧。

    public static long add(long a, long b){        return a+b;    }    public static long sub(long a,long b){        return a-b;    }    public static long mul(long a, long b){        return a*b;    }    public static long div(long a,long b){        return a/b;    }    public static long mod(long a,long b){        return a%b;    }

翻译成字节码是这样的,非常整齐:

  public static long add(long, long);    Code:       0: lload_0       1: lload_2       2: ladd       3: lreturn  public static long sub(long, long);    Code:       0: lload_0       1: lload_2       2: lsub       3: lreturn  public static long mul(long, long);    Code:       0: lload_0       1: lload_2       2: lmul       3: lreturn  public static long div(long, long);    Code:       0: lload_0       1: lload_2       2: ldiv       3: lreturn  public static long mod(long, long);    Code:       0: lload_0       1: lload_2       2: lrem       3: lreturn

加是add,减是sub,乘是mul,除是div,取模是rem。

转换成Dalvik指令的话,连lload都省了,更是看起来赏心悦目。

  1: long com.yunos.xulun.testcppjni2.TestART.add(long, long) (dex_method_idx=16778)    DEX CODE:      0x0000: 9b00 0204                 | add-long v0, v2, v4      0x0002: 1000                      | return-wide v0

我们看看在ARM上的实现。

ARM的整型运算

C++代码和Java基本上是同出一辙的:

long add(long a, long b){    return a+b;}long sub(long a,long b){    return a-b;}long mul(long a, long b){    return a*b;}long div(long a,long b){    return a/b;}long mod(long a,long b){    return a%b;}

ARM v8a的整数运算

我们看看在AArch64下译成什么:

0000000000000694 <_Z3addll>: 694:   8b010000    add x0, x0, x1 698:   d65f03c0    ret000000000000069c <_Z3subll>: 69c:   cb010000    sub x0, x0, x1 6a0:   d65f03c0    ret00000000000006a4 <_Z3mulll>: 6a4:   9b017c00    mul x0, x0, x1 6a8:   d65f03c0    ret00000000000006ac <_Z3divll>: 6ac:   9ac10c00    sdiv    x0, x0, x1 6b0:   d65f03c0    ret00000000000006b4 <_Z3modll>: 6b4:   9ac10c02    sdiv    x2, x0, x1 6b8:   9b018040    msub    x0, x2, x1, x0 6bc:   d65f03c0    ret

ARM v7a的整数运算

AArch32模式下,加减乘都是一条指令

00000fc0 <_Z3addll>:     fc0:   4408        add r0, r1     fc2:   4770        bx  lr00000fc4 <_Z3subll>:     fc4:   1a40        subs    r0, r0, r1     fc6:   4770        bx  lr00000fc8 <_Z3mulll>:     fc8:   4348        muls    r0, r1     fca:   4770        bx  lr

但是除法和取模就不是指令了,得调用函数来处理。

00000fcc <_Z3divll>:     fcc:   b508        push    {r3, lr}     fce:   f000 e830   blx 1030 <__aeabi_idiv>     fd2:   bd08        pop {r3, pc}00000fd4 <_Z3modll>:     fd4:   b508        push    {r3, lr}     fd6:   f000 e89a   blx 110c <__aeabi_idivmod>     fda:   4608        mov r0, r1     fdc:   bd08        pop {r3, pc}

算除法的这个函数可是不短啊,我们先看一下,这个将来可供我们学完指令集之后复习用:

00001030 <__aeabi_idiv>:    1030:   e3510000    cmp r1, #0    1034:   0a000030    beq 10fc <__aeabi_idiv+0xcc>    1038:   e020c001    eor ip, r0, r1    103c:   42611000    rsbmi   r1, r1, #0    1040:   e2512001    subs    r2, r1, #1    1044:   0a00001f    beq 10c8 <__aeabi_idiv+0x98>    1048:   e1b03000    movs    r3, r0    104c:   42603000    rsbmi   r3, r0, #0    1050:   e1530001    cmp r3, r1    1054:   9a00001e    bls 10d4 <__aeabi_idiv+0xa4>    1058:   e1110002    tst r1, r2    105c:   0a000020    beq 10e4 <__aeabi_idiv+0xb4>    1060:   e16f2f11    clz r2, r1    1064:   e16f0f13    clz r0, r3    1068:   e0420000    sub r0, r2, r0    106c:   e3a02001    mov r2, #1    1070:   e1a01011    lsl r1, r1, r0    1074:   e1a02012    lsl r2, r2, r0    1078:   e3a00000    mov r0, #0    107c:   e1530001    cmp r3, r1    1080:   20433001    subcs   r3, r3, r1    1084:   21800002    orrcs   r0, r0, r2    1088:   e15300a1    cmp r3, r1, lsr #1    108c:   204330a1    subcs   r3, r3, r1, lsr #1    1090:   218000a2    orrcs   r0, r0, r2, lsr #1    1094:   e1530121    cmp r3, r1, lsr #2    1098:   20433121    subcs   r3, r3, r1, lsr #2    109c:   21800122    orrcs   r0, r0, r2, lsr #2    10a0:   e15301a1    cmp r3, r1, lsr #3    10a4:   204331a1    subcs   r3, r3, r1, lsr #3    10a8:   218001a2    orrcs   r0, r0, r2, lsr #3    10ac:   e3530000    cmp r3, #0    10b0:   11b02222    lsrsne  r2, r2, #4    10b4:   11a01221    lsrne   r1, r1, #4    10b8:   1affffef    bne 107c <__aeabi_idiv+0x4c>    10bc:   e35c0000    cmp ip, #0    10c0:   42600000    rsbmi   r0, r0, #0    10c4:   e12fff1e    bx  lr    10c8:   e13c0000    teq ip, r0    10cc:   42600000    rsbmi   r0, r0, #0    10d0:   e12fff1e    bx  lr    10d4:   33a00000    movcc   r0, #0    10d8:   01a00fcc    asreq   r0, ip, #31    10dc:   03800001    orreq   r0, r0, #1    10e0:   e12fff1e    bx  lr    10e4:   e16f2f11    clz r2, r1    10e8:   e262201f    rsb r2, r2, #31    10ec:   e35c0000    cmp ip, #0    10f0:   e1a00233    lsr r0, r3, r2    10f4:   42600000    rsbmi   r0, r0, #0    10f8:   e12fff1e    bx  lr    10fc:   e3500000    cmp r0, #0    1100:   c3e00102    mvngt   r0, #-2147483648    ; 0x80000000    1104:   b3a00102    movlt   r0, #-2147483648    ; 0x80000000    1108:   ea000007    b   112c <__aeabi_idiv0>0000110c <__aeabi_idivmod>:    110c:   e3510000    cmp r1, #0    1110:   0afffff9    beq 10fc <__aeabi_idiv+0xcc>    1114:   e92d4003    push    {r0, r1, lr}    1118:   ebffffc6    bl  1038 <__aeabi_idiv+0x8>    111c:   e8bd4006    pop {r1, r2, lr}    1120:   e0030092    mul r3, r2, r0    1124:   e0411003    sub r1, r1, r3    1128:   e12fff1e    bx  lr

传统armeabi的整数运算

加减乘还是没有问题:adds,subs,muls,改状态位。

00001248 <_Z3addll>:    1248:   1840        adds    r0, r0, r1    124a:   4770        bx  lr0000124c <_Z3subll>:    124c:   1a40        subs    r0, r0, r1    124e:   4770        bx  lr00001250 <_Z3mulll>:    1250:   4348        muls    r0, r1    1252:   4770        bx  lr

除法和取模也是调函数:

00001254 <_Z3divll>:    1254:   b508        push    {r3, lr}    1256:   f001 ff47   bl  30e8 <_Unwind_GetTextRelBase+0x8>    125a:   bd08        pop {r3, pc}0000125c <_Z3modll>:    125c:   b508        push    {r3, lr}    125e:   f001 ff4b   bl  30f8 <_Unwind_GetTextRelBase+0x18>    1262:   1c08        adds    r0, r1, #0    1264:   bd08        pop {r3, pc}

OAT编译出来的结果

Dalvik和ARM都学完之后,我们就可以看看Dalvik翻成OAT之后的结果是什么样子的了。

先看个加法的吧:

    CODE: (code_offset=0x0050270c size_offset=0x00502708 size=76)...      0x0050270c: d1400bf0  sub x16, sp, #0x2000 (8192)      0x00502710: b940021f  ldr wzr, [x16]      suspend point dex PC: 0x0000      0x00502714: f81e0fe0  str x0, [sp, #-32]!

复习一下,还是先备份参数到栈里:lr到sp+24,第一个参数到sp+40,第二个参数到sp+48。
然后判断是不是suspend。

      0x00502718: f9000ffe  str lr, [sp, #24]      0x0050271c: f90017e1  str x1, [sp, #40]      0x00502720: f9001be2  str x2, [sp, #48]      0x00502724: 79400250  ldrh w16, [tr] (state_and_flags)      0x00502728: 35000130  cbnz w16, #+0x24 (addr 0x50274c)

开始干活了,将那两个参数从sp+40和sp+48里面读回来,到x0和x1中。
然后算加法,结果在x2中。
x2值再送到栈里,再从栈里读回来到x0,最后返回。

      0x0050272c: f94017e0  ldr x0, [sp, #40]      0x00502730: f9401be1  ldr x1, [sp, #48]      0x00502734: 8b010002  add x2, x0, x1      0x00502738: f800c3e2  stur x2, [sp, #12]      0x0050273c: f840c3e0  ldur x0, [sp, #12]      0x00502740: f9400ffe  ldr lr, [sp, #24]      0x00502744: 910083ff  add sp, sp, #0x20 (32)      0x00502748: d65f03c0  ret      0x0050274c: f9421e5e  ldr lr, [tr, #1080] (pTestSuspend)      0x00502750: d63f03c0  blr lr      suspend point dex PC: 0x0000      0x00502754: 17fffff6  b #-0x28 (addr 0x50272c)

减法和乘法也是类似,我们直接看除法:
这时候64位的好处又体现出来了,不用调函数,直接有指令:
Dalvik代码是这样的:

  3: long com.yunos.xulun.testcppjni2.TestART.div(long, long) (dex_method_idx=16780)    DEX CODE:      0x0000: 9e00 0204                 | div-long v0, v2, v4      0x0002: 1000                      | return-wide v0

OAT代码,sdiv就搞定了。跟前面我们看到的C代码的结果,吻合得非常好。

    CODE: (code_offset=0x0050280c size_offset=0x00502808 size=96)...      0x0050280c: d1400bf0  sub x16, sp, #0x2000 (8192)      0x00502810: b940021f  ldr wzr, [x16]      suspend point dex PC: 0x0000      0x00502814: f81d0fe0  str x0, [sp, #-48]!      0x00502818: f90017fe  str lr, [sp, #40]      0x0050281c: f9001fe1  str x1, [sp, #56]      0x00502820: f90023e2  str x2, [sp, #64]      0x00502824: 79400250  ldrh w16, [tr] (state_and_flags)      0x00502828: 35000190  cbnz w16, #+0x30 (addr 0x502858)

先从sp+64中把除数读进来。
Java先做一件事情,判断除数是不是0。如果除数是0,则cbz会跳转到执行pThrowDivZero去抛出一个除0异常出来。

      0x0050282c: f94023e0  ldr x0, [sp, #64]      0x00502830: b40001a0  cbz x0, #+0x34 (addr 0x502864)

判断是否为0之后,还是把x0存到栈里。
再把被除数和除数都从栈里读出来。

      0x00502834: f80143e0  stur x0, [sp, #20]      0x00502838: f9401fe0  ldr x0, [sp, #56]      0x0050283c: f84143e1  ldur x1, [sp, #20]

开始做除法,结果在x2中,然后存栈里面。再从栈里读回来到x0里,返回。

      0x00502840: 9ac10c02  sdiv x2, x0, x1      0x00502844: f801c3e2  stur x2, [sp, #28]      0x00502848: f841c3e0  ldur x0, [sp, #28]      0x0050284c: f94017fe  ldr lr, [sp, #40]      0x00502850: 9100c3ff  add sp, sp, #0x30 (48)      0x00502854: d65f03c0  ret      0x00502858: f9421e5e  ldr lr, [tr, #1080] (pTestSuspend)      0x0050285c: d63f03c0  blr lr      suspend point dex PC: 0x0000      0x00502860: 17fffff3  b #-0x34 (addr 0x50282c)      0x00502864: f9422a5e  ldr lr, [tr, #1104] (pThrowDivZero)      0x00502868: d63f03c0  blr lr      suspend point dex PC: 0x0000

浮点运算

Java浮点运算

Java真是门好语言啊,JVM已经封装了所有跟浮点相关的细节,基本上从字节码上看,跟长整型只有细节的不同。

    public static double dadd(double a,double b){        return a+b;    }    public static double dsub(double a,double b){        return a-b;    }    public static double dmul(double a,double b){        return a*b;    }    public static double ddiv(double a,double b){        return a/b;    }

字节码如下:

  public static double dadd(double, double);    Code:       0: dload_0       1: dload_2       2: dadd       3: dreturn  public static double dsub(double, double);    Code:       0: dload_0       1: dload_2       2: dsub       3: dreturn  public static double dmul(double, double);    Code:       0: dload_0       1: dload_2       2: dmul       3: dreturn  public static double ddiv(double, double);    Code:       0: dload_0       1: dload_2       2: ddiv       3: dreturn

基本上就是将l换成d,其它没有什么变化。

ARM浮点运算

强大的ARM v8A芯片,已经不输于JVM的设计了,也是很简单。
源代码:

double dadd(double a,double b){    return a+b;}double dsub(double a,double b){    return a-b;}double dmul(double a,double b){    return a*b;}double ddiv(double a,double b){    return a/b;}

ARM v8a的浮点运算

汇编代码:

0000000000000760 <_Z4dadddd>: 760:   1e612800    fadd    d0, d0, d1 764:   d65f03c0    ret0000000000000768 <_Z4dsubdd>: 768:   1e613800    fsub    d0, d0, d1 76c:   d65f03c0    ret0000000000000770 <_Z4dmuldd>: 770:   1e610800    fmul    d0, d0, d1 774:   d65f03c0    ret0000000000000778 <_Z4ddivdd>: 778:   1e611800    fdiv    d0, d0, d1 77c:   d65f03c0    ret

我们可以看到,寄存器已经不是x开头的通用寄存器了,而变成了d开头的NEON寄存器。我们实际上是借用了ARM v7a才出现的NEON指令才使得指令变得这么简单。

ARM v7a的浮点运算:

同样是NEON指令,但是v7a的就比v8a的看起来要复杂一点。不过倒更清晰地反映了逻辑事实。
v7a的NEON指令需要用vmov将通用寄存器中的数传送到NEON寄存器中,然后再进行计算。结果再通过vmov送回到通用寄存器中。

00000fde <_Z4dadddd>:     fde:   ec41 0b17   vmov    d7, r0, r1     fe2:   ec43 2b16   vmov    d6, r2, r3     fe6:   ee37 7b06   vadd.f64    d7, d7, d6     fea:   ec51 0b17   vmov    r0, r1, d7     fee:   4770        bx  lr00000ff0 <_Z4dsubdd>:     ff0:   ec41 0b17   vmov    d7, r0, r1     ff4:   ec43 2b16   vmov    d6, r2, r3     ff8:   ee37 7b46   vsub.f64    d7, d7, d6     ffc:   ec51 0b17   vmov    r0, r1, d7    1000:   4770        bx  lr00001002 <_Z4dmuldd>:    1002:   ec41 0b17   vmov    d7, r0, r1    1006:   ec43 2b16   vmov    d6, r2, r3    100a:   ee27 7b06   vmul.f64    d7, d7, d6    100e:   ec51 0b17   vmov    r0, r1, d7    1012:   4770        bx  lr00001014 <_Z4ddivdd>:    1014:   ec41 0b17   vmov    d7, r0, r1    1018:   ec43 2b16   vmov    d6, r2, r3    101c:   ee87 7b06   vdiv.f64    d7, d7, d6    1020:   ec51 0b17   vmov    r0, r1, d7    1024:   4770        bx  lr

传统ARM的浮点运算

没啥说的,都得函数实现了:

00001248 <_Z3addll>:    1248:   1840        adds    r0, r0, r1    124a:   4770        bx  lr0000124c <_Z3subll>:    124c:   1a40        subs    r0, r0, r1    124e:   4770        bx  lr00001250 <_Z3mulll>:    1250:   4348        muls    r0, r1    1252:   4770        bx  lr00001254 <_Z3divll>:    1254:   b508        push    {r3, lr}    1256:   f001 ff47   bl  30e8 <_Unwind_GetTextRelBase+0x8>    125a:   bd08        pop {r3, pc}0000125c <_Z3modll>:    125c:   b508        push    {r3, lr}    125e:   f001 ff4b   bl  30f8 <_Unwind_GetTextRelBase+0x18>    1262:   1c08        adds    r0, r1, #0    1264:   bd08        pop {r3, pc}

x86芯片的运算指令

几种ARM下的RISC指令集的结果,我们都分析过了。下面我们看看32位的x86芯片上的整数和浮点运算吧。

x86的整数运算

32位的标志是使用32位的寄存器,比如eax,esp,esi。而64位下就是rax等等了。

00005f0 <_Z3addll>: 5f0:   8b 44 24 08             mov    0x8(%esp),%eax 5f4:   03 44 24 04             add    0x4(%esp),%eax 5f8:   c3                      ret     5f9:   8d b4 26 00 00 00 00    lea    0x0(%esi,%eiz,1),%esi00000600 <_Z3subll>: 600:   8b 44 24 04             mov    0x4(%esp),%eax 604:   2b 44 24 08             sub    0x8(%esp),%eax 608:   c3                      ret     609:   8d b4 26 00 00 00 00    lea    0x0(%esi,%eiz,1),%esi00000610 <_Z3mulll>: 610:   8b 44 24 08             mov    0x8(%esp),%eax 614:   0f af 44 24 04          imul   0x4(%esp),%eax 619:   c3                      ret     61a:   8d b6 00 00 00 00       lea    0x0(%esi),%esi00000620 <_Z3divll>: 620:   8b 44 24 04             mov    0x4(%esp),%eax 624:   89 c2                   mov    %eax,%edx 626:   c1 fa 1f                sar    $0x1f,%edx 629:   f7 7c 24 08             idivl  0x8(%esp) 62d:   c3                      ret     62e:   66 90                   xchg   %ax,%ax00000630 <_Z3modll>: 630:   8b 44 24 04             mov    0x4(%esp),%eax 634:   89 c2                   mov    %eax,%edx 636:   c1 fa 1f                sar    $0x1f,%edx 639:   f7 7c 24 08             idivl  0x8(%esp) 63d:   89 d0                   mov    %edx,%eax 63f:   c3                      ret    

x86的CISC的好处是总不至于要调一段复杂的函数来实现除法。

x86_64的整数运算

我们看看64位的rn寄存器出场之后的x86_64的整型指令吧:

00000000000006e0 <_Z3addll>: 6e0:   48 8d 04 37             lea    (%rdi,%rsi,1),%rax 6e4:   c3                      retq    6e5:   66 66 2e 0f 1f 84 00    data16 nopw %cs:0x0(%rax,%rax,1) 6ec:   00 00 00 00 00000000000006f0 <_Z3subll>: 6f0:   48 89 f8                mov    %rdi,%rax 6f3:   48 29 f0                sub    %rsi,%rax 6f6:   c3                      retq    6f7:   66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1) 6fe:   00 00 0000000000000700 <_Z3mulll>: 700:   48 89 f8                mov    %rdi,%rax 703:   48 0f af c6             imul   %rsi,%rax 707:   c3                      retq    708:   0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1) 70f:   00 0000000000000710 <_Z3divll>: 710:   48 89 fa                mov    %rdi,%rdx 713:   48 89 f8                mov    %rdi,%rax 716:   48 c1 fa 3f             sar    $0x3f,%rdx 71a:   48 f7 fe                idiv   %rsi 71d:   c3                      retq    71e:   66 90                   xchg   %ax,%ax0000000000000720 <_Z3modll>: 720:   48 89 fa                mov    %rdi,%rdx 723:   48 89 f8                mov    %rdi,%rax 726:   48 c1 fa 3f             sar    $0x3f,%rdx 72a:   48 f7 fe                idiv   %rsi 72d:   48 89 d0                mov    %rdx,%rax 730:   c3                      retq    731:   66 66 66 66 66 66 2e    data16 data16 data16 data16 data16 nopw %cs:0x0(%rax,%rax,1) 738:   0f 1f 84 00 00 00 00  73f:   00 

x86的符点运算

从30多年前的80486开始,x86芯片就自带FPU,不再需要8087或者80387这样的专用FPU。1998年,AMD在k6-2处理器中使用的3D Now!指令集开创了SIMD与浮点数的结合。1999年,Intel也随之推出了支持单精度浮点的SSE指令。后来一直发展到SSE 4.2.
以我们的双精度计算的例子为例,这使用到了2000年发布的Pentium 4才引入的SSE2指令集。movsd,addsd,divsd等这些指令都是SSE2指令。

00000640 <_Z4dadddd>: 640:   8d 64 24 f4             lea    -0xc(%esp),%esp 644:   f2 0f 10 44 24 18       movsd  0x18(%esp),%xmm0 64a:   f2 0f 58 44 24 10       addsd  0x10(%esp),%xmm0 650:   f2 0f 11 04 24          movsd  %xmm0,(%esp) 655:   dd 04 24                fldl   (%esp) 658:   8d 64 24 0c             lea    0xc(%esp),%esp 65c:   c3                      ret     65d:   8d 76 00                lea    0x0(%esi),%esi00000660 <_Z4dsubdd>: 660:   8d 64 24 f4             lea    -0xc(%esp),%esp 664:   f2 0f 10 44 24 10       movsd  0x10(%esp),%xmm0 66a:   f2 0f 5c 44 24 18       subsd  0x18(%esp),%xmm0 670:   f2 0f 11 04 24          movsd  %xmm0,(%esp) 675:   dd 04 24                fldl   (%esp) 678:   8d 64 24 0c             lea    0xc(%esp),%esp 67c:   c3                      ret     67d:   8d 76 00                lea    0x0(%esi),%esi00000680 <_Z4dmuldd>: 680:   8d 64 24 f4             lea    -0xc(%esp),%esp 684:   f2 0f 10 44 24 18       movsd  0x18(%esp),%xmm0 68a:   f2 0f 59 44 24 10       mulsd  0x10(%esp),%xmm0 690:   f2 0f 11 04 24          movsd  %xmm0,(%esp) 695:   dd 04 24                fldl   (%esp) 698:   8d 64 24 0c             lea    0xc(%esp),%esp 69c:   c3                      ret     69d:   8d 76 00                lea    0x0(%esi),%esi000006a0 <_Z4ddivdd>: 6a0:   8d 64 24 f4             lea    -0xc(%esp),%esp 6a4:   f2 0f 10 44 24 10       movsd  0x10(%esp),%xmm0 6aa:   f2 0f 5e 44 24 18       divsd  0x18(%esp),%xmm0 6b0:   f2 0f 11 04 24          movsd  %xmm0,(%esp) 6b5:   dd 04 24                fldl   (%esp) 6b8:   8d 64 24 0c             lea    0xc(%esp),%esp 6bc:   c3                      ret     6bd:   8d 76 00                lea    0x0(%esi),%esi

x86_64的浮点运算

与arm64有异曲同工之妙,不再需要进出xmm的时候做movsd了,retq指令可以直接从xmm寄存器中返回数据。

0000000000000740 <_Z4dadddd>: 740:   f2 0f 58 c1             addsd  %xmm1,%xmm0 744:   c3                      retq    745:   66 66 2e 0f 1f 84 00    data16 nopw %cs:0x0(%rax,%rax,1) 74c:   00 00 00 00 0000000000000750 <_Z4dsubdd>: 750:   f2 0f 5c c1             subsd  %xmm1,%xmm0 754:   c3                      retq    755:   66 66 2e 0f 1f 84 00    data16 nopw %cs:0x0(%rax,%rax,1) 75c:   00 00 00 00 0000000000000760 <_Z4dmuldd>: 760:   f2 0f 59 c1             mulsd  %xmm1,%xmm0 764:   c3                      retq    765:   66 66 2e 0f 1f 84 00    data16 nopw %cs:0x0(%rax,%rax,1) 76c:   00 00 00 00 0000000000000770 <_Z4ddivdd>: 770:   f2 0f 5e c1             divsd  %xmm1,%xmm0 774:   c3                      retq    775:   66 66 2e 0f 1f 84 00    data16 nopw %cs:0x0(%rax,%rax,1) 77c:   00 00 00 00 

MIPS指令集下的计算指令

最后我们看下可能多数同学们都不熟悉的MIPS指令集吧,其实还是很清爽的:

000006b0 <_Z3addll>: 6b0:   03e00008    jr  ra 6b4:   00851021    addu    v0,a0,a1000006b8 <_Z3subll>: 6b8:   03e00008    jr  ra 6bc:   00851023    subu    v0,a0,a1000006c0 <_Z3mulll>: 6c0:   03e00008    jr  ra 6c4:   70851002    mul v0,a0,a1000006c8 <_Z3divll>: 6c8:   0085001a    div zero,a0,a1 6cc:   00a001f4    teq a1,zero,0x7 6d0:   03e00008    jr  ra 6d4:   00001012    mflo    v0000006d8 <_Z3modll>: 6d8:   0085001a    div zero,a0,a1 6dc:   00a001f4    teq a1,zero,0x7 6e0:   03e00008    jr  ra 6e4:   00001010    mfhi    v0000006e8 <_Z4dadddd>: 6e8:   03e00008    jr  ra 6ec:   462e6000    add.d   $f0,$f12,$f14000006f0 <_Z4dsubdd>: 6f0:   03e00008    jr  ra 6f4:   462e6001    sub.d   $f0,$f12,$f14000006f8 <_Z4dmuldd>: 6f8:   03e00008    jr  ra 6fc:   462e6002    mul.d   $f0,$f12,$f1400000700 <_Z4ddivdd>: 700:   03e00008    jr  ra 704:   462e6003    div.d   $f0,$f12,$f14

mips64位与上面也很相似,整型指令基本是在指令前多个d。浮点除了寄存器变长了,也没什么大的变化。

0000000000000b80 <_Z3addll>: b80:   03e00009    jr  ra b84:   0085102d    daddu   v0,a0,a10000000000000b88 <_Z3subll>: b88:   03e00009    jr  ra b8c:   0085102f    dsubu   v0,a0,a10000000000000b90 <_Z3mulll>: b90:   03e00009    jr  ra b94:   0085109c    dmul    v0,a0,a10000000000000b98 <_Z3divll>: b98:   0085109e    ddiv    v0,a0,a1 b9c:   00a001f4    teq a1,zero,0x7 ba0:   d81f0000    jrc ra ba4:   00000000    nop0000000000000ba8 <_Z3modll>: ba8:   008510de    dmod    v0,a0,a1 bac:   00a001f4    teq a1,zero,0x7 bb0:   d81f0000    jrc ra bb4:   00000000    nop0000000000000bb8 <_Z4dadddd>: bb8:   03e00009    jr  ra bbc:   462d6000    add.d   $f0,$f12,$f130000000000000bc0 <_Z4dsubdd>: bc0:   03e00009    jr  ra bc4:   462d6001    sub.d   $f0,$f12,$f130000000000000bc8 <_Z4dmuldd>: bc8:   03e00009    jr  ra bcc:   462d6002    mul.d   $f0,$f12,$f130000000000000bd0 <_Z4ddivdd>: bd0:   03e00009    jr  ra bd4:   462d6003    div.d   $f0,$f12,$f13

编译出的OAT的浮点运算

节省篇幅,我们先看个dadd的. Dalvik指令很简单,就是一条add-double.

  3: double com.yunos.xulun.testcppjni2.TestART.dadd(double, double) (dex_method_idx=16780)    DEX CODE:      0x0000: ab00 0204                 | add-double v0, v2, v4      0x0002: 1000                      | return-wide v0

跟C++编译出来的一样,OAT生成的arm64指令也是用fadd来实现的,同样没有vmov什么事儿。

    CODE: (code_offset=0x0050280c size_offset=0x00502808 size=76)...      0x0050280c: d1400bf0  sub x16, sp, #0x2000 (8192)      0x00502810: b940021f  ldr wzr, [x16]      suspend point dex PC: 0x0000      0x00502814: f81e0fe0  str x0, [sp, #-32]!      0x00502818: f9000ffe  str lr, [sp, #24]      0x0050281c: fd0017e0  str d0, [sp, #40]      0x00502820: fd001be1  str d1, [sp, #48]      0x00502824: 79400250  ldrh w16, [tr] (state_and_flags)      0x00502828: 35000130  cbnz w16, #+0x24 (addr 0x50284c)

存到栈里的两个加数,用ldr可以直接装载到NEON寄存器里。然后直接调用fadd去计算。

      0x0050282c: fd4017e0  ldr d0, [sp, #40]      0x00502830: fd401be1  ldr d1, [sp, #48]      0x00502834: 1e612802  fadd d2, d0, d1      0x00502838: fc00c3e2  stur d2, [sp, #12]

双精度浮点数的返回值并不在通用寄存器x0中,而是直接在NEON寄存器d0中。

      0x0050283c: fc40c3e0  ldur d0, [sp, #12]      0x00502840: f9400ffe  ldr lr, [sp, #24]      0x00502844: 910083ff  add sp, sp, #0x20 (32)      0x00502848: d65f03c0  ret      0x0050284c: f9421e5e  ldr lr, [tr, #1080] (pTestSuspend)      0x00502850: d63f03c0  blr lr      suspend point dex PC: 0x0000      0x00502854: 17fffff6  b #-0x28 (addr 0x50282c)

然后我们再看除法,Dalvik是div-double。

  4: double com.yunos.xulun.testcppjni2.TestART.ddiv(double, double) (dex_method_idx=16781)    DEX CODE:      0x0000: ae00 0204                 | div-double v0, v2, v4      0x0002: 1000                      | return-wide v0

翻译出来的效果也真不错,真接使用NEON寄存器的fdiv。

    CODE: (code_offset=0x0050287c size_offset=0x00502878 size=76)...      0x0050287c: d1400bf0  sub x16, sp, #0x2000 (8192)      0x00502880: b940021f  ldr wzr, [x16]      suspend point dex PC: 0x0000      0x00502884: f81e0fe0  str x0, [sp, #-32]!      0x00502888: f9000ffe  str lr, [sp, #24]      0x0050288c: fd0017e0  str d0, [sp, #40]      0x00502890: fd001be1  str d1, [sp, #48]      0x00502894: 79400250  ldrh w16, [tr] (state_and_flags)      0x00502898: 35000130  cbnz w16, #+0x24 (addr 0x5028bc)      0x0050289c: fd4017e0  ldr d0, [sp, #40]      0x005028a0: fd401be1  ldr d1, [sp, #48]

除了fadd换成了fdiv,其余跟加法没有区别。

      0x005028a4: 1e611802  fdiv d2, d0, d1      0x005028a8: fc00c3e2  stur d2, [sp, #12]      0x005028ac: fc40c3e0  ldur d0, [sp, #12]      0x005028b0: f9400ffe  ldr lr, [sp, #24]      0x005028b4: 910083ff  add sp, sp, #0x20 (32)      0x005028b8: d65f03c0  ret      0x005028bc: f9421e5e  ldr lr, [tr, #1080] (pTestSuspend)      0x005028c0: d63f03c0  blr lr      suspend point dex PC: 0x0000      0x005028c4: 17fffff6  b #-0x28 (addr 0x50289c)
1 0
原创粉丝点击