Add dev normalized→MIR bridge for P1/P2 mini and JP _atoi

This commit is contained in:
nyash-codex
2025-12-11 22:12:46 +09:00
parent af6f95cd4b
commit a4756f3ce1
13 changed files with 539 additions and 94 deletions

View File

@ -10,7 +10,7 @@ use crate::mir::join_ir::{
JoinModule, MirLikeInst, UnaryOp,
};
use crate::mir::ValueId;
use std::collections::HashSet;
use std::collections::{HashMap, HashSet};
#[cfg(feature = "normalized_dev")]
use std::panic::{catch_unwind, AssertUnwindSafe};
@ -90,6 +90,7 @@ pub enum JpOp {
BinOp(BinOpKind),
Unary(UnaryOp),
Compare(CompareOp),
BoxCall { box_name: String, method: String },
}
/// Normalized JoinIR モジュール(テスト専用)。
@ -180,7 +181,10 @@ pub fn normalized_pattern1_to_structured(norm: &NormalizedModule) -> JoinModule
let mut module = JoinModule::new();
for (jp_id, jp_fn) in &norm.functions {
let params: Vec<ValueId> = env_layout
let params: Vec<ValueId> = jp_fn
.env_layout
.and_then(|id| norm.env_layouts.iter().find(|layout| layout.id == id))
.unwrap_or(env_layout)
.fields
.iter()
.enumerate()
@ -196,6 +200,14 @@ pub fn normalized_pattern1_to_structured(norm: &NormalizedModule) -> JoinModule
dst: *dst,
value: v.clone(),
})),
JpOp::BoxCall { box_name, method } => {
func.body.push(JoinInst::Compute(MirLikeInst::BoxCall {
dst: Some(*dst),
box_name: box_name.clone(),
method: method.clone(),
args: args.clone(),
}))
}
JpOp::BinOp(op) => func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
dst: *dst,
op: *op,
@ -282,9 +294,20 @@ pub fn normalize_pattern2_minimal(structured: &JoinModule) -> NormalizedModule {
"normalize_pattern2_minimal: expected 3 functions (entry/loop_step/k_exit) but got {}",
func_count
);
let param_max = {
let mut max = 3;
#[cfg(feature = "normalized_dev")]
{
if shape_guard::is_jsonparser_atoi_mini(structured) {
max = 8;
}
}
max
};
assert!(
(1..=3).contains(&loop_func.params.len()),
"normalize_pattern2_minimal: expected 1..=3 params (loop var + optional acc + optional host)"
(1..=param_max).contains(&loop_func.params.len()),
"normalize_pattern2_minimal: expected 1..={} params (loop var + carriers + optional host)",
param_max
);
let jump_conds = loop_func
@ -302,27 +325,28 @@ pub fn normalize_pattern2_minimal(structured: &JoinModule) -> NormalizedModule {
"normalize_pattern2_minimal: expected at least one conditional jump and one tail call"
);
let env_layout = EnvLayout {
id: 0,
fields: loop_func
.params
.iter()
.enumerate()
.map(|(idx, vid)| EnvField {
name: format!("field{}", idx),
ty: None,
value_id: Some(*vid),
})
.collect(),
};
let mut functions = BTreeMap::new();
let mut env_layouts = Vec::new();
for (fid, func) in &structured.functions {
let env_layout_id = if func.params.is_empty() {
None
} else {
Some(env_layout.id)
let id = env_layouts.len() as u32;
env_layouts.push(EnvLayout {
id,
fields: func
.params
.iter()
.enumerate()
.map(|(idx, vid)| EnvField {
name: format!("field{}", idx),
ty: None,
value_id: Some(*vid),
})
.collect(),
});
Some(id)
};
let mut body = Vec::new();
@ -333,6 +357,23 @@ pub fn normalize_pattern2_minimal(structured: &JoinModule) -> NormalizedModule {
op: JpOp::Const(value.clone()),
args: vec![],
}),
JoinInst::Compute(MirLikeInst::BoxCall {
dst,
box_name,
method,
args,
}) => {
if let Some(dst) = dst {
body.push(JpInst::Let {
dst: *dst,
op: JpOp::BoxCall {
box_name: box_name.clone(),
method: method.clone(),
},
args: args.clone(),
})
}
}
JoinInst::Compute(MirLikeInst::BinOp { dst, op, lhs, rhs }) => {
body.push(JpInst::Let {
dst: *dst,
@ -377,6 +418,25 @@ pub fn normalize_pattern2_minimal(structured: &JoinModule) -> NormalizedModule {
});
}
}
JoinInst::MethodCall {
dst,
receiver,
method,
args,
..
} => {
let mut call_args = Vec::with_capacity(args.len() + 1);
call_args.push(*receiver);
call_args.extend(args.iter().copied());
body.push(JpInst::Let {
dst: *dst,
op: JpOp::BoxCall {
box_name: "unknown".to_string(),
method: method.clone(),
},
args: call_args,
});
}
_ => {
// Ret / other instructions are ignored in this minimal prototype
}
@ -398,14 +458,14 @@ pub fn normalize_pattern2_minimal(structured: &JoinModule) -> NormalizedModule {
let norm = NormalizedModule {
functions,
entry: structured.entry.map(|e| JpFuncId(e.0)),
env_layouts: vec![env_layout],
env_layouts,
phase: JoinIrPhase::Normalized,
structured_backup: Some(structured.clone()),
};
#[cfg(feature = "normalized_dev")]
{
verify_normalized_pattern2(&norm).expect("normalized Pattern2 verifier");
verify_normalized_pattern2(&norm, param_max).expect("normalized Pattern2 verifier");
}
norm
@ -417,14 +477,12 @@ pub fn normalized_pattern2_to_structured(norm: &NormalizedModule) -> JoinModule
return backup;
}
let env_layout = norm.env_layouts.get(0);
let mut module = JoinModule::new();
for (jp_id, jp_fn) in &norm.functions {
let params: Vec<ValueId> = jp_fn
.env_layout
.and_then(|id| env_layout.filter(|layout| layout.id == id))
.and_then(|id| norm.env_layouts.iter().find(|layout| layout.id == id))
.map(|layout| {
layout
.fields
@ -444,6 +502,14 @@ pub fn normalized_pattern2_to_structured(norm: &NormalizedModule) -> JoinModule
dst: *dst,
value: v.clone(),
})),
JpOp::BoxCall { box_name, method } => {
func.body.push(JoinInst::Compute(MirLikeInst::BoxCall {
dst: Some(*dst),
box_name: box_name.clone(),
method: method.clone(),
args: args.clone(),
}))
}
JpOp::BinOp(op) => func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
dst: *dst,
op: *op,
@ -505,23 +571,32 @@ pub fn normalized_pattern2_to_structured(norm: &NormalizedModule) -> JoinModule
}
#[cfg(feature = "normalized_dev")]
fn verify_normalized_pattern2(module: &NormalizedModule) -> Result<(), String> {
fn verify_normalized_pattern2(
module: &NormalizedModule,
max_env_fields: usize,
) -> Result<(), String> {
if module.phase != JoinIrPhase::Normalized {
return Err("Normalized verifier (Pattern2): phase must be Normalized".to_string());
}
let field_count = module.env_layouts.get(0).map(|e| e.fields.len());
if let Some(field_count) = field_count {
if !(1..=3).contains(&field_count) {
let mut layout_sizes: HashMap<u32, usize> = HashMap::new();
for layout in &module.env_layouts {
let size = layout.fields.len();
if !(1..=max_env_fields).contains(&size) {
return Err(format!(
"Normalized Pattern2 expects 1..=3 env fields, got {}",
field_count
"Normalized Pattern2 expects 1..={} env fields, got {}",
max_env_fields, size
));
}
layout_sizes.insert(layout.id, size);
}
for func in module.functions.values() {
let expected_env_len = func
.env_layout
.and_then(|id| layout_sizes.get(&id))
.copied();
for inst in &func.body {
match inst {
JpInst::Let { .. }
@ -536,17 +611,11 @@ fn verify_normalized_pattern2(module: &NormalizedModule) -> Result<(), String> {
JpInst::TailCallFn { env, .. }
| JpInst::TailCallKont { env, .. }
| JpInst::If { env, .. } => {
if env.is_empty() {
return Err("Normalized Pattern2 env must not be empty".to_string());
}
if let Some(expected) = field_count {
if env.len() > expected {
return Err(format!(
"Normalized Pattern2 env size exceeds layout: env={}, layout={}",
env.len(),
expected
));
if let Some(expected) = expected_env_len {
if env.is_empty() {
return Err("Normalized Pattern2 env must not be empty".to_string());
}
let _ = expected;
}
}
_ => {}
@ -615,6 +684,23 @@ pub fn normalize_pattern1_minimal(structured: &JoinModule) -> NormalizedModule {
op: JpOp::Const(value.clone()),
args: vec![],
}),
JoinInst::Compute(MirLikeInst::BoxCall {
dst,
box_name,
method,
args,
}) => {
if let Some(dst) = dst {
jp_body.push(JpInst::Let {
dst: *dst,
op: JpOp::BoxCall {
box_name: box_name.clone(),
method: method.clone(),
},
args: args.clone(),
})
}
}
JoinInst::Compute(MirLikeInst::BinOp { dst, op, lhs, rhs }) => {
jp_body.push(JpInst::Let {
dst: *dst,
@ -737,12 +823,12 @@ pub(crate) fn normalized_dev_roundtrip_structured(
let norm = normalize_pattern1_minimal(module);
normalized_pattern1_to_structured(&norm)
})),
NormalizedDevShape::Pattern2Mini | NormalizedDevShape::JsonparserSkipWsMini => {
catch_unwind(AssertUnwindSafe(|| {
let norm = normalize_pattern2_minimal(module);
normalized_pattern2_to_structured(&norm)
}))
}
NormalizedDevShape::Pattern2Mini
| NormalizedDevShape::JsonparserSkipWsMini
| NormalizedDevShape::JsonparserAtoiMini => catch_unwind(AssertUnwindSafe(|| {
let norm = normalize_pattern2_minimal(module);
normalized_pattern2_to_structured(&norm)
})),
};
match attempt {