ART世界探險(xiǎn)(7) - 數(shù)組

ART世界探險(xiǎn)(7) - 數(shù)組

Java針對(duì)數(shù)據(jù)是有專門的指令去處理的,這與C/C++有顯著的不同磷账。

Java字節(jié)碼對(duì)于數(shù)組的支持

一個(gè)極簡的例子

Java源代碼

為了簡化,我們?nèi)∫粋€(gè)極簡的例子來說明Java的數(shù)組指令的用法:

我們new一個(gè)長度為1的字節(jié)數(shù)組,然后返回這個(gè)數(shù)組的長度。

    public static int testByteArrayLength(){
        byte[] baArray = new byte[1];
        return baArray.length;
    }

Java字節(jié)碼

有幾條指令先交代一下:

  • newarray:這條指令用于創(chuàng)建一個(gè)新的數(shù)組寇仓,參數(shù)是類型。對(duì)于整型變量烤宙,byte遍烦,short和int占用的空間是一樣的。但是變成數(shù)組之后就不一樣了躺枕。byte數(shù)組可以節(jié)省空間服猪。
  • arraylength:求數(shù)組長度是專門有一條指令來實(shí)現(xiàn)的。
  • aload:從變量中將數(shù)組引用load出來拐云。一句話就是罢猪,要操作哪個(gè)數(shù)組,先把這個(gè)數(shù)組的引用從變量中讀出來叉瘩。
  • astore:將數(shù)組引用存入變量
  • baload:從byte型數(shù)組中讀值
  • bastore:向byte型數(shù)組中寫值

第0號(hào)是將數(shù)組長度1,從常量池1中讀出來膳帕。
第1號(hào),new一個(gè)長度在棧里薇缅,類型為byte的數(shù)組危彩。
第3號(hào),將生成好的這個(gè)數(shù)組的引用存到變量0中泳桦。
第4號(hào)汤徽,從變量0中,讀取數(shù)組的引用蓬痒。
第5號(hào)泻骤,從棧中數(shù)組的引用的數(shù)組中讀取長度。
第6號(hào)梧奢,返回這個(gè)長度值狱掂。

  public static int testByteArrayLength();
    Code:
       0: iconst_1
       1: newarray       byte
       3: astore_0
       4: aload_0
       5: arraylength
       6: ireturn

Dalvik字節(jié)碼

看了Dalvik字節(jié)碼之后,不得不感嘆亲轨,通過寄存器方式的指令趋惨,可讀性確實(shí)提高了很多。

第1句:常量值1放到v1寄存器中
第2句:new一個(gè)類型為byte惦蚊,長度在v1中的數(shù)組器虾,引用放在v0里。
第3句:讀取v0中所存引用的數(shù)組的長度蹦锋,存到v1寄存器中兆沙。
第4句:返回v1的值。

  16: int com.yunos.xulun.testcppjni2.TestART.testByteArrayLength() (dex_method_idx=16793)
    DEX CODE:
      0x0000: 1211                      | const/4 v1, #+1
      0x0001: 2310 9308                 | new-array v0, v1, byte[] // type@2195
      0x0003: 2101                      | array-length v1, v0
      0x0004: 0f01                      | return v1

OAT生成的代碼

我們終于要開始與Java強(qiáng)相關(guān)的指令正面交鋒了莉掂。因?yàn)橄駈ew-array和array-length這樣的指令是不會(huì)有對(duì)應(yīng)的機(jī)器指令來對(duì)應(yīng)的葛圃,因?yàn)橐鎸?duì)的層次有點(diǎn)高,不是物理機(jī)器這一層所關(guān)注的。這正是JVM與其他的真實(shí)或虛擬的機(jī)器非常不同的一點(diǎn)库正。
幸好曲楚,OAT的實(shí)現(xiàn)中,也將這樣的功能封裝到了各個(gè)過程中褥符,比如new-array龙誊,對(duì)應(yīng)到pAllocArray過程中。

我們來分析一下生成的OAT代碼:

    CODE: (code_offset=0x005030bc size_offset=0x005030b8 size=100)...
      0x005030bc: d1400bf0  sub x16, sp, #0x2000 (8192)
      0x005030c0: b940021f  ldr wzr, [x16]
      suspend point dex PC: 0x0000
      0x005030c4: f81e0fe0  str x0, [sp, #-32]!
      0x005030c8: f9000ffe  str lr, [sp, #24]
      0x005030cc: 79400250  ldrh w16, [tr] (state_and_flags)
      0x005030d0: 35000230  cbnz w16, #+0x44 (addr 0x503114)

前面還是存參數(shù)喷楣,判斷是否要被調(diào)試器suspend這些趟大。
將長度參數(shù)1,先存到棧里抡蛙,sp+16护昧,再轉(zhuǎn)到w1,給pAllocArray做為參數(shù)粗截。另外一個(gè)參數(shù)是類型惋耙,就是byte code里看到的type@2195.

      0x005030d4: 52800030  mov w16, #0x1
      0x005030d8: b90013f0  str w16, [sp, #16]
      0x005030dc: b94013e1  ldr w1, [sp, #16]
      0x005030e0: f94003e2  ldr x2, [sp]
      0x005030e4: 52811260  mov w0, #0x893
      0x005030e8: f940ca5e  ldr lr, [tr, #400] (pAllocArray)
      0x005030ec: d63f03c0  blr lr
      suspend point dex PC: 0x0001

返回值放sp+12,再讀回來,這個(gè)是數(shù)組的引用熊昌。
數(shù)組的結(jié)構(gòu)的下一個(gè)+8的位置存的就是數(shù)組的長度绽榛,這個(gè)就不麻煩再寫條指令的專門實(shí)現(xiàn)了。

      0x005030f0: b9000fe0  str w0, [sp, #12]
      0x005030f4: b9400fe0  ldr w0, [sp, #12]
      0x005030f8: b940001f  ldr wzr, [x0]
      suspend point dex PC: 0x0003
      GC map objects:  v0 ([sp + #12])
      0x005030fc: b9400801  ldr w1, [x0, #8]
      suspend point dex PC: 0x0003
      GC map objects:  v0 ([sp + #12])

長度暫存在sp+16婿屹,再讀出來灭美,放到w0里,最后返回昂利。

      0x00503100: b90013e1  str w1, [sp, #16]
      0x00503104: b94013e0  ldr w0, [sp, #16]
      0x00503108: f9400ffe  ldr lr, [sp, #24]
      0x0050310c: 910083ff  add sp, sp, #0x20 (32)
      0x00503110: d65f03c0  ret
      0x00503114: f9421e5e  ldr lr, [tr, #1080] (pTestSuspend)
      0x00503118: d63f03c0  blr lr
      suspend point dex PC: 0x0000
      0x0050311c: 17ffffee  b #-0x48 (addr 0x5030d4)

更實(shí)際一點(diǎn)的例子

Java源代碼

我們寫一段稍有點(diǎn)意義的數(shù)組訪問的代碼届腐。極簡的例子幫助我們學(xué)會(huì)了最簡單的數(shù)組相關(guān)的指令,但是并沒有讀寫數(shù)組的值蜂奸。這個(gè)例子加入對(duì)數(shù)組的讀寫:

    public static int testArray(){
        int sum = 0;
        int[] iaTest = new int[10];
        iaTest[0]=0;
        iaTest[1]=1;
        for(int i=2;i<iaTest.length;i++){
            iaTest[i]=iaTest[i-1] + iaTest[i-2];
        }
        for(int i=0;i<iaTest.length;i++){
            sum += iaTest[i];
        }
        return sum;
    }

Java字節(jié)碼

newarray, aload, astore和arraylength前面都學(xué)過了犁苏,這里面增加了從整型數(shù)組中讀值的iaload和往整型數(shù)組里寫的iastore。

  public static int testArray();
    Code:
       0: iconst_0
       1: istore_0
       2: bipush        10
       4: newarray       int
       6: astore_1
       7: aload_1
       8: iconst_0
       9: iconst_0
      10: iastore
      11: aload_1
      12: iconst_1
      13: iconst_1
      14: iastore
      15: iconst_2
      16: istore_2
      17: iload_2
      18: aload_1
      19: arraylength
      20: if_icmpge     43
      23: aload_1
      24: iload_2
      25: aload_1
      26: iload_2
      27: iconst_1
      28: isub
      29: iaload
      30: aload_1
      31: iload_2
      32: iconst_2
      33: isub
      34: iaload
      35: iadd
      36: iastore
      37: iinc          2, 1
      40: goto          17
      43: iconst_0
      44: istore_2
      45: iload_2
      46: aload_1
      47: arraylength
      48: if_icmpge     63
      51: iload_0
      52: aload_1
      53: iload_2
      54: iaload
      55: iadd
      56: istore_0
      57: iinc          2, 1
      60: goto          45
      63: iload_0
      64: ireturn

對(duì)應(yīng)的Dalvik代碼

Dalvik代碼中用不到aload和astore扩所,反正引用都在寄存器里围详。它通過aput和aget指令來讀寫數(shù)組。

  15: int com.yunos.xulun.testcppjni2.TestART.testArray() (dex_method_idx=16792)
    DEX CODE:
      0x0000: 1215                      | const/4 v5, #+1
      0x0001: 1204                      | const/4 v4, #+0
      0x0002: 1202                      | const/4 v2, #+0
      0x0003: 1303 0a00                 | const/16 v3, #+10
      0x0005: 2331 9708                 | new-array v1, v3, int[] // type@2199
      0x0007: 4b04 0104                 | aput v4, v1, v4
      0x0009: 4b05 0105                 | aput v5, v1, v5
      0x000b: 1220                      | const/4 v0, #+2
      0x000c: 2113                      | array-length v3, v1
      0x000d: 3530 1000                 | if-ge v0, v3, +16
      0x000f: d803 00ff                 | add-int/lit8 v3, v0, #-1
      0x0011: 4403 0103                 | aget v3, v1, v3
      0x0013: d804 00fe                 | add-int/lit8 v4, v0, #-2
      0x0015: 4404 0104                 | aget v4, v1, v4
      0x0017: b043                      | add-int/2addr v3, v4
      0x0018: 4b03 0100                 | aput v3, v1, v0
      0x001a: d800 0001                 | add-int/lit8 v0, v0, #+1
      0x001c: 28f0                      | goto -16
      0x001d: 1200                      | const/4 v0, #+0
      0x001e: 2113                      | array-length v3, v1
      0x001f: 3530 0800                 | if-ge v0, v3, +8
      0x0021: 4403 0100                 | aget v3, v1, v0
      0x0023: b032                      | add-int/2addr v2, v3
      0x0024: d800 0001                 | add-int/lit8 v0, v0, #+1
      0x0026: 28f8                      | goto -8
      0x0027: 0f02                      | return v2

OAT編譯的代碼

這個(gè)代碼稍有點(diǎn)長祖屏。不過我們現(xiàn)在已經(jīng)有充分的知識(shí)可以看懂了助赞。
我來把每句Dalvik跟OAT代碼對(duì)應(yīng)起來,需要的地方再加兩句講解袁勺。

    CODE: (code_offset=0x00502d9c size_offset=0x00502d98 size=764)...
      0x00502d9c: d1400bf0  sub x16, sp, #0x2000 (8192)
      0x00502da0: b940021f  ldr wzr, [x16]
      suspend point dex PC: 0x0000
      0x00502da4: f81c0fe0  str x0, [sp, #-64]!
      0x00502da8: f9001ffe  str lr, [sp, #56]
      0x00502dac: 79400250  ldrh w16, [tr] (state_and_flags)
      0x00502db0: 350014b0  cbnz w16, #+0x294 (addr 0x503044)

第2個(gè)常量1雹食,存在sp+48里。

      // const/4 v5, #+1

      0x00502db4: 52800030  mov w16, #0x1
      0x00502db8: b90033f0  str w16, [sp, #48]

第1個(gè)常量0期丰,存在sp+44里群叶。這兩個(gè)常量一會(huì)兒賦值的時(shí)候會(huì)用到漠嵌。

      // const/4 v4, #+0

      0x00502dbc: 52800010  mov w16, #0x0
      0x00502dc0: b9002ff0  str w16, [sp, #44]

sum初值的那個(gè)0常量,放在sp+36里盖呼。

      // const/4 v2, #+0

      0x00502dc4: 52800010  mov w16, #0x0
      0x00502dc8: b90027f0  str w16, [sp, #36]

數(shù)組的長度10,存在sp+40里化撕。

      // const/16 v3, #+10
      
      0x00502dcc: 52800150  mov w16, #0xa
      0x00502dd0: b9002bf0  str w16, [sp, #40]

把放存進(jìn)去的長度10再讀出來几晤,類型type@2199傳給w0,調(diào)pAllocArray去分配數(shù)組空間植阴。

      // new-array v1, v3, int[] // type@2199
      
      0x00502dd4: b9402be1  ldr w1, [sp, #40]
      0x00502dd8: f94003e2  ldr x2, [sp]
      0x00502ddc: 528112e0  mov w0, #0x897
      0x00502de0: f940ca5e  ldr lr, [tr, #400] (pAllocArray)
      0x00502de4: d63f03c0  blr lr
      suspend point dex PC: 0x0005

返回的數(shù)組引用在w0中蟹瘾,先暫存到sp+32。這時(shí)[x0]指向的就是數(shù)組了掠手。
下一步做

iaTest[0]=0;

下面開始給數(shù)組的0下標(biāo)位置賦0.

      // aput v4, v1, v4

      0x00502de8: b90023e0  str w0, [sp, #32]
      0x00502dec: b94023e0  ldr w0, [sp, #32]
      0x00502df0: b940001f  ldr wzr, [x0]
      suspend point dex PC: 0x0007
      GC map objects:  v1 ([sp + #32])

將數(shù)組引用存到sp+24中憾朴,先加載回來。
然后喷鸽,讀數(shù)組的長度众雷,數(shù)組的第8個(gè)字節(jié)開始的4個(gè)字節(jié)。
長度放到sp+20中做祝。

      0x00502df4: b9001be0  str w0, [sp, #24]
      0x00502df8: b9401be0  ldr w0, [sp, #24]
      0x00502dfc: b9400801  ldr w1, [x0, #8]
      0x00502e00: b90017e1  str w1, [sp, #20]

sp+44的值讀回來砾省,往上翻翻,還記得嗎混槐,這就是那個(gè)0.
然后编兄,體現(xiàn)Java的優(yōu)越性的地方又出來了,它會(huì)做越界檢查声登。如果越界了狠鸳,就跳到后面去調(diào)用pThrowArrayBounds去拋越界異常。

      0x00502e04: b9402fe0  ldr w0, [sp, #44]
      0x00502e08: b94017e1  ldr w1, [sp, #20]
      0x00502e0c: 6b01001f  cmp w0, w1
      0x00502e10: 54001202  b.hs #+0x240 (addr 0x503050)

w0的值悯嗓,就是sp+44里面的那個(gè)0件舵,暫存到sp+16里。
sp+24讀到w0中绅作,往前找找吧芦圾,這個(gè)是數(shù)組的引用。
w1讀取sp+16俄认,剛存的那個(gè)1个少。
w2是sp+44,還是0眯杏。
add語句是根據(jù)下標(biāo)計(jì)算應(yīng)該存到數(shù)組的什么位置里夜焦。
w2的值1,存到add算出來的坐標(biāo)再加上數(shù)組的頭12字節(jié)(比如數(shù)組長度就在頭里)里面岂贩。

      0x00502e14: b90013e0  str w0, [sp, #16]
      0x00502e18: b9401be0  ldr w0, [sp, #24]
      0x00502e1c: b94013e1  ldr w1, [sp, #16]
      0x00502e20: b9402fe2  ldr w2, [sp, #44]
      0x00502e24: 0b010810  add w16, w0, w1, lsl #2
      0x00502e28: b9000e02  str w2, [x16, #12]
      0x00502e2c: b94023e0  ldr w0, [sp, #32]
      0x00502e30: b940001f  ldr wzr, [x0]
      suspend point dex PC: 0x0009
      GC map objects:  v1 ([sp + #32])

sp+24還是數(shù)組的引用茫经。
w1存數(shù)組長度,sp+48是那個(gè)常量1.
然后是判數(shù)組越界等等,跟上面跟下標(biāo)0賦0一樣卸伞,這個(gè)是將數(shù)組下標(biāo)1的值賦1:

iaTest[1]=1;

跟上面一樣抹镊,大家應(yīng)該比較熟悉了。

      // aput v5, v1, v5

      0x00502e34: b9001be0  str w0, [sp, #24]
      0x00502e38: b9401be0  ldr w0, [sp, #24]
      0x00502e3c: b9400801  ldr w1, [x0, #8]
      0x00502e40: b90017e1  str w1, [sp, #20]
      0x00502e44: b94033e0  ldr w0, [sp, #48]
      0x00502e48: b94017e1  ldr w1, [sp, #20]
      0x00502e4c: 6b01001f  cmp w0, w1
      0x00502e50: 54001042  b.hs #+0x208 (addr 0x503058)
      0x00502e54: b90013e0  str w0, [sp, #16]
      0x00502e58: b9401be0  ldr w0, [sp, #24]
      0x00502e5c: b94013e1  ldr w1, [sp, #16]
      0x00502e60: b94033e2  ldr w2, [sp, #48]
      0x00502e64: 0b010810  add w16, w0, w1, lsl #2
      0x00502e68: b9000e02  str w2, [x16, #12]

后面開始第一個(gè)for循環(huán):

        for(int i=2;i<iaTest.length;i++){
            iaTest[i]=iaTest[i-1] + iaTest[i-2];
        }

下面是將常量值2荤傲,存到sp+28中垮耳。
sp+32,也就是v1遂黍,存的是數(shù)組引用终佛。

      // const/4 v0, #+2
      
      0x00502e6c: 52800050  mov w16, #0x2
      0x00502e70: b9001ff0  str w16, [sp, #28]
      0x00502e74: b94023e0  ldr w0, [sp, #32]
      0x00502e78: b940001f  ldr wzr, [x0]
      suspend point dex PC: 0x000c
      GC map objects:  v1 ([sp + #32])

[x0+8]這是標(biāo)準(zhǔn)的array-length指令的翻譯。

      // array-length v3, v1
      
      0x00502e7c: b9400801  ldr w1, [x0, #8]
      suspend point dex PC: 0x000c
      GC map objects:  v1 ([sp + #32])

數(shù)組長度這個(gè)值雾家,存到sp+40中铃彰。
sp+28是循環(huán)控制變量。
然后二者做比較芯咧,如果大于等于就跳轉(zhuǎn)到0x502f9c牙捉,這個(gè)循環(huán)結(jié)束。

      // if-ge v0, v3, +16
      
      0x00502e80: b9002be1  str w1, [sp, #40]
      0x00502e84: b9401fe0  ldr w0, [sp, #28]
      0x00502e88: b9402be1  ldr w1, [sp, #40]
      0x00502e8c: 6b01001f  cmp w0, w1
      0x00502e90: 1a9fb7e2  cset w2, ge
      0x00502e94: 2a0203e0  mov w0, w2
      0x00502e98: 35000820  cbnz w0, #+0x104 (addr 0x502f9c)
      
      // add-int/lit8 v3, v0, #-1
      
      0x00502e9c: b9401fe0  ldr w0, [sp, #28]
      0x00502ea0: 51000401  sub w1, w0, #0x1 (1)
      0x00502ea4: b9002be1  str w1, [sp, #40]
      0x00502ea8: b94023e0  ldr w0, [sp, #32]
      0x00502eac: b940001f  ldr wzr, [x0]
      suspend point dex PC: 0x0011
      GC map objects:  v1 ([sp + #32])

      // aget v3, v1, v3
      
      0x00502eb0: b9001be0  str w0, [sp, #24]
      0x00502eb4: b9401be0  ldr w0, [sp, #24]
      0x00502eb8: b9400801  ldr w1, [x0, #8]
      0x00502ebc: b90017e1  str w1, [sp, #20]
      0x00502ec0: b9402be0  ldr w0, [sp, #40]
      0x00502ec4: b94017e1  ldr w1, [sp, #20]
      0x00502ec8: 6b01001f  cmp w0, w1
      0x00502ecc: 54000ca2  b.hs #+0x194 (addr 0x503060)
      0x00502ed0: b90013e0  str w0, [sp, #16]
      0x00502ed4: b9401be0  ldr w0, [sp, #24]
      0x00502ed8: b94013e1  ldr w1, [sp, #16]
      0x00502edc: 0b010810  add w16, w0, w1, lsl #2
      0x00502ee0: b9400e02  ldr w2, [x16, #12]
      0x00502ee4: b9002be2  str w2, [sp, #40]
      
      // add-int/lit8 v4, v0, #-2
      
      0x00502ee8: b9401fe0  ldr w0, [sp, #28]
      0x00502eec: 51000801  sub w1, w0, #0x2 (2)
      0x00502ef0: b9002fe1  str w1, [sp, #44]
      0x00502ef4: b94023e0  ldr w0, [sp, #32]
      0x00502ef8: b940001f  ldr wzr, [x0]
      suspend point dex PC: 0x0015
      GC map objects:  v1 ([sp + #32])
      
      // aget v4, v1, v4
      
      0x00502efc: b9001be0  str w0, [sp, #24]
      0x00502f00: b9401be0  ldr w0, [sp, #24]
      0x00502f04: b9400801  ldr w1, [x0, #8]
      0x00502f08: b90017e1  str w1, [sp, #20]
      0x00502f0c: b9402fe0  ldr w0, [sp, #44]
      0x00502f10: b94017e1  ldr w1, [sp, #20]
      0x00502f14: 6b01001f  cmp w0, w1
      0x00502f18: 54000a82  b.hs #+0x150 (addr 0x503068)
      0x00502f1c: b90013e0  str w0, [sp, #16]
      0x00502f20: b9401be0  ldr w0, [sp, #24]
      0x00502f24: b94013e1  ldr w1, [sp, #16]
      0x00502f28: 0b010810  add w16, w0, w1, lsl #2
      0x00502f2c: b9400e02  ldr w2, [x16, #12]
      0x00502f30: b9002fe2  str w2, [sp, #44]
      
      // add-int/2addr v3, v4
      
      0x00502f34: b9402be0  ldr w0, [sp, #40]
      0x00502f38: b9402fe1  ldr w1, [sp, #44]
      0x00502f3c: 0b010002  add w2, w0, w1
      0x00502f40: b9002be2  str w2, [sp, #40]
      0x00502f44: b94023e0  ldr w0, [sp, #32]
      0x00502f48: b940001f  ldr wzr, [x0]
      suspend point dex PC: 0x0018
      GC map objects:  v1 ([sp + #32])
      
      // aput v3, v1, v0
      
      0x00502f4c: b9001be0  str w0, [sp, #24]
      0x00502f50: b9401be0  ldr w0, [sp, #24]
      0x00502f54: b9400801  ldr w1, [x0, #8]
      0x00502f58: b90017e1  str w1, [sp, #20]
      0x00502f5c: b9401fe0  ldr w0, [sp, #28]
      0x00502f60: b94017e1  ldr w1, [sp, #20]
      0x00502f64: 6b01001f  cmp w0, w1
      0x00502f68: 54000842  b.hs #+0x108 (addr 0x503070)
      0x00502f6c: b90013e0  str w0, [sp, #16]
      0x00502f70: b9401be0  ldr w0, [sp, #24]
      0x00502f74: b94013e1  ldr w1, [sp, #16]
      0x00502f78: b9402be2  ldr w2, [sp, #40]
      0x00502f7c: 0b010810  add w16, w0, w1, lsl #2
      0x00502f80: b9000e02  str w2, [x16, #12]
      
      // add-int/lit8 v0, v0, #+1
      
      0x00502f84: b9401fe0  ldr w0, [sp, #28]
      0x00502f88: 11000401  add w1, w0, #0x1 (1)
      0x00502f8c: b9001fe1  str w1, [sp, #28]

      // goto -16
      
      0x00502f90: 79400250  ldrh w16, [tr] (state_and_flags)
      0x00502f94: 35000730  cbnz w16, #+0xe4 (addr 0x503078)
      0x00502f98: 17ffffb7  b #-0x124 (addr 0x502e74)

第一個(gè)循環(huán)結(jié)束敬飒,下面是第二個(gè)循環(huán):

        for(int i=0;i<iaTest.length;i++){
            sum += iaTest[i];
        }

這個(gè)循環(huán)的計(jì)算量少了鹃共,就是純遍歷,應(yīng)該更容易理解一些驶拱。
循環(huán)控制變量賦初值0:

      // const/4 v0, #+0

      0x00502f9c: 52800010  mov w16, #0x0
      0x00502fa0: b9001ff0  str w16, [sp, #28]
      0x00502fa4: b94023e0  ldr w0, [sp, #32]
      0x00502fa8: b940001f  ldr wzr, [x0]
      suspend point dex PC: 0x001e
      GC map objects:  v1 ([sp + #32])

循環(huán)結(jié)束的值霜浴,取數(shù)組長度。

      // array-length v3, v1

      0x00502fac: b9400801  ldr w1, [x0, #8]
      suspend point dex PC: 0x001e
      GC map objects:  v1 ([sp + #32])

如果sp+28(v0)的值大于等于sp+40(v3)蓝纲,循環(huán)就結(jié)束了阴孟,跳轉(zhuǎn)到0x503034,return那一段税迷。

      // if-ge v0, v3, +8
      
      0x00502fb0: b9002be1  str w1, [sp, #40]
      0x00502fb4: b9401fe0  ldr w0, [sp, #28]
      0x00502fb8: b9402be1  ldr w1, [sp, #40]
      0x00502fbc: 6b01001f  cmp w0, w1
      0x00502fc0: 1a9fb7e2  cset w2, ge
      0x00502fc4: 2a0203e0  mov w0, w2
      0x00502fc8: 35000360  cbnz w0, #+0x6c (addr 0x503034)
      0x00502fcc: b94023e0  ldr w0, [sp, #32]
      0x00502fd0: b940001f  ldr wzr, [x0]
      suspend point dex PC: 0x0021
      GC map objects:  v1 ([sp + #32])

如果循環(huán)可以繼續(xù)永丝,則將數(shù)組的值讀出來。sp+28(v0)是當(dāng)前索引值箭养,sp+32(v1)是數(shù)組的引用慕嚷,結(jié)果放到sp+40(v3)中。還是有數(shù)組越界的判斷毕泌。

      // aget v3, v1, v0

      0x00502fd4: b9001be0  str w0, [sp, #24]
      0x00502fd8: b9401be0  ldr w0, [sp, #24]
      0x00502fdc: b9400801  ldr w1, [x0, #8]
      0x00502fe0: b90017e1  str w1, [sp, #20]
      0x00502fe4: b9401fe0  ldr w0, [sp, #28]
      0x00502fe8: b94017e1  ldr w1, [sp, #20]
      0x00502fec: 6b01001f  cmp w0, w1
      0x00502ff0: 540004a2  b.hs #+0x94 (addr 0x503084)
      0x00502ff4: b90013e0  str w0, [sp, #16]
      0x00502ff8: b9401be0  ldr w0, [sp, #24]
      0x00502ffc: b94013e1  ldr w1, [sp, #16]
      0x00503000: 0b010810  add w16, w0, w1, lsl #2
      0x00503004: b9400e02  ldr w2, [x16, #12]
      0x00503008: b9002be2  str w2, [sp, #40]

下面一條指令是算求和喝检,v2=v2+v3。sp+36是v2撼泛,sp+40是v3.

      // add-int/2addr v2, v3
      
      0x0050300c: b94027e0  ldr w0, [sp, #36]
      0x00503010: b9402be1  ldr w1, [sp, #40]
      0x00503014: 0b010002  add w2, w0, w1
      0x00503018: b90027e2  str w2, [sp, #36]

循環(huán)控制變量sp+28(v0)加1

      // add-int/lit8 v0, v0, #+1

      0x0050301c: b9401fe0  ldr w0, [sp, #28]
      0x00503020: 11000401  add w1, w0, #0x1 (1)
      0x00503024: b9001fe1  str w1, [sp, #28]
      0x00503028: 79400250  ldrh w16, [tr] (state_and_flags)
      0x0050302c: 35000310  cbnz w16, #+0x60 (addr 0x50308c)

      // goto -8

      0x00503030: 17ffffdd  b #-0x8c (addr 0x502fa4)

      // return v2

      0x00503034: b94027e0  ldr w0, [sp, #36]
      0x00503038: f9401ffe  ldr lr, [sp, #56]
      0x0050303c: 910103ff  add sp, sp, #0x40 (64)
      0x00503040: d65f03c0  ret

后面是一系列的判斷suspend和拋數(shù)組越界異常的地方挠说。

      0x00503044: f9421e5e  ldr lr, [tr, #1080] (pTestSuspend)
      0x00503048: d63f03c0  blr lr
      suspend point dex PC: 0x0000
      0x0050304c: 17ffff5a  b #-0x298 (addr 0x502db4)
      0x00503050: f942265e  ldr lr, [tr, #1096] (pThrowArrayBounds)
      0x00503054: d63f03c0  blr lr
      suspend point dex PC: 0x0007
      GC map objects:  v1 ([sp + #32])
      0x00503058: f942265e  ldr lr, [tr, #1096] (pThrowArrayBounds)
      0x0050305c: d63f03c0  blr lr
      suspend point dex PC: 0x0009
      GC map objects:  v1 ([sp + #32])
      0x00503060: f942265e  ldr lr, [tr, #1096] (pThrowArrayBounds)
      0x00503064: d63f03c0  blr lr
      suspend point dex PC: 0x0011
      GC map objects:  v1 ([sp + #32])
      0x00503068: f942265e  ldr lr, [tr, #1096] (pThrowArrayBounds)
      0x0050306c: d63f03c0  blr lr
      suspend point dex PC: 0x0015
      GC map objects:  v1 ([sp + #32])
      0x00503070: f942265e  ldr lr, [tr, #1096] (pThrowArrayBounds)
      0x00503074: d63f03c0  blr lr
      suspend point dex PC: 0x0018
      GC map objects:  v1 ([sp + #32])
      0x00503078: f9421e5e  ldr lr, [tr, #1080] (pTestSuspend)
      0x0050307c: d63f03c0  blr lr
      suspend point dex PC: 0x001c
      GC map objects:  v1 ([sp + #32])
      0x00503080: 17ffffc6  b #-0xe8 (addr 0x502f98)
      0x00503084: f942265e  ldr lr, [tr, #1096] (pThrowArrayBounds)
      0x00503088: d63f03c0  blr lr
      suspend point dex PC: 0x0021
      GC map objects:  v1 ([sp + #32])
      0x0050308c: f9421e5e  ldr lr, [tr, #1080] (pTestSuspend)
      0x00503090: d63f03c0  blr lr
      suspend point dex PC: 0x0026
      GC map objects:  v1 ([sp + #32])
      0x00503094: 17ffffe7  b #-0x64 (addr 0x503030)

C++的數(shù)組實(shí)現(xiàn)

C++源代碼

int testArray() {
    const int ARRAY_SIZE = 10;
    int sum = 0;
    int *iaTest = new int[ARRAY_SIZE];
    iaTest[0] = 0;
    iaTest[1] = 1;
    for (int i = 2; i < ARRAY_SIZE; i++) {
        iaTest[i] = iaTest[i - 1] + iaTest[i - 2];
    }
    for (int i = 0; i < ARRAY_SIZE; i++) {
        sum += iaTest[i];
    }
    return sum;
}

C++編譯生成的代碼-arm64 v8a

可以看到,這段代碼愿题,編譯器已經(jīng)將循環(huán)優(yōu)化展開了损俭。

0000000000000bf0 <_Z9testArrayv>:
 bf0:   a9bf7bfd    stp x29, x30, [sp,#-16]!
 bf4:   d2800500    mov x0, #0x28                   // #40
 bf8:   910003fd    mov x29, sp
 bfc:   97ffff15    bl  850 <_Znam@plt>
 c00:   b900001f    str wzr, [x0]
 c04:   52800022    mov w2, #0x1                    // #1
 c08:   b9000402    str w2, [x0,#4]
 c0c:   b9000802    str w2, [x0,#8]
 c10:   52800042    mov w2, #0x2                    // #2
 c14:   b9000c02    str w2, [x0,#12]
 c18:   52800062    mov w2, #0x3                    // #3
 c1c:   b9001002    str w2, [x0,#16]
 c20:   528000a2    mov w2, #0x5                    // #5
 c24:   b9001402    str w2, [x0,#20]
 c28:   52800102    mov w2, #0x8                    // #8
 c2c:   b9001802    str w2, [x0,#24]
 c30:   528001a2    mov w2, #0xd                    // #13
 c34:   b9001c02    str w2, [x0,#28]
 c38:   528002a2    mov w2, #0x15                   // #21
 c3c:   aa0003e1    mov x1, x0
 c40:   b9002002    str w2, [x0,#32]
 c44:   52800442    mov w2, #0x22                   // #34
 c48:   b9002402    str w2, [x0,#36]
 c4c:   3cc10420    ldr q0, [x1],#16
 c50:   a8c17bfd    ldp x29, x30, [sp],#16
 c54:   3dc00021    ldr q1, [x1]
 c58:   4ea18400    add v0.4s, v0.4s, v1.4s
 c5c:   4eb1b800    addv    s0, v0.4s
 c60:   0e043c00    mov w0, v0.s[0]
 c64:   1100dc00    add w0, w0, #0x37
 c68:   d65f03c0    ret

x86_64的C++編譯生成代碼

CISC指令的優(yōu)勢終于發(fā)揮出來了蛙奖,arm64的指令都是32位的,4個(gè)字節(jié)杆兵。但是x86_64的指令可以是7個(gè)字節(jié)雁仲。
ARM只能通過str指令訪問內(nèi)存,x86可沒有這種限制琐脏。

0000000000000dc0 <_Z9testArrayv>:
 dc0:   48 8d 64 24 f8          lea    -0x8(%rsp),%rsp
 dc5:   bf 28 00 00 00          mov    $0x28,%edi
 dca:   e8 11 fa ff ff          callq  7e0 <_Znam@plt>
 dcf:   c7 00 00 00 00 00       movl   $0x0,(%rax)
 dd5:   c7 40 04 01 00 00 00    movl   $0x1,0x4(%rax)
 ddc:   c7 40 08 01 00 00 00    movl   $0x1,0x8(%rax)
 de3:   c7 40 0c 02 00 00 00    movl   $0x2,0xc(%rax)
 dea:   c7 40 10 03 00 00 00    movl   $0x3,0x10(%rax)
 df1:   c7 40 14 05 00 00 00    movl   $0x5,0x14(%rax)
 df8:   c7 40 18 08 00 00 00    movl   $0x8,0x18(%rax)
 dff:   c7 40 1c 0d 00 00 00    movl   $0xd,0x1c(%rax)
 e06:   c7 40 20 15 00 00 00    movl   $0x15,0x20(%rax)
 e0d:   c7 40 24 22 00 00 00    movl   $0x22,0x24(%rax)
 e14:   b8 58 00 00 00          mov    $0x58,%eax
 e19:   48 8d 64 24 08          lea    0x8(%rsp),%rsp
 e1e:   c3                      retq   
 e1f:   90                      nop

mips64的C++生成代碼

MIPS也是RISC伯顶,只能通過li,ld這樣的load指令讀內(nèi)存,sw這樣的指令寫內(nèi)存骆膝。所以也沒有x86那樣短。

0000000000000e50 <_Z9testArrayv>:
 e50:   67bdfff0    daddiu  sp,sp,-16
 e54:   ffbc0000    sd  gp,0(sp)
 e58:   3c1c0002    lui gp,0x2
 e5c:   0399e02d    daddu   gp,gp,t9
 e60:   ffbf0008    sd  ra,8(sp)
 e64:   679c91b0    daddiu  gp,gp,-28240
 e68:   df998040    ld  t9,-32704(gp)
 e6c:   0320f809    jalr    t9
 e70:   24040028    li  a0,40
 e74:   dfbf0008    ld  ra,8(sp)
 e78:   ac400000    sw  zero,0(v0)
 e7c:   0040182d    move    v1,v0
 e80:   24040001    li  a0,1
 e84:   24020001    li  v0,1
 e88:   24050002    li  a1,2
 e8c:   24060003    li  a2,3
 e90:   24070005    li  a3,5
 e94:   24080008    li  a4,8
 e98:   2409000d    li  a5,13
 e9c:   240a0015    li  a6,21
 ea0:   240b0022    li  a7,34
 ea4:   ac620004    sw  v0,4(v1)
 ea8:   dfbc0000    ld  gp,0(sp)
 eac:   24020058    li  v0,88
 eb0:   ac640008    sw  a0,8(v1)
 eb4:   ac65000c    sw  a1,12(v1)
 eb8:   ac660010    sw  a2,16(v1)
 ebc:   ac670014    sw  a3,20(v1)
 ec0:   ac680018    sw  a4,24(v1)
 ec4:   ac69001c    sw  a5,28(v1)
 ec8:   ac6a0020    sw  a6,32(v1)
 ecc:   ac6b0024    sw  a7,36(v1)
 ed0:   03e00009    jr  ra
 ed4:   67bd0010    daddiu  sp,sp,16

結(jié)論

雖然優(yōu)化得不如C++好灶体,但是數(shù)組指令還是比較清晰的阅签。Java自帶越界檢查,還是節(jié)省了不少編程的力氣的蝎抽。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末政钟,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子樟结,更是在濱河造成了極大的恐慌养交,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瓢宦,死亡現(xiàn)場離奇詭異碎连,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)驮履,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門鱼辙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人玫镐,你說我怎么就攤上這事倒戏。” “怎么了恐似?”我有些...
    開封第一講書人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵杜跷,是天一觀的道長。 經(jīng)常有香客問我矫夷,道長葛闷,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任双藕,我火速辦了婚禮孵运,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蔓彩。我一直安慰自己治笨,他們只是感情好驳概,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著旷赖,像睡著了一般顺又。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上等孵,一...
    開封第一講書人閱讀 50,084評(píng)論 1 291
  • 那天稚照,我揣著相機(jī)與錄音,去河邊找鬼俯萌。 笑死果录,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的咐熙。 我是一名探鬼主播弱恒,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼棋恼!你這毒婦竟也來了返弹?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤爪飘,失蹤者是張志新(化名)和其女友劉穎义起,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體师崎,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡默终,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了犁罩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片穷蛹。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖昼汗,靈堂內(nèi)的尸體忽然破棺而出肴熏,到底是詐尸還是另有隱情,我是刑警寧澤顷窒,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布蛙吏,位于F島的核電站,受9級(jí)特大地震影響鞋吉,放射性物質(zhì)發(fā)生泄漏鸦做。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一谓着、第九天 我趴在偏房一處隱蔽的房頂上張望泼诱。 院中可真熱鬧,春花似錦赊锚、人聲如沸治筒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽耸袜。三九已至友多,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間堤框,已是汗流浹背域滥。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蜈抓,地道東北人启绰。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像沟使,于是被迫代替她去往敵國和親委可。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理格带,服務(wù)發(fā)現(xiàn),斷路器刹枉,智...
    卡卡羅2017閱讀 134,638評(píng)論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法叽唱,類相關(guān)的語法,內(nèi)部類的語法微宝,繼承相關(guān)的語法棺亭,異常的語法,線程的語...
    子非魚_t_閱讀 31,603評(píng)論 18 399
  • 還記得你收到的情書嗎蟋软? 山有樹木陪伴 樹木有其枝干相依 還記得曾經(jīng)有封沒有任何回應(yīng)的情書嗎镶摘? ...
    不愿回家的呱呱呱閱讀 272評(píng)論 0 0
  • Eclipse的概述 Eclipse是一種可擴(kuò)展的開放源代碼的IDE。 Eclipse的特點(diǎn)描述免費(fèi)純Java語言...
    lutianfei閱讀 443評(píng)論 0 1
  • 我若可以放心岳守,那便真如晴開云散凄敢,而不在想念。若能斷了此念想湿痢,我也不必再為任何事涝缝,任何人而煩憂。心里牽掛的人譬重,有時(shí)在...
    隨筆日記官網(wǎng)閱讀 293評(píng)論 0 0