From affd7034e62c02601599bc8ae6bb534176df6ee5 Mon Sep 17 00:00:00 2001 From: Filipe Rodrigues Date: Sun, 10 Jan 2021 15:01:16 +0000 Subject: [PATCH] Added `pseudo::Decodable` trait for decoding pseudo instructions. --- dcb-exe/src/exe/inst/basic.rs | 1 + dcb-exe/src/exe/inst/iter.rs | 67 +++++++++++++---------- dcb-exe/src/exe/inst/pseudo.rs | 39 ++++++++++--- dcb-exe/src/exe/inst/pseudo/alu_assign.rs | 37 ++++++------- dcb-exe/src/lib.rs | 3 +- 5 files changed, 89 insertions(+), 58 deletions(-) diff --git a/dcb-exe/src/exe/inst/basic.rs b/dcb-exe/src/exe/inst/basic.rs index 7ced48f..9a6265d 100644 --- a/dcb-exe/src/exe/inst/basic.rs +++ b/dcb-exe/src/exe/inst/basic.rs @@ -113,6 +113,7 @@ impl Raw { /// All basic instructions #[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[derive(derive_more::TryInto)] pub enum Inst { /// Alu Alu(alu::Inst), diff --git a/dcb-exe/src/exe/inst/iter.rs b/dcb-exe/src/exe/inst/iter.rs index 109af9e..db74e67 100644 --- a/dcb-exe/src/exe/inst/iter.rs +++ b/dcb-exe/src/exe/inst/iter.rs @@ -2,11 +2,12 @@ // Imports use super::{ - basic::{self, Decodable}, - pseudo, Directive, Inst, + basic::{self, Decodable as _}, + pseudo::{self, Decodable as _}, + Directive, Inst, }; use crate::Pos; -use dcb_util::NextFromBytes; +use std::convert::TryFrom; /// Parsing iterator, reads instructions from a `[u8]` slice #[derive(PartialEq, Eq, Clone, Debug)] @@ -47,34 +48,42 @@ impl<'a> Iterator for ParseIter<'a> { return Some((pos, Inst::Directive(directive))); } - // Else decode an instruction, falling back to a directive if unable to - match self.bytes.next_u32().and_then(basic::Raw::from_u32).and_then(basic::Inst::decode) { - // If we got one, update our bytes and check if it's a pseudo instruction - Some(inst) => { - self.bytes = &self.bytes[4..]; - let pos = self.cur_pos; - self.cur_pos += 4; - match pseudo::Inst::decode(inst, self.bytes) { - Some((inst, len)) => { - self.bytes = &self.bytes[len..]; - self.cur_pos += len as u32; - Some((pos, Inst::Pseudo(inst))) - }, - None => Some((pos, Inst::Basic(inst))), - } - }, + // Else make the instruction iterator + // Note: We fuse it to make sure that pseudo instructions don't try to skip + // invalid instructions. + let mut insts = self + .bytes + .chunks(4) + .map(|word| u32::from_ne_bytes([word[0], word[1], word[2], word[3]])) + .map_while(|word| basic::Raw::from_u32(word).and_then(basic::Inst::decode)) + .fuse(); - // If we don't have enough for a `u32` or we didn't manage to - // parse an instruction, try to parse a directive - None => match Directive::decode(self.cur_pos, self.bytes) { - Some((directive, len)) => { - self.bytes = &self.bytes[len..]; - let pos = self.cur_pos; - self.cur_pos += len as u32; - Some((pos, Inst::Directive(directive))) - }, - None => None, + // Try to decode a pseudo-instruction + if let Some(inst) = pseudo::Inst::decode(insts.clone()) { + let len = inst.size() * 4; + self.bytes = &self.bytes[usize::try_from(len).expect("Instruction size didn't fit into a `usize`")..]; + let pos = self.cur_pos; + self.cur_pos += len; + return Some((pos, Inst::Pseudo(inst))); + } + + // Else try to decode it as an basic instruction + if let Some(inst) = insts.next() { + self.bytes = &self.bytes[4..]; + let pos = self.cur_pos; + self.cur_pos += 4; + return Some((pos, Inst::Basic(inst))); + } + + // Else read it as a directive + match Directive::decode(self.cur_pos, self.bytes) { + Some((directive, len)) => { + self.bytes = &self.bytes[len..]; + let pos = self.cur_pos; + self.cur_pos += len as u32; + Some((pos, Inst::Directive(directive))) }, + None => None, } } } diff --git a/dcb-exe/src/exe/inst/pseudo.rs b/dcb-exe/src/exe/inst/pseudo.rs index 3735213..bd2dd1f 100644 --- a/dcb-exe/src/exe/inst/pseudo.rs +++ b/dcb-exe/src/exe/inst/pseudo.rs @@ -1,7 +1,8 @@ //! Pseudo instructions //! -//! This modules defines all the pseudo instructions usually -//! used in mips. They are variable length. +//! All instructions in this module are variable length, and are decoded +//! from a starting basic instruction and remaining instruction bytes, +//! via the [`Decodable`] trait. // Modules pub mod alu_assign; @@ -60,12 +61,15 @@ pub enum Inst { */ } -impl Inst { - /// Attempts to parse a pseudo instruction from a start - /// basic instruction and remaining bytes - #[must_use] - pub fn decode(inst: basic::Inst, bytes: &[u8]) -> Option<(Self, usize)> { - alu_assign::Inst::decode(inst, bytes).map(|(inst, len)| (Self::AluAssign(inst), len)) +impl Decodable for Inst { + fn decode(insts: impl Iterator + Clone) -> Option { + alu_assign::Inst::decode(insts).map(Self::AluAssign) + } + + fn size(&self) -> u32 { + match self { + Self::AluAssign(inst) => inst.size(), + } } } @@ -96,3 +100,22 @@ impl PseudoInst { } } */ + +/// A decodable pseudo instruction +pub trait Decodable: Sized { + /// Decodes this instruction + #[must_use] + fn decode(insts: impl Iterator + Clone) -> Option; + + /// Returns how many _words_ long this instruction is + fn size(&self) -> u32; +} + +/* +/// An encodable pseudo instruction +pub trait Encodable: Decodable { + /// Encodes this instruction + #[must_use] + fn encode(&self) -> Self::Raw; +} +*/ diff --git a/dcb-exe/src/exe/inst/pseudo/alu_assign.rs b/dcb-exe/src/exe/inst/pseudo/alu_assign.rs index b74fac6..54a0e0b 100644 --- a/dcb-exe/src/exe/inst/pseudo/alu_assign.rs +++ b/dcb-exe/src/exe/inst/pseudo/alu_assign.rs @@ -1,11 +1,12 @@ //! Alu self-assign instructions // Imports +use super::Decodable; use crate::exe::inst::{ basic::{self, alu}, InstFmt, Register, }; -use std::fmt; +use std::{convert::TryInto, fmt}; /// Alu assign kind #[derive(PartialEq, Eq, Clone, Copy, Debug)] @@ -61,30 +62,26 @@ pub struct Inst { pub kind: Kind, } -impl Inst { - /// Decodes this pseudo instruction - #[must_use] - pub fn decode(inst: basic::Inst, _bytes: &[u8]) -> Option<(Self, usize)> { - let inst = match inst { - basic::Inst::Alu(inst) => match inst { - alu::Inst::Imm(alu::imm::Inst { dst, lhs, kind }) if dst == lhs => Some(Self { - dst, - kind: Kind::Imm { kind }, - }), - alu::Inst::Reg(alu::reg::Inst { dst, lhs, rhs, kind }) if dst == lhs => Some(Self { - dst, - kind: Kind::Reg { kind, rhs }, - }), - _ => None, - }, +impl Decodable for Inst { + fn decode(mut insts: impl Iterator + Clone) -> Option { + match insts.next()?.try_into().ok()? { + alu::Inst::Imm(alu::imm::Inst { dst, lhs, kind }) if dst == lhs => Some(Self { + dst, + kind: Kind::Imm { kind }, + }), + alu::Inst::Reg(alu::reg::Inst { dst, lhs, rhs, kind }) if dst == lhs => Some(Self { + dst, + kind: Kind::Reg { kind, rhs }, + }), _ => None, - }; + } + } - inst.map(|inst| (inst, 0)) + fn size(&self) -> u32 { + 1 } } - impl InstFmt for Inst { fn mnemonic(&self) -> &'static str { self.kind.mnemonic() diff --git a/dcb-exe/src/lib.rs b/dcb-exe/src/lib.rs index 8ec19ef..68fc6d2 100644 --- a/dcb-exe/src/lib.rs +++ b/dcb-exe/src/lib.rs @@ -13,7 +13,8 @@ never_type, or_patterns, associated_type_bounds, - bindings_after_at + bindings_after_at, + iter_map_while, )] // Lints #![warn(clippy::restriction, clippy::pedantic, clippy::nursery)]