Skip to content

Commit f6bdade

Browse files
committed
bytecode.rs: Added LT_IMM, LE_IMM, GT_IMM, GE_IMM opcodes (36-39)
vm.rs: Added VM handlers with optimized unsafe register access compiler.rs: Detect comparison with constant in try_compile_binary_op
1 parent 47248a5 commit f6bdade

File tree

3 files changed

+128
-0
lines changed

3 files changed

+128
-0
lines changed

src/bytecode.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ impl std::fmt::Debug for Op {
5656
Self::SET_LIST => write!(f, "SetList({}, {}, {})", self.a(), self.b(), self.c()),
5757
Self::CAR => write!(f, "Car({}, {})", self.a(), self.b()),
5858
Self::CDR => write!(f, "Cdr({}, {})", self.a(), self.b()),
59+
Self::LT_IMM => write!(f, "LtImm({}, {}, {})", self.a(), self.b(), self.c() as i8),
60+
Self::LE_IMM => write!(f, "LeImm({}, {}, {})", self.a(), self.b(), self.c() as i8),
61+
Self::GT_IMM => write!(f, "GtImm({}, {}, {})", self.a(), self.b(), self.c() as i8),
62+
Self::GE_IMM => write!(f, "GeImm({}, {}, {})", self.a(), self.b(), self.c() as i8),
5963
_ => write!(f, "Unknown(0x{:08x})", self.0),
6064
}
6165
}
@@ -99,6 +103,10 @@ impl Op {
99103
pub const SET_LIST: u8 = 33; // ABC: list, index, value
100104
pub const CAR: u8 = 34; // AB: dest, src - get head of cons/list
101105
pub const CDR: u8 = 35; // AB: dest, src - get tail of cons/list
106+
pub const LT_IMM: u8 = 36; // ABC: dest, src, imm (C is i8) - src < imm
107+
pub const LE_IMM: u8 = 37; // ABC: dest, src, imm (C is i8) - src <= imm
108+
pub const GT_IMM: u8 = 38; // ABC: dest, src, imm (C is i8) - src > imm
109+
pub const GE_IMM: u8 = 39; // ABC: dest, src, imm (C is i8) - src >= imm
102110

103111
// ========== Constructors ==========
104112

@@ -342,6 +350,26 @@ impl Op {
342350
Self::abc(Self::CDR, dest, src, 0)
343351
}
344352

353+
#[inline(always)]
354+
pub const fn lt_imm(dest: Reg, src: Reg, imm: i8) -> Self {
355+
Self::abc(Self::LT_IMM, dest, src, imm as u8)
356+
}
357+
358+
#[inline(always)]
359+
pub const fn le_imm(dest: Reg, src: Reg, imm: i8) -> Self {
360+
Self::abc(Self::LE_IMM, dest, src, imm as u8)
361+
}
362+
363+
#[inline(always)]
364+
pub const fn gt_imm(dest: Reg, src: Reg, imm: i8) -> Self {
365+
Self::abc(Self::GT_IMM, dest, src, imm as u8)
366+
}
367+
368+
#[inline(always)]
369+
pub const fn ge_imm(dest: Reg, src: Reg, imm: i8) -> Self {
370+
Self::abc(Self::GE_IMM, dest, src, imm as u8)
371+
}
372+
345373
// ========== Jump patching helpers ==========
346374

347375
/// Check if this is a jump instruction (for patching)

src/compiler.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,25 @@ impl Compiler {
731731
}
732732
}
733733

734+
// Check for comparison with immediate value: (< x 0), (<= n 10), etc.
735+
if op == "<" || op == "<=" || op == ">" || op == ">=" {
736+
// Check if second arg is a small constant integer
737+
if let Some(imm) = args[1].as_int() {
738+
if imm >= i8::MIN as i64 && imm <= i8::MAX as i64 {
739+
// Optimization: compile first arg directly into dest
740+
self.compile_expr(&args[0], dest, false)?;
741+
match op {
742+
"<" => self.emit(Op::lt_imm(dest, dest, imm as i8)),
743+
"<=" => self.emit(Op::le_imm(dest, dest, imm as i8)),
744+
">" => self.emit(Op::gt_imm(dest, dest, imm as i8)),
745+
">=" => self.emit(Op::ge_imm(dest, dest, imm as i8)),
746+
_ => unreachable!(),
747+
};
748+
return Ok(Some(true));
749+
}
750+
}
751+
}
752+
734753
let make_binary_op: Option<fn(Reg, Reg, Reg) -> Op> = match op {
735754
"+" => Some(Op::add),
736755
"-" => Some(Op::sub),

src/vm.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,66 @@ impl VM {
715715
unsafe { *self.registers.get_unchecked_mut(base + dest as usize) = result };
716716
}
717717

718+
Op::LT_IMM => {
719+
let dest = instr.a();
720+
let src = instr.b();
721+
let imm = instr.c() as i8 as i64;
722+
let v = unsafe { self.registers.get_unchecked(base + src as usize) };
723+
let result = if let Some(x) = v.as_int() {
724+
Value::bool(x < imm)
725+
} else if let Some(x) = v.as_float() {
726+
Value::bool(x < imm as f64)
727+
} else {
728+
return Err("< expects a number".to_string());
729+
};
730+
unsafe { *self.registers.get_unchecked_mut(base + dest as usize) = result };
731+
}
732+
733+
Op::LE_IMM => {
734+
let dest = instr.a();
735+
let src = instr.b();
736+
let imm = instr.c() as i8 as i64;
737+
let v = unsafe { self.registers.get_unchecked(base + src as usize) };
738+
let result = if let Some(x) = v.as_int() {
739+
Value::bool(x <= imm)
740+
} else if let Some(x) = v.as_float() {
741+
Value::bool(x <= imm as f64)
742+
} else {
743+
return Err("<= expects a number".to_string());
744+
};
745+
unsafe { *self.registers.get_unchecked_mut(base + dest as usize) = result };
746+
}
747+
748+
Op::GT_IMM => {
749+
let dest = instr.a();
750+
let src = instr.b();
751+
let imm = instr.c() as i8 as i64;
752+
let v = unsafe { self.registers.get_unchecked(base + src as usize) };
753+
let result = if let Some(x) = v.as_int() {
754+
Value::bool(x > imm)
755+
} else if let Some(x) = v.as_float() {
756+
Value::bool(x > imm as f64)
757+
} else {
758+
return Err("> expects a number".to_string());
759+
};
760+
unsafe { *self.registers.get_unchecked_mut(base + dest as usize) = result };
761+
}
762+
763+
Op::GE_IMM => {
764+
let dest = instr.a();
765+
let src = instr.b();
766+
let imm = instr.c() as i8 as i64;
767+
let v = unsafe { self.registers.get_unchecked(base + src as usize) };
768+
let result = if let Some(x) = v.as_int() {
769+
Value::bool(x >= imm)
770+
} else if let Some(x) = v.as_float() {
771+
Value::bool(x >= imm as f64)
772+
} else {
773+
return Err(">= expects a number".to_string());
774+
};
775+
unsafe { *self.registers.get_unchecked_mut(base + dest as usize) = result };
776+
}
777+
718778
_ => {
719779
return Err(format!("Unknown opcode: {}", instr.opcode()));
720780
}
@@ -1263,4 +1323,25 @@ mod tests {
12631323
let cons = result.as_cons().expect("expected cons cell");
12641324
assert_eq!(cons.car.as_int(), Some(3));
12651325
}
1326+
1327+
#[test]
1328+
fn test_comparison_immediate() {
1329+
// Test comparison with immediate values
1330+
assert_eq!(vm_eval("(< 5 10)").unwrap(), Value::Bool(true));
1331+
assert_eq!(vm_eval("(< 10 5)").unwrap(), Value::Bool(false));
1332+
assert_eq!(vm_eval("(<= 5 5)").unwrap(), Value::Bool(true));
1333+
assert_eq!(vm_eval("(<= 6 5)").unwrap(), Value::Bool(false));
1334+
assert_eq!(vm_eval("(> 10 5)").unwrap(), Value::Bool(true));
1335+
assert_eq!(vm_eval("(> 5 10)").unwrap(), Value::Bool(false));
1336+
assert_eq!(vm_eval("(>= 5 5)").unwrap(), Value::Bool(true));
1337+
assert_eq!(vm_eval("(>= 4 5)").unwrap(), Value::Bool(false));
1338+
1339+
// Test with negative immediates
1340+
assert_eq!(vm_eval("(< -5 0)").unwrap(), Value::Bool(true));
1341+
assert_eq!(vm_eval("(<= 0 0)").unwrap(), Value::Bool(true));
1342+
1343+
// Test in a loop context (sum function uses (<= n 0))
1344+
let result = vm_eval("(do (def count-down (fn (n) (if (<= n 0) 0 (+ 1 (count-down (- n 1)))))) (count-down 10))").unwrap();
1345+
assert_eq!(result, Value::Int(10));
1346+
}
12661347
}

0 commit comments

Comments
 (0)