celox-backend-x86 0.8.2

Celox x86-64 machine-code backend
Documentation
use super::*;
use crate::native::{emit, jit_mem::JitCode, mir_legalize, regalloc};

fn compile(mut function: MFunction) -> (JitCode, usize) {
    mir_legalize::legalize(&mut function);
    let allocation = regalloc::run_regalloc(&mut function).unwrap();
    let emitted = emit::emit(
        &function,
        &allocation.assignment,
        allocation.spill_frame_size,
    )
    .unwrap();
    (
        JitCode::new(&emitted.code).unwrap(),
        emitted.required_state_size.max(768) as usize,
    )
}

#[test]
fn repeated_diamonds_preserve_loop_phis_arm_effects_and_edge_values() {
    for variant in 0..5 {
        let mut original = bitmap_worklist::tests::shared_condition_fixture(32);
        if variant == 1 {
            bitmap_worklist::run(&mut original);
        }
        if variant == 2 {
            for (body, offset) in [(BlockId(8), 64), (BlockId(9), 72)] {
                let block = original
                    .blocks
                    .iter_mut()
                    .find(|block| block.id == body)
                    .unwrap();
                block.insts.insert(
                    0,
                    MInst::Store {
                        base: BaseReg::SimState,
                        offset,
                        src: VReg(21),
                        size: OpSize::S64,
                    },
                );
            }
        }
        if variant == 3 {
            let value = original.vregs.alloc();
            original.spill_descs.push(SpillDesc::transient());
            let block = original
                .blocks
                .iter_mut()
                .find(|block| block.id == BlockId(8))
                .unwrap();
            block.phis.push(PhiNode {
                dst: value,
                sources: vec![(BlockId(7), VReg(21))],
            });
            block.insts.insert(
                0,
                MInst::Store {
                    base: BaseReg::SimState,
                    offset: 64,
                    src: value,
                    size: OpSize::S64,
                },
            );
            let block = original
                .blocks
                .iter_mut()
                .find(|block| block.id == BlockId(5))
                .unwrap();
            block.phis[0].sources[1].1 = VReg(21);
        }
        if variant == 4 {
            let block = original
                .blocks
                .iter_mut()
                .find(|block| block.id == BlockId(7))
                .unwrap();
            if let MInst::Branch {
                true_bb, false_bb, ..
            } = &mut block.insts[0]
            {
                std::mem::swap(true_bb, false_bb);
            }
        }
        original.verify();
        let mut optimized = original.clone();
        run(&mut optimized);
        optimized.verify();
        assert_eq!(
            optimized.blocks.len() + 3,
            original.blocks.len(),
            "variant={variant}"
        );
        let (before, before_size) = compile(original);
        let (after, after_size) = compile(optimized);
        for input in [
            0u64,
            1,
            0x5555_5555_aaaa_aaaa,
            0x0123_4567_89ab_cdef,
            u64::MAX,
        ]
        .into_iter()
        .chain((0..64).map(|bit| 1u64 << bit))
        {
            for guard in [0u64, 1, 2, 3, 1 << 32, u64::MAX] {
                for origin in [0u64, 1, 15, 31, 32, 63, u64::MAX] {
                    let mut left = vec![0u8; before_size.max(after_size)];
                    for (offset, value) in [
                        (0, input),
                        (8, input.rotate_right(1) | 1),
                        (16, input.rotate_left(3)),
                        (24, guard),
                        (32, origin),
                    ] {
                        left[offset..offset + 8].copy_from_slice(&value.to_le_bytes());
                    }
                    for index in 0..32usize {
                        left[128 + index * 8..136 + index * 8]
                            .copy_from_slice(&(index as u64 * 7 + 5).to_le_bytes());
                    }
                    let mut right = left.clone();
                    assert_eq!(unsafe { before.call(&mut left) }, 0);
                    assert_eq!(unsafe { after.call(&mut right) }, 0);
                    assert_eq!(
                        &left[..768],
                        &right[..768],
                        "variant={variant} input={input:#x} guard={guard:#x} origin={origin}"
                    );
                }
            }
        }
    }
}

#[test]
fn repeated_diamonds_keep_distinct_conditions_and_observable_first_arms() {
    for blocker in 0..4 {
        let mut function = bitmap_worklist::tests::shared_condition_fixture(32);
        let id = if blocker == 0 || blocker == 2 {
            BlockId(7)
        } else {
            BlockId(3)
        };
        let block = function
            .blocks
            .iter_mut()
            .find(|block| block.id == id)
            .unwrap();
        match blocker {
            0 => {
                if let MInst::Branch { cond, .. } = &mut block.insts[0] {
                    *cond = VReg(5);
                }
            }
            1 => block.insts.insert(
                0,
                MInst::MemFill {
                    dst_offset: 64,
                    byte_len: 1,
                    value: 5,
                },
            ),
            2 => block.insts.insert(
                0,
                MInst::Load {
                    dst: VReg(26),
                    base: BaseReg::SimState,
                    offset: 64,
                    size: OpSize::S64,
                },
            ),
            3 => block.phis.push(PhiNode {
                dst: VReg(26),
                sources: vec![(BlockId(1), VReg(5))],
            }),
            _ => unreachable!(),
        }
        function.verify();
        let before = format!("{function:?}");
        run(&mut function);
        function.verify();
        assert_eq!(format!("{function:?}"), before, "blocker={blocker}");
    }
}