zl程序教程

您现在的位置是:首页 >  大数据

当前栏目

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

计算 世界 指令 art
2023-09-14 09:04:39 时间

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

 public static long add(long a, long b){

 return a+b;

 public static long sub(long a,long b){

 return a-b;

 public static long mul(long a, long b){

 return a*b;

 public static long div(long a,long b){

 return a/b;

 public static long mod(long a,long b){

 return a%b;

 }

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

 public static long add(long, long);

 Code:

 0: lload_0

 1: lload_2

 2: ladd

 3: lreturn

 public static long sub(long, long);

 Code:

 0: lload_0

 1: lload_2

 2: lsub

 3: lreturn

 public static long mul(long, long);

 Code:

 0: lload_0

 1: lload_2

 2: lmul

 3: lreturn

 public static long div(long, long);

 Code:

 0: lload_0

 1: lload_2

 2: ldiv

 3: lreturn

 public static long mod(long, long);

 Code:

 0: lload_0

 1: lload_2

 2: lrem

 3: lreturn

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

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

 1: long com.yunos.xulun.testcppjni2.TestART.add(long, long) (dex_method_idx=16778)

 DEX CODE:

 0x0000: 9b00 0204 | add-long v0, v2, v4

 0x0002: 1000 | return-wide v0

我们看看在ARM上的实现。

ARM的整型运算

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

long add(long a, long b){

 return a+b;

long sub(long a,long b){

 return a-b;

long mul(long a, long b){

 return a*b;

long div(long a,long b){

 return a/b;

long mod(long a,long b){

 return a%b;

}
ARM v8a的整数运算

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

0000000000000694 _Z3addll :

 694: 8b010000 add x0, x0, x1

 698: d65f03c0 ret

000000000000069c _Z3subll :

 69c: cb010000 sub x0, x0, x1

 6a0: d65f03c0 ret

00000000000006a4 _Z3mulll :

 6a4: 9b017c00 mul x0, x0, x1

 6a8: d65f03c0 ret

00000000000006ac _Z3divll :

 6ac: 9ac10c00 sdiv x0, x0, x1

 6b0: d65f03c0 ret

00000000000006b4 _Z3modll :

 6b4: 9ac10c02 sdiv x2, x0, x1

 6b8: 9b018040 msub x0, x2, x1, x0

 6bc: d65f03c0 ret
ARM v7a的整数运算

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

00000fc0 _Z3addll :

 fc0: 4408 add r0, r1

 fc2: 4770 bx lr

00000fc4 _Z3subll :

 fc4: 1a40 subs r0, r0, r1

 fc6: 4770 bx lr

00000fc8 _Z3mulll :

 fc8: 4348 muls r0, r1

 fca: 4770 bx lr

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

00000fcc _Z3divll :

 fcc: b508 push {r3, lr}

 fce: f000 e830 blx 1030 __aeabi_idiv 

 fd2: bd08 pop {r3, pc}

00000fd4 _Z3modll :

 fd4: b508 push {r3, lr}

 fd6: f000 e89a blx 110c __aeabi_idivmod 

 fda: 4608 mov r0, r1

 fdc: bd08 pop {r3, pc}

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

00001030 __aeabi_idiv :

 1030: e3510000 cmp r1, #0

 1034: 0a000030 beq 10fc __aeabi_idiv+0xcc 

 1038: e020c001 eor ip, r0, r1

 103c: 42611000 rsbmi r1, r1, #0

 1040: e2512001 subs r2, r1, #1

 1044: 0a00001f beq 10c8 __aeabi_idiv+0x98 

 1048: e1b03000 movs r3, r0

 104c: 42603000 rsbmi r3, r0, #0

 1050: e1530001 cmp r3, r1

 1054: 9a00001e bls 10d4 __aeabi_idiv+0xa4 

 1058: e1110002 tst r1, r2

 105c: 0a000020 beq 10e4 __aeabi_idiv+0xb4 

 1060: e16f2f11 clz r2, r1

 1064: e16f0f13 clz r0, r3

 1068: e0420000 sub r0, r2, r0

 106c: e3a02001 mov r2, #1

 1070: e1a01011 lsl r1, r1, r0

 1074: e1a02012 lsl r2, r2, r0

 1078: e3a00000 mov r0, #0

 107c: e1530001 cmp r3, r1

 1080: 20433001 subcs r3, r3, r1

 1084: 21800002 orrcs r0, r0, r2

 1088: e15300a1 cmp r3, r1, lsr #1

 108c: 204330a1 subcs r3, r3, r1, lsr #1

 1090: 218000a2 orrcs r0, r0, r2, lsr #1

 1094: e1530121 cmp r3, r1, lsr #2

 1098: 20433121 subcs r3, r3, r1, lsr #2

 109c: 21800122 orrcs r0, r0, r2, lsr #2

 10a0: e15301a1 cmp r3, r1, lsr #3

 10a4: 204331a1 subcs r3, r3, r1, lsr #3

 10a8: 218001a2 orrcs r0, r0, r2, lsr #3

 10ac: e3530000 cmp r3, #0

 10b0: 11b02222 lsrsne r2, r2, #4

 10b4: 11a01221 lsrne r1, r1, #4

 10b8: 1affffef bne 107c __aeabi_idiv+0x4c 

 10bc: e35c0000 cmp ip, #0

 10c0: 42600000 rsbmi r0, r0, #0

 10c4: e12fff1e bx lr

 10c8: e13c0000 teq ip, r0

 10cc: 42600000 rsbmi r0, r0, #0

 10d0: e12fff1e bx lr

 10d4: 33a00000 movcc r0, #0

 10d8: 01a00fcc asreq r0, ip, #31

 10dc: 03800001 orreq r0, r0, #1

 10e0: e12fff1e bx lr

 10e4: e16f2f11 clz r2, r1

 10e8: e262201f rsb r2, r2, #31

 10ec: e35c0000 cmp ip, #0

 10f0: e1a00233 lsr r0, r3, r2

 10f4: 42600000 rsbmi r0, r0, #0

 10f8: e12fff1e bx lr

 10fc: e3500000 cmp r0, #0

 1100: c3e00102 mvngt r0, #-2147483648 ; 0x80000000

 1104: b3a00102 movlt r0, #-2147483648 ; 0x80000000

 1108: ea000007 b 112c __aeabi_idiv0 

0000110c __aeabi_idivmod :

 110c: e3510000 cmp r1, #0

 1110: 0afffff9 beq 10fc __aeabi_idiv+0xcc 

 1114: e92d4003 push {r0, r1, lr}

 1118: ebffffc6 bl 1038 __aeabi_idiv+0x8 

 111c: e8bd4006 pop {r1, r2, lr}

 1120: e0030092 mul r3, r2, r0

 1124: e0411003 sub r1, r1, r3

 1128: e12fff1e bx lr
传统armeabi的整数运算

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

00001248 _Z3addll :

 1248: 1840 adds r0, r0, r1

 124a: 4770 bx lr

0000124c _Z3subll :

 124c: 1a40 subs r0, r0, r1

 124e: 4770 bx lr

00001250 _Z3mulll :

 1250: 4348 muls r0, r1

 1252: 4770 bx lr

除法和取模也是调函数:

00001254 _Z3divll :

 1254: b508 push {r3, lr}

 1256: f001 ff47 bl 30e8 _Unwind_GetTextRelBase+0x8 

 125a: bd08 pop {r3, pc}

0000125c _Z3modll :

 125c: b508 push {r3, lr}

 125e: f001 ff4b bl 30f8 _Unwind_GetTextRelBase+0x18 

 1262: 1c08 adds r0, r1, #0

 1264: bd08 pop {r3, pc}
OAT编译出来的结果

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

先看个加法的吧:

 CODE: (code_offset=0x0050270c size_offset=0x00502708 size=76)...

 0x0050270c: d1400bf0 sub x16, sp, #0x2000 (8192)

 0x00502710: b940021f ldr wzr, [x16]

 suspend point dex PC: 0x0000

 0x00502714: f81e0fe0 str x0, [sp, #-32]!

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

 0x00502718: f9000ffe str lr, [sp, #24]

 0x0050271c: f90017e1 str x1, [sp, #40]

 0x00502720: f9001be2 str x2, [sp, #48]

 0x00502724: 79400250 ldrh w16, [tr](state_and_flags)

 0x00502728: 35000130 cbnz w16, #+0x24 (addr 0x50274c)

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

 0x0050272c: f94017e0 ldr x0, [sp, #40]

 0x00502730: f9401be1 ldr x1, [sp, #48]

 0x00502734: 8b010002 add x2, x0, x1

 0x00502738: f800c3e2 stur x2, [sp, #12]

 0x0050273c: f840c3e0 ldur x0, [sp, #12]

 0x00502740: f9400ffe ldr lr, [sp, #24]

 0x00502744: 910083ff add sp, sp, #0x20 (32)

 0x00502748: d65f03c0 ret

 0x0050274c: f9421e5e ldr lr, [tr, #1080](pTestSuspend)

 0x00502750: d63f03c0 blr lr

 suspend point dex PC: 0x0000

 0x00502754: 17fffff6 b #-0x28 (addr 0x50272c)

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

 3: long com.yunos.xulun.testcppjni2.TestART.div(long, long) (dex_method_idx=16780)

 DEX CODE:

 0x0000: 9e00 0204 | div-long v0, v2, v4

 0x0002: 1000 | return-wide v0

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

 CODE: (code_offset=0x0050280c size_offset=0x00502808 size=96)...

 0x0050280c: d1400bf0 sub x16, sp, #0x2000 (8192)

 0x00502810: b940021f ldr wzr, [x16]

 suspend point dex PC: 0x0000

 0x00502814: f81d0fe0 str x0, [sp, #-48]!

 0x00502818: f90017fe str lr, [sp, #40]

 0x0050281c: f9001fe1 str x1, [sp, #56]

 0x00502820: f90023e2 str x2, [sp, #64]

 0x00502824: 79400250 ldrh w16, [tr](state_and_flags)

 0x00502828: 35000190 cbnz w16, #+0x30 (addr 0x502858)

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

 0x0050282c: f94023e0 ldr x0, [sp, #64]

 0x00502830: b40001a0 cbz x0, #+0x34 (addr 0x502864)

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

 0x00502834: f80143e0 stur x0, [sp, #20]

 0x00502838: f9401fe0 ldr x0, [sp, #56]

 0x0050283c: f84143e1 ldur x1, [sp, #20]

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

 0x00502840: 9ac10c02 sdiv x2, x0, x1

 0x00502844: f801c3e2 stur x2, [sp, #28]

 0x00502848: f841c3e0 ldur x0, [sp, #28]

 0x0050284c: f94017fe ldr lr, [sp, #40]

 0x00502850: 9100c3ff add sp, sp, #0x30 (48)

 0x00502854: d65f03c0 ret

 0x00502858: f9421e5e ldr lr, [tr, #1080](pTestSuspend)

 0x0050285c: d63f03c0 blr lr

 suspend point dex PC: 0x0000

 0x00502860: 17fffff3 b #-0x34 (addr 0x50282c)

 0x00502864: f9422a5e ldr lr, [tr, #1104](pThrowDivZero)

 0x00502868: d63f03c0 blr lr

 suspend point dex PC: 0x0000
Java浮点运算

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

 public static double dadd(double a,double b){

 return a+b;

 public static double dsub(double a,double b){

 return a-b;

 public static double dmul(double a,double b){

 return a*b;

 public static double ddiv(double a,double b){

 return a/b;

 }

字节码如下:

 public static double dadd(double, double);

 Code:

 0: dload_0

 1: dload_2

 2: dadd

 3: dreturn

 public static double dsub(double, double);

 Code:

 0: dload_0

 1: dload_2

 2: dsub

 3: dreturn

 public static double dmul(double, double);

 Code:

 0: dload_0

 1: dload_2

 2: dmul

 3: dreturn

 public static double ddiv(double, double);

 Code:

 0: dload_0

 1: dload_2

 2: ddiv

 3: dreturn

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

ARM浮点运算

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

double dadd(double a,double b){

 return a+b;

double dsub(double a,double b){

 return a-b;

double dmul(double a,double b){

 return a*b;

double ddiv(double a,double b){

 return a/b;

}
ARM v8a的浮点运算

汇编代码:

0000000000000760 _Z4dadddd :

 760: 1e612800 fadd d0, d0, d1

 764: d65f03c0 ret

0000000000000768 _Z4dsubdd :

 768: 1e613800 fsub d0, d0, d1

 76c: d65f03c0 ret

0000000000000770 _Z4dmuldd :

 770: 1e610800 fmul d0, d0, d1

 774: d65f03c0 ret

0000000000000778 _Z4ddivdd :

 778: 1e611800 fdiv d0, d0, d1

 77c: d65f03c0 ret

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

ARM v7a的浮点运算:

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

00000fde _Z4dadddd :

 fde: ec41 0b17 vmov d7, r0, r1

 fe2: ec43 2b16 vmov d6, r2, r3

 fe6: ee37 7b06 vadd.f64 d7, d7, d6

 fea: ec51 0b17 vmov r0, r1, d7

 fee: 4770 bx lr

00000ff0 _Z4dsubdd :

 ff0: ec41 0b17 vmov d7, r0, r1

 ff4: ec43 2b16 vmov d6, r2, r3

 ff8: ee37 7b46 vsub.f64 d7, d7, d6

 ffc: ec51 0b17 vmov r0, r1, d7

 1000: 4770 bx lr

00001002 _Z4dmuldd :

 1002: ec41 0b17 vmov d7, r0, r1

 1006: ec43 2b16 vmov d6, r2, r3

 100a: ee27 7b06 vmul.f64 d7, d7, d6

 100e: ec51 0b17 vmov r0, r1, d7

 1012: 4770 bx lr

00001014 _Z4ddivdd :

 1014: ec41 0b17 vmov d7, r0, r1

 1018: ec43 2b16 vmov d6, r2, r3

 101c: ee87 7b06 vdiv.f64 d7, d7, d6

 1020: ec51 0b17 vmov r0, r1, d7

 1024: 4770 bx lr
传统ARM的浮点运算

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

00001248 _Z3addll :

 1248: 1840 adds r0, r0, r1

 124a: 4770 bx lr

0000124c _Z3subll :

 124c: 1a40 subs r0, r0, r1

 124e: 4770 bx lr

00001250 _Z3mulll :

 1250: 4348 muls r0, r1

 1252: 4770 bx lr

00001254 _Z3divll :

 1254: b508 push {r3, lr}

 1256: f001 ff47 bl 30e8 _Unwind_GetTextRelBase+0x8 

 125a: bd08 pop {r3, pc}

0000125c _Z3modll :

 125c: b508 push {r3, lr}

 125e: f001 ff4b bl 30f8 _Unwind_GetTextRelBase+0x18 

 1262: 1c08 adds r0, r1, #0

 1264: bd08 pop {r3, pc}
x86芯片的运算指令

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

x86的整数运算

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

00005f0 _Z3addll :

 5f0: 8b 44 24 08 mov 0x8(%esp),%eax

 5f4: 03 44 24 04 add 0x4(%esp),%eax

 5f8: c3 ret 

 5f9: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi

00000600 _Z3subll :

 600: 8b 44 24 04 mov 0x4(%esp),%eax

 604: 2b 44 24 08 sub 0x8(%esp),%eax

 608: c3 ret 

 609: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi

00000610 _Z3mulll :

 610: 8b 44 24 08 mov 0x8(%esp),%eax

 614: 0f af 44 24 04 imul 0x4(%esp),%eax

 619: c3 ret 

 61a: 8d b6 00 00 00 00 lea 0x0(%esi),%esi

00000620 _Z3divll :

 620: 8b 44 24 04 mov 0x4(%esp),%eax

 624: 89 c2 mov %eax,%edx

 626: c1 fa 1f sar $0x1f,%edx

 629: f7 7c 24 08 idivl 0x8(%esp)

 62d: c3 ret 

 62e: 66 90 xchg %ax,%ax

00000630 _Z3modll :

 630: 8b 44 24 04 mov 0x4(%esp),%eax

 634: 89 c2 mov %eax,%edx

 636: c1 fa 1f sar $0x1f,%edx

 639: f7 7c 24 08 idivl 0x8(%esp)

 63d: 89 d0 mov %edx,%eax

 63f: c3 ret 

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

x86_64的整数运算

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

00000000000006e0 _Z3addll :

 6e0: 48 8d 04 37 lea (%rdi,%rsi,1),%rax

 6e4: c3 retq 

 6e5: 66 66 2e 0f 1f 84 00 data16 nopw %cs:0x0(%rax,%rax,1)

 6ec: 00 00 00 00 

00000000000006f0 _Z3subll :

 6f0: 48 89 f8 mov %rdi,%rax

 6f3: 48 29 f0 sub %rsi,%rax

 6f6: c3 retq 

 6f7: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)

 6fe: 00 00 

0000000000000700 _Z3mulll :

 700: 48 89 f8 mov %rdi,%rax

 703: 48 0f af c6 imul %rsi,%rax

 707: c3 retq 

 708: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)

 70f: 00 

0000000000000710 _Z3divll :

 710: 48 89 fa mov %rdi,%rdx

 713: 48 89 f8 mov %rdi,%rax

 716: 48 c1 fa 3f sar $0x3f,%rdx

 71a: 48 f7 fe idiv %rsi

 71d: c3 retq 

 71e: 66 90 xchg %ax,%ax

0000000000000720 _Z3modll :

 720: 48 89 fa mov %rdi,%rdx

 723: 48 89 f8 mov %rdi,%rax

 726: 48 c1 fa 3f sar $0x3f,%rdx

 72a: 48 f7 fe idiv %rsi

 72d: 48 89 d0 mov %rdx,%rax

 730: c3 retq 

 731: 66 66 66 66 66 66 2e data16 data16 data16 data16 data16 nopw %cs:0x0(%rax,%rax,1)

 738: 0f 1f 84 00 00 00 00 

 73f: 00 
x86的符点运算

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

00000640 _Z4dadddd :

 640: 8d 64 24 f4 lea -0xc(%esp),%esp

 644: f2 0f 10 44 24 18 movsd 0x18(%esp),%xmm0

 64a: f2 0f 58 44 24 10 addsd 0x10(%esp),%xmm0

 650: f2 0f 11 04 24 movsd %xmm0,(%esp)

 655: dd 04 24 fldl (%esp)

 658: 8d 64 24 0c lea 0xc(%esp),%esp

 65c: c3 ret 

 65d: 8d 76 00 lea 0x0(%esi),%esi

00000660 _Z4dsubdd :

 660: 8d 64 24 f4 lea -0xc(%esp),%esp

 664: f2 0f 10 44 24 10 movsd 0x10(%esp),%xmm0

 66a: f2 0f 5c 44 24 18 subsd 0x18(%esp),%xmm0

 670: f2 0f 11 04 24 movsd %xmm0,(%esp)

 675: dd 04 24 fldl (%esp)

 678: 8d 64 24 0c lea 0xc(%esp),%esp

 67c: c3 ret 

 67d: 8d 76 00 lea 0x0(%esi),%esi

00000680 _Z4dmuldd :

 680: 8d 64 24 f4 lea -0xc(%esp),%esp

 684: f2 0f 10 44 24 18 movsd 0x18(%esp),%xmm0

 68a: f2 0f 59 44 24 10 mulsd 0x10(%esp),%xmm0

 690: f2 0f 11 04 24 movsd %xmm0,(%esp)

 695: dd 04 24 fldl (%esp)

 698: 8d 64 24 0c lea 0xc(%esp),%esp

 69c: c3 ret 

 69d: 8d 76 00 lea 0x0(%esi),%esi

000006a0 _Z4ddivdd :

 6a0: 8d 64 24 f4 lea -0xc(%esp),%esp

 6a4: f2 0f 10 44 24 10 movsd 0x10(%esp),%xmm0

 6aa: f2 0f 5e 44 24 18 divsd 0x18(%esp),%xmm0

 6b0: f2 0f 11 04 24 movsd %xmm0,(%esp)

 6b5: dd 04 24 fldl (%esp)

 6b8: 8d 64 24 0c lea 0xc(%esp),%esp

 6bc: c3 ret 

 6bd: 8d 76 00 lea 0x0(%esi),%esi
x86_64的浮点运算

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

0000000000000740 _Z4dadddd :

 740: f2 0f 58 c1 addsd %xmm1,%xmm0

 744: c3 retq 

 745: 66 66 2e 0f 1f 84 00 data16 nopw %cs:0x0(%rax,%rax,1)

 74c: 00 00 00 00 

0000000000000750 _Z4dsubdd :

 750: f2 0f 5c c1 subsd %xmm1,%xmm0

 754: c3 retq 

 755: 66 66 2e 0f 1f 84 00 data16 nopw %cs:0x0(%rax,%rax,1)

 75c: 00 00 00 00 

0000000000000760 _Z4dmuldd :

 760: f2 0f 59 c1 mulsd %xmm1,%xmm0

 764: c3 retq 

 765: 66 66 2e 0f 1f 84 00 data16 nopw %cs:0x0(%rax,%rax,1)

 76c: 00 00 00 00 

0000000000000770 _Z4ddivdd :

 770: f2 0f 5e c1 divsd %xmm1,%xmm0

 774: c3 retq 

 775: 66 66 2e 0f 1f 84 00 data16 nopw %cs:0x0(%rax,%rax,1)

 77c: 00 00 00 00 
MIPS指令集下的计算指令

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

000006b0 _Z3addll :

 6b0: 03e00008 jr ra

 6b4: 00851021 addu v0,a0,a1

000006b8 _Z3subll :

 6b8: 03e00008 jr ra

 6bc: 00851023 subu v0,a0,a1

000006c0 _Z3mulll :

 6c0: 03e00008 jr ra

 6c4: 70851002 mul v0,a0,a1

000006c8 _Z3divll :

 6c8: 0085001a div zero,a0,a1

 6cc: 00a001f4 teq a1,zero,0x7

 6d0: 03e00008 jr ra

 6d4: 00001012 mflo v0

000006d8 _Z3modll :

 6d8: 0085001a div zero,a0,a1

 6dc: 00a001f4 teq a1,zero,0x7

 6e0: 03e00008 jr ra

 6e4: 00001010 mfhi v0

000006e8 _Z4dadddd :

 6e8: 03e00008 jr ra

 6ec: 462e6000 add.d $f0,$f12,$f14

000006f0 _Z4dsubdd :

 6f0: 03e00008 jr ra

 6f4: 462e6001 sub.d $f0,$f12,$f14

000006f8 _Z4dmuldd :

 6f8: 03e00008 jr ra

 6fc: 462e6002 mul.d $f0,$f12,$f14

00000700 _Z4ddivdd :

 700: 03e00008 jr ra

 704: 462e6003 div.d $f0,$f12,$f14

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

0000000000000b80 _Z3addll :

 b80: 03e00009 jr ra

 b84: 0085102d daddu v0,a0,a1

0000000000000b88 _Z3subll :

 b88: 03e00009 jr ra

 b8c: 0085102f dsubu v0,a0,a1

0000000000000b90 _Z3mulll :

 b90: 03e00009 jr ra

 b94: 0085109c dmul v0,a0,a1

0000000000000b98 _Z3divll :

 b98: 0085109e ddiv v0,a0,a1

 b9c: 00a001f4 teq a1,zero,0x7

 ba0: d81f0000 jrc ra

 ba4: 00000000 nop

0000000000000ba8 _Z3modll :

 ba8: 008510de dmod v0,a0,a1

 bac: 00a001f4 teq a1,zero,0x7

 bb0: d81f0000 jrc ra

 bb4: 00000000 nop

0000000000000bb8 _Z4dadddd :

 bb8: 03e00009 jr ra

 bbc: 462d6000 add.d $f0,$f12,$f13

0000000000000bc0 _Z4dsubdd :

 bc0: 03e00009 jr ra

 bc4: 462d6001 sub.d $f0,$f12,$f13

0000000000000bc8 _Z4dmuldd :

 bc8: 03e00009 jr ra

 bcc: 462d6002 mul.d $f0,$f12,$f13

0000000000000bd0 _Z4ddivdd :

 bd0: 03e00009 jr ra

 bd4: 462d6003 div.d $f0,$f12,$f13
编译出的OAT的浮点运算

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

 3: double com.yunos.xulun.testcppjni2.TestART.dadd(double, double) (dex_method_idx=16780)

 DEX CODE:

 0x0000: ab00 0204 | add-double v0, v2, v4

 0x0002: 1000 | return-wide v0

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

 CODE: (code_offset=0x0050280c size_offset=0x00502808 size=76)...

 0x0050280c: d1400bf0 sub x16, sp, #0x2000 (8192)

 0x00502810: b940021f ldr wzr, [x16]

 suspend point dex PC: 0x0000

 0x00502814: f81e0fe0 str x0, [sp, #-32]!

 0x00502818: f9000ffe str lr, [sp, #24]

 0x0050281c: fd0017e0 str d0, [sp, #40]

 0x00502820: fd001be1 str d1, [sp, #48]

 0x00502824: 79400250 ldrh w16, [tr](state_and_flags)

 0x00502828: 35000130 cbnz w16, #+0x24 (addr 0x50284c)

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

 0x0050282c: fd4017e0 ldr d0, [sp, #40]

 0x00502830: fd401be1 ldr d1, [sp, #48]

 0x00502834: 1e612802 fadd d2, d0, d1

 0x00502838: fc00c3e2 stur d2, [sp, #12]

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

 0x0050283c: fc40c3e0 ldur d0, [sp, #12]

 0x00502840: f9400ffe ldr lr, [sp, #24]

 0x00502844: 910083ff add sp, sp, #0x20 (32)

 0x00502848: d65f03c0 ret

 0x0050284c: f9421e5e ldr lr, [tr, #1080](pTestSuspend)

 0x00502850: d63f03c0 blr lr

 suspend point dex PC: 0x0000

 0x00502854: 17fffff6 b #-0x28 (addr 0x50282c)

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

 4: double com.yunos.xulun.testcppjni2.TestART.ddiv(double, double) (dex_method_idx=16781)

 DEX CODE:

 0x0000: ae00 0204 | div-double v0, v2, v4

 0x0002: 1000 | return-wide v0

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

 CODE: (code_offset=0x0050287c size_offset=0x00502878 size=76)...

 0x0050287c: d1400bf0 sub x16, sp, #0x2000 (8192)

 0x00502880: b940021f ldr wzr, [x16]

 suspend point dex PC: 0x0000

 0x00502884: f81e0fe0 str x0, [sp, #-32]!

 0x00502888: f9000ffe str lr, [sp, #24]

 0x0050288c: fd0017e0 str d0, [sp, #40]

 0x00502890: fd001be1 str d1, [sp, #48]

 0x00502894: 79400250 ldrh w16, [tr](state_and_flags)

 0x00502898: 35000130 cbnz w16, #+0x24 (addr 0x5028bc)

 0x0050289c: fd4017e0 ldr d0, [sp, #40]

 0x005028a0: fd401be1 ldr d1, [sp, #48]

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

 0x005028a4: 1e611802 fdiv d2, d0, d1

 0x005028a8: fc00c3e2 stur d2, [sp, #12]

 0x005028ac: fc40c3e0 ldur d0, [sp, #12]

 0x005028b0: f9400ffe ldr lr, [sp, #24]

 0x005028b4: 910083ff add sp, sp, #0x20 (32)

 0x005028b8: d65f03c0 ret

 0x005028bc: f9421e5e ldr lr, [tr, #1080](pTestSuspend)

 0x005028c0: d63f03c0 blr lr

 suspend point dex PC: 0x0000

 0x005028c4: 17fffff6 b #-0x28 (addr 0x50289c)

生成对抗网络入门指南(内含资源和代码) 生成对抗网络是由两个相互竞争的网络组成的深度神经网络架构。本文对其进行详细讲解,并附上大量相关英文文章链接供参考。
2017/10/18/11:00第一版本笔记,主要更新了生物信息要用那些基本技能和电脑配置简单说明,以及如何在虚拟机器上安装bioconda 2017/10/18/12:30第二版笔记,增加虚拟机配置 2017/10/18/13:05 第三版笔记,增加文件结构 2017/10/19/10:30 第四版笔记, 增加如何用xshell连接虚拟机内部系统 2017/10/20/08:09 第五版笔记,增加线下辅导部分。
教你从头到尾利用DQN自动玩flappy bird(全程命令提示,GPU+CPU版)【转】 转自:http://blog.csdn.net/v_JULY_v/article/details/52810219?locationNum=3&fps=1 目录(?)[-]     教你从头到尾利用DQN自动玩flappy bird全程命令提示GPUCPU 第一部分GPU版教程...
ART世界探险(19) - 优化编译器的编译流程 前面,我们对于快速编译器的知识有了一点了解,对于CompilerDriver,MIRGraph等都有了初步的印象。 下面,我们回头看一下优化编译器的编译过程。
lusing 刘子瑛,阿里系统框架专家。工作十余年,一直对新编程语言、新开发方法、数学与算法相关和并发等相关领域保持浓厚的兴趣。乐于通过技术分享促进新技术。