1use crate::{
2 AddressSpace, CanMaterialize, ConstantValue, ElemType, NoMemoryEffect,
3 dialect::synchronization::SyncScope,
4 prelude::*,
5 types::{AtomicType, PointerType, VectorType, scalar::*},
6};
7use pliron::{
8 alloc::vec::Vec,
9 attribute::{AttrObj, AttributeDict},
10 builtin::{attr_interfaces::TypedAttrInterface, ops::ConstantOp, types::IntegerType},
11 context::Context,
12 derive::{op_interface, type_interface},
13 opts::dce::SideEffects,
14 printable::Printable,
15 r#type::{TypeHandle, type_cast},
16 utils::apint::APInt,
17 value::Use,
18};
19
20pub mod aliasing;
21pub mod control_flow;
22pub mod memory_slot;
23pub mod traits;
24pub mod uniformity;
25
26#[macro_export]
27macro_rules! verify_op_succ {
28 () => {
29 fn verify(
30 _op: &dyn pliron::op::Op,
31 _ctx: &pliron::context::Context,
32 ) -> pliron::result::Result<()>
33 where
34 Self: Sized,
35 {
36 Ok(())
37 }
38 };
39}
40
41#[macro_export]
42macro_rules! verify_ty_succ {
43 () => {
44 fn verify(
45 _op: &dyn pliron::r#type::Type,
46 _ctx: &pliron::context::Context,
47 ) -> pliron::result::Result<()>
48 where
49 Self: Sized,
50 {
51 Ok(())
52 }
53 };
54}
55
56#[macro_export]
57macro_rules! verify_attr_succ {
58 () => {
59 fn verify(
60 _op: &dyn pliron::attribute::Attribute,
61 _ctx: &pliron::context::Context,
62 ) -> pliron::result::Result<()>
63 where
64 Self: Sized,
65 {
66 Ok(())
67 }
68 };
69}
70
71#[op_interface]
73pub trait ReturnLike: IsTerminatorInterface + NResultsInterface<0> {
74 verify_op_succ!();
75}
76
77#[op_interface]
78pub trait TriviallyUnrollable: MaterializableOp {
79 verify_op_succ!();
80}
81
82#[op_interface]
85pub trait MaterializableOp {
86 verify_op_succ!();
87 fn materialize(
88 &self,
89 ctx: &mut Context,
90 result_ty: Vec<TypeHandle>,
91 operands: Vec<Value>,
92 attributes: AttributeDict,
93 ) -> Ptr<Operation>;
94}
95
96CanMaterialize!(ConstantOp);
97
98#[op_interface]
99pub trait Synchronizes: SideEffects {
100 verify_op_succ!();
101
102 fn minimum_scope(&self, ctx: &Context) -> SyncScope;
105 fn maximum_scope(&self, ctx: &Context) -> SyncScope;
108}
109
110macro_rules! synchronizes {
111 ($ty: ty, $scope: expr) => {
112 #[::pliron::derive::op_interface_impl]
113 impl crate::interfaces::Synchronizes for $ty {
114 #[allow(unused_variables)]
115 fn minimum_scope(&self, ctx: &::pliron::context::Context) -> SyncScope {
116 $scope
117 }
118 #[allow(unused_variables)]
119 fn maximum_scope(&self, ctx: &::pliron::context::Context) -> SyncScope {
120 $scope
121 }
122 }
123 #[pliron::derive::op_interface_impl]
124 impl pliron::opts::dce::SideEffects for $ty {
125 fn has_side_effects(&self, _ctx: &Context) -> bool {
126 true
127 }
128 }
129 };
130}
131pub(crate) use synchronizes;
132
133#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
134pub enum MemoryEffect {
135 Read(Value),
136 Write(Value),
137 ReadAllInSpace(AddressSpace),
138 WriteAllInSpace(AddressSpace),
139 ReadAll,
140 WriteAll,
141 Opaque,
143}
144
145impl MemoryEffect {
146 pub fn value(&self) -> Option<Value> {
147 match self {
148 MemoryEffect::Read(value) | MemoryEffect::Write(value) => Some(*value),
149 MemoryEffect::ReadAllInSpace(_)
150 | MemoryEffect::WriteAllInSpace(_)
151 | MemoryEffect::ReadAll
152 | MemoryEffect::WriteAll
153 | MemoryEffect::Opaque => None,
154 }
155 }
156}
157
158impl Printable for MemoryEffect {
159 fn fmt(
160 &self,
161 ctx: &Context,
162 _state: &pliron::printable::State,
163 f: &mut core::fmt::Formatter<'_>,
164 ) -> core::fmt::Result {
165 match self {
166 MemoryEffect::Read(value) => write!(f, "Read({})", value.disp(ctx)),
167 MemoryEffect::Write(value) => write!(f, "Write({})", value.disp(ctx)),
168 MemoryEffect::ReadAllInSpace(address_space) => {
169 write!(f, "ReadAllInSpace({})", address_space.disp(ctx))
170 }
171 MemoryEffect::WriteAllInSpace(address_space) => {
172 write!(f, "WriteAllInSpace({})", address_space.disp(ctx))
173 }
174 MemoryEffect::ReadAll => write!(f, "ReadAll"),
175 MemoryEffect::WriteAll => write!(f, "WriteAll"),
176 MemoryEffect::Opaque => write!(f, "Opaque"),
177 }
178 }
179}
180
181#[op_interface]
182pub trait MemoryEffects {
183 verify_op_succ!();
184 fn memory_effects(&self, ctx: &Context) -> Vec<MemoryEffect>;
185 fn has_effects(&self, ctx: &Context) -> bool {
186 !self.memory_effects(ctx).is_empty()
187 }
188}
189
190NoMemoryEffect!(ConstantOp);
191
192#[type_interface]
193pub trait AlignedType {
194 verify_ty_succ!();
195
196 fn align(&self, ctx: &Context) -> usize;
197}
198
199#[macro_export]
200macro_rules! aligned {
201 ($ty: ty, $align: expr) => {
202 #[::pliron::derive::type_interface_impl]
203 impl $crate::interfaces::AlignedType for $ty {
204 #[allow(unused_variables)]
205 fn align(&self, ctx: &::pliron::context::Context) -> usize {
206 $align
207 }
208 }
209 };
210}
211
212#[type_interface]
213pub trait SizedType: AlignedType {
214 verify_ty_succ!();
215 fn size(&self, ctx: &Context) -> usize;
216 fn size_bits(&self, ctx: &Context) -> usize {
217 self.size(ctx) * 8
218 }
219}
220
221#[macro_export]
222macro_rules! sized {
223 ($ty: ty, $size: expr) => {
224 #[::pliron::derive::type_interface_impl]
225 impl $crate::interfaces::SizedType for $ty {
226 #[allow(unused_variables)]
227 fn size(&self, ctx: &::pliron::context::Context) -> usize {
228 $size
229 }
230 }
231 };
232}
233
234#[type_interface]
235pub trait MaybeVectorizedType {
236 verify_ty_succ!();
237
238 fn vector_size(&self, ctx: &Context) -> usize;
239 fn try_vector_size(&self, ctx: &Context) -> Option<usize> {
240 Some(self.vector_size(ctx))
241 }
242}
243
244#[macro_export]
245macro_rules! scalar {
246 ($ty: ty) => {
247 #[::pliron::derive::type_interface_impl]
248 impl $crate::interfaces::MaybeVectorizedType for $ty {
249 fn vector_size(&self, _ctx: &::pliron::context::Context) -> usize {
250 1
251 }
252 }
253
254 #[::pliron::derive::type_interface_impl]
255 impl $crate::interfaces::ScalarizableType for $ty {
256 fn scalar_type(&self, ctx: &Context) -> ::pliron::r#type::TypeHandle {
257 use ::pliron::r#type::Type;
258 self.get_self_handle(ctx)
259 }
260 }
261
262 #[::pliron::derive::type_interface_impl]
263 impl $crate::interfaces::HasElementType for $ty {
264 fn element_type(&self, ctx: &Context) -> Option<::pliron::r#type::TypeHandle> {
265 use ::pliron::r#type::Type;
266 Some(self.get_self_handle(ctx))
267 }
268 }
269 };
270}
271
272#[type_interface]
273pub trait MaybePackedType {
274 verify_ty_succ!();
275
276 fn packing_factor(&self, ctx: &Context) -> usize;
277}
278
279macro_rules! not_packed {
280 ($ty: ty) => {
281 #[::pliron::derive::type_interface_impl]
282 impl crate::interfaces::MaybePackedType for $ty {
283 fn packing_factor(&self, _ctx: &::pliron::context::Context) -> usize {
284 1
285 }
286 }
287 };
288}
289pub(crate) use not_packed;
290
291#[type_interface]
292pub trait ScalarizableType {
293 verify_ty_succ!();
294 fn scalar_type(&self, ctx: &Context) -> TypeHandle;
295}
296
297#[type_interface]
298pub trait ScalarType {
299 verify_ty_succ!();
300 fn elem_type(&self, ctx: &Context) -> ElemType;
301}
302
303#[type_interface]
304pub trait IndexableType {
305 verify_ty_succ!();
306
307 fn indexed_type(&self, ctx: &Context) -> TypeHandle;
308}
309
310#[type_interface]
311pub trait HasElementType {
312 verify_ty_succ!();
313 fn element_type(&self, ctx: &Context) -> Option<TypeHandle>;
314}
315
316#[op_interface]
317pub trait SimplifyInterface {
318 verify_op_succ!();
319 fn check_fold(&self, ctx: &Context, operand_attrs: &[Option<AttrObj>]) -> Option<Value>;
320}
321
322#[op_interface]
323pub trait CanonicalizeInterface {
324 verify_op_succ!();
325 fn canonicalize(&self, ctx: &mut Context, rewriter: &mut MatchRewriter) -> Result<()>;
326}
327
328#[attr_interface]
329pub trait ConstantAttr: TypedAttrInterface {
330 verify_attr_succ!();
331 fn as_const_val(&self, ctx: &Context) -> ConstantValue;
332 fn as_int(&self, ctx: &Context) -> Option<APInt> {
333 let _ = ctx;
334 None
335 }
336 fn float_as_f64(&self, _ctx: &Context) -> Option<f64> {
337 None
338 }
339}
340
341#[macro_export]
342macro_rules! try_cast_ty {
343 ($ty: expr, $ctx: expr, $interface: ty) => {
344 $crate::prelude::type_cast::<$interface>(&*$ty)
345 .ok_or_else(|| {
346 $crate::alloc::format!(
347 "Expected type {} {} to implement {}",
348 $ty.get_type_id(),
349 $ty.disp($ctx),
350 stringify!($interface)
351 )
352 })
353 .unwrap()
354 };
355}
356
357#[macro_export]
358macro_rules! try_cast_op {
359 ($op: expr, $ctx: expr, $interface: ty) => {
360 $crate::prelude::op_cast::<$interface>(&*$op)
361 .ok_or_else(|| {
362 $crate::alloc::format!(
363 "Expected op {} {} to implement {}",
364 $op.get_opid(),
365 $op.disp($ctx),
366 stringify!($interface)
367 )
368 })
369 .unwrap()
370 };
371}
372
373#[macro_export]
374macro_rules! match_ty {
375 (($handle: expr) { $($ty: ty => $body: expr,)*; _ => $default: expr }) => {
376 (|| {
377 $(if $handle.is::<$ty>() {
378 return $body;
379 })*
380 $default
381 })()
382 };
383 (($handle: expr) { $($ty: ty => $body: expr,)* }) => {
384 (|| {
385 $(if $handle.is::<$ty>() {
386 return $body;
387 })*
388 unreachable!()
389 })()
390 };
391}
392
393pub trait TypedExt: Typed {
394 fn size(&self, ctx: &Context) -> usize {
395 let ty = self.get_type(ctx).deref(ctx);
396 let sized = try_cast_ty!(ty, ctx, dyn SizedType);
397 sized.size(ctx)
398 }
399
400 fn size_bits(&self, ctx: &Context) -> usize {
401 let ty = self.get_type(ctx).deref(ctx);
402 let sized = try_cast_ty!(ty, ctx, dyn SizedType);
403 sized.size_bits(ctx)
404 }
405
406 fn unpacked_size_bits(&self, ctx: &Context) -> usize {
407 self.element_ty(ctx).scalar_ty(ctx).size_bits(ctx) / self.packing_factor(ctx)
408 }
409
410 fn align(&self, ctx: &Context) -> usize {
411 let ty = self.get_type(ctx).deref(ctx);
412 let aligned = try_cast_ty!(ty, ctx, dyn AlignedType);
413 aligned.align(ctx)
414 }
415
416 fn is_ptr(&self, ctx: &Context) -> bool {
417 let ty = self.get_type(ctx).deref(ctx);
418 ty.is::<PointerType>()
419 }
420
421 fn is_atomic(&self, ctx: &Context) -> bool {
422 let ty = self.get_type(ctx).deref(ctx);
423 ty.is::<AtomicType>()
424 }
425
426 fn is_vector(&self, ctx: &Context) -> bool {
427 let ty = self.get_type(ctx).deref(ctx);
428 ty.is::<VectorType>()
429 }
430
431 fn is_vector_of_size(&self, ctx: &Context, size: usize) -> bool {
432 let ty = self.get_type(ctx).deref(ctx);
433 ty.downcast_ref::<VectorType>()
434 .is_some_and(|it| it.vectorization == size)
435 }
436
437 fn is_immutable(&self, ctx: &Context) -> bool {
438 !self.is_ptr(ctx)
439 }
440
441 fn vector_size(&self, ctx: &Context) -> usize {
442 let ty = self.get_type(ctx).deref(ctx);
443 let maybe_vec = try_cast_ty!(ty, ctx, dyn MaybeVectorizedType);
444 maybe_vec.vector_size(ctx)
445 }
446
447 fn try_get_vector_size(&self, ctx: &Context) -> Option<usize> {
448 let ty = self.get_type(ctx).deref(ctx);
449 let maybe_vec = type_cast::<dyn MaybeVectorizedType>(&*ty)?;
450 maybe_vec.try_vector_size(ctx)
451 }
452
453 fn packing_factor(&self, ctx: &Context) -> usize {
454 let ty = self.get_type(ctx).deref(ctx);
455 let maybe_packed = try_cast_ty!(ty, ctx, dyn MaybePackedType);
456 maybe_packed.packing_factor(ctx)
457 }
458
459 fn scalar_ty(&self, ctx: &Context) -> TypeHandle {
460 let ty = self.element_ty(ctx).deref(ctx);
461 let scalarizable = try_cast_ty!(ty, ctx, dyn ScalarizableType);
462 scalarizable.scalar_type(ctx)
463 }
464
465 fn element_ty(&self, ctx: &Context) -> TypeHandle {
466 let ty = self.get_type(ctx).deref(ctx);
467 let has_element_type = try_cast_ty!(ty, ctx, dyn HasElementType);
468 has_element_type
469 .element_type(ctx)
470 .expect("Expected element type to be some")
471 }
472
473 fn unwrap_ptr(&self, ctx: &Context) -> TypeHandle {
474 if let Some(ptr) = self.get_type(ctx).deref(ctx).downcast_ref::<PointerType>() {
475 ptr.inner
476 } else {
477 self.get_type(ctx)
478 }
479 }
480
481 fn try_get_scalar_ty(&self, ctx: &Context) -> Option<TypeHandle> {
482 let ty = self.get_type(ctx).deref(ctx);
483 let scalarizable = type_cast::<dyn ScalarizableType>(&*ty)?;
484 Some(scalarizable.scalar_type(ctx))
485 }
486
487 fn try_get_scalar_elem_ty(&self, ctx: &Context) -> Option<TypeHandle> {
488 let ty = self.get_type(ctx).deref(ctx);
489 let has_elem = type_cast::<dyn HasElementType>(&*ty)?;
490 let ty = has_elem.element_type(ctx)?.deref(ctx);
491 let scalarizable = type_cast::<dyn ScalarizableType>(&*ty)?;
492 Some(scalarizable.scalar_type(ctx))
493 }
494
495 fn is_index(&self, ctx: &Context) -> bool {
496 let ty = self.get_type(ctx).deref(ctx);
497 ty.is::<IndexType>()
498 }
499
500 fn is_int(&self, ctx: &Context) -> bool {
501 let ty = self.get_type(ctx).deref(ctx);
502 ty.is::<IntegerType>()
503 }
504
505 fn is_signed_int(&self, ctx: &Context) -> bool {
506 let ty = self.get_type(ctx).deref(ctx);
507 ty.downcast_ref::<IntegerType>()
508 .is_some_and(|it| it.is_signed())
509 }
510
511 fn is_unsigned_int(&self, ctx: &Context) -> bool {
512 let ty = self.get_type(ctx).deref(ctx);
513 ty.downcast_ref::<IntegerType>()
514 .is_some_and(|it| !it.is_signed())
515 }
516
517 fn is_int_of_width(&self, ctx: &Context, width: usize) -> bool {
518 let ty = self.get_type(ctx).deref(ctx);
519 ty.downcast_ref::<IntegerType>()
520 .is_some_and(|it| it.width() as usize == width)
521 }
522
523 fn is_float64(&self, ctx: &Context) -> bool {
524 self.get_type(ctx).deref(ctx).is::<Float64Type>()
525 }
526
527 fn is_float32(&self, ctx: &Context) -> bool {
528 self.get_type(ctx).deref(ctx).is::<Float32Type>()
529 }
530
531 fn is_tfloat32(&self, ctx: &Context) -> bool {
532 self.get_type(ctx).deref(ctx).is::<TFloat32Type>()
533 }
534
535 fn is_float16(&self, ctx: &Context) -> bool {
536 self.get_type(ctx).deref(ctx).is::<Float16Type>()
537 }
538
539 fn is_bfloat16(&self, ctx: &Context) -> bool {
540 self.get_type(ctx).deref(ctx).is::<BFloat16Type>()
541 }
542
543 fn is_float(&self, ctx: &Context) -> bool {
544 self.is_float16(ctx)
545 | self.is_float32(ctx)
546 | self.is_float64(ctx)
547 | self.is_tfloat32(ctx)
548 | self.is_bfloat16(ctx)
549 }
550
551 fn is_bool(&self, ctx: &Context) -> bool {
552 self.get_type(ctx).deref(ctx).is::<BoolType>()
553 }
554}
555
556impl<T: Typed> TypedExt for T {}
557
558pub trait TypeExt {
559 fn as_ptr(&self, ctx: &Context) -> PointerType;
560}
561
562impl TypeExt for TypeHandle {
563 fn as_ptr(&self, ctx: &Context) -> PointerType {
564 *TypedHandle::from_handle(*self, ctx)
565 .expect("Should be pointer")
566 .deref(ctx)
567 }
568}
569
570pub trait ValueExt {
571 fn replace_all_uses_except_with(&self, ctx: &Context, except: Use<Value>, other: &Value);
572}
573
574impl ValueExt for Value {
575 fn replace_all_uses_except_with(&self, ctx: &Context, except: Use<Value>, other: &Value) {
576 self.replace_some_uses_with(ctx, |_, r#use| r#use != &except, other);
577 }
578}