ART世界探险(6) - 流程控制指令-阿里云开发者社区

开发者社区> 开发与运维> 正文

ART世界探险(6) - 流程控制指令

简介: 在jvm,arm, arm64, mips, x86, x86_64上都是如何实现分支和循环结构的

ART世界探险(6) - 流程控制指令

分支结构

Java分支结构

我们先来个最简单的,比较大小吧。

    public static long bigger(long a, long b){
        if(a>=b){
            return a;
        }else{
            return b;
        }
    }

    public static int less(int a,int b){
        if(a<=b){
            return a;
        }else{
            return b;
        }
    }

看看Java字节码是个什么样子:

  public static long bigger(long, long);
    Code:
       0: lload_0
       1: lload_2
       2: lcmp
       3: iflt          8
       6: lload_0
       7: lreturn
       8: lload_2
       9: lreturn

  public static int less(int, int);
    Code:
       0: iload_0
       1: iload_1
       2: if_icmpgt     7
       5: iload_0
       6: ireturn
       7: iload_1
       8: ireturn

lcmp是两个长整型做比较,结果影响标志位。然后iflt判断是否是小于(lt=less than),如果是则跳到8 ,不是则继续往下走。

普通整型的判断和比较在同一条指令里,if_icmpgt是判大于(gt=greater than),如果大于则跳转到7。

ARM的分支结构

对于这种根据不同情况选择不同值的语句,ARM在设计时专门有一条强大的csel指令,专门做这个事情。
先用cmp指令比较两个值,然后设置条件,ge是大于或等于,le是小于或等于。如果条件满足,则x0就是x0,否则x0取x1的值。

000000000000079c <_Z6biggerll>:
 79c:    eb01001f     cmp    x0, x1
 7a0:    9a81a000     csel    x0, x0, x1, ge
 7a4:    d65f03c0     ret

00000000000007a8 <_Z4lessii>:
 7a8:    6b01001f     cmp    w0, w1
 7ac:    1a81d000     csel    w0, w0, w1, le
 7b0:    d65f03c0     ret

Thumb2指令的分支结构

csel这样的指令需要32位的长度才放得下. Thumb2本着能省则省的原则,改用16位的条件mov指令来实现,可以节省2个字节指令空间。

0000101c <_Z6biggerll>:
    101c:    4288          cmp    r0, r1
    101e:    bfb8          it    lt
    1020:    4608          movlt    r0, r1
    1022:    4770          bx    lr

00001024 <_Z4lessii>:
    1024:    4288          cmp    r0, r1
    1026:    bfa8          it    ge
    1028:    4608          movge    r0, r1
    102a:    4770          bx    lr

Thumb指令的分支结构

Thumb指令没有带条件的mov操作,更不可能有csel这样复杂的指令了。那也没问题,返璞归真,我们直接跳转就是了呗〜
bge.n,是说大于或等于,也就是不小于的时候直接跳到12aa,就是bx lr返回这条指令上去。

adds r0, r1, #0其实也可以翻译成movs r0,r1。前面我们讲过,movs r0,r1其实是adds r0, r1, #0的别名。本质上是一回事。

000012a4 <_Z6biggerll>:
    12a4:    4288          cmp    r0, r1
    12a6:    da00          bge.n    12aa <_Z6biggerll+0x6>
    12a8:    1c08          adds    r0, r1, #0
    12aa:    4770          bx    lr

000012ac <_Z4lessii>:
    12ac:    4288          cmp    r0, r1
    12ae:    dd00          ble.n    12b2 <_Z4lessii+0x6>
    12b0:    1c08          adds    r0, r1, #0
    12b2:    4770          bx    lr

x86_64的分支结构

x86_64中也是有带有条件的赋值指令的。

0000000000000740 <_Z6biggerll>:
 740:    48 39 f7                 cmp    %rsi,%rdi
 743:    48 89 f0                 mov    %rsi,%rax
 746:    48 0f 4d c7              cmovge %rdi,%rax
 74a:    c3                       retq   
 74b:    0f 1f 44 00 00           nopl   0x0(%rax,%rax,1)

0000000000000750 <_Z4lessii>:
 750:    39 f7                    cmp    %esi,%edi
 752:    89 f0                    mov    %esi,%eax
 754:    0f 4e c7                 cmovle %edi,%eax
 757:    c3                       retq   
 758:    0f 1f 84 00 00 00 00     nopl   0x0(%rax,%rax,1)
 75f:    00 

x86的分支结构

cmov在x86上也是有的。

00000630 <_Z6biggerll>:
 630:    8b 44 24 04              mov    0x4(%esp),%eax
 634:    8b 54 24 08              mov    0x8(%esp),%edx
 638:    39 d0                    cmp    %edx,%eax
 63a:    0f 4c c2                 cmovl  %edx,%eax
 63d:    c3                       ret    
 63e:    66 90                    xchg   %ax,%ax

00000640 <_Z4lessii>:
 640:    8b 44 24 04              mov    0x4(%esp),%eax
 644:    8b 54 24 08              mov    0x8(%esp),%edx
 648:    39 d0                    cmp    %edx,%eax
 64a:    0f 4f c2                 cmovg  %edx,%eax
 64d:    c3                       ret    
 64e:    66 90                    xchg   %ax,%ax

mips64的分支结构

感觉上罗嗦了一点,但是本质上跟csel是一样的。

0000000000000c00 <_Z6biggerll>:
 c00:    0085182a     slt    v1,a0,a1
 c04:    00832035     seleqz    a0,a0,v1
 c08:    00a31037     selnez    v0,a1,v1
 c0c:    03e00009     jr    ra
 c10:    00821025     or    v0,a0,v0
 c14:    00000000     nop

0000000000000c18 <_Z4lessii>:
 c18:    00a4102a     slt    v0,a1,a0
 c1c:    00a22837     selnez    a1,a1,v0
 c20:    00822035     seleqz    a0,a0,v0
 c24:    03e00009     jr    ra
 c28:    00a41025     or    v0,a1,a0
 c2c:    00000000     nop

mips的分支结构:

比起上面64位的,是不是感觉更加清爽呢?

00000710 <_Z6biggerll>:
 710:    0085102a     slt    v0,a0,a1
 714:    0082280a     movz    a1,a0,v0
 718:    03e00008     jr    ra
 71c:    00a01021     move    v0,a1

00000720 <_Z4lessii>:
 720:    00a4102a     slt    v0,a1,a0
 724:    0082280a     movz    a1,a0,v0
 728:    03e00008     jr    ra
 72c:    00a01021     move    v0,a1

分支结构生成的OAT

我们先看生成的Dalvik代码:

  2: long com.yunos.xulun.testcppjni2.TestART.bigger(long, long) (dex_method_idx=16779)
    DEX CODE:
      0x0000: 3100 0204                    | cmp-long v0, v2, v4
      0x0002: 3a00 0300                    | if-ltz v0, +3
      0x0004: 1002                         | return-wide v2
      0x0005: 0442                         | move-wide v2, v4
      0x0006: 28fe                         | goto -2

看起来稍微有点绕,不过总体上还算是清晰。

    CODE: (code_offset=0x0050277c size_offset=0x00502778 size=116)...
      0x0050277c: d1400bf0    sub x16, sp, #0x2000 (8192)
      0x00502780: b940021f    ldr wzr, [x16]
      suspend point dex PC: 0x0000
      0x00502784: f81e0fe0    str x0, [sp, #-32]!
      0x00502788: f9000ffe    str lr, [sp, #24]
      0x0050278c: f90017e1    str x1, [sp, #40]
      0x00502790: f9001be2    str x2, [sp, #48]
      0x00502794: 79400250    ldrh w16, [tr](state_and_flags)
      0x00502798: 35000270    cbnz w16, #+0x4c (addr 0x5027e4)

两个参数在上面分别存到了sp+40和sp+48中,现在再读回到x0和x1中。
用cmp指令来比较。
下面用到条件设置的cset指令,这是一条将flag转成1或0的值的指令。这条指令是cinc指令的一个别名。
如果是NE,就将w2设置成1,否则就设成0.

      0x0050279c: f94017e0    ldr x0, [sp, #40]
      0x005027a0: f9401be1    ldr x1, [sp, #48]
      0x005027a4: eb01001f    cmp x0, x1
      0x005027a8: 1a9f07e2    cset w2, ne

下面再来一条cneg,条件取负,如果lt的话,就将-w2赋给w2,否则就将w2赋给w2.
这个结果先暂存到栈里,再读到w0,去做下一次比较。
如果刚才这个条件取负的值大于0,则w1赋给w0.
根所cset的结果,来决定cbnz是不是要跳转,就是要不要将第二个参数sp+48的值换到sp+40的位置。
最后返回sp+40处变量的值。

      0x005027ac: 5a82a442    cneg w2, w2, lt
      0x005027b0: b9000fe2    str w2, [sp, #12]
      0x005027b4: b9400fe0    ldr w0, [sp, #12]
      0x005027b8: 7100001f    cmp w0, #0x0 (0)
      0x005027bc: 1a9fa7e1    cset w1, lt
      0x005027c0: 2a0103e0    mov w0, w1
      0x005027c4: 350000a0    cbnz w0, #+0x14 (addr 0x5027d8)
      0x005027c8: f94017e0    ldr x0, [sp, #40]
      0x005027cc: f9400ffe    ldr lr, [sp, #24]
      0x005027d0: 910083ff    add sp, sp, #0x20 (32)
      0x005027d4: d65f03c0    ret
      0x005027d8: fd401bff    ldr d31, [sp, #48]
      0x005027dc: fd0017ff    str d31, [sp, #40]
      0x005027e0: 17fffffa    b #-0x18 (addr 0x5027c8)
      0x005027e4: f9421e5e    ldr lr, [tr, #1080](pTestSuspend)
      0x005027e8: d63f03c0    blr lr
      suspend point dex PC: 0x0000
      0x005027ec: 17ffffec    b #-0x50 (addr 0x50279c)

循环结构

我们写一个while循环,一个do while循环和一个for循环来测试一下,其实本质上差不多是一回事情:

    public static long testWhile(int value){
        int i=0;
        long sum = 0;
        while(i<=value){
            sum += i;
            i++;
        }
        return sum;
    }

    public static long testDoWhile(int value){
        long sum = 0;
        int i=0;
        do{
            sum += i;
            i++;
        }while(i<value);
        return sum;
    }

    public static long testFor(int value){
        long sum = 0;
        for(int i=0;i<=value;i++){
            sum += i;
        }
        return sum;
    }

Java字节码的循环结构

我们先来解释其中第一个吧。这其中唯一遇到的新指令是无条件跳转的goto指令。
其中第6标号的if_icmpgt我们在分支中学过了。

  public static long testWhile(int);
    Code:
       0: iconst_0
       1: istore_1
       2: lconst_0
       3: lstore_2
       4: iload_1
       5: iload_0
       6: if_icmpgt     20
       9: lload_2
      10: iload_1
      11: i2l
      12: ladd
      13: lstore_2
      14: iinc          1, 1
      17: goto          4
      20: lload_2
      21: lreturn

后面两个与上面的非常类似。只是指令顺序上稍有不同而己。

  public static long testDoWhile(int);
    Code:
       0: lconst_0
       1: lstore_1
       2: iconst_0
       3: istore_3
       4: lload_1
       5: iload_3
       6: i2l
       7: ladd
       8: lstore_1
       9: iinc          3, 1
      12: iload_3
      13: iload_0
      14: if_icmplt     4
      17: lload_1
      18: lreturn

  public static long testFor(int);
    Code:
       0: lconst_0
       1: lstore_1
       2: iconst_0
       3: istore_3
       4: iload_3
       5: iload_0
       6: if_icmpgt     20
       9: lload_1
      10: iload_3
      11: i2l
      12: ladd
      13: lstore_1
      14: iinc          3, 1
      17: goto          4
      20: lload_1
      21: lreturn

Thumb的循环结构

C++代码与Java代码几乎是一字未改:

long testWhile(int value){
    int i=0;
    long sum = 0;
    while(i<=value){
        sum += i;
        i++;
    }
    return sum;
}

long testDoWhile(int value){
    long sum = 0;
    int i=0;
    do{
        sum += i;
        i++;
    }while(i<value);
    return sum;
}

long testFor(int value){
    long sum = 0;
    for(int i=0;i<=value;i++){
        sum += i;
    }
    return sum;
}

我们下面来看看16位的Thumb指令是如何实现循环的。
value参数传进来在r0。将r2,r3清0, r2与参数传进来的r0比较,如果0已经比r0大了,循环结束,将结果r3赋给r0,返回。
如果0比r0小,则继续循环,r3结果等于r3值加上r2值,r2值随后加1。然后无条件回12b8那条比较语句,再判断r2是不是大于r0,如此循环。

000012b4 <_Z9testWhilei>:
    12b4:    2300          movs    r3, #0
    12b6:    1c1a          adds    r2, r3, #0
    12b8:    4282          cmp    r2, r0
    12ba:    dc02          bgt.n    12c2 <_Z9testWhilei+0xe>
    12bc:    189b          adds    r3, r3, r2
    12be:    3201          adds    r2, #1
    12c0:    e7fa          b.n    12b8 <_Z9testWhilei+0x4>
    12c2:    1c18          adds    r0, r3, #0
    12c4:    4770          bx    lr

后面两个也是语句位置变了变,本质是一样的。

000012c6 <_Z11testDoWhilei>:
    12c6:    2300          movs    r3, #0
    12c8:    1c1a          adds    r2, r3, #0
    12ca:    18d2          adds    r2, r2, r3
    12cc:    3301          adds    r3, #1
    12ce:    4283          cmp    r3, r0
    12d0:    dbfb          blt.n    12ca <_Z11testDoWhilei+0x4>
    12d2:    1c10          adds    r0, r2, #0
    12d4:    4770          bx    lr

000012d6 <_Z7testFori>:
    12d6:    2300          movs    r3, #0
    12d8:    1c1a          adds    r2, r3, #0
    12da:    4283          cmp    r3, r0
    12dc:    dc02          bgt.n    12e4 <_Z7testFori+0xe>
    12de:    18d2          adds    r2, r2, r3
    12e0:    3301          adds    r3, #1
    12e2:    e7fa          b.n    12da <_Z7testFori+0x4>
    12e4:    1c10          adds    r0, r2, #0
    12e6:    4770          bx    lr

Thumb2的循环结构

v7a的循环结构

我们只看一个吧,除了地址不同,与前面的Thumb指令完全相同,所以略过不讲了。

0000102c <_Z9testWhilei>:
    102c:    2300          movs    r3, #0
    102e:    461a          mov    r2, r3
    1030:    4282          cmp    r2, r0
    1032:    dc02          bgt.n    103a <_Z9testWhilei+0xe>
    1034:    4413          add    r3, r2
    1036:    3201          adds    r2, #1
    1038:    e7fa          b.n    1030 <_Z9testWhilei+0x4>
    103a:    4618          mov    r0, r3
    103c:    4770          bx    lr

mips的循环结构

我们再看下mips的三个循环结构,也跟上面所讲的差不多。

00000730 <_Z9testWhilei>:
 730:    04800009     bltz    a0,758 <_Z9testWhilei+0x28>
 734:    24840001     addiu    a0,a0,1
 738:    00001021     move    v0,zero
 73c:    00001821     move    v1,zero
 740:    00431021     addu    v0,v0,v1
 744:    24630001     addiu    v1,v1,1
 748:    1464fffe     bne    v1,a0,744 <_Z9testWhilei+0x14>
 74c:    00431021     addu    v0,v0,v1
 750:    03e00008     jr    ra
 754:    00431023     subu    v0,v0,v1
 758:    03e00008     jr    ra
 75c:    00001021     move    v0,zero

00000760 <_Z11testDoWhilei>:
 760:    00001821     move    v1,zero
 764:    00001021     move    v0,zero
 768:    00431021     addu    v0,v0,v1
 76c:    24630001     addiu    v1,v1,1
 770:    0064282a     slt    a1,v1,a0
 774:    14a0fffd     bnez    a1,76c <_Z11testDoWhilei+0xc>
 778:    00431021     addu    v0,v0,v1
 77c:    03e00008     jr    ra
 780:    00431023     subu    v0,v0,v1

00000784 <_Z7testFori>:
 784:    04800009     bltz    a0,7ac <_Z7testFori+0x28>
 788:    24840001     addiu    a0,a0,1
 78c:    00001821     move    v1,zero
 790:    00001021     move    v0,zero
 794:    00431021     addu    v0,v0,v1
 798:    24630001     addiu    v1,v1,1
 79c:    1464fffe     bne    v1,a0,798 <_Z7testFori+0x14>
 7a0:    00431021     addu    v0,v0,v1
 7a4:    03e00008     jr    ra
 7a8:    00431023     subu    v0,v0,v1
 7ac:    03e00008     jr    ra
 7b0:    00001021     move    v0,zero

mips64的循环结构

64位的除了加减法换成支持64位的了,其它没有变化。

0000000000000c30 <_Z9testWhilei>:
 c30:    0480000a     bltz    a0,c5c <_Z9testWhilei+0x2c>
 c34:    64840001     daddiu    a0,a0,1
 c38:    0000182d     move    v1,zero
 c3c:    0000102d     move    v0,zero
 c40:    0043102d     daddu    v0,v0,v1
 c44:    00000000     nop
 c48:    64630001     daddiu    v1,v1,1
 c4c:    1464fffe     bne    v1,a0,c48 <_Z9testWhilei+0x18>
 c50:    0043102d     daddu    v0,v0,v1
 c54:    03e00009     jr    ra
 c58:    0043102f     dsubu    v0,v0,v1
 c5c:    03e00009     jr    ra
 c60:    0000102d     move    v0,zero
 c64:    00000000     nop

0000000000000c68 <_Z11testDoWhilei>:
 c68:    0000182d     move    v1,zero
 c6c:    0000102d     move    v0,zero
 c70:    0043102d     daddu    v0,v0,v1
 c74:    64630001     daddiu    v1,v1,1
 c78:    00032800     sll    a1,v1,0x0
 c7c:    5ca4fffc     bltc    a1,a0,c70 <_Z11testDoWhilei+0x8>
 c80:    00000000     nop
 c84:    d81f0000     jrc    ra

0000000000000c88 <_Z7testFori>:
 c88:    0480000a     bltz    a0,cb4 <_Z7testFori+0x2c>
 c8c:    64840001     daddiu    a0,a0,1
 c90:    0000182d     move    v1,zero
 c94:    0000102d     move    v0,zero
 c98:    0043102d     daddu    v0,v0,v1
 c9c:    00000000     nop
 ca0:    64630001     daddiu    v1,v1,1
 ca4:    1464fffe     bne    v1,a0,ca0 <_Z7testFori+0x18>
 ca8:    0043102d     daddu    v0,v0,v1
 cac:    03e00009     jr    ra
 cb0:    0043102f     dsubu    v0,v0,v1
 cb4:    03e00009     jr    ra
 cb8:    0000102d     move    v0,zero
 cbc:    00000000     nop

arm v8a的循环结构

下面我们看第一个while循环在v8a上的实现:
竟然动用了强大的NEON指令!

00000000000007c0 <_Z9testWhilei>:
 7c0:    2a0003e3     mov    w3, w0
 7c4:    37f80643     tbnz    w3, #31, 88c <_Z9testWhilei+0xcc>
 7c8:    51000c60     sub    w0, w3, #0x3
 7cc:    7100147f     cmp    w3, #0x5
 7d0:    53027c00     lsr    w0, w0, #2
 7d4:    11000464     add    w4, w3, #0x1
 7d8:    11000400     add    w0, w0, #0x1
 7dc:    531e7402     lsl    w2, w0, #2
 7e0:    5400050d     b.le    880 <_Z9testWhilei+0xc0>
 7e4:    100005e5     adr    x5, 8a0 <_Z9testWhilei+0xe0>
 7e8:    4f000484     movi    v4.4s, #0x4
 7ec:    4f000400     movi    v0.4s, #0x0
 7f0:    52800001     mov    w1, #0x0                       // #0
 7f4:    3dc000a1     ldr    q1, [x5]
 7f8:    0f20a422     sxtl    v2.2d, v1.2s
 7fc:    11000421     add    w1, w1, #0x1
 800:    4f20a423     sxtl2    v3.2d, v1.4s
 804:    6b01001f     cmp    w0, w1
 808:    4ea48421     add    v1.4s, v1.4s, v4.4s
 80c:    4ee08440     add    v0.2d, v2.2d, v0.2d
 810:    4ee08460     add    v0.2d, v3.2d, v0.2d
 814:    54ffff28     b.hi    7f8 <_Z9testWhilei+0x38>
 818:    5ef1b800     addp    d0, v0.2d
 81c:    6b02009f     cmp    w4, w2
 820:    4e083c00     mov    x0, v0.d[0]
 824:    540002c0     b.eq    87c <_Z9testWhilei+0xbc>
 828:    11000444     add    w4, w2, #0x1
 82c:    8b22c000     add    x0, x0, w2, sxtw
 830:    6b04007f     cmp    w3, w4
 834:    5400024b     b.lt    87c <_Z9testWhilei+0xbc>
 838:    11000841     add    w1, w2, #0x2
 83c:    8b24c000     add    x0, x0, w4, sxtw
 840:    6b01007f     cmp    w3, w1
 844:    540001cb     b.lt    87c <_Z9testWhilei+0xbc>
 848:    11000c44     add    w4, w2, #0x3
 84c:    8b21c000     add    x0, x0, w1, sxtw
 850:    6b04007f     cmp    w3, w4
 854:    5400014b     b.lt    87c <_Z9testWhilei+0xbc>
 858:    11001041     add    w1, w2, #0x4
 85c:    8b24c000     add    x0, x0, w4, sxtw
 860:    6b01007f     cmp    w3, w1
 864:    540000cb     b.lt    87c <_Z9testWhilei+0xbc>
 868:    11001442     add    w2, w2, #0x5
 86c:    8b21c000     add    x0, x0, w1, sxtw
 870:    6b02007f     cmp    w3, w2
 874:    8b22c001     add    x1, x0, w2, sxtw
 878:    9a80a020     csel    x0, x1, x0, ge
 87c:    d65f03c0     ret
 880:    d2800000     mov    x0, #0x0                       // #0
 884:    52800002     mov    w2, #0x0                       // #0
 888:    17ffffe8     b    828 <_Z9testWhilei+0x68>
 88c:    d2800000     mov    x0, #0x0                       // #0
 890:    d65f03c0     ret
 894:    d503201f     nop
 898:    d503201f     nop
 89c:    d503201f     nop
 8a0:    00000000     .inst    0x00000000 ; undefined
 8a4:    00000001     .inst    0x00000001 ; undefined
 8a8:    00000002     .inst    0x00000002 ; undefined
 8ac:    00000003     .inst    0x00000003 ; undefined

篇幅所限,另两个就不列举了。

x86_64的循环结构

x86不甘人后,也动用了MMX整数的SIMD指令来完成这个小循环。。。

0000000000000760 <_Z9testWhilei>:
 760:    85 ff                    test   %edi,%edi
 762:    0f 88 48 01 00 00        js     8b0 <_Z9testWhilei+0x150>
 768:    8d 47 fd                 lea    -0x3(%rdi),%eax
 76b:    8d 77 01                 lea    0x1(%rdi),%esi
 76e:    c1 e8 02                 shr    $0x2,%eax
 771:    83 c0 01                 add    $0x1,%eax
 774:    83 ff 0e                 cmp    $0xe,%edi
 777:    8d 14 85 00 00 00 00     lea    0x0(,%rax,4),%edx
 77e:    0f 8e 4c 01 00 00        jle    8d0 <_Z9testWhilei+0x170>
 784:    66 0f 6f 1d 34 05 00     movdqa 0x534(%rip),%xmm3        # cc0 <Java_xulun_testcppjni2_TestJniCpp_test+0x30>
 78b:    00 
 78c:    31 c9                    xor    %ecx,%ecx
 78e:    66 0f 6f 0d 1a 05 00     movdqa 0x51a(%rip),%xmm1        # cb0 <Java_xulun_testcppjni2_TestJniCpp_test+0x20>
 795:    00 
 796:    66 0f ef c0              pxor   %xmm0,%xmm0
 79a:    66 0f 38 25 d1           pmovsxdq %xmm1,%xmm2
 79f:    83 c1 01                 add    $0x1,%ecx
 7a2:    66 0f d4 c2              paddq  %xmm2,%xmm0
 7a6:    66 0f 6f d1              movdqa %xmm1,%xmm2
 7aa:    66 0f 73 da 08           psrldq $0x8,%xmm2
 7af:    39 c8                    cmp    %ecx,%eax
 7b1:    66 0f 38 25 d2           pmovsxdq %xmm2,%xmm2
 7b6:    66 0f fe cb              paddd  %xmm3,%xmm1
 7ba:    66 0f d4 c2              paddq  %xmm2,%xmm0
 7be:    77 da                    ja     79a <_Z9testWhilei+0x3a>
 7c0:    66 0f 6f c8              movdqa %xmm0,%xmm1
 7c4:    39 d6                    cmp    %edx,%esi
 7c6:    66 0f 73 d9 08           psrldq $0x8,%xmm1
 7cb:    66 0f d4 c1              paddq  %xmm1,%xmm0
 7cf:    66 48 0f 7e c0           movq   %xmm0,%rax
 7d4:    0f 84 ee 00 00 00        je     8c8 <_Z9testWhilei+0x168>
 7da:    48 63 ca                 movslq %edx,%rcx
 7dd:    48 01 c8                 add    %rcx,%rax
 7e0:    8d 4a 01                 lea    0x1(%rdx),%ecx
 7e3:    39 cf                    cmp    %ecx,%edi
 7e5:    0f 8c d5 00 00 00        jl     8c0 <_Z9testWhilei+0x160>
 7eb:    48 63 c9                 movslq %ecx,%rcx
 7ee:    48 01 c8                 add    %rcx,%rax
 7f1:    8d 4a 02                 lea    0x2(%rdx),%ecx
 7f4:    39 cf                    cmp    %ecx,%edi
 7f6:    0f 8c c4 00 00 00        jl     8c0 <_Z9testWhilei+0x160>
 7fc:    48 63 c9                 movslq %ecx,%rcx
 7ff:    48 01 c8                 add    %rcx,%rax
 802:    8d 4a 03                 lea    0x3(%rdx),%ecx
 805:    39 cf                    cmp    %ecx,%edi
 807:    0f 8c b3 00 00 00        jl     8c0 <_Z9testWhilei+0x160>
 80d:    48 63 c9                 movslq %ecx,%rcx
 810:    48 01 c8                 add    %rcx,%rax
 813:    8d 4a 04                 lea    0x4(%rdx),%ecx
 816:    39 cf                    cmp    %ecx,%edi
 818:    0f 8c a2 00 00 00        jl     8c0 <_Z9testWhilei+0x160>
 81e:    48 63 c9                 movslq %ecx,%rcx
 821:    48 01 c8                 add    %rcx,%rax
 824:    8d 4a 05                 lea    0x5(%rdx),%ecx
 827:    39 cf                    cmp    %ecx,%edi
 829:    0f 8c 91 00 00 00        jl     8c0 <_Z9testWhilei+0x160>
 82f:    48 63 c9                 movslq %ecx,%rcx
 832:    48 01 c8                 add    %rcx,%rax
 835:    8d 4a 06                 lea    0x6(%rdx),%ecx
 838:    39 cf                    cmp    %ecx,%edi
 83a:    0f 8c 80 00 00 00        jl     8c0 <_Z9testWhilei+0x160>
 840:    48 63 c9                 movslq %ecx,%rcx
 843:    48 01 c8                 add    %rcx,%rax
 846:    8d 4a 07                 lea    0x7(%rdx),%ecx
 849:    39 cf                    cmp    %ecx,%edi
 84b:    7c 73                    jl     8c0 <_Z9testWhilei+0x160>
 84d:    48 63 c9                 movslq %ecx,%rcx
 850:    48 01 c8                 add    %rcx,%rax
 853:    8d 4a 08                 lea    0x8(%rdx),%ecx
 856:    39 cf                    cmp    %ecx,%edi
 858:    7c 66                    jl     8c0 <_Z9testWhilei+0x160>
 85a:    48 63 c9                 movslq %ecx,%rcx
 85d:    48 01 c8                 add    %rcx,%rax
 860:    8d 4a 09                 lea    0x9(%rdx),%ecx
 863:    39 cf                    cmp    %ecx,%edi
 865:    7c 59                    jl     8c0 <_Z9testWhilei+0x160>
 867:    48 63 c9                 movslq %ecx,%rcx
 86a:    48 01 c8                 add    %rcx,%rax
 86d:    8d 4a 0a                 lea    0xa(%rdx),%ecx
 870:    39 cf                    cmp    %ecx,%edi
 872:    7c 4c                    jl     8c0 <_Z9testWhilei+0x160>
 874:    48 63 c9                 movslq %ecx,%rcx
 877:    48 01 c8                 add    %rcx,%rax
 87a:    8d 4a 0b                 lea    0xb(%rdx),%ecx
 87d:    39 cf                    cmp    %ecx,%edi
 87f:    7c 3f                    jl     8c0 <_Z9testWhilei+0x160>
 881:    48 63 c9                 movslq %ecx,%rcx
 884:    48 01 c8                 add    %rcx,%rax
 887:    8d 4a 0c                 lea    0xc(%rdx),%ecx
 88a:    39 cf                    cmp    %ecx,%edi
 88c:    7c 32                    jl     8c0 <_Z9testWhilei+0x160>
 88e:    48 63 c9                 movslq %ecx,%rcx
 891:    48 01 c8                 add    %rcx,%rax
 894:    8d 4a 0d                 lea    0xd(%rdx),%ecx
 897:    39 cf                    cmp    %ecx,%edi
 899:    7c 25                    jl     8c0 <_Z9testWhilei+0x160>
 89b:    48 63 c9                 movslq %ecx,%rcx
 89e:    83 c2 0e                 add    $0xe,%edx
 8a1:    48 01 c8                 add    %rcx,%rax
 8a4:    39 d7                    cmp    %edx,%edi
 8a6:    7c 38                    jl     8e0 <_Z9testWhilei+0x180>
 8a8:    48 63 d2                 movslq %edx,%rdx
 8ab:    48 01 d0                 add    %rdx,%rax
 8ae:    c3                       retq   
 8af:    90                       nop
 8b0:    31 c0                    xor    %eax,%eax
 8b2:    66 66 66 66 66 2e 0f     data16 data16 data16 data16 nopw %cs:0x0(%rax,%rax,1)
 8b9:    1f 84 00 00 00 00 00 
 8c0:    c3                       retq   
 8c1:    0f 1f 80 00 00 00 00     nopl   0x0(%rax)
 8c8:    c3                       retq   
 8c9:    0f 1f 80 00 00 00 00     nopl   0x0(%rax)
 8d0:    31 c0                    xor    %eax,%eax
 8d2:    31 d2                    xor    %edx,%edx
 8d4:    e9 01 ff ff ff           jmpq   7da <_Z9testWhilei+0x7a>
 8d9:    0f 1f 80 00 00 00 00     nopl   0x0(%rax)
 8e0:    c3                       retq   
 8e1:    66 66 66 66 66 66 2e     data16 data16 data16 data16 data16 nopw %cs:0x0(%rax,%rax,1)
 8e8:    0f 1f 84 00 00 00 00 
 8ef:    00 

x86的循环结构

32位的x86,仍然动用MMX等SIMD技术来处理这个小循环。

00000650 <_Z9testWhilei>:
 650:    57                       push   %edi
 651:    56                       push   %esi
 652:    53                       push   %ebx
 653:    e8 c8 ff ff ff           call   620 <__cxa_finalize@plt+0xc0>
 658:    81 c3 90 19 00 00        add    $0x1990,%ebx
 65e:    8b 4c 24 10              mov    0x10(%esp),%ecx
 662:    85 c9                    test   %ecx,%ecx
 664:    0f 88 f6 00 00 00        js     760 <_Z9testWhilei+0x110>
 66a:    8d 41 fd                 lea    -0x3(%ecx),%eax
 66d:    8d 71 01                 lea    0x1(%ecx),%esi
 670:    c1 e8 02                 shr    $0x2,%eax
 673:    83 c0 01                 add    $0x1,%eax
 676:    83 f9 0d                 cmp    $0xd,%ecx
 679:    8d 14 85 00 00 00 00     lea    0x0(,%eax,4),%edx
 680:    0f 8e ca 00 00 00        jle    750 <_Z9testWhilei+0x100>
 686:    66 0f 6f 93 c8 ea ff     movdqa -0x1538(%ebx),%xmm2
 68d:    ff 
 68e:    31 ff                    xor    %edi,%edi
 690:    66 0f 6f 83 b8 ea ff     movdqa -0x1548(%ebx),%xmm0
 697:    ff 
 698:    66 0f ef c9              pxor   %xmm1,%xmm1
 69c:    83 c7 01                 add    $0x1,%edi
 69f:    66 0f fe c8              paddd  %xmm0,%xmm1
 6a3:    39 f8                    cmp    %edi,%eax
 6a5:    66 0f fe c2              paddd  %xmm2,%xmm0
 6a9:    77 f1                    ja     69c <_Z9testWhilei+0x4c>
 6ab:    66 0f 6f c1              movdqa %xmm1,%xmm0
 6af:    39 d6                    cmp    %edx,%esi
 6b1:    66 0f 73 d8 08           psrldq $0x8,%xmm0
 6b6:    66 0f fe c8              paddd  %xmm0,%xmm1
 6ba:    66 0f 6f c1              movdqa %xmm1,%xmm0
 6be:    66 0f 73 d8 04           psrldq $0x4,%xmm0
 6c3:    66 0f fe c8              paddd  %xmm0,%xmm1
 6c7:    66 0f 7e c8              movd   %xmm1,%eax
 6cb:    74 79                    je     746 <_Z9testWhilei+0xf6>
 6cd:    8d 72 01                 lea    0x1(%edx),%esi
 6d0:    01 d0                    add    %edx,%eax
 6d2:    39 f1                    cmp    %esi,%ecx
 6d4:    7c 70                    jl     746 <_Z9testWhilei+0xf6>
 6d6:    01 f0                    add    %esi,%eax
 6d8:    8d 72 02                 lea    0x2(%edx),%esi
 6db:    39 f1                    cmp    %esi,%ecx
 6dd:    7c 67                    jl     746 <_Z9testWhilei+0xf6>
 6df:    01 f0                    add    %esi,%eax
 6e1:    8d 72 03                 lea    0x3(%edx),%esi
 6e4:    39 f1                    cmp    %esi,%ecx
 6e6:    7c 5e                    jl     746 <_Z9testWhilei+0xf6>
 6e8:    01 f0                    add    %esi,%eax
 6ea:    8d 72 04                 lea    0x4(%edx),%esi
 6ed:    39 f1                    cmp    %esi,%ecx
 6ef:    7c 55                    jl     746 <_Z9testWhilei+0xf6>
 6f1:    01 f0                    add    %esi,%eax
 6f3:    8d 72 05                 lea    0x5(%edx),%esi
 6f6:    39 f1                    cmp    %esi,%ecx
 6f8:    7c 4c                    jl     746 <_Z9testWhilei+0xf6>
 6fa:    01 f0                    add    %esi,%eax
 6fc:    8d 72 06                 lea    0x6(%edx),%esi
 6ff:    39 f1                    cmp    %esi,%ecx
 701:    7c 43                    jl     746 <_Z9testWhilei+0xf6>
 703:    01 f0                    add    %esi,%eax
 705:    8d 72 07                 lea    0x7(%edx),%esi
 708:    39 f1                    cmp    %esi,%ecx
 70a:    7c 3a                    jl     746 <_Z9testWhilei+0xf6>
 70c:    01 f0                    add    %esi,%eax
 70e:    8d 72 08                 lea    0x8(%edx),%esi
 711:    39 f1                    cmp    %esi,%ecx
 713:    7c 31                    jl     746 <_Z9testWhilei+0xf6>
 715:    01 f0                    add    %esi,%eax
 717:    8d 72 09                 lea    0x9(%edx),%esi
 71a:    39 f1                    cmp    %esi,%ecx
 71c:    7c 28                    jl     746 <_Z9testWhilei+0xf6>
 71e:    01 f0                    add    %esi,%eax
 720:    8d 72 0a                 lea    0xa(%edx),%esi
 723:    39 f1                    cmp    %esi,%ecx
 725:    7c 1f                    jl     746 <_Z9testWhilei+0xf6>
 727:    01 f0                    add    %esi,%eax
 729:    8d 72 0b                 lea    0xb(%edx),%esi
 72c:    39 f1                    cmp    %esi,%ecx
 72e:    7c 16                    jl     746 <_Z9testWhilei+0xf6>
 730:    01 f0                    add    %esi,%eax
 732:    8d 72 0c                 lea    0xc(%edx),%esi
 735:    39 f1                    cmp    %esi,%ecx
 737:    7c 0d                    jl     746 <_Z9testWhilei+0xf6>
 739:    83 c2 0d                 add    $0xd,%edx
 73c:    01 f0                    add    %esi,%eax
 73e:    39 d1                    cmp    %edx,%ecx
 740:    8d 34 10                 lea    (%eax,%edx,1),%esi
 743:    0f 4d c6                 cmovge %esi,%eax
 746:    5b                       pop    %ebx
 747:    5e                       pop    %esi
 748:    5f                       pop    %edi
 749:    c3                       ret    
 74a:    8d b6 00 00 00 00        lea    0x0(%esi),%esi
 750:    31 c0                    xor    %eax,%eax
 752:    31 d2                    xor    %edx,%edx
 754:    e9 74 ff ff ff           jmp    6cd <_Z9testWhilei+0x7d>
 759:    8d b4 26 00 00 00 00     lea    0x0(%esi,%eiz,1),%esi
 760:    31 c0                    xor    %eax,%eax
 762:    eb e2                    jmp    746 <_Z9testWhilei+0xf6>
 764:    8d b6 00 00 00 00        lea    0x0(%esi),%esi
 76a:    8d bf 00 00 00 00        lea    0x0(%edi),%edi

循环结构的OAT生成代码

我们还是以第一个为例吧,Dalvik代码生成如下,看起来还是蛮清晰的。

  13: long com.yunos.xulun.testcppjni2.TestART.testWhile(int) (dex_method_idx=16790)
    DEX CODE:
      0x0000: 1200                         | const/4 v0, #+0
      0x0001: 1602 0000                    | const-wide/16 v2, #+0
      0x0003: 3660 0700                    | if-gt v0, v6, +7
      0x0005: 8104                         | int-to-long v4, v0
      0x0006: bb42                         | add-long/2addr v2, v4
      0x0007: d800 0001                    | add-int/lit8 v0, v0, #+1
      0x0009: 28fa                         | goto -6
      0x000a: 1002                         | return-wide v2

翻译成arm64之后,比前面C++译出来的高大上的NEON指令的可是要朴素多了。

    CODE: (code_offset=0x00502d5c size_offset=0x00502d58 size=160)...
      0x00502d5c: d1400bf0    sub x16, sp, #0x2000 (8192)
      0x00502d60: b940021f    ldr wzr, [x16]
      suspend point dex PC: 0x0000
      0x00502d64: f81d0fe0    str x0, [sp, #-48]!
      0x00502d68: f90017fe    str lr, [sp, #40]
      0x00502d6c: b9003be1    str w1, [sp, #56]
      0x00502d70: 79400250    ldrh w16, [tr](state_and_flags)
      0x00502d74: 35000390    cbnz w16, #+0x70 (addr 0x502de4)

前面例行公事的我们前几章已经分析过了,只有一个参数在w1中,存到sp+56位置。
循环变量用sp+12,和的结果是sp+20。

      0x00502d78: 52800010    mov w16, #0x0
      0x00502d7c: b9000ff0    str w16, [sp, #12]
      0x00502d80: d2800010    mov x16, #0x0
      0x00502d84: f80143f0    stur x16, [sp, #20]

循环控制变量和传进来的值做比较,如果大于则跳到0x502dd4,将sp+20和的结果传给x0,准备返回。

      0x00502d88: b9400fe0    ldr w0, [sp, #12]
      0x00502d8c: b9403be1    ldr w1, [sp, #56]
      0x00502d90: 6b01001f    cmp w0, w1
      0x00502d94: 1a9fd7e2    cset w2, gt
      0x00502d98: 2a0203e0    mov w0, w2
      0x00502d9c: 350001c0    cbnz w0, #+0x38 (addr 0x502dd4)

不大于的话,先把循环控制变量读到w0,然后扩展成64位的。存到另一个位置sp+28中。
将sp+20的和,与sp+28循环当前值加起来。存回sp+20.

      0x00502da0: b9400fe0    ldr w0, [sp, #12]
      0x00502da4: 93407c01    sxtw x1, w0
      0x00502da8: f801c3e1    stur x1, [sp, #28]
      0x00502dac: f84143e0    ldur x0, [sp, #20]
      0x00502db0: f841c3e1    ldur x1, [sp, #28]
      0x00502db4: 8b010002    add x2, x0, x1
      0x00502db8: f80143e2    stur x2, [sp, #20]

sp+12的循环控制变量读进来,加1,存回去。检查一下state,是不是要suspend。不是的话,跳回0x502d88,就是循环变量与传进来的值做比较的那一行,继续循环。

      0x00502dbc: b9400fe0    ldr w0, [sp, #12]
      0x00502dc0: 11000401    add w1, w0, #0x1 (1)
      0x00502dc4: b9000fe1    str w1, [sp, #12]
      0x00502dc8: 79400250    ldrh w16, [tr](state_and_flags)
      0x00502dcc: 35000130    cbnz w16, #+0x24 (addr 0x502df0)
      0x00502dd0: 17ffffee    b #-0x48 (addr 0x502d88)
      0x00502dd4: f84143e0    ldur x0, [sp, #20]
      0x00502dd8: f94017fe    ldr lr, [sp, #40]
      0x00502ddc: 9100c3ff    add sp, sp, #0x30 (48)
      0x00502de0: d65f03c0    ret

后面还是判断pTestSuspend的地方,这次有两处,它们对应的dex PC不同。

      0x00502de4: f9421e5e    ldr lr, [tr, #1080](pTestSuspend)
      0x00502de8: d63f03c0    blr lr
      suspend point dex PC: 0x0000
      0x00502dec: 17ffffe3    b #-0x74 (addr 0x502d78)
      0x00502df0: f9421e5e    ldr lr, [tr, #1080](pTestSuspend)
      0x00502df4: d63f03c0    blr lr
      suspend point dex PC: 0x0009
      0x00502df8: 17fffff6    b #-0x28 (addr 0x502dd0)

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
开发与运维
使用钉钉扫一扫加入圈子
+ 订阅

集结各类场景实战经验,助你开发运维畅行无忧

其他文章