1use core::fmt::Debug;
2
3use alloc::vec::Vec;
4
5use cubecl_ir::{
6 Branch, CoopMma, GlobalState, Instruction, Marker, NonSemantic, Operation, OperationReflect,
7 Operator, Scope, TensorIndexingOps, TmaOps, Value,
8};
9use derive_more::{Deref, DerefMut};
10
11use crate::post_processing::{analysis_helper::GlobalAnalyses, util::AtomicCounter};
12
13pub trait InstructionVisitor: Debug {
19 fn visit_instruction(
20 &mut self,
21 instruction: Instruction,
22 global_state: &GlobalState,
23 analyses: &GlobalAnalyses,
24 changes: &AtomicCounter,
25 ) -> Vec<Instruction>;
26
27 fn visit_scope(&mut self, scope: &Scope, analyses: &GlobalAnalyses, changes: &AtomicCounter) {
28 visit_scope(self, scope, analyses, changes);
29 }
30}
31
32pub fn visit_scope<T: InstructionVisitor + ?Sized>(
33 visitor: &mut T,
34 scope: &Scope,
35 analyses: &GlobalAnalyses,
36 changes: &AtomicCounter,
37) {
38 let instructions = scope.take_instructions();
39 let mut new_instructions = Vec::with_capacity(instructions.len());
40 for inst in instructions {
41 if let Operation::Branch(branch) = &inst.operation {
42 match branch {
43 Branch::If(op) => {
44 visitor.visit_scope(&op.scope, analyses, changes);
45 }
46 Branch::IfElse(op) => {
47 visitor.visit_scope(&op.scope_if, analyses, changes);
48 visitor.visit_scope(&op.scope_else, analyses, changes);
49 }
50 Branch::Switch(op) => {
51 for (_, case) in &op.cases {
52 visitor.visit_scope(case, analyses, changes);
53 }
54 visitor.visit_scope(&op.scope_default, analyses, changes);
55 }
56 Branch::RangeLoop(op) => {
57 visitor.visit_scope(&op.scope, analyses, changes);
58 }
59 Branch::Loop(op) => {
60 visitor.visit_scope(&op.scope, analyses, changes);
61 }
62 _ => {}
63 }
64 }
65
66 new_instructions.extend(visitor.visit_instruction(
67 inst,
68 &scope.global_state,
69 analyses,
70 changes,
71 ));
72 }
73
74 scope.register_all(new_instructions);
75}
76
77#[derive(Deref, DerefMut)]
78pub struct Visitor<T>(pub T);
79
80impl<T> Visitor<T> {
81 pub fn visit_instruction(
82 &mut self,
83 inst: &mut Instruction,
84 analyses: &GlobalAnalyses,
85 visit_read: impl FnMut(&mut T, &mut Value),
86 mut visit_write: impl FnMut(&mut T, &mut Value),
87 ) {
88 self.visit_operation(&mut inst.operation, analyses, visit_read);
89
90 for ptr in inst.operation.write_pointers() {
91 if let Some(source) = analyses.ptr_source().get_mut(&ptr.kind) {
92 visit_write(self, source);
93 }
94 }
95
96 self.visit_out(&mut inst.out, visit_write);
97 }
98
99 pub fn visit_out(
100 &mut self,
101 out: &mut Option<Value>,
102 mut visit_write: impl FnMut(&mut T, &mut Value),
103 ) {
104 if let Some(out) = out {
105 visit_write(self, out)
106 }
107 }
108
109 pub fn visit_operation(
112 &mut self,
113 op: &mut Operation,
114 analyses: &GlobalAnalyses,
115 mut visit_read: impl FnMut(&mut T, &mut Value),
116 ) {
117 for ptr in op.read_pointers() {
118 if let Some(source) = analyses.ptr_source().get_mut(&ptr.kind) {
119 visit_read(self, source);
120 }
121 }
122
123 match op {
124 Operation::Marker(Marker::Free(_)) => {}
125 Operation::CoopMma(coop_mma) => self.visit_cmma(coop_mma, visit_read),
126 Operation::Branch(branch) => self.visit_branch(branch, visit_read),
127 Operation::Tma(tma_ops) => self.visit_tma(tma_ops, visit_read),
128 Operation::TensorIndexing(tensor_ops) => self.visit_tensor_ops(tensor_ops, visit_read),
129 Operation::NonSemantic(non_semantic) => {
130 self.visit_nonsemantic(non_semantic, visit_read)
131 }
132 Operation::DeclareVariable { .. } => {}
133 Operation::Operator(Operator::ReadBuiltin(_)) => {}
134 op => {
135 if let Some(args) = op.args_mut() {
136 for arg in args {
137 visit_read(self, arg);
138 }
139 } else {
140 panic!("Found op {op:?} which doesn't reflect. Needs special handling.");
141 }
142 }
143 }
144 }
145
146 pub fn visit_branch(
149 &mut self,
150 op: &mut Branch,
151 mut visit_read: impl FnMut(&mut T, &mut Value),
152 ) {
153 match op {
154 Branch::If(if_) => visit_read(self, &mut if_.cond),
155 Branch::IfElse(if_else) => visit_read(self, &mut if_else.cond),
156 Branch::Switch(switch) => visit_read(self, &mut switch.value),
157 Branch::RangeLoop(range_loop) => {
158 visit_read(self, &mut range_loop.start);
159 visit_read(self, &mut range_loop.end);
160 }
161 Branch::Loop(_) | Branch::Return | Branch::Break | Branch::Unreachable => {}
162 }
163 }
164
165 fn visit_cmma(&mut self, cmma: &mut CoopMma, mut visit_read: impl FnMut(&mut T, &mut Value)) {
166 match cmma {
167 CoopMma::Fill { value } => {
168 visit_read(self, value);
169 }
170 CoopMma::Load {
171 ptr,
172 stride,
173 layout: _,
174 } => {
175 visit_read(self, ptr);
176 visit_read(self, stride);
177 }
178 CoopMma::LoadTensor {
179 buffer,
180 layout,
181 view,
182 } => {
183 visit_read(self, buffer);
184 visit_read(self, layout);
185 if let Some(view) = view {
186 visit_read(self, view);
187 }
188 }
189 CoopMma::Execute {
190 mat_a,
191 mat_b,
192 mat_c,
193 } => {
194 visit_read(self, mat_a);
195 visit_read(self, mat_b);
196 visit_read(self, mat_c);
197 }
198 CoopMma::Store {
199 mat,
200 stride,
201 destination,
202 layout: _,
203 } => {
204 visit_read(self, mat);
205 visit_read(self, stride);
206 visit_read(self, destination);
207 }
208 CoopMma::StoreTensor { mat, layout, view } => {
209 visit_read(self, mat);
210 visit_read(self, layout);
211 if let Some(view) = view {
212 visit_read(self, view);
213 }
214 }
215 CoopMma::Cast { input } => {
216 visit_read(self, input);
217 }
218 CoopMma::RowIndex { lane_id, i, .. } => {
219 visit_read(self, lane_id);
220 visit_read(self, i);
221 }
222 CoopMma::ColIndex { lane_id, i, .. } => {
223 visit_read(self, lane_id);
224 visit_read(self, i);
225 }
226 CoopMma::LoadMatrix { ptr, .. } => {
227 visit_read(self, ptr);
228 }
229 CoopMma::StoreMatrix {
230 registers,
231 destination,
232 ..
233 } => {
234 visit_read(self, registers);
235 visit_read(self, destination);
236 }
237 CoopMma::ExecuteManual {
238 registers_a,
239 registers_b,
240 registers_c,
241 ..
242 } => {
243 visit_read(self, registers_a);
244 visit_read(self, registers_b);
245 visit_read(self, registers_c);
246 }
247 CoopMma::ExecuteScaled {
248 registers_a,
249 registers_b,
250 registers_c,
251 scales_a,
252 scales_b,
253 ..
254 } => {
255 visit_read(self, registers_a);
256 visit_read(self, registers_b);
257 visit_read(self, registers_c);
258 visit_read(self, scales_a);
259 visit_read(self, scales_b);
260 }
261 CoopMma::ExecuteElementwise { matrix, .. } => {
262 visit_read(self, matrix);
263 }
264 }
265 }
266
267 fn visit_tma(&mut self, tma_ops: &mut TmaOps, mut visit_read: impl FnMut(&mut T, &mut Value)) {
268 match tma_ops {
269 TmaOps::TmaStore {
270 source,
271 coordinates,
272 } => {
273 visit_read(self, source);
274 for coord in coordinates {
275 visit_read(self, coord)
276 }
277 }
278 TmaOps::CommitGroup | TmaOps::WaitGroup { .. } | TmaOps::WaitGroupRead { .. } => {}
279 }
280 }
281
282 fn visit_tensor_ops(
283 &mut self,
284 tensor_ops: &mut TensorIndexingOps,
285 mut visit_read: impl FnMut(&mut T, &mut Value),
286 ) {
287 match tensor_ops {
288 TensorIndexingOps::CreateLayout {
289 shape,
290 strides,
291 clamp_mode: _,
292 } => {
293 for s in shape {
294 visit_read(self, s);
295 }
296 for s in strides.iter_mut().flatten() {
297 visit_read(self, s);
298 }
299 }
300 TensorIndexingOps::CreateView => {}
301 TensorIndexingOps::Slice {
302 layout,
303 offsets,
304 shape,
305 } => {
306 visit_read(self, layout);
307 for o in offsets {
308 visit_read(self, o);
309 }
310 for s in shape {
311 visit_read(self, s);
312 }
313 }
314 }
315 }
316
317 fn visit_nonsemantic(
318 &mut self,
319 non_semantic: &mut NonSemantic,
320 mut visit_read: impl FnMut(&mut T, &mut Value),
321 ) {
322 match non_semantic {
323 NonSemantic::Comment { .. }
324 | NonSemantic::EnterDebugScope
325 | NonSemantic::ExitDebugScope => {}
326 NonSemantic::Print { args, .. } => {
327 for arg in args {
328 visit_read(self, arg);
329 }
330 }
331 }
332 }
333}