Skip to main content

luaur_compiler/methods/
compiler_try_compile_inlined_call.rs

1use crate::functions::compute_cost::compute_cost;
2use crate::records::compiler::Compiler;
3use luaur_ast::records::ast_expr_call::AstExprCall;
4use luaur_ast::records::ast_expr_function::AstExprFunction;
5use luaur_common::macros::luau_assert::LUAU_ASSERT;
6
7impl Compiler {
8    pub fn try_compile_inlined_call(
9        &mut self,
10        expr: *mut AstExprCall,
11        func: *mut AstExprFunction,
12        target: u8,
13        target_count: u8,
14        mult_ret: bool,
15        threshold_base: i32,
16        threshold_max_boost: i32,
17        depth_limit: i32,
18    ) -> bool {
19        unsafe {
20            let fi = self.functions.find(&func);
21            LUAU_ASSERT!(fi.is_some());
22            let fi = fi.unwrap();
23            let fi_stack_size = fi.stack_size;
24            let mut call_cost_model = fi.cost_model;
25            let fi_cost_model = fi.cost_model;
26
27            if self.reg_top > 128 || fi_stack_size > 32 {
28                (*self.bytecode)
29                    .add_debug_remark(format_args!("inlining failed: high register pressure"));
30                return false;
31            }
32
33            if self.inline_frames.len() as i32 >= depth_limit {
34                (*self.bytecode)
35                    .add_debug_remark(format_args!("inlining failed: too many inlined frames"));
36                return false;
37            }
38
39            for frame in &self.inline_frames {
40                if frame.func == func {
41                    (*self.bytecode).add_debug_remark(format_args!(
42                        "inlining failed: can't inline recursive calls"
43                    ));
44                    return false;
45                }
46            }
47
48            if mult_ret {
49                (*self.bytecode).add_debug_remark(format_args!(
50                    "inlining failed: can't convert fixed returns to multret"
51                ));
52                return false;
53            }
54
55            let mut varc = [false; 8];
56            let mut has_constant = false;
57
58            let mut i = 0usize;
59            while i < (*func).args.size && i < (*expr).args.size && i < 8 {
60                if self.is_constant(*(*expr).args.data.add(i)) {
61                    varc[i] = true;
62                    has_constant = true;
63                }
64                i += 1;
65            }
66
67            if (*expr).args.size != 0
68                && !self.is_expr_mult_ret(*(*expr).args.data.add((*expr).args.size - 1))
69            {
70                i = (*expr).args.size;
71                while i < (*func).args.size && i < 8 {
72                    varc[i] = true;
73                    has_constant = true;
74                    i += 1;
75                }
76            }
77
78            if has_constant {
79                call_cost_model = self.cost_model_inlined_call(expr, func);
80            }
81
82            let inlined_cost = compute_cost(
83                call_cost_model,
84                varc.as_ptr(),
85                core::cmp::min((*func).args.size, 8),
86            );
87            let baseline_cost = compute_cost(fi_cost_model, core::ptr::null(), 0) + 3;
88            let inline_profit = if inlined_cost == 0 {
89                threshold_max_boost
90            } else {
91                core::cmp::min(threshold_max_boost, 100 * baseline_cost / inlined_cost)
92            };
93
94            let threshold = threshold_base * inline_profit / 100;
95
96            if inlined_cost > threshold {
97                (*self.bytecode).add_debug_remark(format_args!(
98                    "inlining failed: too expensive (cost {}, profit {:.2}x)",
99                    inlined_cost,
100                    inline_profit as f64 / 100.0
101                ));
102                return false;
103            }
104
105            (*self.bytecode).add_debug_remark(format_args!(
106                "inlining succeeded (cost {}, profit {:.2}x, depth {})",
107                inlined_cost,
108                inline_profit as f64 / 100.0,
109                self.inline_frames.len() as i32
110            ));
111
112            self.compile_inlined_call(expr, func, target, target_count);
113            true
114        }
115    }
116}