From 8ded7090c83aad2cec072c366c227eca82f05228 Mon Sep 17 00:00:00 2001 From: chentianya Date: Wed, 26 Aug 2026 09:31:53 +0800 Subject: [PATCH] =?UTF-8?q?=E9=98=B6=E6=AE=B53-2=EF=BC=9Acodegen=5Ftest=20?= =?UTF-8?q?V2=20=E9=87=8D=E5=86=99=EF=BC=88=E5=8E=BB=20isa=20=E4=BE=9D?= =?UTF-8?q?=E8=B5=96=EF=BC=8CL5=20=E8=8C=83=E5=9B=B4=E6=96=AD=E8=A8=80?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - build_case 完整编译管线(parse→load→link(cfg)→check(cfg)→codegen(cfg)) - disasm_lines 按 instr_len 迭代生成指令文本 - 7 用例断言:头字段(version=2/STATOR2/max_stack/n_slots/values_size)、 常量表 8B 无 tag、CAL.TON/STORE_OFF 字段访问、CALL+max_stack、 line1 全链路(n_slots=5:4 全局+1 实例)、跳转回填、全局初值 - CMake 恢复 codegen_test(链 compiler 去 isa);ctest 12/14 --- tests/CMakeLists.txt | 21 +- tests/src/codegen_test.cpp | 859 +++++++++---------------------------- 2 files changed, 217 insertions(+), 663 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4b7272e..beafb38 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -78,17 +78,16 @@ target_compile_definitions(typecheck_test PRIVATE add_test(NAME typecheck_types COMMAND typecheck_test) -# TODO(V2): 阶段 3 重写 codegen_test(用 compiler::Codec 断言 V2 产物,去 isa 依赖) -# 寄存器码测试(12.8 切片 1):用例 01/02 出映像(REPO_ROOT 注入源目录绝对路径) -# add_executable(codegen_test -# ./src/codegen_test.cpp) -# -# target_link_libraries(codegen_test PRIVATE compiler isa) -# target_compile_definitions(codegen_test PRIVATE -# REPO_ROOT="${CMAKE_SOURCE_DIR}") -# -# add_test(NAME codegen_slice1 -# COMMAND codegen_test) +# 寄存器码测试(V2):用例产物结构断言(用 compiler::Codec,去 isa 依赖) +add_executable(codegen_test + ./src/codegen_test.cpp) + +target_link_libraries(codegen_test PRIVATE compiler) +target_compile_definitions(codegen_test PRIVATE + REPO_ROOT="${CMAKE_SOURCE_DIR}") + +add_test(NAME codegen_slice1 + COMMAND codegen_test) # VM 测试:手工映像 + 编译器产物 + 定时器 + 确定性(REPO_ROOT 注入源目录绝对路径) add_executable(vm_test diff --git a/tests/src/codegen_test.cpp b/tests/src/codegen_test.cpp index 2146d57..3d1cb7b 100644 --- a/tests/src/codegen_test.cpp +++ b/tests/src/codegen_test.cpp @@ -1,22 +1,33 @@ /** * @file codegen_test.cpp - * @brief 寄存器码测试(12.8 切片 1):用例 01/02 出映像 + * @brief 寄存器码测试(V2):编译器产物结构断言(用 compiler::Codec,去 isa 依赖) * @author * @date 2026-08-21 + * + * @details 覆盖(L5 范围): + * - 头字段(version=2/model=STATOR2/n_globals/n_consts/n_funcs/n_slots/values_size/ + * offset_meta/max_stack)与段偏移一致性 + * - 指令序列(Codec disasm 文本:LOADK/CAL.TON/CALL/STORE_OFF/LOAD_OFF/JF 等) + * - 槽表(逐条 addr)、值段尺寸与初值 + * - 常量表 8B 无 tag(位模式) + * - 函数表(nregs/code_offset/code_len 字节) + * - 校验链(sha_ok/model_matches) */ #include -#include +#include #include #include #include +#include "compiler/Codec.h" #include "compiler/Codegen.h" #include "compiler/Linker.h" -#include "compiler/Project.h" -#include "compiler/Typecheck.h" -#include "isa/Encode.h" +#include "compiler/MachineConfig.h" +#include "compiler/Parser.h" #include "compiler/Stb.h" +#include "compiler/Typecheck.h" +#include "compiler/TypeInfo.h" #ifndef REPO_ROOT #define REPO_ROOT "." @@ -33,702 +44,246 @@ static int g_checks = 0; ++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; -} +namespace { -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)) { + /** + * @brief 编译一个用例目录为映像字节。 + * @param dir 用例目录名(tests/cases/) + * @param img 输出映像字节 + * @return true 成功;false(任一阶段失败,已打印) + */ + bool build_case(const char* dir, std::vector* img) { + using namespace compiler; + const std::string toml = + std::string(REPO_ROOT) + "/tests/cases/" + dir + "/project.toml"; + Project p; + std::string err; + if (!parse_project(toml, &p, &err)) { + std::printf("FAIL %s parse: %s\n", dir, err.c_str()); 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)) { + std::vector units; + for (const std::string& f : compile_files(p)) { + SourceUnit u; + if (!load_unit(p.base_dir + "/" + f, &u, &err)) { + std::printf("FAIL %s load: %s\n", dir, err.c_str()); + return false; + } + units.push_back(std::move(u)); + } + MachineConfig cfg; + if (!cfg.load(std::string(REPO_ROOT) + "/compiler/machine.toml", &err)) { + std::printf("FAIL %s machine: %s\n", dir, err.c_str()); return false; } - units.push_back(std::move(u)); + LinkResult link; + if (!link_project(p, units, cfg, &link, &err)) { + std::printf("FAIL %s link: %s\n", dir, err.c_str()); + return false; + } + if (!check_project(p, units, link, cfg, &err)) { + std::printf("FAIL %s type: %s\n", dir, err.c_str()); + return false; + } + if (!codegen_project(p, units, link, cfg, img, &err)) { + std::printf("FAIL %s codegen: %s\n", dir, err.c_str()); + return false; + } + return true; } - LinkResult link; - if (!link_project(p, units, &link, err)) { + + /** + * @brief 按函数 fn_id 生成 disasm 文本行列表。 + */ + std::vector disasm_lines(const compiler::MachineConfig& cfg, + const compiler::StbView& v, + uint32_t fn_id) { + std::vector lines; + const compiler::StbView::FuncRow r = v.func_row(fn_id); + const uint8_t* base = v.code_bytes() + r.code_offset; + uint32_t pc = 0; + while (pc < r.code_len) { + char buf[128]; + const size_t ilen = compiler::instr_len(base + pc); + compiler::disasm(cfg, base + pc, ilen, buf, sizeof buf); + lines.push_back(buf); + pc += static_cast(ilen); + } + return lines; + } + + /// 行列表是否包含指定前缀的指令(如 "LOADK.U8")。 + bool has_line(const std::vector& lines, const std::string& prefix) { + for (const std::string& l : lines) { + if (l.compare(0, prefix.size(), prefix) == 0) { + return true; + } + } return false; } - if (!check_project(p, units, link, err)) { - return false; + + /// 全局机器配置(静态单例,加载仓库 machine.toml)。 + const compiler::MachineConfig& cfg_global() { + static const compiler::MachineConfig* kCfg = [] { + auto* cfg = new compiler::MachineConfig(); + std::string err; + if (!cfg->load(std::string(REPO_ROOT) + "/compiler/machine.toml", &err)) { + std::printf("FAIL machine config: %s\n", err.c_str()); + std::exit(1); + } + return cfg; + }(); + return *kCfg; } - return codegen_project(p, units, link, cfg, image, err); -} -// ---- 1. 用例 01:空 MAIN + RET ---- +} // namespace -static bool test_case01() { +// ---- 1. 01_empty_main:最小映像(RET)---- + +static bool test_empty_main() { + using namespace compiler; std::vector img; - std::string err; - CHECK(compile_case("01_empty_main", &img, &err)); - - const compiler::StbView v = compiler::StbView::from(img); + CHECK(build_case("01_empty_main", &img)); + const StbView v = StbView::from(img); CHECK(v.ok()); - CHECK(v.cycle_limit() == 1000); - CHECK(v.dt_ms() == 10); + // 校验链 + CHECK(v.model_matches("STATOR", 2)); + CHECK(v.sha_ok()); + // 头字段 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); + CHECK(v.n_consts() == 0); + CHECK(v.n_slots() == 0); + CHECK(v.values_size() == 0); + CHECK(v.offset_meta() == 0); + CHECK(v.max_stack() == 12 + 8 * 8); // MAIN 帧:12 帧头 + nregs(8)×8 + // 函数表 + const StbView::FuncRow r = v.func_row(0); + CHECK(r.nregs == 8); + CHECK(r.code_len == 4); // 仅 RET(4B) + // 指令序列 + const std::vector lines = disasm_lines(cfg_global(), v, 0); + CHECK(lines.size() == 1); + CHECK(lines[0] == "RET"); return true; } -// ---- 2. 用例 02:BOOL 赋值(TRUE / FALSE → LOADK)---- +// ---- 2. 02_bool_assign:常量表 + LOADK ---- -static bool test_case02() { +static bool test_bool_assign() { + using namespace compiler; std::vector img; - std::string err; - CHECK(compile_case("02_bool_assign", &img, &err)); - - const compiler::StbView v = compiler::StbView::from(img); + CHECK(build_case("02_bool_assign", &img)); + const StbView v = 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); + CHECK(v.n_consts() == 2); // 0 与 1 + const ConstEntry c0 = v.const_entry(0); + const ConstEntry c1 = v.const_entry(1); + CHECK((c0.value == 0 && c1.value == 1) || (c0.value == 1 && c1.value == 0)); + const std::vector lines = disasm_lines(cfg_global(), v, 0); + CHECK(has_line(lines, "LOADK.U8")); + CHECK(has_line(lines, "RET")); return true; } -// ---- 3. 用例 09:GVL + VAR_EXTERNAL(全局读取 LOAD_GLOBAL + 初值数据段)---- +// ---- 3. 17_ton:内建 FB(CAL.TON + 字段访问)---- -static bool test_case09() { +static bool test_ton() { + using namespace compiler; std::vector img; - std::string err; - CHECK(compile_case("09_gvl_external", &img, &err)); - - const compiler::StbView v = compiler::StbView::from(img); + CHECK(build_case("17_ton", &img)); + const StbView v = 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); + CHECK(v.n_slots() == 1); // 1 个 TON 实例 + CHECK(v.values_size() == 32); // TON 实例 32B(in@0/pt@8/q@16/et@24) + CHECK(v.n_consts() == 2); // in 初值 0、pt 初值 30(T#30ms) + const std::vector lines = disasm_lines(cfg_global(), v, 0); + CHECK(has_line(lines, "STORE_OFF.U8")); + CHECK(has_line(lines, "STORE_OFF.I64")); // pt=TIME → I64 + CHECK(has_line(lines, "CAL.TON")); + CHECK(has_line(lines, "LOAD_OFF.U8")); + // 槽表:1 条 addr=0 + CHECK(v.slots_bytes()[0] == 0 && v.slots_bytes()[1] == 0); return true; } -// ---- 4. io 绑定:LOAD_I / STORE_Q ---- +// ---- 4. 13_function_call:CALL + max_stack ---- -static bool test_io_ops() { +static bool test_function_call() { + using namespace compiler; 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(build_case("13_function_call", &img)); + const StbView v = 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.n_funcs() == 2); // FUNCTION + 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); + const std::vector lines = disasm_lines(cfg_global(), v, 1); + CHECK(has_line(lines, "CALL")); + CHECK(has_line(lines, "MOVE r")); + // max_stack ≥ MAIN 帧 + 被调函数帧 + CHECK(v.max_stack() >= (12 + 11 * 8) + (12 + 10 * 8)); 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 + 全局)---- +// ---- 5. 20_line1:全链路(全局 + FB 实例 + I/Q 语义)---- 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); + std::vector img; + CHECK(build_case("20_line1", &img)); + const StbView v = 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 字节定宽 + CHECK(v.n_globals() == 4); // EmergencyStop/I0_0/I0_1/Q0_0 + CHECK(v.n_slots() == 5); // 4 全局 + 1 实例(字段走偏移,实例占 1 条) + CHECK(v.values_size() == 7); // 全局 4×1B + starter 3×1B + CHECK(v.max_stack() >= 12 + 13 * 8); + // 指令序列:LOAD(I 读)、STORE_OFF(FB 输入写)、LOAD_OFF(FB 输出读)、STORE(Q 写) + const std::vector lines = disasm_lines(cfg_global(), v, 1); + CHECK(has_line(lines, "LOAD.U8")); + CHECK(has_line(lines, "STORE_OFF.U8")); + CHECK(has_line(lines, "LOAD_OFF.U8")); + CHECK(has_line(lines, "STORE.U8")); + CHECK(has_line(lines, "JF r")); + return true; +} - 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); +// ---- 6. 05_while_normal:跳转回填(字节单位)---- - // 12.13 写侧自检:型号标识 + SHA-256(compiler::StbView) - const compiler::StbView self = compiler::StbView::from(img); - CHECK(self.ok()); - CHECK(self.model_matches("STATOR", 1)); - CHECK(self.sha_ok()); - std::vector tampered = img; - tampered[100] ^= 0x01; // 篡改代码段一字节 - const compiler::StbView bad = compiler::StbView::from(tampered); - CHECK(bad.ok() && !bad.sha_ok()); // 结构可解析但 SHA 校验失败 +static bool test_while_jumps() { + using namespace compiler; + std::vector img; + CHECK(build_case("05_while_normal", &img)); + const StbView v = StbView::from(img); + CHECK(v.ok()); + const std::vector lines = disasm_lines(cfg_global(), v, 0); + CHECK(has_line(lines, "JF r")); + CHECK(has_line(lines, "JMP ")); + return true; +} + +// ---- 7. 09_gvl_external:全局变量 + 初值 ---- + +static bool test_gvl() { + using namespace compiler; + std::vector img; + CHECK(build_case("09_gvl_external", &img)); + const StbView v = StbView::from(img); + CHECK(v.ok()); + CHECK(v.n_globals() == 1); + CHECK(v.n_slots() == 1); + CHECK(v.values_size() == 2); // G1 : INT(2 字节,初值 5) 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_empty_main()) return 1; + if (!test_bool_assign()) return 1; + if (!test_ton()) return 1; + if (!test_function_call()) return 1; if (!test_line1()) return 1; + if (!test_while_jumps()) return 1; + if (!test_gvl()) return 1; std::printf("codegen_test: %d checks passed\n", g_checks); return 0; }