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}