Files
Interpreter/tests/src/codegen_test.cpp
T
Admin 5fccbadaa8 阶段 C 步骤 7:isa 拆出 Image,vm 依赖收窄。
- 删 isa/Image.h + Image.cpp(ImageView/FNV/写文件/sidecar 不再属于 isa)
- vm 自实现只读视图 vm/Image.h/cpp:头 104(12.13)/段校验/缺 SHA 尾报错
  (型号匹配与 SHA 校验留步骤 8);Machine 改用 vm::Image + vm::ImageHeader
- executor:本地读文件(替换 isa::read_stb_file)
- 测试:isa_test 删映像/FNV 部分;codegen_test/vm_test 改用 compiler::StbView /
  vm::ConstEntry;vm_test 手拼映像适配新格式(头 104 + 尾 32 占位)
- 验证:line1 编译 288 字节 → BytecodeExecutor 回放 Q 正确;ctest 12/12
2026-08-21 22:24:57 +08:00

725 lines
27 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file codegen_test.cpp
* @brief 寄存器码测试(12.8 切片 1):用例 01/02 出映像
* @author
* @date 2026-08-21
*/
#include <cstdio>
#include <cstring>
#include <filesystem>
#include <string>
#include <vector>
#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<uint8_t>* 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<SourceUnit> 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<uint8_t>* 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<SourceUnit> 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<uint8_t> 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. 用例 02BOOL 赋值(TRUE / FALSE → LOADK----
static bool test_case02() {
std::vector<uint8_t> 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, br8 起)
CHECK(r.code_len == 3);
// 从字节码段取指令(按函数行 code_offset 定位)
const uint8_t* base = v.code_bytes();
char buf[64];
isa::disasm(reinterpret_cast<const uint32_t*>(base)[0], buf, sizeof buf);
CHECK(std::strcmp(buf, "LOADK r8, 0") == 0); // a := TRUE(常量 0
isa::disasm(reinterpret_cast<const uint32_t*>(base)[1], buf, sizeof buf);
CHECK(std::strcmp(buf, "LOADK r9, 1") == 0); // b := FALSE(常量 1
isa::disasm(reinterpret_cast<const uint32_t*>(base)[2], buf, sizeof buf);
CHECK(std::strcmp(buf, "RET") == 0);
// 常量表:0 = BOOL 11 = 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. 用例 09GVL + VAR_EXTERNAL(全局读取 LOAD_GLOBAL + 初值数据段)----
static bool test_case09() {
std::vector<uint8_t> 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<const uint32_t*>(base)[0], buf, sizeof buf);
CHECK(std::strcmp(buf, "LOAD_GLOBAL r8, 0") == 0); // x := G1(槽 0 偏移 0
isa::disasm(reinterpret_cast<const uint32_t*>(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<uint8_t> 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<const uint32_t*>(base)[0], buf, sizeof buf);
CHECK(std::strcmp(buf, "LOAD_I r8, 0") == 0); // q := I0_0io.input → LOAD_I
isa::disasm(reinterpret_cast<const uint32_t*>(base)[1], buf, sizeof buf);
CHECK(std::strcmp(buf, "MOVE r9, r8") == 0); // 临时 ← q
isa::disasm(reinterpret_cast<const uint32_t*>(base)[2], buf, sizeof buf);
CHECK(std::strcmp(buf, "STORE_Q r9, 1") == 0); // Q0_0 := 临时(io.output → STORE_Q
isa::disasm(reinterpret_cast<const uint32_t*>(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<uint8_t> 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<const uint32_t*>(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<uint8_t> 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<uint8_t> 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<const uint32_t*>(base);
// x := a AND bMOVE 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 bMOVE 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<uint8_t> 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<const uint32_t*>(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. 用例 04IF / ELSIF / ELSECMP + JF + JMP 回填)----
static bool test_case04() {
std::vector<uint8_t> 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<const uint32_t*>(base);
// IF sel = 0 THENsel→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. 用例 05WHILE 回环(JF 出口 + JMP 回填)----
static bool test_case05() {
std::vector<uint8_t> 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<const uint32_t*>(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<uint8_t> 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<const uint32_t*>(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. 用例 08TIME 字面量进常量表 ----
static bool test_case08() {
std::vector<uint8_t> 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<const uint32_t*>(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. 用例 13FUNCTION + CALL(调用约定 r0 结果 / r1.. 参数)----
static bool test_case13() {
std::vector<uint8_t> 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();
// Addfn_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<const uint32_t*>(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);
// MAINfn_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<const uint32_t*>(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<uint8_t> 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<const uint32_t*>(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. 用例 17TONCAL_TON + 字段偏移)----
static bool test_case17() {
std::vector<uint8_t> 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<const uint32_t*>(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. 用例 18TOF / CTU ----
static bool test_case18() {
std::vector<uint8_t> 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<const uint32_t*>(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<uint8_t> 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<SourceUnit> 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<const uint32_t*>(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_0io.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_0io.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;
}