From 7aab8b9655889171decf15a53736ef15f4dcfa46 Mon Sep 17 00:00:00 2001 From: Mateusz Russak Date: Thu, 7 Dec 2023 23:57:14 +0100 Subject: [PATCH] feat: stack operations for vm --- .github/workflows/rust.yml | 4 +- crates/compiler/src/lib.rs | 171 +++++++++++++++++++---------- crates/vm/src/instr.rs | 18 +++ crates/vm/src/op_code.rs | 4 + crates/vm/src/vm.rs | 41 +++++-- examples/sieve_of_eratosthenes.lum | 6 +- tests/compute_compiled_test.rs | 4 +- 7 files changed, 175 insertions(+), 73 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index ebda42c..7b2eec8 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -15,6 +15,6 @@ jobs: steps: - uses: actions/checkout@v3 - name: Build - run: cargo build --workspace --verbose + run: cargo build --workspace - name: Run tests - run: cargo test --workspace --verbose + run: cargo test --workspace diff --git a/crates/compiler/src/lib.rs b/crates/compiler/src/lib.rs index d6cee0d..7a263c7 100644 --- a/crates/compiler/src/lib.rs +++ b/crates/compiler/src/lib.rs @@ -15,8 +15,12 @@ impl Code { code: Vec::new(), } } - pub fn push(&mut self, inst: Instr) { + pub fn push(&mut self, inst: Instr) -> usize { self.code.push(inst); + self.code.len() - 1 + } + pub fn append(&mut self, code: Code) { + self.code.extend(code.code); } pub fn to_bytes(&self) -> Vec { Instrs(self.code.iter().map(|c| c.byte_description()).collect()).into() @@ -55,71 +59,118 @@ impl Display for Mem { } } -pub fn compile_node(node: &Node, mut consts: &mut Mem, inst: &mut Code) -> Result<(), PError> { - match node.op { - Op::Add => { - compile_node(&node.children[0], &mut consts, inst)?; - inst.push(Instr::Mov(2, 0)); - compile_node(&node.children[1], &mut consts, inst)?; - inst.push(Instr::Mov(1, 0)); - inst.push(Instr::Add(0,1,2)); - }, - Op::Sub => { - compile_node(&node.children[0], &mut consts, inst)?; - inst.push(Instr::Mov(2, 0)); - compile_node(&node.children[1], &mut consts, inst)?; - inst.push(Instr::Mov(1, 0)); - inst.push(Instr::Sub(0,2,1)); - }, - Op::Value => { - match &node.value { - Some(Value::Number(0)) => inst.push(Instr::Load0(0)), - Some(Value::Number(1)) => inst.push(Instr::Load1(0)), - Some(Value::Number(val)) => { - inst.push(Instr::Load(0, *val as u16)); - }, - Some(..) => { - panic!("Unknown value: {}", node); - }, - None => panic!("No value"), - } - }, - Op::Scope | Op::Paren => { - for child in &node.children { - println!("child: {}", child.op); - compile_node(child, &mut consts, inst)?; - } - inst.push(Instr::Exit); - }, - _ => { - panic!("Unknown op: {} {}", node, node.op); + +struct Stack { + stackptr: usize, +} + +impl Stack { + pub fn new() -> Stack { + Stack { + stackptr: 0, } } - Ok(()) + pub fn push(&mut self, val: u32) -> usize { + let location = self.stackptr; + self.stackptr += 4; + location + } + pub fn pop(&mut self) { + self.stackptr -= 4; + } } -pub fn compile(node: &Node) -> Result, PError> { - let mut inst = Code::new(); - let mut consts = Mem::new(); - compile_node(node, &mut consts, &mut inst)?; - let code = inst.to_bytes(); - Ok(code) +pub struct StackLocation { + address: usize, + size: usize, } -#[cfg(test)] -mod tests { - use parser::Node; - use lexer::Location; - - use super::*; +pub struct Compiler { + stack: Stack, + mem: Mem, + code: Code, +} - #[test] - fn compile_simple() { - let mut node = Node::new(Op::Scope, Location::Eof); - node.add(Node::new(Op::Value, Location::Eof).set_value(1.into())); - let bytes = compile(&node).unwrap(); - assert_eq!(bytes, vec![OpCode::Load1.into(), 0, OpCode::Exit.into()]); +impl Compiler { + pub fn new() -> Compiler { + Compiler { + stack: Stack::new(), + mem: Mem::new(), + code: Code::new(), + } + } + pub fn compile_node(&mut self, node: &Node) -> Result { + match node.op { + Op::Add => { + let mut code = Code::new(); + code.append(self.compile_node(&node.children[0])?); + code.append(self.compile_node(&node.children[1])?); + code.push(Instr::Pop(1)); + code.push(Instr::Pop(2)); + code.push(Instr::Add(0,1,2)); + code.push(Instr::Push(0)); + Ok(code) + }, + Op::Sub => { + let mut code = Code::new(); + code.append(self.compile_node(&node.children[0])?); + code.append(self.compile_node(&node.children[1])?); + code.push(Instr::Pop(1)); + code.push(Instr::Pop(2)); + code.push(Instr::Sub(0,2,1)); + Ok(code) + }, + Op::Value => { + let mut code = Code::new(); + match &node.value { + Some(Value::Number(0)) => code.push(Instr::Load0(0)), + Some(Value::Number(1)) => code.push(Instr::Load1(0)), + Some(Value::Number(val)) => { + code.push(Instr::Load(0, *val as u16)) + }, + Some(..) => { + panic!("Unknown value: {}", node) + }, + None => panic!("No value"), + }; + code.push(Instr::Push(0)); + Ok(code) + }, + Op::Branch => { + let mut code = Code::new(); + code.append(self.compile_node(&node.children[0])?); + code.push(Instr::Pop(1)); + code.push(Instr::Load0(0)); + let thenBody = self.compile_node(&node.children[1])?; + let elseBody = self.compile_node(&node.children[2])?; + let jmp = Instr::Jmp((elseBody.code.len() as u16 + 1) as i32); + code.push(Instr::Beq(0, 1, thenBody.code.len() as i16 + jmp.size() as i16)); + code.append(thenBody); + code.push(jmp); + code.append(elseBody); + Ok(code) + }, + Op::Paren => { + self.compile_node(node.left().unwrap()) + }, + Op::Scope => { + let mut code = Code::new(); + for child in &node.children { + println!("child: {}", child.op); + code.append(self.compile_node(child)?); + } + Ok(code) + }, + _ => { + panic!("Unknown op: {} {}", node, node.op); + } + } + } + pub fn compile(&mut self, node: &Node) -> Result, PError> { + let code = self.compile_node(node)?; + self.code.append(code); + self.code.push(Instr::Exit); + let code = self.code.to_bytes(); + Ok(code) } - } - diff --git a/crates/vm/src/instr.rs b/crates/vm/src/instr.rs index 76c31e7..8f915fe 100644 --- a/crates/vm/src/instr.rs +++ b/crates/vm/src/instr.rs @@ -105,6 +105,9 @@ pub enum Instr { Beq(u8,u8,i16), Bne(u8,u8,i16), Jmp(i32), + Push(u8), + Pop(u8), + Movs(u8, u16), } impl From<&Instr> for OpCode { @@ -128,6 +131,9 @@ impl From<&Instr> for OpCode { Instr::Beq(..) => OpCode::Beq, Instr::Bne(..) => OpCode::Bne, Instr::Jmp(..) => OpCode::Jmp, + Instr::Push(..) => OpCode::Push, + Instr::Pop(..) => OpCode::Pop, + Instr::Movs(..) => OpCode::Movs, } } } @@ -153,6 +159,9 @@ impl Instr { Instr::Beq(r1,r2,addr) => make_instr!(4; op[31;24], r1[23;20], r2[19;16], addr[15;0]; op = OpCode::Beq), Instr::Bne(r1,r2,addr) => make_instr!(4; op[31;24], r1[23;20], r2[19;16], addr[15;0]; op = OpCode::Bne), Instr::Jmp(addr) => make_instr!(4; op[31;24], addr[23;0]; op = OpCode::Jmp), + Instr::Push(tr) => make_instr!(2; op[31;24], tr[23;16]; op = OpCode::Push), + Instr::Pop(tr) => make_instr!(2; op[31;24], tr[23;16]; op = OpCode::Pop), + Instr::Movs(tr, val) => make_instr!(4; op[31;24], tr[23;16], val[15;0]; op = OpCode::Movs), } } @@ -189,6 +198,9 @@ impl Instr { OpCode::Beq=> parse_instr!(val; r1 r2 addr -> Beq), OpCode::Bne=> parse_instr!(val; r1 r2 addr -> Bne), OpCode::Jmp=> parse_instr!(val; adr -> Jmp), + OpCode::Push=> parse_instr!(val; tr -> Push), + OpCode::Pop=> parse_instr!(val; tr -> Pop), + OpCode::Movs=> parse_instr!(val; tr value -> Movs), } } @@ -218,6 +230,9 @@ impl Display for Instr { Instr::Beq(r1,r2,addr) => write!(f, "Beq r{} r{} {}", r1, r2, addr), Instr::Bne(r1,r2,addr) => write!(f, "Bne r{} r{} {}", r1, r2, addr), Instr::Jmp(val) => write!(f, "Jmp {}", val), + Instr::Push(tr) => write!(f, "Push r{}", tr), + Instr::Pop(tr) => write!(f, "Pop r{}", tr), + Instr::Movs(tr, val) => write!(f, "Movs r{} {}", tr, val), } } } @@ -274,4 +289,7 @@ mod tests { check_instr!(bne; Bne(1,2,3); 4); check_instr!(jmp; Jmp(3); 4); check_instr!(jmp_negative; Jmp(-3); 4); + check_instr!(push; Push(1); 2); + check_instr!(pop; Pop(1); 2); + check_instr!(movs; Movs(1,2); 4); } diff --git a/crates/vm/src/op_code.rs b/crates/vm/src/op_code.rs index 95c647b..fcefd86 100644 --- a/crates/vm/src/op_code.rs +++ b/crates/vm/src/op_code.rs @@ -13,6 +13,7 @@ pub enum OpCode { Beq = 0b11001010, Bne = 0b11001011, Jmp = 0b11001100, + Movs = 0b11001101, Mov = 0b10000001, Not = 0b10000010, @@ -20,9 +21,12 @@ pub enum OpCode { Load0 = 0b01000001, Load1 = 0b01000010, Store = 0b01000011, + Push = 0b01000100, + Pop = 0b01000101, Exit = 0b00000000, + } impl From for OpCode { diff --git a/crates/vm/src/vm.rs b/crates/vm/src/vm.rs index da93f5e..652657c 100644 --- a/crates/vm/src/vm.rs +++ b/crates/vm/src/vm.rs @@ -68,6 +68,8 @@ impl VM { fn next(&mut self) -> Option { + println!("next: {} {:?} size: {}", self.pc, Instr::from_bytes(&self.prog, self.pc), Instr::from_bytes(&self.prog, self.pc).unwrap().size()); + Instr::from_bytes(&self.prog, self.pc).map(|i| { self.pc += i.size(); i @@ -75,12 +77,17 @@ impl VM { } pub fn run(&mut self) -> Result { - let mut xx = 10; + let mut pc = 0; + println!("prog len: {}", self.prog.len()); + while pc < self.prog.len() { + let Some(inst) = Instr::from_bytes(&self.prog, pc) else { + break; + }; + println!("{}: {}", pc, inst); + pc += inst.size(); + } while let Some(inst) = self.next() { - xx -= 1; - if xx < 0 {break;} - println!("regs: {:?}", self.regs); - println!("inst: {}", inst); + println!("inst: {} {}", self.pc - inst.size(), inst); match inst { Instr::Sub(target, v1, v2) => { self.regs[target as usize] = self.regs[v1 as usize] - self.regs[v2 as usize]; @@ -117,7 +124,7 @@ impl VM { self.regs[target as usize] = 1; }, Instr::Exit => { - println!("{}", self); + println!("Exit: {}", self); return Ok(self.regs[0]); }, Instr::Mov(target, v1) => { @@ -137,12 +144,23 @@ impl VM { self.pc = adr as usize; } }, + Instr::Push(r1) => { + self.stack.push(self.regs[r1 as usize]); + }, + Instr::Pop(r1) => { + self.regs[r1 as usize] = self.stack.pop().unwrap(); + }, + Instr::Movs(r1, sp) => { + self.regs[r1 as usize] = self.stack[sp as usize]; + }, _ => { panic!("Unknown op code: {}", inst.op_code()); } } + println!("regs: {:?}", self.regs); + println!("stack: {:?}\n", self.stack); } - + println!("{}", self.pc); Ok(0) } } @@ -293,4 +311,13 @@ mod tests { let result = vm.run(); assert_eq!(result.unwrap(), 1); } + + #[test] + fn push_pop() { + let bytes:Vec = code![Load(1, 7), Push(1), Pop(0), Exit]; + println!("{:?}", bytes); + let mut vm = VM::new(bytes); + let result = vm.run(); + assert_eq!(result.unwrap(), 7); + } } diff --git a/examples/sieve_of_eratosthenes.lum b/examples/sieve_of_eratosthenes.lum index 4346b0d..406019b 100644 --- a/examples/sieve_of_eratosthenes.lum +++ b/examples/sieve_of_eratosthenes.lum @@ -1,15 +1,15 @@ tab = table(120); -for( i in 0..120) { +for( i of 0..120) { tab[i] = 1; } -for( i in 0..120) { +for( i of 0..120) { n = 1; while (i*n < 120) { tab[i*n] = 0; } } print("Prime numbers < 120: "); -for( i in 0..120) { +for( i of 0..120) { if(tab[i] == 1) { print(i + {if(i<120) {', '} else {''}); } diff --git a/tests/compute_compiled_test.rs b/tests/compute_compiled_test.rs index 1bdc05c..aa115c6 100644 --- a/tests/compute_compiled_test.rs +++ b/tests/compute_compiled_test.rs @@ -16,7 +16,7 @@ macro_rules! test_return_code{ panic!("\nError:\n{}\n", e.format_error($i, "file.lum", false)); }); println!("{}", node); - let bytes = compiler::compile(&node).unwrap_or_else(|e| { + let bytes = compiler::Compiler::new().compile(&node).unwrap_or_else(|e| { panic!("\nError:\n{}\n", e.format_error($i, "file.lum", false)); }); println!("{:?}", bytes); @@ -33,6 +33,8 @@ test_return_code!(vm_return_1, "1", 1); test_return_code!(vm_compute_add, "1+2", 3); test_return_code!(vm_compute_add_2, "1+2+2", 5); test_return_code!(vm_compute_sub, "4-2", 2); +test_return_code!(vm_compute_complex, "(1+5)-(2+2)", 2); +test_return_code!(vm_compute_if, "if(1){2}else{3}", 2); //test_return_code!(vm_compute_sub_overflow, "2-4", (-2i32) as u32); test_return_code!(vm_compute_add_and_sub, "1+2-2", 1); //test_return_code!(vm_compute_branch, "if(1){2}else{3}", 2);