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)