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::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 Operation::new_op::<Self>(ctx, vec![lhs.get_type(ctx)], vec![lhs, rhs], vec![], 0)
59 }
60
61 fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
62 where
63 Self: Sized,
64 {
65 Ok(())
66 }
67
68 fn lhs(&self, ctx: &Context) -> Value
70 where
71 Self: Sized,
72 {
73 self.get_operand_i(ctx, I::<0>.into())
74 }
75
76 fn rhs(&self, ctx: &Context) -> Value
78 where
79 Self: Sized,
80 {
81 self.get_operand_i(ctx, I::<1>.into())
82 }
83}
84
85#[derive(Error, Debug)]
86#[error("Integer binary arithmetic Op can only have signless integer result/operand type")]
87pub struct IntBinArithOpErr;
88
89#[op_interface]
91pub trait IntBinArithOp: BinArithOp + ScalarOrVectorOpd<IntegerType, 0> {
92 fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
93 where
94 Self: Sized,
95 {
96 let int_ty = op_cast::<dyn ScalarOrVectorOpd<IntegerType, 0>>(op)
97 .expect("Op must impl ScalarOrVectorOpd<IntegerType, 0>")
98 .scalar_or_vector_elem_ty(ctx);
99 let int_ty = int_ty.deref(ctx);
100 if int_ty.signedness() != Signedness::Signless {
101 return verify_err!(op.loc(ctx), IntBinArithOpErr);
102 }
103
104 Ok(())
105 }
106}
107
108dict_key!(
109 ATTR_KEY_INTEGER_OVERFLOW_FLAGS,
111 "llvm_integer_overflow_flags"
112);
113
114#[derive(Error, Debug)]
115#[error("IntegerOverflowFlag missing on Op")]
116pub struct IntBinArithOpWithOverflowFlagErr;
117
118#[op_interface]
120pub trait IntBinArithOpWithOverflowFlag: IntBinArithOp {
121 fn new_with_overflow_flag(
123 ctx: &mut Context,
124 lhs: Value,
125 rhs: Value,
126 flag: IntegerOverflowFlagsAttr,
127 ) -> Self
128 where
129 Self: Sized,
130 {
131 let op = Self::new(ctx, lhs, rhs);
132 op.set_integer_overflow_flag(ctx, flag);
133 op
134 }
135
136 fn integer_overflow_flag(&self, ctx: &Context) -> IntegerOverflowFlagsAttr
138 where
139 Self: Sized,
140 {
141 self.get_operation()
142 .deref(ctx)
143 .attributes
144 .get::<IntegerOverflowFlagsAttr>(&ATTR_KEY_INTEGER_OVERFLOW_FLAGS)
145 .expect("Integer overflow flag missing or is of incorrect type")
146 .clone()
147 }
148
149 fn set_integer_overflow_flag(&self, ctx: &Context, flag: IntegerOverflowFlagsAttr)
151 where
152 Self: Sized,
153 {
154 self.get_operation()
155 .deref_mut(ctx)
156 .attributes
157 .set(ATTR_KEY_INTEGER_OVERFLOW_FLAGS.clone(), flag);
158 }
159
160 fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
161 where
162 Self: Sized,
163 {
164 let op = op.get_operation().deref(ctx);
165 if op
166 .attributes
167 .get::<IntegerOverflowFlagsAttr>(&ATTR_KEY_INTEGER_OVERFLOW_FLAGS)
168 .is_none()
169 {
170 return verify_err!(op.loc(), IntBinArithOpWithOverflowFlagErr);
171 }
172
173 Ok(())
174 }
175}
176
177#[op_interface]
179pub trait FloatBinArithOp: BinArithOp + ScalarOrVectorOpdImpls<dyn FloatTypeInterface, 0> {
180 fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
181 where
182 Self: Sized,
183 {
184 Ok(())
185 }
186}
187
188dict_key!(
189 ATTR_KEY_FAST_MATH_FLAGS,
191 "llvm_fast_math_flags"
192);
193
194#[derive(Error, Debug)]
195#[error("Fastmath flag missing on Op")]
196pub struct FastMathFlagMissingErr;
197
198#[op_interface]
200pub trait FastMathFlags {
201 fn fast_math_flags(&self, ctx: &Context) -> FastmathFlagsAttr
203 where
204 Self: Sized,
205 {
206 *self
207 .get_operation()
208 .deref(ctx)
209 .attributes
210 .get::<FastmathFlagsAttr>(&ATTR_KEY_FAST_MATH_FLAGS)
211 .expect("Fast math flags missing or is of incorrect type")
212 }
213
214 fn set_fast_math_flags(&self, ctx: &Context, flag: FastmathFlagsAttr)
216 where
217 Self: Sized,
218 {
219 self.get_operation()
220 .deref_mut(ctx)
221 .attributes
222 .set(ATTR_KEY_FAST_MATH_FLAGS.clone(), flag);
223 }
224
225 fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
226 where
227 Self: Sized,
228 {
229 let op = op.get_operation().deref(ctx);
230 if op
231 .attributes
232 .get::<FastmathFlagsAttr>(&ATTR_KEY_FAST_MATH_FLAGS)
233 .is_none()
234 {
235 return verify_err!(op.loc(), FastmathFlagMissingErr);
236 }
237
238 Ok(())
239 }
240}
241
242dict_key!(
243 ATTR_KEY_SYNC_SCOPE,
245 "llvm_syncscope"
246);
247
248#[derive(Error, Debug)]
249#[error("Synchronization scope missing on Op")]
250pub struct SyncScopeMissingErr;
251
252#[op_interface]
254pub trait SyncScopeInterface {
255 fn syncscope(&self, ctx: &Context) -> SyncScopeAttr
257 where
258 Self: Sized,
259 {
260 self.get_operation()
261 .deref(ctx)
262 .attributes
263 .get::<SyncScopeAttr>(&ATTR_KEY_SYNC_SCOPE)
264 .expect("Synchronization scope missing or is of incorrect type")
265 .clone()
266 }
267
268 fn set_syncscope(&self, ctx: &Context, syncscope: SyncScopeAttr)
270 where
271 Self: Sized,
272 {
273 self.get_operation()
274 .deref_mut(ctx)
275 .attributes
276 .set(ATTR_KEY_SYNC_SCOPE.clone(), syncscope);
277 }
278
279 fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
280 where
281 Self: Sized,
282 {
283 let op = op.get_operation().deref(ctx);
284 if op
285 .attributes
286 .get::<SyncScopeAttr>(&ATTR_KEY_SYNC_SCOPE)
287 .is_none()
288 {
289 return verify_err!(op.loc(), SyncScopeMissingErr);
290 }
291
292 Ok(())
293 }
294}
295
296#[op_interface]
298pub trait FloatBinArithOpWithFastMathFlags: FloatBinArithOp + FastMathFlags {
299 fn new_with_fast_math_flags(
301 ctx: &mut Context,
302 lhs: Value,
303 rhs: Value,
304 flag: FastmathFlagsAttr,
305 ) -> Self
306 where
307 Self: Sized,
308 {
309 let op = Self::new(ctx, lhs, rhs);
310 op.set_fast_math_flags(ctx, flag);
311 op
312 }
313
314 fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
315 where
316 Self: Sized,
317 {
318 Ok(())
319 }
320}
321
322#[derive(Error, Debug)]
323#[error("Fastmath flag missing on Op")]
324pub struct FastmathFlagMissingErr;
325
326dict_key!(
327 ATTR_KEY_NNEG_FLAG,
329 "llvm_nneg_flag"
330);
331
332#[op_interface]
333pub trait NNegFlag {
334 fn nneg(&self, ctx: &Context) -> bool {
336 self.get_operation()
337 .deref(ctx)
338 .attributes
339 .get::<BoolAttr>(&ATTR_KEY_NNEG_FLAG)
340 .expect("NNEG flag missing or is of incorrect type")
341 .clone()
342 .into()
343 }
344 fn set_nneg(&self, ctx: &Context, flag: bool) {
346 self.get_operation()
347 .deref_mut(ctx)
348 .attributes
349 .set(ATTR_KEY_NNEG_FLAG.clone(), BoolAttr::new(flag));
350 }
351 fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
352 where
353 Self: Sized,
354 {
355 let op = op.get_operation().deref(ctx);
356 if op.attributes.get::<BoolAttr>(&ATTR_KEY_NNEG_FLAG).is_none() {
357 return verify_err!(op.loc(), NNegFlagMissingErr);
358 }
359
360 Ok(())
361 }
362}
363
364#[derive(Error, Debug)]
365#[error("NNEG flag missing on Op")]
366pub struct NNegFlagMissingErr;
367
368#[derive(Error, Debug)]
369#[error("Result must be a pointer type, but is not")]
370pub struct PointerTypeResultVerifyErr;
371
372#[op_interface]
374pub trait PointerTypeResult: OneResultInterface + ResultNOfType<0, PointerType> {
375 fn result_pointee_type(&self, ctx: &Context) -> TypeHandle;
377
378 fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
379 where
380 Self: Sized,
381 {
382 if !op_cast::<dyn OneResultInterface>(op)
383 .expect("An Op here must impl OneResultInterface")
384 .result_type(ctx)
385 .deref(ctx)
386 .is::<PointerType>()
387 {
388 return verify_err!(op.loc(ctx), PointerTypeResultVerifyErr);
389 }
390
391 Ok(())
392 }
393}
394
395#[op_interface]
397pub trait CastOpInterface: OneResultInterface + OneOpdInterface {
398 fn new(ctx: &mut Context, operand: Value, res_type: TypeHandle) -> Self
400 where
401 Self: Sized,
402 {
403 Operation::new_op::<Self>(ctx, vec![res_type], vec![operand], vec![], 0)
404 }
405
406 fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
407 where
408 Self: Sized,
409 {
410 Ok(())
411 }
412}
413
414#[op_interface]
416pub trait CastOpWithNNegInterface: CastOpInterface + NNegFlag {
417 fn new_with_nneg(ctx: &mut Context, operand: Value, res_type: TypeHandle, nneg: bool) -> Self
419 where
420 Self: Sized,
421 {
422 let op = Self::new(ctx, operand, res_type);
423 op.set_nneg(ctx, nneg);
424 op
425 }
426
427 fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
428 where
429 Self: Sized,
430 {
431 Ok(())
432 }
433}
434
435#[op_interface]
437pub trait IsDeclaration {
438 fn is_declaration(&self, ctx: &Context) -> bool
440 where
441 Self: Sized;
442
443 fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
444 where
445 Self: Sized,
446 {
447 Ok(())
448 }
449}
450
451dict_key!(
452 ATTR_KEY_LLVM_SYMBOL_NAME,
454 "llvm_symbol_name"
455);
456
457#[op_interface]
460pub trait LlvmSymbolName: SymbolOpInterface {
461 fn llvm_symbol_name(&self, ctx: &Context) -> Option<String> {
463 self.get_operation()
464 .deref(ctx)
465 .attributes
466 .get::<pliron::builtin::attributes::StringAttr>(&ATTR_KEY_LLVM_SYMBOL_NAME)
467 .map(|attr| attr.clone().into())
468 }
469
470 fn set_llvm_symbol_name(&self, ctx: &Context, name: String) {
472 self.get_operation().deref_mut(ctx).attributes.set(
473 ATTR_KEY_LLVM_SYMBOL_NAME.clone(),
474 pliron::builtin::attributes::StringAttr::new(name),
475 );
476 }
477
478 fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
479 where
480 Self: Sized,
481 {
482 Ok(())
483 }
484}
485
486dict_key!(
487 ATTR_KEY_LLVM_ALIGNMENT,
489 "llvm_alignment"
490);
491
492#[op_interface]
494pub trait AlignableOpInterface {
495 fn alignment(&self, ctx: &Context) -> Option<u32>
497 where
498 Self: Sized,
499 {
500 self.get_operation()
501 .deref(ctx)
502 .attributes
503 .get::<AlignmentAttr>(&ATTR_KEY_LLVM_ALIGNMENT)
504 .map(|attr| attr.0)
505 }
506
507 fn set_alignment(&self, ctx: &Context, alignment: u32)
509 where
510 Self: Sized,
511 {
512 self.get_operation()
513 .deref_mut(ctx)
514 .attributes
515 .set(ATTR_KEY_LLVM_ALIGNMENT.clone(), AlignmentAttr(alignment));
516 }
517
518 fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
519 where
520 Self: Sized,
521 {
522 Ok(())
523 }
524}
525
526dict_key!(
527 ATTR_KEY_LLVM_VOLATILE,
529 "llvm_volatile"
530);
531
532#[op_interface]
534pub trait VolatilityOpInterface {
535 fn is_volatile(&self, ctx: &Context) -> bool
537 where
538 Self: Sized,
539 {
540 self.get_operation()
541 .deref(ctx)
542 .attributes
543 .get::<BoolAttr>(&ATTR_KEY_LLVM_VOLATILE)
544 .map(|attr| attr.clone().into())
545 .unwrap_or(false)
546 }
547
548 fn set_volatile(&self, ctx: &Context, is_volatile: bool)
550 where
551 Self: Sized,
552 {
553 self.get_operation()
554 .deref_mut(ctx)
555 .attributes
556 .set(ATTR_KEY_LLVM_VOLATILE.clone(), BoolAttr::new(is_volatile));
557 }
558
559 fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
560 where
561 Self: Sized,
562 {
563 Ok(())
564 }
565}
566
567#[derive(Debug, Error)]
568pub enum ScalarOrVectorErr {
569 #[error("{0} is not {1} or a vector of it")]
570 NotTOrVectorOfT(String, String),
571 #[error("{0} or its vector element type does not implement interface")]
572 TyOrElemNotImplsI(String),
573}
574
575fn vector_shape_of(ty: TypeHandle, ctx: &Context) -> Option<(u32, VectorTypeKind)> {
577 ty.deref(ctx)
578 .downcast_ref::<VectorType>()
579 .map(|vec_ty| (vec_ty.num_elements(), vec_ty.kind()))
580}
581
582fn elem_ty_of<T: Type>(ty: TypeHandle, ctx: &Context) -> TypedHandle<T> {
586 if let Ok(typed_handle) = TypedHandle::from_handle(ty, ctx) {
587 return typed_handle;
588 }
589 let ty_ref = &*ty.deref(ctx);
590 let elem_ty = ty_ref
591 .downcast_ref::<VectorType>()
592 .expect("verify() guarantees type is T or a vector of T")
593 .elem_type();
594 TypedHandle::from_handle(elem_ty, ctx).expect("verify() guarantees element type is T")
595}
596
597fn verify_t_or_vec_of_t<T: Type>(loc: Location, ty: &dyn Type, ctx: &Context) -> Result<()> {
599 if ty.is::<T>() {
600 return Ok(());
602 }
603 let Some(vec_ty) = ty.downcast_ref::<VectorType>() else {
605 return verify_err!(
606 loc,
607 ScalarOrVectorErr::NotTOrVectorOfT(
608 ty.get_type_id().disp(ctx).to_string(),
609 T::get_type_id_static().disp(ctx).to_string()
610 )
611 );
612 };
613 let elem_ty = &*vec_ty.elem_type().deref(ctx);
614 if !elem_ty.is::<T>() {
615 return verify_err!(
616 loc,
617 ScalarOrVectorErr::NotTOrVectorOfT(
618 ty.get_type_id().disp(ctx).to_string(),
619 T::get_type_id_static().disp(ctx).to_string()
620 )
621 );
622 }
623 Ok(())
624}
625
626fn elem_ty_of_impls<I: ?Sized + TypeInterfaceMarker + 'static>(
630 ty: TypeHandle,
631 ctx: &Context,
632) -> TypeInterfaceHandle<I> {
633 if let Ok(interface_handle) = TypeInterfaceHandle::from_handle(ty, ctx) {
634 return interface_handle;
635 }
636 let ty_ref = &*ty.deref(ctx);
637 let elem_ty = ty_ref
638 .downcast_ref::<VectorType>()
639 .expect("verify() guarantees type impls I or is a vector whose elem impls I")
640 .elem_type();
641 TypeInterfaceHandle::from_handle(elem_ty, ctx)
642 .expect("verify() guarantees element type impls I")
643}
644
645fn verify_impls_i_or_vec_of_impls_i<I: ?Sized + TypeInterfaceMarker + 'static>(
647 loc: Location,
648 ty: &dyn Type,
649 ctx: &Context,
650) -> Result<()> {
651 if type_impls::<I>(ty) {
652 return Ok(());
654 }
655 let Some(vec_ty) = ty.downcast_ref::<VectorType>() else {
657 return verify_err!(
658 loc,
659 ScalarOrVectorErr::TyOrElemNotImplsI(ty.get_type_id().disp(ctx).to_string())
660 );
661 };
662 let elem_ty = &*vec_ty.elem_type().deref(ctx);
663 if !type_impls::<I>(elem_ty) {
664 return verify_err!(
665 loc,
666 ScalarOrVectorErr::TyOrElemNotImplsI(ty.get_type_id().disp(ctx).to_string())
667 );
668 }
669 Ok(())
670}
671
672#[op_interface]
674pub trait ScalarOrVectorOpd<T: Type, const N: usize> {
675 fn scalar_or_vector_elem_ty(&self, ctx: &Context) -> TypedHandle<T> {
677 let op = &*self.get_operation().deref(ctx);
678 elem_ty_of(op.get_operand(N).get_type(ctx), ctx)
679 }
680
681 fn vector_shape(&self, ctx: &Context) -> Option<(u32, VectorTypeKind)> {
683 let op = &*self.get_operation().deref(ctx);
684 vector_shape_of(op.get_operand(N).get_type(ctx), ctx)
685 }
686
687 fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
688 where
689 Self: Sized,
690 {
691 let opd = verify_get_operand_n::<N>(op.get_operation(), ctx)?;
692 verify_t_or_vec_of_t::<T>(op.loc(ctx), &*opd.get_type(ctx).deref(ctx), ctx)
693 }
694}
695
696#[op_interface]
698pub trait ScalarOrVectorOpdImpls<I: ?Sized + TypeInterfaceMarker + 'static, const N: usize> {
699 fn scalar_or_vector_elem_ty(&self, ctx: &Context) -> TypeInterfaceHandle<I> {
701 let op = &*self.get_operation().deref(ctx);
702 elem_ty_of_impls::<I>(op.get_operand(N).get_type(ctx), ctx)
703 }
704
705 fn vector_shape(&self, ctx: &Context) -> Option<(u32, VectorTypeKind)> {
707 let op = &*self.get_operation().deref(ctx);
708 vector_shape_of(op.get_operand(N).get_type(ctx), ctx)
709 }
710
711 fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
712 where
713 Self: Sized,
714 {
715 let opd = verify_get_operand_n::<N>(op.get_operation(), ctx)?;
716 verify_impls_i_or_vec_of_impls_i::<I>(op.loc(ctx), &*opd.get_type(ctx).deref(ctx), ctx)
717 }
718}
719
720#[op_interface]
722pub trait ScalarOrVectorRes<T: Type, const N: usize> {
723 fn scalar_or_vector_elem_ty(&self, ctx: &Context) -> TypedHandle<T> {
725 let op = &*self.get_operation().deref(ctx);
726 elem_ty_of(op.get_result(N).get_type(ctx), ctx)
727 }
728
729 fn vector_shape(&self, ctx: &Context) -> Option<(u32, VectorTypeKind)> {
731 let op = &*self.get_operation().deref(ctx);
732 vector_shape_of(op.get_result(N).get_type(ctx), ctx)
733 }
734
735 fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
736 where
737 Self: Sized,
738 {
739 let res = verify_get_result_n::<N>(op.get_operation(), ctx)?;
740 verify_t_or_vec_of_t::<T>(op.loc(ctx), &*res.get_type(ctx).deref(ctx), ctx)
741 }
742}
743
744#[op_interface]
746pub trait ScalarOrVectorResImpls<I: ?Sized + TypeInterfaceMarker + 'static, const N: usize> {
747 fn scalar_or_vector_elem_ty(&self, ctx: &Context) -> TypeInterfaceHandle<I> {
749 let op = &*self.get_operation().deref(ctx);
750 elem_ty_of_impls::<I>(op.get_result(N).get_type(ctx), ctx)
751 }
752
753 fn vector_shape(&self, ctx: &Context) -> Option<(u32, VectorTypeKind)> {
755 let op = &*self.get_operation().deref(ctx);
756 vector_shape_of(op.get_result(N).get_type(ctx), ctx)
757 }
758
759 fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
760 where
761 Self: Sized,
762 {
763 let res = verify_get_result_n::<N>(op.get_operation(), ctx)?;
764 verify_impls_i_or_vec_of_impls_i::<I>(op.loc(ctx), &*res.get_type(ctx).deref(ctx), ctx)
765 }
766}