- vm/Image.cpp:匿名空间 constexpr 偏移常量(kOffMagic..kOffModelId + 段内行偏移), from/model_id/model_matches/func_row/const_entry 全部替换 - compiler/Stb.h:同值 inline constexpr 表(Codegen 写侧 + Stb 读侧共用) - compiler/Codegen.cpp assemble_image:put_* 头字段/行内偏移替换 - compiler/Stb.cpp StbView:读侧偏移替换 - 两侧 static_assert:头大小 = 72 + 型号标识、字段偏移单调、行内布局自洽
243 lines
11 KiB
C++
243 lines
11 KiB
C++
/**
|
||
* @file Stb.h
|
||
* @brief 编译器自带的 .stb 映像规范(写侧)+ 只读视图 + FNV-1a
|
||
* @author
|
||
* @date 2026-08-21
|
||
*
|
||
* @details 执行器(vm)各有自己的读写实现;格式契约见 Doc/isa/指令与映像.md。
|
||
* 12.13 修订:头 104 = 原 72 + 型号标识[32] @72,文件尾 SHA-256[32]。
|
||
* 常量表 tag 契约:0=BOOL、1=INT、2=TIME;工程哈希用 FNV-1a 64。
|
||
*/
|
||
|
||
#pragma once
|
||
|
||
#include <cstddef>
|
||
#include <cstdint>
|
||
#include <string>
|
||
#include <vector>
|
||
|
||
#include "compiler/Project.h"
|
||
|
||
namespace compiler {
|
||
|
||
// ---- .stb 格式常量(契约;12.13 修订:头 104 = 原 72 + 型号标识[32],文件尾 SHA-256[32])----
|
||
/// 映像魔数:"STSC" 小端。
|
||
static const uint32_t kMagic = 0x43545353u;
|
||
/// 映像格式版本。
|
||
static const uint32_t kVersion = 1;
|
||
/// 映像头字节数(原 72 字段 + 型号标识[32] @72)。
|
||
static const size_t kHeaderSize = 104;
|
||
/// 常量表一行字节数(tag:4 + value:8)。
|
||
static const size_t kConstEntrySize = 12;
|
||
/// 函数表一行字节数(nregs / code_offset / code_len 各 4)。
|
||
static const size_t kFuncRowSize = 12;
|
||
/// SHA-256 摘要长度(文件尾)。
|
||
static const size_t kSha256Size = 32;
|
||
/// 型号标识长度(头内 @72,不足补 '\0')。
|
||
static const size_t kModelIdSize = 32;
|
||
/// FNV-1a 64 哈希基值。
|
||
static const uint64_t kFnvBasis = 0xcbf29ce484222325ull;
|
||
/// FNV-1a 64 哈希素数。
|
||
static const uint64_t kFnvPrime = 0x100000001b3ull;
|
||
|
||
// ---- 头字段偏移(与 Doc/isa/指令与映像.md 字段表一致;vm 侧另有同值一份)----
|
||
inline constexpr size_t kOffMagic = 0; ///< 魔数 "STSC"
|
||
inline constexpr size_t kOffVersion = 4; ///< 格式版本
|
||
inline constexpr size_t kOffCycleLimit = 8; ///< 每周期指令数上限
|
||
inline constexpr size_t kOffDtMs = 12; ///< 周期时长(毫秒)
|
||
inline constexpr size_t kOffProjectHash = 16; ///< 工程哈希(8 字节)
|
||
inline constexpr size_t kOffEntryFnId = 24; ///< 入口函数 fn_id
|
||
inline constexpr size_t kOffNGlobals = 28; ///< 全局槽数
|
||
inline constexpr size_t kOffNI = 32; ///< 输入(I)槽数
|
||
inline constexpr size_t kOffNQ = 36; ///< 输出(Q)槽数
|
||
inline constexpr size_t kOffNM = 40; ///< 中间(M)槽数
|
||
inline constexpr size_t kOffNConsts = 44; ///< 常量表条目数
|
||
inline constexpr size_t kOffNFuncs = 48; ///< 函数表行数
|
||
inline constexpr size_t kOffConst = 52; ///< 常量表段偏移
|
||
inline constexpr size_t kOffFuncs = 56; ///< 函数表段偏移
|
||
inline constexpr size_t kOffCode = 60; ///< 字节码段偏移
|
||
inline constexpr size_t kOffFb = 64; ///< FB 表段偏移
|
||
inline constexpr size_t kOffData = 68; ///< 数据段偏移
|
||
inline constexpr size_t kOffModelId = 72; ///< 型号标识[32](12.13 修订)
|
||
|
||
// ---- 段内行偏移 ----
|
||
inline constexpr size_t kConstTagOff = 0; ///< 常量表行内:tag
|
||
inline constexpr size_t kConstValueOff = 4; ///< 常量表行内:value(8 字节)
|
||
inline constexpr size_t kFuncNregsOff = 0; ///< 函数表行内:nregs
|
||
inline constexpr size_t kFuncCodeOff = 4; ///< 函数表行内:code_offset(字节)
|
||
inline constexpr size_t kFuncLenOff = 8; ///< 函数表行内:code_len(指令条数)
|
||
|
||
// 防漂移:头布局自洽性(与 Doc/isa/指令与映像.md 冻结表核对)
|
||
static_assert(kHeaderSize == kOffModelId + kModelIdSize, "header size = 72 + model id[32]");
|
||
static_assert(kOffData > kOffFb && kOffFb > kOffCode && kOffCode > kOffFuncs &&
|
||
kOffFuncs > kOffConst,
|
||
"header field offsets monotonic");
|
||
static_assert(kOffModelId == kOffData + 4, "model id follows data offset field");
|
||
static_assert(kConstEntrySize == kConstValueOff + 8, "const row: tag + value:8");
|
||
static_assert(kFuncRowSize == kFuncLenOff + 4, "func row: nregs/code_offset/code_len");
|
||
|
||
/**
|
||
* @brief 常量表一行。
|
||
* @details tag 契约:0=BOOL、1=INT、2=TIME。
|
||
*/
|
||
struct ConstEntry {
|
||
uint32_t tag = 0; ///< 类型 tag(0=BOOL、1=INT、2=TIME)
|
||
uint64_t value = 0; ///< 常量值
|
||
};
|
||
|
||
/**
|
||
* @brief FNV-1a 64 增量哈希更新(工程哈希,非密码学)。
|
||
* @param h 当前哈希值(首轮传 kFnvBasis)
|
||
* @param data 输入字节
|
||
* @param len 字节数
|
||
* @return 更新后的哈希值
|
||
*/
|
||
uint64_t fnv1a64_update(uint64_t h, const uint8_t* data, size_t len);
|
||
/**
|
||
* @brief FNV-1a 64 一次性哈希(工程哈希)。
|
||
* @param data 输入字节
|
||
* @param len 字节数
|
||
* @return 哈希值
|
||
*/
|
||
uint64_t fnv1a64(const uint8_t* data, size_t len);
|
||
|
||
/**
|
||
* @brief 一次性 SHA-256(文件完整性校验)。
|
||
* @param data 输入字节
|
||
* @param len 字节数
|
||
* @param out 32 字节摘要输出(大端)
|
||
*/
|
||
void sha256(const uint8_t* data, size_t len, uint8_t out[kSha256Size]);
|
||
|
||
/**
|
||
* @brief 型号标识:name + version 拼 32 字节 ASCII(如 "STATOR1")。
|
||
* @param name 型号名
|
||
* @param version 版本号
|
||
* @param out 32 字节输出缓冲
|
||
* @details 不足补 '\0',超长截断。
|
||
*/
|
||
void fill_model_id(const std::string& name, uint32_t version, char out[kModelIdSize]);
|
||
|
||
/**
|
||
* @brief .stb 映像只读视图(--disasm 用)。
|
||
* @details from() 时校验魔数/版本/段边界,之后各访问器只读;
|
||
* 失败时 ok() == false,error() 取原因。
|
||
*/
|
||
class StbView {
|
||
public:
|
||
/// @brief 函数表一行(nregs / code_offset / code_len)。
|
||
struct FuncRow {
|
||
uint32_t nregs = 0; ///< 帧寄存器数(含变量与临时)
|
||
uint32_t code_offset = 0; ///< 字节码段内偏移(4 字节对齐)
|
||
uint32_t code_len = 0; ///< 指令条数
|
||
};
|
||
|
||
/// 从原始字节构造(不拷贝,调用方保证生命周期)。
|
||
static StbView from(const uint8_t* buf, size_t len);
|
||
/// 从 vector 构造(转发 from(buf.data(), buf.size()))。
|
||
static StbView from(const std::vector<uint8_t>& buf);
|
||
|
||
/// 解析是否成功。
|
||
bool ok() const { return ok_; }
|
||
/// 失败原因(成功时为空串)。
|
||
const std::string& error() const { return err_; }
|
||
|
||
/// 周期上限(头 @8)。
|
||
uint32_t cycle_limit() const { return cycle_limit_; }
|
||
/// 扫描周期 dt_ms(头 @12)。
|
||
uint32_t dt_ms() const { return dt_ms_; }
|
||
/// 工程哈希(FNV-1a 64,头 @16)。
|
||
uint64_t project_hash() const { return project_hash_; }
|
||
/// 入口函数 fn_id(头 @24)。
|
||
uint32_t entry_fn_id() const { return entry_fn_id_; }
|
||
/// 全局槽数(头 @28)。
|
||
uint32_t n_globals() const { return n_globals_; }
|
||
/// 常量表行数(头 @44)。
|
||
uint32_t n_consts() const { return n_consts_; }
|
||
/// 函数表行数(头 @48)。
|
||
uint32_t n_funcs() const { return n_funcs_; }
|
||
/// 字节码段偏移(头 @60)。
|
||
uint32_t offset_code() const { return offset_code_; }
|
||
/// 数据段(FB 区)偏移(头 @64;即字节码段终点)。
|
||
uint32_t offset_fb() const { return offset_fb_; }
|
||
/// 数据段偏移(头 @68)。
|
||
uint32_t offset_data() const { return offset_data_; }
|
||
|
||
/// 读常量表一行;越界时返回全 0。
|
||
ConstEntry const_entry(size_t i) const;
|
||
/// 读函数表一行;越界时返回全 0。
|
||
FuncRow func_row(size_t i) const;
|
||
/// 字节码段起点。
|
||
const uint8_t* code_bytes() const;
|
||
/// 字节码段字节数。
|
||
size_t code_len() const;
|
||
/// 数据段起点。
|
||
const uint8_t* data_bytes() const;
|
||
/// 数据段字节数(不含文件尾 SHA-256)。
|
||
size_t data_len() const;
|
||
|
||
/// 型号标识字符串(头 72..103,截断到首个 '\0')。
|
||
std::string model_id() const; // 头 72..103(补 '\0' 后字符串)
|
||
/// 型号标识是否匹配 name + version。
|
||
bool model_matches(const std::string& name, uint32_t version) const;
|
||
/// 文件尾 32 字节 SHA-256 校验(对文件尾之前全部内容重算)。
|
||
bool sha_ok() const; // 文件尾 32 字节 SHA-256 校验
|
||
|
||
private:
|
||
/// 私有构造(只能经 from() 创建)。
|
||
StbView() : buf_(0), len_(0) {}
|
||
|
||
/// 常量表偏移(from 已校验的段起点)。
|
||
uint32_t offs_of_const() const; // offset_const(from 已校验段起点)
|
||
|
||
const uint8_t* buf_; ///< 映像缓冲(不拥有)
|
||
size_t len_; ///< 缓冲字节数
|
||
bool ok_ = false; ///< 解析成功标志
|
||
std::string err_; ///< 失败原因
|
||
uint32_t cycle_limit_ = 0; ///< 周期上限
|
||
uint32_t dt_ms_ = 0; ///< 扫描周期
|
||
uint64_t project_hash_ = 0; ///< 工程哈希
|
||
uint32_t entry_fn_id_ = 0; ///< 入口函数 fn_id
|
||
uint32_t n_globals_ = 0; ///< 全局槽数
|
||
uint32_t n_consts_ = 0; ///< 常量表行数
|
||
uint32_t n_funcs_ = 0; ///< 函数表行数
|
||
uint32_t offset_code_ = 0; ///< 字节码段偏移
|
||
uint32_t offset_fb_ = 0; ///< 数据段(FB 区)偏移
|
||
uint32_t offset_data_ = 0; ///< 数据段偏移
|
||
};
|
||
|
||
/**
|
||
* @brief 读整个文件为字节(纯字节;校验交给 StbView)。
|
||
* @param path 文件路径
|
||
* @param out 输出字节
|
||
* @param err 错误输出;可为 nullptr(静默)
|
||
* @return true 成功;false(err "cannot open for read: <path>")
|
||
*/
|
||
bool read_stb_file(const char* path, std::vector<uint8_t>* out, std::string* err);
|
||
/**
|
||
* @brief 写字节为文件(纯字节)。
|
||
* @param path 文件路径
|
||
* @param img 映像字节
|
||
* @param err 错误输出;可为 nullptr(静默)
|
||
* @return true 成功;false(err "cannot open for write: <path>" / "write failed: <path>")
|
||
*/
|
||
bool write_stb_file(const char* path, const std::vector<uint8_t>& img, std::string* err);
|
||
|
||
/**
|
||
* @brief 生成 sidecar 文本(TOML)。
|
||
* @param bindings I/O 绑定列表
|
||
* @return TOML 文本(每个绑定一节 [[io.input]] / [[io.output]])
|
||
* @details I/O 绑定 var → 槽号 → channel/bit(不进映像,执行器采样用)。
|
||
*/
|
||
std::string make_sidecar(const std::vector<IoBinding>& bindings);
|
||
/**
|
||
* @brief 生成并写 sidecar 文件。
|
||
* @param path 文件路径
|
||
* @param bindings I/O 绑定列表
|
||
* @param err 错误输出;可为 nullptr(静默)
|
||
* @return true 成功;false(err "cannot open for write: <path>" / "write failed: <path>")
|
||
*/
|
||
bool write_sidecar_file(const char* path, const std::vector<IoBinding>& bindings,
|
||
std::string* err);
|
||
}
|