ART世界探險(xiǎn)(2) - 從java byte code說(shuō)起

ART世界探險(xiǎn)(2) - 從java byte code說(shuō)起

Dalvik時(shí)代,如果不做JIT的話,只需要了解java字節(jié)碼和Dalivk的字節(jié)碼就夠了。但是,到了ART時(shí)代,我們可能還要至少學(xué)習(xí)兩種新東西:一個(gè)是編譯后端的IR中間代碼。比如,我們假如使用LLVM做為編譯后端的話,需要做從dex到LLVM IR的轉(zhuǎn)換工作。這個(gè)IR可能還不只一層,比如分中層的MIR和底層的LIR。
最后,我們還得了解機(jī)器指令。僅就ARM來(lái)說(shuō),現(xiàn)在是64位時(shí)代了,我們需要了解的就是AArch64和AArch32兩種狀態(tài)下的A64,A32,Thumb2和Thumb四種指令集,還有NEON指令擴(kuò)展等。

Java字節(jié)碼指令集一覽

我們先看一下Android提供的Java指令集簡(jiǎn)表,在這個(gè)網(wǎng)址可以看到:http://androidxref.com/6.0.1_r10/xref/dalvik/docs/java-bytecode.html

一共200條指令。不過(guò)大家千萬(wàn)別被這么多指令嚇到啊,相對(duì)來(lái)說(shuō)絕大部分指令還是非常簡(jiǎn)單的。我們用一講的時(shí)間就可以講個(gè)大概,細(xì)節(jié)將來(lái)遇到再說(shuō)。倒是后面我們講ARM指令的時(shí)候篇幅搞不好要長(zhǎng)一點(diǎn)。

字節(jié)碼 10進(jìn)制值(相當(dāng)于序號(hào)) 助記符
0x00 0 nop
0x01 1 aconst_null
0x02 2 iconst_m1
0x03 3 iconst_0
0x04 4 iconst_1
0x05 5 iconst_2
0x06 6 iconst_3
0x07 7 iconst_4
0x08 8 iconst_5
0x09 9 lconst_0
0x0a 10 lconst_1
0x0b 11 fconst_0
0x0c 12 fconst_1
0x0d 13 fconst_2
0x0e 14 dconst_0
0x0f 15 dconst_1
0x10 16 bipush
0x11 17 sipush
0x12 18 ldc
0x13 19 ldc_w
0x14 20 ldc2_w
0x15 21 iload
0x16 22 lload
0x17 23 fload
0x18 24 dload
0x19 25 aload
0x1a 26 iload_0
0x1b 27 iload_1
0x1c 28 iload_2
0x1d 29 iload_3
0x1e 30 lload_0
0x1f 31 lload_1
0x20 32 lload_2
0x21 33 lload_3
0x22 34 fload_0
0x23 35 fload_1
0x24 36 fload_2
0x25 37 fload_3
0x26 38 dload_0
0x27 39 dload_1
0x28 40 dload_2
0x29 41 dload_3
0x2a 42 aload_0
0x2b 43 aload_1
0x2c 44 aload_2
0x2d 45 aload_3
0x2e 46 iaload
0x2f 47 laload
0x30 48 faload
0x31 49 daload
0x32 50 aaload
0x33 51 baload
0x34 52 caload
0x35 53 saload
0x36 54 istore
0x37 55 lstore
0x38 56 fstore
0x39 57 dstore
0x3a 58 astore
0x3b 59 istore_0
0x3c 60 istore_1
0x3d 61 istore_2
0x3e 62 istore_3
0x3f 63 lstore_0
0x40 64 lstore_1
0x41 65 lstore_2
0x42 66 lstore_3
0x43 67 fstore_0
0x44 68 fstore_1
0x45 69 fstore_2
0x46 70 fstore_3
0x47 71 dstore_0
0x48 72 dstore_1
0x49 73 dstore_2
0x4a 74 dstore_3
0x4b 75 astore_0
0x4c 76 astore_1
0x4d 77 astore_2
0x4e 78 astore_3
0x4f 79 iastore
0x50 80 lastore
0x51 81 fastore
0x52 82 dastore
0x53 83 aastore
0x54 84 bastore
0x55 85 castore
0x56 86 sastore
0x57 87 pop
0x58 88 pop2
0x59 89 dup
0x5a 90 dup_x1
0x5b 91 dup_x2
0x5c 92 dup2
0x5d 93 dup2_x1
0x5e 94 dup2_x2
0x5f 95 swap
0x60 96 iadd
0x61 97 ladd
0x62 98 fadd
0x63 99 dadd
0x64 100 isub
0x65 101 lsub
0x66 102 fsub
0x67 103 dsub
0x68 104 imul
0x69 105 lmul
0x6a 106 fmul
0x6b 107 dmul
0x6c 108 idiv
0x6d 109 ldiv
0x6e 110 fdiv
0x6f 111 ddiv
0x70 112 irem
0x71 113 lrem
0x72 114 frem
0x73 115 drem
0x74 116 ineg
0x75 117 lneg
0x76 118 fneg
0x77 119 dneg
0x78 120 ishl
0x79 121 lshl
0x7a 122 ishr
0x7b 123 lshr
0x7c 124 iushr
0x7d 125 lushr
0x7e 126 iand
0x7f 127 land
0x80 128 ior
0x81 129 |lor
0x82 130 ixor
0x83 131 lxor
0x84 132 iinc
0x85 133 i2l
0x86 134 i2f
0x87 135 i2d
0x88 136 l2i
0x89 137 l2f
0x8a 138 l2d
0x8b 139 f2i
0x8c 140 f2l
0x8d 141 f2d
0x8e 142 d2i
0x8f 143 d2l
0x90 144 d2f
0x91 145 i2b
0x92 146 i2c
0x93 147 i2s
0x94 148 lcmp
0x95 149 fcmpl
0x96 150 fcmpg
0x97 151 dcmpl
0x98 152 dcmpg
0x99 153 ifeq
0x9a 154 ifne
0x9b 155 iflt
0x9c 156 ifge
0x9d 157 ifgt
0x9e 158 ifle
0x9f 159 if_icmpeq
0xa0 160 if_icmpne
0xa1 161 if_icmplt
0xa2 162 if_icmpge
0xa3 163 if_icmpgt
0xa4 164 if_icmple
0xa5 165 if_acmpeq
0xa6 166 if_acmpne
0xa7 167 goto
0xa8 168 jsr
0xa9 169 ret
0xaa 170 tableswitch
0xab 171 lookupswitch
0xac 172 ireturn
0xad 173 lreturn
0xae 174 freturn
0xaf 175 dreturn
0xb0 176 areturn
0xb1 177 return
0xb2 178 getstatic
0xb3 179 putstatic
0xb4 180 getfield
0xb5 181 putfield
0xb6 182 invokevirtual
0xb7 183 invokespecial
0xb8 184 invokestatic
0xb9 185 invokeinterface
0xba 186 (unused)
0xbb 187 new
0xbc 188 newarray
0xbd 189 anewarray
0xbe 190 arraylength
0xbf 191 athrow
0xc0 192 checkcast
0xc1 193 instanceof
0xc2 194 monitorenter
0xc3 195 monitorexit
0xc4 196 wide
0xc5 197 multianewarray
0xc6 198 ifnull
0xc7 199 ifnonnull
0xc8 200 goto_w
0xc9 201 jsr_w

反匯編,學(xué)指令

如果一個(gè)一個(gè)指令地講下來(lái),估計(jì)大家都睡著了。所以我們都過(guò)反匯編我們寫的代碼的方式來(lái)學(xué)習(xí),學(xué)得差不多了,我們?cè)侔阎噶畲幌隆R磺幸詫?shí)用為先,我們嘗試一下吧。

首先我們還是以上一講的empty3例子說(shuō)起。
我們首先用javap工具反匯編一下BuildConfig那個(gè)類:

javap -c com.yunos.system.empty3.BuildConfig

反匯編出來(lái)的代碼如下:

Compiled from "BuildConfig.java"
public final class com.yunos.system.empty3.BuildConfig {
  public static final boolean DEBUG;

  public static final java.lang.String APPLICATION_ID = "com.yunos.system.empty3";

  public static final java.lang.String BUILD_TYPE = "debug";

  public static final java.lang.String FLAVOR = "";

  public static final int VERSION_CODE = 1;

  public static final java.lang.String VERSION_NAME = "1.0";

  public com.yunos.system.empty3.BuildConfig();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
    LineNumberTable:
      line 6: 0
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0       5     0  this   Lcom/yunos/system/empty3/BuildConfig;

  static {};
    Code:
       0: ldc           #2                  // String true
       2: invokestatic  #3                  // Method java/lang/Boolean.parseBoolean:(Ljava/lang/String;)Z
       5: putstatic     #4                  // Field DEBUG:Z
       8: return
    LineNumberTable:
      line 7: 0
}

BuildConfig構(gòu)造方法

我們先看BuildConfig構(gòu)造這段:

       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

第一條是aload_<n>指令,這個(gè)家族共有4條指令,aload_0, aload_1, aload_2, aload_3.指令代碼從0x2a到0x2d。
這條指令的含義是:從局部變量中,取第n個(gè)的值。取第0個(gè)就是aload_0,第1個(gè)是aload_1。
如果取第4個(gè)怎么辦?這時(shí)候一個(gè)字節(jié)的指令不夠用了,另有一個(gè)aload指令,后面接一個(gè)字節(jié)提供數(shù)字。
相當(dāng)于,aload_0是aload 0指令的簡(jiǎn)寫,但是aload 0占兩個(gè)字節(jié),而aload_0占一個(gè)字節(jié)。

第二條指令是invokespecial,調(diào)用對(duì)象實(shí)例的方法,尤其是調(diào)用super方法,私有方法和構(gòu)造方法。因?yàn)檫@里是要調(diào)用父類的初始化方法,所以正好該是invokespecial.

第三條是return,不帶值返回。

一共就這3條指令,還是挺好理解的吧?

BuildConfig的靜態(tài)部分

學(xué)習(xí)了3條指令的,我們?cè)賹W(xué)一個(gè)4條指令的:

  static {};
    Code:
       0: ldc           #2                  // String true
       2: invokestatic  #3                  // Method java/lang/Boolean.parseBoolean:(Ljava/lang/String;)Z
       5: putstatic     #4                  // Field DEBUG:Z
       8: return
    LineNumberTable:
      line 7: 0

第一條,ldc,從常量池中將常量讀出來(lái)壓入棧中。JVM是基于棧的,操作數(shù)都是從棧上取,結(jié)果也壓到棧里面去。
第二條,invokestatic,這是我們學(xué)到的第二條invoke類指令了,上一條是invokespecial,這個(gè)顧名思義,就是調(diào)用靜態(tài)方法專用的指令。
第三條,putstatic,將棧中的值,放到靜態(tài)域中。
第四條,return,無(wú)數(shù)據(jù)返回。

流程很簡(jiǎn)單,先從常量池將true讀出來(lái)放到棧里,然后調(diào)用Boolean.parseBoolean,參數(shù)就是剛才放入棧的true字符串,解析好之后的值又入棧。接著,putstatic從棧里讀取這個(gè)boolean的值,寫到DEBUG這個(gè)域中。最后返回。

class的基本結(jié)構(gòu)

因?yàn)槭侨腴T文章,暫時(shí)我們先不講class文件各模塊的細(xì)節(jié),只是先有個(gè)感性認(rèn)識(shí):

  • 常量池:class中有常量池,有很多指令是操作常量池的。將常量池中的值讀出來(lái)放到棧中。
  • 方法:class文件中,方法是有專門存儲(chǔ)模塊的,invoke集指令去調(diào)用的時(shí)候,從中去查找。
  • 域:不管是靜態(tài)域還是對(duì)象實(shí)例中的普通域,我們有很多指令是用來(lái)操作它們的。
  • 棧:JVM最重要的結(jié)構(gòu)就是這個(gè)棧,大部分的操作都是通過(guò)這個(gè)棧來(lái)操作。后面學(xué)習(xí)Dalvik指令的時(shí)候我們會(huì)看到,比起JVM中基本都是棧操作的這種指令,Dalvik大量使用了寄存器。

運(yùn)算指令

下面我們?cè)倏戳硪淮箢惖闹噶?,運(yùn)算相關(guān)的指令。
我們還是老辦法,先寫個(gè)例子,然后再反匯編,看它背后的故事。我們先寫個(gè)最簡(jiǎn)單的加法運(yùn)算:

package com.yunos.xulun.testcppjni2;

public class TestART {
    public static int add(int a, int b){
        return a+b;
    }
}

反匯編之后是這樣的:

Compiled from "TestART.java"
public class com.yunos.xulun.testcppjni2.TestART {
  public com.yunos.xulun.testcppjni2.TestART();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
    LineNumberTable:
      line 3: 0
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0       5     0  this   Lcom/yunos/xulun/testcppjni2/TestART;

  public static int add(int, int);
    Code:
       0: iload_0
       1: iload_1
       2: iadd
       3: ireturn
    LineNumberTable:
      line 5: 0
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0       4     0     a   I
          0       4     1     b   I
}

默認(rèn)生成的構(gòu)造方法,以后我們就略過(guò)不提了。
這個(gè)加法運(yùn)算,一共4條指令:

  1. iload_0,從棧頂?shù)?個(gè)位置取一個(gè)整數(shù)
  2. iload_1,從棧頂?shù)?個(gè)位置取一個(gè)整數(shù)
  3. iadd,將這兩個(gè)整數(shù)相加
  4. ireturn,返回一個(gè)整數(shù)。

運(yùn)算指令多,是因?yàn)橹噶罴?jí)沒(méi)辦法做泛型,針對(duì)每種類型數(shù)據(jù)都得做一條指令,所以,加法這一個(gè)操作,就得4條指令,分別對(duì)應(yīng)整型,長(zhǎng)整型,單精度,雙精度:

指令碼 序號(hào) 助記符
0x60 96 iadd
0x61 97 ladd
0x62 98 fadd
0x63 99 dadd

指令中,第一個(gè)字符為i的對(duì)應(yīng)整型,l是長(zhǎng)整型,f是單精度,d是雙精度。
當(dāng)然不光加法是這樣,減法,乘法,除法也是一樣。從棧上讀數(shù),轉(zhuǎn)化成什么類型,也是4種類型都要支持。

類型轉(zhuǎn)換

那么一個(gè)問(wèn)題來(lái)了,既然只有4種類型的計(jì)算指令,其它類型怎么辦?
JVM提供了一堆類型轉(zhuǎn)換的指令來(lái)滿足這個(gè)需求。
有一些類型直接連轉(zhuǎn)換都省了,比如short和byte,在JVM里,就是當(dāng)int來(lái)處理。

我們做個(gè)試驗(yàn):

    public static int sub(int a, short b){
        return a-b;
    }

反匯編了之后發(fā)現(xiàn),一個(gè)int跟short,或者是兩個(gè)short相減,跟兩個(gè)int做減法就沒(méi)有區(qū)別:

  public static int sub(int, short);
    Code:
       0: iload_0
       1: iload_1
       2: isub
       3: ireturn
    LineNumberTable:
      line 9: 0
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0       4     0     a   I
          0       4     1     b   S

所以,以后大家就用int吧,不是數(shù)組的話,short跟byte也是int。
為什么?因?yàn)闂>褪且詉nt為單位的??!寄存器的還值得拆成兩個(gè)用,棧真就不需要了。

我們?cè)倏匆粋€(gè)帶類型轉(zhuǎn)換的:

    public static long mul(int a, byte b){
        return a*b;
    }

反匯編之后,出現(xiàn)一條將整型轉(zhuǎn)成長(zhǎng)整型的i2l指令。

  public static long mul(int, byte);
    Code:
       0: iload_0
       1: iload_1
       2: imul
       3: i2l
       4: lreturn
    LineNumberTable:
      line 13: 0
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0       5     0     a   I
          0       5     1     b   B

因?yàn)榉祷刂狄彩情L(zhǎng)整型了,所以返回指令變成lreturn了。

趁熱打鐵,我們強(qiáng)勢(shì)切入Dalvik指令

對(duì)JVM指令有了初步的理解之后,我們絕不沾沾自喜,迅速看看Dalvik指令是什么樣子的。

先從記憶中把BuildConfig那段翻出來(lái),JVM是這樣的:

       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

我們看看,轉(zhuǎn)成Dalvik是什么樣的:

00052c:                                        |[00052c] com.yunos.system.empty3.BuildConfig.<init>:()V
00053c: 7010 1100 0000                         |0000: invoke-direct {v0}, Ljava/lang/Object;.<init>:()V // method@0011
000542: 0e00                                   |0003: return-void

看完了之后有沒(méi)有會(huì)心一笑?invokespecial指令換了個(gè)名,叫invoke-direct,帶一個(gè)v0寄存器的參數(shù),所以aload_0省了。
return換了個(gè)更貼切的名字:return-void。我們前面學(xué)過(guò)了ireturn,lreturn,這個(gè)不帶值的return,確實(shí)叫return-void很合適。

再對(duì)比另一段:

       0: ldc           #2                  // String true
       2: invokestatic  #3                  // Method java/lang/Boolean.parseBoolean:(Ljava/lang/String;)Z
       5: putstatic     #4                  // Field DEBUG:Z
       8: return

對(duì)應(yīng)過(guò)來(lái)是:

000508:                                        |[000508] com.yunos.system.empty3.BuildConfig.<clinit>:()V
000518: 1a00 4a00                              |0000: const-string v0, "true" // string@004a
00051c: 7110 1000 0000                         |0002: invoke-static {v0}, Ljava/lang/Boolean;.parseBoolean:(Ljava/lang/String;)Z // method@0010
000522: 0a00                                   |0005: move-result v0
000524: 6a00 0200                              |0006: sput-boolean v0, Lcom/yunos/system/empty3/BuildConfig;.DEBUG:Z // field@0002
000528: 0e00                                   |0008: return-void

ldc變成了const-string,就是換個(gè)名,多個(gè)v0寄存器。invokestatic多了個(gè)"-",也是多了個(gè)寄存器參數(shù)。
因?yàn)閕nvoke-static返回的值是在棧里,所以需要一條額外的move-result指令將棧頂值放入寄存器。
putstatic變成了sput,加上類型,變成sput-boolean。
最后return-void.

好,我們?cè)倏聪?,加,減,乘的那幾個(gè):

0f477c:                                        |[0f477c] com.yunos.xulun.testcppjni2.TestART.add:(II)I
0f478c: 9000 0102                              |0000: add-int v0, v1, v2
0f4790: 0f00                                   |0002: return v0

iadd變成了add-int指令,帶有三個(gè)寄存器參數(shù),v1和v2是兩個(gè)加數(shù),和放在v0中。
ireturn變成return v0

減法以此類推:

0f47ac:                                        |[0f47ac] com.yunos.xulun.testcppjni2.TestART.sub:(IS)I
0f47bc: 9100 0102                              |0000: sub-int v0, v1, v2
0f47c0: 0f00                                   |0002: return v0

乘法的增加一條類型轉(zhuǎn)換:

0f4794:                                        |[0f4794] com.yunos.xulun.testcppjni2.TestART.mul:(IB)J
0f47a4: 9200 0203                              |0000: mul-int v0, v2, v3
0f47a8: 8100                                   |0002: int-to-long v0, v0
0f47aa: 1000                                   |0003: return-wide v0

i2l換了個(gè)馬甲叫int-to-long。
lreturn變成了return-wide。

最后收尾,ARM指令

我們最后看下幾個(gè)計(jì)算函數(shù)生成的機(jī)器代碼吧:

add的機(jī)器代碼

    CODE: (code_offset=0x0050151c size_offset=0x00501518 size=76)...
      0x0050151c: d1400bf0  sub x16, sp, #0x2000 (8192)
      0x00501520: b940021f  ldr wzr, [x16]
      suspend point dex PC: 0x0000
      0x00501524: f81e0fe0  str x0, [sp, #-32]!
      0x00501528: f9000ffe  str lr, [sp, #24]
      0x0050152c: b9002be1  str w1, [sp, #40]
      0x00501530: b9002fe2  str w2, [sp, #44]
      0x00501534: 79400250  ldrh w16, [tr] (state_and_flags)
      0x00501538: 35000130  cbnz w16, #+0x24 (addr 0x50155c)
      0x0050153c: b9402be0  ldr w0, [sp, #40]
      0x00501540: b9402fe1  ldr w1, [sp, #44]
      0x00501544: 0b010002  add w2, w0, w1
      0x00501548: b90013e2  str w2, [sp, #16]
      0x0050154c: b94013e0  ldr w0, [sp, #16]
      0x00501550: f9400ffe  ldr lr, [sp, #24]
      0x00501554: 910083ff  add sp, sp, #0x20 (32)
      0x00501558: d65f03c0  ret
      0x0050155c: f9421e5e  ldr lr, [tr, #1080] (pTestSuspend)
      0x00501560: d63f03c0  blr lr
      suspend point dex PC: 0x0000
      0x00501564: 17fffff6  b #-0x28 (addr 0x50153c)

核心就這一條add w2, w0, w1,其余都是折騰棧和寄存器。指令用的是w[n]而不是x[n],進(jìn)行的是32位的加法。

減法

    CODE: (code_offset=0x0050160c size_offset=0x00501608 size=76)...
      0x0050160c: d1400bf0  sub x16, sp, #0x2000 (8192)
      0x00501610: b940021f  ldr wzr, [x16]
      suspend point dex PC: 0x0000
      0x00501614: f81e0fe0  str x0, [sp, #-32]!
      0x00501618: f9000ffe  str lr, [sp, #24]
      0x0050161c: b9002be1  str w1, [sp, #40]
      0x00501620: b9002fe2  str w2, [sp, #44]
      0x00501624: 79400250  ldrh w16, [tr] (state_and_flags)
      0x00501628: 35000130  cbnz w16, #+0x24 (addr 0x50164c)
      0x0050162c: b9402be0  ldr w0, [sp, #40]
      0x00501630: b9402fe1  ldr w1, [sp, #44]
      0x00501634: 4b010002  sub w2, w0, w1
      0x00501638: b90013e2  str w2, [sp, #16]
      0x0050163c: b94013e0  ldr w0, [sp, #16]
      0x00501640: f9400ffe  ldr lr, [sp, #24]
      0x00501644: 910083ff  add sp, sp, #0x20 (32)
      0x00501648: d65f03c0  ret
      0x0050164c: f9421e5e  ldr lr, [tr, #1080] (pTestSuspend)
      0x00501650: d63f03c0  blr lr
      suspend point dex PC: 0x0000
      0x00501654: 17fffff6  b #-0x28 (addr 0x50162c)

除了加法換成了減法:sub w2, w0, w1,其余基本一樣啊。

乘法

    CODE: (code_offset=0x0050158c size_offset=0x00501588 size=88)...
      0x0050158c: d1400bf0  sub x16, sp, #0x2000 (8192)
      0x00501590: b940021f  ldr wzr, [x16]
      suspend point dex PC: 0x0000
      0x00501594: f81e0fe0  str x0, [sp, #-32]!
      0x00501598: f9000ffe  str lr, [sp, #24]
      0x0050159c: b9002be1  str w1, [sp, #40]
      0x005015a0: b9002fe2  str w2, [sp, #44]
      0x005015a4: 79400250  ldrh w16, [tr] (state_and_flags)
      0x005015a8: 35000190  cbnz w16, #+0x30 (addr 0x5015d8)
      0x005015ac: b9402be0  ldr w0, [sp, #40]
      0x005015b0: b9402fe1  ldr w1, [sp, #44]
      0x005015b4: 1b017c02  mul w2, w0, w1
      0x005015b8: b9000fe2  str w2, [sp, #12]
      0x005015bc: b9400fe0  ldr w0, [sp, #12]
      0x005015c0: 93407c01  sxtw x1, w0
      0x005015c4: f800c3e1  stur x1, [sp, #12]
      0x005015c8: f840c3e0  ldur x0, [sp, #12]
      0x005015cc: f9400ffe  ldr lr, [sp, #24]
      0x005015d0: 910083ff  add sp, sp, #0x20 (32)
      0x005015d4: d65f03c0  ret
      0x005015d8: f9421e5e  ldr lr, [tr, #1080] (pTestSuspend)
      0x005015dc: d63f03c0  blr lr
      suspend point dex PC: 0x0000
      0x005015e0: 17fffff3  b #-0x34 (addr 0x5015ac)

首先,是mul指令:mul w2, w0, w1
另外,還有一條是將32位整數(shù)轉(zhuǎn)成64位的長(zhǎng)整型,請(qǐng)注意,32位的w寄存器之外,64位的x寄存器出來(lái)干活了。
sxtw x1, w0:是將w0中的32位值擴(kuò)展成64位的值,結(jié)果放在x1 64位寄存器中。

基本概念我們先說(shuō)這么多,分支,異常等高級(jí)話題,下面分別討論。
最后我們會(huì)cover到完整的指令集。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容