指令集

navm 的指令使用 8 位(1 字节)无符号整数标识,后面跟随可变长度的操作数。操作数类型为 u64 i64 时,长度为 64 位(8 字节),类型为 u32 i32 时,长度为 32 位(4 字节)。

勘误:之前 cmp.ucmp.f 的指令写反了

下表展示了 navm 的所有指令。其中弹栈和压栈的格式为:栈变化范围[:变量],数字按照栈底到栈顶编号。

指令指令名操作数弹栈压栈介绍
0x00nop---空指令
0x01pushnum:u64-1:num将 num 压栈
0x02pop-1弹栈 1 个 slot
0x03popnnum:u321-num-弹栈 num 个 slot
0x04dup-1:num1:num, 2:num复制栈顶 slot
0x0alocaoff:u32-1:addr加载 off 个 slot 处局部变量的地址
0x0bargaoff:u32-1:addr加载 off 个 slot 处参数/返回值的地址
0x0cgloban:u32-1:addr加载第 n 个全局变量/常量的地址
0x10load.8-1:addr1:val从 addr 加载 8 位 value 压栈
0x11load.16-1:addr1:val从 addr 加载 16 位 value 压栈
0x12load.32-1:addr1:val从 addr 加载 32 位 value 压栈
0x13load.64-1:addr1:val从 addr 加载 64 位 value 压栈
0x14store.8-1:addr, 2:val-把 val 截断到 8 位存入 addr
0x15store.16-1:addr, 2:val-把 val 截断到 16 位存入 addr
0x16store.32-1:addr, 2:val-把 val 截断到 32 位存入 addr
0x17store.64-1:addr, 2:val-把 val 存入 addr
0x18alloc-1:size1:addr在堆上分配 size 字节的内存
0x19free-1:addr-释放 addr 指向的内存块
0x1astackallocsize:u32--在当前栈顶分配 size 个 slot,初始化为 0
0x20add.i-1:lhs, 2:rhs1:res计算 res = lhs + rhs,参数为整数
0x21sub.i-1:lhs, 2:rhs1:res计算 res = lhs - rhs,参数为整数
0x22mul.i-1:lhs, 2:rhs1:res计算 res = lhs * rhs,参数为整数
0x23div.i-1:lhs, 2:rhs1:res计算 res = lhs / rhs,参数为有符号整数
0x24add.f-1:lhs, 2:rhs1:res计算 res = lhs + rhs,参数为浮点数
0x25sub.f-1:lhs, 2:rhs1:res计算 res = lhs - rhs,参数为浮点数
0x26mul.f-1:lhs, 2:rhs1:res计算 res = lhs * rhs,参数为浮点数
0x27div.f-1:lhs, 2:rhs1:res计算 res = lhs / rhs,参数为浮点数
0x28div.u-1:lhs, 2:rhs1:res计算 res = lhs / rhs,参数为无符号整数
0x29shl-1:lhs, 2:rhs1:res计算 res = lhs << rhs
0x2ashr-1:lhs, 2:rhs1:res计算 res = lhs >> rhs (算术右移)
0x2band-1:lhs, 2:rhs1:res计算 res = lhs & rhs
0x2cor-1:lhs, 2:rhs1:res计算 res = lhs | rhs
0x2dxor-1:lhs, 2:rhs1:res计算 res = lhs ^ rhs
0x2enot-1:lhs1:res计算 res = !lhs
0x30cmp.i-1:lhs, 2:rhs1:res比较有符号整数 lhs 和 rhs 大小
0x31cmp.u-1:lhs, 2:rhs1:res比较无符号整数 lhs 和 rhs 大小
0x32cmp.f-1:lhs, 2:rhs1:res比较浮点数 lhs 和 rhs 大小
0x34neg.i-1:lhs1:res对 lhs 取反
0x35neg.f-1:lhs1:res对 lhs 取反
0x36itof-1:lhs1:res把 lhs 从整数转换成浮点数
0x37ftoi-1:lhs1:res把 lhs 从浮点数转换成整数
0x38shrl-1:lhs, 2:rhs1:res计算 res = lhs >>> rhs (逻辑右移)
0x39set.lt-1:lhs1:res如果 lhs < 0 则推入 1,否则 0
0x3aset.gt-1:lhs1:res如果 lhs > 0 则推入 1,否则 0
0x41broff:i32无条件跳转偏移 off
0x42br.falseoff:i321:test如果 test 是 0 则跳转偏移 off
0x43br.trueoff:i321:test如果 test 非 0 则跳转偏移 off
0x48callid:u32见栈帧介绍调用编号为 id 的函数
0x49ret-见栈帧介绍从当前函数返回
0x4acallnameid:u32见栈帧介绍调用名称与编号为 id 的全局变量内容相同的函数
0x50scan.i--1:n从标准输入读入一个整数 n
0x51scan.c--1:c从标准输入读入一个字符 c
0x52scan.f--1:f从标准输入读入一个浮点数 f
0x54print.i-1:x-向标准输出写入一个有符号整数 x
0x55print.c-1:c-向标准输出写入字符 c
0x56print.f-1:f-向标准输出写入浮点数 f
0x57print.s-1:i-向标准输出写入全局变量 i 代表的字符串
0x58println---向标准输出写入一个换行
0xfepanic恐慌(强行退出)

cmp.T 指令

指令会在 lhs < rhs 时压入 -1, lhs > rhs 时压入 1, lhs == rhs 时压入 0。浮点数无法比较时压入 0

load.8/16/32/64 指令

指令会从 addr 处取 T 长度的数据压入栈中。如果 addr 不是 T 的倍数,将会产生 UnalignedAccess 错误。如果 T 小于 64,多余的数位将会被补成 0。

store.8/16/32/64 指令

指令会将 T 长度的数据弹栈并存入 addr 地址处。如果 addr 不是 T 的倍数,将会产生 UnalignedAccess 错误。如果 T 小于 64,数据将被截断至 T 长度。

br 系列分支指令

指令会将(指向下一条指令的)当前指令指针 ipoffset 相加得出新的指令指针的值。比如如果 br 3 是一个函数的第 5 条指令,那么此时 ip = 6,指令执行之后 ip = 6 + 3 = 9。br.true, br.false 指令的执行规则相同。