use llvmkit_asmparser::ll_parser::Parser;
use llvmkit_ir::{AnyTypeEnum, Module};
fn parse_into(src: &str, m: &Module<'_>) {
Parser::new(src.as_bytes(), m)
.expect("lexer primes")
.parse_module()
.expect("parser succeeds");
}
#[test]
fn target_directives_round_trip_through_asm_writer() {
let printed = Module::with_new("target_directives_round_trip", |m| {
parse_into(
"target datalayout = \"e-m:e-i64:64\"\ntarget triple = \"x86_64-unknown-linux-gnu\"\n",
&m,
);
format!("{m}")
});
assert!(printed.contains("target datalayout = \"e-m:e-i64:64\""));
assert!(printed.contains("target triple = \"x86_64-unknown-linux-gnu\""));
}
#[test]
fn source_filename_round_trips_through_asm_writer() {
let printed = Module::with_new("source_file", |m| {
parse_into("source_filename = \"dir/file.c\"\n", &m);
format!("{m}")
});
assert!(
printed.contains("; ModuleID = 'source_file'\nsource_filename = \"dir/file.c\"\n"),
"AsmWriter output: {printed}"
);
}
#[test]
fn top_level_comdat_round_trips() {
let printed = Module::with_new("comdat_module", |m| {
parse_into("$foo = comdat largest\n", &m);
format!("{m}")
});
assert!(
printed.contains("$foo = comdat largest\n"),
"AsmWriter output: {printed}"
);
}
#[test]
fn global_externally_initialized_round_trips() {
let printed = Module::with_new("global_externally_initialized", |m| {
parse_into("@g = externally_initialized global i32 0\n", &m);
format!("{m}")
});
assert!(
printed.contains("@g = externally_initialized global i32 0\n"),
"AsmWriter output: {printed}"
);
}
#[test]
fn global_linkage_round_trips() {
let printed = Module::with_new("global_linkage", |m| {
parse_into("@g = weak_odr global i32 0\n", &m);
format!("{m}")
});
assert!(
printed.contains("@g = weak_odr global i32 0\n"),
"AsmWriter output: {printed}"
);
}
#[test]
fn global_visibility_round_trips() {
let printed = Module::with_new("global_visibility", |m| {
parse_into("@g = hidden global i32 0\n", &m);
format!("{m}")
});
assert!(
printed.contains("@g = hidden global i32 0\n"),
"AsmWriter output: {printed}"
);
}
#[test]
fn global_dll_storage_round_trips() {
let printed = Module::with_new("global_dll_storage", |m| {
parse_into("@g = dllexport global i32 0\n", &m);
format!("{m}")
});
assert!(
printed.contains("@g = dllexport global i32 0\n"),
"AsmWriter output: {printed}"
);
}
#[test]
fn global_tls_mode_round_trips() {
let printed = Module::with_new("global_tls", |m| {
parse_into("@g = thread_local(initialexec) global i32 0\n", &m);
format!("{m}")
});
assert!(
printed.contains("@g = thread_local(initialexec) global i32 0\n"),
"AsmWriter output: {printed}"
);
}
#[test]
fn global_unnamed_addr_round_trips() {
let printed = Module::with_new("global_unnamed_addr", |m| {
parse_into("@g = local_unnamed_addr global i32 0\n", &m);
format!("{m}")
});
assert!(
printed.contains("@g = local_unnamed_addr global i32 0\n"),
"AsmWriter output: {printed}"
);
}
#[test]
fn global_addrspace_round_trips() {
let printed = Module::with_new("global_addrspace", |m| {
parse_into("@g = addrspace(3) global i32 0\n", &m);
format!("{m}")
});
assert!(
printed.contains("@g = addrspace(3) global i32 0\n"),
"AsmWriter output: {printed}"
);
}
#[test]
fn global_trailing_attributes_round_trip() {
let printed = Module::with_new("global_trailing_attrs", |m| {
parse_into(
"$foo = comdat any\n@g = global i32 0, section \".data\", partition \"part\", comdat($foo), align 8\n",
&m,
);
format!("{m}")
});
assert!(
printed.contains(
"@g = global i32 0, section \".data\", partition \"part\", comdat($foo), align 8\n"
),
"AsmWriter output: {printed}"
);
}
#[test]
fn module_asm_directives_accumulate() {
let asm = Module::with_new("module_asm", |m| {
parse_into(
"module asm \"first line\"\nmodule asm \"second line\"\n",
&m,
);
m.module_asm()
});
assert!(asm.contains("first line"));
assert!(asm.contains("second line"));
}
#[test]
fn named_struct_forward_reference_resolves() {
Module::with_new("recursive_named", |m| {
let parser = Parser::new(b"%self = type { ptr }\n", &m).unwrap();
let parsed = parser.parse_module().expect("parser succeeds");
assert!(parsed.slot_mapping.named_types.contains_key("self"));
});
}
#[test]
fn array_and_vector_types_parse() {
Module::with_new("aggregate_types", |m| {
parse_into("declare void @takes([4 x i32], <8 x float>)\n", &m);
let f = m.function_by_name("takes").expect("function present");
let params: Vec<_> = f.signature().params().collect();
assert_eq!(params.len(), 2);
assert!(matches!(
AnyTypeEnum::from(params[0]),
AnyTypeEnum::Array(_)
));
let v = match AnyTypeEnum::from(params[1]) {
AnyTypeEnum::Vector(v) => v,
other => panic!("expected vector type, got {other:?}"),
};
assert!(!v.is_scalable());
});
}
#[test]
fn scalable_vector_type_parses() {
Module::with_new("scalable_vec", |m| {
parse_into("declare void @sv(<vscale x 4 x i32>)\n", &m);
let f = m.function_by_name("sv").expect("function present");
let params: Vec<_> = f.signature().params().collect();
let v = match AnyTypeEnum::from(params[0]) {
AnyTypeEnum::Vector(v) => v,
other => panic!("expected vector type, got {other:?}"),
};
assert!(v.is_scalable());
});
}
#[test]
fn variadic_declaration_round_trips() {
let printed = Module::with_new("variadic_decl", |m| {
parse_into("declare i32 @printf(ptr, ...)\n", &m);
format!("{m}")
});
assert!(
printed.contains("declare i32 @printf(ptr, ...)")
|| printed.contains("declare i32 @printf(ptr %0, ...)"),
"AsmWriter output: {printed}"
);
}
#[test]
fn numbered_global_records_in_slot_mapping() {
Module::with_new("numbered_globals", |m| {
let parser = Parser::new(b"@0 = global i32 0\n@1 = global i32 1\n", &m).unwrap();
let parsed = parser.parse_module().expect("parser succeeds");
assert_eq!(parsed.slot_mapping.global_values.get_next(), 2);
assert!(parsed.slot_mapping.global_values.get(0).is_some());
assert!(parsed.slot_mapping.global_values.get(1).is_some());
});
}
#[test]
fn void_in_value_position_is_rejected() {
let err = Module::with_new("reject_void", |m| {
let parser = Parser::new(b"@x = global void 0\n", &m).unwrap();
parser.parse_module().unwrap_err()
});
let msg = format!("{err}");
assert!(
msg.contains("non-void type") || msg.contains("only allowed at function results"),
"got: {msg}"
);
}
#[test]
fn unknown_top_level_entity_is_typed_error() {
let err = Module::with_new("unknown_top_level", |m| {
let parser = Parser::new(b"42 i32\n", &m).unwrap();
parser.parse_module().unwrap_err()
});
assert!(format!("{err}").contains("top-level entity"));
}