#![cfg(feature = "normalized_dev")] use crate::config::env::joinir_dev_enabled; use crate::mir::join_ir::normalized::dev_env; use crate::mir::join_ir::{JoinFuncId, JoinFunction, JoinInst, JoinModule}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum NormalizedDevShape { Pattern1Mini, Pattern2Mini, JsonparserSkipWsMini, JsonparserSkipWsReal, JsonparserAtoiMini, JsonparserAtoiReal, JsonparserParseNumberReal, } type Detector = fn(&JoinModule) -> bool; const SHAPE_DETECTORS: &[(NormalizedDevShape, Detector)] = &[ (NormalizedDevShape::Pattern1Mini, detectors::is_pattern1_mini), (NormalizedDevShape::Pattern2Mini, detectors::is_pattern2_mini), ( NormalizedDevShape::JsonparserSkipWsMini, detectors::is_jsonparser_skip_ws_mini, ), ( NormalizedDevShape::JsonparserSkipWsReal, detectors::is_jsonparser_skip_ws_real, ), ( NormalizedDevShape::JsonparserAtoiMini, detectors::is_jsonparser_atoi_mini, ), ( NormalizedDevShape::JsonparserAtoiReal, detectors::is_jsonparser_atoi_real, ), ( NormalizedDevShape::JsonparserParseNumberReal, detectors::is_jsonparser_parse_number_real, ), ]; /// direct ブリッジで扱う shape(dev 限定)。 pub(crate) fn direct_shapes(module: &JoinModule) -> Vec { let shapes = detect_shapes(module); log_shapes("direct", &shapes); shapes } /// Structured→Normalized の対象 shape(dev 限定)。 pub(crate) fn supported_shapes(module: &JoinModule) -> Vec { let shapes = detect_shapes(module); log_shapes("roundtrip", &shapes); shapes } /// canonical(常時 Normalized 経路を通す)対象。 /// Phase 41: P2 コアセット(P2 mini + JP skip_ws mini/real + JP atoi mini)。 pub(crate) fn canonical_shapes(module: &JoinModule) -> Vec { let shapes: Vec<_> = detect_shapes(module) .into_iter() .filter(|s| { matches!( s, NormalizedDevShape::Pattern2Mini | NormalizedDevShape::JsonparserSkipWsMini | NormalizedDevShape::JsonparserSkipWsReal | NormalizedDevShape::JsonparserAtoiMini ) }) .collect(); log_shapes("canonical", &shapes); shapes } #[allow(dead_code)] pub(crate) fn is_direct_supported(module: &JoinModule) -> bool { !detect_shapes(module).is_empty() } fn detect_shapes(module: &JoinModule) -> Vec { let mut shapes: Vec<_> = SHAPE_DETECTORS .iter() .filter_map(|(shape, detector)| if detector(module) { Some(*shape) } else { None }) .collect(); // Pattern1 は「最小の後方互換」なので、より具体的な shape が見つかった場合は外しておく。 if shapes.len() > 1 { shapes.retain(|s| *s != NormalizedDevShape::Pattern1Mini); } shapes } // --- 判定ロジック(共通) --- mod detectors { use super::*; pub(super) fn is_pattern1_mini(module: &JoinModule) -> bool { module.is_structured() && find_loop_step(module).is_some() } pub(super) fn is_pattern2_mini(module: &JoinModule) -> bool { if !module.is_structured() || module.functions.len() != 3 { return false; } let loop_func = match find_loop_step(module) { Some(f) => f, None => return false, }; if !(1..=3).contains(&loop_func.params.len()) { return false; } let has_cond_jump = loop_func .body .iter() .any(|inst| matches!(inst, JoinInst::Jump { cond: Some(_), .. })); let has_tail_call = loop_func .body .iter() .any(|inst| matches!(inst, JoinInst::Call { k_next: None, .. })); has_cond_jump && has_tail_call } pub(super) fn is_jsonparser_skip_ws_mini(module: &JoinModule) -> bool { is_pattern2_mini(module) && module .functions .values() .any(|f| f.name == "jsonparser_skip_ws_mini") } pub(crate) fn is_jsonparser_skip_ws_real(module: &JoinModule) -> bool { if !module.is_structured() || module.functions.len() != 3 { return false; } let loop_func = match find_loop_step(module) { Some(f) => f, None => return false, }; if !(2..=6).contains(&loop_func.params.len()) { return false; } let has_cond_jump = loop_func .body .iter() .any(|inst| matches!(inst, JoinInst::Jump { cond: Some(_), .. })); let has_tail_call = loop_func .body .iter() .any(|inst| matches!(inst, JoinInst::Call { k_next: None, .. })); has_cond_jump && has_tail_call && module .functions .values() .any(|f| f.name == "jsonparser_skip_ws_real") } pub(crate) fn is_jsonparser_atoi_mini(module: &JoinModule) -> bool { if !module.is_structured() || module.functions.len() != 3 { return false; } let loop_func = match find_loop_step(module) { Some(f) => f, None => return false, }; if !(3..=8).contains(&loop_func.params.len()) { return false; } let has_cond_jump = loop_func .body .iter() .any(|inst| matches!(inst, JoinInst::Jump { cond: Some(_), .. })); let has_tail_call = loop_func .body .iter() .any(|inst| matches!(inst, JoinInst::Call { k_next: None, .. })); has_cond_jump && has_tail_call && module .functions .values() .any(|f| f.name == "jsonparser_atoi_mini") } pub(crate) fn is_jsonparser_atoi_real(module: &JoinModule) -> bool { if !module.is_structured() || module.functions.len() != 3 { return false; } let loop_func = match find_loop_step(module) { Some(f) => f, None => return false, }; if !(3..=10).contains(&loop_func.params.len()) { return false; } let has_cond_jump = loop_func .body .iter() .any(|inst| matches!(inst, JoinInst::Jump { cond: Some(_), .. })); let has_tail_call = loop_func .body .iter() .any(|inst| matches!(inst, JoinInst::Call { k_next: None, .. })); has_cond_jump && has_tail_call && module .functions .values() .any(|f| f.name == "jsonparser_atoi_real") } pub(crate) fn is_jsonparser_parse_number_real(module: &JoinModule) -> bool { if !module.is_structured() || module.functions.len() != 3 { return false; } let loop_func = match find_loop_step(module) { Some(f) => f, None => return false, }; if !(3..=12).contains(&loop_func.params.len()) { return false; } let has_cond_jump = loop_func .body .iter() .any(|inst| matches!(inst, JoinInst::Jump { cond: Some(_), .. })); let has_tail_call = loop_func .body .iter() .any(|inst| matches!(inst, JoinInst::Call { k_next: None, .. })); has_cond_jump && has_tail_call && module .functions .values() .any(|f| f.name == "jsonparser_parse_number_real") } pub(super) fn find_loop_step(module: &JoinModule) -> Option<&JoinFunction> { module .functions .values() .find(|f| f.name == "loop_step") .or_else(|| module.functions.get(&JoinFuncId::new(1))) } } fn log_shapes(tag: &str, shapes: &[NormalizedDevShape]) { if shapes.is_empty() { return; } if dev_env::normalized_dev_logs_enabled() && joinir_dev_enabled() { eprintln!("[joinir/normalized-dev/shape] {}: {:?}", tag, shapes); } }