vm(hako): add v1 reader/dispatcher (flagged), commonize mir_call handler, share block scan; smokes: add v1 hakovm canary; docs: 20.37/20.38 plans, OOB policy; runner: v1 hakovm toggle; include SKIP summary
This commit is contained in:
162
src/runner/mir_json_v0.rs
Normal file
162
src/runner/mir_json_v0.rs
Normal file
@ -0,0 +1,162 @@
|
||||
use crate::mir::{
|
||||
function::{FunctionSignature, MirFunction, MirModule},
|
||||
BasicBlock, BasicBlockId, ConstValue, MirInstruction, MirType, ValueId,
|
||||
};
|
||||
use serde_json::Value;
|
||||
use super::mir_json::common as mirjson_common;
|
||||
|
||||
/// Parse minimal MIR JSON v0 (no schema_version, root has `functions` and each function has `blocks`).
|
||||
/// Supported ops (minimal): const(i64), copy, compare(op/lhs/rhs), branch(cond/then/else), jump(target), phi(dst,incoming), ret(value?).
|
||||
pub fn parse_mir_v0_to_module(json: &str) -> Result<MirModule, String> {
|
||||
let value: Value = serde_json::from_str(json).map_err(|e| format!("invalid JSON: {}", e))?;
|
||||
let functions = value
|
||||
.get("functions")
|
||||
.and_then(|f| f.as_array())
|
||||
.ok_or_else(|| "JSON missing functions array".to_string())?;
|
||||
|
||||
let mut module = MirModule::new("mir_json_v0".to_string());
|
||||
|
||||
for func in functions {
|
||||
let func_name = func
|
||||
.get("name")
|
||||
.and_then(|n| n.as_str())
|
||||
.unwrap_or("main")
|
||||
.to_string();
|
||||
|
||||
let blocks = func
|
||||
.get("blocks")
|
||||
.and_then(|b| b.as_array())
|
||||
.ok_or_else(|| format!("function '{}' missing blocks array", func_name))?;
|
||||
|
||||
if blocks.is_empty() {
|
||||
return Err(format!("function '{}' has no blocks", func_name));
|
||||
}
|
||||
|
||||
let entry_id = blocks
|
||||
.get(0)
|
||||
.and_then(|b| b.get("id"))
|
||||
.and_then(|id| id.as_u64())
|
||||
.ok_or_else(|| format!("function '{}' entry block missing id", func_name))? as u32;
|
||||
let entry_bb = BasicBlockId::new(entry_id);
|
||||
|
||||
let mut signature = FunctionSignature {
|
||||
name: func_name.clone(),
|
||||
params: Vec::new(),
|
||||
return_type: MirType::Unknown,
|
||||
effects: crate::mir::EffectMask::PURE,
|
||||
};
|
||||
let mut mir_fn = MirFunction::new(signature.clone(), entry_bb);
|
||||
let mut max_value_id: u32 = 0;
|
||||
|
||||
for block in blocks {
|
||||
let block_id = block
|
||||
.get("id")
|
||||
.and_then(|id| id.as_u64())
|
||||
.ok_or_else(|| format!("function '{}' block missing id", func_name))? as u32;
|
||||
let bb_id = BasicBlockId::new(block_id);
|
||||
if mir_fn.get_block(bb_id).is_none() {
|
||||
mir_fn.add_block(BasicBlock::new(bb_id));
|
||||
}
|
||||
let block_ref = mir_fn
|
||||
.get_block_mut(bb_id)
|
||||
.expect("block must exist after insertion");
|
||||
|
||||
let instructions = block
|
||||
.get("instructions")
|
||||
.and_then(|insts| insts.as_array())
|
||||
.ok_or_else(|| format!("function '{}' block {} missing instructions array", func_name, block_id))?;
|
||||
|
||||
for inst in instructions {
|
||||
let op = inst
|
||||
.get("op")
|
||||
.and_then(|o| o.as_str())
|
||||
.ok_or_else(|| format!("function '{}' block {} missing op field", func_name, block_id))?;
|
||||
match op {
|
||||
"const" => {
|
||||
let dst = require_u64(inst, "dst", "const dst")? as u32;
|
||||
let vobj = inst.get("value").ok_or_else(|| "const missing value".to_string())?;
|
||||
let val = parse_const_value(vobj)?;
|
||||
block_ref.add_instruction(MirInstruction::Const { dst: ValueId::new(dst), value: val });
|
||||
max_value_id = max_value_id.max(dst + 1);
|
||||
}
|
||||
"copy" => {
|
||||
let dst = require_u64(inst, "dst", "copy dst")? as u32;
|
||||
let src = require_u64(inst, "src", "copy src")? as u32;
|
||||
block_ref.add_instruction(MirInstruction::Copy { dst: ValueId::new(dst), src: ValueId::new(src) });
|
||||
max_value_id = max_value_id.max(dst + 1);
|
||||
}
|
||||
"compare" => {
|
||||
let dst = require_u64(inst, "dst", "compare dst")? as u32;
|
||||
let lhs = require_u64(inst, "lhs", "compare lhs")? as u32;
|
||||
let rhs = require_u64(inst, "rhs", "compare rhs")? as u32;
|
||||
let operation = inst.get("operation").and_then(Value::as_str).unwrap_or("==");
|
||||
let cop = parse_compare(operation)?;
|
||||
block_ref.add_instruction(MirInstruction::Compare { dst: ValueId::new(dst), op: cop, lhs: ValueId::new(lhs), rhs: ValueId::new(rhs) });
|
||||
max_value_id = max_value_id.max(dst + 1);
|
||||
}
|
||||
"branch" => {
|
||||
let cond = require_u64(inst, "cond", "branch cond")? as u32;
|
||||
let then_bb = require_u64(inst, "then", "branch then")? as u32;
|
||||
let else_bb = require_u64(inst, "else", "branch else")? as u32;
|
||||
block_ref.add_instruction(MirInstruction::Branch { condition: ValueId::new(cond), then_bb: BasicBlockId::new(then_bb), else_bb: BasicBlockId::new(else_bb) });
|
||||
}
|
||||
"jump" => {
|
||||
let target = require_u64(inst, "target", "jump target")? as u32;
|
||||
block_ref.add_instruction(MirInstruction::Jump { target: BasicBlockId::new(target) });
|
||||
}
|
||||
"phi" => {
|
||||
let dst = require_u64(inst, "dst", "phi dst")? as u32;
|
||||
let incoming = inst.get("incoming").and_then(Value::as_array).ok_or_else(|| "phi incoming missing".to_string())?;
|
||||
let mut pairs = Vec::with_capacity(incoming.len());
|
||||
for entry in incoming {
|
||||
let pair = entry.as_array().ok_or_else(|| "phi incoming entry must be array".to_string())?;
|
||||
if pair.len() != 2 { return Err("phi incoming entry must have 2 elements".into()); }
|
||||
let val = pair[0].as_u64().ok_or_else(|| "phi incoming value must be integer".to_string())? as u32;
|
||||
let bb = pair[1].as_u64().ok_or_else(|| "phi incoming block must be integer".to_string())? as u32;
|
||||
pairs.push((BasicBlockId::new(bb), ValueId::new(val)));
|
||||
}
|
||||
block_ref.add_instruction(MirInstruction::Phi { dst: ValueId::new(dst), inputs: pairs });
|
||||
max_value_id = max_value_id.max(dst + 1);
|
||||
}
|
||||
"ret" => {
|
||||
let value = inst.get("value").and_then(|v| v.as_u64()).map(|v| ValueId::new(v as u32));
|
||||
block_ref.add_instruction(MirInstruction::Return { value });
|
||||
if let Some(val) = value { signature.return_type = MirType::Integer; max_value_id = max_value_id.max(val.as_u32() + 1); } else { signature.return_type = MirType::Void; }
|
||||
}
|
||||
other => {
|
||||
return Err(format!("unsupported op '{}' in mir_json_v0 loader", other));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set max value id (best effort)
|
||||
mir_fn.next_value_id = max_value_id;
|
||||
module.functions.insert(func_name.clone(), mir_fn);
|
||||
}
|
||||
|
||||
Ok(module)
|
||||
}
|
||||
|
||||
fn require_u64(node: &Value, key: &str, context: &str) -> Result<u64, String> {
|
||||
node.get(key).and_then(Value::as_u64).ok_or_else(|| format!("{} missing field '{}'", context, key))
|
||||
}
|
||||
|
||||
fn parse_const_value(value_obj: &Value) -> Result<ConstValue, String> {
|
||||
// Delegate to common generic parser (int/float/bool/string/handle(StringBox))
|
||||
// Keeps behavior superset of previous (int-only) without changing existing callers.
|
||||
mirjson_common::parse_const_value_generic(value_obj).map_err(|e| format!("{}", e))
|
||||
}
|
||||
|
||||
fn parse_compare(op: &str) -> Result<crate::mir::types::CompareOp, String> {
|
||||
use crate::mir::types::CompareOp;
|
||||
Ok(match op {
|
||||
"==" => CompareOp::Eq,
|
||||
"!=" => CompareOp::Ne,
|
||||
"<" => CompareOp::Lt,
|
||||
"<=" => CompareOp::Le,
|
||||
">" => CompareOp::Gt,
|
||||
">=" => CompareOp::Ge,
|
||||
s => return Err(format!("unsupported compare op '{}'", s)),
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user