Normalize passes keep spans and clean warnings

This commit is contained in:
nyash-codex
2025-11-24 15:02:51 +09:00
parent 466e636af6
commit da1a5558e5
40 changed files with 547 additions and 362 deletions

View File

@ -148,9 +148,9 @@ impl MirInterpreter {
) -> Option<crate::ast::Span> { ) -> Option<crate::ast::Span> {
let idx = inst_index?; let idx = inst_index?;
if idx < block.instructions.len() { if idx < block.instructions.len() {
block.instruction_span(idx) block.instruction_with_span(idx).map(|sp| sp.span)
} else if idx == block.instructions.len() { } else if idx == block.instructions.len() {
block.terminator_span() block.terminator_spanned().map(|sp| sp.span)
} else { } else {
None None
} }
@ -334,9 +334,10 @@ impl MirInterpreter {
fn execute_block_instructions(&mut self, block: &BasicBlock) -> Result<(), VMError> { fn execute_block_instructions(&mut self, block: &BasicBlock) -> Result<(), VMError> {
let phi_count = block.phi_instructions().count(); let phi_count = block.phi_instructions().count();
for (idx, inst) in block.non_phi_instructions().enumerate() { for (idx, sp) in block.iter_spanned_enumerated().skip(phi_count) {
let inst = sp.inst;
self.last_block = Some(block.id); self.last_block = Some(block.id);
self.last_inst_index = Some(phi_count + idx); self.last_inst_index = Some(idx);
self.last_inst = Some(inst.clone()); self.last_inst = Some(inst.clone());
if Self::trace_enabled() { if Self::trace_enabled() {
eprintln!("[vm-trace] inst bb={:?} {:?}", block.id, inst); eprintln!("[vm-trace] inst bb={:?} {:?}", block.id, inst);

View File

@ -4,7 +4,7 @@
* SSA-form basic blocks with phi functions and terminator instructions * SSA-form basic blocks with phi functions and terminator instructions
*/ */
use super::{EffectMask, MirInstruction, SpannedInstruction, ValueId}; use super::{EffectMask, MirInstruction, SpannedInstRef, SpannedInstruction, ValueId};
use crate::ast::Span; use crate::ast::Span;
use std::collections::HashSet; use std::collections::HashSet;
use std::fmt; use std::fmt;
@ -239,11 +239,64 @@ impl BasicBlock {
self.instruction_spans.get(idx).copied() self.instruction_spans.get(idx).copied()
} }
/// Get instruction with span by index.
pub fn instruction_with_span(&self, idx: usize) -> Option<SpannedInstRef<'_>> {
self.instructions
.get(idx)
.zip(self.instruction_spans.get(idx))
.map(|(inst, span)| SpannedInstRef {
inst,
span: *span,
})
}
/// Get span for terminator instruction /// Get span for terminator instruction
pub fn terminator_span(&self) -> Option<Span> { pub fn terminator_span(&self) -> Option<Span> {
self.terminator_span self.terminator_span
} }
/// Get terminator together with its span.
pub fn terminator_spanned(&self) -> Option<SpannedInstRef<'_>> {
self.terminator.as_ref().map(|inst| SpannedInstRef {
inst,
span: self.terminator_span.unwrap_or_else(Span::unknown),
})
}
/// Iterate instructions with their spans.
pub fn iter_spanned(&self) -> impl Iterator<Item = SpannedInstRef<'_>> {
self.instructions
.iter()
.zip(self.instruction_spans.iter())
.map(|(inst, span)| SpannedInstRef {
inst,
span: *span,
})
}
/// Iterate instructions with index and span.
pub fn iter_spanned_enumerated(
&self,
) -> impl Iterator<Item = (usize, SpannedInstRef<'_>)> {
self.iter_spanned()
.enumerate()
.map(|(idx, sp)| (idx, sp))
}
/// Iterate all instructions (including terminator) with spans.
pub fn all_spanned_instructions(&self) -> impl Iterator<Item = SpannedInstRef<'_>> {
self.iter_spanned()
.chain(self.terminator_spanned().into_iter())
}
/// Iterate all instructions (including terminator) with index and span.
/// Non-phi + phi + terminator share the same indexing as `all_instructions()`.
pub fn all_spanned_instructions_enumerated(
&self,
) -> impl Iterator<Item = (usize, SpannedInstRef<'_>)> {
self.all_spanned_instructions().enumerate()
}
/// Insert instruction at the beginning (after phi instructions) /// Insert instruction at the beginning (after phi instructions)
pub fn insert_instruction_after_phis(&mut self, instruction: MirInstruction) { pub fn insert_instruction_after_phis(&mut self, instruction: MirInstruction) {
let phi_count = self.phi_instructions().count(); let phi_count = self.phi_instructions().count();

View File

@ -119,7 +119,13 @@ impl super::MirBuilder {
self.start_new_block(merge_block)?; self.start_new_block(merge_block)?;
// フェーズM: PHI はブロック先頭に配置cf_common 統一) // フェーズM: PHI はブロック先頭に配置cf_common 統一)
if let (Some(func), Some(cur_bb)) = (self.current_function.as_mut(), self.current_block) { if let (Some(func), Some(cur_bb)) = (self.current_function.as_mut(), self.current_block) {
crate::mir::ssot::cf_common::insert_phi_at_head(func, cur_bb, result_val, phi_inputs); crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
func,
cur_bb,
result_val,
phi_inputs,
self.current_span,
);
} else { } else {
self.emit_instruction(super::MirInstruction::Phi { self.emit_instruction(super::MirInstruction::Phi {
dst: result_val, dst: result_val,

View File

@ -83,8 +83,12 @@ impl MirBuilder {
if let (Some(func), Some(cur_bb)) = if let (Some(func), Some(cur_bb)) =
(self.current_function.as_mut(), self.current_block) (self.current_function.as_mut(), self.current_block)
{ {
crate::mir::ssot::cf_common::insert_phi_at_head( crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
func, cur_bb, merged, inputs, func,
cur_bb,
merged,
inputs,
self.current_span,
); );
} else { } else {
self.emit_instruction(MirInstruction::Phi { self.emit_instruction(MirInstruction::Phi {

View File

@ -7,7 +7,6 @@
//! Box-First理論: PHI insertion の境界を明確にし、差し替え可能な箱として提供 //! Box-First理論: PHI insertion の境界を明確にし、差し替え可能な箱として提供
use super::{BasicBlockId, MirBuilder, ValueId}; use super::{BasicBlockId, MirBuilder, ValueId};
use crate::mir::MirInstruction;
use std::collections::BTreeMap; // Phase 25.1: 決定性確保 use std::collections::BTreeMap; // Phase 25.1: 決定性確保
/// PHI Merge Helper - 統一PHI挿入ロジックConservative戦略 /// PHI Merge Helper - 統一PHI挿入ロジックConservative戦略

View File

@ -213,6 +213,8 @@ impl super::MirBuilder {
/// - While/ForRange は将来 Loop lowering へ委譲する拡張ポイントとして扱い、 /// - While/ForRange は将来 Loop lowering へ委譲する拡張ポイントとして扱い、
/// 現状は他の専用ビルダ/既存パスと同様に build_expression に委譲する。 /// 現状は他の専用ビルダ/既存パスと同様に build_expression に委譲する。
pub(super) fn build_statement(&mut self, node: ASTNode) -> Result<ValueId, String> { pub(super) fn build_statement(&mut self, node: ASTNode) -> Result<ValueId, String> {
// Align current_span to this statement node before lowering expressions under it.
self.current_span = node.span();
match node { match node {
// 将来ここに While / ForRange / Match / Using など statement 専用分岐を追加する。 // 将来ここに While / ForRange / Match / Using など statement 専用分岐を追加する。
other => self.build_expression(other), other => self.build_expression(other),

View File

@ -54,7 +54,6 @@ pub fn lower_min_loop_to_joinir(module: &crate::mir::MirModule) -> Option<JoinMo
let mut main_func = JoinFunction::new(main_id, "main".to_string(), vec![]); let mut main_func = JoinFunction::new(main_id, "main".to_string(), vec![]);
let i_init = ValueId(1000); // 固定 ValueId let i_init = ValueId(1000); // 固定 ValueId
let const_0 = ValueId(1001);
let const_1 = ValueId(1002); let const_1 = ValueId(1002);
let const_2 = ValueId(1003); let const_2 = ValueId(1003);

View File

@ -49,7 +49,6 @@ use crate::mir::join_ir::lowering::value_id_ranges::stage1_using_resolver as vid
use crate::mir::join_ir::JoinModule; use crate::mir::join_ir::JoinModule;
use crate::mir::loop_form::LoopForm; use crate::mir::loop_form::LoopForm;
use crate::mir::query::{MirQuery, MirQueryBox}; use crate::mir::query::{MirQuery, MirQueryBox};
use crate::mir::ValueId;
/// Phase 27.12: Stage1UsingResolverBox.resolve_for_source の JoinIR loweringpublic dispatcher /// Phase 27.12: Stage1UsingResolverBox.resolve_for_source の JoinIR loweringpublic dispatcher
/// ///

View File

@ -22,7 +22,7 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use crate::mir::{BasicBlockId, ValueId}; use crate::mir::ValueId;
// Phase 27.9: Lowering submodule // Phase 27.9: Lowering submodule
pub mod lowering; pub mod lowering;

View File

@ -31,7 +31,6 @@ use crate::mir::{
BasicBlockId, BinaryOp, CompareOp as MirCompareOp, ConstValue as MirConstValue, EffectMask, BasicBlockId, BinaryOp, CompareOp as MirCompareOp, ConstValue as MirConstValue, EffectMask,
FunctionSignature, MirFunction, MirInstruction, MirModule, MirType, ValueId, FunctionSignature, MirFunction, MirInstruction, MirModule, MirType, ValueId,
}; };
use std::collections::HashMap;
/// Phase 27-shortterm S-4 エラー型 /// Phase 27-shortterm S-4 エラー型
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View File

@ -134,17 +134,14 @@ impl LoopBuilderApi for super::builder::MirBuilder {
inputs: Vec<(BasicBlockId, ValueId)>, inputs: Vec<(BasicBlockId, ValueId)>,
) -> Result<(), String> { ) -> Result<(), String> {
if let Some(ref mut f) = self.current_function { if let Some(ref mut f) = self.current_function {
if let Some(bb) = f.get_block_mut(block) { crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
let inst = MirInstruction::Phi { dst, inputs }; f,
// Update effect mask and insert at the very start block,
bb.effects = bb.effects | inst.effects(); dst,
bb.instructions.insert(0, inst); inputs,
bb.instruction_spans self.current_span,
.insert(0, self.current_span); );
Ok(()) Ok(())
} else {
Err(format!("Block {} not found", block.as_u32()))
}
} else { } else {
Err("No current function".into()) Err("No current function".into())
} }

View File

@ -23,7 +23,6 @@
use super::{BasicBlockId, ConstValue, MirInstruction, ValueId}; use super::{BasicBlockId, ConstValue, MirInstruction, ValueId};
use crate::ast::ASTNode; use crate::ast::ASTNode;
use crate::mir::control_form::{is_control_form_trace_on, ControlForm, IfShape, LoopShape}; use crate::mir::control_form::{is_control_form_trace_on, ControlForm, IfShape, LoopShape};
use crate::mir::phi_core::loop_snapshot_merge::LoopSnapshotMergeBox;
use crate::mir::phi_core::loopform_builder::{LoopFormBuilder, LoopFormOps}; use crate::mir::phi_core::loopform_builder::{LoopFormBuilder, LoopFormOps};
use crate::mir::phi_core::phi_input_collector::PhiInputCollector; use crate::mir::phi_core::phi_input_collector::PhiInputCollector;
use std::collections::{BTreeMap, BTreeSet}; // Phase 25.1: 決定性確保 use std::collections::{BTreeMap, BTreeSet}; // Phase 25.1: 決定性確保
@ -1049,7 +1048,7 @@ impl<'a> LoopBuilder<'a> {
} }
let then_var_map_end = self.get_current_variable_map(); let then_var_map_end = self.get_current_variable_map();
// フェーズS修正最強モード指摘の「実到達predecessor捕捉」を統一 // フェーズS修正最強モード指摘の「実到達predecessor捕捉」を統一
let then_pred_to_merge = let _then_pred_to_merge =
capture_actual_predecessor_and_jump(self.parent_builder, merge_bb)?; capture_actual_predecessor_and_jump(self.parent_builder, merge_bb)?;
// Pop then-branch debug region // Pop then-branch debug region
self.parent_builder.debug_pop_region(); self.parent_builder.debug_pop_region();
@ -1093,7 +1092,7 @@ impl<'a> LoopBuilder<'a> {
else_var_map_end_opt = Some(self.get_current_variable_map()); else_var_map_end_opt = Some(self.get_current_variable_map());
} }
// フェーズS修正else branchでも統一実到達predecessor捕捉 // フェーズS修正else branchでも統一実到達predecessor捕捉
let else_pred_to_merge = let _else_pred_to_merge =
capture_actual_predecessor_and_jump(self.parent_builder, merge_bb)?; capture_actual_predecessor_and_jump(self.parent_builder, merge_bb)?;
// Pop else-branch debug region // Pop else-branch debug region
self.parent_builder.debug_pop_region(); self.parent_builder.debug_pop_region();
@ -1315,7 +1314,10 @@ impl crate::mir::phi_core::loop_phi::LoopPhiOps for LoopBuilder<'_> {
preheader_id preheader_id
); );
} }
block.add_instruction(MirInstruction::Copy { dst, src }); block.add_instruction_with_span(
MirInstruction::Copy { dst, src },
self.parent_builder.current_span,
);
Ok(()) Ok(())
} else { } else {
if dbg { if dbg {

View File

@ -56,7 +56,7 @@ pub use instruction::MirInstruction;
pub use join_ir_runner::{run_joinir_function, JoinRuntimeError, JoinValue}; pub use join_ir_runner::{run_joinir_function, JoinRuntimeError, JoinValue};
pub use optimizer::MirOptimizer; pub use optimizer::MirOptimizer;
pub use printer::MirPrinter; pub use printer::MirPrinter;
pub use spanned_instruction::SpannedInstruction; pub use spanned_instruction::{SpannedInstRef, SpannedInstruction};
pub use query::{MirQuery, MirQueryBox}; pub use query::{MirQuery, MirQueryBox};
pub use slot_registry::{BoxTypeId, MethodSlot}; pub use slot_registry::{BoxTypeId, MethodSlot};
pub use types::{ pub use types::{
@ -229,8 +229,8 @@ mod tests {
// Ensure TypeOp exists in the resulting MIR // Ensure TypeOp exists in the resulting MIR
let has_typeop = result.module.functions.values().any(|f| { let has_typeop = result.module.functions.values().any(|f| {
f.blocks.values().any(|b| { f.blocks.values().any(|b| {
b.all_instructions() b.all_spanned_instructions()
.any(|i| matches!(i, MirInstruction::TypeOp { .. })) .any(|sp| matches!(sp.inst, MirInstruction::TypeOp { .. }))
}) })
}); });
assert!( assert!(
@ -264,8 +264,8 @@ mod tests {
// Ensure TypeOp exists in the resulting MIR // Ensure TypeOp exists in the resulting MIR
let has_typeop = result.module.functions.values().any(|f| { let has_typeop = result.module.functions.values().any(|f| {
f.blocks.values().any(|b| { f.blocks.values().any(|b| {
b.all_instructions() b.all_spanned_instructions()
.any(|i| matches!(i, MirInstruction::TypeOp { .. })) .any(|sp| matches!(sp.inst, MirInstruction::TypeOp { .. }))
}) })
}); });
assert!( assert!(

View File

@ -508,8 +508,8 @@ mod tests {
let f = module.get_function("main").unwrap(); let f = module.get_function("main").unwrap();
let block = f.get_block(bb0).unwrap(); let block = f.get_block(bb0).unwrap();
let has_typeop = block let has_typeop = block
.all_instructions() .all_spanned_instructions()
.any(|i| matches!(i, MirInstruction::TypeOp { .. })); .any(|sp| matches!(sp.inst, MirInstruction::TypeOp { .. }));
assert!( assert!(
has_typeop, has_typeop,
"TypeOp should not be dropped by DCE when used by console.log (ExternCall)" "TypeOp should not be dropped by DCE when used by console.log (ExternCall)"

View File

@ -1,7 +1,9 @@
use crate::ast::Span;
use crate::mir::optimizer::MirOptimizer; use crate::mir::optimizer::MirOptimizer;
use crate::mir::optimizer_stats::OptimizationStats; use crate::mir::optimizer_stats::OptimizationStats;
use crate::mir::{BarrierOp, MirModule, SpannedInstruction, TypeOpKind, ValueId, WeakRefOp}; use crate::mir::{
BarrierOp, EffectMask, MirModule, SpannedInstruction, TypeOpKind, ValueId, WeakRefOp,
};
use crate::ast::Span;
fn idemp_enabled() -> bool { fn idemp_enabled() -> bool {
std::env::var("NYASH_MIR_DEV_IDEMP").ok().as_deref() == Some("1") std::env::var("NYASH_MIR_DEV_IDEMP").ok().as_deref() == Some("1")
@ -36,26 +38,46 @@ pub fn force_plugin_invoke(_opt: &mut MirOptimizer, module: &mut MirModule) -> O
None => continue, None => continue,
}; };
for (_bb, block) in &mut function.blocks { for (_bb, block) in &mut function.blocks {
for inst in &mut block.instructions { let old_spanned = block.drain_spanned_instructions();
if let I::BoxCall { let mut new_spanned = Vec::with_capacity(old_spanned.len());
for SpannedInstruction { inst, span } in old_spanned {
let rewritten = match inst {
I::BoxCall {
dst, dst,
box_val, box_val,
method, method,
args, args,
effects, effects,
.. ..
} = inst.clone() } => {
{ stats.intrinsic_optimizations += 1;
*inst = I::PluginInvoke { I::PluginInvoke {
dst, dst,
box_val, box_val,
method, method,
args, args,
effects, effects,
}
}
other => other,
}; };
stats.intrinsic_optimizations += 1; new_spanned.push(SpannedInstruction {
} inst: rewritten,
span,
});
} }
let (insts, spans): (Vec<_>, Vec<_>) = new_spanned
.into_iter()
.map(|sp| (sp.inst, sp.span))
.unzip();
block.instructions = insts;
block.instruction_spans = spans;
block.effects = block
.instructions
.iter()
.chain(block.terminator.iter())
.fold(EffectMask::PURE, |mask, inst| mask | inst.effects());
} }
if idemp_enabled() { if idemp_enabled() {
let key = idemp_key(pass_name, &fname); let key = idemp_key(pass_name, &fname);
@ -85,11 +107,13 @@ pub fn normalize_python_helper_calls(
None => continue, None => continue,
}; };
for (_bb, block) in &mut function.blocks { for (_bb, block) in &mut function.blocks {
for inst in &mut block.instructions { let old_spanned = block.drain_spanned_instructions();
let mut new_spanned = Vec::with_capacity(old_spanned.len());
for SpannedInstruction { mut inst, span } in old_spanned {
if let I::PluginInvoke { if let I::PluginInvoke {
box_val, ref mut box_val,
method, ref method,
args, ref mut args,
.. ..
} = inst } = inst
{ {
@ -105,7 +129,20 @@ pub fn normalize_python_helper_calls(
stats.intrinsic_optimizations += 1; stats.intrinsic_optimizations += 1;
} }
} }
new_spanned.push(SpannedInstruction { inst, span });
} }
let (insts, spans): (Vec<_>, Vec<_>) = new_spanned
.into_iter()
.map(|sp| (sp.inst, sp.span))
.unzip();
block.instructions = insts;
block.instruction_spans = spans;
block.effects = block
.instructions
.iter()
.chain(block.terminator.iter())
.fold(EffectMask::PURE, |mask, inst| mask | inst.effects());
} }
if idemp_enabled() { if idemp_enabled() {
let key = idemp_key(pass_name, &fname); let key = idemp_key(pass_name, &fname);
@ -143,90 +180,79 @@ pub fn normalize_legacy_instructions(
None => continue, None => continue,
}; };
for (_bb, block) in &mut function.blocks { for (_bb, block) in &mut function.blocks {
for inst in &mut block.instructions { let old_spanned = block.drain_spanned_instructions();
match inst { let mut new_spanned = Vec::with_capacity(old_spanned.len());
for SpannedInstruction { inst, span } in old_spanned {
let rewritten = match inst {
I::WeakNew { dst, box_val } => { I::WeakNew { dst, box_val } => {
let d = *dst;
let v = *box_val;
*inst = I::WeakRef {
dst: d,
op: WeakRefOp::New,
value: v,
};
stats.intrinsic_optimizations += 1; stats.intrinsic_optimizations += 1;
I::WeakRef {
dst,
op: WeakRefOp::New,
value: box_val,
}
} }
I::WeakLoad { dst, weak_ref } => { I::WeakLoad { dst, weak_ref } => {
let d = *dst;
let v = *weak_ref;
*inst = I::WeakRef {
dst: d,
op: WeakRefOp::Load,
value: v,
};
stats.intrinsic_optimizations += 1; stats.intrinsic_optimizations += 1;
I::WeakRef {
dst,
op: WeakRefOp::Load,
value: weak_ref,
}
} }
I::BarrierRead { ptr } => { I::BarrierRead { ptr } => {
let p = *ptr;
*inst = I::Barrier {
op: BarrierOp::Read,
ptr: p,
};
stats.intrinsic_optimizations += 1; stats.intrinsic_optimizations += 1;
I::Barrier {
op: BarrierOp::Read,
ptr,
}
} }
I::BarrierWrite { ptr } => { I::BarrierWrite { ptr } => {
let p = *ptr;
*inst = I::Barrier {
op: BarrierOp::Write,
ptr: p,
};
stats.intrinsic_optimizations += 1; stats.intrinsic_optimizations += 1;
I::Barrier {
op: BarrierOp::Write,
ptr,
}
} }
I::Print { value, .. } => { I::Print { value, .. } => {
let v = *value; stats.intrinsic_optimizations += 1;
*inst = I::ExternCall { I::ExternCall {
dst: None, dst: None,
iface_name: "env.console".to_string(), iface_name: "env.console".to_string(),
method_name: "log".to_string(), method_name: "log".to_string(),
args: vec![v], args: vec![value],
effects: crate::mir::EffectMask::PURE.add(crate::mir::Effect::Io), effects: crate::mir::EffectMask::PURE.add(crate::mir::Effect::Io),
}; }
stats.intrinsic_optimizations += 1;
} }
I::ArrayGet { dst, array, index } if array_to_boxcall => { I::ArrayGet { dst, array, index } if array_to_boxcall => {
let d = *dst; stats.intrinsic_optimizations += 1;
let a = *array;
let i = *index;
let mid = let mid =
crate::mir::slot_registry::resolve_slot_by_type_name("ArrayBox", "get"); crate::mir::slot_registry::resolve_slot_by_type_name("ArrayBox", "get");
*inst = I::BoxCall { I::BoxCall {
dst: Some(d), dst: Some(dst),
box_val: a, box_val: array,
method: "get".to_string(), method: "get".to_string(),
method_id: mid, method_id: mid,
args: vec![i], args: vec![index],
effects: crate::mir::EffectMask::READ, effects: crate::mir::EffectMask::READ,
}; }
stats.intrinsic_optimizations += 1;
} }
I::ArraySet { I::ArraySet {
array, array,
index, index,
value, value,
} if array_to_boxcall => { } if array_to_boxcall => {
let a = *array; stats.intrinsic_optimizations += 1;
let i = *index;
let v = *value;
let mid = let mid =
crate::mir::slot_registry::resolve_slot_by_type_name("ArrayBox", "set"); crate::mir::slot_registry::resolve_slot_by_type_name("ArrayBox", "set");
*inst = I::BoxCall { I::BoxCall {
dst: None, dst: None,
box_val: a, box_val: array,
method: "set".to_string(), method: "set".to_string(),
method_id: mid, method_id: mid,
args: vec![i, v], args: vec![index, value],
effects: crate::mir::EffectMask::WRITE, effects: crate::mir::EffectMask::WRITE,
}; }
stats.intrinsic_optimizations += 1;
} }
I::PluginInvoke { I::PluginInvoke {
dst, dst,
@ -235,176 +261,163 @@ pub fn normalize_legacy_instructions(
args, args,
effects, effects,
} => { } => {
let d = *dst;
let recv = *box_val;
let m = method.clone();
let as_ = args.clone();
let eff = *effects;
*inst = I::BoxCall {
dst: d,
box_val: recv,
method: m,
method_id: None,
args: as_,
effects: eff,
};
stats.intrinsic_optimizations += 1; stats.intrinsic_optimizations += 1;
I::BoxCall {
dst,
box_val,
method,
method_id: None,
args,
effects,
} }
I::Debug { .. } if !rw_dbg => {
*inst = I::Nop;
} }
I::Safepoint if !rw_sp => { I::Debug { .. } if !rw_dbg => I::Nop,
*inst = I::Nop; I::Safepoint if !rw_sp => I::Nop,
} I::FutureNew { dst, value } if rw_future => I::ExternCall {
I::FutureNew { dst, value } if rw_future => { dst: Some(dst),
let d = *dst;
let v = *value;
*inst = I::ExternCall {
dst: Some(d),
iface_name: "env.future".to_string(), iface_name: "env.future".to_string(),
method_name: "new".to_string(), method_name: "new".to_string(),
args: vec![v], args: vec![value],
effects: crate::mir::EffectMask::PURE.add(crate::mir::Effect::Io), effects: crate::mir::EffectMask::PURE.add(crate::mir::Effect::Io),
}; },
} I::FutureSet { future, value } if rw_future => I::ExternCall {
I::FutureSet { future, value } if rw_future => {
let f = *future;
let v = *value;
*inst = I::ExternCall {
dst: None, dst: None,
iface_name: "env.future".to_string(), iface_name: "env.future".to_string(),
method_name: "set".to_string(), method_name: "set".to_string(),
args: vec![f, v], args: vec![future, value],
effects: crate::mir::EffectMask::PURE.add(crate::mir::Effect::Io), effects: crate::mir::EffectMask::PURE.add(crate::mir::Effect::Io),
}; },
} I::Await { dst, future } if rw_future => I::ExternCall {
I::Await { dst, future } if rw_future => { dst: Some(dst),
let d = *dst;
let f = *future;
*inst = I::ExternCall {
dst: Some(d),
iface_name: "env.future".to_string(), iface_name: "env.future".to_string(),
method_name: "await".to_string(), method_name: "await".to_string(),
args: vec![f], args: vec![future],
effects: crate::mir::EffectMask::PURE.add(crate::mir::Effect::Io), effects: crate::mir::EffectMask::PURE.add(crate::mir::Effect::Io),
},
other => other,
}; };
new_spanned.push(SpannedInstruction {
inst: rewritten,
span,
});
} }
_ => {}
} let terminator = block.terminator.take();
} let terminator_span = block.terminator_span.take();
// terminator rewrite (subset migrated as needed) if let Some(term) = terminator {
if let Some(term) = &mut block.terminator { let span = terminator_span.unwrap_or_else(Span::unknown);
match term { let rewritten = match term {
I::TypeCheck { I::TypeCheck {
dst, dst,
value, value,
expected_type, expected_type,
} => { } => {
let ty = crate::mir::MirType::Box(expected_type.clone()); let ty = crate::mir::MirType::Box(expected_type);
*term = I::TypeOp {
dst: *dst,
op: TypeOpKind::Check,
value: *value,
ty,
};
stats.intrinsic_optimizations += 1; stats.intrinsic_optimizations += 1;
I::TypeOp {
dst,
op: TypeOpKind::Check,
value,
ty,
}
} }
I::Cast { I::Cast {
dst, dst,
value, value,
target_type, target_type,
} => { } => {
let ty = target_type.clone(); let ty = target_type;
*term = I::TypeOp {
dst: *dst,
op: TypeOpKind::Cast,
value: *value,
ty,
};
stats.intrinsic_optimizations += 1; stats.intrinsic_optimizations += 1;
I::TypeOp {
dst,
op: TypeOpKind::Cast,
value,
ty,
}
} }
I::WeakNew { dst, box_val } => { I::WeakNew { dst, box_val } => {
let d = *dst;
let v = *box_val;
*term = I::WeakRef {
dst: d,
op: WeakRefOp::New,
value: v,
};
stats.intrinsic_optimizations += 1; stats.intrinsic_optimizations += 1;
I::WeakRef {
dst,
op: WeakRefOp::New,
value: box_val,
}
} }
I::WeakLoad { dst, weak_ref } => { I::WeakLoad { dst, weak_ref } => {
let d = *dst;
let v = *weak_ref;
*term = I::WeakRef {
dst: d,
op: WeakRefOp::Load,
value: v,
};
stats.intrinsic_optimizations += 1; stats.intrinsic_optimizations += 1;
I::WeakRef {
dst,
op: WeakRefOp::Load,
value: weak_ref,
}
} }
I::BarrierRead { ptr } => { I::BarrierRead { ptr } => {
let p = *ptr;
*term = I::Barrier {
op: BarrierOp::Read,
ptr: p,
};
stats.intrinsic_optimizations += 1; stats.intrinsic_optimizations += 1;
I::Barrier {
op: BarrierOp::Read,
ptr,
}
} }
I::BarrierWrite { ptr } => { I::BarrierWrite { ptr } => {
let p = *ptr;
*term = I::Barrier {
op: BarrierOp::Write,
ptr: p,
};
stats.intrinsic_optimizations += 1; stats.intrinsic_optimizations += 1;
I::Barrier {
op: BarrierOp::Write,
ptr,
}
} }
I::Print { value, .. } => { I::Print { value, .. } => {
let v = *value; stats.intrinsic_optimizations += 1;
*term = I::ExternCall { I::ExternCall {
dst: None, dst: None,
iface_name: "env.console".to_string(), iface_name: "env.console".to_string(),
method_name: "log".to_string(), method_name: "log".to_string(),
args: vec![v], args: vec![value],
effects: crate::mir::EffectMask::PURE, effects: crate::mir::EffectMask::PURE,
}; }
stats.intrinsic_optimizations += 1;
} }
I::ArrayGet { dst, array, index } if array_to_boxcall => { I::ArrayGet { dst, array, index } if array_to_boxcall => {
let d = *dst; stats.intrinsic_optimizations += 1;
let a = *array; I::BoxCall {
let i = *index; dst: Some(dst),
*term = I::BoxCall { box_val: array,
dst: Some(d),
box_val: a,
method: "get".to_string(), method: "get".to_string(),
method_id: None, method_id: None,
args: vec![i], args: vec![index],
effects: crate::mir::EffectMask::READ, effects: crate::mir::EffectMask::READ,
}; }
stats.intrinsic_optimizations += 1;
} }
I::ArraySet { I::ArraySet {
array, array,
index, index,
value, value,
} if array_to_boxcall => { } if array_to_boxcall => {
let a = *array; stats.intrinsic_optimizations += 1;
let i = *index; I::BoxCall {
let v = *value;
*term = I::BoxCall {
dst: None, dst: None,
box_val: a, box_val: array,
method: "set".to_string(), method: "set".to_string(),
method_id: None, method_id: None,
args: vec![i, v], args: vec![index, value],
effects: crate::mir::EffectMask::WRITE, effects: crate::mir::EffectMask::WRITE,
}
}
other => other,
}; };
stats.intrinsic_optimizations += 1; block.terminator = Some(rewritten);
} block.terminator_span = Some(span);
_ => {}
}
} }
let (insts, spans): (Vec<_>, Vec<_>) = new_spanned
.into_iter()
.map(|sp| (sp.inst, sp.span))
.unzip();
block.instructions = insts;
block.instruction_spans = spans;
block.effects = block
.instructions
.iter()
.chain(block.terminator.iter())
.fold(EffectMask::PURE, |mask, inst| mask | inst.effects());
} }
if idemp_enabled() { if idemp_enabled() {
let key = idemp_key(pass_name, &fname); let key = idemp_key(pass_name, &fname);
@ -555,6 +568,12 @@ pub fn normalize_ref_field_access(
block.terminator_span = Some(term_span); block.terminator_span = Some(term_span);
} }
} }
block.effects = block
.instructions
.iter()
.chain(block.terminator.iter())
.fold(EffectMask::PURE, |mask, inst| mask | inst.effects());
} }
if idemp_enabled() { if idemp_enabled() {
let key = idemp_key(pass_name, &fname); let key = idemp_key(pass_name, &fname);

View File

@ -1,7 +1,9 @@
use crate::ast::Span; use crate::ast::Span;
use crate::mir::optimizer::MirOptimizer; use crate::mir::optimizer::MirOptimizer;
use crate::mir::optimizer_stats::OptimizationStats; use crate::mir::optimizer_stats::OptimizationStats;
use crate::mir::{BinaryOp, CompareOp, EffectMask, MirInstruction as I, MirModule, ValueId}; use crate::mir::{
BinaryOp, CompareOp, EffectMask, MirInstruction as I, MirModule, SpannedInstruction, ValueId,
};
/// Core-13 "pure" normalization: rewrite a few non-13 ops to allowed forms. /// Core-13 "pure" normalization: rewrite a few non-13 ops to allowed forms.
/// - Load(dst, ptr) => ExternCall(Some dst, env.local.get, [ptr]) /// - Load(dst, ptr) => ExternCall(Some dst, env.local.get, [ptr])
@ -16,33 +18,34 @@ pub fn normalize_pure_core13(_opt: &mut MirOptimizer, module: &mut MirModule) ->
let mut stats = OptimizationStats::new(); let mut stats = OptimizationStats::new();
for (_fname, function) in &mut module.functions { for (_fname, function) in &mut module.functions {
for (_bb, block) in &mut function.blocks { for (_bb, block) in &mut function.blocks {
let mut out: Vec<I> = Vec::with_capacity(block.instructions.len() + 8); let old = block.drain_spanned_instructions();
let mut out_spans: Vec<Span> = Vec::with_capacity(block.instructions.len() + 8); let mut out: Vec<SpannedInstruction> = Vec::with_capacity(old.len() + 8);
let old = std::mem::take(&mut block.instructions); for SpannedInstruction { inst, span } in old.into_iter() {
let mut old_spans_iter = std::mem::take(&mut block.instruction_spans).into_iter();
for inst in old.into_iter() {
let span = old_spans_iter.next().unwrap_or_else(Span::unknown);
match inst { match inst {
I::Load { dst, ptr } => { I::Load { dst, ptr } => {
out.push(I::ExternCall { out.push(SpannedInstruction {
inst: I::ExternCall {
dst: Some(dst), dst: Some(dst),
iface_name: "env.local".to_string(), iface_name: "env.local".to_string(),
method_name: "get".to_string(), method_name: "get".to_string(),
args: vec![ptr], args: vec![ptr],
effects: EffectMask::READ, effects: EffectMask::READ,
},
span,
}); });
out_spans.push(span);
stats.intrinsic_optimizations += 1; stats.intrinsic_optimizations += 1;
} }
I::Store { value, ptr } => { I::Store { value, ptr } => {
out.push(I::ExternCall { out.push(SpannedInstruction {
inst: I::ExternCall {
dst: None, dst: None,
iface_name: "env.local".to_string(), iface_name: "env.local".to_string(),
method_name: "set".to_string(), method_name: "set".to_string(),
args: vec![ptr, value], args: vec![ptr, value],
effects: EffectMask::WRITE, effects: EffectMask::WRITE,
},
span,
}); });
out_spans.push(span);
stats.intrinsic_optimizations += 1; stats.intrinsic_optimizations += 1;
} }
I::NewBox { I::NewBox {
@ -53,22 +56,26 @@ pub fn normalize_pure_core13(_opt: &mut MirOptimizer, module: &mut MirModule) ->
// prepend type name as Const String // prepend type name as Const String
let ty_id = ValueId::new(function.next_value_id); let ty_id = ValueId::new(function.next_value_id);
function.next_value_id += 1; function.next_value_id += 1;
out.push(I::Const { out.push(SpannedInstruction {
inst: I::Const {
dst: ty_id, dst: ty_id,
value: crate::mir::ConstValue::String(box_type), value: crate::mir::ConstValue::String(box_type),
},
span,
}); });
out_spans.push(span);
let mut call_args = Vec::with_capacity(1 + args.len()); let mut call_args = Vec::with_capacity(1 + args.len());
call_args.push(ty_id); call_args.push(ty_id);
call_args.append(&mut args); call_args.append(&mut args);
out.push(I::ExternCall { out.push(SpannedInstruction {
inst: I::ExternCall {
dst: Some(dst), dst: Some(dst),
iface_name: "env.box".to_string(), iface_name: "env.box".to_string(),
method_name: "new".to_string(), method_name: "new".to_string(),
args: call_args, args: call_args,
effects: EffectMask::PURE, // constructor is logically alloc; conservatively PURE here effects: EffectMask::PURE, // constructor is logically alloc; conservatively PURE here
},
span,
}); });
out_spans.push(span);
stats.intrinsic_optimizations += 1; stats.intrinsic_optimizations += 1;
} }
I::UnaryOp { dst, op, operand } => { I::UnaryOp { dst, op, operand } => {
@ -76,62 +83,71 @@ pub fn normalize_pure_core13(_opt: &mut MirOptimizer, module: &mut MirModule) ->
crate::mir::UnaryOp::Neg => { crate::mir::UnaryOp::Neg => {
let zero = ValueId::new(function.next_value_id); let zero = ValueId::new(function.next_value_id);
function.next_value_id += 1; function.next_value_id += 1;
out.push(I::Const { out.push(SpannedInstruction {
inst: I::Const {
dst: zero, dst: zero,
value: crate::mir::ConstValue::Integer(0), value: crate::mir::ConstValue::Integer(0),
},
span,
}); });
out_spans.push(span); out.push(SpannedInstruction {
out.push(I::BinOp { inst: I::BinOp {
dst, dst,
op: BinaryOp::Sub, op: BinaryOp::Sub,
lhs: zero, lhs: zero,
rhs: operand, rhs: operand,
},
span,
}); });
out_spans.push(span);
} }
crate::mir::UnaryOp::Not => { crate::mir::UnaryOp::Not => {
let f = ValueId::new(function.next_value_id); let f = ValueId::new(function.next_value_id);
function.next_value_id += 1; function.next_value_id += 1;
out.push(I::Const { out.push(SpannedInstruction {
inst: I::Const {
dst: f, dst: f,
value: crate::mir::ConstValue::Bool(false), value: crate::mir::ConstValue::Bool(false),
},
span,
}); });
out_spans.push(span); out.push(SpannedInstruction {
out.push(I::Compare { inst: I::Compare {
dst, dst,
op: CompareOp::Eq, op: CompareOp::Eq,
lhs: operand, lhs: operand,
rhs: f, rhs: f,
},
span,
}); });
out_spans.push(span);
} }
crate::mir::UnaryOp::BitNot => { crate::mir::UnaryOp::BitNot => {
let all1 = ValueId::new(function.next_value_id); let all1 = ValueId::new(function.next_value_id);
function.next_value_id += 1; function.next_value_id += 1;
out.push(I::Const { out.push(SpannedInstruction {
inst: I::Const {
dst: all1, dst: all1,
value: crate::mir::ConstValue::Integer(-1), value: crate::mir::ConstValue::Integer(-1),
},
span,
}); });
out_spans.push(span); out.push(SpannedInstruction {
out.push(I::BinOp { inst: I::BinOp {
dst, dst,
op: BinaryOp::BitXor, op: BinaryOp::BitXor,
lhs: operand, lhs: operand,
rhs: all1, rhs: all1,
},
span,
}); });
out_spans.push(span);
} }
} }
stats.intrinsic_optimizations += 1; stats.intrinsic_optimizations += 1;
} }
other => { other => out.push(SpannedInstruction { inst: other, span }),
out.push(other);
out_spans.push(span);
} }
} }
} block.instructions = out.iter().map(|s| s.inst.clone()).collect();
block.instructions = out; block.instruction_spans = out.iter().map(|s| s.span).collect();
block.instruction_spans = out_spans;
if let Some(term) = block.terminator.take() { if let Some(term) = block.terminator.take() {
let term_span = block.terminator_span.take().unwrap_or_else(Span::unknown); let term_span = block.terminator_span.take().unwrap_or_else(Span::unknown);

View File

@ -41,8 +41,8 @@ fn analyze_function(func: &MirFunction) -> EscapeInfo {
let mut info = EscapeInfo::default(); let mut info = EscapeInfo::default();
// Collect local boxes: results of NewBox in this function // Collect local boxes: results of NewBox in this function
for block in func.blocks.values() { for block in func.blocks.values() {
for ins in block.instructions.iter() { for sp in block.iter_spanned() {
if let MirInstruction::NewBox { dst, .. } = ins { if let MirInstruction::NewBox { dst, .. } = sp.inst {
info.local_boxes.insert(*dst); info.local_boxes.insert(*dst);
} }
} }
@ -54,8 +54,8 @@ fn analyze_function(func: &MirFunction) -> EscapeInfo {
} }
// Conservative escape marking // Conservative escape marking
for block in func.blocks.values() { for block in func.blocks.values() {
for ins in block.all_instructions() { for sp in block.all_spanned_instructions() {
match ins { match sp.inst {
MirInstruction::Return { value: Some(v) } => { MirInstruction::Return { value: Some(v) } => {
if info.local_boxes.contains(v) { if info.local_boxes.contains(v) {
info.escaping.insert(*v); info.escaping.insert(*v);

View File

@ -158,8 +158,8 @@ impl MirPrinter {
let mut barrier_read = 0usize; let mut barrier_read = 0usize;
let mut barrier_write = 0usize; let mut barrier_write = 0usize;
for block in function.blocks.values() { for block in function.blocks.values() {
for inst in &block.instructions { for sp in block.iter_spanned() {
match inst { match sp.inst {
MirInstruction::Throw { .. } => { MirInstruction::Throw { .. } => {
if dlog::on("NYASH_DEBUG_MIR_PRINTER") { if dlog::on("NYASH_DEBUG_MIR_PRINTER") {
eprintln!("[PRINTER] found throw in {}", function.signature.name); eprintln!("[PRINTER] found throw in {}", function.signature.name);
@ -191,8 +191,8 @@ impl MirPrinter {
_ => {} _ => {}
} }
} }
if let Some(term) = &block.terminator { if let Some(sp) = block.terminator_spanned() {
match term { match sp.inst {
MirInstruction::Throw { .. } => { MirInstruction::Throw { .. } => {
if dlog::on("NYASH_DEBUG_MIR_PRINTER") { if dlog::on("NYASH_DEBUG_MIR_PRINTER") {
eprintln!( eprintln!(
@ -306,17 +306,16 @@ impl MirPrinter {
writeln!(output).unwrap(); writeln!(output).unwrap();
// Instructions // Instructions
let mut line_num = 0; for sp in block.all_spanned_instructions() {
for instruction in block.all_instructions() {
if self.show_line_numbers { if self.show_line_numbers {
write!(output, " {:3}: ", line_num).unwrap(); write!(output, " {:3}: ", sp.span.line).unwrap();
} else { } else {
write!(output, " ").unwrap(); write!(output, " ").unwrap();
} }
let mut line = self.format_instruction(instruction, types); let mut line = self.format_instruction(sp.inst, types);
if self.show_effects_inline { if self.show_effects_inline {
let eff = instruction.effects(); let eff = sp.inst.effects();
let cat = if eff.is_pure() { let cat = if eff.is_pure() {
"pure" "pure"
} else if eff.is_read_only() { } else if eff.is_read_only() {
@ -327,7 +326,6 @@ impl MirPrinter {
line.push_str(&format!(" ; eff: {}", cat)); line.push_str(&format!(" ; eff: {}", cat));
} }
writeln!(output, "{}", line).unwrap(); writeln!(output, "{}", line).unwrap();
line_num += 1;
} }
// Block effects (if verbose and not pure) // Block effects (if verbose and not pure)

View File

@ -109,7 +109,6 @@ impl<'m> MirQuery for MirQueryBox<'m> {
FutureSet { future, value } => vec![*future, *value], FutureSet { future, value } => vec![*future, *value],
Await { future, .. } => vec![*future], Await { future, .. } => vec![*future],
Safepoint => Vec::new(), Safepoint => Vec::new(),
_ => Vec::new(),
} }
} }

View File

@ -1,5 +1,5 @@
use crate::ast::Span;
use super::MirInstruction; use super::MirInstruction;
use crate::ast::Span;
/// MIR instruction bundled with its source span. /// MIR instruction bundled with its source span.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -7,3 +7,10 @@ pub struct SpannedInstruction {
pub inst: MirInstruction, pub inst: MirInstruction,
pub span: Span, pub span: Span,
} }
/// Reference view of a MIR instruction bundled with its span.
#[derive(Debug, Clone, Copy)]
pub struct SpannedInstRef<'a> {
pub inst: &'a MirInstruction,
pub span: Span,
}

View File

@ -1,4 +1,7 @@
use crate::mir::{BasicBlockId, CompareOp, MirFunction, MirInstruction, ValueId}; use crate::mir::{
BasicBlockId, CompareOp, MirFunction, MirInstruction, SpannedInstruction, ValueId,
};
use crate::ast::Span;
/// Emit a MIR Compare instruction into the current block (function-level SSOT helper) /// Emit a MIR Compare instruction into the current block (function-level SSOT helper)
pub fn emit_compare_func( pub fn emit_compare_func(
@ -49,16 +52,28 @@ pub fn set_jump(f: &mut MirFunction, cur_bb: BasicBlockId, target: BasicBlockId)
/// Insert a PHI instruction at block head (after existing PHIs) with normalized inputs order. /// Insert a PHI instruction at block head (after existing PHIs) with normalized inputs order.
pub fn insert_phi_at_head( pub fn insert_phi_at_head(
f: &mut MirFunction,
bb_id: BasicBlockId,
dst: ValueId,
inputs: Vec<(BasicBlockId, ValueId)>,
) {
insert_phi_at_head_spanned(f, bb_id, dst, inputs, Span::unknown());
}
/// Insert a PHI instruction at block head (after existing PHIs) with normalized inputs order.
/// Allows passing a span to retain source mapping when available.
pub fn insert_phi_at_head_spanned(
f: &mut MirFunction, f: &mut MirFunction,
bb_id: BasicBlockId, bb_id: BasicBlockId,
dst: ValueId, dst: ValueId,
mut inputs: Vec<(BasicBlockId, ValueId)>, mut inputs: Vec<(BasicBlockId, ValueId)>,
span: Span,
) { ) {
inputs.sort_by_key(|(bb, _)| bb.0); inputs.sort_by_key(|(bb, _)| bb.0);
if let Some(bb) = f.get_block_mut(bb_id) { if let Some(bb) = f.get_block_mut(bb_id) {
bb.insert_spanned_after_phis(crate::mir::SpannedInstruction { bb.insert_spanned_after_phis(SpannedInstruction {
inst: MirInstruction::Phi { dst, inputs }, inst: MirInstruction::Phi { dst, inputs },
span: crate::ast::Span::unknown(), span,
}); });
} }
} }

View File

@ -72,7 +72,13 @@ impl MirBuilder {
// 統一された挿入ロジック(既存パターンと完全互換) // 統一された挿入ロジック(既存パターンと完全互換)
if let (Some(func), Some(cur_bb)) = (self.current_function.as_mut(), self.current_block) { if let (Some(func), Some(cur_bb)) = (self.current_function.as_mut(), self.current_block) {
// CFG経由の正規化挿入predecessor順序の正規化を含む // CFG経由の正規化挿入predecessor順序の正規化を含む
crate::mir::ssot::cf_common::insert_phi_at_head(func, cur_bb, phi_val, inputs); crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
func,
cur_bb,
phi_val,
inputs,
self.current_span,
);
} else { } else {
// フォールバック: 直接emit主にテストや特殊ケース用 // フォールバック: 直接emit主にテストや特殊ケース用
self.emit_instruction(MirInstruction::Phi { self.emit_instruction(MirInstruction::Phi {
@ -131,7 +137,13 @@ impl MirBuilder {
// 統一された挿入ロジック(既存パターンと完全互換) // 統一された挿入ロジック(既存パターンと完全互換)
if let (Some(func), Some(cur_bb)) = (self.current_function.as_mut(), self.current_block) { if let (Some(func), Some(cur_bb)) = (self.current_function.as_mut(), self.current_block) {
// CFG経由の正規化挿入predecessor順序の正規化を含む // CFG経由の正規化挿入predecessor順序の正規化を含む
crate::mir::ssot::cf_common::insert_phi_at_head(func, cur_bb, dst, inputs); crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
func,
cur_bb,
dst,
inputs,
self.current_span,
);
} else { } else {
// フォールバック: 直接emit主にテストや特殊ケース用 // フォールバック: 直接emit主にテストや特殊ケース用
self.emit_instruction(MirInstruction::Phi { dst, inputs })?; self.emit_instruction(MirInstruction::Phi { dst, inputs })?;

View File

@ -5,7 +5,6 @@
// - ValueIdの意味的分類Parameter, Local, Constant等を導入 // - ValueIdの意味的分類Parameter, Local, Constant等を導入
// - GUARDバグのような「ValueId(0)の曖昧性」から生じるバグを根絶 // - GUARDバグのような「ValueId(0)の曖昧性」から生じるバグを根絶
use crate::mir::ConstValue;
use crate::mir::ValueId; use crate::mir::ValueId;
/// ValueIdの意味的分類型安全性強化 /// ValueIdの意味的分類型安全性強化

View File

@ -93,9 +93,10 @@ impl MirVerifier {
instruction_index instruction_index
); );
if let Some(bb) = function.blocks.get(block) { if let Some(bb) = function.blocks.get(block) {
let inst_opt = bb.all_instructions().nth(*instruction_index); let inst_opt =
if let Some(inst) = inst_opt { bb.all_spanned_instructions_enumerated().nth(*instruction_index);
eprintln!("[mir-ssa-debug-inst] inst={:?}", inst); if let Some((_idx, sp)) = inst_opt {
eprintln!("[mir-ssa-debug-inst] inst={:?}", sp.inst);
} }
} }
} }
@ -291,8 +292,8 @@ impl MirVerifier {
// Collect values used in merge block // Collect values used in merge block
let mut used_values = std::collections::HashSet::new(); let mut used_values = std::collections::HashSet::new();
for inst in merge_bb.all_instructions() { for sp in merge_bb.all_spanned_instructions() {
for v in inst.used_values() { for v in sp.inst.used_values() {
used_values.insert(v); used_values.insert(v);
} }
} }

View File

@ -9,15 +9,15 @@ pub fn check_weakref_and_barrier(function: &MirFunction) -> Result<(), Vec<Verif
let mut def_map: std::collections::HashMap<ValueId, (BasicBlockId, usize, &MirInstruction)> = let mut def_map: std::collections::HashMap<ValueId, (BasicBlockId, usize, &MirInstruction)> =
std::collections::HashMap::new(); std::collections::HashMap::new();
for (bid, block) in &function.blocks { for (bid, block) in &function.blocks {
for (idx, inst) in block.all_instructions().enumerate() { for (idx, sp) in block.all_spanned_instructions_enumerated() {
if let Some(dst) = inst.dst_value() { if let Some(dst) = sp.inst.dst_value() {
def_map.insert(dst, (*bid, idx, inst)); def_map.insert(dst, (*bid, idx, sp.inst));
} }
} }
} }
for (bid, block) in &function.blocks { for (bid, block) in &function.blocks {
for (idx, inst) in block.all_instructions().enumerate() { for (idx, sp) in block.all_spanned_instructions_enumerated() {
match inst { match sp.inst {
MirInstruction::WeakRef { MirInstruction::WeakRef {
op: crate::mir::WeakRefOp::Load, op: crate::mir::WeakRefOp::Load,
value, value,
@ -103,11 +103,10 @@ pub fn check_barrier_context(function: &MirFunction) -> Result<(), Vec<Verificat
let mut errors = Vec::new(); let mut errors = Vec::new();
for (bid, block) in &function.blocks { for (bid, block) in &function.blocks {
let mut insts: Vec<(usize, &MirInstruction)> = let insts: Vec<(usize, &MirInstruction)> = block
block.instructions.iter().enumerate().collect(); .all_spanned_instructions_enumerated()
if let Some(term) = &block.terminator { .map(|(i, sp)| (i, sp.inst))
insts.push((usize::MAX, term)); .collect();
}
for (idx, inst) in &insts { for (idx, inst) in &insts {
let is_barrier = matches!( let is_barrier = matches!(
inst, inst,

View File

@ -42,8 +42,8 @@ pub fn check_merge_uses(function: &MirFunction) -> Result<(), Vec<VerificationEr
let mut phi_dsts_in_block: HashMap<BasicBlockId, HashSet<ValueId>> = HashMap::new(); let mut phi_dsts_in_block: HashMap<BasicBlockId, HashSet<ValueId>> = HashMap::new();
for (bid, block) in &function.blocks { for (bid, block) in &function.blocks {
let set = phi_dsts_in_block.entry(*bid).or_default(); let set = phi_dsts_in_block.entry(*bid).or_default();
for inst in block.all_instructions() { for sp in block.all_spanned_instructions() {
if let crate::mir::MirInstruction::Phi { dst, .. } = inst { if let crate::mir::MirInstruction::Phi { dst, .. } = sp.inst {
set.insert(*dst); set.insert(*dst);
} }
} }
@ -57,11 +57,11 @@ pub fn check_merge_uses(function: &MirFunction) -> Result<(), Vec<VerificationEr
} }
let phi_dsts = phi_dsts_in_block.get(bid); let phi_dsts = phi_dsts_in_block.get(bid);
let doms_of_block = dominators.get(bid).unwrap(); let doms_of_block = dominators.get(bid).unwrap();
for inst in block.all_instructions() { for sp in block.all_spanned_instructions() {
if let crate::mir::MirInstruction::Phi { .. } = inst { if let crate::mir::MirInstruction::Phi { .. } = sp.inst {
continue; continue;
} }
for used in inst.used_values() { for used in sp.inst.used_values() {
if let Some(&db) = def_block.get(&used) { if let Some(&db) = def_block.get(&used) {
if !doms_of_block.contains(&db) { if !doms_of_block.contains(&db) {
let is_phi_dst = phi_dsts.map(|s| s.contains(&used)).unwrap_or(false); let is_phi_dst = phi_dsts.map(|s| s.contains(&used)).unwrap_or(false);

View File

@ -11,11 +11,11 @@ pub fn check_dominance(function: &MirFunction) -> Result<(), Vec<VerificationErr
let def_block = utils::compute_def_blocks(function); let def_block = utils::compute_def_blocks(function);
let dominators = utils::compute_dominators(function); let dominators = utils::compute_dominators(function);
for (use_block_id, block) in &function.blocks { for (use_block_id, block) in &function.blocks {
for instruction in block.all_instructions() { for sp in block.all_spanned_instructions() {
if let crate::mir::MirInstruction::Phi { .. } = instruction { if let crate::mir::MirInstruction::Phi { .. } = sp.inst {
continue; continue;
} }
for used_value in instruction.used_values() { for used_value in sp.inst.used_values() {
if let Some(&def_bb) = def_block.get(&used_value) { if let Some(&def_bb) = def_block.get(&used_value) {
if def_bb != *use_block_id { if def_bb != *use_block_id {
let doms = dominators.get(use_block_id).unwrap(); let doms = dominators.get(use_block_id).unwrap();

View File

@ -9,8 +9,8 @@ pub fn check_no_legacy_ops(function: &MirFunction) -> Result<(), Vec<Verificatio
} }
let mut errors = Vec::new(); let mut errors = Vec::new();
for (bid, block) in &function.blocks { for (bid, block) in &function.blocks {
for (idx, inst) in block.all_instructions().enumerate() { for (idx, sp) in block.all_spanned_instructions_enumerated() {
let legacy_name = match inst { let legacy_name = match sp.inst {
MirInstruction::TypeCheck { .. } => Some("TypeCheck"), // -> TypeOp(Check) MirInstruction::TypeCheck { .. } => Some("TypeCheck"), // -> TypeOp(Check)
MirInstruction::Cast { .. } => Some("Cast"), // -> TypeOp(Cast) MirInstruction::Cast { .. } => Some("Cast"), // -> TypeOp(Cast)
MirInstruction::WeakNew { .. } => Some("WeakNew"), // -> WeakRef(New) MirInstruction::WeakNew { .. } => Some("WeakNew"), // -> WeakRef(New)

View File

@ -14,8 +14,8 @@ pub fn check_ssa_form(function: &MirFunction) -> Result<(), Vec<VerificationErro
} }
for (block_id, block) in &function.blocks { for (block_id, block) in &function.blocks {
for (inst_idx, instruction) in block.all_instructions().enumerate() { for (inst_idx, sp) in block.all_spanned_instructions_enumerated() {
if let Some(dst) = instruction.dst_value() { if let Some(dst) = sp.inst.dst_value() {
if let Some((first_block, _)) = definitions.insert(dst, (*block_id, inst_idx)) { if let Some((first_block, _)) = definitions.insert(dst, (*block_id, inst_idx)) {
errors.push(VerificationError::MultipleDefinition { errors.push(VerificationError::MultipleDefinition {
value: dst, value: dst,
@ -28,12 +28,12 @@ pub fn check_ssa_form(function: &MirFunction) -> Result<(), Vec<VerificationErro
} }
for (block_id, block) in &function.blocks { for (block_id, block) in &function.blocks {
for (inst_idx, instruction) in block.all_instructions().enumerate() { for (inst_idx, sp) in block.all_spanned_instructions_enumerated() {
for used_value in instruction.used_values() { for used_value in sp.inst.used_values() {
if !definitions.contains_key(&used_value) { if !definitions.contains_key(&used_value) {
eprintln!( eprintln!(
"[ssa-undef-debug] fn={} bb={:?} inst_idx={} used={:?} inst={:?}", "[ssa-undef-debug] fn={} bb={:?} inst_idx={} used={:?} inst={:?}",
function.signature.name, block_id, inst_idx, used_value, instruction function.signature.name, block_id, inst_idx, used_value, sp.inst
); );
errors.push(VerificationError::UndefinedValue { errors.push(VerificationError::UndefinedValue {
value: used_value, value: used_value,

View File

@ -17,8 +17,8 @@ pub fn compute_def_blocks(function: &MirFunction) -> HashMap<ValueId, BasicBlock
def_block.insert(*pid, function.entry_block); def_block.insert(*pid, function.entry_block);
} }
for (bid, block) in &function.blocks { for (bid, block) in &function.blocks {
for inst in block.all_instructions() { for sp in block.all_spanned_instructions() {
if let Some(dst) = inst.dst_value() { if let Some(dst) = sp.inst.dst_value() {
def_block.insert(dst, *bid); def_block.insert(dst, *bid);
} }
} }
@ -79,8 +79,8 @@ pub fn compute_reachable_blocks(function: &MirFunction) -> HashSet<BasicBlockId>
worklist.push(*successor); worklist.push(*successor);
} }
} }
for instruction in &block.instructions { for sp in block.iter_spanned() {
if let crate::mir::MirInstruction::Catch { handler_bb, .. } = instruction { if let crate::mir::MirInstruction::Catch { handler_bb, .. } = sp.inst {
if !reachable.contains(handler_bb) { if !reachable.contains(handler_bb) {
worklist.push(*handler_bb); worklist.push(*handler_bb);
} }

View File

@ -3,6 +3,7 @@ use super::merge::new_block;
use super::ternary; use super::ternary;
use super::BridgeEnv; use super::BridgeEnv;
use crate::mir::{BasicBlockId, ConstValue, EffectMask, MirFunction, MirInstruction, ValueId}; use crate::mir::{BasicBlockId, ConstValue, EffectMask, MirFunction, MirInstruction, ValueId};
use crate::ast::Span;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use super::super::ast::ExprV0; use super::super::ast::ExprV0;
@ -290,7 +291,13 @@ pub(super) fn lower_expr_with_scope<S: VarScope>(
} else { } else {
inputs.push((fall_bb, rval)); inputs.push((fall_bb, rval));
} }
crate::mir::ssot::cf_common::insert_phi_at_head(f, merge_bb, out, inputs); crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
f,
merge_bb,
out,
inputs,
Span::unknown(),
);
Ok((out, merge_bb)) Ok((out, merge_bb))
} }
ExprV0::Call { name, args } => { ExprV0::Call { name, args } => {

View File

@ -23,10 +23,10 @@
use super::super::ast::ExprV0; use super::super::ast::ExprV0;
use super::super::ast::StmtV0; use super::super::ast::StmtV0;
use super::{lower_stmt_list_with_vars, new_block, BridgeEnv, LoopContext}; use super::{lower_stmt_list_with_vars, new_block, BridgeEnv, LoopContext};
use crate::mir::phi_core::loop_snapshot_merge::LoopSnapshotMergeBox;
use crate::mir::phi_core::loopform_builder::{LoopFormBuilder, LoopFormOps}; use crate::mir::phi_core::loopform_builder::{LoopFormBuilder, LoopFormOps};
use crate::mir::phi_core::phi_input_collector::PhiInputCollector; use crate::mir::phi_core::phi_input_collector::PhiInputCollector;
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId}; use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
use crate::ast::Span;
use std::collections::BTreeMap; use std::collections::BTreeMap;
/// LoopForm v2 用の JSON bridge 実装。 /// LoopForm v2 用の JSON bridge 実装。
@ -143,7 +143,13 @@ impl LoopFormOps for LoopFormJsonOps<'_> {
dst: ValueId, dst: ValueId,
inputs: Vec<(BasicBlockId, ValueId)>, inputs: Vec<(BasicBlockId, ValueId)>,
) -> Result<(), String> { ) -> Result<(), String> {
crate::mir::ssot::cf_common::insert_phi_at_head(self.f, self.current_block, dst, inputs); crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
self.f,
self.current_block,
dst,
inputs,
Span::unknown(),
);
Ok(()) Ok(())
} }
@ -363,11 +369,12 @@ pub(super) fn lower_loop_stmt(
// 異なる値を持つ場合は PHI ノードを continue_merge_bb に生成 // 異なる値を持つ場合は PHI ノードを continue_merge_bb に生成
let final_inputs = collector.finalize(); let final_inputs = collector.finalize();
let phi_id = ops.f.next_value_id(); let phi_id = ops.f.next_value_id();
crate::mir::ssot::cf_common::insert_phi_at_head( crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
ops.f, ops.f,
continue_merge_bb, continue_merge_bb,
phi_id, phi_id,
final_inputs, final_inputs,
Span::unknown(),
); );
phi_id phi_id
}; };

View File

@ -5,6 +5,7 @@ use super::expr::{lower_expr_with_scope, VarScope};
use super::merge::new_block; use super::merge::new_block;
use super::BridgeEnv; use super::BridgeEnv;
use crate::mir::{BasicBlockId, CompareOp, ConstValue, MirFunction, MirInstruction, ValueId}; use crate::mir::{BasicBlockId, CompareOp, ConstValue, MirFunction, MirInstruction, ValueId};
use crate::ast::Span;
pub(super) fn lower_match_expr_with_scope<S: VarScope>( pub(super) fn lower_match_expr_with_scope<S: VarScope>(
env: &BridgeEnv, env: &BridgeEnv,
@ -85,6 +86,12 @@ pub(super) fn lower_match_expr_with_scope<S: VarScope>(
let out = f.next_value_id(); let out = f.next_value_id();
// フェーズM.2: PHI統一処理no_phi分岐削除 // フェーズM.2: PHI統一処理no_phi分岐削除
let inputs = phi_inputs; let inputs = phi_inputs;
crate::mir::ssot::cf_common::insert_phi_at_head(f, merge_bb, out, inputs); crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
f,
merge_bb,
out,
inputs,
Span::unknown(),
);
Ok((out, merge_bb)) Ok((out, merge_bb))
} }

View File

@ -1,4 +1,5 @@
use crate::mir::{BasicBlock, BasicBlockId, MirFunction, MirInstruction, ValueId}; use crate::mir::{BasicBlock, BasicBlockId, MirFunction, MirInstruction, ValueId};
use crate::ast::Span;
fn next_block_id(f: &MirFunction) -> BasicBlockId { fn next_block_id(f: &MirFunction) -> BasicBlockId {
let mut mx = 0u32; let mut mx = 0u32;
@ -41,11 +42,12 @@ pub(super) fn merge_values(
bb.add_instruction_before_terminator(MirInstruction::Copy { dst, src: val_b }); bb.add_instruction_before_terminator(MirInstruction::Copy { dst, src: val_b });
} }
} else { } else {
crate::mir::ssot::cf_common::insert_phi_at_head( crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
f, f,
merge_bb, merge_bb,
dst, dst,
vec![(pred_a, val_a), (pred_b, val_b)], vec![(pred_a, val_a), (pred_b, val_b)],
Span::unknown(),
); );
} }
dst dst

View File

@ -7,6 +7,7 @@ use super::super::ast::ExprV0;
use super::merge::new_block; use super::merge::new_block;
use super::BridgeEnv; use super::BridgeEnv;
use crate::mir::{BasicBlockId, MirFunction, ValueId}; use crate::mir::{BasicBlockId, MirFunction, ValueId};
use crate::ast::Span;
use super::expr::{lower_expr_with_scope, VarScope}; use super::expr::{lower_expr_with_scope, VarScope};
@ -40,6 +41,12 @@ pub(super) fn lower_ternary_expr_with_scope<S: VarScope>(
let out = f.next_value_id(); let out = f.next_value_id();
// フェーズM.2: PHI統一処理no_phi分岐削除 // フェーズM.2: PHI統一処理no_phi分岐削除
let inputs = vec![(tend, tval), (eend, eval)]; let inputs = vec![(tend, tval), (eend, eval)];
crate::mir::ssot::cf_common::insert_phi_at_head(f, merge_bb, out, inputs); crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
f,
merge_bb,
out,
inputs,
Span::unknown(),
);
Ok((out, merge_bb)) Ok((out, merge_bb))
} }

View File

@ -1,6 +1,7 @@
use super::super::ast::{CatchV0, StmtV0}; use super::super::ast::{CatchV0, StmtV0};
use super::{lower_stmt_list_with_vars, new_block, BridgeEnv, LoopContext}; use super::{lower_stmt_list_with_vars, new_block, BridgeEnv, LoopContext};
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId}; use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
use crate::ast::Span;
use std::collections::BTreeMap; use std::collections::BTreeMap;
pub(super) fn lower_try_stmt( pub(super) fn lower_try_stmt(
@ -102,8 +103,12 @@ pub(super) fn lower_try_stmt(
if let Some(_bb) = f.get_block_mut(catch_bb) { if let Some(_bb) = f.get_block_mut(catch_bb) {
let mut inputs = incoming_exc.clone(); let mut inputs = incoming_exc.clone();
inputs.sort_by_key(|(bbid, _)| bbid.0); inputs.sort_by_key(|(bbid, _)| bbid.0);
crate::mir::ssot::cf_common::insert_phi_at_head( crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
f, catch_bb, phi_dst, inputs, f,
catch_bb,
phi_dst,
inputs,
Span::unknown(),
); );
} }
catch_vars.insert(param.clone(), phi_dst); catch_vars.insert(param.clone(), phi_dst);
@ -170,7 +175,13 @@ pub(super) fn lower_try_stmt(
} }
if let Some(_bb) = f.get_block_mut(finally_block) { if let Some(_bb) = f.get_block_mut(finally_block) {
for (dst, inputs) in phi_entries { for (dst, inputs) in phi_entries {
crate::mir::ssot::cf_common::insert_phi_at_head(f, finally_block, dst, inputs); crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
f,
finally_block,
dst,
inputs,
Span::unknown(),
);
} }
} }
let mut finally_vars = merged_vars.clone(); let mut finally_vars = merged_vars.clone();
@ -226,7 +237,13 @@ pub(super) fn lower_try_stmt(
} }
if let Some(_bb) = f.get_block_mut(exit_bb) { if let Some(_bb) = f.get_block_mut(exit_bb) {
for (dst, inputs) in phi_entries { for (dst, inputs) in phi_entries {
crate::mir::ssot::cf_common::insert_phi_at_head(f, exit_bb, dst, inputs); crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
f,
exit_bb,
dst,
inputs,
Span::unknown(),
);
} }
} }
*vars = merged_vars; *vars = merged_vars;
@ -326,7 +343,13 @@ pub(super) fn lower_try_stmt(
// フェーズM.2: PHI統一処理no_phi分岐削除 // フェーズM.2: PHI統一処理no_phi分岐削除
if let Some(_bb) = f.get_block_mut(finally_block) { if let Some(_bb) = f.get_block_mut(finally_block) {
for (dst, inputs) in phi_entries { for (dst, inputs) in phi_entries {
crate::mir::ssot::cf_common::insert_phi_at_head(f, finally_block, dst, inputs); crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
f,
finally_block,
dst,
inputs,
Span::unknown(),
);
} }
} }
let mut finally_vars = merged_vars.clone(); let mut finally_vars = merged_vars.clone();
@ -377,7 +400,13 @@ pub(super) fn lower_try_stmt(
// フェーズM.2: PHI統一処理no_phi分岐削除 // フェーズM.2: PHI統一処理no_phi分岐削除
if let Some(_bb) = f.get_block_mut(exit_bb) { if let Some(_bb) = f.get_block_mut(exit_bb) {
for (dst, inputs) in phi_entries { for (dst, inputs) in phi_entries {
crate::mir::ssot::cf_common::insert_phi_at_head(f, exit_bb, dst, inputs); crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
f,
exit_bb,
dst,
inputs,
Span::unknown(),
);
} }
} }
*vars = merged_vars; *vars = merged_vars;

View File

@ -101,14 +101,12 @@ fn print_source_snippet(filename: &str, src: &str, line: usize, col: Option<usiz
if line <= lines.len() { if line <= lines.len() {
let text = lines[line - 1]; let text = lines[line - 1];
let mut underline = String::new(); let mut underline = String::new();
let mut idx = 0usize;
for (i, ch) in text.chars().enumerate() { for (i, ch) in text.chars().enumerate() {
if i + 1 >= col { if i + 1 >= col {
break; break;
} }
// Preserve tabs visually; spaces elsewhere // Preserve tabs visually; spaces elsewhere
underline.push(if ch == '\t' { '\t' } else { ' ' }); underline.push(if ch == '\t' { '\t' } else { ' ' });
idx = i;
} }
let pad = " "; // align under " LNNNNN |" let pad = " "; // align under " LNNNNN |"
eprintln!(" {}{}^", pad, underline); eprintln!(" {}{}^", pad, underline);

View File

@ -178,6 +178,7 @@ impl<'a> PreludeManagerBox<'a> {
}); });
current_line += main_lines; current_line += main_lines;
} }
let _ = current_line;
if trace { if trace {
crate::runner::trace::log(format!( crate::runner::trace::log(format!(

View File

@ -999,6 +999,7 @@ pub fn merge_prelude_text(
}); });
current_line += main_lines; current_line += main_lines;
} }
let _ = current_line;
if trace { if trace {
crate::runner::trace::log(format!( crate::runner::trace::log(format!(

View File

@ -21,7 +21,7 @@ mod modules;
use super::NyashRunner; use super::NyashRunner;
use crate::cli::CliGroups; use crate::cli::CliGroups;
use std::{path::Path, process}; use std::path::Path;
impl NyashRunner { impl NyashRunner {
/// If enabled, run the Stage-1 CLI stub as a child process and return its exit code. /// If enabled, run the Stage-1 CLI stub as a child process and return its exit code.