luaur-bytecode 0.1.3

Luau bytecode format and builder (Rust).
Documentation
use crate::enums::bc_block_edge_kind::BcBlockEdgeKind;
use crate::enums::bc_op_kind::BcOpKind;
use crate::records::bc_block::BcBlock;
use crate::records::bc_block_edge::BcBlockEdge;
use crate::records::bc_op::BcOp;
use crate::records::bc_ref::BcRef;
use crate::records::call_inliner::CallInliner;
use alloc::vec::Vec;
use luaur_common::macros::luau_assert::LUAU_ASSERT;

impl<'a> CallInliner<'a> {
    pub fn split_block_on_op(
        &mut self,
        split_op: BcOp,
    ) -> (BcRef<'a, BcBlock>, BcRef<'a, BcBlock>) {
        let prev_block_op = self.caller.instructions[split_op.index as usize].block;
        LUAU_ASSERT!(prev_block_op.kind == BcOpKind::Block);
        LUAU_ASSERT!(self.caller.blocks[prev_block_op.index as usize]
            .ops
            .iter()
            .any(|op| *op == split_op));

        let insn_block_op = self.caller.add_block();
        let prev_block_sortkey = self.caller.blocks[prev_block_op.index as usize].sortkey;
        let prev_block_chainkey = self.caller.blocks[prev_block_op.index as usize].chainkey;
        self.caller.blocks[insn_block_op.index as usize].sortkey = prev_block_sortkey;
        self.caller.blocks[insn_block_op.index as usize].chainkey = prev_block_chainkey + 1;

        let next_block_op = self.caller.add_block();
        self.caller.blocks[next_block_op.index as usize].sortkey =
            self.caller.blocks[insn_block_op.index as usize].sortkey;
        self.caller.blocks[next_block_op.index as usize].chainkey =
            self.caller.blocks[insn_block_op.index as usize].chainkey + 1;

        while *self.caller.blocks[prev_block_op.index as usize]
            .ops
            .back()
            .unwrap()
            != split_op
        {
            let back_op = *self.caller.blocks[prev_block_op.index as usize]
                .ops
                .back()
                .unwrap();
            self.caller.blocks[prev_block_op.index as usize]
                .ops
                .pop_back();
            self.caller.blocks[next_block_op.index as usize]
                .ops
                .push_front(back_op);
            self.caller.instructions[back_op.index as usize].block = next_block_op;
        }

        let prev_successors = self.caller.blocks[prev_block_op.index as usize]
            .successors
            .clone();
        for e in &prev_successors {
            let succ = e.target;
            for pred in self.caller.blocks[succ.index as usize]
                .predecessors
                .iter_mut()
            {
                if pred.target == prev_block_op {
                    pred.target = next_block_op;
                }
            }
        }
        self.caller.blocks[next_block_op.index as usize].successors = prev_successors;
        self.caller.blocks[prev_block_op.index as usize]
            .successors
            .clear();

        self.caller.blocks[prev_block_op.index as usize]
            .successors
            .push_back(BcBlockEdge {
                kind: BcBlockEdgeKind::Fallthrough,
                target: insn_block_op,
            });
        self.caller.blocks[insn_block_op.index as usize]
            .predecessors
            .push_back(BcBlockEdge {
                kind: BcBlockEdgeKind::Fallthrough,
                target: prev_block_op,
            });
        self.caller.blocks[insn_block_op.index as usize]
            .successors
            .push_back(BcBlockEdge {
                kind: BcBlockEdgeKind::Fallthrough,
                target: next_block_op,
            });
        self.caller.blocks[next_block_op.index as usize]
            .predecessors
            .push_back(BcBlockEdge {
                kind: BcBlockEdgeKind::Fallthrough,
                target: insn_block_op,
            });

        LUAU_ASSERT!(
            *self.caller.blocks[prev_block_op.index as usize]
                .ops
                .back()
                .unwrap()
                == split_op
        );
        self.caller.blocks[prev_block_op.index as usize]
            .ops
            .pop_back();
        self.caller.blocks[insn_block_op.index as usize]
            .ops
            .push_back(split_op);
        self.caller.instructions[split_op.index as usize].block = insn_block_op;

        let vec_ptr: *const Vec<BcBlock> = &self.caller.blocks;
        let vec_ref: &'a Vec<BcBlock> = unsafe { &*vec_ptr };
        (
            BcRef {
                vec: vec_ref,
                op: prev_block_op,
            },
            BcRef {
                vec: vec_ref,
                op: next_block_op,
            },
        )
    }
}