luaur_compiler/methods/
compiler_try_compile_inlined_call.rs1use 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}