Skip to main content

luaur_bytecode/methods/
bytecode_graph_parser_find_producer_bytecode_graph_parser.rs

1use crate::enums::bc_block_edge_kind::BcBlockEdgeKind;
2use crate::enums::bc_op_kind::BcOpKind;
3use crate::records::bc_op::BcOp;
4use crate::records::bc_op_hash::BcOpHash;
5use crate::records::bytecode_graph_parser::BytecodeGraphParser;
6use crate::type_aliases::reg::Reg;
7use alloc::collections::BTreeSet;
8use alloc::vec::Vec;
9use luaur_common::macros::luau_assert::LUAU_ASSERT;
10use std::collections::HashSet;
11
12impl<'a> BytecodeGraphParser<'a> {
13    pub fn find_producer_bc_op_reg_unordered_set_bc_op_bc_op_hash(
14        &mut self,
15        block: BcOp,
16        reg: Reg,
17        visited: &mut HashSet<BcOp, BcOpHash>,
18    ) -> Option<BcOp> {
19        visited.insert(block);
20        LUAU_ASSERT!(block.index < self.producers.len() as u32);
21        let block_producers = &self.producers[block.index as usize];
22
23        if (reg as i32) > block_producers.invalidAfter {
24            return None;
25        }
26
27        if let Some(local) = block_producers.own.get(&reg) {
28            return Some(*local);
29        }
30
31        if let Some(cached) = block_producers.cached.get(&reg) {
32            return Some(*cached);
33        }
34
35        if block_producers.multiReturn.kind != BcOpKind::None
36            && reg >= block_producers.multiReturnStart
37        {
38            return Some(self.func.add_proj(
39                block_producers.multiReturn,
40                (reg - block_producers.multiReturnStart) as u32,
41            ));
42        }
43
44        // BcOp does not implement Ord, so we use a Vec and deduplicate manually to mimic std::unordered_set behavior
45        // while avoiding the Ord requirement of BTreeSet.
46        let mut results: Vec<BcOp> = Vec::new();
47        let bl = self.func.block_op(block);
48        let predecessors = bl.predecessors.clone();
49
50        for edge in &predecessors {
51            let ctrl = edge.kind;
52            let pred = edge.target;
53
54            if ctrl == BcBlockEdgeKind::Loop || visited.contains(&pred) {
55                continue;
56            }
57            LUAU_ASSERT!(block != pred);
58
59            if let Some(op) =
60                self.find_producer_bc_op_reg_unordered_set_bc_op_bc_op_hash(pred, reg, visited)
61            {
62                if op.kind == BcOpKind::Phi {
63                    let phi = self.func.phi_op(op);
64                    for &proj in &phi.ops {
65                        if !results.contains(&proj) {
66                            results.push(proj);
67                        }
68                    }
69                } else {
70                    if !results.contains(&op) {
71                        results.push(op);
72                    }
73                }
74            }
75        }
76
77        if results.is_empty() {
78            return None;
79        }
80
81        let res = if results.len() == 1 {
82            results[0]
83        } else {
84            let res = self.func.add_phi();
85            let phi = self.func.phi_op(res);
86            for op in results {
87                phi.ops.push_back(op);
88            }
89            res
90        };
91
92        let block_producers = &mut self.producers[block.index as usize];
93        block_producers.cached.insert(reg, res);
94        Some(res)
95    }
96}