双可执行落地:STCompiler 与 BytecodeExecutor。
- 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 通过
This commit is contained in:
+1
-1
@@ -12,7 +12,7 @@ include("${CMAKE_SOURCE_DIR}/cmake/Standards.cmake")
|
|||||||
add_subdirectory(isa)
|
add_subdirectory(isa)
|
||||||
add_subdirectory(compiler)
|
add_subdirectory(compiler)
|
||||||
add_subdirectory(vm)
|
add_subdirectory(vm)
|
||||||
add_subdirectory(host)
|
add_subdirectory(executor)
|
||||||
|
|
||||||
# 自动化用例
|
# 自动化用例
|
||||||
enable_testing()
|
enable_testing()
|
||||||
|
|||||||
@@ -6,8 +6,15 @@ project(Compiler
|
|||||||
VERSION 0.1
|
VERSION 0.1
|
||||||
DESCRIPTION "ST / toml 编译器")
|
DESCRIPTION "ST / toml 编译器")
|
||||||
|
|
||||||
include_directories(./include)
|
|
||||||
|
|
||||||
# 词法、语法、符号表、类型检查、codegen、链接都留在 compiler 一个库里
|
# 词法、语法、符号表、类型检查、codegen、链接都留在 compiler 一个库里
|
||||||
add_library(compiler STATIC
|
add_library(compiler STATIC
|
||||||
./src/Lexer.cpp)
|
./src/Lexer.cpp)
|
||||||
|
|
||||||
|
target_include_directories(compiler PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||||
|
target_link_libraries(compiler PUBLIC isa)
|
||||||
|
|
||||||
|
# 可执行入口:STCompiler <project.toml> -o <name>.stb
|
||||||
|
add_executable(STCompiler
|
||||||
|
./src/main.cpp)
|
||||||
|
|
||||||
|
target_link_libraries(STCompiler PRIVATE compiler)
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* @file main.cpp
|
||||||
|
* @brief STCompiler 可执行入口
|
||||||
|
* @author
|
||||||
|
* @date 2026-08-19
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
(void)argc;
|
||||||
|
(void)argv;
|
||||||
|
std::printf("STCompiler 0.1\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -2,9 +2,12 @@
|
|||||||
cmake_minimum_required(VERSION 3.21)
|
cmake_minimum_required(VERSION 3.21)
|
||||||
|
|
||||||
# 工程名、版本号、工程描述
|
# 工程名、版本号、工程描述
|
||||||
project(Host
|
project(Executor
|
||||||
VERSION 0.1
|
VERSION 0.1
|
||||||
DESCRIPTION "可执行入口:编译并跑扫描周期")
|
DESCRIPTION "字节码执行器:加载 .stb + sidecar 跑扫描周期")
|
||||||
|
|
||||||
add_executable(host
|
add_executable(BytecodeExecutor
|
||||||
./src/main.cpp)
|
./src/main.cpp)
|
||||||
|
|
||||||
|
# 只链 vm + isa,不链 compiler
|
||||||
|
target_link_libraries(BytecodeExecutor PRIVATE vm)
|
||||||
|
|||||||
+3
-3
@@ -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)。
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* @file main.cpp
|
* @file main.cpp
|
||||||
* @brief host 可执行入口
|
* @brief BytecodeExecutor 可执行入口
|
||||||
|
* @author
|
||||||
* @date 2026-08-19
|
* @date 2026-08-19
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -9,6 +10,6 @@
|
|||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
(void)argc;
|
(void)argc;
|
||||||
(void)argv;
|
(void)argv;
|
||||||
std::printf("Stator host 0.1\n");
|
std::printf("BytecodeExecutor 0.1\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-3
@@ -1,3 +1,14 @@
|
|||||||
# 自动化用例:先放一个必过的空测试,验证 CTest 接线
|
# 自动化用例
|
||||||
add_test(NAME host_version
|
add_test(NAME stcompiler_version
|
||||||
COMMAND host)
|
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)
|
||||||
|
|||||||
@@ -0,0 +1,236 @@
|
|||||||
|
/**
|
||||||
|
* @file isa_test.cpp
|
||||||
|
* @brief isa 合同测试:饱和、编解码、disasm、哈希、映像、sidecar
|
||||||
|
* @author
|
||||||
|
* @date 2026-08-19
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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<int>(TypeTag::Bool) == 0);
|
||||||
|
CHECK(static_cast<int>(TypeTag::Int) == 1);
|
||||||
|
CHECK(static_cast<int>(TypeTag::Time) == 2);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- 2. 操作码与助记符 ----
|
||||||
|
|
||||||
|
static bool test_op() {
|
||||||
|
using namespace isa;
|
||||||
|
CHECK(static_cast<int>(Op::MOVE) == 0);
|
||||||
|
CHECK(static_cast<int>(Op::RET) == 28);
|
||||||
|
CHECK(kOpCount == 29);
|
||||||
|
CHECK(std::strcmp(mnemonic(Op::CAL_CTU), "CAL_CTU") == 0);
|
||||||
|
CHECK(std::strcmp(mnemonic(static_cast<Op>(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<uint8_t> 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<uint8_t> bad = img;
|
||||||
|
bad[0] = 'X';
|
||||||
|
CHECK(!ImageView::from(bad).ok());
|
||||||
|
std::vector<uint8_t> bad2 = img;
|
||||||
|
bad2[60] = 255; // offset_code 越界
|
||||||
|
CHECK(!ImageView::from(bad2).ok());
|
||||||
|
std::vector<uint8_t> bad3 = img;
|
||||||
|
bad3[48] = 2; // n_funcs=2 但表只有 1 行
|
||||||
|
CHECK(!ImageView::from(bad3).ok());
|
||||||
|
std::vector<uint8_t> bad4 = img;
|
||||||
|
bad4[24] = 1; // entry_fn_id 越界
|
||||||
|
CHECK(!ImageView::from(bad4).ok());
|
||||||
|
std::vector<uint8_t> 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<uint8_t> 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<uint8_t> back;
|
||||||
|
CHECK(read_stb_file(stb_path, &back, &err));
|
||||||
|
CHECK(back == img);
|
||||||
|
CHECK(ImageView::from(back).ok());
|
||||||
|
std::remove(stb_path);
|
||||||
|
|
||||||
|
std::vector<IoBinding> 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;
|
||||||
|
}
|
||||||
+3
-2
@@ -6,7 +6,8 @@ project(VM
|
|||||||
VERSION 0.1
|
VERSION 0.1
|
||||||
DESCRIPTION "寄存器虚拟机")
|
DESCRIPTION "寄存器虚拟机")
|
||||||
|
|
||||||
include_directories(./include)
|
|
||||||
|
|
||||||
add_library(vm STATIC
|
add_library(vm STATIC
|
||||||
./src/Machine.cpp)
|
./src/Machine.cpp)
|
||||||
|
|
||||||
|
target_include_directories(vm PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||||
|
target_link_libraries(vm PUBLIC isa)
|
||||||
|
|||||||
Reference in New Issue
Block a user