#[test]
fn llvm_bitops_compile_and_exec() {
    use crate::backend::VM;
    use crate::mir::{
        BasicBlockId, BinaryOp, ConstValue, FunctionSignature, MirFunction, MirInstruction,
        MirModule, MirType,
    };

    // Build MIR: compute sum of bitwise/shift ops -> 48
    let sig = FunctionSignature {
        name: "Main.main".into(),
        params: vec![],
        return_type: MirType::Integer,
        effects: Default::default(),
    };
    let mut f = MirFunction::new(sig, BasicBlockId::new(0));
    let bb = f.entry_block;
    // Constants
    let c5 = f.next_value_id();
    f.get_block_mut(bb)
        .unwrap()
        .add_instruction(MirInstruction::Const {
            dst: c5,
            value: ConstValue::Integer(5),
        });
    let c3 = f.next_value_id();
    f.get_block_mut(bb)
        .unwrap()
        .add_instruction(MirInstruction::Const {
            dst: c3,
            value: ConstValue::Integer(3),
        });
    let c2 = f.next_value_id();
    f.get_block_mut(bb)
        .unwrap()
        .add_instruction(MirInstruction::Const {
            dst: c2,
            value: ConstValue::Integer(2),
        });
    let c1 = f.next_value_id();
    f.get_block_mut(bb)
        .unwrap()
        .add_instruction(MirInstruction::Const {
            dst: c1,
            value: ConstValue::Integer(1),
        });
    let c32 = f.next_value_id();
    f.get_block_mut(bb)
        .unwrap()
        .add_instruction(MirInstruction::Const {
            dst: c32,
            value: ConstValue::Integer(32),
        });
    let c5_sh = f.next_value_id();
    f.get_block_mut(bb)
        .unwrap()
        .add_instruction(MirInstruction::Const {
            dst: c5_sh,
            value: ConstValue::Integer(5),
        });
    let c3_sh = f.next_value_id();
    f.get_block_mut(bb)
        .unwrap()
        .add_instruction(MirInstruction::Const {
            dst: c3_sh,
            value: ConstValue::Integer(3),
        });

    // a = 5 & 3 -> 1
    let a = f.next_value_id();
    f.get_block_mut(bb)
        .unwrap()
        .add_instruction(MirInstruction::BinOp {
            dst: a,
            op: BinaryOp::BitAnd,
            lhs: c5,
            rhs: c3,
        });
    // b = 5 | 2 -> 7
    let b = f.next_value_id();
    f.get_block_mut(bb)
        .unwrap()
        .add_instruction(MirInstruction::BinOp {
            dst: b,
            op: BinaryOp::BitOr,
            lhs: c5,
            rhs: c2,
        });
    // c = 5 ^ 1 -> 4
    let c = f.next_value_id();
    f.get_block_mut(bb)
        .unwrap()
        .add_instruction(MirInstruction::BinOp {
            dst: c,
            op: BinaryOp::BitXor,
            lhs: c5,
            rhs: c1,
        });
    // d = 1 << 5 -> 32
    let d = f.next_value_id();
    f.get_block_mut(bb)
        .unwrap()
        .add_instruction(MirInstruction::BinOp {
            dst: d,
            op: BinaryOp::Shl,
            lhs: c1,
            rhs: c5_sh,
        });
    // e = 32 >> 3 -> 4
    let e = f.next_value_id();
    f.get_block_mut(bb)
        .unwrap()
        .add_instruction(MirInstruction::BinOp {
            dst: e,
            op: BinaryOp::Shr,
            lhs: c32,
            rhs: c3_sh,
        });

    // sum = a + b + c + d + e
    let t1 = f.next_value_id();
    f.get_block_mut(bb)
        .unwrap()
        .add_instruction(MirInstruction::BinOp {
            dst: t1,
            op: BinaryOp::Add,
            lhs: a,
            rhs: b,
        });
    let t2 = f.next_value_id();
    f.get_block_mut(bb)
        .unwrap()
        .add_instruction(MirInstruction::BinOp {
            dst: t2,
            op: BinaryOp::Add,
            lhs: t1,
            rhs: c,
        });
    let t3 = f.next_value_id();
    f.get_block_mut(bb)
        .unwrap()
        .add_instruction(MirInstruction::BinOp {
            dst: t3,
            op: BinaryOp::Add,
            lhs: t2,
            rhs: d,
        });
    let sum = f.next_value_id();
    f.get_block_mut(bb)
        .unwrap()
        .add_instruction(MirInstruction::BinOp {
            dst: sum,
            op: BinaryOp::Add,
            lhs: t3,
            rhs: e,
        });
    f.get_block_mut(bb)
        .unwrap()
        .add_instruction(MirInstruction::Return { value: Some(sum) });

    let mut m = MirModule::new("bitops".into());
    m.add_function(f);

    // VM executes to 48
    let mut vm = VM::new();
    let out = vm.execute_module(&m).expect("vm exec");
    assert_eq!(out.to_string_box().value, "48");

    // LLVM: ensure lowering/emit succeeds; compile_and_execute should also return 48 (via MIR interpreter fallback)
    #[cfg(feature = "llvm-inkwell-legacy")]
    {
        use crate::backend::llvm;
        let tmp = format!(
            "{}/target/aot_objects/test_bitops",
            env!("CARGO_MANIFEST_DIR")
        );
        llvm::compile_to_object(&m, &format!("{}.o", tmp)).expect("llvm emit");
        let out2 = llvm::compile_and_execute(&m, &tmp).expect("llvm compile&exec");
        assert_eq!(out2.to_string_box().value, "48");
    }
}
