From 119632812d3ed40cf294e2a25f946dcdb5b36fd0 Mon Sep 17 00:00:00 2001 From: Brummel Date: Sun, 10 May 2026 23:10:02 +0200 Subject: [PATCH] iter 23.3.2-fixup: emit_compare_ladder helper + drop unused test param --- crates/ailang-codegen/src/lib.rs | 141 ++++++++++++++----------------- 1 file changed, 62 insertions(+), 79 deletions(-) diff --git a/crates/ailang-codegen/src/lib.rs b/crates/ailang-codegen/src/lib.rs index 623753a..7826631 100644 --- a/crates/ailang-codegen/src/lib.rs +++ b/crates/ailang-codegen/src/lib.rs @@ -2519,31 +2519,10 @@ impl<'a> Emitter<'a> { let n = self.locals.len(); let a_ssa = self.locals[n - 2].1.clone(); let b_ssa = self.locals[n - 1].1.clone(); - let lt_test = self.fresh_ssa(); - self.body.push_str(&format!( - " {lt_test} = icmp slt i64 {a_ssa}, {b_ssa}\n" - )); - let id = self.fresh_id(); - let lt_label = format!("cmp_lt_{id}"); - let after_lt_label = format!("cmp_after_lt_{id}"); - self.body.push_str(&format!( - " br i1 {lt_test}, label %{lt_label}, label %{after_lt_label}\n" - )); - self.emit_ordering_arm(<_label, "LT")?; - self.start_block(&after_lt_label); - let eq_test = self.fresh_ssa(); - self.body.push_str(&format!( - " {eq_test} = icmp eq i64 {a_ssa}, {b_ssa}\n" - )); - let eq_label = format!("cmp_eq_{id}"); - let gt_label = format!("cmp_gt_{id}"); - self.body.push_str(&format!( - " br i1 {eq_test}, label %{eq_label}, label %{gt_label}\n" - )); - self.emit_ordering_arm(&eq_label, "EQ")?; - self.emit_ordering_arm(>_label, "GT")?; - self.body.push_str("}\n\n"); - self.block_terminated = true; + self.emit_compare_ladder( + &format!("icmp slt i64 {a_ssa}, {b_ssa}"), + &format!("icmp eq i64 {a_ssa}, {b_ssa}"), + )?; Ok(true) } "compare__Bool" => { @@ -2556,31 +2535,10 @@ impl<'a> Emitter<'a> { let n = self.locals.len(); let a_ssa = self.locals[n - 2].1.clone(); let b_ssa = self.locals[n - 1].1.clone(); - let lt_test = self.fresh_ssa(); - self.body.push_str(&format!( - " {lt_test} = icmp ult i1 {a_ssa}, {b_ssa}\n" - )); - let id = self.fresh_id(); - let lt_label = format!("cmp_lt_{id}"); - let after_lt_label = format!("cmp_after_lt_{id}"); - self.body.push_str(&format!( - " br i1 {lt_test}, label %{lt_label}, label %{after_lt_label}\n" - )); - self.emit_ordering_arm(<_label, "LT")?; - self.start_block(&after_lt_label); - let eq_test = self.fresh_ssa(); - self.body.push_str(&format!( - " {eq_test} = icmp eq i1 {a_ssa}, {b_ssa}\n" - )); - let eq_label = format!("cmp_eq_{id}"); - let gt_label = format!("cmp_gt_{id}"); - self.body.push_str(&format!( - " br i1 {eq_test}, label %{eq_label}, label %{gt_label}\n" - )); - self.emit_ordering_arm(&eq_label, "EQ")?; - self.emit_ordering_arm(>_label, "GT")?; - self.body.push_str("}\n\n"); - self.block_terminated = true; + self.emit_compare_ladder( + &format!("icmp ult i1 {a_ssa}, {b_ssa}"), + &format!("icmp eq i1 {a_ssa}, {b_ssa}"), + )?; Ok(true) } "compare__Str" => { @@ -2597,31 +2555,10 @@ impl<'a> Emitter<'a> { self.body.push_str(&format!( " {cmp_res} = call i32 @ail_str_compare(ptr {a_ssa}, ptr {b_ssa})\n" )); - let lt_test = self.fresh_ssa(); - self.body.push_str(&format!( - " {lt_test} = icmp slt i32 {cmp_res}, 0\n" - )); - let id = self.fresh_id(); - let lt_label = format!("cmp_lt_{id}"); - let after_lt_label = format!("cmp_after_lt_{id}"); - self.body.push_str(&format!( - " br i1 {lt_test}, label %{lt_label}, label %{after_lt_label}\n" - )); - self.emit_ordering_arm(<_label, "LT")?; - self.start_block(&after_lt_label); - let eq_test = self.fresh_ssa(); - self.body.push_str(&format!( - " {eq_test} = icmp eq i32 {cmp_res}, 0\n" - )); - let eq_label = format!("cmp_eq_{id}"); - let gt_label = format!("cmp_gt_{id}"); - self.body.push_str(&format!( - " br i1 {eq_test}, label %{eq_label}, label %{gt_label}\n" - )); - self.emit_ordering_arm(&eq_label, "EQ")?; - self.emit_ordering_arm(>_label, "GT")?; - self.body.push_str("}\n\n"); - self.block_terminated = true; + self.emit_compare_ladder( + &format!("icmp slt i32 {cmp_res}, 0"), + &format!("icmp eq i32 {cmp_res}, 0"), + )?; Ok(true) } _ => Ok(false), @@ -2653,6 +2590,52 @@ impl<'a> Emitter<'a> { Ok(()) } + /// Iter 23.3: emit the labelled three-way branch ladder shared + /// across all three `compare__T` intercept arms. Given two + /// instruction RHS strings (the LT-test and the EQ-test, e.g. + /// `"icmp slt i64 %a, %b"`), assigns each to a fresh SSA, wires + /// them into LT-block / EQ-block / GT-block via + /// `emit_ordering_arm`, and closes the fn body. The EQ-test + /// must be emitted INSIDE the `after_lt_label` block (not before + /// the LT-branch), so the helper takes the RHS as a string and + /// performs the SSA assignment itself at the correct point. The + /// two test instructions vary per arm (`icmp slt i64` / + /// `icmp ult i1` / `icmp slt i32` etc.); any per-arm prep + /// (e.g. `compare__Str`'s `@ail_str_compare` call) must be + /// emitted by the caller before invoking this helper. + fn emit_compare_ladder( + &mut self, + lt_test_instr: &str, + eq_test_instr: &str, + ) -> Result<()> { + let lt_test_ssa = self.fresh_ssa(); + self.body.push_str(&format!( + " {lt_test_ssa} = {lt_test_instr}\n" + )); + let id = self.fresh_id(); + let lt_label = format!("cmp_lt_{id}"); + let after_lt_label = format!("cmp_after_lt_{id}"); + let eq_label = format!("cmp_eq_{id}"); + let gt_label = format!("cmp_gt_{id}"); + self.body.push_str(&format!( + " br i1 {lt_test_ssa}, label %{lt_label}, label %{after_lt_label}\n" + )); + self.emit_ordering_arm(<_label, "LT")?; + self.start_block(&after_lt_label); + let eq_test_ssa = self.fresh_ssa(); + self.body.push_str(&format!( + " {eq_test_ssa} = {eq_test_instr}\n" + )); + self.body.push_str(&format!( + " br i1 {eq_test_ssa}, label %{eq_label}, label %{gt_label}\n" + )); + self.emit_ordering_arm(&eq_label, "EQ")?; + self.emit_ordering_arm(>_label, "GT")?; + self.body.push_str("}\n\n"); + self.block_terminated = true; + Ok(()) + } + /// Iter 16e: lower a `==` call after the two operands have been /// emitted. Dispatches on the resolved AIL type of the arg side /// (both sides have the same type after typecheck). The `_a_ll` @@ -3849,7 +3832,7 @@ mod tests { /// the test does not depend on prelude auto-injection. #[test] fn compare_int_mono_symbol_emits_branch_ladder() { - let m = synth_compare_module("compare__Int", Type::int(), "i64"); + let m = synth_compare_module("compare__Int", Type::int()); let ir = emit_ir(&m).unwrap(); assert!( ir.contains("icmp slt i64"), @@ -3866,7 +3849,7 @@ mod tests { /// Bool ordering false < true); the EQ-test uses `icmp eq i1`. #[test] fn compare_bool_mono_symbol_emits_branch_ladder() { - let m = synth_compare_module("compare__Bool", Type::bool_(), "i1"); + let m = synth_compare_module("compare__Bool", Type::bool_()); let ir = emit_ir(&m).unwrap(); assert!( ir.contains("icmp ult i1"), @@ -3883,7 +3866,7 @@ mod tests { /// else GT. #[test] fn compare_str_mono_symbol_emits_ail_str_compare_call() { - let m = synth_compare_module("compare__Str", Type::str_(), "ptr"); + let m = synth_compare_module("compare__Str", Type::str_()); let ir = emit_ir(&m).unwrap(); assert!( ir.contains("call i32 @ail_str_compare("), @@ -3904,7 +3887,7 @@ mod tests { /// instance-fn shell (the intercept overrides the body), and the /// entry module's `main` is a Unit no-op so `emit_ir` returns a /// well-formed program. Used by the three `compare_*` tests above. - fn synth_compare_module(fn_name: &str, param_ail_ty: Type, _llvm_param_ty: &str) -> Module { + fn synth_compare_module(fn_name: &str, param_ail_ty: Type) -> Module { let ordering = Def::Type(TypeDef { name: "Ordering".into(), vars: vec![],