Skip to main content

luaur_code_gen/functions/
get_assembly_impl.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;
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}