7.3 基于栈和寄存器的指令集
本章节描述的是作者按照 RISC 指令系统设计的虚拟机指令集。
其类似与硬件指令集,可作为编译器后端指令系统目标,用于提供类似硬件指令系统环境,避免编译原理初学者陷入对硬件指令系统不了解的深渊。 降低学习难度,提高学习效率。
这类指令的主要特点如下:
- 1 寄存器的数量固定;
- 2 指令所操作的寄存器数量固定;
- 3 指令的含义简单;
1 寄存器
虚拟机的寄存器分为三种:
- 1 整数寄存器:主要用于整数运算,位宽为 64 bit,根据指令的含义可选择 32 bit 和 64 bit 运算模式。
- 2 浮点寄存器:主要用于浮点数运算,位宽为 64 bit,和通用寄存器一致,可选择位宽模式。
- 3 系统寄存器:对用户不可见,与虚拟机运行系统相关,对其进行操作隐藏在相关指令的实现细节中。
- 4 根据指令格式,整数寄存器、浮点寄存器最多可以有32个。
2 指令格式
每条指令是 32bit 大小对齐的,其中最低 9bit 是操作码,其余23bit作为操作数或者操作码的补充。如下表所示:
操作码 | 操作数 |
---|---|
9 bit | 23 bit |
2.1 格式1
操作码 | 操作数 |
---|---|
9 bit | 23 bit |
2.2 格式2
多数指令使用此格式。
操作码 | 目的操作数 | 源操作数 | 源操作数 | 操作子码 |
---|---|---|---|---|
9 bit | 5 bit | 5 bit | 5 bit | 8 bit |
2.3 格式3
该格式主要用于比较指令后的三元选择赋值操作。
操作码 | 目的操作数 | 源操作数 | 源操作数 | 源操作数 | 操作子码 |
---|---|---|---|---|---|
9 bit | 5 bit | 5 bit | 5 bit | 5 bit | 3 bit |
2.4 格式4
当指令中需要使用立即数时,且在指令中没有足够的空间可以存储时,可以使用指令后跟32bit对齐的立即数操作数。
- 32bit立即数:32bit 指令跟 1 个 32bit 立即数,虚拟机解析执行时,按照指令表达的语义参与运算。
- 64bit立即数:32bit 指令跟 2 个 32bit 立即数,虚拟机解析执行时,按照指令表达的语义,组合成 64bit,后参与运算。
- 其他位宽的立即数类似。
格式如下所示:
指令 | 立即数 1 | 立即数 2 | … | 立即数 n |
---|---|---|---|---|
32 bit | 32 bit | 32 bit | … | 32 bit |
3 寄存器与寄存器—指令集
3.1 空指令
空指令一般用于对齐,在本指令集中没有特殊含义,执行空操作。
格式如下:
助记符 | 操作码 | 操作数 |
---|---|---|
9 bit | 23 bit | |
Nop | 0 | 0 |
3.2 算术运算
算术运算包含 +、-、*、/、%,支持的运算类型有 int32、uint32、int64、uint64、flt32、flt64。
- 文本格式: op des,src,src2
- 指令含义:des = src op src2
- 子操作码:可以用于将运算结果进行截断、扩展。
- 助记符后缀的数字(32、64)标识指令运算使用的寄存器位宽。
- 助记符后缀的 i 表示进行有符号运算。
- 助记符后缀的 u 表示进行无符号运算。
- 助记符后缀的 f 表示进行有符号运算。
指令格式如下所示:
助记符 | 操作码 | 目的操作数 | 源操作数 | 源操作数 | 操作子码 |
---|---|---|---|---|---|
9 bit | 5 bit | 5 bit | 5 bit | 8 bit | |
add-i32 | des | src | src2 | ||
sub-i32 | des | src | src2 | ||
mul-i32 | des | src | src2 | ||
div-i32 | des | src | src2 | ||
mod-i32 | des | src | src2 | ||
add-u32 | des | src | src2 | ||
sub-u32 | des | src | src2 | ||
mul-u32 | des | src | src2 | ||
div-u32 | des | src | src2 | ||
mod-u32 | des | src | src2 | ||
add-i64 | des | src | src2 | ||
sub-i64 | des | src | src2 | ||
mul-i64 | des | src | src2 | ||
div-i64 | des | src | src2 | ||
mod-i64 | des | src | src2 | ||
add-u64 | des | src | src2 | ||
sub-u64 | des | src | src2 | ||
mul-u64 | des | src | src2 | ||
div-u64 | des | src | src2 | ||
mod-u64 | des | src | src2 | ||
add-f32 | des | src | src2 | ||
sub-f32 | des | src | src2 | ||
mul-f32 | des | src | src2 | ||
div-f32 | des | src | src2 | ||
add-f64 | des | src | src2 | ||
sub-f64 | des | src | src2 | ||
mul-f64 | des | src | src2 | ||
div-f64 | des | src | src2 |
3.2 位运算
位运算包括位相关的与、或、非、异或、移位、取反等运算。
支持的运算类型有 uint32、uint64。
- 文本格式: op des,src,src2
- 指令含义:des = src op src2
- 子操作码:可以用于将运算结果进行截断、扩展。
- i32:表示进行 32bit 的位运算。
- i64:表示进行 64bit 的位运算。
- 位运算都是看作无符号运算。
指令格式如下所示:
助记符 | 操作码 | 目的操作数 | 源操作数 | 源操作数 | 操作子码 |
---|---|---|---|---|---|
9 bit | 5 bit | 5 bit | 5 bit | 8 bit | |
sl-i32 | des | src | src2 | ||
sr-i32 | des | src | src2 | ||
sra-i32 | des | src | src2 | ||
and-i32 | des | src | src2 | ||
or-i32 | des | src | src2 | ||
xor-i32 | des | src | src2 | ||
andn-i32 | des | src | src2 | ||
orn-i32 | des | src | src2 | ||
xorn-i32 | des | src | src2 | ||
not-i32 | des | src | src2 | ||
sl-i64 | des | src | src2 | ||
sr-i64 | des | src | src2 | ||
sra-i64 | des | src | src2 | ||
and-i64 | des | src | src2 | ||
or-i64 | des | src | src2 | ||
xor-i64 | des | src | src2 | ||
andn-i64 | des | src | src2 | ||
orn-i64 | des | src | src2 | ||
xorn-i64 | des | src | src2 | ||
not-i64 | des | src | src2 |
3.3 逻辑比较
逻辑比较运算包括:<、>、<=、>=、==、!=、 ==0、!=0. 支持的运算类型有 int32、uint32、int64、uint64、flt32\flt64。
- 文本格式: op des,src,src2
- 指令含义:des = src op src2
- 子操作码:可以用于标识比较的方法。
- 比较的结果,存储在整数寄存器中,且使用位宽 64 bit,即des是整数寄存器。
指令格式如下所示:
助记符 | 操作码 | 目的操作数 | 源操作数 | 源操作数 | 操作子码 |
---|---|---|---|---|---|
9 bit | 5 bit | 5 bit | 5 bit | 8 bit | |
cmp-i32 | des | src | src2 | ||
cmp-u32 | des | src | src2 | ||
cmp-i64 | des | src | src2 | ||
cmp-u64 | des | src | src2 | ||
cmp-f32 | des | src | src2 | ||
cmp-f64 | des | src | src2 |
操作子码定义如下:
助记符 | 操作码 | 含义 |
---|---|---|
lt | < | |
le | <= | |
gt | > | |
ge | >= | |
eq | == | |
ne | != | |
ez | ==0,此时没有源操作数src2 | |
nz | !=0,此时没有源操作数src2 |
当不使用操作子码,而是使用下面方式运算:
- src < src2,设置des寄存器为 -1。
- src == src2,设置des寄存器为 0。
- src > src2,设置des寄存器为 -1。
3.4 条件赋值
条件赋值指令使用比较指令的结果,对另外 2 个源操作数进行选择,传递个目标操作数。
指令格式如下所示:
助记符 | 操作码 | 目的操作数 | 源操作数 | 源操作数 | 源操作数 | 操作子码 |
---|---|---|---|---|---|---|
9 bit | 5 bit | 5 bit | 5 bit | 5 bit | 3 bit | |
sel-i32 | des | src | src2 | cond | ||
sel-u32 | des | src | src2 | cond | ||
sel-i64 | des | src | src2 | cond | ||
sel-u64 | des | src | src2 | cond | ||
sel-f32 | des | src | src2 | cond | ||
sel-f64 | des | src | src2 | cond |
当比较系列指令使用操作子码方式时:
- cond == 0 :des = src
- cond != 0 :des = src2
当比较系列指令不使用操作子码方式时,本系列指令要求使用操作子码进行判断:
操作子码定义如下:
助记符 | 操作码 | 含义 |
---|---|---|
lt | < :des = cond == -1 ? src : src2 | |
le | <= :des = cond < 1 ? src : src2 | |
gt | > :des = cond == 1 ? src : src2 | |
ge | >= :des = cond > -1 ? src : src2 | |
eq | == :des = cond == 0 ? src : src2 | |
ne | != :des = cond != 0 ? src : src2 |
3.5 赋值指令
此系列包括寄存器间赋值或类型转换。
助记符 | 操作码 | 目的操作数 | 源操作数 | 源操作数 | 操作子码 |
---|---|---|---|---|---|
9 bit | 5 bit | 5 bit | 5 bit | 8 bit | |
mov | des | src |
操作子码用于获取源操作数并转换到响应的类型。
操作子码定义如下:
助记符 | 操作码 | 含义 |
---|---|---|
i8-i32 | ||
u8-i32 | ||
i8-i64 | ||
u8-u64 | ||
i16-i32 | ||
u16-i32 | ||
i16-i64 | ||
u16-i64 | ||
i32_i32 | ||
i32-i64 | ||
i32-f32 | ||
i32-f64 | ||
u32-u32 | ||
u32-u64 | ||
u32-f32 | ||
u32-f64 | ||
i64-i64 | ||
i64-f32 | ||
i64-f64 | ||
u64-u64 | ||
u64-f32 | ||
u64-f64 | ||
f32-i32 | ||
f32-u32 | ||
f32-i64 | ||
f32-u64 | ||
f32-f32 | ||
f32-f64 | ||
f64-i32 | ||
f64-u32 | ||
f64-i64 | ||
f64-u64 | ||
f64-f32 | ||
f64-f64 | ||
f32-bit-u32 | 位转换 | |
u32-bit-f32 | 位转换 | |
f64-bit-u64 | 位转换 | |
u64-bit-f64 | 位转换 |
3.6 加载存储指令
此系列指令功能是从内存中读取、存储特定类型数据。
助记符 | 操作码 | 目的操作数 | 源操作数 | 源操作数 | 操作子码 | 注释 |
---|---|---|---|---|---|---|
9 bit | 5 bit | 5 bit | 5 bit | 8 bit | ||
load | des | base | offset | 从基址内存中获取数据 | ||
store | des | base | offset | |||
gload | des | base | offset | 从全局内存中获取数据 | ||
gstore | des | base | offset |
操作子码定义如下:
助记符 | 操作码 | 含义 |
---|---|---|
i8 | ||
u8 | ||
i16 | ||
u16 | ||
i32 | ||
u32 | ||
i64 | ||
u64 | ||
f32 | ||
f64 |
3.7 出入栈指令
此系列指令用于将数据入栈,同时可以进行类型转换。 注意:入栈数据应当进行 32bit 对齐。以加快虚拟机执行速度数据
助记符 | 操作码 | 目的操作数 | 源操作数 | 源操作数 | 操作子码 |
---|---|---|---|---|---|
9 bit | 5 bit | 5 bit | 5 bit | 8 bit | |
push | des | ||||
pop | des | ||||
操作子码定义如下: | |||||
助记符 | 操作码 | 含义 | |||
— | — | — | |||
i8 | |||||
u8 | |||||
i16 | |||||
u16 | |||||
i32 | |||||
u32 | |||||
i64 | |||||
u64 | |||||
f32 | |||||
f64 |
3.8 栈内存分配释放
此系列指令用于栈内存分配释放。 注意:立即数是无符号整数。
助记符 | 操作码 | 目的操作数 | 立即数 | 注释 |
---|---|---|---|---|
9 bit | 5 bit | 18 bit | ||
grown | des | imm | 分配栈内存,将分配后的栈指针值传递到 des 寄存器 | |
9 bit | 23 bit | 释放栈内存, | ||
shrunk | imm |
3.9 跳转指令
- 跳转的偏移量是无符号整数值;
- 相对于函数代码的起始地址;
- 偏移量是偏移的指令条数;
助记符 | 操作码 | 目的操作数 | 立即数 | 注释 |
---|---|---|---|---|
9 bit | 23 bit | |||
jmp | imm | 直接跳转 | ||
9 bit | 5 bit | 18 bit | ||
jmpx | reg | 偏移量在 reg 寄存器中 | ||
9 bit | 5 bit | 18 bit | ||
jtab | reg | imm | reg 表示偏移量索引,imm表示偏移量数组中偏移数据条数,指令后面接 32 bit 对齐的偏移量数组 |
3.10 分支指令
此系列指令用于条件分支跳转。
助记符 | 操作码 | 目的操作数 | 源操作数 | 源操作数 | 操作子码 | 偏移立即数 |
---|---|---|---|---|---|---|
9 bit | 5 bit | 5 bit | 5 bit | 8 bit | 32 bit | |
jbr-i32 | src | src2 | cond | offset | ||
jbr-u32 | src | src2 | cond | offset | ||
jbr-i64 | src | src2 | cond | offset | ||
jbr-u64 | src | src2 | cond | offset | ||
jbr-f32 | src | src2 | cond | offset | ||
jbr-f64 | src | src2 | cond | offset |
操作子码定义如下:
助记符 | 操作码 | 含义 |
---|---|---|
lt | < | |
le | <= | |
gt | > | |
ge | >= | |
eq | == | |
ne | != | |
ez | ==0,此时没有源操作数src2 | |
nz | !=0,此时没有源操作数src2 |
3.11 函数指令
助记符 | 操作码 | 目的操作数 | 立即数 | 注释 |
---|---|---|---|---|
9 bit | 23 bit | 32 bit | ||
call | imm | imm表示函数的偏移量或者是编号,看虚拟机的具体实现 | ||
9 bit | 5 bit | 18 bit | ||
callx | reg | reg 寄存器中保存函数的偏移量或者是编号,看虚拟机的具体实现 | ||
9 bit | 23 bit | |||
ret | 函数返回 |
4 寄存器与立即数–指令集
待续