Crate melior_next

source ·
Expand description

Melior-next is the rustic MLIR bindings for Rust. It aims to provide a simple, safe, and complete API for MLIR with a reasonably sane ownership model represented by the type system in Rust.

This crate is a wrapper of the MLIR C API.

Dependencies

LLVM/MLIR 16 needs to be installed on your system.

Safety

Although Melior aims to be completely safe, some part of the current API is not.

  • Access to operations, types, or attributes that belong to dialects not loaded in contexts can lead to runtime errors or segmentation faults in the worst case.
    • Fix plan: Load all dialects by default on creation of contexts, and provide unsafe constructors of contexts for advanced users.
  • IR object references returned from functions that move ownership of arguments might get invalidated later.
    • This is because we need to borrow &self rather than &mut self to return such references.
    • e.g. Region::append_block()
    • Fix plan: Use dynamic check, such as RefCell, for the objects.

Examples

Building a function to add integers and executing it using the JIT engine.

use melior_next::{
    Context,
    dialect::{self, arith},
    ir::*,
    pass,
    utility::*,
    ExecutionEngine
};

let registry = dialect::Registry::new();
register_all_dialects(&registry);

let context = Context::new();
context.append_dialect_registry(&registry);
context.get_or_load_dialect("func");
context.get_or_load_dialect("arith");
register_all_llvm_translations(&context);

let location = Location::unknown(&context);
let mut module = Module::new(location);

let integer_type = Type::integer(&context, 64);

let function = {
    let region = Region::new();
    let block = Block::new(&[(integer_type, location), (integer_type, location)]);
    let arg1 = block.argument(0).unwrap().into();
    let arg2 = block.argument(1).unwrap().into();

    let sum = block.append_operation(
        arith::addi(arg1, arg2, integer_type, location)
    );

    block.append_operation(
        operation::Builder::new("func.return", Location::unknown(&context))
            .add_operands(&[sum.result(0).unwrap().into()])
            .build(),
    );

    region.append_block(block);

    operation::Builder::new("func.func", Location::unknown(&context))
        .add_attributes(
            &NamedAttribute::new_parsed_vec(&context, &[
                ("function_type", "(i64, i64) -> i64"),
                ("sym_name", "\"add\""),
                ("llvm.emit_c_interface", "unit"),
            ]).unwrap()
        )
        .add_regions(vec![region])
        .build()
};

module.body().append_operation(function);

assert!(module.as_operation().verify());

let pass_manager = pass::Manager::new(&context);
register_all_passes();
pass_manager.add_pass(pass::conversion::convert_scf_to_cf());
pass_manager.add_pass(pass::conversion::convert_cf_to_llvm());
pass_manager.add_pass(pass::conversion::convert_func_to_llvm());
pass_manager.add_pass(pass::conversion::convert_arithmetic_to_llvm());
pass_manager.enable_verifier(true);
pass_manager.run(&mut module).unwrap();

let engine = ExecutionEngine::new(&module, 2, &[], false);

let mut argument1: i64 = 2;
let mut argument2: i64 = 4;
let mut result: i64 = -1;

unsafe {
    engine
        .invoke_packed(
                "add",
                &mut [
                    &mut argument1 as *mut i64 as *mut (),
                    &mut argument2 as *mut i64 as *mut (),
                    &mut result as *mut i64 as *mut ()
                ])
        .unwrap();
};

assert_eq!(result, 6);

Modules

Structs

Enums