常规设计
- 机器模型和调用约定旨在近似模仿常见的真实架构和 C 样式调用约定
- 该机器是基于寄存器的,帧在创建时大小固定。每个帧都包含特定数量的寄存器(由方法指定)以及执行该方法所需的任何附加数据,例如(但不限于)程序计数器和对包含该方法的
.dex
文件的引用。 - 当用于位值(例如整数和浮点数)时,寄存器被认为是 32 位宽。相邻的寄存器对用于 64 位值。寄存器对没有对齐要求。
- 当用于对象引用时,寄存器被认为足够宽,可以容纳一个这样的引用。
- 就按位表示而言,
(Object) null == (int) 0
。 - 方法的 N 个参数按照顺序存放在方法调用帧的最后 N 个寄存器中。宽参数占用两个寄存器。实例方法会传递一个
this
引用作为它们的第一个参数。
- 该机器是基于寄存器的,帧在创建时大小固定。每个帧都包含特定数量的寄存器(由方法指定)以及执行该方法所需的任何附加数据,例如(但不限于)程序计数器和对包含该方法的
- 指令流中的存储单位是一个 16 位的无符号量。某些指令中的某些位会被忽略/必须为零。
- 指令并非随意地限定于特定类型。例如,在不解释的情况下移动 32 位寄存器值的指令不必指定它们移动的是整数还是浮点数。
- 对于字符串、类型、字段和方法的引用,存在单独枚举和索引的常量池。
- 按位字面数据以内联方式在指令流中表示。
- 因为在实践中,一个方法通常不需要超过 16 个寄存器,并且因为需要超过 8 个寄存器确实相当常见,所以许多指令仅限于寻址前 16 个寄存器。在合理的情况下,指令允许引用最多前 256 个寄存器。此外,一些指令具有变体,允许更大的寄存器计数,包括一对可以寻址
v0
–v65535
范围内寄存器的全能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
”是目标寄存器(由操作暗示;同样,规则是目标参数始终位于最前面),它必须在v0
–v255
范围内。 - “
vBBBB
”是源寄存器,它必须在v0
–v65535
范围内。
- “
- 有关各种指令格式(在“Op & Format”下列出)以及操作码语法的更多详细信息,请参阅指令格式文档。
- 有关字节码在更大范围内的位置的更多详细信息,请参阅
.dex
文件格式文档。
字节码集摘要
操作码 & 格式 | 助记符 / 语法 | 参数 | 描述 |
---|---|---|---|
00 10x | nop | 浪费周期。 注意:携带数据的伪指令用此操作码标记,在这种情况下,操作码单元的高位字节指示数据的性质。请参阅下面的“ |
|
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 位) |
将一个寄存器对的内容移动到另一个寄存器对。 注意:从 |
05 22x | move-wide/from16 vAA, vBBBB | A: 目标寄存器对(8 位)B: 源寄存器对(16 位) |
将一个寄存器对的内容移动到另一个寄存器对。 注意:实现注意事项与上面的 |
06 32x | move-wide/16 vAAAA, vBBBB | A: 目标寄存器对(16 位)B: 源寄存器对(16 位) |
将一个寄存器对的内容移动到另一个寄存器对。 注意:实现注意事项与上面的 |
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-kind 或 filled-new-array 的对象结果移动到指示的寄存器中。这必须作为 invoke-kind 或 filled-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 已经前进到超出指令一样执行。可以认为此指令成功执行(在某种意义上),并且异常在指令之后但在下一个指令有机会运行之前被抛出。此定义使得方法可以将监视器清理全捕获(例如, |
1f 21c | check-cast vAA, type@BBBB | A: 承载引用的寄存器(8 位)B: 类型索引(16 位) |
如果给定寄存器中的引用无法强制转换为指示的类型,则抛出 ClassCastException 。注意:由于 |
20 22c | instance-of vA, vB, type@CCCC | A: 目标寄存器(4 位)B: 承载引用的寄存器(4 位)C: 类型索引(16 位) |
如果指示的引用是给定类型的实例,则在给定的目标寄存器中存储 1 ,否则存储 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 位) |
构造给定类型和大小的数组,并用提供的内容填充它。类型必须是数组类型。数组的内容必须是单字的(即,没有 long 或 double 数组,但引用类型是可以接受的)。构造的实例存储为“结果”,其方式与方法调用指令存储其结果的方式相同,因此构造的实例必须使用紧随其后的 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 位) |
无条件跳转到指示的指令。 注意:分支偏移量不得为 |
29 20t | goto/16 +AAAA | A: 有符号分支偏移量(16 位) |
无条件跳转到指示的指令。 注意:分支偏移量不得为 |
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 。例如,要检查浮点 |
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 位) |
如果给定的两个寄存器的值按指定方式比较,则分支到给定的目标。 注意:分支偏移量不得为 |
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 的比较如指定,则分支到给定的目标。 注意:分支偏移量不得为 |
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* 变体作为紧随其后的指令来存储。
当 在 Dex 文件版本
注意:这些操作码是静态链接的合理候选者,将方法参数更改为更直接的偏移量(或偏移量对)。 |
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 位) |
对两个源寄存器执行已标识的二元运算,并将结果存储在目标寄存器中。 注意:与其他 |
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 位) |
对两个源寄存器执行已标识的二元运算,并将结果存储在第一个源寄存器中。 注意:与其他 |
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 位) |
对指示的寄存器(第一个参数)和字面值(第二个参数)执行指示的二元运算,并将结果存储在目标寄存器中。 注意: |
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 位) |
对指示的寄存器(第一个参数)和字面值(第二个参数)执行指示的二元运算,并将结果存储在目标寄存器中。 注意:请参阅下文,了解 |
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.invoke 或 java.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 :
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 。 |