Giter VIP home page Giter VIP logo

melior's Introduction

Melior

GitHub Action Crate License

Melior is the 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.

Examples

Building a function to add integers

use melior::{
    Context,
    dialect::{arith, DialectRegistry, func},
    ir::{*, attribute::{StringAttribute, TypeAttribute}, r#type::FunctionType},
    utility::register_all_dialects,
};

let registry = DialectRegistry::new();
register_all_dialects(&registry);

let context = Context::new();
context.append_dialect_registry(&registry);
context.load_all_available_dialects();

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

let index_type = Type::index(&context);

module.body().append_operation(func::func(
    &context,
    StringAttribute::new(&context, "add"),
    TypeAttribute::new(FunctionType::new(&context, &[index_type, index_type], &[index_type]).into()),
    {
        let block = Block::new(&[(index_type, location), (index_type, location)]);

        let sum = block.append_operation(arith::addi(
            block.argument(0).unwrap().into(),
            block.argument(1).unwrap().into(),
            location
        ));

        block.append_operation(func::r#return( &[sum.result(0).unwrap().into()], location));

        let region = Region::new();
        region.append_block(block);
        region
    },
    &[],
    location,
));

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

Install

cargo add melior

Dependencies

LLVM/MLIR 17 needs to be installed on your system. On Linux and macOS, you can install it via Homebrew.

brew install llvm@17

Documentation

On GitHub Pages.

Contribution

Contribution is welcome! But, Melior is still in the alpha stage as well as the MLIR C API. Note that the API is unstable and can have breaking changes in the future.

Technical notes

  • We always use &T for MLIR objects instead of &mut T to mitigate the intricacy of representing a loose ownership model of the MLIR C API in Rust.
  • Only UTF-8 is supported as string encoding.
    • Most string conversion between Rust and C is cached internally.

Naming conventions

  • Mlir<X> objects are named <X> if they have no destructor. Otherwise, they are named <X> for owned objects and <X>Ref for borrowed references.
  • mlir<X>Create functions are renamed as <X>::new.
  • mlir<X>Get<Y> functions are renamed as follows:
    • If the resulting objects refer to &self, they are named <X>::as_<Y>.
    • Otherwise, they are named just <X>::<Y> and may have arguments, such as position indices.

Safety

Although Melior aims to be completely type 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.

References

License

Apache 2.0

melior's People

Contributors

azteca1998 avatar badbastion avatar danacus avatar dependabot[bot] avatar edg-l avatar raviqqe avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

melior's Issues

tests don't pass locally

An important note is that i have llvm compiled with assertions on.

melior-2d0b8d39558e8344: /.../llvm-project-16.0.0.src/mlir/lib/IR/BuiltinAttributes.cpp:924: static mlir::DenseElementsAttr mlir::DenseElementsAttr::get(mlir::ShapedType, ArrayRef<mlir::Attribute>): Assertion `intAttr.getType() == eltType && "expected integer attribute type to equal element type"' failed.
test dialect::scf::tests::compile_index_switch ... ok
error: test failed, to rerun pass `-p melior --lib`

Caused by:
  process didn't exit successfully: `/.../melior/target/debug/deps/melior-2d0b8d39558e8344` (signal: 6, SIGABRT: process abort signal)

Since it SIGABRT it's hard to pointpoint what test causes it

Call to `enable_result_type_inference` is not safe

Calling enable_result_type_inference on OperationBuilder may trigger undefined behavior if types cannot be inferred, since mlirOperationCreate returns a nullptr in this case. OperationBuilder::build does not check for nullity, and as such an invalid Operation may be constructed, causing undefined behavior when it is accessed or dropped.

Code to reproduce:

{
    let _ = OperationBuilder::new("test", location)
        .enable_result_type_inference()
        .build();
}

Suggested fix: check for nullity before calling Operation::from_raw in OperationBuilder::build and return a Result<Operation, _> or simply panic.

PDL API

Problem

  • We don't have the PDL API.
    • PDL dialect is not implemented yet.
    • But it should be available and easy to implement through the usual operation construction API.

Solution

Proposing the C API for PDL

Extending mlir-sys

TBD

References

  • The original discussion is in #248.

TOSA dialect type inference error

Discussed in #370

Originally posted by gmmyung December 2, 2023
Hi,

I am currently working on a PoC of a AOT compiled ML framework in Rust using eerie (Rust binding to IREE).

I am fairly new to MLIR, and struggling from the following error:

pub fn new_tensor<'a, T>(&'a mut self, shape: &'a [u64]) -> impl Tensor + 'a {
      let location = Location::unknown(&self.melior_context);
      let values: Vec<_> = vec![1.0f32, 2.0f32, 3.0f32, 4.0f32]
          .iter()
          .map(|x| {
              FloatAttribute::new(
                  &self.melior_context,
                  *x as f64,
                  Type::float32(&self.melior_context),
              )
              .into()
          })
          .collect();
      let mlir_type = RankedTensorType::new(shape, Type::float32(&self.melior_context), None);
      let attr =
          DenseElementsAttribute::new(mlir_type.into(), &values).unwrap();
      println!("{:?}", attr);
      let op = tosa::r#const(&self.melior_context, attr.into(), location);
      ConstTensor { op, shape }
 }
warning: `eerietest` (bin "eerietest") generated 3 warnings (run `cargo fix --bin "eerietest"` to apply 2 suggestions)
    Finished dev [unoptimized + debuginfo] target(s) in 1.36s
     Running `target/debug/eerietest`
dense<[[1.000000e+00, 2.000000e+00], [3.000000e+00, 4.000000e+00]]> : tensor<2x2xf32>
error: type inference was requested for the operation tosa.const, but the operation does not support type inference; result types must be specified explicitly
thread 'main' panicked at /Users/gmmyung/.cargo/registry/src/index.crates.io-6f17d22bba15001f/melior-0.13.0/src/dialect/ods.rs:99:1:
valid operation: OperationBuild

What am I doing wrong? Any suggestions will be appreciated!

Feedback to MLIR

  • Lack of API to check if dialects are loaded in contexts.
    • But this is possible operation-wise via mlirContextIsRegisteredOperation().
  • String refs as inputs need to be null-terminated (C-style) strings.
    • The documentation says both are fine...
  • Use after move of operations, regions, and blocks
  • LLVM bitcode emission
    • This is possible only with a mlir-translate command right now.
  • mlirMemRefTypeGetElementType()
    • Use the ShapedType API.

References

Upcoming LLVM 18

Jan 23: release/18.x branch created
Jan 26: 18.1.0-rc1
Feb 6: 18.1.0-rc2
Feb 20: 18.1.0-rc3
Mar 5: 18.1.0
Mar 19: 18.1.1
Apr 2: 18.1.2
Apr 16: 18.1.3
Apr 30: 18.1.4
May 14: 18.1.5
May 28: 18.1.6 (If necessary)

https://discourse.llvm.org/t/llvm-18-release-schedule/76175

LLVM 18 looks like it will release soon.

Some new things it will have are the mlirTranslateToLLVMIR (which needs a llvm_sys context btw).

I'm also planning to land a pr soon adding all the LLVM DINode debug attributes, so debug info actually shows up on the generated llvm ir, hoping it will land in 18 too: llvm/llvm-project@main...edg-l:llvm-project:mlir_c_llvm_attributes

Making this issue more as an informative thing.

Remove hacky `StringRef` cache

Problem

  • The implicit global StringRef cache grows forever.
  • We can never clean them safely.
  • That makes the use of Melior impossible in compilers that share states in memory among compilation of modules.

Solution

  • Should the MLIR C API accept non-null-terminated strings?
  • Should we move this into a Context type making it a non-transparent type?

static STRING_CACHE: Lazy<RwLock<HashMap<String, CString>>> = Lazy::new(Default::default);

'mlir-c/AffineExpr.h' file not found

I am compiling on windows, and it seems that mlir-c in the llvm directory cannot be found correctly.

image

Another problem is CXXFLAGS always send -std:c++17, while needs c++17


  • Full logs:
error: failed to run custom build command for `tblgen v0.3.0`
note: To improve backtraces for build dependencies, set the CARGO_PROFILE_TEST_BUILD_OVERRIDE_DEBUG=true environment variable to enable debug information generation.
Caused by:
  process didn't exit successfully: `C:\Users\Dell\CLionProjects\mlir-backend\target\debug\build\tblgen-46227c0434f68353\build-script-build` (exit code: 1)
  --- stdout
  cargo:rerun-if-changed=wrapper.h
  cargo:rerun-if-changed=cc
  cargo:rustc-link-search=C:\Users\Dell\CLionProjects\llvm-project\llvm\out\build\x64-Release\lib
  cargo:rustc-link-lib=static=LLVMCore
  cargo:rustc-link-lib=static=LLVMSupport
  cargo:rustc-link-lib=static=LLVMTableGen
  cargo:rustc-link-lib=
  cargo:rustc-link-search=C:\Users\Dell\CLionProjects\mlir-backend\target\debug\build\tblgen-33e1f869219f9010\out
  TARGET = Some("x86_64-pc-windows-msvc")
  HOST = Some("x86_64-pc-windows-msvc")
  cargo:rerun-if-env-changed=CXX_x86_64-pc-windows-msvc
  CXX_x86_64-pc-windows-msvc = None
  cargo:rerun-if-env-changed=CXX_x86_64_pc_windows_msvc
  CXX_x86_64_pc_windows_msvc = None
  cargo:rerun-if-env-changed=HOST_CXX
  HOST_CXX = None
  cargo:rerun-if-env-changed=CXX
  CXX = None
  cargo:rerun-if-env-changed=CRATE_CC_NO_DEFAULTS
  CRATE_CC_NO_DEFAULTS = None
  CARGO_CFG_TARGET_FEATURE = Some("fxsr,sse,sse2")
  DEBUG = Some("false")
  cargo:rerun-if-env-changed=CXXFLAGS_x86_64-pc-windows-msvc
  CXXFLAGS_x86_64-pc-windows-msvc = None
  cargo:rerun-if-env-changed=CXXFLAGS_x86_64_pc_windows_msvc
  CXXFLAGS_x86_64_pc_windows_msvc = None
  cargo:rerun-if-env-changed=HOST_CXXFLAGS
  HOST_CXXFLAGS = None
  cargo:rerun-if-env-changed=CXXFLAGS
  CXXFLAGS = Some("-IC:\\Users\\Dell\\CLionProjects\\llvm-project\\llvm\\include -IC:\\Users\\Dell\\CLionProjects\\llvm-project\\llvm\\out\\build\\x64-Release\\include -std:c++17   /EHs-c- /GR- -D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_NONSTDC_NO_WARNINGS -D_SCL_SECURE_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS -DUNICODE -D_UNICODE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS")
  running: "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.37.32822\\bin\\HostX64\\x64\\cl.exe" "-nologo" "-MD" "-O2" "-Brepro" "-IC:\\Users\\Dell\\CLionProjects\\llvm-project\\llvm\\include" "-IC:\\Users\\Dell\\CLionProjects\\llvm-project\\llvm\\out\\build\\x64-Release\\include" "-std:c++17" "/EHs-c-" "/GR-" "-D_CRT_SECURE_NO_DEPRECATE" "-D_CRT_SECURE_NO_WARNINGS" "-D_CRT_NONSTDC_NO_DEPRECATE" "-D_CRT_NONSTDC_NO_WARNINGS" "-D_SCL_SECURE_NO_DEPRECATE" "-D_SCL_SECURE_NO_WARNINGS" "-DUNICODE" "-D_UNICODE" "-D__STDC_CONSTANT_MACROS" "-D__STDC_FORMAT_MACROS" "-D__STDC_LIMIT_MACROS" "-I" "cc/include" "-I" "C:/Users/Dell/CLionProjects/llvm-project/llvm/include" "-FoC:\\Users\\Dell\\CLionProjects\\mlir-backend\\target\\debug\\build\\tblgen-33e1f869219f9010\\out\\cc/lib\\Record.o" "-c" "cc/lib\\Record.cpp"
  Record.cpp
  C:\Users\Dell\.cargo\registry\src\index.crates.io-6f17d22bba15001f\tblgen-0.3.0\cc\lib\TableGen.hpp(63): error C7555: Using the specified initializer requires at least "/std:c++20"
  cc/lib\Record.cpp(22): error C7555: Using the specified initializer requires at least "/std:c++20"
  exit code: 2
  --- stderr
  error occurred: Command "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.37.32822\\bin\\HostX64\\x64\\cl.exe" "-nologo" "-MD" "-O2" "-Brepro" "-IC:\\Users\\Dell\\CLionProjects\\llvm-project\\llvm\\include" "-IC:\\Users\\Dell\\CLionProjects\\llvm-project\\llvm\\out\\build\\x64-Release\\include" "-std:c++17" "/EHs-c-" "/GR-" "-D_CRT_SECURE_NO_DEPRECATE" "-D_CRT_SECURE_NO_WARNINGS" "-D_CRT_NONSTDC_NO_DEPRECATE" "-D_CRT_NONSTDC_NO_WARNINGS" "-D_SCL_SECURE_NO_DEPRECATE" "-D_SCL_SECURE_NO_WARNINGS" "-DUNICODE" "-D_UNICODE" "-D__STDC_CONSTANT_MACROS" "-D__STDC_FORMAT_MACROS" "-D__STDC_LIMIT_MACROS" "-I" "cc/include" "-I" "C:/Users/Dell/CLionProjects/llvm-project/llvm/include" "-FoC:\\Users\\Dell\\CLionProjects\\mlir-backend\\target\\debug\\build\\tblgen-33e1f869219f9010\\out\\cc/lib\\Record.o" "-c" "cc/lib\\Record.cpp" with args "cl.exe" did not execute successfully (status code exit code: 2).
warning: build failed, waiting for other jobs to finish...
error: failed to run custom build command for `mlir-sys v0.2.1`
note: To improve backtraces for build dependencies, set the CARGO_PROFILE_TEST_BUILD_OVERRIDE_DEBUG=true environment variable to enable debug information generation.
Caused by:
  process didn't exit successfully: `C:\Users\Dell\CLionProjects\mlir-backend\target\debug\build\mlir-sys-ca7903e2b2ae84b8\build-script-build` (exit code: 101)
  --- stdout
  cargo:rerun-if-changed=wrapper.h
  cargo:rustc-link-search=C:\Users\Dell\CLionProjects\llvm-project\llvm\out\build\x64-Release\lib
  cargo:rustc-link-lib=
  cargo:rerun-if-env-changed=TARGET
  cargo:rerun-if-env-changed=BINDGEN_EXTRA_CLANG_ARGS_x86_64-pc-windows-msvc
  cargo:rerun-if-env-changed=BINDGEN_EXTRA_CLANG_ARGS_x86_64_pc_windows_msvc
  cargo:rerun-if-env-changed=BINDGEN_EXTRA_CLANG_ARGS
  --- stderr
  wrapper.h:1:10: fatal error: 'mlir-c/AffineExpr.h' file not found
  thread 'main' panicked at C:\Users\Dell\.cargo\registry\src\index.crates.io-6f17d22bba15001f\mlir-sys-0.2.1\build.rs:98:10:
  called `Result::unwrap()` on an `Err` value: ClangDiagnostic("wrapper.h:1:10: fatal error: 'mlir-c/AffineExpr.h' file not found\n")
  stack backtrace:
     0: std::panicking::begin_panic_handler
               at /rustc/4578435e1695863d921c7763d5a0add98f8e3869/library\std\src\panicking.rs:597
     1: core::panicking::panic_fmt
               at /rustc/4578435e1695863d921c7763d5a0add98f8e3869/library\core\src\panicking.rs:72
     2: core::result::unwrap_failed
               at /rustc/4578435e1695863d921c7763d5a0add98f8e3869/library\core\src\result.rs:1653
     3: core::result::Result<T,E>::unwrap
     4: <core::ops::control_flow::ControlFlow<B,C> as core::ops::try_trait::Try>::branch
     5: <core::ops::control_flow::ControlFlow<B,C> as core::ops::try_trait::Try>::branch
     6: core::ops::function::FnOnce::call_once
     7: <core::result::Result<T,F> as core::ops::try_trait::FromResidual<core::result::Result<core::convert::Infallible,E>>>::from_residual
     8: std::rt::lang_start::{{closure}}
     9: std::rt::lang_start_internal::closure$2
               at /rustc/4578435e1695863d921c7763d5a0add98f8e3869/library\std\src\rt.rs:148
    10: std::panicking::try::do_call
               at /rustc/4578435e1695863d921c7763d5a0add98f8e3869/library\std\src\panicking.rs:504
    11: std::panicking::try
               at /rustc/4578435e1695863d921c7763d5a0add98f8e3869/library\std\src\panicking.rs:468
    12: std::panic::catch_unwind
               at /rustc/4578435e1695863d921c7763d5a0add98f8e3869/library\std\src\panic.rs:142
    13: std::rt::lang_start_internal
               at /rustc/4578435e1695863d921c7763d5a0add98f8e3869/library\std\src\rt.rs:148
    14: std::rt::lang_start
    15: main
    16: invoke_main
               at D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:78
    17: __scrt_common_main_seh
               at D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288
    18: BaseThreadInitThunk
    19: RtlUserThreadStart
  note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

Transparent representation of MLIR objects

Problem

  • Converting collections of MLIR objects is costly at O(n).

Solution

  • Use transparent representation (i.e. #[repr(transparent)].)
  • Some functions might not benefit from this without refactoring their signatures.
    • e.g. Block::new(pairs: &[(Type<'c>, Location<'c>)])

Segfault using ODS llvm alloca builder

Caused by:
  process didn't exit successfully: `/snip/melior/target/debug/deps/melior-eb2c58f5b3bbae69 compile_ods_llvm_alloca` (signal: 11, SIGSEGV: invalid memory reference)

#[test]
    fn compile_ods_llvm_alloca() {
        let context = create_test_context();

        let location = Location::unknown(&context);
        let mut module = Module::new(location);
        let integer_type = IntegerType::new(&context, 64).into();
        let ptr_type = crate::dialect::llvm::r#type::opaque_pointer(&context);

        module.body().append_operation(func::func(
            &context,
            StringAttribute::new(&context, "foo"),
            TypeAttribute::new(FunctionType::new(&context, &[integer_type], &[]).into()),
            {
                let block = Block::new(&[(integer_type, location)]);

                let alloca_size = block.argument(0).unwrap().into();

                block.append_operation(
                    llvm::AllocaOpBuilder::new(location)
                        .alignment(IntegerAttribute::new(
                            8,
                            IntegerType::new(&context, 64).into(),
                        ))
                        .array_size(alloca_size)
                        .res(ptr_type)
                        .build()
                        .into(),
                );

                block.append_operation(func::r#return(&[], location));

                let region = Region::new();
                region.append_block(block);
                region
            },
            &[],
            location,
        ));

        convert_module(&context, &mut module);

        assert!(module.as_operation().verify());
        insta::assert_display_snapshot!(module.as_operation());
    }

Roadmap to v1

Todo

  • Better namespaces

IR

  • Affine expression
  • Affine map
  • Attribute
  • Block
  • Identifier
  • Module
  • Operation
  • Operation state
  • Region
  • Type
    • Function
    • Integer
    • Memref
    • Tuple
    • Vector
  • Type ID
  • Value
    • Block argument
    • Operation result

Support

  • Location
  • Logical result
  • String ref

Pass

  • Pass
    • Built-in passes
  • Pass manager
  • Operation pass manager

JIT

  • Execution engine

Dialect

  • Dialect
  • Dialect handle
  • Dialect registry

Dialects

  • async
  • cf
  • gpu
  • linalg
  • llvm
  • pdl
  • quant
  • scf
  • shape
  • sparse_tensor
  • tensor

Future work

  • Diagnostics
  • Raw object conversion
  • External pass

Issues to be resolved

Investigate segfaults due to wrong API usage.

Sometimes using the api the wrong way, like passing a the wrong type to memref, or other dialects can cause a segfault (i think specially with the pass manager and sometimes verify(?)).

I'll try to have minimal examples when i can, but just wanted to mention i noticed segfaults during my development using melior.

Generation of dialect bindings with TableGen and proc macro

The Python bindings for MLIR dialects are automatically generated using a TableGen backend (see OpPythonBindingGen). This made me wonder if this could be done for Rust/melior.

Just for fun, I implemented a TableGen backend as a proc macro crate.

  • I ported old tablegen-rs to modern LLVM and created a wrapper crate: tblgen-rs
  • And created a proc macro crate to generate melior bindings from TableGen ODS: dialectgen-rs, which can be used together with melior (the code is pure chaos, I wrote it in a few days, it's a proof of concept).

When calling dialect!("MyDialects.td");, where MyDialects.td is a file containing

include "mlir/Dialect/Arith/IR/ArithOps.td"
include "mlir/Dialect/Func/IR/FuncOps.td"
include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.td"
include "mlir/Dialect/Index/IR/IndexOps.td"
include "mlir/Dialect/LLVMIR/LLVMOps.td"
include "mlir/Dialect/MemRef/IR/MemRefOps.td"
include "mlir/Dialect/SCF/IR/SCFOps.td"
include "mlir/Dialect/PDL/IR/PDLOps.td"
include "mlir/Dialect/Math/IR/MathOps.td"
include "mlir/Dialect/GPU/IR/GPUOps.td"
include "mlir/Dialect/Linalg/IR/LinalgOps.td"
include "mlir/Dialect/Async/IR/AsyncOps.td"
include "mlir/Dialect/Quant/QuantOps.td"
include "mlir/Dialect/Shape/IR/ShapeOps.td"
include "mlir/Dialect/Tensor/IR/TensorOps.td"

it generates the following:

  • A newtype struct for each operation, e.g.
pub struct FuncOp<'c> {
        operation: ::melior::ir::operation::Operation<'c>,
}
  • Getters and setters for operands, results and attributes specified in the dialect, e.g.
        pub fn sym_name<'a>(&'a self) -> ::melior::ir::attribute::StringAttribute<'c> {
            self.operation
                .attribute("sym_name")
                .expect("operation should have attribute sym_name")
                .try_into()
                .expect("sym_name should be a ::melior::ir::attribute::StringAttribute")
        }
        pub fn set_sym_name(
            &mut self,
            value: ::melior::ir::attribute::StringAttribute<'c>,
        ) {
            self.operation.set_attribute("sym_name", value);
        }
  • A typestate builder, which is a wrapper around OperationBuilder for each Op that ensures all required operands, results and attributes are set at compile time before allowing you to build the Op, e.g.
#[doc(hidden)]
    pub struct ReturnOpOperands;
    #[doc(hidden)]
    pub struct ReturnOpNoOperands;
    pub struct ReturnOpBuilder<'c, Toperands> {
        #[doc(hidden)]
        builder: ::melior::ir::operation::OperationBuilder<'c>,
        #[doc(hidden)]
        context: &'c ::melior::Context,
        #[doc(hidden)]
        _operands: ::std::marker::PhantomData<Toperands>,
    }
    impl<'c, 'a> ReturnOpBuilder<'c, ReturnOpNoOperands> {
        pub fn new(
            context: &'c ::melior::Context,
            location: ::melior::ir::Location<'c>,
        ) -> Self {
            Self {
                context,
                builder: ::melior::ir::operation::OperationBuilder::new(
                    "func.return",
                    location,
                ),
                _operands: ::std::marker::PhantomData,
            }
        }
    }
    impl<'c, 'a> ReturnOpBuilder<'c, ReturnOpNoOperands> {
        pub fn operands(
            mut self,
            operands: &::melior::ir::Value<'c, 'a>,
        ) -> ReturnOpBuilder<'c, ReturnOpOperands> {
            self.builder = self.builder.add_operand(operands);
            let Self { context, mut builder, _operands } = self;
            ReturnOpBuilder {
                context,
                builder,
                _operands: ::std::marker::PhantomData,
            }
        }
    }
    impl<'c, 'a> ReturnOpBuilder<'c, ReturnOpOperands> {
        pub fn build(self) -> ReturnOp<'c> {
            self.builder.build().try_into().expect("should be a valid ReturnOp")
        }
    }
  • A default constructor similar to those provided by melior
    pub fn r#return<'c, 'a: 'c>(
        context: &'c Context,
        operands: &::melior::ir::Value<'c, 'a>,
        location: Location<'c>,
    ) -> ReturnOp<'c> {
        ReturnOp::builder(context, location).operands(operands).build()
    }

Although I very much enjoyed writing this, I do realize that this might be a bit absurd (the macro generates around 100000 LoC) and I don't know how useful this will be. What are your thoughts?

Remaining work

  • Refactor codes in the melior-macro crate.
  • Prefer Result's rather than panics.
    • At least in macros (#283).
    • We haven't decided yet for methods of "typed" operations in dialect modules as described in #274. panics removed in #286.
  • Remove or use unused codes in *Constraint types.
  • Add an OperationLike trait and implement it for Module and dialect-specific operations.
  • Write unit tests for representative operations.
    • Generate unit tests for each dialect-specific operation and builder functions?
  • Support VariadicOfVariadic: argument should be &[&[Value]] instead of &[Value] and segment sizes attribute should be derived from this, see this MLIR diff (low priority issue).
  • Move some common functionality (e.g. segment computation) to separate function to reduce code size?

ExternalPass API

As mentioned in #24, support for the ExternalPass API is considered future work.

Since I wanted to explore the possibility of writing MLIR passes in Rust, I have made a working implementation of this as a prototype. I've created an ExternalPass trait with the pass callbacks as methods. Any struct that implements the ExternalPass trait can be converted into a Pass using a provided method. I would like to extend this implementation in the future to maybe even allow Rust closures to be converted into a Pass using this API (e.g. by implementing ExternalPass for any stuct that implements Fn/FnMut/FnOnce).

Would you be interested in merging such implementation? If so, I will clean up the code and create a merge request soon.

As a side note, I've been experimenting with PDL, which enables declarative passes using pattern description. Unfortunately, this is not supported by the MLIR CAPI, but beaver has extended the CAPI to enable this. I've done a similar thing and extended both mlir-sys and melior to support PDL modules and pattern sets. However, I'm not sure if it is desirable to extend mlir-sys with features that are not already part of the CAPI. I might make a separate issue for this.

API for ArrayAttr

I see there's a rustic API for DenseI64ArrayAttribute & DenseI32ArrayAttribute, but I don't see one for a normal ArrayAttribute. There are a bunch of functions in the vector dialect that use attributes with the ::mlir::ArrayAttr type, and using a dense array here will result in an error.

Rust to build the op:

            let vector_extract_op = OperationBuilder::new("vector.extract", location)
                .add_attributes(&[(
                    Identifier::new(&context, "position"),
                    DenseI64ArrayAttribute::new(&context, &[0]).into()
                )])
                .add_operands(&[block.argument(0).unwrap().into()])
                .add_results(&[float32_type])
                .build();

Resulting MLIR for this op:

%0 = "vector.extract"(%arg0) {position = array<i64: 0>} : (vector<2xf32>) -> f32
    "func.return"(%0) : (f32) -> ()

error:

error: 'vector.extract' op attribute 'position' failed to satisfy constraint: 64-bit integer array attribute
thread 'main' panicked at 'assertion failed: module_op.verify()', src/main.rs:79:5

Also, while playing with melior I've noticed a lot of structs / functions are marked private or pub(crate). I think it may be more ergonomic to mark these pub & unsafe to let users get the raw MLIR handles while the safe API is still being worked on. For example, I believe this should work:

mlir_sys::mlirArrayAttrGet(context.to_raw(), 1, &IntegerAttribute::new(1, index_type).to_raw());

IntegerAttribute::to_raw() is safe & public, but Context::to_raw() is pub(crate) unsafe!

Problem with value lifetimes when trying to use a symbol table

Hey, first of all, thank you for this crate!

I'm trying it out by implementing a small toy project. My initial plan was to use a simple symbol table, e.g. HashMap<String, mlir::Value<'c>> (where 'c is the Context lifetime) to translate variables (e.g. similar to how it is done in the MLIR example project).

However, my naive approach doesn't work because values resulting from operations are bound to the lifetime of the operation which is itself tied to the lifetime of the block.

A simple hack making it borrow check is to extend the following lifetimes to the context lifetime 'c like so:

-    pub fn append_operation(&self, operation: Operation) -> OperationRef {
+    pub fn append_operation(&self, operation: Operation) -> OperationRef<'c> {
-pub struct OperationRef<'a> {
+pub struct OperationRef<'c> {
     raw: MlirOperation,
-    _reference: PhantomData<&'a Operation<'a>>,
+    _reference: PhantomData<&'c Context>,
 }

However, I guess this is not desired. Do you have any advice on how to work around this problem or what changes should perhaps be made to the library, if any? Thanks a lot in advance!

melior 0.14.1 is broken if Cargo.lock contains melior-macro 0.8.0 and not 0.8.1

error: invalid field table_gen
  --> /Users/edgar/.cargo/registry/src/index.crates.io-6f17d22bba15001f/melior-0.14.1/src/dialect/ods.rs:44:16
   |
44 |     table_gen: r#"include "mlir/Dialect/Func/IR/FuncOps.td""#
   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: invalid field table_gen
  --> /Users/edgar/.cargo/registry/src/index.crates.io-6f17d22bba15001f/melior-0.14.1/src/dialect/ods.rs:48:16
   |
48 |     table_gen: r#"include "mlir/Dialect/Index/IR/IndexOps.td""#
   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...

For more information about this error, try `rustc --explain E0433`.
error: could not compile `melior` (lib) due to 30 previous errors
warning: build failed, waiting for other jobs to finish...

Updating melior to 0.14.1 will break it if you do not run cargo update or cargo update -p melior-macro.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.