Some scaffolding for an assembler (bad)

This commit is contained in:
Luke Street 2023-01-28 19:57:28 -05:00
parent 9ae36eef34
commit 3af08db591
8 changed files with 4198 additions and 3 deletions

60
Cargo.lock generated
View File

@ -276,6 +276,44 @@ dependencies = [
"indexmap",
]
[[package]]
name = "phf"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c"
dependencies = [
"phf_shared",
]
[[package]]
name = "phf_codegen"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a56ac890c5e3ca598bbdeaa99964edb5b0258a583a9eb6ef4e89fc85d9224770"
dependencies = [
"phf_generator",
"phf_shared",
]
[[package]]
name = "phf_generator"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf"
dependencies = [
"phf_shared",
"rand",
]
[[package]]
name = "phf_shared"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676"
dependencies = [
"siphasher",
]
[[package]]
name = "ppc750cl"
version = "0.2.0"
@ -284,6 +322,13 @@ dependencies = [
"serde",
]
[[package]]
name = "ppc750cl-asm"
version = "0.1.0"
dependencies = [
"phf",
]
[[package]]
name = "ppc750cl-flow-graph"
version = "0.2.0"
@ -310,6 +355,9 @@ name = "ppc750cl-genisa"
version = "0.2.0"
dependencies = [
"itertools",
"phf",
"phf_codegen",
"pratt",
"proc-macro2",
"quote",
"serde",
@ -340,6 +388,12 @@ version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "pratt"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17e0a4425d076f0718b820673a38fbf3747080c61017eeb0dd79bc7e472b8bb8"
[[package]]
name = "proc-macro2"
version = "1.0.37"
@ -511,6 +565,12 @@ dependencies = [
"rand_core",
]
[[package]]
name = "siphasher"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
[[package]]
name = "smallvec"
version = "1.8.0"

View File

@ -1,5 +1,6 @@
[workspace]
members = [
"asm",
"disasm",
"disasm-py",
"dol",

12
asm/Cargo.toml Normal file
View File

@ -0,0 +1,12 @@
[package]
name = "ppc750cl-asm"
version = "0.1.0"
edition = "2021"
authors = ["Luke Street <luke@street.dev>"]
license = "GPL-3.0-or-later"
description = "Assembler for PowerPC 750CL"
keywords = ["powerpc", "wii", "gamecube"]
repository = "https://github.com/encounter/ppc750cl"
[dependencies]
phf = "0.11.1"

3618
asm/src/generated.rs Normal file

File diff suppressed because it is too large Load Diff

8
asm/src/lib.rs Normal file
View File

@ -0,0 +1,8 @@
mod generated;
pub mod prelude {}
#[cfg(test)]
mod tests {
use super::*;
}

View File

@ -15,3 +15,7 @@ quote = "1.0"
syn = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.8"
phf = "0.11.1"
phf_codegen = "0.11.1"
pratt = "0.4.0"

483
genisa/src/asm.rs Normal file
View File

@ -0,0 +1,483 @@
use std::{
collections::{hash_map::Entry, HashMap},
fs::File,
io::Write,
str::FromStr,
};
use pratt::{Affix, Associativity, PrattParser, Precedence, Result as PrattResult};
use proc_macro2::{token_stream, Ident, Literal, Spacing, Span, TokenStream, TokenTree};
use quote::{__private::ext::RepToTokensExt, quote, ToTokens};
use syn::{parse::Parser, LitInt};
use crate::{load_isa, rustfmt, to_rust_variant, Field, Isa, Modifier, Opcode};
type Error = Box<dyn std::error::Error>;
type Result<T> = std::result::Result<T, Error>;
macro_rules! token_stream {
($stream:ident) => {
TokenStream::from_iter($stream.into_iter())
};
}
pub(crate) fn asm_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, "{}", gen_fields(&isa)?)?;
writeln!(&mut unformatted_code, "{}", gen_opcode_from_str(&isa)?)?;
let formatted_code = rustfmt(unformatted_code);
File::create("./asm/src/generated.rs")?.write_all(&formatted_code)?;
Ok(())
}
fn gen_apply_field(field: &Field) -> Result<TokenStream> {
let mut val = quote! { value as u32 };
let val_shift = field.shift_left;
if field.shift_left > 0 {
val = quote!((#val >> #val_shift));
}
// https://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend
if field.signed {
// let mask2 = 1u32 << (self.bits.0.len() - 1);
// let mask2 = LitInt::new(&format!("0x{:x}", mask2), Span::call_site());
// val = quote!((((#val ^ #mask2).wrapping_sub(#mask2)) as i32))
} else {
// val = quote! { #val };
}
if field.split {
val = quote!((((#val & 0b11111_00000u32) >> 5u32) | ((#val & 0b00000_11111u32) << 5u32)) as u32);
}
let mask = (1u32 << field.bits.0.len()) - 1;
if mask != 0xFFFF_FFFF {
let mask = LitInt::new(&format!("0x{:x}", mask), Span::call_site());
val = quote!((#val & #mask));
}
let shift = 32 - field.bits.0.end;
if shift > 0 {
val = quote!((#val << #shift));
}
let ident = to_rust_variant(&field.name)?;
Ok(quote! {
Field::#ident => code | #val,
})
}
fn gen_fields(isa: &Isa) -> Result<TokenStream> {
let fields = TokenStream::from_iter(
isa.fields
.iter()
.filter(|field| !field.bits.0.is_empty())
.map(|field| -> Result<TokenStream> {
let ident = to_rust_variant(field.name.as_str())?;
Ok(quote! { #ident, })
})
.try_collect::<TokenStream>()?,
);
let field_match = TokenStream::from_iter(
isa.fields
.iter()
.filter(|field| !field.bits.0.is_empty())
.map(|field| gen_apply_field(field))
.try_collect::<TokenStream>()?,
);
return Ok(quote! {
pub enum Field {
#fields
}
pub const fn apply_field(code: u32, field: Field, value: i32) -> u32 {
match field {
#field_match
}
}
});
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_gpr() {
assert_eq!(parse_gpr("r0").unwrap(), (0, ""));
assert_eq!(parse_gpr("r31").unwrap(), (31, ""));
assert_eq!(parse_gpr("1234").unwrap(), (1234, ""));
}
}
struct OpcodeInfo {
code: u32,
args: Vec<String>,
}
fn gen_opcode_from_str(isa: &Isa) -> Result<TokenStream> {
let mut map_builder = phf_codegen::Map::<String>::new();
let mut opcode_map = HashMap::<String, Vec<Option<OpcodeInfo>>>::new();
for opcode in &isa.opcodes {
let mut modifiers = Vec::<Modifier>::with_capacity(opcode.modifiers.len());
for modifier_name in &opcode.modifiers {
modifiers.push(match isa.modifiers.iter().find(|m| m.name == *modifier_name) {
Some(modifier) => modifier.clone(),
None => return Err(Error::from(format!("Modifier {} not found", modifier_name))),
});
}
'outer: for bits in 0..(1 << modifiers.len()) {
let mut suffix = String::new();
let mut set_bits = 0u32;
for (idx, modifier) in modifiers.iter().enumerate() {
if bits & (1 << idx) != 0 {
if set_bits & (1 << modifier.bit) != 0 {
// Incompatible combination
continue 'outer;
}
set_bits |= 1 << modifier.bit;
suffix.push(modifier.suffix);
}
}
let name = format!("{}{}", opcode.name, suffix);
let info = OpcodeInfo { code: opcode.pattern, args: opcode.args.clone() };
match opcode_map.entry(name) {
Entry::Occupied(mut entry) => {
let vec = entry.get_mut();
if vec.len() < opcode.args.len() + 1 {
vec.resize_with(opcode.args.len() + 1, || None);
}
vec[opcode.args.len()] = Some(info);
}
Entry::Vacant(entry) => {
let mut vec = Vec::<Option<OpcodeInfo>>::new();
vec.resize_with(opcode.args.len() + 1, || None);
vec[opcode.args.len()] = Some(info);
entry.insert(vec);
}
}
// println!("Adding opcode {}", name);
// let quoted_name = format!("\"{}\"", name);
// map_builder.entry(name, quoted_name.as_str());
}
}
for mnemonic in &isa.mnemonics {
let opcode = isa.opcodes.iter().find(|o| o.name == mnemonic.opcode).ok_or_else(|| {
Error::from(format!("Opcode {} not found for {}", mnemonic.opcode, mnemonic.name))
})?;
let modifier_names = mnemonic.modifiers.as_ref().unwrap_or(&opcode.modifiers);
let mut modifiers = Vec::<Modifier>::with_capacity(modifier_names.len());
for modifier_name in modifier_names {
modifiers.push(match isa.modifiers.iter().find(|m| m.name == *modifier_name) {
Some(modifier) => modifier.clone(),
None => return Err(Error::from(format!("Modifier {} not found", modifier_name))),
});
}
'outer: for bits in 0..(1 << modifiers.len()) {
let mut suffix = String::new();
let mut set_bits = 0u32;
for (idx, modifier) in modifiers.iter().enumerate() {
if bits & (1 << idx) != 0 {
if set_bits & (1 << modifier.bit) != 0 {
// Incompatible combination
continue 'outer;
}
set_bits |= 1 << modifier.bit;
suffix.push(modifier.suffix);
}
}
let name = format!("{}{}", mnemonic.name, suffix);
let mut code = opcode.pattern;
{
let tokens: TokenStream = mnemonic.condition.parse()?;
let mut iter = tokens.into_iter();
let mut vec = Vec::<ParsedToken>::new();
loop {
match parse_token(&mut iter) {
Some(token) => vec.push(token),
None => break,
}
}
let expr = ExprParser.parse(vec.into_iter()).unwrap();
match apply_expr(code, expr, isa)? {
ExprResult::Int(out) => {
code = out;
}
_ => unreachable!(),
}
}
// for arg in mnemonic.args {
// code = apply_field(code, )
// }
let info = OpcodeInfo { code, args: mnemonic.args.clone() };
match opcode_map.entry(name) {
Entry::Occupied(mut entry) => {
let vec = entry.get_mut();
if vec.len() < mnemonic.args.len() + 1 {
vec.resize_with(mnemonic.args.len() + 1, || None);
}
vec[mnemonic.args.len()] = Some(info);
}
Entry::Vacant(entry) => {
let mut vec = Vec::<Option<OpcodeInfo>>::new();
vec.resize_with(mnemonic.args.len() + 1, || None);
vec[mnemonic.args.len()] = Some(info);
entry.insert(vec);
}
}
// println!("Adding mnemonic {}", name);
// let quoted_name = format!("\"{}\"", name);
// map_builder.entry(name, quoted_name.as_str());
}
}
for (name, infos) in opcode_map {
let opcodes = TokenStream::from_iter(infos.iter().map(|info| {
if let Some(info) = info {
let code = LitInt::new(&format!("0x{:x}", info.code), Span::call_site());
let args = TokenStream::from_iter(info.args.iter().map(|arg| {
let arg_s = arg.split_once('=').map(|(first, _)| first).unwrap_or(arg);
let ident = to_rust_variant(arg_s).unwrap();
quote! { Field::#ident, }
}));
quote! {
Some(OpcodeInfo {
code: #code,
args: &[ #args ],
}),
}
} else {
quote! { None, }
}
}));
map_builder.entry(name, quote! { &[#opcodes] }.to_string().as_str());
}
let map: TokenStream = map_builder.build().to_string().parse()?;
return Ok(quote! {
struct OpcodeInfo {
code: u32,
args: &'static [Field],
}
static OPCODES: phf::Map<&'static str, &'static [Option<OpcodeInfo>]> = #map;
fn opcode_from_str(str: &str) -> Option<&'static [Option<OpcodeInfo>]> {
OPCODES.get(str).map(|x| *x)
}
});
}
fn apply_field(code: u32, field: &Field, value: i32) -> u32 {
let mut val = value as u32;
let val_shift = field.shift_left;
if field.shift_left > 0 {
val = val >> val_shift;
}
// https://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend
if field.signed {
// let mask2 = 1u32 << (self.bits.0.len() - 1);
// let mask2 = LitInt::new(&format!("0x{:x}", mask2), Span::call_site());
// val = quote!((((#val ^ #mask2).wrapping_sub(#mask2)) as i32))
} else {
// val = quote! { #val };
}
if field.split {
val = (((val & 0b11111_00000u32) >> 5u32) | ((val & 0b00000_11111u32) << 5u32)) as u32;
}
let mask = (1u32 << field.bits.0.len()) - 1;
if mask != 0xFFFF_FFFF {
val = val & mask;
}
let shift = 32 - field.bits.0.end;
if shift > 0 {
val = val << shift;
}
code | val
}
#[derive(Debug, PartialEq)]
enum Operator {
BitAnd,
LogicalAnd,
Equal,
}
#[derive(Debug)]
enum ParsedToken {
Ident(Ident),
Group(Vec<ParsedToken>),
Literal(Literal),
Operator(Operator),
}
#[derive(Debug)]
pub enum Expr {
BinOp(Box<Expr>, BinOpKind, Box<Expr>),
// UnOp(UnOpKind, Box<Expr>),
Literal(Literal),
Ident(Ident),
}
#[derive(Debug)]
pub enum BinOpKind {
// &
BitAnd,
// &&
LogicalAnd,
// ==
Eq,
}
#[derive(Debug)]
pub enum UnOp {}
fn parse_token(iter: &mut token_stream::IntoIter) -> Option<ParsedToken> {
match iter.next() {
Some(TokenTree::Group(group)) => {
let mut iter = group.stream().into_iter();
let mut vec = Vec::<ParsedToken>::new();
loop {
match parse_token(&mut iter) {
Some(token) => vec.push(token),
None => break,
}
}
Some(ParsedToken::Group(vec))
}
Some(TokenTree::Punct(mut punct)) => {
let mut str = String::new();
str.push(punct.as_char());
while punct.spacing() == Spacing::Joint {
match iter.next() {
Some(TokenTree::Punct(new_punct)) => {
punct = new_punct;
}
token => panic!("unexpected token {:?}", token),
}
str.push(punct.as_char());
}
Some(ParsedToken::Operator(match str.as_str() {
"&" => Operator::BitAnd,
"&&" => Operator::LogicalAnd,
"==" => Operator::Equal,
op => todo!("operator {}", op),
}))
}
Some(TokenTree::Ident(ident)) => Some(ParsedToken::Ident(ident)),
Some(TokenTree::Literal(literal)) => Some(ParsedToken::Literal(literal)),
None => None,
}
}
struct ExprParser;
impl<I> PrattParser<I> for ExprParser
where I: Iterator<Item = ParsedToken>
{
type Error = pratt::NoError;
type Input = ParsedToken;
type Output = Expr;
fn query(&mut self, tree: &ParsedToken) -> PrattResult<Affix> {
let affix = match tree {
ParsedToken::Operator(Operator::BitAnd) => {
Affix::Infix(Precedence(3), Associativity::Left)
}
ParsedToken::Operator(Operator::Equal) => {
Affix::Infix(Precedence(2), Associativity::Left)
}
ParsedToken::Operator(Operator::LogicalAnd) => {
Affix::Infix(Precedence(1), Associativity::Left)
}
ParsedToken::Group(_) | ParsedToken::Literal(_) | ParsedToken::Ident(_) => {
Affix::Nilfix
}
};
Ok(affix)
}
// Construct a primary expression, e.g. a number
fn primary(&mut self, tree: ParsedToken) -> PrattResult<Expr> {
let expr = match tree {
ParsedToken::Ident(num) => Expr::Ident(num),
ParsedToken::Literal(literal) => Expr::Literal(literal),
ParsedToken::Group(group) => self.parse(&mut group.into_iter()).unwrap(),
_ => unreachable!(),
};
Ok(expr)
}
// Construct a binary infix expression, e.g. 1+1
fn infix(&mut self, lhs: Expr, tree: ParsedToken, rhs: Expr) -> PrattResult<Expr> {
let op = match tree {
ParsedToken::Operator(Operator::BitAnd) => BinOpKind::BitAnd,
ParsedToken::Operator(Operator::LogicalAnd) => BinOpKind::LogicalAnd,
ParsedToken::Operator(Operator::Equal) => BinOpKind::Eq,
_ => unreachable!(),
};
Ok(Expr::BinOp(Box::new(lhs), op, Box::new(rhs)))
}
fn prefix(&mut self, _tree: ParsedToken, _rhs: Expr) -> PrattResult<Expr> { unreachable!() }
fn postfix(&mut self, _lhs: Expr, _tree: ParsedToken) -> PrattResult<Expr> { unreachable!() }
}
#[derive(Debug)]
enum ExprResult {
Int(u32),
Ident(String),
}
fn apply_expr(mut code: u32, expr: Expr, isa: &Isa) -> Result<ExprResult> {
match expr {
Expr::BinOp(lhs, kind, rhs) => match kind {
BinOpKind::BitAnd => match *lhs {
// ignoring rhs
Expr::Ident(ident) => Ok(ExprResult::Ident(ident.to_string())),
other => todo!("BitAnd {:?}", other),
},
BinOpKind::LogicalAnd => {
code = match apply_expr(code, *lhs, isa)? {
ExprResult::Int(code) => code,
_ => unreachable!(),
};
code = match apply_expr(code, *rhs, isa)? {
ExprResult::Int(code) => code,
_ => unreachable!(),
};
Ok(ExprResult::Int(code))
}
BinOpKind::Eq => {
let field_name = match apply_expr(code, *lhs, isa)? {
ExprResult::Ident(ident) => ident,
other => todo!("eq lhs {:?}", other),
};
let field = isa
.fields
.iter()
.find(|field| field.name == field_name)
.ok_or_else(|| Error::from(format!("Field {} not found", field_name)))?;
let value = match apply_expr(code, *rhs, isa)? {
ExprResult::Int(value) => value,
// other => todo!("eq rhs {:?}", other),
_ => return Ok(ExprResult::Int(code))
};
code = apply_field(code, field, value as i32);
Ok(ExprResult::Int(code))
}
},
Expr::Literal(lit) => Ok(ExprResult::Int(LitInt::from(lit).base10_parse()?)),
Expr::Ident(id) => Ok(ExprResult::Ident(id.to_string())),
}
}

View File

@ -1,3 +1,7 @@
#![feature(iterator_try_collect)]
mod asm;
use std::collections::HashMap;
use std::fs::File;
use std::io::Write;
@ -10,6 +14,7 @@ use proc_macro2::{Group, Ident, Literal, Span, TokenStream, TokenTree};
use quote::quote;
use serde::{Deserialize, Deserializer};
use syn::{LitChar, LitInt, LitStr};
use crate::asm::asm_main;
macro_rules! token_stream {
($stream:ident) => {
@ -22,6 +27,10 @@ fn main() {
eprintln!("{}", err);
std::process::exit(1);
}
// if let Err(err) = asm_main() {
// eprintln!("{}", err);
// std::process::exit(1);
// }
}
type Error = Box<dyn std::error::Error>;
@ -238,7 +247,7 @@ pub(crate) struct Mnemonic {
condition: String,
}
#[derive(Deserialize, Default)]
#[derive(Deserialize, Default, Clone)]
#[serde(default)]
pub(crate) struct Modifier {
name: String,
@ -660,7 +669,7 @@ impl Isa {
}
/// Converts the given key into an identifier.
fn to_rust_ident(prefix: &str, key: &str) -> TokenTree {
pub(crate) fn to_rust_ident(prefix: &str, key: &str) -> TokenTree {
TokenTree::Ident(Ident::new(
&(prefix.to_owned() + &key.replace('.', "_")),
Span::call_site(),
@ -668,7 +677,7 @@ fn to_rust_ident(prefix: &str, key: &str) -> TokenTree {
}
/// Converts the given key into an enum variant key.
fn to_rust_variant(key: &str) -> Result<TokenTree> {
pub(crate) fn to_rust_variant(key: &str) -> Result<TokenTree> {
Ok(TokenTree::Ident(Ident::new(
&to_rust_variant_str(key)?,
Span::call_site(),