more flexible modifier handling

This commit is contained in:
Richard Patel 2022-04-07 05:33:38 +02:00
parent 99c7f252f8
commit 4c5735e403
9 changed files with 937 additions and 870 deletions

136
Cargo.lock generated
View File

@ -2,6 +2,17 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.1.0"
@ -17,12 +28,33 @@ dependencies = [
"serde",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "3.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71c47df61d9e16dc010b55dba1952a57d8c215dbb533fd13cdd13369aac73b1c"
dependencies = [
"atty",
"bitflags",
"indexmap",
"os_str_bytes",
"strsim",
"termcolor",
"textwrap",
]
[[package]]
name = "dol"
version = "0.1.0"
@ -38,6 +70,12 @@ version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "fixedbitset"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "279fb028e20b3c4c320317955b77c5e0c9701f05a1d309905d6fc702cdc5053e"
[[package]]
name = "getrandom"
version = "0.2.6"
@ -95,6 +133,12 @@ version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]]
name = "memchr"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "num-traits"
version = "0.2.14"
@ -114,6 +158,34 @@ dependencies = [
"libc",
]
[[package]]
name = "os_str_bytes"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
dependencies = [
"memchr",
]
[[package]]
name = "parse_int"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d695b79916a2c08bcff7be7647ab60d1402885265005a6658ffe6d763553c5a"
dependencies = [
"num-traits",
]
[[package]]
name = "petgraph"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f"
dependencies = [
"fixedbitset",
"indexmap",
]
[[package]]
name = "ppc750cl"
version = "0.1.1"
@ -122,6 +194,18 @@ dependencies = [
"serde",
]
[[package]]
name = "ppc750cl-flow-graph"
version = "0.1.1"
dependencies = [
"clap",
"dol",
"itertools",
"parse_int",
"petgraph",
"ppc750cl",
]
[[package]]
name = "ppc750cl-fuzz"
version = "0.1.1"
@ -253,6 +337,12 @@ dependencies = [
"rand_core",
]
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.91"
@ -264,6 +354,21 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "termcolor"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
dependencies = [
"winapi-util",
]
[[package]]
name = "textwrap"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
[[package]]
name = "thiserror"
version = "1.0.30"
@ -296,6 +401,37 @@ version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "yaml-rust"
version = "0.4.5"

View File

@ -4,5 +4,6 @@ members = [
"dol",
"fuzz",
"genisa",
"flow-graph",
"rand",
]

View File

@ -7,7 +7,7 @@ pub struct FormattedIns(pub Ins);
impl Display for FormattedIns {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let simple = self.0.clone().simplified();
write!(f, "{}{}", simple.mnemonic, simple.modifiers)?;
write!(f, "{}{}", simple.mnemonic, simple.ins.suffix())?;
let mut writing_offset = false;
for (i, arg) in simple.args.iter().enumerate() {
if i == 0 {

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,6 @@ pub mod prelude {
pub use crate::Argument;
pub use crate::Field::*;
pub use crate::Ins;
pub use crate::Modifiers;
pub use crate::Opcode::*;
pub use crate::SimplifiedIns;
pub use crate::{
@ -210,32 +209,6 @@ impl Field {
}
}
#[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(())
}
}
impl Opcode {
/// Detects the opcode of a machine code instruction.
pub fn detect(code: u32) -> Self {
@ -289,9 +262,9 @@ impl Ins {
self._fields() // auto-generated
}
/// Gets the modifiers of an instruction.
pub fn modifiers(&self) -> Modifiers {
self._modifiers() // auto-generated
/// Gets the suffix of an instruction mnemonic.
pub fn suffix(&self) -> String {
self._suffix() // auto-generated
}
/// Gets the defs of an instruction.
@ -314,10 +287,9 @@ impl Ins {
bits(self.code, range)
}
/*
pub fn branch_offset(&self) -> Option<i32> {
match self.op {
Opcode::B => Some(self.li()),
Opcode::B => Some(self.field_LI() as i32),
Opcode::Bc | Opcode::Bcctr | Opcode::Bclr => Some(self.field_BD() as i32),
_ => None,
}
@ -332,20 +304,18 @@ impl Ins {
}
})
}
*/
}
/// A simplified PowerPC 750CL instruction.
pub struct SimplifiedIns {
pub ins: Ins,
pub mnemonic: &'static str,
pub modifiers: Modifiers,
pub args: Vec<Argument>,
}
impl Display for SimplifiedIns {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}{} ", self.mnemonic, self.modifiers)?;
write!(f, "{}{} ", self.mnemonic, self.ins.suffix())?;
let mut writing_offset = false;
for (i, argument) in self.args.iter().enumerate() {
write!(f, "{}", argument)?;
@ -370,7 +340,6 @@ impl SimplifiedIns {
pub(crate) fn basic_form(ins: Ins) -> Self {
Self {
mnemonic: ins.op.mnemonic(),
modifiers: ins.modifiers(),
args: ins
.fields()
.iter()

View File

@ -8,6 +8,7 @@ use petgraph::algo::dominators::Dominators;
use petgraph::graph::{DefaultIx, NodeIndex};
use petgraph::Graph;
use ppc750cl::formatter::FormattedIns;
use ppc750cl::{Ins, Opcode};
use crate::slices::{BasicSlices, CodeIdx};
@ -53,21 +54,21 @@ impl<'a> BasicBlock<'a> {
for ins in code {
match ins.op {
Opcode::Addis => {
if ins.a() == 0 {
if ins.field_rA() == 0 {
// lis
defs.insert(ins.d(), ins.field_uimm() as u16);
defs.insert(ins.field_rD() as u8, ins.field_uimm() as u16);
} else {
defs.remove(&ins.d());
defs.remove(&(ins.field_rD() as u8));
}
}
Opcode::Addi => {
if let Some(hi) = defs.get(&ins.a()) {
if let Some(hi) = defs.get(&(ins.field_rA() as u8)) {
data_refs.insert(
ins.addr / 4,
((*hi as u32) << 16) + (ins.field_uimm() as u32),
);
}
defs.remove(&ins.d());
defs.remove(&(ins.field_rD() as u8));
}
_ => (),
}
@ -91,7 +92,7 @@ impl<'a> Debug for BasicBlock<'a> {
self.range.end * 4
)?;
for ins in self.code {
writeln!(f, "{}", ins.to_string())?;
writeln!(f, "{}", FormattedIns(ins.clone()))?;
if let Some(addr) = self.data_refs.get(&(ins.addr / 4)) {
writeln!(f, " ref: {:0>#8x}", addr)?;
}
@ -154,8 +155,8 @@ impl<'a> FlowGraph<'a> {
// Get last instruction of left block.
// Unless it's an unconditional branch, we can connect the blocks.
let last_ins = &src_block.code.last().unwrap();
if last_ins.code == Opcode::BLR
|| (last_ins.op == Opcode::B && last_ins.bo() == 0b10100)
if last_ins.code == 0x4E800020
|| (last_ins.op == Opcode::B && last_ins.field_BO() == 0b10100)
{
continue;
}

View File

@ -24,7 +24,7 @@ impl BasicSlices {
let is_control_flow_ins = match ins.op {
// Direct branches are control flow instructions if they don't save the link register.
// If they do, we encountered a function call.
Opcode::B | Opcode::Bc => ins.lk() == 0,
Opcode::B | Opcode::Bc => !ins.field_LK(),
// Switch table
Opcode::Bcctr => panic!("jump tables not supported yet"),
_ => false,

View File

@ -9,7 +9,7 @@ use itertools::Itertools;
use proc_macro2::{Ident, Literal, Span, TokenStream, TokenTree};
use quote::quote;
use serde::{Deserialize, Deserializer};
use syn::{LitInt, LitStr};
use syn::{LitChar, LitInt, LitStr};
macro_rules! token_stream {
($stream:ident) => {
@ -194,6 +194,7 @@ impl Field {
quote!(usize)
};
quote! {
#[inline(always)]
pub fn #field_variant(&self) -> #ret_type {
#value as _
}
@ -236,12 +237,32 @@ pub(crate) struct Mnemonic {
pub(crate) struct Modifier {
name: String,
suffix: char,
bit: u8,
}
impl Modifier {
fn express_value_self(&self) -> TokenStream {
let modifier_bit = self.bit as usize;
quote!(self.bit(#modifier_bit))
}
fn construct_accessor(&self) -> TokenStream {
let field_variant = to_rust_ident("field_", &self.name);
let value = self.express_value_self();
quote! {
#[inline(always)]
pub fn #field_variant(&self) -> bool {
#value
}
}
}
}
#[derive(Deserialize, Default)]
#[serde(default)]
pub(crate) struct Isa {
fields: Vec<Field>,
modifiers: Vec<Modifier>,
opcodes: Vec<Opcode>,
mnemonics: Vec<Mnemonic>,
}
@ -294,8 +315,7 @@ impl Isa {
.iter()
.map(|opcode| {
let variant = opcode.variant_identifier()?;
let literal =
Literal::string(opcode.name.strip_suffix('.').unwrap_or(&opcode.name));
let literal = Literal::string(&opcode.name);
Ok(quote! {
Opcode::#variant => #literal,
})
@ -370,6 +390,10 @@ impl Isa {
for field in &self.fields {
field_by_name.insert(field.name.clone(), field);
}
let mut modifier_by_name = HashMap::<String, &Modifier>::new();
for modifier in &self.modifiers {
modifier_by_name.insert(modifier.name.clone(), modifier);
}
// Map mnemonics by opcode.
let mut mnemonics_by_opcode = HashMap::<&String, Vec<&Mnemonic>>::new();
for simple in &self.mnemonics {
@ -382,7 +406,7 @@ impl Isa {
let mut field_match_arms = Vec::new();
let mut def_match_arms = Vec::new();
let mut use_match_arms = Vec::new();
let mut modifier_match_arms = Vec::new();
let mut suffix_match_arms = Vec::new();
let mut simplified_ins_match_arms = Vec::new();
for opcode in &self.opcodes {
// Generate fields of opcode.
@ -402,13 +426,9 @@ impl Isa {
});
// Generate modifiers.
let modifiers = ModifiersExpr {
modifiers: opcode.modifiers.clone(),
side_effects: opcode.side_effects.clone(),
}
.build()?;
modifier_match_arms.push(quote! {
Opcode::#ident => #modifiers,
let suffix = express_suffix(&modifier_by_name, opcode)?;
suffix_match_arms.push(quote! {
Opcode::#ident => #suffix,
});
// Generate defs.
@ -480,12 +500,6 @@ impl Isa {
)?);
// Emit branch.
let mnemonic_lit = LitStr::new(&mnemonic.name, Span::call_site());
// Extract modifier bits.
let modifiers = ModifiersExpr {
modifiers: mnemonic.modifiers.clone(),
side_effects: vec![],
}
.build()?;
// Extract arguments.
let mut args = Vec::new();
for arg in &mnemonic.args {
@ -501,7 +515,6 @@ impl Isa {
{
return SimplifiedIns {
mnemonic: #mnemonic_lit,
modifiers: #modifiers,
args: vec![#args],
ins: self,
};
@ -519,7 +532,7 @@ impl Isa {
let field_match_arms = token_stream!(field_match_arms);
let def_match_arms = token_stream!(def_match_arms);
let use_match_arms = token_stream!(use_match_arms);
let modifier_match_arms = token_stream!(modifier_match_arms);
let suffix_match_arms = token_stream!(suffix_match_arms);
let simplified_ins_match_arms = token_stream!(simplified_ins_match_arms);
let field_accessors = self
.fields
@ -527,6 +540,12 @@ impl Isa {
.map(|field| field.construct_accessor())
.collect::<Vec<_>>();
let field_accessors = token_stream!(field_accessors);
let modifier_accessors = self
.modifiers
.iter()
.map(|modifier| modifier.construct_accessor())
.collect::<Vec<_>>();
let modifier_accessors = token_stream!(modifier_accessors);
// Generate final fields function.
let ins_impl = quote! {
#[allow(clippy::all, unused_mut)]
@ -552,10 +571,10 @@ impl Isa {
}
}
pub(crate) fn _modifiers(&self) -> Modifiers {
pub(crate) fn _suffix(&self) -> String {
match self.op {
Opcode::Illegal => Modifiers::default(),
#modifier_match_arms
Opcode::Illegal => String::new(),
#suffix_match_arms
}
}
@ -570,6 +589,7 @@ impl Isa {
#[allow(clippy::all, non_snake_case)]
impl Ins {
#field_accessors
#modifier_accessors
}
};
Ok(ins_impl)
@ -624,56 +644,6 @@ fn to_rust_variant_str(key: &str) -> Result<String> {
}
}
#[derive(Default)]
pub(crate) struct ModifiersExpr {
pub(crate) modifiers: Vec<String>,
pub(crate) side_effects: Vec<String>,
}
impl ModifiersExpr {
fn build(&self) -> Result<TokenStream> {
if self.modifiers.is_empty() && self.side_effects.is_empty() {
return Ok(Self::build_empty());
}
let mut statements: Vec<TokenTree> = Vec::new();
for modifier in &self.modifiers {
statements.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(format!("unsupported modifier {}", modifier).into());
}
})
}
for modifier in &self.side_effects {
statements.extend(match modifier.as_str() {
// TODO dedup modifiers
"OE" => quote! { m.oe = true; },
"Rc" => quote! { m.rc = true; },
"AA" => quote! { m.aa = true; },
"LK" => quote! { m.lk = true; },
_ => {
return Err(format!("unsupported modifier {}", modifier).into());
}
})
}
let statements = token_stream!(statements);
Ok(quote! {
{
let mut m = Modifiers::default();
#statements
m
}
})
}
fn build_empty() -> TokenStream {
quote!(Modifiers::default())
}
}
/// Compiles conditions such as `S == B` into valid Rust expressions on a PowerPC instruction.
fn compile_mnemonic_condition(
field_by_name: &HashMap<String, &Field>,
@ -690,3 +660,34 @@ fn compile_mnemonic_condition(
});
Ok(TokenStream::from_iter(token_iter))
}
fn express_suffix(
modifier_by_name: &HashMap<String, &Modifier>,
opcode: &Opcode,
) -> Result<TokenStream> {
Ok(if opcode.modifiers.is_empty() {
quote!(String::new())
} else {
let mut chars = Vec::new();
for mod_name in &opcode.modifiers {
let modifier: &Modifier = modifier_by_name
.get(mod_name)
.ok_or_else(|| Error::from(format!("undefined modifier {}", mod_name)))?;
let lit_char = LitChar::new(modifier.suffix, Span::call_site());
let modifier_bit = modifier.express_value_self();
chars.push(quote! {
if #modifier_bit {
s.push(#lit_char);
}
});
}
let chars = token_stream!(chars);
quote!({
{
let mut s = String::with_capacity(4);
#chars
s
}
})
})
}

View File

@ -141,12 +141,16 @@ fields:
modifiers:
- name: OE
suffix: o
bit: 21
- name: Rc
suffix: .
bit: 31
- name: LK
suffix: l
bit: 31
- name: AA
suffix: a
bit: 30
opcodes:
- name: add