luaur-code-gen 0.1.0

Native (A64/X64) code generation for Luau (Rust).
Documentation
use crate::enums::ir_cmd::IrCmd;
use crate::enums::ir_value_kind::IrValueKind;
use crate::enums::size_x_64::SizeX64;
use crate::functions::qword_reg::qword_reg;
use crate::macros::codegen_assert::CODEGEN_ASSERT;
use crate::records::ir_inst::IrInst;
use crate::records::ir_reg_alloc_x_64::IrRegAllocX64;
use crate::records::ir_spill_x_64::IrSpillX64;
use crate::records::operand_x_64::OperandX64;
use crate::records::register_x_64::RegisterX64;

const S_TEMPORARY_SLOT: i32 = 64;
const S_SPILL_AREA: i32 = 72;

fn r_state() -> RegisterX64 {
    RegisterX64 {
        bits: (15u8 << RegisterX64::INDEX_SHIFT) | SizeX64::qword as u8,
    }
}

fn mem(size: SizeX64, base: RegisterX64, disp: i32) -> OperandX64 {
    OperandX64::mem(size, RegisterX64::noreg, 1, base, disp)
}

fn temp_qword() -> OperandX64 {
    mem(SizeX64::qword, RegisterX64::rsp, S_TEMPORARY_SLOT)
}

impl IrRegAllocX64 {
    pub fn restore(&mut self, inst: &mut IrInst, into_original_location: bool) {
        let inst_idx = unsafe { (&*self.function).get_inst_index(inst) };

        let mut i = 0;
        while i < self.spills.len() {
            if self.spills[i].inst_idx == inst_idx {
                let original_loc = self.spills[i].original_loc;
                let reg = if into_original_location {
                    self.take_reg(original_loc, inst_idx)
                } else {
                    self.alloc_reg(original_loc.size(), inst_idx)
                };

                let restore_location =
                    unsafe { (&*self.function).find_restore_location_ir_inst_bool(inst, false) };
                let emergency_temp = if reg.size() == SizeX64::xmmword {
                    RegisterX64::r11
                } else {
                    qword_reg(reg)
                };
                let spill = self.spills[i].clone();

                let mut restore_addr;

                if spill.stack_slot != IrSpillX64::kNoStackSlot {
                    if self.is_extra_spill_slot(spill.stack_slot as u32) {
                        let extra_offset =
                            self.get_extra_spill_address_offset(spill.stack_slot as u32);

                        let build = unsafe { &mut *self.build };
                        if reg.size() == SizeX64::xmmword {
                            build.mov(temp_qword(), OperandX64::reg(emergency_temp));
                        }

                        build.mov(
                            OperandX64::reg(emergency_temp),
                            mem(
                                SizeX64::qword,
                                r_state(),
                                core::mem::offset_of!(
                                    luaur_vm::records::lua_state::lua_State,
                                    global
                                ) as i32,
                            ),
                        );
                        build.lea_operand_x_64_operand_x_64(
                            OperandX64::reg(emergency_temp),
                            mem(
                                SizeX64::none,
                                emergency_temp,
                                core::mem::offset_of!(
                                    luaur_vm::records::global_state::global_State,
                                    ecbdata
                                ) as i32
                                    + extra_offset,
                            ),
                        );

                        restore_addr = mem(reg.size(), emergency_temp, 0);
                    } else {
                        restore_addr = mem(
                            SizeX64::none,
                            RegisterX64::rsp,
                            S_SPILL_AREA + spill.stack_slot as i32 * 4,
                        );
                        restore_addr.memSize = reg.size();
                    }

                    if spill.value_kind == IrValueKind::Double
                        || spill.value_kind == IrValueKind::Int64
                    {
                        restore_addr.memSize = SizeX64::qword;
                    } else if spill.value_kind == IrValueKind::Float {
                        restore_addr.memSize = SizeX64::dword;
                    }

                    let end = spill.stack_slot as u32
                        + crate::enums::ir_value_kind::K_VALUE_DWORD_SIZE
                            [spill.value_kind as usize];

                    for pos in spill.stack_slot as u32..end {
                        self.used_spill_slot_halfs[(pos / 64) as usize] &= !(1u64 << (pos % 64));
                    }
                } else {
                    restore_addr = self.get_restore_address(inst, restore_location);
                }

                let build = unsafe { &mut *self.build };
                match spill.value_kind {
                    IrValueKind::Tvalue => build.vmovups(OperandX64::reg(reg), restore_addr),
                    IrValueKind::Double => {
                        build.vmovsd_operand_x_64_operand_x_64(OperandX64::reg(reg), restore_addr)
                    }
                    IrValueKind::Int if restore_location.kind == IrValueKind::Double => {
                        if restore_location.conversion_cmd == IrCmd::INT_TO_NUM {
                            build.vcvttsd2si(OperandX64::reg(reg), restore_addr);
                        } else if restore_location.conversion_cmd == IrCmd::UINT_TO_NUM {
                            build.vcvttsd2si(OperandX64::reg(qword_reg(reg)), restore_addr);
                        } else {
                            CODEGEN_ASSERT!(
                                false,
                                "re-materialization not supported for this conversion command"
                            );
                        }
                    }
                    IrValueKind::Tag
                    | IrValueKind::Int
                    | IrValueKind::Int64
                    | IrValueKind::Pointer => {
                        build.mov(OperandX64::reg(reg), restore_addr);
                    }
                    IrValueKind::Float => {
                        build.vmovss_operand_x_64_operand_x_64(OperandX64::reg(reg), restore_addr)
                    }
                    _ => CODEGEN_ASSERT!(false, "value kind not supported for restore"),
                }

                if spill.stack_slot != IrSpillX64::kNoStackSlot
                    && self.is_extra_spill_slot(spill.stack_slot as u32)
                {
                    if reg.size() == SizeX64::xmmword {
                        let build = unsafe { &mut *self.build };
                        build.mov(OperandX64::reg(emergency_temp), temp_qword());
                    }
                }

                inst.reg_x64 = reg;
                inst.spilled = false;
                inst.needs_reload = false;

                self.spills[i] = self.spills[self.spills.len() - 1].clone();
                self.spills.pop();
                return;
            }
            i += 1;
        }
    }
}