Files
Interpreter/compiler/include/compiler/Stb.h
T
Admin 7aa2bbefcf stb 头字段偏移常量表:消除 vm/compiler 两侧裸魔法数
- 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 + 型号标识、字段偏移单调、行内布局自洽
2026-08-24 08:55:57 +08:00

243 lines
11 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @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; ///< 常量表行内:value8 字节)
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; ///< 类型 tag0=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() == falseerror() 取原因。
*/
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_constfrom 已校验段起点)
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 成功;falseerr "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 成功;falseerr "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 成功;falseerr "cannot open for write: <path>" / "write failed: <path>"
*/
bool write_sidecar_file(const char* path, const std::vector<IoBinding>& bindings,
std::string* err);
}