阶段 C 步骤 7:isa 拆出 Image,vm 依赖收窄。

- 删 isa/Image.h + Image.cpp(ImageView/FNV/写文件/sidecar 不再属于 isa)
- vm 自实现只读视图 vm/Image.h/cpp:头 104(12.13)/段校验/缺 SHA 尾报错
  (型号匹配与 SHA 校验留步骤 8);Machine 改用 vm::Image + vm::ImageHeader
- executor:本地读文件(替换 isa::read_stb_file)
- 测试:isa_test 删映像/FNV 部分;codegen_test/vm_test 改用 compiler::StbView /
  vm::ConstEntry;vm_test 手拼映像适配新格式(头 104 + 尾 32 占位)
- 验证:line1 编译 288 字节 → BytecodeExecutor 回放 Q 正确;ctest 12/12
This commit is contained in:
2026-08-21 22:24:57 +08:00
parent a308ae713d
commit 5fccbadaa8
12 changed files with 334 additions and 647 deletions
+1 -2
View File
@@ -7,7 +7,6 @@ project(ISA
DESCRIPTION "指令集架构")
add_library(isa STATIC
./src/Encode.cpp
./src/Image.cpp)
./src/Encode.cpp)
target_include_directories(isa PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
-125
View File
@@ -1,125 +0,0 @@
/**
* @file Image.h
* @brief 映像头、段、FNV-1a 哈希、.stb 文件与 sidecar
* @author
* @date 2026-08-19
*/
#pragma once
#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>
#include "isa/Types.h"
namespace isa {
// 魔数 "STSC"(小端 u32)、版本
static const uint32_t kMagic = 0x43545353u;
static const uint32_t kVersion = 1;
// 头与表行宽(见 Doc/isa/指令与映像.md
static const size_t kHeaderSize = 72;
static const size_t kConstEntrySize = 12;
static const size_t kFuncRowSize = 12;
// FNV-1a 64basis / prime(见 Doc/isa/指令与映像.md
static const uint64_t kFnvBasis = 0xcbf29ce484222325ull;
static const uint64_t kFnvPrime = 0x100000001b3ull;
// FNV-1a 64 增量与一次性
uint64_t fnv1a64_update(uint64_t h, const uint8_t* data, size_t len);
uint64_t fnv1a64(const uint8_t* data, size_t len);
// 映像头(字节布局见 Doc/isa/指令与映像.md
struct ImageHeader {
uint32_t cycle_limit;
uint32_t dt_ms;
uint64_t project_hash;
uint32_t entry_fn_id;
uint32_t n_globals;
uint32_t n_i;
uint32_t n_q;
uint32_t n_m;
uint32_t n_consts;
uint32_t n_funcs;
uint32_t offset_const;
uint32_t offset_funcs;
uint32_t offset_code;
uint32_t offset_fb;
uint32_t offset_data;
};
// 常量表一行
struct ConstEntry {
types::TypeTag tag;
uint64_t value;
};
// 函数表一行
struct FuncRow {
uint32_t nregs;
uint32_t code_offset; // 相对字节码段起点
uint32_t code_len; // 指令条数
};
// I/O 绑定(sidecar 一行)
struct IoBinding {
std::string var;
uint32_t slot;
uint32_t channel;
uint32_t bit;
bool is_input; // true = [[io.input]]false = [[io.output]]
};
// 只读视图:校验过的映像
class ImageView {
public:
// 默认构造为无效态(ok() == falseerror() == "uninitialized"
ImageView();
static ImageView from(const uint8_t* buf, size_t len);
static ImageView from(const std::vector<uint8_t>& buf);
bool ok() const;
const std::string& error() const;
const ImageHeader& header() const;
const uint8_t* raw() const;
size_t raw_len() const;
ConstEntry const_entry(size_t i) const;
FuncRow func_row(size_t i) const;
const uint8_t* code_bytes() const; // 字节码段起点
size_t code_len() const; // 字节数
const uint8_t* data_bytes() const; // 数据段起点
size_t data_len() const; // 字节数
private:
// 私有成员与内部构造辅助(from 使用)
const uint8_t* buf_;
size_t len_;
bool ok_;
std::string err_;
ImageHeader hdr_;
};
// 写最小映像:空槽 + MAINfn_id=0+ 一条 RET
std::vector<uint8_t> write_ret_main(uint32_t cycle_limit, uint32_t dt_ms,
uint64_t project_hash);
// .stb 文件读写(纯字节,校验交给 read_image / ImageView
bool write_stb_file(const char* path, const std::vector<uint8_t>& image,
std::string* err);
bool read_stb_file(const char* path, std::vector<uint8_t>* image,
std::string* err);
// sidecar 生成与写文件(格式见 Doc/isa/指令与映像.md
std::string make_sidecar(const std::vector<IoBinding>& bindings);
bool write_sidecar_file(const char* path,
const std::vector<IoBinding>& bindings,
std::string* err);
}
-321
View File
@@ -1,321 +0,0 @@
/**
* @file Image.cpp
* @brief 映像头、段、FNV-1a 哈希、.stb 文件与 sidecar
* @author
* @date 2026-08-19
*/
#include "isa/Image.h"
#include <cstdio>
#include <cstring>
#include "isa/Instr.h"
namespace isa {
// ---- 小端读写(只放 .cpp,不把 packed struct 当 ABI----
static void put_le32(std::vector<uint8_t>& b, size_t off, uint32_t v) {
b[off + 0] = static_cast<uint8_t>(v & 0xFFu);
b[off + 1] = static_cast<uint8_t>((v >> 8) & 0xFFu);
b[off + 2] = static_cast<uint8_t>((v >> 16) & 0xFFu);
b[off + 3] = static_cast<uint8_t>((v >> 24) & 0xFFu);
}
static 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);
}
static void put_le64(std::vector<uint8_t>& b, size_t off, uint64_t v) {
for (int i = 0; i < 8; ++i) {
b[off + i] = static_cast<uint8_t>((v >> (8 * i)) & 0xFFu);
}
}
static 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;
}
// ---- FNV-1a 64 ----
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);
}
// ---- 最小映像:空槽 + MAIN + 一条 RET ----
std::vector<uint8_t> write_ret_main(uint32_t cycle_limit, uint32_t dt_ms,
uint64_t project_hash) {
std::vector<uint8_t> b(kHeaderSize + kFuncRowSize + 4, 0);
put_le32(b, 0, kMagic);
put_le32(b, 4, kVersion);
put_le32(b, 8, cycle_limit);
put_le32(b, 12, dt_ms);
put_le64(b, 16, project_hash);
put_le32(b, 24, 0); // entry_fn_id = MAIN = 0
// n_globals / n_i / n_q / n_m = 0
// n_consts = 0
put_le32(b, 48, 1); // n_funcs = 1
const size_t off_funcs = kHeaderSize; // 72
const size_t off_code = off_funcs + kFuncRowSize; // 84
const size_t off_end = off_code + 4; // 88
put_le32(b, 52, static_cast<uint32_t>(off_funcs)); // offset_const
put_le32(b, 56, static_cast<uint32_t>(off_funcs)); // offset_funcs
put_le32(b, 60, static_cast<uint32_t>(off_code)); // offset_code
put_le32(b, 64, static_cast<uint32_t>(off_end)); // offset_fb
put_le32(b, 68, static_cast<uint32_t>(off_end)); // offset_data
// 函数表一行:MAIN,无寄存器,代码 0 起 1 条
put_le32(b, off_funcs + 0, 0); // nregs
put_le32(b, off_funcs + 4, 0); // code_offset
put_le32(b, off_funcs + 8, 1); // code_len
// 字节码:RET
put_le32(b, off_code, pack(Op::RET, 0, 0, 0));
return b;
}
// ---- 只读视图 ----
ImageView::ImageView()
: buf_(0), len_(0), ok_(false), err_("uninitialized"), hdr_() {}
ImageView ImageView::from(const uint8_t* buf, size_t len) {
ImageView v;
v.buf_ = buf;
v.len_ = len;
if (buf == 0) {
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;
}
ImageHeader& h = v.hdr_;
h.cycle_limit = get_le32(buf + 8);
h.dt_ms = get_le32(buf + 12);
h.project_hash = get_le64(buf + 16);
h.entry_fn_id = get_le32(buf + 24);
h.n_globals = get_le32(buf + 28);
h.n_i = get_le32(buf + 32);
h.n_q = get_le32(buf + 36);
h.n_m = get_le32(buf + 40);
h.n_consts = get_le32(buf + 44);
h.n_funcs = get_le32(buf + 48);
h.offset_const = get_le32(buf + 52);
h.offset_funcs = get_le32(buf + 56);
h.offset_code = get_le32(buf + 60);
h.offset_fb = get_le32(buf + 64);
h.offset_data = get_le32(buf + 68);
// 段校验:连续、单调不减、末段终点不越界
const uint64_t offs[5] = {
h.offset_const, h.offset_funcs, h.offset_code, h.offset_fb, h.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;
}
}
const uint64_t const_len = h.offset_funcs - h.offset_const;
const uint64_t funcs_len = h.offset_code - h.offset_funcs;
if (const_len != static_cast<uint64_t>(h.n_consts) * kConstEntrySize) {
v.err_ = "const table size mismatch";
return v;
}
if (funcs_len != static_cast<uint64_t>(h.n_funcs) * kFuncRowSize) {
v.err_ = "function table size mismatch";
return v;
}
if ((h.offset_fb - h.offset_code) % 4 != 0) {
v.err_ = "code segment not 4-byte aligned";
return v;
}
if (h.entry_fn_id >= h.n_funcs && h.n_funcs != 0) {
v.err_ = "entry fn_id out of range";
return v;
}
v.ok_ = true;
v.err_.clear();
return v;
}
ImageView ImageView::from(const std::vector<uint8_t>& buf) {
return from(buf.data(), buf.size());
}
bool ImageView::ok() const { return ok_; }
const std::string& ImageView::error() const { return err_; }
const ImageHeader& ImageView::header() const { return hdr_; }
const uint8_t* ImageView::raw() const { return buf_; }
size_t ImageView::raw_len() const { return len_; }
ConstEntry ImageView::const_entry(size_t i) const {
ConstEntry e;
e.tag = types::Bool;
e.value = 0;
if (ok_ && i < hdr_.n_consts) {
const uint8_t* p = buf_ + hdr_.offset_const + i * kConstEntrySize;
const uint32_t tag = get_le32(p);
if (tag <= static_cast<uint32_t>(types::Time)) {
e.tag = static_cast<types::TypeTag>(tag);
}
e.value = get_le64(p + 4);
}
return e;
}
FuncRow ImageView::func_row(size_t i) const {
FuncRow r = {0, 0, 0};
if (ok_ && i < hdr_.n_funcs) {
const uint8_t* p = buf_ + hdr_.offset_funcs + 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* ImageView::code_bytes() const {
return ok_ ? buf_ + hdr_.offset_code : 0;
}
size_t ImageView::code_len() const {
return ok_ ? hdr_.offset_fb - hdr_.offset_code : 0;
}
const uint8_t* ImageView::data_bytes() const {
return ok_ ? buf_ + hdr_.offset_data : 0;
}
size_t ImageView::data_len() const {
return ok_ ? len_ - hdr_.offset_data : 0;
}
// ---- .stb 文件读写 ----
bool write_stb_file(const char* path, const std::vector<uint8_t>& image,
std::string* err) {
FILE* f = std::fopen(path, "wb");
if (f == 0) {
if (err) {
*err = std::string("cannot open for write: ") + path;
}
return false;
}
const bool ok = image.empty() || std::fwrite(&image[0], 1, image.size(), f) == image.size();
std::fclose(f);
if (!ok && err) {
*err = std::string("write failed: ") + path;
}
return ok;
}
bool read_stb_file(const char* path, std::vector<uint8_t>* image,
std::string* err) {
FILE* f = std::fopen(path, "rb");
if (f == 0) {
if (err) {
*err = std::string("cannot open for read: ") + path;
}
return false;
}
std::fseek(f, 0, SEEK_END);
const long size = std::ftell(f);
std::fseek(f, 0, SEEK_SET);
if (size < 0) {
std::fclose(f);
if (err) {
*err = "tell failed";
}
return false;
}
image->resize(static_cast<size_t>(size));
const bool ok = size == 0 || std::fread(&(*image)[0], 1, static_cast<size_t>(size), f) == static_cast<size_t>(size);
std::fclose(f);
if (!ok && err) {
*err = std::string("read failed: ") + path;
}
return ok;
}
// ---- sidecar ----
std::string make_sidecar(const std::vector<IoBinding>& bindings) {
std::string out;
for (size_t i = 0; i < bindings.size(); ++i) {
const IoBinding& b = bindings[i];
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) {
FILE* f = std::fopen(path, "wb");
if (f == 0) {
if (err) {
*err = std::string("cannot open for write: ") + 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 = std::string("write failed: ") + path;
}
return ok;
}
} // namespace isa