arkTS反编译

Published: 2025年09月08日

In Reverse.

抽象(🐶)

之前分析过v8的字节码反编译,那个是直接借助ghidra这个框架去实现,那个工作主要聚焦在怎么讲v8 bytecode转换为ghidra p-code,而这次打算更深入的了解一个完整的过程。现在先说明下编译与反编译过程中常见的几个角色:

角色

AST

它是源代码的第一个结构化表示。它精确地反映了代码的语法结构,比如一个函数调用节点会包含函数名和所有参数子节点。

IR

广义的说所有非目标机器代码都是中间表示,如AST,ByteCode等,而本文说的是狭义的IR,它是一个独立于源语言和目标平台的通用表示,专门用于做各种优化和分析的指令集。

ByteCode

正向编译的最终产物,它是一种可移植、紧凑的二进制表示,也是本文反编译的目标。

正向编译过程

这是将高级语言代码转换成机器可执行指令的常规流程。

1.词法分析 & 语法分析 -> 抽象语法树 (AST):这个阶段的代码非常接近源语言,易于理解和静态检查。

2.语义分析 & 类型检查 (在AST上进行):编译器遍历AST,检查代码的语义是否正确。例如,变量是否已经声明,函数调用的参数数量和类型是否匹配,是否存在类型转换错误等。它确保了代码在逻辑上是有效的,为后续生成正确的IR和字节码打下基础。

3.AST -> 中间表示 (IR) -> 优化:AST虽然结构化,但对于复杂的优化(如循环优化、公共子表达式消除)来说不够方便。因此,编译器会将AST“降级”(Lowering)到一个更适合分析和优化的中间表示(IR),比如接下来要讨论的Panda IR。Panda IR通常是基于图的结构,包含了控制流图 (CFG) 和使用静态单赋值 (SSA) 形式的数据流信息,重要步骤为:

  • 控制流分析 (CFA):在IR层面,编译器通过构建CFG来分析代码的执行路径,识别出循环、分支和基本块,这是进行循环优化、死代码消除等的基础。

  • 数据流分析 (DFA):同样在IR上,编译器分析数据如何在代码中定义和使用。例如,常量传播(如果一个变量总是同一个常量,就直接替换它)、活跃变量分析(判断哪些变量在某一点仍然有用,用于寄存器分配)等。

4.IR -> 字节码生成:经过充分优化后,IR被转换成最终的Panda Bytecode。这个过程包括:

  • 指令选择:为IR操作选择最合适的字节码指令

  • 寄存器分配:为IR中的变量和临时值分配虚拟寄存器(v0, v1, ...)

5.abc文件生成:将生成的字节码、元信息和调试信息等写入文件

逆向反编译过程

这是我们的分析重点,即将二进制字节码还原成可读的高级语言形式。

1.abc文件解析:解析文件到class和method,反编译以method为单位,这里主要是获取其字节码信息

2.字节码反汇编:这是最简单的一步,将二进制的字节码指令逐条翻译成人类可读的助记符,例如将二进制0xAB 01 02 03翻译成add v1, v2, v3

3.转换为中间表示 (IR) - (Lifting & Analysis):这是反编译过程中最核心、最复杂的一步,通常被称为“提升”(Lifting)。目标是将在低级的、线性的指令,转换成一个高级的、基于图的、适合分析的结构,包括:

  • 构建控制流图 (CFG):首先,通过识别所有跳转指令,将线性指令流切分成基本块(Basic Block),并构建出控制流图。

  • 构建数据流图 (SSA Form):接着,在CFG的基础上,反编译器会进行数据流分析,将基于寄存器的读写操作转换成静态单赋值(SSA)形式。这意味着每个变量只会被赋值一次。在控制流的交汇点,会插入phi函数来合并不同路径的值。

  • 在IR上分析与优化:一旦有了SSA形式的IR,就可以利用大量成熟的编译器算法来进行分析和简化。

这一步能输出一个经过分析和简化的、高层次的、基于图的中间表示 (IR),这个IR已经摆脱了具体寄存器的限制,转而使用带类型的、有明确依赖关系的虚拟变量,并且包含了丰富的控制流和数据流信息。

4.转换为抽象语法树 (AST) - (Structural Analysis):这一步的重点是恢复程序原始的语法结构,反编译器会分析IR的控制流图,识别出高级语言中的典型编程结构。

  • 控制结构识别:通过图论算法(如支配树分析、循环查找等)在CFG上识别特定的区域(循环/条件分支),再根据各种算法(如最简单的递归规约)识别结构。
  • 构建AST:每当识别出一个高级结构,反编译器就会创建一个对应的AST节点(如IfStatementNode,WhileLoopNode)。IR中经过简化的表达式树则被用作填充这些AST节点的内容(例如,if语句的条件表达式、循环体内的语句等)。

5.转换为源码 (Pretty Printing):这是反编译的最后一步,它相对机械,主要是对AST进行一次深度优先遍历:

  • 遍历器会访问AST的每个节点,并根据节点的类型,按照特定语言(如ArkTS)的语法规则,将其“打印”成文本。
  • 例如,当访问一个IfStatementNode时,它会先打印if (,然后递归访问并打印条件表达式子节点,接着打印) {,再递归访问并打印then分支的语句块子节点,以此类推。
  • 这个过程还会负责处理代码的缩进、换行、花括号等格式,使其看起来像人类编写的代码。

ECMA/JS/TS与arkTs

先说几个容易懵的东西:

  • ECMAScript (ES):是一个由Ecma国际制定的脚本语言规范。它定义了语言的语法、类型、对象、操作符等核心部分。

  • JavaScript (JS):是 ECMAScript 规范最著名的一种实现。我们日常在浏览器和Node.js中使用的JS都遵循ECMAScript标准。

  • TypeScript (TS):是建立在 JavaScript之上的一个强类型超集。它在JS的基础上增加了静态类型、接口、泛型等,最终会被“转译”(transpile)成普通的JavaScript代码来运行,任何合法的JS代码都是合法的TS代码。

  • ArkTS:是建立在 TypeScript 之上,并针对鸿蒙应用开发场景进行优化的语言扩展。它继承了TS的所有静态类型能力,并进一步强化,同时为了AOT编译和高效运行,舍弃了TS/JS中的部分动态特性。

即ArkTS 从根本上是遵循 ECMAScript 核心规范的,但它在 TypeScript 的基础上,走上了一条更偏向“静态化”和“原生编译”的道路。

这里主要关注他们的差异点,Ark TS为了性能和安全性对TS/JS做了一些删减和强制语法:

  • ArkTS:它致力于成为一门真正的静态类型语言,所有变量在编译时都必须有明确的类型。所以不能使用any

  • ArkTS:原生集成了声明式UI描述能力。它引入了一套以 @ 符号开头的装饰器来构建UI,这是它最显著的扩展,如@Component定义一个自定义UI组件,@Entry标记一个组件为页面的入口等。

  • 动态特性的限制:为了实现高效的AOT编译和安全性,ArkTS限制或禁用了JS/TS中一些过于动态、难以在编译期预测行为的特性。比如禁止 eval()with(),这些动态执行代码或修改作用域的特性被完全禁止。对 Object.defineProperty 的限制,不能在运行时随意修改对象的结构。更严格的类定义,ArkTS的类在编译后结构是固定的,不能在运行时动态添加或删除属性和方法。

OK,你已经学会了arkts咯,咱们开始看它的编译器和运行时吧!

ABC

Panda字节码存储在后缀为.abc的文件里,所以这些反编译器都叫他abc文件,它的详细描述可见Panda File Format,或者看看010Edit的abc.bt模板文件,这里就简单说明下,总的来说,运行时所需要的信息都有,如果要调试(默认支持),调试所需的信息也都有!

PandaBC

先看看assembly_format.md,然后看isa.yaml,Panda ByteCode就是基于这个文件和同目录下的模板文件生成的。

指令细节

数据操作 (Data Movement)

这类指令负责在寄存器、累加器和内存之间移动数据:

指令组 助记符 签名 acc 伪代码 中文说明
No operation nop nop none skip 执行一个无任何行为的操作,含一种格式['op_none']
Move register-to-register mov mov v1:out:b32, v2:in:b32 none vd = vs 在寄存器之间移动值,含三种格式['op_v1_4_v2_4', 'op_v1_8_v2_8', 'op_v1_16_v2_16']
mov.64 mov.64 v1:out:b64, v2:in:b64 none 在寄存器之间移动值,含二种格式['op_v1_4_v2_4','op_v1_16_v2_16']
mov.obj mov.obj v1:out:ref, v2:in:ref none 在寄存器之间移动值,含三种格式['op_v1_4_v2_4', 'op_v1_8_v2_8', 'op_v1_16_v2_16']
Move immediate-to-register movi movi v:out:i32, imm:i32 none vd = imm 将立即数移入寄存器。对于短格式,立即数会符号位扩展到操作数大小,含四种格式['op_v_4_imm_4', 'op_v_8_imm_8', 'op_v_8_imm_16', 'op_v_8_imm_32']
movi.64 movi.64 v:out:i64, imm:i64 none 将立即数移入寄存器。对于短格式,立即数会符号位扩展到操作数大小,含一种格式['op_v_8_imm_64']
fmovi fmovi v:out:f32, imm:f32 none 将立即数移入寄存器。对于短格式,立即数会符号位扩展到操作数大小,含一种格式['pref_op_v_8_imm_32']
fmovi.64 fmovi.64 v:out:f64, imm:f64 none 将立即数移入寄存器。对于短格式,立即数会符号位扩展到操作数大小,含一种格式['op_v_8_imm_64']
Move null reference into register mov.null mov.null v:out:ref none vd = null 将 null 引用移入寄存器,含一种格式['op_v_8']
Load accumulator from register lda lda v:in:b32 out:b32 acc = vs 将寄存器内容移入累加器,含一种格式['op_v_8']
lda.64 lda.64 v:in:b64 out:b64
lda.obj lda.obj v:in:ref out:ref
Load accumulator from immediate ldai ldai imm:i32 out:i32 acc = imm 将立即数加载到累加器。对于短格式,立即数会符号位扩展到操作数大小,含三种格式['op_imm_8', 'op_imm_16', 'op_imm_32']
ldai.64 ldai.64 imm:i64 out:i64 将立即数加载到累加器。对于短格式,立即数会符号位扩展到操作数大小,含一种格式['op_imm_64']
fldai fldai imm:f32 out:f32 将立即数加载到累加器。对于短格式,立即数会符号位扩展到操作数大小,含一种格式['pref_op_imm_32']
fldai.64 fldai.64 imm:f64 out:f64 将立即数加载到累加器。对于短格式,立即数会符号位扩展到操作数大小,含一种格式['op_imm_64']
Load accumulator from string constant pool lda.str lda.str string_id out:ref acc = load(id) 根据ID将指定字符串加载到累加器。在动态类型语言上下文中,作为 'any' 类型加载,含一种格式['op_id_32']
Load accumulator from type constant pool lda.type lda.type type_id out:ref
type = load(id)
if type == nullptr then
  throw NoClassDefFoundError
end
acc = type
根据ID将指定类型加载到累加器,含一种格式['op_id_16']
Load null reference into accumulator lda.null lda.null out:ref acc = null 将 null 引用加载到累加器,含一种格式['op_none']
Store accumulator sta sta v:out:b32 in:b32 vd = acc 将累加器内容移入寄存器,含一种格式['op_v_8']
sta.64 sta.64 v:out:b64 in:b64
sta.obj sta.obj v:out:ref in:ref
Load from array ldarr.8 ldarr.8 v:in:i8[] inout:i32
if vs == null then
  throw NullPointerException
end
if acc < 0 || acc >= len(vs) then
  throw ArrayIndexOutOfBoundsException
end
if op == ldarr.8 then
  acc = i8toi32(vs[acc])
else if op == ldarru.8 then
  acc = u8toi32(vs[acc])
else if op == ldarr.16 then
  acc = i16toi32(vs[acc])
else if op == ldarru.16 then
  acc = u16toi32(vs[acc])
else
  acc = vs[acc]
end
使用累加器作为索引从数组加载一个元素,并将其放入累加器,含一种格式['op_v_8']
ldarru.8 ldarru.8 v:in:u8[] inout:i32
ldarr.16 ldarr.16 v:in:i16[] inout:i32
ldarru.16 ldarru.16 v:in:u16[] inout:i32
ldarr ldarr v:in:i32[] inout:i32
ldarr.64 ldarr.64 v:in:i64[] inout:i32->i64
fldarr.32 fldarr.32 v:in:f32[] inout:i32->f32
fldarr.64 fldarr.64 v:in:f64[] inout:i32->f64
ldarr.obj ldarr.obj v:in:ref[] inout:i32->ref
Store to array starr.8 starr.8 v1:in:i8[], v2:in:i32 in:i8
if vs1 == null then
  throw NullPointerException
end
if vs2 < 0 || vs2 >= len(vs1) then
  throw ArrayIndexOutOfBoundsException
end
if op == starr.obj && !has_type(acc, component_type(vs1))
  throw ArrayStoreException
end
if op == starr.8 then
  vs1[vs2] = truncateto8(acc)
else if op == starr.16 then
  vs1[vs2] = truncateto16(acc)
else
  vs1[vs2] = acc
end
将累加器内容存储到由索引指向的数组槽中,含一种格式['op_v1_4_v2_4']
starr.16 starr.16 v1:in:i16[], v2:in:i32 in:i16
starr starr v1:in:i32[], v2:in:i32 in:i32
starr.64 starr.64 v1:in:i64[], v2:in:i32 in:i64
fstarr.32 fstarr.32 v1:in:f32[], v2:in:i32 in:f32
fstarr.64 fstarr.64 v1:in:f64[], v2:in:i32 in:f64
starr.obj starr.obj v1:in:ref[], v2:in:i32 in:ref
Get field from object to accumulator ldobj ldobj v:in:ref, field_id out:b32
if vs == null then
  throw NullPointerException
end
field = resolve(field_id)
if op == ldobj and size(field) < 32 then
  acc = extendto32(vs.get(field))
else
  acc = vs.get(field)
end
通过字段id从对象获取字段值,并将其放入累加器,含一种格式['op_v_8_id_16']
ldobj.64 ldobj.64 v:in:ref, field_id out:b64
ldobj.obj ldobj.obj v:in:ref, field_id out:ref
Store accumulator content into object field stobj stobj v:in:ref, field_id in:b32
if vs == null then
  throw NullPointerException
end
field = resolve(field_id)
if op == stobj and size(field) < 32 then
  vs.set(field, truncate(field, acc))
else
  vs.set(field, acc)
end
通过字段id将累加器内容存储到对象字段中,含一种格式['op_v_8_id_16']
stobj.64 stobj.64 v:in:ref, field_id in:b64
stobj.obj stobj.obj v:in:ref, field_id in:ref
Get field from object to register ldobj.v ldobj.v v1:out:b32, v2:in:ref, field_id none
if v2 == null then
  throw NullPointerException
end
field = resolve(field_id)
if op == ldobj and size(field) < 32 then
  v1 = extendto32(v2.get(field))
else
  v1 = v2.get(field)
end
通过字段id从对象获取字段值,并将其放入寄存器,含一种格式['op_v1_4_v2_4_id_16']
ldobj.v.64 ldobj.v.64 v1:out:b64, v2:in:ref, field_id none
ldobj.v.obj ldobj.v.obj v1:out:ref, v2:in:ref, field_id none
Store register content into object field stobj.v stobj.v v1:in:b32, v2:in:ref, field_id none
if v2 == null then
  throw NullPointerException
end
field = resolve(field_id)
if op == stobj and size(field) < 32 then
  v2.set(field, truncate(field, v1))
else
  v2.set(field, v1)
end
通过字段id将寄存器内容存储到对象字段中,含一种格式['op_v1_4_v2_4_id_16']
stobj.v.64 stobj.v.64 v1:in:b64, v2:in:ref, field_id none
stobj.v.obj stobj.v.obj v1:in:ref, v2:in:ref, field_id none
Get static field ldstatic ldstatic field_id out:b32
field, type = resolve(field_id)
if not initialized(type)
  initialize(type)
end
if op == ldstatic and size(field) < 32 then
  acc = extendto32(get_static(field))
else
  acc = get_static(field)
end
通过字段id获取静态字段值,并将其放入累加器,含一种格式['op_id_16']
ldstatic.64 ldstatic.64 field_id out:b64
ldstatic.obj ldstatic.obj field_id out:ref
Store to static field ststatic ststatic field_id in:b32
field, type = resolve(field_id)
if not initialized(type)
  initialize(type)
end
if op == ststatic and size(field) < 32 then
  set_static(field, truncate(field, acc))
else
  set_static(field, acc)
end
通过字段id将累加器内容存储到静态字段中,含一种格式['op_id_16']
ststatic.64 ststatic.64 field_id in:b64
ststatic.obj ststatic.obj field_id in:ref
Create and initialize new constant array lda.const lda.const v:out:ref, literalarray_id none
arr = load(id)
if arr == nullptr then
   throw OutOfMemoryError
end
v = arr
创建新一维常量字面量数组,并将其引用放入寄存器,含一种格式['op_v_8_id_16']

算术与逻辑运算 (Arithmetic & Logic)

这类指令执行数学计算、比较和位运算:

比较与一元运算
指令组 助记符 签名 acc 伪代码 中文说明
Integer comparison cmp.64 cmp.64 v:in:i64 inout:i64->i32
if acc < vs then
  acc = -1
else if acc == vs then
  acc = 0
else
  acc = 1
end
在寄存器和累加器之间执行指定的有符号或无符号整数比较,含一种格式['op_v_8']
ucmp ucmp v:in:u32 inout:u32->i32 在寄存器和累加器之间执行指定的有符号或无符号整数比较,含一种格式['pref_op_v_8']
ucmp.64 ucmp.64 v:in:u64 inout:u64->i32 在寄存器和累加器之间执行指定的有符号或无符号整数比较,含一种格式['pref_op_v_8']
Floating-point comparison fcmpl fcmpl v:in:f32 inout:f32->i32
if isnan(acc) or isnan(vs) then
  switch (op)
  case fcmpg:
  case fcmpg.64:
    acc = 1
  case fcmpl:
  case fcmpl.64:
    acc = -1
  else
    if acc < vs then
      acc = -1
    else if acc == vs then
      acc = 0
    else
      acc = 1
    end
  end
end
在寄存器和累加器之间执行指定的浮点数比较,含一种格式['pref_op_v_8']
fcmpl.64 fcmpl.64 v:in:f64 inout:f64->i32 在寄存器和累加器之间执行指定的浮点数比较,含一种格式['op_v_8']
fcmpg fcmpg v:in:f32 inout:f32->i32 在寄存器和累加器之间执行指定的浮点数比较,含一种格式['pref_op_v_8']
fcmpg.64 fcmpg.64 v:in:f64 inout:f64->i32 在寄存器和累加器之间执行指定的浮点数比较,含一种格式['op_v_8']
Floating-point unary fneg fneg inout:f32
switch (op)
case fneg:
case fneg.64:
  acc = -acc
对累加器执行指定的浮点一元运算,含一种格式['pref_op_none']
fneg.64 fneg.64 inout:f64 对累加器执行指定的浮点一元运算,含一种格式['op_none']
Unary neg neg inout:i32
switch (op)
case neg:
case neg.64:
  acc = -acc
case not:
case not.64:
  acc = ~acc
对累加器执行指定的一元运算,含一种格式['op_none']
neg.64 neg.64 inout:i64 对累加器执行指定的一元运算,含一种格式['op_none']
not not inout:i32 对累加器执行指定的一元运算,含一种格式['pref_op_none']
not.64 not.64 inout:i64 对累加器执行指定的一元运算,含一种格式['pref_op_none']
两地址运算
指令组 助记符 签名 acc 伪代码 中文说明
Two address binary operation on accumulator add2 add2 v:in:i32 inout:i32
switch (op)
case add2:
  acc = (acc + vs) % 2^32
case add2.64:
  acc = (acc + vs) % 2^64
case sub2:
  acc = (acc - vs) % 2^32
case sub2.64:
  acc = (acc - vs) % 2^64
case mul2:
  acc = (acc * vs) % 2^32
case mul2.64:
  acc = (acc * vs) % 2^64
case and2:
  acc = acc & vs
case and2.64:
  acc = acc & vs
case or2:
  acc = acc | vs
case or2.64:
  acc = acc | vs
case xor2:
  acc = acc ^ vs
case xor2.64:
  acc = acc ^ vs
case shl2:
  acc = (acc << (vs & 0x1f)) % 2^32
case shl2.64:
  acc = (acc << (vs & 0x3f)) % 2^64
case shr2:
  acc = acc >> (vs & 0x1f)
case shr2.64:
  acc = acc >> (vs & 0x3f)
case ashr2:
  acc = (acc < 0) ? ~(~acc >> vs & 0x1f) : acc >> vs & 0x1f
case ashr2.64:
  acc = (acc < 0) ? ~(~acc >> vs & 0x3f) : acc >> vs & 0x3f
对累加器和寄存器执行指定的二元运算,并将结果存入累加器,含一种格式['op_v_8']
add2.64 add2.64 v:in:i64 inout:i64 对累加器和寄存器执行指定的二元运算,并将结果存入累加器,含一种格式['op_v_8']
sub2 sub2 v:in:i32 inout:i32 对累加器和寄存器执行指定的二元运算,并将结果存入累加器,含一种格式['op_v_8']
sub2.64 sub2.64 v:in:i64 inout:i64 对累加器和寄存器执行指定的二元运算,并将结果存入累加器,含一种格式['op_v_8']
mul2 mul2 v:in:i32 inout:i32 对累加器和寄存器执行指定的二元运算,并将结果存入累加器,含一种格式['op_v_8']
mul2.64 mul2.64 v:in:i64 inout:i64 对累加器和寄存器执行指定的二元运算,并将结果存入累加器,含一种格式['op_v_8']
and2 and2 v:in:i32 inout:i32 对累加器和寄存器执行指定的二元运算,并将结果存入累加器,含一种格式['pref_op_v_8']
and2.64 and2.64 v:in:i64 inout:i64 对累加器和寄存器执行指定的二元运算,并将结果存入累加器,含一种格式['pref_op_v_8']
or2 or2 v:in:i32 inout:i32 对累加器和寄存器执行指定的二元运算,并将结果存入累加器,含一种格式['pref_op_v_8']
or2.64 or2.64 v:in:i64 inout:i64 对累加器和寄存器执行指定的二元运算,并将结果存入累加器,含一种格式['pref_op_v_8']
xor2 xor2 v:in:i32 inout:i32 对累加器和寄存器执行指定的二元运算,并将结果存入累加器,含一种格式['pref_op_v_8']
xor2.64 xor2.64 v:in:i64 inout:i64 对累加器和寄存器执行指定的二元运算,并将结果存入累加器,含一种格式['pref_op_v_8']
shl2 shl2 v:in:i32 inout:i32 对累加器和寄存器执行指定的二元运算,并将结果存入累加器,含一种格式['pref_op_v_8']
shl2.64 shl2.64 v:in:i64 inout:i64 对累加器和寄存器执行指定的二元运算,并将结果存入累加器,含一种格式['pref_op_v_8']
shr2 shr2 v:in:i32 inout:i32 对累加器和寄存器执行指定的二元运算,并将结果存入累加器,含一种格式['pref_op_v_8']
shr2.64 shr2.64 v:in:i64 inout:i64 对累加器和寄存器执行指定的二元运算,并将结果存入累加器,含一种格式['pref_op_v_8']
ashr2 ashr2 v:in:i32 inout:i32 对累加器和寄存器执行指定的二元运算,并将结果存入累加器,含一种格式['pref_op_v_8']
ashr2.64 ashr2.64 v:in:i64 inout:i64 对累加器和寄存器执行指定的二元运算,并将结果存入累加器,含一种格式['pref_op_v_8']
Two address binary operation on accumulator with result in register add2v add2v v1:out:i32, v2:in:i32 in:i32
switch (op)
case add2:
  vd = (acc + vs) % 2^32
case add2.64:
  vd = (acc + vs) % 2^64
case sub2:
  vd = (acc - vs) % 2^32
case sub2.64:
  vd = (acc - vs) % 2^64
case mul2:
  vd = (acc * vs) % 2^32
case mul2.64:
  acc = (acc * vs) % 2^64
case and2:
  vd = acc & vs
case and2.64:
  vd = acc & vs
case or2:
  vd = acc | vs
case or2.64:
  vd = acc | vs
case xor2:
  vd = acc ^ vs
case xor2.64:
  vd = acc ^ vs
case shl2:
  vd = (acc << (vs & 0x1f)) % 2^32
case shl2.64:
  vd = (acc << (vs & 0x3f)) % 2^64
case shr2:
  vd = acc >> (vs & 0x1f)
case shr2.64:
  vd = acc >> (vs & 0x3f)
case ashr2:
  vd = (acc < 0) ? ~(~acc >> vs & 0x1f) : acc >> vs & 0x1f
case ashr2.64:
  vd = (acc < 0) ? ~(~acc >> vs & 0x3f) : acc >> vs & 0x3f
对累加器和寄存器执行指定的二元运算,并将结果存入寄存器,含一种格式['op_v1_8_v2_8']
add2v.64 add2v.64 v1:out:i64, v2:in:i64 in:i64 对累加器和寄存器执行指定的二元运算,并将结果存入寄存器,含一种格式['op_v1_8_v2_8']
sub2v sub2v v1:out:i32, v2:in:i32 in:i32 对累加器和寄存器执行指定的二元运算,并将结果存入寄存器,含一种格式['op_v1_8_v2_8']
sub2v.64 sub2v.64 v1:out:i64, v2:in:i64 in:i64 对累加器和寄存器执行指定的二元运算,并将结果存入寄存器,含一种格式['op_v1_8_v2_8']
mul2v mul2v v1:out:i32, v2:in:i32 in:i32 对累加器和寄存器执行指定的二元运算,并将结果存入寄存器,含一种格式['op_v1_8_v2_8']
mul2v.64 mul2v.64 v1:out:i64, v2:in:i64 in:i64 对累加器和寄存器执行指定的二元运算,并将结果存入寄存器,含一种格式['op_v1_8_v2_8']
and2v and2v v1:out:i32, v2:in:i32 in:i32 对累加器和寄存器执行指定的二元运算,并将结果存入寄存器,含一种格式['pref_op_v1_8_v2_8']
and2v.64 and2v.64 v1:out:i64, v2:in:i64 in:i64 对累加器和寄存器执行指定的二元运算,并将结果存入寄存器,含一种格式['pref_op_v1_8_v2_8']
or2v or2v v1:out:i32, v2:in:i32 in:i32 对累加器和寄存器执行指定的二元运算,并将结果存入寄存器,含一种格式['pref_op_v1_8_v2_8']
or2v.64 or2v.64 v1:out:i64, v2:in:i64 in:i64 对累加器和寄存器执行指定的二元运算,并将结果存入寄存器,含一种格式['pref_op_v1_8_v2_8']
xor2v xor2v v1:out:i32, v2:in:i32 in:i32 对累加器和寄存器执行指定的二元运算,并将结果存入寄存器,含一种格式['pref_op_v1_8_v2_8']
xor2v.64 xor2v.64 v1:out:i64, v2:in:i64 in:i64 对累加器和寄存器执行指定的二元运算,并将结果存入寄存器,含一种格式['pref_op_v1_8_v2_8']
shl2v shl2v v1:out:i32, v2:in:i32 in:i32 对累加器和寄存器执行指定的二元运算,并将结果存入寄存器,含一种格式['pref_op_v1_8_v2_8']
shl2v.64 shl2v.64 v1:out:i64, v2:in:i64 in:i64 对累加器和寄存器执行指定的二元运算,并将结果存入寄存器,含一种格式['pref_op_v1_8_v2_8']
shr2v shr2v v1:out:i32, v2:in:i32 in:i32 对累加器和寄存器执行指定的二元运算,并将结果存入寄存器,含一种格式['pref_op_v1_8_v2_8']
shr2v.64 shr2v.64 v1:out:i64, v2:in:i64 in:i64 对累加器和寄存器执行指定的二元运算,并将结果存入寄存器,含一种格式['pref_op_v1_8_v2_8']
ashr2v ashr2v v1:out:i32, v2:in:i32 in:i32 对累加器和寄存器执行指定的二元运算,并将结果存入寄存器,含一种格式['pref_op_v1_8_v2_8']
ashr2v.64 ashr2v.64 v1:out:i64, v2:in:i64 in:i64 对累加器和寄存器执行指定的二元运算,并将结果存入寄存器,含一种格式['pref_op_v1_8_v2_8']
Two address floating-point binary operation on accumulator fadd2 fadd2 v:in:f32 inout:f32
switch (op)
case fadd2:
case fadd2.64:
  acc = acc + vs
case fsub2:
case fsub2.64:
  acc = acc - vs
case fmul2:
case fmul2.64:
  acc = acc * vs
case fdiv2:
case fdiv2.64:
  acc = acc / vs
case fmod2:
case fmod2.64:
  acc = acc % vs
对累加器和寄存器执行指定的浮点二元运算,并将结果存入累加器。指令结果遵循IEEE-754算术规则,含一种格式['pref_op_v_8']
fadd2.64 fadd2.64 v:in:f64 inout:f64 对累加器和寄存器执行指定的浮点二元运算,并将结果存入累加器。指令结果遵循IEEE-754算术规则,含一种格式['op_v_8']
fsub2 fsub2 v:in:f32 inout:f32 对累加器和寄存器执行指定的浮点二元运算,并将结果存入累加器。指令结果遵循IEEE-754算术规则,含一种格式['pref_op_v_8']
fsub2.64 fsub2.64 v:in:f64 inout:f64 对累加器和寄存器执行指定的浮点二元运算,并将结果存入累加器。指令结果遵循IEEE-754算术规则,含一种格式['op_v_8']
fmul2 fmul2 v:in:f32 inout:f32 对累加器和寄存器执行指定的浮点二元运算,并将结果存入累加器。指令结果遵循IEEE-754算术规则,含一种格式['pref_op_v_8']
fmul2.64 fmul2.64 v:in:f64 inout:f64 对累加器和寄存器执行指定的浮点二元运算,并将结果存入累加器。指令结果遵循IEEE-754算术规则,含一种格式['op_v_8']
fdiv2 fdiv2 v:in:f32 inout:f32 对累加器和寄存器执行指定的浮点二元运算,并将结果存入累加器。指令结果遵循IEEE-754算术规则,含一种格式['pref_op_v_8']
fdiv2.64 fdiv2.64 v:in:f64 inout:f64 对累加器和寄存器执行指定的浮点二元运算,并将结果存入累加器。指令结果遵循IEEE-754算术规则,含一种格式['op_v_8']
fmod2 fmod2 v:in:f32 inout:f32 对累加器和寄存器执行指定的浮点二元运算,并将结果存入累加器。指令结果遵循IEEE-754算术规则,含一种格式['pref_op_v_8']
fmod2.64 fmod2.64 v:in:f64 inout:f64 对累加器和寄存器执行指定的浮点二元运算,并将结果存入累加器。指令结果遵循IEEE-754算术规则,含一种格式['op_v_8']
Two address floating-point binary operation on accumulator with result in register fadd2v fadd2v v1:out:f32, v2:in:f32 in:f32
switch (op)
case fadd2:
case fadd2.64:
  vd = acc + vs
case fsub2:
case fsub2.64:
  vd = acc - vs
case fmul2:
case fmul2.64:
  vd = acc * vs
case fdiv2:
case fdiv2.64:
  vd = acc / vs
case fmod2:
case fmod2.64:
  vd = acc % vs
对累加器和寄存器执行指定的浮点二元运算,并将结果存入寄存器。指令结果遵循IEEE-754算术规则,含一种格式['pref_op_v1_8_v2_8']
fadd2v.64 fadd2v.64 v1:out:f64, v2:in:f64 in:f64 对累加器和寄存器执行指定的浮点二元运算,并将结果存入寄存器。指令结果遵循IEEE-754算术规则,含一种格式['op_v1_8_v2_8']
fsub2v fsub2v v1:out:f32, v2:in:f32 in:f32 对累加器和寄存器执行指定的浮点二元运算,并将结果存入寄存器。指令结果遵循IEEE-754算术规则,含一种格式['pref_op_v1_8_v2_8']
fsub2v.64 fsub2v.64 v1:out:f64, v2:in:f64 in:f64 对累加器和寄存器执行指定的浮点二元运算,并将结果存入寄存器。指令结果遵循IEEE-754算术规则,含一种格式['op_v1_8_v2_8']
fmul2v fmul2v v1:out:f32, v2:in:f32 in:f32 对累加器和寄存器执行指定的浮点二元运算,并将结果存入寄存器。指令结果遵循IEEE-754算术规则,含一种格式['pref_op_v1_8_v2_8']
fmul2v.64 fmul2v.64 v1:out:f64, v2:in:f64 in:f64 对累加器和寄存器执行指定的浮点二元运算,并将结果存入寄存器。指令结果遵循IEEE-754算术规则,含一种格式['op_v1_8_v2_8']
fdiv2v fdiv2v v1:out:f32, v2:in:f32 in:f32 对累加器和寄存器执行指定的浮点二元运算,并将结果存入寄存器。指令结果遵循IEEE-754算术规则,含一种格式['pref_op_v1_8_v2_8']
fdiv2v.64 fdiv2v.64 v1:out:f64, v2:in:f64 in:f64 对累加器和寄存器执行指定的浮点二元运算,并将结果存入寄存器。指令结果遵循IEEE-754算术规则,含一种格式['op_v1_8_v2_8']
fmod2v fmod2v v1:out:f32, v2:in:f32 in:f32 对累加器和寄存器执行指定的浮点二元运算,并将结果存入寄存器。指令结果遵循IEEE-754算术规则,含一种格式['pref_op_v1_8_v2_8']
fmod2v.64 fmod2v.64 v1:out:f64, v2:in:f64 in:f64 对累加器和寄存器执行指定的浮点二元运算,并将结果存入寄存器。指令结果遵循IEEE-754算术规则,含一种格式['op_v1_8_v2_8']
Two address integer division and modulo on accumulator div2 div2 v:in:i32 inout:i32
if vs == 0 then
  throw ArithmeticException
end
switch (op)
case div:
  if acc == INT32_MIN and vs == -1 then
    acc = INT32_MIN
  else
    acc = acc / vs
  end
case div.64:
  if acc == INT64_MIN and vs == -1 then
    acc = INT64_MIN
  else
    acc = acc / vs
  end
case divu:
case divu.64:
  acc = acc / vs
case mod:
  if acc == INT32_MIN and vs == -1 then
    acc = 0
  else
    acc = acc % vs
  end
case mod.64:
  if acc == INT64_MIN and vs == -1 then
    acc = 0
  else
    acc = acc % vs
  end
case modu:
case modu.64:
  acc = acc % vs
对累加器和寄存器执行整数除法或取模运算,并将结果存入累加器,含一种格式['op_v_8']
div2.64 div2.64 v:in:i64 inout:i64 对累加器和寄存器执行整数除法或取模运算,并将结果存入累加器,含一种格式['op_v_8']
mod2 mod2 v:in:i32 inout:i32 对累加器和寄存器执行整数除法或取模运算,并将结果存入累加器,含一种格式['op_v_8']
mod2.64 mod2.64 v:in:i64 inout:i64 对累加器和寄存器执行整数除法或取模运算,并将结果存入累加器,含一种格式['op_v_8']
divu2 divu2 v:in:u32 inout:u32 对累加器和寄存器执行整数除法或取模运算,并将结果存入累加器,含一种格式['pref_op_v_8']
divu2.64 divu2.64 v:in:u64 inout:u64 对累加器和寄存器执行整数除法或取模运算,并将结果存入累加器,含一种格式['pref_op_v_8']
modu2 modu2 v:in:u32 inout:u32 对累加器和寄存器执行整数除法或取模运算,并将结果存入累加器,含一种格式['pref_op_v_8']
modu2.64 modu2.64 v:in:u64 inout:u64 对累加器和寄存器执行整数除法或取模运算,并将结果存入累加器,含一种格式['pref_op_v_8']
Two address integer division and modulo on accumulator with result in register div2v div2v v1:out:i32, v2:in:i32 in:i32
if vs == 0 then
  throw ArithmeticException
end
switch (op)
case div:
  if acc == INT32_MIN and vs == -1 then
    vd = INT32_MIN
  else
    vd = acc / vs
  end
case div.64:
  if acc == INT64_MIN and vs == -1 then
    vd = INT64_MIN
  else
    vd = acc / vs
  end
case divu:
case divu.64:
  vd = acc / vs
case mod:
  if acc == INT32_MIN and vs == -1 then
    vd = 0
  else
    vd = acc % vs
  end
case mod.64:
  if acc == INT64_MIN and vs == -1 then
    vd = 0
  else
    vd = acc % vs
  end
case modu:
case modu.64:
  vd = acc % vs
对累加器和寄存器执行整数除法或取模运算,并将结果存入寄存器,含一种格式['op_v1_8_v2_8']
div2v.64 div2v.64 v1:out:i64, v2:in:i64 in:i64 对累加器和寄存器执行整数除法或取模运算,并将结果存入寄存器,含一种格式['op_v1_8_v2_8']
mod2v mod2v v1:out:i32, v2:in:i32 in:i32 对累加器和寄存器执行整数除法或取模运算,并将结果存入寄存器,含一种格式['op_v1_8_v2_8']
mod2v.64 mod2v.64 v1:out:i64, v2:in:i64 in:i64 对累加器和寄存器执行整数除法或取模运算,并将结果存入寄存器,含一种格式['op_v1_8_v2_8']
divu2v divu2v v1:out:u32, v2:in:u32 in:u32 对累加器和寄存器执行整数除法或取模运算,并将结果存入寄存器,含一种格式['pref_op_v1_8_v2_8']
divu2v.64 divu2v.64 v1:out:u64, v2:in:u64 in:u64 对累加器和寄存器执行整数除法或取模运算,并将结果存入寄存器,含一种格式['pref_op_v1_8_v2_8']
modu2v modu2v v1:out:u32, v2:in:u32 in:u32 对累加器和寄存器执行整数除法或取模运算,并将结果存入寄存器,含一种格式['pref_op_v1_8_v2_8']
modu2v.64 modu2v.64 v1:out:u64, v2:in:u64 in:u64 对累加器和寄存器执行整数除法或取模运算,并将结果存入寄存器,含一种格式['pref_op_v1_8_v2_8']
立即数运算
指令组 助记符 签名 acc 伪代码 中文说明
Two address binary operation with immediate on accumulator addi addi imm:i32 inout:i32
switch (op)
case addi:
  acc = (acc + imm) % 2^32
case subi:
  acc = (acc - imm) % 2^32
case muli:
  acc = (acc * imm) % 2^32
case andi:
  acc = acc & imm
case ori:
  acc = acc | imm
case xori:
  acc = acc ^ imm
case shli:
  acc = (acc << (imm & 0x1f)) % 2^32
case shri:
  acc = acc >> (imm & 0x1f)
case ashri:
  acc = (acc < 0) ? ~(~acc >> imm & 0x1f) : acc >> imm & 0x1f
对累加器和立即数执行指定的二元运算,并将结果存入累加器。立即数会符号位扩展到操作数大小,含一种格式['op_imm_8']
subi subi imm:i32 inout:i32 对累加器和立即数执行指定的二元运算,并将结果存入累加器。立即数会符号位扩展到操作数大小,含一种格式['op_imm_8']
muli muli imm:i32 inout:i32 对累加器和立即数执行指定的二元运算,并将结果存入累加器。立即数会符号位扩展到操作数大小,含一种格式['op_imm_8']
andi andi imm:i32 inout:i32 对累加器和立即数执行指定的二元运算,并将结果存入累加器。立即数会符号位扩展到操作数大小,含一种格式['op_imm_32']
ori ori imm:i32 inout:i32 对累加器和立即数执行指定的二元运算,并将结果存入累加器。立即数会符号位扩展到操作数大小,含一种格式['op_imm_32']
xori xori imm:i32 inout:i32 对累加器和立即数执行指定的二元运算,并将结果存入累加器。立即数会符号位扩展到操作数大小,含一种格式['pref_op_imm_32']
shli shli imm:i32 inout:i32 对累加器和立即数执行指定的二元运算,并将结果存入累加器。立即数会符号位扩展到操作数大小,含一种格式['op_imm_8']
shri shri imm:i32 inout:i32 对累加器和立即数执行指定的二元运算,并将结果存入累加器。立即数会符号位扩展到操作数大小,含一种格式['op_imm_8']
ashri ashri imm:i32 inout:i32 对累加器和立即数执行指定的二元运算,并将结果存入累加器。立即数会符号位扩展到操作数大小,含一种格式['op_imm_8']
Two address binary operation with immediate addiv addiv v1:out:i32, v2:in:i32, imm:i32 none
switch (op)
case addi:
  v1 = (v2 + imm) % 2^32
case subi:
  v1 = (v2 - imm) % 2^32
case muli:
  v1 = (v2 * imm) % 2^32
case andi:
  v1 = v2 & imm
case ori:
  v1 = v2 | imm
case xori:
  v1 = v2 ^ imm
case shli:
  v1 = (v2 << (imm & 0x1f)) % 2^32
case shri:
  v1 = v2 >> (imm & 0x1f)
case ashri:
  v1 = (v2 < 0) ? ~(~v2 >> imm & 0x1f) : v2 >> imm & 0x1f
对寄存器和立即数执行指定的二元运算,并将结果存入另一个寄存器。立即数会符号位扩展到操作数大小,含一种格式['op_v1_4_v2_4_imm_8']
subiv subiv v1:out:i32, v2:in:i32, imm:i32 none 对寄存器和立即数执行指定的二元运算,并将结果存入另一个寄存器。立即数会符号位扩展到操作数大小,含一种格式['op_v1_4_v2_4_imm_8']
muliv muliv v1:out:i32, v2:in:i32, imm:i32 none 对寄存器和立即数执行指定的二元运算,并将结果存入另一个寄存器。立即数会符号位扩展到操作数大小,含一种格式['op_v1_4_v2_4_imm_8']
andiv andiv v1:out:i32, v2:in:i32, imm:i32 none 对寄存器和立即数执行指定的二元运算,并将结果存入另一个寄存器。立即数会符号位扩展到操作数大小,含一种格式['op_v1_4_v2_4_imm_32']
oriv oriv v1:out:i32, v2:in:i32, imm:i32 none 对寄存器和立即数执行指定的二元运算,并将结果存入另一个寄存器。立即数会符号位扩展到操作数大小,含一种格式['op_v1_4_v2_4_imm_32']
xoriv xoriv v1:out:i32, v2:in:i32, imm:i32 none 对寄存器和立即数执行指定的二元运算,并将结果存入另一个寄存器。立即数会符号位扩展到操作数大小,含一种格式['pref_op_v1_4_v2_4_imm_32']
shliv shliv v1:out:i32, v2:in:i32, imm:i32 none 对寄存器和立即数执行指定的二元运算,并将结果存入另一个寄存器。立即数会符号位扩展到操作数大小,含一种格式['op_v1_4_v2_4_imm_8']
shriv shriv v1:out:i32, v2:in:i32, imm:i32 none 对寄存器和立即数执行指定的二元运算,并将结果存入另一个寄存器。立即数会符号位扩展到操作数大小,含一种格式['op_v1_4_v2_4_imm_8']
ashriv ashriv v1:out:i32, v2:in:i32, imm:i32 none 对寄存器和立即数执行指定的二元运算,并将结果存入另一个寄存器。立即数会符号位扩展到操作数大小,含一种格式['op_v1_4_v2_4_imm_8']
Two address integer division or modulo with immediate on accumulator divi divi imm:i32 inout:i32
if imm == 0 then
  throw ArithmeticException
end
switch(op)
case divi:
  if acc == INT32_MIN and imm == -1 then
    acc = INT32_MIN
  else
    acc = acc / imm
  end
case modi:
  if acc == INT32_MIN and imm == -1 then
    acc = 0
  else
    acc = acc % imm
  end
对累加器和立即数执行整数除法或取模运算,并将结果存入累加器。立即数会符号位扩展到操作数大小,含一种格式['op_imm_8']
modi modi imm:i32 inout:i32
Two address integer division or modulo with immediate diviv diviv v1:out:i32, v2:in:i32, imm:i32 none
if imm == 0 then
  throw ArithmeticException
end
switch(op)
case divi:
  if v1 == INT32_MIN and imm == -1 then
    v1 = INT32_MIN
  else
    v1 = v2 / imm
  end
case modi:
  if v1 == INT32_MIN and imm == -1 then
    v1 = 0
  else
    v1 = v2 % imm
  end
对寄存器和立即数执行整数除法或取模运算,并将结果存入另一个寄存器。立即数会符号位扩展到操作数大小,含一种格式['op_v1_4_v2_4_imm_8']
modiv modiv v1:out:i32, v2:in:i32, imm:i32 none
Increment register with immediate inci inci v:inout:i32, imm:i32 none vx = (vx + imm) % 2^32 使用指定的立即数增加虚拟寄存器的值。立即数会符号位扩展到操作数大小,含一种格式['op_v_4_imm_4']
三地址运算
指令组 助记符 签名 acc 伪代码 中文说明
Three address binary operation add add v1:in:i32, v2:in:i32 out:i32
switch (op)
case add:
  acc = (vs1 + vs2) % 2^32
case sub:
  acc = (vs1 - vs2) % 2^32
case mul:
  acc = (vs1 * vs2) % 2^32
case and:
  acc = vs1 & vs2
case or:
  acc = vs1 | vs2
case xor:
  acc = vs1 ^ vs2
case shl:
  acc = (vs1 << (vs2 & 0x1f)) % 2^32
case shr:
  acc = vs1 >> (vs2 & 0x1f)
case ashr:
  acc = (vs1 < 0) ? ~(~vs1 >> vs2 & 0x1f) : vs1 >> vs2 & 0x1f
对两个寄存器执行指定的二元运算,并将结果存入累加器,含一种格式['op_v1_4_v2_4']
sub sub v1:in:i32, v2:in:i32 out:i32 对两个寄存器执行指定的二元运算,并将结果存入累加器,含一种格式['op_v1_4_v2_4']
mul mul v1:in:i32, v2:in:i32 out:i32 对两个寄存器执行指定的二元运算,并将结果存入累加器,含一种格式['op_v1_4_v2_4']
and and v1:in:i32, v2:in:i32 out:i32 对两个寄存器执行指定的二元运算,并将结果存入累加器,含一种格式['pref_op_v1_4_v2_4']
or or v1:in:i32, v2:in:i32 out:i32 对两个寄存器执行指定的二元运算,并将结果存入累加器,含一种格式['pref_op_v1_4_v2_4']
xor xor v1:in:i32, v2:in:i32 out:i32 对两个寄存器执行指定的二元运算,并将结果存入累加器,含一种格式['pref_op_v1_4_v2_4']
shl shl v1:in:i32, v2:in:i32 out:i32 对两个寄存器执行指定的二元运算,并将结果存入累加器,含一种格式['pref_op_v1_4_v2_4']
shr shr v1:in:i32, v2:in:i32 out:i32 对两个寄存器执行指定的二元运算,并将结果存入累加器,含一种格式['pref_op_v1_4_v2_4']
ashr ashr v1:in:i32, v2:in:i32 out:i32 对两个寄存器执行指定的二元运算,并将结果存入累加器,含一种格式['pref_op_v1_4_v2_4']
Three address binary operation with result in register addv addv v1:inout:i32, v2:in:i32 none
switch (op)
case add:
  v1 = (v1 + v2) % 2^32
case sub:
  v1 = (v1 - v2) % 2^32
case mul:
  v1 = (v1 * v2) % 2^32
case and:
  v1 = v1 & v2
case or:
  v1 = v1 | v2
case xor:
  v1 = v1 ^ v2
case shl:
  v1 = (v1 << (v2 & 0x1f)) % 2^32
case shr:
  v1 = v1 >> (v2 & 0x1f)
case ashr:
  v1 = (v1 < 0) ? ~(~v1 >> v2 & 0x1f) : v1 >> v2 & 0x1f
对两个寄存器执行指定的二元运算,并将结果存入寄存器,含一种格式['op_v1_4_v2_4']
subv subv v1:inout:i32, v2:in:i32 none 对两个寄存器执行指定的二元运算,并将结果存入寄存器,含一种格式['op_v1_4_v2_4']
mulv mulv v1:inout:i32, v2:in:i32 none 对两个寄存器执行指定的二元运算,并将结果存入寄存器,含一种格式['op_v1_4_v2_4']
andv andv v1:inout:i32, v2:in:i32 none 对两个寄存器执行指定的二元运算,并将结果存入寄存器,含一种格式['pref_op_v1_4_v2_4']
orv orv v1:inout:i32, v2:in:i32 none 对两个寄存器执行指定的二元运算,并将结果存入寄存器,含一种格式['pref_op_v1_4_v2_4']
xorv xorv v1:inout:i32, v2:in:i32 none 对两个寄存器执行指定的二元运算,并将结果存入寄存器,含一种格式['pref_op_v1_4_v2_4']
shlv shlv v1:inout:i32, v2:in:i32 none 对两个寄存器执行指定的二元运算,并将结果存入寄存器,含一种格式['pref_op_v1_4_v2_4']
shrv shrv v1:inout:i32, v2:in:i32 none 对两个寄存器执行指定的二元运算,并将结果存入寄存器,含一种格式['pref_op_v1_4_v2_4']
ashrv ashrv v1:inout:i32, v2:in:i32 none 对两个寄存器执行指定的二元运算,并将结果存入寄存器,含一种格式['pref_op_v1_4_v2_4']
Three address integer division or modulo div div v1:in:i32, v2:in:i32 out:i32
if vs2 == 0 then
  throw ArithmeticException
end
switch(op)
case div:
  if vs1 == INT32_MIN and vs2 == -1 then
    acc = INT32_MIN
  else
    acc = vs1 / vs2
  end
case mod:
  if vs1 == INT32_MIN and vs2 == -1 then
    acc = 0
  else
    acc = vs1 % vs2
  end
对两个寄存器执行整数除法或取模运算,并将结果存入累加器,含一种格式['op_v1_4_v2_4']
mod mod v1:in:i32, v2:in:i32 out:i32
Three address integer division or modulo with result in register divv divv v1:inout:i32, v2:in:i32 none
if v2 == 0 then
  throw ArithmeticException
end
switch(op)
case div:
  if v1 == INT32_MIN and v2 == -1 then
    v1 = INT32_MIN
  else
    v1 = v1 / v2
  end
case mod:
  if v1 == INT32_MIN and v2 == -1 then
    v1 = 0
  else
    v1 = v1 % v2
  end
对两个寄存器执行整数除法或取模运算,并将结果存入寄存器,含一种格式['op_v1_4_v2_4']
modv modv v1:inout:i32, v2:in:i32 none

控制流 (Control Flow)

这类指令控制程序的执行流程,如跳转、方法调用和返回:

指令组 助记符 签名 acc 伪代码 中文说明
Unconditional jump jmp jmp imm:i32 none pc += imm 无条件地将执行转移到距离当前指令开头 offset 字节处的指令。Offset 会符号位扩展到指令地址的大小。含三种格式['op_imm_8', 'op_imm_16', 'op_imm_32']
Conditional object comparison jump jeq.obj jeq.obj v:in:ref, imm:i32 in:ref
switch (cc)
case eq:
  if acc == vs then
    pc += imm
  end
case ne:
  if acc != vs then
    pc += imm
end
如果累加器和源寄存器中的对象引用按指定方式比较为真,则将执行转移到距离当前指令开头 offset 字节处的指令。Offset 会符号位扩展到指令地址的大小。含二种格式['op_v_8_imm_8', 'op_v_8_imm_16']
jne.obj jne.obj v:in:ref, imm:i32 in:ref
Conditional compared to null jump jeqz.obj jeqz.obj imm:i32 in:ref
switch (cc)
case eq:
  if acc == null then
    pc += imm
  end
case ne:
  if acc != null then
    pc += imm
end
如果累加器中的对象引用与 null 的比较按指定方式为真,则将执行转移到距离当前指令开头 offset 字节处的指令。Offset会符号位扩展到指令地址的大小。含二种格式['op_imm_8', 'op_imm_16']
jnez.obj jnez.obj imm:i32 in:ref
Conditional compared to zero jump jeqz jeqz imm:i32 in:i32
switch (cc)
case eq:
  if acc == 0 then
    pc += imm
  end
case ne:
  if acc != 0 then
    pc += imm
  end
case lt:
  if acc < 0 then
    pc += imm
  end
case gt:
  if acc > 0 then
    pc += imm
  end
case le:
  if acc <= 0 then
    pc += imm
  end
case ge:
  if acc >= 0 then
    pc += imm
  end
如果累加器中的有符号32位整数与0的比较按指定方式为真,则将执行转移到距离当前指令开头 offset 字节处的指令。Offset会符号位扩展到指令地址的大小。含二种格式['op_imm_8', 'op_imm_16']
jnez jnez imm:i32 in:i32
jltz jltz imm:i32 in:i32
jgtz jgtz imm:i32 in:i32
jlez jlez imm:i32 in:i32
jgez jgez imm:i32 in:i32
Conditional compared to register jump jeq jeq v:in:i32, imm:i32 in:i32
diff = signed_cmp(acc, vs)
switch (cc)
case eq:
  if diff == 0 then
    pc += imm
  end
case ne:
  if diff != 0 then
    pc += imm
  end
case lt:
  if diff < 0 then
    pc += imm
  end
case gt:
  if diff > 0 then
    pc += imm
  end
case le:
  if diff <= 0 then
    pc += imm
  end
case ge:
  if diff >= 0 then
    pc += imm
  end
如果累加器和寄存器中的有符号32位整数按指定方式比较为真,则将执行转移到距离当前指令开头 offset 字节处的指令。Offset 会符号位扩展到指令地址的大小。含二种格式['op_v_8_imm_8', 'op_v_8_imm_16']
jne jne v:in:i32, imm:i32 in:i32
jlt jlt v:in:i32, imm:i32 in:i32
jgt jgt v:in:i32, imm:i32 in:i32
jle jle v:in:i32, imm:i32 in:i32
jge jge v:in:i32, imm:i32 in:i32
Return value from method return return in:b32 return acc 从当前方法返回,返回值在累加器中。含一种格式['op_none']
return.64 return.64 in:b64
return.obj return.obj in:ref
Return from a void method return.void return.void none return 从无返回值的方法返回。调用者不能使用累加器的值。含一种格式['op_none']
Throw exception throw throw v:in:ref none
if vs == null then
  throw NullPointerException
end
throw vs
抛出寄存器中的异常对象。含一种格式['op_v_8']
Static call call.short call.short method_id, v1:in:top, v2:in:top out:top
if not initialized(type)
  initialize(type)
end
acc = call(method, args)
调用静态方法,创建新帧并传递参数。含一种格式['op_v1_4_v2_4_id_16']
call call method_id, v1:in:top, v2:in:top, v3:in:top, v4:in:top out:top 调用静态方法,创建新帧并传递参数。含一种格式['op_v1_4_v2_4_v3_4_v4_4_id_16']
call.range call.range method_id, v:in:top out:top 调用静态方法,创建新帧并传递参数。含一种格式['op_v_8_id_16']
Static call with accumulator as input call.acc.short call.acc.short method_id, v:in:top, imm:u1 inout:top
args = []
for (i = 0; i < 4; ++i)
  if (i < imm) then
    args[i] = regs[i]
  else if (i == imm) then
    args[i] = acc
  else
    args[i] = regs[i - 1]
  end
end
method = resolve(method_id)
acc = call(method, args)
调用静态方法,累加器作为参数之一。含一种格式['op_v_4_imm_4_id_16']
call.acc call.acc method_id, v1:in:top, v2:in:top, v3:in:top, imm:u2 inout:top 调用静态方法,累加器作为参数之一。含一种格式['op_v1_4_v2_4_v3_4_imm_4_id_16']
Virtual calls call.virt.short call.virt.short method_id, v1:in:top, v2:in:top out:top
if args[0] == null then
  throw NullPointerException
end
method = resolve(method_id)
if is_abstract(method) then
  throw AbstractMethodError
end
acc = call(method, args)
调用虚方法,根据对象引用解析方法。含一种格式['op_v1_4_v2_4_id_16']
call.virt call.virt method_id, v1:in:top, v2:in:top, v3:in:top, v4:in:top out:top 调用虚方法,根据对象引用解析方法。含一种格式['op_v1_4_v2_4_v3_4_v4_4_id_16']
call.virt.range call.virt.range method_id, v:in:top out:top 调用虚方法,根据对象引用解析方法。含一种格式['op_v_8_id_16']
Virtual calls with accumulator as input call.virt.acc.short call.virt.acc.short method_id, v:in:top, imm:u1 inout:top
args = []
for (i = 0; i < 4; ++i)
  if (i < imm) then
    args[i] = regs[i]
  else if (i == imm) then
    args[i] = acc
  else
    args[i] = regs[i - 1]
  end
end
if args[0] == null then
  throw NullPointerException
end
method = resolve(method_id)
if is_abstract(method) then
  throw AbstractMethodError
end
acc = call(method, args)
调用虚方法,累加器作为参数之一。含一种格式['op_v_4_imm_4_id_16']
call.virt.acc call.virt.acc method_id, v1:in:top, v2:in:top, v3:in:top, imm:u2 inout:top 调用虚方法,累加器作为参数之一。含一种格式['op_v1_4_v2_4_v3_4_imm_4_id_16']

类型与对象操作 (Type & Object Operations)

这类指令用于创建和操作对象、数组,以及进行类型检查:

指令组 助记符 签名 acc 伪代码 中文说明
Array length lenarr lenarr v:in:top[] out:i32
if x == null
      then
  throw NullPointerException
end
acc = len(vs)
获取数组长度并放入累加器,含一种格式['op_v_8']
Create new array newarr newarr v1:out:ref, v2:in:i32, type_id none
if v2 < 0 then
  throw NegativeArraySizeException
end
type = resolve(type_id)
v1 = new_array(v2, type)
if not initialized(type)
  initialize(type)
end
if v1 == null then
  throw OutOfMemoryError
end
创建给定类型和大小的新一维数组,并将其引用放入寄存器,含一种格式['op_v1_4_v2_4_id_16']
Create new object newobj newobj v:out:ref, type_id none
type
      = resolve(type_id)
if not initialized(type)
  initialize(type)
end
v = new_instance(type)
if v == null then
  throw OutOfMemoryError
end
根据type_id解析类,为对象分配内存,并将其引用放入寄存器,含一种格式['op_v_8_id_16']
Create new object and call initializer initobj.short initobj.short method_id, v1:in:none, v2:in:none < code>out:ref
init, type = resolve(method_id)
if not initialized(type)
  initialize(type)
end
acc = new_instance(type)
if acc == null then
  throw OutOfMemoryError
end
init(acc, args)
根据初始化方法的method_id解析类,分配内存,调用初始化方法,并将新对象的引用放入累加器,含一种格式['op_v1_4_v2_4_id_16']
initobj initobj method_id, v1:in:none, v2:in:none, v3:in:none, v4:in:none out:ref 根据初始化方法的method_id解析类,分配内存,调用初始化方法,并将新对象的引用放入累加器,含一种格式['op_v1_4_v2_4_v3_4_v4_4_id_16']
initobj.range initobj.range method_id, v:in:none out:ref 根据初始化方法的method_id解析类,分配内存,调用初始化方法,并将新对象的引用放入累加器,含一种格式['op_v_8_id_16']
Check cast checkcast checkcast type_id in:ref
type =
      resolve(type_id)
if acc != null and !has_type(acc, type) then
  throw ClassCastException
end
根据ID解析对象类型,如果累加器中的对象可以转换为该类型,则累加器内容不变。否则抛出ClassCastException,含一种格式['op_id_16']
Is instance isinstance isinstance type_id inout:ref->i32

      type = resolve(type_id)
if acc == null or !has_type(acc, type) then
  acc = 0
else
  acc = 1
end
根据ID解析对象类型,如果累加器中的对象是该类型的实例,则将1放入累加器,否则放入0,含一种格式['op_id_16']

类型转换 (Type Conversion)

这类指令用于在不同的原始数据类型之间进行转换:

指令组 助记符 签名 acc 伪代码 中文说明
Conversions between integer and floating point types i32tof32 i32tof32 inout:i32->f32 (dest_type) acc = (src_type) acc 对累加器执行指定的原始类型转换。从浮点到整数的转换遵循向零舍入的规则,并处理溢出、无穷大和NaN的情况,含一种格式['pref_op_none']
i32tof64 i32tof64 inout:i32->f64
u32tof32 u32tof32 inout:u32->f32
u32tof64 u32tof64 inout:u32->f64
i64tof32 i64tof32 inout:i64->f32
i64tof64 i64tof64 inout:i64->f64
u64tof32 u64tof32 inout:u64->f32
u64tof64 u64tof64 inout:u64->f64
f32tof64 f32tof64 inout:f32->f64
f32toi32 f32toi32 inout:f32->i32
f32toi64 f32toi64 inout:f32->i64
f32tou32 f32tou32 inout:f32->u32
f32tou64 f32tou64 inout:f32->u64
f64toi32 f64toi32 inout:f64->i32
f64toi64 f64toi64 inout:f64->i64
f64tou32 f64tou32 inout:f64->u32
f64tou64 f64tou64 inout:f64->u64
f64tof32 f64tof32 inout:f64->f32
Conversions from integer types to u1 i32tou1 i32tou1 inout:i32->u1 (dest_type) acc = (src_type) acc 从整数类型到u1(布尔)的转换:如果整数不为零,结果为1;否则结果为0,含一种格式['pref_op_none']
i64tou1 i64tou1 inout:i64->u1
u32tou1 u32tou1 inout:u32->u1
u64tou1 u64tou1 inout:u64->u1
Integer truncations and extensions. i32toi64 i32toi64 inout:i32->i64 (dest_type) acc = (src_type) acc 对累加器执行指定的整数扩展或截断。截断保留目标类型的位数。扩展根据源类型是否为有符号,进行符号位扩展或零扩展,含一种格式['pref_op_none']
i32toi16 i32toi16 inout:i32->i16
i32tou16 i32tou16 inout:i32->u16
i32toi8 i32toi8 inout:i32->i8
i32tou8 i32tou8 inout:i32->u8
i64toi32 i64toi32 inout:i64->i32
u32toi64 u32toi64 inout:u32->i64
u32toi16 u32toi16 inout:u32->i16
u32tou16 u32tou16 inout:u32->u16
u32toi8 u32toi8 inout:u32->i8
u32tou8 u32tou8 inout:u32->u8
u64toi32 u64toi32 inout:u64->i32
u64tou32 u64tou32 inout:u64->u32

动态语言支持 (Dynamic Language Support)

这些是专门为动态类型语言设计的特殊指令:

指令组 助记符 签名 acc 伪代码 中文说明
Dynamic move register-to-register mov.dyn mov.dyn v1:out:any, v2:in:any none vd = vs 在寄存器之间移动 'any' 类型的值,含两种格式['op_v1_8_v2_8', 'op_v1_16_v2_16']
Dynamic load accumulator from register lda.dyn lda.dyn v:in:any out:any acc = v 将 'any' 类型的值从寄存器移动到累加器,含一种格式['op_v_8']
Dynamic store accumulator sta.dyn sta.dyn v:out:any in:any v = acc 将 'any' 类型的值从累加器移动到寄存器,含一种格式['op_v_8']
Dynamic load accumulator from immediate ldai.dyn ldai.dyn imm:i32 out:any acc = imm 将立即数作为 'any' 类型值移动到累加器,含一种格式['op_imm_32']
fldai.dyn fldai.dyn imm:f64 out:any 将立即数作为 'any' 类型值移动到累加器,含一种格式['op_imm_64']

PandaIR

总述

Panda IR是方舟编译器(鸿蒙编译器)内部使用的中间表示,它在将高级语言代码转换为目标机器码的过程中扮演着至关重要的角色。设计文档可见ir_format.md,下面主要参考它做些解释。

核心设计目标

Panda IR的设计主要围绕以下几个核心目标:

  1. 强大的优化与分析能力:IR的结构必须清晰,能够方便地实现和集成各种复杂的代码优化算法和分析过程
  2. 全面的特性支持:完全支持Panda字节码的所有功能和指令
  3. 聚焦目标架构:当前主要以ARM64作为核心目标架构进行优化
  4. 可控的编译开销:在即时编译(JIT)场景下,力求将每条字节码指令的编译开销控制在约十万条本地指令以内,这是JIT编译器的行业标准
  5. 良好的兼容性:具备与其他IR(如LLVM IR)相互转换的能力,以便利用其他编译器的优化成果或进行性能对比

优化与分析框架

Panda IR的一个关键特性是其对优化与分析(即Pass)的灵活管理。编译器开发者可以根据需要调整Pass的执行顺序、增删Pass,从而为不同的优化创造有利条件。例如,某些优化会为后续的其他优化准备好代码上下文。

Panda IR支持一系列丰富的优化,包括但不限于:IR构建分支消除检查消除Cleanup常量折叠内敛优化循环不变量移出Lowering加载存储消除(LSE)等(所有文档直接看这个目录吧)。同时,它也内置了多种分析能力,如别名分析、边界分析、支配树和活跃度分析和,线性序和逆后续(RPO)访问(访问者模式),这些都是实现高级优化的基础。

双层指令集

为了平衡高级语言特性与底层硬件优化的需求,Panda IR巧妙地采用了一种包含高阶和低阶指令的混合设计。

  • 高阶指令:在编译初期,Panda字节码被直接转换成与之一一对应的高阶IR指令。这个阶段主要进行与具体硬件架构无关的通用优化。
  • 低阶指令:在后续阶段,高阶指令会被“降级”(Lowering)为多条更接近机器指令的低阶指令。这个阶段主要进行针对ARM64架构的特定优化。

这种设计使得Panda IR既能高效地表示源码的语义,又能精细地控制最终生成的机器码。

IR结构

Panda IR选择了“基于控制流图(CFG)和静态单赋值(SSA)”的结构,而非结构更复杂的“Sea-of-Nodes”。这使得它在保证强大功能的同时,构建IR的开销更小,特别适合需要快速生成代码的轻量级编译场景。

其核心结构由以下几个部分组成:

  • Graph:这是最顶层的容器,包含了关于当前方法的所有编译信息,如基本块列表、运行时接口、Pass管理器等,它可以创建/添加新指令,增删BB,构造PRO和支配树等。
  • BasicBlock(基本块):代表了一段线性的、没有分支的指令序列。每个基本块都维护着其前驱和后继块的列表,形成了控制流图。一个Graph总是从一个没有前驱的Start块开始,到没有后继的End块结束。
  • Instruction(指令):指令通过类继承体系实现。所有指令都派生自一个基础的Inst类,它定义了操作码、对应字节码PC、所属BB、前驱和后继指令、类型、输入(每个源操作数数据来自的指令地址)、用户(User 目的操作数被使用的指令地址)等通用信息。根据输入操作数的数量是固定的还是动态的,又分为FixedInputsInstDynamicInputsInst等子类。常量和函数参数作为特殊的指令,统一存放在Start块中,并保证全局唯一。为了方便管理,所有指令的属性都通过一个名为instructions.yaml的配置文件来定义和生成。

数据流表示

指令之间的输入和输出关系构成了数据流图,其访问效率直接影响JIT编译的开销。Panda IR对此进行了专门优化。指令的输入数通常是确定的,所以它根据指令的输入数量是固定的还是动态的,采用了不同的内存存储策略。对于绝大多数输入固定的指令,其输入输出信息与指令对象本身连续存储,访问效率极高。对于少数动态输入的指令(如Phi指令),则采用可动态扩容的独立存储。而指令的用户数是无法预知的,所以没有相关区分:

def-use-structure

从IR到机器码的完整流程

Panda IR经过一系列优化后,会经历以下几个阶段最终生成可执行的机器码:

  1. Lowering(降级):将高阶IR指令转换为更接近机器码的低阶指令。
  2. Register Allocation(寄存器分配):负责将指令的操作数分配到CPU寄存器。Panda IR目前采用开销较低的“线性扫描”(Linear Scan)算法,这对于注重编译速度的JIT模式至关重要。
  3. Code Generation(代码生成):这是最后一步,将优化和分配好寄存器的IR转换为真正的ARM64机器码。为了快速实现和保证可靠性,Panda IR选择了ARM官方开发的vixl库来完成这一过程。

指令细节

对Panda IR有个大概了解后,可以看指令细节了,前面提到Panda IR也是使用yaml定义,配合templates目录下的大量模板来生成实际代码的,我就从对这个文件的解析开始,一条指令定义如下:

instructions:
  - opcode: Neg # 操作码
    base: UnaryOperation # 指令基类 一元操作
    signature: [d-number, number] # 指令签名,即它的源和目的操作数信息
    flags: [acc_write, acc_read, ifcvt] # 指令属性 如这里有acc的读写,可被用于ifcvt优化
    description: Negate the value.  # 指令描述
    verification:   # 验证模板
      - $verify_unary

下面就先说下签名:

指令签名

先列出操作数类型token说明:

操作数标记 说明
d 标记该操作数为目的操作数(输出)
i8 8位有符号整数
i16 16位有符号整数
i32 32位有符号整数
i64 64位有符号整数
u8 8位无符号整数
u16 16位无符号整数
u32 32位无符号整数
u64 64位无符号整数
f32 单精度浮点数
f64 双精度浮点数
bool 布尔类型
ref 对象引用类型
ptr 指针类型
void Void类型,表示无返回值
int 所有整数类型(bool, i8...u64)的并集
float f32 和 f64 的并集
number int 和 float 的并集,代表所有数字类型
real number 和 ref 的并集,代表数字或引用
any 类型未被静态定义(用于动态语言)
pseudo 伪目的操作数,表示指令实际上不写入该寄存器
zc 零检查,表示该输入必须是ZeroCheck指令的结果
bc 边界检查,表示该输入必须是BoundsCheck指令的结果
nc 空检查,表示该输入必须是NullCheck指令的结果
ngc 负数检查,表示该输入应为NegativeCheck指令的结果
save_state 表示该输入必须是SaveState指令
dyn 动态操作数,表示该操作数可以重复零次或多次

签名中操作数间用逗号,分割,而单个操作数以短横线-分割指定多个属性或类型,如果有目的操作数那么它必须是第一个且有d类型。接着是指令的属性:

指令属性

Flag 含义
cf 指令影响代码的控制流(Control Flow),例如跳转、返回等
terminator 这是一个终结指令,表示当前基本块的执行会在此结束
load 指令会从内存中加载数据
store 指令会向内存中存储数据
can_throw 指令在执行过程中可能会抛出异常
call 这是一个函数或方法调用指令
is_check 这是一个运行时检查指令(如空指针检查、边界检查),通常也带有can_throw标志
no_dce 指令不能被死代码消除(Dead Code Elimination)优化所移除
no_cse 指令不能被公共子表达式消除(Common Subexpression Elimination)优化
no_dst 指令没有目标操作数(即不产生结果)
no_nullptr 指令的执行结果不会是nullptr
pseudo_dst 指令有一个伪目标操作数,它不实际影响数据流
implicit_runtime_call 指令在加载源操作数之前可能会调用运行时,这期间垃圾回收(GC)可能会移动对象
low_level 这是一个低阶指令,更接近于机器码
no_hoist 该指令不能被循环不变量外提(Loop Hoisting)等优化提前执行
barrier 该指令是指令调度(Instruction Scheduling)的一个屏障,不允许其他指令跨越它重排
ref_special 一个特殊的引用相关指令,不能被移动到运行时调用的另一侧
ifcvt 该指令可以被用于if-conversion优化(将分支转换为无分支的条件执行指令)
require_state 该指令可能会调用运行时,因此需要一个SaveState作为其输入,以保存当前状态
runtime_call 该指令必定会调用运行时(Runtime)
commutative 该指令满足交换律,即操作数的顺序不影响结果(如Add, Or, And)
alloc 该指令会在堆(Heap)上分配一个新的对象
acc_read 该指令会读取累加器(Accumulator)寄存器
acc_write 该指令会写入累加器(Accumulator)寄存器
heap_inv 该指令会使已知的堆信息失效(Invalidates heap)
mem_barrier 该指令之后需要插入一个内存屏障
native 该指令用于生成非托管(Native)代码
can_deoptimize 该指令的执行可能会触发去优化(Deoptimization),并切换到解释器执行
catch_input 该指令用于catch块中
require_tmp 该指令的实现需要一个额外的通用临时寄存器

指令表

Panda IR本身不是拿来手写的,通常使用它会直接使用CreateXX去创建指令,它的构造函数会也会设置Input,所以它不像字节码有助记形式的表达式,不过官方也提供了一个口之,若想手写IR可使用irtoc,详见irtoc.md文档:

irtoc_build_flow

现在列出存在的所有指令与说明:

算数/比较运算
指令名称 指令签名 指令标志 指令说明
Neg d-number, number acc_write, acc_read, ifcvt 取反
Abs d-number, number acc_write, ifcvt 取绝对值
Sqrt d-float, float acc_write, ifcvt 计算平方根
Not d-int-bool, int-bool acc_write, acc_read, ifcvt 按位取反
Add d-number-ptr, number-ptr, number-ptr commutative, acc_write, acc_read, ifcvt 将两个输入相加
Sub d-number-ptr, number-ptr, number-ptr acc_write, acc_read, ifcvt 将两个输入相减
Mul d-number, number, number commutative, acc_write, acc_read, ifcvt 将两个输入相乘
MulOverflowCheck d-int, int, int, save_state no_dce, no_hoist, no_cse, barrier, acc_read, require_state, can_deoptimize 两数相乘,如果发生溢出则去优化
Div d-number, number, number-zc acc_write, acc_read 将两个输入相除
Mod d-number, number, number-zc acc_write, acc_read 取模
Min d-number, number, number commutative, ifcvt 从两个输入中获取最小值
Max d-number, number, number commutative, ifcvt 从两个输入中获取最大值
Shl d-int, int, int acc_write, acc_read, ifcvt 左移
Shr d-int, int, int acc_write, acc_read, ifcvt 右移
AShr d-int, int, int acc_write, acc_read, ifcvt 算术左移
And d-int-bool, int-bool, int-bool commutative, acc_write, acc_read, ifcvt 按位与
Or d-int-bool, int-bool, int-bool commutative, acc_write, acc_read, ifcvt 按位或
Xor d-int-bool, int-bool, int-bool commutative, acc_write, acc_read, ifcvt 按位异或
Compare d-bool, real-any, real-any acc_read, acc_write, ifcvt 根据条件码比较两个整数或引用值
Cmp d-int, number, number acc_read, acc_write, ifcvt 比较两个浮点数或整数值
CompareAnyType d-bool, any acc_read, acc_write, ifcvt 比较任意动态类型的值和类型
GetAnyTypeName d-any acc_read, acc_write, ifcvt, no_hoist, ref_special 获取带有任意类型名称的字符串
CastAnyTypeValue d-real, any acc_read, acc_write 将任意动态类型值转换为编译器类型值
CastValueToAnyType d-any, real acc_read, acc_write 将编译器类型值转换为任意动态类型值
Cast d-real, real acc_read, acc_write, ifcvt 将值从一种类型转换到另一种
Bitcast d-real-ptr, real-ptr ifcvt 将值从一种类型按位转换为另一种
Constant d-i32-i64-f32-f64-any no_cse, ifcvt 常量值
Parameter d-real-any no_cse, no_hoist 方法的参数
NullPtr d-ref no_cse, no_hoist, ifcvt 空指针
LoadUndefined d-ref load 从TLS加载Undefined对象
对象和数组指令
指令名称 指令签名 指令标志 指令说明
NewArray d-ref, ref, int-ngc, save_state can_throw, no_dce, no_hoist, no_cse, alloc, require_state, runtime_call, mem_barrier 创建新数组
NewObject d-ref, ref, save_state can_throw, no_dce, no_hoist, no_cse, alloc, require_state, runtime_call, mem_barrier 创建新对象
InitObject d-ref, real-dyn, save_state can_throw, no_dce, no_hoist, no_cse, alloc, require_state, runtime_call, acc_write 创建新对象并调用其默认构造函数
InitEmptyString d-ref, save_state can_throw, no_dce, no_hoist, no_cse, alloc, require_state, runtime_call, acc_write 创建并初始化空字符串
InitString d-ref, ref, save_state can_throw, no_dce, no_hoist, no_cse, barrier, alloc, require_state, runtime_call, acc_write 创建并初始化字符串
LoadArray d-real-any, ref-any-nc, int-bc load, no_hoist, no_cse, acc_read, acc_write 从数组加载值
LoadCompressedStringChar d-u16, ref-nc, int-bc, int load, no_cse, acc_read, acc_write 当字符串压缩启用时,从字符串加载字符
StoreArray ref-nc-any, int-bc, real-any store, no_dce, no_hoist, no_cse, acc_read, require_tmp 在数组中存储值
LoadObject d-real-any, ref-nc-any load, no_hoist, no_cse, acc_write 从对象的字段加载值
LoadObjectDynamic d-any, ref-any, any, any load, no_hoist, no_cse, acc_write 从动态对象的字段加载值
Load d-real-any, ptr-ref, int native, low_level, load, no_hoist, no_cse 按偏移从内存加载值
LoadI d-real-any, ptr-ref native, low_level, load, no_hoist, no_cse 按立即数偏移从内存加载值
Store ptr-ref-any, int, real-any native, low_level, store, no_dce, no_hoist, no_cse 按偏移在内存中存储值
StoreI ptr-ref, real-any native, low_level, store, no_dce, no_hoist, no_cse 按立即数偏移在内存中存储值
ResolveObjectField d-u32, save_state can_throw, no_dce, require_state, runtime_call 解析对象的字段
LoadResolvedObjectField d-real, ref-nc, u32 load, no_hoist, no_cse, acc_write 从已解析的对象字段加载值
StoreObject ref-nc, real-any store, no_dce, no_hoist, no_cse, acc_read 在对象的字段中存储值
StoreObjectDynamic d-any, ref-any, any, any, any store, no_dce, no_hoist, no_cse, acc_read 在动态对象的字段中存储值
StoreResolvedObjectField ref-nc, real, u32 store, no_dce, no_hoist, no_cse, acc_read, require_tmp 在已解析的对象字段中存储值
LoadStatic d-real, ref load, no_hoist, no_cse, acc_write 从类加载静态字段
ResolveObjectFieldStatic d-ref, save_state can_throw, no_dce, require_state, runtime_call 解析对象的静态字段
LoadResolvedObjectFieldStatic d-real, ref load, no_hoist, no_cse, acc_write 从已解析的静态对象字段加载值
StoreStatic ref, real store, no_dce, no_hoist, no_cse, acc_read 将值存储到静态字段
UnresolvedStoreStatic real, save_state store, can_throw, no_dce, no_hoist, no_cse, barrier, require_state, runtime_call, implicit_runtime_call, require_tmp 将值存储到未解析的静态字段
StoreResolvedObjectFieldStatic ref, real store, no_dce, no_hoist, no_cse 将值存储到已解析的静态字段
LenArray d-int, ref-nc acc_write 获取数组的长度
LoadString d-ref, save_state load, can_throw, no_hoist, no_cse, require_state, runtime_call, acc_write 从常量池加载字符串
LoadFromConstantPool d-any, any load, acc_write 从动态常量池加载对象
LoadConstArray d-ref, save_state load, can_throw, no_hoist, no_dce, no_cse, require_state, barrier, runtime_call 通过id创建新的常量数组并填充其内容
LoadConstantPool d-any, any load 从函数对象加载常量池
LoadLexicalEnv d-any, any load 从函数对象加载词法环境
FillConstArray ref, save_state store, no_hoist, no_dce, no_cse, no_dst, require_state, barrier, runtime_call, require_tmp 填充常量数组的内容
LoadType d-ref, save_state load, can_throw, no_hoist, no_cse, require_state, runtime_call, acc_write 从常量池加载类型
UnresolvedLoadType d-ref, save_state load, can_throw, no_dce, no_hoist, barrier, no_cse, require_state, runtime_call 从常量池加载未解析的类型
CheckCast ref, ref, save_state can_throw, no_dce, no_hoist, no_cse, require_state, runtime_call, acc_read 检查对象是否可以转换为指定类型
IsInstance d-bool, ref, ref, save_state require_state, runtime_call, acc_read, acc_write 检查对象是否是指定类型的实例
InitClass save_state can_throw, no_hoist, barrier, no_dce, require_state, runtime_call 在内联函数前调用类的初始化
LoadClass d-ref, save_state can_throw, no_hoist, barrier, no_dce, require_state, runtime_call 加载类
LoadAndInitClass d-ref, save_state can_throw, no_dce, no_hoist, barrier, require_state, runtime_call 在LoadStatic和StoreStatic之前调用类的初始化
UnresolvedLoadAndInitClass d-ref, save_state can_throw, no_dce, no_cse, no_hoist, barrier, require_state, runtime_call 为未解析的类调用初始化
LoadRuntimeClass d-ref ref_special 从线程寄存器获取特殊的运行时类
GetInstanceClass d-ref, ref ref_special 从输入对象获取类
LoadImmediate d-ref-ptr-any ifcvt 从立即数获取对象
FunctionImmediate d-any load, ifcvt 从立即数获取函数
LoadObjFromConst d-ref-any load, ifcvt 从句柄/常量内存加载对象
GetGlobalVarAddress d-ref, any, save_state can_throw, no_hoist, barrier, no_dce, require_state, runtime_call 获取全局变量地址
检查指令
指令名称 指令签名 指令标志 指令说明
NullCheck d-ref-pseudo, ref, save_state can_throw, is_check, no_dce, no_hoist, no_cse, require_state 检查对象是否不为null
BoundsCheck d-int-pseudo, int, int, save_state can_throw, is_check, no_dce, no_cse, no_hoist, require_state, acc_read 检查值是否在指定边界内
RefTypeCheck d-ref-pseudo, ref, ref, save_state can_throw, no_dce, is_check, no_hoist, no_cse, require_state, runtime_call 检查存储的引用类型是否与数组类型匹配
ZeroCheck d-int-pseudo, int, save_state can_throw, is_check, no_dce, no_cse, no_hoist, require_state 检查整数值是否不为零
NegativeCheck d-int-pseudo, int, save_state can_throw, is_check, no_dce, no_cse, no_hoist, require_state 检查整数值是否小于零
NotPositiveCheck d-int-pseudo, int, save_state can_throw, is_check, no_dce, no_cse, no_hoist, require_state 检查整数值是否小于或等于零
ObjByIndexCheck d-any-pseudo, any, save_state can_throw, is_check, no_dce, no_cse, no_hoist, require_state, can_deoptimize 检查按索引访问对象是否是访问数组
AnyTypeCheck d-any-pseudo, any, save_state can_throw, no_dce, no_cse, is_check, no_hoist, require_state, runtime_call, can_deoptimize 比较任意类型值和类型,如果类型不同则去优化
HclassCheck ref, save_state can_throw, no_dce, no_cse, is_check, no_hoist, require_state, runtime_call, can_deoptimize 检查对象的hclass是否适用于动态调用
Deoptimize save_state cf, can_throw, no_dce, no_cse, no_hoist, require_state, barrier, can_deoptimize, terminator 无条件切换到解释器
DeoptimizeIf any-int-bool, save_state can_throw, no_dce, no_cse, no_hoist, require_state, acc_read, barrier, can_deoptimize 如果值为true,则跳转到解释器
DeoptimizeCompare any-int-bool-ref-ptr, any-int-bool-ref-ptr, save_state can_throw, no_dce, no_cse, no_hoist, require_state, acc_read, barrier, can_deoptimize, low_level 比较,如果结果为true,则跳转到解释器
DeoptimizeCompareImm any-int-bool-ref, save_state can_throw, no_dce, no_cse, no_hoist, require_state, acc_read, barrier, can_deoptimize, low_level 与立即数比较,如果结果为true,则跳转到解释器
IsMustDeoptimize d-bool no_cse, no_hoist 检查当前方法是否必须去优化
控制流指令
指令名称 指令签名 指令标志 指令说明
ReturnVoid cf, no_dce, no_hoist, no_cse, barrier, terminator 从方法返回
Return real-any cf, no_dce, no_hoist, no_cse, barrier, acc_read, terminator 从方法返回一个值
ReturnInlined save_state no_dce, no_hoist, no_cse, barrier, require_state, acc_read 从内联方法返回
Throw ref, save_state cf, can_throw, no_dce, no_hoist, no_cse, barrier, require_state, terminator 抛出异常
调用指令
指令名称 指令签名 指令标志 指令说明
CallStatic d-real-void, real-dyn, save_state can_throw, no_dce, no_hoist, no_cse, barrier, require_state, runtime_call, call, acc_write, acc_read 调用静态方法
ResolveStatic d-ptr, save_state can_throw, barrier, require_state, runtime_call 为CallResolvedStatic准备方法地址
CallResolvedStatic d-real-void, ptr, real-dyn, save_state can_throw, no_dce, no_hoist, no_cse, barrier, require _state, call, acc_write, acc_read 调用由ResolveStatic解析的静态方法
CallVirtual d-real-void, ref-nc, real-dyn, save_state can_throw, no_dce, no_hoist, no_cse, barrier, require_state, runtime_call, call, acc_write, acc_read 调用虚方法
ResolveVirtual d-ptr, ref-nc, save_state can_throw, barrier, require_state, runtime_call 为CallResolvedVirtual准备方法地址
CallResolvedVirtual d-real-void, ptr, ref-nc, real-dyn, save_state can_throw, no_dce, no_hoist, no_cse, barrier, require_state, call, acc_write, acc_read 调用由ResolveVirtual解析的虚方法
CallLaunchStatic d-real-void, real-dyn, save_state can_throw, no_dce, no_hoist, no_cse, barrier, require_state, runtime_call, call, acc_write, acc_read 启动静态方法
CallResolvedLaunchStatic d-real-void, ptr, real-dyn, save_state can_throw, no_dce, no_hoist, no_cse, barrier, require_state, call, acc_write, acc_read 启动由ResolveStatic解析的静态方法
CallLaunchVirtual d-real-void, ref-nc, real-dyn, save_state can_throw, no_dce, no_hoist, no_cse, barrier, require_state, runtime_call, call, acc_write, acc_read 启动虚方法
CallResolvedLaunchVirtual d-real-void, ptr, ref-nc, real-dyn, save_state can_throw, no_dce, no_hoist, no_cse, barrier, require_state, call, acc_write, acc_read 启动由ResolveVirtual解析的虚方法
CallDynamic d-any-void, any-dyn, save_state can_throw, no_dce, no_hoist, no_cse, barrier, require_state, runtime_call, call 调用动态方法
CallIndirect d-real-void, ptr, real-dyn can_throw, no_dce, no_hoist, no_cse, barrier, call, low_level 通过给定地址进行低阶调用
Call d-real-void-any, real-dyn can_throw, no_dce, no_hoist, no_cse, barrier, call, low_level 低阶调用
MultiArray d-ref, ref, int-ngc-dyn, save_state can_throw, no_dce, no_hoist, no_cse, alloc, barrier, require_state, runtime_call, mem_barrier 创建多维数组
Monitor ref-nc, save_state can_throw, no_dce, no_hoist, no_cse, barrier, require_state, runtime_call, acc_read 为对象启用或禁用监视器锁
Intrinsic d-real-void-any, real-dyn can_throw, heap_inv, no_dce, no_hoist, no_cse, barrier, require_state, runtime_call, call, acc_write, acc_read 直接调用运行时内置函数
Builtin d-real-void, real-dyn 使用Encoder展开已知的内置函数
低级指令
指令名称 指令签名 指令标志 指令说明
AddI d-number-ptr, real-ptr low_level, acc_write, acc_read, ifcvt 将一个值与立即数相加
SubI d-number-ptr, real-ptr low_level, acc_write, acc_read, ifcvt 从一个值中减去立即数
MulI d-int, int low_level, acc_write, acc_read, ifcvt 将值与立即数相乘
DivI d-int, int low_level, acc_write, acc_read, ifcvt 将值除以立即数
ModI d-int, int low_level, acc_write, acc_read, ifcvt 与立即数的取模指令
ShlI d-int, int low_level, acc_write, acc_read, ifcvt 左移
ShrI d-int, int low_level, acc_write, acc_read, ifcvt 右移
AShrI d-int, int low_level, acc_write, acc_read, ifcvt 算术右移
AndI d-int-bool, int-bool low_level, acc_write, acc_read, ifcvt 按位与
OrI d-int-bool, int-bool low_level, acc_write, acc_read, ifcvt 按位或
XorI d-int-bool, int-bool low_level, acc_write, acc_read, ifcvt 按位异或
MAdd d-number, number, number, number low_level, ifcvt 乘加
MSub d-number, number, number, number low_level, ifcvt 乘减
MNeg d-number, number, number low_level, ifcvt 乘加取负
OrNot d-int-bool, int-bool, int-bool low_level, ifcvt 第一个操作数与第二个操作数按位取反的结果进行按位或
AndNot d-int, int-bool, int-bool low_level, ifcvt 第一个操作数与第二个操作数按位取反的结果进行按位与
XorNot d-int-bool, int-bool, int-bool low_level, ifcvt 第一个操作数与第二个操作数按位取反的结果进行按位异或
AndSR d-int, int, int low_level, ifcvt 第一个操作数与第二个操作数的移位值进行按位与
OrSR d-int, int, int low_level, ifcvt 第一个操作数与第二个操作数的移位值进行按位或
XorSR d-int, int, int low_level, ifcvt 第一个操作数与第二个操作数的移位值进行按位异或
AndNotSR d-int, int, int low_level, ifcvt 第一个操作数与第二个操作数移位值的取反进行按位与
OrNotSR d-int, int, int low_level, ifcvt 第一个操作数与第二个操作数移位值的取反进行按位或
XorNotSR d-int, int, int low_level, ifcvt 第一个操作数与第二个操作数移位值的取反进行按位异或
AddSR d-int, int, int low_level, ifcvt 第一个操作数与第二个操作数的移位值相加
SubSR d-int, int, int low_level, ifcvt 从第一个操作数中减去第二个操作数的移位值
NegSR d-int, int low_level, ifcvt 移位然后取反操作数的值
BoundsCheckI d-int-pseudo, int, save_state can_throw, is_check, no_dce, no_hoist, no_cse, require_state, low_level 检查值是否在指定边界内
LoadArrayI d-real-any, ref-any-nc load, no_hoist, no_cse, low_level, acc_read, acc_write 从数组加载值
LoadCompressedStringCharI d-u16, ref-nc, int load, no_cse, low_level, acc_read, acc_write 当字符串压缩启用时,从字符串加载字符
StoreArrayI ref-nc, real-any store, no_dce, no_hoist, no_cse, low_level, acc_read 在数组中存储值
LoadArrayPair d-real-any, ref-any-nc, int-bc load, no_hoist, no_cse, low_level, acc_write 一次从数组加载多个值
LoadArrayPairI d-real-any, ref-any-nc load, no_hoist, no_cse, low_level, acc_write 在立即数索引处一次从数组加载多个值
LoadPairPart d-real-any-pseudo, real-any load, low_level, acc_write, no_hoist, no_cse 从向量加载元素
LoadObjectPair d-real-any, ref-any-nc load, no_hoist, no_cse, low_level 一次从对象加载多个值
StoreArrayPair ref-nc, int-bc, real-any, real-any store, no_dce, no_hoist, no_cse, low_level, acc_read, require_tmp 一次向数组存储多个值
StoreArrayPairI ref-nc, real-any, real-any store, no_dce, no_hoist, no_cse, low_level, acc_read 在立即数索引处一次向数组存储多个值
StoreObjectPair ref-nc, real-any, real-any store, no_dce, no_hoist, no_cse, low_level, require_tmp 一次向对象存储多个值
ReturnI cf, no_dce, no_hoist, no_cse, barrier, low_level, acc_read, terminator 从方法返回一个值
特殊伪指令
指令名称 指令签名 指令标志 指令说明
Phi d-real-ref-any, real-ref-any-dyn no_cse, no_hoist Phi指令
SpillFill no_cse, no_dce 由寄存器分配器插入的伪指令
SaveState d-real-pseudo, real-dyn no_hoist, no_cse 包含在离开编译代码前必须保存的虚拟寄存器信息
SafePoint real-dyn no_dce, no_hoist, no_cse, barrier, runtime_call GC安全点指令
SaveStateDeoptimize d-real-pseudo, real-dyn no_dce, no_hoist, no_cse, barrier 包含在离开编译代码前必须保存的虚拟寄存器信息
SaveStateOsr d-real-pseudo, real-dyn no_dce, no_hoist, no_cse, barrier 包含用于填充OSR栈图的虚拟寄存器信息
Select d-real-any, real-any, real-any, real-any, real-any low_level, ifcvt 根据比较结果选择要移动到目标的值
SelectImm d-real-any, real-any, real-any, real-any low_level, ifcvt 根据与立即数的比较结果选择要移动到目标的值
AddOverflow d-int, int, int cf, no_dce, no_hoist, no_cse, barrier, acc_read 两数相加并检查溢出
SubOverflow d-int, int, int cf, no_dce, no_hoist, no_cse, barrier, acc_read 两数相减并检查溢出
AddOverflowCheck d-int, int, int, save_state no_dce, no_hoist, no_cse, barrier, acc_read, require_state, can_deoptimize 两数相加,如果发生溢出则去优化
SubOverflowCheck d-int, int, int, save_state no_dce, no_hoist, no_cse, barrier, acc_read, require_state, can_deoptimize 两数相减,如果发生溢出则去优化
NegOverflowAndZeroCheck d-int, int, save_state no_dce, no_hoist, no_cse, barrier, acc_read, require_state, can_deoptimize 取反,如果发生溢出则去优化
If real-any, real-any cf, no_dce, no_hoist, no_cse, barrier, low_level, acc_read 执行比较和跳转
IfImm real-any cf, no_dce, no_hoist, no_cse, barrier, acc_read 执行与立即数的比较和跳转
NOP no_dst, ifcvt, no_hoist 伪指令,替换要删除的指令
Try no_dce, no_hoist, no_cse, barrier 伪指令,插入在try块的开头
CatchPhi d-real-ref-any, real-ref-any-dyn no_hoist, no_cse 伪指令,插入在Catch处理程序基本块中
LiveIn d-real-any no_dce, no_hoist, no_cse, low_level 伪指令,定义活跃的入参寄存器
LiveOut d-real, real-any no_dce, no_hoist, no_cse, low_level 伪指令,定义活跃的出参寄存器
伪指令

这些伪指令只是为了手动编写IR指令方便而提供的

指令名称 指令签名 指令标志 指令说明
Else pseudo, cf
While pseudo, cf
Goto pseudo, cf
Label pseudo, cf
Register d-real pseudo
WhilePhi pseudo

arkdecompiler

现在网上有多个abc反编译项目,包括深蓝的abcdabc-decompilerxpandadayu等,这里面abcd闭源不知道咋实现的,abc-decompiler是基于jadx,xpanda和dayu似乎是完全重头实现的,俺选择arkdecompiler,因为它是在鸿蒙编译器已有框架下做了最少量工作而实现的!

编译说明

项目建议使用x64 ubuntu20.04编译,试过arm64 mac以失败告终,目前官方代码似乎有点小问题需修改:

  1. 直接打开dockerfile去执行里面的命令下载相关源码和预编译工具
  2. 打补丁,有个补丁搞反了,需要加参数反着打
  3. arkdecompiler里依赖的protobuf和arkcompiler不一致会造成冲突,需要改成同一个

改完这些就能编译啦

项目分析

鸿蒙编译器和运行时是开源的,arkdecompiler直接借助了鸿蒙编译器已有的能力:

1.使用panda::disasm::Disassembler解析abc文件,获取方法的汇编代码和元信息

2.使用compiler:IRBuilder完成汇编转IR,这个过程也使用了内建的的Pass优化代码

接下来,它自己实现了两个关键的步骤:

1.实现AstGen来将IR转换为AST节点,当然了这里的AST节点定义也是用的es2panda里的

2.实现ArkTSGen来将AST转换为ArkTS源码

之后有空再详细分析,溜了溜了~