阶段 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:
+13
-4
@@ -21,7 +21,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "isa/Encode.h"
|
||||
#include "isa/Image.h"
|
||||
#include "vm/Image.h"
|
||||
#include "vm/Machine.h"
|
||||
|
||||
namespace {
|
||||
@@ -199,8 +199,17 @@ int main(int argc, char** argv) {
|
||||
|
||||
std::string err;
|
||||
std::vector<uint8_t> bytes;
|
||||
if (!isa::read_stb_file(stb_path.c_str(), &bytes, &err)) {
|
||||
std::fprintf(stderr, "error: %s\n", err.c_str());
|
||||
{
|
||||
std::ifstream in(stb_path, std::ios::binary);
|
||||
if (!in) {
|
||||
std::fprintf(stderr, "error: cannot open: %s\n", stb_path.c_str());
|
||||
return 1;
|
||||
}
|
||||
bytes.assign(std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>());
|
||||
}
|
||||
const vm::Image img = vm::Image::from(bytes);
|
||||
if (!img.ok()) {
|
||||
std::fprintf(stderr, "error: %s\n", img.error().c_str());
|
||||
return 1;
|
||||
}
|
||||
vm::Machine machine;
|
||||
@@ -233,7 +242,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
const isa::ImageHeader& h = machine.header();
|
||||
const vm::ImageHeader& h = machine.header();
|
||||
std::printf("image: %s (%zu bytes, dt_ms=%u, cycle_limit=%u)\n", stb_path.c_str(),
|
||||
bytes.size(), h.dt_ms, h.cycle_limit);
|
||||
|
||||
|
||||
+1
-2
@@ -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)
|
||||
@@ -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 64:basis / 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() == false,error() == "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_;
|
||||
};
|
||||
|
||||
// 写最小映像:空槽 + MAIN(fn_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);
|
||||
}
|
||||
@@ -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
|
||||
+67
-67
@@ -16,7 +16,7 @@
|
||||
#include "compiler/Project.h"
|
||||
#include "compiler/Typecheck.h"
|
||||
#include "isa/Encode.h"
|
||||
#include "isa/Image.h"
|
||||
#include "compiler/Stb.h"
|
||||
|
||||
#ifndef REPO_ROOT
|
||||
#define REPO_ROOT "."
|
||||
@@ -123,17 +123,17 @@ static bool test_case01() {
|
||||
std::string err;
|
||||
CHECK(compile_case("01_empty_main", &img, &err));
|
||||
|
||||
const isa::ImageView v = isa::ImageView::from(img);
|
||||
const compiler::StbView v = compiler::StbView::from(img);
|
||||
CHECK(v.ok());
|
||||
CHECK(v.header().cycle_limit == 1000);
|
||||
CHECK(v.header().dt_ms == 10);
|
||||
CHECK(v.header().entry_fn_id == 0);
|
||||
CHECK(v.header().n_funcs == 1);
|
||||
CHECK(v.header().n_consts == 0);
|
||||
CHECK(v.header().n_globals == 0);
|
||||
CHECK(v.header().project_hash != isa::kFnvBasis);
|
||||
CHECK(v.cycle_limit() == 1000);
|
||||
CHECK(v.dt_ms() == 10);
|
||||
CHECK(v.entry_fn_id() == 0);
|
||||
CHECK(v.n_funcs() == 1);
|
||||
CHECK(v.n_consts() == 0);
|
||||
CHECK(v.n_globals() == 0);
|
||||
CHECK(v.project_hash() != compiler::kFnvBasis);
|
||||
|
||||
const isa::FuncRow r = v.func_row(0);
|
||||
const compiler::StbView::FuncRow r = v.func_row(0);
|
||||
CHECK(r.nregs == 8); // 帧基址(调用约定区)
|
||||
CHECK(r.code_len == 1);
|
||||
|
||||
@@ -150,12 +150,12 @@ static bool test_case02() {
|
||||
std::string err;
|
||||
CHECK(compile_case("02_bool_assign", &img, &err));
|
||||
|
||||
const isa::ImageView v = isa::ImageView::from(img);
|
||||
const compiler::StbView v = compiler::StbView::from(img);
|
||||
CHECK(v.ok());
|
||||
CHECK(v.header().n_funcs == 1);
|
||||
CHECK(v.header().n_consts == 2); // TRUE 与 FALSE
|
||||
CHECK(v.n_funcs() == 1);
|
||||
CHECK(v.n_consts() == 2); // TRUE 与 FALSE
|
||||
|
||||
const isa::FuncRow r = v.func_row(0);
|
||||
const compiler::StbView::FuncRow r = v.func_row(0);
|
||||
CHECK(r.nregs == 10); // a, b(r8 起)
|
||||
CHECK(r.code_len == 3);
|
||||
|
||||
@@ -170,10 +170,10 @@ static bool test_case02() {
|
||||
CHECK(std::strcmp(buf, "RET") == 0);
|
||||
|
||||
// 常量表:0 = BOOL 1,1 = BOOL 0
|
||||
const isa::ConstEntry c0 = v.const_entry(0);
|
||||
CHECK(c0.tag == isa::types::Bool && c0.value == 1);
|
||||
const isa::ConstEntry c1 = v.const_entry(1);
|
||||
CHECK(c1.tag == isa::types::Bool && c1.value == 0);
|
||||
const compiler::ConstEntry c0 = v.const_entry(0);
|
||||
CHECK(c0.tag == 0 && c0.value == 1);
|
||||
const compiler::ConstEntry c1 = v.const_entry(1);
|
||||
CHECK(c1.tag == 0 && c1.value == 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -184,14 +184,14 @@ static bool test_case09() {
|
||||
std::string err;
|
||||
CHECK(compile_case("09_gvl_external", &img, &err));
|
||||
|
||||
const isa::ImageView v = isa::ImageView::from(img);
|
||||
const compiler::StbView v = compiler::StbView::from(img);
|
||||
CHECK(v.ok());
|
||||
CHECK(v.header().n_globals == 1);
|
||||
CHECK(v.data_len() == 8 + 32); // 8 字节定宽槽 + SHA-256 尾
|
||||
CHECK(v.n_globals() == 1);
|
||||
CHECK(v.data_len() == 8); // 8 字节定宽槽(不含 SHA 尾)
|
||||
CHECK(v.data_bytes()[0] == 5); // G1 初值 5(小端低位)
|
||||
CHECK(v.data_bytes()[1] == 0);
|
||||
|
||||
const isa::FuncRow r = v.func_row(0);
|
||||
const compiler::StbView::FuncRow r = v.func_row(0);
|
||||
CHECK(r.nregs == 9); // r8 起:仅局部 x
|
||||
CHECK(r.code_len == 2);
|
||||
|
||||
@@ -221,11 +221,11 @@ static bool test_io_ops() {
|
||||
"END_PROGRAM\n";
|
||||
CHECK(compile_src("io", io_extra, globals_st, main_st, &img, &err));
|
||||
|
||||
const isa::ImageView v = isa::ImageView::from(img);
|
||||
const compiler::StbView v = compiler::StbView::from(img);
|
||||
CHECK(v.ok());
|
||||
CHECK(v.header().n_globals == 2);
|
||||
CHECK(v.n_globals() == 2);
|
||||
|
||||
const isa::FuncRow r = v.func_row(0);
|
||||
const compiler::StbView::FuncRow r = v.func_row(0);
|
||||
CHECK(r.nregs == 10); // r8 起:q + 1 临时
|
||||
|
||||
char buf[64];
|
||||
@@ -258,10 +258,10 @@ static bool test_layout() {
|
||||
"END_PROGRAM\n";
|
||||
CHECK(compile_src("layout", "", globals_st, main_st, &img, &err));
|
||||
|
||||
const isa::ImageView v = isa::ImageView::from(img);
|
||||
const compiler::StbView v = compiler::StbView::from(img);
|
||||
CHECK(v.ok());
|
||||
CHECK(v.header().n_globals == 3);
|
||||
CHECK(v.data_len() == 24 + 32); // 3 槽 × 8 字节定宽 + SHA-256 尾
|
||||
CHECK(v.n_globals() == 3);
|
||||
CHECK(v.data_len() == 24); // 3 槽 × 8 字节定宽
|
||||
CHECK(v.data_bytes()[0] == 0); // B 无初值
|
||||
CHECK(v.data_bytes()[8] == 0x2C && v.data_bytes()[9] == 0x01); // I = 300(槽 1)
|
||||
CHECK(v.data_bytes()[16] == 10); // T = 10ms(槽 2)
|
||||
@@ -299,9 +299,9 @@ static bool test_case03() {
|
||||
std::string err;
|
||||
CHECK(compile_case("03_short_circuit", &img, &err));
|
||||
|
||||
const isa::ImageView v = isa::ImageView::from(img);
|
||||
const compiler::StbView v = compiler::StbView::from(img);
|
||||
CHECK(v.ok());
|
||||
const isa::FuncRow r = v.func_row(0);
|
||||
const compiler::StbView::FuncRow r = v.func_row(0);
|
||||
CHECK(r.nregs == 12); // r8 起:a b x + 1 临时
|
||||
CHECK(r.code_len == 9);
|
||||
|
||||
@@ -342,9 +342,9 @@ static bool test_not() {
|
||||
"END_PROGRAM\n";
|
||||
CHECK(compile_src("not", "", "", main_st, &img, &err));
|
||||
|
||||
const isa::ImageView v = isa::ImageView::from(img);
|
||||
const compiler::StbView v = compiler::StbView::from(img);
|
||||
CHECK(v.ok());
|
||||
const isa::FuncRow r = v.func_row(0);
|
||||
const compiler::StbView::FuncRow r = v.func_row(0);
|
||||
CHECK(r.nregs == 11); // r8 起:a x + 1 临时
|
||||
CHECK(r.code_len == 3);
|
||||
|
||||
@@ -367,9 +367,9 @@ static bool test_case04() {
|
||||
std::string err;
|
||||
CHECK(compile_case("04_if_elsif_else", &img, &err));
|
||||
|
||||
const isa::ImageView v = isa::ImageView::from(img);
|
||||
const compiler::StbView v = compiler::StbView::from(img);
|
||||
CHECK(v.ok());
|
||||
const isa::FuncRow r = v.func_row(0);
|
||||
const compiler::StbView::FuncRow r = v.func_row(0);
|
||||
CHECK(r.nregs == 13); // r8 起:sel out + 3 临时
|
||||
CHECK(r.code_len == 14);
|
||||
|
||||
@@ -417,9 +417,9 @@ static bool test_case05() {
|
||||
std::string err;
|
||||
CHECK(compile_case("05_while_normal", &img, &err));
|
||||
|
||||
const isa::ImageView v = isa::ImageView::from(img);
|
||||
const compiler::StbView v = compiler::StbView::from(img);
|
||||
CHECK(v.ok());
|
||||
const isa::FuncRow r = v.func_row(0);
|
||||
const compiler::StbView::FuncRow r = v.func_row(0);
|
||||
CHECK(r.nregs == 12); // r8 起:n + 3 临时(条件结果 r9 + 两操作数)
|
||||
CHECK(r.code_len == 10);
|
||||
|
||||
@@ -458,9 +458,9 @@ static bool test_case07() {
|
||||
std::string err;
|
||||
CHECK(compile_case("07_int_arith", &img, &err));
|
||||
|
||||
const isa::ImageView v = isa::ImageView::from(img);
|
||||
const compiler::StbView v = compiler::StbView::from(img);
|
||||
CHECK(v.ok());
|
||||
const isa::FuncRow r = v.func_row(0);
|
||||
const compiler::StbView::FuncRow r = v.func_row(0);
|
||||
CHECK(r.nregs == 14); // r8 起:a b c eq + 2 临时
|
||||
|
||||
char buf[64];
|
||||
@@ -485,15 +485,15 @@ static bool test_case08() {
|
||||
std::string err;
|
||||
CHECK(compile_case("08_time_literal", &img, &err));
|
||||
|
||||
const isa::ImageView v = isa::ImageView::from(img);
|
||||
const compiler::StbView v = compiler::StbView::from(img);
|
||||
CHECK(v.ok());
|
||||
CHECK(v.header().n_consts == 2);
|
||||
const isa::ConstEntry c0 = v.const_entry(0);
|
||||
CHECK(c0.tag == isa::types::Time && c0.value == 10); // T#10ms
|
||||
const isa::ConstEntry c1 = v.const_entry(1);
|
||||
CHECK(c1.tag == isa::types::Time && c1.value == 1250); // T#1s250ms
|
||||
CHECK(v.n_consts() == 2);
|
||||
const compiler::ConstEntry c0 = v.const_entry(0);
|
||||
CHECK(c0.tag == 2 && c0.value == 10); // T#10ms
|
||||
const compiler::ConstEntry c1 = v.const_entry(1);
|
||||
CHECK(c1.tag == 2 && c1.value == 1250); // T#1s250ms
|
||||
|
||||
const isa::FuncRow r = v.func_row(0);
|
||||
const compiler::StbView::FuncRow r = v.func_row(0);
|
||||
CHECK(r.nregs == 10); // r8 起
|
||||
char buf[64];
|
||||
const uint32_t* ins = reinterpret_cast<const uint32_t*>(v.code_bytes());
|
||||
@@ -511,15 +511,15 @@ static bool test_case13() {
|
||||
std::string err;
|
||||
CHECK(compile_case("13_function_call", &img, &err));
|
||||
|
||||
const isa::ImageView v = isa::ImageView::from(img);
|
||||
const compiler::StbView v = compiler::StbView::from(img);
|
||||
CHECK(v.ok());
|
||||
CHECK(v.header().n_funcs == 2);
|
||||
CHECK(v.header().entry_fn_id == 1); // MAIN 是第二个 POU
|
||||
CHECK(v.n_funcs() == 2);
|
||||
CHECK(v.entry_fn_id() == 1); // MAIN 是第二个 POU
|
||||
|
||||
char buf[64];
|
||||
const uint8_t* base = v.code_bytes();
|
||||
// Add(fn_id 0):结果 r0、输入 r1(a) r2(b)、临时 r8/r9
|
||||
const isa::FuncRow fa = v.func_row(0);
|
||||
const compiler::StbView::FuncRow fa = v.func_row(0);
|
||||
CHECK(fa.nregs == 10);
|
||||
CHECK(fa.code_len == 4);
|
||||
const uint32_t* ia = reinterpret_cast<const uint32_t*>(base);
|
||||
@@ -533,7 +533,7 @@ static bool test_case13() {
|
||||
CHECK(std::strcmp(buf, "RET") == 0);
|
||||
|
||||
// MAIN(fn_id 1):x 在 r8,实参临时 r9/r10
|
||||
const isa::FuncRow fm = v.func_row(1);
|
||||
const compiler::StbView::FuncRow fm = v.func_row(1);
|
||||
CHECK(fm.nregs == 11);
|
||||
CHECK(fm.code_len == 7);
|
||||
const uint32_t* im = reinterpret_cast<const uint32_t*>(base) + fm.code_offset / 4;
|
||||
@@ -561,13 +561,13 @@ static bool test_case15() {
|
||||
std::string err;
|
||||
CHECK(compile_case("15_fb_instance", &img, &err));
|
||||
|
||||
const isa::ImageView v = isa::ImageView::from(img);
|
||||
const compiler::StbView v = compiler::StbView::from(img);
|
||||
CHECK(v.ok());
|
||||
CHECK(v.header().n_funcs == 2); // FB 占位 + MAIN
|
||||
CHECK(v.header().entry_fn_id == 1);
|
||||
CHECK(v.data_len() == 24 + 32); // 实例 3 槽 × 8 字节定宽 + SHA-256 尾
|
||||
CHECK(v.n_funcs() == 2); // FB 占位 + MAIN
|
||||
CHECK(v.entry_fn_id() == 1);
|
||||
CHECK(v.data_len() == 24); // 实例 3 槽 × 8 字节定宽
|
||||
|
||||
const isa::FuncRow fm = v.func_row(1);
|
||||
const compiler::StbView::FuncRow fm = v.func_row(1);
|
||||
CHECK(fm.nregs == 12);
|
||||
CHECK(fm.code_len == 12);
|
||||
const uint32_t* im = reinterpret_cast<const uint32_t*>(v.code_bytes()) + fm.code_offset / 4;
|
||||
@@ -601,10 +601,10 @@ static bool test_case17() {
|
||||
std::string err;
|
||||
CHECK(compile_case("17_ton", &img, &err));
|
||||
|
||||
const isa::ImageView v = isa::ImageView::from(img);
|
||||
const compiler::StbView v = compiler::StbView::from(img);
|
||||
CHECK(v.ok());
|
||||
CHECK(v.data_len() == 32 + 32); // in@0 pt@8 q@16 et@24 + SHA-256 尾
|
||||
const isa::FuncRow r = v.func_row(0);
|
||||
CHECK(v.data_len() == 32); // in@0 pt@8 q@16 et@24
|
||||
const compiler::StbView::FuncRow r = v.func_row(0);
|
||||
CHECK(r.nregs == 11);
|
||||
const uint32_t* ins = reinterpret_cast<const uint32_t*>(v.code_bytes());
|
||||
char buf[64];
|
||||
@@ -630,10 +630,10 @@ static bool test_case18() {
|
||||
std::string err;
|
||||
CHECK(compile_case("18_tof_ctu", &img, &err));
|
||||
|
||||
const isa::ImageView v = isa::ImageView::from(img);
|
||||
const compiler::StbView v = compiler::StbView::from(img);
|
||||
CHECK(v.ok());
|
||||
CHECK(v.data_len() == 72 + 32); // tf 4 槽 + c 5 槽 = 9 槽 × 8 + SHA-256 尾
|
||||
const isa::FuncRow r = v.func_row(0);
|
||||
CHECK(v.data_len() == 72); // tf 4 槽 + c 5 槽 = 9 槽 × 8
|
||||
const compiler::StbView::FuncRow r = v.func_row(0);
|
||||
CHECK(r.nregs == 14);
|
||||
const uint32_t* ins = reinterpret_cast<const uint32_t*>(v.code_bytes());
|
||||
char buf[64];
|
||||
@@ -670,14 +670,14 @@ static bool test_line1() {
|
||||
CHECK(load_cfg(&cfg));
|
||||
CHECK(codegen_project(p, units, link, cfg, &img, &err));
|
||||
|
||||
const isa::ImageView v = isa::ImageView::from(img);
|
||||
const compiler::StbView v = compiler::StbView::from(img);
|
||||
CHECK(v.ok());
|
||||
CHECK(v.header().n_globals == 4);
|
||||
CHECK(v.header().n_funcs == 2);
|
||||
CHECK(v.header().entry_fn_id == 1);
|
||||
CHECK(v.data_len() == 56 + 32); // 7 槽 × 8 字节定宽 + SHA-256 尾
|
||||
CHECK(v.n_globals() == 4);
|
||||
CHECK(v.n_funcs() == 2);
|
||||
CHECK(v.entry_fn_id() == 1);
|
||||
CHECK(v.data_len() == 56); // 7 槽 × 8 字节定宽
|
||||
|
||||
const isa::FuncRow fm = v.func_row(1);
|
||||
const compiler::StbView::FuncRow fm = v.func_row(1);
|
||||
CHECK(fm.nregs == 13);
|
||||
CHECK(fm.code_len == 17);
|
||||
const uint32_t* im = reinterpret_cast<const uint32_t*>(v.code_bytes()) + fm.code_offset / 4;
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <vector>
|
||||
|
||||
#include "isa/Encode.h"
|
||||
#include "isa/Image.h"
|
||||
#include "isa/Instr.h"
|
||||
#include "isa/Op.h"
|
||||
#include "isa/Types.h"
|
||||
@@ -127,114 +126,11 @@ static bool test_encode() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---- 5. FNV-1a 64 ----
|
||||
|
||||
static bool test_fnv() {
|
||||
using namespace isa;
|
||||
CHECK(fnv1a64(0, 0) == kFnvBasis);
|
||||
const uint8_t a1[] = {'a'};
|
||||
CHECK(fnv1a64(a1, 1) == 0xaf63dc4c8601ec8cull);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---- 6. 映像:write_ret_main → ImageView ----
|
||||
|
||||
static bool test_image() {
|
||||
using namespace isa;
|
||||
const uint64_t hash = 0x123456789abcdef0ull;
|
||||
std::vector<uint8_t> img = write_ret_main(100000, 10, hash);
|
||||
|
||||
ImageView v = ImageView::from(img);
|
||||
CHECK(v.ok());
|
||||
CHECK(v.error().empty());
|
||||
CHECK(v.header().cycle_limit == 100000);
|
||||
CHECK(v.header().dt_ms == 10);
|
||||
CHECK(v.header().project_hash == hash);
|
||||
CHECK(v.header().entry_fn_id == 0);
|
||||
CHECK(v.header().n_funcs == 1);
|
||||
CHECK(v.header().n_globals == 0);
|
||||
CHECK(v.header().n_i == 0);
|
||||
CHECK(v.header().n_q == 0);
|
||||
CHECK(v.header().n_m == 0);
|
||||
CHECK(v.header().offset_const == kHeaderSize);
|
||||
CHECK(v.header().offset_data == kHeaderSize + kFuncRowSize + 4);
|
||||
CHECK(v.code_len() == 4);
|
||||
CHECK(v.data_len() == 0);
|
||||
|
||||
const FuncRow r = v.func_row(0);
|
||||
CHECK(r.nregs == 0);
|
||||
CHECK(r.code_offset == 0);
|
||||
CHECK(r.code_len == 1);
|
||||
|
||||
char buf[32];
|
||||
disasm(v.code_bytes()[0], buf, sizeof buf);
|
||||
CHECK(std::strcmp(buf, "RET") == 0);
|
||||
|
||||
// 损坏映像必须拒绝
|
||||
std::vector<uint8_t> bad = img;
|
||||
bad[0] = 'X';
|
||||
CHECK(!ImageView::from(bad).ok());
|
||||
std::vector<uint8_t> bad2 = img;
|
||||
bad2[60] = 255; // offset_code 越界
|
||||
CHECK(!ImageView::from(bad2).ok());
|
||||
std::vector<uint8_t> bad3 = img;
|
||||
bad3[48] = 2; // n_funcs=2 但表只有 1 行
|
||||
CHECK(!ImageView::from(bad3).ok());
|
||||
std::vector<uint8_t> bad4 = img;
|
||||
bad4[24] = 1; // entry_fn_id 越界
|
||||
CHECK(!ImageView::from(bad4).ok());
|
||||
std::vector<uint8_t> short_img = img;
|
||||
short_img.resize(kHeaderSize - 1);
|
||||
CHECK(!ImageView::from(short_img).ok());
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---- 7. .stb 文件与 sidecar ----
|
||||
|
||||
static bool test_files() {
|
||||
using namespace isa;
|
||||
std::vector<uint8_t> img = write_ret_main(100000, 10, 0);
|
||||
|
||||
const char* stb_path = "isa_test_tmp.stb";
|
||||
std::string err;
|
||||
CHECK(write_stb_file(stb_path, img, &err));
|
||||
std::vector<uint8_t> back;
|
||||
CHECK(read_stb_file(stb_path, &back, &err));
|
||||
CHECK(back == img);
|
||||
CHECK(ImageView::from(back).ok());
|
||||
std::remove(stb_path);
|
||||
|
||||
std::vector<IoBinding> bs;
|
||||
IoBinding b1;
|
||||
b1.var = "I0_0"; b1.slot = 1; b1.channel = 0; b1.bit = 0; b1.is_input = true;
|
||||
IoBinding b2;
|
||||
b2.var = "Q0_0"; b2.slot = 3; b2.channel = 0; b2.bit = 0; b2.is_input = false;
|
||||
bs.push_back(b1);
|
||||
bs.push_back(b2);
|
||||
|
||||
const std::string sc = make_sidecar(bs);
|
||||
CHECK(sc.find("[[io.input]]") == 0);
|
||||
CHECK(sc.find("var = \"I0_0\"") != std::string::npos);
|
||||
CHECK(sc.find("slot = 1") != std::string::npos);
|
||||
CHECK(sc.find("channel = 0") != std::string::npos);
|
||||
CHECK(sc.find("[[io.output]]") != std::string::npos);
|
||||
CHECK(sc.find("var = \"Q0_0\"") != std::string::npos);
|
||||
CHECK(sc.find("slot = 3") != std::string::npos);
|
||||
|
||||
const char* sc_path = "isa_test_tmp.runtime.toml";
|
||||
CHECK(write_sidecar_file(sc_path, bs, &err));
|
||||
std::remove(sc_path);
|
||||
return true;
|
||||
}
|
||||
|
||||
int main() {
|
||||
if (!test_sat()) return 1;
|
||||
if (!test_op()) return 1;
|
||||
if (!test_instr()) return 1;
|
||||
if (!test_encode()) return 1;
|
||||
if (!test_fnv()) return 1;
|
||||
if (!test_image()) return 1;
|
||||
if (!test_files()) return 1;
|
||||
std::printf("isa_test: %d checks passed\n", g_checks);
|
||||
return 0;
|
||||
}
|
||||
|
||||
+12
-13
@@ -15,7 +15,6 @@
|
||||
#include "compiler/Project.h"
|
||||
#include "compiler/Typecheck.h"
|
||||
#include "isa/Encode.h"
|
||||
#include "isa/Image.h"
|
||||
#include "isa/Instr.h"
|
||||
#include "isa/Op.h"
|
||||
#include "vm/Machine.h"
|
||||
@@ -93,7 +92,7 @@ struct HFunc {
|
||||
uint32_t nregs = 8;
|
||||
};
|
||||
|
||||
static std::vector<uint8_t> hand_image(const std::vector<isa::ConstEntry>& consts,
|
||||
static std::vector<uint8_t> hand_image(const std::vector<vm::ConstEntry>& consts,
|
||||
const std::vector<HFunc>& funcs,
|
||||
uint32_t entry = 0,
|
||||
uint32_t cycle_limit = 100000,
|
||||
@@ -102,11 +101,11 @@ static std::vector<uint8_t> hand_image(const std::vector<isa::ConstEntry>& const
|
||||
for (const HFunc& f : funcs) {
|
||||
code_total += f.code.size() * 4;
|
||||
}
|
||||
const uint32_t off_const = 72;
|
||||
const uint32_t off_const = 104;
|
||||
const uint32_t off_funcs = off_const + static_cast<uint32_t>(consts.size()) * 12;
|
||||
const uint32_t off_code = off_funcs + static_cast<uint32_t>(funcs.size()) * 12;
|
||||
const uint32_t off_data = off_code + static_cast<uint32_t>(code_total);
|
||||
const uint32_t off_end = off_data + static_cast<uint32_t>(data.size());
|
||||
const uint32_t off_end = off_data + static_cast<uint32_t>(data.size()) + 32; // SHA 尾占位
|
||||
|
||||
std::vector<uint8_t> b(off_end, 0);
|
||||
auto put32 = [&](size_t o, uint32_t v) {
|
||||
@@ -120,8 +119,8 @@ static std::vector<uint8_t> hand_image(const std::vector<isa::ConstEntry>& const
|
||||
b[o + i] = static_cast<uint8_t>((v >> (8 * i)) & 0xFFu);
|
||||
}
|
||||
};
|
||||
put32(0, isa::kMagic);
|
||||
put32(4, isa::kVersion);
|
||||
put32(0, 0x43545353u);
|
||||
put32(4, 1u);
|
||||
put32(8, cycle_limit);
|
||||
put32(12, 10); // dt_ms
|
||||
put32(24, entry);
|
||||
@@ -163,10 +162,10 @@ static std::vector<uint8_t> hand_image(const std::vector<isa::ConstEntry>& const
|
||||
|
||||
static bool test_hand_scalar() {
|
||||
// r8 := 5;r9 := r8 + 3;槽 0 ← r9;槽 1 → r10;JT 跳过一条
|
||||
const std::vector<isa::ConstEntry> consts = {
|
||||
{isa::types::Int, 5},
|
||||
{isa::types::Int, 3},
|
||||
{isa::types::Int, 1},
|
||||
const std::vector<vm::ConstEntry> consts = {
|
||||
{1, 5},
|
||||
{1, 3},
|
||||
{1, 1},
|
||||
};
|
||||
const std::vector<HFunc> funcs = {{
|
||||
{
|
||||
@@ -201,9 +200,9 @@ static bool test_hand_scalar() {
|
||||
static bool test_hand_call() {
|
||||
// fn0(入口):r8=5、r2=7、实参 r1←r8;CALL 1;结果 r0 → r8
|
||||
// fn1:r0 = r1 + r2(r1/r2 来自调用约定区复制)
|
||||
const std::vector<isa::ConstEntry> consts = {
|
||||
{isa::types::Int, 5},
|
||||
{isa::types::Int, 7},
|
||||
const std::vector<vm::ConstEntry> consts = {
|
||||
{1, 5},
|
||||
{1, 7},
|
||||
};
|
||||
const std::vector<HFunc> funcs = {
|
||||
{{
|
||||
|
||||
+2
-1
@@ -7,7 +7,8 @@ project(VM
|
||||
DESCRIPTION "寄存器虚拟机")
|
||||
|
||||
add_library(vm STATIC
|
||||
./src/Machine.cpp)
|
||||
./src/Machine.cpp
|
||||
./src/Image.cpp)
|
||||
|
||||
target_include_directories(vm PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
target_link_libraries(vm PUBLIC isa)
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* @file Image.h
|
||||
* @brief vm 自带的 .stb 只读视图(执行器侧;compiler 各有实现)
|
||||
* @author
|
||||
* @date 2026-08-21
|
||||
*
|
||||
* @details 格式契约见 Doc/isa/指令与映像.md(12.13 修订:头 104 = 72 + 型号标识[32],
|
||||
* 文件尾 SHA-256[32])。步骤 7:解析头与段;步骤 8:补型号匹配与 SHA-256 校验。
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace vm {
|
||||
|
||||
// 映像头(字段与 Doc/isa/指令与映像.md 一致)
|
||||
struct ImageHeader {
|
||||
uint32_t cycle_limit = 0;
|
||||
uint32_t dt_ms = 0;
|
||||
uint64_t project_hash = 0;
|
||||
uint32_t entry_fn_id = 0;
|
||||
uint32_t n_globals = 0;
|
||||
uint32_t n_i = 0;
|
||||
uint32_t n_q = 0;
|
||||
uint32_t n_m = 0;
|
||||
uint32_t n_consts = 0;
|
||||
uint32_t n_funcs = 0;
|
||||
uint32_t offset_const = 0;
|
||||
uint32_t offset_funcs = 0;
|
||||
uint32_t offset_code = 0;
|
||||
uint32_t offset_fb = 0;
|
||||
uint32_t offset_data = 0;
|
||||
};
|
||||
|
||||
struct FuncRow {
|
||||
uint32_t nregs = 0;
|
||||
uint32_t code_offset = 0; // 相对字节码段起点(字节)
|
||||
uint32_t code_len = 0; // 指令条数
|
||||
};
|
||||
|
||||
struct ConstEntry {
|
||||
uint32_t tag = 0;
|
||||
uint64_t value = 0;
|
||||
};
|
||||
|
||||
// 只读视图:校验过的映像(指向外部缓冲)
|
||||
class Image {
|
||||
public:
|
||||
static Image from(const uint8_t* buf, size_t len);
|
||||
static Image from(const std::vector<uint8_t>& buf);
|
||||
|
||||
bool ok() const { return ok_; }
|
||||
const std::string& error() const { return err_; }
|
||||
const ImageHeader& header() const { return hdr_; }
|
||||
|
||||
FuncRow func_row(size_t i) const;
|
||||
ConstEntry const_entry(size_t i) const;
|
||||
|
||||
const uint8_t* code_bytes() const; // 字节码段起点
|
||||
const uint8_t* data_bytes() const; // 数据段起点
|
||||
size_t data_len() const; // 字节数
|
||||
|
||||
public:
|
||||
// 默认构造为无效态(ok() == false)
|
||||
Image() : buf_(nullptr), len_(0) {}
|
||||
|
||||
private:
|
||||
const uint8_t* buf_;
|
||||
size_t len_;
|
||||
bool ok_ = false;
|
||||
std::string err_;
|
||||
ImageHeader hdr_;
|
||||
};
|
||||
}
|
||||
@@ -19,7 +19,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "isa/Image.h"
|
||||
#include "vm/Image.h"
|
||||
#include "isa/Instr.h"
|
||||
|
||||
namespace vm {
|
||||
@@ -67,7 +67,7 @@ namespace vm {
|
||||
bool ended() const;
|
||||
|
||||
/** @brief 映像头(dt_ms / cycle_limit 等) */
|
||||
const isa::ImageHeader& header() const;
|
||||
const vm::ImageHeader& header() const;
|
||||
|
||||
/** @brief 当前指令下标(相对当前函数字节码段) */
|
||||
uint32_t pc() const;
|
||||
@@ -105,7 +105,7 @@ namespace vm {
|
||||
uint32_t ret_fn_id = 0; // 返回目标函数
|
||||
};
|
||||
|
||||
isa::ImageView image_; // 映像只读视图
|
||||
vm::Image image_; // 映像只读视图(vm 自实现)
|
||||
std::vector<uint8_t> data_; // 数据区工作副本(8 字节定宽槽)
|
||||
std::vector<Frame> frames_; // 调用栈([0] = MAIN,跨周期保留)
|
||||
// 边沿检测上次输入(每槽 2 字节:[slot*2] 第一边沿、[slot*2+1] 第二边沿;
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* @file Image.cpp
|
||||
* @brief vm 自带的 .stb 只读视图(执行器侧)
|
||||
* @author
|
||||
* @date 2026-08-21
|
||||
*/
|
||||
|
||||
#include "vm/Image.h"
|
||||
|
||||
namespace vm {
|
||||
|
||||
namespace {
|
||||
|
||||
const uint32_t kMagic = 0x43545353u; // "STSC" 小端
|
||||
const uint32_t kVersion = 1;
|
||||
const size_t kHeaderSize = 104;
|
||||
const size_t kConstEntrySize = 12;
|
||||
const size_t kFuncRowSize = 12;
|
||||
const size_t kSha256Size = 32; // 文件尾(步骤 8 校验)
|
||||
|
||||
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
|
||||
|
||||
Image Image::from(const uint8_t* buf, size_t len) {
|
||||
Image 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;
|
||||
}
|
||||
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);
|
||||
|
||||
// 段校验(数据段之后是 SHA-256 文件尾;步骤 8 做校验,这里仅保证有尾)
|
||||
if (len < static_cast<size_t>(h.offset_data) + kSha256Size) {
|
||||
v.err_ = "missing sha256 tail";
|
||||
return v;
|
||||
}
|
||||
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 - kSha256Size) {
|
||||
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>(h.n_consts) * kConstEntrySize) {
|
||||
v.err_ = "const table size mismatch";
|
||||
return v;
|
||||
}
|
||||
if (offs[2] - offs[1] != 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;
|
||||
}
|
||||
|
||||
Image Image::from(const std::vector<uint8_t>& buf) {
|
||||
return from(buf.data(), buf.size());
|
||||
}
|
||||
|
||||
FuncRow Image::func_row(size_t i) const {
|
||||
FuncRow r;
|
||||
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;
|
||||
}
|
||||
|
||||
ConstEntry Image::const_entry(size_t i) const {
|
||||
ConstEntry e;
|
||||
if (ok_ && i < hdr_.n_consts) {
|
||||
const uint8_t* p = buf_ + hdr_.offset_const + i * kConstEntrySize;
|
||||
e.tag = get_le32(p);
|
||||
e.value = get_le64(p + 4);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
const uint8_t* Image::code_bytes() const {
|
||||
return ok_ ? buf_ + hdr_.offset_code : nullptr;
|
||||
}
|
||||
|
||||
const uint8_t* Image::data_bytes() const {
|
||||
return ok_ ? buf_ + hdr_.offset_data : nullptr;
|
||||
}
|
||||
|
||||
size_t Image::data_len() const {
|
||||
// 步骤 7:含 SHA 尾(步骤 8 收紧并校验)
|
||||
return ok_ ? len_ - hdr_.offset_data : 0;
|
||||
}
|
||||
|
||||
} // namespace vm
|
||||
+7
-7
@@ -25,7 +25,7 @@ namespace vm {
|
||||
|
||||
bool Machine::create(const std::vector<uint8_t>& image, Machine* out,
|
||||
std::string* err) {
|
||||
out->image_ = isa::ImageView::from(image);
|
||||
out->image_ = vm::Image::from(image);
|
||||
if (!out->image_.ok()) {
|
||||
if (err) {
|
||||
*err = out->image_.error();
|
||||
@@ -39,7 +39,7 @@ bool Machine::create(const std::vector<uint8_t>& image, Machine* out,
|
||||
|
||||
// MAIN 帧(跨周期保留)
|
||||
const uint32_t entry = out->image_.header().entry_fn_id;
|
||||
const isa::FuncRow main = out->image_.func_row(entry);
|
||||
const vm::FuncRow main = out->image_.func_row(entry);
|
||||
out->frames_.clear();
|
||||
Frame f;
|
||||
f.fn_id = entry;
|
||||
@@ -79,7 +79,7 @@ bool Machine::step() {
|
||||
|
||||
Fault Machine::fault() const { return fault_; }
|
||||
bool Machine::ended() const { return ended_; }
|
||||
const isa::ImageHeader& Machine::header() const { return image_.header(); }
|
||||
const vm::ImageHeader& Machine::header() const { return image_.header(); }
|
||||
uint32_t Machine::pc() const { return pc_; }
|
||||
uint32_t Machine::fn_id() const { return cur_frame().fn_id; }
|
||||
uint32_t Machine::cycle_count() const { return cycle_count_; }
|
||||
@@ -94,7 +94,7 @@ uint32_t Machine::nregs() const {
|
||||
}
|
||||
|
||||
isa::Instr Machine::cur_instr() const {
|
||||
const isa::FuncRow row = image_.func_row(cur_frame().fn_id);
|
||||
const vm::FuncRow row = image_.func_row(cur_frame().fn_id);
|
||||
const uint8_t* base = image_.code_bytes() + row.code_offset;
|
||||
if (pc_ >= row.code_len) {
|
||||
return isa::pack(isa::Op::RET, 0, 0, 0);
|
||||
@@ -107,7 +107,7 @@ const Machine::Frame& Machine::cur_frame() const { return frames_.back(); }
|
||||
|
||||
bool Machine::exec_one() {
|
||||
// ---- 取指 ----
|
||||
const isa::FuncRow row = image_.func_row(cur_frame().fn_id);
|
||||
const vm::FuncRow row = image_.func_row(cur_frame().fn_id);
|
||||
if (pc_ >= row.code_len) {
|
||||
fault_ = Fault::BadOp;
|
||||
return false;
|
||||
@@ -165,7 +165,7 @@ bool Machine::exec_one() {
|
||||
fault_ = Fault::BadConst;
|
||||
return false;
|
||||
}
|
||||
const isa::ConstEntry c = image_.const_entry(cid);
|
||||
const vm::ConstEntry c = image_.const_entry(cid);
|
||||
if (c.tag == isa::types::Bool) {
|
||||
regs[rd] = c.value ? 1 : 0;
|
||||
} else if (c.tag == isa::types::Int) {
|
||||
@@ -310,7 +310,7 @@ Fault Machine::do_call(uint32_t fn_id) {
|
||||
if (frames_.size() >= 64) {
|
||||
return Fault::StackOverflow;
|
||||
}
|
||||
const isa::FuncRow row = image_.func_row(fn_id);
|
||||
const vm::FuncRow row = image_.func_row(fn_id);
|
||||
Frame f;
|
||||
f.fn_id = fn_id;
|
||||
f.regs.assign(row.nregs, 0);
|
||||
|
||||
Reference in New Issue
Block a user