1#![cfg_attr(feature = "filelocal", feature(proc_macro_span))]
2extern crate proc_macro;
13
14use syn::parse;
15use syn::{Token, parse_macro_input};
16use proc_macro2::{TokenTree, TokenStream};
17use quote::quote;
18use proc_macro_error2::proc_macro_error;
19
20use std::collections::HashMap;
21
22#[cfg(feature = "filelocal")]
23use std::sync::{MutexGuard, Mutex};
24#[cfg(feature = "filelocal")]
25use std::path::PathBuf;
26#[cfg(any(feature = "filelocal", feature = "dynasm_opmap", feature = "dynasm_extract"))]
27use proc_macro2::Span;
28
29mod common;
31mod arch;
33mod directive;
35mod serialize;
37mod parse_helpers;
39
40#[proc_macro]
43#[proc_macro_error]
44pub fn dynasm(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
45 let dynasm = parse_macro_input!(tokens as Dynasm);
48
49 serialize::serialize(&dynasm.target, dynasm.stmts).into()
51}
52
53#[proc_macro]
58#[proc_macro_error]
59pub fn dynasm_backwards(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
60 let dynasm = parse_macro_input!(tokens as Dynasm);
63
64 let stmts = serialize::invert(dynasm.stmts);
66
67 serialize::serialize(&dynasm.target, stmts).into()
69}
70
71struct Dynasm {
75 target: TokenTree,
76 stmts: Vec<common::Stmt>
77}
78
79impl parse::Parse for Dynasm {
83 fn parse(input: parse::ParseStream) -> parse::Result<Self> {
84
85 let target: syn::Expr = input.parse()?;
87 let target = common::delimited(target);
89
90 let mut provider = ContextProvider::new();
92 let invocation_context = provider.get_context_mut();
93
94 let mut stmts = Vec::new();
96
97 while !input.is_empty() {
99 let _: Token![;] = input.parse()?;
100
101 if input.peek(Token![;]) {
103 let _: Token![;] = input.parse()?;
104
105 let mut buffer = TokenStream::new();
107 while !(input.is_empty() || input.peek(Token![;])) {
108 buffer.extend(std::iter::once(input.parse::<TokenTree>()?));
109 }
110 buffer.extend(quote! { ; } );
112
113 if !buffer.is_empty() {
114 let stmt: syn::Stmt = syn::parse2(buffer)?;
116 stmts.push(common::Stmt::Stmt(quote!{ #stmt }));
117 }
118 continue;
119 }
120
121 if input.peek(Token![->]) {
123 let _: Token![->] = input.parse()?;
124
125 let name: syn::Ident = input.parse()?;
126 let _: Token![:] = input.parse()?;
127
128 stmts.push(common::Stmt::GlobalLabel(name));
129 continue;
130 }
131
132 if input.peek(Token![=>]) {
134 let _: Token![=>] = input.parse()?;
135
136 let expr: syn::Expr = input.parse()?;
137
138 stmts.push(common::Stmt::DynamicLabel(common::delimited(expr)));
139 continue;
140 }
141
142 if input.peek(syn::Ident) && input.peek2(Token![:]) {
144
145 let name: syn::Ident = input.parse()?;
146 let _: Token![:] = input.parse()?;
147
148 stmts.push(common::Stmt::LocalLabel(name));
149 continue;
150 }
151
152
153 if input.peek(Token![.]) {
155 let _: Token![.] = input.parse()?;
156
157 directive::evaluate_directive(invocation_context, &mut stmts, input)?;
158 } else {
159 let mut state = State {
162 stmts: &mut stmts,
163 invocation_context: &*invocation_context,
164 };
165 invocation_context.current_arch.compile_instruction(&mut state, input)?;
166 }
167
168 }
169
170 Ok(Dynasm {
171 target,
172 stmts
173 })
174 }
175}
176
177#[cfg(feature = "dynasm_opmap")]
180#[proc_macro]
181pub fn dynasm_opmap(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
182
183 let opmap = parse_macro_input!(tokens as DynasmOpmap);
185
186 let mut s = String::new();
187 s.push_str("% Instruction Reference\n\n");
188
189 s.push_str(&match opmap.arch.as_str() {
190 "x64" | "x86" => arch::x64::create_opmap(),
191 "aarch64" => arch::aarch64::create_opmap(),
192 "riscv" => arch::riscv::create_opmap(),
193 x => panic!("Unknown architecture {}", x)
194 });
195
196 let token = quote::quote_spanned! { Span::mixed_site()=>
197 #s
198 };
199 token.into()
200}
201
202#[cfg(feature = "dynasm_extract")]
205#[proc_macro]
206pub fn dynasm_extract(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
207
208 let opmap = parse_macro_input!(tokens as DynasmOpmap);
210
211 let s = match opmap.arch.as_str() {
212 "x64" | "x86" => "UNIMPLEMENTED".into(),
213 "aarch64" => arch::aarch64::extract_opmap(),
214 "riscv" => arch::riscv::extract_opmap(),
215 x => panic!("Unknown architecture {}", x)
216 };
217
218 let token = quote::quote_spanned! { Span::mixed_site()=>
219 #s
220 };
221 token.into()
222}
223
224#[cfg(any(feature="dynasm_extract", feature="dynasm_opmap"))]
227struct DynasmOpmap {
228 pub arch: String
229}
230
231#[cfg(any(feature="dynasm_extract", feature="dynasm_opmap"))]
234impl parse::Parse for DynasmOpmap {
235 fn parse(input: parse::ParseStream) -> parse::Result<Self> {
236 let arch: syn::Ident = input.parse()?;
237
238 Ok(DynasmOpmap {
239 arch: arch.to_string()
240 })
241 }
242}
243
244struct State<'a> {
246 pub stmts: &'a mut Vec<common::Stmt>,
247 pub invocation_context: &'a DynasmContext,
248}
249
250struct DynasmContext {
254 pub current_arch: Box<dyn arch::Arch>,
255 pub aliases: HashMap<String, String>,
256}
257
258impl DynasmContext {
259 fn new() -> DynasmContext {
260 DynasmContext {
261 current_arch: arch::from_str(arch::CURRENT_ARCH).expect("Invalid default architecture"),
262 aliases: HashMap::new()
263 }
264 }
265}
266
267#[cfg(not(feature = "filelocal"))]
269struct ContextProvider {
270 context: DynasmContext
271}
272
273#[cfg(not(feature = "filelocal"))]
274impl ContextProvider {
275 pub fn new() -> ContextProvider {
276 ContextProvider {
277 context: DynasmContext::new()
278 }
279 }
280
281 pub fn get_context_mut(&mut self) -> &mut DynasmContext {
282 &mut self.context
283 }
284}
285
286#[cfg(feature = "filelocal")]
288struct ContextProvider {
289 guard: MutexGuard<'static, HashMap<PathBuf, DynasmContext>>
290}
291
292#[cfg(feature = "filelocal")]
293impl ContextProvider {
294 pub fn new() -> ContextProvider {
295 ContextProvider {
296 guard: CONTEXT_STORAGE.lock().unwrap()
297 }
298 }
299
300 pub fn get_context_mut(&mut self) -> &mut DynasmContext {
301 let span = Span::call_site().unstable();
303
304 let id = span.source_file().path();
306
307 self.guard.entry(id).or_insert_with(DynasmContext::new)
308 }
309}
310
311#[cfg(feature = "filelocal")]
312lazy_static::lazy_static! {
313 static ref CONTEXT_STORAGE: Mutex<HashMap<PathBuf, DynasmContext>> = Mutex::new(HashMap::new());
314}