/** * @file vm_test.cpp * @brief VM 测试(12.9):手工映像 + 编译器产物 + 定时器 + 确定性 * @author * @date 2026-08-21 */ #include #include #include #include #include "compiler/Codegen.h" #include "compiler/Linker.h" #include "compiler/Project.h" #include "compiler/Stb.h" #include "compiler/Typecheck.h" #include "isa/Encode.h" #include "isa/Instr.h" #include "isa/Op.h" #include "vm/Machine.h" #ifndef REPO_ROOT #define REPO_ROOT "." #endif static int g_checks = 0; #define CHECK(cond) \ do { \ if (!(cond)) { \ std::printf("FAIL %s:%d: %s\n", __FILE__, __LINE__, #cond); \ return false; \ } \ ++g_checks; \ } while (0) // ---- 数据区槽访问 ---- static int64_t slot(const vm::Machine& m, int s) { int64_t v = 0; for (int i = 0; i < 8; ++i) { v |= static_cast(m.data()[s * 8 + i]) << (8 * i); } return v; } static void wslot(vm::Machine& m, int s, int64_t v) { for (int i = 0; i < 8; ++i) { m.data()[s * 8 + i] = static_cast((v >> (8 * i)) & 0xFF); } } // ---- 编译器产物全链路(编译 → 建机 → 跑周期)---- static bool make_machine(const char* dir, vm::Machine* m, std::string* err) { using namespace compiler; MachineConfig cfg; if (!cfg.load(std::string(REPO_ROOT) + "/compiler/machine.toml", err)) { return false; } const std::string toml = std::string(REPO_ROOT) + "/" + dir + "/project.toml"; Project p; if (!parse_project(toml, &p, err)) { return false; } std::vector units; for (const std::string& f : compile_files(p)) { SourceUnit u; if (!load_unit(p.base_dir + "/" + f, &u, err)) { return false; } units.push_back(std::move(u)); } LinkResult link; if (!link_project(p, units, &link, err)) { return false; } if (!check_project(p, units, link, err)) { return false; } std::vector img; if (!codegen_project(p, units, link, cfg, &img, err)) { return false; } return vm::Machine::create(img, m, err); } // ---- 手工拼映像 ---- struct HFunc { std::vector code; uint32_t nregs = 8; }; static std::vector hand_image(const std::vector& consts, const std::vector& funcs, uint32_t entry = 0, uint32_t cycle_limit = 100000, const std::vector& data = {}) { size_t code_total = 0; for (const HFunc& f : funcs) { code_total += f.code.size() * 4; } const uint32_t off_const = 104; const uint32_t off_funcs = off_const + static_cast(consts.size()) * 12; const uint32_t off_code = off_funcs + static_cast(funcs.size()) * 12; const uint32_t off_data = off_code + static_cast(code_total); const uint32_t off_end = off_data + static_cast(data.size()) + 32; // SHA 尾占位 std::vector b(off_end, 0); auto put32 = [&](size_t o, uint32_t v) { b[o + 0] = static_cast(v & 0xFFu); b[o + 1] = static_cast((v >> 8) & 0xFFu); b[o + 2] = static_cast((v >> 16) & 0xFFu); b[o + 3] = static_cast((v >> 24) & 0xFFu); }; auto put64 = [&](size_t o, uint64_t v) { for (int i = 0; i < 8; ++i) { b[o + i] = static_cast((v >> (8 * i)) & 0xFFu); } }; put32(0, 0x43545353u); put32(4, 1u); put32(8, cycle_limit); put32(12, 10); // dt_ms put32(24, entry); put32(44, static_cast(consts.size())); put32(48, static_cast(funcs.size())); put32(52, off_const); put32(56, off_funcs); put32(60, off_code); put32(64, off_data); put32(68, off_data); for (size_t i = 0; i < consts.size(); ++i) { const size_t o = off_const + i * 12; put32(o, static_cast(consts[i].tag)); put64(o + 4, consts[i].value); } size_t acc = 0; for (size_t i = 0; i < funcs.size(); ++i) { const size_t o = off_funcs + i * 12; put32(o, funcs[i].nregs); put32(o + 4, static_cast(acc)); put32(o + 8, static_cast(funcs[i].code.size())); acc += funcs[i].code.size() * 4; } size_t c = off_code; for (const HFunc& f : funcs) { for (const isa::Instr in : f.code) { put32(c, in); c += 4; } } for (size_t i = 0; i < data.size(); ++i) { b[off_data + i] = data[i]; } // 型号标识(STATOR1)+ 真实 SHA-256 尾(用 compiler 实现填) const char* mid = "STATOR1"; for (size_t i = 0; i < 32; ++i) { b[72 + i] = i < 7 ? static_cast(mid[i]) : 0; } uint8_t digest[32]; compiler::sha256(b.data(), b.size() - 32, digest); for (int i = 0; i < 32; ++i) { b[b.size() - 32 + i] = digest[i]; } return b; } // ---- 1. 手工映像:标量 + 跳转 + 数据区 ---- static bool test_hand_scalar() { // r8 := 5;r9 := r8 + 3;槽 0 ← r9;槽 1 → r10;JT 跳过一条 const std::vector consts = { {1, 5}, {1, 3}, {1, 1}, }; const std::vector funcs = {{ { isa::enc_imm(isa::Op::LOADK, 8, 0), isa::enc_rr(isa::Op::MOVE, 9, 8), isa::enc_imm(isa::Op::LOADK, 11, 1), isa::enc_rrr(isa::Op::ADD, 9, 9, 11), isa::enc_slot(isa::Op::STORE_GLOBAL, 9, 0), isa::enc_slot(isa::Op::LOAD_GLOBAL, 10, 0), isa::enc_jc(isa::Op::JT, 9, 1), // r9≠0 → 跳过下一条 isa::enc_imm(isa::Op::LOADK, 10, 2), isa::enc_ret(), }, 12, }}; const std::vector img = hand_image(consts, funcs, 0, 100000, std::vector(16, 0)); // 2 槽 vm::Machine m; std::string err; CHECK(vm::Machine::create(img, &m, &err)); CHECK(m.run_cycle() == vm::Fault::None); CHECK(m.reg(8) == 5); CHECK(m.reg(9) == 8); // 5 + 3 CHECK(slot(m, 0) == 8); // 槽 0 收到 8 CHECK(m.reg(10) == 8); // 槽 1 读回(JT 跳过覆盖) CHECK(m.cycle_count() == 8); return true; } // ---- 2. 手工映像:CALL/RET 帧复制 ---- static bool test_hand_call() { // fn0(入口):r8=5、r2=7、实参 r1←r8;CALL 1;结果 r0 → r8 // fn1:r0 = r1 + r2(r1/r2 来自调用约定区复制) const std::vector consts = { {1, 5}, {1, 7}, }; const std::vector funcs = { {{ isa::enc_imm(isa::Op::LOADK, 8, 0), isa::enc_imm(isa::Op::LOADK, 2, 1), isa::enc_rr(isa::Op::MOVE, 1, 8), isa::enc_call(1), isa::enc_rr(isa::Op::MOVE, 8, 0), isa::enc_ret(), }, 12}, {{ isa::enc_rrr(isa::Op::ADD, 0, 1, 2), isa::enc_ret(), }, 10}, }; const std::vector img = hand_image(consts, funcs); vm::Machine m; std::string err; CHECK(vm::Machine::create(img, &m, &err)); CHECK(m.run_cycle() == vm::Fault::None); CHECK(m.reg(8) == 12); // 5 + 7 CHECK(m.call_depth() == 1); return true; } // ---- 3. 手工映像:自调用 → StackOverflow ---- static bool test_stack_overflow() { const std::vector funcs = {{ { isa::enc_call(0), isa::enc_ret(), }, 8, }}; const std::vector img = hand_image({}, funcs); vm::Machine m; std::string err; CHECK(vm::Machine::create(img, &m, &err)); CHECK(m.run_cycle() == vm::Fault::StackOverflow); CHECK(m.call_depth() == 1); // 周期结束已清栈(留 MAIN) return true; } // ---- 4. 手工映像:越界槽 → BadSlot ---- static bool test_bad_slot() { const std::vector funcs = {{ { isa::enc_slot(isa::Op::STORE_GLOBAL, 8, 60000), // 数据区 0 字节 isa::enc_ret(), }, 12, }}; const std::vector img = hand_image({}, funcs); vm::Machine m; std::string err; CHECK(vm::Machine::create(img, &m, &err)); CHECK(m.run_cycle() == vm::Fault::BadSlot); return true; } // ---- 5. 编译器产物正例 ---- static bool test_cases_positive() { std::string err; vm::Machine m; // 01 空 MAIN CHECK(make_machine("tests/cases/01_empty_main", &m, &err)); CHECK(m.run_cycle() == vm::Fault::None); // 02 BOOL 赋值:a=TRUE(r8) b=FALSE(r9) CHECK(make_machine("tests/cases/02_bool_assign", &m, &err)); CHECK(m.run_cycle() == vm::Fault::None); CHECK(m.reg(8) == 1 && m.reg(9) == 0); // 03 短路:a=0 b=0 → x=0 CHECK(make_machine("tests/cases/03_short_circuit", &m, &err)); CHECK(m.run_cycle() == vm::Fault::None); CHECK(m.reg(10) == 0); // 04 IF:sel=0 → out=10 CHECK(make_machine("tests/cases/04_if_elsif_else", &m, &err)); CHECK(m.run_cycle() == vm::Fault::None); CHECK(m.reg(9) == 10); // 05 WHILE:n=10 CHECK(make_machine("tests/cases/05_while_normal", &m, &err)); CHECK(m.run_cycle() == vm::Fault::None); CHECK(m.reg(8) == 10); // 07 算术:a=42 b=10 c=32 eq=1 CHECK(make_machine("tests/cases/07_int_arith", &m, &err)); CHECK(m.run_cycle() == vm::Fault::None); CHECK(m.reg(8) == 42 && m.reg(9) == 10 && m.reg(10) == 32 && m.reg(11) == 1); // 08 TIME 字面量:t1=10 t2=1250 CHECK(make_machine("tests/cases/08_time_literal", &m, &err)); CHECK(m.run_cycle() == vm::Fault::None); CHECK(m.reg(8) == 10 && m.reg(9) == 1250); // 09 GVL + EXTERNAL:x = G1 初值 5(槽 0) CHECK(make_machine("tests/cases/09_gvl_external", &m, &err)); CHECK(m.run_cycle() == vm::Fault::None); CHECK(m.reg(8) == 5); // 13 FUNCTION:x = Add(3,4) = 7 CHECK(make_machine("tests/cases/13_function_call", &m, &err)); CHECK(m.run_cycle() == vm::Fault::None); CHECK(m.reg(8) == 7); // 15 FB 内联:starter(start=TRUE, stop=FALSE) → q=TRUE CHECK(make_machine("tests/cases/15_fb_instance", &m, &err)); CHECK(m.run_cycle() == vm::Fault::None); CHECK(m.reg(8) == 1); // 17 TON:dt=10、pt=30,in 恒真 → 第 3 周期 q=1(槽 2) CHECK(make_machine("tests/cases/17_ton", &m, &err)); m.run_cycle(); m.run_cycle(); CHECK(m.run_cycle() == vm::Fault::None); CHECK(slot(m, 2) == 1); // t.Q CHECK(slot(m, 3) == 30); // t.ET // 18 TOF/CTU:跑一个周期无故障 CHECK(make_machine("tests/cases/18_tof_ctu", &m, &err)); CHECK(m.run_cycle() == vm::Fault::None); // 20 line1:I 组合 → Q0_0 真值表(槽 0=急停 1=I0_0 2=I0_1 3=Q0_0) CHECK(make_machine("tests/cases/20_line1", &m, &err)); struct { int start, stop, es, expect; } table[] = { {1, 0, 0, 1}, {1, 1, 0, 0}, {0, 0, 0, 0}, {1, 0, 1, 0}, }; for (const auto& t : table) { wslot(m, 0, t.es); wslot(m, 1, t.start); wslot(m, 2, t.stop); CHECK(m.run_cycle() == vm::Fault::None); CHECK(slot(m, 3) == t.expect); } return true; } // ---- 6. 用例 06:cycle_limit 打满 ---- static bool test_cycle_limit() { std::string err; vm::Machine m; CHECK(make_machine("tests/cases/06_while_cycle_limit", &m, &err)); CHECK(m.run_cycle() == vm::Fault::CycleLimit); return true; } // ---- 7. 确定性:同一 I 序列跑两遍,数据区一致 ---- static bool test_determinism() { std::string err; vm::Machine a, b; CHECK(make_machine("tests/cases/20_line1", &a, &err)); CHECK(make_machine("tests/cases/20_line1", &b, &err)); const int seq[4][3] = {{1, 0, 0}, {1, 1, 0}, {0, 0, 0}, {1, 0, 1}}; for (int rep = 0; rep < 2; ++rep) { vm::Machine& m = rep ? b : a; for (const auto& s : seq) { wslot(m, 0, s[2]); wslot(m, 1, s[0]); wslot(m, 2, s[1]); CHECK(m.run_cycle() == vm::Fault::None); } } // 两遍结束:数据区逐字节一致(含 starter 实例状态) CHECK(a.data_len() == b.data_len()); for (size_t i = 0; i < a.data_len(); ++i) { if (a.data()[i] != b.data()[i]) { std::printf("FAIL determinism byte %zu: %d vs %d\n", i, a.data()[i], b.data()[i]); return false; } } ++g_checks; return true; } // ---- 8. step 单步:停在指令边界 ---- static bool test_step() { std::string err; vm::Machine m; CHECK(make_machine("tests/cases/02_bool_assign", &m, &err)); // 3 条指令:LOADK ×2 + RET CHECK(m.step()); CHECK(m.cycle_count() == 1 && m.reg(8) == 1); CHECK(m.step()); CHECK(m.cycle_count() == 2 && m.reg(9) == 0); CHECK(!m.step()); // RET → 周期结束 CHECK(m.cycle_count() == 3); CHECK(!m.step()); // 结束后不再执行 return true; } // ---- 9. 手工映像:坏常量 id → BadConst ---- static bool test_bad_const() { const std::vector funcs = {{ { isa::enc_imm(isa::Op::LOADK, 8, 99), // 常量表为空 isa::enc_ret(), }, 12, }}; const std::vector img = hand_image({}, funcs); vm::Machine m; std::string err; CHECK(vm::Machine::create(img, &m, &err)); CHECK(m.run_cycle() == vm::Fault::BadConst); return true; } // ---- 10. 12.13 校验:SHA 篡改拒绝 + 型号不匹配拒绝 ---- static bool test_verify() { std::string err; vm::Machine m; // 正常映像可跑 CHECK(make_machine("tests/cases/01_empty_main", &m, &err)); CHECK(m.run_cycle() == vm::Fault::None); // SHA 篡改:改一字节 → create 拒绝 { const std::string toml = std::string(REPO_ROOT) + "/tests/cases/01_empty_main/project.toml"; compiler::Project p; CHECK(compiler::parse_project(toml, &p, &err)); std::vector units; for (const std::string& f : compiler::compile_files(p)) { compiler::SourceUnit u; CHECK(compiler::load_unit(p.base_dir + "/" + f, &u, &err)); units.push_back(std::move(u)); } compiler::LinkResult link; CHECK(compiler::link_project(p, units, &link, &err)); compiler::MachineConfig cfg; CHECK(cfg.load(std::string(REPO_ROOT) + "/compiler/machine.toml", &err)); std::vector img; CHECK(compiler::codegen_project(p, units, link, cfg, &img, &err)); img[100] ^= 0x01; // 篡改代码段一字节 vm::Machine bad; CHECK(!vm::Machine::create(img, &bad, &err)); CHECK(err.find("sha256 mismatch") != std::string::npos); } // 型号不匹配:手拼映像改型号(重算 SHA,仅型号不一致)→ create 拒绝 { const std::vector consts; const std::vector funcs = {{ { isa::enc_ret(), }, 8, }}; std::vector img = hand_image(consts, funcs); for (int i = 0; i < 5; ++i) { img[72 + i] = "OTHER"[i]; } uint8_t digest[32]; compiler::sha256(img.data(), img.size() - 32, digest); for (int i = 0; i < 32; ++i) { img[img.size() - 32 + i] = digest[i]; } vm::Machine bad; CHECK(!vm::Machine::create(img, &bad, &err)); CHECK(err.find("model mismatch") != std::string::npos); } return true; } int main() { if (!test_hand_scalar()) return 1; if (!test_hand_call()) return 1; if (!test_stack_overflow()) return 1; if (!test_bad_slot()) return 1; if (!test_cases_positive()) return 1; if (!test_cycle_limit()) return 1; if (!test_determinism()) return 1; if (!test_step()) return 1; if (!test_bad_const()) return 1; if (!test_verify()) return 1; std::printf("vm_test: %d checks passed\n", g_checks); return 0; }