Refine normalized bridge direct path and env guard
This commit is contained in:
@ -17,6 +17,8 @@ use std::panic::{catch_unwind, AssertUnwindSafe};
|
|||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
pub mod fixtures;
|
pub mod fixtures;
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
pub mod dev_env;
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
pub(crate) mod shape_guard;
|
pub(crate) mod shape_guard;
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
use crate::mir::join_ir::normalized::shape_guard::NormalizedDevShape;
|
use crate::mir::join_ir::normalized::shape_guard::NormalizedDevShape;
|
||||||
|
|||||||
38
src/mir/join_ir/normalized/dev_env.rs
Normal file
38
src/mir/join_ir/normalized/dev_env.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#![cfg(feature = "normalized_dev")]
|
||||||
|
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
/// 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>,
|
||||||
|
}
|
||||||
|
|
||||||
|
static NORMALIZED_ENV_LOCK: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
|
||||||
|
|
||||||
|
impl NormalizedDevEnvGuard {
|
||||||
|
pub fn new(enabled: bool) -> Self {
|
||||||
|
let lock = NORMALIZED_ENV_LOCK
|
||||||
|
.lock()
|
||||||
|
.expect("normalized env mutex poisoned");
|
||||||
|
let prev = std::env::var("NYASH_JOINIR_NORMALIZED_DEV_RUN").ok();
|
||||||
|
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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,6 +10,11 @@ pub(crate) enum NormalizedDevShape {
|
|||||||
JsonparserAtoiMini,
|
JsonparserAtoiMini,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 直接 Normalized→MIR ブリッジで扱う shape を返す(dev 限定)。
|
||||||
|
pub(crate) fn direct_shapes(module: &JoinModule) -> Vec<NormalizedDevShape> {
|
||||||
|
supported_shapes(module)
|
||||||
|
}
|
||||||
|
|
||||||
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) {
|
if is_jsonparser_atoi_mini(module) {
|
||||||
@ -27,6 +32,11 @@ pub(crate) fn supported_shapes(module: &JoinModule) -> Vec<NormalizedDevShape> {
|
|||||||
shapes
|
shapes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) fn is_direct_supported(module: &JoinModule) -> bool {
|
||||||
|
!direct_shapes(module).is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn is_pattern1_mini(module: &JoinModule) -> bool {
|
pub(crate) fn is_pattern1_mini(module: &JoinModule) -> bool {
|
||||||
module.is_structured() && find_loop_step(module).is_some()
|
module.is_structured() && find_loop_step(module).is_some()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -81,7 +81,7 @@ fn try_normalized_direct_bridge(
|
|||||||
module: &JoinModule,
|
module: &JoinModule,
|
||||||
meta: &JoinFuncMetaMap,
|
meta: &JoinFuncMetaMap,
|
||||||
) -> Result<Option<MirModule>, JoinIrVmBridgeError> {
|
) -> Result<Option<MirModule>, JoinIrVmBridgeError> {
|
||||||
let shapes = shape_guard::supported_shapes(module);
|
let shapes = shape_guard::direct_shapes(module);
|
||||||
if shapes.is_empty() {
|
if shapes.is_empty() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,10 +9,32 @@ use crate::mir::join_ir::{
|
|||||||
};
|
};
|
||||||
use crate::mir::MirModule;
|
use crate::mir::MirModule;
|
||||||
|
|
||||||
|
mod direct;
|
||||||
|
|
||||||
|
/// Direct Normalized → MIR 変換が未対応のときに使うフォールバック。
|
||||||
|
fn lower_normalized_via_structured(
|
||||||
|
norm: &NormalizedModule,
|
||||||
|
meta: &JoinFuncMetaMap,
|
||||||
|
) -> Result<MirModule, JoinIrVmBridgeError> {
|
||||||
|
let structured = if let Some(snapshot) = norm.to_structured() {
|
||||||
|
snapshot
|
||||||
|
} else if norm.functions.len() <= 2 {
|
||||||
|
normalized_pattern1_to_structured(norm)
|
||||||
|
} else {
|
||||||
|
normalized_pattern2_to_structured(norm)
|
||||||
|
};
|
||||||
|
|
||||||
|
if crate::config::env::joinir_dev_enabled() {
|
||||||
|
eprintln!("[joinir/normalized-bridge/fallback] using structured path (functions={})", structured.functions.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
lower_joinir_structured_to_mir_with_meta(&structured, meta)
|
||||||
|
}
|
||||||
|
|
||||||
/// Dev-only Normalized → MIR ブリッジ(Pattern1/2 ミニ + JP mini/atoi mini 専用)
|
/// Dev-only Normalized → MIR ブリッジ(Pattern1/2 ミニ + JP mini/atoi mini 専用)
|
||||||
pub(crate) fn lower_normalized_to_mir_minimal(
|
pub(crate) fn lower_normalized_to_mir_minimal(
|
||||||
norm: &NormalizedModule,
|
norm: &NormalizedModule,
|
||||||
_meta: &JoinFuncMetaMap,
|
meta: &JoinFuncMetaMap,
|
||||||
) -> Result<MirModule, JoinIrVmBridgeError> {
|
) -> Result<MirModule, JoinIrVmBridgeError> {
|
||||||
if norm.phase != JoinIrPhase::Normalized {
|
if norm.phase != JoinIrPhase::Normalized {
|
||||||
return Err(JoinIrVmBridgeError::new(
|
return Err(JoinIrVmBridgeError::new(
|
||||||
@ -49,14 +71,14 @@ pub(crate) fn lower_normalized_to_mir_minimal(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut norm_clone = norm.clone();
|
// direct 対象は Normalized → MIR をそのまま吐く。未対応 shape は Structured 経由にフォールバック。
|
||||||
norm_clone.structured_backup = None;
|
match direct::lower_normalized_direct_minimal(norm) {
|
||||||
|
Ok(mir) => Ok(mir),
|
||||||
let structured = if norm_clone.functions.len() <= 2 {
|
Err(err) => {
|
||||||
normalized_pattern1_to_structured(&norm_clone)
|
if crate::config::env::joinir_dev_enabled() {
|
||||||
} else {
|
eprintln!("[joinir/normalized-bridge/fallback] direct path failed: {}; falling back to Structured path", err.message);
|
||||||
normalized_pattern2_to_structured(&norm_clone)
|
}
|
||||||
};
|
lower_normalized_via_structured(norm, meta)
|
||||||
|
}
|
||||||
lower_joinir_structured_to_mir_with_meta(&structured, _meta)
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
388
src/mir/join_ir_vm_bridge/normalized_bridge/direct.rs
Normal file
388
src/mir/join_ir_vm_bridge/normalized_bridge/direct.rs
Normal file
@ -0,0 +1,388 @@
|
|||||||
|
#![cfg(feature = "normalized_dev")]
|
||||||
|
|
||||||
|
use super::super::join_func_name;
|
||||||
|
use super::super::JoinIrVmBridgeError;
|
||||||
|
use super::super::convert_mir_like_inst;
|
||||||
|
use crate::ast::Span;
|
||||||
|
use crate::config::env::joinir_dev_enabled;
|
||||||
|
use crate::mir::join_ir::normalized::{JpFuncId, JpFunction, JpInst, JpOp, NormalizedModule};
|
||||||
|
use crate::mir::join_ir::{JoinFuncId, JoinIrPhase, MirLikeInst};
|
||||||
|
use crate::mir::{
|
||||||
|
BasicBlock, BasicBlockId, ConstValue as MirConstValue, EffectMask, FunctionSignature,
|
||||||
|
MirFunction, MirInstruction, MirModule, MirType, ValueId,
|
||||||
|
};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
/// Normalized → MIR の最小 direct 変換(Pattern1/2 ミニ+JP mini/atoi mini)。
|
||||||
|
pub(crate) fn lower_normalized_direct_minimal(
|
||||||
|
norm: &NormalizedModule,
|
||||||
|
) -> Result<MirModule, JoinIrVmBridgeError> {
|
||||||
|
if norm.phase != JoinIrPhase::Normalized {
|
||||||
|
return Err(JoinIrVmBridgeError::new(
|
||||||
|
"[joinir/normalized-bridge] expected Normalized JoinIR module",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let debug_dump = std::env::var("JOINIR_TEST_DEBUG").is_ok();
|
||||||
|
let verbose = joinir_dev_enabled();
|
||||||
|
if verbose {
|
||||||
|
eprintln!(
|
||||||
|
"[joinir/normalized-bridge/direct] lowering normalized module (functions={}, env_layouts={})",
|
||||||
|
norm.functions.len(),
|
||||||
|
norm.env_layouts.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut mir_module = MirModule::new("joinir_normalized_direct".to_string());
|
||||||
|
|
||||||
|
for func in norm.functions.values() {
|
||||||
|
let mir_func = lower_normalized_function_direct(func, norm)?;
|
||||||
|
mir_module.add_function(mir_func);
|
||||||
|
}
|
||||||
|
|
||||||
|
if debug_dump {
|
||||||
|
eprintln!(
|
||||||
|
"[joinir/normalized-bridge/direct] produced MIR (debug dump): {:#?}",
|
||||||
|
mir_module
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(mir_module)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lower_normalized_function_direct(
|
||||||
|
func: &JpFunction,
|
||||||
|
norm: &NormalizedModule,
|
||||||
|
) -> Result<MirFunction, JoinIrVmBridgeError> {
|
||||||
|
let verbose = joinir_dev_enabled();
|
||||||
|
let env_fields = func
|
||||||
|
.env_layout
|
||||||
|
.and_then(|id| norm.env_layouts.iter().find(|layout| layout.id == id));
|
||||||
|
|
||||||
|
let params: Vec<ValueId> = env_fields
|
||||||
|
.map(|layout| {
|
||||||
|
layout
|
||||||
|
.fields
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(idx, f)| f.value_id.unwrap_or(ValueId(idx as u32)))
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
// Build a dense ValueId mapping to ensure VM registersはVoidにならない。
|
||||||
|
let mut value_map: BTreeMap<ValueId, ValueId> = BTreeMap::new();
|
||||||
|
for id in ¶ms {
|
||||||
|
remap_value(*id, &mut value_map);
|
||||||
|
}
|
||||||
|
for inst in &func.body {
|
||||||
|
for vid in value_ids_in_inst(inst) {
|
||||||
|
remap_value(vid, &mut value_map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let remap = |id: ValueId, map: &mut BTreeMap<ValueId, ValueId>| remap_value(id, map);
|
||||||
|
let remap_vec = |ids: &[ValueId], map: &mut BTreeMap<ValueId, ValueId>| {
|
||||||
|
ids.iter().map(|id| remap_value(*id, map)).collect::<Vec<_>>()
|
||||||
|
};
|
||||||
|
|
||||||
|
let remapped_params = remap_vec(¶ms, &mut value_map);
|
||||||
|
let param_types = vec![MirType::Unknown; remapped_params.len()];
|
||||||
|
let signature = FunctionSignature {
|
||||||
|
name: join_func_name(JoinFuncId(func.id.0)),
|
||||||
|
params: param_types,
|
||||||
|
return_type: MirType::Unknown,
|
||||||
|
effects: EffectMask::PURE,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut mir_func = MirFunction::new(signature, BasicBlockId(0));
|
||||||
|
if !remapped_params.is_empty() {
|
||||||
|
mir_func.params = remapped_params.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
mir_func.next_value_id = value_map.len() as u32;
|
||||||
|
|
||||||
|
let mut current_block_id = BasicBlockId(0);
|
||||||
|
let mut next_block_id = 1;
|
||||||
|
let mut current_insts: Vec<MirInstruction> = Vec::new();
|
||||||
|
let mut terminated = false;
|
||||||
|
|
||||||
|
if verbose {
|
||||||
|
eprintln!(
|
||||||
|
"[joinir/normalized-bridge/direct] lowering fn={} params={:?} remapped_params={:?} body_len={}",
|
||||||
|
func.name, params, remapped_params, func.body.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for inst in &func.body {
|
||||||
|
if terminated {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
match inst {
|
||||||
|
JpInst::Let { dst, op, args } => {
|
||||||
|
let remapped_dst = remap(*dst, &mut value_map);
|
||||||
|
let remapped_args = remap_vec(args, &mut value_map);
|
||||||
|
let mir_like = jp_op_to_mir_like(remapped_dst, op, &remapped_args)?;
|
||||||
|
let mir_inst = convert_mir_like_inst(&mir_like)?;
|
||||||
|
current_insts.push(mir_inst);
|
||||||
|
}
|
||||||
|
JpInst::EnvLoad { .. } | JpInst::EnvStore { .. } => {
|
||||||
|
return Err(JoinIrVmBridgeError::new(
|
||||||
|
"[joinir/normalized-bridge/direct] EnvLoad/EnvStore not supported in minimal direct bridge",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
JpInst::TailCallFn { target, env } => {
|
||||||
|
let env_remapped = remap_vec(env, &mut value_map);
|
||||||
|
let (instructions, terminator) = build_tail_call(&mut mir_func, target, &env_remapped);
|
||||||
|
finalize_block(
|
||||||
|
&mut mir_func,
|
||||||
|
current_block_id,
|
||||||
|
{
|
||||||
|
let mut insts = mem::take(&mut current_insts);
|
||||||
|
insts.extend(instructions);
|
||||||
|
insts
|
||||||
|
},
|
||||||
|
terminator,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
terminated = true;
|
||||||
|
}
|
||||||
|
JpInst::TailCallKont { env, .. } => {
|
||||||
|
let env_remapped = remap_vec(env, &mut value_map);
|
||||||
|
let return_val = env_remapped.first().copied();
|
||||||
|
finalize_block(
|
||||||
|
&mut mir_func,
|
||||||
|
current_block_id,
|
||||||
|
mem::take(&mut current_insts),
|
||||||
|
MirInstruction::Return { value: return_val },
|
||||||
|
Some(env_remapped),
|
||||||
|
);
|
||||||
|
terminated = true;
|
||||||
|
}
|
||||||
|
JpInst::If {
|
||||||
|
cond,
|
||||||
|
then_target,
|
||||||
|
else_target,
|
||||||
|
env,
|
||||||
|
} => {
|
||||||
|
let then_bb = BasicBlockId(next_block_id);
|
||||||
|
next_block_id += 1;
|
||||||
|
let else_bb = BasicBlockId(next_block_id);
|
||||||
|
next_block_id += 1;
|
||||||
|
|
||||||
|
let cond_remapped = remap(*cond, &mut value_map);
|
||||||
|
let env_remapped = remap_vec(env, &mut value_map);
|
||||||
|
|
||||||
|
// Branch from current block
|
||||||
|
finalize_block(
|
||||||
|
&mut mir_func,
|
||||||
|
current_block_id,
|
||||||
|
mem::take(&mut current_insts),
|
||||||
|
MirInstruction::Branch {
|
||||||
|
condition: cond_remapped,
|
||||||
|
then_bb,
|
||||||
|
else_bb,
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Decide which branch continues the loop (self) and which exits.
|
||||||
|
let (exit_bb, exit_target, cont_bb) = if then_target.0 == func.id.0 {
|
||||||
|
(else_bb, else_target, then_bb)
|
||||||
|
} else if else_target.0 == func.id.0 {
|
||||||
|
(then_bb, then_target, else_bb)
|
||||||
|
} else {
|
||||||
|
// Both branches exit; build both and stop.
|
||||||
|
build_exit_or_tail_branch(
|
||||||
|
&mut mir_func,
|
||||||
|
then_bb,
|
||||||
|
then_target,
|
||||||
|
&env_remapped,
|
||||||
|
func.id,
|
||||||
|
)?;
|
||||||
|
build_exit_or_tail_branch(
|
||||||
|
&mut mir_func,
|
||||||
|
else_bb,
|
||||||
|
else_target,
|
||||||
|
&env_remapped,
|
||||||
|
func.id,
|
||||||
|
)?;
|
||||||
|
terminated = true;
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
build_exit_or_tail_branch(
|
||||||
|
&mut mir_func,
|
||||||
|
exit_bb,
|
||||||
|
exit_target,
|
||||||
|
&env_remapped,
|
||||||
|
func.id,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
mir_func
|
||||||
|
.blocks
|
||||||
|
.entry(cont_bb)
|
||||||
|
.or_insert_with(|| BasicBlock::new(cont_bb));
|
||||||
|
current_block_id = cont_bb;
|
||||||
|
current_insts = Vec::new();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !terminated {
|
||||||
|
// Flush remaining instructions into the current block and end with Return None
|
||||||
|
let block = mir_func
|
||||||
|
.blocks
|
||||||
|
.entry(current_block_id)
|
||||||
|
.or_insert_with(|| BasicBlock::new(current_block_id));
|
||||||
|
if !current_insts.is_empty() {
|
||||||
|
block.instructions = current_insts;
|
||||||
|
block.instruction_spans = vec![Span::unknown(); block.instructions.len()];
|
||||||
|
}
|
||||||
|
if block.terminator.is_none() {
|
||||||
|
block.terminator = Some(MirInstruction::Return { value: None });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(mir_func)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn jp_op_to_mir_like(
|
||||||
|
dst: ValueId,
|
||||||
|
op: &JpOp,
|
||||||
|
args: &[ValueId],
|
||||||
|
) -> Result<MirLikeInst, JoinIrVmBridgeError> {
|
||||||
|
match op {
|
||||||
|
JpOp::Const(v) => Ok(MirLikeInst::Const {
|
||||||
|
dst,
|
||||||
|
value: v.clone(),
|
||||||
|
}),
|
||||||
|
JpOp::BinOp(op) => Ok(MirLikeInst::BinOp {
|
||||||
|
dst,
|
||||||
|
op: *op,
|
||||||
|
lhs: args.get(0).copied().unwrap_or(ValueId(0)),
|
||||||
|
rhs: args.get(1).copied().unwrap_or(ValueId(0)),
|
||||||
|
}),
|
||||||
|
JpOp::Unary(op) => Ok(MirLikeInst::UnaryOp {
|
||||||
|
dst,
|
||||||
|
op: *op,
|
||||||
|
operand: args.get(0).copied().unwrap_or(ValueId(0)),
|
||||||
|
}),
|
||||||
|
JpOp::Compare(op) => Ok(MirLikeInst::Compare {
|
||||||
|
dst,
|
||||||
|
op: *op,
|
||||||
|
lhs: args.get(0).copied().unwrap_or(ValueId(0)),
|
||||||
|
rhs: args.get(1).copied().unwrap_or(ValueId(0)),
|
||||||
|
}),
|
||||||
|
JpOp::BoxCall { box_name, method } => Ok(MirLikeInst::BoxCall {
|
||||||
|
dst: Some(dst),
|
||||||
|
box_name: box_name.clone(),
|
||||||
|
method: method.clone(),
|
||||||
|
args: args.to_vec(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_tail_call(
|
||||||
|
mir_func: &mut MirFunction,
|
||||||
|
target: &JpFuncId,
|
||||||
|
env: &[ValueId],
|
||||||
|
) -> (Vec<MirInstruction>, MirInstruction) {
|
||||||
|
let func_name_id = mir_func.next_value_id();
|
||||||
|
let result_id = mir_func.next_value_id();
|
||||||
|
let func_name = join_func_name(JoinFuncId(target.0));
|
||||||
|
|
||||||
|
let mut instructions = Vec::new();
|
||||||
|
instructions.push(MirInstruction::Const {
|
||||||
|
dst: func_name_id,
|
||||||
|
value: MirConstValue::String(func_name),
|
||||||
|
});
|
||||||
|
instructions.push(MirInstruction::Call {
|
||||||
|
dst: Some(result_id),
|
||||||
|
func: func_name_id,
|
||||||
|
callee: None,
|
||||||
|
args: env.to_vec(),
|
||||||
|
effects: EffectMask::PURE,
|
||||||
|
});
|
||||||
|
|
||||||
|
(instructions, MirInstruction::Return { value: Some(result_id) })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_exit_or_tail_branch(
|
||||||
|
mir_func: &mut MirFunction,
|
||||||
|
block_id: BasicBlockId,
|
||||||
|
target: &JpFuncId,
|
||||||
|
env: &[ValueId],
|
||||||
|
self_id: JpFuncId,
|
||||||
|
) -> Result<(), JoinIrVmBridgeError> {
|
||||||
|
if target.0 == self_id.0 {
|
||||||
|
// Continue the loop: tail call self with env, then return its result
|
||||||
|
let (insts, term) = build_tail_call(mir_func, target, env);
|
||||||
|
let block = mir_func
|
||||||
|
.blocks
|
||||||
|
.entry(block_id)
|
||||||
|
.or_insert_with(|| BasicBlock::new(block_id));
|
||||||
|
block.instructions = insts;
|
||||||
|
block.instruction_spans = vec![Span::unknown(); block.instructions.len()];
|
||||||
|
block.terminator = Some(term);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit: return first env arg (Pattern2 minis pass loop state in env[0])
|
||||||
|
let ret_val = env.first().copied();
|
||||||
|
let block = mir_func
|
||||||
|
.blocks
|
||||||
|
.entry(block_id)
|
||||||
|
.or_insert_with(|| BasicBlock::new(block_id));
|
||||||
|
block.instructions.clear();
|
||||||
|
block.instruction_spans.clear();
|
||||||
|
block.terminator = Some(MirInstruction::Return { value: ret_val });
|
||||||
|
block.jump_args = Some(env.to_vec());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn value_ids_in_inst(inst: &JpInst) -> Vec<ValueId> {
|
||||||
|
match inst {
|
||||||
|
JpInst::Let { dst, args, .. } => {
|
||||||
|
let mut ids = vec![*dst];
|
||||||
|
ids.extend(args.iter().copied());
|
||||||
|
ids
|
||||||
|
}
|
||||||
|
JpInst::EnvLoad { dst, env, .. } => vec![*dst, *env],
|
||||||
|
JpInst::EnvStore { env, src, .. } => vec![*env, *src],
|
||||||
|
JpInst::TailCallFn { env, .. } | JpInst::TailCallKont { env, .. } => env.clone(),
|
||||||
|
JpInst::If { cond, env, .. } => {
|
||||||
|
let mut ids = vec![*cond];
|
||||||
|
ids.extend(env.iter().copied());
|
||||||
|
ids
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remap_value(id: ValueId, map: &mut BTreeMap<ValueId, ValueId>) -> ValueId {
|
||||||
|
if let Some(mapped) = map.get(&id) {
|
||||||
|
return *mapped;
|
||||||
|
}
|
||||||
|
let next = ValueId(map.len() as u32);
|
||||||
|
map.insert(id, next);
|
||||||
|
next
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finalize_block(
|
||||||
|
mir_func: &mut MirFunction,
|
||||||
|
block_id: BasicBlockId,
|
||||||
|
instructions: Vec<MirInstruction>,
|
||||||
|
terminator: MirInstruction,
|
||||||
|
jump_args: Option<Vec<ValueId>>,
|
||||||
|
) {
|
||||||
|
let block = mir_func
|
||||||
|
.blocks
|
||||||
|
.entry(block_id)
|
||||||
|
.or_insert_with(|| BasicBlock::new(block_id));
|
||||||
|
block.instructions = instructions;
|
||||||
|
block.instruction_spans = vec![Span::unknown(); block.instructions.len()];
|
||||||
|
block.terminator = Some(terminator);
|
||||||
|
block.jump_args = jump_args;
|
||||||
|
}
|
||||||
@ -6,6 +6,7 @@ use nyash_rust::mir::join_ir::{
|
|||||||
normalized_pattern2_to_structured, BinOpKind, ConstValue, JoinContId, JoinFuncId,
|
normalized_pattern2_to_structured, BinOpKind, ConstValue, JoinContId, JoinFuncId,
|
||||||
JoinFunction, JoinInst, JoinIrPhase, JoinModule, MirLikeInst,
|
JoinFunction, JoinInst, JoinIrPhase, JoinModule, MirLikeInst,
|
||||||
};
|
};
|
||||||
|
use nyash_rust::mir::join_ir::normalized::dev_env::NormalizedDevEnvGuard;
|
||||||
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_atoi_structured_for_normalized_dev,
|
||||||
build_jsonparser_skip_ws_structured_for_normalized_dev,
|
build_jsonparser_skip_ws_structured_for_normalized_dev,
|
||||||
@ -15,39 +16,6 @@ use nyash_rust::mir::join_ir_runner::run_joinir_function;
|
|||||||
use nyash_rust::mir::join_ir_ops::JoinValue;
|
use nyash_rust::mir::join_ir_ops::JoinValue;
|
||||||
use nyash_rust::mir::join_ir_vm_bridge::run_joinir_via_vm;
|
use nyash_rust::mir::join_ir_vm_bridge::run_joinir_via_vm;
|
||||||
use nyash_rust::mir::ValueId;
|
use nyash_rust::mir::ValueId;
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use std::sync::Mutex;
|
|
||||||
|
|
||||||
static NORMALIZED_ENV_LOCK: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
|
|
||||||
|
|
||||||
struct NormalizedRunGuard {
|
|
||||||
_lock: std::sync::MutexGuard<'static, ()>,
|
|
||||||
prev: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NormalizedRunGuard {
|
|
||||||
fn new(enabled: bool) -> Self {
|
|
||||||
let lock = NORMALIZED_ENV_LOCK.lock().expect("env mutex poisoned");
|
|
||||||
let prev = std::env::var("NYASH_JOINIR_NORMALIZED_DEV_RUN").ok();
|
|
||||||
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 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for NormalizedRunGuard {
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn assert_normalized_dev_ready() {
|
fn assert_normalized_dev_ready() {
|
||||||
assert!(
|
assert!(
|
||||||
nyash_rust::config::env::normalized_dev_enabled(),
|
nyash_rust::config::env::normalized_dev_enabled(),
|
||||||
@ -61,7 +29,7 @@ fn run_joinir_runner(
|
|||||||
args: &[JoinValue],
|
args: &[JoinValue],
|
||||||
normalized: bool,
|
normalized: bool,
|
||||||
) -> JoinValue {
|
) -> JoinValue {
|
||||||
let _guard = NormalizedRunGuard::new(normalized);
|
let _guard = NormalizedDevEnvGuard::new(normalized);
|
||||||
if normalized {
|
if normalized {
|
||||||
assert_normalized_dev_ready();
|
assert_normalized_dev_ready();
|
||||||
}
|
}
|
||||||
@ -75,7 +43,7 @@ fn run_joinir_vm_bridge(
|
|||||||
args: &[JoinValue],
|
args: &[JoinValue],
|
||||||
normalized: bool,
|
normalized: bool,
|
||||||
) -> JoinValue {
|
) -> JoinValue {
|
||||||
let _guard = NormalizedRunGuard::new(normalized);
|
let _guard = NormalizedDevEnvGuard::new(normalized);
|
||||||
if normalized {
|
if normalized {
|
||||||
assert_normalized_dev_ready();
|
assert_normalized_dev_ready();
|
||||||
}
|
}
|
||||||
@ -160,10 +128,8 @@ fn normalized_pattern1_exec_matches_structured() {
|
|||||||
let entry = structured.entry.unwrap_or(JoinFuncId::new(1));
|
let entry = structured.entry.unwrap_or(JoinFuncId::new(1));
|
||||||
let input = [JoinValue::Int(0)];
|
let input = [JoinValue::Int(0)];
|
||||||
|
|
||||||
let result_structured =
|
let result_structured = run_joinir_vm_bridge(&structured, entry, &input, false);
|
||||||
run_joinir_via_vm(&structured, entry, &input).expect("structured run should succeed");
|
let result_norm = run_joinir_vm_bridge(&reconstructed, entry, &input, false);
|
||||||
let result_norm = run_joinir_via_vm(&reconstructed, entry, &input)
|
|
||||||
.expect("normalized roundtrip run should succeed");
|
|
||||||
|
|
||||||
assert_eq!(result_structured, result_norm);
|
assert_eq!(result_structured, result_norm);
|
||||||
}
|
}
|
||||||
@ -180,9 +146,9 @@ fn normalized_pattern1_exec_matches_structured_roundtrip_backup() {
|
|||||||
let entry = structured.entry.unwrap_or(JoinFuncId::new(1));
|
let entry = structured.entry.unwrap_or(JoinFuncId::new(1));
|
||||||
let input = [JoinValue::Int(0)];
|
let input = [JoinValue::Int(0)];
|
||||||
|
|
||||||
let base = run_joinir_via_vm(&structured, entry, &input).expect("structured run");
|
let base = run_joinir_vm_bridge(&structured, entry, &input, false);
|
||||||
let recon = run_joinir_via_vm(&reconstructed, entry, &input).expect("reconstructed run");
|
let recon = run_joinir_vm_bridge(&reconstructed, entry, &input, false);
|
||||||
let restored = run_joinir_via_vm(&restored_backup, entry, &input).expect("backup run");
|
let restored = run_joinir_vm_bridge(&restored_backup, entry, &input, false);
|
||||||
|
|
||||||
assert_eq!(base, recon);
|
assert_eq!(base, recon);
|
||||||
assert_eq!(base, restored);
|
assert_eq!(base, restored);
|
||||||
@ -218,8 +184,8 @@ fn normalized_pattern2_exec_matches_structured() {
|
|||||||
let entry = structured.entry.unwrap_or(JoinFuncId::new(0));
|
let entry = structured.entry.unwrap_or(JoinFuncId::new(0));
|
||||||
let input = [JoinValue::Int(0)];
|
let input = [JoinValue::Int(0)];
|
||||||
|
|
||||||
let base = run_joinir_via_vm(&structured, entry, &input).expect("structured run");
|
let base = run_joinir_vm_bridge(&structured, entry, &input, false);
|
||||||
let recon = run_joinir_via_vm(&reconstructed, entry, &input).expect("normalized roundtrip run");
|
let recon = run_joinir_vm_bridge(&reconstructed, entry, &input, false);
|
||||||
|
|
||||||
assert_eq!(base, recon);
|
assert_eq!(base, recon);
|
||||||
}
|
}
|
||||||
@ -264,10 +230,8 @@ fn normalized_pattern2_real_loop_exec_matches_structured() {
|
|||||||
|
|
||||||
for n in cases {
|
for n in cases {
|
||||||
let input = [JoinValue::Int(n)];
|
let input = [JoinValue::Int(n)];
|
||||||
let base =
|
let base = run_joinir_vm_bridge(&structured, entry, &input, false);
|
||||||
run_joinir_via_vm(&structured, entry, &input).expect("structured execution should pass");
|
let recon = run_joinir_vm_bridge(&reconstructed, entry, &input, false);
|
||||||
let recon = run_joinir_via_vm(&reconstructed, entry, &input)
|
|
||||||
.expect("normalized roundtrip execution should pass");
|
|
||||||
|
|
||||||
assert_eq!(base, recon, "mismatch at n={}", n);
|
assert_eq!(base, recon, "mismatch at n={}", n);
|
||||||
let expected_sum = n * (n.saturating_sub(1)) / 2;
|
let expected_sum = n * (n.saturating_sub(1)) / 2;
|
||||||
|
|||||||
Reference in New Issue
Block a user