Skip to main content

melior/ir/operation/
builder.rs

1use super::Operation;
2use crate::{
3    context::Context,
4    ir::{Attribute, AttributeLike, Block, Identifier, Location, Region, Type, Value},
5    string_ref::StringRef,
6    Error,
7};
8use mlir_sys::{
9    mlirNamedAttributeGet, mlirOperationCreate, mlirOperationStateAddAttributes,
10    mlirOperationStateAddOperands, mlirOperationStateAddOwnedRegions, mlirOperationStateAddResults,
11    mlirOperationStateAddSuccessors, mlirOperationStateEnableResultTypeInference,
12    mlirOperationStateGet, MlirOperationState,
13};
14use std::{
15    marker::PhantomData,
16    mem::{forget, transmute, ManuallyDrop},
17};
18
19/// An operation builder.
20pub struct OperationBuilder<'c> {
21    raw: MlirOperationState,
22    _context: PhantomData<&'c Context>,
23}
24
25impl<'c> OperationBuilder<'c> {
26    /// Creates an operation builder.
27    pub fn new(name: &str, location: Location<'c>) -> Self {
28        Self {
29            raw: unsafe { mlirOperationStateGet(StringRef::new(name).to_raw(), location.to_raw()) },
30            _context: Default::default(),
31        }
32    }
33
34    /// Adds results.
35    pub fn add_results(mut self, results: &[Type<'c>]) -> Self {
36        unsafe {
37            mlirOperationStateAddResults(
38                &mut self.raw,
39                results.len() as isize,
40                results.as_ptr() as *const _,
41            )
42        }
43
44        self
45    }
46
47    /// Adds operands.
48    pub fn add_operands(mut self, operands: &[Value<'c, '_>]) -> Self {
49        unsafe {
50            mlirOperationStateAddOperands(
51                &mut self.raw,
52                operands.len() as isize,
53                operands.as_ptr() as *const _,
54            )
55        }
56
57        self
58    }
59
60    /// Adds regions.
61    pub fn add_regions<const N: usize>(mut self, regions: [Region<'c>; N]) -> Self {
62        unsafe {
63            mlirOperationStateAddOwnedRegions(
64                &mut self.raw,
65                regions.len() as isize,
66                regions.as_ptr() as *const _,
67            )
68        }
69
70        forget(regions);
71
72        self
73    }
74
75    /// Adds regions in a [`Vec`](std::vec::Vec).
76    pub fn add_regions_vec(mut self, regions: Vec<Region<'c>>) -> Self {
77        unsafe {
78            // This may fire with -D clippy::nursery, however, it is
79            // guaranteed by the std that ManuallyDrop<T> has the same layout as T
80            #[allow(clippy::transmute_undefined_repr)]
81            mlirOperationStateAddOwnedRegions(
82                &mut self.raw,
83                regions.len() as isize,
84                transmute::<Vec<Region>, Vec<ManuallyDrop<Region>>>(regions).as_ptr() as *const _,
85            )
86        }
87
88        self
89    }
90
91    /// Adds successor blocks.
92    // TODO Fix this to ensure blocks are alive while they are referenced by the
93    // operation.
94    pub fn add_successors(mut self, successors: &[&Block<'c>]) -> Self {
95        for block in successors {
96            unsafe {
97                mlirOperationStateAddSuccessors(&mut self.raw, 1, &[block.to_raw()] as *const _)
98            }
99        }
100
101        self
102    }
103
104    /// Adds attributes.
105    pub fn add_attributes(mut self, attributes: &[(Identifier<'c>, Attribute<'c>)]) -> Self {
106        for (identifier, attribute) in attributes {
107            unsafe {
108                mlirOperationStateAddAttributes(
109                    &mut self.raw,
110                    1,
111                    &[mlirNamedAttributeGet(
112                        identifier.to_raw(),
113                        attribute.to_raw(),
114                    )] as *const _,
115                )
116            }
117        }
118
119        self
120    }
121
122    /// Enables result type inference.
123    pub fn enable_result_type_inference(mut self) -> Self {
124        unsafe { mlirOperationStateEnableResultTypeInference(&mut self.raw) }
125
126        self
127    }
128
129    /// Builds an operation.
130    pub fn build(mut self) -> Result<Operation<'c>, Error> {
131        unsafe { Operation::from_option_raw(mlirOperationCreate(&mut self.raw)) }
132            .ok_or(Error::OperationBuild)
133    }
134}
135
136#[cfg(test)]
137mod tests {
138    use super::*;
139    use crate::{
140        ir::{Block, ValueLike},
141        test::create_test_context,
142    };
143
144    #[test]
145    fn new() {
146        let context = create_test_context();
147        context.set_allow_unregistered_dialects(true);
148
149        OperationBuilder::new("foo", Location::unknown(&context))
150            .build()
151            .unwrap();
152    }
153
154    #[test]
155    fn add_operands() {
156        let context = create_test_context();
157        context.set_allow_unregistered_dialects(true);
158
159        let location = Location::unknown(&context);
160        let r#type = Type::index(&context);
161        let block = Block::new(&[(r#type, location)]);
162        let argument = block.argument(0).unwrap().into();
163
164        OperationBuilder::new("foo", Location::unknown(&context))
165            .add_operands(&[argument])
166            .build()
167            .unwrap();
168    }
169
170    #[test]
171    fn add_results() {
172        let context = create_test_context();
173        context.set_allow_unregistered_dialects(true);
174
175        OperationBuilder::new("foo", Location::unknown(&context))
176            .add_results(&[Type::parse(&context, "i1").unwrap()])
177            .build()
178            .unwrap();
179    }
180
181    #[test]
182    fn add_regions() {
183        let context = create_test_context();
184        context.set_allow_unregistered_dialects(true);
185
186        OperationBuilder::new("foo", Location::unknown(&context))
187            .add_regions([Region::new()])
188            .build()
189            .unwrap();
190    }
191
192    #[test]
193    fn add_successors() {
194        let context = create_test_context();
195        context.set_allow_unregistered_dialects(true);
196
197        OperationBuilder::new("foo", Location::unknown(&context))
198            .add_successors(&[&Block::new(&[])])
199            .build()
200            .unwrap();
201    }
202
203    #[test]
204    fn add_attributes() {
205        let context = create_test_context();
206        context.set_allow_unregistered_dialects(true);
207
208        OperationBuilder::new("foo", Location::unknown(&context))
209            .add_attributes(&[(
210                Identifier::new(&context, "foo"),
211                Attribute::parse(&context, "unit").unwrap(),
212            )])
213            .build()
214            .unwrap();
215    }
216
217    #[test]
218    fn enable_result_type_inference() {
219        let context = create_test_context();
220        context.set_allow_unregistered_dialects(true);
221
222        let location = Location::unknown(&context);
223        let r#type = Type::index(&context);
224        let block = Block::new(&[(r#type, location)]);
225        let argument = block.argument(0).unwrap().into();
226
227        assert_eq!(
228            OperationBuilder::new("arith.addi", location)
229                .add_operands(&[argument, argument])
230                .enable_result_type_inference()
231                .build()
232                .unwrap()
233                .result(0)
234                .unwrap()
235                .r#type(),
236            r#type,
237        );
238    }
239}