12.8 切片 2:全局数据区与 I/Q/M(LOAD/STORE 指令)。
- 数据区布局:声明序 + 对齐(BOOL 1B/INT 2B/TIME 8B),初值入数据段 - 读全局 LOAD_GLOBAL / io.input 绑定用 LOAD_I;写 STORE_GLOBAL / io.output 用 STORE_Q;写 io.input 拒绝 - 临时寄存器分配:语句内递增、语句结束复用基址 - codegen_test:51 断言(用例 09 初值+LOAD_GLOBAL、io 绑定 LOAD_I/STORE_Q、混合布局偏移、写输入负例),ctest 9/9
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -58,6 +59,50 @@ static bool compile_case(const char* dir, std::vector<uint8_t>* image, std::stri
|
||||
return codegen_project(p, units, link, 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;
|
||||
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, image, err);
|
||||
}
|
||||
|
||||
// ---- 1. 用例 01:空 MAIN + RET ----
|
||||
|
||||
static bool test_case01() {
|
||||
@@ -119,9 +164,128 @@ static bool test_case02() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---- 3. 用例 09:GVL + 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 isa::ImageView v = isa::ImageView::from(img);
|
||||
CHECK(v.ok());
|
||||
CHECK(v.header().n_globals == 1);
|
||||
CHECK(v.data_len() == 2); // INT 2 字节
|
||||
CHECK(v.data_bytes()[0] == 5); // G1 初值 5(小端低位)
|
||||
CHECK(v.data_bytes()[1] == 0);
|
||||
|
||||
const isa::FuncRow r = v.func_row(0);
|
||||
CHECK(r.nregs == 1); // 仅局部 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 r0, 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 isa::ImageView v = isa::ImageView::from(img);
|
||||
CHECK(v.ok());
|
||||
CHECK(v.header().n_globals == 2);
|
||||
|
||||
const isa::FuncRow r = v.func_row(0);
|
||||
CHECK(r.nregs == 2); // 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 r0, 0") == 0); // q := I0_0(io.input → LOAD_I)
|
||||
isa::disasm(reinterpret_cast<const uint32_t*>(base)[1], buf, sizeof buf);
|
||||
CHECK(std::strcmp(buf, "MOVE r1, r0") == 0); // 临时 ← q
|
||||
isa::disasm(reinterpret_cast<const uint32_t*>(base)[2], buf, sizeof buf);
|
||||
CHECK(std::strcmp(buf, "STORE_Q r1, 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 isa::ImageView v = isa::ImageView::from(img);
|
||||
CHECK(v.ok());
|
||||
CHECK(v.header().n_globals == 3);
|
||||
CHECK(v.data_len() == 16); // 0 + 2 + 8(对齐)= 16
|
||||
CHECK(v.data_bytes()[0] == 0); // B 无初值
|
||||
CHECK(v.data_bytes()[2] == 0x2C && v.data_bytes()[3] == 0x01); // I = 300
|
||||
CHECK(v.data_bytes()[8] == 10); // T = 10ms
|
||||
|
||||
char buf[64];
|
||||
isa::disasm(reinterpret_cast<const uint32_t*>(v.code_bytes())[0], buf, sizeof buf);
|
||||
CHECK(std::strcmp(buf, "LOAD_GLOBAL r0, 2") == 0); // x := I(INT 偏移 2)
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
std::printf("codegen_test: %d checks passed\n", g_checks);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user