deb5dd9a57fea25663596025edbfc9c72ec65d75
[power-instruction-analyzer.git] / power-instruction-analyzer-proc-macro / src / lib.rs
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 // See Notices.txt for copyright information
3
4 use proc_macro2::{Ident, Span, TokenStream};
5 use quote::{quote, ToTokens, TokenStreamExt};
6 use std::fmt;
7 use syn::{
8 braced, bracketed, parenthesized,
9 parse::{Parse, ParseStream},
10 parse_macro_input,
11 punctuated::Punctuated,
12 Attribute, Error, ItemFn, LitStr, Token,
13 };
14
15 macro_rules! valid_enumerants_as_string {
16 ($enumerant:ident) => {
17 concat!("`", stringify!($enumerant), "`")
18 };
19 ($enumerant1:ident, $enumerant2:ident) => {
20 concat!("`", stringify!($enumerant1), "` and `", stringify!($enumerant2), "`")
21 };
22 ($($enumerant:ident),+) => {
23 valid_enumerants_as_string!((), ($($enumerant),+))
24 };
25 (($first_enumerant:ident, $($enumerant:ident,)+), ($last_enumerant:ident)) => {
26 concat!(
27 "`",
28 stringify!($first_enumerant),
29 $(
30 "`, `",
31 stringify!($enumerant),
32 )+
33 "`, and `",
34 stringify!($last_enumerant),
35 "`"
36 )
37 };
38 (($($enumerants:ident,)*), ($next_enumerant:ident, $($rest:ident),*)) => {
39 valid_enumerants_as_string!(($($enumerants,)* $next_enumerant,), ($($rest),*))
40 };
41 () => {
42 "<nothing>"
43 };
44 }
45
46 macro_rules! ident_enum {
47 (
48 #[parse_error_msg = $parse_error_msg:literal]
49 enum $enum_name:ident {
50 $(
51 $enumerant:ident,
52 )*
53 }
54 ) => {
55 #[derive(Copy, Clone, Eq, PartialEq, Hash)]
56 enum $enum_name<T = Span> {
57 $(
58 $enumerant(T),
59 )*
60 }
61
62 impl<T> fmt::Debug for $enum_name<T> {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 f.write_str(self.name())
65 }
66 }
67
68 impl<T> $enum_name<T> {
69 fn enumerant(&self) -> $enum_name<()> {
70 match self {
71 $(
72 $enum_name::$enumerant(_) => $enum_name::$enumerant(()),
73 )*
74 }
75 }
76 fn name(&self) -> &'static str {
77 match self {
78 $(
79 $enum_name::$enumerant(_) => stringify!($enumerant),
80 )*
81 }
82 }
83 }
84
85 impl $enum_name {
86 fn into_ident(self) -> Ident {
87 match self {
88 $(
89 $enum_name::$enumerant(span) => Ident::new(stringify!($enumerant), span),
90 )*
91 }
92 }
93 }
94
95 impl ToTokens for $enum_name<Span> {
96 fn to_tokens(&self, tokens: &mut TokenStream) {
97 tokens.append(self.clone().into_ident());
98 }
99 }
100
101 impl Parse for $enum_name<Span> {
102 fn parse(input: ParseStream) -> syn::Result<Self> {
103 let id: Ident = input.parse()?;
104 $(
105 if id == stringify!($enumerant) {
106 return Ok($enum_name::$enumerant(id.span()));
107 }
108 )*
109 Err(Error::new_spanned(
110 id,
111 concat!(
112 $parse_error_msg,
113 ": valid values are: ",
114 valid_enumerants_as_string!($($enumerant),*)
115 )
116 ))
117 }
118 }
119 };
120 }
121
122 ident_enum! {
123 #[parse_error_msg = "unknown instruction input"]
124 enum InstructionInput {
125 Ra,
126 Rb,
127 Rc,
128 Carry,
129 }
130 }
131
132 ident_enum! {
133 #[parse_error_msg = "unknown instruction output"]
134 enum InstructionOutput {
135 Rt,
136 Carry,
137 Overflow,
138 CR0,
139 }
140 }
141
142 #[derive(Debug)]
143 struct Instruction {
144 enumerant: Ident,
145 fn_name: Ident,
146 inputs: Punctuated<InstructionInput, Token!(,)>,
147 outputs: Punctuated<InstructionOutput, Token!(,)>,
148 instruction_name: LitStr,
149 }
150
151 impl Parse for Instruction {
152 fn parse(input: ParseStream) -> syn::Result<Self> {
153 input.parse::<Token!(#)>()?;
154 let enumerant_attr_tokens;
155 bracketed!(enumerant_attr_tokens in input);
156 let enumerant_name: Ident = enumerant_attr_tokens.parse()?;
157 if enumerant_name != "enumerant" {
158 return Err(Error::new_spanned(
159 enumerant_name,
160 "expected `#[enumerant = ...]` attribute",
161 ));
162 }
163 enumerant_attr_tokens.parse::<Token!(=)>()?;
164 let enumerant: Ident = enumerant_attr_tokens.parse()?;
165 input.parse::<Token!(fn)>()?;
166 let fn_name: Ident = input.parse()?;
167 let inputs_tokens;
168 parenthesized!(inputs_tokens in input);
169 let inputs = inputs_tokens.parse_terminated(InstructionInput::parse)?;
170 input.parse::<Token!(->)>()?;
171 let outputs_tokens;
172 parenthesized!(outputs_tokens in input);
173 let outputs = outputs_tokens.parse_terminated(InstructionOutput::parse)?;
174 let body_tokens;
175 braced!(body_tokens in input);
176 let instruction_name: LitStr = body_tokens.parse()?;
177 Ok(Self {
178 enumerant,
179 fn_name,
180 inputs,
181 outputs,
182 instruction_name,
183 })
184 }
185 }
186
187 impl Instruction {
188 fn map_input_registers(&self) -> syn::Result<Vec<TokenStream>> {
189 todo!()
190 }
191 fn to_assembly_text(&self) -> syn::Result<String> {
192 let mut retval = String::new();
193 retval += "mfxer $1\n\
194 and $1, $1, $7\n\
195 mtxer $1\n";
196 todo!("map_instr_asm_args!([$($args)*], [$($results)*], []),");
197 retval += "\n\
198 mfxer $1\n\
199 mfcr $2\n";
200 Ok(retval)
201 }
202 fn to_native_fn_tokens(&self) -> syn::Result<TokenStream> {
203 let Instruction {
204 enumerant,
205 fn_name,
206 inputs,
207 outputs,
208 instruction_name,
209 } = self;
210 let assembly_text = self.to_assembly_text()?;
211 let mut handle_inputs = Vec::<TokenStream>::new();
212 unimplemented!("fill handle_inputs");
213 let mut handle_outputs = Vec::<TokenStream>::new();
214 unimplemented!(
215 "fill handle_outputs\
216 map_instr_results!(rt, xer, cr, retval, [$($results)*]);"
217 );
218 Ok(quote! {
219 pub fn #fn_name(inputs: InstructionInput) -> InstructionResult {
220 #![allow(unused_variables, unused_assignments)]
221 let InstructionInput {
222 ra,
223 rb,
224 rc,
225 carry,
226 } = inputs;
227 let rt: u64;
228 let xer: u64;
229 let cr: u32;
230 #(#handle_inputs)*
231 unsafe {
232 llvm_asm!(
233 #assembly_text
234 : "=&b"(rt), "=&b"(xer), "=&b"(cr)
235 : "b"(ra), "b"(rb), "b"(rc), "b"(0u64), "b"(!0x8000_0000u64)
236 : "xer", "cr");
237 }
238 let mut retval = InstructionOutput::default();
239 #(#handle_outputs)*
240 retval
241 }
242 })
243 }
244 }
245
246 #[derive(Debug)]
247 struct Instructions {
248 instructions: Vec<Instruction>,
249 }
250
251 impl Instructions {
252 fn to_tokens(&self) -> syn::Result<TokenStream> {
253 let mut fn_names = Vec::new();
254 let mut instr_enumerants = Vec::new();
255 let mut get_native_fn_match_cases = Vec::new();
256 let mut get_model_fn_match_cases = Vec::new();
257 let mut get_used_input_registers_match_cases = Vec::new();
258 let mut name_match_cases = Vec::new();
259 let mut enumerants = Vec::new();
260 let mut native_fn_tokens = Vec::new();
261 for instruction in &self.instructions {
262 let Instruction {
263 enumerant,
264 fn_name,
265 inputs,
266 outputs,
267 instruction_name,
268 } = instruction;
269 fn_names.push(fn_name);
270 enumerants.push(enumerant);
271 instr_enumerants.push(quote! {
272 #[serde(rename = #instruction_name)]
273 #enumerant,
274 });
275 get_native_fn_match_cases.push(quote! {
276 Self::#enumerant => native_instrs::#fn_name,
277 });
278 get_model_fn_match_cases.push(quote! {
279 Self::#enumerant => instr_models::#fn_name,
280 });
281 let mapped_input_registers = instruction.map_input_registers()?;
282 get_used_input_registers_match_cases.push(quote! {
283 Self::#enumerant => &[#(#mapped_input_registers),*],
284 });
285 name_match_cases.push(quote! {
286 Self::#enumerant => #instruction_name,
287 });
288 native_fn_tokens.push(instruction.to_native_fn_tokens()?);
289 }
290 Ok(quote! {
291 #[cfg(feature = "python")]
292 macro_rules! wrap_all_instr_fns {
293 ($m:ident) => {
294 wrap_instr_fns! {
295 #![pymodule($m)]
296
297 #(fn #fn_names(inputs: InstructionInput) -> InstructionOutput;)*
298 }
299 };
300 }
301
302 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
303 pub enum Instr {
304 #(#instr_enumerants)*
305 }
306
307 impl Instr {
308 #[cfg(feature = "native_instrs")]
309 pub fn get_native_fn(self) -> fn(InstructionInput) -> InstructionOutput {
310 match self {
311 #(#get_native_fn_match_cases)*
312 }
313 }
314 pub fn get_model_fn(self) -> fn(InstructionInput) -> InstructionOutput {
315 match self {
316 #(#get_model_fn_match_cases)*
317 }
318 }
319 pub fn get_used_input_registers(self) -> &'static [InstructionInputRegister] {
320 match self {
321 #(#get_used_input_registers_match_cases)*
322 }
323 }
324 pub fn name(self) -> &'static str {
325 match self {
326 #(#name_match_cases)*
327 }
328 }
329 pub const VALUES: &'static [Self] = &[
330 #(Self::#enumerants,)*
331 ];
332 }
333
334 #[cfg(feature = "native_instrs")]
335 pub mod native_instrs {
336 use super::*;
337
338 #(#native_fn_tokens)*
339 }
340 })
341 }
342 }
343
344 impl Parse for Instructions {
345 fn parse(input: ParseStream) -> syn::Result<Self> {
346 let mut instructions = Vec::new();
347 while !input.is_empty() {
348 instructions.push(input.parse()?);
349 }
350 Ok(Self { instructions })
351 }
352 }
353
354 #[proc_macro]
355 pub fn instructions(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
356 let input = parse_macro_input!(input as Instructions);
357 match input.to_tokens() {
358 Ok(retval) => retval,
359 Err(err) => err.to_compile_error(),
360 }
361 .into()
362 }