API updates and cleanup
This commit is contained in:
parent
c4af15ddc2
commit
d31bf75009
|
@ -4,7 +4,6 @@ resolver = "2"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
opt-level = "z"
|
|
||||||
|
|
||||||
[profile.release-lto]
|
[profile.release-lto]
|
||||||
inherits = "release"
|
inherits = "release"
|
||||||
|
|
2063
asm/src/generated.rs
2063
asm/src/generated.rs
File diff suppressed because it is too large
Load Diff
|
@ -4,11 +4,11 @@ use core::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::generated::{
|
use crate::generated::{
|
||||||
Arguments, Opcode, BASE_MNEMONICS, DEFS_FUNCTIONS, SIMPLIFIED_MNEMONICS, USES_FUNCTIONS,
|
parse_basic, parse_defs, parse_simplified, parse_uses, Arguments, Opcode, EMPTY_ARGS,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A PowerPC 750CL instruction.
|
/// A PowerPC 750CL instruction.
|
||||||
#[derive(Default, Clone, Debug, Eq, PartialEq)]
|
#[derive(Default, Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct Ins {
|
pub struct Ins {
|
||||||
pub code: u32,
|
pub code: u32,
|
||||||
pub op: Opcode,
|
pub op: Opcode,
|
||||||
|
@ -21,23 +21,59 @@ impl Ins {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse the instruction into a simplified mnemonic, if any match.
|
/// Parse the instruction into a simplified mnemonic, if any match.
|
||||||
pub fn simplified(self) -> SimplifiedIns {
|
#[inline]
|
||||||
SimplifiedIns::new(self)
|
pub fn parse_simplified(self, out: &mut ParsedIns) {
|
||||||
|
parse_simplified(out, self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the simplified form of the instruction, if any match.
|
||||||
|
#[inline]
|
||||||
|
pub fn simplified(self) -> ParsedIns {
|
||||||
|
let mut out = ParsedIns::new();
|
||||||
|
parse_simplified(&mut out, self);
|
||||||
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse the instruction into its basic form.
|
/// Parse the instruction into its basic form.
|
||||||
pub fn basic_form(self) -> SimplifiedIns {
|
#[inline]
|
||||||
SimplifiedIns::basic_form(self)
|
pub fn parse_basic(self, out: &mut ParsedIns) {
|
||||||
|
parse_basic(out, self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the basic form of the instruction.
|
||||||
|
#[inline]
|
||||||
|
pub fn basic(self) -> ParsedIns {
|
||||||
|
let mut out = ParsedIns::new();
|
||||||
|
parse_basic(&mut out, self);
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Emits all registers defined by the instruction into the given argument list.
|
||||||
|
#[inline]
|
||||||
|
pub fn parse_defs(self, out: &mut Arguments) {
|
||||||
|
parse_defs(out, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns all registers defined by the instruction.
|
/// Returns all registers defined by the instruction.
|
||||||
pub fn defs(&self) -> Arguments {
|
#[inline]
|
||||||
DEFS_FUNCTIONS[self.op as usize](self)
|
pub fn defs(self) -> Arguments {
|
||||||
|
let mut out = Arguments::default();
|
||||||
|
parse_defs(&mut out, self);
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Emits all registers used by the instruction into the given argument list.
|
||||||
|
#[inline]
|
||||||
|
pub fn parse_uses(self, out: &mut Arguments) {
|
||||||
|
parse_uses(out, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns all registers used by the instruction.
|
/// Returns all registers used by the instruction.
|
||||||
pub fn uses(&self) -> Arguments {
|
#[inline]
|
||||||
USES_FUNCTIONS[self.op as usize](self)
|
pub fn uses(self) -> Arguments {
|
||||||
|
let mut out = Arguments::default();
|
||||||
|
parse_uses(&mut out, self);
|
||||||
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the relative branch offset of the instruction, if any.
|
/// Returns the relative branch offset of the instruction, if any.
|
||||||
|
@ -258,7 +294,7 @@ impl From<u8> for OpaqueU {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Eq, PartialEq)]
|
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
|
||||||
pub enum Argument {
|
pub enum Argument {
|
||||||
#[default]
|
#[default]
|
||||||
None,
|
None,
|
||||||
|
@ -296,33 +332,38 @@ impl Display for Argument {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A simplified PowerPC 750CL instruction.
|
/// A parsed PowerPC 750CL instruction.
|
||||||
pub struct SimplifiedIns {
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub ins: Ins,
|
pub struct ParsedIns {
|
||||||
pub mnemonic: &'static str,
|
pub mnemonic: &'static str,
|
||||||
pub args: Arguments,
|
pub args: Arguments,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SimplifiedIns {
|
impl Default for ParsedIns {
|
||||||
pub fn new(ins: Ins) -> Self {
|
fn default() -> Self {
|
||||||
let (mnemonic, args) = SIMPLIFIED_MNEMONICS[ins.op as usize](&ins);
|
Self::new()
|
||||||
Self { ins, mnemonic, args }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn basic_form(ins: Ins) -> Self {
|
|
||||||
let (mnemonic, args) = BASE_MNEMONICS[ins.op as usize](&ins);
|
|
||||||
Self { ins, mnemonic, args }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for SimplifiedIns {
|
impl ParsedIns {
|
||||||
|
/// An empty parsed instruction.
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self { mnemonic: "<illegal>", args: EMPTY_ARGS }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator over the arguments of the instruction,
|
||||||
|
/// stopping at the first [Argument::None].
|
||||||
|
#[inline]
|
||||||
|
pub fn args_iter(&self) -> impl Iterator<Item = &Argument> {
|
||||||
|
self.args.iter().take_while(|x| !matches!(x, Argument::None))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ParsedIns {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{}", self.mnemonic)?;
|
write!(f, "{}", self.mnemonic)?;
|
||||||
let mut writing_offset = false;
|
let mut writing_offset = false;
|
||||||
for (i, argument) in self.args.iter().enumerate() {
|
for (i, argument) in self.args_iter().enumerate() {
|
||||||
if matches!(argument, Argument::None) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
write!(f, " ")?;
|
write!(f, " ")?;
|
||||||
} else if !writing_offset {
|
} else if !writing_offset {
|
||||||
|
@ -332,9 +373,7 @@ impl Display for SimplifiedIns {
|
||||||
if let Argument::Offset(_) = argument {
|
if let Argument::Offset(_) = argument {
|
||||||
write!(f, "(")?;
|
write!(f, "(")?;
|
||||||
writing_offset = true;
|
writing_offset = true;
|
||||||
continue;
|
} else if writing_offset {
|
||||||
}
|
|
||||||
if writing_offset {
|
|
||||||
write!(f, ")")?;
|
write!(f, ")")?;
|
||||||
writing_offset = false;
|
writing_offset = false;
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,7 +3,7 @@ mod disasm;
|
||||||
mod generated;
|
mod generated;
|
||||||
|
|
||||||
pub use disasm::{
|
pub use disasm::{
|
||||||
Argument, BranchDest, CRBit, CRField, Ins, Offset, OpaqueU, Simm, SimplifiedIns, Uimm, FPR,
|
Argument, BranchDest, CRBit, CRField, Ins, Offset, OpaqueU, ParsedIns, Simm, Uimm, FPR, GPR,
|
||||||
GPR, GQR, SPR, SR,
|
GQR, SPR, SR,
|
||||||
};
|
};
|
||||||
pub use generated::{Arguments, Opcode};
|
pub use generated::{Arguments, Opcode};
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
use ppc750cl::{Argument, Ins, Opcode, SimplifiedIns, FPR, GPR};
|
use ppc750cl::{Argument, Ins, Opcode, FPR, GPR};
|
||||||
|
|
||||||
macro_rules! assert_asm {
|
macro_rules! assert_asm {
|
||||||
($ins:ident, $disasm:literal) => {{
|
($ins:ident, $disasm:literal) => {{
|
||||||
assert_eq!(format!("{}", SimplifiedIns::new($ins)), $disasm)
|
assert_eq!(format!("{}", $ins.simplified()), $disasm)
|
||||||
}};
|
}};
|
||||||
($code:literal, $disasm:literal) => {{
|
($code:literal, $disasm:literal) => {{
|
||||||
let ins = Ins::new($code);
|
let ins = Ins::new($code);
|
||||||
assert_eq!(format!("{}", SimplifiedIns::new(ins)), $disasm)
|
assert_eq!(format!("{}", ins.simplified()), $disasm)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! assert_basic {
|
macro_rules! assert_basic {
|
||||||
($ins:ident, $disasm:literal) => {{
|
($ins:ident, $disasm:literal) => {{
|
||||||
assert_eq!(format!("{}", SimplifiedIns::basic_form($ins)), $disasm)
|
assert_eq!(format!("{}", $ins.basic_form()), $disasm)
|
||||||
}};
|
}};
|
||||||
($code:literal, $disasm:literal) => {{
|
($code:literal, $disasm:literal) => {{
|
||||||
let ins = Ins::new($code);
|
let ins = Ins::new($code);
|
||||||
assert_eq!(format!("{}", SimplifiedIns::basic_form(ins)), $disasm)
|
assert_eq!(format!("{}", ins.basic()), $disasm)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,6 @@ use std::sync::atomic::{AtomicU32, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use ppc750cl::Ins;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let matches = clap::Command::new("ppc750cl-fuzz")
|
let matches = clap::Command::new("ppc750cl-fuzz")
|
||||||
.version("0.2.0")
|
.version("0.2.0")
|
||||||
|
@ -56,9 +54,11 @@ impl MultiFuzzer {
|
||||||
fn dispatch_progress_monitor(&self) {
|
fn dispatch_progress_monitor(&self) {
|
||||||
let this = self.clone();
|
let this = self.clone();
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
|
let start = Instant::now();
|
||||||
let mut last = 0u32;
|
let mut last = 0u32;
|
||||||
loop {
|
loop {
|
||||||
std::thread::sleep(Duration::from_secs(1));
|
std::thread::sleep(Duration::from_secs(1));
|
||||||
|
let elapsed = start.elapsed();
|
||||||
let mut now = 0u32;
|
let mut now = 0u32;
|
||||||
for thread in &this.threads {
|
for thread in &this.threads {
|
||||||
now += thread.counter.load(Ordering::Relaxed) - thread.range.start;
|
now += thread.counter.load(Ordering::Relaxed) - thread.range.start;
|
||||||
|
@ -66,7 +66,8 @@ impl MultiFuzzer {
|
||||||
let per_second = now - last;
|
let per_second = now - last;
|
||||||
last = now;
|
last = now;
|
||||||
let progress = 100f32 * ((now as f32) / (0x1_0000_0000u64 as f32));
|
let progress = 100f32 * ((now as f32) / (0x1_0000_0000u64 as f32));
|
||||||
println!("{}/s\t{:05.2}%\tn=0x{:08x}", per_second, progress, now);
|
let avg = now as f32 / elapsed.as_secs_f32() / this.threads.len() as f32;
|
||||||
|
println!("{}/s\t{:05.2}%\tn=0x{:08x} (avg {}/s)", per_second, progress, now, avg);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -99,9 +100,10 @@ impl Fuzzer {
|
||||||
let counter = Arc::clone(&self.counter);
|
let counter = Arc::clone(&self.counter);
|
||||||
let range = self.range.clone();
|
let range = self.range.clone();
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
|
let mut parsed = ppc750cl::ParsedIns::default();
|
||||||
for x in range.clone() {
|
for x in range.clone() {
|
||||||
let ins = Ins::new(x);
|
ppc750cl::Ins::new(x).parse_simplified(&mut parsed);
|
||||||
writeln!(&mut devnull, "{}", ins.simplified()).unwrap();
|
writeln!(&mut devnull, "{}", parsed).unwrap();
|
||||||
if x % (1 << 19) == 0 {
|
if x % (1 << 19) == 0 {
|
||||||
counter.store(x, Ordering::Relaxed);
|
counter.store(x, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::isa::{
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
use proc_macro2::{Literal, TokenStream};
|
use proc_macro2::{Literal, TokenStream};
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
use std::collections::HashMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
pub fn gen_asm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
pub fn gen_asm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||||
let mut functions = TokenStream::new();
|
let mut functions = TokenStream::new();
|
||||||
|
@ -20,7 +20,7 @@ pub fn gen_asm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut mnemonic_map = HashMap::<String, Vec<Mnemonic>>::new();
|
let mut mnemonic_map = BTreeMap::<String, Vec<Mnemonic>>::new();
|
||||||
for mnemonic in &isa.mnemonics {
|
for mnemonic in &isa.mnemonics {
|
||||||
mnemonic_map.entry(mnemonic.name.clone()).or_default().push(mnemonic.clone());
|
mnemonic_map.entry(mnemonic.name.clone()).or_default().push(mnemonic.clone());
|
||||||
}
|
}
|
||||||
|
@ -88,11 +88,12 @@ pub fn gen_asm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
|
#[comment = " Code generated by ppc750-genisa. DO NOT EDIT."]
|
||||||
use crate::types::*;
|
use crate::types::*;
|
||||||
pub type Arguments = [Argument; #max_args];
|
pub type Arguments = [Argument; #max_args];
|
||||||
#functions
|
#functions
|
||||||
type MnemonicFn = fn(&Arguments, u32) -> Result<u32, ArgumentError>;
|
type MnemonicFn = fn(&Arguments, u32) -> Result<u32, ArgumentError>;
|
||||||
const MNEMONIC_MAP: phf::Map<&'static str, (MnemonicFn, u32)> = #func_map;
|
static MNEMONIC_MAP: phf::Map<&'static str, (MnemonicFn, u32)> = #func_map;
|
||||||
pub fn assemble(mnemonic: &str, args: &Arguments) -> Result<u32, ArgumentError> {
|
pub fn assemble(mnemonic: &str, args: &Arguments) -> Result<u32, ArgumentError> {
|
||||||
if let Some(&(fn_ptr, modifiers)) = MNEMONIC_MAP.get(mnemonic) {
|
if let Some(&(fn_ptr, modifiers)) = MNEMONIC_MAP.get(mnemonic) {
|
||||||
fn_ptr(args, modifiers)
|
fn_ptr(args, modifiers)
|
||||||
|
|
|
@ -5,6 +5,7 @@ use anyhow::{bail, ensure, Result};
|
||||||
use proc_macro2::{Literal, TokenStream};
|
use proc_macro2::{Literal, TokenStream};
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::num::NonZeroU8;
|
||||||
|
|
||||||
pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||||
// The entry table allows us to quickly find the range of possible opcodes
|
// The entry table allows us to quickly find the range of possible opcodes
|
||||||
|
@ -79,13 +80,11 @@ pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut sign_bit = bits.len() - 1;
|
|
||||||
let mut shift_right = bits.shift();
|
let mut shift_right = bits.shift();
|
||||||
let mut shift_left = field.shift_left;
|
let mut shift_left = field.shift_left;
|
||||||
if shift_right == shift_left {
|
if shift_right == shift_left {
|
||||||
// Optimization: these cancel each other out
|
// Optimization: these cancel each other out
|
||||||
// Adjust subsequent operations to operate on the full value
|
// Adjust subsequent operations to operate on the full value
|
||||||
sign_bit += shift_left;
|
|
||||||
shift_right = 0;
|
shift_right = 0;
|
||||||
shift_left = 0;
|
shift_left = 0;
|
||||||
}
|
}
|
||||||
|
@ -101,20 +100,20 @@ pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||||
|
|
||||||
// Determine the smallest integer type that can hold the value
|
// Determine the smallest integer type that can hold the value
|
||||||
let num_bits = bits.len() + field.shift_left;
|
let num_bits = bits.len() + field.shift_left;
|
||||||
let (out_type, cast) = match (num_bits, field.signed) {
|
let (out_type, cast, sign_shift) = match (num_bits, field.signed) {
|
||||||
(1..=8, false) => (ident!(u8), true),
|
(1..=8, false) => (ident!(u8), true, None),
|
||||||
(9..=16, false) => (ident!(u16), true),
|
(9..=16, false) => (ident!(u16), true, None),
|
||||||
(17..=32, false) => (ident!(u32), false),
|
(17..=32, false) => (ident!(u32), false, None),
|
||||||
(1..=8, true) => (ident!(i8), true),
|
(1..=8, true) => (ident!(i8), true, NonZeroU8::new(8 - num_bits)),
|
||||||
(9..=16, true) => (ident!(i16), true),
|
(9..=16, true) => (ident!(i16), true, NonZeroU8::new(16 - num_bits)),
|
||||||
(17..=32, true) => (ident!(i32), true),
|
(17..=32, true) => (ident!(i32), true, NonZeroU8::new(32 - num_bits)),
|
||||||
(v, _) => bail!("Unsupported field size {v}"),
|
(v, _) => bail!("Unsupported field size {v}"),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle sign extension
|
// Handle sign extension
|
||||||
if field.signed {
|
if let Some(sign_shift) = sign_shift {
|
||||||
let sign_value = HexLiteral(1 << sign_bit);
|
let sign_shift = Literal::u8_unsuffixed(sign_shift.get());
|
||||||
inner = quote! { ((#inner) ^ #sign_value).wrapping_sub(#sign_value) as #out_type };
|
inner = quote! { (((#inner) << #sign_shift) as #out_type) >> #sign_shift };
|
||||||
} else if cast {
|
} else if cast {
|
||||||
inner = quote! { (#inner) as #out_type };
|
inner = quote! { (#inner) as #out_type };
|
||||||
}
|
}
|
||||||
|
@ -162,7 +161,7 @@ pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||||
|
|
||||||
// Generate simplified mnemonics
|
// Generate simplified mnemonics
|
||||||
let mut mnemonic_functions = TokenStream::new();
|
let mut mnemonic_functions = TokenStream::new();
|
||||||
let mut base_functions_ref = TokenStream::new();
|
let mut basic_functions_ref = TokenStream::new();
|
||||||
let mut simplified_functions_ref = TokenStream::new();
|
let mut simplified_functions_ref = TokenStream::new();
|
||||||
for opcode in &sorted_ops {
|
for opcode in &sorted_ops {
|
||||||
let mnemonics =
|
let mnemonics =
|
||||||
|
@ -186,47 +185,43 @@ pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||||
)?;
|
)?;
|
||||||
mnemonic_conditions.extend(quote! {
|
mnemonic_conditions.extend(quote! {
|
||||||
if #(#conditions)&&* {
|
if #(#conditions)&&* {
|
||||||
return #inner;
|
*out = #inner;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to the base opcode name if no mnemonic matches
|
// Fallback to the basic opcode name if no mnemonic matches
|
||||||
let inner =
|
let inner =
|
||||||
gen_mnemonic(&opcode.name, &opcode.args, &opcode.modifiers, isa, max_args, None)?;
|
gen_mnemonic(&opcode.name, &opcode.args, &opcode.modifiers, isa, max_args, None)?;
|
||||||
let base_name = format_ident!("base_{}", opcode.ident());
|
let basic_name = format_ident!("basic_{}", opcode.ident());
|
||||||
if mnemonics.is_empty() {
|
if mnemonics.is_empty() {
|
||||||
mnemonic_functions.extend(quote! {
|
mnemonic_functions.extend(quote! {
|
||||||
const fn #base_name(ins: &Ins) -> (&'static str, Arguments) {
|
fn #basic_name(out: &mut ParsedIns, ins: Ins) {
|
||||||
#inner
|
*out = #inner;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
base_functions_ref.extend(quote! { #base_name, });
|
basic_functions_ref.extend(quote! { #basic_name, });
|
||||||
simplified_functions_ref.extend(quote! { #base_name, });
|
simplified_functions_ref.extend(quote! { #basic_name, });
|
||||||
} else {
|
} else {
|
||||||
let simplified_name = format_ident!("simplified_{}", opcode.ident());
|
let simplified_name = format_ident!("simplified_{}", opcode.ident());
|
||||||
mnemonic_functions.extend(quote! {
|
mnemonic_functions.extend(quote! {
|
||||||
#[inline(always)]
|
fn #basic_name(out: &mut ParsedIns, ins: Ins) {
|
||||||
const fn #base_name(ins: &Ins) -> (&'static str, Arguments) {
|
*out = #inner;
|
||||||
#inner
|
|
||||||
}
|
}
|
||||||
const fn #simplified_name(ins: &Ins) -> (&'static str, Arguments) {
|
fn #simplified_name(out: &mut ParsedIns, ins: Ins) {
|
||||||
#mnemonic_conditions
|
#mnemonic_conditions
|
||||||
#base_name(ins)
|
#basic_name(out, ins)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
base_functions_ref.extend(quote! { #base_name, });
|
basic_functions_ref.extend(quote! { #basic_name, });
|
||||||
simplified_functions_ref.extend(quote! { #simplified_name, });
|
simplified_functions_ref.extend(quote! { #simplified_name, });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut none_args = TokenStream::new();
|
|
||||||
for _ in 0..max_args {
|
|
||||||
none_args.extend(quote! { Argument::None, });
|
|
||||||
}
|
|
||||||
mnemonic_functions.extend(quote! {
|
mnemonic_functions.extend(quote! {
|
||||||
const fn mnemonic_illegal(_ins: &Ins) -> (&'static str, Arguments) {
|
fn mnemonic_illegal(out: &mut ParsedIns, _ins: Ins) {
|
||||||
("<illegal>", [#none_args])
|
*out = ParsedIns::new();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO rework defs/uses to account for modifiers and special registers (CTR, LR, etc)
|
// TODO rework defs/uses to account for modifiers and special registers (CTR, LR, etc)
|
||||||
|
@ -245,9 +240,6 @@ pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||||
defs.extend(quote! { #arg, });
|
defs.extend(quote! { #arg, });
|
||||||
defs_count += 1;
|
defs_count += 1;
|
||||||
}
|
}
|
||||||
for _ in defs_count..max_args {
|
|
||||||
defs.extend(quote! { Argument::None, });
|
|
||||||
}
|
|
||||||
let mut use_count = 0;
|
let mut use_count = 0;
|
||||||
for use_ in &opcode.uses {
|
for use_ in &opcode.uses {
|
||||||
if let Some(use_) = use_.strip_suffix(".nz") {
|
if let Some(use_) = use_.strip_suffix(".nz") {
|
||||||
|
@ -264,44 +256,65 @@ pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||||
uses.extend(quote! { #arg, });
|
uses.extend(quote! { #arg, });
|
||||||
use_count += 1;
|
use_count += 1;
|
||||||
}
|
}
|
||||||
for _ in use_count..max_args {
|
|
||||||
uses.extend(quote! { Argument::None, });
|
if defs_count > 0 {
|
||||||
|
for _ in defs_count..max_args {
|
||||||
|
defs.extend(quote! { Argument::None, });
|
||||||
|
}
|
||||||
|
let defs_name = format_ident!("defs_{}", opcode.ident());
|
||||||
|
defs_uses_functions.extend(quote! {
|
||||||
|
fn #defs_name(out: &mut Arguments, ins: Ins) { *out = [#defs]; }
|
||||||
|
});
|
||||||
|
defs_refs.extend(quote! { #defs_name, });
|
||||||
|
} else {
|
||||||
|
defs_refs.extend(quote! { defs_uses_empty, });
|
||||||
|
}
|
||||||
|
|
||||||
|
if use_count > 0 {
|
||||||
|
for _ in use_count..max_args {
|
||||||
|
uses.extend(quote! { Argument::None, });
|
||||||
|
}
|
||||||
|
let uses_name = format_ident!("uses_{}", opcode.ident());
|
||||||
|
defs_uses_functions.extend(quote! {
|
||||||
|
fn #uses_name(out: &mut Arguments, ins: Ins) { *out = [#uses]; }
|
||||||
|
});
|
||||||
|
uses_refs.extend(quote! { #uses_name, });
|
||||||
|
} else {
|
||||||
|
uses_refs.extend(quote! { defs_uses_empty, });
|
||||||
}
|
}
|
||||||
let defs_name = format_ident!("defs_{}", opcode.ident());
|
|
||||||
let uses_name = format_ident!("uses_{}", opcode.ident());
|
|
||||||
defs_uses_functions.extend(quote! {
|
|
||||||
const fn #defs_name(ins: &Ins) -> Arguments { [#defs] }
|
|
||||||
const fn #uses_name(ins: &Ins) -> Arguments { [#uses] }
|
|
||||||
});
|
|
||||||
defs_refs.extend(quote! { #defs_name, });
|
|
||||||
uses_refs.extend(quote! { #uses_name, });
|
|
||||||
}
|
}
|
||||||
defs_uses_functions.extend(quote! {
|
defs_uses_functions.extend(quote! {
|
||||||
const fn defs_uses_illegal(_ins: &Ins) -> Arguments { [#none_args] }
|
fn defs_uses_empty(out: &mut Arguments, _ins: Ins) { *out = EMPTY_ARGS; }
|
||||||
});
|
});
|
||||||
|
|
||||||
// Filling the tables to 256 entries to avoid bounds checks
|
// Filling the tables to 256 entries to avoid bounds checks
|
||||||
for _ in sorted_ops.len()..256 {
|
for _ in sorted_ops.len()..256 {
|
||||||
opcode_patterns.extend(quote! { (0, 0), });
|
opcode_patterns.extend(quote! { (0, 0), });
|
||||||
opcode_names.extend(quote! { "<illegal>", });
|
opcode_names.extend(quote! { "<illegal>", });
|
||||||
base_functions_ref.extend(quote! { mnemonic_illegal, });
|
basic_functions_ref.extend(quote! { mnemonic_illegal, });
|
||||||
simplified_functions_ref.extend(quote! { mnemonic_illegal, });
|
simplified_functions_ref.extend(quote! { mnemonic_illegal, });
|
||||||
defs_refs.extend(quote! { defs_uses_illegal, });
|
defs_refs.extend(quote! { defs_uses_empty, });
|
||||||
uses_refs.extend(quote! { defs_uses_illegal, });
|
uses_refs.extend(quote! { defs_uses_empty, });
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut none_args = TokenStream::new();
|
||||||
|
for _ in 0..max_args {
|
||||||
|
none_args.extend(quote! { Argument::None, });
|
||||||
}
|
}
|
||||||
|
|
||||||
let max_args = Literal::usize_unsuffixed(max_args);
|
let max_args = Literal::usize_unsuffixed(max_args);
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
|
#[comment = " Code generated by ppc750-genisa. DO NOT EDIT."]
|
||||||
use crate::disasm::*;
|
use crate::disasm::*;
|
||||||
#[doc = " The entry table allows us to quickly find the range of possible opcodes for a"]
|
#[doc = " The entry table allows us to quickly find the range of possible opcodes for a"]
|
||||||
#[doc = " given 6-bit prefix. 2*64 bytes should fit in a cache line (or two)."]
|
#[doc = " given 6-bit prefix. 2*64 bytes should fit in a cache line (or two)."]
|
||||||
const OPCODE_ENTRIES: [(u8, u8); 64] = [#opcode_entries];
|
static OPCODE_ENTRIES: [(u8, u8); 64] = [#opcode_entries];
|
||||||
#[doc = " The bitmask and pattern for each opcode."]
|
#[doc = " The bitmask and pattern for each opcode."]
|
||||||
const OPCODE_PATTERNS: [(u32, u32); 256] = [#opcode_patterns];
|
static OPCODE_PATTERNS: [(u32, u32); 256] = [#opcode_patterns];
|
||||||
#[doc = " The name of each opcode."]
|
#[doc = " The name of each opcode."]
|
||||||
const OPCODE_NAMES: [&str; 256] = [#opcode_names];
|
static OPCODE_NAMES: [&str; 256] = [#opcode_names];
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
|
@ -314,21 +327,19 @@ pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||||
}
|
}
|
||||||
impl Opcode {
|
impl Opcode {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn _mnemonic(self) -> &'static str {
|
pub fn _mnemonic(self) -> &'static str {
|
||||||
OPCODE_NAMES[self as usize]
|
OPCODE_NAMES[self as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn _detect(code: u32) -> Self {
|
pub fn _detect(code: u32) -> Self {
|
||||||
let entry = OPCODE_ENTRIES[(code >> 26) as usize];
|
let entry = OPCODE_ENTRIES[(code >> 26) as usize];
|
||||||
let mut i = entry.0;
|
for i in entry.0..entry.1 {
|
||||||
while i < entry.1 {
|
|
||||||
let pattern = OPCODE_PATTERNS[i as usize];
|
let pattern = OPCODE_PATTERNS[i as usize];
|
||||||
if (code & pattern.0) == pattern.1 {
|
if (code & pattern.0) == pattern.1 {
|
||||||
#[comment = " Safety: The enum is repr(u8) and marked non_exhaustive"]
|
#[comment = " Safety: The enum is repr(u8) and marked non_exhaustive"]
|
||||||
return unsafe { core::mem::transmute(i) };
|
return unsafe { core::mem::transmute(i) };
|
||||||
}
|
}
|
||||||
i += 1;
|
|
||||||
}
|
}
|
||||||
Self::Illegal
|
Self::Illegal
|
||||||
}
|
}
|
||||||
|
@ -339,14 +350,33 @@ pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Arguments = [Argument; #max_args];
|
pub type Arguments = [Argument; #max_args];
|
||||||
pub type MnemonicFunction = fn(&Ins) -> (&'static str, Arguments);
|
pub const EMPTY_ARGS: Arguments = [#none_args];
|
||||||
|
|
||||||
|
type MnemonicFunction = fn(&mut ParsedIns, Ins);
|
||||||
#mnemonic_functions
|
#mnemonic_functions
|
||||||
pub const BASE_MNEMONICS: [MnemonicFunction; 256] = [#base_functions_ref];
|
static BASIC_MNEMONICS: [MnemonicFunction; 256] = [#basic_functions_ref];
|
||||||
pub const SIMPLIFIED_MNEMONICS: [MnemonicFunction; 256] = [#simplified_functions_ref];
|
#[inline]
|
||||||
|
pub fn parse_basic(out: &mut ParsedIns, ins: Ins) {
|
||||||
|
BASIC_MNEMONICS[ins.op as usize](out, ins)
|
||||||
|
}
|
||||||
|
static SIMPLIFIED_MNEMONICS: [MnemonicFunction; 256] = [#simplified_functions_ref];
|
||||||
|
#[inline]
|
||||||
|
pub fn parse_simplified(out: &mut ParsedIns, ins: Ins) {
|
||||||
|
SIMPLIFIED_MNEMONICS[ins.op as usize](out, ins)
|
||||||
|
}
|
||||||
|
|
||||||
|
type DefsUsesFunction = fn(&mut Arguments, Ins);
|
||||||
#defs_uses_functions
|
#defs_uses_functions
|
||||||
pub type DefsUsesFunction = fn(&Ins) -> Arguments;
|
static DEFS_FUNCTIONS: [DefsUsesFunction; 256] = [#defs_refs];
|
||||||
pub const DEFS_FUNCTIONS: [DefsUsesFunction; 256] = [#defs_refs];
|
#[inline]
|
||||||
pub const USES_FUNCTIONS: [DefsUsesFunction; 256] = [#uses_refs];
|
pub fn parse_defs(out: &mut Arguments, ins: Ins) {
|
||||||
|
DEFS_FUNCTIONS[ins.op as usize](out, ins)
|
||||||
|
}
|
||||||
|
static USES_FUNCTIONS: [DefsUsesFunction; 256] = [#uses_refs];
|
||||||
|
#[inline]
|
||||||
|
pub fn parse_uses(out: &mut Arguments, ins: Ins) {
|
||||||
|
USES_FUNCTIONS[ins.op as usize](out, ins)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,17 +418,22 @@ fn gen_mnemonic(
|
||||||
max_args: usize,
|
max_args: usize,
|
||||||
replace: Option<&HashMap<String, String>>,
|
replace: Option<&HashMap<String, String>>,
|
||||||
) -> Result<TokenStream> {
|
) -> Result<TokenStream> {
|
||||||
let mut arguments = TokenStream::new();
|
let arguments = if args.is_empty() {
|
||||||
for field in args {
|
quote! { EMPTY_ARGS }
|
||||||
let arg = gen_argument(field, isa, replace.and_then(|m| m.get(field)))?;
|
} else {
|
||||||
arguments.extend(quote! { #arg, });
|
let mut inner = TokenStream::new();
|
||||||
}
|
for field in args {
|
||||||
for _ in args.len()..max_args {
|
let arg = gen_argument(field, isa, replace.and_then(|m| m.get(field)))?;
|
||||||
arguments.extend(quote! { Argument::None, });
|
inner.extend(quote! { #arg, });
|
||||||
}
|
}
|
||||||
|
for _ in args.len()..max_args {
|
||||||
|
inner.extend(quote! { Argument::None, });
|
||||||
|
}
|
||||||
|
quote! { [#inner] }
|
||||||
|
};
|
||||||
|
|
||||||
if modifiers.is_empty() {
|
if modifiers.is_empty() {
|
||||||
Ok(quote! { (#name, [#arguments]) })
|
Ok(quote! { ParsedIns { mnemonic: #name, args: #arguments } })
|
||||||
} else {
|
} else {
|
||||||
let names = modifier_names(name, modifiers, isa);
|
let names = modifier_names(name, modifiers, isa);
|
||||||
let mut bitset = quote! { 0 };
|
let mut bitset = quote! { 0 };
|
||||||
|
@ -411,6 +446,6 @@ fn gen_mnemonic(
|
||||||
bitset.extend(quote! { | (ins.#modifier() as usize) << #i });
|
bitset.extend(quote! { | (ins.#modifier() as usize) << #i });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(quote! { ([#(#names),*][#bitset], [#arguments]) })
|
Ok(quote! { ParsedIns { mnemonic: [#(#names),*][#bitset], args: #arguments } })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue