use llvmkit_asmparser::ll_parser::Parser;
use llvmkit_ir::Module;
fn parse_and_print(src: &str) -> String {
let m = Module::new("session3");
Parser::new(src.as_bytes(), &m)
.expect("lex")
.parse_module()
.expect("parse");
format!("{m}")
}
#[test]
fn parses_void_function_body() {
let printed = parse_and_print("define void @f() {\nentry:\n ret void\n}\n");
assert!(printed.contains("define void @f"));
assert!(printed.contains("ret void"));
}
#[test]
fn parses_int_add_and_ret() {
let printed = parse_and_print(
"define i32 @add(i32 %a, i32 %b) {\nentry:\n %r = add i32 %a, %b\n ret i32 %r\n}\n",
);
assert!(printed.contains("add i32 %a, %b"));
assert!(printed.contains("ret i32 %r"));
}
#[test]
fn parses_numbered_params() {
let printed = parse_and_print(
"define i32 @add(i32, i32) {\nentry:\n %r = add i32 %0, %1\n ret i32 %r\n}\n",
);
assert!(printed.contains("ret i32 %r"));
}
#[test]
fn parses_unreachable_terminator() {
let printed = parse_and_print("define void @abort() {\nentry:\n unreachable\n}\n");
assert!(printed.contains("unreachable"));
}
#[test]
fn parses_icmp_and_cond_br() {
let printed = parse_and_print(
"define i32 @abs(i32 %x) {\n\
entry:\n \
%is_zero = icmp eq i32 %x, 0\n \
br i1 %is_zero, label %zero_path, label %nonzero\n\
zero_path:\n \
ret i32 0\n\
nonzero:\n \
ret i32 %x\n\
}\n",
);
assert!(printed.contains("icmp eq i32 %x, 0"));
assert!(printed.contains("br i1 %is_zero, label %zero_path, label %nonzero"));
}
#[test]
fn parses_forward_block_reference() {
let printed = parse_and_print(
"define void @forward() {\n\
entry:\n \
br label %later\n\
later:\n \
ret void\n\
}\n",
);
assert!(printed.contains("br label %later"));
assert!(printed.contains("ret void"));
}
#[test]
fn parses_sub_and_mul() {
let printed = parse_and_print(
"define i32 @poly(i32 %x) {\n\
entry:\n \
%a = sub i32 %x, 1\n \
%b = mul i32 %a, %x\n \
ret i32 %b\n\
}\n",
);
assert!(printed.contains("sub i32 %x, 1"));
assert!(printed.contains("mul i32 %a, %x"));
}
#[test]
fn unsupported_opcode_is_typed_error() {
let m = Module::new("unsupported_opcode");
let parser = Parser::new(
b"define i32 @f(i32 %a) {\nentry:\n %x = store i32 %a, ptr null\n ret i32 %a\n}\n",
&m,
)
.unwrap();
let err = parser.parse_module().unwrap_err();
let msg = format!("{err}");
assert!(
msg.contains("instruction opcode supported by this parser"),
"got: {msg}"
);
}
#[test]
fn parses_div_and_rem_opcodes() {
let printed = parse_and_print(
"define i32 @divrem(i32 %a, i32 %b) {\nentry:\n \
%u = udiv i32 %a, %b\n \
%s = sdiv i32 %a, %b\n \
%ur = urem i32 %a, %b\n \
%sr = srem i32 %a, %b\n \
ret i32 %sr\n\
}\n",
);
assert!(printed.contains("udiv i32 %a, %b"));
assert!(printed.contains("sdiv i32 %a, %b"));
assert!(printed.contains("urem i32 %a, %b"));
assert!(printed.contains("srem i32 %a, %b"));
}
#[test]
fn parses_shift_and_bitwise_opcodes() {
let printed = parse_and_print(
"define i32 @bits(i32 %a, i32 %b) {\nentry:\n \
%s1 = shl i32 %a, 1\n \
%s2 = lshr i32 %s1, 1\n \
%s3 = ashr i32 %s2, 1\n \
%s4 = and i32 %s3, %b\n \
%s5 = or i32 %s4, %b\n \
%s6 = xor i32 %s5, %b\n \
ret i32 %s6\n\
}\n",
);
for op in ["shl", "lshr", "ashr", "and", "or", "xor"] {
assert!(printed.contains(op), "missing opcode {op}: {printed}");
}
}
#[test]
fn parses_int_casts() {
let printed = parse_and_print(
"define i64 @widen(i32 %a) {\nentry:\n \
%t = trunc i32 %a to i16\n \
%z = zext i16 %t to i32\n \
%s = sext i32 %z to i64\n \
ret i64 %s\n\
}\n",
);
assert!(printed.contains("trunc i32 %a to i16"));
assert!(printed.contains("zext i16 %t to i32"));
assert!(printed.contains("sext i32 %z to i64"));
}
#[test]
fn parses_ptr_int_casts() {
let printed = parse_and_print(
"define i64 @addr(ptr %p) {\nentry:\n \
%i = ptrtoint ptr %p to i64\n \
%q = inttoptr i64 %i to ptr\n \
%j = ptrtoint ptr %q to i64\n \
ret i64 %j\n\
}\n",
);
assert!(printed.contains("ptrtoint ptr %p to i64"));
assert!(printed.contains("inttoptr i64 %i to ptr"));
}
#[test]
fn parses_fp_arith_opcodes() {
let printed = parse_and_print(
"define float @fmath(float %a, float %b) {\nentry:\n \
%x = fadd float %a, %b\n \
%y = fsub float %x, %a\n \
%z = fmul float %y, %b\n \
%w = fdiv float %z, %a\n \
%r = frem float %w, %b\n \
ret float %r\n\
}\n",
);
for op in ["fadd", "fsub", "fmul", "fdiv", "frem"] {
assert!(printed.contains(op), "missing {op}: {printed}");
}
}
#[test]
fn parses_fneg_opcode() {
let printed = parse_and_print(
"define float @neg(float %a) {\nentry:\n %r = fneg float %a\n ret float %r\n}\n",
);
assert!(printed.contains("fneg float %a"));
}
#[test]
fn parses_fcmp_opcodes() {
let printed = parse_and_print(
"define i1 @ord(float %a, float %b) {\nentry:\n \
%r = fcmp oeq float %a, %b\n \
ret i1 %r\n\
}\n",
);
assert!(printed.contains("fcmp oeq float %a, %b"));
}
#[test]
fn parses_alloca_load_store() {
let printed = parse_and_print(
"define i32 @rw(i32 %v) {\nentry:\n \
%slot = alloca i32\n \
store i32 %v, ptr %slot\n \
%r = load i32, ptr %slot\n \
ret i32 %r\n\
}\n",
);
assert!(printed.contains("alloca i32"));
assert!(printed.contains("store i32 %v, ptr %slot"));
assert!(printed.contains("load i32, ptr %slot"));
}
#[test]
fn parses_gep_plain_and_inbounds() {
let printed = parse_and_print(
"define ptr @walk(ptr %p, i64 %i) {\nentry:\n \
%a = getelementptr i32, ptr %p, i64 %i\n \
%b = getelementptr inbounds i32, ptr %p, i64 %i\n \
ret ptr %b\n\
}\n",
);
assert!(printed.contains("getelementptr i32, ptr %p, i64 %i"));
assert!(printed.contains("getelementptr inbounds i32, ptr %p, i64 %i"));
}
#[test]
fn parses_select_int_fp_ptr() {
let printed = parse_and_print(
"define void @sel(i1 %c, i32 %a, i32 %b, float %fa, float %fb, ptr %pa, ptr %pb) {\nentry:\n \
%ri = select i1 %c, i32 %a, i32 %b\n \
%rf = select i1 %c, float %fa, float %fb\n \
%rp = select i1 %c, ptr %pa, ptr %pb\n \
ret void\n\
}\n",
);
assert!(printed.contains("select i1 %c, i32 %a, i32 %b"));
assert!(printed.contains("select i1 %c, float %fa, float %fb"));
assert!(printed.contains("select i1 %c, ptr %pa, ptr %pb"));
}
#[test]
fn parses_fp_to_int_casts() {
let printed = parse_and_print(
"define void @to_int(float %f) {\nentry:\n \
%s = fptosi float %f to i32\n \
%u = fptoui float %f to i32\n \
ret void\n\
}\n",
);
assert!(printed.contains("fptosi float %f to i32"));
assert!(printed.contains("fptoui float %f to i32"));
}
#[test]
fn parses_int_to_fp_casts() {
let printed = parse_and_print(
"define void @to_fp(i32 %i) {\nentry:\n \
%s = sitofp i32 %i to float\n \
%u = uitofp i32 %i to float\n \
ret void\n\
}\n",
);
assert!(printed.contains("sitofp i32 %i to float"));
assert!(printed.contains("uitofp i32 %i to float"));
}
#[test]
fn parses_addrspacecast() {
let printed = parse_and_print(
"define ptr addrspace(1) @as_cast(ptr %p) {\nentry:\n \
%r = addrspacecast ptr %p to ptr addrspace(1)\n \
ret ptr addrspace(1) %r\n\
}\n",
);
assert!(printed.contains("addrspacecast ptr %p to ptr addrspace(1)"));
}