diff --git a/cspell.json b/cspell.json index 06b341b8b2..3caaaf4be4 100644 --- a/cspell.json +++ b/cspell.json @@ -50,6 +50,7 @@ "rustc", "sccp", "shli", + "shrsi", "shrui", "sigabrt", "signless", diff --git a/melior/src/block_ext.rs b/melior/src/block_ext.rs deleted file mode 100644 index 39e5095e78..0000000000 --- a/melior/src/block_ext.rs +++ /dev/null @@ -1,657 +0,0 @@ -//! Trait that extends the melior Block type to aid in codegen and consistency. - -use core::fmt; - -use crate::{ - dialect::{ - arith::{self, CmpiPredicate}, - llvm::r#type::pointer, - ods, - }, - ir::{ - attribute::{ - DenseI32ArrayAttribute, DenseI64ArrayAttribute, IntegerAttribute, TypeAttribute, - }, - r#type::IntegerType, - Attribute, Block, Location, Operation, Type, Value, ValueLike, - }, - Context, Error, -}; - -/// Index types for LLVM GEP. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum GepIndex<'c, 'a> { - /// A compile time known index. - Const(i32), - /// A runtime value index. - Value(Value<'c, 'a>), -} - -pub trait BlockExt<'ctx> { - fn arg(&self, idx: usize) -> Result, Error>; - - /// Creates an arith.cmpi operation. - fn cmpi( - &self, - context: &'ctx Context, - pred: CmpiPredicate, - lhs: Value<'ctx, '_>, - rhs: Value<'ctx, '_>, - location: Location<'ctx>, - ) -> Result, Error>; - - fn extui( - &self, - lhs: Value<'ctx, '_>, - target_type: Type<'ctx>, - location: Location<'ctx>, - ) -> Result, Error>; - - fn extsi( - &self, - lhs: Value<'ctx, '_>, - target_type: Type<'ctx>, - location: Location<'ctx>, - ) -> Result, Error>; - - fn trunci( - &self, - lhs: Value<'ctx, '_>, - target_type: Type<'ctx>, - location: Location<'ctx>, - ) -> Result, Error>; - - fn shrui( - &self, - lhs: Value<'ctx, '_>, - rhs: Value<'ctx, '_>, - location: Location<'ctx>, - ) -> Result, Error>; - - fn shli( - &self, - lhs: Value<'ctx, '_>, - rhs: Value<'ctx, '_>, - location: Location<'ctx>, - ) -> Result, Error>; - - fn addi( - &self, - lhs: Value<'ctx, '_>, - rhs: Value<'ctx, '_>, - location: Location<'ctx>, - ) -> Result, Error>; - - fn subi( - &self, - lhs: Value<'ctx, '_>, - rhs: Value<'ctx, '_>, - location: Location<'ctx>, - ) -> Result, Error>; - - fn divui( - &self, - lhs: Value<'ctx, '_>, - rhs: Value<'ctx, '_>, - location: Location<'ctx>, - ) -> Result, Error>; - - fn divsi( - &self, - lhs: Value<'ctx, '_>, - rhs: Value<'ctx, '_>, - location: Location<'ctx>, - ) -> Result, Error>; - - fn xori( - &self, - lhs: Value<'ctx, '_>, - rhs: Value<'ctx, '_>, - location: Location<'ctx>, - ) -> Result, Error>; - - fn ori( - &self, - lhs: Value<'ctx, '_>, - rhs: Value<'ctx, '_>, - location: Location<'ctx>, - ) -> Result, Error>; - - fn andi( - &self, - lhs: Value<'ctx, '_>, - rhs: Value<'ctx, '_>, - location: Location<'ctx>, - ) -> Result, Error>; - - fn muli( - &self, - lhs: Value<'ctx, '_>, - rhs: Value<'ctx, '_>, - location: Location<'ctx>, - ) -> Result, Error>; - - /// Appends the operation and returns the first result. - fn append_op_result(&self, operation: Operation<'ctx>) -> Result, Error>; - - /// Creates a constant of the given integer bit width. Do not use for felt252. - fn const_int( - &self, - context: &'ctx Context, - location: Location<'ctx>, - value: T, - bits: u32, - ) -> Result, Error>; - - /// Creates a constant of the given integer type. Do not use for felt252. - fn const_int_from_type( - &self, - context: &'ctx Context, - location: Location<'ctx>, - value: T, - int_type: Type<'ctx>, - ) -> Result, Error>; - - /// Uses a llvm::extract_value operation to return the value at the given index of a container (e.g struct). - fn extract_value( - &self, - context: &'ctx Context, - location: Location<'ctx>, - container: Value<'ctx, '_>, - value_type: Type<'ctx>, - index: usize, - ) -> Result, Error>; - - /// Uses a llvm::insert_value operation to insert the value at the given index of a container (e.g struct), - /// the result is the container with the value. - fn insert_value( - &self, - context: &'ctx Context, - location: Location<'ctx>, - container: Value<'ctx, '_>, - value: Value<'ctx, '_>, - index: usize, - ) -> Result, Error>; - - /// Uses a llvm::insert_value operation to insert the values starting from index 0 into a container (e.g struct), - /// the result is the container with the values. - fn insert_values<'block>( - &'block self, - context: &'ctx Context, - location: Location<'ctx>, - container: Value<'ctx, 'block>, - values: &[Value<'ctx, 'block>], - ) -> Result, Error>; - - /// Loads a value from the given addr. - fn load( - &self, - context: &'ctx Context, - location: Location<'ctx>, - addr: Value<'ctx, '_>, - value_type: Type<'ctx>, - ) -> Result, Error>; - - /// Allocates the given number of elements of type in memory on the stack, returning a opaque pointer. - fn alloca( - &self, - context: &'ctx Context, - location: Location<'ctx>, - element_type: Type<'ctx>, - element_count: Value<'ctx, '_>, - align: usize, - ) -> Result, Error>; - - /// Allocates one element of the given type in memory on the stack, returning a opaque pointer. - fn alloca1( - &self, - context: &'ctx Context, - location: Location<'ctx>, - element_type: Type<'ctx>, - align: usize, - ) -> Result, Error>; - - /// Allocates one integer of the given bit width. - fn alloca_int( - &self, - context: &'ctx Context, - location: Location<'ctx>, - bits: u32, - align: usize, - ) -> Result, Error>; - - /// Stores a value at the given addr. - fn store( - &self, - context: &'ctx Context, - location: Location<'ctx>, - addr: Value<'ctx, '_>, - value: Value<'ctx, '_>, - ) -> Result<(), Error>; - - /// Creates a memcpy operation. - fn memcpy( - &self, - context: &'ctx Context, - location: Location<'ctx>, - src: Value<'ctx, '_>, - dst: Value<'ctx, '_>, - len_bytes: Value<'ctx, '_>, - ); - - /// Creates a getelementptr operation. Returns a pointer to the indexed element. - /// This method allows combining both compile time indexes and runtime value indexes. - /// - /// See: - /// - https://llvm.org/docs/LangRef.html#getelementptr-instruction - /// - https://llvm.org/docs/GetElementPtr.html - /// - /// Get Element Pointer is used to index into pointers, it uses the given - /// element type to compute the offsets, it allows indexing deep into a structure (field of field of a ptr for example), - /// this is why it accepts a array of indexes, it indexes through the list, offsetting depending on the element type, - /// for example it knows when you index into a struct field, the following index will use the struct field type for offsets, etc. - /// - /// Address computation is done at compile time. - /// - /// Note: This GEP sets the inbounds attribute: - /// - /// The base pointer has an in bounds address of the allocated object that it is based on. This means that it points into that allocated object, or to its end. Note that the object does not have to be live anymore; being in-bounds of a deallocated object is sufficient. - /// - /// During the successive addition of offsets to the address, the resulting pointer must remain in bounds of the allocated object at each step. - fn gep( - &self, - context: &'ctx Context, - location: Location<'ctx>, - ptr: Value<'ctx, '_>, - indexes: &[GepIndex<'ctx, '_>], - element_type: Type<'ctx>, - ) -> Result, Error>; -} - -impl<'ctx> BlockExt<'ctx> for Block<'ctx> { - #[inline] - fn arg(&self, idx: usize) -> Result, Error> { - Ok(self.argument(idx)?.into()) - } - - #[inline] - fn cmpi( - &self, - context: &'ctx Context, - pred: CmpiPredicate, - lhs: Value<'ctx, '_>, - rhs: Value<'ctx, '_>, - location: Location<'ctx>, - ) -> Result, Error> { - self.append_op_result(arith::cmpi(context, pred, lhs, rhs, location)) - } - - #[inline] - fn extsi( - &self, - lhs: Value<'ctx, '_>, - target_type: Type<'ctx>, - location: Location<'ctx>, - ) -> Result, Error> { - self.append_op_result(arith::extsi(lhs, target_type, location)) - } - - #[inline] - fn extui( - &self, - lhs: Value<'ctx, '_>, - target_type: Type<'ctx>, - location: Location<'ctx>, - ) -> Result, Error> { - self.append_op_result(arith::extui(lhs, target_type, location)) - } - - #[inline] - fn trunci( - &self, - lhs: Value<'ctx, '_>, - target_type: Type<'ctx>, - location: Location<'ctx>, - ) -> Result, Error> { - self.append_op_result(arith::trunci(lhs, target_type, location)) - } - - #[inline] - fn shli( - &self, - lhs: Value<'ctx, '_>, - rhs: Value<'ctx, '_>, - location: Location<'ctx>, - ) -> Result, Error> { - self.append_op_result(arith::shli(lhs, rhs, location)) - } - - #[inline] - fn shrui( - &self, - lhs: Value<'ctx, '_>, - rhs: Value<'ctx, '_>, - location: Location<'ctx>, - ) -> Result, Error> { - self.append_op_result(arith::shrui(lhs, rhs, location)) - } - - #[inline] - fn addi( - &self, - lhs: Value<'ctx, '_>, - rhs: Value<'ctx, '_>, - location: Location<'ctx>, - ) -> Result, Error> { - self.append_op_result(arith::addi(lhs, rhs, location)) - } - - #[inline] - fn subi( - &self, - lhs: Value<'ctx, '_>, - rhs: Value<'ctx, '_>, - location: Location<'ctx>, - ) -> Result, Error> { - self.append_op_result(arith::subi(lhs, rhs, location)) - } - - #[inline] - fn divui( - &self, - lhs: Value<'ctx, '_>, - rhs: Value<'ctx, '_>, - location: Location<'ctx>, - ) -> Result, Error> { - self.append_op_result(arith::divui(lhs, rhs, location)) - } - - #[inline] - fn divsi( - &self, - lhs: Value<'ctx, '_>, - rhs: Value<'ctx, '_>, - location: Location<'ctx>, - ) -> Result, Error> { - self.append_op_result(arith::divsi(lhs, rhs, location)) - } - - #[inline] - fn xori( - &self, - lhs: Value<'ctx, '_>, - rhs: Value<'ctx, '_>, - location: Location<'ctx>, - ) -> Result, Error> { - self.append_op_result(arith::xori(lhs, rhs, location)) - } - - #[inline] - fn ori( - &self, - lhs: Value<'ctx, '_>, - rhs: Value<'ctx, '_>, - location: Location<'ctx>, - ) -> Result, Error> { - self.append_op_result(arith::ori(lhs, rhs, location)) - } - - #[inline] - fn andi( - &self, - lhs: Value<'ctx, '_>, - rhs: Value<'ctx, '_>, - location: Location<'ctx>, - ) -> Result, Error> { - self.append_op_result(arith::andi(lhs, rhs, location)) - } - - #[inline] - fn muli( - &self, - lhs: Value<'ctx, '_>, - rhs: Value<'ctx, '_>, - location: Location<'ctx>, - ) -> Result, Error> { - self.append_op_result(arith::muli(lhs, rhs, location)) - } - - #[inline] - fn const_int( - &self, - context: &'ctx Context, - location: Location<'ctx>, - value: T, - bits: u32, - ) -> Result, Error> { - let ty = IntegerType::new(context, bits).into(); - self.append_op_result( - ods::arith::constant( - context, - ty, - Attribute::parse(context, &format!("{} : {}", value, ty)).unwrap(), - location, - ) - .into(), - ) - } - - #[inline] - fn const_int_from_type( - &self, - context: &'ctx Context, - location: Location<'ctx>, - value: T, - ty: Type<'ctx>, - ) -> Result, Error> { - self.append_op_result( - ods::arith::constant( - context, - ty, - Attribute::parse(context, &format!("{} : {}", value, ty)).unwrap(), - location, - ) - .into(), - ) - } - - #[inline] - fn extract_value( - &self, - context: &'ctx Context, - location: Location<'ctx>, - container: Value<'ctx, '_>, - value_type: Type<'ctx>, - index: usize, - ) -> Result, Error> { - self.append_op_result( - ods::llvm::extractvalue( - context, - value_type, - container, - DenseI64ArrayAttribute::new(context, &[index.try_into().unwrap()]).into(), - location, - ) - .into(), - ) - } - - #[inline] - fn insert_value( - &self, - context: &'ctx Context, - location: Location<'ctx>, - container: Value<'ctx, '_>, - value: Value<'ctx, '_>, - index: usize, - ) -> Result, Error> { - self.append_op_result( - ods::llvm::insertvalue( - context, - container.r#type(), - container, - value, - DenseI64ArrayAttribute::new(context, &[index.try_into().unwrap()]).into(), - location, - ) - .into(), - ) - } - - #[inline] - fn insert_values<'block>( - &'block self, - context: &'ctx Context, - location: Location<'ctx>, - mut container: Value<'ctx, 'block>, - values: &[Value<'ctx, 'block>], - ) -> Result, Error> { - for (i, value) in values.iter().enumerate() { - container = self.insert_value(context, location, container, *value, i)?; - } - Ok(container) - } - - #[inline] - fn store( - &self, - context: &'ctx Context, - location: Location<'ctx>, - addr: Value<'ctx, '_>, - value: Value<'ctx, '_>, - ) -> Result<(), Error> { - self.append_operation(ods::llvm::store(context, value, addr, location).into()); - Ok(()) - } - - // Use this only when returning the result. Otherwise, append_operation is fine. - #[inline] - fn append_op_result(&self, operation: Operation<'ctx>) -> Result, Error> { - Ok(self.append_operation(operation).result(0)?.into()) - } - - #[inline] - fn load( - &self, - context: &'ctx Context, - location: Location<'ctx>, - addr: Value<'ctx, '_>, - value_type: Type<'ctx>, - ) -> Result, Error> { - self.append_op_result(ods::llvm::load(context, value_type, addr, location).into()) - } - - #[inline] - fn memcpy( - &self, - context: &'ctx Context, - location: Location<'ctx>, - src: Value<'ctx, '_>, - dst: Value<'ctx, '_>, - len_bytes: Value<'ctx, '_>, - ) { - self.append_operation( - ods::llvm::intr_memcpy( - context, - dst, - src, - len_bytes, - IntegerAttribute::new(IntegerType::new(context, 1).into(), 0), - location, - ) - .into(), - ); - } - - #[inline] - fn alloca( - &self, - context: &'ctx Context, - location: Location<'ctx>, - element_type: Type<'ctx>, - element_count: Value<'ctx, '_>, - align: usize, - ) -> Result, Error> { - let mut op = ods::llvm::alloca( - context, - pointer(context, 0), - element_count, - TypeAttribute::new(element_type), - location, - ); - - op.set_elem_type(TypeAttribute::new(element_type)); - op.set_alignment(IntegerAttribute::new( - IntegerType::new(context, 64).into(), - align.try_into().unwrap(), - )); - - self.append_op_result(op.into()) - } - - #[inline] - fn alloca1( - &self, - context: &'ctx Context, - location: Location<'ctx>, - element_type: Type<'ctx>, - align: usize, - ) -> Result, Error> { - let element_count = self.const_int(context, location, 1, 64)?; - self.alloca(context, location, element_type, element_count, align) - } - - #[inline] - fn alloca_int( - &self, - context: &'ctx Context, - location: Location<'ctx>, - bits: u32, - align: usize, - ) -> Result, Error> { - let element_count = self.const_int(context, location, 1, 64)?; - self.alloca( - context, - location, - IntegerType::new(context, bits).into(), - element_count, - align, - ) - } - - #[inline] - fn gep( - &self, - context: &'ctx Context, - location: Location<'ctx>, - ptr: Value<'ctx, '_>, - indexes: &[GepIndex<'ctx, '_>], - element_type: Type<'ctx>, - ) -> Result, Error> { - let mut dynamic_indices = Vec::with_capacity(indexes.len()); - let mut raw_constant_indices = Vec::with_capacity(indexes.len()); - - for index in indexes { - match index { - GepIndex::Const(idx) => raw_constant_indices.push(*idx), - GepIndex::Value(value) => { - dynamic_indices.push(*value); - raw_constant_indices.push(i32::MIN); // marker for dynamic index - } - } - } - - let mut op = ods::llvm::getelementptr( - context, - pointer(context, 0), - ptr, - &dynamic_indices, - DenseI32ArrayAttribute::new(context, &raw_constant_indices), - TypeAttribute::new(element_type), - location, - ); - op.set_inbounds(Attribute::unit(context)); - - self.append_op_result(op.into()) - } -} diff --git a/melior/src/error.rs b/melior/src/error.rs index ab5caee550..4f2e5f2bc9 100644 --- a/melior/src/error.rs +++ b/melior/src/error.rs @@ -10,6 +10,7 @@ use std::{ pub enum Error { AttributeExpected(&'static str, String), AttributeNotFound(String), + AttributeParse(String), BlockArgumentExpected(String), ElementExpected { r#type: &'static str, @@ -41,6 +42,9 @@ impl Display for Error { Self::AttributeNotFound(name) => { write!(formatter, "attribute {name} not found") } + Self::AttributeParse(string) => { + write!(formatter, "failed to parse attribute: {string}") + } Self::BlockArgumentExpected(value) => { write!(formatter, "block argument expected: {value}") } diff --git a/melior/src/helpers.rs b/melior/src/helpers.rs new file mode 100644 index 0000000000..caa7fa607c --- /dev/null +++ b/melior/src/helpers.rs @@ -0,0 +1,9 @@ +//! Traits that extends the [`Block`](crate::ir::Block) type to aid in code generation and consistency. + +mod arith; +mod builtin; +mod llvm; + +pub use arith::ArithBlockExt; +pub use builtin::BuiltinBlockExt; +pub use llvm::{GepIndex, LlvmBlockExt}; diff --git a/melior/src/helpers/arith.rs b/melior/src/helpers/arith.rs new file mode 100644 index 0000000000..5a120b6a84 --- /dev/null +++ b/melior/src/helpers/arith.rs @@ -0,0 +1,199 @@ +use super::builtin::BuiltinBlockExt; +use crate::{ + dialect::{ + arith::{ + addi, andi, cmpi, divsi, divui, extsi, extui, muli, ori, shli, shrsi, shrui, subi, + trunci, xori, CmpiPredicate, + }, + ods, + }, + ir::{r#type::IntegerType, Attribute, Block, Location, Type, Value}, + Context, Error, +}; +use core::fmt::Display; + +macro_rules! binary_operation_declaration { + ($name:ident, $documentation:literal) => { + #[doc=$documentation] + fn $name( + &self, + lhs: Value<'c, '_>, + rhs: Value<'c, '_>, + location: Location<'c>, + ) -> Result, Error>; + }; +} + +macro_rules! binary_operation { + ($name:ident) => { + #[inline] + fn $name( + &self, + lhs: Value<'c, '_>, + rhs: Value<'c, '_>, + location: Location<'c>, + ) -> Result, Error> { + self.append_op_result($name(lhs, rhs, location)) + } + }; +} + +/// A block extension for an `arith` dialect. +pub trait ArithBlockExt<'c>: BuiltinBlockExt<'c> { + binary_operation_declaration!(addi, "Creates an `arith.addi` operation."); + binary_operation_declaration!(andi, "Creates an `arith.andi` operation."); + binary_operation_declaration!(divsi, "Creates an `arith.divsi` operation."); + binary_operation_declaration!(divui, "Creates an `arith.divui` operation."); + binary_operation_declaration!(muli, "Creates an `arith.muli` operation."); + binary_operation_declaration!(ori, "Creates an `arith.ori` operation."); + binary_operation_declaration!(shli, "Creates an `arith.shli` operation."); + binary_operation_declaration!(shrsi, "Creates an `arith.shrsi` operation."); + binary_operation_declaration!(shrui, "Creates an `arith.shrui` operation."); + binary_operation_declaration!(subi, "Creates an `arith.subi` operation."); + binary_operation_declaration!(xori, "Creates an `arith.xori` operation."); + + /// Creates an `arith.cmpi` operation. + fn cmpi( + &self, + context: &'c Context, + pred: CmpiPredicate, + lhs: Value<'c, '_>, + rhs: Value<'c, '_>, + location: Location<'c>, + ) -> Result, Error>; + + /// Creates an `arith.extui` operation. + fn extui( + &self, + lhs: Value<'c, '_>, + target_type: Type<'c>, + location: Location<'c>, + ) -> Result, Error>; + + /// Creates an `arith.extui` operation. + fn extsi( + &self, + lhs: Value<'c, '_>, + target_type: Type<'c>, + location: Location<'c>, + ) -> Result, Error>; + + /// Creates an `arith.extui` operation. + fn trunci( + &self, + lhs: Value<'c, '_>, + target_type: Type<'c>, + location: Location<'c>, + ) -> Result, Error>; + + /// Creates a constant of the given integer bit width. + fn const_int( + &self, + context: &'c Context, + location: Location<'c>, + value: impl Display, + bits: u32, + ) -> Result, Error>; + + /// Creates a constant of the given integer type. + fn const_int_from_type( + &self, + context: &'c Context, + location: Location<'c>, + value: impl Display, + r#type: Type<'c>, + ) -> Result, Error>; +} + +impl<'c> ArithBlockExt<'c> for Block<'c> { + binary_operation!(addi); + binary_operation!(andi); + binary_operation!(divsi); + binary_operation!(divui); + binary_operation!(muli); + binary_operation!(ori); + binary_operation!(shli); + binary_operation!(shrsi); + binary_operation!(shrui); + binary_operation!(subi); + binary_operation!(xori); + + #[inline] + fn cmpi( + &self, + context: &'c Context, + predicate: CmpiPredicate, + lhs: Value<'c, '_>, + rhs: Value<'c, '_>, + location: Location<'c>, + ) -> Result, Error> { + self.append_op_result(cmpi(context, predicate, lhs, rhs, location)) + } + + #[inline] + fn extsi( + &self, + value: Value<'c, '_>, + target_type: Type<'c>, + location: Location<'c>, + ) -> Result, Error> { + self.append_op_result(extsi(value, target_type, location)) + } + + #[inline] + fn extui( + &self, + value: Value<'c, '_>, + target_type: Type<'c>, + location: Location<'c>, + ) -> Result, Error> { + self.append_op_result(extui(value, target_type, location)) + } + + #[inline] + fn trunci( + &self, + value: Value<'c, '_>, + target_type: Type<'c>, + location: Location<'c>, + ) -> Result, Error> { + self.append_op_result(trunci(value, target_type, location)) + } + + #[inline] + fn const_int( + &self, + context: &'c Context, + location: Location<'c>, + value: impl Display, + bits: u32, + ) -> Result, Error> { + self.const_int_from_type( + context, + location, + value, + IntegerType::new(context, bits).into(), + ) + } + + #[inline] + fn const_int_from_type( + &self, + context: &'c Context, + location: Location<'c>, + value: impl Display, + r#type: Type<'c>, + ) -> Result, Error> { + let attribute = format!("{value} : {type}"); + + self.append_op_result( + ods::arith::constant( + context, + r#type, + Attribute::parse(context, &attribute).ok_or(Error::AttributeParse(attribute))?, + location, + ) + .into(), + ) + } +} diff --git a/melior/src/helpers/builtin.rs b/melior/src/helpers/builtin.rs new file mode 100644 index 0000000000..778b75275b --- /dev/null +++ b/melior/src/helpers/builtin.rs @@ -0,0 +1,25 @@ +use crate::{ + ir::{Block, Operation, Value}, + Error, +}; + +/// A block extension for a `builtin` dialect and constructs. +pub trait BuiltinBlockExt<'c> { + /// Returns a block argument as a value. + fn arg(&self, index: usize) -> Result, Error>; + + /// Appends an operation and returns its first value. + fn append_op_result(&self, operation: Operation<'c>) -> Result, Error>; +} + +impl<'c> BuiltinBlockExt<'c> for Block<'c> { + #[inline] + fn arg(&self, index: usize) -> Result, Error> { + Ok(self.argument(index)?.into()) + } + + #[inline] + fn append_op_result(&self, operation: Operation<'c>) -> Result, Error> { + Ok(self.append_operation(operation).result(0)?.into()) + } +} diff --git a/melior/src/helpers/llvm.rs b/melior/src/helpers/llvm.rs new file mode 100644 index 0000000000..4209bc5b9e --- /dev/null +++ b/melior/src/helpers/llvm.rs @@ -0,0 +1,322 @@ +use super::arith::ArithBlockExt; +use super::builtin::BuiltinBlockExt; +use crate::{ + dialect::{llvm::r#type, ods}, + ir::{ + attribute::{ + DenseI32ArrayAttribute, DenseI64ArrayAttribute, IntegerAttribute, TypeAttribute, + }, + r#type::IntegerType, + Attribute, Block, Location, Type, Value, ValueLike, + }, + Context, Error, +}; + +/// An index for an `llvm.getelementptr` instruction. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum GepIndex<'c, 'a> { + /// A compile time known index. + Const(i32), + /// A runtime value index. + Value(Value<'c, 'a>), +} + +/// A block extension for an `llvm` dialect. +pub trait LlvmBlockExt<'c>: BuiltinBlockExt<'c> + ArithBlockExt<'c> { + /// Creates an `llvm.extractvalue` operation. + fn extract_value( + &self, + context: &'c Context, + location: Location<'c>, + container: Value<'c, '_>, + value_type: Type<'c>, + index: usize, + ) -> Result, Error>; + + /// Creates an `llvm.insertvalue` operation. + fn insert_value( + &self, + context: &'c Context, + location: Location<'c>, + container: Value<'c, '_>, + value: Value<'c, '_>, + index: usize, + ) -> Result, Error>; + + /// Creates an `llvm.insertvalue` operation that insert multiple elements into an aggregate + /// from the first index. + fn insert_values<'block>( + &'block self, + context: &'c Context, + location: Location<'c>, + container: Value<'c, 'block>, + values: &[Value<'c, 'block>], + ) -> Result, Error>; + + /// Creates an `llvm.load` operation. + fn load( + &self, + context: &'c Context, + location: Location<'c>, + addr: Value<'c, '_>, + value_type: Type<'c>, + ) -> Result, Error>; + + /// Creates an `llvm.alloca` operation. + fn alloca( + &self, + context: &'c Context, + location: Location<'c>, + element_type: Type<'c>, + element_count: Value<'c, '_>, + align: usize, + ) -> Result, Error>; + + /// Creates an `llvm.alloca` operation that allocates one element. + fn alloca1( + &self, + context: &'c Context, + location: Location<'c>, + r#type: Type<'c>, + align: usize, + ) -> Result, Error>; + + /// Creates an `llvm.alloca` operation that allocates one element of the given size of an integer. + fn alloca_int( + &self, + context: &'c Context, + location: Location<'c>, + bits: u32, + align: usize, + ) -> Result, Error>; + + /// Creates an `llvm.store` operation. + fn store( + &self, + context: &'c Context, + location: Location<'c>, + pointer: Value<'c, '_>, + value: Value<'c, '_>, + ) -> Result<(), Error>; + + /// Creates a memcpy operation. + fn memcpy( + &self, + context: &'c Context, + location: Location<'c>, + src: Value<'c, '_>, + dst: Value<'c, '_>, + len_bytes: Value<'c, '_>, + ); + + /// Creates an `llvm.getelementptr` operation. + /// + /// This method allows combining both compile time indexes and runtime value indexes. + fn gep( + &self, + context: &'c Context, + location: Location<'c>, + pointer: Value<'c, '_>, + indexes: &[GepIndex<'c, '_>], + element_type: Type<'c>, + ) -> Result, Error>; +} + +impl<'c> LlvmBlockExt<'c> for Block<'c> { + #[inline] + fn extract_value( + &self, + context: &'c Context, + location: Location<'c>, + container: Value<'c, '_>, + value_type: Type<'c>, + index: usize, + ) -> Result, Error> { + self.append_op_result( + ods::llvm::extractvalue( + context, + value_type, + container, + DenseI64ArrayAttribute::new(context, &[index as _]).into(), + location, + ) + .into(), + ) + } + + #[inline] + fn insert_value( + &self, + context: &'c Context, + location: Location<'c>, + container: Value<'c, '_>, + value: Value<'c, '_>, + index: usize, + ) -> Result, Error> { + self.append_op_result( + ods::llvm::insertvalue( + context, + container.r#type(), + container, + value, + DenseI64ArrayAttribute::new(context, &[index as _]).into(), + location, + ) + .into(), + ) + } + + #[inline] + fn insert_values<'block>( + &'block self, + context: &'c Context, + location: Location<'c>, + mut container: Value<'c, 'block>, + values: &[Value<'c, 'block>], + ) -> Result, Error> { + for (i, value) in values.iter().enumerate() { + container = self.insert_value(context, location, container, *value, i)?; + } + Ok(container) + } + + #[inline] + fn store( + &self, + context: &'c Context, + location: Location<'c>, + addr: Value<'c, '_>, + value: Value<'c, '_>, + ) -> Result<(), Error> { + self.append_operation(ods::llvm::store(context, value, addr, location).into()); + Ok(()) + } + + #[inline] + fn load( + &self, + context: &'c Context, + location: Location<'c>, + addr: Value<'c, '_>, + value_type: Type<'c>, + ) -> Result, Error> { + self.append_op_result(ods::llvm::load(context, value_type, addr, location).into()) + } + + #[inline] + fn memcpy( + &self, + context: &'c Context, + location: Location<'c>, + src: Value<'c, '_>, + dst: Value<'c, '_>, + len_bytes: Value<'c, '_>, + ) { + self.append_operation( + ods::llvm::intr_memcpy( + context, + dst, + src, + len_bytes, + IntegerAttribute::new(IntegerType::new(context, 1).into(), 0), + location, + ) + .into(), + ); + } + + #[inline] + fn alloca( + &self, + context: &'c Context, + location: Location<'c>, + element_type: Type<'c>, + element_count: Value<'c, '_>, + align: usize, + ) -> Result, Error> { + let mut operation = ods::llvm::alloca( + context, + r#type::pointer(context, 0), + element_count, + TypeAttribute::new(element_type), + location, + ); + + operation.set_elem_type(TypeAttribute::new(element_type)); + operation.set_alignment(IntegerAttribute::new( + IntegerType::new(context, 64).into(), + align as _, + )); + + self.append_op_result(operation.into()) + } + + #[inline] + fn alloca1( + &self, + context: &'c Context, + location: Location<'c>, + r#type: Type<'c>, + align: usize, + ) -> Result, Error> { + self.alloca( + context, + location, + r#type, + self.const_int(context, location, 1, 64)?, + align, + ) + } + + #[inline] + fn alloca_int( + &self, + context: &'c Context, + location: Location<'c>, + bits: u32, + align: usize, + ) -> Result, Error> { + self.alloca1( + context, + location, + IntegerType::new(context, bits).into(), + align, + ) + } + + #[inline] + fn gep( + &self, + context: &'c Context, + location: Location<'c>, + pointer: Value<'c, '_>, + indexes: &[GepIndex<'c, '_>], + element_type: Type<'c>, + ) -> Result, Error> { + let mut static_indices = Vec::with_capacity(indexes.len()); + let mut dynamic_indices = Vec::with_capacity(indexes.len()); + + for index in indexes { + match index { + GepIndex::Const(index) => static_indices.push(*index), + GepIndex::Value(value) => { + static_indices.push(i32::MIN); // marker for dynamic index + dynamic_indices.push(*value); + } + } + } + + let mut operation = ods::llvm::getelementptr( + context, + r#type::pointer(context, 0), + pointer, + &dynamic_indices, + DenseI32ArrayAttribute::new(context, &static_indices), + TypeAttribute::new(element_type), + location, + ); + operation.set_inbounds(Attribute::unit(context)); + + self.append_op_result(operation.into()) + } +} diff --git a/melior/src/lib.rs b/melior/src/lib.rs index cc2efe7b4d..230909ff0e 100644 --- a/melior/src/lib.rs +++ b/melior/src/lib.rs @@ -4,20 +4,18 @@ extern crate self as melior; #[macro_use] mod r#macro; -#[cfg(feature = "helpers")] -mod block_ext; mod context; pub mod diagnostic; pub mod dialect; mod error; mod execution_engine; +#[cfg(feature = "helpers")] +pub mod helpers; pub mod ir; mod logical_result; pub mod pass; mod string_ref; -#[cfg(feature = "helpers")] -pub use block_ext::{BlockExt, GepIndex}; #[cfg(test)] mod test; pub mod utility;