Skip to main content

luau_bytecode/graph/
export.rs

1use super::{
2    BytecodeBlockId, BytecodeEdgeKind, BytecodeImmediate, BytecodeInstruction,
3    BytecodeInstructionId, BytecodeOperand,
4};
5use crate::builder::BytecodeBuilder;
6use crate::function::{BytecodeFunction, BytecodeFunctionConstant};
7use crate::model::{BytecodeImportId, Register};
8use crate::opcodes::Opcode;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub enum BytecodeWriteError {
12    ChildFunctionLimitExceeded {
13        id: u32,
14    },
15    MissingResultRegister {
16        operand: BytecodeOperand,
17    },
18    MissingOperand {
19        opcode: Opcode,
20        index: usize,
21    },
22    UnexpectedOperand {
23        opcode: Opcode,
24        index: usize,
25        expected: &'static str,
26        actual: BytecodeOperand,
27    },
28    UnexpectedImmediate {
29        opcode: Opcode,
30        index: usize,
31        expected: &'static str,
32        actual: BytecodeImmediate,
33    },
34    VmConstantOutOfRange {
35        value: i32,
36    },
37}
38
39pub(crate) fn encode_function_bytecode<'strings>(
40    builder: &mut BytecodeBuilder<'strings>,
41    function: &mut BytecodeFunction<'strings>,
42) -> Result<Vec<u8>, BytecodeWriteError> {
43    let function_id = builder.begin_function(function.num_params, function.is_vararg);
44    if !function.debug_name.is_empty() {
45        builder.set_debug_function_name(function.debug_name);
46    }
47    builder.set_debug_function_line_defined(function.line_defined as i32);
48    builder.set_function_type_info(function.type_info.clone());
49
50    for ty in function.upvalue_types.iter().copied() {
51        builder.push_upvalue_type_info(ty);
52    }
53    for upvalue in &function.upvalue_names {
54        builder.push_debug_upvalue(*upvalue);
55    }
56
57    for constant in &function.constants {
58        match constant {
59            BytecodeFunctionConstant::Nil => {
60                builder.add_constant_nil();
61            }
62            BytecodeFunctionConstant::Boolean(value) => {
63                builder.add_constant_boolean(*value);
64            }
65            BytecodeFunctionConstant::Number(value) => {
66                builder.add_constant_number(*value);
67            }
68            BytecodeFunctionConstant::Vector(value) => {
69                builder.add_constant_vector(value.x(), value.y(), value.z(), value.w());
70            }
71            BytecodeFunctionConstant::VectorDouble(value) => {
72                builder.add_constant_vector_double(value.x(), value.y(), value.z(), value.w());
73            }
74            BytecodeFunctionConstant::String(value) => {
75                builder.add_constant_string(*value);
76            }
77            BytecodeFunctionConstant::Import(value) => {
78                builder.add_import(BytecodeImportId::from_raw(*value));
79            }
80            BytecodeFunctionConstant::TableIndex(index) => {
81                builder.add_constant_table(&function.table_shapes[*index as usize]);
82            }
83            BytecodeFunctionConstant::Closure(id) => {
84                builder.add_constant_closure(*id);
85            }
86            BytecodeFunctionConstant::Integer(value) => {
87                builder.add_constant_integer(*value);
88            }
89            BytecodeFunctionConstant::ClassIndex(index) => {
90                builder.add_class_shape(function.class_shapes[*index as usize].clone());
91            }
92        }
93    }
94
95    for proto in &function.protos {
96        builder
97            .add_child_function(*proto)
98            .ok_or(BytecodeWriteError::ChildFunctionLimitExceeded { id: *proto })?;
99    }
100
101    let instruction_pcs = FunctionCodeEmitter::new(builder, function).emit()?;
102
103    for local in &function.local_types {
104        let start_pc = remap_pc(&instruction_pcs, builder.get_debug_pc(), local.start_pc);
105        let end_pc = remap_pc(&instruction_pcs, builder.get_debug_pc(), local.end_pc);
106        builder.push_local_type_info(local.ty, local.register, start_pc, end_pc);
107    }
108
109    for local in &function.locals {
110        let start_pc = remap_pc(&instruction_pcs, builder.get_debug_pc(), local.start_pc);
111        let end_pc = remap_pc(&instruction_pcs, builder.get_debug_pc(), local.end_pc);
112        builder.push_debug_local(local.name, local.register, start_pc, end_pc);
113    }
114
115    builder.fold_jumps();
116    builder.expand_jumps();
117    builder.end_function(
118        function.max_stack_size,
119        function.upvalue_count,
120        function.flags,
121        0,
122    );
123    Ok(builder.get_function_data(function_id))
124}
125
126fn remap_pc(instruction_pcs: &[u32], debug_pc: u32, pc: u32) -> u32 {
127    instruction_pcs
128        .get(pc as usize)
129        .copied()
130        .unwrap_or(debug_pc)
131}
132
133struct FunctionCodeEmitter<'function, 'strings> {
134    function: &'function mut BytecodeFunction<'strings>,
135    builder: &'function mut BytecodeBuilder<'strings>,
136    instruction_pcs: Vec<u32>,
137    jumps: Vec<JumpInfo>,
138}
139
140#[derive(Clone, Copy)]
141struct JumpInfo {
142    opcode: Opcode,
143    pc: usize,
144    target: BytecodeBlockId,
145}
146
147impl<'function, 'strings> FunctionCodeEmitter<'function, 'strings> {
148    fn new(
149        builder: &'function mut BytecodeBuilder<'strings>,
150        function: &'function mut BytecodeFunction<'strings>,
151    ) -> Self {
152        let instruction_pcs = vec![u32::MAX; function.instructions.len()];
153        Self {
154            function,
155            builder,
156            instruction_pcs,
157            jumps: Vec::new(),
158        }
159    }
160
161    fn emit(mut self) -> Result<Vec<u32>, BytecodeWriteError> {
162        let schedule = self.reschedule();
163
164        for (index, block_id) in schedule.iter().copied().enumerate() {
165            if let Some(fallthrough) = self.fallthrough(block_id)
166                && fallthrough != self.function.exit_block
167                && !self.function.block(fallthrough).is_dead()
168                && schedule.get(index + 1).copied() != Some(fallthrough)
169            {
170                self.append_fallthrough_jump(block_id, fallthrough);
171            }
172
173            self.function.blocks[block_id.index()].set_start_pc(self.builder.get_debug_pc());
174            let mut instruction_index = 0usize;
175            while instruction_index < self.function.block(block_id).graph_instructions().len() {
176                let instruction_id =
177                    self.function.block(block_id).graph_instructions()[instruction_index];
178                if instruction_id.index() >= self.instruction_pcs.len() {
179                    self.instruction_pcs
180                        .resize(instruction_id.index() + 1, self.builder.get_debug_pc());
181                }
182                self.instruction_pcs[instruction_id.index()] = self.builder.get_debug_pc();
183                self.emit_instruction(instruction_id)?;
184                instruction_index += 1;
185            }
186        }
187
188        for jump_index in 0..self.jumps.len() {
189            let jump = self.jumps[jump_index];
190            self.patch_jump(jump);
191        }
192
193        Ok(self.instruction_pcs)
194    }
195
196    fn reschedule(&self) -> Vec<BytecodeBlockId> {
197        let mut schedule = self
198            .function
199            .blocks()
200            .iter()
201            .enumerate()
202            .filter(|(_, block)| !block.is_dead())
203            .map(|(index, _)| BytecodeBlockId::new(index))
204            .collect::<Vec<_>>();
205        schedule.sort_by_key(|block| self.function.block(*block).sort_key());
206        debug_assert_eq!(schedule.pop(), Some(self.function.exit_block));
207        schedule
208    }
209
210    fn fallthrough(&self, block: BytecodeBlockId) -> Option<BytecodeBlockId> {
211        self.function
212            .block(block)
213            .successors()
214            .iter()
215            .find(|edge| edge.kind == BytecodeEdgeKind::Fallthrough)
216            .map(|edge| edge.target)
217    }
218
219    fn append_fallthrough_jump(&mut self, block: BytecodeBlockId, fallthrough: BytecodeBlockId) {
220        let id = BytecodeInstructionId::new(self.function.instructions.len());
221        self.function
222            .instructions
223            .push(BytecodeInstruction::synthetic_jump(block, fallthrough));
224        self.function.blocks[block.index()].append_graph_instruction(id);
225    }
226
227    fn emit_instruction(&mut self, id: BytecodeInstructionId) -> Result<(), BytecodeWriteError> {
228        let instruction = self.function.graph_instruction(id).clone();
229        match instruction.opcode() {
230            Opcode::Nop | Opcode::Break | Opcode::NativeCall => {
231                self.emit_abc(instruction.opcode(), 0, 0, 0, instruction.line());
232            }
233            Opcode::LoadNil => {
234                self.emit_abc(
235                    Opcode::LoadNil,
236                    self.register(BytecodeOperand::Instruction(id))?,
237                    0,
238                    0,
239                    instruction.line(),
240                );
241            }
242            Opcode::LoadB => {
243                if instruction.operands().len() > 1 {
244                    self.record_jump(instruction.opcode(), self.block_input(&instruction, 1)?);
245                }
246                self.emit_abc(
247                    Opcode::LoadB,
248                    self.register(BytecodeOperand::Instruction(id))?,
249                    u8::from(self.bool_imm(&instruction, 0)?),
250                    0,
251                    instruction.line(),
252                );
253            }
254            Opcode::LoadN => {
255                self.emit_ad(
256                    Opcode::LoadN,
257                    self.register(BytecodeOperand::Instruction(id))?,
258                    self.int_imm(&instruction, 0)? as i16,
259                    instruction.line(),
260                );
261            }
262            Opcode::LoadK => {
263                self.emit_ad(
264                    Opcode::LoadK,
265                    self.register(BytecodeOperand::Instruction(id))?,
266                    self.vm_const(&instruction, 0)? as i16,
267                    instruction.line(),
268                );
269            }
270            Opcode::Move => {
271                self.emit_abc(
272                    Opcode::Move,
273                    self.register(BytecodeOperand::Instruction(id))?,
274                    self.reg_input(&instruction, 0)?,
275                    0,
276                    instruction.line(),
277                );
278            }
279            Opcode::GetGlobal => {
280                self.emit_abc(
281                    Opcode::GetGlobal,
282                    self.register(BytecodeOperand::Instruction(id))?,
283                    0,
284                    self.int_imm(&instruction, 0)? as u8,
285                    instruction.line(),
286                );
287                self.emit_aux(self.vm_const_word(&instruction, 1)?, instruction.line());
288            }
289            Opcode::SetGlobal => {
290                self.emit_abc(
291                    Opcode::SetGlobal,
292                    self.reg_input(&instruction, 0)?,
293                    0,
294                    self.int_imm(&instruction, 1)? as u8,
295                    instruction.line(),
296                );
297                self.emit_aux(self.vm_const_word(&instruction, 2)?, instruction.line());
298            }
299            Opcode::GetUpval => {
300                self.emit_abc(
301                    Opcode::GetUpval,
302                    self.register(BytecodeOperand::Instruction(id))?,
303                    self.upvalue(&instruction, 0)?,
304                    0,
305                    instruction.line(),
306                );
307            }
308            Opcode::SetUpval => {
309                self.emit_abc(
310                    Opcode::SetUpval,
311                    self.reg_input(&instruction, 0)?,
312                    self.upvalue(&instruction, 1)?,
313                    0,
314                    instruction.line(),
315                );
316            }
317            Opcode::CloseUpvals => {
318                self.emit_abc(
319                    Opcode::CloseUpvals,
320                    self.vm_reg(&instruction, 0)?,
321                    0,
322                    0,
323                    instruction.line(),
324                );
325            }
326            Opcode::GetImport => {
327                self.emit_ad(
328                    Opcode::GetImport,
329                    self.register(BytecodeOperand::Instruction(id))?,
330                    self.vm_const(&instruction, 0)? as i16,
331                    instruction.line(),
332                );
333                self.emit_aux(self.import_imm(&instruction, 1)?, instruction.line());
334            }
335            Opcode::GetTable => {
336                self.emit_abc(
337                    Opcode::GetTable,
338                    self.register(BytecodeOperand::Instruction(id))?,
339                    self.reg_input(&instruction, 0)?,
340                    self.reg_input(&instruction, 1)?,
341                    instruction.line(),
342                );
343            }
344            Opcode::SetTable => {
345                self.emit_abc(
346                    Opcode::SetTable,
347                    self.reg_input(&instruction, 0)?,
348                    self.reg_input(&instruction, 1)?,
349                    self.reg_input(&instruction, 2)?,
350                    instruction.line(),
351                );
352            }
353            Opcode::GetUDataKs | Opcode::GetTableKs => {
354                self.emit_abc(
355                    instruction.opcode(),
356                    self.register(BytecodeOperand::Instruction(id))?,
357                    self.reg_input(&instruction, 0)?,
358                    self.int_imm(&instruction, 1)? as u8,
359                    instruction.line(),
360                );
361                self.emit_aux(self.vm_const_word(&instruction, 2)?, instruction.line());
362            }
363            Opcode::SetUDataKs | Opcode::SetTableKs => {
364                self.emit_abc(
365                    instruction.opcode(),
366                    self.reg_input(&instruction, 0)?,
367                    self.reg_input(&instruction, 1)?,
368                    self.int_imm(&instruction, 2)? as u8,
369                    instruction.line(),
370                );
371                self.emit_aux(self.vm_const_word(&instruction, 3)?, instruction.line());
372            }
373            Opcode::GetTableN => {
374                self.emit_abc(
375                    Opcode::GetTableN,
376                    self.register(BytecodeOperand::Instruction(id))?,
377                    self.reg_input(&instruction, 0)?,
378                    (self.int_imm(&instruction, 1)? - 1) as u8,
379                    instruction.line(),
380                );
381            }
382            Opcode::SetTableN => {
383                self.emit_abc(
384                    Opcode::SetTableN,
385                    self.reg_input(&instruction, 0)?,
386                    self.reg_input(&instruction, 1)?,
387                    (self.int_imm(&instruction, 2)? - 1) as u8,
388                    instruction.line(),
389                );
390            }
391            Opcode::NewClosure => {
392                self.emit_ad(
393                    Opcode::NewClosure,
394                    self.register(BytecodeOperand::Instruction(id))?,
395                    self.proto(&instruction, 0)? as i16,
396                    instruction.line(),
397                );
398            }
399            Opcode::NameCall | Opcode::NameCallUData => {
400                self.emit_abc(
401                    instruction.opcode(),
402                    self.register(BytecodeOperand::Instruction(id))?,
403                    self.reg_input(&instruction, 0)?,
404                    self.int_imm(&instruction, 1)? as u8,
405                    instruction.line(),
406                );
407                self.emit_aux(self.vm_const_word(&instruction, 2)?, instruction.line());
408            }
409            Opcode::Call => {
410                self.emit_abc(
411                    Opcode::Call,
412                    self.reg_input(&instruction, 2)?,
413                    (self.int_imm(&instruction, 0)? + 1) as u8,
414                    (self.int_imm(&instruction, 1)? + 1) as u8,
415                    instruction.line(),
416                );
417            }
418            Opcode::CallFb => {
419                self.emit_abc(
420                    Opcode::CallFb,
421                    self.reg_input(&instruction, 3)?,
422                    (self.int_imm(&instruction, 0)? + 1) as u8,
423                    (self.int_imm(&instruction, 1)? + 1) as u8,
424                    instruction.line(),
425                );
426                self.emit_aux(self.int_imm(&instruction, 2)? as u32, instruction.line());
427            }
428            Opcode::Return => {
429                self.emit_abc(
430                    Opcode::Return,
431                    self.reg_input(&instruction, 1)?,
432                    (self.int_imm(&instruction, 0)? + 1) as u8,
433                    0,
434                    instruction.line(),
435                );
436            }
437            Opcode::Jump | Opcode::JumpBack => {
438                self.record_jump(instruction.opcode(), self.block_input(&instruction, 0)?);
439                self.emit_ad(instruction.opcode(), 0, 0, instruction.line());
440            }
441            Opcode::JumpIf | Opcode::JumpIfNot => {
442                self.record_jump(instruction.opcode(), self.block_input(&instruction, 1)?);
443                self.emit_ad(
444                    instruction.opcode(),
445                    self.reg_input(&instruction, 0)?,
446                    0,
447                    instruction.line(),
448                );
449            }
450            Opcode::JumpIfEq
451            | Opcode::JumpIfLe
452            | Opcode::JumpIfLt
453            | Opcode::JumpIfNotEq
454            | Opcode::JumpIfNotLe
455            | Opcode::JumpIfNotLt => {
456                self.record_jump(instruction.opcode(), self.block_input(&instruction, 2)?);
457                self.emit_ad(
458                    instruction.opcode(),
459                    self.reg_input(&instruction, 0)?,
460                    0,
461                    instruction.line(),
462                );
463                self.emit_aux(
464                    u32::from(self.reg_input(&instruction, 1)?),
465                    instruction.line(),
466                );
467            }
468            Opcode::Add
469            | Opcode::Sub
470            | Opcode::Mul
471            | Opcode::Div
472            | Opcode::Mod
473            | Opcode::Pow
474            | Opcode::And
475            | Opcode::Or
476            | Opcode::IDiv => {
477                self.emit_abc(
478                    instruction.opcode(),
479                    self.register(BytecodeOperand::Instruction(id))?,
480                    self.reg_input(&instruction, 0)?,
481                    self.reg_input(&instruction, 1)?,
482                    instruction.line(),
483                );
484            }
485            Opcode::AddK
486            | Opcode::SubK
487            | Opcode::MulK
488            | Opcode::DivK
489            | Opcode::ModK
490            | Opcode::PowK
491            | Opcode::AndK
492            | Opcode::OrK
493            | Opcode::IDivK => {
494                self.emit_abc(
495                    instruction.opcode(),
496                    self.register(BytecodeOperand::Instruction(id))?,
497                    self.reg_input(&instruction, 0)?,
498                    self.vm_const(&instruction, 1)? as u8,
499                    instruction.line(),
500                );
501            }
502            Opcode::Concat => {
503                self.emit_abc(
504                    Opcode::Concat,
505                    self.register(BytecodeOperand::Instruction(id))?,
506                    self.reg_input(&instruction, 0)?,
507                    self.reg_input(&instruction, instruction.operands().len() - 1)?,
508                    instruction.line(),
509                );
510            }
511            Opcode::Not | Opcode::Minus | Opcode::Length => {
512                self.emit_abc(
513                    instruction.opcode(),
514                    self.register(BytecodeOperand::Instruction(id))?,
515                    self.reg_input(&instruction, 0)?,
516                    0,
517                    instruction.line(),
518                );
519            }
520            Opcode::NewTable => {
521                self.emit_abc(
522                    Opcode::NewTable,
523                    self.register(BytecodeOperand::Instruction(id))?,
524                    self.int_imm(&instruction, 0)? as u8,
525                    0,
526                    instruction.line(),
527                );
528                self.emit_aux(self.int_imm(&instruction, 1)? as u32, instruction.line());
529            }
530            Opcode::DupTable => {
531                self.emit_ad(
532                    Opcode::DupTable,
533                    self.register(BytecodeOperand::Instruction(id))?,
534                    self.vm_const(&instruction, 0)? as i16,
535                    instruction.line(),
536                );
537            }
538            Opcode::SetList => {
539                self.emit_abc(
540                    Opcode::SetList,
541                    self.reg_input(&instruction, 2)?,
542                    self.reg_input(&instruction, 3)?,
543                    (self.int_imm(&instruction, 1)? + 1) as u8,
544                    instruction.line(),
545                );
546                self.emit_aux(self.int_imm(&instruction, 0)? as u32, instruction.line());
547            }
548            Opcode::ForNPrep | Opcode::ForNLoop => {
549                self.record_jump(instruction.opcode(), self.block_input(&instruction, 3)?);
550                self.emit_ad(
551                    instruction.opcode(),
552                    self.reg_input(&instruction, 0)?,
553                    0,
554                    instruction.line(),
555                );
556            }
557            Opcode::ForGPrep | Opcode::ForGPrepNext | Opcode::ForGPrepInext => {
558                self.record_jump(instruction.opcode(), self.block_input(&instruction, 3)?);
559                self.emit_ad(
560                    instruction.opcode(),
561                    self.reg_input(&instruction, 0)?,
562                    0,
563                    instruction.line(),
564                );
565            }
566            Opcode::ForGLoop => {
567                self.record_jump(Opcode::ForGLoop, self.block_input(&instruction, 5)?);
568                self.emit_ad(
569                    Opcode::ForGLoop,
570                    self.reg_input(&instruction, 0)?,
571                    0,
572                    instruction.line(),
573                );
574                self.emit_aux(
575                    (u32::from(self.bool_imm(&instruction, 3)?) << 31)
576                        | self.int_imm(&instruction, 4)? as u32,
577                    instruction.line(),
578                );
579            }
580            Opcode::FastCall => {
581                self.emit_abc(
582                    Opcode::FastCall,
583                    self.int_imm(&instruction, 0)? as u8,
584                    0,
585                    self.int_imm(&instruction, 1)? as u8,
586                    instruction.line(),
587                );
588            }
589            Opcode::FastCall1 => {
590                self.emit_abc(
591                    Opcode::FastCall1,
592                    self.int_imm(&instruction, 0)? as u8,
593                    self.reg_input(&instruction, 1)?,
594                    self.int_imm(&instruction, 2)? as u8,
595                    instruction.line(),
596                );
597            }
598            Opcode::FastCall2 => {
599                self.emit_abc(
600                    Opcode::FastCall2,
601                    self.int_imm(&instruction, 0)? as u8,
602                    self.reg_input(&instruction, 1)?,
603                    self.int_imm(&instruction, 3)? as u8,
604                    instruction.line(),
605                );
606                self.emit_aux(
607                    u32::from(self.reg_input(&instruction, 2)?),
608                    instruction.line(),
609                );
610            }
611            Opcode::FastCall2K => {
612                self.emit_abc(
613                    Opcode::FastCall2K,
614                    self.int_imm(&instruction, 0)? as u8,
615                    self.reg_input(&instruction, 1)?,
616                    self.int_imm(&instruction, 3)? as u8,
617                    instruction.line(),
618                );
619                self.emit_aux(self.vm_const_word(&instruction, 2)?, instruction.line());
620            }
621            Opcode::FastCall3 => {
622                self.emit_abc(
623                    Opcode::FastCall3,
624                    self.int_imm(&instruction, 0)? as u8,
625                    self.reg_input(&instruction, 1)?,
626                    self.int_imm(&instruction, 4)? as u8,
627                    instruction.line(),
628                );
629                self.emit_aux(
630                    u32::from(self.reg_input(&instruction, 2)?)
631                        | (u32::from(self.reg_input(&instruction, 3)?) << 8),
632                    instruction.line(),
633                );
634            }
635            Opcode::GetVarargs => {
636                self.emit_abc(
637                    Opcode::GetVarargs,
638                    self.vm_reg(&instruction, 0)?,
639                    (self.int_imm(&instruction, 1)? + 1) as u8,
640                    0,
641                    instruction.line(),
642                );
643            }
644            Opcode::DupClosure => {
645                self.emit_ad(
646                    Opcode::DupClosure,
647                    self.register(BytecodeOperand::Instruction(id))?,
648                    self.vm_const(&instruction, 0)? as i16,
649                    instruction.line(),
650                );
651            }
652            Opcode::PrepVarargs => {
653                self.emit_ad(
654                    Opcode::PrepVarargs,
655                    self.int_imm(&instruction, 0)? as u8,
656                    0,
657                    instruction.line(),
658                );
659            }
660            Opcode::LoadKx => {
661                self.emit_ad(
662                    Opcode::LoadKx,
663                    self.register(BytecodeOperand::Instruction(id))?,
664                    0,
665                    instruction.line(),
666                );
667                self.emit_aux(self.vm_const_word(&instruction, 0)?, instruction.line());
668            }
669            Opcode::JumpX => {
670                self.record_jump(Opcode::JumpX, self.block_input(&instruction, 0)?);
671                self.emit_e(Opcode::JumpX, 0, instruction.line());
672            }
673            Opcode::Coverage => {
674                self.emit_e(
675                    Opcode::Coverage,
676                    self.int_imm(&instruction, 0)?,
677                    instruction.line(),
678                );
679            }
680            Opcode::Capture => {
681                let capture_type = self.int_imm(&instruction, 0)? as u8;
682                let captured = if capture_type <= 1 {
683                    self.reg_input(&instruction, 1)?
684                } else {
685                    self.upvalue(&instruction, 1)?
686                };
687                self.emit_abc(
688                    Opcode::Capture,
689                    capture_type,
690                    captured,
691                    self.int_imm(&instruction, 2)? as u8,
692                    instruction.line(),
693                );
694            }
695            Opcode::SubRK | Opcode::DivRK => {
696                self.emit_abc(
697                    instruction.opcode(),
698                    self.register(BytecodeOperand::Instruction(id))?,
699                    self.vm_const(&instruction, 0)? as u8,
700                    self.reg_input(&instruction, 1)?,
701                    instruction.line(),
702                );
703            }
704            Opcode::JumpXEqKNil => {
705                self.record_jump(Opcode::JumpXEqKNil, self.block_input(&instruction, 2)?);
706                self.emit_ad(
707                    Opcode::JumpXEqKNil,
708                    self.reg_input(&instruction, 0)?,
709                    0,
710                    instruction.line(),
711                );
712                self.emit_aux(
713                    u32::from(self.bool_imm(&instruction, 1)?) << 31,
714                    instruction.line(),
715                );
716            }
717            Opcode::JumpXEqKB => {
718                self.record_jump(Opcode::JumpXEqKB, self.block_input(&instruction, 2)?);
719                self.emit_ad(
720                    Opcode::JumpXEqKB,
721                    self.reg_input(&instruction, 0)?,
722                    0,
723                    instruction.line(),
724                );
725                self.emit_aux(
726                    (u32::from(self.bool_imm(&instruction, 1)?) << 31)
727                        | u32::from(self.bool_imm(&instruction, 3)?),
728                    instruction.line(),
729                );
730            }
731            Opcode::JumpXEqKN | Opcode::JumpXEqKS => {
732                self.record_jump(instruction.opcode(), self.block_input(&instruction, 2)?);
733                self.emit_ad(
734                    instruction.opcode(),
735                    self.reg_input(&instruction, 0)?,
736                    0,
737                    instruction.line(),
738                );
739                self.emit_aux(
740                    (u32::from(self.bool_imm(&instruction, 1)?) << 31)
741                        | self.vm_const_word(&instruction, 3)?,
742                    instruction.line(),
743                );
744            }
745            Opcode::CmpProto => {
746                self.record_jump(Opcode::CmpProto, self.block_input(&instruction, 2)?);
747                self.emit_ad(
748                    Opcode::CmpProto,
749                    self.reg_input(&instruction, 0)?,
750                    0,
751                    instruction.line(),
752                );
753                self.emit_aux(self.int_imm(&instruction, 1)? as u32, instruction.line());
754            }
755            Opcode::NewClassMember => {
756                self.emit_abc(
757                    Opcode::NewClassMember,
758                    self.reg_input(&instruction, 0)?,
759                    0,
760                    self.reg_input(&instruction, 1)?,
761                    instruction.line(),
762                );
763                self.emit_aux(self.vm_const_word(&instruction, 2)?, instruction.line());
764            }
765            Opcode::NewClass => {
766                self.emit_abc(
767                    Opcode::NewClass,
768                    self.register(BytecodeOperand::Instruction(id))?,
769                    self.reg_input(&instruction, 0)?,
770                    0,
771                    instruction.line(),
772                );
773                self.emit_aux(self.vm_const_word(&instruction, 1)?, instruction.line());
774            }
775        }
776
777        Ok(())
778    }
779
780    fn register(&self, operand: BytecodeOperand) -> Result<Register, BytecodeWriteError> {
781        match operand {
782            BytecodeOperand::Phi(id) => {
783                if let Some(register) = self.function.registers.get(&BytecodeOperand::Phi(id)) {
784                    return Ok(*register);
785                }
786                let operands = self.function.phi(id).operands();
787                debug_assert!(!operands.is_empty());
788                let register = self.register(operands[0])?;
789                Ok(register)
790            }
791            BytecodeOperand::Projection(id) => {
792                let projection = self.function.projection(id);
793                Ok(self.register(projection.source)? + projection.index as u8)
794            }
795            BytecodeOperand::VmRegister(register) => Ok(register),
796            _ => self
797                .function
798                .registers
799                .get(&operand)
800                .copied()
801                .ok_or(BytecodeWriteError::MissingResultRegister { operand }),
802        }
803    }
804
805    fn operand(
806        &self,
807        instruction: &BytecodeInstruction,
808        index: usize,
809    ) -> Result<BytecodeOperand, BytecodeWriteError> {
810        instruction
811            .operands()
812            .get(index)
813            .copied()
814            .ok_or(BytecodeWriteError::MissingOperand {
815                opcode: instruction.opcode(),
816                index,
817            })
818    }
819
820    fn int_imm(
821        &self,
822        instruction: &BytecodeInstruction,
823        index: usize,
824    ) -> Result<i32, BytecodeWriteError> {
825        let operand = self.operand(instruction, index)?;
826        let BytecodeOperand::Immediate(id) = operand else {
827            return Err(BytecodeWriteError::UnexpectedOperand {
828                opcode: instruction.opcode(),
829                index,
830                expected: "immediate",
831                actual: operand,
832            });
833        };
834        let BytecodeImmediate::Int(value) = *self.function.immediate(id) else {
835            return Err(BytecodeWriteError::UnexpectedImmediate {
836                opcode: instruction.opcode(),
837                index,
838                expected: "integer",
839                actual: *self.function.immediate(id),
840            });
841        };
842        Ok(value)
843    }
844
845    fn bool_imm(
846        &self,
847        instruction: &BytecodeInstruction,
848        index: usize,
849    ) -> Result<bool, BytecodeWriteError> {
850        let operand = self.operand(instruction, index)?;
851        let BytecodeOperand::Immediate(id) = operand else {
852            return Err(BytecodeWriteError::UnexpectedOperand {
853                opcode: instruction.opcode(),
854                index,
855                expected: "immediate",
856                actual: operand,
857            });
858        };
859        let BytecodeImmediate::Boolean(value) = *self.function.immediate(id) else {
860            return Err(BytecodeWriteError::UnexpectedImmediate {
861                opcode: instruction.opcode(),
862                index,
863                expected: "boolean",
864                actual: *self.function.immediate(id),
865            });
866        };
867        Ok(value)
868    }
869
870    fn import_imm(
871        &self,
872        instruction: &BytecodeInstruction,
873        index: usize,
874    ) -> Result<u32, BytecodeWriteError> {
875        let operand = self.operand(instruction, index)?;
876        let BytecodeOperand::Immediate(id) = operand else {
877            return Err(BytecodeWriteError::UnexpectedOperand {
878                opcode: instruction.opcode(),
879                index,
880                expected: "immediate",
881                actual: operand,
882            });
883        };
884        let BytecodeImmediate::Import(value) = *self.function.immediate(id) else {
885            return Err(BytecodeWriteError::UnexpectedImmediate {
886                opcode: instruction.opcode(),
887                index,
888                expected: "import",
889                actual: *self.function.immediate(id),
890            });
891        };
892        Ok(value)
893    }
894
895    fn reg_input(
896        &self,
897        instruction: &BytecodeInstruction,
898        index: usize,
899    ) -> Result<Register, BytecodeWriteError> {
900        self.register(self.operand(instruction, index)?)
901    }
902
903    fn vm_reg(
904        &self,
905        instruction: &BytecodeInstruction,
906        index: usize,
907    ) -> Result<Register, BytecodeWriteError> {
908        let operand = self.operand(instruction, index)?;
909        let BytecodeOperand::VmRegister(register) = operand else {
910            return Err(BytecodeWriteError::UnexpectedOperand {
911                opcode: instruction.opcode(),
912                index,
913                expected: "VM register",
914                actual: operand,
915            });
916        };
917        Ok(register)
918    }
919
920    fn vm_const(
921        &self,
922        instruction: &BytecodeInstruction,
923        index: usize,
924    ) -> Result<i32, BytecodeWriteError> {
925        let operand = self.operand(instruction, index)?;
926        let BytecodeOperand::VmConstant(value) = operand else {
927            return Err(BytecodeWriteError::UnexpectedOperand {
928                opcode: instruction.opcode(),
929                index,
930                expected: "VM constant",
931                actual: operand,
932            });
933        };
934        Ok(value)
935    }
936
937    fn vm_const_word(
938        &self,
939        instruction: &BytecodeInstruction,
940        index: usize,
941    ) -> Result<u32, BytecodeWriteError> {
942        let value = self.vm_const(instruction, index)?;
943        u32::try_from(value).map_err(|_| BytecodeWriteError::VmConstantOutOfRange { value })
944    }
945
946    fn upvalue(
947        &self,
948        instruction: &BytecodeInstruction,
949        index: usize,
950    ) -> Result<u8, BytecodeWriteError> {
951        let operand = self.operand(instruction, index)?;
952        let BytecodeOperand::VmUpvalue(value) = operand else {
953            return Err(BytecodeWriteError::UnexpectedOperand {
954                opcode: instruction.opcode(),
955                index,
956                expected: "VM upvalue",
957                actual: operand,
958            });
959        };
960        Ok(value as u8)
961    }
962
963    fn proto(
964        &self,
965        instruction: &BytecodeInstruction,
966        index: usize,
967    ) -> Result<u16, BytecodeWriteError> {
968        let operand = self.operand(instruction, index)?;
969        let BytecodeOperand::VmProto(value) = operand else {
970            return Err(BytecodeWriteError::UnexpectedOperand {
971                opcode: instruction.opcode(),
972                index,
973                expected: "VM proto",
974                actual: operand,
975            });
976        };
977        Ok(value as u16)
978    }
979
980    fn block_input(
981        &self,
982        instruction: &BytecodeInstruction,
983        index: usize,
984    ) -> Result<BytecodeBlockId, BytecodeWriteError> {
985        let operand = self.operand(instruction, index)?;
986        let BytecodeOperand::Block(block) = operand else {
987            return Err(BytecodeWriteError::UnexpectedOperand {
988                opcode: instruction.opcode(),
989                index,
990                expected: "block",
991                actual: operand,
992            });
993        };
994        Ok(block)
995    }
996
997    fn record_jump(&mut self, opcode: Opcode, target: BytecodeBlockId) {
998        self.jumps.push(JumpInfo {
999            opcode,
1000            pc: self.builder.get_instruction_count(),
1001            target,
1002        });
1003    }
1004
1005    fn patch_jump(&mut self, jump: JumpInfo) {
1006        let target_pc = self.function.block(jump.target).start_pc() as usize;
1007
1008        if jump.opcode.is_jump_d() {
1009            debug_assert!(self.builder.patch_jump_d(jump.pc, target_pc));
1010        } else if jump.opcode.is_skip_c() {
1011            debug_assert!(self.builder.patch_skip_c(jump.pc, target_pc));
1012        } else if jump.opcode == Opcode::JumpX {
1013            debug_assert!(self.builder.patch_jump_e(jump.pc, target_pc));
1014        }
1015    }
1016
1017    fn emit_abc(&mut self, opcode: Opcode, a: u8, b: u8, c: u8, line: u32) {
1018        self.builder.set_debug_line(line as usize);
1019        self.builder.emit_abc(opcode, a, b, c);
1020    }
1021
1022    fn emit_ad(&mut self, opcode: Opcode, a: u8, d: i16, line: u32) {
1023        self.builder.set_debug_line(line as usize);
1024        self.builder.emit_ad(opcode, a, d);
1025    }
1026
1027    fn emit_e(&mut self, opcode: Opcode, e: i32, line: u32) {
1028        self.builder.set_debug_line(line as usize);
1029        self.builder.emit_e(opcode, e);
1030    }
1031
1032    fn emit_aux(&mut self, word: u32, line: u32) {
1033        self.builder.set_debug_line(line as usize);
1034        self.builder.emit_aux(word);
1035    }
1036}