Skip to main content

cubecl_opt/
instructions.rs

1use cubecl_ir::{
2    CoopMma, Instruction, Marker, Metadata, NonSemantic, Operation, OperationReflect, Operator,
3    TensorIndexingOps, TmaOps, Value, ValueKind,
4};
5
6use crate::{
7    ControlFlow, Function, GlobalState, MemoryBlock, analyses::pointer_source::PointerSource,
8};
9
10impl Function {
11    pub fn visit_out(
12        &mut self,
13        val: &mut Option<Value>,
14        mut visit_write: impl FnMut(&mut Self, &mut Value),
15    ) {
16        if let Some(out) = val {
17            visit_write(self, out);
18        }
19    }
20
21    /// Visit an instruction with only a write visitor. Visits both `out` and any pointer writes.
22    pub fn visit_instruction_memory_writes(
23        &mut self,
24        state: &GlobalState,
25        inst: &Instruction,
26        mut visit_write: impl FnMut(&mut Self, &MemoryBlock),
27    ) {
28        let pointer_source = self.analysis::<PointerSource>(state);
29        for ptr in inst.operation.write_pointers() {
30            if let Some(source) = pointer_source.borrow_mut().get_mut(&ptr.id()) {
31                visit_write(self, source);
32            }
33        }
34
35        if let Some(Value {
36            kind: ValueKind::Value { id },
37            ..
38        }) = inst.out
39            && let Some(source) = pointer_source.borrow_mut().get_mut(&id)
40        {
41            visit_write(self, source);
42        }
43    }
44
45    pub fn visit_instruction_memory_reads(
46        &mut self,
47        state: &GlobalState,
48        inst: &Instruction,
49        mut visit_read: impl FnMut(&mut Self, &MemoryBlock),
50    ) {
51        let pointer_source = self.analysis::<PointerSource>(state);
52        for ptr in inst.operation.read_pointers() {
53            if let Some(source) = pointer_source.borrow_mut().get_mut(&ptr.id()) {
54                visit_read(self, source);
55            }
56        }
57    }
58
59    /// Visit an operation with a set of read and write visitors. Each visitor will be called with
60    /// each read or written to variable.
61    pub fn visit_instruction(
62        &mut self,
63        state: &GlobalState,
64        inst: &mut Instruction,
65        visit_read: impl FnMut(&mut Self, &mut Value),
66        visit_write: impl FnMut(&mut Self, &mut Value),
67    ) {
68        self.visit_operation(state, &mut inst.operation, visit_read);
69        self.visit_out(&mut inst.out, visit_write);
70    }
71
72    /// Visit an operation with a set of read and write visitors. Each visitor will be called with
73    /// each read or written to variable.
74    pub fn visit_operation(
75        &mut self,
76        state: &GlobalState,
77        op: &mut Operation,
78        mut visit_read: impl FnMut(&mut Self, &mut Value),
79    ) {
80        match op {
81            Operation::Marker(Marker::Free(_)) => {}
82            Operation::Metadata(meta) => self.visit_meta(meta, visit_read),
83            Operation::CoopMma(coop_mma) => self.visit_cmma(state, coop_mma, visit_read),
84            Operation::Branch(_) => unreachable!(),
85            Operation::Tma(tma_ops) => self.visit_tma(tma_ops, visit_read),
86            Operation::TensorIndexing(tensor_ops) => self.visit_tensor_ops(tensor_ops, visit_read),
87            Operation::NonSemantic(non_semantic) => {
88                self.visit_nonsemantic(non_semantic, visit_read)
89            }
90            Operation::Operator(Operator::ReadBuiltin(_)) => {}
91            Operation::DeclareVariable { .. } => {}
92            op => {
93                if let Some(args) = op.args_mut() {
94                    for arg in args {
95                        visit_read(self, arg);
96                    }
97                } else {
98                    panic!("Found op {op} which doesn't reflect. Needs special handling.");
99                }
100            }
101        }
102    }
103
104    /// Visit a control flow finisher with a set of read and write visitors. Each visitor will be called with
105    /// each read or written to variable.
106    pub fn visit_control_flow(
107        &mut self,
108        op: &mut ControlFlow,
109        mut visit_read: impl FnMut(&mut Self, &mut Value),
110    ) {
111        match op {
112            ControlFlow::IfElse { cond, .. } => visit_read(self, cond),
113            ControlFlow::Switch { value, .. } => visit_read(self, value),
114            ControlFlow::Loop { .. } => {}
115            ControlFlow::LoopBreak { break_cond, .. } => visit_read(self, break_cond),
116            ControlFlow::Return { value } => {
117                if let Some(value) = value {
118                    visit_read(self, value);
119                }
120            }
121            ControlFlow::Unreachable | ControlFlow::None => {}
122        }
123    }
124
125    fn visit_meta(
126        &mut self,
127        metadata: &mut Metadata,
128        mut visit_read: impl FnMut(&mut Self, &mut Value),
129    ) {
130        // Don't count buffer as a read, since it's actually the info buffer that's read.
131        match metadata {
132            Metadata::BufferLength { .. } => {}
133            Metadata::Stride { dim, .. } => {
134                visit_read(self, dim);
135            }
136            Metadata::Shape { dim, .. } => {
137                visit_read(self, dim);
138            }
139        }
140    }
141
142    fn visit_cmma(
143        &mut self,
144        state: &GlobalState,
145        cmma: &mut CoopMma,
146        mut visit_read: impl FnMut(&mut Self, &mut Value),
147    ) {
148        match cmma {
149            CoopMma::Fill { value } => {
150                visit_read(self, value);
151            }
152            CoopMma::Load {
153                ptr,
154                stride,
155                layout: _,
156            } => {
157                visit_read(self, ptr);
158                visit_read(self, stride);
159            }
160            CoopMma::LoadTensor {
161                buffer,
162                layout,
163                view,
164            } => {
165                visit_read(self, buffer);
166                visit_read(self, layout);
167                if let Some(view) = view {
168                    visit_read(self, view);
169                }
170            }
171            CoopMma::Execute {
172                mat_a,
173                mat_b,
174                mat_c,
175            } => {
176                visit_read(self, mat_a);
177                visit_read(self, mat_b);
178                visit_read(self, mat_c);
179            }
180            CoopMma::Store {
181                mat,
182                stride,
183                destination,
184                layout: _,
185            } => {
186                visit_read(self, mat);
187                visit_read(self, stride);
188                visit_read(self, destination);
189            }
190            CoopMma::StoreTensor { mat, layout, view } => {
191                visit_read(self, mat);
192                visit_read(self, layout);
193                if let Some(view) = view {
194                    visit_read(self, view);
195                }
196            }
197            CoopMma::Cast { input } => {
198                visit_read(self, input);
199            }
200            CoopMma::RowIndex { lane_id, i, .. } => {
201                visit_read(self, lane_id);
202                visit_read(self, i);
203            }
204            CoopMma::ColIndex { lane_id, i, .. } => {
205                visit_read(self, lane_id);
206                visit_read(self, i);
207            }
208            CoopMma::LoadMatrix { ptr, .. } => {
209                visit_read(self, ptr);
210            }
211            CoopMma::StoreMatrix { registers, .. } => {
212                visit_read(self, registers);
213            }
214            CoopMma::ExecuteManual {
215                registers_a,
216                registers_b,
217                registers_c,
218                ..
219            } => {
220                visit_read(self, registers_a);
221                visit_read(self, registers_b);
222                visit_read(self, registers_c);
223            }
224            CoopMma::ExecuteScaled {
225                registers_a,
226                registers_b,
227                registers_c,
228                scales_a,
229                scales_b,
230                ..
231            } => {
232                visit_read(self, registers_a);
233                visit_read(self, registers_b);
234                visit_read(self, registers_c);
235                visit_read(self, scales_a);
236                visit_read(self, scales_b);
237            }
238            CoopMma::ExecuteElementwise { matrix, op } => {
239                visit_read(self, matrix);
240                let func = &state.extra_functions[op];
241                for mut capture in func.implicit_params.clone() {
242                    visit_read(self, &mut capture)
243                }
244            }
245        }
246    }
247
248    fn visit_tma(
249        &mut self,
250        tma_ops: &mut TmaOps,
251        mut visit_read: impl FnMut(&mut Self, &mut Value),
252    ) {
253        match tma_ops {
254            TmaOps::TmaStore {
255                source,
256                coordinates,
257            } => {
258                visit_read(self, source);
259                for coord in coordinates {
260                    visit_read(self, coord)
261                }
262            }
263            TmaOps::CommitGroup | TmaOps::WaitGroup { .. } | TmaOps::WaitGroupRead { .. } => {}
264        }
265    }
266
267    fn visit_tensor_ops(
268        &mut self,
269        tensor_ops: &mut TensorIndexingOps,
270        mut visit_read: impl FnMut(&mut Self, &mut Value),
271    ) {
272        match tensor_ops {
273            TensorIndexingOps::CreateLayout {
274                shape,
275                strides,
276                clamp_mode: _,
277            } => {
278                for s in shape {
279                    visit_read(self, s);
280                }
281                for s in strides.iter_mut().flatten() {
282                    visit_read(self, s);
283                }
284            }
285            TensorIndexingOps::CreateView => {}
286            TensorIndexingOps::Slice {
287                layout,
288                offsets,
289                shape,
290            } => {
291                visit_read(self, layout);
292                for o in offsets {
293                    visit_read(self, o);
294                }
295                for s in shape {
296                    visit_read(self, s);
297                }
298            }
299        }
300    }
301
302    fn visit_nonsemantic(
303        &mut self,
304        non_semantic: &mut NonSemantic,
305        mut visit_read: impl FnMut(&mut Self, &mut Value),
306    ) {
307        match non_semantic {
308            NonSemantic::Comment { .. }
309            | NonSemantic::EnterDebugScope
310            | NonSemantic::ExitDebugScope => {}
311            NonSemantic::Print { args, .. } => {
312                for arg in args {
313                    visit_read(self, arg);
314                }
315            }
316        }
317    }
318}