1

I am building a cranelift based compiler for mathematical expressions but I am having trouble getting the REPL working. my compiler

use cranelift::{
    codegen::Context,
    frontend::{FunctionBuilder, FunctionBuilderContext, Variable},
    prelude::{EntityRef, InstBuilder, Value},
};
use cranelift_jit::{JITBuilder, JITModule};
use cranelift_module::{default_libcall_names, DataContext, Linkage, Module};
use math_parser::{Atom, Expr, Op};
use std::{cell::RefCell, collections::HashMap};

#[allow(dead_code)]
pub struct Compiler<'ctx> {
    builder_ctx: FunctionBuilderContext,
    ctx: Context,
    data: DataContext,
    module: JITModule,
    builder: RefCell<Option<FunctionBuilder<'ctx>>>,
    symbol_table: RefCell<HashMap<String, Variable>>,
    var_index: RefCell<usize>,
}

impl Default for Compiler<'_> {
    fn default() -> Self {
        let builder = JITBuilder::new(default_libcall_names()).unwrap();
        let module = JITModule::new(builder);
        let func_ctx = FunctionBuilderContext::new();
        Self {
            builder_ctx: func_ctx,
            ctx: module.make_context(),
            data: DataContext::new(),
            module,
            builder: RefCell::new(None),
            symbol_table: RefCell::new(HashMap::new()),
            var_index: RefCell::new(0),
        }
    }
}

impl<'ctx> Compiler<'ctx> {
    pub fn initialize(&'ctx mut self) {
        let mut builder = FunctionBuilder::new(&mut self.ctx.func, &mut self.builder_ctx);
        let entry_block = builder.create_block();
        builder.append_block_params_for_function_params(entry_block);
        builder.switch_to_block(entry_block);
        builder.seal_block(entry_block);
        self.builder = RefCell::new(Some(builder));
    }
    pub fn compile_expr(&self, expr: Expr) -> Value {
        match expr {
            Expr::Unary(atom) => self.compile_atom(atom),
            Expr::BinOp {
                lhs: _,
                ref op,
                rhs: _,
            } if matches!(op, &Op::Eq) => self.compile_bindig(expr),
            Expr::BinOp {
                lhs: _,
                op: _,
                rhs: _,
            } => todo!(),
        }
    }
    fn compile_atom(&self, atom: Atom) -> Value {
        let mut builder = self.builder.borrow_mut();
        let builder = builder.as_mut().unwrap();
        match atom {
            Atom::Ident(ref name) => match (*self.symbol_table.borrow()).get(name) {
                Some(var) => builder.use_var(*var),
                None => builder.ins().f64const(f64::default()),
            },
            Atom::Integer(int) => builder.ins().f64const(int),
        }
    }
    fn compile_bindig(&self, expr: Expr) -> Value {
        let mut builder = self.builder.borrow_mut();
        let builder = builder.as_mut().unwrap();
        if let Expr::BinOp { lhs, op, rhs } = expr {
            if op != Op::Eq {
                panic!("not a binding expression")
            }
            let rhs = self.compile_expr(*rhs);
            let var = Variable::new(*self.var_index.borrow());
            *self.var_index.borrow_mut() += 1;
            builder.def_var(var, rhs);
            let mut symbol_table = self.symbol_table.borrow_mut();
            if let Expr::Unary(Atom::Ident(lhs)) = *lhs {
                if symbol_table.contains_key(lhs.as_str()) {
                    symbol_table.remove(lhs.as_str());
                }
                symbol_table.insert(lhs, var);
            }
            return rhs;
        } else {
            panic!("not a binding expression")
        }
    }
    pub fn compile(&mut self, input: Expr) -> Result<*const u8, String> {
        self.compile_expr(input);
        let id = self
            .module
            .declare_function("calc_main", Linkage::Export, &self.ctx.func.signature)
            .map_err(|e| e.to_string())?;
        self.module
            .define_function(id, &mut self.ctx)
            .map_err(|e| e.to_string())?;
        self.module.clear_context(&mut self.ctx);
        self.module.finalize_definitions();
        let code = self.module.get_finalized_function(id);
        Ok(code)
    }
}

unsafe impl Send for Compiler<'_> {}
unsafe impl Sync for Compiler<'_> {}

my dependencies

cranelift-jit = "0.84.0"
cranelift = "0.84.0"
cranelift-module = "0.84.0"
cranelift-codegen = "0.84.0"
lazy_static = "1.4.0"
math_parser = "0.3.2"

my binary

use math_parser::parse
//rc_lib is the above given lib
use rc_lib::Compiler;
use std::{
    io::{stdin, stdout, Write},
    mem::transmute
};

fn main() {
    let mut jit = Compiler::default();
    jit.initialize();
    loop {
        print!(">>> ");
        stdout().flush().unwrap();
        let mut input = String::new();
        stdin().read_line(&mut input).unwrap();
        unsafe {
            let output = run_code::<f64>(&mut jit, input.trim());
            match output {
                Ok(val) => println!("{}", val),
                Err(err) => {
                    eprintln!("{}", err);
                    continue;
                }
            }
        }
    }
}

unsafe fn run_code<O>(jit: &mut Compiler, code: &str) -> Result<O, String> {
    let code = match parse(code) {
        Ok(ast) => ast,
        Err(err) => return Err(format!("{}", err)),
    };
    let code_ptr = jit.compile(code)?;
    let code_fn = transmute::<_, fn() -> O>(code_ptr);
    Ok(code_fn())
}

But I get the error

error[E0499]: cannot borrow `jit` as mutable more than once at a time
  --> src/main.rs:17:42
   |
10 |     jit.initialize();
   |     ---------------- first mutable borrow occurs here
...
17 |             let output = run_code::<f64>(&mut jit, input.trim());
   |                                          ^^^^^^^^
   |                                          |
   |                                          second mutable borrow occurs here
   |                                          first borrow later used here

For more information about this error, try `rustc --explain E0499`.
error: could not compile `rusty-calc` due to previous error

if I change the reciever of the initialise function from &'ctx mut self to &mut self then I get the error

error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
  --> src/lib.rs:41:48
   |
41 |         let mut builder = FunctionBuilder::new(&mut self.ctx.func, &mut self.builder_ctx);
   |                                                ^^^^^^^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime defined here...
  --> src/lib.rs:40:23
   |
40 |     pub fn initialize(&mut self) {
   |                       ^^^^^^^^^
note: ...so that reference does not outlive borrowed content
  --> src/lib.rs:41:48
   |
41 |         let mut builder = FunctionBuilder::new(&mut self.ctx.func, &mut self.builder_ctx);
   |                                                ^^^^^^^^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'ctx` as defined here...
  --> src/lib.rs:39:6
   |
39 | impl<'ctx> Compiler<'ctx> {
   |      ^^^^
note: ...so that the expression is assignable
  --> src/lib.rs:46:37
   |
46 |         self.builder = RefCell::new(Some(builder));
   |                                     ^^^^^^^^^^^^^
   = note: expected `Option<FunctionBuilder<'ctx>>`
              found `Option<FunctionBuilder<'_>>`

For more information about this error, try `rustc --explain E0495`.
error: could not compile `rusty-calc` due to previous error
muppi090909
  • 87
  • 1
  • 7
  • Indeed, you've called `borrow_mut()` multiple times on the same JIT `RefCell`. – John Kugelman Jun 01 '22 at 16:45
  • 3
    please provide a minimal example instead, preferably posted in https://play.rust-lang.org/ – Netwave Jun 01 '22 at 16:50
  • 6
    The extensive, haphazard use of `Rc`, `RefCell`, `ManuallyDrop`, etc., makes me think you've been tangling with the borrow checker and have been fighting it off by adding layers of pointers and cloning and whatnot. That's not a sustainable strategy. You're not really fixing borrow errors, you're just moving them from compile-time to run-time. – John Kugelman Jun 01 '22 at 16:50
  • 1
    You should never, ever have to use `ManuallyDrop` unless you are doing something like giving ownership of a value from Rust to C code (in which case `Box::leak()` is probably more appropriate anyway), or the drop order for values in a struct needs to be dynamic. – cdhowie Jun 01 '22 at 18:16
  • @JohnKugelman Yes, my original code involved use of just a `mut` variable. – muppi090909 Jun 02 '22 at 11:54
  • @Netwave I cannot as my crate *math_parser* is not available on the playground only on crates.io – muppi090909 Jun 02 '22 at 12:00
  • Does this answer your question? [What is the difference between '&self' and '&'a self'?](https://stackoverflow.com/questions/45833618/what-is-the-difference-between-self-and-a-self) and [Linking the lifetimes of self and a reference in method](https://stackoverflow.com/questions/30273850/linking-the-lifetimes-of-self-and-a-reference-in-method) – John Kugelman Jun 02 '22 at 12:50
  • Change `pub fn initialize(&'ctx mut self)` to `pub fn initialize(&mut self)`. – John Kugelman Jun 02 '22 at 12:52
  • @JohnKugelmy I dont see how this helps in my current situation because i am dealing with trying to get 2 mutable borrows working *or avoiding it* – muppi090909 Jun 02 '22 at 13:16

0 Answers0