Skip to main content

luaur_compiler/methods/
compiler_compile_function.rs

1use crate::enums::type_compiler::Type as LoopJumpType;
2use crate::functions::model_cost_cost_model::model_cost_ast_node_ast_local_usize_dense_hash_map_ast_expr_call_i32_dense_hash_map_ast_expr_constant;
3use crate::records::compiler::Compiler;
4use crate::records::return_visitor::ReturnVisitor;
5use luaur_ast::records::ast_expr_function::AstExprFunction;
6use luaur_ast::records::ast_node::AstNode;
7use luaur_ast::records::ast_stat::AstStat;
8use luaur_common::enums::luau_bytecode_type::LBC_TYPE_ANY;
9use luaur_common::enums::luau_opcode::LuauOpcode;
10use luaur_common::macros::luau_assert::LUAU_ASSERT;
11use luaur_common::macros::luau_timetrace_argument::LUAU_TIMETRACE_ARGUMENT;
12use luaur_common::macros::luau_timetrace_scope::LUAU_TIMETRACE_SCOPE;
13
14impl Compiler {
15    pub fn compile_function(&mut self, func: *mut AstExprFunction, protoflags: &mut u8) -> u32 {
16        LUAU_TIMETRACE_SCOPE!("Compiler::compileFunction", "Compiler");
17        unsafe {
18            let func_ref = &*func;
19            if !func_ref.debugname.value.is_null() {
20                LUAU_TIMETRACE_ARGUMENT!("name", func_ref.debugname.value);
21            }
22
23            LUAU_ASSERT!(!self.functions.contains(&func));
24            LUAU_ASSERT!(
25                self.reg_top == 0
26                    && self.stack_size == 0
27                    && self.local_stack.is_empty()
28                    && self.upvals.is_empty()
29            );
30            if luaur_common::FFlag::LuauExportValueSyntax.get() {
31                self.current_function = func;
32            }
33
34            let mut rs = self.reg_scope_compiler();
35            let self_ = if !func_ref.self_.is_null() { 1 } else { 0 };
36            let fid = (*self.bytecode)
37                .begin_function((self_ + func_ref.args.size) as u8, func_ref.vararg);
38
39            self.set_debug_line_ast_node(func as *mut AstNode);
40
41            if func_ref.vararg {
42                (*self.bytecode).emit_abc(
43                    LuauOpcode::LOP_PREPVARARGS,
44                    (self_ + func_ref.args.size) as u8,
45                    0,
46                    0,
47                );
48            }
49
50            let args = self.alloc_reg(func as *mut AstNode, (self_ + func_ref.args.size) as u32);
51            if !func_ref.self_.is_null() {
52                self.push_local(func_ref.self_, args, 0);
53            }
54            for i in 0..func_ref.args.size {
55                self.push_local(*func_ref.args.data.add(i), args + self_ as u8 + i as u8, 0);
56            }
57
58            self.arg_count = self.local_stack.len();
59            let stat = func_ref.body;
60            let mut terminates_early = false;
61            self.current_function = func;
62
63            for i in 0..(*stat).body.size {
64                let body_stat = *(*stat).body.data.add(i);
65                self.compile_stat(body_stat);
66                if self.always_terminates(body_stat) {
67                    terminates_early = true;
68                    break;
69                }
70            }
71
72            if luaur_common::FFlag::LuauExportValueSyntax.get() {
73                self.set_debug_line_end(stat as *mut AstNode);
74                if (!self.exported_locals.is_empty() || !self.exported_classes.is_empty())
75                    && self.at_top_level()
76                {
77                    self.compile_export_table();
78                } else if !terminates_early {
79                    self.close_locals(0);
80                    (*self.bytecode).emit_abc(LuauOpcode::LOP_RETURN, 0, 1, 0);
81                }
82            } else if !terminates_early {
83                self.set_debug_line_end(stat as *mut AstNode);
84                self.close_locals(0);
85                (*self.bytecode).emit_abc(LuauOpcode::LOP_RETURN, 0, 1, 0);
86            }
87
88            if self.options.optimization_level >= 1 && self.options.debug_level >= 2 {
89                self.gather_const_upvals(func);
90            }
91
92            (*self.bytecode)
93                .set_debug_function_line_defined(func_ref.base.base.location.begin.line as i32 + 1);
94
95            if self.options.debug_level >= 1 && !func_ref.debugname.value.is_null() {
96                (*self.bytecode).set_debug_function_name(
97                    crate::functions::sref_compiler::sref_ast_name(func_ref.debugname),
98                );
99            }
100
101            if self.options.debug_level >= 2 {
102                for &l in &self.upvals {
103                    (*self.bytecode).push_debug_upval(
104                        crate::functions::sref_compiler::sref_ast_name((*l).name),
105                    );
106                }
107            }
108
109            if self.options.type_info_level >= 1 {
110                for &l in &self.upvals {
111                    let ty = self.local_types.find(&l).copied().unwrap_or(LBC_TYPE_ANY);
112                    (*self.bytecode).push_upval_type_info(ty);
113                }
114            }
115
116            if self.options.optimization_level >= 1 {
117                (*self.bytecode).fold_jumps();
118            }
119            (*self.bytecode).expand_jumps();
120            self.pop_locals(0);
121
122            if (*self.bytecode).get_instruction_count() > 1000000 {
123                crate::records::compile_error::CompileError::raise(
124                    &func_ref.base.base.location,
125                    format_args!("Exceeded function instruction limit; split the function into parts to compile"),
126                );
127            }
128
129            if let Some(func_type) = self.function_types.find(&func) {
130                (*self.bytecode).set_function_type_info(func_type.clone());
131            }
132
133            if func_ref.function_depth == 0 && !self.has_loops {
134                *protoflags |= 2; // LPF_NATIVE_COLD = 1 << 1 (was wrongly 1)
135            }
136            if func_ref.has_native_attribute() {
137                *protoflags |= 4; // LPF_NATIVE_FUNCTION = 1 << 2 (was wrongly 2)
138            }
139
140            // C: `isInlinable = !vararg && !getfenvUsed && !setfenvUsed;
141            //     if (FFlag::LuauEmitCallFeedback && isInlinable && upvals.empty()) flags |= LPF_INLINABLE;`
142            // This whole block was missing, so the Rust compiler never marked functions inlinable.
143            let is_inlinable = !func_ref.vararg && !self.getfenv_used && !self.setfenv_used;
144            if luaur_common::FFlag::LuauEmitCallFeedback.get()
145                && is_inlinable
146                && self.upvals.len() == 0
147            {
148                *protoflags |= 8; // LPF_INLINABLE = 1 << 3
149            }
150
151            (*self.bytecode).end_function(
152                self.stack_size as u8,
153                self.upvals.len() as u8,
154                *protoflags,
155            );
156
157            {
158                let f = self.functions.get_or_insert(func);
159                f.id = fid;
160                f.upvals = self.upvals.clone();
161            }
162
163            if self.options.optimization_level >= 2
164                && !func_ref.vararg
165                && func_ref.self_.is_null()
166                && !self.getfenv_used
167                && !self.setfenv_used
168            {
169                let cost_model = model_cost_ast_node_ast_local_usize_dense_hash_map_ast_expr_call_i32_dense_hash_map_ast_expr_constant(
170                    func_ref.body as *mut AstNode,
171                    func_ref.args.data as *const _,
172                    func_ref.args.size,
173                    &*self.builtins_fold,
174                    &self.constants,
175                );
176                let returns_one = if self.always_terminates(func_ref.body as *mut AstStat) {
177                    let mut rv = self.return_visitor_return_visitor();
178                    luaur_ast::visit::ast_stat_block_visit(&*func_ref.body, &mut rv);
179                    Some(rv.returns_one)
180                } else {
181                    None
182                };
183
184                let f = self.functions.get_or_insert(func);
185                // C++: `f.canInline = !(DebugLuauNoInline && func->hasAttribute(DebugNoinline))`.
186                f.can_inline = !(luaur_common::FFlag::DebugLuauNoInline.get()
187                    && !func_ref
188                        .get_attribute(luaur_ast::records::ast_attr::AstAttrType::DebugNoinline)
189                        .is_null());
190                f.stack_size = self.stack_size as u32;
191                f.cost_model = cost_model;
192                if let Some(returns_one) = returns_one {
193                    f.returns_one = returns_one;
194                }
195            }
196
197            self.upvals.clear();
198            self.stack_size = 0;
199            self.arg_count = 0;
200            self.has_loops = false;
201            self.current_function = core::ptr::null_mut();
202            fid
203        }
204    }
205}