switch from macro to ahead-of-time generated code
This commit is contained in:
parent
171666e6fc
commit
5431836376
|
@ -325,7 +325,6 @@ name = "ppc750cl"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"ppc750cl-macros",
|
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -350,7 +349,7 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppc750cl-macros"
|
name = "ppc750cl-genisa"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itertools",
|
"itertools",
|
||||||
|
|
|
@ -3,8 +3,8 @@ members = [
|
||||||
"disasm",
|
"disasm",
|
||||||
"disasm-py",
|
"disasm-py",
|
||||||
"dol",
|
"dol",
|
||||||
"macros",
|
|
||||||
"fuzz",
|
"fuzz",
|
||||||
|
"genisa",
|
||||||
"flow-graph",
|
"flow-graph",
|
||||||
"rand",
|
"rand",
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "ppc750cl-py"
|
name = "ppc750cl-py"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
authors = ["Richard Patel <me@terorie.dev>"]
|
authors = ["Richard Patel <me@terorie.dev>"]
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
description = "Python bindings for PowerPC 750CL Disassembler"
|
description = "Python bindings for PowerPC 750CL Disassembler"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "ppc750cl"
|
name = "ppc750cl"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
authors = ["Richard Patel <me@terorie.dev>"]
|
authors = ["Richard Patel <me@terorie.dev>"]
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
description = "Disassembler for PowerPC 750CL"
|
description = "Disassembler for PowerPC 750CL"
|
||||||
|
@ -10,5 +10,4 @@ repository = "https://github.com/terorie/ppc750cl"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
ppc750cl-macros = { path = "../macros", version = "0.1.1" }
|
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
|
|
|
@ -3,19 +3,20 @@ use std::ops::Range;
|
||||||
|
|
||||||
use num_traits::{AsPrimitive, PrimInt};
|
use num_traits::{AsPrimitive, PrimInt};
|
||||||
|
|
||||||
use ppc750cl_macros::{fields, ins_impl, opcodes};
|
|
||||||
|
|
||||||
pub use crate::iter::{disasm_iter, DisasmIterator};
|
pub use crate::iter::{disasm_iter, DisasmIterator};
|
||||||
|
|
||||||
pub mod formatter;
|
pub mod formatter;
|
||||||
mod iter;
|
mod iter;
|
||||||
|
mod generated;
|
||||||
|
use generated::*;
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::formatter::FormattedIns;
|
pub use crate::formatter::FormattedIns;
|
||||||
pub use crate::Field;
|
|
||||||
pub use crate::Field::*;
|
pub use crate::Field::*;
|
||||||
pub use crate::Ins;
|
pub use crate::Ins;
|
||||||
pub use crate::Opcode::*;
|
pub use crate::Opcode::*;
|
||||||
|
pub use crate::Modifiers;
|
||||||
|
pub use crate::SimplifiedIns;
|
||||||
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,
|
||||||
};
|
};
|
||||||
|
@ -165,9 +166,6 @@ impl Display for Argument {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate the Field enum and impls.
|
|
||||||
fields!();
|
|
||||||
|
|
||||||
impl Field {
|
impl Field {
|
||||||
pub fn argument(&self) -> Option<Argument> {
|
pub fn argument(&self) -> Option<Argument> {
|
||||||
match self {
|
match self {
|
||||||
|
@ -237,10 +235,6 @@ impl std::fmt::Display for Modifiers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate the Opcode enum and impls.
|
|
||||||
// TODO This could be made more readable with a derive over an empty enum.
|
|
||||||
opcodes!();
|
|
||||||
|
|
||||||
impl Opcode {
|
impl Opcode {
|
||||||
/// Detects the opcode of a machine code instruction.
|
/// Detects the opcode of a machine code instruction.
|
||||||
pub fn detect(code: u32) -> Self {
|
pub fn detect(code: u32) -> Self {
|
||||||
|
@ -320,8 +314,6 @@ impl Ins {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ins_impl!();
|
|
||||||
|
|
||||||
/// A simplified PowerPC 750CL instruction.
|
/// A simplified PowerPC 750CL instruction.
|
||||||
pub struct SimplifiedIns {
|
pub struct SimplifiedIns {
|
||||||
pub ins: Ins,
|
pub ins: Ins,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "dol"
|
name = "dol"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
authors = ["Richard Patel <me@terorie.dev>"]
|
authors = ["Richard Patel <me@terorie.dev>"]
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
description = "Deserializer for the DOL executable format"
|
description = "Deserializer for the DOL executable format"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "ppc750cl-flow-graph"
|
name = "ppc750cl-flow-graph"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
authors = ["riidefi <riidefi@rii.dev>", "Richard Patel <me@terorie.dev>"]
|
authors = ["riidefi <riidefi@rii.dev>", "Richard Patel <me@terorie.dev>"]
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
description = "Control flow graph analysis for PowerPC 750CL"
|
description = "Control flow graph analysis for PowerPC 750CL"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "ppc750cl-fuzz"
|
name = "ppc750cl-fuzz"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
authors = ["Richard Patel <me@terorie.dev>"]
|
authors = ["Richard Patel <me@terorie.dev>"]
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
description = "Complete fuzzer for ppc750cl"
|
description = "Complete fuzzer for ppc750cl"
|
||||||
|
|
|
@ -1,19 +1,17 @@
|
||||||
[package]
|
[package]
|
||||||
name = "ppc750cl-macros"
|
name = "ppc750cl-genisa"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
authors = ["Richard Patel <me@terorie.dev>"]
|
authors = ["Richard Patel <me@terorie.dev>"]
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
description = "Auxiliary procedural macros for the ppc750cl disassembler"
|
description = "Rust code generator for ppc750cl"
|
||||||
repository = "https://github.com/terorie/ppc750cl"
|
repository = "https://github.com/terorie/ppc750cl"
|
||||||
|
|
||||||
[lib]
|
|
||||||
proc-macro = true
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
itertools = "0.10.1"
|
itertools = "0.10"
|
||||||
proc-macro2 = "1.0"
|
proc-macro2 = "1.0"
|
||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
|
syn = { version = "1.0" }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_yaml = "0.8"
|
serde_yaml = "0.8"
|
||||||
syn = { version = "1.0", features = ["full", "parsing"] }
|
|
|
@ -1,6 +1,8 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::iter::FromIterator;
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use proc_macro2::{Ident, Literal, Span, TokenStream, TokenTree};
|
use proc_macro2::{Ident, Literal, Span, TokenStream, TokenTree};
|
||||||
|
@ -14,13 +16,58 @@ macro_rules! token_stream {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if let Err(err) = _main() {
|
||||||
|
eprintln!("{}", err);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _main() -> Result<()> {
|
||||||
|
let isa = load_isa()?;
|
||||||
|
|
||||||
|
let mut unformatted_code = Vec::<u8>::new();
|
||||||
|
writeln!(&mut unformatted_code, "{}", quote! {
|
||||||
|
use crate::prelude::*;
|
||||||
|
})?;
|
||||||
|
writeln!(&mut unformatted_code, "{}", isa.gen_opcode_enum()?)?;
|
||||||
|
writeln!(&mut unformatted_code, "{}", isa.gen_field_enum()?)?;
|
||||||
|
writeln!(&mut unformatted_code, "{}", isa.gen_ins_impl()?)?;
|
||||||
|
|
||||||
|
let formatted_code = rustfmt(unformatted_code);
|
||||||
|
File::create("./disasm/src/generated.rs")?
|
||||||
|
.write_all(&formatted_code)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rustfmt(code: Vec<u8>) -> Vec<u8> {
|
||||||
|
let mut rustfmt = Command::new("rustfmt")
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.expect("failed to spawn rustfmt");
|
||||||
|
|
||||||
|
let mut stdin = rustfmt.stdin.take().unwrap();
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
let _ = stdin.write_all(&code);
|
||||||
|
});
|
||||||
|
|
||||||
|
let rustfmt_res = rustfmt.wait_with_output().expect("failed to run rustfmt");
|
||||||
|
if !rustfmt_res.status.success() {
|
||||||
|
panic!("rustfmt failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
rustfmt_res.stdout
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub(crate) struct BitRange(Range<u8>);
|
pub(crate) struct BitRange(Range<u8>);
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for BitRange {
|
impl<'de> Deserialize<'de> for BitRange {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
let range_str: String = Deserialize::deserialize(deserializer)?;
|
let range_str: String = Deserialize::deserialize(deserializer)?;
|
||||||
if let Some((start_str, stop_str)) = range_str.split_once("..") {
|
if let Some((start_str, stop_str)) = range_str.split_once("..") {
|
||||||
|
@ -115,7 +162,7 @@ pub(crate) struct Opcode {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Opcode {
|
impl Opcode {
|
||||||
fn variant_identifier(&self) -> syn::Result<TokenTree> {
|
fn variant_identifier(&self) -> Result<TokenTree> {
|
||||||
to_rust_variant(&self.name)
|
to_rust_variant(&self.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -153,19 +200,25 @@ pub(crate) struct Isa {
|
||||||
mnemonics: Vec<Mnemonic>,
|
mnemonics: Vec<Mnemonic>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn load_isa() -> Result<Isa> {
|
||||||
|
let yaml_file = File::open("isa.yaml")?;
|
||||||
|
let isa: Isa = serde_yaml::from_reader(yaml_file)?;
|
||||||
|
Ok(isa)
|
||||||
|
}
|
||||||
|
|
||||||
impl Isa {
|
impl Isa {
|
||||||
pub(crate) fn gen_opcode_enum(&self) -> syn::Result<TokenStream> {
|
fn gen_opcode_enum(&self) -> Result<TokenStream> {
|
||||||
// Create enum variants.
|
// Create enum variants.
|
||||||
let enum_variants = self
|
let enum_variants = self
|
||||||
.opcodes
|
.opcodes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|opcode| -> syn::Result<TokenStream> {
|
.map(|opcode| -> Result<TokenStream> {
|
||||||
let ident = opcode.variant_identifier()?;
|
let ident = opcode.variant_identifier()?;
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
#ident,
|
#ident,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.try_collect::<TokenStream, Vec<TokenStream>, syn::Error>()?;
|
.try_collect::<TokenStream, Vec<TokenStream>, Error>()?;
|
||||||
let enum_variants = token_stream!(enum_variants);
|
let enum_variants = token_stream!(enum_variants);
|
||||||
|
|
||||||
// Create functions.
|
// Create functions.
|
||||||
|
@ -187,7 +240,7 @@ impl Isa {
|
||||||
Ok(opcode_enum)
|
Ok(opcode_enum)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_mnemonic_fn(&self) -> syn::Result<TokenStream> {
|
fn gen_mnemonic_fn(&self) -> Result<TokenStream> {
|
||||||
// Create match arms.
|
// Create match arms.
|
||||||
let match_arms = self
|
let match_arms = self
|
||||||
.opcodes
|
.opcodes
|
||||||
|
@ -200,11 +253,11 @@ impl Isa {
|
||||||
Opcode::#variant => #literal,
|
Opcode::#variant => #literal,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.try_collect::<TokenStream, Vec<TokenStream>, syn::Error>()?;
|
.try_collect::<TokenStream, Vec<TokenStream>, Error>()?;
|
||||||
let match_arms = token_stream!(match_arms);
|
let match_arms = token_stream!(match_arms);
|
||||||
// Create final function.
|
// Create final function.
|
||||||
let mnemonic_fn = quote! {
|
let mnemonic_fn = quote! {
|
||||||
fn _mnemonic(self) -> &'static str {
|
pub(crate) fn _mnemonic(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Opcode::Illegal => "<illegal>",
|
Opcode::Illegal => "<illegal>",
|
||||||
#match_arms
|
#match_arms
|
||||||
|
@ -214,7 +267,7 @@ impl Isa {
|
||||||
Ok(mnemonic_fn)
|
Ok(mnemonic_fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn gen_opcode_detect(&self) -> syn::Result<TokenStream> {
|
pub(crate) fn gen_opcode_detect(&self) -> Result<TokenStream> {
|
||||||
// Generate if chain.
|
// Generate if chain.
|
||||||
let if_chain = self
|
let if_chain = self
|
||||||
.opcodes
|
.opcodes
|
||||||
|
@ -231,11 +284,11 @@ impl Isa {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.try_collect::<TokenStream, Vec<TokenStream>, syn::Error>()?;
|
.try_collect::<TokenStream, Vec<TokenStream>, Error>()?;
|
||||||
let if_chain = token_stream!(if_chain);
|
let if_chain = token_stream!(if_chain);
|
||||||
// Generate function.
|
// Generate function.
|
||||||
let func = quote! {
|
let func = quote! {
|
||||||
fn _detect(code: u32) -> Self {
|
pub(crate) fn _detect(code: u32) -> Self {
|
||||||
#if_chain
|
#if_chain
|
||||||
Opcode::Illegal
|
Opcode::Illegal
|
||||||
}
|
}
|
||||||
|
@ -243,7 +296,7 @@ impl Isa {
|
||||||
Ok(func)
|
Ok(func)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn gen_field_enum(&self) -> syn::Result<TokenStream> {
|
pub(crate) fn gen_field_enum(&self) -> Result<TokenStream> {
|
||||||
// Create enum variants.
|
// Create enum variants.
|
||||||
let mut enum_variants = Vec::new();
|
let mut enum_variants = Vec::new();
|
||||||
for field in &self.fields {
|
for field in &self.fields {
|
||||||
|
@ -255,6 +308,7 @@ impl Isa {
|
||||||
|
|
||||||
// Create final enum.
|
// Create final enum.
|
||||||
let field_enum = quote! {
|
let field_enum = quote! {
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
pub enum Field {
|
pub enum Field {
|
||||||
#enum_variants
|
#enum_variants
|
||||||
|
@ -263,7 +317,8 @@ impl Isa {
|
||||||
Ok(field_enum)
|
Ok(field_enum)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn gen_ins_impl(&self) -> syn::Result<TokenStream> {
|
|
||||||
|
pub(crate) fn gen_ins_impl(&self) -> Result<TokenStream> {
|
||||||
// Map fields by name.
|
// Map fields by name.
|
||||||
let mut field_by_name = HashMap::<String, &Field>::new();
|
let mut field_by_name = HashMap::<String, &Field>::new();
|
||||||
for field in &self.fields {
|
for field in &self.fields {
|
||||||
|
@ -280,7 +335,7 @@ impl Isa {
|
||||||
let mut fields = Vec::new();
|
let mut fields = Vec::new();
|
||||||
for arg in &opcode.args {
|
for arg in &opcode.args {
|
||||||
let field: &Field = field_by_name.get(arg).ok_or_else(|| {
|
let field: &Field = field_by_name.get(arg).ok_or_else(|| {
|
||||||
syn::Error::new(Span::call_site(), format!("undefined field {}", arg))
|
Error::from(format!("undefined field {}", arg))
|
||||||
})?;
|
})?;
|
||||||
let variant = field.construct_variant_self();
|
let variant = field.construct_variant_self();
|
||||||
fields.extend(quote! { #variant, })
|
fields.extend(quote! { #variant, })
|
||||||
|
@ -301,10 +356,7 @@ impl Isa {
|
||||||
"AA" => quote! { m.aa = self.bit(30); },
|
"AA" => quote! { m.aa = self.bit(30); },
|
||||||
"LK" => quote! { m.lk = self.bit(31); },
|
"LK" => quote! { m.lk = self.bit(31); },
|
||||||
_ => {
|
_ => {
|
||||||
return Err(syn::Error::new(
|
return Err(format!("unsupported modifier {}", modifier).into());
|
||||||
Span::call_site(),
|
|
||||||
format!("unsupported modifier {}", modifier),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -315,10 +367,7 @@ impl Isa {
|
||||||
"AA" => quote! { m.aa = true; },
|
"AA" => quote! { m.aa = true; },
|
||||||
"LK" => quote! { m.lk = true; },
|
"LK" => quote! { m.lk = true; },
|
||||||
_ => {
|
_ => {
|
||||||
return Err(syn::Error::new(
|
return Err(format!("unsupported modifier {}", modifier).into());
|
||||||
Span::call_site(),
|
|
||||||
format!("unsupported modifier {}", modifier),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -393,39 +442,38 @@ impl Isa {
|
||||||
// Generate final fields function.
|
// Generate final fields function.
|
||||||
let ins_impl = quote! {
|
let ins_impl = quote! {
|
||||||
impl Ins {
|
impl Ins {
|
||||||
fn _fields(&self) -> Vec<Field> {
|
pub(crate) fn _fields(&self) -> Vec<Field> {
|
||||||
match self.op {
|
match self.op {
|
||||||
Opcode::Illegal => vec![],
|
Opcode::Illegal => vec![],
|
||||||
#field_match_arms
|
#field_match_arms
|
||||||
_ => todo!()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _defs(&self) -> Vec<Field> {
|
#[allow(unused_mut)]
|
||||||
|
pub(crate) fn _defs(&self) -> Vec<Field> {
|
||||||
match self.op {
|
match self.op {
|
||||||
Opcode::Illegal => vec![],
|
Opcode::Illegal => vec![],
|
||||||
#def_match_arms
|
#def_match_arms
|
||||||
_ => todo!()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _uses(&self) -> Vec<Field> {
|
#[allow(unused_mut)]
|
||||||
|
pub(crate) fn _uses(&self) -> Vec<Field> {
|
||||||
match self.op {
|
match self.op {
|
||||||
Opcode::Illegal => vec![],
|
Opcode::Illegal => vec![],
|
||||||
#use_match_arms
|
#use_match_arms
|
||||||
_ => todo!()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _modifiers(&self) -> Modifiers {
|
#[allow(unused_mut)]
|
||||||
|
pub(crate) fn _modifiers(&self) -> Modifiers {
|
||||||
match self.op {
|
match self.op {
|
||||||
Opcode::Illegal => Modifiers::default(),
|
Opcode::Illegal => Modifiers::default(),
|
||||||
#modifier_match_arms
|
#modifier_match_arms
|
||||||
_ => todo!()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _simplified(self) -> SimplifiedIns {
|
pub(crate) fn _simplified(self) -> SimplifiedIns {
|
||||||
SimplifiedIns {
|
SimplifiedIns {
|
||||||
mnemonic: self.op.mnemonic(),
|
mnemonic: self.op.mnemonic(),
|
||||||
modifiers: self._modifiers(),
|
modifiers: self._modifiers(),
|
||||||
|
@ -444,15 +492,15 @@ fn to_rust_ident(key: &str) -> TokenTree {
|
||||||
TokenTree::Ident(Ident::new(&key.replace(".", "_"), Span::call_site()))
|
TokenTree::Ident(Ident::new(&key.replace(".", "_"), Span::call_site()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts the given key into a struct variant key.
|
/// Converts the given key into an enum variant key.
|
||||||
fn to_rust_variant(key: &str) -> syn::Result<TokenTree> {
|
fn to_rust_variant(key: &str) -> Result<TokenTree> {
|
||||||
Ok(TokenTree::Ident(Ident::new(
|
Ok(TokenTree::Ident(Ident::new(
|
||||||
&to_rust_variant_str(key).map_err(|e| syn::Error::new(Span::call_site(), e))?,
|
&to_rust_variant_str(key)?,
|
||||||
Span::call_site(),
|
Span::call_site(),
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_rust_variant_str(key: &str) -> Result<String, String> {
|
fn to_rust_variant_str(key: &str) -> Result<String> {
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
let mut chars = key.chars();
|
let mut chars = key.chars();
|
||||||
loop {
|
loop {
|
||||||
|
@ -464,7 +512,7 @@ fn to_rust_variant_str(key: &str) -> Result<String, String> {
|
||||||
s.push(match c {
|
s.push(match c {
|
||||||
'a'..='z' => c.to_ascii_uppercase(),
|
'a'..='z' => c.to_ascii_uppercase(),
|
||||||
'A'..='Z' => c,
|
'A'..='Z' => c,
|
||||||
_ => return Err(format!("invalid identifier: {}", key)),
|
_ => return Err(format!("invalid identifier: {}", key).into()),
|
||||||
});
|
});
|
||||||
loop {
|
loop {
|
||||||
let c = match chars.next() {
|
let c = match chars.next() {
|
||||||
|
@ -478,8 +526,11 @@ fn to_rust_variant_str(key: &str) -> Result<String, String> {
|
||||||
s.push('_');
|
s.push('_');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
_ => return Err(format!("invalid character in opcode name: {}", key)),
|
_ => return Err(format!("invalid character in opcode name: {}", key).into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Error = Box<dyn std::error::Error>;
|
||||||
|
type Result<T> = std::result::Result<T, Error>;
|
|
@ -1,66 +0,0 @@
|
||||||
#![feature(proc_macro_span, proc_macro_def_site)]
|
|
||||||
|
|
||||||
mod isa;
|
|
||||||
//mod writer;
|
|
||||||
|
|
||||||
use std::fs::File;
|
|
||||||
|
|
||||||
use proc_macro::Span;
|
|
||||||
|
|
||||||
use crate::isa::Isa;
|
|
||||||
|
|
||||||
#[proc_macro]
|
|
||||||
pub fn opcodes(_: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
|
||||||
let isa = match load_isa() {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(err) => return err,
|
|
||||||
};
|
|
||||||
match isa.gen_opcode_enum() {
|
|
||||||
Ok(v) => v.into(),
|
|
||||||
Err(err) => proc_macro::TokenStream::from(err.to_compile_error()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[proc_macro]
|
|
||||||
pub fn fields(_: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
|
||||||
let isa = match load_isa() {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(err) => return err,
|
|
||||||
};
|
|
||||||
match isa.gen_field_enum() {
|
|
||||||
Ok(v) => v.into(),
|
|
||||||
Err(err) => proc_macro::TokenStream::from(err.to_compile_error()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[proc_macro]
|
|
||||||
pub fn ins_impl(_: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
|
||||||
let isa = match load_isa() {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(err) => return err,
|
|
||||||
};
|
|
||||||
match isa.gen_ins_impl() {
|
|
||||||
Ok(v) => v.into(),
|
|
||||||
Err(err) => proc_macro::TokenStream::from(err.to_compile_error()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_isa() -> Result<Isa, proc_macro::TokenStream> {
|
|
||||||
_load_isa().map_err(|err| {
|
|
||||||
proc_macro::TokenStream::from(
|
|
||||||
syn::Error::new(Span::def_site().into(), err).to_compile_error(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn _load_isa() -> Result<Isa, Box<dyn std::error::Error>> {
|
|
||||||
// Figure out YAML path.
|
|
||||||
let def_site = Span::def_site();
|
|
||||||
let rust_path = def_site.source_file().path();
|
|
||||||
let yaml_path = rust_path.parent().unwrap().join("isa.yaml");
|
|
||||||
// Open and deserialize YAML file.
|
|
||||||
let yaml_file = File::open(yaml_path).map_err(|e| syn::Error::new(def_site.into(), e))?;
|
|
||||||
let isa: Isa =
|
|
||||||
serde_yaml::from_reader(yaml_file).map_err(|e| syn::Error::new(def_site.into(), e))?;
|
|
||||||
Ok(isa)
|
|
||||||
}
|
|
|
@ -1,170 +0,0 @@
|
||||||
use std::iter::FromIterator;
|
|
||||||
use std::string::ToString;
|
|
||||||
|
|
||||||
use proc_macro2::{Delimiter, Group, TokenStream};
|
|
||||||
use quote::quote;
|
|
||||||
use quote::ToTokens;
|
|
||||||
use syn::parse::{Parse, ParseStream};
|
|
||||||
use syn::punctuated::Punctuated;
|
|
||||||
use syn::spanned::Spanned;
|
|
||||||
use syn::{Expr, ExprLit, ExprPath, Ident};
|
|
||||||
|
|
||||||
struct Arguments {
|
|
||||||
formatter: Expr,
|
|
||||||
ins: Expr,
|
|
||||||
args: Punctuated<Argument, syn::token::Semi>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parse for Arguments {
|
|
||||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
|
||||||
let formatter = input.parse()?;
|
|
||||||
input.parse::<syn::token::Comma>()?;
|
|
||||||
let ins = input.parse()?;
|
|
||||||
input.parse::<syn::token::FatArrow>()?;
|
|
||||||
let content;
|
|
||||||
syn::braced!(content in input);
|
|
||||||
let args = Punctuated::parse_terminated(&content)?;
|
|
||||||
Ok(Self {
|
|
||||||
formatter,
|
|
||||||
ins,
|
|
||||||
args,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A single part of an instruction.
|
|
||||||
///
|
|
||||||
/// Examples:
|
|
||||||
/// ```ignore
|
|
||||||
/// (op.mnemonic, rc, oe) -> mnemonic;
|
|
||||||
/// d -> fpr;
|
|
||||||
/// ```
|
|
||||||
struct Argument {
|
|
||||||
sources: Vec<Expr>,
|
|
||||||
target: Ident,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parse for Argument {
|
|
||||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
|
||||||
// Parse source part.
|
|
||||||
let lookahead = input.lookahead1();
|
|
||||||
let sources;
|
|
||||||
if lookahead.peek(syn::token::Paren) {
|
|
||||||
// Parse multiple if we found a parenthesis.
|
|
||||||
let content;
|
|
||||||
syn::parenthesized!(content in input);
|
|
||||||
sources = content
|
|
||||||
.parse_terminated::<Expr, syn::token::Comma>(Expr::parse)?
|
|
||||||
.into_iter()
|
|
||||||
.collect();
|
|
||||||
} else if lookahead.peek(syn::LitStr) || lookahead.peek(syn::LitInt) {
|
|
||||||
let expr = input.parse::<ExprLit>()?.into();
|
|
||||||
sources = vec![expr];
|
|
||||||
} else {
|
|
||||||
let expr = input.parse::<ExprPath>()?.into();
|
|
||||||
sources = vec![expr];
|
|
||||||
}
|
|
||||||
input.parse::<syn::token::Colon>()?;
|
|
||||||
let target = input.parse()?;
|
|
||||||
Ok(Self { sources, target })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Arguments {
|
|
||||||
fn format_mnemonic(&self) -> Vec<TokenStream> {
|
|
||||||
let arg = &self.args[0];
|
|
||||||
assert!(!arg.sources.is_empty());
|
|
||||||
// Print the mnemonic.
|
|
||||||
let mut calls = vec![self.format_call(&arg.target, self.ins_call(&arg.sources[0]))];
|
|
||||||
// Print any mnemonic suffixes.
|
|
||||||
for src in arg.sources.iter().skip(1) {
|
|
||||||
calls.push(self.format_call(
|
|
||||||
&Ident::new(&src.into_token_stream().to_string(), src.span()),
|
|
||||||
self.ins_call(src),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
calls
|
|
||||||
}
|
|
||||||
|
|
||||||
fn format_call(&self, method_arg: &Ident, args: TokenStream) -> TokenStream {
|
|
||||||
let arg_str = method_arg.to_string();
|
|
||||||
let method_name = format!("write_{}", arg_str);
|
|
||||||
let method_name = Ident::new(&method_name, method_arg.span());
|
|
||||||
let formatter = &self.formatter;
|
|
||||||
if arg_str == "branch_target" {
|
|
||||||
quote!(#formatter.write_branch_target(#args, self.addr)?)
|
|
||||||
} else {
|
|
||||||
quote!(#formatter.#method_name(#args)?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ins_call(&self, call: &Expr) -> TokenStream {
|
|
||||||
match call {
|
|
||||||
Expr::Lit(_) => call.to_token_stream(),
|
|
||||||
_ => {
|
|
||||||
let ins = &self.ins;
|
|
||||||
quote!(#ins.#call())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn write_asm(input: TokenStream) -> syn::Result<TokenStream> {
|
|
||||||
let arguments: Arguments = syn::parse2(input)?;
|
|
||||||
assert!(!arguments.args.is_empty());
|
|
||||||
|
|
||||||
// Create a list of calls to execute.
|
|
||||||
let mut calls = Vec::<TokenStream>::new();
|
|
||||||
calls.extend(arguments.format_mnemonic());
|
|
||||||
let mut offset_open = false;
|
|
||||||
for (i, arg) in arguments.args.iter().enumerate().skip(1) {
|
|
||||||
// Separate operands from one another unless the last one was an offset.
|
|
||||||
if !offset_open {
|
|
||||||
if i == 1 {
|
|
||||||
calls.push(
|
|
||||||
arguments
|
|
||||||
.format_call(&Ident::new("opcode_separator", arg.target.span()), quote!()),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
calls.push(arguments.format_call(
|
|
||||||
&Ident::new("operand_separator", arg.target.span()),
|
|
||||||
quote!(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Arguments to out.write_x(...);
|
|
||||||
let format_args = arg.sources.iter().map(|src| arguments.ins_call(src));
|
|
||||||
let format_args_punct: Punctuated<TokenStream, syn::token::Comma> =
|
|
||||||
Punctuated::from_iter(format_args);
|
|
||||||
// Create call.
|
|
||||||
if arg.target.to_string().starts_with("offset") {
|
|
||||||
// Offsets are a special case since we need to call close afterwards.
|
|
||||||
if offset_open {
|
|
||||||
return Err(syn::Error::new(
|
|
||||||
arg.target.span(),
|
|
||||||
"two consecutive offset arguments",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
calls.push(arguments.format_call(
|
|
||||||
&Ident::new(&(arg.target.to_string() + "_open"), arg.target.span()),
|
|
||||||
format_args_punct.to_token_stream(),
|
|
||||||
));
|
|
||||||
offset_open = true;
|
|
||||||
} else {
|
|
||||||
calls.push(arguments.format_call(&arg.target, format_args_punct.to_token_stream()));
|
|
||||||
if offset_open {
|
|
||||||
calls.push(
|
|
||||||
arguments.format_call(&Ident::new("offset_close", arg.target.span()), quote!()),
|
|
||||||
);
|
|
||||||
offset_open = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrap calls in a block returning Ok(()).
|
|
||||||
calls.push(quote!(std::io::Result::Ok(())));
|
|
||||||
let statements = Punctuated::<TokenStream, syn::token::Semi>::from_iter(calls);
|
|
||||||
let tokens = Group::new(Delimiter::Brace, statements.to_token_stream());
|
|
||||||
|
|
||||||
Ok(tokens.to_token_stream())
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "ppc750cl-rand"
|
name = "ppc750cl-rand"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
authors = ["Richard Patel <me@terorie.dev>"]
|
authors = ["Richard Patel <me@terorie.dev>"]
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
description = "Generate random PowerPC 750CL instructions"
|
description = "Generate random PowerPC 750CL instructions"
|
||||||
|
|
Loading…
Reference in New Issue