12.10 双可执行接线:编译产出 sidecar,BytecodeExecutor 跑周期。
- STCompiler -o:另写 <name>.runtime.toml(io 绑定 var→槽号→channel/bit,槽号来自链接结果) - BytecodeExecutor <name>.stb [--cycles N] [--replay <file>]:加载 .stb + sidecar, 逐周期采样 I(回放行按 io.input 顺序)→ 跑周期 → 打印 I/Q;故障打印 FAULT 退出码 1 - 手写 sidecar 解析(冻结 TOML 子集);Machine 暴露 header()(dt_ms/cycle_limit) - 验证:line1 回放 0/1 组合 Q 正确(急停/start 语义)、用例 06 CycleLimit FAULT;ctest 10/10
This commit is contained in:
@@ -4,6 +4,16 @@ BytecodeExecutor 模块:可执行入口。CMake 目标:`BytecodeExecutor`(
|
||||
|
||||
第一版两个可执行:`STCompiler` 编译出 `<name>.stb` 与 `<name>.runtime.toml`;`BytecodeExecutor` 加载映像按扫描周期跑。总顺序见 [`初步计划.md`](../初步计划.md) 第 12.10 节。
|
||||
|
||||
## 用法
|
||||
|
||||
```text
|
||||
BytecodeExecutor <name>.stb [--cycles N] [--replay <file>]
|
||||
```
|
||||
|
||||
- `--cycles N`:跑 N 个周期(缺省 10)
|
||||
- `--replay <file>`:读录制的 I 序列(每行空格分隔的 0/1,按 sidecar `io.input` 绑定顺序),逐周期喂入;读尽后保持最后一行
|
||||
- 每周期打印 `cycle N: I=[...] Q=[...]`(按 sidecar 绑定顺序);故障打印 `FAULT(n)` 并退出码 1
|
||||
|
||||
## 职责
|
||||
|
||||
- 加载 `.stb` 映像与 `<name>.runtime.toml` sidecar(I/O 绑定 var → 槽号 → channel/bit)
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
* @date 2026-08-21
|
||||
*/
|
||||
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -167,6 +169,27 @@ int main(int argc, char** argv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// sidecar:I/O 绑定 var → 槽号 → channel/bit(不进映像,执行器采样用)
|
||||
std::vector<isa::IoBinding> bindings;
|
||||
for (const isa::IoBinding& b : proj.io) {
|
||||
isa::IoBinding out_b = b;
|
||||
std::string key = b.var;
|
||||
for (char& ch : key) {
|
||||
ch = static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
|
||||
}
|
||||
const auto it = link.global_index.find(key);
|
||||
if (it != link.global_index.end()) {
|
||||
out_b.slot = it->second; // 12.6 已校验存在
|
||||
}
|
||||
bindings.push_back(out_b);
|
||||
}
|
||||
std::filesystem::path sidecar = std::filesystem::path(out_path);
|
||||
sidecar.replace_extension(".runtime.toml");
|
||||
if (!isa::write_sidecar_file(sidecar.string().c_str(), bindings, &err)) {
|
||||
std::fprintf(stderr, "error: %s\n", err.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
const isa::ImageView v = isa::ImageView::from(image);
|
||||
std::printf("compiled: %s (%zu bytes, %u functions, %u globals)\n",
|
||||
out_path.c_str(), image.size(), v.header().n_funcs, v.header().n_globals);
|
||||
|
||||
+230
-4
@@ -2,14 +2,240 @@
|
||||
* @file main.cpp
|
||||
* @brief BytecodeExecutor 可执行入口
|
||||
* @author
|
||||
* @date 2026-08-19
|
||||
* @date 2026-08-21
|
||||
*
|
||||
* @details 12.10:加载 .stb + sidecar,按扫描周期执行。
|
||||
* - `BytecodeExecutor <name>.stb [--cycles N] [--replay <file>]`
|
||||
* - 只链 vm + isa,不链 compiler;dt_ms / cycle_limit 从映像头取
|
||||
* - I 采样 / Q 写回按 sidecar(var → 槽号 → channel/bit),不创造变量
|
||||
* - --replay:读录制文本(每行空格分隔的 0/1,按 io.input 绑定顺序),
|
||||
* 逐周期喂入;文件读尽后保持最后一行
|
||||
* - 每周期打印 I/Q(最小可观测)
|
||||
*/
|
||||
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "isa/Image.h"
|
||||
#include "vm/Machine.h"
|
||||
|
||||
namespace {
|
||||
|
||||
void usage() {
|
||||
std::printf("usage: BytecodeExecutor <name>.stb [--cycles N] [--replay <file>]\n"
|
||||
" 加载 .stb + <name>.runtime.toml,按扫描周期执行\n"
|
||||
" --cycles N 跑 N 个周期(缺省 10)\n"
|
||||
" --replay <file> 读录制的 I 序列(每行按 io.input 顺序的 0/1)\n"
|
||||
" --help 打印本帮助\n");
|
||||
}
|
||||
|
||||
// sidecar 绑定(TOML 子集,格式冻结于 Doc/isa/指令与映像.md)
|
||||
struct Binding {
|
||||
bool is_input = true;
|
||||
std::string var;
|
||||
uint32_t slot = 0;
|
||||
uint32_t channel = 0;
|
||||
uint32_t bit = 0;
|
||||
};
|
||||
|
||||
// 解析 <name>.runtime.toml(逐行手写解析,仅冻结子集)
|
||||
bool parse_sidecar(const std::string& path, std::vector<Binding>* out,
|
||||
std::string* err) {
|
||||
std::ifstream in(path);
|
||||
if (!in) {
|
||||
*err = "cannot open sidecar: " + path;
|
||||
return false;
|
||||
}
|
||||
std::string line;
|
||||
bool is_input = true;
|
||||
bool in_entry = false;
|
||||
Binding cur;
|
||||
while (std::getline(in, line)) {
|
||||
// 去首尾空白
|
||||
size_t b = line.find_first_not_of(" \t\r");
|
||||
if (b == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
line = line.substr(b);
|
||||
if (line.empty() || line[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
if (line == "[[io.input]]") {
|
||||
if (in_entry) out->push_back(cur);
|
||||
cur = Binding();
|
||||
is_input = true;
|
||||
in_entry = true;
|
||||
continue;
|
||||
}
|
||||
if (line == "[[io.output]]") {
|
||||
if (in_entry) out->push_back(cur);
|
||||
cur = Binding();
|
||||
is_input = false;
|
||||
in_entry = true;
|
||||
continue;
|
||||
}
|
||||
const size_t eq = line.find('=');
|
||||
if (eq == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
const std::string key = line.substr(0, eq);
|
||||
std::string val = line.substr(eq + 1);
|
||||
const size_t b2 = key.find_first_not_of(" \t");
|
||||
const size_t e2 = key.find_last_not_of(" \t");
|
||||
const size_t b3 = val.find_first_not_of(" \t");
|
||||
const size_t e3 = val.find_last_not_of(" \t\r");
|
||||
const std::string k = key.substr(b2, e2 - b2 + 1);
|
||||
const std::string v = val.substr(b3, e3 - b3 + 1);
|
||||
if (k == "var") {
|
||||
cur.var = v.substr(1, v.size() - 2); // 去引号
|
||||
} else if (k == "slot") {
|
||||
cur.slot = static_cast<uint32_t>(std::stoul(v));
|
||||
} else if (k == "channel") {
|
||||
cur.channel = static_cast<uint32_t>(std::stoul(v));
|
||||
} else if (k == "bit") {
|
||||
cur.bit = static_cast<uint32_t>(std::stoul(v));
|
||||
}
|
||||
cur.is_input = is_input;
|
||||
}
|
||||
if (in_entry) {
|
||||
out->push_back(cur);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 读一个槽的 BOOL 值(低字节)
|
||||
int slot_bool(vm::Machine& m, uint32_t slot) {
|
||||
return m.data()[static_cast<size_t>(slot) * 8] ? 1 : 0;
|
||||
}
|
||||
|
||||
void put_bool(vm::Machine& m, uint32_t slot, int v) {
|
||||
m.data()[static_cast<size_t>(slot) * 8] = v ? 1 : 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
std::printf("BytecodeExecutor 0.1\n");
|
||||
if (argc < 2) {
|
||||
std::printf("BytecodeExecutor 0.1\n");
|
||||
usage();
|
||||
return 0;
|
||||
}
|
||||
if (std::strcmp(argv[1], "--help") == 0) {
|
||||
usage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
const std::string stb_path = argv[1];
|
||||
uint32_t cycles = 10;
|
||||
std::string replay;
|
||||
for (int i = 2; i < argc; ++i) {
|
||||
if (std::strcmp(argv[i], "--cycles") == 0 && i + 1 < argc) {
|
||||
cycles = static_cast<uint32_t>(std::stoul(argv[i + 1]));
|
||||
} else if (std::strcmp(argv[i], "--replay") == 0 && i + 1 < argc) {
|
||||
replay = argv[i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
return 1;
|
||||
}
|
||||
vm::Machine machine;
|
||||
if (!vm::Machine::create(bytes, &machine, &err)) {
|
||||
std::fprintf(stderr, "error: %s\n", err.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// sidecar:<name>.runtime.toml
|
||||
std::string sidecar_path = stb_path;
|
||||
const size_t dot = sidecar_path.find_last_of('.');
|
||||
if (dot != std::string::npos) {
|
||||
sidecar_path = sidecar_path.substr(0, dot);
|
||||
}
|
||||
sidecar_path += ".runtime.toml";
|
||||
std::vector<Binding> bindings;
|
||||
if (!parse_sidecar(sidecar_path, &bindings, &err)) {
|
||||
std::fprintf(stderr, "error: %s\n", err.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 回放缓冲
|
||||
std::vector<int> replay_line;
|
||||
std::ifstream replay_in;
|
||||
if (!replay.empty()) {
|
||||
replay_in.open(replay);
|
||||
if (!replay_in) {
|
||||
std::fprintf(stderr, "error: cannot open replay: %s\n", replay.c_str());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
const isa::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);
|
||||
|
||||
for (uint32_t c = 1; c <= cycles; ++c) {
|
||||
// 采样:回放行按 io.input 顺序填,无回放则全 0
|
||||
if (replay_in.is_open()) {
|
||||
std::string line;
|
||||
if (std::getline(replay_in, line)) {
|
||||
replay_line.clear();
|
||||
size_t pos = 0;
|
||||
while (pos < line.size()) {
|
||||
while (pos < line.size() &&
|
||||
std::isspace(static_cast<unsigned char>(line[pos]))) {
|
||||
++pos;
|
||||
}
|
||||
if (pos >= line.size()) break;
|
||||
const size_t s = pos;
|
||||
while (pos < line.size() &&
|
||||
!std::isspace(static_cast<unsigned char>(line[pos]))) {
|
||||
++pos;
|
||||
}
|
||||
replay_line.push_back(std::stoi(line.substr(s, pos - s)));
|
||||
}
|
||||
}
|
||||
size_t idx = 0;
|
||||
for (const Binding& b : bindings) {
|
||||
if (!b.is_input) continue;
|
||||
put_bool(machine, b.slot, idx < replay_line.size() ? replay_line[idx] : 0);
|
||||
++idx;
|
||||
}
|
||||
} else {
|
||||
for (const Binding& b : bindings) {
|
||||
if (b.is_input) {
|
||||
put_bool(machine, b.slot, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const vm::Fault f = machine.run_cycle();
|
||||
|
||||
// 打印 I/Q(按 sidecar 绑定顺序)
|
||||
std::string is, qs;
|
||||
for (const Binding& b : bindings) {
|
||||
if (b.is_input) {
|
||||
if (!is.empty()) is += " ";
|
||||
is += std::to_string(slot_bool(machine, b.slot));
|
||||
} else {
|
||||
if (!qs.empty()) qs += " ";
|
||||
qs += std::to_string(slot_bool(machine, b.slot));
|
||||
}
|
||||
}
|
||||
std::printf("cycle %u: I=[%s] Q=[%s]", c, is.c_str(), qs.c_str());
|
||||
if (f != vm::Fault::None) {
|
||||
std::printf(" FAULT(%d)", static_cast<int>(f));
|
||||
}
|
||||
std::printf("\n");
|
||||
if (f != vm::Fault::None) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -62,6 +62,9 @@ namespace vm {
|
||||
/** @brief 最近一次故障(step 返回 false 后查询) */
|
||||
Fault fault() const;
|
||||
|
||||
/** @brief 映像头(dt_ms / cycle_limit 等) */
|
||||
const isa::ImageHeader& header() const;
|
||||
|
||||
/** @brief 当前指令下标(相对当前函数字节码段) */
|
||||
uint32_t pc() const;
|
||||
|
||||
|
||||
@@ -78,6 +78,7 @@ bool Machine::step() {
|
||||
}
|
||||
|
||||
Fault Machine::fault() const { return fault_; }
|
||||
const isa::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_; }
|
||||
|
||||
Reference in New Issue
Block a user