/** * @file codegen_test.cpp * @brief 寄存器码测试(12.8 切片 1):用例 01/02 出映像 * @author * @date 2026-08-21 */ #include #include #include #include #include #include "compiler/Codegen.h" #include "compiler/Linker.h" #include "compiler/Project.h" #include "compiler/Typecheck.h" #include "isa/Encode.h" #include "compiler/Stb.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 bool load_cfg(compiler::MachineConfig* cfg) { std::string err; if (!cfg->load(std::string(REPO_ROOT) + "/compiler/machine.toml", &err)) { std::printf("FAIL load machine.toml: %s\n", err.c_str()); return false; } return true; } static bool compile_case(const char* dir, std::vector* image, std::string* err) { using namespace compiler; compiler::MachineConfig cfg; if (!load_cfg(&cfg)) return false; const std::string toml = std::string(REPO_ROOT) + "/tests/cases/" + 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; } return codegen_project(p, units, link, cfg, image, err); } // 从临时工程(globals.st + main.st + 可含 io 绑定)走完整管线 static bool compile_src(const char* name, const char* io_extra, const char* globals_st, const char* main_st, std::vector* image, std::string* err) { using namespace compiler; compiler::MachineConfig cfg; if (!load_cfg(&cfg)) return false; const std::string dir = std::string(REPO_ROOT) + "/build/cg_tmp_" + name; std::filesystem::remove_all(dir); std::filesystem::create_directories(dir); const std::string toml_path = dir + "/project.toml"; std::FILE* f = std::fopen(toml_path.c_str(), "w"); std::fprintf(f, "[project]\nname = \"cg\"\nentry = \"program MAIN\"\n" "cycle_limit = 1000\ndt_ms = 10\n" "[files]\nst = [\"globals.st\", \"main.st\"]\n" "[gvl]\nfile = \"globals.st\"\n%s", io_extra); std::fclose(f); f = std::fopen((dir + "/globals.st").c_str(), "w"); std::fputs(globals_st, f); std::fclose(f); f = std::fopen((dir + "/main.st").c_str(), "w"); std::fputs(main_st, f); std::fclose(f); Project p; if (!parse_project(toml_path, &p, err)) { return false; } std::vector units; for (const std::string& file : compile_files(p)) { SourceUnit u; if (!load_unit(p.base_dir + "/" + file, &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; } return codegen_project(p, units, link, cfg, image, err); } // ---- 1. 用例 01:空 MAIN + RET ---- static bool test_case01() { std::vector img; std::string err; CHECK(compile_case("01_empty_main", &img, &err)); const compiler::StbView v = compiler::StbView::from(img); CHECK(v.ok()); CHECK(v.cycle_limit() == 1000); CHECK(v.dt_ms() == 10); CHECK(v.entry_fn_id() == 0); CHECK(v.n_funcs() == 1); CHECK(v.n_consts() == 0); CHECK(v.n_globals() == 0); CHECK(v.project_hash() != compiler::kFnvBasis); const compiler::StbView::FuncRow r = v.func_row(0); CHECK(r.nregs == 8); // 帧基址(调用约定区) CHECK(r.code_len == 1); char buf[32]; isa::disasm(v.code_bytes()[0], buf, sizeof buf); CHECK(std::strcmp(buf, "RET") == 0); return true; } // ---- 2. 用例 02:BOOL 赋值(TRUE / FALSE → LOADK)---- static bool test_case02() { std::vector img; std::string err; CHECK(compile_case("02_bool_assign", &img, &err)); const compiler::StbView v = compiler::StbView::from(img); CHECK(v.ok()); CHECK(v.n_funcs() == 1); CHECK(v.n_consts() == 2); // TRUE 与 FALSE const compiler::StbView::FuncRow r = v.func_row(0); CHECK(r.nregs == 10); // a, b(r8 起) CHECK(r.code_len == 3); // 从字节码段取指令(按函数行 code_offset 定位) const uint8_t* base = v.code_bytes(); char buf[64]; isa::disasm(reinterpret_cast(base)[0], buf, sizeof buf); CHECK(std::strcmp(buf, "LOADK r8, 0") == 0); // a := TRUE(常量 0) isa::disasm(reinterpret_cast(base)[1], buf, sizeof buf); CHECK(std::strcmp(buf, "LOADK r9, 1") == 0); // b := FALSE(常量 1) isa::disasm(reinterpret_cast(base)[2], buf, sizeof buf); CHECK(std::strcmp(buf, "RET") == 0); // 常量表:0 = BOOL 1,1 = BOOL 0 const compiler::ConstEntry c0 = v.const_entry(0); CHECK(c0.tag == 0 && c0.value == 1); const compiler::ConstEntry c1 = v.const_entry(1); CHECK(c1.tag == 0 && c1.value == 0); return true; } // ---- 3. 用例 09:GVL + VAR_EXTERNAL(全局读取 LOAD_GLOBAL + 初值数据段)---- static bool test_case09() { std::vector img; std::string err; CHECK(compile_case("09_gvl_external", &img, &err)); const compiler::StbView v = compiler::StbView::from(img); CHECK(v.ok()); CHECK(v.n_globals() == 1); CHECK(v.data_len() == 8); // 8 字节定宽槽(不含 SHA 尾) CHECK(v.data_bytes()[0] == 5); // G1 初值 5(小端低位) CHECK(v.data_bytes()[1] == 0); const compiler::StbView::FuncRow r = v.func_row(0); CHECK(r.nregs == 9); // r8 起:仅局部 x CHECK(r.code_len == 2); char buf[64]; const uint8_t* base = v.code_bytes(); isa::disasm(reinterpret_cast(base)[0], buf, sizeof buf); CHECK(std::strcmp(buf, "LOAD_GLOBAL r8, 0") == 0); // x := G1(槽 0 偏移 0) isa::disasm(reinterpret_cast(base)[1], buf, sizeof buf); CHECK(std::strcmp(buf, "RET") == 0); return true; } // ---- 4. io 绑定:LOAD_I / STORE_Q ---- static bool test_io_ops() { std::vector img; std::string err; const char* io_extra = "[[io.input]]\nvar = \"I0_0\"\nchannel = 0\nbit = 0\n" "[[io.output]]\nvar = \"Q0_0\"\nchannel = 0\nbit = 0\n"; const char* globals_st = "VAR_GLOBAL\n I0_0 : BOOL;\n Q0_0 : BOOL;\nEND_VAR\n"; const char* main_st = "PROGRAM MAIN\nVAR\n q : BOOL;\nEND_VAR\n" " q := I0_0;\n" " Q0_0 := q;\n" "END_PROGRAM\n"; CHECK(compile_src("io", io_extra, globals_st, main_st, &img, &err)); const compiler::StbView v = compiler::StbView::from(img); CHECK(v.ok()); CHECK(v.n_globals() == 2); const compiler::StbView::FuncRow r = v.func_row(0); CHECK(r.nregs == 10); // r8 起:q + 1 临时 char buf[64]; const uint8_t* base = v.code_bytes(); isa::disasm(reinterpret_cast(base)[0], buf, sizeof buf); CHECK(std::strcmp(buf, "LOAD_I r8, 0") == 0); // q := I0_0(io.input → LOAD_I) isa::disasm(reinterpret_cast(base)[1], buf, sizeof buf); CHECK(std::strcmp(buf, "MOVE r9, r8") == 0); // 临时 ← q isa::disasm(reinterpret_cast(base)[2], buf, sizeof buf); CHECK(std::strcmp(buf, "STORE_Q r9, 1") == 0); // Q0_0 := 临时(io.output → STORE_Q) isa::disasm(reinterpret_cast(base)[3], buf, sizeof buf); CHECK(std::strcmp(buf, "RET") == 0); return true; } // ---- 5. 混合类型布局:BOOL@0 INT@2 TIME@8,初值入数据段 ---- static bool test_layout() { std::vector img; std::string err; const char* globals_st = "VAR_GLOBAL\n" " B : BOOL;\n" " I : INT := 300;\n" " T : TIME := T#10ms;\n" "END_VAR\n"; const char* main_st = "PROGRAM MAIN\nVAR\n x : INT;\nEND_VAR\n" " x := I;\n" "END_PROGRAM\n"; CHECK(compile_src("layout", "", globals_st, main_st, &img, &err)); const compiler::StbView v = compiler::StbView::from(img); CHECK(v.ok()); CHECK(v.n_globals() == 3); CHECK(v.data_len() == 24); // 3 槽 × 8 字节定宽 CHECK(v.data_bytes()[0] == 0); // B 无初值 CHECK(v.data_bytes()[8] == 0x2C && v.data_bytes()[9] == 0x01); // I = 300(槽 1) CHECK(v.data_bytes()[16] == 10); // T = 10ms(槽 2) char buf[64]; isa::disasm(reinterpret_cast(v.code_bytes())[0], buf, sizeof buf); CHECK(std::strcmp(buf, "LOAD_GLOBAL r8, 1") == 0); // x := I(槽 1) return true; } // ---- 6. 写 io.input → codegen error ---- static bool test_write_input() { std::vector img; std::string err; const char* io_extra = "[[io.input]]\nvar = \"I0_0\"\nchannel = 0\nbit = 0\n"; const char* globals_st = "VAR_GLOBAL\n I0_0 : BOOL;\nEND_VAR\n"; const char* main_st = "PROGRAM MAIN\n I0_0 := TRUE;\nEND_PROGRAM\n"; if (compile_src("winput", io_extra, globals_st, main_st, &img, &err)) { std::printf("FAIL write_input: codegen passed\n"); return false; } CHECK(err.find("codegen error") == 0); CHECK(err.find("cannot write to input") != std::string::npos); return true; } // ---- 7. 用例 03:短路 AND / OR 必须编跳转 ---- static bool test_case03() { std::vector img; std::string err; CHECK(compile_case("03_short_circuit", &img, &err)); const compiler::StbView v = compiler::StbView::from(img); CHECK(v.ok()); const compiler::StbView::FuncRow r = v.func_row(0); CHECK(r.nregs == 12); // r8 起:a b x + 1 临时 CHECK(r.code_len == 9); char buf[64]; const uint8_t* base = v.code_bytes(); const uint32_t* ins = reinterpret_cast(base); // x := a AND b:MOVE r2,r0 / JF r2,+2 / MOVE r3,r1 / MOVE r2,r3 isa::disasm(ins[0], buf, sizeof buf); CHECK(std::strcmp(buf, "MOVE r10, r8") == 0); isa::disasm(ins[1], buf, sizeof buf); CHECK(std::strcmp(buf, "JF r10, +2") == 0); isa::disasm(ins[2], buf, sizeof buf); CHECK(std::strcmp(buf, "MOVE r11, r9") == 0); isa::disasm(ins[3], buf, sizeof buf); CHECK(std::strcmp(buf, "MOVE r10, r11") == 0); // x := a OR b:MOVE r2,r0 / JT r2,+2 / MOVE r3,r1 / MOVE r2,r3 isa::disasm(ins[4], buf, sizeof buf); CHECK(std::strcmp(buf, "MOVE r10, r8") == 0); isa::disasm(ins[5], buf, sizeof buf); CHECK(std::strcmp(buf, "JT r10, +2") == 0); isa::disasm(ins[6], buf, sizeof buf); CHECK(std::strcmp(buf, "MOVE r11, r9") == 0); isa::disasm(ins[7], buf, sizeof buf); CHECK(std::strcmp(buf, "MOVE r10, r11") == 0); isa::disasm(ins[8], buf, sizeof buf); CHECK(std::strcmp(buf, "RET") == 0); return true; } // ---- 8. NOT ---- static bool test_not() { std::vector img; std::string err; const char* main_st = "PROGRAM MAIN\nVAR\n a, x : BOOL;\nEND_VAR\n" " x := NOT a;\n" "END_PROGRAM\n"; CHECK(compile_src("not", "", "", main_st, &img, &err)); const compiler::StbView v = compiler::StbView::from(img); CHECK(v.ok()); const compiler::StbView::FuncRow r = v.func_row(0); CHECK(r.nregs == 11); // r8 起:a x + 1 临时 CHECK(r.code_len == 3); char buf[64]; const uint8_t* base = v.code_bytes(); const uint32_t* ins = reinterpret_cast(base); isa::disasm(ins[0], buf, sizeof buf); CHECK(std::strcmp(buf, "MOVE r10, r8") == 0); // 临时 ← a isa::disasm(ins[1], buf, sizeof buf); CHECK(std::strcmp(buf, "NOT r9, r10") == 0); // x := NOT 临时 isa::disasm(ins[2], buf, sizeof buf); CHECK(std::strcmp(buf, "RET") == 0); return true; } // ---- 9. 用例 04:IF / ELSIF / ELSE(CMP + JF + JMP 回填)---- static bool test_case04() { std::vector img; std::string err; CHECK(compile_case("04_if_elsif_else", &img, &err)); const compiler::StbView v = compiler::StbView::from(img); CHECK(v.ok()); const compiler::StbView::FuncRow r = v.func_row(0); CHECK(r.nregs == 13); // r8 起:sel out + 3 临时 CHECK(r.code_len == 14); char buf[64]; const uint8_t* base = v.code_bytes(); const uint32_t* ins = reinterpret_cast(base); // IF sel = 0 THEN:sel→r3、0→r4、CMP_EQ r2(常量序:0、10、1、20、30) isa::disasm(ins[0], buf, sizeof buf); CHECK(std::strcmp(buf, "MOVE r11, r8") == 0); isa::disasm(ins[1], buf, sizeof buf); CHECK(std::strcmp(buf, "LOADK r12, 0") == 0); isa::disasm(ins[2], buf, sizeof buf); CHECK(std::strcmp(buf, "CMP_EQ r10, r11, r12") == 0); isa::disasm(ins[3], buf, sizeof buf); CHECK(std::strcmp(buf, "JF r10, +2") == 0); // 假 → elsif([6]) isa::disasm(ins[4], buf, sizeof buf); CHECK(std::strcmp(buf, "LOADK r9, 1") == 0); // out := 10(常量 1) isa::disasm(ins[5], buf, sizeof buf); CHECK(std::strcmp(buf, "JMP +7") == 0); // → end([13]) // ELSIF sel = 1 isa::disasm(ins[6], buf, sizeof buf); CHECK(std::strcmp(buf, "MOVE r11, r8") == 0); isa::disasm(ins[7], buf, sizeof buf); CHECK(std::strcmp(buf, "LOADK r12, 2") == 0); isa::disasm(ins[8], buf, sizeof buf); CHECK(std::strcmp(buf, "CMP_EQ r10, r11, r12") == 0); isa::disasm(ins[9], buf, sizeof buf); CHECK(std::strcmp(buf, "JF r10, +2") == 0); // 假 → else([12]) isa::disasm(ins[10], buf, sizeof buf); CHECK(std::strcmp(buf, "LOADK r9, 3") == 0); isa::disasm(ins[11], buf, sizeof buf); CHECK(std::strcmp(buf, "JMP +1") == 0); // → end([13]) // ELSE isa::disasm(ins[12], buf, sizeof buf); CHECK(std::strcmp(buf, "LOADK r9, 4") == 0); isa::disasm(ins[13], buf, sizeof buf); CHECK(std::strcmp(buf, "RET") == 0); return true; } // ---- 10. 用例 05:WHILE 回环(JF 出口 + JMP 回填)---- static bool test_case05() { std::vector img; std::string err; CHECK(compile_case("05_while_normal", &img, &err)); const compiler::StbView v = compiler::StbView::from(img); CHECK(v.ok()); const compiler::StbView::FuncRow r = v.func_row(0); CHECK(r.nregs == 12); // r8 起:n + 3 临时(条件结果 r9 + 两操作数) CHECK(r.code_len == 10); char buf[64]; const uint8_t* base = v.code_bytes(); const uint32_t* ins = reinterpret_cast(base); isa::disasm(ins[0], buf, sizeof buf); CHECK(std::strcmp(buf, "LOADK r8, 0") == 0); // n := 0 // L_loop [1]:n < 10 → r9 isa::disasm(ins[1], buf, sizeof buf); CHECK(std::strcmp(buf, "MOVE r10, r8") == 0); isa::disasm(ins[2], buf, sizeof buf); CHECK(std::strcmp(buf, "LOADK r11, 1") == 0); // 常量 1 = 10 isa::disasm(ins[3], buf, sizeof buf); CHECK(std::strcmp(buf, "CMP_LT r9, r10, r11") == 0); isa::disasm(ins[4], buf, sizeof buf); CHECK(std::strcmp(buf, "JF r9, +4") == 0); // 假 → RET([9]) // n := n + 1(体语句临时复用 r9/r10) isa::disasm(ins[5], buf, sizeof buf); CHECK(std::strcmp(buf, "MOVE r9, r8") == 0); isa::disasm(ins[6], buf, sizeof buf); CHECK(std::strcmp(buf, "LOADK r10, 2") == 0); // 常量 2 = 1 isa::disasm(ins[7], buf, sizeof buf); CHECK(std::strcmp(buf, "ADD r8, r9, r10") == 0); isa::disasm(ins[8], buf, sizeof buf); CHECK(std::strcmp(buf, "JMP -8") == 0); // 回 L_loop([1]) isa::disasm(ins[9], buf, sizeof buf); CHECK(std::strcmp(buf, "RET") == 0); return true; } // ---- 11. 用例 07:四则 + 比较 ---- static bool test_case07() { std::vector img; std::string err; CHECK(compile_case("07_int_arith", &img, &err)); const compiler::StbView v = compiler::StbView::from(img); CHECK(v.ok()); const compiler::StbView::FuncRow r = v.func_row(0); CHECK(r.nregs == 14); // r8 起:a b c eq + 2 临时 char buf[64]; const uint8_t* base = v.code_bytes(); const uint32_t* ins = reinterpret_cast(base); bool saw_mul = false, saw_div = false, saw_sub = false, saw_gt = false; for (uint32_t i = 0; i < r.code_len; ++i) { isa::disasm(ins[i], buf, sizeof buf); if (std::strstr(buf, "MUL r8, ")) saw_mul = true; if (std::strstr(buf, "DIV r9, ")) saw_div = true; if (std::strstr(buf, "SUB r10, ")) saw_sub = true; if (std::strstr(buf, "CMP_GT r11, ")) saw_gt = true; } CHECK(saw_mul && saw_div && saw_sub && saw_gt); return true; } // ---- 12. 用例 08:TIME 字面量进常量表 ---- static bool test_case08() { std::vector img; std::string err; CHECK(compile_case("08_time_literal", &img, &err)); const compiler::StbView v = compiler::StbView::from(img); CHECK(v.ok()); CHECK(v.n_consts() == 2); const compiler::ConstEntry c0 = v.const_entry(0); CHECK(c0.tag == 2 && c0.value == 10); // T#10ms const compiler::ConstEntry c1 = v.const_entry(1); CHECK(c1.tag == 2 && c1.value == 1250); // T#1s250ms const compiler::StbView::FuncRow r = v.func_row(0); CHECK(r.nregs == 10); // r8 起 char buf[64]; const uint32_t* ins = reinterpret_cast(v.code_bytes()); isa::disasm(ins[0], buf, sizeof buf); CHECK(std::strcmp(buf, "LOADK r8, 0") == 0); isa::disasm(ins[1], buf, sizeof buf); CHECK(std::strcmp(buf, "LOADK r9, 1") == 0); return true; } // ---- 13. 用例 13:FUNCTION + CALL(调用约定 r0 结果 / r1.. 参数)---- static bool test_case13() { std::vector img; std::string err; CHECK(compile_case("13_function_call", &img, &err)); const compiler::StbView v = compiler::StbView::from(img); CHECK(v.ok()); CHECK(v.n_funcs() == 2); CHECK(v.entry_fn_id() == 1); // MAIN 是第二个 POU char buf[64]; const uint8_t* base = v.code_bytes(); // Add(fn_id 0):结果 r0、输入 r1(a) r2(b)、临时 r8/r9 const compiler::StbView::FuncRow fa = v.func_row(0); CHECK(fa.nregs == 10); CHECK(fa.code_len == 4); const uint32_t* ia = reinterpret_cast(base); isa::disasm(ia[0], buf, sizeof buf); CHECK(std::strcmp(buf, "MOVE r8, r1") == 0); // t ← a isa::disasm(ia[1], buf, sizeof buf); CHECK(std::strcmp(buf, "MOVE r9, r2") == 0); // t ← b isa::disasm(ia[2], buf, sizeof buf); CHECK(std::strcmp(buf, "ADD r0, r8, r9") == 0); // 结果 r0 isa::disasm(ia[3], buf, sizeof buf); CHECK(std::strcmp(buf, "RET") == 0); // MAIN(fn_id 1):x 在 r8,实参临时 r9/r10 const compiler::StbView::FuncRow fm = v.func_row(1); CHECK(fm.nregs == 11); CHECK(fm.code_len == 7); const uint32_t* im = reinterpret_cast(base) + fm.code_offset / 4; isa::disasm(im[0], buf, sizeof buf); CHECK(std::strcmp(buf, "LOADK r9, 0") == 0); // 3 isa::disasm(im[1], buf, sizeof buf); CHECK(std::strcmp(buf, "LOADK r10, 1") == 0); // 4 isa::disasm(im[2], buf, sizeof buf); CHECK(std::strcmp(buf, "MOVE r1, r9") == 0); // 参数 1 ← 3 isa::disasm(im[3], buf, sizeof buf); CHECK(std::strcmp(buf, "MOVE r2, r10") == 0); // 参数 2 ← 4 isa::disasm(im[4], buf, sizeof buf); CHECK(std::strcmp(buf, "CALL 0") == 0); isa::disasm(im[5], buf, sizeof buf); CHECK(std::strcmp(buf, "MOVE r8, r0") == 0); // x ← 结果 isa::disasm(im[6], buf, sizeof buf); CHECK(std::strcmp(buf, "RET") == 0); return true; } // ---- 14. 用例 15:用户 FB 内联展开 ---- static bool test_case15() { std::vector img; std::string err; CHECK(compile_case("15_fb_instance", &img, &err)); const compiler::StbView v = compiler::StbView::from(img); CHECK(v.ok()); CHECK(v.n_funcs() == 2); // FB 占位 + MAIN CHECK(v.entry_fn_id() == 1); CHECK(v.data_len() == 24); // 实例 3 槽 × 8 字节定宽 const compiler::StbView::FuncRow fm = v.func_row(1); CHECK(fm.nregs == 12); CHECK(fm.code_len == 12); const uint32_t* im = reinterpret_cast(v.code_bytes()) + fm.code_offset / 4; char buf[64]; // 实参写字段:start := TRUE / stop := FALSE isa::disasm(im[0], buf, sizeof buf); CHECK(std::strcmp(buf, "LOADK r9, 0") == 0); isa::disasm(im[1], buf, sizeof buf); CHECK(std::strcmp(buf, "STORE_GLOBAL r9, 0") == 0); isa::disasm(im[2], buf, sizeof buf); CHECK(std::strcmp(buf, "LOADK r10, 1") == 0); isa::disasm(im[3], buf, sizeof buf); CHECK(std::strcmp(buf, "STORE_GLOBAL r10, 1") == 0); // 内联体:Q := start AND NOT stop(短路跳转) isa::disasm(im[4], buf, sizeof buf); CHECK(std::strcmp(buf, "LOAD_GLOBAL r9, 0") == 0); isa::disasm(im[5], buf, sizeof buf); CHECK(std::strcmp(buf, "JF r9, +3") == 0); isa::disasm(im[9], buf, sizeof buf); CHECK(std::strcmp(buf, "STORE_GLOBAL r9, 2") == 0); // Q 字段 // q := starter.Q isa::disasm(im[10], buf, sizeof buf); CHECK(std::strcmp(buf, "LOAD_GLOBAL r8, 2") == 0); return true; } // ---- 15. 用例 17:TON(CAL_TON + 字段偏移)---- static bool test_case17() { std::vector img; std::string err; CHECK(compile_case("17_ton", &img, &err)); const compiler::StbView v = compiler::StbView::from(img); CHECK(v.ok()); CHECK(v.data_len() == 32); // in@0 pt@8 q@16 et@24 const compiler::StbView::FuncRow r = v.func_row(0); CHECK(r.nregs == 11); const uint32_t* ins = reinterpret_cast(v.code_bytes()); char buf[64]; isa::disasm(ins[0], buf, sizeof buf); CHECK(std::strcmp(buf, "LOADK r9, 0") == 0); isa::disasm(ins[1], buf, sizeof buf); CHECK(std::strcmp(buf, "STORE_GLOBAL r9, 0") == 0); // in isa::disasm(ins[2], buf, sizeof buf); CHECK(std::strcmp(buf, "LOADK r10, 1") == 0); isa::disasm(ins[3], buf, sizeof buf); CHECK(std::strcmp(buf, "STORE_GLOBAL r10, 1") == 0); // pt(槽 1) isa::disasm(ins[4], buf, sizeof buf); CHECK(std::strcmp(buf, "CAL_TON 0") == 0); isa::disasm(ins[5], buf, sizeof buf); CHECK(std::strcmp(buf, "LOAD_GLOBAL r8, 2") == 0); // t.Q(槽 2) return true; } // ---- 16. 用例 18:TOF / CTU ---- static bool test_case18() { std::vector img; std::string err; CHECK(compile_case("18_tof_ctu", &img, &err)); const compiler::StbView v = compiler::StbView::from(img); CHECK(v.ok()); CHECK(v.data_len() == 72); // tf 4 槽 + c 5 槽 = 9 槽 × 8 const compiler::StbView::FuncRow r = v.func_row(0); CHECK(r.nregs == 14); const uint32_t* ins = reinterpret_cast(v.code_bytes()); char buf[64]; isa::disasm(ins[4], buf, sizeof buf); CHECK(std::strcmp(buf, "CAL_TOF 0") == 0); isa::disasm(ins[12], buf, sizeof buf); CHECK(std::strcmp(buf, "CAL_CTU 4") == 0); isa::disasm(ins[13], buf, sizeof buf); CHECK(std::strcmp(buf, "LOAD_GLOBAL r9, 7") == 0); // c.Q(槽 7) isa::disasm(ins[14], buf, sizeof buf); CHECK(std::strcmp(buf, "LOAD_GLOBAL r10, 8") == 0); // c.CV(槽 8) return true; } // ---- 17. line1 全链路(MAIN + Motor FB + I/O + 全局)---- static bool test_line1() { std::vector img; std::string err; using namespace compiler; const std::string toml = std::string(REPO_ROOT) + "/examples/line1/project.toml"; Project p; CHECK(parse_project(toml, &p, &err)); std::vector units; for (const std::string& f : compile_files(p)) { SourceUnit u; CHECK(load_unit(p.base_dir + "/" + f, &u, &err)); units.push_back(std::move(u)); } LinkResult link; CHECK(link_project(p, units, &link, &err)); CHECK(check_project(p, units, link, &err)); compiler::MachineConfig cfg; CHECK(load_cfg(&cfg)); CHECK(codegen_project(p, units, link, cfg, &img, &err)); const compiler::StbView v = compiler::StbView::from(img); CHECK(v.ok()); CHECK(v.n_globals() == 4); CHECK(v.n_funcs() == 2); CHECK(v.entry_fn_id() == 1); CHECK(v.data_len() == 56); // 7 槽 × 8 字节定宽 const compiler::StbView::FuncRow fm = v.func_row(1); CHECK(fm.nregs == 13); CHECK(fm.code_len == 17); const uint32_t* im = reinterpret_cast(v.code_bytes()) + fm.code_offset / 4; char buf[64]; isa::disasm(im[0], buf, sizeof buf); CHECK(std::strcmp(buf, "LOAD_I r8, 1") == 0); // I0_0(io.input) isa::disasm(im[1], buf, sizeof buf); CHECK(std::strcmp(buf, "STORE_GLOBAL r8, 4") == 0); // starter.start(槽 4) isa::disasm(im[2], buf, sizeof buf); CHECK(std::strcmp(buf, "LOAD_GLOBAL r9, 2") == 0); // I0_1 isa::disasm(im[3], buf, sizeof buf); CHECK(std::strcmp(buf, "STORE_GLOBAL r9, 5") == 0); // starter.stop(槽 5) isa::disasm(im[13], buf, sizeof buf); CHECK(std::strcmp(buf, "STORE_GLOBAL r8, 6") == 0); // starter.Q(槽 6) isa::disasm(im[14], buf, sizeof buf); CHECK(std::strcmp(buf, "LOAD_GLOBAL r8, 6") == 0); // starter.Q 读回 isa::disasm(im[15], buf, sizeof buf); CHECK(std::strcmp(buf, "STORE_Q r8, 3") == 0); // Q0_0(io.output) isa::disasm(im[16], buf, sizeof buf); CHECK(std::strcmp(buf, "RET") == 0); return true; } int main() { if (!test_case01()) return 1; if (!test_case02()) return 1; if (!test_case09()) return 1; if (!test_io_ops()) return 1; if (!test_layout()) return 1; if (!test_write_input()) return 1; if (!test_case03()) return 1; if (!test_not()) return 1; if (!test_case04()) return 1; if (!test_case05()) return 1; if (!test_case07()) return 1; if (!test_case08()) return 1; if (!test_case13()) return 1; if (!test_case15()) return 1; if (!test_case17()) return 1; if (!test_case18()) return 1; if (!test_line1()) return 1; std::printf("codegen_test: %d checks passed\n", g_checks); return 0; }