Skip to main content

luaur_code_gen/functions/
compile_internal.rs

1//! @interface-stub
2use alloc::string::String;
3use alloc::vec::Vec;
4
5use crate::enums::code_gen_compilation_result::CodeGenCompilationResult;
6use crate::enums::code_gen_flags::CodeGenFlags;
7#[cfg(target_arch = "aarch64")]
8use crate::functions::assemble_helpers_code_gen_a_64::assemble_helpers as assemble_helpers_a_64;
9#[cfg(not(target_arch = "aarch64"))]
10use crate::functions::assemble_helpers_code_gen_x_64::assemble_helpers as assemble_helpers_x_64;
11#[cfg(target_arch = "aarch64")]
12use crate::functions::create_native_function::create_native_function_a_64;
13#[cfg(not(target_arch = "aarch64"))]
14use crate::functions::create_native_function::create_native_function_x_64;
15use crate::functions::gather_functions::gather_functions;
16use crate::functions::get_code_gen_context::get_code_gen_context;
17#[cfg(target_arch = "aarch64")]
18use crate::functions::get_cpu_features_a_64::get_cpu_features_a_64;
19#[cfg(not(target_arch = "aarch64"))]
20use crate::functions::get_cpu_features_x_64::get_cpu_features_x_64;
21use crate::functions::get_native_proto_exec_data_header_native_proto_exec_data::get_native_proto_exec_data_header_mut;
22use crate::macros::codegen_assert::CODEGEN_ASSERT;
23#[cfg(target_arch = "aarch64")]
24use crate::records::assembly_builder_a_64::AssemblyBuilderA64;
25#[cfg(not(target_arch = "aarch64"))]
26use crate::records::assembly_builder_x_64::AssemblyBuilderX64;
27use crate::records::compilation_options::CompilationOptions;
28use crate::records::compilation_result::CompilationResult;
29use crate::records::compilation_stats::CompilationStats;
30use crate::records::module_helpers::ModuleHelpers;
31use crate::records::proto_compilation_failure::ProtoCompilationFailure;
32use crate::type_aliases::lua_state::lua_State;
33use crate::type_aliases::module_id::ModuleId;
34use crate::type_aliases::native_proto_exec_data_ptr::NativeProtoExecDataPtr;
35use luaur_common::enums::luau_proto_flag::LuauProtoFlag;
36use luaur_vm::functions::lua_a_toobject::luaA_toobject;
37use luaur_vm::functions::lua_is_lfunction::lua_is_lfunction;
38use luaur_vm::macros::clvalue::clvalue;
39use luaur_vm::macros::getstr::getstr;
40use luaur_vm::records::proto::Proto;
41use luaur_vm::type_aliases::instruction::Instruction;
42
43pub unsafe fn compile_internal(
44    module_id: &Option<ModuleId>,
45    l: *mut lua_State,
46    idx: i32,
47    options: &CompilationOptions,
48    stats: *mut CompilationStats,
49) -> CompilationResult {
50    CODEGEN_ASSERT!(lua_is_lfunction(l, idx) != 0);
51    let func = luaA_toobject(l, idx);
52
53    let root: *mut Proto = (*(*clvalue!(func)).inner.l).p;
54
55    if (options.flags & CodeGenFlags::CodeGen_OnlyNativeModules as u32) != 0
56        && ((*root).flags & LuauProtoFlag::LPF_NATIVE_MODULE as u8) == 0
57        && ((*root).flags & LuauProtoFlag::LPF_NATIVE_FUNCTION as u8) == 0
58    {
59        return CompilationResult {
60            result: CodeGenCompilationResult::NotNativeModule,
61            proto_failures: Vec::new(),
62        };
63    }
64
65    let code_gen_context = get_code_gen_context(l);
66    if code_gen_context.is_null() {
67        return CompilationResult {
68            result: CodeGenCompilationResult::CodeGenNotInitialized,
69            proto_failures: Vec::new(),
70        };
71    }
72
73    let mut protos: Vec<*mut Proto> = Vec::new();
74    gather_functions(
75        &mut protos,
76        root,
77        options.flags,
78        ((*root).flags & LuauProtoFlag::LPF_NATIVE_FUNCTION as u8) != 0,
79    );
80
81    protos.retain(|p| {
82        let proto = *p;
83        !proto.is_null() && unsafe { (*proto).execdata.is_null() }
84    });
85
86    if protos.is_empty() {
87        return CompilationResult {
88            result: CodeGenCompilationResult::NothingToCompile,
89            proto_failures: Vec::new(),
90        };
91    }
92
93    if !stats.is_null() {
94        (*stats).functions_total = protos.len() as u32;
95    }
96
97    if let Some(module_id) = module_id.as_ref() {
98        if let Some(try_bind_existing_module_fn) = (*code_gen_context).try_bind_existing_module_fn {
99            if let Some(existing_module_bind_result) =
100                try_bind_existing_module_fn(code_gen_context, module_id, &protos)
101            {
102                if !stats.is_null() {
103                    (*stats).functions_bound = existing_module_bind_result.functions_bound;
104                }
105
106                return CompilationResult {
107                    result: existing_module_bind_result.compilation_result,
108                    proto_failures: Vec::new(),
109                };
110            }
111        }
112    }
113
114    #[cfg(target_arch = "aarch64")]
115    let mut build = {
116        let cpu_features = get_cpu_features_a_64();
117        let mut build = AssemblyBuilderA64 {
118            data: Vec::new(),
119            code: Vec::new(),
120            text: String::new(),
121            log_text: false,
122            features: 0,
123            next_label: 1,
124            pending_labels: Vec::new(),
125            label_locations: Vec::new(),
126            finalized: false,
127            overflowed: false,
128            data_pos: 0,
129            code_pos: core::ptr::null_mut(),
130            code_end: core::ptr::null_mut(),
131        };
132        build.assembly_builder_a_64_assembly_builder_a_64(false, cpu_features);
133        build
134    };
135
136    #[cfg(not(target_arch = "aarch64"))]
137    let mut build = {
138        let cpu_features = get_cpu_features_x_64();
139        AssemblyBuilderX64::assembly_builder_x_64_bool_i32(false, cpu_features)
140    };
141
142    let mut helpers = ModuleHelpers::default();
143
144    #[cfg(target_arch = "aarch64")]
145    assemble_helpers_a_64(&mut build, &mut helpers);
146
147    #[cfg(not(target_arch = "aarch64"))]
148    assemble_helpers_x_64(&mut build, &mut helpers);
149
150    let mut compilation_result = CompilationResult::default();
151    let mut native_protos: Vec<NativeProtoExecDataPtr> = Vec::with_capacity(protos.len());
152    let mut total_ir_inst_count = 0u32;
153
154    for proto in &protos {
155        let mut proto_result = CodeGenCompilationResult::Success;
156
157        let native_exec_data = {
158            #[cfg(target_arch = "aarch64")]
159            {
160                create_native_function_a_64(
161                    &mut build,
162                    &mut helpers,
163                    *proto,
164                    &mut total_ir_inst_count,
165                    options,
166                    &mut proto_result,
167                )
168            }
169
170            #[cfg(not(target_arch = "aarch64"))]
171            {
172                create_native_function_x_64(
173                    &mut build,
174                    &mut helpers,
175                    *proto,
176                    &mut total_ir_inst_count,
177                    options,
178                    &mut proto_result,
179                )
180            }
181        };
182
183        if let Some(native_exec_data) = native_exec_data {
184            native_protos.push(native_exec_data);
185        } else {
186            compilation_result
187                .proto_failures
188                .push(ProtoCompilationFailure {
189                    result: proto_result,
190                    debugname: if !(*(*proto)).debugname.is_null() {
191                        let name = getstr((*(*proto)).debugname as *const _);
192                        core::ffi::CStr::from_ptr(name)
193                            .to_string_lossy()
194                            .into_owned()
195                    } else {
196                        String::new()
197                    },
198                    line: (*(*proto)).linedefined,
199                });
200        }
201    }
202
203    if !build.finalize() {
204        compilation_result.result = CodeGenCompilationResult::CodeGenAssemblerFinalizationFailure;
205        return compilation_result;
206    }
207
208    if native_protos.is_empty() {
209        return compilation_result;
210    }
211
212    let native_code_size_bytes = core::mem::size_of_val(build.code.as_slice());
213
214    if !stats.is_null() {
215        for native_exec_data in &native_protos {
216            let header = &*get_native_proto_exec_data_header_mut(native_exec_data.as_ptr());
217
218            (*stats).bytecode_size_bytes +=
219                header.bytecode_instruction_count as usize * core::mem::size_of::<Instruction>();
220            (*stats).native_metadata_size_bytes +=
221                header.bytecode_instruction_count as usize * core::mem::size_of::<u32>();
222        }
223
224        (*stats).functions_compiled += native_protos.len() as u32;
225        (*stats).native_code_size_bytes += native_code_size_bytes;
226        (*stats).native_data_size_bytes += build.data.len();
227    }
228
229    for i in 0..native_protos.len() {
230        let header = get_native_proto_exec_data_header_mut(native_protos[i].as_ptr());
231
232        let begin = (*header).entry_offset_or_address as usize as u32;
233        let end = if i + 1 < native_protos.len() {
234            let next_header = get_native_proto_exec_data_header_mut(native_protos[i + 1].as_ptr());
235            (*next_header).entry_offset_or_address as usize as u32
236        } else {
237            native_code_size_bytes as u32
238        };
239
240        CODEGEN_ASSERT!(begin < end);
241
242        (*header).native_code_size = (end - begin) as usize;
243    }
244
245    let bind_result = ((*code_gen_context).bind_module_fn.unwrap())(
246        code_gen_context,
247        module_id,
248        &protos,
249        native_protos,
250        build.data.as_ptr(),
251        build.data.len(),
252        build.code.as_ptr() as *const u8,
253        native_code_size_bytes,
254    );
255
256    if !stats.is_null() {
257        (*stats).functions_bound = bind_result.functions_bound;
258    }
259
260    if bind_result.compilation_result != CodeGenCompilationResult::Success {
261        compilation_result.result = bind_result.compilation_result;
262    }
263
264    compilation_result
265}