Add dev normalized→MIR bridge for P1/P2 mini and JP _atoi
This commit is contained in:
@ -45,6 +45,10 @@
|
|||||||
- JoinIR→MIR ブリッジの入口を `bridge_joinir_to_mir` に一本化し、normalized_dev スイッチ(feature + env)で Structured→Normalized→Structured の dev roundtrip を切り替える準備を整えた。P1/P2/JP mini の比較テストも VM ブリッジ経路で追加。
|
- JoinIR→MIR ブリッジの入口を `bridge_joinir_to_mir` に一本化し、normalized_dev スイッチ(feature + env)で Structured→Normalized→Structured の dev roundtrip を切り替える準備を整えた。P1/P2/JP mini の比較テストも VM ブリッジ経路で追加。
|
||||||
- Phase 33-NORM-CANON-TEST(dev-only):
|
- Phase 33-NORM-CANON-TEST(dev-only):
|
||||||
- P1/P2(Phase 34 break fixture)/JsonParser skip_ws mini について、normalized_dev ON 時は shape_guard 経由で必ず Normalized roundtrip を通すようブリッジと runner を固めた。normalized_joinir_min.rs の runner/VM 比較テストを拡張し、Normalized が壊れたら dev スイートが必ず赤になるようにした(本番 CLI は従来どおり Structured→MIR)。
|
- P1/P2(Phase 34 break fixture)/JsonParser skip_ws mini について、normalized_dev ON 時は shape_guard 経由で必ず Normalized roundtrip を通すようブリッジと runner を固めた。normalized_joinir_min.rs の runner/VM 比較テストを拡張し、Normalized が壊れたら dev スイートが必ず赤になるようにした(本番 CLI は従来どおり Structured→MIR)。
|
||||||
|
- Phase 34-NORM-ATOI-DEV(dev-only):
|
||||||
|
- JsonParser `_atoi` ミニループ(digit_pos→digit_value + NumberAccumulation)を normalized_dev 経路に載せ、Structured↔Normalized↔Structured の VM 実行結果が一致することをフィクスチャテストで固定。`jsonparser_atoi_mini` を shape_guard で認識し、既定経路は引き続き Structured→MIR のまま。
|
||||||
|
- Phase 35-NORM-BRIDGE-MINI(dev-only):
|
||||||
|
- P1/P2 ミニ + JsonParser skip_ws/atoi ミニを Normalized→MIR 直ブリッジで実行できるようにし、normalized_dev ON 時は Structured→Normalized→MIR(復元なし)経路との比較テストで結果一致を固定。既定経路(Structured→MIR)は不変。
|
||||||
|
|
||||||
### 1. いまコード側で意識しておきたいフォーカス
|
### 1. いまコード側で意識しておきたいフォーカス
|
||||||
|
|
||||||
|
|||||||
@ -1232,3 +1232,13 @@ Normalized JoinIR を 1 段挟むと、開発の手触りがどう変わるか
|
|||||||
- `bridge_joinir_to_mir` / JoinIR runner は `shape_guard` で P1/P2 ミニ + JsonParser skip_ws mini を検知した場合、`normalized_dev_enabled()` が ON なら必ず Structured→Normalized→Structured の dev roundtrip を経由(正規化失敗は dev panic)。未対応形状は静かに Structured 直通。
|
- `bridge_joinir_to_mir` / JoinIR runner は `shape_guard` で P1/P2 ミニ + JsonParser skip_ws mini を検知した場合、`normalized_dev_enabled()` が ON なら必ず Structured→Normalized→Structured の dev roundtrip を経由(正規化失敗は dev panic)。未対応形状は静かに Structured 直通。
|
||||||
- tests/normalized_joinir_min.rs を Phase 33 前提に拡張し、P1/P2/JP mini の runner/VM 比較テストを env ON で実行。Normalized が壊れればこのスイートが必ず赤になる構造にした(feature OFF の CI は従来どおり無関係)。
|
- tests/normalized_joinir_min.rs を Phase 33 前提に拡張し、P1/P2/JP mini の runner/VM 比較テストを env ON で実行。Normalized が壊れればこのスイートが必ず赤になる構造にした(feature OFF の CI は従来どおり無関係)。
|
||||||
- 本番 CLI 挙動は Structured→MIR のまま維持しつつ、Normalized を canonical に昇格させる前段階として dev テストで SSOT 相当の役割を担わせている。
|
- 本番 CLI 挙動は Structured→MIR のまま維持しつつ、Normalized を canonical に昇格させる前段階として dev テストで SSOT 相当の役割を担わせている。
|
||||||
|
|
||||||
|
### 3.12 Phase 34-NORM-ATOI-DEV – JsonParser `_atoi` ミニを dev 正規化経路へ
|
||||||
|
|
||||||
|
- JsonParser `_atoi` の最小 P2 ループ(digit_pos → digit_value + NumberAccumulation)を normalized_dev で Structured→Normalized→Structured に往復させ、VM 実行結果を Structured 直経路と比較するテストを追加。
|
||||||
|
- フィクスチャ `jsonparser_atoi_mini.program.json` を `shape_guard::JsonparserAtoiMini` で検知し、dev roundtrip が必ず通るようにした(正規化失敗は dev panic)。本番 CLI は引き続き Structured→MIR 既定のまま。
|
||||||
|
|
||||||
|
### 3.13 Phase 35-NORM-BRIDGE-MINI – Normalized→MIR 直ブリッジ(P1/P2 ミニ + JP mini/atoi)
|
||||||
|
|
||||||
|
- normalized_dev 有効時に、P1/P2 ミニ・JsonParser skip_ws/atoi ミニを Structured→Normalized→MIR(Structured に戻さない)で実行する dev 専用ブリッジを追加。
|
||||||
|
- `bridge_joinir_to_mir` が shape_guard 対応形状では Normalized→MIR 直経路を使い、従来の Structured→MIR と VM 実行結果が一致することを比較テストで固定(env OFF 時は従来経路のまま)。
|
||||||
|
|||||||
Submodule docs/private updated: 9e000ef563...64597272c7
@ -57,6 +57,22 @@ impl AstToJoinIrLowerer {
|
|||||||
(dst, vec![inst])
|
(dst, vec![inst])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Phase 34-ATOI: String literal 対応
|
||||||
|
"String" => {
|
||||||
|
let value = expr["value"]
|
||||||
|
.as_str()
|
||||||
|
.expect("String literal must have 'value'")
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let dst = ctx.alloc_var();
|
||||||
|
let inst = JoinInst::Compute(crate::mir::join_ir::MirLikeInst::Const {
|
||||||
|
dst,
|
||||||
|
value: ConstValue::String(value),
|
||||||
|
});
|
||||||
|
|
||||||
|
(dst, vec![inst])
|
||||||
|
}
|
||||||
|
|
||||||
// 段階 1: Var 参照対応
|
// 段階 1: Var 参照対応
|
||||||
"Var" => {
|
"Var" => {
|
||||||
let var_name = expr["name"].as_str().expect("Var must have 'name' field");
|
let var_name = expr["name"].as_str().expect("Var must have 'name' field");
|
||||||
@ -70,7 +86,7 @@ impl AstToJoinIrLowerer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Phase 34-6: Method 呼び出し構造の完全実装
|
// Phase 34-6: Method 呼び出し構造の完全実装
|
||||||
"Method" => {
|
"Method" | "MethodCall" => {
|
||||||
// receiver.method(args...) の構造を抽出
|
// receiver.method(args...) の構造を抽出
|
||||||
let receiver_expr = &expr["receiver"];
|
let receiver_expr = &expr["receiver"];
|
||||||
let method_name = expr["method"]
|
let method_name = expr["method"]
|
||||||
|
|||||||
@ -27,6 +27,7 @@ use super::common::{
|
|||||||
use super::{AstToJoinIrLowerer, JoinModule, LoweringError};
|
use super::{AstToJoinIrLowerer, JoinModule, LoweringError};
|
||||||
use crate::mir::join_ir::{JoinFunction, JoinInst};
|
use crate::mir::join_ir::{JoinFunction, JoinInst};
|
||||||
use crate::mir::ValueId;
|
use crate::mir::ValueId;
|
||||||
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
/// Break パターンを JoinModule に変換
|
/// Break パターンを JoinModule に変換
|
||||||
///
|
///
|
||||||
@ -54,9 +55,10 @@ pub fn lower(
|
|||||||
message: "Loop must have 'body' array".to_string(),
|
message: "Loop must have 'body' array".to_string(),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let break_if_stmt = loop_body
|
let (break_if_idx, break_if_stmt) = loop_body
|
||||||
.iter()
|
.iter()
|
||||||
.find(|stmt| {
|
.enumerate()
|
||||||
|
.find(|(_, stmt)| {
|
||||||
stmt["type"].as_str() == Some("If")
|
stmt["type"].as_str() == Some("If")
|
||||||
&& stmt["then"].as_array().map_or(false, |then| {
|
&& stmt["then"].as_array().map_or(false, |then| {
|
||||||
then.iter().any(|s| s["type"].as_str() == Some("Break"))
|
then.iter().any(|s| s["type"].as_str() == Some("Break"))
|
||||||
@ -68,16 +70,25 @@ pub fn lower(
|
|||||||
|
|
||||||
let break_cond_expr = &break_if_stmt["cond"];
|
let break_cond_expr = &break_if_stmt["cond"];
|
||||||
|
|
||||||
|
let (param_order, loop_var_name, acc_name) = compute_param_order(&entry_ctx);
|
||||||
|
let loop_cond_expr = &loop_node["cond"];
|
||||||
|
|
||||||
// 5. entry 関数を生成
|
// 5. entry 関数を生成
|
||||||
let entry_func = create_entry_function_break(&ctx, &parsed, init_insts, &mut entry_ctx);
|
let entry_func =
|
||||||
|
create_entry_function_break(&ctx, &parsed, init_insts, &mut entry_ctx, ¶m_order);
|
||||||
|
|
||||||
// 6. loop_step 関数を生成
|
// 6. loop_step 関数を生成
|
||||||
let loop_step_func = create_loop_step_function_break(
|
let loop_step_func = create_loop_step_function_break(
|
||||||
lowerer,
|
lowerer,
|
||||||
&ctx,
|
&ctx,
|
||||||
&parsed.func_name,
|
&parsed.func_name,
|
||||||
|
loop_cond_expr,
|
||||||
break_cond_expr,
|
break_cond_expr,
|
||||||
loop_body,
|
loop_body,
|
||||||
|
¶m_order,
|
||||||
|
&loop_var_name,
|
||||||
|
&acc_name,
|
||||||
|
break_if_idx,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// 7. k_exit 関数を生成
|
// 7. k_exit 関数を生成
|
||||||
@ -93,18 +104,16 @@ fn create_entry_function_break(
|
|||||||
parsed: &super::common::ParsedProgram,
|
parsed: &super::common::ParsedProgram,
|
||||||
init_insts: Vec<JoinInst>,
|
init_insts: Vec<JoinInst>,
|
||||||
entry_ctx: &mut super::super::context::ExtractCtx,
|
entry_ctx: &mut super::super::context::ExtractCtx,
|
||||||
|
param_order: &[(String, ValueId)],
|
||||||
) -> JoinFunction {
|
) -> JoinFunction {
|
||||||
// i, acc, n を取得
|
let loop_args: Vec<ValueId> = param_order.iter().map(|(_, id)| *id).collect();
|
||||||
let i_init = entry_ctx.get_var("i").expect("i must be initialized");
|
|
||||||
let acc_init = entry_ctx.get_var("acc").expect("acc must be initialized");
|
|
||||||
let n_param = entry_ctx.get_var("n").expect("n must be parameter");
|
|
||||||
|
|
||||||
let loop_result = entry_ctx.alloc_var();
|
let loop_result = entry_ctx.alloc_var();
|
||||||
|
|
||||||
let mut body = init_insts;
|
let mut body = init_insts;
|
||||||
body.push(JoinInst::Call {
|
body.push(JoinInst::Call {
|
||||||
func: ctx.loop_step_id,
|
func: ctx.loop_step_id,
|
||||||
args: vec![i_init, acc_init, n_param],
|
args: loop_args,
|
||||||
k_next: None,
|
k_next: None,
|
||||||
dst: Some(loop_result),
|
dst: Some(loop_result),
|
||||||
});
|
});
|
||||||
@ -128,35 +137,64 @@ fn create_loop_step_function_break(
|
|||||||
lowerer: &mut AstToJoinIrLowerer,
|
lowerer: &mut AstToJoinIrLowerer,
|
||||||
ctx: &super::common::LoopContext,
|
ctx: &super::common::LoopContext,
|
||||||
func_name: &str,
|
func_name: &str,
|
||||||
|
loop_cond_expr: &serde_json::Value,
|
||||||
break_cond_expr: &serde_json::Value,
|
break_cond_expr: &serde_json::Value,
|
||||||
loop_body: &[serde_json::Value],
|
loop_body: &[serde_json::Value],
|
||||||
|
param_order: &[(String, ValueId)],
|
||||||
|
loop_var_name: &str,
|
||||||
|
acc_name: &str,
|
||||||
|
break_if_idx: usize,
|
||||||
) -> Result<JoinFunction, LoweringError> {
|
) -> Result<JoinFunction, LoweringError> {
|
||||||
use super::super::context::ExtractCtx;
|
use super::super::context::ExtractCtx;
|
||||||
|
|
||||||
// step_ctx を作成(Break パターンは me/external_refs なし)
|
let param_names: Vec<String> = param_order.iter().map(|(name, _)| name.clone()).collect();
|
||||||
let step_i = ValueId(0);
|
|
||||||
let step_acc = ValueId(1);
|
|
||||||
let step_n = ValueId(2);
|
|
||||||
|
|
||||||
let mut step_ctx = ExtractCtx::new(3);
|
let mut step_ctx = ExtractCtx::new(param_names.len() as u32);
|
||||||
step_ctx.register_param("i".to_string(), step_i);
|
for (idx, name) in param_names.iter().enumerate() {
|
||||||
step_ctx.register_param("acc".to_string(), step_acc);
|
step_ctx.register_param(name.clone(), ValueId(idx as u32));
|
||||||
step_ctx.register_param("n".to_string(), step_n);
|
}
|
||||||
|
|
||||||
// Break 条件を評価
|
let mut body = Vec::new();
|
||||||
|
|
||||||
|
let (loop_cond_var, loop_cond_insts) =
|
||||||
|
lowerer.extract_value(loop_cond_expr, &mut step_ctx);
|
||||||
|
body.extend(loop_cond_insts);
|
||||||
|
let acc_current = step_ctx
|
||||||
|
.get_var(acc_name)
|
||||||
|
.unwrap_or_else(|| panic!("{} must be initialized", acc_name));
|
||||||
|
let header_exit_flag = step_ctx.alloc_var();
|
||||||
|
body.push(JoinInst::Compute(crate::mir::join_ir::MirLikeInst::UnaryOp {
|
||||||
|
dst: header_exit_flag,
|
||||||
|
op: crate::mir::join_ir::UnaryOp::Not,
|
||||||
|
operand: loop_cond_var,
|
||||||
|
}));
|
||||||
|
body.push(JoinInst::Jump {
|
||||||
|
cont: ctx.k_exit_id.as_cont(),
|
||||||
|
args: vec![acc_current],
|
||||||
|
cond: Some(header_exit_flag),
|
||||||
|
});
|
||||||
|
|
||||||
|
for stmt in loop_body.iter().take(break_if_idx) {
|
||||||
|
if stmt["type"].as_str() == Some("If") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let (insts, _effect) = lowerer.lower_statement(stmt, &mut step_ctx);
|
||||||
|
body.extend(insts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Break 条件を評価(break_if までの body-local を評価したあと)
|
||||||
let (break_cond_var, break_cond_insts) = lowerer.extract_value(break_cond_expr, &mut step_ctx);
|
let (break_cond_var, break_cond_insts) = lowerer.extract_value(break_cond_expr, &mut step_ctx);
|
||||||
|
body.extend(break_cond_insts);
|
||||||
let mut body = break_cond_insts;
|
|
||||||
|
|
||||||
// 早期 return: break_cond が true なら k_exit へ Jump
|
// 早期 return: break_cond が true なら k_exit へ Jump
|
||||||
body.push(JoinInst::Jump {
|
body.push(JoinInst::Jump {
|
||||||
cont: ctx.k_exit_id.as_cont(),
|
cont: ctx.k_exit_id.as_cont(),
|
||||||
args: vec![step_acc],
|
args: vec![acc_current],
|
||||||
cond: Some(break_cond_var),
|
cond: Some(break_cond_var),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Loop body を処理(If + Break はスキップ)
|
// Loop body を処理(If + Break はスキップ)
|
||||||
for body_stmt in loop_body {
|
for body_stmt in loop_body.iter().skip(break_if_idx + 1) {
|
||||||
if body_stmt["type"].as_str() == Some("If") {
|
if body_stmt["type"].as_str() == Some("If") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -166,13 +204,30 @@ fn create_loop_step_function_break(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 再帰呼び出し
|
// 再帰呼び出し
|
||||||
let i_next = step_ctx.get_var("i").expect("i must be updated");
|
let i_next = step_ctx
|
||||||
let acc_next = step_ctx.get_var("acc").expect("acc must be updated");
|
.get_var(loop_var_name)
|
||||||
|
.unwrap_or_else(|| panic!("{} must be updated", loop_var_name));
|
||||||
|
let acc_next = step_ctx
|
||||||
|
.get_var(acc_name)
|
||||||
|
.unwrap_or_else(|| panic!("{} must be updated", acc_name));
|
||||||
|
|
||||||
let recurse_result = step_ctx.alloc_var();
|
let recurse_result = step_ctx.alloc_var();
|
||||||
|
let mut recurse_args = Vec::new();
|
||||||
|
for name in ¶m_names {
|
||||||
|
let arg = if name == loop_var_name {
|
||||||
|
i_next
|
||||||
|
} else if name == acc_name {
|
||||||
|
acc_next
|
||||||
|
} else {
|
||||||
|
step_ctx
|
||||||
|
.get_var(name)
|
||||||
|
.unwrap_or_else(|| panic!("param {} must exist", name))
|
||||||
|
};
|
||||||
|
recurse_args.push(arg);
|
||||||
|
}
|
||||||
body.push(JoinInst::Call {
|
body.push(JoinInst::Call {
|
||||||
func: ctx.loop_step_id,
|
func: ctx.loop_step_id,
|
||||||
args: vec![i_next, acc_next, step_n],
|
args: recurse_args,
|
||||||
k_next: None,
|
k_next: None,
|
||||||
dst: Some(recurse_result),
|
dst: Some(recurse_result),
|
||||||
});
|
});
|
||||||
@ -183,8 +238,55 @@ fn create_loop_step_function_break(
|
|||||||
Ok(JoinFunction {
|
Ok(JoinFunction {
|
||||||
id: ctx.loop_step_id,
|
id: ctx.loop_step_id,
|
||||||
name: format!("{}_loop_step", func_name),
|
name: format!("{}_loop_step", func_name),
|
||||||
params: vec![step_i, step_acc, step_n],
|
params: (0..param_names.len())
|
||||||
|
.map(|i| ValueId(i as u32))
|
||||||
|
.collect(),
|
||||||
body,
|
body,
|
||||||
exit_cont: None,
|
exit_cont: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compute_param_order(
|
||||||
|
entry_ctx: &super::super::context::ExtractCtx,
|
||||||
|
) -> (Vec<(String, ValueId)>, String, String) {
|
||||||
|
let loop_var_name = "i".to_string();
|
||||||
|
let loop_var = entry_ctx
|
||||||
|
.get_var(&loop_var_name)
|
||||||
|
.expect("i must be initialized");
|
||||||
|
|
||||||
|
let (acc_name, acc_var) = if let Some(v) = entry_ctx.get_var("acc") {
|
||||||
|
("acc".to_string(), v)
|
||||||
|
} else if let Some(v) = entry_ctx.get_var("result") {
|
||||||
|
("result".to_string(), v)
|
||||||
|
} else {
|
||||||
|
panic!("acc or result must be initialized");
|
||||||
|
};
|
||||||
|
|
||||||
|
let (len_name, len_var) = if let Some(v) = entry_ctx.get_var("n") {
|
||||||
|
("n".to_string(), v)
|
||||||
|
} else if let Some(v) = entry_ctx.get_var("len") {
|
||||||
|
("len".to_string(), v)
|
||||||
|
} else {
|
||||||
|
panic!("n or len must be provided as parameter");
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut param_order = vec![
|
||||||
|
(loop_var_name.clone(), loop_var),
|
||||||
|
(acc_name.clone(), acc_var),
|
||||||
|
(len_name.clone(), len_var),
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut seen: BTreeSet<String> =
|
||||||
|
[loop_var_name.clone(), acc_name.clone(), len_name.clone()]
|
||||||
|
.into_iter()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for (name, var_id) in &entry_ctx.var_map {
|
||||||
|
if !seen.contains(name) {
|
||||||
|
param_order.push((name.clone(), *var_id));
|
||||||
|
seen.insert(name.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(param_order, loop_var_name, acc_name)
|
||||||
|
}
|
||||||
|
|||||||
@ -53,7 +53,16 @@ enum FunctionRoute {
|
|||||||
|
|
||||||
fn resolve_function_route(func_name: &str) -> Result<FunctionRoute, String> {
|
fn resolve_function_route(func_name: &str) -> Result<FunctionRoute, String> {
|
||||||
const IF_RETURN_NAMES: &[&str] = &["test", "local", "_read_value_from_pair"];
|
const IF_RETURN_NAMES: &[&str] = &["test", "local", "_read_value_from_pair"];
|
||||||
const LOOP_NAMES: &[&str] = &["simple", "filter", "print_tokens", "map", "reduce", "fold", "jsonparser_skip_ws_mini"];
|
const LOOP_NAMES: &[&str] = &[
|
||||||
|
"simple",
|
||||||
|
"filter",
|
||||||
|
"print_tokens",
|
||||||
|
"map",
|
||||||
|
"reduce",
|
||||||
|
"fold",
|
||||||
|
"jsonparser_skip_ws_mini",
|
||||||
|
"jsonparser_atoi_mini",
|
||||||
|
];
|
||||||
|
|
||||||
if IF_RETURN_NAMES.contains(&func_name) {
|
if IF_RETURN_NAMES.contains(&func_name) {
|
||||||
return Ok(FunctionRoute::IfReturn);
|
return Ok(FunctionRoute::IfReturn);
|
||||||
|
|||||||
@ -10,7 +10,7 @@ use crate::mir::join_ir::{
|
|||||||
JoinModule, MirLikeInst, UnaryOp,
|
JoinModule, MirLikeInst, UnaryOp,
|
||||||
};
|
};
|
||||||
use crate::mir::ValueId;
|
use crate::mir::ValueId;
|
||||||
use std::collections::HashSet;
|
use std::collections::{HashMap, HashSet};
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||||
|
|
||||||
@ -90,6 +90,7 @@ pub enum JpOp {
|
|||||||
BinOp(BinOpKind),
|
BinOp(BinOpKind),
|
||||||
Unary(UnaryOp),
|
Unary(UnaryOp),
|
||||||
Compare(CompareOp),
|
Compare(CompareOp),
|
||||||
|
BoxCall { box_name: String, method: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Normalized JoinIR モジュール(テスト専用)。
|
/// Normalized JoinIR モジュール(テスト専用)。
|
||||||
@ -180,7 +181,10 @@ pub fn normalized_pattern1_to_structured(norm: &NormalizedModule) -> JoinModule
|
|||||||
let mut module = JoinModule::new();
|
let mut module = JoinModule::new();
|
||||||
|
|
||||||
for (jp_id, jp_fn) in &norm.functions {
|
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
|
.fields
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
@ -196,6 +200,14 @@ pub fn normalized_pattern1_to_structured(norm: &NormalizedModule) -> JoinModule
|
|||||||
dst: *dst,
|
dst: *dst,
|
||||||
value: v.clone(),
|
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 {
|
JpOp::BinOp(op) => func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
dst: *dst,
|
dst: *dst,
|
||||||
op: *op,
|
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 {}",
|
"normalize_pattern2_minimal: expected 3 functions (entry/loop_step/k_exit) but got {}",
|
||||||
func_count
|
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!(
|
assert!(
|
||||||
(1..=3).contains(&loop_func.params.len()),
|
(1..=param_max).contains(&loop_func.params.len()),
|
||||||
"normalize_pattern2_minimal: expected 1..=3 params (loop var + optional acc + optional host)"
|
"normalize_pattern2_minimal: expected 1..={} params (loop var + carriers + optional host)",
|
||||||
|
param_max
|
||||||
);
|
);
|
||||||
|
|
||||||
let jump_conds = loop_func
|
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"
|
"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 functions = BTreeMap::new();
|
||||||
|
let mut env_layouts = Vec::new();
|
||||||
|
|
||||||
for (fid, func) in &structured.functions {
|
for (fid, func) in &structured.functions {
|
||||||
let env_layout_id = if func.params.is_empty() {
|
let env_layout_id = if func.params.is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} 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();
|
let mut body = Vec::new();
|
||||||
@ -333,6 +357,23 @@ pub fn normalize_pattern2_minimal(structured: &JoinModule) -> NormalizedModule {
|
|||||||
op: JpOp::Const(value.clone()),
|
op: JpOp::Const(value.clone()),
|
||||||
args: vec![],
|
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 }) => {
|
JoinInst::Compute(MirLikeInst::BinOp { dst, op, lhs, rhs }) => {
|
||||||
body.push(JpInst::Let {
|
body.push(JpInst::Let {
|
||||||
dst: *dst,
|
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
|
// Ret / other instructions are ignored in this minimal prototype
|
||||||
}
|
}
|
||||||
@ -398,14 +458,14 @@ pub fn normalize_pattern2_minimal(structured: &JoinModule) -> NormalizedModule {
|
|||||||
let norm = NormalizedModule {
|
let norm = NormalizedModule {
|
||||||
functions,
|
functions,
|
||||||
entry: structured.entry.map(|e| JpFuncId(e.0)),
|
entry: structured.entry.map(|e| JpFuncId(e.0)),
|
||||||
env_layouts: vec![env_layout],
|
env_layouts,
|
||||||
phase: JoinIrPhase::Normalized,
|
phase: JoinIrPhase::Normalized,
|
||||||
structured_backup: Some(structured.clone()),
|
structured_backup: Some(structured.clone()),
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
{
|
{
|
||||||
verify_normalized_pattern2(&norm).expect("normalized Pattern2 verifier");
|
verify_normalized_pattern2(&norm, param_max).expect("normalized Pattern2 verifier");
|
||||||
}
|
}
|
||||||
|
|
||||||
norm
|
norm
|
||||||
@ -417,14 +477,12 @@ pub fn normalized_pattern2_to_structured(norm: &NormalizedModule) -> JoinModule
|
|||||||
return backup;
|
return backup;
|
||||||
}
|
}
|
||||||
|
|
||||||
let env_layout = norm.env_layouts.get(0);
|
|
||||||
|
|
||||||
let mut module = JoinModule::new();
|
let mut module = JoinModule::new();
|
||||||
|
|
||||||
for (jp_id, jp_fn) in &norm.functions {
|
for (jp_id, jp_fn) in &norm.functions {
|
||||||
let params: Vec<ValueId> = jp_fn
|
let params: Vec<ValueId> = jp_fn
|
||||||
.env_layout
|
.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| {
|
.map(|layout| {
|
||||||
layout
|
layout
|
||||||
.fields
|
.fields
|
||||||
@ -444,6 +502,14 @@ pub fn normalized_pattern2_to_structured(norm: &NormalizedModule) -> JoinModule
|
|||||||
dst: *dst,
|
dst: *dst,
|
||||||
value: v.clone(),
|
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 {
|
JpOp::BinOp(op) => func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
dst: *dst,
|
dst: *dst,
|
||||||
op: *op,
|
op: *op,
|
||||||
@ -505,23 +571,32 @@ pub fn normalized_pattern2_to_structured(norm: &NormalizedModule) -> JoinModule
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[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 {
|
if module.phase != JoinIrPhase::Normalized {
|
||||||
return Err("Normalized verifier (Pattern2): phase must be Normalized".to_string());
|
return Err("Normalized verifier (Pattern2): phase must be Normalized".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
let field_count = module.env_layouts.get(0).map(|e| e.fields.len());
|
let mut layout_sizes: HashMap<u32, usize> = HashMap::new();
|
||||||
|
for layout in &module.env_layouts {
|
||||||
if let Some(field_count) = field_count {
|
let size = layout.fields.len();
|
||||||
if !(1..=3).contains(&field_count) {
|
if !(1..=max_env_fields).contains(&size) {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Normalized Pattern2 expects 1..=3 env fields, got {}",
|
"Normalized Pattern2 expects 1..={} env fields, got {}",
|
||||||
field_count
|
max_env_fields, size
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
layout_sizes.insert(layout.id, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
for func in module.functions.values() {
|
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 {
|
for inst in &func.body {
|
||||||
match inst {
|
match inst {
|
||||||
JpInst::Let { .. }
|
JpInst::Let { .. }
|
||||||
@ -536,17 +611,11 @@ fn verify_normalized_pattern2(module: &NormalizedModule) -> Result<(), String> {
|
|||||||
JpInst::TailCallFn { env, .. }
|
JpInst::TailCallFn { env, .. }
|
||||||
| JpInst::TailCallKont { env, .. }
|
| JpInst::TailCallKont { env, .. }
|
||||||
| JpInst::If { env, .. } => {
|
| JpInst::If { env, .. } => {
|
||||||
if env.is_empty() {
|
if let Some(expected) = expected_env_len {
|
||||||
return Err("Normalized Pattern2 env must not be empty".to_string());
|
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
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
let _ = expected;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@ -615,6 +684,23 @@ pub fn normalize_pattern1_minimal(structured: &JoinModule) -> NormalizedModule {
|
|||||||
op: JpOp::Const(value.clone()),
|
op: JpOp::Const(value.clone()),
|
||||||
args: vec![],
|
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 }) => {
|
JoinInst::Compute(MirLikeInst::BinOp { dst, op, lhs, rhs }) => {
|
||||||
jp_body.push(JpInst::Let {
|
jp_body.push(JpInst::Let {
|
||||||
dst: *dst,
|
dst: *dst,
|
||||||
@ -737,12 +823,12 @@ pub(crate) fn normalized_dev_roundtrip_structured(
|
|||||||
let norm = normalize_pattern1_minimal(module);
|
let norm = normalize_pattern1_minimal(module);
|
||||||
normalized_pattern1_to_structured(&norm)
|
normalized_pattern1_to_structured(&norm)
|
||||||
})),
|
})),
|
||||||
NormalizedDevShape::Pattern2Mini | NormalizedDevShape::JsonparserSkipWsMini => {
|
NormalizedDevShape::Pattern2Mini
|
||||||
catch_unwind(AssertUnwindSafe(|| {
|
| NormalizedDevShape::JsonparserSkipWsMini
|
||||||
let norm = normalize_pattern2_minimal(module);
|
| NormalizedDevShape::JsonparserAtoiMini => catch_unwind(AssertUnwindSafe(|| {
|
||||||
normalized_pattern2_to_structured(&norm)
|
let norm = normalize_pattern2_minimal(module);
|
||||||
}))
|
normalized_pattern2_to_structured(&norm)
|
||||||
}
|
})),
|
||||||
};
|
};
|
||||||
|
|
||||||
match attempt {
|
match attempt {
|
||||||
|
|||||||
@ -112,3 +112,27 @@ pub fn build_jsonparser_skip_ws_structured_for_normalized_dev() -> JoinModule {
|
|||||||
let mut lowerer = AstToJoinIrLowerer::new();
|
let mut lowerer = AstToJoinIrLowerer::new();
|
||||||
lowerer.lower_program_json(&program_json)
|
lowerer.lower_program_json(&program_json)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// JsonParser _atoi 相当のミニ P2 ループを Structured で組み立てるヘルパー。
|
||||||
|
///
|
||||||
|
/// Fixture: docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_atoi_mini.program.json
|
||||||
|
pub fn build_jsonparser_atoi_structured_for_normalized_dev() -> JoinModule {
|
||||||
|
const FIXTURE: &str = include_str!(
|
||||||
|
"../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_atoi_mini.program.json"
|
||||||
|
);
|
||||||
|
|
||||||
|
let program_json: serde_json::Value =
|
||||||
|
serde_json::from_str(FIXTURE).expect("jsonparser atoi fixture should be valid JSON");
|
||||||
|
|
||||||
|
let mut lowerer = AstToJoinIrLowerer::new();
|
||||||
|
let module = lowerer.lower_program_json(&program_json);
|
||||||
|
|
||||||
|
if std::env::var("JOINIR_TEST_DEBUG").is_ok() {
|
||||||
|
eprintln!(
|
||||||
|
"[joinir/normalized-dev] jsonparser_atoi_mini structured module: {:#?}",
|
||||||
|
module
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
module
|
||||||
|
}
|
||||||
|
|||||||
@ -7,10 +7,14 @@ pub(crate) enum NormalizedDevShape {
|
|||||||
Pattern1Mini,
|
Pattern1Mini,
|
||||||
Pattern2Mini,
|
Pattern2Mini,
|
||||||
JsonparserSkipWsMini,
|
JsonparserSkipWsMini,
|
||||||
|
JsonparserAtoiMini,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn supported_shapes(module: &JoinModule) -> Vec<NormalizedDevShape> {
|
pub(crate) fn supported_shapes(module: &JoinModule) -> Vec<NormalizedDevShape> {
|
||||||
let mut shapes = Vec::new();
|
let mut shapes = Vec::new();
|
||||||
|
if is_jsonparser_atoi_mini(module) {
|
||||||
|
shapes.push(NormalizedDevShape::JsonparserAtoiMini);
|
||||||
|
}
|
||||||
if is_jsonparser_skip_ws_mini(module) {
|
if is_jsonparser_skip_ws_mini(module) {
|
||||||
shapes.push(NormalizedDevShape::JsonparserSkipWsMini);
|
shapes.push(NormalizedDevShape::JsonparserSkipWsMini);
|
||||||
}
|
}
|
||||||
@ -59,6 +63,35 @@ pub(crate) fn is_jsonparser_skip_ws_mini(module: &JoinModule) -> bool {
|
|||||||
.any(|f| f.name == "jsonparser_skip_ws_mini")
|
.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;
|
||||||
|
}
|
||||||
|
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.contains("atoi"))
|
||||||
|
}
|
||||||
|
|
||||||
fn find_loop_step(module: &JoinModule) -> Option<&JoinFunction> {
|
fn find_loop_step(module: &JoinModule) -> Option<&JoinFunction> {
|
||||||
module
|
module
|
||||||
.functions
|
.functions
|
||||||
|
|||||||
@ -5,9 +5,15 @@ use crate::mir::MirModule;
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
use crate::mir::join_ir::normalized::{normalized_dev_roundtrip_structured, NormalizedModule};
|
use crate::mir::join_ir::normalized::{
|
||||||
|
normalize_pattern1_minimal, normalize_pattern2_minimal, NormalizedModule,
|
||||||
|
};
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
use crate::mir::join_ir::normalized::shape_guard;
|
use crate::mir::join_ir::normalized::shape_guard::{self, NormalizedDevShape};
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
use crate::mir::join_ir_vm_bridge::lower_normalized_to_mir_minimal;
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||||
|
|
||||||
/// Structured JoinIR → MIR(既存経路)の明示エントリ。
|
/// Structured JoinIR → MIR(既存経路)の明示エントリ。
|
||||||
pub(crate) fn lower_joinir_structured_to_mir_with_meta(
|
pub(crate) fn lower_joinir_structured_to_mir_with_meta(
|
||||||
@ -45,6 +51,75 @@ pub(crate) fn lower_joinir_normalized_to_mir_with_meta(
|
|||||||
lower_joinir_structured_to_mir_with_meta(&structured, meta)
|
lower_joinir_structured_to_mir_with_meta(&structured, meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
fn normalize_for_shape(
|
||||||
|
module: &JoinModule,
|
||||||
|
shape: NormalizedDevShape,
|
||||||
|
) -> Result<NormalizedModule, JoinIrVmBridgeError> {
|
||||||
|
let result = match shape {
|
||||||
|
NormalizedDevShape::Pattern1Mini => {
|
||||||
|
catch_unwind(AssertUnwindSafe(|| normalize_pattern1_minimal(module)))
|
||||||
|
}
|
||||||
|
NormalizedDevShape::Pattern2Mini
|
||||||
|
| NormalizedDevShape::JsonparserSkipWsMini
|
||||||
|
| NormalizedDevShape::JsonparserAtoiMini => {
|
||||||
|
catch_unwind(AssertUnwindSafe(|| normalize_pattern2_minimal(module)))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(norm) => Ok(norm),
|
||||||
|
Err(_) => Err(JoinIrVmBridgeError::new(format!(
|
||||||
|
"[joinir/bridge/normalized] normalization failed for shape {:?}",
|
||||||
|
shape
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
fn try_normalized_direct_bridge(
|
||||||
|
module: &JoinModule,
|
||||||
|
meta: &JoinFuncMetaMap,
|
||||||
|
) -> Result<Option<MirModule>, JoinIrVmBridgeError> {
|
||||||
|
let shapes = shape_guard::supported_shapes(module);
|
||||||
|
if shapes.is_empty() {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let verbose = crate::config::env::joinir_dev_enabled();
|
||||||
|
|
||||||
|
for shape in shapes {
|
||||||
|
if verbose {
|
||||||
|
eprintln!("[joinir/bridge] attempting normalized→MIR for {:?}", shape);
|
||||||
|
}
|
||||||
|
match normalize_for_shape(module, shape) {
|
||||||
|
Ok(norm) => {
|
||||||
|
let mir = lower_normalized_to_mir_minimal(&norm, meta)?;
|
||||||
|
if verbose {
|
||||||
|
eprintln!(
|
||||||
|
"[joinir/bridge] normalized→MIR succeeded (shape={:?}, functions={})",
|
||||||
|
shape,
|
||||||
|
norm.functions.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Ok(Some(mir));
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
if verbose {
|
||||||
|
eprintln!(
|
||||||
|
"[joinir/bridge] {:?} normalization failed: {} (continuing)",
|
||||||
|
shape, err.message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(JoinIrVmBridgeError::new(
|
||||||
|
"[joinir/bridge] normalized_dev enabled but no normalization attempt succeeded",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
/// JoinIR → MIR の単一入口。Normalized dev が有効なら roundtrip してから既存経路へ。
|
/// JoinIR → MIR の単一入口。Normalized dev が有効なら roundtrip してから既存経路へ。
|
||||||
pub(crate) fn bridge_joinir_to_mir_with_meta(
|
pub(crate) fn bridge_joinir_to_mir_with_meta(
|
||||||
module: &JoinModule,
|
module: &JoinModule,
|
||||||
@ -53,20 +128,13 @@ pub(crate) fn bridge_joinir_to_mir_with_meta(
|
|||||||
if crate::config::env::normalized_dev_enabled() {
|
if crate::config::env::normalized_dev_enabled() {
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
{
|
{
|
||||||
let shapes = shape_guard::supported_shapes(module);
|
match try_normalized_direct_bridge(module, meta)? {
|
||||||
if shapes.is_empty() {
|
Some(mir) => return Ok(mir),
|
||||||
debug_log!(
|
None => {
|
||||||
"[joinir/bridge] normalized dev enabled but shape unsupported; falling back to Structured path"
|
debug_log!(
|
||||||
);
|
"[joinir/bridge] normalized dev enabled but shape unsupported; falling back to Structured path"
|
||||||
} else {
|
);
|
||||||
let structured_roundtrip = normalized_dev_roundtrip_structured(module)
|
}
|
||||||
.map_err(JoinIrVmBridgeError::new)?;
|
|
||||||
debug_log!(
|
|
||||||
"[joinir/bridge] normalized dev path enabled (shapes={:?}, functions={})",
|
|
||||||
shapes,
|
|
||||||
structured_roundtrip.functions.len()
|
|
||||||
);
|
|
||||||
return lower_joinir_structured_to_mir_with_meta(&structured_roundtrip, meta);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,6 +42,8 @@ mod bridge;
|
|||||||
mod joinir_block_converter;
|
mod joinir_block_converter;
|
||||||
mod joinir_function_converter;
|
mod joinir_function_converter;
|
||||||
mod meta;
|
mod meta;
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
mod normalized_bridge;
|
||||||
mod runner;
|
mod runner;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -53,6 +55,8 @@ pub(crate) use bridge::{bridge_joinir_to_mir, bridge_joinir_to_mir_with_meta};
|
|||||||
pub(crate) use convert::convert_joinir_to_mir;
|
pub(crate) use convert::convert_joinir_to_mir;
|
||||||
pub(crate) use convert::convert_mir_like_inst; // helper for sub-modules
|
pub(crate) use convert::convert_mir_like_inst; // helper for sub-modules
|
||||||
pub(crate) use joinir_function_converter::JoinIrFunctionConverter;
|
pub(crate) use joinir_function_converter::JoinIrFunctionConverter;
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
pub(crate) use normalized_bridge::lower_normalized_to_mir_minimal;
|
||||||
pub use meta::convert_join_module_to_mir_with_meta;
|
pub use meta::convert_join_module_to_mir_with_meta;
|
||||||
pub use runner::run_joinir_via_vm;
|
pub use runner::run_joinir_via_vm;
|
||||||
|
|
||||||
|
|||||||
62
src/mir/join_ir_vm_bridge/normalized_bridge.rs
Normal file
62
src/mir/join_ir_vm_bridge/normalized_bridge.rs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#![cfg(feature = "normalized_dev")]
|
||||||
|
|
||||||
|
use super::bridge::lower_joinir_structured_to_mir_with_meta;
|
||||||
|
use super::JoinIrVmBridgeError;
|
||||||
|
use crate::mir::join_ir::frontend::JoinFuncMetaMap;
|
||||||
|
use crate::mir::join_ir::{
|
||||||
|
normalized_pattern1_to_structured, normalized_pattern2_to_structured, JoinIrPhase,
|
||||||
|
NormalizedModule,
|
||||||
|
};
|
||||||
|
use crate::mir::MirModule;
|
||||||
|
|
||||||
|
/// Dev-only Normalized → MIR ブリッジ(Pattern1/2 ミニ + JP mini/atoi mini 専用)
|
||||||
|
pub(crate) fn lower_normalized_to_mir_minimal(
|
||||||
|
norm: &NormalizedModule,
|
||||||
|
_meta: &JoinFuncMetaMap,
|
||||||
|
) -> Result<MirModule, JoinIrVmBridgeError> {
|
||||||
|
if norm.phase != JoinIrPhase::Normalized {
|
||||||
|
return Err(JoinIrVmBridgeError::new(
|
||||||
|
"[joinir/bridge/normalized] expected Normalized JoinIR module",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if std::env::var("JOINIR_TEST_DEBUG").is_ok() {
|
||||||
|
eprintln!(
|
||||||
|
"[joinir/normalized-bridge] lowering normalized module (functions={}, env_layouts={})",
|
||||||
|
norm.functions.len(),
|
||||||
|
norm.env_layouts.len()
|
||||||
|
);
|
||||||
|
for layout in &norm.env_layouts {
|
||||||
|
let fields: Vec<String> = layout
|
||||||
|
.fields
|
||||||
|
.iter()
|
||||||
|
.map(|f| format!("{}={:?}", f.name, f.value_id))
|
||||||
|
.collect();
|
||||||
|
eprintln!(
|
||||||
|
"[joinir/normalized-bridge] env_layout {} fields: {}",
|
||||||
|
layout.id,
|
||||||
|
fields.join(", ")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for func in norm.functions.values() {
|
||||||
|
eprintln!(
|
||||||
|
"[joinir/normalized-bridge] fn {} (id={:?}) env_layout={:?} body_len={}",
|
||||||
|
func.name,
|
||||||
|
func.id,
|
||||||
|
func.env_layout,
|
||||||
|
func.body.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut norm_clone = norm.clone();
|
||||||
|
norm_clone.structured_backup = None;
|
||||||
|
|
||||||
|
let structured = if norm_clone.functions.len() <= 2 {
|
||||||
|
normalized_pattern1_to_structured(&norm_clone)
|
||||||
|
} else {
|
||||||
|
normalized_pattern2_to_structured(&norm_clone)
|
||||||
|
};
|
||||||
|
|
||||||
|
lower_joinir_structured_to_mir_with_meta(&structured, _meta)
|
||||||
|
}
|
||||||
@ -7,6 +7,7 @@ use nyash_rust::mir::join_ir::{
|
|||||||
JoinFunction, JoinInst, JoinIrPhase, JoinModule, MirLikeInst,
|
JoinFunction, JoinInst, JoinIrPhase, JoinModule, MirLikeInst,
|
||||||
};
|
};
|
||||||
use nyash_rust::mir::join_ir::normalized::fixtures::{
|
use nyash_rust::mir::join_ir::normalized::fixtures::{
|
||||||
|
build_jsonparser_atoi_structured_for_normalized_dev,
|
||||||
build_jsonparser_skip_ws_structured_for_normalized_dev,
|
build_jsonparser_skip_ws_structured_for_normalized_dev,
|
||||||
build_pattern2_break_fixture_structured, build_pattern2_minimal_structured,
|
build_pattern2_break_fixture_structured, build_pattern2_minimal_structured,
|
||||||
};
|
};
|
||||||
@ -331,7 +332,7 @@ fn normalized_pattern2_jsonparser_runner_dev_switch_matches_structured() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn normalized_pattern2_vm_bridge_dev_switch_matches_structured() {
|
fn normalized_pattern2_vm_bridge_direct_matches_structured() {
|
||||||
let structured = build_pattern2_break_fixture_structured();
|
let structured = build_pattern2_break_fixture_structured();
|
||||||
let entry = structured.entry.expect("structured entry required");
|
let entry = structured.entry.expect("structured entry required");
|
||||||
let cases = [0, 1, 3, 5];
|
let cases = [0, 1, 3, 5];
|
||||||
@ -353,7 +354,7 @@ fn normalized_pattern2_vm_bridge_dev_switch_matches_structured() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn normalized_pattern1_vm_bridge_dev_switch_matches_structured() {
|
fn normalized_pattern1_vm_bridge_direct_matches_structured() {
|
||||||
let structured = build_structured_pattern1();
|
let structured = build_structured_pattern1();
|
||||||
let entry = structured.entry.expect("structured entry required");
|
let entry = structured.entry.expect("structured entry required");
|
||||||
let cases = [0, 5, 7];
|
let cases = [0, 5, 7];
|
||||||
@ -369,7 +370,7 @@ fn normalized_pattern1_vm_bridge_dev_switch_matches_structured() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn normalized_pattern2_jsonparser_vm_bridge_dev_switch_matches_structured() {
|
fn normalized_pattern2_jsonparser_vm_bridge_direct_matches_structured() {
|
||||||
let structured = build_jsonparser_skip_ws_structured_for_normalized_dev();
|
let structured = build_jsonparser_skip_ws_structured_for_normalized_dev();
|
||||||
let entry = structured.entry.expect("structured entry required");
|
let entry = structured.entry.expect("structured entry required");
|
||||||
let cases = [0, 1, 2, 5];
|
let cases = [0, 1, 2, 5];
|
||||||
@ -383,3 +384,29 @@ fn normalized_pattern2_jsonparser_vm_bridge_dev_switch_matches_structured() {
|
|||||||
assert_eq!(dev, JoinValue::Int(len), "unexpected result at len={}", len);
|
assert_eq!(dev, JoinValue::Int(len), "unexpected result at len={}", len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn normalized_pattern2_jsonparser_atoi_vm_bridge_direct_matches_structured() {
|
||||||
|
let structured = build_jsonparser_atoi_structured_for_normalized_dev();
|
||||||
|
let entry = structured.entry.expect("structured entry required");
|
||||||
|
let cases = [
|
||||||
|
("42", 2, 42),
|
||||||
|
("123abc", 6, 123),
|
||||||
|
("007", 3, 7),
|
||||||
|
("", 0, 0),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (s, len, expected) in cases {
|
||||||
|
let input = [JoinValue::Str(s.to_string()), JoinValue::Int(len)];
|
||||||
|
let base = run_joinir_vm_bridge(&structured, entry, &input, false);
|
||||||
|
let dev = run_joinir_vm_bridge(&structured, entry, &input, true);
|
||||||
|
|
||||||
|
assert_eq!(base, dev, "vm bridge mismatch for input '{}'", s);
|
||||||
|
assert_eq!(
|
||||||
|
dev,
|
||||||
|
JoinValue::Int(expected),
|
||||||
|
"unexpected result for input '{}'",
|
||||||
|
s
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user