步骤C:FB 函数化——共享体(r1 实例指针 + LOAD_PTR/STORE_PTR 基址寻址 + CALL),退役内联/fb_temp,嵌套 r1 保护
This commit is contained in:
@@ -338,6 +338,10 @@ void disasm(const MachineConfig& cfg, const uint8_t* code, size_t len,
|
|||||||
snprintf(out, cap, "%s%s r%u, s%u, %d", op->name.c_str(), suffix.c_str(),
|
snprintf(out, cap, "%s%s r%u, s%u, %d", op->name.c_str(), suffix.c_str(),
|
||||||
static_cast<unsigned>(rd_of(code)), static_cast<unsigned>(slot32_of(code)),
|
static_cast<unsigned>(rd_of(code)), static_cast<unsigned>(slot32_of(code)),
|
||||||
static_cast<int>(off32_hi_of(code)));
|
static_cast<int>(off32_hi_of(code)));
|
||||||
|
} else if (f == "RS+off") {
|
||||||
|
snprintf(out, cap, "%s%s r%u, r%u, %d", op->name.c_str(), suffix.c_str(),
|
||||||
|
static_cast<unsigned>(rd_of(code)), static_cast<unsigned>(rs1_of(code)),
|
||||||
|
static_cast<int>(off32_hi_of(code)));
|
||||||
} else { // NONE / 预留
|
} else { // NONE / 预留
|
||||||
snprintf(out, cap, "%s", op->name.c_str());
|
snprintf(out, cap, "%s", op->name.c_str());
|
||||||
}
|
}
|
||||||
|
|||||||
+116
-80
@@ -120,8 +120,9 @@ namespace {
|
|||||||
std::vector<uint8_t> code; ///< 字节码(变长指令,字节流)
|
std::vector<uint8_t> code; ///< 字节码(变长指令,字节流)
|
||||||
std::map<std::string, uint8_t> regs; ///< 变量名 → 帧寄存器
|
std::map<std::string, uint8_t> regs; ///< 变量名 → 帧寄存器
|
||||||
std::map<std::string, uint8_t> reg_func; ///< 变量名 → func(类型)
|
std::map<std::string, uint8_t> reg_func; ///< 变量名 → func(类型)
|
||||||
std::map<std::string, uint8_t> fb_temp; ///< FB 内联 VAR_TEMP → 帧寄存器(当前内联层)
|
bool is_fb = false; ///< 本函数是 FUNCTION_BLOCK(共享体,r1=实例指针)
|
||||||
std::map<std::string, uint8_t> fb_temp_func; ///< FB 内联 VAR_TEMP → func
|
std::map<std::string, int32_t> fb_field_off; ///< 本 FB 类型字段名 → 偏移
|
||||||
|
std::map<std::string, uint8_t> fb_field_func; ///< 本 FB 类型字段名 → func
|
||||||
uint8_t nlocals = 0; ///< 变量区终点 = 临时寄存器起始
|
uint8_t nlocals = 0; ///< 变量区终点 = 临时寄存器起始
|
||||||
uint8_t nregs = 0; ///< 寄存器峰值
|
uint8_t nregs = 0; ///< 寄存器峰值
|
||||||
uint8_t temp_used = 0; ///< 本语句已用临时数
|
uint8_t temp_used = 0; ///< 本语句已用临时数
|
||||||
@@ -206,6 +207,15 @@ namespace {
|
|||||||
set_off32_hi(f.code.data() + s, off);
|
set_off32_hi(f.code.data() + s, off);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
/// 基址寻址字段访问(rd, 基址寄存器rs, 偏移;16B RS+off)。
|
||||||
|
size_t E_rs_off(FuncCtx& f, const char* name, uint8_t rd, uint8_t rs,
|
||||||
|
int32_t off) {
|
||||||
|
const size_t s = emit(f, opc(name));
|
||||||
|
set_rd(f.code.data() + s, rd);
|
||||||
|
set_rs1(f.code.data() + s, rs);
|
||||||
|
set_off32_hi(f.code.data() + s, off);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
/// 内建 FB 调用(fb_id16 + 实例槽32)。
|
/// 内建 FB 调用(fb_id16 + 实例槽32)。
|
||||||
size_t E_cal(FuncCtx& f, uint16_t fb_id, uint32_t slot) {
|
size_t E_cal(FuncCtx& f, uint16_t fb_id, uint32_t slot) {
|
||||||
const size_t s = emit(f, opc("CAL"));
|
const size_t s = emit(f, opc("CAL"));
|
||||||
@@ -242,6 +252,7 @@ namespace {
|
|||||||
std::map<std::string, int32_t> field_off; ///< 字段名 → 值区内偏移
|
std::map<std::string, int32_t> field_off; ///< 字段名 → 值区内偏移
|
||||||
std::map<std::string, uint8_t> field_func; ///< 字段名 → func(类型)
|
std::map<std::string, uint8_t> field_func; ///< 字段名 → func(类型)
|
||||||
uint32_t slot = 0; ///< 实例槽号(槽表条目)
|
uint32_t slot = 0; ///< 实例槽号(槽表条目)
|
||||||
|
uint32_t addr = 0; ///< 实例基址(值区偏移,LOADK r1 用)
|
||||||
std::string type_name; ///< 实例的 FB 类型名
|
std::string type_name; ///< 实例的 FB 类型名
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -295,10 +306,68 @@ namespace {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// FB 类型字段偏移(类型级,对齐规则与 layout_slots_values 一致)。
|
||||||
|
void fb_type_field_off(const FbLayout& lay,
|
||||||
|
std::map<std::string, int32_t>* off,
|
||||||
|
std::map<std::string, uint8_t>* func) {
|
||||||
|
int32_t cur = 0;
|
||||||
|
for (const FbField& fd : lay.fields) {
|
||||||
|
const TypeMeta tm = type_meta(cfg_, fd.type_name);
|
||||||
|
cur = static_cast<int32_t>(align_up(static_cast<uint32_t>(cur), tm.width()));
|
||||||
|
(*off)[fd.name] = cur;
|
||||||
|
(*func)[fd.name] = static_cast<uint8_t>(tm.func());
|
||||||
|
cur += static_cast<int32_t>(tm.width());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool build_function(const POU& pou, FuncCtx* f) {
|
bool build_function(const POU& pou, FuncCtx* f) {
|
||||||
if (pou.kind == PouKind::FunctionBlock) {
|
if (pou.kind == PouKind::FunctionBlock) {
|
||||||
|
// FB 共享体:编译为独立函数,r1 = 实例指针(值区地址,约定)
|
||||||
|
// 字段(输入/输出/内部 VAR)在实例存储 → LOAD_PTR/STORE_PTR r1+off;
|
||||||
|
// 局部寄存器 = VAR_TEMP + 表达式临时(r8 起),帧每周期重建 → 天然重置
|
||||||
|
f->is_fb = true;
|
||||||
f->nlocals = 8;
|
f->nlocals = 8;
|
||||||
f->nregs = 8;
|
f->nregs = 8;
|
||||||
|
const auto fl = link_.fb_types.find(pou.name);
|
||||||
|
if (fl == link_.fb_types.end()) {
|
||||||
|
return fail("no FB type layout for '" + pou.name + "'");
|
||||||
|
}
|
||||||
|
fb_type_field_off(fl->second, &f->fb_field_off, &f->fb_field_func);
|
||||||
|
uint8_t r = 8;
|
||||||
|
for (const VarBlock& b : pou.blocks) {
|
||||||
|
if (b.section != VarSection::Temp) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const VarDecl& d : b.vars) {
|
||||||
|
if (d.type.kind != TypeKind::Scalar) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
f->regs[d.name] = r;
|
||||||
|
f->reg_func[d.name] = func_of_name(type_name_of_var(pou, d.name));
|
||||||
|
++r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f->nlocals = r;
|
||||||
|
f->nregs = r;
|
||||||
|
if (map_ != nullptr) {
|
||||||
|
std::map<std::string, std::string> vars;
|
||||||
|
for (const auto& kv : f->regs) {
|
||||||
|
std::string tn = "int";
|
||||||
|
const auto tf = f->reg_func.find(kv.first);
|
||||||
|
if (tf != f->reg_func.end()) {
|
||||||
|
tn = std::to_string(tf->second);
|
||||||
|
}
|
||||||
|
vars[kv.first] = "r" + std::to_string(kv.second) + " (func " + tn + ")";
|
||||||
|
}
|
||||||
|
map_->funcs.emplace_back(f->name, vars);
|
||||||
|
}
|
||||||
|
emit_temp_init(*f, pou, f->regs, f->reg_func);
|
||||||
|
for (const Stmt& st : pou.body) {
|
||||||
|
begin_stmt(*f);
|
||||||
|
if (!compile_stmt(*f, st)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
E_ret(*f);
|
E_ret(*f);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -373,38 +442,24 @@ namespace {
|
|||||||
if (st.kind != StmtKind::Assign) {
|
if (st.kind != StmtKind::Assign) {
|
||||||
return fail("statement not supported");
|
return fail("statement not supported");
|
||||||
}
|
}
|
||||||
if (inline_fields_) {
|
if (f.is_fb) {
|
||||||
const auto fit = inline_fields_->field_off.find(st.target);
|
// FB 共享体:本实例字段(裸名)→ STORE_PTR r1+off
|
||||||
if (fit != inline_fields_->field_off.end()) {
|
const auto fit = f.fb_field_off.find(st.target);
|
||||||
|
if (fit != f.fb_field_off.end()) {
|
||||||
const uint8_t t = alloc_temp(f);
|
const uint8_t t = alloc_temp(f);
|
||||||
const auto ff_ = inline_fields_->field_func.find(st.target);
|
const auto ff_ = f.fb_field_func.find(st.target);
|
||||||
if (!compile_expr(f, *st.value, t,
|
if (!compile_expr(f, *st.value, t,
|
||||||
ff_ != inline_fields_->field_func.end()
|
ff_ != f.fb_field_func.end() ? ff_->second : 0)) {
|
||||||
? ff_->second
|
|
||||||
: 0)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const size_t so = E_slot_off(f, "STORE_OFF", t, inline_fields_->slot,
|
const size_t so = E_rs_off(f, "STORE_PTR", t, 1, fit->second);
|
||||||
fit->second);
|
const auto ff = f.fb_field_func.find(st.target);
|
||||||
const auto ff = inline_fields_->field_func.find(st.target);
|
if (ff != f.fb_field_func.end()) {
|
||||||
if (ff != inline_fields_->field_func.end()) {
|
|
||||||
set_func(f.code.data() + so, ff->second);
|
set_func(f.code.data() + so, ff->second);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const auto tit = f.fb_temp.find(st.target);
|
|
||||||
if (tit != f.fb_temp.end()) {
|
|
||||||
const uint8_t t = alloc_temp(f);
|
|
||||||
const uint8_t tf_ = f.fb_temp_func.count(st.target)
|
|
||||||
? f.fb_temp_func.at(st.target)
|
|
||||||
: func_of_name("INT");
|
|
||||||
if (!compile_expr(f, *st.value, t, tf_)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
E_rr(f, "MOVE", tit->second, t);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const auto it = f.regs.find(st.target);
|
const auto it = f.regs.find(st.target);
|
||||||
if (it == f.regs.end()) {
|
if (it == f.regs.end()) {
|
||||||
return store_target(f, st.target, *st.value);
|
return store_target(f, st.target, *st.value);
|
||||||
@@ -449,7 +504,25 @@ namespace {
|
|||||||
if (user_fb == nullptr) {
|
if (user_fb == nullptr) {
|
||||||
return fail("no FB type '" + inst->type_name + "'");
|
return fail("no FB type '" + inst->type_name + "'");
|
||||||
}
|
}
|
||||||
return compile_fb_inline(f, *user_fb, *inst);
|
// 用户 FB → CALL 共享体:r1 = 实例指针(值区地址)→ CALL
|
||||||
|
const int fb_fn = find_fn_id(user_fb->name);
|
||||||
|
if (fb_fn < 0) {
|
||||||
|
return fail("no fn_id for '" + user_fb->name + "'");
|
||||||
|
}
|
||||||
|
f.calls.push_back(static_cast<uint32_t>(fb_fn));
|
||||||
|
uint8_t save_r = 0;
|
||||||
|
if (f.is_fb) {
|
||||||
|
// 嵌套(FB 函数体内再调 FB):保存本层 r1,调用后恢复
|
||||||
|
save_r = alloc_temp(f);
|
||||||
|
E_rr(f, "MOVE", save_r, 1);
|
||||||
|
}
|
||||||
|
const size_t lk = E_imm(f, "LOADK", 1, const_id(inst->addr));
|
||||||
|
set_func(f.code.data() + lk, func_of_name("UDINT"));
|
||||||
|
E_call(f, static_cast<uint32_t>(fb_fn));
|
||||||
|
if (f.is_fb) {
|
||||||
|
E_rr(f, "MOVE", 1, save_r);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string uppercase_of(const std::string& s) {
|
static std::string uppercase_of(const std::string& s) {
|
||||||
@@ -460,43 +533,6 @@ namespace {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool compile_fb_inline(FuncCtx& f, const POU& fb, const InstFields& inst) {
|
|
||||||
const InstFields* saved = inline_fields_;
|
|
||||||
inline_fields_ = &inst;
|
|
||||||
// VAR_TEMP:本调用点分配独立固定寄存器(MAIN 帧,nregs 之后),
|
|
||||||
// 内联体开头装载初值 → 每次调用重新初始化;嵌套内联 save/restore
|
|
||||||
const std::map<std::string, uint8_t> saved_temp = f.fb_temp;
|
|
||||||
const std::map<std::string, uint8_t> saved_temp_func = f.fb_temp_func;
|
|
||||||
f.fb_temp.clear();
|
|
||||||
f.fb_temp_func.clear();
|
|
||||||
for (const VarBlock& b : fb.blocks) {
|
|
||||||
if (b.section != VarSection::Temp) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (const VarDecl& d : b.vars) {
|
|
||||||
if (d.type.kind != TypeKind::Scalar) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const uint8_t r = f.nregs++;
|
|
||||||
f.fb_temp[d.name] = r;
|
|
||||||
f.fb_temp_func[d.name] = func_of_name(type_name_of_var(fb, d.name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 临时寄存器起点提到 Temp 区之上,避免语句内 alloc_temp 撞上 Temp 寄存器
|
|
||||||
f.nlocals = f.nregs;
|
|
||||||
emit_temp_init(f, fb, f.fb_temp, f.fb_temp_func);
|
|
||||||
for (const Stmt& s : fb.body) {
|
|
||||||
begin_stmt(f);
|
|
||||||
if (!compile_stmt(f, s)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f.fb_temp = saved_temp;
|
|
||||||
f.fb_temp_func = saved_temp_func;
|
|
||||||
inline_fields_ = saved;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool compile_while(FuncCtx& f, const Stmt& st) {
|
bool compile_while(FuncCtx& f, const Stmt& st) {
|
||||||
const size_t loop = f.code.size();
|
const size_t loop = f.code.size();
|
||||||
const uint8_t t = alloc_temp(f);
|
const uint8_t t = alloc_temp(f);
|
||||||
@@ -609,27 +645,18 @@ namespace {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (e.kind == ExprKind::VarRef) {
|
if (e.kind == ExprKind::VarRef) {
|
||||||
if (inline_fields_) {
|
if (f.is_fb) {
|
||||||
const auto fit = inline_fields_->field_off.find(e.name);
|
// FB 共享体:本实例字段(裸名)→ LOAD_PTR r1+off
|
||||||
if (fit != inline_fields_->field_off.end()) {
|
const auto fit = f.fb_field_off.find(e.name);
|
||||||
const size_t lo = E_slot_off(f, "LOAD_OFF", rd,
|
if (fit != f.fb_field_off.end()) {
|
||||||
inline_fields_->slot, fit->second);
|
const size_t lo = E_rs_off(f, "LOAD_PTR", rd, 1, fit->second);
|
||||||
const auto ff = inline_fields_->field_func.find(e.name);
|
const auto ff = f.fb_field_func.find(e.name);
|
||||||
if (ff != inline_fields_->field_func.end()) {
|
if (ff != f.fb_field_func.end()) {
|
||||||
set_func(f.code.data() + lo, ff->second);
|
set_func(f.code.data() + lo, ff->second);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const auto tit = f.fb_temp.find(e.name);
|
|
||||||
if (tit != f.fb_temp.end()) {
|
|
||||||
const size_t s = E_rr(f, "MOVE", rd, tit->second);
|
|
||||||
const auto tf = f.fb_temp_func.find(e.name);
|
|
||||||
set_func(f.code.data() + s,
|
|
||||||
tf != f.fb_temp_func.end() ? tf->second
|
|
||||||
: func_of_name("INT"));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const auto sit = f.regs.find(e.name);
|
const auto sit = f.regs.find(e.name);
|
||||||
if (sit != f.regs.end()) {
|
if (sit != f.regs.end()) {
|
||||||
const size_t s = E_rr(f, "MOVE", rd, sit->second);
|
const size_t s = E_rr(f, "MOVE", rd, sit->second);
|
||||||
@@ -736,11 +763,20 @@ namespace {
|
|||||||
if (fn_id < 0) {
|
if (fn_id < 0) {
|
||||||
return fail("no fn_id for '" + e.name + "'");
|
return fail("no fn_id for '" + e.name + "'");
|
||||||
}
|
}
|
||||||
|
uint8_t save_r = 0;
|
||||||
|
if (f.is_fb) {
|
||||||
|
// FB 函数内调 FUNCTION:实参区 r1..r7 会覆盖 r1(实例指针)→ 保存恢复
|
||||||
|
save_r = alloc_temp(f);
|
||||||
|
E_rr(f, "MOVE", save_r, 1);
|
||||||
|
}
|
||||||
for (size_t i = 0; i < args.size(); ++i) {
|
for (size_t i = 0; i < args.size(); ++i) {
|
||||||
E_rr(f, "MOVE", static_cast<uint8_t>(1 + i), args[i]);
|
E_rr(f, "MOVE", static_cast<uint8_t>(1 + i), args[i]);
|
||||||
}
|
}
|
||||||
f.calls.push_back(static_cast<uint32_t>(fn_id));
|
f.calls.push_back(static_cast<uint32_t>(fn_id));
|
||||||
E_call(f, static_cast<uint32_t>(fn_id));
|
E_call(f, static_cast<uint32_t>(fn_id));
|
||||||
|
if (f.is_fb) {
|
||||||
|
E_rr(f, "MOVE", 1, save_r);
|
||||||
|
}
|
||||||
E_rr(f, "MOVE", rd, 0);
|
E_rr(f, "MOVE", rd, 0);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -896,6 +932,7 @@ namespace {
|
|||||||
max_align = max_align > tm.width() ? max_align : tm.width();
|
max_align = max_align > tm.width() ? max_align : tm.width();
|
||||||
}
|
}
|
||||||
cur_addr = align_up(cur_addr, max_align);
|
cur_addr = align_up(cur_addr, max_align);
|
||||||
|
inst.addr = cur_addr;
|
||||||
slots_.push_back(cur_addr);
|
slots_.push_back(cur_addr);
|
||||||
values_.resize(cur_addr + static_cast<uint32_t>(off), 0);
|
values_.resize(cur_addr + static_cast<uint32_t>(off), 0);
|
||||||
instances_[sc.name + "/" + s.name] = inst;
|
instances_[sc.name + "/" + s.name] = inst;
|
||||||
@@ -1153,7 +1190,6 @@ namespace {
|
|||||||
std::vector<uint8_t> values_; ///< 值段字节
|
std::vector<uint8_t> values_; ///< 值段字节
|
||||||
std::map<std::string, uint32_t> global_slot_; ///< 全局名 → 槽号
|
std::map<std::string, uint32_t> global_slot_; ///< 全局名 → 槽号
|
||||||
std::map<std::string, InstFields> instances_; ///< "POU名/实例名" → 实例布局
|
std::map<std::string, InstFields> instances_; ///< "POU名/实例名" → 实例布局
|
||||||
const InstFields* inline_fields_ = nullptr; ///< 内联 FB 字段表
|
|
||||||
uint32_t max_stack_ = 0; ///< 栈区字节数
|
uint32_t max_stack_ = 0; ///< 栈区字节数
|
||||||
CodegenMap* map_ = nullptr; ///< 变量/槽映射输出(可空)
|
CodegenMap* map_ = nullptr; ///< 变量/槽映射输出(可空)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -237,14 +237,20 @@ static bool test_line1() {
|
|||||||
CHECK(v.n_globals() == 4); // EmergencyStop/I0_0/I0_1/Q0_0
|
CHECK(v.n_globals() == 4); // EmergencyStop/I0_0/I0_1/Q0_0
|
||||||
CHECK(v.n_slots() == 5); // 4 全局 + 1 实例(字段走偏移,实例占 1 条)
|
CHECK(v.n_slots() == 5); // 4 全局 + 1 实例(字段走偏移,实例占 1 条)
|
||||||
CHECK(v.values_size() == 7); // 全局 4×1B + starter 3×1B
|
CHECK(v.values_size() == 7); // 全局 4×1B + starter 3×1B
|
||||||
CHECK(v.max_stack() >= 12 + 13 * 8);
|
CHECK(v.max_stack() >= (12 + 13 * 8) + (12 + 10 * 8)); // fn0 共享体 13 + MAIN 10
|
||||||
// 指令序列:LOAD(I 读)、STORE_OFF(FB 输入写)、LOAD_OFF(FB 输出读)、STORE(Q 写)
|
// MAIN(fn 1):LOAD(I 读)、STORE_OFF(实参写)、LOADK r1 + CALL(共享体)、LOAD_OFF(输出读)、STORE(Q 写)
|
||||||
const std::vector<std::string> lines = disasm_lines(cfg_global(), v, 1);
|
const std::vector<std::string> lines = disasm_lines(cfg_global(), v, 1);
|
||||||
CHECK(has_line(lines, "LOAD.U8"));
|
CHECK(has_line(lines, "LOAD.U8"));
|
||||||
CHECK(has_line(lines, "STORE_OFF.U8"));
|
CHECK(has_line(lines, "STORE_OFF.U8"));
|
||||||
CHECK(has_line(lines, "LOAD_OFF.U8"));
|
CHECK(has_line(lines, "LOAD_OFF.U8"));
|
||||||
CHECK(has_line(lines, "STORE.U8"));
|
CHECK(has_line(lines, "STORE.U8"));
|
||||||
CHECK(has_line(lines, "JF r"));
|
CHECK(has_line(lines, "LOADK.U32 r1"));
|
||||||
|
CHECK(has_line(lines, "CALL 0"));
|
||||||
|
// MotorStarter 共享体(fn 0):LOAD_PTR/STORE_PTR + 短路 JF
|
||||||
|
const std::vector<std::string> fb = disasm_lines(cfg_global(), v, 0);
|
||||||
|
CHECK(has_line(fb, "LOAD_PTR.U8"));
|
||||||
|
CHECK(has_line(fb, "STORE_PTR.U8"));
|
||||||
|
CHECK(has_line(fb, "JF r"));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user