1use alloc::{
7 string::{String, ToString},
8 vec,
9};
10
11use pliron::{
12 builtin::{
13 attributes::BoolAttr,
14 op_interfaces::{
15 NOpdsInterface, OneOpdInterface, ResultNOfType, SymbolOpInterface,
16 verify_get_operand_n, verify_get_result_n,
17 },
18 type_interfaces::FloatTypeInterface,
19 },
20 derive::op_interface,
21 dict_key,
22 printable::Printable,
23 r#type::{Type, TypeInterfaceHandle, TypeInterfaceMarker, TypedHandle, type_impls},
24 utils::const_bound_n::I,
25};
26use thiserror::Error;
27
28use pliron::{
29 builtin::{
30 op_interfaces::{OneResultInterface, SameOperandsAndResultType},
31 types::{IntegerType, Signedness},
32 },
33 context::Context,
34 location::{Located, Location},
35 op::{Op, op_cast},
36 operation::Operation,
37 result::Result,
38 r#type::{TypeHandle, Typed},
39 value::Value,
40 verify_err,
41};
42
43use crate::{
44 attributes::{AlignmentAttr, FastmathFlagsAttr, SyncScopeAttr},
45 types::{VectorType, VectorTypeKind},
46};
47
48use super::{attributes::IntegerOverflowFlagsAttr, types::PointerType};
49
50#[op_interface]
52pub trait BinArithOp: SameOperandsAndResultType + OneResultInterface + NOpdsInterface<2> {
53 fn new(ctx: &mut Context, lhs: Value, rhs: Value) -> Self
55 where
56 Self: Sized,
57 {
58 let op = Operation::new(
59 ctx,
60 Self::get_concrete_op_info(),
61 vec![lhs.get_type(ctx)],
62 vec![lhs, rhs],
63 vec![],
64 0,
65 );
66 Self::from_operation(op)
67 }
68
69 fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
70 where
71 Self: Sized,
72 {
73 Ok(())
74 }
75
76 fn lhs(&self, ctx: &Context) -> Value
78 where
79 Self: Sized,
80 {
81 self.get_operand_i(ctx, I::<0>.into())
82 }
83
84 fn rhs(&self, ctx: &Context) -> Value
86 where
87 Self: Sized,
88 {
89 self.get_operand_i(ctx, I::<1>.into())
90 }
91}
92
93#[derive(Error, Debug)]
94#[error("Integer binary arithmetic Op can only have signless integer result/operand type")]
95pub struct IntBinArithOpErr;
96
97#[op_interface]
99pub trait IntBinArithOp: BinArithOp + ScalarOrVectorOpd<IntegerType, 0> {
100 fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
101 where
102 Self: Sized,
103 {
104 let int_ty = op_cast::<dyn ScalarOrVectorOpd<IntegerType, 0>>(op)
105 .expect("Op must impl ScalarOrVectorOpd<IntegerType, 0>")
106 .scalar_or_vector_elem_ty(ctx);
107 let int_ty = int_ty.deref(ctx);
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 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#[op_interface]
128pub trait IntBinArithOpWithOverflowFlag: IntBinArithOp {
129 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 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 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#[op_interface]
187pub trait FloatBinArithOp: BinArithOp + ScalarOrVectorOpdImpls<dyn FloatTypeInterface, 0> {
188 fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
189 where
190 Self: Sized,
191 {
192 Ok(())
193 }
194}
195
196dict_key!(
197 ATTR_KEY_FAST_MATH_FLAGS,
199 "llvm_fast_math_flags"
200);
201
202#[derive(Error, Debug)]
203#[error("Fastmath flag missing on Op")]
204pub struct FastMathFlagMissingErr;
205
206#[op_interface]
208pub trait FastMathFlags {
209 fn fast_math_flags(&self, ctx: &Context) -> FastmathFlagsAttr
211 where
212 Self: Sized,
213 {
214 *self
215 .get_operation()
216 .deref(ctx)
217 .attributes
218 .get::<FastmathFlagsAttr>(&ATTR_KEY_FAST_MATH_FLAGS)
219 .expect("Fast math flags missing or is of incorrect type")
220 }
221
222 fn set_fast_math_flags(&self, ctx: &Context, flag: FastmathFlagsAttr)
224 where
225 Self: Sized,
226 {
227 self.get_operation()
228 .deref_mut(ctx)
229 .attributes
230 .set(ATTR_KEY_FAST_MATH_FLAGS.clone(), flag);
231 }
232
233 fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
234 where
235 Self: Sized,
236 {
237 let op = op.get_operation().deref(ctx);
238 if op
239 .attributes
240 .get::<FastmathFlagsAttr>(&ATTR_KEY_FAST_MATH_FLAGS)
241 .is_none()
242 {
243 return verify_err!(op.loc(), FastmathFlagMissingErr);
244 }
245
246 Ok(())
247 }
248}
249
250dict_key!(
251 ATTR_KEY_SYNC_SCOPE,
253 "llvm_syncscope"
254);
255
256#[derive(Error, Debug)]
257#[error("Synchronization scope missing on Op")]
258pub struct SyncScopeMissingErr;
259
260#[op_interface]
262pub trait SyncScopeInterface {
263 fn syncscope(&self, ctx: &Context) -> SyncScopeAttr
265 where
266 Self: Sized,
267 {
268 self.get_operation()
269 .deref(ctx)
270 .attributes
271 .get::<SyncScopeAttr>(&ATTR_KEY_SYNC_SCOPE)
272 .expect("Synchronization scope missing or is of incorrect type")
273 .clone()
274 }
275
276 fn set_syncscope(&self, ctx: &Context, syncscope: SyncScopeAttr)
278 where
279 Self: Sized,
280 {
281 self.get_operation()
282 .deref_mut(ctx)
283 .attributes
284 .set(ATTR_KEY_SYNC_SCOPE.clone(), syncscope);
285 }
286
287 fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
288 where
289 Self: Sized,
290 {
291 let op = op.get_operation().deref(ctx);
292 if op
293 .attributes
294 .get::<SyncScopeAttr>(&ATTR_KEY_SYNC_SCOPE)
295 .is_none()
296 {
297 return verify_err!(op.loc(), SyncScopeMissingErr);
298 }
299
300 Ok(())
301 }
302}
303
304#[op_interface]
306pub trait FloatBinArithOpWithFastMathFlags: FloatBinArithOp + FastMathFlags {
307 fn new_with_fast_math_flags(
309 ctx: &mut Context,
310 lhs: Value,
311 rhs: Value,
312 flag: FastmathFlagsAttr,
313 ) -> Self
314 where
315 Self: Sized,
316 {
317 let op = Self::new(ctx, lhs, rhs);
318 op.set_fast_math_flags(ctx, flag);
319 op
320 }
321
322 fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
323 where
324 Self: Sized,
325 {
326 Ok(())
327 }
328}
329
330#[derive(Error, Debug)]
331#[error("Fastmath flag missing on Op")]
332pub struct FastmathFlagMissingErr;
333
334dict_key!(
335 ATTR_KEY_NNEG_FLAG,
337 "llvm_nneg_flag"
338);
339
340#[op_interface]
341pub trait NNegFlag {
342 fn nneg(&self, ctx: &Context) -> bool {
344 self.get_operation()
345 .deref(ctx)
346 .attributes
347 .get::<BoolAttr>(&ATTR_KEY_NNEG_FLAG)
348 .expect("NNEG flag missing or is of incorrect type")
349 .clone()
350 .into()
351 }
352 fn set_nneg(&self, ctx: &Context, flag: bool) {
354 self.get_operation()
355 .deref_mut(ctx)
356 .attributes
357 .set(ATTR_KEY_NNEG_FLAG.clone(), BoolAttr::new(flag));
358 }
359 fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
360 where
361 Self: Sized,
362 {
363 let op = op.get_operation().deref(ctx);
364 if op.attributes.get::<BoolAttr>(&ATTR_KEY_NNEG_FLAG).is_none() {
365 return verify_err!(op.loc(), NNegFlagMissingErr);
366 }
367
368 Ok(())
369 }
370}
371
372#[derive(Error, Debug)]
373#[error("NNEG flag missing on Op")]
374pub struct NNegFlagMissingErr;
375
376#[derive(Error, Debug)]
377#[error("Result must be a pointer type, but is not")]
378pub struct PointerTypeResultVerifyErr;
379
380#[op_interface]
382pub trait PointerTypeResult: OneResultInterface + ResultNOfType<0, PointerType> {
383 fn result_pointee_type(&self, ctx: &Context) -> TypeHandle;
385
386 fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
387 where
388 Self: Sized,
389 {
390 if !op_cast::<dyn OneResultInterface>(op)
391 .expect("An Op here must impl OneResultInterface")
392 .result_type(ctx)
393 .deref(ctx)
394 .is::<PointerType>()
395 {
396 return verify_err!(op.loc(ctx), PointerTypeResultVerifyErr);
397 }
398
399 Ok(())
400 }
401}
402
403#[op_interface]
405pub trait CastOpInterface: OneResultInterface + OneOpdInterface {
406 fn new(ctx: &mut Context, operand: Value, res_type: TypeHandle) -> Self
408 where
409 Self: Sized,
410 {
411 let op = Operation::new(
412 ctx,
413 Self::get_concrete_op_info(),
414 vec![res_type],
415 vec![operand],
416 vec![],
417 0,
418 );
419 Self::from_operation(op)
420 }
421
422 fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
423 where
424 Self: Sized,
425 {
426 Ok(())
427 }
428}
429
430#[op_interface]
432pub trait CastOpWithNNegInterface: CastOpInterface + NNegFlag {
433 fn new_with_nneg(ctx: &mut Context, operand: Value, res_type: TypeHandle, nneg: bool) -> Self
435 where
436 Self: Sized,
437 {
438 let op = Self::new(ctx, operand, res_type);
439 op.set_nneg(ctx, nneg);
440 op
441 }
442
443 fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
444 where
445 Self: Sized,
446 {
447 Ok(())
448 }
449}
450
451#[op_interface]
453pub trait IsDeclaration {
454 fn is_declaration(&self, ctx: &Context) -> bool
456 where
457 Self: Sized;
458
459 fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
460 where
461 Self: Sized,
462 {
463 Ok(())
464 }
465}
466
467dict_key!(
468 ATTR_KEY_LLVM_SYMBOL_NAME,
470 "llvm_symbol_name"
471);
472
473#[op_interface]
476pub trait LlvmSymbolName: SymbolOpInterface {
477 fn llvm_symbol_name(&self, ctx: &Context) -> Option<String> {
479 self.get_operation()
480 .deref(ctx)
481 .attributes
482 .get::<pliron::builtin::attributes::StringAttr>(&ATTR_KEY_LLVM_SYMBOL_NAME)
483 .map(|attr| attr.clone().into())
484 }
485
486 fn set_llvm_symbol_name(&self, ctx: &Context, name: String) {
488 self.get_operation().deref_mut(ctx).attributes.set(
489 ATTR_KEY_LLVM_SYMBOL_NAME.clone(),
490 pliron::builtin::attributes::StringAttr::new(name),
491 );
492 }
493
494 fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
495 where
496 Self: Sized,
497 {
498 Ok(())
499 }
500}
501
502dict_key!(
503 ATTR_KEY_LLVM_ALIGNMENT,
505 "llvm_alignment"
506);
507
508#[op_interface]
510pub trait AlignableOpInterface {
511 fn alignment(&self, ctx: &Context) -> Option<u32>
513 where
514 Self: Sized,
515 {
516 self.get_operation()
517 .deref(ctx)
518 .attributes
519 .get::<AlignmentAttr>(&ATTR_KEY_LLVM_ALIGNMENT)
520 .map(|attr| attr.0)
521 }
522
523 fn set_alignment(&self, ctx: &Context, alignment: u32)
525 where
526 Self: Sized,
527 {
528 self.get_operation()
529 .deref_mut(ctx)
530 .attributes
531 .set(ATTR_KEY_LLVM_ALIGNMENT.clone(), AlignmentAttr(alignment));
532 }
533
534 fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
535 where
536 Self: Sized,
537 {
538 Ok(())
539 }
540}
541
542dict_key!(
543 ATTR_KEY_LLVM_VOLATILE,
545 "llvm_volatile"
546);
547
548#[op_interface]
550pub trait VolatilityOpInterface {
551 fn is_volatile(&self, ctx: &Context) -> bool
553 where
554 Self: Sized,
555 {
556 self.get_operation()
557 .deref(ctx)
558 .attributes
559 .get::<BoolAttr>(&ATTR_KEY_LLVM_VOLATILE)
560 .map(|attr| attr.clone().into())
561 .unwrap_or(false)
562 }
563
564 fn set_volatile(&self, ctx: &Context, is_volatile: bool)
566 where
567 Self: Sized,
568 {
569 self.get_operation()
570 .deref_mut(ctx)
571 .attributes
572 .set(ATTR_KEY_LLVM_VOLATILE.clone(), BoolAttr::new(is_volatile));
573 }
574
575 fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
576 where
577 Self: Sized,
578 {
579 Ok(())
580 }
581}
582
583#[derive(Debug, Error)]
584pub enum ScalarOrVectorErr {
585 #[error("{0} is not {1} or a vector of it")]
586 NotTOrVectorOfT(String, String),
587 #[error("{0} or its vector element type does not implement interface")]
588 TyOrElemNotImplsI(String),
589}
590
591fn vector_shape_of(ty: TypeHandle, ctx: &Context) -> Option<(u32, VectorTypeKind)> {
593 ty.deref(ctx)
594 .downcast_ref::<VectorType>()
595 .map(|vec_ty| (vec_ty.num_elements(), vec_ty.kind()))
596}
597
598fn elem_ty_of<T: Type>(ty: TypeHandle, ctx: &Context) -> TypedHandle<T> {
602 if let Ok(typed_handle) = TypedHandle::from_handle(ty, ctx) {
603 return typed_handle;
604 }
605 let ty_ref = &*ty.deref(ctx);
606 let elem_ty = ty_ref
607 .downcast_ref::<VectorType>()
608 .expect("verify() guarantees type is T or a vector of T")
609 .elem_type();
610 TypedHandle::from_handle(elem_ty, ctx).expect("verify() guarantees element type is T")
611}
612
613fn verify_t_or_vec_of_t<T: Type>(loc: Location, ty: &dyn Type, ctx: &Context) -> Result<()> {
615 if ty.is::<T>() {
616 return Ok(());
618 }
619 let Some(vec_ty) = ty.downcast_ref::<VectorType>() else {
621 return verify_err!(
622 loc,
623 ScalarOrVectorErr::NotTOrVectorOfT(
624 ty.get_type_id().disp(ctx).to_string(),
625 T::get_type_id_static().disp(ctx).to_string()
626 )
627 );
628 };
629 let elem_ty = &*vec_ty.elem_type().deref(ctx);
630 if !elem_ty.is::<T>() {
631 return verify_err!(
632 loc,
633 ScalarOrVectorErr::NotTOrVectorOfT(
634 ty.get_type_id().disp(ctx).to_string(),
635 T::get_type_id_static().disp(ctx).to_string()
636 )
637 );
638 }
639 Ok(())
640}
641
642fn elem_ty_of_impls<I: ?Sized + TypeInterfaceMarker + 'static>(
646 ty: TypeHandle,
647 ctx: &Context,
648) -> TypeInterfaceHandle<I> {
649 if let Ok(interface_handle) = TypeInterfaceHandle::from_handle(ty, ctx) {
650 return interface_handle;
651 }
652 let ty_ref = &*ty.deref(ctx);
653 let elem_ty = ty_ref
654 .downcast_ref::<VectorType>()
655 .expect("verify() guarantees type impls I or is a vector whose elem impls I")
656 .elem_type();
657 TypeInterfaceHandle::from_handle(elem_ty, ctx)
658 .expect("verify() guarantees element type impls I")
659}
660
661fn verify_impls_i_or_vec_of_impls_i<I: ?Sized + TypeInterfaceMarker + 'static>(
663 loc: Location,
664 ty: &dyn Type,
665 ctx: &Context,
666) -> Result<()> {
667 if type_impls::<I>(ty) {
668 return Ok(());
670 }
671 let Some(vec_ty) = ty.downcast_ref::<VectorType>() else {
673 return verify_err!(
674 loc,
675 ScalarOrVectorErr::TyOrElemNotImplsI(ty.get_type_id().disp(ctx).to_string())
676 );
677 };
678 let elem_ty = &*vec_ty.elem_type().deref(ctx);
679 if !type_impls::<I>(elem_ty) {
680 return verify_err!(
681 loc,
682 ScalarOrVectorErr::TyOrElemNotImplsI(ty.get_type_id().disp(ctx).to_string())
683 );
684 }
685 Ok(())
686}
687
688#[op_interface]
690pub trait ScalarOrVectorOpd<T: Type, const N: usize> {
691 fn scalar_or_vector_elem_ty(&self, ctx: &Context) -> TypedHandle<T> {
693 let op = &*self.get_operation().deref(ctx);
694 elem_ty_of(op.get_operand(N).get_type(ctx), ctx)
695 }
696
697 fn vector_shape(&self, ctx: &Context) -> Option<(u32, VectorTypeKind)> {
699 let op = &*self.get_operation().deref(ctx);
700 vector_shape_of(op.get_operand(N).get_type(ctx), ctx)
701 }
702
703 fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
704 where
705 Self: Sized,
706 {
707 let opd = verify_get_operand_n::<N>(op.get_operation(), ctx)?;
708 verify_t_or_vec_of_t::<T>(op.loc(ctx), &*opd.get_type(ctx).deref(ctx), ctx)
709 }
710}
711
712#[op_interface]
714pub trait ScalarOrVectorOpdImpls<I: ?Sized + TypeInterfaceMarker + 'static, const N: usize> {
715 fn scalar_or_vector_elem_ty(&self, ctx: &Context) -> TypeInterfaceHandle<I> {
717 let op = &*self.get_operation().deref(ctx);
718 elem_ty_of_impls::<I>(op.get_operand(N).get_type(ctx), ctx)
719 }
720
721 fn vector_shape(&self, ctx: &Context) -> Option<(u32, VectorTypeKind)> {
723 let op = &*self.get_operation().deref(ctx);
724 vector_shape_of(op.get_operand(N).get_type(ctx), ctx)
725 }
726
727 fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
728 where
729 Self: Sized,
730 {
731 let opd = verify_get_operand_n::<N>(op.get_operation(), ctx)?;
732 verify_impls_i_or_vec_of_impls_i::<I>(op.loc(ctx), &*opd.get_type(ctx).deref(ctx), ctx)
733 }
734}
735
736#[op_interface]
738pub trait ScalarOrVectorRes<T: Type, const N: usize> {
739 fn scalar_or_vector_elem_ty(&self, ctx: &Context) -> TypedHandle<T> {
741 let op = &*self.get_operation().deref(ctx);
742 elem_ty_of(op.get_result(N).get_type(ctx), ctx)
743 }
744
745 fn vector_shape(&self, ctx: &Context) -> Option<(u32, VectorTypeKind)> {
747 let op = &*self.get_operation().deref(ctx);
748 vector_shape_of(op.get_result(N).get_type(ctx), ctx)
749 }
750
751 fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
752 where
753 Self: Sized,
754 {
755 let res = verify_get_result_n::<N>(op.get_operation(), ctx)?;
756 verify_t_or_vec_of_t::<T>(op.loc(ctx), &*res.get_type(ctx).deref(ctx), ctx)
757 }
758}
759
760#[op_interface]
762pub trait ScalarOrVectorResImpls<I: ?Sized + TypeInterfaceMarker + 'static, const N: usize> {
763 fn scalar_or_vector_elem_ty(&self, ctx: &Context) -> TypeInterfaceHandle<I> {
765 let op = &*self.get_operation().deref(ctx);
766 elem_ty_of_impls::<I>(op.get_result(N).get_type(ctx), ctx)
767 }
768
769 fn vector_shape(&self, ctx: &Context) -> Option<(u32, VectorTypeKind)> {
771 let op = &*self.get_operation().deref(ctx);
772 vector_shape_of(op.get_result(N).get_type(ctx), ctx)
773 }
774
775 fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
776 where
777 Self: Sized,
778 {
779 let res = verify_get_result_n::<N>(op.get_operation(), ctx)?;
780 verify_impls_i_or_vec_of_impls_i::<I>(op.loc(ctx), &*res.get_type(ctx).deref(ctx), ctx)
781 }
782}