Skip to content

Commit

Permalink
feat: stack operations for vm
Browse files Browse the repository at this point in the history
  • Loading branch information
Mateusz Russak committed Dec 7, 2023
1 parent 3e0cca3 commit 7aab8b9
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 73 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
171 changes: 111 additions & 60 deletions crates/compiler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u8> {
Instrs(self.code.iter().map(|c| c.byte_description()).collect()).into()
Expand Down Expand Up @@ -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<Vec<u8>, 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<Code, PError> {
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<Vec<u8>, PError> {
let code = self.compile_node(node)?;
self.code.append(code);
self.code.push(Instr::Exit);
let code = self.code.to_bytes();
Ok(code)
}

}

18 changes: 18 additions & 0 deletions crates/vm/src/instr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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,
}
}
}
Expand All @@ -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),
}
}

Expand Down Expand Up @@ -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),
}
}

Expand Down Expand Up @@ -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),
}
}
}
Expand Down Expand Up @@ -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);
}
4 changes: 4 additions & 0 deletions crates/vm/src/op_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,20 @@ pub enum OpCode {
Beq = 0b11001010,
Bne = 0b11001011,
Jmp = 0b11001100,
Movs = 0b11001101,

Mov = 0b10000001,
Not = 0b10000010,

Load0 = 0b01000001,
Load1 = 0b01000010,
Store = 0b01000011,
Push = 0b01000100,
Pop = 0b01000101,

Exit = 0b00000000,


}

impl From<u8> for OpCode {
Expand Down
41 changes: 34 additions & 7 deletions crates/vm/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,19 +68,26 @@ impl VM {


fn next(&mut self) -> Option<Instr> {
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
})
}

pub fn run(&mut self) -> Result<u32, PError> {
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];
Expand Down Expand Up @@ -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) => {
Expand All @@ -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)
}
}
Expand Down Expand Up @@ -293,4 +311,13 @@ mod tests {
let result = vm.run();
assert_eq!(result.unwrap(), 1);
}

#[test]
fn push_pop() {
let bytes:Vec<u8> = 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);
}
}
6 changes: 3 additions & 3 deletions examples/sieve_of_eratosthenes.lum
Original file line number Diff line number Diff line change
@@ -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 {''});
}
Expand Down
4 changes: 3 additions & 1 deletion tests/compute_compiled_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down

0 comments on commit 7aab8b9

Please sign in to comment.