Skip to main content

luaur_bytecode/methods/
call_inliner_split_block_on_op.rs

1use crate::enums::bc_block_edge_kind::BcBlockEdgeKind;
2use crate::enums::bc_op_kind::BcOpKind;
3use crate::records::bc_block::BcBlock;
4use crate::records::bc_block_edge::BcBlockEdge;
5use crate::records::bc_op::BcOp;
6use crate::records::bc_ref::BcRef;
7use crate::records::call_inliner::CallInliner;
8use alloc::vec::Vec;
9use luaur_common::macros::luau_assert::LUAU_ASSERT;
10
11impl<'a> CallInliner<'a> {
12    pub fn split_block_on_op(
13        &mut self,
14        split_op: BcOp,
15    ) -> (BcRef<'a, BcBlock>, BcRef<'a, BcBlock>) {
16        let prev_block_op = self.caller.instructions[split_op.index as usize].block;
17        LUAU_ASSERT!(prev_block_op.kind == BcOpKind::Block);
18        LUAU_ASSERT!(self.caller.blocks[prev_block_op.index as usize]
19            .ops
20            .iter()
21            .any(|op| *op == split_op));
22
23        let insn_block_op = self.caller.add_block();
24        let prev_block_sortkey = self.caller.blocks[prev_block_op.index as usize].sortkey;
25        let prev_block_chainkey = self.caller.blocks[prev_block_op.index as usize].chainkey;
26        self.caller.blocks[insn_block_op.index as usize].sortkey = prev_block_sortkey;
27        self.caller.blocks[insn_block_op.index as usize].chainkey = prev_block_chainkey + 1;
28
29        let next_block_op = self.caller.add_block();
30        self.caller.blocks[next_block_op.index as usize].sortkey =
31            self.caller.blocks[insn_block_op.index as usize].sortkey;
32        self.caller.blocks[next_block_op.index as usize].chainkey =
33            self.caller.blocks[insn_block_op.index as usize].chainkey + 1;
34
35        while *self.caller.blocks[prev_block_op.index as usize]
36            .ops
37            .back()
38            .unwrap()
39            != split_op
40        {
41            let back_op = *self.caller.blocks[prev_block_op.index as usize]
42                .ops
43                .back()
44                .unwrap();
45            self.caller.blocks[prev_block_op.index as usize]
46                .ops
47                .pop_back();
48            self.caller.blocks[next_block_op.index as usize]
49                .ops
50                .push_front(back_op);
51            self.caller.instructions[back_op.index as usize].block = next_block_op;
52        }
53
54        let prev_successors = self.caller.blocks[prev_block_op.index as usize]
55            .successors
56            .clone();
57        for e in &prev_successors {
58            let succ = e.target;
59            for pred in self.caller.blocks[succ.index as usize]
60                .predecessors
61                .iter_mut()
62            {
63                if pred.target == prev_block_op {
64                    pred.target = next_block_op;
65                }
66            }
67        }
68        self.caller.blocks[next_block_op.index as usize].successors = prev_successors;
69        self.caller.blocks[prev_block_op.index as usize]
70            .successors
71            .clear();
72
73        self.caller.blocks[prev_block_op.index as usize]
74            .successors
75            .push_back(BcBlockEdge {
76                kind: BcBlockEdgeKind::Fallthrough,
77                target: insn_block_op,
78            });
79        self.caller.blocks[insn_block_op.index as usize]
80            .predecessors
81            .push_back(BcBlockEdge {
82                kind: BcBlockEdgeKind::Fallthrough,
83                target: prev_block_op,
84            });
85        self.caller.blocks[insn_block_op.index as usize]
86            .successors
87            .push_back(BcBlockEdge {
88                kind: BcBlockEdgeKind::Fallthrough,
89                target: next_block_op,
90            });
91        self.caller.blocks[next_block_op.index as usize]
92            .predecessors
93            .push_back(BcBlockEdge {
94                kind: BcBlockEdgeKind::Fallthrough,
95                target: insn_block_op,
96            });
97
98        LUAU_ASSERT!(
99            *self.caller.blocks[prev_block_op.index as usize]
100                .ops
101                .back()
102                .unwrap()
103                == split_op
104        );
105        self.caller.blocks[prev_block_op.index as usize]
106            .ops
107            .pop_back();
108        self.caller.blocks[insn_block_op.index as usize]
109            .ops
110            .push_back(split_op);
111        self.caller.instructions[split_op.index as usize].block = insn_block_op;
112
113        let vec_ptr: *const Vec<BcBlock> = &self.caller.blocks;
114        let vec_ref: &'a Vec<BcBlock> = unsafe { &*vec_ptr };
115        (
116            BcRef {
117                vec: vec_ref,
118                op: prev_block_op,
119            },
120            BcRef {
121                vec: vec_ref,
122                op: next_block_op,
123            },
124        )
125    }
126}