阶段 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:
@@ -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
|
||||
Reference in New Issue
Block a user