#![cfg(feature = "llvm-17-or-greater")]

//! Test that we can parse the copy of `compatibility.ll` in LLVM 17's `test/Bitcode` directory

use llvm_ir::Module;
use std::path::Path;

macro_rules! llvm_test {
    ($path:expr, $func:ident) => {
        #[test]
        #[allow(non_snake_case)]
        fn $func() {
            let _ = env_logger::builder().is_test(true).try_init(); // capture log messages with test harness
            let path = Path::new($path);
            let _ = Module::from_bc_path(&path).expect("Failed to parse module");
        }
    };
}

llvm_test!(
    "tests/llvm_bc/compatibility-as-of-llvm-17.bc",
    compatibility_llvm_17
);

// tests for nuw, nsw, and exact flags, whose getters were added to the LLVM C API in LLVM 17
use llvm_ir::instruction;
use std::convert::TryInto;

#[test]
fn nuw_nsw_exact() {
    let _ = env_logger::builder().is_test(true).try_init(); // capture log messages with test harness
    let path = Path::new("tests/llvm_bc/compatibility-as-of-llvm-17.bc");
    let module = Module::from_bc_path(&path).expect("Failed to parse module");
    let func = module
        .get_func_by_name("instructions.binops")
        .expect("Failed to find function");
    let bb = func.basic_blocks
        .get(0)
        .expect("expected function to have a basic block");
    let add: &instruction::Add = &bb
        .instrs[0]
        .clone()
        .try_into()
        .expect("Expected an add");
    assert_eq!(add.nuw, false);
    assert_eq!(add.nsw, false);
    assert_eq!(&format!("{add}"), "%1 = add i8 %op1, i8 %op2");
    let add: &instruction::Add = &bb
        .instrs[1]
        .clone()
        .try_into()
        .expect("Expected an add");
    assert_eq!(add.nuw, true);
    assert_eq!(add.nsw, false);
    assert_eq!(&format!("{add}"), "%2 = add nuw i8 %op1, i8 %op2");
    let add: &instruction::Add = &bb
        .instrs[2]
        .clone()
        .try_into()
        .expect("Expected an add");
    assert_eq!(add.nuw, false);
    assert_eq!(add.nsw, true);
    assert_eq!(&format!("{add}"), "%3 = add nsw i8 %op1, i8 %op2");
    let add: &instruction::Add = &bb
        .instrs[3]
        .clone()
        .try_into()
        .expect("Expected an add");
    assert_eq!(add.nuw, true);
    assert_eq!(add.nsw, true);
    assert_eq!(&format!("{add}"), "%4 = add nuw nsw i8 %op1, i8 %op2");

    let sub: &instruction::Sub = &bb
        .instrs[4]
        .clone()
        .try_into()
        .expect("Expected a sub");
    assert_eq!(sub.nuw, false);
    assert_eq!(sub.nsw, false);
    assert_eq!(&format!("{sub}"), "%5 = sub i8 %op1, i8 %op2");
    let sub: &instruction::Sub = &bb
        .instrs[5]
        .clone()
        .try_into()
        .expect("Expected a sub");
    assert_eq!(sub.nuw, true);
    assert_eq!(sub.nsw, false);
    assert_eq!(&format!("{sub}"), "%6 = sub nuw i8 %op1, i8 %op2");
    let sub: &instruction::Sub = &bb
        .instrs[6]
        .clone()
        .try_into()
        .expect("Expected a sub");
    assert_eq!(sub.nuw, false);
    assert_eq!(sub.nsw, true);
    assert_eq!(&format!("{sub}"), "%7 = sub nsw i8 %op1, i8 %op2");
    let sub: &instruction::Sub = &bb
        .instrs[7]
        .clone()
        .try_into()
        .expect("Expected a sub");
    assert_eq!(sub.nuw, true);
    assert_eq!(sub.nsw, true);
    assert_eq!(&format!("{sub}"), "%8 = sub nuw nsw i8 %op1, i8 %op2");

    let mul: &instruction::Mul = &bb
        .instrs[8]
        .clone()
        .try_into()
        .expect("Expected a mul");
    assert_eq!(mul.nuw, false);
    assert_eq!(mul.nsw, false);
    assert_eq!(&format!("{mul}"), "%9 = mul i8 %op1, i8 %op2");
    let mul: &instruction::Mul = &bb
        .instrs[9]
        .clone()
        .try_into()
        .expect("Expected a mul");
    assert_eq!(mul.nuw, true);
    assert_eq!(mul.nsw, false);
    assert_eq!(&format!("{mul}"), "%10 = mul nuw i8 %op1, i8 %op2");
    let mul: &instruction::Mul = &bb
        .instrs[10]
        .clone()
        .try_into()
        .expect("Expected a mul");
    assert_eq!(mul.nuw, false);
    assert_eq!(mul.nsw, true);
    assert_eq!(&format!("{mul}"), "%11 = mul nsw i8 %op1, i8 %op2");
    let mul: &instruction::Mul = &bb
        .instrs[11]
        .clone()
        .try_into()
        .expect("Expected a mul");
    assert_eq!(mul.nuw, true);
    assert_eq!(mul.nsw, true);
    assert_eq!(&format!("{mul}"), "%12 = mul nuw nsw i8 %op1, i8 %op2");

    let udiv: &instruction::UDiv = &bb
        .instrs[12]
        .clone()
        .try_into()
        .expect("Expected a udiv");
    assert_eq!(udiv.exact, false);
    assert_eq!(&format!("{udiv}"), "%13 = udiv i8 %op1, i8 %op2");
    let udiv: &instruction::UDiv = &bb
        .instrs[13]
        .clone()
        .try_into()
        .expect("Expected a udiv");
    assert_eq!(udiv.exact, true);
    assert_eq!(&format!("{udiv}"), "%14 = udiv exact i8 %op1, i8 %op2");

    let sdiv: &instruction::SDiv = &bb
        .instrs[14]
        .clone()
        .try_into()
        .expect("Expected an sdiv");
    assert_eq!(sdiv.exact, false);
    assert_eq!(&format!("{sdiv}"), "%15 = sdiv i8 %op1, i8 %op2");
    let sdiv: &instruction::SDiv = &bb
        .instrs[15]
        .clone()
        .try_into()
        .expect("Expected an sdiv");
    assert_eq!(sdiv.exact, true);
    assert_eq!(&format!("{sdiv}"), "%16 = sdiv exact i8 %op1, i8 %op2");
}