Skip to main content

cubecl_core/post_processing/
disaggregate.rs

1use alloc::{format, vec::Vec};
2
3use cubecl_ir::{GlobalState, Instruction, NonSemantic, Operation, Scope, Value, ValueKind};
4use hashbrown::HashMap;
5
6use crate::post_processing::{
7    analysis_helper::GlobalAnalyses,
8    util::AtomicCounter,
9    visitor::{InstructionVisitor, Visitor},
10};
11
12type Substitutes = HashMap<ValueKind, Vec<Value>>;
13type Extracted = HashMap<ValueKind, Value>;
14
15/// Disaggregates compiler-internal aggregates like bounds checked pointers and slice pointers into
16/// individual variables.
17#[derive(Debug, Default)]
18pub struct DisaggregateVisitor {
19    substitutes: Substitutes,
20    extracted: Extracted,
21}
22
23impl DisaggregateVisitor {
24    pub fn apply(scope: &Scope) {
25        let mut this = Self::default();
26        let changes = AtomicCounter::new(0);
27        // We don't care about pointer sources or used variables at this point
28        let analyses = GlobalAnalyses::default();
29        this.visit_scope(scope, &analyses, &changes);
30    }
31}
32
33impl InstructionVisitor for DisaggregateVisitor {
34    fn visit_instruction(
35        &mut self,
36        mut instruction: Instruction,
37        _global_state: &GlobalState,
38        analyses: &GlobalAnalyses,
39        _changes: &AtomicCounter,
40    ) -> Vec<Instruction> {
41        let mut visitor = Visitor(());
42
43        // This needs to run even for aggregates so extract -> construct will be properly replaced
44        visitor.visit_operation(&mut instruction.operation, analyses, |_, val| {
45            if let Some(replacement) = self.extracted.get(&val.kind).copied() {
46                *val = replacement;
47            }
48        });
49        visitor.visit_out(&mut instruction.out, |_, val| {
50            if let Some(replacement) = self.extracted.get(&val.kind) {
51                *val = *replacement;
52            }
53        });
54
55        let mut new_instructions = Vec::new();
56
57        match &mut instruction.operation {
58            Operation::ConstructAggregate(fields) => {
59                let fields = fields.clone();
60                self.substitutes.insert(instruction.out().kind, fields);
61            }
62            Operation::ExtractAggregateField(operands) => {
63                let substitutes = self.substitutes.get(&operands.aggregate.kind);
64                let substitutes =
65                    substitutes.expect("Should have aggregate registered before any extraction");
66                let substitute = substitutes[operands.field];
67                self.extracted.insert(instruction.out().kind, substitute);
68            }
69            // Fix validate prints
70            Operation::NonSemantic(NonSemantic::Print { format_string, .. }) => {
71                for (from, to) in self.extracted.iter() {
72                    *format_string = format_string.replace(&format!("{from}"), &format!("{to}"));
73                }
74                new_instructions.push(instruction);
75            }
76            _ => new_instructions.push(instruction),
77        }
78
79        new_instructions
80    }
81}