Dalvik 字节码格式

常规设计

  • 机器模型和调用约定旨在近似模仿常见的真实架构和 C 样式调用约定
    • 该机器是基于寄存器的,帧在创建时大小固定。每个帧都包含特定数量的寄存器(由方法指定)以及执行该方法所需的任何附加数据,例如(但不限于)程序计数器和对包含该方法的 .dex 文件的引用。
    • 当用于位值(例如整数和浮点数)时,寄存器被认为是 32 位宽。相邻的寄存器对用于 64 位值。寄存器对没有对齐要求。
    • 当用于对象引用时,寄存器被认为足够宽,可以容纳一个这样的引用。
    • 就按位表示而言,(Object) null == (int) 0
    • 方法的 N 个参数按照顺序存放在方法调用帧的最后 N 个寄存器中。宽参数占用两个寄存器。实例方法会传递一个 this 引用作为它们的第一个参数。
  • 指令流中的存储单位是一个 16 位的无符号量。某些指令中的某些位会被忽略/必须为零。
  • 指令并非随意地限定于特定类型。例如,在不解释的情况下移动 32 位寄存器值的指令不必指定它们移动的是整数还是浮点数。
  • 对于字符串、类型、字段和方法的引用,存在单独枚举和索引的常量池。
  • 按位字面数据以内联方式在指令流中表示。
  • 因为在实践中,一个方法通常不需要超过 16 个寄存器,并且因为需要超过 8 个寄存器确实相当常见,所以许多指令仅限于寻址前 16 个寄存器。在合理的情况下,指令允许引用最多前 256 个寄存器。此外,一些指令具有变体,允许更大的寄存器计数,包括一对可以寻址 v0v65535 范围内寄存器的全能 move 指令。在指令变体不可用于寻址所需寄存器的情况下,预期寄存器内容会从原始寄存器移动到低位寄存器(在操作之前)和/或从低位结果寄存器移动到高位寄存器(在操作之后)。
  • 存在几个“伪指令”,用于保存可变长度的数据有效负载,这些有效负载由常规指令引用(例如,fill-array-data)。在正常执行流程中绝不能遇到此类指令。此外,这些指令必须位于偶数字节码偏移量上(即,4 字节对齐)。为了满足此要求,dex 生成工具必须发出额外的 nop 指令作为间隔符,以防此类指令原本未对齐。最后,虽然不是必需的,但预期大多数工具会选择在方法末尾发出这些指令,因为否则很可能需要额外的指令来绕过它们进行分支。
  • 当安装在运行系统上时,某些指令可能会被更改,从而改变它们的格式,作为安装时静态链接优化。这是为了在链接已知后允许更快的执行。请参阅相关的指令格式文档,了解建议的变体。“建议”一词是谨慎使用的;实现这些并非强制性的。
  • 人类语法和助记符
    • 目标-然后-源的参数顺序。
    • 一些操作码具有消除歧义的名称后缀,以指示它们操作的类型
      • 类型通用的 32 位操作码是未标记的。
      • 类型通用的 64 位操作码带有后缀 -wide
      • 类型特定的操作码带有它们的类型后缀(或直接的缩写),以下之一:-boolean -byte -char -short -int -long -float -double -object -string -class -void
    • 一些操作码具有消除歧义的后缀,以区分具有不同指令布局或选项的其他相同操作。这些后缀用斜杠(“/”)与主要名称分隔,并且主要存在以使其与生成和解释可执行代码的静态常量存在一对一的映射(即,减少人类的歧义)。
    • 在此处的描述中,值的宽度(例如,指示常量范围或可能寻址的寄存器数量)通过每个四位宽度使用一个字符来强调。
    • 例如,在指令“move-wide/from16 vAA, vBBBB”中
      • move”是基本操作码,指示基本操作(移动寄存器的值)。
      • wide”是名称后缀,指示它对宽(64 位)数据进行操作。
      • from16”是操作码后缀,指示变体具有 16 位寄存器引用作为源。
      • vAA”是目标寄存器(由操作暗示;同样,规则是目标参数始终位于最前面),它必须在 v0v255 范围内。
      • vBBBB”是源寄存器,它必须在 v0v65535 范围内。
  • 有关各种指令格式(在“Op & Format”下列出)以及操作码语法的更多详细信息,请参阅指令格式文档
  • 有关字节码在更大范围内的位置的更多详细信息,请参阅.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 之后的紧随指令完成,该指令的(单字,非对象)结果不能被忽略;在任何其他地方都是无效的。
0b 11x move-result-wide vAA A: 目标寄存器对(8 位) 将最近一次 invoke-kind 的双字结果移动到指示的寄存器对中。这必须作为 invoke-kind 之后的紧随指令完成,该指令的(双字)结果不能被忽略;在任何其他地方都是无效的。
0c 11x move-result-object vAA A: 目标寄存器(8 位) 将最近一次 invoke-kindfilled-new-array 的对象结果移动到指示的寄存器中。这必须作为 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: 有符号整数(4 位)
将给定的字面值(符号扩展为 32 位)移动到指定的寄存器中。
13 21s const/16 vAA, #+BBBB A: 目标寄存器(8 位)
B: 有符号整数(16 位)
将给定的字面值(符号扩展为 32 位)移动到指定的寄存器中。
14 31i const vAA, #+BBBBBBBB A: 目标寄存器(8 位)
B: 任意 32 位常量
将给定的字面值移动到指定的寄存器中。
15 21h const/high16 vAA, #+BBBB0000 A: 目标寄存器(8 位)
B: 有符号整数(16 位)
将给定的字面值(右侧零扩展为 32 位)移动到指定的寄存器中。
16 21s const-wide/16 vAA, #+BBBB A: 目标寄存器(8 位)
B: 有符号整数(16 位)
将给定的字面值(符号扩展为 64 位)移动到指定的寄存器对中。
17 31i const-wide/32 vAA, #+BBBBBBBB A: 目标寄存器(8 位)
B: 有符号整数(32 位)
将给定的字面值(符号扩展为 64 位)移动到指定的寄存器对中。
18 51l const-wide vAA, #+BBBBBBBBBBBBBBBB A: 目标寄存器(8 位)
B: 任意双宽度(64 位)常量
将给定的字面值移动到指定的寄存器对中。
19 21h const-wide/high16 vAA, #+BBBB000000000000 A: 目标寄存器(8 位)
B: 有符号整数(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 array-length 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 filled-new-array {vC, vD, vE, vF, vG}, type@BBBB A: 数组大小和参数字计数(4 位)
B: 类型索引(16 位)
C..G: 参数寄存器(每个 4 位)
构造给定类型和大小的数组,并用提供的内容填充它。类型必须是数组类型。数组的内容必须是单字的(即,没有 longdouble 数组,但引用类型是可以接受的)。构造的实例存储为“结果”,其方式与方法调用指令存储其结果的方式相同,因此构造的实例必须使用紧随其后的 move-result-object 指令移动到寄存器(如果要使用它)。
25 3rc filled-new-array/range {vCCCC .. vNNNN}, type@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 throw 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 偏差)
2e: cmpg-float (gt 偏差)
2f: cmpl-double (lt 偏差)
30: cmpg-double (gt 偏差)
31: cmp-long
A: 目标寄存器(8 位)
B: 第一个源寄存器或对
C: 第二个源寄存器或对
执行指示的浮点或 long 比较,如果 b == c,则将 a 设置为 0,如果 b > c,则设置为 1,如果 b < c,则设置为 -1。浮点运算列出的“偏差”指示如何处理 NaN 比较:“gt 偏差”指令为 NaN 比较返回 1,“lt 偏差”指令返回 -1

例如,要检查浮点 x < y 是否成立,建议使用 cmpg-float;结果为 -1 表示测试为真,其他值表示测试为假,无论是由于有效比较还是由于其中一个值为 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 位)
对已标识的字段执行已标识的对象实例字段操作,加载或存储到值寄存器中。

注意:这些操作码是静态链接的合理候选者,将字段参数更改为更直接的偏移量。

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 位)
对已标识的静态字段执行已标识的对象静态字段操作,加载或存储到值寄存器中。

注意:这些操作码是静态链接的合理候选者,将字段参数更改为更直接的偏移量。

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 相同的Method限制适用。

在 Dex 文件版本 037 或更高版本中,如果 method_id 引用接口方法,则 invoke-super 用于调用该接口上定义的最具体、未被覆盖的该方法版本。与 invoke-virtual 相同的Method限制适用。在版本 037 之前的 Dex 文件中,具有接口 method_id 是非法的且未定义的。

invoke-direct 用于调用非 static 直接方法(即,本质上不可覆盖的实例方法,即 private 实例方法或构造函数)。

invoke-static 用于调用 static 方法(始终被视为直接方法)。

invoke-interface 用于调用 interface 方法,即在具体类未知的情况下,使用引用 interfacemethod_id 在对象上调用。

注意:这些操作码是静态链接的合理候选者,将方法参数更改为更直接的偏移量(或偏移量对)。

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 22s binop/lit16 vA, vB, #+CCCC
d0: add-int/lit16
d1: rsub-int (反向减法)
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: 有符号整数常量(16 位)
对指示的寄存器(第一个参数)和字面值(第二个参数)执行指示的二元运算,并将结果存储在目标寄存器中。

注意:rsub-int 没有后缀,因为此版本是其系列的主要操作码。另请参阅下文,了解其语义的详细信息。

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: 有符号整数常量(8 位)
对指示的寄存器(第一个参数)和字面值(第二个参数)执行指示的二元运算,并将结果存储在目标寄存器中。

注意:请参阅下文,了解 rsub-int 的语义的详细信息。

e3..f9 10x (未使用)   (未使用)
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 文档中描述。

从 Dex 文件版本 038 开始提供。
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 描述。

从 Dex 文件版本 038 开始提供。
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-polymorphic(如上所述)一样,使用方法句柄和 invoke-custom 指令的参数作为精确方法句柄调用的参数。

由引导程序链接器方法引发的异常会包装在 java.lang.BootstrapMethodError 中。在以下情况下,也会引发 BootstrapMethodError
  • 引导程序链接器方法未能返回 java.lang.invoke.CallSite 实例。
  • 返回的 java.lang.invoke.CallSite 具有 null 方法句柄目标。
  • 方法句柄目标的类型与请求的类型不符。
从 Dex 文件版本 038 开始提供。
fd 3rc invoke-custom/range {vCCCC .. vNNNN}, call_site@BBBB A: 参数字计数(8 位)
B: 调用点引用索引(16 位)
C: 首个参数寄存器(16 位)
N = A + C - 1
解析并调用调用点。有关详情,请参阅上文中的 invoke-custom 说明。

从 Dex 文件版本 038 开始提供。
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 标识伪操作码
大小 ushort 表中的条目数
first_key int 第一个(也是最小的)switch case 值
目标 int[] size 个相对分支目标的列表。目标相对于 switch 操作码的地址,而不是此表的地址。

注意:此表的实例的代码单元总数为 (size * 2) + 4

sparse-switch-payload 格式

名称 格式 描述
ident ushort = 0x0200 标识伪操作码
大小 ushort 表中的条目数
int[] size 个键值的列表,按从低到高排序
目标 int[] size 个相对分支目标的列表,每个目标对应于同一索引处的键值。目标相对于 switch 操作码的地址,而不是此表的地址。

注意:此表的实例的代码单元总数为 (size * 4) + 2

fill-array-data-payload 格式

名称 格式 描述
ident ushort = 0x0300 标识伪操作码
element_width ushort 每个元素中的字节数
大小 uint 表中的元素数
数据 ubyte[] 数据值

注意:此表的实例的代码单元总数为 (size * element_width + 1) / 2 + 4

数学运算详情

注意:浮点运算必须遵循 IEEE 754 规则,使用舍入到最近的规则和渐进下溢,除非另有说明。

操作码 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-to-float int32 a;
float result = (float) a;
int32 转换为 float,使用舍入到最近的规则。对于某些值,这会损失精度。
int-to-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-to-int float a;
int32 result = (int32) a;
float 转换为 int32,使用向零舍入的规则。NaN-0.0(负零)转换为整数 0。无穷大和值过大而无法表示的值会根据符号转换为 0x7fffffff-0x80000000
float-to-long float a;
int64 result = (int64) a;
float 转换为 int64,使用向零舍入的规则。与 float-to-int 相同的特殊情况规则也适用于此处,但超出范围的值会根据符号转换为 0x7fffffffffffffff-0x8000000000000000
float-to-double 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-to-byte int32 a;
int32 result = (a << 24) >> 24;
int32 截断为 int8,对结果进行符号扩展。
int-to-char int32 a;
int32 result = a & 0xffff;
int32 截断为 uint16,不进行符号扩展。
int-to-short int32 a;
int32 result = (a << 16) >> 16;
int32 截断为 int16,对结果进行符号扩展。
add-int int32 a, b;
int32 result = a + b;
补码加法。
sub-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;
按位与。
or-int int32 a, b;
int32 result = a | b;
按位或。
xor-int int32 a, b;
int32 result = a ^ b;
按位异或。
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;
补码加法。
sub-long 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;
按位与。
or-long int64 a, b;
int64 result = a | b;
按位或。
xor-long int64 a, b;
int64 result = a ^ b;
按位异或。
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;
浮点加法。
sub-float 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;
浮点加法。
sub-double 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