melior/helpers/
llvm.rs

1use super::{arith::ArithBlockExt, builtin::BuiltinBlockExt};
2use crate::{
3    Context, Error,
4    dialect::{llvm::r#type, ods},
5    ir::{
6        Block, Location, Type, Value, ValueLike,
7        attribute::{
8            DenseI32ArrayAttribute, DenseI64ArrayAttribute, IntegerAttribute, TypeAttribute,
9        },
10        block::BlockLike,
11        r#type::IntegerType,
12    },
13};
14
15/// An index for an `llvm.getelementptr` instruction.
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum GepIndex<'c, 'a> {
18    /// A compile time known index.
19    Const(i32),
20    /// A runtime value index.
21    Value(Value<'c, 'a>),
22}
23
24/// A block extension for an `llvm` dialect.
25pub trait LlvmBlockExt<'c>: BuiltinBlockExt<'c> + ArithBlockExt<'c> {
26    /// Creates an `llvm.extractvalue` operation.
27    fn extract_value(
28        &self,
29        context: &'c Context,
30        location: Location<'c>,
31        container: Value<'c, '_>,
32        value_type: Type<'c>,
33        index: usize,
34    ) -> Result<Value<'c, '_>, Error>;
35
36    /// Creates an `llvm.insertvalue` operation.
37    fn insert_value(
38        &self,
39        context: &'c Context,
40        location: Location<'c>,
41        container: Value<'c, '_>,
42        value: Value<'c, '_>,
43        index: usize,
44    ) -> Result<Value<'c, '_>, Error>;
45
46    /// Creates an `llvm.insertvalue` operation that insert multiple elements
47    /// into an aggregate from the first index.
48    fn insert_values<'block>(
49        &'block self,
50        context: &'c Context,
51        location: Location<'c>,
52        container: Value<'c, 'block>,
53        values: &[Value<'c, 'block>],
54    ) -> Result<Value<'c, 'block>, Error>;
55
56    /// Creates an `llvm.load` operation.
57    fn load(
58        &self,
59        context: &'c Context,
60        location: Location<'c>,
61        addr: Value<'c, '_>,
62        value_type: Type<'c>,
63    ) -> Result<Value<'c, '_>, Error>;
64
65    /// Creates an `llvm.alloca` operation.
66    fn alloca(
67        &self,
68        context: &'c Context,
69        location: Location<'c>,
70        element_type: Type<'c>,
71        element_count: Value<'c, '_>,
72        align: usize,
73    ) -> Result<Value<'c, '_>, Error>;
74
75    /// Creates an `llvm.alloca` operation that allocates one element.
76    fn alloca1(
77        &self,
78        context: &'c Context,
79        location: Location<'c>,
80        r#type: Type<'c>,
81        align: usize,
82    ) -> Result<Value<'c, '_>, Error>;
83
84    /// Creates an `llvm.alloca` operation that allocates one element of the
85    /// given size of an integer.
86    fn alloca_int(
87        &self,
88        context: &'c Context,
89        location: Location<'c>,
90        bits: u32,
91        align: usize,
92    ) -> Result<Value<'c, '_>, Error>;
93
94    /// Creates an `llvm.store` operation.
95    fn store(
96        &self,
97        context: &'c Context,
98        location: Location<'c>,
99        pointer: Value<'c, '_>,
100        value: Value<'c, '_>,
101    ) -> Result<(), Error>;
102
103    /// Creates an `llvm.memcpy` operation.
104    fn memcpy(
105        &self,
106        context: &'c Context,
107        location: Location<'c>,
108        src: Value<'c, '_>,
109        dst: Value<'c, '_>,
110        len_bytes: Value<'c, '_>,
111    );
112
113    /// Creates an `llvm.getelementptr` operation.
114    ///
115    /// This method allows combining both compile time indexes and runtime value
116    /// indexes.
117    fn gep(
118        &self,
119        context: &'c Context,
120        location: Location<'c>,
121        pointer: Value<'c, '_>,
122        indexes: &[GepIndex<'c, '_>],
123        element_type: Type<'c>,
124    ) -> Result<Value<'c, '_>, Error>;
125}
126
127impl<'c> LlvmBlockExt<'c> for Block<'c> {
128    #[inline]
129    fn extract_value(
130        &self,
131        context: &'c Context,
132        location: Location<'c>,
133        container: Value<'c, '_>,
134        value_type: Type<'c>,
135        index: usize,
136    ) -> Result<Value<'c, '_>, Error> {
137        self.append_op_result(
138            ods::llvm::extractvalue(
139                context,
140                value_type,
141                container,
142                DenseI64ArrayAttribute::new(context, &[index as _]).into(),
143                location,
144            )
145            .into(),
146        )
147    }
148
149    #[inline]
150    fn insert_value(
151        &self,
152        context: &'c Context,
153        location: Location<'c>,
154        container: Value<'c, '_>,
155        value: Value<'c, '_>,
156        index: usize,
157    ) -> Result<Value<'c, '_>, Error> {
158        self.append_op_result(
159            ods::llvm::insertvalue(
160                context,
161                container.r#type(),
162                container,
163                value,
164                DenseI64ArrayAttribute::new(context, &[index as _]).into(),
165                location,
166            )
167            .into(),
168        )
169    }
170
171    #[inline]
172    fn insert_values<'block>(
173        &'block self,
174        context: &'c Context,
175        location: Location<'c>,
176        mut container: Value<'c, 'block>,
177        values: &[Value<'c, 'block>],
178    ) -> Result<Value<'c, 'block>, Error> {
179        for (i, value) in values.iter().enumerate() {
180            container = self.insert_value(context, location, container, *value, i)?;
181        }
182        Ok(container)
183    }
184
185    #[inline]
186    fn store(
187        &self,
188        context: &'c Context,
189        location: Location<'c>,
190        addr: Value<'c, '_>,
191        value: Value<'c, '_>,
192    ) -> Result<(), Error> {
193        self.append_operation(ods::llvm::store(context, value, addr, location).into());
194        Ok(())
195    }
196
197    #[inline]
198    fn load(
199        &self,
200        context: &'c Context,
201        location: Location<'c>,
202        addr: Value<'c, '_>,
203        value_type: Type<'c>,
204    ) -> Result<Value<'c, '_>, Error> {
205        self.append_op_result(ods::llvm::load(context, value_type, addr, location).into())
206    }
207
208    #[inline]
209    fn memcpy(
210        &self,
211        context: &'c Context,
212        location: Location<'c>,
213        src: Value<'c, '_>,
214        dst: Value<'c, '_>,
215        len_bytes: Value<'c, '_>,
216    ) {
217        self.append_operation(
218            ods::llvm::intr_memcpy(
219                context,
220                dst,
221                src,
222                len_bytes,
223                IntegerAttribute::new(IntegerType::new(context, 1).into(), 0),
224                location,
225            )
226            .into(),
227        );
228    }
229
230    #[inline]
231    fn alloca(
232        &self,
233        context: &'c Context,
234        location: Location<'c>,
235        element_type: Type<'c>,
236        element_count: Value<'c, '_>,
237        align: usize,
238    ) -> Result<Value<'c, '_>, Error> {
239        let mut operation = ods::llvm::alloca(
240            context,
241            r#type::pointer(context, 0),
242            element_count,
243            TypeAttribute::new(element_type),
244            location,
245        );
246
247        operation.set_elem_type(TypeAttribute::new(element_type));
248        operation.set_alignment(IntegerAttribute::new(
249            IntegerType::new(context, 64).into(),
250            align as _,
251        ));
252
253        self.append_op_result(operation.into())
254    }
255
256    #[inline]
257    fn alloca1(
258        &self,
259        context: &'c Context,
260        location: Location<'c>,
261        r#type: Type<'c>,
262        align: usize,
263    ) -> Result<Value<'c, '_>, Error> {
264        self.alloca(
265            context,
266            location,
267            r#type,
268            self.const_int(context, location, 1, 64)?,
269            align,
270        )
271    }
272
273    #[inline]
274    fn alloca_int(
275        &self,
276        context: &'c Context,
277        location: Location<'c>,
278        bits: u32,
279        align: usize,
280    ) -> Result<Value<'c, '_>, Error> {
281        self.alloca1(
282            context,
283            location,
284            IntegerType::new(context, bits).into(),
285            align,
286        )
287    }
288
289    #[inline]
290    fn gep(
291        &self,
292        context: &'c Context,
293        location: Location<'c>,
294        pointer: Value<'c, '_>,
295        indexes: &[GepIndex<'c, '_>],
296        element_type: Type<'c>,
297    ) -> Result<Value<'c, '_>, Error> {
298        let mut static_indices = Vec::with_capacity(indexes.len());
299        let mut dynamic_indices = Vec::with_capacity(indexes.len());
300
301        for index in indexes {
302            match index {
303                GepIndex::Const(index) => static_indices.push(*index),
304                GepIndex::Value(value) => {
305                    static_indices.push(i32::MIN); // marker for dynamic index
306                    dynamic_indices.push(*value);
307                }
308            }
309        }
310
311        let operation = ods::llvm::getelementptr(
312            context,
313            r#type::pointer(context, 0),
314            pointer,
315            &dynamic_indices,
316            DenseI32ArrayAttribute::new(context, &static_indices),
317            TypeAttribute::new(element_type),
318            location,
319        );
320
321        self.append_op_result(operation.into())
322    }
323}