feat(joinir): Phase 248 - Normalized JoinIR infrastructure
Major refactoring of JoinIR normalization pipeline: Key changes: - Structured→Normalized→MIR(direct) pipeline established - ShapeGuard enhanced with Pattern2 loop validation - dev_env.rs: New development fixtures and env control - fixtures.rs: jsonparser_parse_number_real fixture - normalized_bridge/direct.rs: Direct MIR generation from Normalized - pattern2_step_schedule.rs: Extracted step scheduling logic Files changed: - normalized.rs: Enhanced NormalizedJoinModule with DevEnv support - shape_guard.rs: Pattern2-specific validation (+300 lines) - normalized_bridge.rs: Unified bridge with direct path - loop_with_break_minimal.rs: Integrated step scheduling - Deleted: step_schedule.rs (moved to pattern2_step_schedule.rs) New files: - param_guess.rs: Loop parameter inference - pattern2_step_schedule.rs: Step scheduling for Pattern2 - phase43-norm-canon-p2-mid.md: Design doc Tests: 937/937 PASS (+6 from baseline 931) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -1,38 +1,111 @@
|
||||
#![cfg(feature = "normalized_dev")]
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::{Mutex, MutexGuard};
|
||||
|
||||
/// RAII guard for normalized_dev env toggling (NYASH_JOINIR_NORMALIZED_DEV_RUN).
|
||||
/// 汚染防止のため tests/runner の両方で再利用できるようにここに置く。
|
||||
/// ネストを許可し、最初の呼び出し時の状態だけを保存・復元する。
|
||||
pub struct NormalizedDevEnvGuard {
|
||||
_lock: std::sync::MutexGuard<'static, ()>,
|
||||
prev: Option<String>,
|
||||
active: bool,
|
||||
}
|
||||
|
||||
static NORMALIZED_ENV_LOCK: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
|
||||
#[derive(Default)]
|
||||
struct EnvState {
|
||||
stack: Vec<Option<String>>,
|
||||
}
|
||||
|
||||
static NORMALIZED_ENV_STATE: Lazy<Mutex<EnvState>> = Lazy::new(|| Mutex::new(EnvState::default()));
|
||||
static NORMALIZED_TEST_LOCK: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
|
||||
|
||||
impl NormalizedDevEnvGuard {
|
||||
pub fn new(enabled: bool) -> Self {
|
||||
let lock = NORMALIZED_ENV_LOCK
|
||||
let mut state = NORMALIZED_ENV_STATE
|
||||
.lock()
|
||||
.expect("normalized env mutex poisoned");
|
||||
|
||||
// Save current value before overriding.
|
||||
let prev = std::env::var("NYASH_JOINIR_NORMALIZED_DEV_RUN").ok();
|
||||
state.stack.push(prev);
|
||||
|
||||
if enabled {
|
||||
std::env::set_var("NYASH_JOINIR_NORMALIZED_DEV_RUN", "1");
|
||||
} else {
|
||||
std::env::remove_var("NYASH_JOINIR_NORMALIZED_DEV_RUN");
|
||||
}
|
||||
Self { _lock: lock, prev }
|
||||
|
||||
Self { active: true }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for NormalizedDevEnvGuard {
|
||||
fn drop(&mut self) {
|
||||
if let Some(prev) = &self.prev {
|
||||
std::env::set_var("NYASH_JOINIR_NORMALIZED_DEV_RUN", prev);
|
||||
} else {
|
||||
std::env::remove_var("NYASH_JOINIR_NORMALIZED_DEV_RUN");
|
||||
if !self.active {
|
||||
return;
|
||||
}
|
||||
let mut state = NORMALIZED_ENV_STATE
|
||||
.lock()
|
||||
.expect("normalized env mutex poisoned");
|
||||
if let Some(prev) = state.stack.pop() {
|
||||
if let Some(prev) = prev {
|
||||
std::env::set_var("NYASH_JOINIR_NORMALIZED_DEV_RUN", prev);
|
||||
} else {
|
||||
std::env::remove_var("NYASH_JOINIR_NORMALIZED_DEV_RUN");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// normalized_dev feature + env の ON/OFF をまとめた判定。
|
||||
pub fn normalized_dev_enabled() -> bool {
|
||||
crate::config::env::normalized_dev_enabled()
|
||||
}
|
||||
|
||||
/// normalized_dev かつ test/debug ログが有効なときだけ true。
|
||||
pub fn normalized_dev_logs_enabled() -> bool {
|
||||
crate::config::env::normalized_dev_enabled() && crate::config::env::joinir_test_debug_enabled()
|
||||
}
|
||||
|
||||
/// テスト用コンテキスト:env を ON にしつつロックで並列汚染を防ぐ。
|
||||
pub struct NormalizedTestContext<'a> {
|
||||
_lock: MutexGuard<'a, ()>,
|
||||
_env_guard: NormalizedDevEnvGuard,
|
||||
}
|
||||
|
||||
impl<'a> NormalizedTestContext<'a> {
|
||||
fn new(lock: MutexGuard<'a, ()>) -> Self {
|
||||
let env_guard = NormalizedDevEnvGuard::new(true);
|
||||
NormalizedTestContext {
|
||||
_lock: lock,
|
||||
_env_guard: env_guard,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// テストで使う共通ガード。
|
||||
pub fn test_ctx() -> NormalizedTestContext<'static> {
|
||||
let lock = NORMALIZED_TEST_LOCK
|
||||
.lock()
|
||||
.unwrap_or_else(|e| e.into_inner());
|
||||
NormalizedTestContext::new(lock)
|
||||
}
|
||||
|
||||
/// 簡易ラッパー:クロージャを normalized_dev ON で実行。
|
||||
pub fn with_dev_env<F, R>(f: F) -> R
|
||||
where
|
||||
F: FnOnce() -> R,
|
||||
{
|
||||
let _ctx = test_ctx();
|
||||
f()
|
||||
}
|
||||
|
||||
/// env が既に ON のときはそのまま、OFF のときだけ with_dev_env を噛ませる。
|
||||
pub fn with_dev_env_if_unset<F, R>(f: F) -> R
|
||||
where
|
||||
F: FnOnce() -> R,
|
||||
{
|
||||
if normalized_dev_enabled() {
|
||||
f()
|
||||
} else {
|
||||
with_dev_env(f)
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ use crate::mir::join_ir::lowering::loop_update_analyzer::UpdateExpr;
|
||||
use crate::mir::join_ir::lowering::loop_with_break_minimal::lower_loop_with_break_minimal;
|
||||
use crate::mir::join_ir::JoinModule;
|
||||
use crate::mir::{BasicBlockId, ValueId};
|
||||
use crate::{config::env::joinir_dev_enabled, config::env::joinir_test_debug_enabled};
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
/// Structured Pattern2 (joinir_min_loop 相当) をテスト用に生成するヘルパー。
|
||||
@ -113,6 +114,54 @@ pub fn build_jsonparser_skip_ws_structured_for_normalized_dev() -> JoinModule {
|
||||
lowerer.lower_program_json(&program_json)
|
||||
}
|
||||
|
||||
/// JsonParser _skip_whitespace 本体相当の P2 ループを Structured で組み立てるヘルパー。
|
||||
///
|
||||
/// Fixture: docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_skip_ws_real.program.json
|
||||
pub fn build_jsonparser_skip_ws_real_structured_for_normalized_dev() -> JoinModule {
|
||||
const FIXTURE: &str = include_str!(
|
||||
"../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_skip_ws_real.program.json"
|
||||
);
|
||||
|
||||
let program_json: serde_json::Value =
|
||||
serde_json::from_str(FIXTURE).expect("jsonparser skip_ws real fixture should be valid JSON");
|
||||
|
||||
let mut lowerer = AstToJoinIrLowerer::new();
|
||||
let module = lowerer.lower_program_json(&program_json);
|
||||
|
||||
if joinir_dev_enabled() && joinir_test_debug_enabled() {
|
||||
eprintln!(
|
||||
"[joinir/normalized-dev] jsonparser_skip_ws_real structured module: {:#?}",
|
||||
module
|
||||
);
|
||||
}
|
||||
|
||||
module
|
||||
}
|
||||
|
||||
/// JsonParser _parse_number 本体相当の P2 ループを Structured で組み立てるヘルパー。
|
||||
///
|
||||
/// Fixture: docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_parse_number_real.program.json
|
||||
pub fn build_jsonparser_parse_number_real_structured_for_normalized_dev() -> JoinModule {
|
||||
const FIXTURE: &str = include_str!(
|
||||
"../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_parse_number_real.program.json"
|
||||
);
|
||||
|
||||
let program_json: serde_json::Value =
|
||||
serde_json::from_str(FIXTURE).expect("jsonparser parse_number real fixture should be valid JSON");
|
||||
|
||||
let mut lowerer = AstToJoinIrLowerer::new();
|
||||
let module = lowerer.lower_program_json(&program_json);
|
||||
|
||||
if joinir_dev_enabled() && joinir_test_debug_enabled() {
|
||||
eprintln!(
|
||||
"[joinir/normalized-dev] jsonparser_parse_number_real structured module: {:#?}",
|
||||
module
|
||||
);
|
||||
}
|
||||
|
||||
module
|
||||
}
|
||||
|
||||
/// JsonParser _atoi 相当のミニ P2 ループを Structured で組み立てるヘルパー。
|
||||
///
|
||||
/// Fixture: docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_atoi_mini.program.json
|
||||
@ -127,12 +176,48 @@ pub fn build_jsonparser_atoi_structured_for_normalized_dev() -> JoinModule {
|
||||
let mut lowerer = AstToJoinIrLowerer::new();
|
||||
let module = lowerer.lower_program_json(&program_json);
|
||||
|
||||
if std::env::var("JOINIR_TEST_DEBUG").is_ok() {
|
||||
if joinir_dev_enabled() && joinir_test_debug_enabled() {
|
||||
eprintln!(
|
||||
"[joinir/normalized-dev] jsonparser_atoi_mini structured module: {:#?}",
|
||||
"[joinir/normalized-dev] jsonparser_atoi_mini structured module: {:#?}",
|
||||
module
|
||||
);
|
||||
}
|
||||
|
||||
module
|
||||
}
|
||||
|
||||
/// JsonParser _atoi 本体相当の P2 ループを Structured で組み立てるヘルパー。
|
||||
///
|
||||
/// Fixture: docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_atoi_real.program.json
|
||||
pub fn build_jsonparser_atoi_real_structured_for_normalized_dev() -> JoinModule {
|
||||
const FIXTURE: &str = include_str!(
|
||||
"../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_atoi_real.program.json"
|
||||
);
|
||||
|
||||
let program_json: serde_json::Value =
|
||||
serde_json::from_str(FIXTURE).expect("jsonparser atoi real fixture should be valid JSON");
|
||||
|
||||
let mut lowerer = AstToJoinIrLowerer::new();
|
||||
let module = lowerer.lower_program_json(&program_json);
|
||||
|
||||
if joinir_dev_enabled() && joinir_test_debug_enabled() {
|
||||
eprintln!(
|
||||
"[joinir/normalized-dev] jsonparser_atoi_real structured module: {:#?}",
|
||||
module
|
||||
);
|
||||
}
|
||||
|
||||
module
|
||||
}
|
||||
|
||||
/// まとめて import したいとき用のプレリュード。
|
||||
pub mod prelude {
|
||||
pub use super::{
|
||||
build_jsonparser_atoi_real_structured_for_normalized_dev,
|
||||
build_jsonparser_atoi_structured_for_normalized_dev,
|
||||
build_jsonparser_parse_number_real_structured_for_normalized_dev,
|
||||
build_jsonparser_skip_ws_real_structured_for_normalized_dev,
|
||||
build_jsonparser_skip_ws_structured_for_normalized_dev,
|
||||
build_pattern2_break_fixture_structured, build_pattern2_minimal_structured,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
#![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)]
|
||||
@ -7,105 +9,261 @@ pub(crate) enum NormalizedDevShape {
|
||||
Pattern1Mini,
|
||||
Pattern2Mini,
|
||||
JsonparserSkipWsMini,
|
||||
JsonparserSkipWsReal,
|
||||
JsonparserAtoiMini,
|
||||
JsonparserAtoiReal,
|
||||
JsonparserParseNumberReal,
|
||||
}
|
||||
|
||||
/// 直接 Normalized→MIR ブリッジで扱う shape を返す(dev 限定)。
|
||||
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<NormalizedDevShape> {
|
||||
supported_shapes(module)
|
||||
let shapes = detect_shapes(module);
|
||||
log_shapes("direct", &shapes);
|
||||
shapes
|
||||
}
|
||||
|
||||
/// Structured→Normalized の対象 shape(dev 限定)。
|
||||
pub(crate) fn supported_shapes(module: &JoinModule) -> Vec<NormalizedDevShape> {
|
||||
let mut shapes = Vec::new();
|
||||
if is_jsonparser_atoi_mini(module) {
|
||||
shapes.push(NormalizedDevShape::JsonparserAtoiMini);
|
||||
}
|
||||
if is_jsonparser_skip_ws_mini(module) {
|
||||
shapes.push(NormalizedDevShape::JsonparserSkipWsMini);
|
||||
}
|
||||
if is_pattern2_mini(module) {
|
||||
shapes.push(NormalizedDevShape::Pattern2Mini);
|
||||
}
|
||||
if is_pattern1_mini(module) {
|
||||
shapes.push(NormalizedDevShape::Pattern1Mini);
|
||||
}
|
||||
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<NormalizedDevShape> {
|
||||
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 {
|
||||
!direct_shapes(module).is_empty()
|
||||
!detect_shapes(module).is_empty()
|
||||
}
|
||||
|
||||
pub(crate) fn is_pattern1_mini(module: &JoinModule) -> bool {
|
||||
module.is_structured() && find_loop_step(module).is_some()
|
||||
}
|
||||
fn detect_shapes(module: &JoinModule) -> Vec<NormalizedDevShape> {
|
||||
let mut shapes: Vec<_> = SHAPE_DETECTORS
|
||||
.iter()
|
||||
.filter_map(|(shape, detector)| if detector(module) { Some(*shape) } else { None })
|
||||
.collect();
|
||||
|
||||
pub(crate) 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;
|
||||
// Pattern1 は「最小の後方互換」なので、より具体的な shape が見つかった場合は外しておく。
|
||||
if shapes.len() > 1 {
|
||||
shapes.retain(|s| *s != NormalizedDevShape::Pattern1Mini);
|
||||
}
|
||||
|
||||
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
|
||||
shapes
|
||||
}
|
||||
|
||||
pub(crate) fn is_jsonparser_skip_ws_mini(module: &JoinModule) -> bool {
|
||||
is_pattern2_mini(module)
|
||||
&& module
|
||||
// --- 判定ロジック(共通) ---
|
||||
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()
|
||||
.any(|f| f.name == "jsonparser_skip_ws_mini")
|
||||
}
|
||||
|
||||
pub(crate) fn is_jsonparser_atoi_mini(module: &JoinModule) -> bool {
|
||||
if !module.is_structured() || module.functions.len() != 3 {
|
||||
return false;
|
||||
.find(|f| f.name == "loop_step")
|
||||
.or_else(|| module.functions.get(&JoinFuncId::new(1)))
|
||||
}
|
||||
let loop_func = match find_loop_step(module) {
|
||||
Some(f) => f,
|
||||
None => return false,
|
||||
};
|
||||
if !(3..=8).contains(&loop_func.params.len()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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.contains("atoi"))
|
||||
}
|
||||
|
||||
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)))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user