From dd7f7bcba7a31269abe79638e98acf3a262aa61a Mon Sep 17 00:00:00 2001 From: chentianya Date: Wed, 19 Aug 2026 14:40:44 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=8C=E5=8F=AF=E6=89=A7=E8=A1=8C=E8=90=BD?= =?UTF-8?q?=E5=9C=B0=EF=BC=9ASTCompiler=20=E4=B8=8E=20BytecodeExecutor?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - STCompiler 入口在 compiler/src/main.cpp,链 compiler+isa - host/ 改名 executor/,BytecodeExecutor 链 vm+isa 不链 compiler - compiler/vm 补 PUBLIC 链 isa;顶层加入 executor - CTest:版本冒烟 2 例 + isa_roundtrip(89 断言),3/3 通过 --- CMakeLists.txt | 2 +- compiler/CMakeLists.txt | 11 +- compiler/src/main.cpp | 15 +++ executor/CMakeLists.txt | 9 +- executor/README.md | 6 +- executor/src/main.cpp | 5 +- tests/CMakeLists.txt | 17 ++- tests/src/isa_test.cpp | 236 ++++++++++++++++++++++++++++++++++++++++ vm/CMakeLists.txt | 5 +- 9 files changed, 290 insertions(+), 16 deletions(-) create mode 100644 compiler/src/main.cpp create mode 100644 tests/src/isa_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d813b6..ff69001 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ include("${CMAKE_SOURCE_DIR}/cmake/Standards.cmake") add_subdirectory(isa) add_subdirectory(compiler) add_subdirectory(vm) -add_subdirectory(host) +add_subdirectory(executor) # 自动化用例 enable_testing() diff --git a/compiler/CMakeLists.txt b/compiler/CMakeLists.txt index fb16c4f..c09970c 100644 --- a/compiler/CMakeLists.txt +++ b/compiler/CMakeLists.txt @@ -6,8 +6,15 @@ project(Compiler VERSION 0.1 DESCRIPTION "ST / toml 编译器") -include_directories(./include) - # 词法、语法、符号表、类型检查、codegen、链接都留在 compiler 一个库里 add_library(compiler STATIC ./src/Lexer.cpp) + +target_include_directories(compiler PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(compiler PUBLIC isa) + +# 可执行入口:STCompiler -o .stb +add_executable(STCompiler + ./src/main.cpp) + +target_link_libraries(STCompiler PRIVATE compiler) diff --git a/compiler/src/main.cpp b/compiler/src/main.cpp new file mode 100644 index 0000000..222bc03 --- /dev/null +++ b/compiler/src/main.cpp @@ -0,0 +1,15 @@ +/** + * @file main.cpp + * @brief STCompiler 可执行入口 + * @author + * @date 2026-08-19 + */ + +#include + +int main(int argc, char** argv) { + (void)argc; + (void)argv; + std::printf("STCompiler 0.1\n"); + return 0; +} diff --git a/executor/CMakeLists.txt b/executor/CMakeLists.txt index c7f1fa0..0395530 100644 --- a/executor/CMakeLists.txt +++ b/executor/CMakeLists.txt @@ -2,9 +2,12 @@ cmake_minimum_required(VERSION 3.21) # 工程名、版本号、工程描述 -project(Host +project(Executor VERSION 0.1 - DESCRIPTION "可执行入口:编译并跑扫描周期") + DESCRIPTION "字节码执行器:加载 .stb + sidecar 跑扫描周期") -add_executable(host +add_executable(BytecodeExecutor ./src/main.cpp) + +# 只链 vm + isa,不链 compiler +target_link_libraries(BytecodeExecutor PRIVATE vm) diff --git a/executor/README.md b/executor/README.md index 8ffe590..ca9eb17 100644 --- a/executor/README.md +++ b/executor/README.md @@ -1,5 +1,5 @@ -# host +# executor -可执行入口。CMake 目标:`host`(`EXECUTABLE`),链接 `compiler` 和 `vm`。 +BytecodeExecutor 模块:可执行入口。CMake 目标:`BytecodeExecutor`(`EXECUTABLE`),链接 `vm` 和 `isa`,**不链 `compiler`**。 -职责与边界见 [`doc/host/宿主入口.md`](../doc/host/宿主入口.md)。 +职责与边界见 [`doc/executor/执行器入口.md`](../doc/executor/执行器入口.md)。 diff --git a/executor/src/main.cpp b/executor/src/main.cpp index 5a1330c..b1a1b6d 100644 --- a/executor/src/main.cpp +++ b/executor/src/main.cpp @@ -1,6 +1,7 @@ /** * @file main.cpp - * @brief host 可执行入口 + * @brief BytecodeExecutor 可执行入口 + * @author * @date 2026-08-19 */ @@ -9,6 +10,6 @@ int main(int argc, char** argv) { (void)argc; (void)argv; - std::printf("Stator host 0.1\n"); + std::printf("BytecodeExecutor 0.1\n"); return 0; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8129641..44ab0de 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,3 +1,14 @@ -# 自动化用例:先放一个必过的空测试,验证 CTest 接线 -add_test(NAME host_version - COMMAND host) +# 自动化用例 +add_test(NAME stcompiler_version + COMMAND STCompiler) +add_test(NAME bytecode_executor_version + COMMAND BytecodeExecutor) + +# isa 合同测试:链接 isa 库,断言覆盖饱和/编解码/disasm/哈希/映像/sidecar +add_executable(isa_test + ./src/isa_test.cpp) + +target_link_libraries(isa_test PRIVATE isa) + +add_test(NAME isa_roundtrip + COMMAND isa_test) diff --git a/tests/src/isa_test.cpp b/tests/src/isa_test.cpp new file mode 100644 index 0000000..ce95a3b --- /dev/null +++ b/tests/src/isa_test.cpp @@ -0,0 +1,236 @@ +/** + * @file isa_test.cpp + * @brief isa 合同测试:饱和、编解码、disasm、哈希、映像、sidecar + * @author + * @date 2026-08-19 + */ + +#include +#include +#include +#include +#include + +#include "isa/Encode.h" +#include "isa/Image.h" +#include "isa/Instr.h" +#include "isa/Op.h" +#include "isa/Types.h" + +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) + +// ---- 1. 饱和算术 ---- + +static bool test_sat() { + using namespace isa::types; + CHECK(sat_add(30000, 30000) == 32767); + CHECK(sat_add(-30000, -30000) == -32768); + CHECK(sat_add(10, 20) == 30); + CHECK(sat_sub(-30000, 30000) == -32768); + CHECK(sat_sub(5, 3) == 2); + CHECK(sat_mul(300, 200) == 32767); + CHECK(sat_mul(-300, 200) == -32768); + CHECK(sat_mul(6, 7) == 42); + CHECK(sat_div(10, 3) == 3); + CHECK(sat_div(-32768, -1) == 32767); // INT_MIN / -1 饱和 + CHECK(sat_div(7, 0) == 7); // 除零返回被除数 + CHECK(static_cast(TypeTag::Bool) == 0); + CHECK(static_cast(TypeTag::Int) == 1); + CHECK(static_cast(TypeTag::Time) == 2); + return true; +} + +// ---- 2. 操作码与助记符 ---- + +static bool test_op() { + using namespace isa; + CHECK(static_cast(Op::MOVE) == 0); + CHECK(static_cast(Op::RET) == 28); + CHECK(kOpCount == 29); + CHECK(std::strcmp(mnemonic(Op::CAL_CTU), "CAL_CTU") == 0); + CHECK(std::strcmp(mnemonic(static_cast(255)), "???") == 0); + return true; +} + +// ---- 3. 指令打包 / 拆字段 ---- + +static bool test_instr() { + using namespace isa; + const Instr w = pack(Op::ADD, 5, 3, 4); + CHECK(op(w) == Op::ADD); + CHECK(rd(w) == 5); + CHECK(a(w) == 3); + CHECK(b(w) == 4); + CHECK(imm16(enc_imm(Op::LOADK, 0, 0x1234)) == 0x1234); + CHECK(off16(enc_jmp(-1)) == -1); + CHECK(off16(enc_jmp(4)) == 4); + CHECK(rd(enc_jc(Op::JT, 2, -3)) == 2); + CHECK(off16(enc_jc(Op::JT, 2, -3)) == -3); + CHECK(imm16(enc_call(0xABCD)) == 0xABCD); + CHECK(imm16(enc_slot(Op::LOAD_I, 7, 300)) == 300); + CHECK(rd(enc_slot(Op::LOAD_I, 7, 300)) == 7); + CHECK(rd(enc_rr(Op::MOVE, 1, 2)) == 1); + CHECK(a(enc_rr(Op::MOVE, 1, 2)) == 2); + CHECK(op(enc_ret()) == Op::RET); + return true; +} + +// ---- 4. encode / decode / disasm ---- + +static bool test_encode() { + using namespace isa; + const Instr w = enc_rrr(Op::ADD, 5, 3, 4); + CHECK(encode(decode(w)) == w); + CHECK(decode(w).op == Op::ADD); + CHECK(decode(w).rd == 5); + CHECK(decode(w).a == 3); + CHECK(decode(w).b == 4); + + char buf[64]; + disasm(w, buf, sizeof buf); + CHECK(std::strcmp(buf, "ADD r5, r3, r4") == 0); + disasm(enc_rr(Op::MOVE, 1, 2), buf, sizeof buf); + CHECK(std::strcmp(buf, "MOVE r1, r2") == 0); + disasm(enc_imm(Op::LOADK, 0, 3), buf, sizeof buf); + CHECK(std::strcmp(buf, "LOADK r0, 3") == 0); + disasm(enc_jmp(4), buf, sizeof buf); + CHECK(std::strcmp(buf, "JMP +4") == 0); + disasm(enc_jmp(-1), buf, sizeof buf); + CHECK(std::strcmp(buf, "JMP -1") == 0); + disasm(enc_jc(Op::JT, 0, -1), buf, sizeof buf); + CHECK(std::strcmp(buf, "JT r0, -1") == 0); + disasm(enc_call(1), buf, sizeof buf); + CHECK(std::strcmp(buf, "CALL 1") == 0); + disasm(enc_slot(Op::LOAD_I, 0, 2), buf, sizeof buf); + CHECK(std::strcmp(buf, "LOAD_I r0, 2") == 0); + disasm(enc_slot(Op::STORE_Q, 4, 0), buf, sizeof buf); + CHECK(std::strcmp(buf, "STORE_Q r4, 0") == 0); + disasm(enc_ret(), buf, sizeof buf); + CHECK(std::strcmp(buf, "RET") == 0); + disasm(enc_imm(Op::CAL_TON, 0, 0), buf, sizeof buf); + CHECK(std::strcmp(buf, "CAL_TON 0") == 0); + disasm(0xFFu, buf, sizeof buf); // 未知操作码 + CHECK(std::strcmp(buf, "??? 0x000000ff") == 0); + return true; +} + +// ---- 5. FNV-1a 64 ---- + +static bool test_fnv() { + using namespace isa; + CHECK(fnv1a64(0, 0) == kFnvBasis); + const uint8_t a1[] = {'a'}; + CHECK(fnv1a64(a1, 1) == 0xaf63dc4c8601ec8cull); + return true; +} + +// ---- 6. 映像:write_ret_main → ImageView ---- + +static bool test_image() { + using namespace isa; + const uint64_t hash = 0x123456789abcdef0ull; + std::vector img = write_ret_main(100000, 10, hash); + + ImageView v = ImageView::from(img); + CHECK(v.ok()); + CHECK(v.error().empty()); + CHECK(v.header().cycle_limit == 100000); + CHECK(v.header().dt_ms == 10); + CHECK(v.header().project_hash == hash); + CHECK(v.header().entry_fn_id == 0); + CHECK(v.header().n_funcs == 1); + CHECK(v.header().n_globals == 0); + CHECK(v.header().n_i == 0); + CHECK(v.header().n_q == 0); + CHECK(v.header().n_m == 0); + CHECK(v.header().offset_const == kHeaderSize); + CHECK(v.header().offset_data == kHeaderSize + kFuncRowSize + 4); + CHECK(v.code_len() == 4); + CHECK(v.data_len() == 0); + + const FuncRow r = v.func_row(0); + CHECK(r.nregs == 0); + CHECK(r.code_offset == 0); + CHECK(r.code_len == 1); + + char buf[32]; + disasm(v.code_bytes()[0], buf, sizeof buf); + CHECK(std::strcmp(buf, "RET") == 0); + + // 损坏映像必须拒绝 + std::vector bad = img; + bad[0] = 'X'; + CHECK(!ImageView::from(bad).ok()); + std::vector bad2 = img; + bad2[60] = 255; // offset_code 越界 + CHECK(!ImageView::from(bad2).ok()); + std::vector bad3 = img; + bad3[48] = 2; // n_funcs=2 但表只有 1 行 + CHECK(!ImageView::from(bad3).ok()); + std::vector bad4 = img; + bad4[24] = 1; // entry_fn_id 越界 + CHECK(!ImageView::from(bad4).ok()); + std::vector short_img = img; + short_img.resize(kHeaderSize - 1); + CHECK(!ImageView::from(short_img).ok()); + return true; +} + +// ---- 7. .stb 文件与 sidecar ---- + +static bool test_files() { + using namespace isa; + std::vector img = write_ret_main(100000, 10, 0); + + const char* stb_path = "isa_test_tmp.stb"; + std::string err; + CHECK(write_stb_file(stb_path, img, &err)); + std::vector back; + CHECK(read_stb_file(stb_path, &back, &err)); + CHECK(back == img); + CHECK(ImageView::from(back).ok()); + std::remove(stb_path); + + std::vector bs; + IoBinding b1; + b1.var = "I0_0"; b1.slot = 1; b1.channel = 0; b1.bit = 0; b1.is_input = true; + IoBinding b2; + b2.var = "Q0_0"; b2.slot = 3; b2.channel = 0; b2.bit = 0; b2.is_input = false; + bs.push_back(b1); + bs.push_back(b2); + + const std::string sc = make_sidecar(bs); + CHECK(sc.find("[[io.input]]") == 0); + CHECK(sc.find("var = \"I0_0\"") != std::string::npos); + CHECK(sc.find("slot = 1") != std::string::npos); + CHECK(sc.find("channel = 0") != std::string::npos); + CHECK(sc.find("[[io.output]]") != std::string::npos); + CHECK(sc.find("var = \"Q0_0\"") != std::string::npos); + CHECK(sc.find("slot = 3") != std::string::npos); + + const char* sc_path = "isa_test_tmp.runtime.toml"; + CHECK(write_sidecar_file(sc_path, bs, &err)); + std::remove(sc_path); + return true; +} + +int main() { + if (!test_sat()) return 1; + if (!test_op()) return 1; + if (!test_instr()) return 1; + if (!test_encode()) return 1; + if (!test_fnv()) return 1; + if (!test_image()) return 1; + if (!test_files()) return 1; + std::printf("isa_test: %d checks passed\n", g_checks); + return 0; +} diff --git a/vm/CMakeLists.txt b/vm/CMakeLists.txt index 9ffb15f..6f39576 100644 --- a/vm/CMakeLists.txt +++ b/vm/CMakeLists.txt @@ -6,7 +6,8 @@ project(VM VERSION 0.1 DESCRIPTION "寄存器虚拟机") -include_directories(./include) - add_library(vm STATIC ./src/Machine.cpp) + +target_include_directories(vm PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(vm PUBLIC isa)