1use core::{
2 fmt::Debug,
3 hash::Hash,
4 marker::PhantomData,
5 ops::{Deref, Range},
6};
7
8use cubecl_macros_internal::NamedRewrite;
9use derive_more::{Deref, DerefMut, From};
10use derive_new::new;
11use pliron::{
12 attribute::{AttrObj, Attribute},
13 builtin::{
14 given_names::{get_operation_result_name, set_operation_result_name},
15 ops::ConstantOp,
16 },
17 graph::walkers::{
18 WalkConfig,
19 uninterruptible::{
20 immutable::{self},
21 mutable,
22 },
23 },
24 irbuild::{
25 dialect_conversion::apply_dialect_conversion,
26 match_rewrite::{RewriterOrder, apply_match_rewrite},
27 },
28 op::{OpInterfaceMarker, OpObj},
29 value::Use,
30 verify_err_noloc,
31};
32
33use crate::{
34 dialect::BlockPtrExt,
35 interfaces::{CanonicalizeInterface, SimplifyInterface},
36 prelude::*,
37};
38
39pub const WALKCONFIG_ANY: WalkConfig = WALKCONFIG_PREORDER_FORWARD;
41
42pub trait NamedRewrite {
43 fn name(&self) -> &str;
44}
45
46#[derive(new, From, Clone, Debug, Default)]
47pub struct DialectConversionPass<T: DialectConversion>(T);
48
49impl<T: DialectConversion + NamedRewrite> Pass for DialectConversionPass<T> {
50 fn name(&self) -> &str {
51 self.0.name()
52 }
53
54 fn run(
55 &mut self,
56 op: Ptr<Operation>,
57 ctx: &mut Context,
58 _analyses: &mut AnalysisManager,
59 ) -> Result<PassResult> {
60 let mut res = PassResult::default();
61 res.ir_changed = apply_dialect_conversion(ctx, &mut self.0, op)?;
62 Ok(res)
63 }
64}
65
66#[derive(new, From, Clone, Debug, Default)]
67pub struct MatchRewritePass<T: MatchRewrite>(pub T);
68
69impl<T: MatchRewrite + NamedRewrite> Pass for MatchRewritePass<T> {
70 fn name(&self) -> &str {
71 self.0.name()
72 }
73
74 fn run(
75 &mut self,
76 op: Ptr<Operation>,
77 ctx: &mut Context,
78 _analyses: &mut AnalysisManager,
79 ) -> Result<PassResult> {
80 let mut res = PassResult::default();
81 res.ir_changed = apply_match_rewrite(ctx, &mut self.0, RewriterOrder::default(), op)?;
82 Ok(res)
83 }
84}
85
86#[derive(new, Clone, Copy, Default, Debug)]
87pub struct CombinedPass<P1: Pass, P2: Pass> {
88 pass_1: P1,
89 pass_2: P2,
90}
91
92#[pass_name]
93impl<P1: Pass, P2: Pass> Pass for CombinedPass<P1, P2> {
94 fn run(
95 &mut self,
96 op: Ptr<Operation>,
97 ctx: &mut Context,
98 analyses: &mut AnalysisManager,
99 ) -> Result<PassResult> {
100 let mut res = PassResult::default();
101 res.ir_changed |= self.pass_1.run(op, ctx, analyses)?.ir_changed;
102 res.ir_changed |= self.pass_2.run(op, ctx, analyses)?.ir_changed;
103 Ok(res)
104 }
105}
106
107pub type SimplifyOpsPass = MatchRewritePass<SimplifyOps>;
108
109#[derive(Default, Clone, Copy, NamedRewrite)]
110pub struct SimplifyOps;
111
112impl MatchRewrite for SimplifyOps {
113 fn r#match(&mut self, ctx: &Context, op: Ptr<Operation>) -> bool {
114 op.impls::<dyn SimplifyInterface>(ctx)
115 }
116
117 fn rewrite(
118 &mut self,
119 ctx: &mut Context,
120 rewriter: &mut MatchRewriter,
121 op: Ptr<Operation>,
122 ) -> Result<()> {
123 let dyn_op = op.dyn_op(ctx);
124 let operand_attrs = const_operands(ctx, op);
125 let simplify = op_cast::<dyn SimplifyInterface>(&*dyn_op).unwrap();
126 if let Some(value) = simplify.check_fold(ctx, &operand_attrs) {
127 rewriter.replace_operation_with_values(ctx, op, vec![value]);
128 }
129 Ok(())
130 }
131}
132
133pub fn const_operand<T: Attribute>(ctx: &Context, op: Ptr<Operation>, idx: usize) -> Option<T> {
134 Some(*const_operands(ctx, op).remove(idx)?.downcast().ok()?)
135}
136
137pub fn const_operands(ctx: &Context, op: Ptr<Operation>) -> Vec<Option<AttrObj>> {
138 op.deref(ctx)
139 .operands()
140 .map(|opd| {
141 Some(
142 opd.defining_op()?
143 .as_op::<ConstantOp>(ctx)?
144 .get_attr_builtin_constant_value(ctx)?
145 .clone(),
146 )
147 })
148 .collect()
149}
150
151pub type CanonicalizePass = MatchRewritePass<Canonicalize>;
152
153#[derive(Default, Clone, Copy, NamedRewrite)]
154pub struct Canonicalize;
155
156impl MatchRewrite for Canonicalize {
157 fn r#match(&mut self, ctx: &Context, op: Ptr<Operation>) -> bool {
158 op.impls::<dyn CanonicalizeInterface>(ctx)
159 }
160
161 fn rewrite(
162 &mut self,
163 ctx: &mut Context,
164 rewriter: &mut MatchRewriter,
165 op: Ptr<Operation>,
166 ) -> Result<()> {
167 let dyn_op = op.dyn_op(ctx);
168 let canonicalize = op_cast::<dyn CanonicalizeInterface>(&*dyn_op).unwrap();
169 canonicalize.canonicalize(ctx, rewriter)?;
170 Ok(())
171 }
172}
173
174pub type VisitOpsCallback<T, State> = fn(&Context, &mut State, T);
175pub type VisitOpsMutCallback<T, State> = fn(&mut Context, &mut State, T);
176
177pub fn visit_all_ops_of_type<T: Op, State>(
178 ctx: &Context,
179 state: &mut State,
180 root: Ptr<Operation>,
181 callback: VisitOpsCallback<T, State>,
182) {
183 immutable::walk_op(
184 ctx,
185 &mut (state, callback),
186 &WALKCONFIG_PREORDER_FORWARD,
187 root,
188 |ctx, (state, callback), node| {
189 if let IRNode::Operation(op) = node
190 && let Some(op) = op.as_op::<T>(ctx)
191 {
192 callback(ctx, state, op)
193 }
194 },
195 );
196}
197
198pub fn visit_all_ops_of_type_mut<T: Op, State>(
199 ctx: &mut Context,
200 state: &mut State,
201 root: Ptr<Operation>,
202 callback: VisitOpsMutCallback<T, State>,
203) {
204 mutable::walk_op(
205 ctx,
206 &mut (state, callback),
207 &WALKCONFIG_PREORDER_FORWARD,
208 root,
209 |ctx, (state, callback), node| {
210 if let IRNode::Operation(op) = node
211 && let Some(op) = op.as_op::<T>(ctx)
212 {
213 callback(ctx, state, op)
214 }
215 },
216 );
217}
218
219pub fn visit_all_ops_with_interface<T: ?Sized + OpInterfaceMarker + 'static, State>(
220 ctx: &Context,
221 state: &mut State,
222 root: Ptr<Operation>,
223 callback: for<'a> fn(&Context, &mut State, &'a T),
224) {
225 immutable::walk_op(
226 ctx,
227 &mut (state, callback),
228 &WALKCONFIG_ANY,
229 root,
230 |ctx, (state, callback), node| {
231 if let IRNode::Operation(op) = node {
232 let dyn_op = op.dyn_op(ctx);
233 if let Some(op) = op_cast::<T>(&*dyn_op) {
234 callback(ctx, state, op)
235 }
236 }
237 },
238 );
239}
240
241pub fn visit_all_values<State>(
242 ctx: &Context,
243 state: &mut State,
244 root: Ptr<Operation>,
245 callback: for<'a> fn(&Context, &mut State, Value),
246) {
247 immutable::walk_op(
248 ctx,
249 &mut (state, callback),
250 &WALKCONFIG_PREORDER_FORWARD,
251 root,
252 |ctx, (state, callback), node| match node {
253 IRNode::Operation(ptr) => {
254 for res in ptr.results(ctx) {
255 callback(ctx, state, res)
256 }
257 }
258 IRNode::BasicBlock(ptr) => {
259 for res in ptr.arguments(ctx) {
260 callback(ctx, state, res)
261 }
262 }
263 IRNode::Region(_) => {}
264 },
265 );
266}
267
268pub trait RewriteOp<T: Op> {
269 fn should_rewrite(&self, _ctx: &Context, _op: T) -> bool {
270 true
271 }
272
273 fn rewrite(&mut self, ctx: &mut Context, rewriter: &mut MatchRewriter, op: T);
274}
275
276#[derive(new, Deref, DerefMut)]
277pub struct MatchRewriteOp<T: Op, R: RewriteOp<T>> {
278 #[deref]
279 #[deref_mut]
280 pub inner: R,
281 _t: PhantomData<T>,
282}
283
284impl<T: Op, R: RewriteOp<T>> From<R> for MatchRewriteOp<T, R> {
285 fn from(value: R) -> Self {
286 Self::new(value)
287 }
288}
289
290impl<T: Op, R: RewriteOp<T>> MatchRewrite for MatchRewriteOp<T, R> {
291 fn r#match(&mut self, ctx: &Context, op: Ptr<Operation>) -> bool {
292 op.as_op::<T>(ctx)
293 .is_some_and(|op| self.should_rewrite(ctx, op))
294 }
295
296 fn rewrite(
297 &mut self,
298 ctx: &mut Context,
299 rewriter: &mut MatchRewriter,
300 op: Ptr<Operation>,
301 ) -> Result<()> {
302 let op = op.as_op::<T>(ctx).unwrap();
303 RewriteOp::rewrite(&mut self.inner, ctx, rewriter, op);
304 Ok(())
305 }
306}
307
308pub trait RewriterExt: Rewriter {
309 fn replace_op_with(&mut self, ctx: &mut Context, op: Ptr<Operation>, new_op: Ptr<Operation>) {
310 new_op.insert_before(ctx, op);
311 transfer_result_names(ctx, op, &new_op.results(ctx));
312 self.replace_operation(ctx, op, new_op);
313 }
314 fn append_op_with_result(&mut self, ctx: &mut Context, op: &impl OneResultInterface) -> Value {
315 self.append_op(ctx, op);
316 op.get_result(ctx)
317 }
318}
319impl<R: Rewriter> RewriterExt for R {}
320
321pub fn transfer_result_names(ctx: &Context, old_op: Ptr<Operation>, values: &[Value]) {
322 for (idx, value) in values.iter().enumerate() {
323 transfer_result_name(ctx, old_op, *value, idx);
324 }
325}
326
327pub fn transfer_result_name(ctx: &Context, old_op: Ptr<Operation>, value: Value, idx: usize) {
328 if let Some(new_op) = value.defining_op() {
329 set_operation_result_name(
330 ctx,
331 new_op,
332 idx,
333 get_operation_result_name(ctx, old_op, idx),
334 );
335 }
336}
337
338pub struct TraitOp<T: OpInterfaceMarker + ?Sized> {
339 obj: OpObj,
340 _marker: PhantomData<T>,
341}
342
343impl<T: OpInterfaceMarker + ?Sized> TraitOp<T> {
344 pub fn dyn_op(&self) -> &dyn Op {
345 &*self.obj
346 }
347}
348
349impl<T: OpInterfaceMarker + 'static + ?Sized> TraitOp<T> {
350 pub fn try_from_op(op: Ptr<Operation>, ctx: &Context) -> Option<Self> {
351 let op = op.dyn_op(ctx);
352 if !op_impls::<T>(&*op) {
353 None
354 } else {
355 Some(TraitOp {
356 obj: op,
357 _marker: PhantomData,
358 })
359 }
360 }
361}
362
363impl<T: OpInterfaceMarker + 'static + ?Sized> TryFrom<OpObj> for TraitOp<T> {
364 type Error = pliron::result::Error;
365
366 fn try_from(value: OpObj) -> Result<Self> {
367 if !op_impls::<T>(&*value) {
368 verify_err_noloc!("Op doesn't implement trait")
369 } else {
370 Ok(TraitOp {
371 obj: value,
372 _marker: PhantomData,
373 })
374 }
375 }
376}
377
378impl<T: OpInterfaceMarker + 'static + ?Sized> Deref for TraitOp<T> {
379 type Target = T;
380
381 fn deref(&self) -> &Self::Target {
382 op_cast(self.obj.as_ref()).unwrap()
383 }
384}
385
386impl<T: OpInterfaceMarker + 'static + ?Sized> Eq for TraitOp<T> {}
387impl<T: OpInterfaceMarker + 'static + ?Sized> PartialEq for TraitOp<T> {
388 fn eq(&self, other: &Self) -> bool {
389 self.obj == other.obj && self._marker == other._marker
390 }
391}
392
393impl<T: OpInterfaceMarker + 'static + ?Sized> Hash for TraitOp<T> {
394 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
395 self.obj.hash(state);
396 }
397}
398
399impl<T: OpInterfaceMarker + 'static + ?Sized> Copy for TraitOp<T> {}
400impl<T: OpInterfaceMarker + 'static + ?Sized> Clone for TraitOp<T> {
401 fn clone(&self) -> Self {
402 *self
403 }
404}
405
406impl<T: OpInterfaceMarker + 'static + ?Sized> Debug for TraitOp<T> {
407 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
408 let op = self.obj.get_operation();
409 Debug::fmt(&op, f)
410 }
411}
412
413pub(crate) fn operand_range_to_uses(
414 ctx: &Context,
415 op: Ptr<Operation>,
416 range: Range<usize>,
417) -> Vec<Use<Value>> {
418 let op = op.deref(ctx);
419 range.map(|idx| op.get_operand_as_use(idx)).collect()
420}
421
422#[macro_export]
423macro_rules! small_map {
424 {$($k: expr => $v: expr),* $(,)?} => {{
425 let mut out = $crate::pliron::utils::table::SmallMap::new();
426 $(out.insert($k, $v);)*
427 out
428 }};
429}
430
431#[macro_export]
432macro_rules! small_set {
433 {$($v: expr),* $(,)?} => {{
434 let mut out = $crate::pliron::utils::table::SmallSet::new();
435 $(out.insert($v);)*
436 out
437 }};
438}