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

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)
时间: 2024-10-03 18:06:12

ART世界探险(6) - 流程控制指令的相关文章

ART世界探险(20) - Android N上的编译流程

ART世界探险(20) - Android N上的编译流程 就在我们分析Android M版本的ART还只走出了一小段路的时候,Android N的新ART就问世了. Android N上的ART还是有不小的改进的.不过做为一个关注细节的系列文章,我们还是从Compile的过程说起. 流程概述 在安装的时候,默认情况下,Android N只做interpret-only的编译,如下命令行所示: /system/bin/dex2oat --zip-fd=7 --zip-location=base.

ART世界探险(19) - 优化编译器的编译流程

ART世界探险(19) - 优化编译器的编译流程 前面,我们对于快速编译器的知识有了一点了解,对于CompilerDriver,MIRGraph等都有了初步的印象. 下面,我们回头看一下优化编译器的编译过程.有了前面的基础,后面的学习过程会更顺利一些. 下面我们先看个地图,看看我们将遇到哪些新的对象: OptimizingCompiler::Compile 我们先来看看优化编译的入口点,Compile函数: CompiledMethod OptimizingCompiler::Compile(c

ART世界探险(18) InlineMethod

ART世界探险(18) InlineMethod 好,我们还是先复习一下上上节学到的图: 在开始InlineMethod之前,我们再继续补充一点BasicBlock的知识. BasicBlock中针对MIR的相关操作 AppendMIR AppendMIR的作用是将MIR增加到一个BasicBlock的结尾. / Insert an MIR instruction to the end of a basic block. / void BasicBlock::AppendMIR(MIR* mir

ART世界探险(3) - ARM 64位CPU的架构快餐教程

ART世界探险(3) - ARM 64位CPU的架构快餐教程 前面我们说过,Dalvik如果没有JIT的话,可以做到架构无关,让Dalvik指令都解释执行.但是ART是AOT,要编译成针对芯片具体的机器指令. 所以,研究Dalvik的时候可以不用太关心目标指令,而我们研究ART必须对目前最流行的微处理器的架构有个基本的了解. 在上一讲我们对于ART从java byte code到ARM64 v8指令的整个流程有了一个大概的了解之后,我们就目前最流行的ARM64位芯片的知识进行一些探索. 我们的目

ART世界探险(15) - CompilerDriver,ClassLinker,Runtime三大组件

ART世界探险(15) - CompilerDriver,ClassLinker,Runtime三大组件 CompilerDriver 调用编译器的接口是CompilerDriver. 我们看一看CompilerDriver的结构图吧: 这是我们在ART里能遇见的第一个复杂的大类.但凡编译相关,都要通过它来打交道.结果,它就把自己搞成了一个大杂烩. ClassLinker Java是门面向对象的语言,导致类相关的操作比较复杂. 在应用层有ClassLoader,在运行环境层就有ClassLink

ART世界探险(16) - 快速编译器下的方法编译

ART世界探险(16) - 快速编译器下的方法编译 我们对三大组件有了了解之后,下面终于可以开始正餐,开始分析两种Compiler下的Compile函数. 我们先看一张图,对于整个流程有个整体的印象,然后我们再去看代码: QuickCompiler的Compile CompiledMethod QuickCompiler::Compile(const DexFile::CodeItem code_item, uint32_t access_flags, InvokeType invoke_typ

ART世界探险(13) - 初入dex2oat

ART世界探险(13) - 初入dex2oat dex2oat流程分析 进入整个流程之前,我们先看一下地图,大致熟悉一下我们下一步要去哪里: 主函数 dex2oat的main函数,直接是dex2oat工厂函数的封装. int main(int argc, char** argv) { int result = art::dex2oat(argc, argv); // Everything was done, do an explicit exit here to avoid running Ru

ART世界探险(9) - 同步锁

ART世界探险(9) - 同步锁 Java是一种把同步锁写进语言和指令集的语言. 从语言层面,Java提供了synchronized关键字. 从指令集层面,Java提供了monitorenter和monitorexit两条指令. 下面我们就看看它们是如何实现的吧. 三种锁的方式 Java代码 有三种方式来加锁: 直接在函数上加synchronized关键字 在函数内用某Object去做同步 调用concurrent库中的其他工具 public synchronized int newID(){

ART世界探险(12) - OAT文件分析(2) - ELF文件头分析(中)

ART世界探险(12) - OAT文件分析(2) - ELF文件头分析(中) 段(section)的概念 一块内存分配给应用程序之后,从代码的组织上,我们就有将它们分段的需求. 比如,可以分为代码段,数据段,只读数据段,堆栈段,未初始化的数据段等等. 在GAS汇编器中,我们通过.section伪指令来指定段名.ARM编译器我买不起,我就忽略它了. 标准section 段的描述 默认段名 代码段 .text 经过初始化的数据段 .data 未经初始化的数据段 .bss BSS是Block Star