ART世界探险(6) - 流程控制指令
来源:互联网 发布:如何查询淘宝买家等级 编辑:程序博客网 时间:2024/04/30 01:06
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 ret00000000000007a8 <_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 lr00001024 <_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 lr000012ac <_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,%ax00000640 <_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 nop0000000000000c18 <_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,a100000720 <_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 lr000012d6 <_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,zero00000760 <_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,v100000784 <_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 nop0000000000000c68 <_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 ra0000000000000c88 <_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)
- ART世界探险(6) - 流程控制指令
- ART世界探险(5) - 计算指令
- ART世界探险(19) - 优化编译器的编译流程
- ART世界探险(20) - Android N上的编译流程
- ART世界探险(7) - 数组
- ART世界探险(18) InlineMethod
- ART世界探险(4) - 数据传送指令和桶型移位器
- ART探险(1) - oatdump看到的世界
- ART世界探险(8) - 面向对象编程
- ART世界探险(9) - 同步锁
- ART世界探险(10) - 异常处理
- ART世界探险(11) - OAT文件格式分析
- ART世界探险(13) - 初入dex2oat
- ART世界探险(2) - 从java byte code说起
- ART世界探险(14) - 快速编译器和优化编译器
- ART世界探险(15) - CompilerDriver,ClassLinker,Runtime三大组件
- ART世界探险(16) - 快速编译器下的方法编译
- ART世界探险(17) - 中层中间代码MIR
- Ofbiz模块加载机制即创建独立模块(脱离热部署)
- 练手程序120个(转载)
- struts2: tag s:property must be empty, but is not
- CodeForces 197D Infinite Maze (BFS+状态压缩)
- android中dip、px相互换算
- ART世界探险(6) - 流程控制指令
- 教你一招手机微信聊天记录删除了怎么恢复?
- 显示访问IP
- 秒杀面试 - 程序员面试宝典
- List和ArrayList
- ~用心专研下技术~
- jsp 路径问题
- 关于字符集的若干问题总结
- 字符集典型场景