Dalvik 位元碼格式

一般設計

  • 機器模型和呼叫慣例旨在模仿常見的實際架構和 C 樣式呼叫慣例:
    • 機器是根據註冊建立,因此建立時會固定影格大小。每個影格都包含特定數量的註冊器 (由方法指定),以及執行方法所需的任何附加資料,例如 (但不限於) 程式計數器和包含方法的 .dex 檔案參照。
    • 當用於位元值 (例如整數和浮點數) 時,寄存器的寬度會視為 32 位元。相鄰的註冊組合用於 64 位元值。註冊組不須對齊。
    • 當用於物件參照時,註冊的寬度足以容納一項此類參照。
    • 就位元表示法而言,(Object) null == (int) 0
    • 方法的 N 引數會依序放置在方法呼叫結構體的最後 N 暫存器中。寬引數會消耗兩個註冊。例項方法會傳遞 this 參照做為第一個引數。
  • 指令串流中的儲存單位是 16 位元無符號數量。部分指令中的某些位元會遭到忽略 / 必須為零。
  • 指令不會無故限制為特定類型。舉例來說,如果指令是移動 32 位元註冊值而無需解讀,則不必指定是否要移動 int 或浮點值。
  • 針對字串、類型、欄位和方法的參照,會分別列舉並編入索引的常數集區。
  • 位元值文字資料會在指令串流中內嵌表示。
  • 因為在實際情況下,方法需要超過 16 個註冊的情況並不常見,而且需要超過八個註冊的情況相當常見,因此許多指令都只會處理前 16 個註冊。在合理可行情況下,指令可參照前 256 個寄存器。此外,部分指令具有可支援更大暫存器數量的變化版本,包括一組萬用 move 指令,可處理 v0v65535 範圍內的暫存器。如果指令變化版本無法處理所需的暫存器,則暫存器內容會從原始暫存器移至低暫存器 (在作業前),以及/或者從低結果暫存器移至高暫存器 (在作業後)。
  • 有幾個「偽指令」用於保存變長資料酬載,並由一般指令 (例如 fill-array-data) 參照。在執行的正常流程中,絕對不會遇到這類指令。此外,指令必須位於偶數位元組碼偏移位置 (也就是 4 位元組對齊)。為了符合這項要求,如果這類指令無法對齊,則 dex 產生工具必須以間隔符號的形式發出額外的 nop 指令。最後,雖然並非必要,但大多數工具都會選擇在方法結尾處發出這些指令,因為如果不這樣做,可能就需要額外指令來分支。
  • 在執行中的系統上安裝時,部分指令可能會變更格式,以便進行安裝時的靜態連結最佳化。這樣一來,一旦連結關係明確,就能加快執行速度。如要查看建議的變化版本,請參閱相關的指示格式文件。我們謹慎使用「建議」一詞,因為這些做法並非強制規定。
  • 人性化語法和助憶法:
    • 引數的「Dest-then-source」排序。
    • 部分 Opcode 具有可區分的名稱後置字元,用於指出其運算的類型:
      • 類型通用 32 位元操作碼未標示。
      • 類型一般 64 位元 Opcode 的後置字串為 -wide
      • 類型專屬的 Opcode 會加上其類型 (或簡單的縮寫) 做為後置字元,包括:-boolean-byte-char-short-int-long-float-double-object-string-class-void
    • 部分 Opcode 會附上可區分的副檔名,以便區分具有不同指令版面配置或選項的相同運算。這些後置字串會以斜線 (「/」) 與主要名稱分隔,主要目的是讓產生及解讀可執行檔的程式碼中,靜態常數與一對一對應,以減少人為的模糊性。
    • 在本說明中,每四位元寬度使用一個字元來強調值的寬度 (例如常數的範圍或可能位址的暫存器數量)。
    • 例如在「move-wide/from16 vAA, vBBBB」指令中:
      • move」是基本運算碼,表示基本運算 (移動註冊值)。
      • wide」是名稱後置字串,表示該函式會處理寬 (64 位元) 資料。
      • from16」是運算碼後置字元,表示變體會將 16 位元註冊參照做為來源。
      • vAA」是目的地暫存器 (由作業隱含;再次提醒,規則是目的地引數一律會先出現),必須位於 v0v255 的範圍內。
      • vBBBB」是來源登錄,必須位於 v0v65535 的範圍內。
  • 如要進一步瞭解各種指令格式 (列於「運算元式與格式」下方),以及運算碼語法的詳細資訊,請參閱指令格式說明文件
  • 如要進一步瞭解位元碼在整體架構中的適當位置,請參閱 .dex 檔案格式說明文件

位元碼集摘要

作業與格式 助憶法 / 語法 引數 說明
00 10x nop   浪費週期。

注意:含有資料的疑似指令會加上這個運算碼,在這種情況下,運算碼單位的高位元組會指出資料的性質。請參閱下方的「packed-switch-payload 格式」、「sparse-switch-payload 格式」和「fill-array-data-payload 格式」。

01 12x move vA, vB A: 目的地暫存器 (4 位元)
B: 來源暫存器 (4 位元)
將一個非物件型註冊器的內容移至另一個註冊器。
02 22x move/from16 vAA, vBBBB A: 目的地暫存器 (8 位元)
B: 來源暫存器 (16 位元)
將一個非物件型註冊器的內容移至另一個註冊器。
03 32x move/16 vAAAA, vBBBB A: 目的地暫存器 (16 位元)
B: 來源暫存器 (16 位元)
將一個非物件型註冊器的內容移至另一個註冊器。
04 12x move-wide vA, vB A: 目的地暫存器組 (4 位元)
B: 來源暫存器組 (4 位元)
將一個註冊組合的內容移至另一個註冊組合。

注意:vN 移至 vN-1vN+1 是合法的,因此實作時必須安排讀取寄存器組的兩個部分,然後再寫入任何內容。

05 22x move-wide/from16 vAA, vBBBB A: 目的地暫存器組 (8 位元)
B: 來源暫存器組 (16 位元)
將一個註冊組合的內容移至另一個註冊組合。

注意:導入方面的注意事項與上述 move-wide 相同。

06 32x move-wide/16 vAAAA, vBBBB A: 目的地暫存器組 (16 位元)
B: 來源暫存器組 (16 位元)
將一個註冊組合的內容移至另一個註冊組合。

注意:導入方面的注意事項與上述 move-wide 相同。

07 12x move-object vA, vB A: 目的地暫存器 (4 位元)
B: 來源暫存器 (4 位元)
將一個含有物件的註冊表內容移至另一個註冊表。
08 22x move-object/from16 vAA, vBBBB A: 目的地暫存器 (8 位元)
B: 來源暫存器 (16 位元)
將一個含有物件的註冊表內容移至另一個註冊表。
09 32x move-object/16 vAAAA, vBBBB A: 目的地暫存器 (16 位元)
B: 來源暫存器 (16 位元)
將一個含有物件的註冊表內容移至另一個註冊表。
0a 11x move-result vAA A: 目的地暫存器 (8 位元) 將最近一次 invoke-kind 的單字非物件結果移至指定的註冊表。這項操作必須在 invoke-kind 之後立即執行,且該 invoke-kind 的 (單字、非物件) 結果不得忽略;其他位置均無效。
0b 11x move-result-wide vAA A: 目的地暫存器組 (8 位元) 將最近 invoke-kind 的雙字結果移至指定的註冊組。這項操作必須在 invoke-kind 之後立即執行,且該指令的 (雙字) 結果不得忽略;其他位置皆無效。
0c 11x move-result-object vAA A: 目的地暫存器 (8 位元) 將最近一次 invoke-kind 的物件結果移至指定的註冊表。這項操作必須在 invoke-kindfilled-new-array 之後立即執行,且不應忽略其 (物件) 結果;否則其他位置均無效。
0d 11x move-exception vAA A: 目的地暫存器 (8 位元) 將剛捕獲的例外狀況儲存到指定的註冊表中。這必須是任何例外狀況處理常式的首要指令,且所捕獲的例外狀況不得遭到忽略,且此指令「只能」以例外狀況處理常式的首要指令出現,其他位置皆無效。
0e 10x return-void   void 方法傳回。
0f 11x return vAA A: 傳回值登錄器 (8 位元) 從單寬 (32 位元) 非物件值傳回方法傳回。
10 11x return-wide vAA A: 傳回值暫存器組 (8 位元) 從傳回值的雙寬 (64 位元) 方法傳回。
11 11x return-object vAA A: 傳回值暫存器 (8 位元) 從物件傳回方法傳回。
12 11n const/4 vA, #+B A: 目的地暫存器 (4 位元)
B: 已簽署的 int (4 位元)
將指定的字面值 (已延伸至 32 位元的符號) 移至指定的註冊器。
13 21 秒 const/16 vAA, #+BBBB A: 目的地暫存器 (8 位元)
B: 已簽署的 int (16 位元)
將指定的字面值 (已延伸至 32 位元的符號) 移至指定的註冊器。
14 31i const vAA, #+BBBBBBBB A: 目的地暫存器 (8 位元)
B: 任意 32 位元常數
將指定的常值移至指定的註冊器。
15 21h const/high16 vAA, #+BBBB0000 A: 目的地暫存器 (8 位元)
B: 已簽署的 int (16 位元)
將指定的字面值 (右零擴展至 32 位元) 移至指定的註冊器。
16 21 秒 const-wide/16 vAA, #+BBBB A: 目的地暫存器 (8 位元)
B: 已簽署的 int (16 位元)
將指定的字面值 (已擴充至 64 位元的符號) 移至指定的寄存器組。
17 31i const-wide/32 vAA, #+BBBBBBBB A: 目的地暫存器 (8 位元)
B: 已簽署的 int (32 位元)
將指定的字面值 (已擴充至 64 位元的符號) 移至指定的寄存器組。
18 51l const-wide vAA, #+BBBBBBBBBBBBBBBB A: 目的地暫存器 (8 位元)
B: 任意雙寬 (64 位元) 常數
將指定的常值移至指定的暫存器組。
19 21h const-wide/high16 vAA, #+BBBB000000000000 A: 目的地暫存器 (8 位元)
B: 已簽署的 int (16 位元)
將指定的字面值 (右零延伸至 64 位元) 移至指定的寄存器組。
1a 21c const-string vAA, string@BBBB A: 目的地暫存器 (8 位元)
B: 字串索引
將參照由指定索引指定的字串移至指定的註冊器。
1b 31c const-string/jumbo vAA, string@BBBBBBBB A: 目的地暫存器 (8 位元)
B: 字串索引
將參照由指定索引指定的字串移至指定的註冊器。
1c 21c const-class vAA, type@BBBB A: 目的地暫存器 (8 位元)
B: 型別索引
將參照指定索引所指定類別的參照移至指定的註冊器。如果指定的類型為基本類型,這會儲存基本類型的退化類別參照。
1d 11x monitor-enter vAA A: 參照含有註冊 (8 位元) 取得指定物件的監控器。
1e 11x monitor-exit vAA A: 參照含有註冊 (8 位元) 釋出指定物件的監控器。

注意:如果這個指令需要擲回例外狀況,則必須以 PC 已進展到指令後的狀態擲回。您或許可以將這視為指令已成功執行 (在某種意義上),而例外狀況是在指令「後」,但在下一個指令有機會執行之前發生。這個定義可讓方法使用監控清理萬用字 (例如finally) 區塊做為該區塊本身的監控清理作業,藉此處理因 Thread.stop() 的歷史實作而可能擲回的任意例外狀況,同時仍管理適當的監控衛生狀況。

1f 21c check-cast vAA, type@BBBB A: 參照含有登錄器 (8 位元)
B: 型別索引 (16 位元)
如果指定登錄表中的參照無法轉換為指定類型,則會擲回 ClassCastException

注意:由於 A 一律必須是參照 (而非基本值),因此如果 B 參照基本型別,這項作業在執行階段一定會失敗 (也就是會擲回例外狀況)。

20 22c instance-of vA, vB, type@CCCC A: 目的地暫存器 (4 位元)
B: 參照暫存器 (4 位元)
C: 類型索引 (16 位元)
如果指定的參照是指定類型的例項,則儲存在指定目的地註冊 1 中;如果不是,則儲存在 0 中。

注意:由於 B 一律必須是參照 (而非基本值),因此如果 C 參照基本型別,系統一律會儲存 0

21 12x 陣列長度 vA、vB A: 目的地暫存器 (4 位元)
B: 陣列參照暫存器 (4 位元)
在指定目的地註冊中儲存指定陣列的長度,以項目為單位
22 21c new-instance vAA, type@BBBB A: 目的地暫存器 (8 位元)
B: 型別索引
建構指定類型的新例項,並在目的地儲存對該例項的參照。類型必須參照非陣列類別。
23 22c new-array vA, vB, type@CCCC A: 目的地暫存器 (4 位元)
B: 大小暫存器
C: 類型索引
建構指定類型和大小的新陣列。類型必須是陣列類型。
24 35c 已填入的陣列 {vC, vD, vE, vF, vG},類型@BBBB A: 陣列大小和引數字詞數量 (4 位元)
B: 型別索引 (16 位元)
C..G: 引數註冊 (每個 4 位元)
建構指定類型和大小的陣列,並以提供的內容填入陣列。類型必須是陣列類型。陣列的內容必須是單字 (也就是沒有 longdouble 的陣列,但參照類型是可接受的)。建構的例項會以「結果」的形式儲存,這與方法叫用指示的儲存結果方式相同,因此建構的例項必須透過後續的 move-result-object 指示 (如果要使用) 移至寄存器。
25 3rc 已填入的陣列/範圍 {vCCCC .. vNNNN},型別@BBBB A: 陣列大小和引數字節數 (8 位元)
B: 型別索引 (16 位元)
C: 第一個引數註冊 (16 位元)
N = A + C - 1
建構指定類型和大小的陣列,並以提供的內容填入陣列。說明和限制與上述 filled-new-array 相同。
26 31t fill-array-data vAA, +BBBBBBBB (附加資料,如下方「fill-array-data-payload 格式」所述) A: 陣列參照 (8 位元)
B: 以表格資料虛擬指令為基準的帶正號「分支」偏移量 (32 位元)
使用指定資料填入指定陣列。參照項目必須是原始類型的陣列,且資料表格必須與其類型相符,且所含元素不得超過陣列可容納的數量。也就是說,陣列可能比資料表大,如果是這種情況,系統只會設定陣列的初始元素,而不會處理其餘元素。
27 11x 擲回 vAA A: 例外狀況含有暫存器 (8 位元)
擲回指定的例外狀況。
28 10t goto +AA A: 有符號分支偏移量 (8 位元) 無條件跳到指定的指令。

注意:分支偏移值不得為 0。(您可以使用 goto/32 或在分支前加入 nop 做為目標,以合法方式建立旋轉迴圈)。

29 20t goto/16 +AAAA A: 帶符號分支偏移量 (16 位元)
無條件跳到指定的指令。

注意:分支偏移值不得為 0。(您可以使用 goto/32 或在分支前加入 nop 做為目標,以合法方式建立旋轉迴圈)。

2a 30t goto/32 +AAAAAAAA A: 已簽署的分支偏移 (32 位元)
無條件跳到指定的指令。
2b 31t packed-switch vAA, +BBBBBBBB (附加資料請參閱下方的「packed-switch-payload 格式」) A: 註冊以測試
B: 已簽署的「分支」偏移至表格資料的疑似指令 (32 位元)
根據指定寄存器中的值,使用對應特定整數範圍中每個值的偏移量表格,跳至新的指令,或在沒有相符項目時,跳至下一個指令。
2c 31t sparse-switch vAA, +BBBBBBBB (附加資料請參閱下方的「sparse-switch-payload 格式」) A: 註冊以測試
B: 已簽署的「分支」偏移量,可用於表格資料的疑似指令 (32 位元)
根據指定登錄器中的值,使用值-偏移量組合的排序表跳至新指令,或在沒有相符項目時跳至下一個指令。
2d..31 23x cmpkind vAA、vBB、vCC
2d: cmpl-float (lt bias)
2e: cmpg-float (gt bias)
2f: cmpl-double (lt bias)
30: cmpg-double (gt bias)
31: cmp-long
A: 目的地暫存器 (8 位元)
B: 第一個來源暫存器或組
C: 第二個來源暫存器或組
執行指定的浮點或 long 比較作業,如果是 b == c,請將 a 設為 0;如果是 b > c,請設為 1;如果是 b < c,請設為 -1。浮點運算所列的「偏差」會指出 NaN 比較的處理方式:如果是「gt bias」,指令會針對 NaN 比較傳回 1;如果是「lt bias」,則會傳回 -1

舉例來說,如要檢查浮點 x < y 是否有效,建議使用 cmpg-float;結果為 -1 表示測試為 true,其他值則表示測試為 false,可能是因為有效比較,也可能是因為其中一個值為 NaN

32..37 22t if-test vA, vB, +CCCC
32: if-eq
33: if-ne
34: if-lt
35: if-ge
36: if-gt
37: if-le
A: 要測試的第一個暫存器 (4 位元)
B: 要測試的第二個暫存器 (4 位元)
C: 帶符號分支偏移量 (16 位元)
如果指定的兩個註冊值符合指定的比較條件,則分支至指定的目的。

注意:分支偏移值不得為 0。(您可以透過在回溯 goto 附近分支,或是在分支前將 nop 納入為目標,合法地建立旋轉迴圈。)

38..3d 21t if-testz vAA, +BBBB
38: if-eqz
39: if-nez
3a: if-ltz
3b: if-gez
3c: if-gtz
3d: if-lez
A: 要測試的註冊 (8 位元)
B: 有符號分支偏移量 (16 位元)
如果指定的註冊器值與指定的 0 比較,則分支至指定的目的。

注意:分支偏移值不得為 0。(您可以透過在回溯 goto 附近分支,或是在分支前將 nop 納入為目標,合法地建立旋轉迴圈。)

3e..43 10x (未使用)   (未使用)
44..51 23x arrayop vAA、vBB、vCC
44: aget
45: aget-wide
46: aget-object
47: aget-boolean
48: aget-byte
49: aget-char
4a: aget-short
4b: aput
4c: aput-wide
4d: aput-object
4e: aput-boolean
4f: aput-byte
50: aput-char
51: aput-short
A: 值登錄器或組合;可能是來源或目的地 (8 位元)
B: 陣列登錄器 (8 位元)
C: 索引登錄器 (8 位元)
在指定陣列的指定索引處執行已識別的陣列運算,並將值載入或儲存至值登錄。
52..5f 22c iinstanceop vA, vB, field@CCCC
52: iget
53: iget-wide
54: iget-object
55: iget-boolean
56: iget-byte
57: iget-char
58: iget-short
59: iput
5a: iput-wide
5b: iput-object
5c: iput-boolean
5d: iput-byte
5e: iput-char
5f: iput-short
A: 值登錄或組合;可能是來源或目的地 (4 位元)
B: 物件登錄 (4 位元)
C: 例項欄位參照索引 (16 位元)
使用已識別的欄位,執行已識別的物件執行個體欄位作業,並載入或儲存至值登錄。

注意:這些 Opcode 是靜態連結的合理候選項目,可將欄位引數改為更直接的偏移值。

60..6d 21c sstaticop vAA, field@BBBB
60: sget
61: sget-wide
62: sget-object
63: sget-boolean
64: sget-byte
65: sget-char
66: sget-short
67: sput
68: sput-wide
69: sput-object
6a: sput-boolean
6b: sput-byte
6c: sput-char
6d: sput-short
A: 值登錄或組合;可能是來源或目的地 (8 位元)
B: 靜態欄位參照索引 (16 位元)
使用已識別的靜態欄位,執行已識別的物件靜態欄位作業,並載入或儲存至值登錄。

注意:這些 Opcode 是靜態連結的合理候選項目,可將欄位引數改為更直接的偏移值。

6e..72 35c invoke-kind {vC, vD, vE, vF, vG}, meth@BBBB
6e: invoke-virtual
6f: invoke-super
70: invoke-direct
71: invoke-static
72: invoke-interface
A: 引數字詞數量 (4 位元)
B: 方法參照索引 (16 位元)
C..G: 引數暫存器 (每個 4 位元)
呼叫指定的方法。結果 (如有) 可能會與適當的 move-result* 變體一併儲存,做為後續立即執行的指令。

invoke-virtual 用於叫用一般虛擬方法,也就是非 staticprivate 或建構函式的函式。

method_id 參照非介面類別的方法時,invoke-super 會用於叫用最接近超類別的虛擬方法 (而非在呼叫類別中具有相同 method_id 的一個)。這項方法的限制與 invoke-virtual 相同。

在 DEX 檔案 037 以上版本中,如果 method_id 是指介面方法,invoke-super 會用於叫用該介面中定義的該方法最具體的、未覆寫的版本。這項方法的限制與 invoke-virtual 相同。在 037 之前的 Dex 檔案中,使用 method_id 介面是不合法且未定義的。

invoke-direct 用於叫用非 static 直接方法 (也就是本質上無法覆寫的例項方法,即 private 例項方法或建構函式)。

invoke-static 用於叫用 static 方法 (一律視為直接方法)。

invoke-interface 用於叫用 interface 方法,也就是在具體類別不明的物件上,使用參照 interfacemethod_id

注意:這些 Opcode 是靜態連結的合理候選項目,可將方法引數改為更直接的偏移值 (或其組合)。

73 10x (未使用)   (未使用)
74..78 3rc invoke-kind/range {vCCCC .. vNNNN}, meth@BBBB
74: invoke-virtual/range
75: invoke-super/range
76: invoke-direct/range
77: invoke-static/range
78: invoke-interface/range
A: 引數字數 (8 位元)
B: 方法參照索引 (16 位元)
C: 第一個引數註冊 (16 位元)
N = A + C - 1
呼叫指定的方法。如需詳細資訊、注意事項和建議,請參閱上述第一個 invoke-kind 說明。
79..7a 10x (未使用)   (未使用)
7b..8f 12x unop vA, vB
7b: neg-int
7c: not-int
7d: neg-long
7e: not-long
7f: neg-float
80: neg-double
81: int-to-long
82: int-to-float
83: int-to-double
84: long-to-int
85: long-to-float
86: long-to-double
87: float-to-int
88: float-to-long
89: float-to-double
8a: double-to-int
8b: double-to-long
8c: double-to-float
8d: int-to-byte
8e: int-to-char
8f: int-to-short
A:目的地暫存器或組合 (4 位元)
B:來源暫存器或組合 (4 位元)
對來源暫存器執行已識別的單一運算,並將結果儲存在目的地暫存器中。
90..af 23x binop vAA, vBB, vCC
90: add-int
91: sub-int
92: mul-int
93: div-int
94: rem-int
95: and-int
96: or-int
97: xor-int
98: shl-int
99: shr-int
9a: ushr-int
9b: add-long
9c: sub-long
9d: mul-long
9e: div-long
9f: rem-long
a0: and-long
a1: or-long
a2: xor-long
a3: shl-long
a4: shr-long
a5: ushr-long
a6: add-float
a7: sub-float
a8: mul-float
a9: div-float
aa: rem-float
ab: add-double
ac: sub-double
ad: mul-double
ae: div-double
af: rem-double
A: 目的地暫存器或組 (8 位元)
B: 第一個來源暫存器或組 (8 位元)
C: 第二個來源暫存器或組 (8 位元)
對兩個來源暫存器執行已識別的二進位運算,並將結果儲存在目的地暫存器中。

注意:與其他 -long 數學運算 (會為第一個和第二個來源使用暫存器組) 不同,shl-longshr-longushr-long 會為第一個來源 (要位移的值) 使用暫存器組,但會為第二個來源 (位移距離) 使用單一暫存器。

b0..cf 12x binop/2addr vA, vB
b0: add-int/2addr
b1: sub-int/2addr
b2: mul-int/2addr
b3: div-int/2addr
b4: rem-int/2addr
b5: and-int/2addr
b6: or-int/2addr
b7: xor-int/2addr
b8: shl-int/2addr
b9: shr-int/2addr
ba: ushr-int/2addr
bb: add-long/2addr
bc: sub-long/2addr
bd: mul-long/2addr
be: div-long/2addr
bf: rem-long/2addr
c0: and-long/2addr
c1: or-long/2addr
c2: xor-long/2addr
c3: shl-long/2addr
c4: shr-long/2addr
c5: ushr-long/2addr
c6: add-float/2addr
c7: sub-float/2addr
c8: mul-float/2addr
c9: div-float/2addr
ca: rem-float/2addr
cb: add-double/2addr
cc: sub-double/2addr
cd: mul-double/2addr
ce: div-double/2addr
cf: rem-double/2addr
A: 目的地和第一個來源暫存器或組合 (4 位元)
B: 第二個來源暫存器或組合 (4 位元)
對兩個來源暫存器執行已識別的二進位運算,並將結果儲存在第一個來源暫存器中。

注意:與其他 -long/2addr 數學運算 (會為目的地/第一個來源和第二個來源使用寄存器組) 不同,shl-long/2addrshr-long/2addrushr-long/2addr 會為目的地/第一個來源 (要轉換的值) 使用寄存器組,但會為第二個來源 (轉換距離) 使用單一寄存器。

d0..d7 22 秒 binop/lit16 vA, vB, #+CCCC
d0: add-int/lit16
d1: rsub-int (reverse subtract)
d2: mul-int/lit16
d3: div-int/lit16
d4: rem-int/lit16
d5: and-int/lit16
d6: or-int/lit16
d7: xor-int/lit16
A: 目的地暫存器 (4 位元)
B: 來源暫存器 (4 位元)
C: 已簽署的 int 常數 (16 位元)
在指定的登錄器 (第一個引數) 和文字值 (第二個引數) 上執行指定的二元運算,並將結果儲存在目的地登錄器中。

注意: rsub-int 沒有後置字串,因為這個版本是其系列的主要 Opcode。此外,請參閱下文,瞭解其語意。

d8..e2 22b binop/lit8 vAA, vBB, #+CC
d8: add-int/lit8
d9: rsub-int/lit8
da: mul-int/lit8
db: div-int/lit8
dc: rem-int/lit8
dd: and-int/lit8
de: or-int/lit8
df: xor-int/lit8
e0: shl-int/lit8
e1: shr-int/lit8
e2: ushr-int/lit8
A: 目的地暫存器 (8 位元)
B: 來源暫存器 (8 位元)
C: 已簽署的 int 常數 (8 位元)
在指定的登錄器 (第一個引數) 和文字值 (第二個引數) 上執行指定的二元運算,並將結果儲存在目的地登錄器中。

注意:如要進一步瞭解 rsub-int 的語意,請參閱下文。

e3..f9 10 倍 (未使用)   (未使用)
fa 45cc invoke-polymorphic {vC, vD, vE, vF, vG}, meth@BBBB, proto@HHHH A: 引數字節數 (4 位元)
B: 方法參照索引 (16 位元)
C: 接收器 (4 位元)
D..G: 引數暫存器 (每個 4 位元)
H: 原型參照索引 (16 位元)
叫用指定簽章多型別方法。結果 (如有) 可能會與適當的 move-result* 變化版本一併儲存,做為後續立即執行的指令。

方法參照必須是簽章多型態方法,例如 java.lang.invoke.MethodHandle.invokejava.lang.invoke.MethodHandle.invokeExact

接收器必須是支援所要叫用的簽章多型態方法的物件。

原型參照會說明提供的引數類型和預期的傳回類型。

invoke-polymorphic 位元碼在執行時可能會發生例外狀況。針對所叫用的簽名多型別方法,API 說明文件會說明這些例外狀況。

038 版起,會出現在 Dex 檔案中。
fb 4rcc invoke-polymorphic/range {vCCCC .. vNNNN}, meth@BBBB, proto@HHHH A: 引數字數 (8 位元)
B: 方法參照索引 (16 位元)
C: 接收器 (16 位元)
H: 原型參照索引 (16 位元)
N = A + C - 1
叫用指定的方法句柄。詳情請參閱上方的invoke-polymorphic說明。

038 版起,會出現在 Dex 檔案中。
fc 35c invoke-custom {vC, vD, vE, vF, vG}, call_site@BBBB A: 引數字詞數量 (4 位元)
B: 呼叫網站參照索引 (16 位元)
C..G: 引數暫存器 (每個 4 位元)
解析並叫用指定的呼叫網址。叫用作業的結果 (如有) 可能會與適當的 move-result* 變化版本一併儲存,做為後續指令。

この命令會在兩個階段執行:呼叫網址解析和呼叫網址叫用。

呼叫端點解析會檢查指定的呼叫端點是否有相關聯的 java.lang.invoke.CallSite 例項。如果沒有,系統會使用 DEX 檔案中的引數,叫用指定呼叫網址的啟動連結器方法 (請參閱 call_site_item)。引導程式連結器方法會傳回 java.lang.invoke.CallSite 例項,如果沒有關聯,則會與指定的呼叫網址建立關聯。另一個執行緒可能已先建立關聯,如果是這樣,指令的執行作業會繼續使用第一個關聯的 java.lang.invoke.CallSite 例項。

呼叫網站呼叫會在已解析 java.lang.invoke.CallSite 例項的 java.lang.invoke.MethodHandle 目標上執行。如果使用方法句柄和 invoke-custom 指令的引數,以確切方法句柄叫用方式執行 invoke-polymorphic (如上所述),系統就會叫用目標。

引導程式連結器方法所產生的例外狀況會包裝在 java.lang.BootstrapMethodError 中。如果符合下列條件,系統也會擲回 BootstrapMethodError
  • 引導程序連結器方法無法傳回 java.lang.invoke.CallSite 例項。
  • 傳回的 java.lang.invoke.CallSite 具有 null 方法的控制代碼目標。
  • 方法句柄目標不是所要求的類型。
038 版起,會出現在 Dex 檔案中。
fd 3rc invoke-custom/range {vCCCC .. vNNNN}, call_site@BBBB A: 引數字數 (8 位元)
B: 呼叫網站參照索引 (16 位元)
C: 第一個引數註冊 (16 位元)
N = A + C - 1
解析並叫用呼叫位置。詳情請參閱上方的invoke-custom說明。

038 版起,會出現在 Dex 檔案中。
fe 21c const-method-handle vAA, method_handle@BBBB A: 目的地登錄 (8 位元)
B: 方法句柄索引 (16 位元)
將參照由指定索引指定的方法句柄移至指定的註冊器。

039 版起,會出現在 Dex 檔案中。
ff 21c const-method-type vAA, proto@BBBB A: 目的地註冊 (8 位元)
B: 方法原型參考資料 (16 位元)
將參照指定索引所指定方法原型的參照,移至指定的註冊器。

039 版起,會出現在 Dex 檔案中。

Packed-Switch-Payload 格式

名稱 格式 說明
ident ushort = 0x0100 辨識虛擬指令
size ushort 資料表中的項目數
first_key int 第一個 (也是最低) 切換案例值
目標 int[] size 相對分支目標的清單。目標是相對於切換 Opcode 的位址,而非這個表格的位址。

注意:這個資料表的執行個體程式碼單位總數為 (size * 2) + 4

稀疏切換酬載格式

名稱 格式 說明
ident ushort = 0x0200 辨識虛擬指令
size ushort 資料表中的項目數
金鑰 int[] size 鍵值清單,由低到高排序
目標 int[] size 相對分支目標清單,每個項目都對應至相同索引的鍵值。目標是相對於切換 Opcode 的位址,而非這個表格的位址。

注意:這個資料表的執行個體程式碼單位總數為 (size * 4) + 2

fill-array-data-payload 格式

名稱 格式 說明
ident ushort = 0x0300 辨識虛擬指令
element_width ushort 每個元素的位元組數
size uint 資料表中的元素數
data ubyte[] 資料值

注意:這個資料表的執行個體程式碼單位總數為 (size * element_width + 1) / 2 + 4

數學運算詳細資料

注意:浮點運算必須遵循 IEEE 754 規則,使用四捨五入和漸進性下溢,除非另有說明。

Opcode C 語意 附註
neg-int int32 a;
int32 result = -a;
一元二補法。
not-int int32 a;
int32 result = ~a;
一元一補。
neg-long int64 a;
int64 result = -a;
一元二補法。
not-long int64 a;
int64 result = ~a;
一元一補。
neg-float float a;
float result = -a;
浮點值否定。
neg-double double a;
double result = -a;
浮點值否定。
int-to-long int32 a;
int64 result = (int64) a;
int32 的符號擴展至 int64
int 轉換為 float int32 a;
float result = (float) a;
使用四捨五入法將 int32 轉換為 float。這會導致部分值的精確度降低。
int 轉換為 double int32 a;
double result = (double) a;
int32 轉換為 double
long-to-int int64 a;
int32 result = (int32) a;
int64 截斷為 int32
long-to-float int64 a;
float result = (float) a;
使用四捨五入法將 int64 轉換為 float。這會導致部分值的精確度降低。
long-to-double int64 a;
double result = (double) a;
使用四捨五入法將 int64 轉換為 double。這會導致部分值的精確度降低。
浮點數轉整數 float a;
int32 result = (int32) a;
使用捨入法將 float 轉換為 int32NaN-0.0 (負零) 會轉換為整數 0。無窮大和值的大小過大而無法表示時,會根據符號轉換為 0x7fffffff-0x80000000
float-to-long float a;
int64 result = (int64) a;
使用捨入法將 float 轉換為 int64。此處適用與 float-to-int 相同的特殊情況規則,但超出範圍的值會根據符號轉換為 0x7fffffffffffffff-0x8000000000000000
浮點轉換為雙精度 float a;
double result = (double) a;
float 轉換為 double,並確切保留該值。
double-to-int double a;
int32 result = (int32) a;
使用捨入法將 double 轉換為 int32。這裡適用的特殊案例規則與 float-to-int 相同。
double-to-long double a;
int64 result = (int64) a;
使用捨入法將 double 轉換為 int64。這裡適用的特殊案例規則與 float-to-long 相同。
double-to-float double a;
float result = (float) a;
使用四捨五入法將 double 轉換為 float。這會導致部分值的精確度降低。
int 到位元組 int32 a;
int32 result = (a << 24) >> 24;
int32 截斷為 int8,延長結果的符號。
int 轉換為 char int32 a;
int32 result = a & 0xffff;
int32 截斷為 uint16,但不延伸符號。
int-to-short int32 a;
int32 結果 = (a << 16) >> 16;
int32 截斷為 int16,延長結果的符號。
add-int int32 a, b;
int32 result = a + b;
二補加法。
子 int int32 a, b;
int32 result = a - b;
二補減法。
rsub-int int32 a, b;
int32 result = b - a;
二補法反向減法。
mul-int int32 a, b;
int32 result = a * b;
二補乘法。
div-int int32 a, b;
int32 result = a / b;
二補法除法,四捨五入 (也就是截斷為整數)。如果 b == 0,則會擲回 ArithmeticException
rem-int int32 a, b;
int32 result = a % b;
除法後的二補餘數。結果的符號與 a 相同,更精確的定義為 result == a - (a / b) * b。如果 b == 0,則會擲回 ArithmeticException
and-int int32 a, b;
int32 result = a & b;
位元 AND。
or-int int32 a, b;
int32 result = a | b;
位元 OR。
xor-int int32 a, b;
int32 result = a ^ b;
位元 XOR。
shl-int int32 a, b;
int32 result = a << (b & 0x1f);
位元左移 (帶有遮罩引數)。
shr-int int32 a, b;
int32 result = a >> (b & 0x1f);
以位元為單位向右移位 (含遮罩引數)。
ushr-int uint32 a, b;
int32 result = a >> (b & 0x1f);
位元位移右 (含遮罩引數)。
add-long int64 a, b;
int64 result = a + b;
二補加法。
子長度 int64 a, b;
int64 result = a - b;
二補減法。
mul-long int64 a, b;
int64 result = a * b;
二補乘法。
div-long int64 a, b;
int64 result = a / b;
二補法除法,四捨五入 (也就是截斷為整數)。如果 b == 0,則會擲回 ArithmeticException
rem-long int64 a, b;
int64 result = a % b;
除法後的二補餘數。結果的符號與 a 相同,更精確的定義為 result == a - (a / b) * b。如果 b == 0,則會擲回 ArithmeticException
and-long int64 a, b;
int64 result = a & b;
位元 AND。
or-long int64 a, b;
int64 result = a | b;
位元 OR。
xor-long int64 a, b;
int64 result = a ^ b;
位元 XOR。
shl-long int64 a;
int32 b;
int64 result = a << (b & 0x3f);
位元左移 (帶有遮罩引數)。
shr-long int64 a;
int32 b;
int64 result = a >> (b & 0x3f);
位元有符號向右移位 (使用遮罩引數)。
ushr-long uint64 a;
int32 b;
int64 result = a >> (b & 0x3f);
位元位移右 (含遮罩引數)。
add-float float a, b;
float result = a + b;
浮點加法。
子浮動 float a, b;
float result = a - b;
浮點數減法。
mul-float float a, b;
float result = a * b;
浮點乘法。
div-float float a, b;
float result = a / b;
浮點除法。
rem-float float a, b;
float result = a % b;
除法後的浮點餘數。這個函式與 IEEE 754 餘數不同,並定義為 result == a - roundTowardZero(a / b) * b
add-double double a, b;
double result = a + b;
浮點加法。
子雙精度浮點數 double a, b;
double result = a - b;
浮點數減法。
mul-double double a, b;
double result = a * b;
浮點乘法。
div-double double a, b;
double result = a / b;
浮點除法。
rem-double double a, b;
double result = a % b;
除法後的浮點餘數。這個函式與 IEEE 754 餘數不同,並定義為 result == a - roundTowardZero(a / b) * b