阶段 B 步骤 4:compiler 配置驱动化,去除 isa 依赖。

- Project.h 自带 IoBinding;Stb.h/cpp:compiler 侧 .stb 规范(常量/ConstEntry/FNV/StbView/读写/sidecar)
- Codec.h/cpp:指令字 pack/字段/配置驱动 disasm(format 表驱动)
- Codegen:opcode 全查 MachineConfig(E_* 发射器)、常量 tag 查配置类型、初值按基元宽度写、
  映像拼装用 Stb 常量;codegen_project 增加 cfg 参数
- main.cpp:--machine <path>(编译/反汇编必填),--disasm 用 StbView+Codec
- CMake:compiler 不再链接 isa(仅 toml++ 私有);测试补链 isa(自身断言用)
- 验证:20 用例字节不变(hash 0xc3fe4ae74ad45900 相同)、ctest 12/12、缺 --machine 报错退出
This commit is contained in:
2026-08-21 22:14:29 +08:00
parent 44b5ecd46d
commit 447a8d0119
15 changed files with 739 additions and 390 deletions
+225
View File
@@ -0,0 +1,225 @@
/**
* @file Stb.cpp
* @brief 编译器自带的 .stb 映像规范(写侧)+ 只读视图 + FNV-1a + sidecar
* @author
* @date 2026-08-21
*/
#include "compiler/Stb.h"
#include <cstdio>
#include <cstring>
#include <fstream>
#include "compiler/Project.h"
namespace compiler {
uint64_t fnv1a64_update(uint64_t h, const uint8_t* data, size_t len) {
for (size_t i = 0; i < len; ++i) {
h ^= data[i];
h *= kFnvPrime;
}
return h;
}
uint64_t fnv1a64(const uint8_t* data, size_t len) {
return fnv1a64_update(kFnvBasis, data, len);
}
namespace {
uint32_t get_le32(const uint8_t* p) {
return static_cast<uint32_t>(p[0])
| (static_cast<uint32_t>(p[1]) << 8)
| (static_cast<uint32_t>(p[2]) << 16)
| (static_cast<uint32_t>(p[3]) << 24);
}
uint64_t get_le64(const uint8_t* p) {
uint64_t v = 0;
for (int i = 0; i < 8; ++i) {
v |= static_cast<uint64_t>(p[i]) << (8 * i);
}
return v;
}
} // namespace
StbView StbView::from(const uint8_t* buf, size_t len) {
StbView v;
v.buf_ = buf;
v.len_ = len;
if (buf == nullptr) {
v.err_ = "null buffer";
return v;
}
if (len < kHeaderSize) {
v.err_ = "image too short";
return v;
}
if (get_le32(buf + 0) != kMagic) {
v.err_ = "bad magic";
return v;
}
if (get_le32(buf + 4) != kVersion) {
v.err_ = "bad version";
return v;
}
v.cycle_limit_ = get_le32(buf + 8);
v.dt_ms_ = get_le32(buf + 12);
v.project_hash_ = get_le64(buf + 16);
v.entry_fn_id_ = get_le32(buf + 24);
v.n_globals_ = get_le32(buf + 28);
v.n_consts_ = get_le32(buf + 44);
v.n_funcs_ = get_le32(buf + 48);
v.offset_code_ = get_le32(buf + 60);
v.offset_fb_ = get_le32(buf + 64);
v.offset_data_ = get_le32(buf + 68);
// 段校验
const uint64_t offs[5] = {get_le32(buf + 52), get_le32(buf + 56), v.offset_code_,
v.offset_fb_, v.offset_data_};
for (int i = 0; i < 5; ++i) {
if (offs[i] < kHeaderSize || offs[i] > len) {
v.err_ = "segment offset out of range";
return v;
}
if (i > 0 && offs[i] < offs[i - 1]) {
v.err_ = "segment offsets not monotonic";
return v;
}
}
if (offs[1] - offs[0] != static_cast<uint64_t>(v.n_consts_) * kConstEntrySize) {
v.err_ = "const table size mismatch";
return v;
}
if (offs[2] - offs[1] != static_cast<uint64_t>(v.n_funcs_) * kFuncRowSize) {
v.err_ = "function table size mismatch";
return v;
}
if ((v.offset_fb_ - v.offset_code_) % 4 != 0) {
v.err_ = "code segment not 4-byte aligned";
return v;
}
if (v.entry_fn_id_ >= v.n_funcs_ && v.n_funcs_ != 0) {
v.err_ = "entry fn_id out of range";
return v;
}
v.ok_ = true;
v.err_.clear();
return v;
}
StbView StbView::from(const std::vector<uint8_t>& buf) {
return from(buf.data(), buf.size());
}
ConstEntry StbView::const_entry(size_t i) const {
ConstEntry e;
if (ok_ && i < n_consts_) {
const uint8_t* p = buf_ + offs_of_const() + i * kConstEntrySize;
e.tag = get_le32(p);
e.value = get_le64(p + 4);
}
return e;
}
uint32_t StbView::offs_of_const() const {
// offset_const = offs[0],由 from() 已校验的段起点
return static_cast<uint32_t>(get_le32(buf_ + 52));
}
StbView::FuncRow StbView::func_row(size_t i) const {
FuncRow r;
if (ok_ && i < n_funcs_) {
const uint8_t* p = buf_ + get_le32(buf_ + 56) + i * kFuncRowSize;
r.nregs = get_le32(p + 0);
r.code_offset = get_le32(p + 4);
r.code_len = get_le32(p + 8);
}
return r;
}
const uint8_t* StbView::code_bytes() const {
return ok_ ? buf_ + offset_code_ : nullptr;
}
size_t StbView::code_len() const {
return ok_ ? offset_fb_ - offset_code_ : 0;
}
const uint8_t* StbView::data_bytes() const {
return ok_ ? buf_ + offset_data_ : nullptr;
}
size_t StbView::data_len() const {
return ok_ ? len_ - offset_data_ : 0;
}
bool read_stb_file(const char* path, std::vector<uint8_t>* out, std::string* err) {
std::ifstream in(path, std::ios::binary);
if (!in) {
if (err) {
*err = "cannot open for read: " + std::string(path);
}
return false;
}
out->assign(std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>());
return true;
}
bool write_stb_file(const char* path, const std::vector<uint8_t>& img, std::string* err) {
std::FILE* f = std::fopen(path, "wb");
if (f == nullptr) {
if (err) {
*err = "cannot open for write: " + std::string(path);
}
return false;
}
const bool ok = img.empty() || std::fwrite(&img[0], 1, img.size(), f) == img.size();
std::fclose(f);
if (!ok && err) {
*err = "write failed: " + std::string(path);
}
return ok;
}
std::string make_sidecar(const std::vector<IoBinding>& bindings) {
std::string out;
for (const IoBinding& b : bindings) {
char buf[64];
out += b.is_input ? "[[io.input]]\n" : "[[io.output]]\n";
out += "var = \"";
out += b.var;
out += "\"\n";
std::snprintf(buf, sizeof buf, "slot = %u\n", static_cast<unsigned>(b.slot));
out += buf;
std::snprintf(buf, sizeof buf, "channel = %u\n", static_cast<unsigned>(b.channel));
out += buf;
std::snprintf(buf, sizeof buf, "bit = %u\n", static_cast<unsigned>(b.bit));
out += buf;
out += "\n";
}
return out;
}
bool write_sidecar_file(const char* path, const std::vector<IoBinding>& bindings,
std::string* err) {
std::FILE* f = std::fopen(path, "wb");
if (f == nullptr) {
if (err) {
*err = "cannot open for write: " + std::string(path);
}
return false;
}
const std::string s = make_sidecar(bindings);
const bool ok = s.empty() || std::fwrite(s.data(), 1, s.size(), f) == s.size();
std::fclose(f);
if (!ok && err) {
*err = "write failed: " + std::string(path);
}
return ok;
}
} // namespace compiler