Skip to content

Commit 707a086

Browse files
committed
Add Op::CONS (opcode 50) with ABC format: dest, car, cdr
Add VM handler that creates cons cells directly Update compiler to detect (cons ...) calls and emit spec opcode
1 parent a2fe7ab commit 707a086

File tree

3 files changed

+67
-0
lines changed

3 files changed

+67
-0
lines changed

src/bytecode.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ impl std::fmt::Debug for Op {
7070
Self::JUMP_IF_GE_IMM => write!(f, "JumpIfGeImm({}, {}, {})", self.a(), self.b() as i8, self.c() as i8),
7171
Self::JUMP_IF_NIL => write!(f, "JumpIfNil({}, {})", self.a(), self.sbx()),
7272
Self::JUMP_IF_NOT_NIL => write!(f, "JumpIfNotNil({}, {})", self.a(), self.sbx()),
73+
Self::CONS => write!(f, "Cons({}, {}, {})", self.a(), self.b(), self.c()),
7374
_ => write!(f, "Unknown(0x{:08x})", self.0),
7475
}
7576
}
@@ -129,6 +130,8 @@ impl Op {
129130
// Specialized nil check opcodes (common in list processing)
130131
pub const JUMP_IF_NIL: u8 = 48; // A: src, sBx: offset - jump if src is nil
131132
pub const JUMP_IF_NOT_NIL: u8 = 49; // A: src, sBx: offset - jump if src is NOT nil
133+
// Specialized cons opcode (very common in list construction)
134+
pub const CONS: u8 = 50; // ABC: dest, car, cdr - create cons cell
132135

133136
// ========== Constructors ==========
134137

@@ -445,6 +448,12 @@ impl Op {
445448
Self::asbx(Self::JUMP_IF_NOT_NIL, src, offset)
446449
}
447450

451+
// Specialized cons (very common in list construction)
452+
#[inline(always)]
453+
pub const fn cons(dest: Reg, car: Reg, cdr: Reg) -> Self {
454+
Self::abc(Self::CONS, dest, car, cdr)
455+
}
456+
448457
// ========== Jump patching helpers ==========
449458

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

src/compiler.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -949,6 +949,19 @@ impl Compiler {
949949
_ => {}
950950
}
951951
}
952+
953+
// Try specialized cons opcode (two arguments)
954+
if args.len() == 2 && op == "cons" {
955+
// Compile car into dest
956+
self.compile_expr(&args[0], dest, false)?;
957+
// Compile cdr into temp register
958+
let cdr_reg = self.alloc_reg();
959+
self.compile_expr(&args[1], cdr_reg, false)?;
960+
// Emit Cons
961+
self.emit(Op::cons(dest, dest, cdr_reg));
962+
self.free_reg(); // free cdr_reg
963+
return Ok(());
964+
}
952965
}
953966

954967
// Check if calling a global symbol (optimization: use CallGlobal/TailCallGlobal)

src/vm.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,17 @@ impl VM {
908908
}
909909
}
910910

911+
// Specialized cons opcode (very common in list construction)
912+
Op::CONS => {
913+
let dest = instr.a();
914+
let car_reg = instr.b();
915+
let cdr_reg = instr.c();
916+
let car = unsafe { self.registers.get_unchecked(base + car_reg as usize).clone() };
917+
let cdr = unsafe { self.registers.get_unchecked(base + cdr_reg as usize).clone() };
918+
let cons = Value::cons(car, cdr);
919+
unsafe { *self.registers.get_unchecked_mut(base + dest as usize) = cons };
920+
}
921+
911922
_ => {
912923
return Err(format!("Unknown opcode: {}", instr.opcode()));
913924
}
@@ -1539,4 +1550,38 @@ mod tests {
15391550
(list-sum (cons 1 (cons 2 (cons 3 (cons 4 (cons 5 nil))))) 0))").unwrap();
15401551
assert_eq!(result, Value::Int(15));
15411552
}
1553+
1554+
#[test]
1555+
fn test_specialized_cons() {
1556+
// Test specialized cons opcode
1557+
// Basic cons
1558+
let result = vm_eval("(cons 1 nil)").unwrap();
1559+
let cons = result.as_cons().expect("expected cons cell");
1560+
assert_eq!(cons.car.as_int(), Some(1));
1561+
assert!(cons.cdr.is_nil());
1562+
1563+
// Nested cons
1564+
let result = vm_eval("(cons 1 (cons 2 (cons 3 nil)))").unwrap();
1565+
let cons = result.as_cons().expect("expected cons cell");
1566+
assert_eq!(cons.car.as_int(), Some(1));
1567+
1568+
// List reversal using cons (exercises the opcode heavily)
1569+
let result = vm_eval("(do
1570+
(def reverse-acc (fn (lst acc)
1571+
(if (nil? lst)
1572+
acc
1573+
(reverse-acc (cdr lst) (cons (car lst) acc)))))
1574+
(reverse-acc (cons 1 (cons 2 (cons 3 nil))) nil))").unwrap();
1575+
let cons = result.as_cons().expect("expected cons cell");
1576+
assert_eq!(cons.car.as_int(), Some(3));
1577+
1578+
// Map function using cons
1579+
let result = vm_eval("(do
1580+
(def map-double (fn (lst)
1581+
(if (nil? lst)
1582+
nil
1583+
(cons (* 2 (car lst)) (map-double (cdr lst))))))
1584+
(car (map-double (cons 5 (cons 10 nil)))))").unwrap();
1585+
assert_eq!(result, Value::Int(10)); // first element 5 * 2 = 10
1586+
}
15421587
}

0 commit comments

Comments
 (0)