support mnemonic suffixes

This commit is contained in:
Richard Patel 2021-08-29 09:02:31 +02:00
parent 756e23f240
commit 40142dcd9b
5 changed files with 156 additions and 56 deletions

View File

@ -1,4 +1,4 @@
use std::fmt::{Display, LowerHex, UpperHex, Formatter}; use std::fmt::{Display, Formatter, LowerHex, UpperHex};
use num_traits::PrimInt; use num_traits::PrimInt;
@ -14,8 +14,7 @@ impl Display for FormattedIns {
impl FormattedIns { impl FormattedIns {
fn fmt_ins(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt_ins(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mnemonic = self.0.op.mnemonic(); write!(f, "{}{} ", self.0.op.mnemonic(), self.0.modifiers())?;
write!(f, "{} ", mnemonic)?;
let fields = self.0.fields(); let fields = self.0.fields();
let mut writing_offset = false; let mut writing_offset = false;
for (i, field) in fields.iter().enumerate() { for (i, field) in fields.iter().enumerate() {

View File

@ -1,23 +1,26 @@
use std::fmt::Formatter;
use std::ops::Range;
use num_traits::AsPrimitive;
use ppc750cl_macros::{fields, ins_impl, opcodes};
pub use crate::iter::{disasm_iter, DisasmIterator};
pub mod formatter; pub mod formatter;
mod iter; mod iter;
pub mod prelude { pub mod prelude {
pub use crate::formatter::FormattedIns;
pub use crate::Field; pub use crate::Field;
pub use crate::Field::*; pub use crate::Field::*;
pub use crate::Ins; pub use crate::Ins;
pub use crate::formatter::FormattedIns;
pub use crate::Opcode::*; pub use crate::Opcode::*;
pub use crate::{ pub use crate::{
Bit, BranchDest, CRBit, CRField, Offset, OpaqueU, Simm, Uimm, FPR, GPR, GQR, SPR, SR, Bit, BranchDest, CRBit, CRField, Offset, OpaqueU, Simm, Uimm, FPR, GPR, GQR, SPR, SR,
}; };
} }
use ppc750cl_macros::{fields, ins_impl, opcodes};
//pub use crate::formatter::AsmFormatter;
//use crate::formatter::SimpleFormatter;
pub use crate::iter::{disasm_iter, DisasmIterator};
macro_rules! field_arg { macro_rules! field_arg {
($name:ident, $typ:ident) => { ($name:ident, $typ:ident) => {
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
@ -25,6 +28,21 @@ macro_rules! field_arg {
}; };
} }
#[inline(always)]
fn bit(x: u32, idx: usize) -> bool {
((x >> (32 - idx - 1)) & 1) == 1
}
#[inline(always)]
fn bits<F>(x: u32, range: Range<usize>) -> F
where
F: 'static + std::marker::Copy,
u32: AsPrimitive<F>,
{
let masked: u32 = (x >> (32 - range.end)) & ((1 << range.len()) - 1);
masked.as_()
}
// General-purpose register. // General-purpose register.
field_arg!(GPR, u8); field_arg!(GPR, u8);
// Floating-point register (direct or paired-singles mode). // Floating-point register (direct or paired-singles mode).
@ -55,10 +73,48 @@ field_arg!(OpaqueU, u32);
// Generate the Field enum and impls. // Generate the Field enum and impls.
fields!(); fields!();
#[derive(Debug, Default)]
pub struct Modifiers {
pub oe: bool,
pub rc: bool,
pub lk: bool,
pub aa: bool,
}
impl std::fmt::Display for Modifiers {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
if self.aa {
write!(f, "a")?;
}
if self.lk {
write!(f, "l")?;
}
if self.oe {
write!(f, "o")?;
}
if self.rc {
write!(f, ".")?;
}
Ok(())
}
}
// Generate the Opcode enum and impls. // Generate the Opcode enum and impls.
// TODO This could be made more readable with a derive over an empty enum. // TODO This could be made more readable with a derive over an empty enum.
opcodes!(); opcodes!();
impl Opcode {
/// Detects the opcode of a machine code instruction.
pub fn detect(code: u32) -> Self {
Self::_detect(code) // auto-generated
}
/// Prints the basic mnemonic of an opcode.
pub fn mnemonic(self) -> &'static str {
self._mnemonic() // auto-generated
}
}
impl Default for Opcode { impl Default for Opcode {
fn default() -> Self { fn default() -> Self {
Opcode::Illegal Opcode::Illegal
@ -95,6 +151,11 @@ impl Ins {
self._fields() // auto-generated self._fields() // auto-generated
} }
/// Gets the modifiers of an instruction.
pub fn modifiers(&self) -> Modifiers {
self._modifiers() // auto-generated
}
/// Gets the defs of an instruction. /// Gets the defs of an instruction.
pub fn defs(&self) -> Vec<Field> { pub fn defs(&self) -> Vec<Field> {
self._defs() // auto-generated self._defs() // auto-generated
@ -104,6 +165,16 @@ impl Ins {
pub fn uses(&self) -> Vec<Field> { pub fn uses(&self) -> Vec<Field> {
self._uses() // auto-generated self._uses() // auto-generated
} }
/// Gets the given bit from the machine code instruction.
pub fn bit(&self, idx: usize) -> bool {
bit(self.code, idx)
}
/// Gets the given range of btis from the machine code instruction.
pub fn bits(&self, range: Range<usize>) -> u32 {
bits(self.code, range)
}
} }
ins_impl!(); ins_impl!();

View File

@ -4,6 +4,10 @@ macro_rules! assert_asm {
($ins:ident, $disasm:literal) => {{ ($ins:ident, $disasm:literal) => {{
assert_eq!(format!("{}", FormattedIns($ins)), $disasm) assert_eq!(format!("{}", FormattedIns($ins)), $disasm)
}}; }};
($code:literal, $disasm:literal) => {{
let ins = Ins::new($code, 0x8000_0000);
assert_eq!(format!("{}", FormattedIns(ins)), $disasm)
}};
} }
#[test] #[test]
@ -25,6 +29,13 @@ fn test_ins_addi() {
assert_eq!(ins.defs(), vec![rD(GPR(0))]); assert_eq!(ins.defs(), vec![rD(GPR(0))]);
assert_eq!(ins.uses(), vec![rA(GPR(1))]); assert_eq!(ins.uses(), vec![rA(GPR(1))]);
assert_asm!(ins, "addi r0, r1, 0x140"); assert_asm!(ins, "addi r0, r1, 0x140");
assert_asm!(0x38010008, "addi r0, r1, 0x8");
assert_asm!(0x38010010, "addi r0, r1, 0x10");
assert_asm!(0x38010018, "addi r0, r1, 0x18");
assert_asm!(0x38010140, "addi r0, r1, 0x140");
assert_asm!(0x38049000, "addi r0, r4, -0x7000");
//assert_asm!(0x38a00000, "li r5, 0");
} }
#[test] #[test]
@ -45,48 +56,26 @@ fn test_ins_psq_lx() {
assert_eq!(ins.uses(), vec![rB(GPR(0))]); assert_eq!(ins.uses(), vec![rB(GPR(0))]);
} }
/*
#[test]
fn test_ins_addc() {
assert_asm!(0x7c002014, "addc r0, r0, r4");
}
#[test] #[test]
fn test_ins_adde() { fn test_ins_adde() {
assert_asm!(0x7c006114, "adde r0, r0, r12"); assert_asm!(0x7c006114, "adde r0, r0, r12");
} }
#[test]
fn test_ins_addi() {
assert_asm!(0x38010008, "addi r0, r1, 0x8");
assert_asm!(0x38010010, "addi r0, r1, 0x10");
assert_asm!(0x38010018, "addi r0, r1, 0x18");
assert_asm!(0x38010140, "addi r0, r1, 0x140");
assert_asm!(0x38049000, "addi r0, r4, -28672");
assert_asm!(0x38a00000, "li r5, 0");
}
#[test] #[test]
fn test_ins_addic() { fn test_ins_addic() {
assert_asm!(0x3060ffff, "addic r3, r0, -1"); assert_asm!(0x3060ffff, "addic r3, r0, -0x1");
assert_asm!(0x30840800, "addic r4, r4, 0x800"); assert_asm!(0x30840800, "addic r4, r4, 0x800");
assert_asm!(0x30a50008, "addic r5, r5, 0x8"); assert_asm!(0x30a50008, "addic r5, r5, 0x8");
assert_asm!(0x37DF001C, "addic. r30, r31, 0x1c"); assert_asm!(0x37DF001C, "addic. r30, r31, 0x1c");
assert_asm!(0x37E06278, "addic. r31, r0, 0x6278"); assert_asm!(0x37E06278, "addic. r31, r0, 0x6278");
assert_asm!(0x37E3FFFF, "addic. r31, r3, -1"); assert_asm!(0x37E3FFFF, "addic. r31, r3, -0x1");
}
#[test]
fn test_ins_addic_() {
assert_asm!(0x341D001C, "addic. r0, r29, 0x1c");
} }
#[test] #[test]
fn test_ins_addis() { fn test_ins_addis() {
assert_asm!(0x3C030000, "addis r0, r3, 0"); assert_asm!(0x3C030000, "addis r0, r3, 0x0");
assert_asm!(0x3C038000, "addis r0, r3, 0x8000"); assert_asm!(0x3C038000, "addis r0, r3, 0x8000");
assert_asm!(0x3D00EFCE, "lis r8, 0xefce"); //assert_asm!(0x3D00EFCE, "lis r8, 0xefce");
} }
#[test] #[test]
@ -100,6 +89,8 @@ fn test_ins_and() {
assert_asm!(0x7C001839, "and. r0, r0, r3"); assert_asm!(0x7C001839, "and. r0, r0, r3");
} }
/*
#[test] #[test]
fn test_ins_andc() { fn test_ins_andc() {
assert_asm!(0x7C001878, "andc r0, r0, r3"); assert_asm!(0x7C001878, "andc r0, r0, r3");

View File

@ -8,6 +8,12 @@ use serde::{Deserialize, Deserializer};
use std::collections::HashMap; use std::collections::HashMap;
use syn::LitInt; use syn::LitInt;
macro_rules! token_stream {
($stream:ident) => {
TokenStream::from_iter($stream.into_iter())
};
}
#[derive(Default)] #[derive(Default)]
pub(crate) struct BitRange(Range<u8>); pub(crate) struct BitRange(Range<u8>);
@ -159,9 +165,8 @@ impl Isa {
#ident, #ident,
}) })
}) })
.try_collect::<TokenStream, Vec<TokenStream>, syn::Error>()? .try_collect::<TokenStream, Vec<TokenStream>, syn::Error>()?;
.into_iter(); let enum_variants = token_stream!(enum_variants);
let enum_variants = TokenStream::from_iter(enum_variants);
// Create functions. // Create functions.
let mnemonic_fn = self.gen_mnemonic_fn()?; let mnemonic_fn = self.gen_mnemonic_fn()?;
@ -194,12 +199,11 @@ impl Isa {
Opcode::#variant => #literal, Opcode::#variant => #literal,
}) })
}) })
.try_collect::<TokenStream, Vec<TokenStream>, syn::Error>()? .try_collect::<TokenStream, Vec<TokenStream>, syn::Error>()?;
.into_iter(); let match_arms = token_stream!(match_arms);
let match_arms = TokenStream::from_iter(match_arms);
// Create final function. // Create final function.
let mnemonic_fn = quote! { let mnemonic_fn = quote! {
pub fn mnemonic(self) -> &'static str { fn _mnemonic(self) -> &'static str {
match self { match self {
Opcode::Illegal => "<illegal>", Opcode::Illegal => "<illegal>",
#match_arms #match_arms
@ -226,12 +230,11 @@ impl Isa {
} }
}) })
}) })
.try_collect::<TokenStream, Vec<TokenStream>, syn::Error>()? .try_collect::<TokenStream, Vec<TokenStream>, syn::Error>()?;
.into_iter(); let if_chain = token_stream!(if_chain);
let if_chain = TokenStream::from_iter(if_chain);
// Generate function. // Generate function.
let func = quote! { let func = quote! {
pub fn detect(code: u32) -> Self { fn _detect(code: u32) -> Self {
#if_chain #if_chain
Opcode::Illegal Opcode::Illegal
} }
@ -247,7 +250,7 @@ impl Isa {
enum_variants.push(field); enum_variants.push(field);
} }
} }
let enum_variants = TokenStream::from_iter(enum_variants.into_iter()); let enum_variants = token_stream!(enum_variants);
// Create final enum. // Create final enum.
let field_enum = quote! { let field_enum = quote! {
@ -269,6 +272,7 @@ impl Isa {
let mut field_match_arms = Vec::new(); let mut field_match_arms = Vec::new();
let mut def_match_arms = Vec::new(); let mut def_match_arms = Vec::new();
let mut use_match_arms = Vec::new(); let mut use_match_arms = Vec::new();
let mut modifier_match_arms = Vec::new();
for opcode in &self.opcodes { for opcode in &self.opcodes {
// Generate fields of opcode. // Generate fields of opcode.
// TODO Support mnemonics. // TODO Support mnemonics.
@ -280,13 +284,39 @@ impl Isa {
let variant = field.construct_variant_self(); let variant = field.construct_variant_self();
fields.extend(quote! { #variant, }) fields.extend(quote! { #variant, })
} }
let fields = TokenStream::from_iter(fields.into_iter()); let fields = token_stream!(fields);
// Emit match arm. // Emit match arm.
let ident = opcode.variant_identifier()?; let ident = opcode.variant_identifier()?;
field_match_arms.push(quote! { field_match_arms.push(quote! {
Opcode::#ident => vec![#fields], Opcode::#ident => vec![#fields],
}); });
// Generate modifiers.
let mut set_modifiers: Vec<TokenTree> = Vec::new();
for modifier in &opcode.modifiers {
set_modifiers.extend(match modifier.as_str() {
"OE" => quote! { m.oe = self.bit(21); },
"Rc" => quote! { m.rc = self.bit(31); },
"AA" => quote! { m.aa = self.bit(30); },
"LK" => quote! { m.lk = self.bit(31); },
_ => {
return Err(syn::Error::new(
Span::call_site(),
format!("unsupported modifier {}", modifier),
))
}
})
}
let set_modifiers = token_stream!(set_modifiers);
modifier_match_arms.push(quote! {
Opcode::#ident => {
let mut m: Modifiers = std::default::Default::default();
#set_modifiers
m
}
});
// Generate defs.
let mut defs = Vec::new(); let mut defs = Vec::new();
for arg in &opcode.defs { for arg in &opcode.defs {
let field: &Field = field_by_name.get(arg).ok_or_else(|| { let field: &Field = field_by_name.get(arg).ok_or_else(|| {
@ -295,12 +325,13 @@ impl Isa {
let variant = field.construct_variant_self(); let variant = field.construct_variant_self();
defs.extend(quote! { #variant, }) defs.extend(quote! { #variant, })
} }
let defs = TokenStream::from_iter(defs.into_iter()); let defs = token_stream!(defs);
let ident = opcode.variant_identifier()?; let ident = opcode.variant_identifier()?;
def_match_arms.push(quote! { def_match_arms.push(quote! {
Opcode::#ident => vec![#defs], Opcode::#ident => vec![#defs],
}); });
// Generate uses.
let mut uses = Vec::new(); let mut uses = Vec::new();
let mut special_uses = Vec::new(); let mut special_uses = Vec::new();
for arg in &opcode.uses { for arg in &opcode.uses {
@ -329,9 +360,9 @@ impl Isa {
}) })
} }
} }
let uses = TokenStream::from_iter(uses.into_iter()); let uses = token_stream!(uses);
let ident = opcode.variant_identifier()?; let ident = opcode.variant_identifier()?;
let special_uses = TokenStream::from_iter(special_uses.into_iter()); let special_uses = token_stream!(special_uses);
use_match_arms.push(quote! { use_match_arms.push(quote! {
Opcode::#ident => { Opcode::#ident => {
let mut uses = vec![#uses]; let mut uses = vec![#uses];
@ -340,9 +371,10 @@ impl Isa {
}, },
}); });
} }
let field_match_arms = TokenStream::from_iter(field_match_arms.into_iter()); let field_match_arms = token_stream!(field_match_arms);
let def_match_arms = TokenStream::from_iter(def_match_arms.into_iter()); let def_match_arms = token_stream!(def_match_arms);
let use_match_arms = TokenStream::from_iter(use_match_arms.into_iter()); let use_match_arms = token_stream!(use_match_arms);
let modifier_match_arms = token_stream!(modifier_match_arms);
// Generate final fields function. // Generate final fields function.
let ins_impl = quote! { let ins_impl = quote! {
impl Ins { impl Ins {
@ -369,6 +401,14 @@ impl Isa {
_ => todo!() _ => todo!()
} }
} }
fn _modifiers(&self) -> Modifiers {
match self.op {
Opcode::Illegal => std::default::Default::default(),
#modifier_match_arms
_ => todo!()
}
}
} }
}; };
Ok(ins_impl) Ok(ins_impl)

View File

@ -204,7 +204,6 @@ opcodes:
bitmask: 0xfc000000 bitmask: 0xfc000000
pattern: 0x3c000000 pattern: 0x3c000000
args: [ "rD", "rA", "uimm" ] args: [ "rD", "rA", "uimm" ]
side_effects: [ "Rc" ]
defs: [ "rD" ] defs: [ "rD" ]
uses: [ "rA.nz" ] uses: [ "rA.nz" ]