1use super::BytecodeBuilder;
2use super::BytecodeStringRef;
3use crate::dump::{append_closure_name, append_string_constant};
4use crate::model::{
5 BytecodeClass, BytecodeFeedbackSlot, BytecodeImportId, BytecodeTypedLocal, BytecodeVector,
6 BytecodeVectorDouble, ClosureIndex, ConstantIndex, Instruction, InstructionWord, Register,
7 TableShape,
8};
9use crate::wire::{BytecodeFunctionWire, ClosureNameLookup};
10use bitfields::bitfield;
11use luau_common::{BStr, BString, ByteSlice, DenseHashHasher, DenseHashMap, flags};
12use std::hash::{Hash, Hasher};
13use std::io::Write;
14
15#[cfg(debug_assertions)]
16use crate::model::InstructionAux;
17#[cfg(debug_assertions)]
18use crate::opcodes::{CaptureType, Opcode};
19
20pub trait BytecodeEncoder: std::fmt::Debug {
21 fn encode(&self, data: &mut [InstructionWord]);
22}
23
24#[derive(Debug, Clone, Copy)]
25pub struct BytecodeStringHasher;
26
27pub(super) fn builder_string_hash(bytes: &[u8]) -> u64 {
28 let mut hash = 2166136261u32;
29
30 for byte in bytes {
31 hash ^= u32::from(*byte);
32 hash = hash.wrapping_mul(16777619);
33 }
34
35 u64::from(hash)
36}
37
38impl DenseHashHasher<BytecodeStringRef<'_>> for BytecodeStringHasher {
39 fn hash(key: &BytecodeStringRef<'_>) -> u64 {
40 builder_string_hash(key.as_bytes())
41 }
42}
43
44pub(super) fn constant_cache_hash(key: &ConstantCacheKey) -> u64 {
45 match key.kind {
46 ConstantCacheKeyKind::Vector => {
47 let mut values = [
48 key.value as u32,
49 (key.value >> 32) as u32,
50 key.extra as u32,
51 (key.extra >> 32) as u32,
52 ];
53
54 for value in &mut values {
56 *value ^= *value >> 17;
57 }
58
59 let hash = values[0].wrapping_mul(73856093)
60 ^ values[1].wrapping_mul(19349663)
61 ^ values[2].wrapping_mul(83492791)
62 ^ values[3].wrapping_mul(39916801);
63
64 u64::from(hash)
65 }
66 ConstantCacheKeyKind::VectorDouble => {
67 let mut values = [key.value, key.extra, key.extra2, key.extra3];
68 for value in &mut values {
69 *value ^= *value >> 32;
70 }
71
72 let hash = (values[0] as u32).wrapping_mul(73856093)
73 ^ (values[1] as u32).wrapping_mul(19349663)
74 ^ (values[2] as u32).wrapping_mul(83492791)
75 ^ (values[3] as u32).wrapping_mul(39916801);
76
77 u64::from(hash)
78 }
79 _ => {
80 let ty = match key.kind {
81 ConstantCacheKeyKind::Nil => 0u32,
82 ConstantCacheKeyKind::Boolean => 1u32,
83 ConstantCacheKeyKind::Number => 2u32,
84 ConstantCacheKeyKind::Integer64 => 3u32,
85 ConstantCacheKeyKind::String => 5u32,
86 ConstantCacheKeyKind::Import => 6u32,
87 ConstantCacheKeyKind::Closure => 8u32,
88 ConstantCacheKeyKind::Vector => unreachable!(),
89 ConstantCacheKeyKind::VectorDouble => unreachable!(),
90 };
91 let value = key.value;
92
93 let m = 0x5bd1e995u32;
95
96 let mut h1 = value as u32;
97 let mut h2 = ((value >> 32) as u32) ^ ty.wrapping_mul(m);
98
99 h1 ^= h2 >> 18;
100 h1 = h1.wrapping_mul(m);
101 h2 ^= h1 >> 22;
102 h2 = h2.wrapping_mul(m);
103 h1 ^= h2 >> 17;
104 h1 = h1.wrapping_mul(m);
105 h2 ^= h1 >> 19;
106 h2 = h2.wrapping_mul(m);
107
108 u64::from(h2)
109 }
110 }
111}
112
113#[derive(Debug, Clone, Copy)]
114pub(super) struct ConstantCacheHasher;
115
116impl DenseHashHasher<ConstantCacheKey> for ConstantCacheHasher {
117 fn hash(key: &ConstantCacheKey) -> u64 {
118 constant_cache_hash(key)
119 }
120}
121
122pub(super) fn table_shape_hash(shape: &TableShape) -> u64 {
123 let mut hash = 2166136261u32;
124
125 for entry in shape.entries() {
126 hash ^= entry.key as u32;
127 hash = hash.wrapping_mul(16777619);
128
129 if let Some(value) = entry.value {
130 hash ^= value as u32;
131 hash = hash.wrapping_mul(16777619);
132 }
133 }
134
135 u64::from(hash)
136}
137
138#[derive(Debug, Clone, Copy)]
139pub(super) struct TableShapeCacheHasher;
140
141impl DenseHashHasher<TableShapeCacheKey> for TableShapeCacheHasher {
142 fn hash(key: &TableShapeCacheKey) -> u64 {
143 table_shape_hash(&key.shape)
144 }
145}
146
147#[derive(Debug, Clone, Copy)]
148pub(super) struct U32IdentityHasher;
149
150impl DenseHashHasher<u32> for U32IdentityHasher {
151 fn hash(key: &u32) -> u64 {
152 u64::from(*key)
153 }
154}
155
156#[derive(Debug, Default)]
157pub(super) struct BytecodeBuilderFunction {
158 pub(super) data: Vec<u8>,
159 pub(super) max_stack_size: u8,
160 pub(super) num_params: u8,
161 pub(super) upvalue_count: u8,
162 pub(super) is_vararg: bool,
163 pub(super) flags: u8,
164 pub(super) cost: u64,
165 pub(super) type_info: Vec<u8>,
166 pub(super) debug_name: Option<u32>,
167 pub(super) line_defined: i32,
168 pub(super) dump: BString,
169 pub(super) dump_name: BString,
170 pub(super) dump_instruction_offsets: Vec<i32>,
171}
172
173impl BytecodeBuilderFunction {
174 pub(super) fn new(num_params: u8, is_vararg: bool) -> Self {
175 Self {
176 num_params,
177 is_vararg,
178 max_stack_size: num_params,
179 ..Self::default()
180 }
181 }
182}
183
184pub(super) struct BytecodeBuilderScratch<'src> {
185 pub(super) upvalue_types: Vec<u8>,
186 pub(super) local_types: Vec<BytecodeTypedLocal>,
187 pub(super) code: Vec<Instruction>,
188 pub(super) constants: Vec<BytecodeBuilderConstant>,
189 pub(super) constant_index: DenseHashMap<ConstantCacheKey, ConstantIndex, ConstantCacheHasher>,
190 pub(super) table_shapes: Vec<TableShape>,
191 pub(super) table_shape_index:
192 DenseHashMap<TableShapeCacheKey, ConstantIndex, TableShapeCacheHasher>,
193 pub(super) child_functions: Vec<u32>,
194 pub(super) child_function_map: DenseHashMap<u32, i16, U32IdentityHasher>,
195 pub(super) jumps: Vec<Jump>,
196 pub(super) has_long_jumps: bool,
197 pub(super) lines: Vec<i32>,
198 pub(super) local_vars: Vec<BytecodeBuilderLocal>,
199 pub(super) upvalues: Vec<u32>,
200 pub(super) feedback_slots: Vec<BytecodeFeedbackSlot>,
201 pub(super) debug_remarks: Vec<StoredDebugRemark>,
202 _src: std::marker::PhantomData<&'src ()>,
203}
204
205impl<'src> Default for BytecodeBuilderScratch<'src> {
206 fn default() -> Self {
207 Self {
208 upvalue_types: Vec::new(),
209 local_types: Vec::new(),
210 code: Vec::new(),
211 constants: Vec::new(),
212 constant_index: DenseHashMap::new(ConstantCacheKey::empty()),
213 table_shapes: Vec::new(),
214 table_shape_index: DenseHashMap::new(TableShapeCacheKey::empty()),
215 child_functions: Vec::new(),
216 child_function_map: DenseHashMap::new(u32::MAX),
217 jumps: Vec::new(),
218 has_long_jumps: false,
219 lines: Vec::new(),
220 local_vars: Vec::new(),
221 upvalues: Vec::new(),
222 feedback_slots: Vec::new(),
223 debug_remarks: Vec::new(),
224 _src: std::marker::PhantomData,
225 }
226 }
227}
228
229impl<'src> BytecodeBuilderScratch<'src> {
230 pub(super) fn borrowed_wire<'a>(
231 &'a self,
232 strings: &'a [BytecodeStringRef<'a>],
233 class_shapes: &'a [BytecodeClass],
234 ) -> BorrowedBytecodeBuilderFunction<'a, 'a> {
235 BorrowedBytecodeBuilderFunction {
236 code: &self.code,
237 constants: &self.constants,
238 table_shapes: &self.table_shapes,
239 class_shapes,
240 lines: &self.lines,
241 strings,
242 _src: std::marker::PhantomData,
243 }
244 }
245
246 pub(super) fn clear(&mut self) {
247 self.upvalue_types.clear();
248 self.local_types.clear();
249 self.code.clear();
250 self.constants.clear();
251 self.table_shapes.clear();
252 self.constant_index.clear_with_threshold(32);
253 self.table_shape_index.clear_with_threshold(32);
254 self.child_functions.clear();
255 self.child_function_map.clear_with_threshold(32);
256 self.jumps.clear();
257 self.has_long_jumps = false;
258 self.lines.clear();
259 self.local_vars.clear();
260 self.upvalues.clear();
261 self.feedback_slots.clear();
262 self.debug_remarks.clear();
263 }
264
265 #[cfg(debug_assertions)]
266 pub(super) fn validate(
267 &self,
268 function: &BytecodeBuilderFunction,
269 functions: &[BytecodeBuilderFunction],
270 ) {
271 self.validate_instructions(function, functions);
272 self.validate_variadic();
273 }
274
275 #[cfg(debug_assertions)]
276 fn validate_instructions(
277 &self,
278 function: &BytecodeBuilderFunction,
279 functions: &[BytecodeBuilderFunction],
280 ) {
281 let mut instruction_valid = vec![false; self.code.len()];
282 let mut pc = 0usize;
283 while pc < self.code.len() {
284 let opcode = unsafe { self.code[pc].opcode_unchecked() };
285 instruction_valid[pc] = true;
286 pc += opcode.length();
287 debug_assert!(pc <= self.code.len());
288 }
289
290 let mut open_captures = Vec::new();
291 let mut pc = 0usize;
292 while pc < self.code.len() {
293 let instruction = self.code[pc];
294 let opcode = unsafe { instruction.opcode_unchecked() };
295 let aux = || {
296 self.code
297 .get(pc + 1)
298 .copied()
299 .map(Instruction::word)
300 .map(InstructionAux::new)
301 .expect("instruction requires aux word")
302 };
303 let aux_word = || aux().word();
304 let reg = |register: u8| debug_assert!(register < function.max_stack_size);
305 let reg_range = |register: u8, count: i32| {
306 let end = i32::from(register) + count.max(0);
307 debug_assert!(end <= i32::from(function.max_stack_size));
308 };
309 let upvalue = |index: u8| debug_assert!(index < function.upvalue_count);
310 let any_constant = |index: u32| {
311 debug_assert!((index as usize) < self.constants.len());
312 };
313 let constant = |index: u32, kind: ConstantKind| {
314 any_constant(index);
315 debug_assert_eq!(self.constants[index as usize].kind(), kind);
316 };
317 let jump = |offset: i32| {
318 let target = pc as i32 + 1 + offset;
319 debug_assert!(target >= 0);
320 let target = target as usize;
321 debug_assert!(target < self.code.len());
322 debug_assert!(instruction_valid[target]);
323 };
324
325 match opcode {
326 Opcode::Nop | Opcode::Break | Opcode::Coverage | Opcode::NativeCall => {}
327 Opcode::LoadNil | Opcode::LoadN | Opcode::NewTable => reg(instruction.a()),
328 Opcode::LoadB => {
329 reg(instruction.a());
330 debug_assert!(instruction.b() == 0 || instruction.b() == 1);
331 jump(i32::from(instruction.c()));
332 }
333 Opcode::LoadK => {
334 reg(instruction.a());
335 any_constant(instruction.d() as u16 as u32);
336 }
337 Opcode::Move => {
338 reg(instruction.a());
339 reg(instruction.b());
340 }
341 Opcode::GetGlobal | Opcode::SetGlobal => {
342 reg(instruction.a());
343 constant(aux_word(), ConstantKind::String);
344 }
345 Opcode::GetUpval | Opcode::SetUpval => {
346 reg(instruction.a());
347 upvalue(instruction.b());
348 }
349 Opcode::CloseUpvals => {
350 reg(instruction.a());
351 while open_captures
352 .last()
353 .is_some_and(|capture| *capture >= instruction.a())
354 {
355 open_captures.pop();
356 }
357 }
358 Opcode::GetImport => {
359 reg(instruction.a());
360 constant(instruction.d() as u16 as u32, ConstantKind::Import);
361 let import_id = aux_word();
362 debug_assert!((import_id >> 30) != 0);
363 for index in 0..(import_id >> 30) {
364 constant(
365 (import_id >> (20 - 10 * index)) & 1023,
366 ConstantKind::String,
367 );
368 }
369 }
370 Opcode::GetTable | Opcode::SetTable => {
371 reg(instruction.a());
372 reg(instruction.b());
373 reg(instruction.c());
374 }
375 Opcode::GetTableKs | Opcode::SetTableKs => {
376 reg(instruction.a());
377 reg(instruction.b());
378 constant(aux_word(), ConstantKind::String);
379 }
380 Opcode::GetTableN | Opcode::SetTableN => {
381 reg(instruction.a());
382 reg(instruction.b());
383 }
384 Opcode::NewClosure => {
385 reg(instruction.a());
386 let child = instruction.d() as u16 as usize;
387 debug_assert!(child < self.child_functions.len());
388 let child_id = self.child_functions[child] as usize;
389 debug_assert!(child_id < functions.len());
390 let upvalues = functions[child_id].upvalue_count;
391 for capture in 0..upvalues as usize {
392 debug_assert!(pc + 1 + capture < self.code.len());
393 debug_assert_eq!(
394 unsafe { self.code[pc + 1 + capture].opcode_unchecked() },
395 Opcode::Capture
396 );
397 }
398 }
399 Opcode::NameCall => {
400 reg(instruction.a());
401 reg(instruction.b());
402 constant(aux_word(), ConstantKind::String);
403 debug_assert!(self.code.get(pc + 2).is_some_and(|next| matches!(
404 unsafe { next.opcode_unchecked() },
405 Opcode::Call | Opcode::CallFb
406 )));
407 }
408 Opcode::Call | Opcode::CallFb => {
409 let params = i32::from(instruction.b()) - 1;
410 let results = i32::from(instruction.c()) - 1;
411 reg(instruction.a());
412 reg_range(instruction.a().saturating_add(1), params);
413 reg_range(instruction.a(), results);
414 }
415 Opcode::Return => {
416 reg_range(instruction.a(), i32::from(instruction.b()) - 1);
417 }
418 Opcode::Jump | Opcode::JumpBack => jump(i32::from(instruction.d())),
419 Opcode::CmpProto => {
420 reg(instruction.a());
421 jump(i32::from(instruction.d()));
422 }
423 Opcode::JumpIf | Opcode::JumpIfNot => {
424 reg(instruction.a());
425 jump(i32::from(instruction.d()));
426 }
427 Opcode::JumpIfEq
428 | Opcode::JumpIfLe
429 | Opcode::JumpIfLt
430 | Opcode::JumpIfNotEq
431 | Opcode::JumpIfNotLe
432 | Opcode::JumpIfNotLt => {
433 reg(instruction.a());
434 reg(aux_word() as u8);
435 jump(i32::from(instruction.d()));
436 }
437 Opcode::JumpXEqKNil | Opcode::JumpXEqKB => {
438 reg(instruction.a());
439 jump(i32::from(instruction.d()));
440 }
441 Opcode::JumpXEqKN => {
442 reg(instruction.a());
443 constant(aux_word() & 0x00ff_ffff, ConstantKind::Number);
444 jump(i32::from(instruction.d()));
445 }
446 Opcode::JumpXEqKS => {
447 reg(instruction.a());
448 constant(aux_word() & 0x00ff_ffff, ConstantKind::String);
449 jump(i32::from(instruction.d()));
450 }
451 Opcode::Add
452 | Opcode::Sub
453 | Opcode::Mul
454 | Opcode::Div
455 | Opcode::IDiv
456 | Opcode::Mod
457 | Opcode::Pow
458 | Opcode::And
459 | Opcode::Or
460 | Opcode::Concat => {
461 reg(instruction.a());
462 reg(instruction.b());
463 reg(instruction.c());
464 if opcode == Opcode::Concat {
465 debug_assert!(instruction.b() <= instruction.c());
466 }
467 }
468 Opcode::AddK
469 | Opcode::SubK
470 | Opcode::MulK
471 | Opcode::DivK
472 | Opcode::IDivK
473 | Opcode::ModK
474 | Opcode::PowK => {
475 reg(instruction.a());
476 reg(instruction.b());
477 constant(u32::from(instruction.c()), ConstantKind::Number);
478 }
479 Opcode::SubRK | Opcode::DivRK => {
480 reg(instruction.a());
481 constant(u32::from(instruction.b()), ConstantKind::Number);
482 reg(instruction.c());
483 }
484 Opcode::AndK | Opcode::OrK => {
485 reg(instruction.a());
486 reg(instruction.b());
487 any_constant(u32::from(instruction.c()));
488 }
489 Opcode::Not | Opcode::Minus | Opcode::Length => {
490 reg(instruction.a());
491 reg(instruction.b());
492 }
493 Opcode::DupTable => {
494 reg(instruction.a());
495 constant(instruction.d() as u16 as u32, ConstantKind::Table);
496 }
497 Opcode::SetList => {
498 reg(instruction.a());
499 reg_range(instruction.b(), i32::from(instruction.c()) - 1);
500 }
501 Opcode::ForNPrep | Opcode::ForNLoop => {
502 reg(instruction.a().saturating_add(2));
503 jump(i32::from(instruction.d()));
504 }
505 Opcode::ForGPrep => {
506 reg(instruction.a().saturating_add(3));
507 jump(i32::from(instruction.d()));
508 }
509 Opcode::ForGLoop => {
510 reg(instruction.a().saturating_add(2 + aux_word() as u8));
511 jump(i32::from(instruction.d()));
512 debug_assert!(aux_word() as u8 >= 1);
513 }
514 Opcode::ForGPrepInext | Opcode::ForGPrepNext => {
515 reg(instruction.a().saturating_add(4));
516 jump(i32::from(instruction.d()));
517 }
518 Opcode::GetVarargs => {
519 reg_range(instruction.a(), i32::from(instruction.b()) - 1);
520 }
521 Opcode::DupClosure => {
522 reg(instruction.a());
523 constant(instruction.d() as u16 as u32, ConstantKind::Closure);
524 let child = match &self.constants[instruction.d() as u16 as usize] {
525 BytecodeBuilderConstant::Closure(index) => index.as_usize(),
526 _ => unreachable!(),
527 };
528 debug_assert!(child < functions.len());
529 let upvalues = functions[child].upvalue_count;
530 for capture in 0..upvalues as usize {
531 debug_assert!(pc + 1 + capture < self.code.len());
532 let capture_instruction = self.code[pc + 1 + capture];
533 debug_assert_eq!(
534 unsafe { capture_instruction.opcode_unchecked() },
535 Opcode::Capture
536 );
537 debug_assert!(matches!(
538 CaptureType::try_from(capture_instruction.a()),
539 Ok(CaptureType::Val | CaptureType::Upval)
540 ));
541 }
542 }
543 Opcode::PrepVarargs => {
544 debug_assert_eq!(instruction.a(), function.num_params);
545 debug_assert!(function.is_vararg);
546 }
547 Opcode::LoadKx => {
548 reg(instruction.a());
549 any_constant(aux_word());
550 }
551 Opcode::JumpX => jump(instruction.e()),
552 Opcode::FastCall => {
553 jump(i32::from(instruction.c()));
554 debug_assert_eq!(
555 unsafe {
556 self.code[pc + 1 + usize::from(instruction.c())].opcode_unchecked()
557 },
558 Opcode::Call
559 );
560 }
561 Opcode::FastCall1 => {
562 reg(instruction.b());
563 jump(i32::from(instruction.c()));
564 debug_assert_eq!(
565 unsafe {
566 self.code[pc + 1 + usize::from(instruction.c())].opcode_unchecked()
567 },
568 Opcode::Call
569 );
570 }
571 Opcode::FastCall2 => {
572 reg(instruction.b());
573 jump(i32::from(instruction.c()));
574 debug_assert_eq!(
575 unsafe {
576 self.code[pc + 1 + usize::from(instruction.c())].opcode_unchecked()
577 },
578 Opcode::Call
579 );
580 reg(aux_word() as u8);
581 }
582 Opcode::FastCall2K => {
583 reg(instruction.b());
584 jump(i32::from(instruction.c()));
585 debug_assert_eq!(
586 unsafe {
587 self.code[pc + 1 + usize::from(instruction.c())].opcode_unchecked()
588 },
589 Opcode::Call
590 );
591 any_constant(aux_word());
592 }
593 Opcode::FastCall3 => {
594 reg(instruction.b());
595 jump(i32::from(instruction.c()));
596 debug_assert_eq!(
597 unsafe {
598 self.code[pc + 1 + usize::from(instruction.c())].opcode_unchecked()
599 },
600 Opcode::Call
601 );
602 reg((aux_word() & 0xff) as u8);
603 reg(((aux_word() >> 8) & 0xff) as u8);
604 }
605 Opcode::Capture => match CaptureType::try_from(instruction.a()) {
606 Ok(CaptureType::Val) => reg(instruction.b()),
607 Ok(CaptureType::Ref) => {
608 reg(instruction.b());
609 open_captures.push(instruction.b());
610 }
611 Ok(CaptureType::Upval) => upvalue(instruction.b()),
612 Err(_) => debug_assert!(false, "unsupported capture type"),
613 },
614 Opcode::NewClassMember => {
615 reg(instruction.a());
616 debug_assert_eq!(instruction.b(), 0);
617 reg(instruction.c());
618 constant(aux_word(), ConstantKind::String);
619 }
620 Opcode::NewClass => {
621 reg(instruction.a());
622 debug_assert!(
623 instruction.b() == u8::MAX || instruction.b() < function.max_stack_size
624 );
625 debug_assert_eq!(instruction.c(), 0);
626 constant(aux_word(), ConstantKind::Class);
627 }
628 Opcode::GetUDataKs | Opcode::SetUDataKs => {
629 reg(instruction.a());
630 reg(instruction.b());
631 constant(aux().kv16().into(), ConstantKind::String);
632 }
633 Opcode::NameCallUData => {
634 reg(instruction.a());
635 reg(instruction.b());
636 constant(aux().kv16().into(), ConstantKind::String);
637 debug_assert!(
638 self.code
639 .get(pc + 2)
640 .is_some_and(|next| unsafe { next.opcode_unchecked() } == Opcode::Call)
641 );
642 }
643 }
644
645 pc += opcode.length();
646 debug_assert!(pc <= self.code.len());
647 }
648
649 debug_assert!(open_captures.is_empty());
650 }
651
652 #[cfg(debug_assertions)]
653 fn validate_variadic(&self) {
654 let mut variadic_sequence = false;
655 let mut instruction_targets = vec![false; self.code.len()];
656
657 let mut pc = 0usize;
658 while pc < self.code.len() {
659 let instruction = self.code[pc];
660 let opcode = unsafe { instruction.opcode_unchecked() };
661
662 if let Some(target) = unsafe { instruction.jump_target_unchecked(pc as u32) }
663 .filter(|_| !opcode.is_fast_call())
664 .filter(|target| *target >= 0)
665 {
666 debug_assert!((target as usize) < self.code.len());
667 instruction_targets[target as usize] = true;
668 }
669
670 pc += opcode.length();
671 debug_assert!(pc <= self.code.len());
672 }
673
674 let mut pc = 0usize;
675 while pc < self.code.len() {
676 let instruction = self.code[pc];
677 let opcode = unsafe { instruction.opcode_unchecked() };
678
679 if variadic_sequence {
680 debug_assert!(!instruction_targets[pc]);
681 }
682
683 if matches!(opcode, Opcode::Call | Opcode::CallFb) {
684 if instruction.b() == 0 {
685 debug_assert!(variadic_sequence);
686 variadic_sequence = false;
687 } else {
688 debug_assert!(!variadic_sequence);
689 }
690
691 if instruction.c() == 0 {
692 debug_assert!(!variadic_sequence);
693 variadic_sequence = true;
694 }
695 } else if opcode == Opcode::GetVarargs && instruction.b() == 0 {
696 debug_assert!(!variadic_sequence);
697 variadic_sequence = true;
698 } else if (opcode == Opcode::Return && instruction.b() == 0)
699 || (opcode == Opcode::SetList && instruction.c() == 0)
700 {
701 debug_assert!(variadic_sequence);
702 variadic_sequence = false;
703 } else if opcode == Opcode::FastCall {
704 let call_pc = pc + usize::from(instruction.c()) + 1;
705 debug_assert!(call_pc < self.code.len());
706 debug_assert_eq!(
707 unsafe { self.code[call_pc].opcode_unchecked() },
708 Opcode::Call
709 );
710
711 if self.code[call_pc].b() == 0 {
712 debug_assert!(variadic_sequence);
713 } else {
714 debug_assert!(!variadic_sequence);
715 }
716 } else if matches!(
717 opcode,
718 Opcode::CloseUpvals
719 | Opcode::NameCall
720 | Opcode::GetImport
721 | Opcode::Move
722 | Opcode::GetUpval
723 | Opcode::GetGlobal
724 | Opcode::GetTableKs
725 | Opcode::Coverage
726 ) {
727 } else {
728 debug_assert!(!variadic_sequence);
729 }
730
731 pc += opcode.length();
732 debug_assert!(pc <= self.code.len());
733 }
734
735 debug_assert!(!variadic_sequence);
736 }
737}
738
739#[derive(Debug, Clone, PartialEq)]
740pub(super) enum BytecodeBuilderConstant {
741 Boolean(bool),
742 Class(u32),
743 Closure(ClosureIndex),
744 Import(BytecodeImportId),
745 Integer64(i64),
746 Nil,
747 Number(f64),
748 String(u32),
749 Table(u32),
750 Vector(BytecodeVector),
751 VectorDouble(BytecodeVectorDouble),
752}
753
754impl BytecodeBuilderConstant {
755 pub(super) fn cache_key(&self) -> ConstantCacheKey {
756 match self {
757 Self::Boolean(value) => ConstantCacheKey {
758 kind: ConstantCacheKeyKind::Boolean,
759 value: u64::from(*value),
760 extra: 0,
761 extra2: 0,
762 extra3: 0,
763 },
764 Self::Class(_) => unreachable!("class constants use dedicated class-shape storage"),
765 Self::Closure(value) => ConstantCacheKey {
766 kind: ConstantCacheKeyKind::Closure,
767 value: u64::from(value.get()),
768 extra: 0,
769 extra2: 0,
770 extra3: 0,
771 },
772 Self::Import(value) => ConstantCacheKey {
773 kind: ConstantCacheKeyKind::Import,
774 value: u64::from(value.raw()),
775 extra: 0,
776 extra2: 0,
777 extra3: 0,
778 },
779 Self::Integer64(value) => ConstantCacheKey {
780 kind: ConstantCacheKeyKind::Integer64,
781 value: *value as u64,
782 extra: 0,
783 extra2: 0,
784 extra3: 0,
785 },
786 Self::Nil => ConstantCacheKey::nil(),
787 Self::Number(value) => ConstantCacheKey {
788 kind: ConstantCacheKeyKind::Number,
789 value: value.to_bits(),
790 extra: 0,
791 extra2: 0,
792 extra3: 0,
793 },
794 Self::String(value) => ConstantCacheKey {
795 kind: ConstantCacheKeyKind::String,
796 value: u64::from(*value),
797 extra: 0,
798 extra2: 0,
799 extra3: 0,
800 },
801 Self::Table(_) => unreachable!("table constants use dedicated table-shape storage"),
802 Self::Vector(value) => {
803 let bits = value.to_bits();
804 ConstantCacheKey {
805 kind: ConstantCacheKeyKind::Vector,
806 value: u64::from(bits[0]) | (u64::from(bits[1]) << 32),
807 extra: u64::from(bits[2]) | (u64::from(bits[3]) << 32),
808 extra2: 0,
809 extra3: 0,
810 }
811 }
812 Self::VectorDouble(value) => {
813 let bits = value.to_bits();
814 ConstantCacheKey {
815 kind: ConstantCacheKeyKind::VectorDouble,
816 value: bits[0],
817 extra: bits[1],
818 extra2: bits[2],
819 extra3: bits[3],
820 }
821 }
822 }
823 }
824
825 #[cfg(debug_assertions)]
826 fn kind(&self) -> ConstantKind {
827 match self {
828 Self::Boolean(_) => ConstantKind::Boolean,
829 Self::Class(_) => ConstantKind::Class,
830 Self::Closure(_) => ConstantKind::Closure,
831 Self::Import(_) => ConstantKind::Import,
832 Self::Integer64(_) => ConstantKind::Integer64,
833 Self::Nil => ConstantKind::Nil,
834 Self::Number(_) => ConstantKind::Number,
835 Self::String(_) => ConstantKind::String,
836 Self::Table(_) => ConstantKind::Table,
837 Self::Vector(_) => ConstantKind::Vector,
838 Self::VectorDouble(_) => ConstantKind::VectorDouble,
839 }
840 }
841}
842
843#[derive(Debug, Clone)]
844pub(super) struct BytecodeBuilderLocal {
845 pub(super) name: u32,
846 pub(super) start_pc: u32,
847 pub(super) end_pc: u32,
848 pub(super) register: Register,
849}
850
851#[derive(Debug)]
852pub(super) struct BorrowedBytecodeBuilderFunction<'a, 'src> {
853 code: &'a [Instruction],
854 constants: &'a [BytecodeBuilderConstant],
855 table_shapes: &'a [TableShape],
856 class_shapes: &'a [BytecodeClass],
857 lines: &'a [i32],
858 strings: &'a [BytecodeStringRef<'src>],
859 _src: std::marker::PhantomData<&'src ()>,
860}
861
862pub(super) struct BuilderClosureNames<'a> {
863 pub(super) functions: &'a [BytecodeBuilderFunction],
864}
865
866impl ClosureNameLookup for BuilderClosureNames<'_> {
867 fn closure_name(&self, id: ClosureIndex) -> Option<&BStr> {
868 self.functions
869 .get(id.as_usize())
870 .map(|function| function.dump_name.as_bstr())
871 .filter(|name| !name.is_empty())
872 }
873}
874
875impl BorrowedBytecodeBuilderFunction<'_, '_> {
876 fn string_bytes(&self, index: u32) -> Option<&BStr> {
877 index
878 .checked_sub(1)
879 .and_then(|index| self.strings.get(index as usize))
880 .map(|value| value.as_bytes().as_bstr())
881 }
882}
883
884#[derive(Debug, Clone, Copy, PartialEq, Eq)]
885#[cfg(debug_assertions)]
886enum ConstantKind {
887 Boolean,
888 Class,
889 Closure,
890 Import,
891 Integer64,
892 Nil,
893 Number,
894 String,
895 Table,
896 Vector,
897 VectorDouble,
898}
899
900#[derive(Debug, Clone, Copy, PartialEq, Eq)]
901pub(super) enum ConstantCacheKeyKind {
902 Nil,
903 Boolean,
904 Number,
905 Integer64,
906 String,
907 Import,
908 Closure,
909 Vector,
910 VectorDouble,
911}
912
913#[derive(Debug, Clone, PartialEq, Eq)]
914pub(super) struct ConstantCacheKey {
915 kind: ConstantCacheKeyKind,
916 value: u64,
917 extra: u64,
918 extra2: u64,
919 extra3: u64,
920}
921
922impl ConstantCacheKey {
923 pub(super) const fn empty() -> Self {
924 Self {
925 kind: ConstantCacheKeyKind::Nil,
926 value: u64::MAX,
927 extra: 0,
928 extra2: 0,
929 extra3: 0,
930 }
931 }
932
933 pub(super) const fn nil() -> Self {
934 Self {
935 kind: ConstantCacheKeyKind::Nil,
936 value: 0,
937 extra: 0,
938 extra2: 0,
939 extra3: 0,
940 }
941 }
942}
943
944impl Hash for ConstantCacheKey {
945 fn hash<H: Hasher>(&self, state: &mut H) {
946 state.write_u64(constant_cache_hash(self));
947 }
948}
949
950#[derive(Debug, Clone)]
951pub(super) struct TableShapeCacheKey {
952 shape: TableShape,
953}
954
955impl TableShapeCacheKey {
956 pub(super) fn empty() -> Self {
957 Self {
958 shape: TableShape::new(Vec::new()),
959 }
960 }
961
962 pub(super) fn new(shape: TableShape) -> Self {
963 Self { shape }
964 }
965}
966
967impl PartialEq for TableShapeCacheKey {
968 fn eq(&self, other: &Self) -> bool {
969 self.shape == other.shape
970 }
971}
972
973impl Eq for TableShapeCacheKey {}
974
975impl Hash for TableShapeCacheKey {
976 fn hash<H: Hasher>(&self, state: &mut H) {
977 state.write_u64(table_shape_hash(&self.shape));
978 }
979}
980
981impl BytecodeFunctionWire for BorrowedBytecodeBuilderFunction<'_, '_> {
982 fn code(&self) -> &[Instruction] {
983 self.code
984 }
985
986 fn constant_count(&self) -> usize {
987 self.constants.len()
988 }
989
990 fn append_constant(
991 &self,
992 result: &mut Vec<u8>,
993 index: usize,
994 closure_names: &dyn ClosureNameLookup,
995 detailed: bool,
996 ) {
997 match self.constants.get(index) {
998 Some(BytecodeBuilderConstant::Nil) => result.extend_from_slice(b"nil"),
999 Some(BytecodeBuilderConstant::Boolean(true)) => result.extend_from_slice(b"true"),
1000 Some(BytecodeBuilderConstant::Boolean(false)) => result.extend_from_slice(b"false"),
1001 Some(BytecodeBuilderConstant::Number(n)) => {
1002 luau_printf::sprintf!(=> result, "%.17g", *n);
1003 }
1004 Some(BytecodeBuilderConstant::Integer64(n)) => {
1005 write!(result, "{n}").unwrap();
1006 }
1007 Some(BytecodeBuilderConstant::String(value)) => {
1008 if let Some(bytes) = self.string_bytes(*value) {
1009 append_string_constant(result, bytes);
1010 } else {
1011 write!(result, "K{index}").unwrap();
1012 }
1013 }
1014 Some(BytecodeBuilderConstant::Import(import_id)) => {
1015 let count = import_id.raw() >> 30;
1016
1017 for component in 0..count {
1018 let constant = (import_id.raw() >> (20 - 10 * component)) & 1023;
1019
1020 let Some(BytecodeBuilderConstant::String(value)) =
1021 self.constants.get(constant as usize)
1022 else {
1023 continue;
1024 };
1025
1026 if component > 0 {
1027 result.push(b'.');
1028 }
1029
1030 if let Some(bytes) = self.string_bytes(*value) {
1031 result.extend_from_slice(bytes);
1032 }
1033 }
1034 }
1035 Some(BytecodeBuilderConstant::Table(shape_index)) => {
1036 let Some(shape) = self.table_shapes.get(*shape_index as usize) else {
1037 result.extend_from_slice(b"{...}");
1038 return;
1039 };
1040 if detailed {
1041 let entries = shape.entries();
1042 let sizenode = if entries.is_empty() {
1043 0
1044 } else {
1045 1u32 << (i32::BITS - (entries.len() as i32 - 1).leading_zeros())
1046 };
1047 let mask = sizenode.saturating_sub(1);
1048
1049 let mut slots = vec![0u32; entries.len()];
1050 let mut slot_owner = vec![usize::MAX; sizenode as usize];
1051
1052 for (shape_index, entry) in entries.iter().enumerate() {
1053 let Some(BytecodeBuilderConstant::String(key)) =
1054 self.constants.get(entry.key as usize)
1055 else {
1056 result.extend_from_slice(b"{...}");
1057 return;
1058 };
1059
1060 let Some(key_bytes) = self.string_bytes(*key) else {
1061 result.extend_from_slice(b"{...}");
1062 return;
1063 };
1064
1065 slots[shape_index] = BytecodeBuilder::get_string_hash(key_bytes) & mask;
1066
1067 if slot_owner[slots[shape_index] as usize] == usize::MAX {
1068 slot_owner[slots[shape_index] as usize] = shape_index;
1069 }
1070 }
1071
1072 result.push(b'{');
1073
1074 for (shape_index, entry) in entries.iter().enumerate() {
1075 if shape_index > 0 {
1076 result.extend_from_slice(b", ");
1077 }
1078
1079 result.push(b'[');
1080 self.append_constant(result, entry.key as usize, closure_names, false);
1081 result.extend_from_slice(b"]");
1082
1083 if let Some(value) = entry.value {
1084 result.extend_from_slice(b" = ");
1085 self.append_constant(result, value as usize, closure_names, false);
1086 }
1087
1088 write!(result, " #{}", slots[shape_index]).unwrap();
1089
1090 if slot_owner[slots[shape_index] as usize] != shape_index {
1091 result.extend_from_slice(b" (conflict)");
1092 }
1093 }
1094
1095 write!(result, "}} sizenode={sizenode}").unwrap();
1096 } else {
1097 result.extend_from_slice(b"{...}");
1098 }
1099 }
1100 Some(BytecodeBuilderConstant::Closure(id)) => {
1101 append_closure_name(result, *id, closure_names)
1102 }
1103 Some(BytecodeBuilderConstant::Vector(value)) => {
1104 if value.w() == 0.0 {
1105 luau_printf::sprintf!(
1106 => result,
1107 "%.9g, %.9g, %.9g",
1108 f64::from(value.x()),
1109 f64::from(value.y()),
1110 f64::from(value.z())
1111 );
1112 } else {
1113 luau_printf::sprintf!(
1114 => result,
1115 "%.9g, %.9g, %.9g, %.9g",
1116 f64::from(value.x()),
1117 f64::from(value.y()),
1118 f64::from(value.z()),
1119 f64::from(value.w())
1120 );
1121 }
1122 }
1123 Some(BytecodeBuilderConstant::VectorDouble(value)) => {
1124 if flags::LuauCompileEmitVectorDouble.get() {
1125 if value.w() == 0.0 {
1126 luau_printf::sprintf!(
1127 => result,
1128 "%.17g, %.17g, %.17g",
1129 value.x(),
1130 value.y(),
1131 value.z()
1132 );
1133 } else {
1134 luau_printf::sprintf!(
1135 => result,
1136 "%.17g, %.17g, %.17g, %.17g",
1137 value.x(),
1138 value.y(),
1139 value.z(),
1140 value.w()
1141 );
1142 }
1143 } else if value.w() == 0.0 {
1144 luau_printf::sprintf!(
1145 => result,
1146 "%.9g, %.9g, %.9g",
1147 value.x() as f32 as f64,
1148 value.y() as f32 as f64,
1149 value.z() as f32 as f64
1150 );
1151 } else {
1152 luau_printf::sprintf!(
1153 => result,
1154 "%.9g, %.9g, %.9g, %.9g",
1155 value.x() as f32 as f64,
1156 value.y() as f32 as f64,
1157 value.z() as f32 as f64,
1158 value.w() as f32 as f64
1159 );
1160 }
1161 }
1162 Some(BytecodeBuilderConstant::Class(class_index)) => {
1163 let Some(class) = self.class_shapes.get(*class_index as usize) else {
1164 result.extend_from_slice(b"class ?");
1165 return;
1166 };
1167 result.extend_from_slice(b"class ");
1168 match self.constants.get(class.class_name as usize) {
1169 Some(BytecodeBuilderConstant::String(value)) => {
1170 if let Some(bytes) = self.string_bytes(*value) {
1171 result.extend_from_slice(bytes);
1172 } else {
1173 write!(result, "K{}", class.class_name).unwrap();
1174 }
1175 }
1176 _ => write!(result, "K{}", class.class_name).unwrap(),
1177 }
1178 write!(
1179 result,
1180 " (props: {}, methods: {})",
1181 class.property_names.len(),
1182 class.method_names.len()
1183 )
1184 .unwrap();
1185 }
1186 None => result.extend_from_slice(b"?"),
1187 }
1188 }
1189
1190 fn line_for_pc(&self, pc: usize) -> Option<i32> {
1191 self.lines.get(pc).copied()
1192 }
1193}
1194
1195#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1196pub(super) struct Jump {
1197 pub(super) source: usize,
1198 pub(super) target: usize,
1199}
1200
1201#[derive(Debug, Clone, PartialEq, Eq)]
1202pub(super) struct StoredDebugRemark {
1203 pub(super) pc: usize,
1204 pub(super) line: i32,
1205 pub(super) text: BString,
1206}
1207
1208#[bitfield(u32)]
1209#[derive(PartialEq, Eq)]
1210pub struct BytecodeDumpFlags {
1211 #[bits(default = false)]
1212 code: bool,
1213 #[bits(default = false)]
1214 lines: bool,
1215 #[bits(default = false)]
1216 source: bool,
1217 #[bits(default = false)]
1218 locals: bool,
1219 #[bits(default = false)]
1220 remarks: bool,
1221 #[bits(default = false)]
1222 types: bool,
1223 #[bits(default = false)]
1224 constants: bool,
1225 #[bits(25, default = 0)]
1226 _reserved: u32,
1227}