smokes: add PHI/core/integration tests; parity uses Python LLVM harness; test runner noise filter\nparser: add opt-in TokenCursor bridge (NYASH_PARSER_TOKEN_CURSOR=1) for expressions\nmir: fix PHI incoming preds to use exit blocks; add debug PHI verification\nplugins(net/filebox): warning cleanup (dead_code), no behavior change\ndocs: smokes v2 README – add test accumulation policy and LLVM harness note\nCURRENT_TASK: Phase 15.5 newline refactor resume + plan
This commit is contained in:
@ -67,6 +67,43 @@ pub(super) fn extract_assigned_var(ast: &ASTNode) -> Option<String> {
|
||||
}
|
||||
|
||||
impl MirBuilder {
|
||||
#[inline]
|
||||
#[cfg(debug_assertions)]
|
||||
fn debug_verify_phi_inputs(&self, inputs: &Vec<(BasicBlockId, ValueId)>) {
|
||||
use std::collections::HashSet;
|
||||
if let Some(cur_bb) = self.current_block {
|
||||
let mut seen = HashSet::new();
|
||||
for (pred, _v) in inputs.iter() {
|
||||
debug_assert_ne!(
|
||||
*pred, cur_bb,
|
||||
"PHI incoming predecessor must not be the merge block itself"
|
||||
);
|
||||
debug_assert!(
|
||||
seen.insert(*pred),
|
||||
"Duplicate PHI incoming predecessor detected: {:?}",
|
||||
pred
|
||||
);
|
||||
}
|
||||
// Ensure all incoming predecessors are known CFG predecessors of the merge block
|
||||
if let Some(func) = &self.current_function {
|
||||
if let Some(block) = func.blocks.get(&cur_bb) {
|
||||
for (pred, _v) in inputs.iter() {
|
||||
debug_assert!(
|
||||
block.predecessors.contains(pred),
|
||||
"PHI incoming pred {:?} is not a predecessor of merge bb {:?}",
|
||||
pred,
|
||||
cur_bb
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn debug_verify_phi_inputs(&self, _inputs: &Vec<(BasicBlockId, ValueId)>) {}
|
||||
|
||||
/// Merge all variables modified in then/else relative to pre_if_snapshot.
|
||||
/// In PHI-off mode inserts edge copies from branch exits to merge. In PHI-on mode emits Phi.
|
||||
/// `skip_var` allows skipping a variable already merged elsewhere (e.g., bound to an expression result).
|
||||
@ -74,8 +111,8 @@ impl MirBuilder {
|
||||
&mut self,
|
||||
then_block: super::BasicBlockId,
|
||||
else_block: super::BasicBlockId,
|
||||
_then_exit_block: super::BasicBlockId,
|
||||
_else_exit_block_opt: Option<super::BasicBlockId>,
|
||||
then_exit_block: super::BasicBlockId,
|
||||
else_exit_block_opt: Option<super::BasicBlockId>,
|
||||
pre_if_snapshot: &std::collections::HashMap<String, super::ValueId>,
|
||||
then_map_end: &std::collections::HashMap<String, super::ValueId>,
|
||||
else_map_end_opt: &Option<std::collections::HashMap<String, super::ValueId>>,
|
||||
@ -114,13 +151,13 @@ impl MirBuilder {
|
||||
.and_then(|m| m.get(name).copied())
|
||||
.unwrap_or(pre);
|
||||
// フェーズM: 常にPHI命令を使用(no_phi_mode撤廃)
|
||||
// incoming の predecessor は "実際に merge に遷移してくる出口ブロック" を使用する
|
||||
let then_pred = then_exit_block;
|
||||
let else_pred = else_exit_block_opt.unwrap_or(else_block);
|
||||
let merged = self.value_gen.next();
|
||||
self.emit_instruction(
|
||||
MirInstruction::Phi {
|
||||
dst: merged,
|
||||
inputs: vec![(then_block, then_v), (else_block, else_v)],
|
||||
}
|
||||
)?;
|
||||
let inputs = vec![(then_pred, then_v), (else_pred, else_v)];
|
||||
self.debug_verify_phi_inputs(&inputs);
|
||||
self.emit_instruction(MirInstruction::Phi { dst: merged, inputs })?;
|
||||
self.variable_map.insert(name.to_string(), merged);
|
||||
}
|
||||
Ok(())
|
||||
@ -131,8 +168,8 @@ impl MirBuilder {
|
||||
&mut self,
|
||||
then_block: BasicBlockId,
|
||||
else_block: BasicBlockId,
|
||||
_then_exit_block_opt: Option<BasicBlockId>,
|
||||
_else_exit_block_opt: Option<BasicBlockId>,
|
||||
then_exit_block_opt: Option<BasicBlockId>,
|
||||
else_exit_block_opt: Option<BasicBlockId>,
|
||||
then_value_raw: ValueId,
|
||||
else_value_raw: ValueId,
|
||||
pre_if_var_map: &HashMap<String, ValueId>,
|
||||
@ -171,22 +208,22 @@ impl MirBuilder {
|
||||
// Else doesn't assign: use pre-if value if available
|
||||
pre_then_var_value.unwrap_or(else_value_raw)
|
||||
};
|
||||
// predecessor を then/else の exit ブロックに揃える
|
||||
let then_pred = then_exit_block_opt.unwrap_or(then_block);
|
||||
let else_pred = else_exit_block_opt.unwrap_or(else_block);
|
||||
// Emit Phi for the assigned variable and bind it
|
||||
self.emit_instruction(MirInstruction::Phi {
|
||||
dst: result_val,
|
||||
inputs: vec![
|
||||
(then_block, then_value_for_var),
|
||||
(else_block, else_value_for_var),
|
||||
],
|
||||
})?;
|
||||
let inputs = vec![(then_pred, then_value_for_var), (else_pred, else_value_for_var)];
|
||||
self.debug_verify_phi_inputs(&inputs);
|
||||
self.emit_instruction(MirInstruction::Phi { dst: result_val, inputs })?;
|
||||
self.variable_map = pre_if_var_map.clone();
|
||||
self.variable_map.insert(var_name, result_val);
|
||||
} else {
|
||||
// No variable assignment pattern detected – just emit Phi for expression result
|
||||
self.emit_instruction(MirInstruction::Phi {
|
||||
dst: result_val,
|
||||
inputs: vec![(then_block, then_value_raw), (else_block, else_value_raw)],
|
||||
})?;
|
||||
let then_pred = then_exit_block_opt.unwrap_or(then_block);
|
||||
let else_pred = else_exit_block_opt.unwrap_or(else_block);
|
||||
let inputs = vec![(then_pred, then_value_raw), (else_pred, else_value_raw)];
|
||||
self.debug_verify_phi_inputs(&inputs);
|
||||
self.emit_instruction(MirInstruction::Phi { dst: result_val, inputs })?;
|
||||
// Merge variable map conservatively to pre-if snapshot (no new bindings)
|
||||
self.variable_map = pre_if_var_map.clone();
|
||||
}
|
||||
|
||||
@ -10,6 +10,8 @@ use super::common::ParserUtils;
|
||||
use super::{NyashParser, ParseError};
|
||||
use crate::ast::{ASTNode, Span, UnaryOperator};
|
||||
use crate::tokenizer::TokenType;
|
||||
use crate::parser::cursor::TokenCursor;
|
||||
use crate::parser::expr_cursor::ExprParserWithCursor;
|
||||
|
||||
// Debug macros are now imported from the parent module via #[macro_export]
|
||||
use crate::must_advance;
|
||||
@ -22,6 +24,16 @@ fn is_sugar_enabled() -> bool {
|
||||
impl NyashParser {
|
||||
/// 式をパース (演算子優先順位あり)
|
||||
pub(super) fn parse_expression(&mut self) -> Result<ASTNode, ParseError> {
|
||||
// Experimental bridge: Opt-in TokenCursor path (Phase 15.5 newline refactor)
|
||||
// Guard: NYASH_PARSER_TOKEN_CURSOR=1
|
||||
if std::env::var("NYASH_PARSER_TOKEN_CURSOR").ok().as_deref() == Some("1") {
|
||||
let mut cursor = TokenCursor::new(&self.tokens);
|
||||
cursor.set_position(self.current);
|
||||
let ast = ExprParserWithCursor::parse_expression(&mut cursor)?;
|
||||
// Reflect consumed position back to legacy parser index
|
||||
self.current = cursor.position();
|
||||
return Ok(ast);
|
||||
}
|
||||
self.parse_pipeline()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user