Dalvik指令集是学习Android虚拟机中必不可少的知识点,它是被Android虚拟机所识别且直接执行的指令。

Dalvik是基于寄存器指令集,他的几乎所有指令操作都来自对寄存器的操作,而不像x86/ARM因为寄存器个数不够导致运算结果存不下,需要依靠堆栈来存储情况。Dalvik一共有65536个寄存器,执行一个函数调用后虚拟机内部会保存当前方法中用到的寄存器,并且重置所有寄存器供被调用方法的新环境使用。

由于Dalvik是虚拟机指令,最终依然会被解析成机器指令执行,因此65536个寄存器中大多数寄存器实际上仍然会被存储于堆栈中。少量运算中所使用的寄存器会被放在物理机的寄存器中使用。Dalvik虚拟机内部维护了调用栈,调用新的方法时会将寄存器与返回地址存放于调用栈中,返回时取回。

1. Dalvik寄存器

Dalvik中所有的寄存器均为32位长度。当表示64位数值时则使用相邻的两个32位寄存器存放,在使用时指定相邻的第一个寄存器,第二个寄存器也会参与运算。用.registers伪指令描述当前方法中使用的寄存器个数(包括形参)。

寄存器的命名有两种:

v0~vN+M-1:当前方法中运算所使用的寄存器。(var)

p0~pM-1:当前方法中的形参。(param)

其中N为当前方法的局部变量个数,M为当前方法形参个数。 vN~vN+M-1与p0~pM-1是一样的。

如果被调用的方法不是静态的,则p0为方法所属类的实例。

假设这里有个方法Add:

class Clazz{
    public int Add(int a, int b)
    {
        int c = a + b;
        return c;
    }
}

在排除编译器自动生成的临时变量与优化以外,这里使用了四个寄存器,即.registers 4

其中形参p0为Clazz的实例,p1为a, p2为b。也可以使用v1、v2、v3,不过命名通常以pM的居多。

其中局部变量v0为c。

2. 数据类型

java类型

dalvik类型

void

V

boolean

Z

byte

B

char

C

short

S

int

I

long

J

float

F

double

D

class

L类全路径;

int[]、char[][]、...

[I、[[C、...

 

基本数据类型中除了boolean->V、long->J以外,均为首字母大写。

类类型为L加类的全路径,且以/分割,比如有个类的全路径为com.example.MainClass,那么在Dalvik中命名为Lcom/example/MainClass; 注意分号。

3. 类的描述:

有如下类:

package com.example;
public class User{
    private String name;
    private String nick;
    public String getName()
    {return name;}
    public void setName(String name)
    {this.name = name;}
}

注:其中String 的全路径为 java.lang.String。

.class 指令用于描述该类的起始位置:.class public User

.super 紧跟在.class后用于描述该类的父类(没有父类时继承自Object):.super Ljava/lang/Object;

.implements 描述类实现的接口:.implements 接口名

.annotation 描述类注解:

.annotation [注解的属性] <注解类名>

            [注解字段=值]

            ...

        .end

.field 指令用于描述类中的字段:.field private name:Ljava/lang/String;

.method 描述方法的开始:.method public getName()Ljava/lang/String;(返回值类型写在最后面)

方法特例:

        .method public constructor <init>()V 描述构造函数

        .method static constructor <clinit>()V 描述静态构造函数

.end method 描述方法的结束

当外部使用类中的方法时将会被描述为

getName:Lcom/example/User;->getName(Ljava/lang/String)V;

setName:Lcom/example/User;->setName()Ljava/lang/String;

jeb Dalvik汇编窗口与Java代码对比样例:

        136ad06d6bdb94b4b2a4a28ce84c3f2a.png

 

 

4. 常见指令

v*表示任意寄存器,#+*表示任意常量

其中A、B、C等表示指令的参数,其个数表示它的取值范围。

寄存器:

A、B、C等的个数表示指令能接受的寄存器下标范围。

如vA表示2^4-1内的寄存器可以使用,vAA则表示2^8-1内的寄存器可以使用。

最多为4个,如vAAAA表示2^16-1内的寄存器可以使用(v0~v65535)

常量:

如#+B为4位常量(通常为boolean)。

#+BBBBBBBBBBBBBBBB则为64位常量(long / double)。最多为16个

 

[下文整合分类自https://source.android.com/docs/core/runtime/dalvik-bytecode]

数据定义指令

用于将一个常量放入寄存器中

opcode

指令

参数

描述

12 11n

const/4 vA, #+B

A: 目标寄存器(4 位)

将给定的字面量值(符号扩展为 32 位)移到指定的寄存器中。

B: 有符号整数(4 位)

13 21s

const/16 vAA, #+BBBB

A: 目标寄存器(8 位)

将给定的字面量值(符号扩展为 32 位)移到指定的寄存器中。

B: 有符号整数(16 位)

14 31i

const vAA, #+BBBBBBBB

A: 目标寄存器(8 位)

将给定的字面量值移到指定的寄存器中。

B: 任意 32 位常量

15 21h

const/high16 vAA, #+BBBB0000

A: 目标寄存器(8 位)

将给定的字面量值(右零扩展为 32 位)移到指定的寄存器中。

B: 有符号整数(16 位)

16 21s

const-wide/16 vAA, #+BBBB

A: 目标寄存器(8 位)

将给定的字面量值(符号扩展为 64 位)移到指定的寄存器对中。

B: 有符号整数(16 位)

17 31i

const-wide/32 vAA, #+BBBBBBBB

A: 目标寄存器(8 位)

将给定的字面量值(符号扩展为 64 位)移到指定的寄存器对中。

B: 有符号整数(32 位)

18 51l

const-wide vAA, #+BBBBBBBBBBBBBBBB

A: 目标寄存器(8 位)

将给定的字面量值移到指定的寄存器对中。

B: 任意双字宽度(64 位)常量

19 21h

const-wide/high16 vAA, #+BBBB000000000000

A: 目标寄存器(8 位)

将给定的字面量值(右零扩展为 64 位)移到指定的寄存器对中。

B: 有符号整数(16 位)

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: 类型索引

数据操作指令

移动数值到新的地方

opcode

指令

参数

描述

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-1 或 vN+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-king表示一系列invoke-开头的指令,用于调用方法

0b 11x

move-result-wide vAA

A: 目标寄存器对(8 位)

将最新的 invoke-kind 的双字结果移到指定的寄存器对中。该指令必须紧跟在其(双字)结果不会被忽略的 invoke-kind 之后执行,否则无效。

0c 11x

move-result-object vAA

A: 目标寄存器(8 位)

将最新的 invoke-kind 的对象结果移到指定的寄存器中。该指令必须紧跟在其(对象)结果不会被忽略的 invoke-kind 或 filled-new-array 之后执行,否则无效。

0d 11x

move-exception vAA

A: 目标寄存器(8 位)

将刚刚捕获的异常保存到给定寄存器中。该指令必须为捕获的异常不会被忽略的任何异常处理程序的第一条指令,且该指令必须仅作为异常处理程序的第一条指令执行,否则无效。

运算指令格式

opcode

指令

参数

描述

7b..8f 12x

unop vA, vB

A: 目标寄存器或寄存器对(4 位)
B: 源寄存器或寄存器对(4 位)

对源寄存器执行已确定的一元运算,并将结果存储到目标寄存器中。

90..af 23x

binop vAA, vBB, vCC

A: 目标寄存器或寄存器对(8 位)
B: 第一个源寄存器或寄存器对(8 位)
C: 第二个源寄存器或寄存器对(8 位)

对两个源寄存器执行已确定的二元运算,并将结果存储到目标寄存器中。

注意:与其他 -long 数学运算(对第一个和第二个源采用寄存器对)相反,shl-long、shr-long 和 ushr-long 会对其第一个源采用寄存器对(存放要移位的值),但会对其第二个源采用单个寄存器(存放移位的距离)。

b0..cf 12x

binop/2addr vA, vB

A: 目标寄存器或寄存器对和第一个源寄存器或寄存器对(4 位)
B: 第二个源寄存器或寄存器对(4 位)

对两个源寄存器执行已确定的二元运算,并将结果存储到第一个源寄存器中。

注意:与其他 -long/2addr 数学运算(对其目标/第一个源和第二个源都采用寄存器对)相反,shl-long/2addr、shr-long/2addr 和 ushr-long/2addr 会对其目标/第一个源采用寄存器对(存放要移位的值),但会对其第二个源采用单个寄存器(存放移位的距离)。

d0..d7 22s

binop/lit16 vA, vB, #+CCCC

A: 目标寄存器(4 位)
B: 源寄存器(4 位)
C: 有符号整数常量(16 位)

对指定的寄存器(第一个参数)和字面量值(第二个参数)执行指定的二元运算,并将结果存储到目标寄存器中。

注意:rsub-int 不含后缀,因为此版本是其一系列运算码中的主运算码。另外,有关语义的详细信息,请参阅下文。

d8..e2 22b

binop/lit8 vAA, vBB, #+CC

A: 目标寄存器(8 位)
B: 源寄存器(8 位)
C: 有符号整数常量(8 位)

对指定的寄存器(第一个参数)和字面量值(第二个参数)执行指定的二元运算,并将结果存储到目标寄存器中。

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

运算指令

运算码

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。

对象操作指令

与对象实例相关的操作,如对象创建、类型检查、类型转换等。

opcode

指令

参数

描述

22 21c

new-instance vAA, type@BBBB

A: 目标寄存器(8 位)
B: 类型索引

根据指定的类型构造新实例,并将对该新实例的引用存储到目标寄存器中。该类型必须引用非数组类。

20 22c

instance-of vA, vB, type@CCCC

A: 目标寄存器(4 位)
B: 引用传送寄存器(4 位)
C: 类型索引(16 位)

如果指定的引用是给定类型的实例,则为给定目标寄存器赋值 1,否则赋值 0。

注意:由于 B 必须一律为引用(而非基元值),因此如果 C 引用基元类型,则始终赋值 0。

基元值:基本数据类型,如int,long之类。

1f 21c

check-cast vAA, type@BBBB

A: 引用传送寄存器(8 位)
B: 类型索引(16 位)

如果给定寄存器中的引用不能转型为指定的类型,则抛出 ClassCastException。

注意:由于 A 必须一律为引用(而非基元值),因此如果 B 引用基元类型,则必然会在运行时失败(即抛出异常)。

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 位)

对已标识的字段执行已确定的对象实例字段运算,并将结果加载或存储到值寄存器中。iget取值,iput存值

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 位)

对已标识的静态字段执行已确定的对象静态字段运算,并将结果加载或存储到值寄存器中。

注意:这些运算码是静态链接的合理候选项,将字段参数更改为更直接的偏移量。sget 取值,sput存值

6e..72 35c

invoke-kind {vC, vD, vE, vF, vG}, method_id@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 用于调用正常的虚拟方法(该方法不是 private、static 或 final,也不是构造函数)。

当 method_id 引用非接口类方法时,使用 invoke-super 调用最近父类的虚拟方法(这与调用类中具有相同 method_id 的方法相反)。invoke-virtual 具有相同的方法限制。

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

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

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

invoke-interface 用于调用 interface 方法,也就是说,对具体类未知的对象使用引用 interface 的 method_id。

数组操作指令

dalvik中专门为数组的操作制定了一系列指令

opcode

指令

参数

描述

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 位)

根据给定类型和大小构造数组,并使用提供的内容填充该数组。该类型必须是数组类型。数组的内容必须是单字类型(即不接受 long 或 double 类型的数组,但接受引用类型的数组)。构造的实例会存储为一个“结果”,方式与方法调用指令存储其结果的方式相同,因此构造的实例必须移到后面紧跟 move-result-object 指令(如果要使用的话)的寄存器。

B: 类型索引(16 位)

C..G: 参数寄存器(每个寄存器各占 4 位)

25 3rc

filled-new-array/range {vCCCC .. vNNNN}, type@BBBB

A: 数组大小和参数字数(8 位)

根据给定类型和大小构造数组,并使用提供的内容填充该数组。相关的说明和限制与上文所述 filled-new-array 相同。

B: 类型索引(16 位)

C: 第一个参数寄存器(16 位)

N = A + C - 1

26 31t

fill-array-data vAA, +BBBBBBBB

(有关补充数据,请参阅下文的“fill-array-data-payload 格式”)

A: 数组引用(8 位)

用指定的数据填充给定数组。必须引用基元类型的数组,且数据表格的类型必须与数组匹配;此外,数据表格所包含的元素个数不得超出数组中的元素个数。也就是说,数组可以比表格大;如果是这样,仅设置数组的初始元素,而忽略剩余元素。

B: 到表格数据伪指令的有符号“分支”偏移量(32 位)

21 12x

array-length vA, vB

A: 目标寄存器(4 位)
B: 数组引用传送寄存器(4 位)

将指定数组的长度(条目个数)赋值给给定目标寄存器

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 位)

在给定数组的已标识索引处执行已确定的数组运算,并将结果加载或存储到值寄存器中。aget取值,aput存值

方法返回指令

opcode

指令

参数

描述

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 位)

从对象返回方法返回。

跳转指令

opcode

指令

参数

描述

2d..31 23x

cmp kind 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。为浮点运算列出的“bias”表示如何处理 NaN 比较运算:对于 NaN 比较,“gt bias”指令返回 1,而“lt bias”指令返回 -1。

例如,建议使用 cmpg-float 检查浮点数是否满足条件 x < y;如果结果是 -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 作为目标来正常构造。)

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

(有关补充数据,请参阅下文的“packed-switch-payload 格式”)

A: 要测试的寄存器
B: 到表格数据伪指令的有符号“分支”偏移量(32 位)

通过使用偏移值对的有序表,基于给定寄存器中的值跳转到新指令;如果没有匹配项,则跳转到下一条指令。

 

packed-switch-payload 格式

名称

格式

说明

ident

ushort = 0x0100

识别伪运算码

size

ushort

表格中的条目数

first_key

int

第一位(即最低位)switch case 的值

targets

int[]

与 size 相对的分支目标的列表。这些目标相对应的是 switch 运算码的地址(而非此表格的地址)。

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

 

sparse-switch-payload 格式

名称

格式

说明

ident

ushort = 0x0200

识别伪运算码

size

ushort

表格中的条目数

keys

int[]

size 键值列表,从低到高排序

targets

int[]

与 size 相对的分支目标的列表,每一个目标对应相同索引下的键值。这些目标相对应的是 switch 运算码的地址(而非此表格的地址)。

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

 

fill-array-data-payload 格式

名称

格式

说明

ident

ushort = 0x0300

识别伪运算码

element_width

ushort

每个元素的字节数

size

uint

表格中的元素数

data

ubyte[]

数据值

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

 

更多指令参考https://source.android.com/docs/core/runtime/dalvik-bytecode

 

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐