Skip to main content

pliron_llvm/
op_interfaces.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) The pliron contributors
3
4//! [Op] Interfaces defined in the LLVM dialect.
5
6use pliron::{
7    builtin::{
8        attributes::BoolAttr,
9        op_interfaces::{NOpdsInterface, OneOpdInterface, ResultNOfType, SymbolOpInterface},
10        type_interfaces::FloatTypeInterface,
11    },
12    derive::op_interface,
13    dict_key,
14    r#type::type_cast,
15    utils::const_bound_n::I,
16};
17use thiserror::Error;
18
19use pliron::{
20    builtin::{
21        op_interfaces::{OneResultInterface, SameOperandsAndResultType},
22        types::{IntegerType, Signedness},
23    },
24    context::Context,
25    location::Located,
26    op::{Op, op_cast},
27    operation::Operation,
28    result::Result,
29    r#type::{TypeHandle, Typed},
30    value::Value,
31    verify_err,
32};
33
34use crate::{
35    attributes::{AlignmentAttr, FastmathFlagsAttr},
36    types::VectorType,
37};
38
39use super::{attributes::IntegerOverflowFlagsAttr, types::PointerType};
40
41/// Binary arithmetic [Op].
42#[op_interface]
43pub trait BinArithOp: SameOperandsAndResultType + OneResultInterface + NOpdsInterface<2> {
44    /// Create a new binary arithmetic operation given the operands.
45    fn new(ctx: &mut Context, lhs: Value, rhs: Value) -> Self
46    where
47        Self: Sized,
48    {
49        let op = Operation::new(
50            ctx,
51            Self::get_concrete_op_info(),
52            vec![lhs.get_type(ctx)],
53            vec![lhs, rhs],
54            vec![],
55            0,
56        );
57        Self::from_operation(op)
58    }
59
60    fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
61    where
62        Self: Sized,
63    {
64        Ok(())
65    }
66
67    /// Get the left-hand side operand of this binary arithmetic [Op].
68    fn lhs(&self, ctx: &Context) -> Value
69    where
70        Self: Sized,
71    {
72        self.get_operand_i(ctx, I::<0>.into())
73    }
74
75    /// Get the right-hand side operand of this binary arithmetic [Op].
76    fn rhs(&self, ctx: &Context) -> Value
77    where
78        Self: Sized,
79    {
80        self.get_operand_i(ctx, I::<1>.into())
81    }
82}
83
84#[derive(Error, Debug)]
85#[error("Integer binary arithmetic Op can only have signless integer result/operand type")]
86pub struct IntBinArithOpErr;
87
88/// Integer binary arithmetic [Op]
89#[op_interface]
90pub trait IntBinArithOp: BinArithOp {
91    fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
92    where
93        Self: Sized,
94    {
95        let mut ty = op_cast::<dyn BinArithOp>(op)
96            .expect("Op must impl BinArithOp")
97            .operand_type_i(ctx, I::<0>.into());
98
99        if let Some(vec_ty) = ty.deref(ctx).downcast_ref::<VectorType>() {
100            ty = vec_ty.elem_type();
101        }
102
103        let ty = ty.deref(ctx);
104        let Some(int_ty) = ty.downcast_ref::<IntegerType>() else {
105            return verify_err!(op.loc(ctx), IntBinArithOpErr);
106        };
107
108        if int_ty.signedness() != Signedness::Signless {
109            return verify_err!(op.loc(ctx), IntBinArithOpErr);
110        }
111
112        Ok(())
113    }
114}
115
116dict_key!(
117    /// Attribute key for integer overflow flags.
118    ATTR_KEY_INTEGER_OVERFLOW_FLAGS,
119    "llvm_integer_overflow_flags"
120);
121
122#[derive(Error, Debug)]
123#[error("IntegerOverflowFlag missing on Op")]
124pub struct IntBinArithOpWithOverflowFlagErr;
125
126/// Integer binary arithmetic [Op] with [IntegerOverflowFlagsAttr]
127#[op_interface]
128pub trait IntBinArithOpWithOverflowFlag: IntBinArithOp {
129    /// Create a new integer binary op with overflow flags set.
130    fn new_with_overflow_flag(
131        ctx: &mut Context,
132        lhs: Value,
133        rhs: Value,
134        flag: IntegerOverflowFlagsAttr,
135    ) -> Self
136    where
137        Self: Sized,
138    {
139        let op = Self::new(ctx, lhs, rhs);
140        op.set_integer_overflow_flag(ctx, flag);
141        op
142    }
143
144    /// Get the integer overflow flag on this [Op].
145    fn integer_overflow_flag(&self, ctx: &Context) -> IntegerOverflowFlagsAttr
146    where
147        Self: Sized,
148    {
149        self.get_operation()
150            .deref(ctx)
151            .attributes
152            .get::<IntegerOverflowFlagsAttr>(&ATTR_KEY_INTEGER_OVERFLOW_FLAGS)
153            .expect("Integer overflow flag missing or is of incorrect type")
154            .clone()
155    }
156
157    /// Set the integer overflow flag for this [Op].
158    fn set_integer_overflow_flag(&self, ctx: &Context, flag: IntegerOverflowFlagsAttr)
159    where
160        Self: Sized,
161    {
162        self.get_operation()
163            .deref_mut(ctx)
164            .attributes
165            .set(ATTR_KEY_INTEGER_OVERFLOW_FLAGS.clone(), flag);
166    }
167
168    fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
169    where
170        Self: Sized,
171    {
172        let op = op.get_operation().deref(ctx);
173        if op
174            .attributes
175            .get::<IntegerOverflowFlagsAttr>(&ATTR_KEY_INTEGER_OVERFLOW_FLAGS)
176            .is_none()
177        {
178            return verify_err!(op.loc(), IntBinArithOpWithOverflowFlagErr);
179        }
180
181        Ok(())
182    }
183}
184
185#[derive(Error, Debug)]
186#[error("Floating point arithmetic Op can only have signless floating point result/operand type")]
187pub struct FloatBinArithOpErr;
188
189/// Floating point binary arithmetic [Op]
190#[op_interface]
191pub trait FloatBinArithOp: BinArithOp {
192    fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
193    where
194        Self: Sized,
195    {
196        let mut ty = op_cast::<dyn BinArithOp>(op)
197            .expect("Op must impl BinArithOp")
198            .operand_type_i(ctx, I::<0>.into());
199
200        if let Some(vec_ty) = ty.deref(ctx).downcast_ref::<VectorType>() {
201            ty = vec_ty.elem_type();
202        }
203
204        let ty = ty.deref(ctx);
205        if type_cast::<dyn FloatTypeInterface>(&*ty).is_none() {
206            return verify_err!(op.loc(ctx), FloatBinArithOpErr);
207        }
208        Ok(())
209    }
210}
211
212dict_key!(
213    /// Attribute key for fastmath flags.
214    ATTR_KEY_FAST_MATH_FLAGS,
215    "llvm_fast_math_flags"
216);
217
218#[derive(Error, Debug)]
219#[error("Fastmath flag missing on Op")]
220pub struct FastMathFlagMissingErr;
221
222/// Ops that have fast math flags.
223#[op_interface]
224pub trait FastMathFlags {
225    /// Get the fast math flags on this [Op].
226    fn fast_math_flags(&self, ctx: &Context) -> FastmathFlagsAttr
227    where
228        Self: Sized,
229    {
230        *self
231            .get_operation()
232            .deref(ctx)
233            .attributes
234            .get::<FastmathFlagsAttr>(&ATTR_KEY_FAST_MATH_FLAGS)
235            .expect("Fast math flags missing or is of incorrect type")
236    }
237
238    /// Set the fast math flags for this [Op].
239    fn set_fast_math_flags(&self, ctx: &Context, flag: FastmathFlagsAttr)
240    where
241        Self: Sized,
242    {
243        self.get_operation()
244            .deref_mut(ctx)
245            .attributes
246            .set(ATTR_KEY_FAST_MATH_FLAGS.clone(), flag);
247    }
248
249    fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
250    where
251        Self: Sized,
252    {
253        let op = op.get_operation().deref(ctx);
254        if op
255            .attributes
256            .get::<FastmathFlagsAttr>(&ATTR_KEY_FAST_MATH_FLAGS)
257            .is_none()
258        {
259            return verify_err!(op.loc(), FastmathFlagMissingErr);
260        }
261
262        Ok(())
263    }
264}
265
266/// Floating point binary arithmetic [Op] with [FastmathFlagsAttr]
267#[op_interface]
268pub trait FloatBinArithOpWithFastMathFlags: FloatBinArithOp + FastMathFlags {
269    /// Create a new floating point binary op with fast math flags set.
270    fn new_with_fast_math_flags(
271        ctx: &mut Context,
272        lhs: Value,
273        rhs: Value,
274        flag: FastmathFlagsAttr,
275    ) -> Self
276    where
277        Self: Sized,
278    {
279        let op = Self::new(ctx, lhs, rhs);
280        op.set_fast_math_flags(ctx, flag);
281        op
282    }
283
284    fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
285    where
286        Self: Sized,
287    {
288        Ok(())
289    }
290}
291
292#[derive(Error, Debug)]
293#[error("Fastmath flag missing on Op")]
294pub struct FastmathFlagMissingErr;
295
296dict_key!(
297    /// Attribute key for nneg flag.
298    ATTR_KEY_NNEG_FLAG,
299    "llvm_nneg_flag"
300);
301
302#[op_interface]
303pub trait NNegFlag {
304    // Get the current NNEG flag value.
305    fn nneg(&self, ctx: &Context) -> bool {
306        self.get_operation()
307            .deref(ctx)
308            .attributes
309            .get::<BoolAttr>(&ATTR_KEY_NNEG_FLAG)
310            .expect("NNEG flag missing or is of incorrect type")
311            .clone()
312            .into()
313    }
314    // Set the current NNEG flag value.
315    fn set_nneg(&self, ctx: &Context, flag: bool) {
316        self.get_operation()
317            .deref_mut(ctx)
318            .attributes
319            .set(ATTR_KEY_NNEG_FLAG.clone(), BoolAttr::new(flag));
320    }
321    fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
322    where
323        Self: Sized,
324    {
325        let op = op.get_operation().deref(ctx);
326        if op.attributes.get::<BoolAttr>(&ATTR_KEY_NNEG_FLAG).is_none() {
327            return verify_err!(op.loc(), NNegFlagMissingErr);
328        }
329
330        Ok(())
331    }
332}
333
334#[derive(Error, Debug)]
335#[error("NNEG flag missing on Op")]
336pub struct NNegFlagMissingErr;
337
338#[derive(Error, Debug)]
339#[error("Result must be a pointer type, but is not")]
340pub struct PointerTypeResultVerifyErr;
341
342/// An [Op] with a single result whose type is [PointerType]
343#[op_interface]
344pub trait PointerTypeResult: OneResultInterface + ResultNOfType<0, PointerType> {
345    /// Get the pointee type of the result pointer.
346    fn result_pointee_type(&self, ctx: &Context) -> TypeHandle;
347
348    fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
349    where
350        Self: Sized,
351    {
352        if !op_cast::<dyn OneResultInterface>(op)
353            .expect("An Op here must impl OneResultInterface")
354            .result_type(ctx)
355            .deref(ctx)
356            .is::<PointerType>()
357        {
358            return verify_err!(op.loc(ctx), PointerTypeResultVerifyErr);
359        }
360
361        Ok(())
362    }
363}
364
365/// A Cast [Op] has one argument and one result.
366#[op_interface]
367pub trait CastOpInterface: OneResultInterface + OneOpdInterface {
368    /// Create a new cast operation given the operand.
369    fn new(ctx: &mut Context, operand: Value, res_type: TypeHandle) -> Self
370    where
371        Self: Sized,
372    {
373        let op = Operation::new(
374            ctx,
375            Self::get_concrete_op_info(),
376            vec![res_type],
377            vec![operand],
378            vec![],
379            0,
380        );
381        Self::from_operation(op)
382    }
383
384    fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
385    where
386        Self: Sized,
387    {
388        Ok(())
389    }
390}
391
392/// A Cast [Op] with NNEG flag.
393#[op_interface]
394pub trait CastOpWithNNegInterface: CastOpInterface + NNegFlag {
395    /// Create a new cast operation with nneg flag
396    fn new_with_nneg(ctx: &mut Context, operand: Value, res_type: TypeHandle, nneg: bool) -> Self
397    where
398        Self: Sized,
399    {
400        let op = Self::new(ctx, operand, res_type);
401        op.set_nneg(ctx, nneg);
402        op
403    }
404
405    fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
406    where
407        Self: Sized,
408    {
409        Ok(())
410    }
411}
412
413/// Is a global value (variable or function) declaration.
414#[op_interface]
415pub trait IsDeclaration {
416    /// Check if this global value (variable or function) is a declaration.
417    fn is_declaration(&self, ctx: &Context) -> bool
418    where
419        Self: Sized;
420
421    fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
422    where
423        Self: Sized,
424    {
425        Ok(())
426    }
427}
428
429dict_key!(
430    /// Attribute key for LLVM symbol name.
431    ATTR_KEY_LLVM_SYMBOL_NAME,
432    "llvm_symbol_name"
433);
434
435/// Since LLVM symbols can have characters that are illegal in pliron,
436/// this interface provides a way to get the original LLVM symbol name.
437#[op_interface]
438pub trait LlvmSymbolName: SymbolOpInterface {
439    /// Get the original LLVM symbol name, if it's different from the pliron symbol name.
440    fn llvm_symbol_name(&self, ctx: &Context) -> Option<String> {
441        self.get_operation()
442            .deref(ctx)
443            .attributes
444            .get::<pliron::builtin::attributes::StringAttr>(&ATTR_KEY_LLVM_SYMBOL_NAME)
445            .map(|attr| attr.clone().into())
446    }
447
448    /// Set the original LLVM symbol name.
449    fn set_llvm_symbol_name(&self, ctx: &Context, name: String) {
450        self.get_operation().deref_mut(ctx).attributes.set(
451            ATTR_KEY_LLVM_SYMBOL_NAME.clone(),
452            pliron::builtin::attributes::StringAttr::new(name),
453        );
454    }
455
456    fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
457    where
458        Self: Sized,
459    {
460        Ok(())
461    }
462}
463
464dict_key!(
465    /// Attribute key for alignment.
466    ATTR_KEY_LLVM_ALIGNMENT,
467    "llvm_alignment"
468);
469
470/// Ops that can have an alignment set.
471#[op_interface]
472pub trait AlignableOpInterface {
473    /// Get the alignment of this [Op], if set.
474    fn alignment(&self, ctx: &Context) -> Option<u32>
475    where
476        Self: Sized,
477    {
478        self.get_operation()
479            .deref(ctx)
480            .attributes
481            .get::<AlignmentAttr>(&ATTR_KEY_LLVM_ALIGNMENT)
482            .map(|attr| attr.0)
483    }
484
485    /// Set the alignment of this [Op].
486    fn set_alignment(&self, ctx: &Context, alignment: u32)
487    where
488        Self: Sized,
489    {
490        self.get_operation()
491            .deref_mut(ctx)
492            .attributes
493            .set(ATTR_KEY_LLVM_ALIGNMENT.clone(), AlignmentAttr(alignment));
494    }
495
496    fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
497    where
498        Self: Sized,
499    {
500        Ok(())
501    }
502}