Skip to main content

luaur_bytecode/methods/
call_inliner_inline_target.rs

1use crate::enums::bc_block_edge_kind::BcBlockEdgeKind;
2use crate::enums::bc_block_edge_kind::BcBlockEdgeKind::Fallthrough;
3use crate::enums::bc_block_edge_kind::BcBlockEdgeKind::Fallthrough as FallthroughKind;
4use crate::enums::bc_block_edge_kind::BcBlockEdgeKind::{Branch, Loop};
5use crate::enums::bc_block_flag::BcBlockFlag;
6use crate::enums::bc_op_kind::BcOpKind;
7use crate::enums::bc_op_kind::BcOpKind::Block;
8use crate::records::bc_block::BcBlock;
9use crate::records::bc_block_edge::BcBlockEdge;
10use crate::records::bc_function::BcFunction;
11use crate::records::bc_inst::BcInst;
12use crate::records::bc_op::BcOp;
13use crate::records::bc_ref::BcRef;
14use crate::records::call_inliner::CallInliner;
15use luaur_common::enums::luau_opcode::LuauOpcode;
16use luaur_common::macros::luau_assert::LUAU_ASSERT;
17
18const k_max_inliner_combined_stack_size: u8 = 250;
19
20impl<'a> CallInliner<'a> {
21    pub fn inline_target(&mut self, target_proto_id: u32) -> bool {
22        let mut new_max_stack_size: u32 =
23            self.caller.maxstacksize as u32 + self.target.maxstacksize as u32;
24
25        if self.target.is_vararg {
26            new_max_stack_size = new_max_stack_size.wrapping_add(self.call_params.len() as u32);
27        }
28
29        if new_max_stack_size >= k_max_inliner_combined_stack_size as u32 {
30            return false;
31        }
32
33        if self.call.param_count() < 0 || self.call.return_count() < 0 {
34            return false;
35        }
36
37        // inlining of upvalues is not supported yet
38        if self.target.nups > 0 {
39            return false;
40        }
41
42        self.caller.maxstacksize = new_max_stack_size as u8;
43
44        let (mut prev_block, mut next_block) = self.split_block_on_op(self.call.op());
45
46        let mut target_op: BcOp = self.call.target();
47
48        if prev_block.operator_deref().ops.len() > 0 {
49            let last_inst = self
50                .caller
51                .inst(prev_block.operator_deref().ops.back().copied().unwrap());
52            if last_inst.operator_deref().op == LuauOpcode::LOP_NAMECALL {
53                target_op = self.replace_namecall(last_inst.op, &mut prev_block);
54            }
55        }
56
57        self.append_cmp_proto(&mut prev_block, target_op, target_proto_id);
58
59        self.allocate_graph_entities_for_target();
60
61        self.fill_under_call_arguments();
62
63        self.find_target_call_projections();
64
65        if !self.migrate_blocks(&mut next_block) {
66            return false;
67        }
68
69        let caller_inlined_entry_op = self.map_block_op(self.target.entry_block);
70
71        // Remove prevBlock fallthrough to call block from its predecessors
72        let call_block = self.call.base.operator_deref().block;
73        let mut call_block_ref = self.caller.block(call_block);
74        let insn_preds: &mut crate::type_aliases::bc_edges::BcEdges =
75            &mut call_block_ref.operator_deref_mut().predecessors;
76
77        let prev_block_op = prev_block.op;
78
79        let retained: Vec<_> = insn_preds
80            .iter()
81            .cloned()
82            .filter(|p| !(p.kind == BcBlockEdgeKind::Fallthrough && p.target == prev_block_op))
83            .collect();
84
85        insn_preds.clear();
86        for edge in retained {
87            insn_preds.push_back(edge);
88        }
89
90        {
91            let edges = &mut prev_block.operator_deref_mut().successors;
92            let mut found = false;
93            for edge in edges.iter_mut() {
94                if edge.kind == BcBlockEdgeKind::Fallthrough {
95                    edge.target = caller_inlined_entry_op;
96                    found = true;
97                    break;
98                }
99            }
100            if !found {
101                edges.push_back(BcBlockEdge {
102                    kind: BcBlockEdgeKind::Fallthrough,
103                    target: caller_inlined_entry_op,
104                });
105            }
106        }
107        {
108            let edges =
109                &mut self.caller.blocks[caller_inlined_entry_op.index as usize].predecessors;
110            let mut found = false;
111            for edge in edges.iter_mut() {
112                if edge.kind == BcBlockEdgeKind::Fallthrough {
113                    edge.target = prev_block.op;
114                    found = true;
115                    break;
116                }
117            }
118            if !found {
119                edges.push_back(BcBlockEdge {
120                    kind: BcBlockEdgeKind::Fallthrough,
121                    target: prev_block.op,
122                });
123            }
124        }
125
126        self.migrate_instructions();
127
128        self.replace_call_usages_with_return_phis();
129
130        self.drop_prep_var_args_in_inlined_path();
131
132        LUAU_ASSERT!(self.validate_cfg());
133
134        true
135    }
136}