loopform(hints): detect up to 2 assigned vars in loop body (no break/continue) and emit LoopCarrier hint; add smoke for two-vars case
This commit is contained in:
@ -182,6 +182,10 @@ impl MirBuilder {
|
||||
pub(crate) fn hint_scope_leave(&mut self, id: u32) { self.hint_sink.scope_leave(id); }
|
||||
#[inline]
|
||||
pub(crate) fn hint_join_result<S: Into<String>>(&mut self, var: S) { self.hint_sink.join_result(var.into()); }
|
||||
#[inline]
|
||||
pub(crate) fn hint_loop_carrier<S: Into<String>>(&mut self, vars: impl IntoIterator<Item = S>) {
|
||||
self.hint_sink.loop_carrier(vars.into_iter().map(|s| s.into()).collect::<Vec<_>>());
|
||||
}
|
||||
|
||||
// moved to builder_calls.rs: lower_method_as_function
|
||||
|
||||
|
||||
@ -143,6 +143,39 @@ impl<'a> LoopBuilder<'a> {
|
||||
condition: ASTNode,
|
||||
body: Vec<ASTNode>,
|
||||
) -> Result<ValueId, String> {
|
||||
// Pre-scan body for simple carrier pattern (up to 2 assigned variables, no break/continue)
|
||||
fn collect_assigns(n: &ASTNode, vars: &mut Vec<String>, has_ctrl: &mut bool) {
|
||||
match n {
|
||||
ASTNode::Assignment { target, .. } => {
|
||||
if let ASTNode::Variable { name, .. } = target.as_ref() {
|
||||
if !vars.iter().any(|v| v == name) {
|
||||
vars.push(name.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
ASTNode::Break { .. } | ASTNode::Continue { .. } => { *has_ctrl = true; }
|
||||
ASTNode::If { then_body, else_body, .. } => {
|
||||
let tp = ASTNode::Program { statements: then_body.clone(), span: crate::ast::Span::unknown() };
|
||||
collect_assigns(&tp, vars, has_ctrl);
|
||||
if let Some(eb) = else_body {
|
||||
let ep = ASTNode::Program { statements: eb.clone(), span: crate::ast::Span::unknown() };
|
||||
collect_assigns(&ep, vars, has_ctrl);
|
||||
}
|
||||
}
|
||||
ASTNode::Program { statements, .. } => {
|
||||
for s in statements { collect_assigns(s, vars, has_ctrl); }
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
let mut assigned_vars: Vec<String> = Vec::new();
|
||||
let mut has_ctrl = false;
|
||||
for st in &body { collect_assigns(st, &mut assigned_vars, &mut has_ctrl); }
|
||||
if !has_ctrl && !assigned_vars.is_empty() && assigned_vars.len() <= 2 {
|
||||
// Emit a carrier hint (no-op sink by default; visible with NYASH_MIR_TRACE_HINTS=1)
|
||||
self.parent_builder.hint_loop_carrier(assigned_vars.clone());
|
||||
}
|
||||
|
||||
// 1. ブロックの準備
|
||||
let preheader_id = self.current_block()?;
|
||||
let (header_id, body_id, after_loop_id) =
|
||||
|
||||
25
tools/test/smoke/mir/hints_loop_carrier_two_vars_smoke.sh
Normal file
25
tools/test/smoke/mir/hints_loop_carrier_two_vars_smoke.sh
Normal file
@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
root=$(cd "$(dirname "$0")"/../../../.. && pwd)
|
||||
bin="$root/target/release/nyash"
|
||||
src="apps/tests/macro/loopform/two_vars.nyash"
|
||||
|
||||
if [ ! -x "$bin" ]; then
|
||||
echo "nyash binary not found at $bin; build first (cargo build --release)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export NYASH_MIR_TRACE_HINTS=1
|
||||
|
||||
out=$({ "$bin" --backend vm "$src" 1>/dev/null; } 2>&1 || true)
|
||||
|
||||
# Check the LoopCarrier hint contains both variable names (order-agnostic)
|
||||
echo "$out" | rg -q "\[mir\]\[hint\] LoopCarrier\((i,sum|sum,i)\)" || {
|
||||
echo "[FAIL] missing LoopCarrier(i,sum) hint" >&2
|
||||
printf '%s\n' "$out" | tail -n 80 >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
echo "[OK] MIR hints LoopCarrier(two vars) trace smoke passed"
|
||||
|
||||
Reference in New Issue
Block a user