阶段4-1:isa 重构 V2 完成(Types/Op/Instr/Encode + isa_test 91 checks)

- Types.h:func 宽度/符号表(kFuncWidth/kFuncSigned)、饱和模板(各宽度
  __int128 中间量 + 除法 A:除零返回被除数/MIN/-1 夹界/截断)、删 TypeTag
- Op.h/.cpp:前缀分区 + 72 条指令表(与 machine.toml 强校验一致)、
  uses_rd/rs1/rs2 直推、kFbNames(15 内建 FB)
- Instr.h:变长 instr_len、位号 get/set(含 JC off@55..24、SLOT_OFF off@87..56)、
  enc_* 发射器
- Encode.h/.cpp:decode/encode 往返、disasm(类型后缀 + CAL fb 名 + 未知 0x dump)
- isa_test V2:饱和边界/func 表/前缀定长/位号往返/编解码/disasm/uses_*/指令表

修复的实现 bug:find_op 字符串比较(指针==)、set_byte0 11/10 与 010xxx 分支、
CAL 字段约定((slot<<16)|fb_id)、disasm 未知前缀 used 长度

状态:vm/executor 编译暂破(V1 代码引用旧 isa 接口),4-2 同步
This commit is contained in:
2026-08-26 09:56:43 +08:00
parent 384e681ef3
commit 4f3da55ed7
8 changed files with 968 additions and 466 deletions
+212 -91
View File
@@ -1,15 +1,12 @@
/**
* @file isa_test.cpp
* @brief isa 合同测试:饱和、编解码、disasm、哈希、映像、sidecar
* @brief isa V2 测试:饱和模板 / func 表 / 前缀定长 / 位号往返 / 编解码 / disasm / uses_*
* @author
* @date 2026-08-19
*/
#include <cassert>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include "isa/Encode.h"
#include "isa/Instr.h"
@@ -18,119 +15,243 @@
static int g_checks = 0;
#define CHECK(cond) \
do { \
if (!(cond)) { \
#define CHECK(cond) \
do { \
if (!(cond)) { \
std::printf("FAIL %s:%d: %s\n", __FILE__, __LINE__, #cond); \
return false; \
} \
++g_checks; \
return false; \
} \
++g_checks; \
} while (0)
// ---- 1. 饱和算术 ----
// ---- 1. 饱和模板(各宽度 + 除法 A----
static bool test_sat() {
using namespace isa::types;
CHECK(sat_add(30000, 30000) == 32767);
CHECK(sat_add(-30000, -30000) == -32768);
CHECK(sat_add(10, 20) == 30);
CHECK(sat_sub(-30000, 30000) == -32768);
CHECK(sat_sub(5, 3) == 2);
CHECK(sat_mul(300, 200) == 32767);
CHECK(sat_mul(-300, 200) == -32768);
CHECK(sat_mul(6, 7) == 42);
CHECK(sat_div(10, 3) == 3);
CHECK(sat_div(-32768, -1) == 32767); // INT_MIN / -1 饱和
CHECK(sat_div(7, 0) == 7); // 除零返回被除数
CHECK(static_cast<int>(TypeTag::Bool) == 0);
CHECK(static_cast<int>(TypeTag::Int) == 1);
CHECK(static_cast<int>(TypeTag::Time) == 2);
// int8
CHECK(sat_add<int8_t>(100, 100) == 127);
CHECK(sat_add<int8_t>(-100, -100) == -128);
CHECK(sat_sub<int8_t>(-128, 1) == -128);
CHECK(sat_mul<int8_t>(20, 20) == 127);
CHECK(sat_div<int8_t>(-128, -1) == 127); // MIN/-1 饱和
CHECK(sat_div<int8_t>(7, 0) == 7); // 除零返回被除数
CHECK(sat_div<int8_t>(-7, 3) == -2); // 截断
// int16
CHECK(sat_add<int16_t>(30000, 30000) == 32767);
CHECK(sat_add<int16_t>(-30000, -30000) == -32768);
CHECK(sat_div<int16_t>(-32768, -1) == 32767);
CHECK(sat_div<int16_t>(10, 3) == 3);
CHECK(sat_div<int16_t>(7, 0) == 7);
// int32 / int64 边界
CHECK(sat_add<int32_t>(2147483000, 1000) == 2147483647);
CHECK(sat_div<int64_t>(INT64_MIN, -1) == INT64_MAX);
CHECK(sat_add<int64_t>(INT64_MAX, 1) == INT64_MAX);
return true;
}
// ---- 2. 操作码与助记符 ----
// ---- 2. func 表 ----
static bool test_op() {
using namespace isa;
CHECK(static_cast<int>(Op::MOVE) == 0);
CHECK(static_cast<int>(Op::CAL_TON) == 24);
CHECK(static_cast<int>(Op::CAL_F_TRIG) == 31); // 8 个 CAL_* 连续 24..31
CHECK(static_cast<int>(Op::CALL) == 32);
CHECK(static_cast<int>(Op::RET) == 33);
CHECK(kOpCount == 34);
CHECK(std::strcmp(mnemonic(Op::CAL_CTUD), "CAL_CTUD") == 0);
CHECK(std::strcmp(mnemonic(Op::CAL_F_TRIG), "CAL_F_TRIG") == 0);
CHECK(std::strcmp(mnemonic(static_cast<Op>(255)), "???") == 0);
static bool test_func_table() {
using namespace isa::types;
CHECK(kFuncWidth[0] == 1 && !kFuncSigned[0]); // 1B 无
CHECK(kFuncWidth[1] == 1 && kFuncSigned[1]); // 1B 有
CHECK(kFuncWidth[3] == 2 && kFuncSigned[3]); // 2B 有(INT
CHECK(kFuncWidth[4] == 4 && !kFuncSigned[4]); // 4B 无
CHECK(kFuncWidth[6] == 8 && !kFuncSigned[6]); // 8B 无
CHECK(kFuncWidth[7] == 8 && kFuncSigned[7]); // 8B 有
CHECK(kFuncWidth[8] == 4); // F32
CHECK(kFuncWidth[9] == 8); // F64
CHECK(kFuncWidth[10] == 0); // 预留
return true;
}
// ---- 3. 指令打包 / 拆字段 ----
// ---- 3. 前缀定长 ----
static bool test_instr() {
static bool test_instr_len() {
using namespace isa;
const Instr w = pack(Op::ADD, 5, 3, 4);
CHECK(op(w) == Op::ADD);
CHECK(rd(w) == 5);
CHECK(a(w) == 3);
CHECK(b(w) == 4);
CHECK(imm16(enc_imm(Op::LOADK, 0, 0x1234)) == 0x1234);
CHECK(off16(enc_jmp(-1)) == -1);
CHECK(off16(enc_jmp(4)) == 4);
CHECK(rd(enc_jc(Op::JT, 2, -3)) == 2);
CHECK(off16(enc_jc(Op::JT, 2, -3)) == -3);
CHECK(imm16(enc_call(0xABCD)) == 0xABCD);
CHECK(imm16(enc_slot(Op::LOAD_I, 7, 300)) == 300);
CHECK(rd(enc_slot(Op::LOAD_I, 7, 300)) == 7);
CHECK(rd(enc_rr(Op::MOVE, 1, 2)) == 1);
CHECK(a(enc_rr(Op::MOVE, 1, 2)) == 2);
CHECK(op(enc_ret()) == Op::RET);
uint8_t buf[16] = {0};
// 各前缀编码 → 长度
set_byte0(buf, "11", 0);
CHECK(instr_len(buf) == 4);
set_byte0(buf, "10", 1);
CHECK(instr_len(buf) == 4);
set_byte0(buf, "011", 0);
CHECK(instr_len(buf) == 8);
set_byte0(buf, "010001", 0);
CHECK(instr_len(buf) == 8);
set_byte0(buf, "0010", 0);
CHECK(instr_len(buf) == 16);
set_byte0(buf, "0011", 0);
CHECK(instr_len(buf) == 16);
set_byte0(buf, "0001", 0);
CHECK(instr_len(buf) == 32);
return true;
}
// ---- 4. encode / decode / disasm ----
// ---- 4. 位号往返 ----
static bool test_encode() {
static bool test_fields() {
using namespace isa;
const Instr w = enc_rrr(Op::ADD, 5, 3, 4);
CHECK(encode(decode(w)) == w);
CHECK(decode(w).op == Op::ADD);
CHECK(decode(w).rd == 5);
CHECK(decode(w).a == 3);
CHECK(decode(w).b == 4);
uint8_t buf[16] = {0};
// RRMOVE 11 区)
enc_rr(buf, "11", static_cast<uint8_t>(isa::Op::MOVE), 0, 5, 3);
CHECK(op_of(buf) == static_cast<uint8_t>(isa::Op::MOVE));
CHECK(rd_of(buf) == 5);
CHECK(rs1_of(buf) == 3);
// RRRADD 011 区,func=3
enc_rrr(buf, "011", static_cast<uint8_t>(isa::Op::ADD), 3, 1, 2, 4);
CHECK(op_of(buf) == static_cast<uint8_t>(isa::Op::ADD));
CHECK(func_of(buf) == 3);
CHECK(rd_of(buf) == 1 && rs1_of(buf) == 2 && rs2_of(buf) == 4);
// SLOTLOAD 010001 区)
enc_slot(buf, "010001", static_cast<uint8_t>(isa::Op::LOAD), 0, 7, 300);
CHECK(rd_of(buf) == 7);
CHECK(slot32_of(buf) == 300);
// IMMLOADK
enc_imm(buf, "010000", static_cast<uint8_t>(isa::Op::LOADK), 3, 0, 0x12345678);
CHECK(imm32_of(buf) == 0x12345678);
// JMP010010
enc_jmp(buf, -3);
CHECK(off32_of(buf) == -3);
// JC010011off@55..24 不覆盖 rd
enc_jc(buf, static_cast<uint8_t>(isa::Op::JT), 2, -3);
CHECK(rd_of(buf) == 2);
CHECK(off32_jc_of(buf) == -3);
// CALL010100
enc_call(buf, 7);
CHECK(fn_id32_of(buf) == 7);
// CAL010101fb_id16 + slot32
enc_cal(buf, 3, 42);
CHECK(fb_id16_of(buf) == 3);
CHECK(cal_slot32_of(buf) == 42);
// SLOT_OFF0010off@87..56
enc_slot_off(buf, static_cast<uint8_t>(isa::Op::LOAD_OFF), 3, 1, 9, -12);
CHECK(slot32_of(buf) == 9);
CHECK(off32_hi_of(buf) == -12);
// RET
enc_ret(buf);
CHECK(op_of(buf) == static_cast<uint8_t>(isa::Op::RET));
return true;
}
char buf[64];
disasm(w, buf, sizeof buf);
CHECK(std::strcmp(buf, "ADD r5, r3, r4") == 0);
disasm(enc_rr(Op::MOVE, 1, 2), buf, sizeof buf);
CHECK(std::strcmp(buf, "MOVE r1, r2") == 0);
disasm(enc_imm(Op::LOADK, 0, 3), buf, sizeof buf);
CHECK(std::strcmp(buf, "LOADK r0, 3") == 0);
disasm(enc_jmp(4), buf, sizeof buf);
CHECK(std::strcmp(buf, "JMP +4") == 0);
disasm(enc_jmp(-1), buf, sizeof buf);
CHECK(std::strcmp(buf, "JMP -1") == 0);
disasm(enc_jc(Op::JT, 0, -1), buf, sizeof buf);
CHECK(std::strcmp(buf, "JT r0, -1") == 0);
disasm(enc_call(1), buf, sizeof buf);
CHECK(std::strcmp(buf, "CALL 1") == 0);
disasm(enc_slot(Op::LOAD_I, 0, 2), buf, sizeof buf);
CHECK(std::strcmp(buf, "LOAD_I r0, 2") == 0);
disasm(enc_slot(Op::STORE_Q, 4, 0), buf, sizeof buf);
CHECK(std::strcmp(buf, "STORE_Q r4, 0") == 0);
disasm(enc_ret(), buf, sizeof buf);
CHECK(std::strcmp(buf, "RET") == 0);
disasm(enc_imm(Op::CAL_TON, 0, 0), buf, sizeof buf);
CHECK(std::strcmp(buf, "CAL_TON 0") == 0);
disasm(0xFFu, buf, sizeof buf); // 未知操作码
CHECK(std::strcmp(buf, "??? 0x000000ff") == 0);
// ---- 5. decode/encode 往返 ----
static bool test_roundtrip() {
using namespace isa;
uint8_t buf[16] = {0};
enc_rrr(buf, "011", static_cast<uint8_t>(isa::Op::ADD), 5, 3, 7, 9);
const isa::Decoded d = isa::decode(buf, 8);
CHECK(std::strcmp(d.prefix, "011") == 0);
CHECK(d.op == static_cast<uint8_t>(isa::Op::ADD));
CHECK(d.func == 5);
CHECK(d.rd == 3 && d.rs1 == 7 && d.rs2 == 9);
uint8_t buf2[16] = {0};
const size_t len = isa::encode(d, buf2);
CHECK(len == 8);
CHECK(std::memcmp(buf, buf2, 8) == 0);
return true;
}
// ---- 6. disasm 文本 ----
static bool test_disasm() {
using namespace isa;
uint8_t buf[16] = {0};
char out[64];
// 无类型:RET
enc_ret(buf);
isa::disasm(buf, 4, out, sizeof out);
CHECK(std::strcmp(out, "RET") == 0);
// RRMOVE
enc_rr(buf, "11", static_cast<uint8_t>(isa::Op::MOVE), 0, 1, 2);
isa::disasm(buf, 4, out, sizeof out);
CHECK(std::strcmp(out, "MOVE r1, r2") == 0);
// RRR 类型后缀:ADD.F32
enc_rrr(buf, "011", static_cast<uint8_t>(isa::Op::ADD), 8, 1, 2, 3);
isa::disasm(buf, 8, out, sizeof out);
CHECK(std::strcmp(out, "ADD.F32 r1, r2, r3") == 0);
// SLOTLOAD.I16
enc_slot(buf, "010001", static_cast<uint8_t>(isa::Op::LOAD), 3, 5, 4);
isa::disasm(buf, 8, out, sizeof out);
CHECK(std::strcmp(out, "LOAD.I16 r5, s4") == 0);
// JMP
enc_jmp(buf, 12);
isa::disasm(buf, 8, out, sizeof out);
CHECK(std::strcmp(out, "JMP +12") == 0);
// JC
enc_jc(buf, static_cast<uint8_t>(isa::Op::JT), 0, -1);
isa::disasm(buf, 8, out, sizeof out);
CHECK(std::strcmp(out, "JT r0, -1") == 0);
// CALL
enc_call(buf, 3);
isa::disasm(buf, 8, out, sizeof out);
CHECK(std::strcmp(out, "CALL 3") == 0);
// CALfb 名
enc_cal(buf, 0, 5);
isa::disasm(buf, 8, out, sizeof out);
CHECK(std::strcmp(out, "CAL.TON s5") == 0);
enc_cal(buf, 4, 9);
isa::disasm(buf, 8, out, sizeof out);
CHECK(std::strcmp(out, "CAL.DCTU s9") == 0);
// SLOT_OFF
enc_slot_off(buf, static_cast<uint8_t>(isa::Op::LOAD_OFF), 3, 2, 8, 16);
isa::disasm(buf, 16, out, sizeof out);
CHECK(std::strcmp(out, "LOAD_OFF.I16 r2, s8, 16") == 0);
// 未知(010110 预留区)
buf[0] = 0x58;
isa::disasm(buf, 8, out, sizeof out);
CHECK(std::strncmp(out, "??? 0x", 6) == 0);
return true;
}
// ---- 7. uses_* 直推 ----
static bool test_uses() {
using namespace isa;
CHECK(uses_rd(OpFormat::RR) && uses_rs1(OpFormat::RR) && !uses_rs2(OpFormat::RR));
CHECK(uses_rd(OpFormat::RRR) && uses_rs1(OpFormat::RRR) && uses_rs2(OpFormat::RRR));
CHECK(uses_rd(OpFormat::IMM) && !uses_rs1(OpFormat::IMM));
CHECK(uses_rd(OpFormat::SLOT) && !uses_rs1(OpFormat::SLOT));
CHECK(uses_rd(OpFormat::JC) && !uses_rs1(OpFormat::JC));
CHECK(!uses_rd(OpFormat::JMP) && !uses_rd(OpFormat::CALL));
CHECK(!uses_rd(OpFormat::CAL) && !uses_rd(OpFormat::NONE));
CHECK(uses_rd(OpFormat::SLOT_OFF) && uses_rd(OpFormat::IMM64));
return true;
}
// ---- 8. 指令表查询 ----
static bool test_op_table() {
using namespace isa;
CHECK(find_op("11", static_cast<uint8_t>(Op::MOVE)) == 0);
CHECK(find_op("011", static_cast<uint8_t>(Op::ADD)) >= 0);
CHECK(find_op("010001", static_cast<uint8_t>(Op::LOAD)) >= 0);
CHECK(find_op("010101", static_cast<uint8_t>(Op::CAL)) >= 0);
CHECK(find_op("99", 0) == -1);
CHECK(find_op_by_name("MOVE") == 0);
CHECK(find_op_by_name("ADD") >= 0);
CHECK(find_op_by_name("CAL") >= 0);
CHECK(find_op_by_name("NOPE") == -1);
CHECK(std::strcmp(mnemonic("11", static_cast<uint8_t>(Op::MOVE)), "MOVE") == 0);
CHECK(std::strcmp(mnemonic("011", static_cast<uint8_t>(Op::ADD)), "ADD") == 0);
CHECK(std::strcmp(mnemonic("99", 0), "???") == 0);
CHECK(format_of("010101", static_cast<uint8_t>(Op::CAL)) == OpFormat::CAL);
CHECK(func_mode_of("11", static_cast<uint8_t>(Op::MOVE)) == FuncMode::Width);
CHECK(func_mode_of("010001", static_cast<uint8_t>(Op::LOAD)) == FuncMode::Full);
CHECK(std::strcmp(kFbNames[0], "TON") == 0);
CHECK(std::strcmp(kFbNames[14], "RTC") == 0);
return true;
}
int main() {
if (!test_sat()) return 1;
if (!test_op()) return 1;
if (!test_instr()) return 1;
if (!test_encode()) return 1;
if (!test_func_table()) return 1;
if (!test_instr_len()) return 1;
if (!test_fields()) return 1;
if (!test_roundtrip()) return 1;
if (!test_disasm()) return 1;
if (!test_uses()) return 1;
if (!test_op_table()) return 1;
std::printf("isa_test: %d checks passed\n", g_checks);
return 0;
}