Files
hakorune/src/runner/mir_json_v0.rs
nyash-codex f9d100ce01 chore: Phase 25.1 完了 - LoopForm v2/Stage1 CLI/環境変数削減 + Phase 26-D からの変更
Phase 25.1 完了成果:
-  LoopForm v2 テスト・ドキュメント・コメント完備
  - 4ケース(A/B/C/D)完全テストカバレッジ
  - 最小再現ケース作成(SSAバグ調査用)
  - SSOT文書作成(loopform_ssot.md)
  - 全ソースに [LoopForm] コメントタグ追加

-  Stage-1 CLI デバッグ環境構築
  - stage1_cli.hako 実装
  - stage1_bridge.rs ブリッジ実装
  - デバッグツール作成(stage1_debug.sh/stage1_minimal.sh)
  - アーキテクチャ改善提案文書

-  環境変数削減計画策定
  - 25変数の完全調査・分類
  - 6段階削減ロードマップ(25→5、80%削減)
  - 即時削除可能変数特定(NYASH_CONFIG/NYASH_DEBUG)

Phase 26-D からの累積変更:
- PHI実装改善(ExitPhiBuilder/HeaderPhiBuilder等)
- MIRビルダーリファクタリング
- 型伝播・最適化パス改善
- その他約300ファイルの累積変更

🎯 技術的成果:
- SSAバグ根本原因特定(条件分岐内loop変数変更)
- Region+next_iパターン適用完了(UsingCollectorBox等)
- LoopFormパターン文書化・テスト化完了
- セルフホスティング基盤強化

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: ChatGPT <noreply@openai.com>
Co-Authored-By: Task Assistant <task@anthropic.com>
2025-11-21 06:25:17 +09:00

313 lines
14 KiB
Rust

use super::mir_json::common as mirjson_common;
use crate::mir::{
function::{FunctionSignature, MirFunction, MirModule},
BasicBlock, BasicBlockId, ConstValue, EffectMask, MirInstruction, MirType, ValueId,
};
use serde_json::Value;
/// 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);
}
"binop" => {
let dst = require_u64(inst, "dst", "binop dst")? as u32;
let lhs = require_u64(inst, "lhs", "binop lhs")? as u32;
let rhs = require_u64(inst, "rhs", "binop rhs")? as u32;
let operation = inst
.get("operation")
.and_then(Value::as_str)
.ok_or_else(|| "binop missing operation".to_string())?;
let bop = parse_binop(operation)?;
block_ref.add_instruction(MirInstruction::BinOp {
dst: ValueId::new(dst),
op: bop,
lhs: ValueId::new(lhs),
rhs: ValueId::new(rhs),
});
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;
}
}
"newbox" => {
let dst = require_u64(inst, "dst", "newbox dst")? as u32;
let ty = inst
.get("type")
.and_then(Value::as_str)
.ok_or_else(|| "newbox missing type".to_string())?
.to_string();
let args_v = inst
.get("args")
.and_then(Value::as_array)
.cloned()
.unwrap_or_default();
let mut args: Vec<ValueId> = Vec::with_capacity(args_v.len());
for a in args_v {
let id = a
.as_u64()
.ok_or_else(|| "newbox arg must be integer".to_string())?
as u32;
args.push(ValueId::new(id));
}
block_ref.add_instruction(MirInstruction::NewBox {
dst: ValueId::new(dst),
box_type: ty,
args,
});
max_value_id = max_value_id.max(dst + 1);
}
"boxcall" => {
// { op:"boxcall", box:<vid>, method:"name", args:[vid...], dst?:<vid> }
let box_id = require_u64(inst, "box", "boxcall box")? as u32;
let method = inst
.get("method")
.and_then(Value::as_str)
.ok_or_else(|| "boxcall missing method".to_string())?
.to_string();
let dst_opt = inst
.get("dst")
.and_then(Value::as_u64)
.map(|v| ValueId::new(v as u32));
let args_v = inst
.get("args")
.and_then(Value::as_array)
.cloned()
.unwrap_or_default();
let mut args: Vec<ValueId> = Vec::with_capacity(args_v.len());
for a in args_v {
let id = a
.as_u64()
.ok_or_else(|| "boxcall arg must be integer".to_string())?
as u32;
args.push(ValueId::new(id));
}
block_ref.add_instruction(MirInstruction::BoxCall {
dst: dst_opt,
box_val: ValueId::new(box_id),
method,
method_id: None,
args,
effects: EffectMask::READ,
});
if let Some(dv) = dst_opt {
max_value_id = max_value_id.max(dv.as_u32() + 1);
}
}
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)),
})
}
fn parse_binop(op: &str) -> Result<crate::mir::types::BinaryOp, String> {
use crate::mir::types::BinaryOp;
Ok(match op {
"+" => BinaryOp::Add,
"-" => BinaryOp::Sub,
"*" => BinaryOp::Mul,
"/" => BinaryOp::Div,
"%" => BinaryOp::Mod,
s => return Err(format!("unsupported binary op '{}'", s)),
})
}