libvex_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::{format_ident, quote, quote_spanned};
3use syn::{braced, parenthesized, parse_macro_input, token, Ident, LitInt, Result, Token};
4use syn::parse::{Parse, ParseStream};
5use syn::punctuated::Punctuated;
6use syn::token::Brace;
7
8
9struct ImportArgs {
10    arch: Ident,
11    _arrow: Token![=>],
12    _brace: Brace,
13    items: Punctuated<Ident, Token![,]>,
14}
15
16impl Parse for ImportArgs {
17    fn parse(input: ParseStream) -> Result<Self> {
18        let items;
19        Ok(Self {
20            arch: input.parse()?,
21            _arrow: input.parse()?,
22            _brace: braced!(items in input),
23            items: items.parse_terminated(Ident::parse)?,
24        })
25    }
26}
27
28
29#[proc_macro]
30pub fn import_offsets(item: TokenStream) -> TokenStream {
31    let offsets = parse_macro_input!(item as ImportArgs);
32    let mut output = quote!(
33        use vex_sys::Int;
34    );
35    for reg in &offsets.items {
36        // switch order of arguments to make the span of the output depend on the register.
37        let offset = format_ident!("OFFSET_{1}_{0}", reg, offsets.arch);
38        output = quote!(
39            #output
40            pub const #reg: Int = vex_sys::#offset as Int;
41        );
42    }
43    // Some arches (MIPS) have lowercase register names.
44    quote!(
45        #[allow(non_upper_case_globals)]
46        pub mod offset {
47            #output
48        }
49    ).into()
50}
51
52#[proc_macro]
53pub fn import_hwcaps(item: TokenStream) -> TokenStream {
54    let hwcaps = parse_macro_input!(item as ImportArgs);
55    let arch = hwcaps.arch.to_string().to_uppercase();
56    let mut output = quote!();
57    for hwcap in &hwcaps.items {
58        let offset = format_ident!("VEX_HWCAPS_{}_{}", arch, hwcap);
59        output = quote!(
60            #output
61            pub use vex_sys::#offset as #hwcap;
62        );
63    }
64    quote!(
65        pub mod hwcap {
66            #output
67        }
68    ).into()
69}
70
71struct TypeEnv {
72    tmps: Vec<(Ident, Token![:], Ident)>,
73}
74
75impl Parse for TypeEnv {
76    fn parse(input: ParseStream) -> Result<Self> {
77        let mut tmps = Vec::new();
78        while input.peek(Ident) && input.peek2(Token![:]) {
79            tmps.push((input.parse()?, input.parse()?, input.parse()?));
80        }
81        Ok(TypeEnv {
82            tmps,
83        })
84    }
85}
86
87mod kw {
88    syn::custom_keyword!(IR); // currently only used for IR-NoOp
89    syn::custom_keyword!(NoOp);
90    syn::custom_keyword!(IMark);
91    syn::custom_keyword!(AbiHint);
92    syn::custom_keyword!(CCall);
93    syn::custom_keyword!(PUT);
94    syn::custom_keyword!(GET);
95    syn::custom_keyword!(STbe);
96    syn::custom_keyword!(STle);
97    syn::custom_keyword!(LDbe);
98    syn::custom_keyword!(LDle);
99    syn::custom_keyword!(exit);
100}
101
102enum LoadEndness {
103    Big(kw::LDbe),
104    Little(kw::LDle),
105}
106
107enum StoreEndness {
108    Big(kw::STbe),
109    Little(kw::STle),
110}
111
112impl quote::ToTokens for LoadEndness {
113    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
114        tokens.extend(match self {
115            Self::Big(b) => quote_spanned!(b.span => IREndness::Iend_BE),
116            Self::Little(l) => quote_spanned!(l.span => IREndness::Iend_LE),
117        })
118    }
119}
120
121impl quote::ToTokens for StoreEndness {
122    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
123        tokens.extend(match self {
124            Self::Big(b) => quote_spanned!(b.span => IREndness::Iend_BE),
125            Self::Little(l) => quote_spanned!(l.span => IREndness::Iend_LE),
126        })
127    }
128}
129
130impl Parse for LoadEndness {
131    fn parse(input: ParseStream) -> Result<Self> {
132        let lookahead = input.lookahead1();
133        if lookahead.peek(kw::LDbe) {
134            input.parse().map(Self::Big)
135        } else if lookahead.peek(kw::LDle) {
136            input.parse().map(Self::Little)
137        } else {
138            Err(lookahead.error())
139        }
140    }
141}
142
143impl Parse for StoreEndness {
144    fn parse(input: ParseStream) -> Result<Self> {
145        let lookahead = input.lookahead1();
146        if lookahead.peek(kw::STbe) {
147            input.parse().map(Self::Big)
148        } else if lookahead.peek(kw::STle) {
149            input.parse().map(Self::Little)
150        } else {
151            Err(lookahead.error())
152        }
153    }
154}
155
156enum Expr {
157    Get(ExprGet),
158    Op(ExprOp),
159    Load(ExprLoad),
160    Const(ExprConst),
161    RdTmp(Ident),
162    CCall,
163}
164
165struct ExprGet {
166    _get: kw::GET,
167    _colon: Token![:],
168    size: Ident,
169    _paren: token::Paren,
170    offset: LitInt,
171}
172
173struct ExprOp {
174    op: Ident,
175    _paren: token::Paren,
176    args: Punctuated<Expr, Token![,]>,
177}
178
179struct ExprLoad {
180    end: LoadEndness,
181    _colon: Token![:],
182    ty: Ident,
183    _paren: token::Paren,
184    addr: Box<Expr>,
185}
186
187struct ExprConst {
188    co: LitInt,
189    _colon: Token![:],
190    ty: Ident,
191}
192
193impl Parse for Expr {
194    fn parse(input: ParseStream) -> Result<Self> {
195        let lookahead = input.lookahead1();
196        if lookahead.peek(kw::GET) {
197            input.parse().map(Self::Get)
198        } else if lookahead.peek(kw::LDle) || lookahead.peek(kw::LDbe) {
199            input.parse().map(Self::Load)
200        } else if lookahead.peek(LitInt) {
201            input.parse().map(Self::Const)
202        } else if lookahead.peek(Ident) && input.peek2(token::Paren) {
203            input.parse().map(Self::Op)
204        } else if lookahead.peek(Ident) {
205            input.parse().map(Self::RdTmp)
206        } else {
207            Err(lookahead.error())
208        }
209    }
210}
211
212impl Parse for ExprGet {
213    fn parse(input: ParseStream) -> Result<Self> {
214        let offset;
215        Ok(Self {
216            _get: input.parse()?,
217            _colon: input.parse()?,
218            size: input.parse()?,
219            _paren: parenthesized!(offset in input),
220            offset: offset.parse()?,
221        })
222    }
223}
224
225impl Parse for ExprLoad {
226    fn parse(input: ParseStream) -> Result<Self> {
227        let addr;
228        Ok(Self {
229            end: input.parse()?,
230            _colon: input.parse()?,
231            ty: input.parse()?,
232            _paren: parenthesized!(addr in input),
233            addr: addr.parse()?
234        })
235    }
236}
237
238impl Parse for ExprConst {
239    fn parse(input: ParseStream) -> Result<Self> {
240        Ok(Self {
241            co: input.parse()?,
242            _colon: input.parse()?,
243            ty: input.parse()?,
244        })
245    }
246}
247
248impl Parse for ExprOp {
249    fn parse(input: ParseStream) -> Result<Self> {
250        let args;
251        Ok(Self {
252            op: input.parse()?,
253            _paren: parenthesized!(args in input),
254            args: args.parse_terminated(Expr::parse)?,
255        })
256    }
257}
258
259impl quote::ToTokens for Expr {
260    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
261        match self {
262            Self::Get(get) => get.to_tokens(tokens),
263            Self::Op(op) => op.to_tokens(tokens),
264            Self::Const(co) => co.to_tokens(tokens),
265            Self::RdTmp(tmp) => tokens.extend(quote!(Expr::rd_tmp(#tmp))),
266            Self::Load(load) => load.to_tokens(tokens),
267            Self::CCall => tokens.extend(quote!(Expr::ccall())),
268        }
269    }
270}
271
272impl quote::ToTokens for ExprGet {
273    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
274        let offset = &self.offset;
275        let ty = format_ident!("Ity_{}", self.size);
276        tokens.extend(quote!(Expr::get(#offset, Type::#ty)))
277    }
278}
279
280impl quote::ToTokens for ExprOp {
281    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
282        let op = format_ident!("Iop_{}", &self.op);
283        let args = &self.args;
284        match self.args.len() {
285            1 => tokens.extend(quote!(Expr::unop(Op::#op, #args))),
286            2 => tokens.extend(quote!(Expr::binop(Op::#op, #args))),
287            3 => tokens.extend(quote!(Expr::triop(Op::#op, #args))),
288            4 => tokens.extend(quote!(Expr::qop(Op::#op, #args))),
289            _ => panic!(),
290        }
291    }
292}
293        
294impl quote::ToTokens for ExprLoad {
295    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
296        let (end, addr) = (&self.end, &self.addr);
297        let ty = format_ident!("Ity_{}", self.ty);
298        tokens.extend(quote!(Expr::load(#end, Type::#ty, #addr)))
299    }
300}
301        
302impl quote::ToTokens for ExprConst {
303    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
304        tokens.extend(match self.ty.to_string().as_str() {
305            "I1" => match self.co.base10_parse() {
306                Ok(0) => quote!(Expr::const_(Const::u1(false))),
307                Ok(1) => quote!(Expr::const_(Const::u1(true))),
308                _ => quote!(compile_error!("Const of type I1 must be 0 or 1")),
309            }
310            "I8" => {
311                let co = &self.co;
312                quote!(Expr::const_(Const::u8(#co)))
313            }
314            "I16" => {
315                let co = &self.co;
316                quote!(Expr::const_(Const::u16(#co)))
317            }
318            "I32" => {
319                let co = &self.co;
320                quote!(Expr::const_(Const::u32(#co)))
321            }
322            "I64" => {
323                let co = &self.co;
324                quote!(Expr::const_(Const::u64(#co)))
325            }
326            _ => quote!(compile_error!("Invalid type")),
327        });
328    }
329}
330        
331enum Stmt {
332    NoOp(StmtNoOp),
333    IMark(StmtIMark),
334    AbiHint(StmtAbiHint),
335    WrTmp(StmtWrTmp),
336    Put(StmtPut),
337    Store(StmtStore),
338}
339
340struct StmtNoOp {
341    _ir: kw::IR,
342    _dash: Token![-],
343    _no_op: kw::NoOp,
344}
345
346struct StmtIMark {
347    _imark: kw::IMark,
348    _paren: token::Paren,
349    info: Punctuated<LitInt, Token![,]>,
350}
351
352struct StmtAbiHint {
353    base: Expr,
354    len: LitInt,
355    nia: Expr,
356}
357
358struct StmtWrTmp {
359    tmp: Ident,
360    _eq: Token![=],
361    data: Expr,
362}
363
364struct StmtPut {
365    _put: kw::PUT,
366    _paren: token::Paren,
367    offset: LitInt,    
368    _eq: Token![=],
369    data: Expr,
370}
371
372struct StmtStore {
373    store: StoreEndness,
374    _paren: token::Paren,
375    addr: Expr,
376    _eq: Token![=],
377    data: Expr,
378}
379
380impl Parse for Stmt {
381    fn parse(input: ParseStream) -> Result<Self> {
382        let lookahead = input.lookahead1();
383        if lookahead.peek(kw::IR) {
384            input.parse().map(Self::NoOp)
385        } else if lookahead.peek(Token![-]) || lookahead.peek(kw::IMark) {
386            input.parse().map(Self::IMark)
387        } else if lookahead.peek(Token![=]) || lookahead.peek(kw::AbiHint) {
388            input.parse().map(Self::AbiHint)
389        } else if lookahead.peek(kw::PUT) {
390            input.parse().map(Self::Put)
391        } else if lookahead.peek(kw::STbe) || lookahead.peek(kw::STle) {
392            input.parse().map(Self::Store)
393        } else if lookahead.peek(Ident) {
394            input.parse().map(Self::WrTmp)
395        } else {
396            Err(lookahead.error())
397        }
398    }
399}
400
401impl Parse for StmtNoOp {
402    fn parse(input: ParseStream) -> Result<Self> {
403        Ok(Self {
404            _ir: input.parse()?,
405            _dash: input.parse()?,
406            _no_op: input.parse()?,
407        })
408    }
409}
410
411impl Parse for StmtIMark {
412    fn parse(input: ParseStream) -> Result<Self> {
413        while input.parse::<Token![-]>().is_ok() {}
414        let info;
415        let res = Self {
416            _imark: input.parse()?,
417            _paren: parenthesized!(info in input),
418            info: info.parse_terminated(LitInt::parse)?
419        };
420        while input.parse::<Token![-]>().is_ok() {}
421        Ok(res)
422    }
423}
424
425
426enum AbiArg {
427    Expr(Expr),
428    Int(LitInt),
429}
430
431impl AbiArg {
432    fn expect_expr(self) -> syn::Result<Expr> {
433        match self {
434            Self::Expr(expr) => Ok(expr),
435            _ => panic!(),
436        }
437    }
438
439    fn expect_lit(self) -> syn::Result<LitInt> {
440        match self {
441            Self::Int(lit) => Ok(lit),
442            _ => panic!(),
443        }
444    }
445}
446
447
448impl Parse for AbiArg {
449    fn parse(input: ParseStream) -> Result<Self> {
450        let lookahead = input.lookahead1();
451        if lookahead.peek(LitInt) {
452            input.parse().map(Self::Int)
453        } else {
454            input.parse().map(Self::Expr)
455        }
456    }
457}
458
459
460impl Parse for StmtAbiHint {
461    fn parse(input: ParseStream) -> Result<Self> {
462        while input.parse::<Token![=]>().is_ok() {}
463        let info;
464        let _abi_hint: kw::AbiHint = input.parse()?;
465        let _paren = parenthesized!(info in input);
466        let info = info.parse_terminated::<_, Token![,]>(AbiArg::parse)?;
467        let mut info_iter = info.into_iter();
468
469        let res = Self {
470            base: info_iter.next().unwrap().expect_expr()?,
471            len: info_iter.next().unwrap().expect_lit()?,
472            nia: info_iter.next().unwrap().expect_expr()?,
473        };
474        while input.parse::<Token![=]>().is_ok() {}
475        Ok(res)
476    }
477}
478
479impl Parse for StmtWrTmp {
480    fn parse(input: ParseStream) -> Result<Self> {
481        Ok(Self {
482            tmp: input.parse()?,
483            _eq: input.parse()?,
484            data: input.parse()?,
485        })
486    }
487}
488
489impl Parse for StmtPut {
490    fn parse(input: ParseStream) -> Result<Self> {
491        let offset;
492        Ok(Self {
493            _put: input.parse()?,
494            _paren: parenthesized!(offset in input),
495            offset: offset.parse()?,
496            _eq: input.parse()?,
497            data: input.parse()?,
498        })
499    }
500}
501
502impl Parse for StmtStore {
503    fn parse(input: ParseStream) -> Result<Self> {
504        let addr;
505        Ok(Self {
506            store: input.parse()?,
507            _paren: parenthesized!(addr in input),
508            addr: addr.parse()?,
509            _eq: input.parse()?,
510            data: input.parse()?,
511        })
512    }
513}
514
515impl quote::ToTokens for Stmt {
516    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
517        match self {
518            Self::NoOp(..) => tokens.extend(quote!(Stmt::no_op())),
519            Self::IMark(imark) => imark.to_tokens(tokens),
520            Self::AbiHint(hint) => hint.to_tokens(tokens),
521            Self::WrTmp(wr_tmp) => wr_tmp.to_tokens(tokens),
522            Self::Put(put) => put.to_tokens(tokens),
523            Self::Store(store) => store.to_tokens(tokens),
524        }
525    }
526}
527
528impl quote::ToTokens for StmtIMark {
529    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
530        let info = &self.info;
531        tokens.extend(quote!(Stmt::imark(#info)))
532    }
533}
534
535impl quote::ToTokens for StmtAbiHint {
536    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
537        let StmtAbiHint {
538            base,
539            len,
540            nia,
541        } = self;
542        tokens.extend(quote!(Stmt::abi_hint(#base, #len, #nia)))
543    }
544}
545
546impl quote::ToTokens for StmtWrTmp {
547    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
548        let (tmp, data) = (&self.tmp, &self.data);
549        tokens.extend(quote!(Stmt::wr_tmp(#tmp, #data)))
550    }
551}
552
553impl quote::ToTokens for StmtPut {
554    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
555        let (offset, data) = (&self.offset, &self.data);
556        tokens.extend(quote!(Stmt::put(#offset, #data)))
557    }
558}
559
560impl quote::ToTokens for StmtStore {
561    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
562        let (end, addr, data) = (&self.store, &self.addr, &self.data);
563        tokens.extend(quote!(Stmt::store(#end, #addr, #data)))
564    }
565}
566
567struct ExitKind {
568    _semi: Token![;],
569    _exit: kw::exit,
570    _dash: Token![-],
571    kind: Ident,
572}
573
574impl Parse for ExitKind {
575    fn parse(input: ParseStream) -> Result<Self> {
576        Ok(Self {
577            _semi: input.parse()?,
578            _exit: input.parse()?,
579            _dash: input.parse()?,
580            kind: input.parse()?,
581        })
582    }
583}
584
585struct IRSB {
586    ty_env: TypeEnv,
587    stmts: Vec<Stmt>,
588    exit: ExitKind,
589}
590
591impl Parse for IRSB {
592    fn parse(input: ParseStream) -> Result<Self> {
593        let ty_env = input.parse()?;
594        let mut stmts = Vec::new();
595        while !(input.is_empty() || input.peek(Token![;])) {
596            stmts.push(input.parse()?);
597        }
598        Ok(IRSB {
599            ty_env,
600            stmts,
601            exit: input.parse()?
602        })
603    }
604}
605
606#[allow(non_snake_case)]
607#[proc_macro]
608pub fn IRSB(item: TokenStream) -> TokenStream {
609    let irsb = parse_macro_input!(item as IRSB);
610
611    let (ip_offset, next, stmts) = match irsb.stmts.split_last() {
612        Some((Stmt::Put(StmtPut { offset, data, .. }), stmts)) => {
613            (offset, data, stmts)
614        }
615        Some(_) => {
616            return quote!(compile_error!("The last statement is not a valid 'next' statement (PUT(ip_offset) = Expr)")).into();
617        }
618        None => {
619            return quote!(compile_error!("No statements found! (There must be at least the 'next' statement.)")).into();
620        }
621    };
622    let jk = format_ident!("Ijk_{}", &irsb.exit.kind);
623    let mut output = quote!{
624        use libvex::ir::{Const, Expr, IREndness, IRSB, JumpKind, Op, Stmt, Type};
625        let mut irsb = IRSB::new();
626    };
627    for (tmp, _colon, ty) in irsb.ty_env.tmps.iter() {
628        let ty = format_ident!("Ity_{}", ty);
629        output = quote!{
630            #output
631            let #tmp = irsb.type_env().new_tmp(Type::#ty);
632        };
633    }
634    for stmt in stmts {
635        output = quote!{
636            #output
637            irsb.add_stmt(#stmt);
638        };
639    }
640    quote!(unsafe {
641        #output
642        irsb.set_next(#next);
643        irsb.set_offs_ip(#ip_offset);
644        irsb.set_jump_kind(JumpKind::#jk);
645        irsb
646    }).into()
647}