extern crate proc_macro2;
extern crate syn;
extern crate quote;
use proc_macro2::{Span, TokenStream};
use syn::{
braced,
Fields,
Generics,
Ident,
ItemEnum,
LitInt,
LitStr,
token,
Token,
parenthesized,
parse::{ Parse, ParseStream },
parse_macro_input,
parse_quote,
punctuated::Punctuated,
Result,
Error,
Visibility,
VisPublic
};
use quote::{quote, ToTokens};
enum Variant {
R(LitInt, LitInt, LitInt),
I(LitInt, LitInt),
S(LitInt, LitInt),
B(LitInt, LitInt),
U(LitInt),
J(LitInt),
SH(LitInt, LitInt, LitInt),
}
impl Variant {
fn opcode(&self) -> TokenStream {
match self {
Self::R(opcode, ..) => opcode.into_token_stream(),
Self::I(opcode, ..) => opcode.into_token_stream(),
Self::S(opcode, ..) => opcode.into_token_stream(),
Self::B(opcode, ..) => opcode.into_token_stream(),
Self::U(opcode) => opcode.into_token_stream(),
Self::J(opcode) => opcode.into_token_stream(),
Self::SH(opcode, ..) => opcode.into_token_stream(),
}
}
fn funct3(&self) -> TokenStream {
match self {
Self::R(_, funct3, ..) => funct3.into_token_stream(),
Self::I(_, funct3) => funct3.into_token_stream(),
Self::S(_, funct3) => funct3.into_token_stream(),
Self::B(_, funct3) => funct3.into_token_stream(),
Self::SH(_, funct3, ..) => funct3.into_token_stream(),
_ => Token!{_}(Span::call_site()).into_token_stream()
}
}
fn funct7(&self) -> TokenStream {
match self {
Self::R(_, _, funct7) => funct7.into_token_stream(),
_ => Token!{_}(Span::call_site()).into_token_stream()
}
}
fn instruction_len(&self) -> usize {
match self {
Self::R(..) | Self::I(..) | Self::S(..) | Self::B(..) | Self::U(..) | Self::J(..) | Self::SH(..) => 4,
}
}
fn match_pattern(&self) -> TokenStream {
let opcode = self.opcode();
let funct3 = self.funct3();
let funct7 = self.funct7();
match self {
Self::SH(_, _, arith) => quote!{(#opcode, #funct3, funct7) if #arith << 2 == funct7 & 0x3C },
_ => quote!{(#opcode, #funct3, #funct7)}
}
}
fn build(&self) -> TokenStream {
match self {
Self::R(..) => quote!{{
destination: destination!(instruction),
source1: source1!(instruction),
source2: source2!(instruction)
}},
Self::I(..) => quote!{{
destination: destination!(instruction),
source: source1!(instruction),
immediate: immediate_i!(instruction)
}},
Self::S(..) => quote!{{
source1: source1!(instruction),
source2: source2!(instruction),
immediate: immediate_s!(instruction)
}},
Self::B(..) => quote!{{
source1: source1!(instruction),
source2: source2!(instruction),
immediate: immediate_b!(instruction)
}},
Self::U(..) => quote!{{
destination: destination!(instruction),
immediate: immediate_u!(instruction)
}},
Self::J(..) => quote!{{
destination: destination!(instruction),
immediate: immediate_j!(instruction)
}},
Self::SH(..) => quote!{{
destination: destination!(instruction),
source: source1!(instruction),
amount: (immediate_i!(instruction) & 0x7F) as usize
}},
}
}
fn encode_pattern(&self) -> TokenStream {
match self {
Self::R(..) => quote!{{
destination,
source1,
source2
}},
Self::I(..) => quote!{{
destination,
source,
immediate
}},
Self::S(..) => quote!{{
source1,
source2,
immediate
}},
Self::B(..) => quote!{{
source1,
source2,
immediate
}},
Self::U(..) => quote!{{
destination,
immediate
}},
Self::J(..) => quote!{{
destination,
immediate
}},
Self::SH(..) => quote!{{
destination,
source,
amount
}},
}
}
fn encode(&self) -> TokenStream {
match self {
Self::R(opcode, funct3, funct7) => quote!{MachineInstruction::Normal(
[
#opcode | ((destination as u8 & 0x1F) << 7),
((destination as u8 & 0x1F) >> 1) | ((#funct3 & 0b111) << 4) | ((source1 as u8 & 0x1F) << 7),
((source1 as u8 & 0x1F) >> 1) | ((source2 as u8 & 0x1F) << 4),
((source2 as u8 & 0x1F) >> 4) | ((#funct7 & 0b111_1111) << 1)
]
)},
Self::I(opcode, funct3) => quote!{MachineInstruction::Normal(
[
#opcode | ((destination as u8 & 0x1F) << 7),
((destination as u8 & 0x1F) >> 1) | ((#funct3 & 0b111) << 4) | ((source as u8 & 0x1F) << 7),
((source as u8 & 0x1F) >> 1) | ((immediate as u8 & 0x0F) << 4),
((immediate >> 4) as u8)
]
)},
Self::S(opcode, funct3) => quote!{MachineInstruction::Normal(
[
#opcode | ((immediate as u8 & 0x01) << 7),
((immediate as u8 & 0x1E) >> 1) | ((#funct3 & 0b111) << 4) | ((source1 as u8 & 0x1F) << 7),
((source1 as u8 & 0x1F) >> 1) | ((source2 as u8 & 0x1F) << 4),
((source2 as u8 & 0x1F) >> 4) | ((immediate & 0xFE0) >> 4) as u8
]
)},
Self::B(opcode, funct3) => quote!{MachineInstruction::Normal(
[
#opcode | ((immediate >> 4) as u8 & 0x80),
((immediate as u8 & 0x1E) >> 1) | ((#funct3 & 0b111) << 4) | ((source1 as u8 & 0x1F) << 7),
((source1 as u8 & 0x1F) >> 1) | ((source2 as u8 & 0x1F) << 4),
((source2 as u8 & 0x1F) >> 4) | (((immediate & 0x7E0) >> 4) as u8) | (((immediate & 0x1000) >> 5) as u8)
]
)},
Self::U(opcode) => quote!{MachineInstruction::Normal(
[
#opcode | ((destination as u8 & 0x1F) << 7),
((destination as u8 & 0x1F) >> 1) | ((immediate >> 8) as u8 & 0xF0),
((immediate >> 16) as u8),
((immediate >> 24) as u8)
]
)},
Self::J(opcode) => quote!{MachineInstruction::Normal(
[
#opcode | ((destination as u8 & 0x1F) << 7),
((destination as u8 & 0x1F) >> 1) | ((immediate >> 8) as u8 & 0xF0),
((immediate >> 16) as u8 & 0x0F) | ((immediate >> 7) as u8 & 0x10) | ((immediate << 4) as u8 & 0xE0),
((immediate >> 4) as u8 & 0x7F) | ((immediate >> 12) as u8 & 0x80)
]
)},
Self::SH(opcode, funct3, arith) => quote!{MachineInstruction::Normal(
[
#opcode | ((destination as u8 & 0x1F) << 7),
((destination as u8 & 0x1F) >> 1) | ((#funct3 & 0b111) << 4) | ((source as u8 & 0x1F) << 7),
((source as u8 & 0x1F) >> 1) | ((amount as u8 & 0x1F) << 4),
((amount as u8 & 0x7F) >> 4) | ((#arith & 0b11111) << 1)
]
)},
}
}
}
impl Parse for Variant {
fn parse(input: ParseStream) -> Result<Self> {
let ident: Ident = input.parse()?;
let bit_pattern;
if ident == "R" {
let _: token::Paren = parenthesized!(bit_pattern in input);
Ok(Self::R(
bit_pattern.parse()?,
{let _: Token!{,} = bit_pattern.parse()?; bit_pattern.parse()?},
{let _: Token!{,} = bit_pattern.parse()?; bit_pattern.parse()?}
))
} else if ident == "I" {
let _: token::Paren = parenthesized!(bit_pattern in input);
Ok(Self::I(
bit_pattern.parse()?,
{let _: Token!{,} = bit_pattern.parse()?; bit_pattern.parse()?}
))
} else if ident == "S" {
let _: token::Paren = parenthesized!(bit_pattern in input);
Ok(Self::S(
bit_pattern.parse()?,
{let _: Token!{,} = bit_pattern.parse()?; bit_pattern.parse()?}
))
} else if ident == "B" {
let _: token::Paren = parenthesized!(bit_pattern in input);
Ok(Self::B(
bit_pattern.parse()?,
{let _: Token!{,} = bit_pattern.parse()?; bit_pattern.parse()?}
))
} else if ident == "U" {
let _: token::Paren = parenthesized!(bit_pattern in input);
Ok(Self::U(bit_pattern.parse()?))
} else if ident == "J" {
let _: token::Paren = parenthesized!(bit_pattern in input);
Ok(Self::J(bit_pattern.parse()?))
} else if ident == "SH" {
let _: token::Paren = parenthesized!(bit_pattern in input);
Ok(Self::SH(
bit_pattern.parse()?,
{let _: Token!{,} = bit_pattern.parse()?; bit_pattern.parse()?},
{let _: Token!{,} = bit_pattern.parse()?; bit_pattern.parse()?}
))
} else {
Err(Error::new_spanned(ident, "Invalid variant. Expected one of R, I, S, B, U or J"))
}
}
}
#[allow(dead_code)]
struct Instruction {
ident: Ident,
arrow: Token!{->},
variant: Variant,
brace: token::Brace,
human_name: LitStr,
}
impl Instruction {
fn any_pattern(&self) -> TokenStream {
let ident = self.ident.clone();
quote!{Self::#ident {..}}
}
fn build(&self) -> TokenStream {
let pattern = self.variant.build();
let ident = self.ident.clone();
quote!{Self::#ident #pattern}
}
fn encode_pattern(&self) -> TokenStream {
let pattern = self.variant.encode_pattern();
let ident = self.ident.clone();
quote!{Self::#ident #pattern}
}
}
impl Parse for Instruction {
fn parse(input: ParseStream) -> Result<Self> {
let content;
Ok(
Self {
ident: input.parse()?,
arrow: input.parse()?,
variant: input.parse()?,
brace: braced!(content in input),
human_name: content.parse()?,
}
)
}
}
struct Instructions(Punctuated<Instruction, Token!{,}>);
impl Parse for Instructions {
fn parse(input: ParseStream) -> Result<Self> {
Ok(
Self(Punctuated::parse_terminated(input)?)
)
}
}
#[proc_macro]
pub fn base_instructions(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let Instructions(instructions) = parse_macro_input!(input as Instructions);
let span = Span::call_site();
let mut instructions_enum = ItemEnum {
attrs: vec![
parse_quote!{#[derive(Copy, Clone, Debug)]},
parse_quote!{#[allow(clippy::unreadable_literal)]}
],
vis: Visibility::Public(VisPublic {
pub_token: Token!{pub}(span)
}),
enum_token: Token!{enum}(span),
ident: Ident::new("BaseInstruction", span),
generics: Generics {
lt_token: None,
params: Punctuated::new(),
gt_token: None,
where_clause: None
},
brace_token: token::Brace(span),
variants: Punctuated::new()
};
for instruction in instructions.iter() {
instructions_enum.variants.push(syn::Variant {
attrs: vec![],
ident: instruction.ident.clone(),
fields: Fields::Named(match instruction.variant {
Variant::R(..) =>
parse_quote!{{
destination: usize,
source1: usize,
source2: usize,
}},
Variant::I(..) =>
parse_quote!{{
destination: usize,
source: usize,
immediate: i32,
}},
Variant::S(..) =>
parse_quote!{{
source1: usize,
source2: usize,
immediate: i32,
}},
Variant::B(..) =>
parse_quote!{{
source1: usize,
source2: usize,
immediate: i32,
}},
Variant::U(..) =>
parse_quote!{{
destination: usize,
immediate: i32,
}},
Variant::J(..) =>
parse_quote!{{
destination: usize,
immediate: i32,
}},
Variant::SH(..) =>
parse_quote!{{
destination: usize,
source: usize,
amount: usize,
}},
}),
discriminant: None
})
}
let mut tokens = instructions_enum.into_token_stream();
let mnemonic_pattern = instructions.iter().map(|instruction| instruction.any_pattern());
let name_pattern = mnemonic_pattern.clone();
let mnemonic = instructions.iter().map(|instruction| format!("{}", instruction.ident));
let name = instructions.iter().map(|instruction| instruction.human_name.clone());
let decode_pattern = instructions.iter().map(|instruction| instruction.variant.match_pattern());
let decode_variant = instructions.iter().map(|instruction| instruction.build());
let decode_len = instructions.iter().map(|instruction| instruction.variant.instruction_len());
let encode_pattern = instructions.iter().map(|instruction| instruction.encode_pattern());
let encode_variant = instructions.iter().map(|instruction| instruction.variant.encode());
tokens.extend(quote!{
impl BaseInstruction {
pub fn decode(mut instruction: &mut [u8]) -> Option<(Self, &mut [u8])> {
let opcode = instruction[0] & 0x7F;
let funct3 = (instruction[1] & 0x70) >> 4;
let funct7 = (instruction[3] & 0xFE) >> 1;
match (opcode, funct3, funct7) {
#(
#decode_pattern => Some((#decode_variant, &mut instruction[#decode_len..])),
)*
_ => None
}
}
pub fn encode(self) -> MachineInstruction {
match self {
#(
#encode_pattern => #encode_variant,
)*
}
}
pub fn mnemonic(&self) -> &'static str {
match self {
#(
#mnemonic_pattern => #mnemonic
),*
}
}
pub fn name(&self) -> &'static str {
match self {
#(
#name_pattern => #name
),*
}
}
}
});
proc_macro::TokenStream::from(tokens)
}