variant_config/dsl/
mod.rs

1pub mod fn_translator;
2mod frontend;
3mod utils;
4mod variant_value;
5
6use ahash::RandomState;
7use anyhow;
8use cranelift::prelude::*;
9use cranelift_jit::{JITBuilder, JITModule};
10use cranelift_module::{Linkage, Module};
11use fn_translator::*;
12use frontend::*;
13use hashbrown::HashMap;
14use std::mem;
15pub use variant_value::*;
16
17const N_PARAMS: usize = 20;
18// TODO: Use macro instead
19pub type FnSignature = fn(
20    i64,
21    i64,
22    i64,
23    i64,
24    i64,
25    i64,
26    i64,
27    i64,
28    i64,
29    i64,
30    i64,
31    i64,
32    i64,
33    i64,
34    i64,
35    i64,
36    i64,
37    i64,
38    i64,
39    i64,
40) -> bool;
41pub const BOOL: Type = types::B1;
42pub const INT: Type = types::I64;
43
44pub struct FnJitter {
45    module: JITModule,
46    random_state: RandomState,
47    params: HashMap<String, Variable>,
48    pub func: FnSignature,
49}
50
51impl FnJitter {
52    pub fn new(input: &str) -> anyhow::Result<Self> {
53        let builder = JITBuilder::new(cranelift_module::default_libcall_names());
54        let mut module = JITModule::new(builder);
55        let mut ctx = module.make_context();
56        let random_state = RandomState::new();
57        let mut params = HashMap::with_capacity(N_PARAMS);
58        let fn_ptr = Self::compile(input, &mut module, &mut ctx, &random_state, &mut params)
59            .map_err(|e| {
60                anyhow::anyhow!(format!("Unable to parse condition `{}` :{}", input, e))
61            })?;
62        Ok(Self {
63            module,
64            random_state,
65            params,
66            func: unsafe { mem::transmute::<_, FnSignature>(fn_ptr) },
67        })
68    }
69
70    pub fn evaluate(&self, params: &HashMap<String, VariantValue>) -> bool {
71        let mut a: [i64; N_PARAMS] = [0; N_PARAMS];
72        for (k, idx) in &self.params {
73            if let Some(v) = params.get(k) {
74                a[idx.index()] = v.to_i64(&self.random_state);
75            }
76        }
77
78        (self.func)(
79            a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13],
80            a[14], a[15], a[16], a[17], a[18], a[19],
81        )
82    }
83
84    fn compile(
85        input: &str,
86        module: &mut JITModule,
87        ctx: &mut codegen::Context,
88        random_state: &RandomState,
89        params: &mut HashMap<String, Variable>,
90    ) -> anyhow::Result<*const u8> {
91        let stmts = parser::statements(input).map_err(|e| e)?;
92        for _ in 0..N_PARAMS {
93            ctx.func.signature.params.push(AbiParam::new(INT));
94        }
95        ctx.func.signature.returns.push(AbiParam::new(BOOL));
96
97        let mut builder_context = FunctionBuilderContext::new();
98        let mut builder = FunctionBuilder::new(&mut ctx.func, &mut builder_context);
99
100        let entry_block = builder.create_block();
101        builder.append_block_params_for_function_params(entry_block);
102        builder.switch_to_block(entry_block);
103        builder.seal_block(entry_block);
104
105        let mut return_value = ValueWrapper::new(builder.ins().bconst(BOOL, false), BOOL);
106        let mut trans = FunctionTranslator::new(random_state, &mut builder, params, entry_block);
107        for expr in stmts {
108            return_value = trans.translate_expr(expr)?;
109        }
110        return_value = trans.convert_int_to_bool(return_value);
111        trans.return_and_finalize(return_value.value);
112
113        let id = module
114            .declare_function("fn", Linkage::Export, &ctx.func.signature)
115            .map_err(|e| e)?;
116        module
117            .define_function(
118                id,
119                ctx,
120                &mut codegen::binemit::NullTrapSink {},
121                &mut codegen::binemit::NullStackMapSink {},
122            )
123            .map_err(|e| e)?;
124        module.clear_context(ctx);
125        module.finalize_definitions();
126        let code = module.get_finalized_function(id);
127        Ok(code)
128    }
129
130    pub(crate) unsafe fn free_memory(self) {
131        self.module.free_memory();
132    }
133}