Skip to main content

luaur_code_gen/functions/
build_bytecode_blocks.rs

1use crate::functions::get_jump_target::get_jump_target;
2use crate::functions::get_op_length::get_op_length;
3use crate::functions::is_fast_call::is_fast_call;
4use crate::records::bytecode_block::BytecodeBlock;
5use crate::records::ir_function::IrFunction;
6use luaur_common::enums::luau_opcode::LuauOpcode;
7use luaur_common::macros::luau_insn_op::LUAU_INSN_OP;
8
9macro_rules! CODEGEN_ASSERT {
10    ($expr:expr) => {
11        assert!($expr);
12    };
13}
14
15pub fn build_bytecode_blocks(function: &mut IrFunction, jump_targets: &[u8]) {
16    CODEGEN_ASSERT!(!function.proto.is_null());
17
18    let proto = unsafe { &*function.proto };
19    let bc_blocks = &mut function.bc_blocks;
20
21    // Using the same jump targets, create VM bytecode basic blocks
22    bc_blocks.push(BytecodeBlock {
23        startpc: 0,
24        finishpc: -1,
25    });
26
27    let mut previ = 0;
28    let mut i = 0;
29
30    while i < proto.sizecode {
31        let pc_val = unsafe { *proto.code.add(i as usize) };
32        let op_val = LUAU_INSN_OP(pc_val) as u8;
33        let op: LuauOpcode = unsafe { core::mem::transmute(op_val) };
34
35        let nexti = i + get_op_length(op);
36
37        // If instruction is a jump target, begin new block starting from it
38        if i != 0 && jump_targets[i as usize] != 0 {
39            if let Some(last) = bc_blocks.last_mut() {
40                last.finishpc = previ;
41            }
42            bc_blocks.push(BytecodeBlock {
43                startpc: i,
44                finishpc: -1,
45            });
46        }
47
48        let target = get_jump_target(pc_val, i as u32);
49
50        // Implicit fallthrough terminate the block and might start a new one
51        if target >= 0 && !is_fast_call(op) {
52            if let Some(last) = bc_blocks.last_mut() {
53                last.finishpc = i;
54            }
55
56            // Start a new block if there was no explicit jump for the fallthrough
57            if jump_targets[nexti as usize] == 0 {
58                bc_blocks.push(BytecodeBlock {
59                    startpc: nexti,
60                    finishpc: -1,
61                });
62            }
63        }
64        // Returns just terminate the block
65        else if op == LuauOpcode::LOP_RETURN {
66            if let Some(last) = bc_blocks.last_mut() {
67                last.finishpc = i;
68            }
69        }
70
71        previ = i;
72        i = nexti;
73        CODEGEN_ASSERT!(i <= proto.sizecode);
74    }
75}