Dakvik bytecode总体设计
- 机器模型和调用约定大致是模仿常见的真实指令体系结构和C语言风格的调用约定:
- 执行指令的机器是 基于寄存器 的,并且,帧的大小在 创建时 就已经 固定 。每个帧由特定数量的寄存器(由方法指定)以及用于执行方法的附加数据组成,例如,程序计数器PC和指向包含该方法的
dex
文件的引用。 - 当寄存器用于处理数字常量时(例如
int
,float
等),寄存器的位宽为32位。相邻的一对寄存器用于64位值的处理。一对的寄存器没有对齐的要求。 - 当寄存器用于处理引用数据时,寄存器位宽足够容纳一个引用(即寄存器宽度=指针长度)。
- 从bit位角度看,
(Object) null == (int) 0
成立。 - 方法的
N
个参数会 依次 落在方法调用栈帧的最后N
个寄存器上。宽字节 参数占用 两个寄存器。实例方法的第一个参数为this
引用。
- 执行指令的机器是 基于寄存器 的,并且,帧的大小在 创建时 就已经 固定 。每个帧由特定数量的寄存器(由方法指定)以及用于执行方法的附加数据组成,例如,程序计数器PC和指向包含该方法的
- 指令中的存储单元是16位无符号大小的。在一些指令中的某些bit位会被忽略或者为0。
- 指令不会被无条件限制成某种特定类型。例如,对于在没有解释说明下,对32位的寄存器执行
move
指令是不能确定究竟是移动int还是float。
- 对于字符串,类型,字段和方法的引用,有 单独 的枚举和索引常量池。
- bit字面值在指令流中是以内联形式表示。
- 因为在实际应用中,一个方法使用超过16个寄存器并不常见,并且因为需要8个寄存器的方法比较普遍,所以很多指令仅仅限于访问前16个寄存器。在合理可能的情况下,指令也允许有引用最多256个寄存器。除此之外,一些指令变体具有允许更大数量的寄存器,例如,可以在
v0 – v65535
的寄存器数量范围内寻址的一对catch-allmove
指令。在某些情况下,当一些指令变体不能寻址到需要寻址的寄存器时,那么就需要在操作进行前,把寄存器的值移动到更低位的寄存器以便指令能够访问,在完成操作后,把结果从低位寄存器移动到高位寄存器中。
- 有几个“伪指令”用于保存可变长度的数据载荷,而这些数据载荷实际是由常规指令引用的(例如,
fill-array-data
指令,可参考:https://stackoverflow.com/questions/19721477/when-will-the-instruction-filled-new-array-appear )。在正常执行流程中,一般是不会见到这种指令。此外,指令必须位于偶数字节码偏移的,也就是4直接对齐。为了满足这个要求,如果这些指令导致不对齐,dex生成工具会插入nop
指令以填补空缺。最后,尽管这不是必需的,但是还是希望大部分工具在方法的末尾选择插