luaur_code_gen/functions/
get_assembly_impl.rs1use alloc::string::String;
3use alloc::vec::Vec;
4
5use crate::enums::code_gen_compilation_result::CodeGenCompilationResult;
6use crate::enums::code_gen_flags::CodeGenFlags;
7use crate::functions::assemble_helpers_code_gen_a_64::assemble_helpers as assemble_helpers_a_64;
8use crate::functions::assemble_helpers_code_gen_x_64::assemble_helpers as assemble_helpers_x_64;
9use crate::functions::gather_functions::gather_functions;
10use crate::functions::get_instruction_count_code_gen_assembly::get_instruction_count_instruction_size;
11use crate::functions::log_function_header::log_function_header;
12use crate::functions::log_function_types::log_function_types;
13use crate::functions::lower_function::{lower_function_a_64, lower_function_x_64};
14use crate::records::assembly_builder_a_64::AssemblyBuilderA64;
15use crate::records::assembly_builder_x_64::AssemblyBuilderX64;
16use crate::records::assembly_options::AssemblyOptions;
17use crate::records::function_bytecode_summary::FunctionBytecodeSummary;
18use crate::records::function_stats::FunctionStats;
19use crate::records::ir_builder::IrBuilder;
20use crate::records::lowering_stats::{FunctionStats_Enable, LoweringStats};
21use crate::records::module_helpers::ModuleHelpers;
22use crate::traits::LogAppend;
23use luaur_common::enums::luau_proto_flag::LuauProtoFlag;
24use luaur_vm::macros::clvalue::clvalue;
25use luaur_vm::macros::getstr::getstr;
26use luaur_vm::records::proto::Proto;
27use luaur_vm::type_aliases::t_value::TValue;
28
29impl LogAppend for AssemblyBuilderX64 {
30 fn log_append(&mut self, args: core::fmt::Arguments<'_>) {
31 self.log_append(args);
32 }
33}
34
35impl LogAppend for AssemblyBuilderA64 {
36 fn log_append(&mut self, args: core::fmt::Arguments<'_>) {
37 self.log_append(args);
38 }
39}
40
41pub unsafe fn get_assembly_impl_x_64(
42 build: &mut AssemblyBuilderX64,
43 func: *const TValue,
44 options: AssemblyOptions,
45 stats: *mut LoweringStats,
46) -> String {
47 let cl = clvalue!(func);
48 let root: *mut Proto = (*(*cl).inner.l).p;
49
50 if (options.compilation_options.flags & CodeGenFlags::CodeGen_OnlyNativeModules as u32) != 0
51 && ((*root).flags & LuauProtoFlag::LPF_NATIVE_MODULE as u8) == 0
52 {
53 build.finalize();
54 return String::new();
55 }
56
57 let mut protos: Vec<*mut Proto> = Vec::new();
58 gather_functions(
59 &mut protos,
60 root,
61 options.compilation_options.flags,
62 ((*root).flags & LuauProtoFlag::LPF_NATIVE_FUNCTION as u8) != 0,
63 );
64
65 protos.retain(|p| !p.is_null());
66
67 if !stats.is_null() {
68 (*stats).total_functions += protos.len() as u32;
69 }
70
71 if protos.is_empty() {
72 build.finalize();
73 return String::new();
74 }
75
76 let mut helpers = ModuleHelpers::default();
77 assemble_helpers_x_64(build, &mut helpers);
78
79 if !options.include_outlined_code && options.include_assembly {
80 build.text.clear();
81 build.log_append(format_args!(
82 "; skipping {} bytes of outlined helpers\n",
83 build
84 .get_code_size()
85 .wrapping_mul(core::mem::size_of::<u8>() as u32)
86 ));
87 }
88
89 for p in protos {
90 let mut ir = IrBuilder::ir_builder_ir_builder(&options.compilation_options.hooks);
91 ir.build_function_ir(p);
92 let mut asm_size = build.get_code_size();
93 let mut asm_count = build.get_instruction_count();
94
95 if options.include_assembly || options.include_ir {
96 log_function_header(build, p);
97 }
98
99 if options.include_ir_types {
100 log_function_types(
101 build,
102 &ir.function,
103 options.compilation_options.userdata_types,
104 );
105 }
106
107 let mut result = CodeGenCompilationResult::Success;
108
109 if !lower_function_x_64(
110 &mut ir,
111 build,
112 &mut helpers,
113 p,
114 options.clone(),
115 stats,
116 &mut result,
117 ) {
118 if build.log_text {
119 build.log_append(format_args!("; skipping (can't lower)\n"));
120 }
121
122 asm_size = 0;
123 asm_count = 0;
124
125 if !stats.is_null() {
126 (*stats).skipped_functions += 1;
127 }
128 } else {
129 asm_size = build.get_code_size().wrapping_sub(asm_size);
130 asm_count = build.get_instruction_count().wrapping_sub(asm_count);
131 }
132
133 if !stats.is_null() && ((*stats).function_stats_flags & FunctionStats_Enable) != 0 {
134 let mut function_stat = FunctionStats::default();
135
136 function_stat.name = if !(*p).debugname.is_null() {
137 let name = getstr((*p).debugname as *const _);
138 core::ffi::CStr::from_ptr(name)
139 .to_string_lossy()
140 .into_owned()
141 } else if (*p).bytecodeid == (*root).bytecodeid {
142 String::from("[top level]")
143 } else {
144 String::from("[anonymous]")
145 };
146 function_stat.line = (*p).linedefined;
147 function_stat.bcode_count =
148 get_instruction_count_instruction_size((*p).code, (*p).sizecode as u32);
149 function_stat.ir_count = ir.function.instructions.len() as u32;
150 function_stat.asm_size = asm_size.wrapping_mul(core::mem::size_of::<u8>() as u32);
151 function_stat.asm_count = asm_count;
152
153 if ((*stats).function_stats_flags
154 & crate::enums::function_stats_flags::FunctionStatsFlags::FunctionStats_BytecodeSummary
155 as u32)
156 != 0
157 {
158 let summary = FunctionBytecodeSummary::from_proto(p, 0);
159 function_stat
160 .bytecode_summary
161 .push(summary.get_counts(0).clone());
162 }
163
164 (*stats).functions.push(function_stat);
165 }
166
167 if build.log_text {
168 build.log_append(format_args!("\n"));
169 }
170 }
171
172 if !build.finalize() {
173 return String::new();
174 }
175
176 if options.output_binary {
177 let mut bytes = Vec::with_capacity(build.code.len() + build.data.len());
178 bytes.extend_from_slice(&build.code);
179 bytes.extend_from_slice(&build.data);
180 String::from_utf8_unchecked(bytes)
181 } else {
182 build.text.clone()
183 }
184}
185
186pub unsafe fn get_assembly_impl_a_64(
187 build: &mut AssemblyBuilderA64,
188 func: *const TValue,
189 options: AssemblyOptions,
190 stats: *mut LoweringStats,
191) -> String {
192 let cl = clvalue!(func);
193 let root: *mut Proto = (*(*cl).inner.l).p;
194
195 if (options.compilation_options.flags & CodeGenFlags::CodeGen_OnlyNativeModules as u32) != 0
196 && ((*root).flags & LuauProtoFlag::LPF_NATIVE_MODULE as u8) == 0
197 {
198 build.finalize();
199 return String::new();
200 }
201
202 let mut protos: Vec<*mut Proto> = Vec::new();
203 gather_functions(
204 &mut protos,
205 root,
206 options.compilation_options.flags,
207 ((*root).flags & LuauProtoFlag::LPF_NATIVE_FUNCTION as u8) != 0,
208 );
209
210 protos.retain(|p| !p.is_null());
211
212 if !stats.is_null() {
213 (*stats).total_functions += protos.len() as u32;
214 }
215
216 if protos.is_empty() {
217 build.finalize();
218 return String::new();
219 }
220
221 let mut helpers = ModuleHelpers::default();
222 assemble_helpers_a_64(build, &mut helpers);
223
224 if !options.include_outlined_code && options.include_assembly {
225 build.text.clear();
226 build.log_append(format_args!(
227 "; skipping {} bytes of outlined helpers\n",
228 build
229 .get_code_size()
230 .wrapping_mul(core::mem::size_of::<u32>() as u32)
231 ));
232 }
233
234 for p in protos {
235 let mut ir = IrBuilder::ir_builder_ir_builder(&options.compilation_options.hooks);
236 ir.build_function_ir(p);
237 let mut asm_size = build.get_code_size();
238 let mut asm_count = build.get_instruction_count();
239
240 if options.include_assembly || options.include_ir {
241 log_function_header(build, p);
242 }
243
244 if options.include_ir_types {
245 log_function_types(
246 build,
247 &ir.function,
248 options.compilation_options.userdata_types,
249 );
250 }
251
252 let mut result = CodeGenCompilationResult::Success;
253
254 if !lower_function_a_64(
255 &mut ir,
256 build,
257 &mut helpers,
258 p,
259 options.clone(),
260 stats,
261 &mut result,
262 ) {
263 if build.log_text {
264 build.log_append(format_args!("; skipping (can't lower)\n"));
265 }
266
267 asm_size = 0;
268 asm_count = 0;
269
270 if !stats.is_null() {
271 (*stats).skipped_functions += 1;
272 }
273 } else {
274 asm_size = build.get_code_size().wrapping_sub(asm_size);
275 asm_count = build.get_instruction_count().wrapping_sub(asm_count);
276 }
277
278 if !stats.is_null() && ((*stats).function_stats_flags & FunctionStats_Enable) != 0 {
279 let mut function_stat = FunctionStats::default();
280
281 function_stat.name = if !(*p).debugname.is_null() {
282 let name = getstr((*p).debugname as *const _);
283 core::ffi::CStr::from_ptr(name)
284 .to_string_lossy()
285 .into_owned()
286 } else if (*p).bytecodeid == (*root).bytecodeid {
287 String::from("[top level]")
288 } else {
289 String::from("[anonymous]")
290 };
291 function_stat.line = (*p).linedefined;
292 function_stat.bcode_count =
293 get_instruction_count_instruction_size((*p).code, (*p).sizecode as u32);
294 function_stat.ir_count = ir.function.instructions.len() as u32;
295 function_stat.asm_size = asm_size.wrapping_mul(core::mem::size_of::<u32>() as u32);
296 function_stat.asm_count = asm_count;
297
298 if ((*stats).function_stats_flags
299 & crate::enums::function_stats_flags::FunctionStatsFlags::FunctionStats_BytecodeSummary
300 as u32)
301 != 0
302 {
303 let summary = FunctionBytecodeSummary::from_proto(p, 0);
304 function_stat
305 .bytecode_summary
306 .push(summary.get_counts(0).clone());
307 }
308
309 (*stats).functions.push(function_stat);
310 }
311
312 if build.log_text {
313 build.log_append(format_args!("\n"));
314 }
315 }
316
317 if !build.finalize() {
318 return String::new();
319 }
320
321 if options.output_binary {
322 let code = core::slice::from_raw_parts(
323 build.code.as_ptr().cast::<u8>(),
324 build.code.len() * core::mem::size_of::<u32>(),
325 );
326 let mut bytes = Vec::with_capacity(code.len() + build.data.len());
327 bytes.extend_from_slice(code);
328 bytes.extend_from_slice(&build.data);
329 String::from_utf8_unchecked(bytes)
330 } else {
331 build.text.clone()
332 }
333}