From 1f93732a7ce7b363d1a69b22efad26322f18a2f2 Mon Sep 17 00:00:00 2001 From: Alek Ratzloff Date: Fri, 6 Mar 2020 12:53:07 -0500 Subject: [PATCH] Reorganize instruction assembler logic (fixing a known bug), add interrupts to frontend * Instruction assembler used to use macros for handling source, dest-source, source-source, etc instructions. It did not have a source-source implementation, however, so the two source-source instructions (cmpeq and cmplt) were treated as dest-source. This has been refactored into some local methods that handle this now. * Syntax for interrupts and interrupt returns are implemented and appear to be working Signed-off-by: Alek Ratzloff --- src/libvm/src/obj/assemble.rs | 187 +++++++++++++++++---------------- src/libvm/src/obj/syn/ast.rs | 4 + src/libvm/src/obj/syn/lexer.l | 2 + src/libvm/src/obj/syn/parser.y | 2 + 4 files changed, 102 insertions(+), 93 deletions(-) diff --git a/src/libvm/src/obj/assemble.rs b/src/libvm/src/obj/assemble.rs index 26c8bd5..f83c53e 100644 --- a/src/libvm/src/obj/assemble.rs +++ b/src/libvm/src/obj/assemble.rs @@ -177,115 +177,116 @@ impl Asm for Inst { type Out = Vec; fn assemble(&self, session: &mut AsmSession) -> Result { - let len = self.len(); - - macro_rules! map_inst { - ($op:expr, $dest:expr, $source:expr) => {{ - let mut bytes = Vec::with_capacity(len); - bytes.write_u16::($op).unwrap(); - let dest = $dest; - let dest_encoding = - dest.dest_encoding() - .ok_or_else(|| AsmError::IllegalDestValue { - value: dest.clone(), - })?; - let source = $source; - let source_encoding = source.source_encoding(); - bytes - .write_u8((dest_encoding << 4) | source_encoding) - .unwrap(); - bytes.extend(dest.assemble(session)?); - bytes.extend(source.assemble(session)?); - assert_eq!( - self.len(), - bytes.len(), - "instruction size mismatch in {} instruction - {:?} produces these bytes {:?}", - stringify!($op), - self, - bytes - ); - Ok(bytes) - }}; - - ($op:expr, $source:expr) => {{ - let mut bytes = Vec::with_capacity(len); - bytes.write_u16::($op).unwrap(); - let source = $source; - let source_encoding = source.source_encoding() << 4; - bytes.write_u8(source_encoding).unwrap(); - bytes.extend(source.assemble(session)?); - assert_eq!( - self.len(), - bytes.len(), - "instruction size mismatch in {} instruction - {:?} produces these bytes {:?}", - stringify!($op), - self, - bytes - ); - Ok(bytes) - }}; - - ($op:expr) => {{ - let mut bytes = Vec::with_capacity(len); - bytes.write_u16::($op).unwrap(); - assert_eq!( - self.len(), - bytes.len(), - "instruction size mismatch in {} instruction - {:?} produces these bytes {:?}", - stringify!($op), - self, - bytes - ); - Ok(bytes) - }}; - } match self { - Inst::Add(v1, v2) => map_inst!(inst::ADD, v1, v2), - Inst::Sub(v1, v2) => map_inst!(inst::SUB, v1, v2), - Inst::Mul(v1, v2) => map_inst!(inst::MUL, v1, v2), - Inst::Div(v1, v2) => map_inst!(inst::DIV, v1, v2), - Inst::IDiv(v1, v2) => map_inst!(inst::IDIV, v1, v2), - Inst::Mod(v1, v2) => map_inst!(inst::MOD, v1, v2), - Inst::And(v1, v2) => map_inst!(inst::AND, v1, v2), - Inst::Or(v1, v2) => map_inst!(inst::OR, v1, v2), - Inst::Xor(v1, v2) => map_inst!(inst::XOR, v1, v2), - Inst::Shl(v1, v2) => map_inst!(inst::SHL, v1, v2), - Inst::Shr(v1, v2) => map_inst!(inst::SHR, v1, v2), - Inst::INeg(v1, v2) => map_inst!(inst::INEG, v1, v2), - Inst::Inv(v1, v2) => map_inst!(inst::INV, v1, v2), - Inst::Not(v1, v2) => map_inst!(inst::NOT, v1, v2), - // TODO/BUG: CmpEq and CmpLt both take two sources instead of a source and destination - Inst::CmpEq(v1, v2) => map_inst!(inst::CMPEQ, v1, v2), - Inst::CmpLt(v1, v2) => map_inst!(inst::CMPLT, v1, v2), - Inst::Jmp(v) => map_inst!(inst::JMP, v), - Inst::Jz(v) => map_inst!(inst::JZ, v), - Inst::Jnz(v) => map_inst!(inst::JNZ, v), - Inst::Call(v) => map_inst!(inst::CALL, v), - Inst::Ret => map_inst!(inst::RET), - Inst::Push(v) => map_inst!(inst::PUSH, v), + Inst::Add(v1, v2) => self.map_dest_source(session, inst::ADD, v1, v2), + Inst::Sub(v1, v2) => self.map_dest_source(session, inst::SUB, v1, v2), + Inst::Mul(v1, v2) => self.map_dest_source(session, inst::MUL, v1, v2), + Inst::Div(v1, v2) => self.map_dest_source(session, inst::DIV, v1, v2), + Inst::IDiv(v1, v2) => self.map_dest_source(session, inst::IDIV, v1, v2), + Inst::Mod(v1, v2) => self.map_dest_source(session, inst::MOD, v1, v2), + Inst::And(v1, v2) => self.map_dest_source(session, inst::AND, v1, v2), + Inst::Or(v1, v2) => self.map_dest_source(session, inst::OR, v1, v2), + Inst::Xor(v1, v2) => self.map_dest_source(session, inst::XOR, v1, v2), + Inst::Shl(v1, v2) => self.map_dest_source(session, inst::SHL, v1, v2), + Inst::Shr(v1, v2) => self.map_dest_source(session, inst::SHR, v1, v2), + Inst::INeg(v1, v2) => self.map_dest_source(session, inst::INEG, v1, v2), + Inst::Inv(v1, v2) => self.map_dest_source(session, inst::INV, v1, v2), + Inst::Not(v1, v2) => self.map_dest_source(session, inst::NOT, v1, v2), + Inst::CmpEq(v1, v2) => self.map_source_source(session, inst::CMPEQ, v1, v2), + Inst::CmpLt(v1, v2) => self.map_source_source(session, inst::CMPLT, v1, v2), + Inst::Jmp(v) => self.map_source(session, inst::JMP, v), + Inst::Jz(v) => self.map_source(session, inst::JZ, v), + Inst::Jnz(v) => self.map_source(session, inst::JNZ, v), + Inst::Call(v) => self.map_source(session, inst::CALL, v), + Inst::Ret => self.map_inst(inst::RET), + Inst::Push(v) => self.map_source(session, inst::PUSH, v), Inst::Pop(dest) => { + let len = self.len(); let mut bytes = Vec::with_capacity(len); bytes.write_u16::(inst::POP).unwrap(); let dest_encoding = dest.source_encoding() << 4; bytes.write_u8(dest_encoding).unwrap(); bytes.extend(dest.assemble(session)?); assert_eq!( - self.len(), + len, bytes.len(), - "instruction size mismatch in inst::PUSH instruction - {:?} produces these bytes {:?}", - self, - bytes ); Ok(bytes) } - Inst::Mov(v1, v2) => map_inst!(inst::MOV, v1, v2), - Inst::Halt => map_inst!(inst::HALT), - Inst::Nop => map_inst!(inst::NOP), - Inst::Dump => map_inst!(inst::DUMP), + Inst::Int(v1, v2) => self.map_source_source(session, inst::INT, v1, v2), + Inst::IRet => self.map_inst(inst::IRET), + Inst::Mov(v1, v2) => self.map_dest_source(session, inst::MOV, v1, v2), + Inst::Halt => self.map_inst(inst::HALT), + Inst::Nop => self.map_inst(inst::NOP), + Inst::Dump => self.map_inst(inst::DUMP), } } } +impl Inst { + fn map_dest_source(&self, session: &mut AsmSession, op: inst::InstOp, dest: &Value, source: &Value) -> Result> { + let len = self.len(); + let mut bytes = Vec::with_capacity(len); + bytes.write_u16::(op).unwrap(); + let dest_encoding = + dest.dest_encoding() + .ok_or_else(|| AsmError::IllegalDestValue { + value: dest.clone(), + })?; + let source_encoding = source.source_encoding(); + bytes + .write_u8((dest_encoding << 4) | source_encoding) + .unwrap(); + bytes.extend(dest.assemble(session)?); + bytes.extend(source.assemble(session)?); + assert_eq!( + len, + bytes.len(), + ); + Ok(bytes) + } + + fn map_source_source(&self, session: &mut AsmSession, op: inst::InstOp, s1: &Value, s2: &Value) -> Result> { + let len = self.len(); + let mut bytes = Vec::with_capacity(len); + bytes.write_u16::(op).unwrap(); + let s1_encoding = s1.source_encoding(); + let s2_encoding = s2.source_encoding(); + bytes + .write_u8((s1_encoding << 4) | s2_encoding) + .unwrap(); + bytes.extend(s1.assemble(session)?); + bytes.extend(s2.assemble(session)?); + assert_eq!( + len, + bytes.len(), + ); + Ok(bytes) + } + + fn map_source(&self, session: &mut AsmSession, op: inst::InstOp, source: &Value) -> Result> { + let len = self.len(); + let mut bytes = Vec::with_capacity(len); + bytes.write_u16::(op).unwrap(); + let source_encoding = source.source_encoding() << 4; + bytes.write_u8(source_encoding).unwrap(); + bytes.extend(source.assemble(session)?); + assert_eq!(len, bytes.len()); + Ok(bytes) + } + + fn map_inst(&self, op: inst::InstOp) -> Result> { + let len = self.len(); + let mut bytes = Vec::with_capacity(len); + bytes.write_u16::(op).unwrap(); + assert_eq!( + len, + bytes.len(), + ); + Ok(bytes) + } +} + impl Asm for Value { type Out = Vec; diff --git a/src/libvm/src/obj/syn/ast.rs b/src/libvm/src/obj/syn/ast.rs index 2aa8c6f..649c668 100644 --- a/src/libvm/src/obj/syn/ast.rs +++ b/src/libvm/src/obj/syn/ast.rs @@ -217,6 +217,8 @@ pub enum Inst { Ret, Push(Value), Pop(Value), + Int(Value, Value), + IRet, Mov(Value, Value), Halt, Nop, @@ -242,6 +244,7 @@ impl Inst { | Inst::Not(v1, v2) | Inst::CmpEq(v1, v2) | Inst::CmpLt(v1, v2) + | Inst::Int(v1, v2) | Inst::Mov(v1, v2) => { 3 + v1.len() + v2.len() } Inst::Jmp(v) | Inst::Jz(v) @@ -250,6 +253,7 @@ impl Inst { | Inst::Push(v) | Inst::Pop(v) => { 3 + v.len() } Inst::Ret + | Inst::IRet | Inst::Halt | Inst::Nop | Inst::Dump => { 2 } diff --git a/src/libvm/src/obj/syn/lexer.l b/src/libvm/src/obj/syn/lexer.l index 4fa0dbf..06ed8ca 100644 --- a/src/libvm/src/obj/syn/lexer.l +++ b/src/libvm/src/obj/syn/lexer.l @@ -51,6 +51,8 @@ call "CALL" ret "RET" push "PUSH" pop "POP" +int "INT" +iret "IRET" mov "MOV" halt "HALT" nop "NOP" diff --git a/src/libvm/src/obj/syn/parser.y b/src/libvm/src/obj/syn/parser.y index 68e75e9..adea542 100644 --- a/src/libvm/src/obj/syn/parser.y +++ b/src/libvm/src/obj/syn/parser.y @@ -105,6 +105,8 @@ Inst -> Inst: | 'RET' { Inst::Ret } | 'PUSH' Value { Inst::Push($2) } | 'POP' Value { Inst::Pop($2) } + | 'INT' Value 'COMMA' Value { Inst::Int($2, $4) } + | 'IRET' { Inst::IRet } | 'MOV' Value 'COMMA' Value { Inst::Mov($2, $4) } | 'HALT' { Inst::Halt } | 'NOP' { Inst::Nop }