luaur_code_gen/functions/
compile_internal.rs1use 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}