2 Commits

Author SHA1 Message Date
ok fa7dadbdeb codegen: LOOP exits on index==limit (Forth 2012 boundary crossing) 2026-07-13 13:04:44 +02:00
ok2 d5acdc0e7b fix: Rust 1.95 clippy — match guards + map_or
CI / check (push) Has been cancelled
Rust 1.95 promoted collapsible_match and map_unwrap_or; CI runs
-D warnings so they break the build. Collapse nested `if`s into
match guards across codegen/optimizer/export, and swap
map().unwrap_or(..) for map_or / is_ok_and.
2026-04-21 17:00:21 +02:00
5 changed files with 50 additions and 53 deletions
+32 -29
View File
@@ -1088,10 +1088,12 @@ fn emit_do_loop(f: &mut Function, body: &[IrOp], is_plus_loop: bool, ctx: &mut E
.instruction(&Instruction::End); .instruction(&Instruction::End);
} }
// if index >= limit, exit // Forth 2012: LOOP exits when the index crosses the boundary between
// limit-1 and limit. With step +1 that is exactly new_index == limit
// in wraparound arithmetic — start >= limit must wrap, not exit early.
f.instruction(&Instruction::LocalGet(index_local)) f.instruction(&Instruction::LocalGet(index_local))
.instruction(&Instruction::LocalGet(limit_local)) .instruction(&Instruction::LocalGet(limit_local))
.instruction(&Instruction::I32GeS) .instruction(&Instruction::I32Eq)
.instruction(&Instruction::BrIf(1)) // break to $exit .instruction(&Instruction::BrIf(1)) // break to $exit
.instruction(&Instruction::Br(0)) // continue loop .instruction(&Instruction::Br(0)) // continue loop
.instruction(&Instruction::End) // end loop .instruction(&Instruction::End) // end loop
@@ -1897,7 +1899,8 @@ fn emit_promoted_op(f: &mut Function, op: &IrOp, sim: &mut StackSim) {
// Fix up stack for next iteration (LOOP body is stack-neutral) // Fix up stack for next iteration (LOOP body is stack-neutral)
emit_promoted_loop_fixup(f, sim, &loop_top_stack); emit_promoted_loop_fixup(f, sim, &loop_top_stack);
// LOOP: increment by 1, check >= limit // LOOP: increment by 1, exit when new_index == limit
// (Forth 2012 boundary crossing; start >= limit wraps around)
f.instruction(&Instruction::LocalGet(index_local)); f.instruction(&Instruction::LocalGet(index_local));
f.instruction(&Instruction::I32Const(1)); f.instruction(&Instruction::I32Const(1));
f.instruction(&Instruction::I32Add); f.instruction(&Instruction::I32Add);
@@ -1905,7 +1908,7 @@ fn emit_promoted_op(f: &mut Function, op: &IrOp, sim: &mut StackSim) {
f.instruction(&Instruction::LocalGet(index_local)); f.instruction(&Instruction::LocalGet(index_local));
f.instruction(&Instruction::LocalGet(limit_local)); f.instruction(&Instruction::LocalGet(limit_local));
f.instruction(&Instruction::I32GeS); f.instruction(&Instruction::I32Eq);
f.instruction(&Instruction::BrIf(1)); // break to $exit f.instruction(&Instruction::BrIf(1)); // break to $exit
} }
@@ -2012,14 +2015,12 @@ fn emit_promoted_op(f: &mut Function, op: &IrOp, sim: &mut StackSim) {
// Outside loops, RFetch shouldn't appear in promoted code // Outside loops, RFetch shouldn't appear in promoted code
} }
IrOp::LoopJ => { IrOp::LoopJ if sim.loop_index_stack.len() >= 2 => {
if sim.loop_index_stack.len() >= 2 { let (outer_index, _) = sim.loop_index_stack[sim.loop_index_stack.len() - 2];
let (outer_index, _) = sim.loop_index_stack[sim.loop_index_stack.len() - 2]; let result = sim.alloc();
let result = sim.alloc(); f.instruction(&Instruction::LocalGet(outer_index));
f.instruction(&Instruction::LocalGet(outer_index)); f.instruction(&Instruction::LocalSet(result));
f.instruction(&Instruction::LocalSet(result)); sim.push(result);
sim.push(result);
}
} }
IrOp::Exit => { IrOp::Exit => {
@@ -2147,15 +2148,15 @@ fn needs_f64_locals(ops: &[IrOp]) -> bool {
return true; return true;
} }
} }
IrOp::DoLoop { body, .. } | IrOp::BeginUntil { body } | IrOp::BeginAgain { body } => { IrOp::DoLoop { body, .. } | IrOp::BeginUntil { body } | IrOp::BeginAgain { body }
if needs_f64_locals(body) { if needs_f64_locals(body) =>
return true; {
} return true;
} }
IrOp::BeginWhileRepeat { test, body } => { IrOp::BeginWhileRepeat { test, body }
if needs_f64_locals(test) || needs_f64_locals(body) { if needs_f64_locals(test) || needs_f64_locals(body) =>
return true; {
} return true;
} }
IrOp::BeginDoubleWhileRepeat { IrOp::BeginDoubleWhileRepeat {
outer_test, outer_test,
@@ -2209,15 +2210,15 @@ fn body_needs_return_stack(ops: &[IrOp]) -> bool {
return true; return true;
} }
} }
IrOp::DoLoop { body, .. } | IrOp::BeginUntil { body } | IrOp::BeginAgain { body } => { IrOp::DoLoop { body, .. } | IrOp::BeginUntil { body } | IrOp::BeginAgain { body }
if body_needs_return_stack(body) { if body_needs_return_stack(body) =>
return true; {
} return true;
} }
IrOp::BeginWhileRepeat { test, body } => { IrOp::BeginWhileRepeat { test, body }
if body_needs_return_stack(test) || body_needs_return_stack(body) { if body_needs_return_stack(test) || body_needs_return_stack(body) =>
return true; {
} return true;
} }
IrOp::BeginDoubleWhileRepeat { IrOp::BeginDoubleWhileRepeat {
outer_test, outer_test,
@@ -2831,9 +2832,11 @@ fn emit_consolidated_do_loop(
.instruction(&Instruction::End); .instruction(&Instruction::End);
} }
// Forth 2012 boundary crossing: exit when new_index == limit
// (start >= limit wraps around instead of exiting early).
f.instruction(&Instruction::LocalGet(index_local)) f.instruction(&Instruction::LocalGet(index_local))
.instruction(&Instruction::LocalGet(limit_local)) .instruction(&Instruction::LocalGet(limit_local))
.instruction(&Instruction::I32GeS) .instruction(&Instruction::I32Eq)
.instruction(&Instruction::BrIf(1)) .instruction(&Instruction::BrIf(1))
.instruction(&Instruction::Br(0)) .instruction(&Instruction::Br(0))
.instruction(&Instruction::End) .instruction(&Instruction::End)
+2 -4
View File
@@ -131,10 +131,8 @@ pub fn export_module(
fn collect_external_calls(ops: &[IrOp], ir_ids: &HashSet<WordId>, host_ids: &mut HashSet<WordId>) { fn collect_external_calls(ops: &[IrOp], ir_ids: &HashSet<WordId>, host_ids: &mut HashSet<WordId>) {
for op in ops { for op in ops {
match op { match op {
IrOp::Call(id) | IrOp::TailCall(id) => { IrOp::Call(id) | IrOp::TailCall(id) if !ir_ids.contains(id) => {
if !ir_ids.contains(id) { host_ids.insert(*id);
host_ids.insert(*id);
}
} }
IrOp::If { IrOp::If {
then_body, then_body,
+14 -16
View File
@@ -591,15 +591,15 @@ fn contains_call_to(ops: &[IrOp], target: WordId) -> bool {
return true; return true;
} }
} }
IrOp::DoLoop { body, .. } | IrOp::BeginUntil { body } | IrOp::BeginAgain { body } => { IrOp::DoLoop { body, .. } | IrOp::BeginUntil { body } | IrOp::BeginAgain { body }
if contains_call_to(body, target) { if contains_call_to(body, target) =>
return true; {
} return true;
} }
IrOp::BeginWhileRepeat { test, body } => { IrOp::BeginWhileRepeat { test, body }
if contains_call_to(test, target) || contains_call_to(body, target) { if contains_call_to(test, target) || contains_call_to(body, target) =>
return true; {
} return true;
} }
IrOp::BeginDoubleWhileRepeat { IrOp::BeginDoubleWhileRepeat {
outer_test, outer_test,
@@ -651,15 +651,13 @@ fn contains_exit(ops: &[IrOp]) -> bool {
return true; return true;
} }
} }
IrOp::DoLoop { body, .. } | IrOp::BeginUntil { body } | IrOp::BeginAgain { body } => { IrOp::DoLoop { body, .. } | IrOp::BeginUntil { body } | IrOp::BeginAgain { body }
if contains_exit(body) { if contains_exit(body) =>
return true; {
} return true;
} }
IrOp::BeginWhileRepeat { test, body } => { IrOp::BeginWhileRepeat { test, body } if contains_exit(test) || contains_exit(body) => {
if contains_exit(test) || contains_exit(body) { return true;
return true;
}
} }
_ => {} _ => {}
} }
+1 -2
View File
@@ -397,8 +397,7 @@ impl<R: Runtime> ForthVM<R> {
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
let seed = SystemTime::now() let seed = SystemTime::now()
.duration_since(UNIX_EPOCH) .duration_since(UNIX_EPOCH)
.map(|d| d.as_nanos() as u64) .map_or(0xDEAD_BEEF_CAFE_BABE, |d| d.as_nanos() as u64);
.unwrap_or(0xDEAD_BEEF_CAFE_BABE);
Arc::new(Mutex::new(if seed == 0 { Arc::new(Mutex::new(if seed == 0 {
0xDEAD_BEEF_CAFE_BABE 0xDEAD_BEEF_CAFE_BABE
} else { } else {
+1 -2
View File
@@ -26,8 +26,7 @@ fn probe_gforth(candidate: &str) -> bool {
.arg("-e") .arg("-e")
.arg("bye") .arg("bye")
.output() .output()
.map(|o| o.status.success()) .is_ok_and(|o| o.status.success())
.unwrap_or(false)
} }
fn find_gforth() -> Option<&'static str> { fn find_gforth() -> Option<&'static str> {