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")]
|
||||
pub mod fixtures;
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
pub mod dev_env;
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
pub(crate) mod shape_guard;
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
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,
|
||||
}
|
||||
|
||||
/// 直接 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> {
|
||||
let mut shapes = Vec::new();
|
||||
if is_jsonparser_atoi_mini(module) {
|
||||
@ -27,6 +32,11 @@ pub(crate) fn supported_shapes(module: &JoinModule) -> Vec<NormalizedDevShape> {
|
||||
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 {
|
||||
module.is_structured() && find_loop_step(module).is_some()
|
||||
}
|
||||
|
||||
@ -81,7 +81,7 @@ fn try_normalized_direct_bridge(
|
||||
module: &JoinModule,
|
||||
meta: &JoinFuncMetaMap,
|
||||
) -> Result<Option<MirModule>, JoinIrVmBridgeError> {
|
||||
let shapes = shape_guard::supported_shapes(module);
|
||||
let shapes = shape_guard::direct_shapes(module);
|
||||
if shapes.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
@ -9,10 +9,32 @@ use crate::mir::join_ir::{
|
||||
};
|
||||
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 専用)
|
||||
pub(crate) fn lower_normalized_to_mir_minimal(
|
||||
norm: &NormalizedModule,
|
||||
_meta: &JoinFuncMetaMap,
|
||||
meta: &JoinFuncMetaMap,
|
||||
) -> Result<MirModule, JoinIrVmBridgeError> {
|
||||
if norm.phase != JoinIrPhase::Normalized {
|
||||
return Err(JoinIrVmBridgeError::new(
|
||||
@ -49,14 +71,14 @@ pub(crate) fn lower_normalized_to_mir_minimal(
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
// direct 対象は Normalized → MIR をそのまま吐く。未対応 shape は Structured 経由にフォールバック。
|
||||
match direct::lower_normalized_direct_minimal(norm) {
|
||||
Ok(mir) => Ok(mir),
|
||||
Err(err) => {
|
||||
if crate::config::env::joinir_dev_enabled() {
|
||||
eprintln!("[joinir/normalized-bridge/fallback] direct path failed: {}; falling back to Structured path", err.message);
|
||||
}
|
||||
lower_normalized_via_structured(norm, 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,
|
||||
JoinFunction, JoinInst, JoinIrPhase, JoinModule, MirLikeInst,
|
||||
};
|
||||
use nyash_rust::mir::join_ir::normalized::dev_env::NormalizedDevEnvGuard;
|
||||
use nyash_rust::mir::join_ir::normalized::fixtures::{
|
||||
build_jsonparser_atoi_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_vm_bridge::run_joinir_via_vm;
|
||||
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() {
|
||||
assert!(
|
||||
nyash_rust::config::env::normalized_dev_enabled(),
|
||||
@ -61,7 +29,7 @@ fn run_joinir_runner(
|
||||
args: &[JoinValue],
|
||||
normalized: bool,
|
||||
) -> JoinValue {
|
||||
let _guard = NormalizedRunGuard::new(normalized);
|
||||
let _guard = NormalizedDevEnvGuard::new(normalized);
|
||||
if normalized {
|
||||
assert_normalized_dev_ready();
|
||||
}
|
||||
@ -75,7 +43,7 @@ fn run_joinir_vm_bridge(
|
||||
args: &[JoinValue],
|
||||
normalized: bool,
|
||||
) -> JoinValue {
|
||||
let _guard = NormalizedRunGuard::new(normalized);
|
||||
let _guard = NormalizedDevEnvGuard::new(normalized);
|
||||
if normalized {
|
||||
assert_normalized_dev_ready();
|
||||
}
|
||||
@ -160,10 +128,8 @@ fn normalized_pattern1_exec_matches_structured() {
|
||||
let entry = structured.entry.unwrap_or(JoinFuncId::new(1));
|
||||
let input = [JoinValue::Int(0)];
|
||||
|
||||
let result_structured =
|
||||
run_joinir_via_vm(&structured, entry, &input).expect("structured run should succeed");
|
||||
let result_norm = run_joinir_via_vm(&reconstructed, entry, &input)
|
||||
.expect("normalized roundtrip run should succeed");
|
||||
let result_structured = run_joinir_vm_bridge(&structured, entry, &input, false);
|
||||
let result_norm = run_joinir_vm_bridge(&reconstructed, entry, &input, false);
|
||||
|
||||
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 input = [JoinValue::Int(0)];
|
||||
|
||||
let base = run_joinir_via_vm(&structured, entry, &input).expect("structured run");
|
||||
let recon = run_joinir_via_vm(&reconstructed, entry, &input).expect("reconstructed run");
|
||||
let restored = run_joinir_via_vm(&restored_backup, entry, &input).expect("backup run");
|
||||
let base = run_joinir_vm_bridge(&structured, entry, &input, false);
|
||||
let recon = run_joinir_vm_bridge(&reconstructed, entry, &input, false);
|
||||
let restored = run_joinir_vm_bridge(&restored_backup, entry, &input, false);
|
||||
|
||||
assert_eq!(base, recon);
|
||||
assert_eq!(base, restored);
|
||||
@ -218,8 +184,8 @@ fn normalized_pattern2_exec_matches_structured() {
|
||||
let entry = structured.entry.unwrap_or(JoinFuncId::new(0));
|
||||
let input = [JoinValue::Int(0)];
|
||||
|
||||
let base = run_joinir_via_vm(&structured, entry, &input).expect("structured run");
|
||||
let recon = run_joinir_via_vm(&reconstructed, entry, &input).expect("normalized roundtrip run");
|
||||
let base = run_joinir_vm_bridge(&structured, entry, &input, false);
|
||||
let recon = run_joinir_vm_bridge(&reconstructed, entry, &input, false);
|
||||
|
||||
assert_eq!(base, recon);
|
||||
}
|
||||
@ -264,10 +230,8 @@ fn normalized_pattern2_real_loop_exec_matches_structured() {
|
||||
|
||||
for n in cases {
|
||||
let input = [JoinValue::Int(n)];
|
||||
let base =
|
||||
run_joinir_via_vm(&structured, entry, &input).expect("structured execution should pass");
|
||||
let recon = run_joinir_via_vm(&reconstructed, entry, &input)
|
||||
.expect("normalized roundtrip execution should pass");
|
||||
let base = run_joinir_vm_bridge(&structured, entry, &input, false);
|
||||
let recon = run_joinir_vm_bridge(&reconstructed, entry, &input, false);
|
||||
|
||||
assert_eq!(base, recon, "mismatch at n={}", n);
|
||||
let expected_sum = n * (n.saturating_sub(1)) / 2;
|
||||
|
||||
Reference in New Issue
Block a user